summaryrefslogtreecommitdiff
path: root/Dafny
diff options
context:
space:
mode:
authorGravatar mikebarnett <unknown>2009-07-15 21:03:41 +0000
committerGravatar mikebarnett <unknown>2009-07-15 21:03:41 +0000
commitdb6ef6c9e2bca859280cfbe17871c38da74977fc (patch)
treed742312e6c9c69386f4c7111d9133ccc3d810e01 /Dafny
Initial set of files.
Diffstat (limited to 'Dafny')
-rw-r--r--Dafny/Dafny.atg963
-rw-r--r--Dafny/DafnyAst.ssc1049
-rw-r--r--Dafny/DafnyMain.ssc73
-rw-r--r--Dafny/DafnyPipeline.sscproj118
-rw-r--r--Dafny/Makefile15
-rw-r--r--Dafny/Parser.ssc1592
-rw-r--r--Dafny/Printer.ssc663
-rw-r--r--Dafny/Resolver.ssc1419
-rw-r--r--Dafny/Scanner.ssc491
-rw-r--r--Dafny/Translator.ssc2446
-rw-r--r--Dafny/parser.frame103
-rw-r--r--Dafny/scanner.frame170
12 files changed, 9102 insertions, 0 deletions
diff --git a/Dafny/Dafny.atg b/Dafny/Dafny.atg
new file mode 100644
index 00000000..3d7630ae
--- /dev/null
+++ b/Dafny/Dafny.atg
@@ -0,0 +1,963 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+
+/*---------------------------------------------------------------------------
+// Dafny
+// Rustan Leino, first created 25 January 2008
+//--------------------------------------------------------------------------*/
+
+using System.Collections.Generic;
+using Microsoft.Boogie;
+
+
+COMPILER Dafny
+
+/*--------------------------------------------------------------------------*/
+
+static List<ClassDecl!>! theClasses = new List<ClassDecl!>();
+
+
+static Expression! dummyExpr = new LiteralExpr(Token.NoToken);
+static Statement! dummyStmt = new ReturnStmt(Token.NoToken);
+static Attributes.Argument! dummyAttrArg = new Attributes.Argument("dummyAttrArg");
+static Scope<string>! parseVarScope = new Scope<string>();
+
+///<summary>
+/// Parses top level declarations from "filename" and appends them to "classes".
+/// Returns the number of parsing errors encountered.
+/// Note: first initialize the Scanner.
+///</summary>
+public static int Parse (string! filename, List<ClassDecl!>! classes) /* throws System.IO.IOException */ {
+ using (System.IO.StreamReader reader = new System.IO.StreamReader(filename)) {
+ BoogiePL.Buffer.Fill(reader);
+ Scanner.Init(filename);
+ return Parse(classes);
+ }
+}
+
+///<summary>
+/// Parses top level declarations and appends them to "classes".
+/// Returns the number of parsing errors encountered.
+/// Note: first initialize the Scanner.
+///</summary>
+public static int Parse (List<ClassDecl!>! classes) {
+ List<ClassDecl!> oldClasses = theClasses;
+ theClasses = classes;
+ Parse();
+ theClasses = oldClasses;
+ return Errors.count;
+}
+
+/*--------------------------------------------------------------------------*/
+CHARACTERS
+ letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".
+ digit = "0123456789".
+ special = "'_?`\\".
+ glyph = "`~!@#$%^&*()-_=+[{]}|;:',<.>/?\\".
+
+ cr = '\r'.
+ lf = '\n'.
+ tab = '\t'.
+
+ space = ' '.
+ quote = '"'.
+
+ nondigit = letter + special.
+ nonquote = letter + digit + space + glyph.
+
+
+/*------------------------------------------------------------------------*/
+TOKENS
+ ident = nondigit {nondigit | digit}.
+ digits = digit {digit}.
+ string = quote {nonquote} quote.
+
+COMMENTS FROM "/*" TO "*/" NESTED
+COMMENTS FROM "//" TO lf
+
+IGNORE cr + lf + tab
+
+
+/*------------------------------------------------------------------------*/
+PRODUCTIONS
+
+Dafny
+= (. ClassDecl! c; .)
+ { ClassDecl<out c> (. theClasses.Add(c); .)
+ }
+ EOF
+ .
+
+ClassDecl<out ClassDecl! c>
+= (. Token! id;
+ Attributes attrs = null;
+ List<TypeParameter!> typeArgs = new List<TypeParameter!>();
+ List<MemberDecl!> members = new List<MemberDecl!>();
+ .)
+ "class"
+ { Attribute<ref attrs> }
+ Ident<out id>
+ [ GenericParameters<typeArgs> ]
+ "{"
+ { ClassMemberDecl<members>
+ }
+ "}" (. c = new ClassDecl(id, id.val, typeArgs, members, attrs); .)
+ .
+
+ClassMemberDecl<List<MemberDecl!\>! mm>
+= (. Method! m;
+ Function! f;
+ .)
+ ( FieldDecl<mm>
+ | FunctionDecl<out f> (. mm.Add(f); .)
+ | MethodDecl<out m> (. mm.Add(m); .)
+ | FrameDecl
+ )
+ .
+
+FieldDecl<List<MemberDecl!\>! mm>
+= (. Attributes attrs = null;
+ Token! id; Type! ty;
+ .)
+ "var"
+ { Attribute<ref attrs> }
+ IdentType<out id, out ty> (. mm.Add(new Field(id, id.val, ty, attrs)); .)
+ { "," IdentType<out id, out ty> (. mm.Add(new Field(id, id.val, ty, attrs)); .)
+ }
+ ";"
+ .
+
+IdentType<out Token! id, out Type! ty>
+= Ident<out id>
+ ":"
+ Type<out ty>
+ .
+
+IdentTypeOptional<out BoundVar! var>
+= (. Token! id; Type! ty; Type optType = null;
+ .)
+ Ident<out id>
+ [ ":" Type<out ty> (. optType = ty; .)
+ ]
+ (. var = new BoundVar(id, id.val, optType == null ? new InferredTypeProxy() : optType); .)
+ .
+
+/*------------------------------------------------------------------------*/
+
+GenericParameters<List<TypeParameter!\>! typeArgs>
+= (. Token! id; .)
+ "<"
+ Ident<out id> (. typeArgs.Add(new TypeParameter(id, id.val)); .)
+ { "," Ident<out id> (. typeArgs.Add(new TypeParameter(id, id.val)); .)
+ }
+ ">"
+ .
+
+FrameDecl
+= (. Token! id;
+ Attributes attrs = null;
+ .)
+ "frame"
+ { Attribute<ref attrs> }
+ Ident<out id>
+ "{"
+ /* TBD */
+ "}"
+ .
+
+/*------------------------------------------------------------------------*/
+
+MethodDecl<out Method! m>
+= (. Token! id;
+ Attributes attrs = null;
+ List<TypeParameter!>! typeArgs = new List<TypeParameter!>();
+ List<Formal!> ins = new List<Formal!>();
+ List<Formal!> outs = new List<Formal!>();
+ List<MaybeFreeExpression!> req = new List<MaybeFreeExpression!>();
+ List<Expression!> mod = new List<Expression!>();
+ List<MaybeFreeExpression!> ens = new List<MaybeFreeExpression!>();
+ Statement! bb; Statement body = null;
+ .)
+ "method"
+ { Attribute<ref attrs> }
+ Ident<out id>
+ [ GenericParameters<typeArgs> ]
+ (. parseVarScope.PushMarker(); .)
+ Formals<true, ins>
+ [ "returns"
+ Formals<false, outs>
+ ]
+
+ ( ";" { MethodSpec<req, mod, ens> }
+ | { MethodSpec<req, mod, ens> } BlockStmt<out bb> (. body = bb; .)
+ )
+
+ (. parseVarScope.PopMarker();
+ m = new Method(id, id.val, typeArgs, ins, outs, req, mod, ens, body, attrs);
+ .)
+ .
+
+MethodSpec<List<MaybeFreeExpression!\>! req, List<Expression!\>! mod, List<MaybeFreeExpression!\>! ens>
+= (. Expression! e; bool isFree = false;
+ .)
+ ( "modifies" [ Expression<out e> (. mod.Add(e); .)
+ { "," Expression<out e> (. mod.Add(e); .)
+ }
+ ] ";"
+ | [ "free" (. isFree = true; .)
+ ]
+ ( "requires" Expression<out e> ";" (. req.Add(new MaybeFreeExpression(e, isFree)); .)
+ | "ensures" Expression<out e> ";" (. ens.Add(new MaybeFreeExpression(e, isFree)); .)
+ )
+ )
+ .
+
+Formals<bool incoming, List<Formal!\>! formals>
+= (. Token! id; Type! ty; .)
+ "("
+ [
+ IdentType<out id, out ty> (. formals.Add(new Formal(id, id.val, ty, incoming)); parseVarScope.Push(id.val, id.val); .)
+ { "," IdentType<out id, out ty> (. formals.Add(new Formal(id, id.val, ty, incoming)); parseVarScope.Push(id.val, id.val); .)
+ }
+ ]
+ ")"
+ .
+
+/*------------------------------------------------------------------------*/
+
+Type<out Type! ty>
+= (. ty = new BoolType(); /*keep compiler happy*/
+ .)
+ ( "bool" (. /* yeah, that's it! */ .)
+ | "int" (. ty = new IntType(); .)
+ | ReferenceType<out ty>
+ )
+ .
+
+ReferenceType<out Type! ty>
+= (. Token! x;
+ ty = new BoolType(); /*keep compiler happy*/
+ List<Type!>! gt;
+ .)
+ ( "object" (. ty = new ObjectType(); .)
+
+ | (. gt = new List<Type!>(); .)
+ Ident<out x>
+ [ GenericInstantiation<gt> ] (. ty = new ClassType(x, x.val, gt); .)
+
+ | (. gt = new List<Type!>(); .)
+ "set"
+ GenericInstantiation<gt> (. if (gt.Count != 1) {
+ SemErr("set type expects exactly one type argument");
+ }
+ ty = new SetType(gt[0]);
+ .)
+
+ | (. gt = new List<Type!>(); .)
+ "seq"
+ GenericInstantiation<gt> (. if (gt.Count != 1) {
+ SemErr("seq type expects exactly one type argument");
+ }
+ ty = new SeqType(gt[0]);
+ .)
+ )
+ .
+
+GenericInstantiation<List<Type!\>! gt>
+= (. Type! ty; .)
+ "<"
+ Type<out ty> (. gt.Add(ty); .)
+ { "," Type<out ty> (. gt.Add(ty); .)
+ }
+ ">"
+ .
+
+/*------------------------------------------------------------------------*/
+
+FunctionDecl<out Function! f>
+= (. Attributes attrs = null;
+ Token! id;
+ List<TypeParameter!> typeArgs = new List<TypeParameter!>();
+ List<Formal!> formals = new List<Formal!>();
+ Type! returnType;
+ List<Expression!> reqs = new List<Expression!>();
+ List<Expression!> reads = new List<Expression!>();
+ Expression! bb; Expression body = null;
+ bool use = false;
+ .)
+ "function"
+ { Attribute<ref attrs> }
+ [ "use" (. use = true; .) ]
+ Ident<out id>
+ [ GenericParameters<typeArgs> ]
+ (. parseVarScope.PushMarker(); .)
+ Formals<true, formals>
+ ":"
+ Type<out returnType>
+ ( ";"
+ { FunctionSpec<reqs, reads> }
+ | { FunctionSpec<reqs, reads> }
+ ExtendedExpr<out bb> (. body = bb; .)
+ )
+ (. parseVarScope.PopMarker();
+ f = new Function(id, id.val, use, typeArgs, formals, returnType, reqs, reads, body, attrs);
+ .)
+ .
+
+FunctionSpec<List<Expression!\>! reqs, List<Expression!\>! reads>
+= (. Expression! e; .)
+ ( "requires" Expression<out e> ";" (. reqs.Add(e); .)
+ | ReadsClause<reads>
+ )
+ .
+
+ReadsClause<List<Expression!\>! reads>
+= "reads"
+ [ Expressions<reads> ]
+ ";"
+ .
+
+ExtendedExpr<out Expression! e>
+= (. e = dummyExpr; .)
+ "{"
+ ( IfThenElseExpr<out e>
+ | Expression<out e>
+ )
+ "}"
+ .
+
+IfThenElseExpr<out Expression! e>
+= (. Token! x; Expression! e0; Expression! e1 = dummyExpr; .)
+ "if" (. x = token; .)
+ "(" Expression<out e> ")"
+ ExtendedExpr<out e0>
+ "else"
+ ( IfThenElseExpr<out e1>
+ | ExtendedExpr<out e1>
+ ) (. e = new ITEExpr(x, e, e0, e1); .)
+ .
+
+/*------------------------------------------------------------------------*/
+
+BlockStmt<out Statement! block>
+= (. Token! x;
+ List<Statement!> body = new List<Statement!>();
+ Statement! s;
+ .)
+ (. parseVarScope.PushMarker(); .)
+ "{" (. x = token; .)
+ { Stmt<body>
+ }
+ "}" (. block = new BlockStmt(x, body); .)
+ (. parseVarScope.PopMarker(); .)
+ .
+
+Stmt<List<Statement!\>! ss>
+= (. Statement! s; .)
+ /* By first reading a sequence of block statements, we avoid problems in the generated parser, despite
+ the ambiguity in the grammar. See Note in ConstAtomExpression production.
+ */
+ { BlockStmt<out s> (. ss.Add(s); .)
+ }
+ ( OneStmt<out s> (. ss.Add(s); .)
+ | VarDeclStmts<ss>
+ )
+ .
+
+OneStmt<out Statement! s>
+= (. Token! x; Token! id; string label = null;
+ s = dummyStmt; /* to please the compiler */
+ .)
+ /* This list does not contain BlockStmt, see comment above in Stmt production. */
+ ( AssertStmt<out s>
+ | AssumeStmt<out s>
+ | UseStmt<out s>
+ | AssignStmt<out s>
+ | HavocStmt<out s>
+ | CallStmt<out s>
+ | IfStmt<out s>
+ | WhileStmt<out s>
+ | ForeachStmt<out s>
+ | "label" (. x = token; .)
+ Ident<out id> ":" (. s = new LabelStmt(x, id.val); .)
+ | "break" (. x = token; .)
+ [ Ident<out id> (. label = id.val; .)
+ ] ";" (. s = new BreakStmt(x, label); .)
+ | "return" (. x = token; .)
+ ";" (. s = new ReturnStmt(x); .)
+ )
+ .
+
+AssignStmt<out Statement! s>
+= (. Token! x;
+ Expression! lhs;
+ Expression rhs;
+ Type ty;
+ s = dummyStmt;
+ .)
+ LhsExpr<out lhs>
+ ":=" (. x = token; .)
+ AssignRhs<out rhs, out ty> (. if (rhs != null) {
+ s = new AssignStmt(x, lhs, rhs);
+ } else {
+ assert ty != null;
+ s = new AssignStmt(x, lhs, ty);
+ }
+ .)
+ ";"
+ .
+
+AssignRhs<out Expression e, out Type ty>
+/* ensures e == null <==> ty == null; */
+= (. Expression! ee; Type! tt;
+ e = null; ty = null;
+ .)
+ ( "new" ReferenceType<out tt> (. ty = tt; .)
+ | Expression<out ee> (. e = ee; .)
+ ) (. if (e == null && ty == null) { e = dummyExpr; } .)
+ .
+
+HavocStmt<out Statement! s>
+= (. Token! x; Expression! lhs; .)
+ "havoc" (. x = token; .)
+ LhsExpr<out lhs> ";" (. s = new AssignStmt(x, lhs); .)
+ .
+
+LhsExpr<out Expression! e>
+= Expression<out e> /* TODO: restrict LHS further */
+ .
+
+VarDeclStmts<List<Statement!\>! ss>
+= (. VarDecl! d; .)
+ "var"
+ IdentTypeRhs<out d> (. ss.Add(d); parseVarScope.Push(d.Name, d.Name); .)
+ { "," IdentTypeRhs<out d> (. ss.Add(d); parseVarScope.Push(d.Name, d.Name); .)
+ }
+ ";"
+ .
+
+IdentTypeRhs<out VarDecl! d>
+= (. Token! id; Type! ty; Expression! e;
+ Expression rhs = null; Type newType = null;
+ Type optionalType = null; DeterminedAssignmentRhs optionalRhs = null;
+ .)
+ Ident<out id>
+ [ ":" Type<out ty> (. optionalType = ty; .)
+ ]
+ [ ":="
+ AssignRhs<out rhs, out newType>
+ ]
+ (. if (rhs != null) {
+ assert newType == null;
+ optionalRhs = new ExprRhs(rhs);
+ } else if (newType != null) {
+ optionalRhs = new TypeRhs(newType);
+ } else if (optionalType == null) {
+ optionalType = new InferredTypeProxy();
+ }
+ d = new VarDecl(id, id.val, optionalType, optionalRhs);
+ .)
+ .
+
+IfStmt<out Statement! ifStmt>
+= (. Token! x;
+ Expression guard;
+ Statement! thn;
+ Statement! s;
+ Statement els = null;
+ .)
+ "if" (. x = token; .)
+ Guard<out guard>
+ BlockStmt<out thn>
+ [ "else"
+ ( IfStmt<out s> (. els = s; .)
+ | BlockStmt<out s> (. els = s; .)
+ )
+ ]
+ (. ifStmt = new IfStmt(x, guard, thn, els); .)
+ .
+
+WhileStmt<out Statement! stmt>
+= (. Token! x;
+ Expression guard;
+ bool isFree; Expression! e;
+ List<MaybeFreeExpression!> invariants = new List<MaybeFreeExpression!>();
+ List<Expression!> decreases = new List<Expression!>();
+ Statement! body;
+ .)
+ "while" (. x = token; .)
+ Guard<out guard> (. assume guard == null || Owner.None(guard); .)
+ {( (. isFree = false; .)
+ [ "free" (. isFree = true; .)
+ ]
+ "invariant"
+ Expression<out e> (. invariants.Add(new MaybeFreeExpression(e, isFree)); .)
+ ";"
+ )
+ |
+ (
+ "decreases"
+ Expression<out e> (. decreases.Add(e); .)
+ { "," Expression<out e> (. decreases.Add(e); .)
+ }
+ ";"
+ )
+ }
+ BlockStmt<out body> (. stmt = new WhileStmt(x, guard, invariants, decreases, body); .)
+ .
+
+Guard<out Expression e> /* null represents demonic-choice */
+= (. Expression! ee; e = null; .)
+ "("
+ ( "*" (. e = null; .)
+ | Expression<out ee> (. e = ee; .)
+ )
+ ")"
+ .
+
+CallStmt<out Statement! s>
+= (. Token! x, id;
+ Expression! e;
+ List<IdentifierExpr!> lhs = new List<IdentifierExpr!>();
+ .)
+ "call" (. x = token; .)
+ CallStmtSubExpr<out e>
+
+ [ "," /* call a,b,c,... := ... */
+ (. if (e is IdentifierExpr) {
+ lhs.Add((IdentifierExpr)e);
+ } else if (e is FieldSelectExpr) {
+ SemErr(e.tok, "each LHS of call statement must be a variable, not a field");
+ } else {
+ SemErr(e.tok, "each LHS of call statement must be a variable");
+ }
+ .)
+ Ident<out id> (. lhs.Add(new IdentifierExpr(id, id.val)); .)
+ { "," Ident<out id> (. lhs.Add(new IdentifierExpr(id, id.val)); .)
+ }
+ ":="
+ CallStmtSubExpr<out e>
+
+ | ":=" /* call a := ... */
+ (. if (e is IdentifierExpr) {
+ lhs.Add((IdentifierExpr)e);
+ } else if (e is FieldSelectExpr) {
+ SemErr(e.tok, "each LHS of call statement must be a variable, not a field");
+ } else {
+ SemErr(e.tok, "each LHS of call statement must be a variable");
+ }
+ .)
+ CallStmtSubExpr<out e>
+ ]
+ ";"
+
+ /* "e" has now been parsed as one of: IdentifierExpr, FunctionCallExpr, FieldSelectExpr.
+ It denotes the RHS, so to be legal it must be a FunctionCallExpr. */
+ (. if (e is FunctionCallExpr) {
+ FunctionCallExpr fce = (FunctionCallExpr)e;
+ s = new CallStmt(x, lhs, fce.Receiver, fce.Name, fce.Args); // this actually does an ownership transfer of fce.Args
+ } else {
+ SemErr("RHS of call statement must denote a method invocation");
+ s = new CallStmt(x, lhs, dummyExpr, "dummyMethodName", new List<Expression!>());
+ }
+ .)
+ .
+
+/*------------------------------------------------------------------------*/
+
+ForeachStmt<out Statement! s>
+= (. Token! x, boundVar;
+ Type! ty;
+ Expression! collection;
+ Expression! range;
+ List<PredicateStmt!> bodyPrefix = new List<PredicateStmt!>();
+ AssignStmt bodyAssign = null;
+ .)
+ (. parseVarScope.PushMarker(); .)
+ "foreach" (. x = token;
+ range = new LiteralExpr(x, true);
+ ty = new InferredTypeProxy();
+ .)
+ "(" Ident<out boundVar>
+ [ ":" Type<out ty> ]
+ "in" Expression<out collection>
+ (. parseVarScope.Push(boundVar.val, boundVar.val); .)
+ [ "|" Expression<out range> ]
+ ")"
+ "{"
+ { AssertStmt<out s> (. if (s is PredicateStmt) { bodyPrefix.Add((PredicateStmt)s); } .)
+ | AssumeStmt<out s> (. if (s is PredicateStmt) { bodyPrefix.Add((PredicateStmt)s); } .)
+ | UseStmt<out s> (. if (s is PredicateStmt) { bodyPrefix.Add((PredicateStmt)s); } .)
+ }
+ ( AssignStmt<out s> (. if (s is AssignStmt) { bodyAssign = (AssignStmt)s; } .)
+ | HavocStmt<out s> (. if (s is AssignStmt) { bodyAssign = (AssignStmt)s; } .)
+ )
+ "}" (. s = new ForeachStmt(x, new BoundVar(boundVar, boundVar.val, ty), collection, range, bodyPrefix, bodyAssign); .)
+ (. parseVarScope.PopMarker(); .)
+ .
+
+AssertStmt<out Statement! s>
+= (. Token! x; Expression! e; .)
+ "assert" (. x = token; .)
+ Expression<out e> ";" (. s = new AssertStmt(x, e); .)
+ .
+
+AssumeStmt<out Statement! s>
+= (. Token! x; Expression! e; .)
+ "assume" (. x = token; .)
+ Expression<out e> ";" (. s = new AssumeStmt(x, e); .)
+ .
+
+UseStmt<out Statement! s>
+= (. Token! x; Expression! e; .)
+ "use" (. x = token; .)
+ Expression<out e> ";" (. s = new UseStmt(x, e); .)
+ .
+
+/*------------------------------------------------------------------------*/
+
+Expression<out Expression! e0>
+= (. Token! x; Expression! e1; .)
+ ImpliesExpression<out e0>
+ { EquivOp (. x = token; .)
+ ImpliesExpression<out e1> (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.Iff, e0, e1); .)
+ }
+ .
+
+EquivOp = "<==>" | '\u21d4'.
+
+/*------------------------------------------------------------------------*/
+ImpliesExpression<out Expression! e0>
+= (. Token! x; Expression! e1; .)
+ LogicalExpression<out e0>
+ [ ImpliesOp (. x = token; .)
+ ImpliesExpression<out e1> (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.Imp, e0, e1); .)
+ ]
+ .
+
+ImpliesOp = "==>" | '\u21d2'.
+
+/*------------------------------------------------------------------------*/
+LogicalExpression<out Expression! e0>
+= (. Token! x; Expression! e1; .)
+ RelationalExpression<out e0>
+ [ AndOp (. x = token; .)
+ RelationalExpression<out e1> (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.And, e0, e1); .)
+ { AndOp (. x = token; .)
+ RelationalExpression<out e1> (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.And, e0, e1); .)
+ }
+ | OrOp (. x = token; .)
+ RelationalExpression<out e1> (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.Or, e0, e1); .)
+ { OrOp (. x = token; .)
+ RelationalExpression<out e1> (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.Or, e0, e1); .)
+ }
+ ]
+ .
+
+AndOp = "&&" | '\u2227'.
+OrOp = "||" | '\u2228'.
+
+/*------------------------------------------------------------------------*/
+RelationalExpression<out Expression! e0>
+= (. Token! x; Expression! e1; BinaryExpr.Opcode op; .)
+ Term<out e0>
+ [ RelOp<out x, out op>
+ Term<out e1> (. e0 = new BinaryExpr(x, op, e0, e1); .)
+ ]
+ .
+
+RelOp<out Token! x, out BinaryExpr.Opcode op>
+= (. x = Token.NoToken; op = BinaryExpr.Opcode.Add/*(dummy)*/; .)
+ ( "==" (. x = token; op = BinaryExpr.Opcode.Eq; .)
+ | "<" (. x = token; op = BinaryExpr.Opcode.Lt; .)
+ | ">" (. x = token; op = BinaryExpr.Opcode.Gt; .)
+ | "<=" (. x = token; op = BinaryExpr.Opcode.Le; .)
+ | ">=" (. x = token; op = BinaryExpr.Opcode.Ge; .)
+ | "!=" (. x = token; op = BinaryExpr.Opcode.Neq; .)
+ | "!!" (. x = token; op = BinaryExpr.Opcode.Disjoint; .)
+ | "in" (. x = token; op = BinaryExpr.Opcode.In; .)
+ | '\u2260' (. x = token; op = BinaryExpr.Opcode.Neq; .)
+ | '\u2264' (. x = token; op = BinaryExpr.Opcode.Le; .)
+ | '\u2265' (. x = token; op = BinaryExpr.Opcode.Ge; .)
+ )
+ .
+
+/*------------------------------------------------------------------------*/
+Term<out Expression! e0>
+= (. Token! 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 Token! x, out BinaryExpr.Opcode op>
+= (. x = Token.NoToken; op=BinaryExpr.Opcode.Add/*(dummy)*/; .)
+ ( "+" (. x = token; op = BinaryExpr.Opcode.Add; .)
+ | "-" (. x = token; op = BinaryExpr.Opcode.Sub; .)
+ )
+ .
+
+/*------------------------------------------------------------------------*/
+Factor<out Expression! e0>
+= (. Token! 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 Token! x, out BinaryExpr.Opcode op>
+= (. x = Token.NoToken; op = BinaryExpr.Opcode.Add/*(dummy)*/; .)
+ ( "*" (. x = token; op = BinaryExpr.Opcode.Mul; .)
+ | "/" (. x = token; op = BinaryExpr.Opcode.Div; .)
+ | "%" (. x = token; op = BinaryExpr.Opcode.Mod; .)
+ )
+ .
+
+/*------------------------------------------------------------------------*/
+UnaryExpression<out Expression! e>
+= (. Token! x; e = dummyExpr; .)
+ ( "-" (. x = token; .)
+ UnaryExpression<out e> (. e = new BinaryExpr(x, BinaryExpr.Opcode.Sub, new LiteralExpr(x, 0), e); .)
+ | NegOp (. x = token; .)
+ UnaryExpression<out e> (. e = new UnaryExpr(x, UnaryExpr.Opcode.Not, e); .)
+ | SelectExpression<out e>
+ | ConstAtomExpression<out e>
+ )
+ .
+
+NegOp = "!" | '\u00ac'.
+
+ConstAtomExpression<out Expression! e>
+= (. Token! x; int n; List<Expression!>! elements;
+ e = dummyExpr;
+ .)
+ ( "false" (. e = new LiteralExpr(token, false); .)
+ | "true" (. e = new LiteralExpr(token, true); .)
+ | "null" (. e = new LiteralExpr(token); .)
+ | Nat<out n> (. e = new LiteralExpr(token, n); .)
+ | "fresh" (. x = token; .)
+ "(" Expression<out e> ")" (. e = new FreshExpr(x, e); .)
+ | "|" (. x = token; .)
+ Expression<out e> (. e = new UnaryExpr(x, UnaryExpr.Opcode.SeqLength, e); .)
+ "|"
+ /* Note, the following open-curly-brace causes grammar ambiguities that result in two Coco warnings.
+ One of these is the confusion between BlockStmt and AssignStmt, the former starting with an open
+ curly brace and the latter starting with a left-hand side Expression, which syntactically can
+ start with an open curly brace. The other is the confusion between a quantifier's triggers/attributes
+ and the quantifier's body. The disamiguation I've chosen involves giving priority to BlockStmt
+ (since no semantically legal AssignStmt can have a set constructor as its left-hand side) and to
+ triggers/attributes (which, unfortunately, changes what programs are syntactically allowed, but there
+ is a simple workaround, which is for a user to put parentheses around any quantifier body that begins
+ with a set constructor.
+ */
+ | "{" (. x = token; elements = new List<Expression!>(); .)
+ [ Expressions<elements> ] (. e = new SetDisplayExpr(x, elements); .)
+ "}"
+ | "[" (. x = token; elements = new List<Expression!>(); .)
+ [ Expressions<elements> ] (. e = new SeqDisplayExpr(x, elements); .)
+ "]"
+ )
+ .
+
+/*------------------------------------------------------------------------*/
+
+/* returns one of:
+ -- IdentifierExpr
+ -- FunctionCallExpr
+ -- FieldSelectExpr
+*/
+CallStmtSubExpr<out Expression! e>
+= (. e = dummyExpr; .)
+ ( IdentOrFuncExpression<out e>
+ | ObjectExpression<out e>
+ SelectOrCallSuffix<ref e>
+ )
+ { SelectOrCallSuffix<ref e> }
+ .
+
+SelectExpression<out Expression! e>
+= (. Token! id; e = dummyExpr; .)
+ ( IdentOrFuncExpression<out e>
+ | ObjectExpression<out e>
+ )
+ { SelectOrCallSuffix<ref e> }
+ .
+
+IdentOrFuncExpression<out Expression! e>
+= (. Token! id; e = dummyExpr; List<Expression!>! args; .)
+ Ident<out id>
+ [ "(" (. args = new List<Expression!>(); .)
+ [ Expressions<args> ]
+ ")" (. e = new FunctionCallExpr(id, id.val, new ImplicitThisExpr(id), args); .)
+ ] (. if (e == dummyExpr) {
+ if (parseVarScope.Find(id.val) != null) {
+ e = new IdentifierExpr(id, id.val);
+ } else {
+ e = new FieldSelectExpr(id, new ImplicitThisExpr(id), id.val);
+ }
+ }
+ .)
+ .
+
+SelectOrCallSuffix<ref Expression! e>
+= (. Token! id, x; List<Expression!>! args;
+ Expression e0 = null; Expression e1 = null; Expression! ee; bool anyDots = false;
+ bool func = false;
+ .)
+ ( "."
+ Ident<out id>
+ [ "(" (. args = new List<Expression!>(); func = true; .)
+ [ Expressions<args> ]
+ ")" (. e = new FunctionCallExpr(id, id.val, e, args); .)
+ ] (. if (!func) { e = new FieldSelectExpr(id, e, id.val); } .)
+
+ | "[" (. x = token; .)
+ ( Expression<out ee> (. e0 = ee; .)
+ [ ".." (. anyDots = true; .)
+ [ Expression<out ee> (. e1 = ee; .)
+ ]
+ ]
+ | ".." Expression<out ee> (. anyDots = true; e1 = ee; .)
+ ) (. if (!anyDots) {
+ assert e1 == null;
+ e = new SeqSelectExpr(x, true, e, e0, null);
+ } else {
+ assert e0 != null || e1 != null;
+ e = new SeqSelectExpr(x, false, e, e0, e1);
+ }
+ .)
+ "]"
+ )
+ .
+
+/* ObjectExpression represents those expressions E that could possibly be used in E.f
+ or E(...), except Ident. Since the lookahead is just 1, quantifier expressions are also
+ parsed here. The expression returned is never an lvalue.
+*/
+ObjectExpression<out Expression! e>
+= (. Token! x; e = dummyExpr; .)
+ ( "this" (. e = new ThisExpr(token); .)
+ | "old" (. x = token; .)
+ "("
+ Expression<out e>
+ ")" (. e = new OldExpr(x, e); .)
+ | "(" ( QuantifierGuts<out e>
+ | Expression<out e>
+ )
+ ")"
+ )
+ .
+
+/*------------------------------------------------------------------------*/
+
+QuantifierGuts<out Expression! q>
+= (. Token! x = Token.NoToken;
+ bool univ = false;
+ BoundVar! bv;
+ List<BoundVar!> bvars = new List<BoundVar!>();
+ Token! tok; Expr! e; ExprSeq! es;
+ Attributes attrs = null;
+ Triggers trigs = null;
+ Expression! body;
+ .)
+ ( Forall (. x = token; univ = true; .)
+ | Exists (. x = token; .)
+ )
+ (. parseVarScope.PushMarker(); .)
+ IdentTypeOptional<out bv> (. bvars.Add(bv); parseVarScope.Push(bv.Name, bv.Name); .)
+ { ","
+ IdentTypeOptional<out bv> (. bvars.Add(bv); parseVarScope.Push(bv.Name, bv.Name); .)
+ }
+ QSep
+ /* The grammar is ambiguous in how to resolve triggers/attributes versus the expression body.
+ However, the loop generated by Coco for the next two lines of grammar declarations is still
+ fine--it will loop, picking up as many triggers/attributes it can before going on to parse an
+ expression. This seems good, because there's a simple workaround for quantifier bodies that
+ begin with a set constructor: simply put parentheses around the quantifier body. See also
+ the Note in production ConstAtomExpression.
+ */
+ { AttributeOrTrigger<ref attrs, ref trigs> }
+ Expression<out body>
+ (. if (univ) {
+ q = new ForallExpr(x, bvars, body, trigs, attrs);
+ } else {
+ q = new ExistsExpr(x, bvars, body, trigs, attrs);
+ }
+ parseVarScope.PopMarker();
+ .)
+ .
+
+Forall = "forall" | '\u2200'.
+Exists = "exists" | '\u2203'.
+QSep = "::" | '\u2022'.
+
+Expressions<List<Expression!\>! 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 = token.val; .)
+ [ AttributeArg<out aArg> (. aArgs.Add(aArg); .)
+ { "," AttributeArg<out aArg> (. aArgs.Add(aArg); .)
+ }
+ ] (. attrs = new Attributes(aName, aArgs, attrs); .)
+ .
+
+AttributeArg<out Attributes.Argument! arg>
+= (. Expression! e; arg = dummyAttrArg; .)
+ ( string (. arg = new Attributes.Argument(token.val.Substring(1, token.val.Length-2)); .)
+ | Expression<out e> (. arg = new Attributes.Argument(e); .)
+ )
+ .
+
+AttributeOrTrigger<ref Attributes attrs, ref Triggers trigs>
+= (. List<Expression!> es = new List<Expression!>();
+ .)
+ "{"
+ ( AttributeBody<ref attrs>
+ | (. es = new List<Expression!>(); .)
+ Expressions<es> (. trigs = new Triggers(es, trigs); .)
+ )
+ "}"
+ .
+
+/*------------------------------------------------------------------------*/
+
+Ident<out Token! x>
+=
+ ident (. x = token; .)
+ .
+
+Nat<out int n>
+=
+ digits
+ (. try {
+ n = System.Convert.ToInt32(token.val);
+ } catch (System.FormatException) {
+ SemErr("incorrectly formatted number");
+ n = 0;
+ }
+ .)
+ .
+
+END Dafny.
diff --git a/Dafny/DafnyAst.ssc b/Dafny/DafnyAst.ssc
new file mode 100644
index 00000000..8f6821dd
--- /dev/null
+++ b/Dafny/DafnyAst.ssc
@@ -0,0 +1,1049 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+
+namespace Microsoft.Dafny
+{
+ public class Program {
+ public readonly string! Name;
+ public readonly List<ClassDecl!>! Classes;
+ public Program(string! name, [Captured] List<ClassDecl!>! classes) {
+ Name = name;
+ Classes = classes;
+ }
+ }
+
+ public class Attributes {
+ public readonly string! Name;
+ /*Frozen*/ public readonly List<Argument!>! Args;
+ public readonly Attributes Prev;
+
+ public Attributes(string! name, [Captured] List<Argument!>! args, Attributes prev)
+ {
+ Name = name;
+ Args = args;
+ Prev = prev;
+ }
+
+ public class Argument {
+ public readonly string S;
+ public readonly Expression E;
+ invariant (S == null) != (E == null);
+
+ public Argument(string! s) {
+ S = s;
+ }
+ public Argument(Expression! e) {
+ 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 { return new InferredTypeProxy(); }
+ }
+
+ public bool IsRefType {
+ get {
+ if (this is ObjectType) {
+ return true;
+ } else {
+ ClassType ct = this as ClassType;
+ return ct != null && ct.ResolvedParam == null;
+ }
+ }
+ }
+ public bool IsTypeParameter {
+ get
+ ensures result ==> this is ClassType && ((ClassType)this).ResolvedParam != null;
+ {
+ ClassType ct = this as ClassType;
+ return ct != null && ct.ResolvedParam != null;
+ }
+ }
+ }
+
+ public abstract class BasicType : Type {
+ }
+
+ public class BoolType : BasicType {
+ [Pure] public override string! ToString() {
+ return "bool";
+ }
+ }
+
+ public class IntType : BasicType {
+ [Pure] public override string! ToString() {
+ return "int";
+ }
+ }
+
+ public class ObjectType : BasicType {
+ [Pure] public override string! ToString() {
+ return "object";
+ }
+ }
+
+ public abstract class CollectionType : Type {
+ public readonly Type! Arg;
+ public CollectionType(Type! arg) {
+ this.Arg = arg;
+ }
+ }
+
+ public class SetType : CollectionType {
+ public SetType(Type! arg) {
+ base(arg);
+ }
+ [Pure] public override string! ToString() {
+ assume Arg.IsPeerConsistent;
+ return "set<" + Arg + ">";
+ }
+ }
+
+ public class SeqType : CollectionType {
+ public SeqType(Type! arg) {
+ base(arg);
+ }
+ [Pure] public override string! ToString() {
+ assume Arg.IsPeerConsistent;
+ return "seq<" + Arg + ">";
+ }
+ }
+
+ public class ClassType : Type {
+ public readonly Token! tok;
+ public readonly string! Name;
+ [Rep] public readonly List<Type!>! TypeArgs;
+
+ public ClassDecl ResolvedClass; // filled in by resolution, if Name denotes a class and TypeArgs match the type parameters of that class
+ public TypeParameter ResolvedParam; // filled in by resolution, if Name denotes an enclosing type parameter and TypeArgs is the empty list
+
+ public ClassType(Token! tok, string! name, [Captured] List<Type!>! typeArgs) {
+ this.tok = tok;
+ this.Name = name;
+ this.TypeArgs = typeArgs;
+ }
+
+ /// <summary>
+ /// This constructor constructs a resolved class type
+ /// </summary>
+ public ClassType(Token! tok, string! name, ClassDecl! cd, [Captured] List<Type!>! typeArgs) {
+ this.tok = tok;
+ this.Name = name;
+ this.TypeArgs = typeArgs;
+ this.ResolvedClass = cd;
+ }
+
+ /// <summary>
+ /// This constructor constructs a resolved type parameter
+ /// </summary>
+ public ClassType(Token! tok, string! name, TypeParameter! tp) {
+ this.tok = tok;
+ this.Name = name;
+ this.TypeArgs = new List<Type!>();
+ this.ResolvedParam = tp;
+ }
+
+ /// <summary>
+ /// If type denotes a resolved class type, then return that class type.
+ /// Otherwise, return null.
+ /// </summary>
+ public static ClassType DenotesClass(Type! type)
+ ensures result != null ==> result.ResolvedClass != null;
+ {
+ while (true)
+ invariant type.IsPeerConsistent;
+ {
+ TypeProxy pt = type as TypeProxy;
+ if (pt != null && pt.T != null) {
+ type = pt.T;
+ assume type.IsPeerConsistent;
+ } else {
+ break;
+ }
+ }
+ ClassType ct = type as ClassType;
+ if (ct != null && ct.ResolvedClass != null) {
+ return ct;
+ } else {
+ return null;
+ }
+ }
+
+ [Pure] public override string! ToString() {
+ string s = Name;
+ if (TypeArgs.Count != 0) {
+ string sep = "<";
+ foreach (Type t in TypeArgs) {
+ assume t.IsPeerConsistent;
+ s += sep + t;
+ sep = ",";
+ }
+ s += ">";
+ }
+ return s;
+ }
+ }
+
+ public abstract class TypeProxy : Type {
+ public Type T; // filled in during resolution
+ internal TypeProxy() { }
+
+ [Pure] public override string! ToString() {
+ assume T == null || T.IsPeerConsistent;
+ return T == null ? "?" : T.ToString();
+ }
+ }
+
+ 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 {
+ TypeParameter! orig;
+ public ParamTypeProxy(TypeParameter! orig) {
+ 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 object or any class type.
+ /// </summary>
+ public class ObjectTypeProxy : RestrictedTypeProxy {
+ public override int OrderID { get { return 0; } }
+ }
+
+ /// <summary>
+ /// This proxy stands for object or any class type or a set or sequence of object or a class type.
+ /// </summary>
+ public class ObjectsTypeProxy : RestrictedTypeProxy {
+ public override int OrderID { get { return 1; } }
+ }
+
+ /// <summary>
+ /// This proxy stands for either:
+ /// set(Arg) or seq(Arg)
+ /// </summary>
+ public class CollectionTypeProxy : RestrictedTypeProxy {
+ public readonly Type! Arg;
+ public CollectionTypeProxy(Type! arg) {
+ Arg = arg;
+ }
+ public override int OrderID { get { return 2; } }
+ }
+
+ /// <summary>
+ /// This proxy stands for either:
+ /// int or set or seq
+ /// if AllowSeq, or:
+ /// int or set
+ /// if !AllowSeq.
+ /// </summary>
+ public class OperationTypeProxy : RestrictedTypeProxy {
+ public readonly bool AllowSeq;
+ public OperationTypeProxy(bool allowSeq) {
+ AllowSeq = allowSeq;
+ }
+ public override int OrderID { get { return 3; } }
+ }
+
+ // ------------------------------------------------------------------------------------------------------
+
+ public abstract class Declaration {
+ public Token! tok;
+ public readonly string! Name;
+ public readonly Attributes Attributes;
+
+ public Declaration(Token! tok, string! name, Attributes attributes) {
+ this.tok = tok;
+ this.Name = name;
+ this.Attributes = attributes;
+ }
+ }
+
+ public class TypeParameter : Declaration {
+ public interface ParentType { }
+ [Peer] ParentType parent;
+ public ParentType Parent {
+ get {
+ return parent;
+ }
+ [param: Captured]
+ set
+ requires Parent == null; // set it only once
+ 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].
+ requires value is ClassDecl || value is Function || value is Method;
+ modifies parent;
+ {
+ parent = value;
+ }
+ }
+ public TypeParameter(Token! tok, string! name) {
+ base(tok, name, null);
+ }
+ }
+
+ public class ClassDecl : Declaration, TypeParameter.ParentType {
+ public List<TypeParameter!>! TypeArgs;
+ public List<MemberDecl!>! Members;
+
+ public ClassDecl(Token! tok, string! name, List<TypeParameter!>! typeArgs, [Captured] List<MemberDecl!>! members, Attributes attributes) {
+ TypeArgs = typeArgs;
+ Members = members;
+ base(tok, name, attributes);
+ }
+ }
+
+ public abstract class MemberDecl : Declaration {
+ public ClassDecl EnclosingClass; // filled in during resolution
+
+ public MemberDecl(Token! tok, string! name, Attributes attributes) {
+ base(tok, name, attributes);
+ }
+ /// <summary>
+ /// Returns className+"."+memberName. Available only after resolution.
+ /// </summary>
+ public string! FullName {
+ get
+ requires EnclosingClass != null;
+ {
+ return EnclosingClass.Name + "." + Name;
+ }
+ }
+ }
+
+ public class Field : MemberDecl {
+ public readonly Type! Type;
+
+ public Field(Token! tok, string! name, Type! type, Attributes attributes) {
+ Type = type;
+ base(tok, name, attributes);
+ }
+ }
+
+ public interface IVariable {
+ string! Name { get; }
+ string! UniqueName { get; }
+ Type! Type { get; }
+ bool IsMutable { get; }
+ }
+
+ public abstract class NonglobalVariable : IVariable {
+ public readonly Token! tok;
+ readonly string! name;
+ public string! Name { get { return name; } }
+ readonly int varId = varIdCount++;
+ public string! UniqueName { get { return name + "#" + varId; } }
+ Type! type;
+ [Pure(false)] // TODO: if Type gets the status of [Frozen], then this attribute is not needed
+ public Type! Type { get {
+ assume type.IsPeerConsistent;
+ while (true)
+ invariant type.IsPeerConsistent;
+ {
+ TypeProxy t = type as TypeProxy;
+ if (t != null && t.T != null) {
+ type = t.T;
+ assume type.IsPeerConsistent;
+ } else {
+ return type;
+ }
+ }
+ } }
+ public abstract bool IsMutable { get; }
+
+ public NonglobalVariable(Token! tok, string! name, Type! type) {
+ this.tok = tok;
+ this.name = name;
+ this.type = type;
+ }
+
+ 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(Token! tok, string! name, Type! type, bool inParam) {
+ InParam = inParam;
+ base(tok, name, type);
+ }
+ }
+
+ public class BoundVar : NonglobalVariable {
+ public override bool IsMutable { get { return false; } }
+
+ public BoundVar(Token! tok, string! name, Type! type) {
+ base(tok, name, type);
+ }
+ }
+
+ public class Function : MemberDecl, TypeParameter.ParentType {
+ public readonly bool Use;
+ public readonly List<TypeParameter!>! TypeArgs;
+ public readonly List<Formal!>! Formals;
+ public readonly Type! ResultType;
+ public readonly List<Expression!>! Req;
+ public readonly List<Expression!>! Reads;
+ public readonly Expression Body; // an extended expression
+
+ public Function(Token! tok, string! name, bool use, [Captured] List<TypeParameter!>! typeArgs, [Captured] List<Formal!>! formals, Type! resultType,
+ List<Expression!>! req, List<Expression!>! reads, Expression body, Attributes attributes) {
+ this.Use = use;
+ this.TypeArgs = typeArgs;
+ this.Formals = formals;
+ this.ResultType = resultType;
+ this.Req = req;
+ this.Reads = reads;
+ this.Body = body;
+ base(tok, name, attributes);
+ }
+ }
+
+ public class Method : MemberDecl, TypeParameter.ParentType {
+ public readonly List<TypeParameter!>! TypeArgs;
+ public readonly List<Formal!>! Ins;
+ public readonly List<Formal!>! Outs;
+ public readonly List<MaybeFreeExpression!>! Req;
+ public readonly List<Expression!>! Mod;
+ public readonly List<MaybeFreeExpression!>! Ens;
+ public readonly Statement Body;
+
+ public Method(Token! tok, string! name,
+ [Captured] List<TypeParameter!>! typeArgs,
+ [Captured] List<Formal!>! ins, [Captured] List<Formal!>! outs,
+ [Captured] List<MaybeFreeExpression!>! req, [Captured] List<Expression!>! mod, [Captured] List<MaybeFreeExpression!>! ens,
+ [Captured] Statement body,
+ Attributes attributes) {
+ this.TypeArgs = typeArgs;
+ this.Ins = ins;
+ this.Outs = outs;
+ this.Req = req;
+ this.Mod = mod;
+ this.Ens = ens;
+ this.Body = body;
+ base(tok, name, attributes);
+ }
+ }
+
+ // ------------------------------------------------------------------------------------------------------
+
+ public abstract class Statement {
+ public readonly Token! Tok;
+ public Statement(Token! tok) {
+ this.Tok = tok;
+ }
+ }
+
+ public abstract class PredicateStmt : Statement {
+ [Peer] public readonly Expression! Expr;
+ [Captured]
+ public PredicateStmt(Token! tok, Expression! expr)
+ ensures Owner.Same(this, expr);
+ {
+ base(tok);
+ Owner.AssignSame(this, expr);
+ this.Expr = expr;
+ }
+ }
+
+ public class AssertStmt : PredicateStmt {
+ [Captured]
+ public AssertStmt(Token! tok, Expression! expr)
+ ensures Owner.Same(this, expr);
+ {
+ base(tok, expr);
+ }
+ }
+
+ public class AssumeStmt : PredicateStmt {
+ [Captured]
+ public AssumeStmt(Token! tok, Expression! expr)
+ ensures Owner.Same(this, expr);
+ {
+ base(tok, expr);
+ }
+ }
+
+ public class UseStmt : PredicateStmt {
+ [Captured]
+ public UseStmt(Token! tok, Expression! expr)
+ ensures Owner.Same(this, expr);
+ {
+ base(tok, expr);
+ }
+ [Peer] private FunctionCallExpr fce;
+ /// <summary>
+ /// This method assumes the statement has been successfully resolved.
+ /// </summary>
+ [Pure(false)]
+ public FunctionCallExpr! FunctionCallExpr {
+ get {
+ if (fce == null) {
+ Expression expr = Expr;
+ while (true)
+ invariant Owner.Same(this, expr);
+ {
+ if (expr is OldExpr) {
+ expr = ((OldExpr)expr).E;
+ } else {
+ break;
+ }
+ }
+ assume expr is FunctionCallExpr;
+ fce = (FunctionCallExpr)expr;
+ }
+ return fce;
+ }
+ }
+ public bool EvalInOld {
+ get {
+ return Expr is OldExpr;
+ }
+ }
+ }
+
+ public class LabelStmt : Statement {
+ public readonly string! Label;
+ public LabelStmt(Token! tok, string! label) {
+ this.Label = label;
+ base(tok);
+ }
+ }
+
+ public class BreakStmt : Statement {
+ public readonly string TargetLabel;
+ public Statement TargetStmt; // filled in during resolution
+
+ public BreakStmt(Token! tok, string targetLabel) {
+ this.TargetLabel = targetLabel;
+ base(tok);
+ }
+ }
+
+ public class ReturnStmt : Statement {
+ public ReturnStmt(Token! tok) {
+ base(tok);
+ }
+ }
+
+ public abstract class AssignmentRhs {
+ internal AssignmentRhs() { }
+ }
+
+ public abstract class DeterminedAssignmentRhs : AssignmentRhs {
+ internal DeterminedAssignmentRhs() { }
+ }
+
+ public class ExprRhs : DeterminedAssignmentRhs {
+ public readonly Expression! Expr;
+ public ExprRhs(Expression! expr) {
+ Expr = expr;
+ }
+ }
+
+ public class TypeRhs : DeterminedAssignmentRhs {
+ public readonly Type! Type;
+ public TypeRhs(Type! type) {
+ Type = type;
+ }
+ }
+
+ public class HavocRhs : AssignmentRhs {
+ }
+
+ public class AssignStmt : Statement {
+ public readonly Expression! Lhs;
+ public readonly AssignmentRhs! Rhs;
+ public AssignStmt(Token! tok, Expression! lhs, Expression! rhs) { // ordinary assignment statement
+ this.Lhs = lhs;
+ this.Rhs = new ExprRhs(rhs);
+ base(tok);
+ }
+ public AssignStmt(Token! tok, Expression! lhs, Type! type) { // alloc statement
+ this.Lhs = lhs;
+ this.Rhs = new TypeRhs(type);
+ base(tok);
+ }
+ public AssignStmt(Token! tok, Expression! lhs) { // havoc
+ this.Lhs = lhs;
+ this.Rhs = new HavocRhs();
+ base(tok);
+ }
+ }
+
+ public class VarDecl : Statement, IVariable {
+ readonly string! name;
+ public string! Name { get { return name; } }
+ readonly int varId = NonglobalVariable.varIdCount++;
+ public string! UniqueName { get { return name + "#" + varId; } }
+ 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 {
+ assume type != null; /* we assume object has been resolved */
+ assume type.IsPeerConsistent;
+ while (true)
+ invariant type != null && type.IsPeerConsistent;
+ {
+ TypeProxy t = type as TypeProxy;
+ if (t != null && t.T != null) {
+ type = t.T;
+ assume type.IsPeerConsistent;
+ } else {
+ return type;
+ }
+ }
+ } }
+ public bool IsMutable { get { return true; } }
+
+ public readonly DeterminedAssignmentRhs Rhs;
+ invariant OptionalType != null || Rhs != null;
+
+ public VarDecl(Token! tok, string! name, Type type, DeterminedAssignmentRhs rhs)
+ requires type != null || rhs != null;
+ {
+ this.name = name;
+ this.OptionalType = type;
+ this.Rhs = rhs;
+ base(tok);
+ }
+ }
+
+ public class CallStmt : Statement {
+ public readonly List<IdentifierExpr!>! Lhs;
+ public readonly Expression! Receiver;
+ public readonly string! MethodName;
+ public readonly List<Expression!>! Args;
+ public Method Method; // filled in by resolution
+
+ public CallStmt(Token! tok, List<IdentifierExpr!>! lhs, Expression! receiver, string! methodName, List<Expression!>! args) {
+ this.Lhs = lhs;
+ this.Receiver = receiver;
+ this.MethodName = methodName;
+ this.Args = args;
+ base(tok);
+ }
+ }
+
+ public class BlockStmt : Statement {
+ public readonly List<Statement!>! Body;
+ public BlockStmt(Token! tok, [Captured] List<Statement!>! body) {
+ this.Body = body;
+ base(tok);
+ }
+ }
+
+ public class IfStmt : Statement {
+ public readonly Expression Guard;
+ public readonly Statement! Thn;
+ public readonly Statement Els;
+ invariant Els == null || Els is BlockStmt || Els is IfStmt;
+
+ public IfStmt(Token! tok, Expression guard, Statement! thn, Statement els)
+ requires els == null || els is BlockStmt || els is IfStmt;
+ {
+ this.Guard = guard;
+ this.Thn = thn;
+ this.Els = els;
+ base(tok);
+ }
+ }
+
+ public class WhileStmt : Statement {
+ public readonly Expression Guard;
+ public readonly List<MaybeFreeExpression!>! Invariants;
+ public readonly List<Expression!>! Decreases;
+ public readonly Statement! Body;
+
+ public WhileStmt(Token! tok, Expression guard,
+ List<MaybeFreeExpression!>! invariants, List<Expression!>! decreases,
+ Statement! body) {
+ this.Guard = guard;
+ this.Invariants = invariants;
+ this.Decreases = decreases;
+ this.Body = body;
+ base(tok);
+ }
+ }
+
+ public class ForeachStmt : Statement {
+ public readonly BoundVar! BoundVar;
+ public readonly Expression! Collection;
+ public readonly Expression! Range;
+ public readonly List<PredicateStmt!>! BodyPrefix;
+ public readonly AssignStmt BodyAssign;
+
+ public ForeachStmt(Token! tok, BoundVar! boundVar, Expression! collection, Expression! range, List<PredicateStmt!>! bodyPrefix, AssignStmt bodyAssign) {
+ this.BoundVar = boundVar;
+ this.Collection = collection;
+ this.Range = range;
+ this.BodyPrefix = bodyPrefix;
+ this.BodyAssign = bodyAssign;
+ base(tok);
+ }
+ }
+
+ // ------------------------------------------------------------------------------------------------------
+
+ public abstract class Expression {
+ public readonly Token! tok;
+ 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
+ ensures type == null ==> result == null; // useful in conjunction with postcondition of constructor
+ {
+ while (true) {
+ TypeProxy t = type as TypeProxy;
+ if (t != null && t.T != null) {
+ type = t.T;
+ } else {
+ assume type == null || type.IsPeerConsistent;
+ return type;
+ }
+ }
+ }
+ [NoDefaultContract] // no particular validity of 'this' is required, except that it not be committed
+ set
+ requires this.IsValid;
+ requires Type == null; // set it only once
+ requires value != null && value.IsPeerConsistent;
+ modifies type;
+ {
+ type = value;
+ while (true) {
+ TypeProxy t = type as TypeProxy;
+ if (t != null && t.T != null) {
+ type = t.T;
+ } else {
+ return;
+ }
+ }
+ }
+ }
+
+ public Expression(Token! tok)
+ ensures type == null; // we would have liked to have written Type==null, but that's not admissible or provable
+ {
+ this.tok = tok;
+ }
+ }
+
+ public class LiteralExpr : Expression {
+ public readonly object Value;
+
+ public static bool IsTrue(Expression! e) {
+ if (e is LiteralExpr) {
+ LiteralExpr le = (LiteralExpr)e;
+ return le.Value is bool && (bool)le.Value;
+ } else {
+ return false;
+ }
+ }
+
+ public LiteralExpr(Token! tok) { // represents the Dafny literal "null"
+ this.Value = null;
+ base(tok);
+ }
+
+ public LiteralExpr(Token! tok, int n)
+ requires 0 <= n;
+ {
+ this.Value = n;
+ base(tok);
+ }
+
+ public LiteralExpr(Token! tok, bool b) {
+ this.Value = b;
+ base(tok);
+ }
+ }
+
+ public class ThisExpr : Expression {
+ public ThisExpr(Token! tok) {
+ base(tok);
+ }
+ }
+
+ public class ImplicitThisExpr : ThisExpr {
+ public ImplicitThisExpr(Token! tok) {
+ base(tok);
+ }
+ }
+
+ public class IdentifierExpr : Expression {
+ public readonly string! Name;
+ public IVariable Var; // filled in by resolution
+
+ public IdentifierExpr(Token! tok, string! name) {
+ Name = name;
+ base(tok);
+ }
+ }
+
+ public abstract class DisplayExpression : Expression {
+ public readonly List<Expression!>! Elements;
+ public DisplayExpression(Token! tok, List<Expression!>! elements) {
+ Elements = elements;
+ base(tok);
+ }
+ }
+
+ public class SetDisplayExpr : DisplayExpression {
+ public SetDisplayExpr(Token! tok, List<Expression!>! elements) {
+ base(tok, elements);
+ }
+ }
+
+ public class SeqDisplayExpr : DisplayExpression {
+ public SeqDisplayExpr(Token! tok, List<Expression!>! elements) {
+ base(tok, elements);
+ }
+ }
+
+ public class FieldSelectExpr : Expression {
+ public readonly Expression! Obj;
+ public readonly string! FieldName;
+ public Field Field; // filled in by resolution
+
+ public FieldSelectExpr(Token! tok, Expression! obj, string! fieldName) {
+ this.Obj = obj;
+ this.FieldName = fieldName;
+ base(tok);
+ }
+ }
+
+ 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;
+ invariant SelectOne ==> E1 == null;
+ invariant E0 != null || E1 != null;
+
+ public SeqSelectExpr(Token! tok, bool selectOne, Expression! seq, Expression e0, Expression e1)
+ requires selectOne ==> e1 == null;
+ requires e0 != null || e1 != null;
+ {
+ SelectOne = selectOne;
+ Seq = seq;
+ E0 = e0;
+ E1 = e1;
+ base(tok);
+ }
+ }
+
+ public class FunctionCallExpr : Expression {
+ public readonly string! Name;
+ [Peer] public readonly Expression! Receiver;
+ [Peer] public readonly List<Expression!>! Args;
+ public Function Function; // filled in by resolution
+
+ [Captured]
+ public FunctionCallExpr(Token! tok, string! fn, Expression! receiver, [Captured] List<Expression!>! args)
+ ensures type == null;
+ ensures Owner.Same(this, receiver);
+ {
+ base(tok);
+ this.Name = fn;
+ Owner.AssignSame(this, receiver);
+ this.Receiver = receiver;
+ this.Args = args;
+ }
+ }
+
+ /// <summary>
+ /// UseExpr's are used only temporarily during translation.
+ /// </summary>
+ public class UseExpr : FunctionCallExpr {
+ [NotDelayed] [Captured]
+ public UseExpr(FunctionCallExpr! fce)
+ requires fce.Function != null && fce.Function.Use;
+ {
+ base(fce.tok, fce.Name, fce.Receiver, new List<Expression!>(fce.Args));
+ this.Function = fce.Function;
+ assume Type.Bool.IsPeerConsistent; // This would follow from BoolType being an Immutable type, or from Type.Bool being [Frozen]
+ this.Type = Type.Bool;
+ }
+ }
+
+ public class OldExpr : Expression {
+ [Peer] public readonly Expression! E;
+ [Captured]
+ public OldExpr(Token! tok, Expression! expr) {
+ base(tok);
+ Owner.AssignSame(this, expr);
+ E = expr;
+ }
+ }
+
+ public class FreshExpr : Expression {
+ public readonly Expression! E;
+ public FreshExpr(Token! tok, Expression! expr) {
+ E = expr;
+ base(tok);
+ }
+ }
+
+ public class UnaryExpr : Expression {
+ public enum Opcode { Not, SeqLength }
+ public readonly Opcode Op;
+ public readonly Expression! E;
+
+ public UnaryExpr(Token! tok, Opcode op, Expression! e) {
+ this.Op = op;
+ this.E = e;
+ base(tok);
+ }
+ }
+
+ public class BinaryExpr : Expression {
+ public enum Opcode { Iff, Imp, And, Or,
+ Eq, Neq, Lt, Le, Ge, Gt,
+ Disjoint, In,
+ 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,
+ Union, Intersection, SetDifference,
+ // sequences
+ SeqEq, SeqNeq, ProperPrefix, Prefix, Concat, InSeq
+ }
+ public ResolvedOpcode ResolvedOp; // filled in by resolution
+
+ public static string! OpcodeString(Opcode op) {
+ 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.Add: return "+";
+ case Opcode.Sub: return "-";
+ case Opcode.Mul: return "*";
+ case Opcode.Div: return "/";
+ case Opcode.Mod: return "%";
+ default:
+ assert false; // unexpected operator
+ }
+ }
+ public readonly Expression! E0;
+ public readonly Expression! E1;
+
+ public BinaryExpr(Token! tok, Opcode op, Expression! e0, Expression! e1) {
+ this.Op = op;
+ this.E0 = e0;
+ this.E1 = e1;
+ base(tok);
+ }
+ }
+
+ public abstract class QuantifierExpr : Expression {
+ public readonly List<BoundVar!>! BoundVars;
+ public readonly Expression! Body;
+ public readonly Triggers Trigs;
+ public readonly Attributes Attributes;
+
+ public QuantifierExpr(Token! tok, List<BoundVar!>! bvars, Expression! body, Triggers trigs, Attributes attrs) {
+ this.BoundVars = bvars;
+ this.Body = body;
+ this.Trigs = trigs;
+ this.Attributes = attrs;
+ base(tok);
+ }
+ }
+
+ public class Triggers {
+ public readonly List<Expression!>! Terms;
+ public readonly Triggers Prev;
+
+ public Triggers(List<Expression!>! terms, Triggers prev) {
+ this.Terms = terms;
+ this.Prev = prev;
+ }
+ }
+
+ public class ForallExpr : QuantifierExpr {
+ public ForallExpr(Token! tok, List<BoundVar!>! bvars, Expression! body, Triggers trig, Attributes attrs) {
+ base(tok, bvars, body, trig, attrs);
+ }
+ }
+
+ public class ExistsExpr : QuantifierExpr {
+ public ExistsExpr(Token! tok, List<BoundVar!>! bvars, Expression! body, Triggers trig, Attributes attrs) {
+ base(tok, bvars, body, trig, attrs);
+ }
+ }
+
+ public class ITEExpr : Expression { // an ITEExpr is an "extended expression" and is only allowed in certain places
+ public readonly Expression! Test;
+ public readonly Expression! Thn;
+ public readonly Expression! Els;
+
+ public ITEExpr(Token! tok, Expression! test, Expression! thn, Expression! els) {
+ this.Test = test;
+ this.Thn = thn;
+ this.Els = els;
+ base(tok);
+ }
+ }
+
+ public class MaybeFreeExpression {
+ public readonly Expression! E;
+ public readonly bool IsFree;
+ public MaybeFreeExpression(Expression! e, bool isFree) {
+ E = e;
+ IsFree = isFree;
+ }
+ }
+}
diff --git a/Dafny/DafnyMain.ssc b/Dafny/DafnyMain.ssc
new file mode 100644
index 00000000..84e366e2
--- /dev/null
+++ b/Dafny/DafnyMain.ssc
@@ -0,0 +1,73 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.IO;
+using System.Collections.Generic;
+using Microsoft.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.*;
+ {
+ program = null;
+ Dafny.Errors.count = 0;
+ List<ClassDecl!> classes = new List<ClassDecl!>();
+ foreach (string! dafnyFileName in fileNames){
+ 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, classes);
+ if (errorCount != 0)
+ {
+ return string.Format("{0} parse errors detected in {1}", Dafny.Errors.count, dafnyFileName);
+ }
+ }
+ catch (IOException e)
+ {
+ return string.Format("Error opening file \"{0}\": {1}", dafnyFileName, e.Message);
+ }
+ }
+
+ program = new Program(programName, classes);
+
+ if (Bpl.CommandLineOptions.Clo.DafnyPrintFile != null) {
+ string filename = Bpl.CommandLineOptions.Clo.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) { return null; }
+
+ Dafny.Resolver r = new Dafny.Resolver();
+ r.ResolveProgram(program);
+ if (r.ErrorCount != 0) {
+ return string.Format("{0} resolution/type errors detected in {1}", r.ErrorCount, programName);
+ }
+
+ return null; // success
+ }
+ }
+}
diff --git a/Dafny/DafnyPipeline.sscproj b/Dafny/DafnyPipeline.sscproj
new file mode 100644
index 00000000..38ff8646
--- /dev/null
+++ b/Dafny/DafnyPipeline.sscproj
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="utf-8"?>
+<VisualStudioProject>
+ <XEN ProjectType="Local"
+ SchemaVersion="1.0"
+ Name="Dafny"
+ ProjectGuid="dead83c6-1510-4af9-8f7d-c837ddbb2632"
+ >
+ <Build>
+ <Settings ApplicationIcon=""
+ AssemblyName="DafnyPipeline"
+ OutputType="Library"
+ RootNamespace="DafnyPipeline"
+ StartupObject=""
+ StandardLibraryLocation=""
+ TargetPlatform="v2"
+ TargetPlatformLocation=""
+ >
+ <Config Name="Debug"
+ AllowUnsafeBlocks="False"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="False"
+ ConfigurationOverrideFile=""
+ DefineConstants="DEBUG;TRACE"
+ DocumentationFile=""
+ DebugSymbols="True"
+ FileAlignment="4096"
+ IncrementalBuild="True"
+ Optimize="False"
+ OutputPath="bin\debug"
+ RegisterForComInterop="False"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="False"
+ WarningLevel="4"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ <Config Name="Release"
+ AllowUnsafeBlocks="false"
+ BaseAddress="285212672"
+ CheckForOverflowUnderflow="false"
+ ConfigurationOverrideFile=""
+ DefineConstants="TRACE"
+ DocumentationFile=""
+ DebugSymbols="false"
+ FileAlignment="4096"
+ IncrementalBuild="false"
+ Optimize="true"
+ OutputPath="bin\release"
+ RegisterForComInterop="false"
+ RemoveIntegerChecks="false"
+ TreatWarningsAsErrors="false"
+ WarningLevel="4"
+ CheckContractAdmissibility="True"
+ CheckPurity="False"
+ />
+ </Settings>
+ <References>
+ <Reference Name="System"
+ AssemblyName="System"
+ Private="false"
+ />
+ <Reference Name="System.Data"
+ AssemblyName="System.Data"
+ Private="false"
+ />
+ <Reference Name="System.Xml"
+ AssemblyName="System.Xml"
+ Private="false"
+ />
+ <Reference Name="Core"
+ AssemblyName="Core"
+ Private="false"
+ HintPath="../Core/bin/Debug/Core.dll"
+ />
+ </References>
+ </Build>
+ <Files>
+ <Include>
+ <File RelPath="DafnyAst.ssc"
+ SubType="Code"
+ BuildAction="Compile"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Scanner.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Parser.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="DafnyMain.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Printer.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Resolver.ssc"
+ />
+ <File BuildAction="None"
+ SubType="Content"
+ RelPath="Dafny.atg"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="Translator.ssc"
+ />
+ <File BuildAction="Compile"
+ SubType="Code"
+ RelPath="..\version.ssc"
+ />
+ </Include>
+ </Files>
+ </XEN>
+</VisualStudioProject> \ No newline at end of file
diff --git a/Dafny/Makefile b/Dafny/Makefile
new file mode 100644
index 00000000..fd2141b3
--- /dev/null
+++ b/Dafny/Makefile
@@ -0,0 +1,15 @@
+COCO = ..\..\Binaries\Coco.exe
+ASML = ..\..\Binaries\asmlc.boot.exe
+
+# "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.ssc
+
+Parser.ssc: Scanner.frame Parser.frame Dafny.atg
+ $(COCO) Dafny.atg
+ copy Parser.cs Parser.ssc
+ copy Scanner.cs Scanner.ssc
+
+clean:
+ rm -f Scanner.ssc Parser.ssc
diff --git a/Dafny/Parser.ssc b/Dafny/Parser.ssc
new file mode 100644
index 00000000..a7c30d2f
--- /dev/null
+++ b/Dafny/Parser.ssc
@@ -0,0 +1,1592 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System.Collections.Generic;
+using Microsoft.Boogie;
+using Microsoft.Contracts;
+
+namespace Microsoft.Dafny {
+
+public class Parser {
+ const int maxT = 86;
+
+ const bool T = true;
+ const bool x = false;
+ const int minErrDist = 2;
+
+ static Token/*!*/ token; // last recognized token
+ static Token/*!*/ t; // lookahead token
+ static int errDist = minErrDist;
+
+ static List<ClassDecl!>! theClasses = new List<ClassDecl!>();
+
+
+static Expression! dummyExpr = new LiteralExpr(Token.NoToken);
+static Statement! dummyStmt = new ReturnStmt(Token.NoToken);
+static Attributes.Argument! dummyAttrArg = new Attributes.Argument("dummyAttrArg");
+static Scope<string>! parseVarScope = new Scope<string>();
+
+///<summary>
+/// Parses top level declarations from "filename" and appends them to "classes".
+/// Returns the number of parsing errors encountered.
+/// Note: first initialize the Scanner.
+///</summary>
+public static int Parse (string! filename, List<ClassDecl!>! classes) /* throws System.IO.IOException */ {
+ using (System.IO.StreamReader reader = new System.IO.StreamReader(filename)) {
+ BoogiePL.Buffer.Fill(reader);
+ Scanner.Init(filename);
+ return Parse(classes);
+ }
+}
+
+///<summary>
+/// Parses top level declarations and appends them to "classes".
+/// Returns the number of parsing errors encountered.
+/// Note: first initialize the Scanner.
+///</summary>
+public static int Parse (List<ClassDecl!>! classes) {
+ List<ClassDecl!> oldClasses = theClasses;
+ theClasses = classes;
+ Parse();
+ theClasses = oldClasses;
+ return Errors.count;
+}
+
+/*--------------------------------------------------------------------------*/
+
+
+ static void Error(int n) {
+ if (errDist >= minErrDist) Errors.SynErr(n, t.filename, t.line, t.col);
+ errDist = 0;
+ }
+
+ public static void SemErr(string! msg) {
+ if (errDist >= minErrDist) Errors.SemErr(token.filename, token.line, token.col, msg);
+ errDist = 0;
+ }
+
+ public static void SemErr(Token! tok, string! msg) {
+ if (errDist >= minErrDist) Errors.SemErr(tok.filename, tok.line, tok.col, msg);
+ errDist = 0;
+ }
+
+ static void Get() {
+ for (;;) {
+ token = t;
+ t = Scanner.Scan();
+ if (t.kind<=maxT) {errDist++; return;}
+
+ t = token;
+ }
+ }
+
+ static void Expect(int n) {
+ if (t.kind==n) Get(); else Error(n);
+ }
+
+ static bool StartOf(int s) {
+ return set[s, t.kind];
+ }
+
+ static void ExpectWeak(int n, int follow) {
+ if (t.kind == n) Get();
+ else {
+ Error(n);
+ while (!StartOf(follow)) Get();
+ }
+ }
+
+ static bool WeakSeparator(int n, int syFol, int repFol) {
+ bool[] s = new bool[maxT+1];
+ if (t.kind == n) {Get(); return true;}
+ else if (StartOf(repFol)) return false;
+ else {
+ for (int i=0; i <= maxT; i++) {
+ s[i] = set[syFol, i] || set[repFol, i] || set[0, i];
+ }
+ Error(n);
+ while (!s[t.kind]) Get();
+ return StartOf(syFol);
+ }
+ }
+
+ static void Dafny() {
+ ClassDecl! c;
+ while (t.kind == 4) {
+ ClassDecl(out c);
+ theClasses.Add(c);
+ }
+ Expect(0);
+ }
+
+ static void ClassDecl(out ClassDecl! c) {
+ Token! id;
+ Attributes attrs = null;
+ List<TypeParameter!> typeArgs = new List<TypeParameter!>();
+ List<MemberDecl!> members = new List<MemberDecl!>();
+
+ Expect(4);
+ while (t.kind == 5) {
+ Attribute(ref attrs);
+ }
+ Ident(out id);
+ if (t.kind == 11) {
+ GenericParameters(typeArgs);
+ }
+ Expect(5);
+ while (StartOf(1)) {
+ ClassMemberDecl(members);
+ }
+ Expect(6);
+ c = new ClassDecl(id, id.val, typeArgs, members, attrs);
+ }
+
+ static void Attribute(ref Attributes attrs) {
+ Expect(5);
+ AttributeBody(ref attrs);
+ Expect(6);
+ }
+
+ static void Ident(out Token! x) {
+ Expect(1);
+ x = token;
+ }
+
+ static void GenericParameters(List<TypeParameter!>! typeArgs) {
+ Token! id;
+ Expect(11);
+ Ident(out id);
+ typeArgs.Add(new TypeParameter(id, id.val));
+ while (t.kind == 8) {
+ Get();
+ Ident(out id);
+ typeArgs.Add(new TypeParameter(id, id.val));
+ }
+ Expect(12);
+ }
+
+ static void ClassMemberDecl(List<MemberDecl!>! mm) {
+ Method! m;
+ Function! f;
+
+ if (t.kind == 7) {
+ FieldDecl(mm);
+ } else if (t.kind == 27) {
+ FunctionDecl(out f);
+ mm.Add(f);
+ } else if (t.kind == 14) {
+ MethodDecl(out m);
+ mm.Add(m);
+ } else if (t.kind == 13) {
+ FrameDecl();
+ } else Error(87);
+ }
+
+ static void FieldDecl(List<MemberDecl!>! mm) {
+ Attributes attrs = null;
+ Token! id; Type! ty;
+
+ Expect(7);
+ while (t.kind == 5) {
+ Attribute(ref attrs);
+ }
+ IdentType(out id, out ty);
+ mm.Add(new Field(id, id.val, ty, attrs));
+ while (t.kind == 8) {
+ Get();
+ IdentType(out id, out ty);
+ mm.Add(new Field(id, id.val, ty, attrs));
+ }
+ Expect(9);
+ }
+
+ static void FunctionDecl(out Function! f) {
+ Attributes attrs = null;
+ Token! id;
+ List<TypeParameter!> typeArgs = new List<TypeParameter!>();
+ List<Formal!> formals = new List<Formal!>();
+ Type! returnType;
+ List<Expression!> reqs = new List<Expression!>();
+ List<Expression!> reads = new List<Expression!>();
+ Expression! bb; Expression body = null;
+ bool use = false;
+
+ Expect(27);
+ while (t.kind == 5) {
+ Attribute(ref attrs);
+ }
+ if (t.kind == 28) {
+ Get();
+ use = true;
+ }
+ Ident(out id);
+ if (t.kind == 11) {
+ GenericParameters(typeArgs);
+ }
+ parseVarScope.PushMarker();
+ Formals(true, formals);
+ Expect(10);
+ Type(out returnType);
+ if (t.kind == 9) {
+ Get();
+ while (t.kind == 18 || t.kind == 29) {
+ FunctionSpec(reqs, reads);
+ }
+ } else if (t.kind == 5 || t.kind == 18 || t.kind == 29) {
+ while (t.kind == 18 || t.kind == 29) {
+ FunctionSpec(reqs, reads);
+ }
+ ExtendedExpr(out bb);
+ body = bb;
+ } else Error(88);
+ parseVarScope.PopMarker();
+ f = new Function(id, id.val, use, typeArgs, formals, returnType, reqs, reads, body, attrs);
+
+ }
+
+ static void MethodDecl(out Method! m) {
+ Token! id;
+ Attributes attrs = null;
+ List<TypeParameter!>! typeArgs = new List<TypeParameter!>();
+ List<Formal!> ins = new List<Formal!>();
+ List<Formal!> outs = new List<Formal!>();
+ List<MaybeFreeExpression!> req = new List<MaybeFreeExpression!>();
+ List<Expression!> mod = new List<Expression!>();
+ List<MaybeFreeExpression!> ens = new List<MaybeFreeExpression!>();
+ Statement! bb; Statement body = null;
+
+ Expect(14);
+ while (t.kind == 5) {
+ Attribute(ref attrs);
+ }
+ Ident(out id);
+ if (t.kind == 11) {
+ GenericParameters(typeArgs);
+ }
+ parseVarScope.PushMarker();
+ Formals(true, ins);
+ if (t.kind == 15) {
+ Get();
+ Formals(false, outs);
+ }
+ if (t.kind == 9) {
+ Get();
+ while (StartOf(2)) {
+ MethodSpec(req, mod, ens);
+ }
+ } else if (StartOf(3)) {
+ while (StartOf(2)) {
+ MethodSpec(req, mod, ens);
+ }
+ BlockStmt(out bb);
+ body = bb;
+ } else Error(89);
+ parseVarScope.PopMarker();
+ m = new Method(id, id.val, typeArgs, ins, outs, req, mod, ens, body, attrs);
+
+ }
+
+ static void FrameDecl() {
+ Token! id;
+ Attributes attrs = null;
+
+ Expect(13);
+ while (t.kind == 5) {
+ Attribute(ref attrs);
+ }
+ Ident(out id);
+ Expect(5);
+ Expect(6);
+ }
+
+ static void IdentType(out Token! id, out Type! ty) {
+ Ident(out id);
+ Expect(10);
+ Type(out ty);
+ }
+
+ static void Type(out Type! ty) {
+ ty = new BoolType(); /*keep compiler happy*/
+
+ if (t.kind == 22) {
+ Get();
+
+ } else if (t.kind == 23) {
+ Get();
+ ty = new IntType();
+ } else if (StartOf(4)) {
+ ReferenceType(out ty);
+ } else Error(90);
+ }
+
+ static void IdentTypeOptional(out BoundVar! var) {
+ Token! id; Type! ty; Type optType = null;
+
+ Ident(out id);
+ if (t.kind == 10) {
+ Get();
+ Type(out ty);
+ optType = ty;
+ }
+ var = new BoundVar(id, id.val, optType == null ? new InferredTypeProxy() : optType);
+ }
+
+ static void Formals(bool incoming, List<Formal!>! formals) {
+ Token! id; Type! ty;
+ Expect(20);
+ if (t.kind == 1) {
+ IdentType(out id, out ty);
+ formals.Add(new Formal(id, id.val, ty, incoming)); parseVarScope.Push(id.val, id.val);
+ while (t.kind == 8) {
+ Get();
+ IdentType(out id, out ty);
+ formals.Add(new Formal(id, id.val, ty, incoming)); parseVarScope.Push(id.val, id.val);
+ }
+ }
+ Expect(21);
+ }
+
+ static void MethodSpec(List<MaybeFreeExpression!>! req, List<Expression!>! mod, List<MaybeFreeExpression!>! ens) {
+ Expression! e; bool isFree = false;
+
+ if (t.kind == 16) {
+ Get();
+ if (StartOf(5)) {
+ Expression(out e);
+ mod.Add(e);
+ while (t.kind == 8) {
+ Get();
+ Expression(out e);
+ mod.Add(e);
+ }
+ }
+ Expect(9);
+ } else if (t.kind == 17 || t.kind == 18 || t.kind == 19) {
+ if (t.kind == 17) {
+ Get();
+ isFree = true;
+ }
+ if (t.kind == 18) {
+ Get();
+ Expression(out e);
+ Expect(9);
+ req.Add(new MaybeFreeExpression(e, isFree));
+ } else if (t.kind == 19) {
+ Get();
+ Expression(out e);
+ Expect(9);
+ ens.Add(new MaybeFreeExpression(e, isFree));
+ } else Error(91);
+ } else Error(92);
+ }
+
+ static void BlockStmt(out Statement! block) {
+ Token! x;
+ List<Statement!> body = new List<Statement!>();
+ Statement! s;
+
+ parseVarScope.PushMarker();
+ Expect(5);
+ x = token;
+ while (StartOf(6)) {
+ Stmt(body);
+ }
+ Expect(6);
+ block = new BlockStmt(x, body);
+ parseVarScope.PopMarker();
+ }
+
+ static void Expression(out Expression! e0) {
+ Token! x; Expression! e1;
+ ImpliesExpression(out e0);
+ while (t.kind == 48 || t.kind == 49) {
+ EquivOp();
+ x = token;
+ ImpliesExpression(out e1);
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.Iff, e0, e1);
+ }
+ }
+
+ static void ReferenceType(out Type! ty) {
+ Token! x;
+ ty = new BoolType(); /*keep compiler happy*/
+ List<Type!>! gt;
+
+ if (t.kind == 24) {
+ Get();
+ ty = new ObjectType();
+ } else if (t.kind == 1) {
+ gt = new List<Type!>();
+ Ident(out x);
+ if (t.kind == 11) {
+ GenericInstantiation(gt);
+ }
+ ty = new ClassType(x, x.val, gt);
+ } else if (t.kind == 25) {
+ gt = new List<Type!>();
+ Get();
+ GenericInstantiation(gt);
+ if (gt.Count != 1) {
+ SemErr("set type expects exactly one type argument");
+ }
+ ty = new SetType(gt[0]);
+
+ } else if (t.kind == 26) {
+ gt = new List<Type!>();
+ Get();
+ GenericInstantiation(gt);
+ if (gt.Count != 1) {
+ SemErr("seq type expects exactly one type argument");
+ }
+ ty = new SeqType(gt[0]);
+
+ } else Error(93);
+ }
+
+ static void GenericInstantiation(List<Type!>! gt) {
+ Type! ty;
+ Expect(11);
+ Type(out ty);
+ gt.Add(ty);
+ while (t.kind == 8) {
+ Get();
+ Type(out ty);
+ gt.Add(ty);
+ }
+ Expect(12);
+ }
+
+ static void FunctionSpec(List<Expression!>! reqs, List<Expression!>! reads) {
+ Expression! e;
+ if (t.kind == 18) {
+ Get();
+ Expression(out e);
+ Expect(9);
+ reqs.Add(e);
+ } else if (t.kind == 29) {
+ ReadsClause(reads);
+ } else Error(94);
+ }
+
+ static void ExtendedExpr(out Expression! e) {
+ e = dummyExpr;
+ Expect(5);
+ if (t.kind == 30) {
+ IfThenElseExpr(out e);
+ } else if (StartOf(5)) {
+ Expression(out e);
+ } else Error(95);
+ Expect(6);
+ }
+
+ static void ReadsClause(List<Expression!>! reads) {
+ Expect(29);
+ if (StartOf(5)) {
+ Expressions(reads);
+ }
+ Expect(9);
+ }
+
+ static void Expressions(List<Expression!>! args) {
+ Expression! e;
+ Expression(out e);
+ args.Add(e);
+ while (t.kind == 8) {
+ Get();
+ Expression(out e);
+ args.Add(e);
+ }
+ }
+
+ static void IfThenElseExpr(out Expression! e) {
+ Token! x; Expression! e0; Expression! e1 = dummyExpr;
+ Expect(30);
+ x = token;
+ Expect(20);
+ Expression(out e);
+ Expect(21);
+ ExtendedExpr(out e0);
+ Expect(31);
+ if (t.kind == 30) {
+ IfThenElseExpr(out e1);
+ } else if (t.kind == 5) {
+ ExtendedExpr(out e1);
+ } else Error(96);
+ e = new ITEExpr(x, e, e0, e1);
+ }
+
+ static void Stmt(List<Statement!>! ss) {
+ Statement! s;
+ while (t.kind == 5) {
+ BlockStmt(out s);
+ ss.Add(s);
+ }
+ if (StartOf(7)) {
+ OneStmt(out s);
+ ss.Add(s);
+ } else if (t.kind == 7) {
+ VarDeclStmts(ss);
+ } else Error(97);
+ }
+
+ static void OneStmt(out Statement! s) {
+ Token! x; Token! id; string label = null;
+ s = dummyStmt; /* to please the compiler */
+
+ switch (t.kind) {
+ case 46: {
+ AssertStmt(out s);
+ break;
+ }
+ case 47: {
+ AssumeStmt(out s);
+ break;
+ }
+ case 28: {
+ UseStmt(out s);
+ break;
+ }
+ case 1: case 2: case 5: case 20: case 45: case 65: case 68: case 69: case 70: case 71: case 72: case 73: case 74: case 78: case 79: {
+ AssignStmt(out s);
+ break;
+ }
+ case 37: {
+ HavocStmt(out s);
+ break;
+ }
+ case 42: {
+ CallStmt(out s);
+ break;
+ }
+ case 30: {
+ IfStmt(out s);
+ break;
+ }
+ case 38: {
+ WhileStmt(out s);
+ break;
+ }
+ case 43: {
+ ForeachStmt(out s);
+ break;
+ }
+ case 32: {
+ Get();
+ x = token;
+ Ident(out id);
+ Expect(10);
+ s = new LabelStmt(x, id.val);
+ break;
+ }
+ case 33: {
+ Get();
+ x = token;
+ if (t.kind == 1) {
+ Ident(out id);
+ label = id.val;
+ }
+ Expect(9);
+ s = new BreakStmt(x, label);
+ break;
+ }
+ case 34: {
+ Get();
+ x = token;
+ Expect(9);
+ s = new ReturnStmt(x);
+ break;
+ }
+ default: Error(98); break;
+ }
+ }
+
+ static void VarDeclStmts(List<Statement!>! ss) {
+ VarDecl! d;
+ Expect(7);
+ IdentTypeRhs(out d);
+ ss.Add(d); parseVarScope.Push(d.Name, d.Name);
+ while (t.kind == 8) {
+ Get();
+ IdentTypeRhs(out d);
+ ss.Add(d); parseVarScope.Push(d.Name, d.Name);
+ }
+ Expect(9);
+ }
+
+ static void AssertStmt(out Statement! s) {
+ Token! x; Expression! e;
+ Expect(46);
+ x = token;
+ Expression(out e);
+ Expect(9);
+ s = new AssertStmt(x, e);
+ }
+
+ static void AssumeStmt(out Statement! s) {
+ Token! x; Expression! e;
+ Expect(47);
+ x = token;
+ Expression(out e);
+ Expect(9);
+ s = new AssumeStmt(x, e);
+ }
+
+ static void UseStmt(out Statement! s) {
+ Token! x; Expression! e;
+ Expect(28);
+ x = token;
+ Expression(out e);
+ Expect(9);
+ s = new UseStmt(x, e);
+ }
+
+ static void AssignStmt(out Statement! s) {
+ Token! x;
+ Expression! lhs;
+ Expression rhs;
+ Type ty;
+ s = dummyStmt;
+
+ LhsExpr(out lhs);
+ Expect(35);
+ x = token;
+ AssignRhs(out rhs, out ty);
+ if (rhs != null) {
+ s = new AssignStmt(x, lhs, rhs);
+ } else {
+ assert ty != null;
+ s = new AssignStmt(x, lhs, ty);
+ }
+
+ Expect(9);
+ }
+
+ static void HavocStmt(out Statement! s) {
+ Token! x; Expression! lhs;
+ Expect(37);
+ x = token;
+ LhsExpr(out lhs);
+ Expect(9);
+ s = new AssignStmt(x, lhs);
+ }
+
+ static void CallStmt(out Statement! s) {
+ Token! x, id;
+ Expression! e;
+ List<IdentifierExpr!> lhs = new List<IdentifierExpr!>();
+
+ Expect(42);
+ x = token;
+ CallStmtSubExpr(out e);
+ if (t.kind == 8 || t.kind == 35) {
+ if (t.kind == 8) {
+ Get();
+ if (e is IdentifierExpr) {
+ lhs.Add((IdentifierExpr)e);
+ } else if (e is FieldSelectExpr) {
+ SemErr(e.tok, "each LHS of call statement must be a variable, not a field");
+ } else {
+ SemErr(e.tok, "each LHS of call statement must be a variable");
+ }
+
+ Ident(out id);
+ lhs.Add(new IdentifierExpr(id, id.val));
+ while (t.kind == 8) {
+ Get();
+ Ident(out id);
+ lhs.Add(new IdentifierExpr(id, id.val));
+ }
+ Expect(35);
+ CallStmtSubExpr(out e);
+ } else {
+ Get();
+ if (e is IdentifierExpr) {
+ lhs.Add((IdentifierExpr)e);
+ } else if (e is FieldSelectExpr) {
+ SemErr(e.tok, "each LHS of call statement must be a variable, not a field");
+ } else {
+ SemErr(e.tok, "each LHS of call statement must be a variable");
+ }
+
+ CallStmtSubExpr(out e);
+ }
+ }
+ Expect(9);
+ if (e is FunctionCallExpr) {
+ FunctionCallExpr fce = (FunctionCallExpr)e;
+ s = new CallStmt(x, lhs, fce.Receiver, fce.Name, fce.Args); // this actually does an ownership transfer of fce.Args
+ } else {
+ SemErr("RHS of call statement must denote a method invocation");
+ s = new CallStmt(x, lhs, dummyExpr, "dummyMethodName", new List<Expression!>());
+ }
+
+ }
+
+ static void IfStmt(out Statement! ifStmt) {
+ Token! x;
+ Expression guard;
+ Statement! thn;
+ Statement! s;
+ Statement els = null;
+
+ Expect(30);
+ x = token;
+ Guard(out guard);
+ BlockStmt(out thn);
+ if (t.kind == 31) {
+ Get();
+ if (t.kind == 30) {
+ IfStmt(out s);
+ els = s;
+ } else if (t.kind == 5) {
+ BlockStmt(out s);
+ els = s;
+ } else Error(99);
+ }
+ ifStmt = new IfStmt(x, guard, thn, els);
+ }
+
+ static void WhileStmt(out Statement! stmt) {
+ Token! x;
+ Expression guard;
+ bool isFree; Expression! e;
+ List<MaybeFreeExpression!> invariants = new List<MaybeFreeExpression!>();
+ List<Expression!> decreases = new List<Expression!>();
+ Statement! body;
+
+ Expect(38);
+ x = token;
+ Guard(out guard);
+ assume guard == null || Owner.None(guard);
+ while (t.kind == 17 || t.kind == 39 || t.kind == 40) {
+ if (t.kind == 17 || t.kind == 39) {
+ isFree = false;
+ if (t.kind == 17) {
+ Get();
+ isFree = true;
+ }
+ Expect(39);
+ Expression(out e);
+ invariants.Add(new MaybeFreeExpression(e, isFree));
+ Expect(9);
+ } else {
+ Get();
+ Expression(out e);
+ decreases.Add(e);
+ while (t.kind == 8) {
+ Get();
+ Expression(out e);
+ decreases.Add(e);
+ }
+ Expect(9);
+ }
+ }
+ BlockStmt(out body);
+ stmt = new WhileStmt(x, guard, invariants, decreases, body);
+ }
+
+ static void ForeachStmt(out Statement! s) {
+ Token! x, boundVar;
+ Type! ty;
+ Expression! collection;
+ Expression! range;
+ List<PredicateStmt!> bodyPrefix = new List<PredicateStmt!>();
+ AssignStmt bodyAssign = null;
+
+ parseVarScope.PushMarker();
+ Expect(43);
+ x = token;
+ range = new LiteralExpr(x, true);
+ ty = new InferredTypeProxy();
+
+ Expect(20);
+ Ident(out boundVar);
+ if (t.kind == 10) {
+ Get();
+ Type(out ty);
+ }
+ Expect(44);
+ Expression(out collection);
+ parseVarScope.Push(boundVar.val, boundVar.val);
+ if (t.kind == 45) {
+ Get();
+ Expression(out range);
+ }
+ Expect(21);
+ Expect(5);
+ while (t.kind == 28 || t.kind == 46 || t.kind == 47) {
+ if (t.kind == 46) {
+ AssertStmt(out s);
+ if (s is PredicateStmt) { bodyPrefix.Add((PredicateStmt)s); }
+ } else if (t.kind == 47) {
+ AssumeStmt(out s);
+ if (s is PredicateStmt) { bodyPrefix.Add((PredicateStmt)s); }
+ } else {
+ UseStmt(out s);
+ if (s is PredicateStmt) { bodyPrefix.Add((PredicateStmt)s); }
+ }
+ }
+ if (StartOf(5)) {
+ AssignStmt(out s);
+ if (s is AssignStmt) { bodyAssign = (AssignStmt)s; }
+ } else if (t.kind == 37) {
+ HavocStmt(out s);
+ if (s is AssignStmt) { bodyAssign = (AssignStmt)s; }
+ } else Error(100);
+ Expect(6);
+ s = new ForeachStmt(x, new BoundVar(boundVar, boundVar.val, ty), collection, range, bodyPrefix, bodyAssign);
+ parseVarScope.PopMarker();
+ }
+
+ static void LhsExpr(out Expression! e) {
+ Expression(out e);
+ }
+
+ static void AssignRhs(out Expression e, out Type ty) {
+ Expression! ee; Type! tt;
+ e = null; ty = null;
+
+ if (t.kind == 36) {
+ Get();
+ ReferenceType(out tt);
+ ty = tt;
+ } else if (StartOf(5)) {
+ Expression(out ee);
+ e = ee;
+ } else Error(101);
+ if (e == null && ty == null) { e = dummyExpr; }
+ }
+
+ static void IdentTypeRhs(out VarDecl! d) {
+ Token! id; Type! ty; Expression! e;
+ Expression rhs = null; Type newType = null;
+ Type optionalType = null; DeterminedAssignmentRhs optionalRhs = null;
+
+ Ident(out id);
+ if (t.kind == 10) {
+ Get();
+ Type(out ty);
+ optionalType = ty;
+ }
+ if (t.kind == 35) {
+ Get();
+ AssignRhs(out rhs, out newType);
+ }
+ if (rhs != null) {
+ assert newType == null;
+ optionalRhs = new ExprRhs(rhs);
+ } else if (newType != null) {
+ optionalRhs = new TypeRhs(newType);
+ } else if (optionalType == null) {
+ optionalType = new InferredTypeProxy();
+ }
+ d = new VarDecl(id, id.val, optionalType, optionalRhs);
+
+ }
+
+ static void Guard(out Expression e) {
+ Expression! ee; e = null;
+ Expect(20);
+ if (t.kind == 41) {
+ Get();
+ e = null;
+ } else if (StartOf(5)) {
+ Expression(out ee);
+ e = ee;
+ } else Error(102);
+ Expect(21);
+ }
+
+ static void CallStmtSubExpr(out Expression! e) {
+ e = dummyExpr;
+ if (t.kind == 1) {
+ IdentOrFuncExpression(out e);
+ } else if (t.kind == 20 || t.kind == 78 || t.kind == 79) {
+ ObjectExpression(out e);
+ SelectOrCallSuffix(ref e);
+ } else Error(103);
+ while (t.kind == 74 || t.kind == 76) {
+ SelectOrCallSuffix(ref e);
+ }
+ }
+
+ static void ImpliesExpression(out Expression! e0) {
+ Token! x; Expression! e1;
+ LogicalExpression(out e0);
+ if (t.kind == 50 || t.kind == 51) {
+ ImpliesOp();
+ x = token;
+ ImpliesExpression(out e1);
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.Imp, e0, e1);
+ }
+ }
+
+ static void EquivOp() {
+ if (t.kind == 48) {
+ Get();
+ } else if (t.kind == 49) {
+ Get();
+ } else Error(104);
+ }
+
+ static void LogicalExpression(out Expression! e0) {
+ Token! x; Expression! e1;
+ RelationalExpression(out e0);
+ if (StartOf(8)) {
+ if (t.kind == 52 || t.kind == 53) {
+ AndOp();
+ x = token;
+ RelationalExpression(out e1);
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.And, e0, e1);
+ while (t.kind == 52 || t.kind == 53) {
+ AndOp();
+ x = token;
+ RelationalExpression(out e1);
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.And, e0, e1);
+ }
+ } else {
+ OrOp();
+ x = token;
+ RelationalExpression(out e1);
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.Or, e0, e1);
+ while (t.kind == 54 || t.kind == 55) {
+ OrOp();
+ x = token;
+ RelationalExpression(out e1);
+ e0 = new BinaryExpr(x, BinaryExpr.Opcode.Or, e0, e1);
+ }
+ }
+ }
+ }
+
+ static void ImpliesOp() {
+ if (t.kind == 50) {
+ Get();
+ } else if (t.kind == 51) {
+ Get();
+ } else Error(105);
+ }
+
+ static void RelationalExpression(out Expression! e0) {
+ Token! x; Expression! e1; BinaryExpr.Opcode op;
+ Term(out e0);
+ if (StartOf(9)) {
+ RelOp(out x, out op);
+ Term(out e1);
+ e0 = new BinaryExpr(x, op, e0, e1);
+ }
+ }
+
+ static void AndOp() {
+ if (t.kind == 52) {
+ Get();
+ } else if (t.kind == 53) {
+ Get();
+ } else Error(106);
+ }
+
+ static void OrOp() {
+ if (t.kind == 54) {
+ Get();
+ } else if (t.kind == 55) {
+ Get();
+ } else Error(107);
+ }
+
+ static void Term(out Expression! e0) {
+ Token! x; Expression! e1; BinaryExpr.Opcode op;
+ Factor(out e0);
+ while (t.kind == 64 || t.kind == 65) {
+ AddOp(out x, out op);
+ Factor(out e1);
+ e0 = new BinaryExpr(x, op, e0, e1);
+ }
+ }
+
+ static void RelOp(out Token! x, out BinaryExpr.Opcode op) {
+ x = Token.NoToken; op = BinaryExpr.Opcode.Add/*(dummy)*/;
+ switch (t.kind) {
+ case 56: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Eq;
+ break;
+ }
+ case 11: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Lt;
+ break;
+ }
+ case 12: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Gt;
+ break;
+ }
+ case 57: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Le;
+ break;
+ }
+ case 58: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Ge;
+ break;
+ }
+ case 59: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Neq;
+ break;
+ }
+ case 60: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Disjoint;
+ break;
+ }
+ case 44: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.In;
+ break;
+ }
+ case 61: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Neq;
+ break;
+ }
+ case 62: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Le;
+ break;
+ }
+ case 63: {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Ge;
+ break;
+ }
+ default: Error(108); break;
+ }
+ }
+
+ static void Factor(out Expression! e0) {
+ Token! x; Expression! e1; BinaryExpr.Opcode op;
+ UnaryExpression(out e0);
+ while (t.kind == 41 || t.kind == 66 || t.kind == 67) {
+ MulOp(out x, out op);
+ UnaryExpression(out e1);
+ e0 = new BinaryExpr(x, op, e0, e1);
+ }
+ }
+
+ static void AddOp(out Token! x, out BinaryExpr.Opcode op) {
+ x = Token.NoToken; op=BinaryExpr.Opcode.Add/*(dummy)*/;
+ if (t.kind == 64) {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Add;
+ } else if (t.kind == 65) {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Sub;
+ } else Error(109);
+ }
+
+ static void UnaryExpression(out Expression! e) {
+ Token! x; e = dummyExpr;
+ if (t.kind == 65) {
+ Get();
+ x = token;
+ UnaryExpression(out e);
+ e = new BinaryExpr(x, BinaryExpr.Opcode.Sub, new LiteralExpr(x, 0), e);
+ } else if (t.kind == 68 || t.kind == 69) {
+ NegOp();
+ x = token;
+ UnaryExpression(out e);
+ e = new UnaryExpr(x, UnaryExpr.Opcode.Not, e);
+ } else if (StartOf(10)) {
+ SelectExpression(out e);
+ } else if (StartOf(11)) {
+ ConstAtomExpression(out e);
+ } else Error(110);
+ }
+
+ static void MulOp(out Token! x, out BinaryExpr.Opcode op) {
+ x = Token.NoToken; op = BinaryExpr.Opcode.Add/*(dummy)*/;
+ if (t.kind == 41) {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Mul;
+ } else if (t.kind == 66) {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Div;
+ } else if (t.kind == 67) {
+ Get();
+ x = token; op = BinaryExpr.Opcode.Mod;
+ } else Error(111);
+ }
+
+ static void NegOp() {
+ if (t.kind == 68) {
+ Get();
+ } else if (t.kind == 69) {
+ Get();
+ } else Error(112);
+ }
+
+ static void SelectExpression(out Expression! e) {
+ Token! id; e = dummyExpr;
+ if (t.kind == 1) {
+ IdentOrFuncExpression(out e);
+ } else if (t.kind == 20 || t.kind == 78 || t.kind == 79) {
+ ObjectExpression(out e);
+ } else Error(113);
+ while (t.kind == 74 || t.kind == 76) {
+ SelectOrCallSuffix(ref e);
+ }
+ }
+
+ static void ConstAtomExpression(out Expression! e) {
+ Token! x; int n; List<Expression!>! elements;
+ e = dummyExpr;
+
+ switch (t.kind) {
+ case 70: {
+ Get();
+ e = new LiteralExpr(token, false);
+ break;
+ }
+ case 71: {
+ Get();
+ e = new LiteralExpr(token, true);
+ break;
+ }
+ case 72: {
+ Get();
+ e = new LiteralExpr(token);
+ break;
+ }
+ case 2: {
+ Nat(out n);
+ e = new LiteralExpr(token, n);
+ break;
+ }
+ case 73: {
+ Get();
+ x = token;
+ Expect(20);
+ Expression(out e);
+ Expect(21);
+ e = new FreshExpr(x, e);
+ break;
+ }
+ case 45: {
+ Get();
+ x = token;
+ Expression(out e);
+ e = new UnaryExpr(x, UnaryExpr.Opcode.SeqLength, e);
+ Expect(45);
+ break;
+ }
+ case 5: {
+ Get();
+ x = token; elements = new List<Expression!>();
+ if (StartOf(5)) {
+ Expressions(elements);
+ }
+ e = new SetDisplayExpr(x, elements);
+ Expect(6);
+ break;
+ }
+ case 74: {
+ Get();
+ x = token; elements = new List<Expression!>();
+ if (StartOf(5)) {
+ Expressions(elements);
+ }
+ e = new SeqDisplayExpr(x, elements);
+ Expect(75);
+ break;
+ }
+ default: Error(114); break;
+ }
+ }
+
+ static void Nat(out int n) {
+ Expect(2);
+ try {
+ n = System.Convert.ToInt32(token.val);
+ } catch (System.FormatException) {
+ SemErr("incorrectly formatted number");
+ n = 0;
+ }
+
+ }
+
+ static void IdentOrFuncExpression(out Expression! e) {
+ Token! id; e = dummyExpr; List<Expression!>! args;
+ Ident(out id);
+ if (t.kind == 20) {
+ Get();
+ args = new List<Expression!>();
+ if (StartOf(5)) {
+ Expressions(args);
+ }
+ Expect(21);
+ e = new FunctionCallExpr(id, id.val, new ImplicitThisExpr(id), args);
+ }
+ if (e == dummyExpr) {
+ if (parseVarScope.Find(id.val) != null) {
+ e = new IdentifierExpr(id, id.val);
+ } else {
+ e = new FieldSelectExpr(id, new ImplicitThisExpr(id), id.val);
+ }
+ }
+
+ }
+
+ static void ObjectExpression(out Expression! e) {
+ Token! x; e = dummyExpr;
+ if (t.kind == 78) {
+ Get();
+ e = new ThisExpr(token);
+ } else if (t.kind == 79) {
+ Get();
+ x = token;
+ Expect(20);
+ Expression(out e);
+ Expect(21);
+ e = new OldExpr(x, e);
+ } else if (t.kind == 20) {
+ Get();
+ if (StartOf(12)) {
+ QuantifierGuts(out e);
+ } else if (StartOf(5)) {
+ Expression(out e);
+ } else Error(115);
+ Expect(21);
+ } else Error(116);
+ }
+
+ static void SelectOrCallSuffix(ref Expression! e) {
+ Token! id, x; List<Expression!>! args;
+ Expression e0 = null; Expression e1 = null; Expression! ee; bool anyDots = false;
+ bool func = false;
+
+ if (t.kind == 76) {
+ Get();
+ Ident(out id);
+ if (t.kind == 20) {
+ Get();
+ args = new List<Expression!>(); func = true;
+ if (StartOf(5)) {
+ Expressions(args);
+ }
+ Expect(21);
+ e = new FunctionCallExpr(id, id.val, e, args);
+ }
+ if (!func) { e = new FieldSelectExpr(id, e, id.val); }
+ } else if (t.kind == 74) {
+ Get();
+ x = token;
+ if (StartOf(5)) {
+ Expression(out ee);
+ e0 = ee;
+ if (t.kind == 77) {
+ Get();
+ anyDots = true;
+ if (StartOf(5)) {
+ Expression(out ee);
+ e1 = ee;
+ }
+ }
+ } else if (t.kind == 77) {
+ Get();
+ Expression(out ee);
+ anyDots = true; e1 = ee;
+ } else Error(117);
+ if (!anyDots) {
+ assert e1 == null;
+ e = new SeqSelectExpr(x, true, e, e0, null);
+ } else {
+ assert e0 != null || e1 != null;
+ e = new SeqSelectExpr(x, false, e, e0, e1);
+ }
+
+ Expect(75);
+ } else Error(118);
+ }
+
+ static void QuantifierGuts(out Expression! q) {
+ Token! x = Token.NoToken;
+ bool univ = false;
+ BoundVar! bv;
+ List<BoundVar!> bvars = new List<BoundVar!>();
+ Token! tok; Expr! e; ExprSeq! es;
+ Attributes attrs = null;
+ Triggers trigs = null;
+ Expression! body;
+
+ if (t.kind == 80 || t.kind == 81) {
+ Forall();
+ x = token; univ = true;
+ } else if (t.kind == 82 || t.kind == 83) {
+ Exists();
+ x = token;
+ } else Error(119);
+ parseVarScope.PushMarker();
+ IdentTypeOptional(out bv);
+ bvars.Add(bv); parseVarScope.Push(bv.Name, bv.Name);
+ while (t.kind == 8) {
+ Get();
+ IdentTypeOptional(out bv);
+ bvars.Add(bv); parseVarScope.Push(bv.Name, bv.Name);
+ }
+ QSep();
+ while (t.kind == 5) {
+ AttributeOrTrigger(ref attrs, ref trigs);
+ }
+ Expression(out body);
+ if (univ) {
+ q = new ForallExpr(x, bvars, body, trigs, attrs);
+ } else {
+ q = new ExistsExpr(x, bvars, body, trigs, attrs);
+ }
+ parseVarScope.PopMarker();
+
+ }
+
+ static void Forall() {
+ if (t.kind == 80) {
+ Get();
+ } else if (t.kind == 81) {
+ Get();
+ } else Error(120);
+ }
+
+ static void Exists() {
+ if (t.kind == 82) {
+ Get();
+ } else if (t.kind == 83) {
+ Get();
+ } else Error(121);
+ }
+
+ static void QSep() {
+ if (t.kind == 84) {
+ Get();
+ } else if (t.kind == 85) {
+ Get();
+ } else Error(122);
+ }
+
+ static void AttributeOrTrigger(ref Attributes attrs, ref Triggers trigs) {
+ List<Expression!> es = new List<Expression!>();
+
+ Expect(5);
+ if (t.kind == 10) {
+ AttributeBody(ref attrs);
+ } else if (StartOf(5)) {
+ es = new List<Expression!>();
+ Expressions(es);
+ trigs = new Triggers(es, trigs);
+ } else Error(123);
+ Expect(6);
+ }
+
+ static void AttributeBody(ref Attributes attrs) {
+ string aName;
+ List<Attributes.Argument!> aArgs = new List<Attributes.Argument!>();
+ Attributes.Argument! aArg;
+
+ Expect(10);
+ Expect(1);
+ aName = token.val;
+ if (StartOf(13)) {
+ AttributeArg(out aArg);
+ aArgs.Add(aArg);
+ while (t.kind == 8) {
+ Get();
+ AttributeArg(out aArg);
+ aArgs.Add(aArg);
+ }
+ }
+ attrs = new Attributes(aName, aArgs, attrs);
+ }
+
+ static void AttributeArg(out Attributes.Argument! arg) {
+ Expression! e; arg = dummyAttrArg;
+ if (t.kind == 3) {
+ Get();
+ arg = new Attributes.Argument(token.val.Substring(1, token.val.Length-2));
+ } else if (StartOf(5)) {
+ Expression(out e);
+ arg = new Attributes.Argument(e);
+ } else Error(124);
+ }
+
+
+
+ public static void Parse() {
+ Errors.SynErr = new ErrorProc(SynErr);
+ t = new Token();
+ Get();
+ Dafny();
+
+ }
+
+ [Microsoft.Contracts.Verify(false)]
+ static void SynErr(int n, string filename, int line, int col) {
+ Errors.count++;
+ System.Console.Write("{0}({1},{2}): syntax error: ", filename, line, col);
+ 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 = "string expected"; break;
+ case 4: s = "class expected"; break;
+ case 5: s = "{ expected"; break;
+ case 6: s = "} expected"; break;
+ case 7: s = "var expected"; break;
+ case 8: s = ", expected"; break;
+ case 9: s = "; expected"; break;
+ case 10: s = ": expected"; break;
+ case 11: s = "< expected"; break;
+ case 12: s = "> expected"; break;
+ case 13: s = "frame expected"; break;
+ case 14: s = "method expected"; break;
+ case 15: s = "returns expected"; break;
+ case 16: s = "modifies expected"; break;
+ case 17: s = "free expected"; break;
+ case 18: s = "requires expected"; break;
+ case 19: s = "ensures expected"; break;
+ case 20: s = "( expected"; break;
+ case 21: s = ") expected"; break;
+ case 22: s = "bool expected"; break;
+ case 23: s = "int expected"; break;
+ case 24: s = "object expected"; break;
+ case 25: s = "set expected"; break;
+ case 26: s = "seq expected"; break;
+ case 27: s = "function expected"; break;
+ case 28: s = "use expected"; break;
+ case 29: s = "reads expected"; break;
+ case 30: s = "if expected"; break;
+ case 31: s = "else expected"; break;
+ case 32: s = "label expected"; break;
+ case 33: s = "break expected"; break;
+ case 34: s = "return expected"; break;
+ case 35: s = ":= expected"; break;
+ case 36: s = "new expected"; break;
+ case 37: s = "havoc expected"; break;
+ case 38: s = "while expected"; break;
+ case 39: s = "invariant expected"; break;
+ case 40: s = "decreases expected"; break;
+ case 41: s = "* expected"; break;
+ case 42: s = "call expected"; break;
+ case 43: s = "foreach expected"; break;
+ case 44: s = "in expected"; break;
+ case 45: s = "| expected"; break;
+ case 46: s = "assert expected"; break;
+ case 47: s = "assume expected"; break;
+ case 48: s = "<==> expected"; break;
+ case 49: s = "\\u21d4 expected"; break;
+ case 50: s = "==> expected"; break;
+ case 51: s = "\\u21d2 expected"; break;
+ case 52: s = "&& expected"; break;
+ case 53: s = "\\u2227 expected"; break;
+ case 54: s = "|| expected"; break;
+ case 55: s = "\\u2228 expected"; break;
+ case 56: s = "== expected"; break;
+ case 57: s = "<= expected"; break;
+ case 58: s = ">= expected"; break;
+ case 59: s = "!= expected"; break;
+ case 60: s = "!! expected"; break;
+ case 61: s = "\\u2260 expected"; break;
+ case 62: s = "\\u2264 expected"; break;
+ case 63: s = "\\u2265 expected"; break;
+ case 64: s = "+ expected"; break;
+ case 65: s = "- expected"; break;
+ case 66: s = "/ expected"; break;
+ case 67: s = "% expected"; break;
+ case 68: s = "! expected"; break;
+ case 69: s = "\\u00ac expected"; break;
+ case 70: s = "false expected"; break;
+ case 71: s = "true expected"; break;
+ case 72: s = "null expected"; break;
+ case 73: s = "fresh expected"; break;
+ case 74: s = "[ expected"; break;
+ case 75: s = "] expected"; break;
+ case 76: s = ". expected"; break;
+ case 77: s = ".. expected"; break;
+ case 78: s = "this expected"; break;
+ case 79: s = "old expected"; break;
+ case 80: s = "forall expected"; break;
+ case 81: s = "\\u2200 expected"; break;
+ case 82: s = "exists expected"; break;
+ case 83: s = "\\u2203 expected"; break;
+ case 84: s = ":: expected"; break;
+ case 85: s = "\\u2022 expected"; break;
+ case 86: s = "??? expected"; break;
+ case 87: s = "invalid ClassMemberDecl"; break;
+ case 88: s = "invalid FunctionDecl"; break;
+ case 89: s = "invalid MethodDecl"; break;
+ case 90: s = "invalid Type"; break;
+ case 91: s = "invalid MethodSpec"; break;
+ case 92: s = "invalid MethodSpec"; break;
+ case 93: s = "invalid ReferenceType"; break;
+ case 94: s = "invalid FunctionSpec"; break;
+ case 95: s = "invalid ExtendedExpr"; break;
+ case 96: s = "invalid IfThenElseExpr"; break;
+ case 97: s = "invalid Stmt"; break;
+ case 98: s = "invalid OneStmt"; break;
+ case 99: s = "invalid IfStmt"; break;
+ case 100: s = "invalid ForeachStmt"; break;
+ case 101: s = "invalid AssignRhs"; break;
+ case 102: s = "invalid Guard"; break;
+ case 103: s = "invalid CallStmtSubExpr"; break;
+ case 104: s = "invalid EquivOp"; break;
+ case 105: s = "invalid ImpliesOp"; break;
+ case 106: s = "invalid AndOp"; break;
+ case 107: s = "invalid OrOp"; break;
+ case 108: s = "invalid RelOp"; break;
+ case 109: s = "invalid AddOp"; break;
+ case 110: s = "invalid UnaryExpression"; break;
+ case 111: s = "invalid MulOp"; break;
+ case 112: s = "invalid NegOp"; break;
+ case 113: s = "invalid SelectExpression"; break;
+ case 114: s = "invalid ConstAtomExpression"; break;
+ case 115: s = "invalid ObjectExpression"; break;
+ case 116: s = "invalid ObjectExpression"; break;
+ case 117: s = "invalid SelectOrCallSuffix"; break;
+ case 118: s = "invalid SelectOrCallSuffix"; break;
+ case 119: s = "invalid QuantifierGuts"; break;
+ case 120: s = "invalid Forall"; break;
+ case 121: s = "invalid Exists"; break;
+ case 122: s = "invalid QSep"; break;
+ case 123: s = "invalid AttributeOrTrigger"; break;
+ case 124: s = "invalid AttributeArg"; break;
+
+ default: s = "error " + n; break;
+ }
+ System.Console.WriteLine(s);
+ }
+
+ static bool[,]! set = {
+ {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,T, x,x,x,x, x,T,T,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, 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,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,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,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,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, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, T,T,T,T, T,T,T,x, x,x,T,T, x,x,x,x, x,x,x,x},
+ {x,T,T,x, x,T,x,T, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, T,x,T,x, T,T,T,x, x,T,T,x, x,x,T,T, x,T,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, T,T,T,T, T,T,T,x, x,x,T,T, 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, T,x,x,x, x,x,x,x, T,x,T,x, T,T,T,x, x,T,T,x, x,x,T,T, x,T,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, T,T,T,T, T,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,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, 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,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, T,T,T,T, T,T,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x},
+ {x,T,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,T,T, x,x,x,x, x,x,x,x},
+ {x,x,T,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, 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,T,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x},
+ {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,T,T,T, x,x,x,x},
+ {x,T,T,T, x,T,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,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, T,T,T,T, T,T,T,x, x,x,T,T, x,x,x,x, x,x,x,x}
+
+ };
+
+ [Microsoft.Contracts.Verify(false)]
+ static Parser() {}
+} // end Parser
+
+} // end namespace
diff --git a/Dafny/Printer.ssc b/Dafny/Printer.ssc
new file mode 100644
index 00000000..4a861553
--- /dev/null
+++ b/Dafny/Printer.ssc
@@ -0,0 +1,663 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.IO;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+using Bpl = Microsoft.Boogie;
+
+namespace Microsoft.Dafny {
+ class Printer {
+ TextWriter! wr;
+ public Printer(TextWriter! wr) {
+ this.wr = wr;
+ }
+
+ public void PrintProgram(Program! prog) {
+ 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);
+ foreach (ClassDecl c in prog.Classes) {
+ wr.WriteLine();
+ PrintClass(c);
+ }
+ }
+
+ public void PrintClass(ClassDecl! c) {
+ PrintClassMethodHelper("class", c.Attributes, "", c.Name, c.TypeArgs);
+ if (c.Members.Count == 0) {
+ wr.WriteLine(" { }");
+ } else {
+ int state = 0; // 0 - no members yet; 1 - previous member was a field; 2 - previous member was non-field
+ wr.WriteLine(" {");
+ foreach (MemberDecl m in c.Members) {
+ if (m is Method) {
+ if (state != 0) { wr.WriteLine(); }
+ PrintMethod((Method)m);
+ state = 2;
+ } else if (m is Field) {
+ if (state == 2) { wr.WriteLine(); }
+ PrintField((Field)m);
+ state = 1;
+ } else if (m is Function) {
+ if (state != 0) { wr.WriteLine(); }
+ PrintFunction((Function)m);
+ state = 2;
+ } else {
+ assert false; // unexpected member
+ }
+ }
+ wr.WriteLine("}");
+ }
+ }
+
+ void PrintClassMethodHelper(string! kind, Attributes attrs, string! modifiers, string! name, List<TypeParameter!>! typeArgs) {
+ wr.Write("{0} ", kind);
+ PrintAttributes(attrs);
+ wr.Write(modifiers);
+ wr.Write(name);
+ if (typeArgs.Count != 0) {
+ wr.Write("<");
+ string sep = "";
+ foreach (TypeParameter tp in typeArgs) {
+ wr.Write("{0}{1}", sep, tp.Name);
+ sep = ", ";
+ }
+ wr.Write(">");
+ }
+ }
+
+ public void PrintAttributes(Attributes a) {
+ if (a != null) {
+ PrintAttributes(a.Prev);
+
+ wr.Write("{ :{0}", a.Name);
+ string prefix = " ";
+ foreach (Attributes.Argument arg in a.Args) {
+ wr.Write(prefix);
+ prefix = ", ";
+ if (arg.S != null) {
+ wr.Write("\"{0}\"", arg.S);
+ } else {
+ assert arg.E != null;
+ PrintExpression(arg.E);
+ }
+ }
+ wr.Write(" } ");
+ }
+ }
+
+ public void PrintField(Field! field) {
+ Indent(IndentAmount);
+ wr.Write("var ");
+ PrintAttributes(field.Attributes);
+ wr.Write("{0}: ", field.Name);
+ PrintType(field.Type);
+ wr.WriteLine(";");
+ }
+
+ public void PrintFunction(Function! f) {
+ Indent(IndentAmount);
+ PrintClassMethodHelper("function", f.Attributes, f.Use ? "use " : "", f.Name, f.TypeArgs);
+ PrintFormals(f.Formals);
+ wr.Write(": ");
+ PrintType(f.ResultType);
+ if (f.Body == null) {
+ wr.WriteLine(";");
+ }
+ foreach (Expression r in f.Req) {
+ Indent(2 * IndentAmount);
+ wr.Write("requires ");
+ PrintExpression(r);
+ wr.WriteLine(";");
+ }
+ foreach (Expression r in f.Reads) {
+ Indent(2 * IndentAmount);
+ wr.Write("reads ");
+ PrintExpression(r);
+ wr.WriteLine(";");
+ }
+ if (f.Body != null) {
+ Indent(IndentAmount);
+ PrintExtendedExpr(f.Body, IndentAmount);
+ wr.WriteLine();
+ }
+ }
+
+ // ----------------------------- PrintMethod -----------------------------
+
+ const int IndentAmount = 2;
+ const string! BunchaSpaces = " ";
+ void Indent(int amount)
+ requires 0 <= amount;
+ {
+ while (0 < amount) {
+ wr.Write(BunchaSpaces.Substring(0, amount));
+ amount -= BunchaSpaces.Length;
+ }
+ }
+
+ public void PrintMethod(Method! method) {
+ Indent(IndentAmount);
+ PrintClassMethodHelper("method", method.Attributes, "", method.Name, method.TypeArgs);
+ PrintFormals(method.Ins);
+ if (method.Outs.Count != 0) {
+ if (method.Ins.Count + method.Outs.Count <= 3) {
+ wr.Write(" returns ");
+ } else {
+ wr.WriteLine();
+ Indent(3 * IndentAmount);
+ wr.Write("returns ");
+ }
+ PrintFormals(method.Outs);
+ }
+ wr.WriteLine(method.Body == null ? ";" : ":");
+
+ PrintSpec("requires", method.Req, 2 * IndentAmount);
+ PrintSpec("modifies", method.Mod, 2 * IndentAmount);
+ PrintSpec("ensures", method.Ens, 2 * IndentAmount);
+
+ if (method.Body != null) {
+ Indent(IndentAmount);
+ PrintStatement(method.Body, IndentAmount);
+ wr.WriteLine();
+ }
+ }
+
+ void PrintFormals(List<Formal!>! ff) {
+ wr.Write("(");
+ string sep = "";
+ foreach (Formal f in ff) {
+ wr.Write(sep);
+ sep = ", ";
+ PrintFormal(f);
+ }
+ wr.Write(")");
+ }
+
+ void PrintFormal(Formal! f) {
+ if (!f.Name.StartsWith("#")) {
+ wr.Write("{0}: ", f.Name);
+ }
+ PrintType(f.Type);
+ }
+
+ void PrintSpec(string! kind, List<Expression!>! ee, int indent) {
+ foreach (Expression e in ee) {
+ Indent(indent);
+ wr.Write("{0} ", kind);
+ PrintExpression(e);
+ wr.WriteLine(";");
+ }
+ }
+
+ void PrintSpec(string! kind, List<MaybeFreeExpression!>! ee, int indent) {
+ foreach (MaybeFreeExpression e in ee) {
+ Indent(indent);
+ wr.Write("{0}{1} ", e.IsFree ? "free " : "", kind);
+ PrintExpression(e.E);
+ wr.WriteLine(";");
+ }
+ }
+
+ // ----------------------------- PrintType -----------------------------
+
+ public void PrintType(Type! ty) {
+ wr.Write(ty.ToString());
+ }
+
+ // ----------------------------- 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) {
+ if (stmt is AssertStmt) {
+ wr.Write("assert ");
+ PrintExpression(((AssertStmt)stmt).Expr);
+ wr.Write(";");
+
+ } else if (stmt is AssumeStmt) {
+ wr.Write("assume ");
+ PrintExpression(((AssertStmt)stmt).Expr);
+ wr.Write(";");
+
+ } else if (stmt is UseStmt) {
+ wr.Write("use ");
+ PrintExpression(((AssertStmt)stmt).Expr);
+ wr.Write(";");
+
+ } else if (stmt is LabelStmt) {
+ wr.Write("label {0}:", ((LabelStmt)stmt).Label);
+
+ } else if (stmt is BreakStmt) {
+ BreakStmt s = (BreakStmt)stmt;
+ if (s.TargetLabel == null) {
+ wr.Write("break;");
+ } else {
+ wr.Write("break {0};", s.TargetLabel);
+ }
+
+ } else if (stmt is ReturnStmt) {
+ wr.Write("return;");
+
+ } else if (stmt is AssignStmt) {
+ AssignStmt s = (AssignStmt)stmt;
+ if (s.Rhs is HavocRhs) {
+ wr.Write("havoc ");
+ PrintExpression(s.Lhs);
+ } else {
+ PrintExpression(s.Lhs);
+ wr.Write(" := ");
+ PrintDeterminedRhs((DeterminedAssignmentRhs)s.Rhs);
+ }
+ wr.Write(";");
+
+ } else if (stmt is VarDecl) {
+ VarDecl s = (VarDecl)stmt;
+ wr.Write("var {0}", s.Name);
+ if (s.OptionalType != null) {
+ wr.Write(": ");
+ PrintType(s.OptionalType);
+ }
+ if (s.Rhs != null) {
+ wr.Write(" := ");
+ PrintDeterminedRhs(s.Rhs);
+ }
+ wr.Write(";");
+
+ } else if (stmt is CallStmt) {
+ CallStmt s = (CallStmt)stmt;
+ wr.Write("call ");
+ 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 ThisExpr)) {
+ PrintExpr(s.Receiver, 0x70, 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;
+ while (true) {
+ 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;
+ }
+ }
+
+ } else if (stmt is WhileStmt) {
+ WhileStmt s = (WhileStmt)stmt;
+ wr.Write("while (");
+ PrintGuard(s.Guard);
+ wr.WriteLine(")");
+
+ int ind = indent + IndentAmount;
+ PrintSpec("invariant", s.Invariants, ind);
+ if (s.Decreases.Count != 0) {
+ Indent(indent);
+ string sep = "decreases ";
+ foreach (Expression e in s.Decreases) {
+ wr.Write(sep);
+ sep = ", ";
+ PrintExpression(e);
+ }
+ wr.WriteLine(";");
+ }
+ Indent(indent);
+ PrintStatement(s.Body, indent);
+
+ } else if (stmt is ForeachStmt) {
+ ForeachStmt s = (ForeachStmt)stmt;
+ wr.Write("foreach ({0} in ", s.BoundVar);
+ PrintExpression(s.Collection);
+ if (!LiteralExpr.IsTrue(s.Range)) {
+ wr.Write(" | ");
+ PrintExpression(s.Range);
+ }
+ wr.WriteLine(") {");
+ int ind = indent + IndentAmount;
+ foreach (PredicateStmt t in s.BodyPrefix) {
+ Indent(ind);
+ PrintStatement(t, ind);
+ wr.WriteLine();
+ }
+ if (s.BodyAssign != null) {
+ Indent(ind);
+ PrintStatement(s.BodyAssign, ind);
+ wr.WriteLine();
+ }
+ wr.Write("}");
+
+ } else {
+ assert false; // unexpected statement
+ }
+ }
+
+ void PrintDeterminedRhs(DeterminedAssignmentRhs! rhs) {
+ if (rhs is ExprRhs) {
+ PrintExpression(((ExprRhs)rhs).Expr);
+ } else if (rhs is TypeRhs) {
+ wr.Write("new ");
+ PrintType(((TypeRhs)rhs).Type);
+ } else {
+ assert false; // unexpected RHS
+ }
+ }
+
+ void PrintGuard(Expression guard) {
+ if (guard == null) {
+ wr.Write("*");
+ } else {
+ PrintExpression(guard);
+ }
+ }
+
+ // ----------------------------- PrintExpression -----------------------------
+
+ public void PrintExtendedExpr(Expression! expr, int indent) {
+ wr.WriteLine("{");
+ int ind = indent + IndentAmount;
+ Indent(ind);
+ if (expr is ITEExpr) {
+ Expression els = expr; // bogus assignment, just to please the compiler
+ for (ITEExpr ite = (ITEExpr)expr; ite != null; ite = els as ITEExpr) {
+ wr.Write("if (");
+ PrintExpression(ite.Test);
+ wr.Write(") ");
+ PrintExtendedExpr(ite.Thn, ind);
+ wr.Write(" else ");
+ els = ite.Els;
+ }
+ PrintExtendedExpr(els, ind);
+ } else {
+ PrintExpression(expr, ind);
+ wr.WriteLine();
+ }
+ Indent(indent);
+ wr.Write("}");
+ }
+
+ public void PrintExpression(Expression! expr) {
+ PrintExpr(expr, 0, false, -1);
+ }
+
+ /// <summary>
+ /// An indent of -1 means print the entire expression on one line.
+ /// </summary>
+ public void PrintExpression(Expression! expr, int indent) {
+ PrintExpr(expr, 0, false, indent);
+ }
+
+ /// <summary>
+ /// An indent of -1 means print the entire expression on one line.
+ /// </summary>
+ void PrintExpr(Expression! expr, int contextBindingStrength, bool fragileContext, int indent)
+ requires -1 <= indent;
+ {
+ 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((int)e.Value);
+ }
+
+ } else if (expr is ThisExpr) {
+ wr.Write("this");
+
+ } else if (expr is IdentifierExpr) {
+ wr.Write(((IdentifierExpr)expr).Name);
+
+ } else if (expr is DisplayExpression) {
+ DisplayExpression e = (DisplayExpression)expr;
+ wr.Write(e is SetDisplayExpr ? "{" : "[");
+ PrintExpressionList(e.Elements);
+ wr.Write(e is SetDisplayExpr ? "}" : "]");
+
+ } 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, -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, 00, false, indent); // BOGUS: fix me
+ wr.Write("[");
+ if (e.SelectOne) {
+ 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 FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)expr;
+ // determine if parens are needed
+ int opBindingStrength = 0x70;
+ bool parensNeeded = !(e.Receiver is ThisExpr) &&
+ opBindingStrength < contextBindingStrength ||
+ (fragileContext && opBindingStrength == contextBindingStrength);
+
+ if (parensNeeded) { wr.Write("("); }
+ if (!(e.Receiver is ThisExpr)) {
+ PrintExpr(e.Receiver, opBindingStrength, false, -1);
+ wr.Write(".");
+ }
+ wr.Write(e.Name);
+ 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 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.Not:
+ op = "!"; opBindingStrength = 0x60; break;
+ default:
+ assert false; // unexpected unary opcode
+ }
+ bool parensNeeded = opBindingStrength < contextBindingStrength ||
+ (fragileContext && opBindingStrength == contextBindingStrength);
+
+ if (parensNeeded) { wr.Write("("); }
+ wr.Write(op);
+ PrintExpr(e.E, opBindingStrength, false, -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:
+ 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 = 0x00; break;
+ default:
+ assert false; // unexpected binary operator
+ }
+ int opBS = opBindingStrength & 0xF0;
+ int ctxtBS = contextBindingStrength & 0xF0;
+ 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, indent);
+ wr.WriteLine(" {0}", op);
+ Indent(indent);
+ PrintExpr(e.E1, opBindingStrength, fragileRightContext, indent);
+ } else if (0 <= indent && e.Op == BinaryExpr.Opcode.Imp) {
+ PrintExpr(e.E0, opBindingStrength, fragileLeftContext, indent);
+ wr.WriteLine(" {0}", op);
+ int ind = indent + IndentAmount;
+ Indent(ind);
+ PrintExpr(e.E1, opBindingStrength, fragileRightContext, ind);
+ } else {
+ PrintExpr(e.E0, opBindingStrength, fragileLeftContext, -1);
+ wr.Write(" {0} ", op);
+ PrintExpr(e.E1, opBindingStrength, fragileRightContext, -1);
+ }
+ if (parensNeeded) { wr.Write(")"); }
+
+ } else if (expr is QuantifierExpr) {
+ QuantifierExpr e = (QuantifierExpr)expr;
+ wr.Write(e is ForallExpr ? "(forall " : "(exists ");
+ string sep = "";
+ foreach (BoundVar bv in e.BoundVars) {
+ wr.Write("{0}{1}: ", sep, bv.Name);
+ sep = ", ";
+ PrintType(bv.Type);
+ }
+ wr.Write(" :: ");
+ PrintAttributes(e.Attributes);
+ PrintTriggers(e.Trigs);
+ if (0 <= indent) {
+ int ind = indent + IndentAmount;
+ wr.WriteLine();
+ Indent(ind);
+ PrintExpression(e.Body, ind);
+ } else {
+ PrintExpression(e.Body);
+ }
+ wr.Write(")");
+
+ } else if (expr is ITEExpr) {
+ assert false; // ITE is an extended expression and should be printed only using PrintExtendedExpr
+ } else {
+ assert false; // unexpected expression
+ }
+ }
+
+ void PrintTriggers(Triggers trigs) {
+ if (trigs != null) {
+ PrintTriggers(trigs.Prev);
+
+ wr.Write("{ ");
+ PrintExpressionList(trigs.Terms);
+ wr.Write(" } ");
+ }
+ }
+
+ void PrintExpressionList(List<Expression!>! exprs) {
+ string sep = "";
+ foreach (Expression e in exprs) {
+ wr.Write(sep);
+ sep = ", ";
+ PrintExpression(e);
+ }
+ }
+ }
+}
diff --git a/Dafny/Resolver.ssc b/Dafny/Resolver.ssc
new file mode 100644
index 00000000..66cdabe1
--- /dev/null
+++ b/Dafny/Resolver.ssc
@@ -0,0 +1,1419 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+
+namespace Microsoft.Dafny {
+ public class Resolver {
+ public int ErrorCount = 0;
+ void Error(Token! tok, string! msg, params object[] args) {
+ 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++;
+ }
+ void Error(Declaration! d, string! msg, params object[] args) {
+ Error(d.tok, msg, args);
+ }
+ void Error(Statement! s, string! msg, params object[] args) {
+ Error(s.Tok, msg, args);
+ }
+ void Error(NonglobalVariable! v, string! msg, params object[] args) {
+ Error(v.tok, msg, args);
+ }
+ void Error(Expression! e, string! msg, params object[] args) {
+ Error(e.tok, msg, args);
+ }
+
+ readonly Dictionary<string!,ClassDecl!>! classes = new Dictionary<string!,ClassDecl!>();
+ readonly Dictionary<ClassDecl!,Dictionary<string!,MemberDecl!>!>! classMembers = new Dictionary<ClassDecl!,Dictionary<string!,MemberDecl!>!>();
+
+ public void ResolveProgram(Program! prog) {
+ foreach (ClassDecl cl in prog.Classes) {
+ // register the class name
+ if (classes.ContainsKey(cl.Name)) {
+ Error(cl, "Duplicate class name: {0}", cl.Name);
+ } else {
+ classes.Add(cl.Name, cl);
+ }
+
+ // register the names of the class members
+ Dictionary<string!,MemberDecl!> members = new Dictionary<string!,MemberDecl!>();
+ classMembers.Add(cl, members);
+
+ 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);
+ }
+ }
+ }
+
+ // resolve each class
+ foreach (ClassDecl cl in prog.Classes) {
+ allTypeParameters.PushMarker();
+ ResolveTypeParameters(cl.TypeArgs, true, cl);
+ ResolveClassMemberTypes(cl);
+ allTypeParameters.PopMarker();
+ }
+ foreach (ClassDecl cl in prog.Classes) {
+ allTypeParameters.PushMarker();
+ ResolveTypeParameters(cl.TypeArgs, false, cl);
+ ResolveClassMemberBodies(cl);
+ allTypeParameters.PopMarker();
+ }
+ }
+
+ ClassDecl currentClass;
+ readonly Scope<TypeParameter>! allTypeParameters = new Scope<TypeParameter>();
+ readonly Scope<IVariable>! scope = new Scope<IVariable>();
+ readonly Scope<Statement>! labeledStatements = new Scope<Statement>();
+
+ /// <summary>
+ /// Assumes type parameters have already been pushed
+ /// </summary>
+ void ResolveClassMemberTypes(ClassDecl! cl)
+ requires currentClass == null;
+ ensures currentClass == null;
+ {
+ currentClass = cl;
+ foreach (MemberDecl member in cl.Members) {
+ member.EnclosingClass = cl;
+ if (member is Field) {
+ ResolveType(((Field)member).Type);
+
+ } 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 {
+ assert false; // 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)
+ requires currentClass == null;
+ ensures currentClass == null;
+ {
+ ResolveAttributes(cl.Attributes, false);
+ currentClass = cl;
+ foreach (MemberDecl member in cl.Members) {
+ ResolveAttributes(member.Attributes, false);
+ if (member is Field) {
+ // 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 {
+ assert false; // unexpected member type
+ }
+ }
+ currentClass = null;
+ }
+
+ void ResolveAttributes(Attributes attrs, bool twoState) {
+ // order does not matter for resolution, so resolve them in reverse order
+ for (; attrs != null; attrs = attrs.Prev) {
+ foreach (Attributes.Argument aa in attrs.Args) {
+ if (aa.E != null) {
+ ResolveExpression(aa.E, twoState);
+ }
+ }
+ }
+ }
+
+ void ResolveTriggers(Triggers trigs, bool twoState) {
+ // order does not matter for resolution, so resolve them in reverse order
+ for (; trigs != null; trigs = trigs.Prev) {
+ foreach (Expression e in trigs.Terms) {
+ ResolveExpression(e, twoState);
+ }
+ }
+ }
+
+ void ResolveTypeParameters(List<TypeParameter!>! tparams, bool emitErrors, TypeParameter.ParentType! parent) {
+ // push non-duplicated type parameter names
+ foreach (TypeParameter tp in tparams) {
+ if (emitErrors) {
+ // we're seeing this TypeParameter for the first time
+ tp.Parent = parent;
+ }
+ 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) {
+ scope.PushMarker();
+ foreach (Formal p in f.Formals) {
+ if (!scope.Push(p.Name, p)) {
+ Error(p, "Duplicate parameter name: {0}", p.Name);
+ }
+ ResolveType(p.Type);
+ }
+ ResolveType(f.ResultType);
+ scope.PopMarker();
+ }
+
+ /// <summary>
+ /// Assumes type parameters have already been pushed
+ /// </summary>
+ void ResolveFunction(Function! f) {
+ scope.PushMarker();
+ foreach (Formal p in f.Formals) {
+ scope.Push(p.Name, p);
+ }
+ foreach (Expression r in f.Req) {
+ ResolveExpression(r, false);
+ 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 (Expression r in f.Reads) {
+ ResolveExpression(r, false);
+ Type t = r.Type;
+ assert t != null; // follows from postcondition of ResolveExpression
+ if (t is CollectionType) {
+ t = ((CollectionType)t).Arg;
+ }
+ if (t is ObjectType) {
+ // fine
+ } else if (ClassType.DenotesClass(t) != null) {
+ // fine
+ } else {
+ Error(r, "a reads-clause expression must denote an object or a collection of objects (instead got {0})", r.Type);
+ }
+ }
+ if (f.Body != null) {
+ ResolveExpression(f.Body, false);
+ 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);
+ }
+ }
+ scope.PopMarker();
+ }
+
+ /// <summary>
+ /// Assumes type parameters have already been pushed
+ /// </summary>
+ void ResolveMethodSignature(Method! m) {
+ scope.PushMarker();
+ // 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.Type);
+ }
+ // 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.Type);
+ }
+ scope.PopMarker();
+ }
+
+ /// <summary>
+ /// Assumes type parameters have already been pushed
+ /// </summary>
+ void ResolveMethod(Method! m) {
+ // Add in-parameters to the scope, but don't care about any duplication errors, since they have already been reported
+ scope.PushMarker();
+ foreach (Formal p in m.Ins) {
+ scope.Push(p.Name, p);
+ }
+
+ // Start resolving specification...
+ foreach (MaybeFreeExpression e in m.Req) {
+ ResolveExpression(e.E, false);
+ 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 (Expression e in m.Mod) {
+ ResolveExpression(e, false);
+ Type t = e.Type;
+ assert t != null; // follows from postcondition of ResolveExpression
+ if (t is CollectionType) {
+ t = ((CollectionType)t).Arg;
+ }
+ if (t is ObjectType) {
+ // fine
+ } else if (ClassType.DenotesClass(t) != null) {
+ // fine
+ } else {
+ Error(e, "a modifies-clause expression must denote an object or a collection of objects (instead got {0})", e.Type);
+ }
+ }
+
+ // Add out-parameters to the scope, but don't care about any duplication errors, since they have already been reported
+ foreach (Formal p in m.Outs) {
+ scope.Push(p.Name, p);
+ }
+
+ // ... continue resolving specification
+ foreach (MaybeFreeExpression e in m.Ens) {
+ ResolveExpression(e.E, true);
+ 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) {
+ ResolveStatement(m.Body);
+ }
+
+ scope.PopMarker();
+ }
+
+ public void ResolveType(Type! type) {
+ if (type is BasicType) {
+ // nothing to resolve
+ } else if (type is CollectionType) {
+ ResolveType(((CollectionType)type).Arg);
+ } else if (type is ClassType) {
+ ClassType t = (ClassType)type;
+ foreach (Type tt in t.TypeArgs) {
+ ResolveType(tt);
+ }
+ TypeParameter tp = allTypeParameters.Find(t.Name);
+ 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 {
+ ClassDecl cl;
+ if (!classes.TryGetValue(t.Name, out cl)) {
+ Error(t.tok, "Undeclared class type or type parameter: {0}", t.Name);
+ } else if (((!)cl).TypeArgs.Count == t.TypeArgs.Count) {
+ t.ResolvedClass = cl;
+ } else {
+ Error(t.tok, "Wrong number of type arguments ({0} instead of {1}) passed to class: {2}", t.TypeArgs.Count, cl.TypeArgs.Count, t.Name);
+ }
+ }
+
+ } else if (type is TypeProxy) {
+ TypeProxy t = (TypeProxy)type;
+ if (t.T != null) {
+ ResolveType(t.T);
+ }
+
+ } else {
+ assert false; // unexpected type
+ }
+ }
+
+ public bool UnifyTypes(Type! a, Type! b) {
+ 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
+ // allow anything 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 SeqType) {
+ return b is SeqType && UnifyTypes(((SeqType)a).Arg, ((SeqType)b).Arg);
+ } else if (a is ClassType) {
+ if (!(b is ClassType)) {
+ return false;
+ }
+ ClassType aa = (ClassType)a;
+ ClassType bb = (ClassType)b;
+ if (aa.ResolvedClass != null && aa.ResolvedClass == bb.ResolvedClass) {
+ // these are both resolved class types
+ 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
+ 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 {
+ assert false; // unexpected type
+ }
+ }
+
+ bool AssignProxy(TypeProxy! proxy, Type! t)
+ requires proxy.T == null;
+ requires t is TypeProxy ==> ((TypeProxy)t).T == null;
+ modifies proxy.T, ((TypeProxy)t).T; // might also change t.T if t is a proxy
+ ensures result ==> 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 ObjectTypeProxy) {
+ if (t is ObjectType || ClassType.DenotesClass(t) != null) {
+ // all is fine, proxy can be redirected to t
+ } else {
+ return false;
+ }
+
+ } else if (proxy is ObjectsTypeProxy) {
+ if (t is ObjectType || ClassType.DenotesClass(t) != null) {
+ // all is good
+ } else if (t is CollectionType) {
+ proxy.T = new CollectionTypeProxy(new ObjectTypeProxy());
+ return UnifyTypes(proxy.T, t);
+ }
+
+ } 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 || (opProxy.AllowSeq && t is SeqType)) {
+ // this is the expected case
+ } else {
+ return false;
+ }
+
+ } else {
+ assert false; // unexpected proxy type
+ }
+
+ // do the merge
+ proxy.T = t;
+ return true;
+ }
+
+ bool AssignRestrictedProxies(RestrictedTypeProxy! a, RestrictedTypeProxy! b)
+ requires a != b;
+ requires a.T == null && b.T == null;
+ requires a.OrderID <= b.OrderID;
+ modifies a.T, b.T;
+ ensures result ==> a.T != null || b.T != null;
+ {
+ if (a is ObjectTypeProxy) {
+ if (b is ObjectTypeProxy) {
+ // all is fine
+ a.T = b;
+ return true;
+ } else if (b is ObjectsTypeProxy) {
+ // unify a and b by redirecting b to a, since a gives the stronger requirement
+ b.T = a;
+ return true;
+ } else {
+ return false;
+ }
+
+ } else if (a is ObjectsTypeProxy) {
+ if (b is ObjectsTypeProxy) {
+ // fine
+ a.T = b;
+ return true;
+ } else if (b is CollectionTypeProxy) {
+ // fine provided b's collection-element-type can be unified with object or a class type
+ a.T = new ObjectTypeProxy();
+ return UnifyTypes(a.T, ((CollectionTypeProxy)b).Arg);
+ } else if (b is OperationTypeProxy) {
+ // fine; restrict a to sets of object/class, and restrict b to set/seq of object/class
+ if (((OperationTypeProxy)b).AllowSeq) {
+ a.T = new CollectionTypeProxy(new ObjectTypeProxy());
+ b.T = a.T;
+ } else {
+ a.T = new SetType(new ObjectTypeProxy());
+ b.T = a.T;
+ }
+ return true;
+ } else {
+ assert false; // unexpected restricted-proxy type
+ }
+
+ } 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 {
+ assert false; // unexpected restricted-proxy type
+ }
+
+ } else if (a is OperationTypeProxy) {
+ assert b is OperationTypeProxy; // else we we have unexpected restricted-proxy type
+ if (((OperationTypeProxy)a).AllowSeq ==> ((OperationTypeProxy)b).AllowSeq) {
+ b.T = a;
+ } else {
+ a.T = b; // b has the stronger requirement
+ }
+ return true;
+
+ } else {
+ assert false; // unexpected restricted-proxy type
+ }
+ }
+
+ public void ResolveStatement(Statement! stmt)
+ requires !(stmt is LabelStmt); // these should be handled inside lists of statements
+ {
+ if (stmt is UseStmt) {
+ UseStmt s = (UseStmt)stmt;
+ ResolveExpression(s.Expr, true);
+ assert s.Expr.Type != null; // follows from postcondition of ResolveExpression
+ Expression expr = s.Expr;
+ while (true) {
+ if (expr is OldExpr) {
+ expr = ((OldExpr)expr).E;
+ } else {
+ break;
+ }
+ }
+ FunctionCallExpr fce = expr as FunctionCallExpr;
+ if (fce == null || fce.Function == null || !fce.Function.Use) {
+ Error(s.Expr, "use statement must indicate a function declared as use");
+ }
+ } else if (stmt is PredicateStmt) {
+ PredicateStmt s = (PredicateStmt)stmt;
+ ResolveExpression(s.Expr, true);
+ 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 BreakStmt) {
+ BreakStmt 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;
+ }
+ }
+
+ } else if (stmt is ReturnStmt) {
+ // nothing to resolve
+
+ } else if (stmt is AssignStmt) {
+ AssignStmt s = (AssignStmt)stmt;
+ ResolveExpression(s.Lhs, true);
+ assert s.Lhs.Type != null; // follows from postcondition of ResolveExpression
+ // check that LHS denotes a mutable variable or a field
+ if (s.Lhs is IdentifierExpr) {
+ IVariable var = ((IdentifierExpr)s.Lhs).Var;
+ if (var == null) {
+ // the LHS didn't resolve correctly; some error would already have been reported
+ } else if (!var.IsMutable) {
+ Error(stmt, "LHS of assignment must denote a mutable variable or field");
+ }
+ } else if (s.Lhs is FieldSelectExpr) {
+ // LHS is fine, but restrict the RHS to ExprRhs
+ if (!(s.Rhs is ExprRhs)) {
+ Error(stmt, "Assignment to field must have an expression RHS; try using a temporary local variable");
+ }
+ } else {
+ Error(stmt, "LHS of assignment must denote a mutable variable or field");
+ }
+
+ if (s.Rhs is ExprRhs) {
+ ExprRhs rr = (ExprRhs)s.Rhs;
+ ResolveExpression(rr.Expr, true);
+ assert rr.Expr.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(s.Lhs.Type, rr.Expr.Type)) {
+ Error(stmt, "RHS (of type {0}) not assignable to LHS (of type {1})", rr.Expr.Type, s.Lhs.Type);
+ }
+ } else if (s.Rhs is TypeRhs) {
+ TypeRhs rr = (TypeRhs)s.Rhs;
+ ResolveType(rr.Type);
+ if (!UnifyTypes(s.Lhs.Type, rr.Type)) {
+ Error(stmt, "type {0} is not assignable to LHS (of type {1})", rr.Type, s.Lhs.Type);
+ }
+ } else if (s.Rhs is HavocRhs) {
+ // nothing else to do
+ } else {
+ assert false; // unexpected RHS
+ }
+
+ } else if (stmt is VarDecl) {
+ VarDecl s = (VarDecl)stmt;
+ if (s.OptionalType != null) {
+ ResolveType(s.OptionalType);
+ s.type = s.OptionalType;
+ }
+ if (s.Rhs != null) {
+ Type! rhsType;
+ if (s.Rhs is ExprRhs) {
+ ExprRhs rr = (ExprRhs)s.Rhs;
+ ResolveExpression(rr.Expr, true);
+ assert rr.Expr.Type != null; // follows from postcondition of ResolveExpression
+ rhsType = rr.Expr.Type;
+ } else if (s.Rhs is TypeRhs) {
+ TypeRhs rr = (TypeRhs)s.Rhs;
+ ResolveType(rr.Type);
+ rhsType = rr.Type;
+ } else {
+ assert false; // unexpected RHS
+ }
+ if (s.OptionalType == null) {
+ s.type = rhsType;
+ } else if (!UnifyTypes(s.OptionalType, rhsType)) {
+ Error(stmt, "initialization RHS (of type {0}) not assignable to variable (of type {1})", rhsType, 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);
+ }
+
+ } else if (stmt is CallStmt) {
+ CallStmt s = (CallStmt)stmt;
+
+ // resolve left-hand side
+ Dictionary<string!,object> lhsNameSet = new Dictionary<string!,object>();
+ foreach (IdentifierExpr lhs in s.Lhs) {
+ ResolveExpression(lhs, true);
+ if (lhsNameSet.ContainsKey(lhs.Name)) {
+ Error(s, "Duplicate variable in left-hand side of call statement: {0}", lhs.Name);
+ } else {
+ lhsNameSet.Add(lhs.Name, null);
+ }
+ }
+ // resolve receiver
+ ResolveExpression(s.Receiver, true);
+ assert s.Receiver.Type != null; // follows from postcondition of ResolveExpression
+ // resolve arguments
+ foreach (Expression e in s.Args) {
+ ResolveExpression(e, true);
+ }
+
+ // resolve the method name
+ ClassType ctype;
+ MemberDecl member = ResolveMember(s.Tok, s.Receiver.Type, s.MethodName, out ctype);
+ if (member == null) {
+ // error has already been reported by ResolveMember
+ } else if (!(member is Method)) {
+ Error(s, "member {0} in class {1} does not refer to a method", s.MethodName, ((!)ctype).Name);
+ } else {
+ Method method = (Method)member;
+ s.Method = method;
+ if (method.Ins.Count != s.Args.Count) {
+ Error(s, "wrong number of method arguments (got {0}, expected {1})", s.Args.Count, method.Ins.Count);
+ } else if (method.Outs.Count != s.Lhs.Count) {
+ Error(s, "wrong number of method result arguments (got {0}, expected {1})", s.Lhs.Count, method.Outs.Count);
+ } else {
+ assert ctype != null; // follows from postcondition of ResolveMember
+ // 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]);
+ }
+ foreach (TypeParameter p in method.TypeArgs) {
+ subst.Add(p, new ParamTypeProxy(p));
+ }
+ // type check the arguments
+ for (int i = 0; i < method.Ins.Count; i++) {
+ UnifyTypes((!)s.Args[i].Type, SubstType(method.Ins[i].Type, subst));
+ }
+ for (int i = 0; i < method.Outs.Count; i++) {
+ UnifyTypes((!)s.Lhs[i].Type, SubstType(method.Outs[i].Type, subst));
+ }
+ }
+ }
+
+ } else if (stmt is BlockStmt) {
+ scope.PushMarker();
+ int labelsToPop = 0;
+ foreach (Statement ss in ((BlockStmt)stmt).Body) {
+ if (ss is LabelStmt) {
+ LabelStmt ls = (LabelStmt)ss;
+ labeledStatements.PushMarker();
+ bool b = labeledStatements.Push(ls.Label, ls);
+ assert b; // since we just pushed a marker, we expect the Push to succeed
+ labelsToPop++;
+ } else {
+ ResolveStatement(ss);
+ for (; 0 < labelsToPop; labelsToPop--) { labeledStatements.PopMarker(); }
+ }
+ }
+ for (; 0 < labelsToPop; labelsToPop--) { labeledStatements.PopMarker(); }
+ scope.PopMarker();
+
+ } else if (stmt is IfStmt) {
+ IfStmt s = (IfStmt)stmt;
+ if (s.Guard != null) {
+ ResolveExpression(s.Guard, true);
+ assert s.Guard.Type != null; // follows from postcondition of ResolveExpression
+ 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);
+ }
+ }
+ ResolveStatement(s.Thn);
+ if (s.Els != null) {
+ ResolveStatement(s.Els);
+ }
+
+ } else if (stmt is WhileStmt) {
+ WhileStmt s = (WhileStmt)stmt;
+ if (s.Guard != null) {
+ ResolveExpression(s.Guard, true);
+ assert s.Guard.Type != null; // follows from postcondition of ResolveExpression
+ 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);
+ }
+ }
+ foreach (MaybeFreeExpression inv in s.Invariants) {
+ ResolveExpression(inv.E, true);
+ 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) {
+ ResolveExpression(e, true);
+ // any type is fine
+ }
+ ResolveStatement(s.Body);
+
+ } else if (stmt is ForeachStmt) {
+ ForeachStmt s = (ForeachStmt)stmt;
+ scope.PushMarker();
+ bool b = scope.Push(s.BoundVar.Name, s.BoundVar);
+ assert b; // since we just pushed a marker, we expect the Push to succeed
+ ResolveType(s.BoundVar.Type);
+
+ ResolveExpression(s.Collection, true);
+ assert s.Collection.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(s.Collection.Type, new CollectionTypeProxy(s.BoundVar.Type))) {
+ Error(s.Collection, "The type is expected to be a collection of {0} (instead got {1})", s.BoundVar.Type, s.Collection.Type);
+ }
+
+ ResolveExpression(s.Range, true);
+ assert s.Range.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(s.Range.Type, Type.Bool)) {
+ Error(s.Range, "range condition is expected to be of type {0}, but is {1}", Type.Bool, s.Range.Type);
+ }
+
+ foreach (PredicateStmt ss in s.BodyPrefix) {
+ ResolveStatement(ss);
+ }
+ if (s.BodyAssign != null) {
+ ResolveStatement(s.BodyAssign);
+ // check for correct usage of BoundVar in LHS and RHS of this assignment
+ FieldSelectExpr lhs = s.BodyAssign.Lhs as FieldSelectExpr;
+ IdentifierExpr obj = lhs == null ? null : lhs.Obj as IdentifierExpr;
+ if (obj != null && obj.Var == s.BoundVar) {
+ // exemplary!
+ } else {
+ Error(s, "assignment inside foreach must assign to a field of the bound variable of the foreach statement");
+ }
+ }
+ scope.PopMarker();
+
+ } else {
+ assert false;
+ }
+ }
+
+ MemberDecl ResolveMember(Token! tok, Type! receiverType, string! memberName, out ClassType ctype)
+ ensures result != null ==> ctype != null && ctype.ResolvedClass != null;
+ {
+ ctype = ClassType.DenotesClass(receiverType);
+ if (ctype == null) {
+ Error(tok, "receiver (of type {0}) must be of a class type", receiverType);
+ } else {
+ assert ctype.ResolvedClass != null; // follows from postcondition of DenotesClass
+ assert ctype.TypeArgs.Count == ctype.ResolvedClass.TypeArgs.Count; // follows from the fact that ctype was resolved
+ MemberDecl member;
+ if (!classMembers[ctype.ResolvedClass].TryGetValue(memberName, out member)) {
+ Error(tok, "member {0} does not exist in class {1}", memberName, ctype.Name);
+ } else {
+ return (!)member;
+ }
+ }
+ ctype = null;
+ return null;
+ }
+
+ Type! SubstType(Type! type, Dictionary<TypeParameter!,Type!>! subst) {
+ if (type is BasicType) {
+ return type;
+ } 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 SeqType) {
+ return new SeqType(arg);
+ } else {
+ assert false; // unexpected collection type
+ }
+ } else if (type is ClassType) {
+ ClassType t = (ClassType)type;
+ if (t.ResolvedParam != null) {
+ assert t.TypeArgs.Count == 0;
+ Type s;
+ if (subst.TryGetValue(t.ResolvedParam, out s)) {
+ return (!)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 ClassType(t.tok, t.Name, t.ResolvedClass, newArgs);
+ }
+ } else {
+ // there's neither a resolved param nor a resolved class, which means the ClassType 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 {
+ assert false; // unexpected type
+ }
+ }
+
+ public static ClassType! GetThisType(Token! tok, ClassDecl! cl) {
+ List<Type!> args = new List<Type!>();
+ foreach (TypeParameter tp in cl.TypeArgs) {
+ args.Add(new ClassType(tok, tp.Name, tp));
+ }
+ return new ClassType(tok, cl.Name, cl, args);
+ }
+
+ /// <summary>
+ /// "twoState" implies that "old" and "fresh" expressions are allowed
+ /// </summary>
+ void ResolveExpression(Expression! expr, bool twoState)
+ requires currentClass != null;
+ 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 LiteralExpr) {
+ LiteralExpr e = (LiteralExpr)expr;
+ if (e.Value == null) {
+ e.Type = new ObjectTypeProxy();
+ } else if (e.Value is int) {
+ e.Type = Type.Int;
+ } else if (e.Value is bool) {
+ e.Type = Type.Bool;
+ } else {
+ assert false; // unexpected literal type
+ }
+
+ } else if (expr is ThisExpr) {
+ expr.Type = GetThisType(expr.tok, currentClass);
+
+ } 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 DisplayExpression) {
+ DisplayExpression e = (DisplayExpression)expr;
+ Type elementType = new InferredTypeProxy();
+ foreach (Expression ee in e.Elements) {
+ ResolveExpression(ee, twoState);
+ 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 {
+ expr.Type = new SeqType(elementType);
+ }
+
+ } else if (expr is FieldSelectExpr) {
+ FieldSelectExpr e = (FieldSelectExpr)expr;
+ ResolveExpression(e.Obj, twoState);
+ assert e.Obj.Type != null; // follows from postcondition of ResolveExpression
+ ClassType ctype;
+ MemberDecl member = ResolveMember(expr.tok, e.Obj.Type, e.FieldName, out ctype);
+ if (member == null) {
+ // error has already been reported by ResolveMember
+ } else if (!(member is Field)) {
+ Error(expr, "member {0} in class {1} does not refer to a field", e.FieldName, ((!)ctype).Name);
+ } else {
+ assert ctype != null && ctype.ResolvedClass != null; // follows from postcondition of ResolveMember
+ e.Field = (Field)member;
+ // 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;
+ bool seqErr = false;
+ ResolveExpression(e.Seq, twoState);
+ assert e.Seq.Type != null; // follows from postcondition of ResolveExpression
+ Type elementType = new InferredTypeProxy();
+ if (!UnifyTypes(e.Seq.Type, new SeqType(elementType))) {
+ Error(expr, "sequence selection requires a sequence (got {0})", e.Seq.Type);
+ seqErr = true;
+ }
+ if (e.E0 != null) {
+ ResolveExpression(e.E0, twoState);
+ assert e.E0.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(e.E0.Type, Type.Int)) {
+ Error(e.E0, "sequence selections requires integer indices (got {0})", e.E0.Type);
+ }
+ }
+ if (e.E1 != null) {
+ ResolveExpression(e.E1, twoState);
+ assert e.E1.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(e.E1.Type, Type.Int)) {
+ Error(e.E1, "sequence selections requires integer indices (got {0})", e.E1.Type);
+ }
+ }
+ if (!seqErr) {
+ if (e.SelectOne) {
+ expr.Type = elementType;
+ } else {
+ expr.Type = e.Seq.Type;
+ }
+ }
+
+ } else if (expr is FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)expr;
+ ResolveExpression(e.Receiver, twoState);
+ assert e.Receiver.Type != null; // follows from postcondition of ResolveExpression
+ ClassType ctype;
+ MemberDecl member = ResolveMember(expr.tok, e.Receiver.Type, e.Name, out ctype);
+ if (member == null) {
+ // error has already been reported by ResolveMember
+ } else if (!(member is Function)) {
+ Error(expr, "member {0} in class {1} does not refer to a function", e.Name, ((!)ctype).Name);
+ } else {
+ Function function = (Function)member;
+ e.Function = function;
+ if (function.Formals.Count != e.Args.Count) {
+ Error(expr, "wrong number of function arguments (got {0}, expected {1})", e.Args.Count, function.Formals.Count);
+ } else {
+ assert ctype != null; // follows from postcondition of ResolveMember
+ // 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]);
+ }
+ foreach (TypeParameter p in function.TypeArgs) {
+ subst.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);
+ assert farg.Type != null; // follows from postcondition of ResolveExpression
+ UnifyTypes(farg.Type, SubstType(function.Formals[i].Type, subst));
+ }
+ expr.Type = SubstType(function.ResultType, subst);
+ }
+ }
+
+ } 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);
+ expr.Type = e.E.Type;
+
+ } 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;
+ assert t != null; // follows from postcondition of ResolveExpression
+ if (t is CollectionType) {
+ t = ((CollectionType)t).Arg;
+ }
+ if (t is ObjectType) {
+ // fine
+ } else if (ClassType.DenotesClass(t) != null) {
+ // fine
+ } 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);
+ 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.SeqLength:
+ if (!UnifyTypes(e.E.Type, new SeqType(new InferredTypeProxy()))) {
+ Error(expr, "sequence-length operator expects a sequence argument (instead got {0})", e.E.Type);
+ }
+ expr.Type = Type.Int;
+ break;
+ default:
+ assert false; // unexpected unary operator
+ }
+
+ } else if (expr is BinaryExpr) {
+ BinaryExpr e = (BinaryExpr)expr;
+ ResolveExpression(e.E0, twoState);
+ assert e.E0.Type != null; // follows from postcondition of ResolveExpression
+ ResolveExpression(e.E1, twoState);
+ 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 (!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:
+ if (!UnifyTypes(e.E0.Type, new SetType(new InferredTypeProxy()))) {
+ Error(expr, "arguments must be of a set type (got {0})", e.E0.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.Lt:
+ case BinaryExpr.Opcode.Le:
+ case BinaryExpr.Opcode.Add:
+ {
+ 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:
+ {
+ 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:
+ if (!UnifyTypes(e.E1.Type, new CollectionTypeProxy(e.E0.Type))) {
+ Error(expr, "second argument to {0} must be a set or sequence of type {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:
+ assert false; // unexpected operator
+ }
+ e.ResolvedOp = ResolveOp(e.Op, e.E1.Type);
+
+ } else if (expr is QuantifierExpr) {
+ QuantifierExpr e = (QuantifierExpr)expr;
+ scope.PushMarker();
+ foreach (BoundVar v in e.BoundVars) {
+ if (!scope.Push(v.Name, v)) {
+ Error(v, "Duplicate parameter name: {0}", v.Name);
+ }
+ ResolveType(v.Type);
+ }
+ ResolveExpression(e.Body, twoState);
+ assert e.Body.Type != null; // follows from postcondition of ResolveExpression
+ if (!UnifyTypes(e.Body.Type, Type.Bool)) {
+ Error(expr, "body of quantifier must be of type bool (instead got {0})", e.Body.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 and triggers (below).
+ ResolveAttributes(e.Attributes, twoState);
+ ResolveTriggers(e.Trigs, twoState);
+ scope.PopMarker();
+ expr.Type = Type.Bool;
+
+ } else if (expr is ITEExpr) {
+ ITEExpr e = (ITEExpr)expr;
+ assert !twoState;
+ ResolveExpression(e.Test, twoState);
+ assert e.Test.Type != null; // follows from postcondition of ResolveExpression
+ ResolveExpression(e.Thn, twoState);
+ assert e.Thn.Type != null; // follows from postcondition of ResolveExpression
+ ResolveExpression(e.Els, twoState);
+ 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, Type.Bool)) {
+ Error(expr, "the then branch of an if-then-else expression must be a boolean (instead got {0})", e.Thn.Type);
+ }
+ if (!UnifyTypes(e.Els.Type, Type.Bool)) {
+ Error(expr, "the else branch of an if-then-else expression must be a boolean (instead got {0})", e.Els.Type);
+ }
+ expr.Type = Type.Bool;
+
+ } else {
+ assert false; // unexpected expression
+ }
+
+ if (expr.Type == null) {
+ // some resolution error occurred
+ expr.Type = Type.Flexible;
+ }
+ }
+
+ /// <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) {
+ 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 SeqType) {
+ return BinaryExpr.ResolvedOpcode.SeqEq;
+ } else {
+ return BinaryExpr.ResolvedOpcode.EqCommon;
+ }
+ case BinaryExpr.Opcode.Neq:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.SetNeq;
+ } else if (operandType is SeqType) {
+ return BinaryExpr.ResolvedOpcode.SeqNeq;
+ } else {
+ return BinaryExpr.ResolvedOpcode.NeqCommon;
+ }
+ case BinaryExpr.Opcode.Disjoint: return BinaryExpr.ResolvedOpcode.Disjoint;
+ case BinaryExpr.Opcode.Lt:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.ProperSubset;
+ } 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 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 SeqType) {
+ return BinaryExpr.ResolvedOpcode.Concat;
+ } else {
+ return BinaryExpr.ResolvedOpcode.Add;
+ }
+ case BinaryExpr.Opcode.Sub:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.SetDifference;
+ } else {
+ return BinaryExpr.ResolvedOpcode.Sub;
+ }
+ case BinaryExpr.Opcode.Mul:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.Intersection;
+ } else {
+ return BinaryExpr.ResolvedOpcode.Mul;
+ }
+ case BinaryExpr.Opcode.Gt:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.ProperSuperset;
+ } else {
+ return BinaryExpr.ResolvedOpcode.Gt;
+ }
+ case BinaryExpr.Opcode.Ge:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.Superset;
+ } else {
+ return BinaryExpr.ResolvedOpcode.Ge;
+ }
+ case BinaryExpr.Opcode.In:
+ if (operandType is SetType) {
+ return BinaryExpr.ResolvedOpcode.InSet;
+ } else {
+ return BinaryExpr.ResolvedOpcode.InSeq;
+ }
+ case BinaryExpr.Opcode.Div: return BinaryExpr.ResolvedOpcode.Div;
+ case BinaryExpr.Opcode.Mod: return BinaryExpr.ResolvedOpcode.Mod;
+ default:
+ assert false; // unexpected operator
+ }
+ }
+ }
+
+ 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?>();
+ invariant names.Count == things.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);
+ }
+
+ // Pushes name-->var 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) {
+ if (Find(name, true) != null) {
+ return false;
+ } else {
+ names.Add(name);
+ things.Add(thing);
+ return true;
+ }
+ }
+
+ Thing? Find(string! name, bool topScopeOnly) {
+ 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];
+ assert t != null;
+ return t;
+ }
+ }
+ return null; // not present
+ }
+
+ public Thing? Find(string! name) {
+ return Find(name, false);
+ }
+ }
+}
diff --git a/Dafny/Scanner.ssc b/Dafny/Scanner.ssc
new file mode 100644
index 00000000..e026ca7e
--- /dev/null
+++ b/Dafny/Scanner.ssc
@@ -0,0 +1,491 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.Contracts;
+using Bpl = Microsoft.Boogie;
+using BoogiePL;
+
+
+namespace Microsoft.Dafny {
+
+ [Immutable]
+ public class Token : Bpl.Token {
+ public Token();
+ public Token(int linenum, int colnum) {
+ base(linenum, colnum);
+ }
+ public new static Token! NoToken = new Token();
+ }
+
+}
+
+namespace Microsoft.Dafny {
+
+
+public class Scanner {
+ const char EOF = '\0';
+ const char CR = '\r';
+ const char LF = '\n';
+
+ [Microsoft.Contracts.Verify(false)]
+ static Scanner() {
+ start[0] = 50;
+ start[33] = 31;
+ start[34] = 3;
+ start[37] = 40;
+ start[38] = 25;
+ start[39] = 1;
+ start[40] = 12;
+ start[41] = 13;
+ start[42] = 15;
+ start[43] = 37;
+ start[44] = 7;
+ start[45] = 38;
+ start[46] = 44;
+ start[47] = 39;
+ start[48] = 2;
+ start[49] = 2;
+ start[50] = 2;
+ start[51] = 2;
+ start[52] = 2;
+ start[53] = 2;
+ start[54] = 2;
+ start[55] = 2;
+ start[56] = 2;
+ start[57] = 2;
+ start[58] = 9;
+ start[59] = 8;
+ start[60] = 10;
+ start[61] = 21;
+ start[62] = 11;
+ start[63] = 1;
+ start[65] = 1;
+ start[66] = 1;
+ start[67] = 1;
+ start[68] = 1;
+ start[69] = 1;
+ start[70] = 1;
+ start[71] = 1;
+ start[72] = 1;
+ start[73] = 1;
+ start[74] = 1;
+ start[75] = 1;
+ start[76] = 1;
+ start[77] = 1;
+ start[78] = 1;
+ start[79] = 1;
+ start[80] = 1;
+ start[81] = 1;
+ start[82] = 1;
+ start[83] = 1;
+ start[84] = 1;
+ start[85] = 1;
+ start[86] = 1;
+ start[87] = 1;
+ start[88] = 1;
+ start[89] = 1;
+ start[90] = 1;
+ start[91] = 42;
+ start[92] = 1;
+ start[93] = 43;
+ start[95] = 1;
+ start[96] = 1;
+ start[97] = 1;
+ start[98] = 1;
+ start[99] = 1;
+ start[100] = 1;
+ start[101] = 1;
+ start[102] = 1;
+ start[103] = 1;
+ start[104] = 1;
+ start[105] = 1;
+ start[106] = 1;
+ start[107] = 1;
+ start[108] = 1;
+ start[109] = 1;
+ start[110] = 1;
+ start[111] = 1;
+ start[112] = 1;
+ start[113] = 1;
+ start[114] = 1;
+ start[115] = 1;
+ start[116] = 1;
+ start[117] = 1;
+ start[118] = 1;
+ start[119] = 1;
+ start[120] = 1;
+ start[121] = 1;
+ start[122] = 1;
+ start[123] = 5;
+ start[124] = 16;
+ start[125] = 6;
+ start[172] = 41;
+ start[8226] = 49;
+ start[8658] = 24;
+ start[8660] = 20;
+ start[8704] = 46;
+ start[8707] = 47;
+ start[8743] = 27;
+ start[8744] = 29;
+ start[8800] = 34;
+ start[8804] = 35;
+ start[8805] = 36;
+ }
+ const int noSym = 86;
+ static short[] start = new short[16385];
+
+
+
+ static Token/*!*/ t; // current token
+ static char ch; // current input character
+ static int pos; // column number of current character
+ static int line; // line number of current character
+ static int lineStart; // start position of current line
+ static Queue! oldEols; // EOLs that appeared in a comment;
+ static BitArray/*!*/ ignore; // set of characters to be ignored by the scanner
+ static string Filename;
+
+ ///<summary>
+ ///Initializes the scanner. Note: first fill the Buffer.
+ ///</summary>
+ ///<param name="filename">File name used for error reporting</param>
+ public static void Init (string filename) {
+ Filename = filename;
+ pos = -1; line = 1; lineStart = 0;
+ oldEols = new Queue();
+ NextCh();
+ ignore = new BitArray(16384);
+ ignore[9] = true; ignore[10] = true; ignore[13] = true; ignore[32] = true;
+
+ }
+
+ private static void NextCh() {
+ if (oldEols.Count > 0) {
+ ch = (char) ((!)oldEols.Dequeue());
+ } else {
+ while (true) {
+ ch = (char)BoogiePL.Buffer.Read(); pos++;
+ if (ch == BoogiePL.Buffer.eof) {
+ ch = EOF;
+ } else if (ch == LF) {
+ line++;
+ lineStart = pos + 1;
+
+ } else if (ch == '#' && pos == lineStart) {
+ int prLine = line;
+ int prColumn = pos - lineStart; // which is 0
+
+ string hashLine = BoogiePL.Buffer.ReadToEOL(); pos += hashLine.Length;
+ line++;
+ lineStart = pos + 1;
+
+ 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 (System.FormatException) {
+ // just fall down through to produce an error message
+ }
+ Errors.SemErr(Filename, prLine, prColumn, "Malformed (#line num [filename]) pragma: #" + hashLine);
+ continue;
+ }
+
+ Errors.SemErr(Filename, prLine, prColumn, "Unrecognized pragma: #" + hashLine);
+ continue;
+ }
+ return;
+ }
+ }
+ }
+
+
+ static bool Comment0() {
+ int level = 1, line0 = line, lineStart0 = lineStart;
+ NextCh();
+ if (ch == '/') {
+ NextCh();
+ for(;;) {
+ if (ch == 10) {
+ level--;
+ if (level == 0) {
+ while(line0 < line) {oldEols.Enqueue('\r'); oldEols.Enqueue('\n'); line0++;}
+ NextCh(); return true;
+ }
+ NextCh();
+ } else if (ch == EOF) return false;
+ else NextCh();
+ }
+ } else {
+ if (ch==CR) {line--; lineStart = lineStart0;}
+ pos = pos - 2; Buffer.Pos = pos+1; NextCh();
+ }
+ return false;
+ }
+
+ static bool Comment1() {
+ int level = 1, line0 = line, lineStart0 = lineStart;
+ NextCh();
+ if (ch == '*') {
+ NextCh();
+ for(;;) {
+ if (ch == '*') {
+ NextCh();
+ if (ch == '/') {
+ level--;
+ if (level == 0) {
+ while(line0 < line) {oldEols.Enqueue('\r'); oldEols.Enqueue('\n'); line0++;}
+ NextCh(); return true;
+ }
+ NextCh();
+ }
+ } else if (ch == '/') {
+ NextCh();
+ if (ch == '*') {
+ level++; NextCh();
+ }
+ } else if (ch == EOF) return false;
+ else NextCh();
+ }
+ } else {
+ if (ch==CR) {line--; lineStart = lineStart0;}
+ pos = pos - 2; Buffer.Pos = pos+1; NextCh();
+ }
+ return false;
+ }
+
+
+ static void CheckLiteral() {
+ switch (t.val) {
+ case "class": t.kind = 4; break;
+ case "var": t.kind = 7; break;
+ case "frame": t.kind = 13; break;
+ case "method": t.kind = 14; break;
+ case "returns": t.kind = 15; break;
+ case "modifies": t.kind = 16; break;
+ case "free": t.kind = 17; break;
+ case "requires": t.kind = 18; break;
+ case "ensures": t.kind = 19; break;
+ case "bool": t.kind = 22; break;
+ case "int": t.kind = 23; break;
+ case "object": t.kind = 24; break;
+ case "set": t.kind = 25; break;
+ case "seq": t.kind = 26; break;
+ case "function": t.kind = 27; break;
+ case "use": t.kind = 28; break;
+ case "reads": t.kind = 29; break;
+ case "if": t.kind = 30; break;
+ case "else": t.kind = 31; break;
+ case "label": t.kind = 32; break;
+ case "break": t.kind = 33; break;
+ case "return": t.kind = 34; break;
+ case "new": t.kind = 36; break;
+ case "havoc": t.kind = 37; break;
+ case "while": t.kind = 38; break;
+ case "invariant": t.kind = 39; break;
+ case "decreases": t.kind = 40; break;
+ case "call": t.kind = 42; break;
+ case "foreach": t.kind = 43; break;
+ case "in": t.kind = 44; break;
+ case "assert": t.kind = 46; break;
+ case "assume": t.kind = 47; break;
+ case "false": t.kind = 70; break;
+ case "true": t.kind = 71; break;
+ case "null": t.kind = 72; break;
+ case "fresh": t.kind = 73; break;
+ case "this": t.kind = 78; break;
+ case "old": t.kind = 79; break;
+ case "forall": t.kind = 80; break;
+ case "exists": t.kind = 82; break;
+ default: break;
+
+ }
+ }
+
+ public static Token/*!*/ Scan() {
+ while (ignore[ch]) { NextCh(); }
+ if (ch == '/' && Comment0() || ch == '/' && Comment1() ) return Scan();
+ t = new Token();
+ t.pos = pos; t.col = pos - lineStart + 1; t.line = line; t.filename = Filename;
+ int state = (/*^ (!) ^*/ start)[ch];
+ StringBuilder buf = new StringBuilder(16);
+ buf.Append(ch); NextCh();
+
+ switch (state) {
+ case 0: {t.kind = noSym; goto done;} // NextCh already done
+ case 1:
+ if ((ch == 39 || ch >= '0' && ch <= '9' || ch == '?' || ch >= 'A' && ch <= 'Z' || ch == 92 || ch >= '_' && ch <= 'z')) {buf.Append(ch); NextCh(); goto case 1;}
+ else {t.kind = 1; t.val = buf.ToString(); CheckLiteral(); return t;}
+ case 2:
+ if ((ch >= '0' && ch <= '9')) {buf.Append(ch); NextCh(); goto case 2;}
+ else {t.kind = 2; goto done;}
+ case 3:
+ if ((ch == '"')) {buf.Append(ch); NextCh(); goto case 4;}
+ else if ((ch >= ' ' && ch <= '!' || ch >= '#' && ch <= '~')) {buf.Append(ch); NextCh(); goto case 3;}
+ else {t.kind = noSym; goto done;}
+ case 4:
+ {t.kind = 3; goto done;}
+ case 5:
+ {t.kind = 5; goto done;}
+ case 6:
+ {t.kind = 6; goto done;}
+ case 7:
+ {t.kind = 8; goto done;}
+ case 8:
+ {t.kind = 9; goto done;}
+ case 9:
+ if (ch == '=') {buf.Append(ch); NextCh(); goto case 14;}
+ else if (ch == ':') {buf.Append(ch); NextCh(); goto case 48;}
+ else {t.kind = 10; goto done;}
+ case 10:
+ if (ch == '=') {buf.Append(ch); NextCh(); goto case 17;}
+ else {t.kind = 11; goto done;}
+ case 11:
+ if (ch == '=') {buf.Append(ch); NextCh(); goto case 30;}
+ else {t.kind = 12; goto done;}
+ case 12:
+ {t.kind = 20; goto done;}
+ case 13:
+ {t.kind = 21; goto done;}
+ case 14:
+ {t.kind = 35; goto done;}
+ case 15:
+ {t.kind = 41; goto done;}
+ case 16:
+ if (ch == '|') {buf.Append(ch); NextCh(); goto case 28;}
+ else {t.kind = 45; goto done;}
+ case 17:
+ if (ch == '=') {buf.Append(ch); NextCh(); goto case 18;}
+ else {t.kind = 57; goto done;}
+ case 18:
+ if (ch == '>') {buf.Append(ch); NextCh(); goto case 19;}
+ else {t.kind = noSym; goto done;}
+ case 19:
+ {t.kind = 48; goto done;}
+ case 20:
+ {t.kind = 49; goto done;}
+ case 21:
+ if (ch == '=') {buf.Append(ch); NextCh(); goto case 22;}
+ else {t.kind = noSym; goto done;}
+ case 22:
+ if (ch == '>') {buf.Append(ch); NextCh(); goto case 23;}
+ else {t.kind = 56; goto done;}
+ case 23:
+ {t.kind = 50; goto done;}
+ case 24:
+ {t.kind = 51; goto done;}
+ case 25:
+ if (ch == '&') {buf.Append(ch); NextCh(); goto case 26;}
+ else {t.kind = noSym; goto done;}
+ case 26:
+ {t.kind = 52; goto done;}
+ case 27:
+ {t.kind = 53; goto done;}
+ case 28:
+ {t.kind = 54; goto done;}
+ case 29:
+ {t.kind = 55; goto done;}
+ case 30:
+ {t.kind = 58; goto done;}
+ case 31:
+ if (ch == '=') {buf.Append(ch); NextCh(); goto case 32;}
+ else if (ch == '!') {buf.Append(ch); NextCh(); goto case 33;}
+ else {t.kind = 68; goto done;}
+ case 32:
+ {t.kind = 59; goto done;}
+ case 33:
+ {t.kind = 60; goto done;}
+ case 34:
+ {t.kind = 61; goto done;}
+ case 35:
+ {t.kind = 62; goto done;}
+ case 36:
+ {t.kind = 63; goto done;}
+ case 37:
+ {t.kind = 64; goto done;}
+ case 38:
+ {t.kind = 65; goto done;}
+ case 39:
+ {t.kind = 66; goto done;}
+ case 40:
+ {t.kind = 67; goto done;}
+ case 41:
+ {t.kind = 69; goto done;}
+ case 42:
+ {t.kind = 74; goto done;}
+ case 43:
+ {t.kind = 75; goto done;}
+ case 44:
+ if (ch == '.') {buf.Append(ch); NextCh(); goto case 45;}
+ else {t.kind = 76; goto done;}
+ case 45:
+ {t.kind = 77; goto done;}
+ case 46:
+ {t.kind = 81; goto done;}
+ case 47:
+ {t.kind = 83; goto done;}
+ case 48:
+ {t.kind = 84; goto done;}
+ case 49:
+ {t.kind = 85; goto done;}
+ case 50: {t.kind = 0; goto done;}
+ }
+ done:
+ t.val = buf.ToString();
+ return t;
+ }
+
+} // end Scanner
+
+
+public delegate void ErrorProc(int n, string filename, int line, int col);
+
+public class Errors {
+ public static int count = 0; // number of errors detected
+ public static ErrorProc/*!*/ SynErr; // syntactic errors
+
+ public static void SemErr(string filename, int line, int col, string! msg) { // semantic errors
+ System.ConsoleColor color = System.Console.ForegroundColor;
+ System.Console.ForegroundColor = System.ConsoleColor.Red;
+ System.Console.WriteLine("{0}({1},{2}): Error: {3}", filename, line, col, msg);
+ System.Console.ForegroundColor = color;
+ count++;
+ }
+
+ public static void SemErr(Bpl.IToken! tok, string! msg) { // semantic errors
+ SemErr(tok.filename, tok.line, tok.col, msg);
+ }
+
+ public static void Exception (string s) {
+ System.ConsoleColor color = System.Console.ForegroundColor;
+ System.Console.ForegroundColor = System.ConsoleColor.Red;
+ System.Console.WriteLine(s);
+ System.Console.ForegroundColor = color;
+ System.Environment.Exit(0);
+ }
+
+} // Errors
+
+} // end namespace
diff --git a/Dafny/Translator.ssc b/Dafny/Translator.ssc
new file mode 100644
index 00000000..c5f85130
--- /dev/null
+++ b/Dafny/Translator.ssc
@@ -0,0 +1,2446 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using Microsoft.Contracts;
+using Bpl = Microsoft.Boogie;
+
+// TODO: wellformedness checks for function bodies
+// TODO: totality checks for statements
+// TODO: totality checks for pre/post conditions
+
+namespace Microsoft.Dafny {
+ public class Translator {
+ public Translator() {
+ Bpl.Program boogieProgram = ReadPrelude();
+ if (boogieProgram != null) {
+ sink = boogieProgram;
+ predef = FindPredefinedDecls(boogieProgram);
+ }
+ }
+
+ // translation state
+ readonly Dictionary<ClassDecl!,Bpl.Constant!>! classes = new Dictionary<ClassDecl!,Bpl.Constant!>();
+ readonly Dictionary<Field!,Bpl.Constant!>! fields = new Dictionary<Field!,Bpl.Constant!>();
+ readonly Dictionary<Function!,Bpl.Function!>! functions = new Dictionary<Function!,Bpl.Function!>();
+ readonly Dictionary<Method!,Bpl.Procedure!>! methods = new Dictionary<Method!,Bpl.Procedure!>();
+
+ readonly Bpl.Program sink;
+ readonly PredefinedDecls predef;
+ internal class PredefinedDecls {
+ public readonly Bpl.Type! RefType;
+ private readonly Bpl.TypeCtorDecl! seqTypeCtor;
+ public Bpl.Type! SeqType(Token! tok, Bpl.Type! ty) {
+ return new Bpl.CtorType(Token.NoToken, seqTypeCtor, new Bpl.TypeSeq(ty));
+ }
+ readonly Bpl.TypeCtorDecl! fieldName;
+ public Bpl.Type! FieldName(Token! tok, Bpl.Type! ty) {
+ return new Bpl.CtorType(tok, fieldName, new Bpl.TypeSeq(ty));
+ }
+ public readonly Bpl.Type! HeapType;
+ public readonly Bpl.Type! ClassNameType;
+ public readonly Bpl.Expr! Null;
+ private readonly Bpl.Constant! allocField;
+ public Bpl.IdentifierExpr! Alloc(Token! tok) {
+ return new Bpl.IdentifierExpr(tok, allocField);
+ }
+
+ public PredefinedDecls(Bpl.TypeCtorDecl! refType, Bpl.TypeCtorDecl! seqTypeCtor, Bpl.TypeCtorDecl! fieldNameType,
+ Bpl.GlobalVariable! heap, Bpl.TypeCtorDecl! classNameType, Bpl.Constant! allocField) {
+ Bpl.CtorType refT = new Bpl.CtorType(Token.NoToken, refType, new Bpl.TypeSeq());
+ this.RefType = refT;
+ this.seqTypeCtor = seqTypeCtor;
+ this.fieldName = fieldNameType;
+ this.HeapType = heap.TypedIdent.Type;
+ this.ClassNameType = new Bpl.CtorType(Token.NoToken, classNameType, new Bpl.TypeSeq());
+ this.allocField = allocField;
+ this.Null = new Bpl.IdentifierExpr(Token.NoToken, "null", refT);
+ }
+ }
+
+ static PredefinedDecls FindPredefinedDecls(Bpl.Program! prog) {
+ if (prog.Resolve() != 0) {
+ Console.WriteLine("Error: resolution errors encountered in Dafny prelude");
+ return null;
+ }
+
+ Bpl.TypeCtorDecl refType = null;
+ Bpl.TypeCtorDecl seqTypeCtor = null;
+ Bpl.TypeCtorDecl fieldNameType = null;
+ Bpl.TypeCtorDecl classNameType = null;
+ Bpl.GlobalVariable heap = null;
+ Bpl.Constant allocField = null;
+ foreach (Bpl.Declaration 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 == "ref") {
+ refType = dt;
+ }
+ } else if (d is Bpl.Constant) {
+ Bpl.Constant c = (Bpl.Constant)d;
+ if (c.Name == "alloc") {
+ allocField = c;
+ }
+ } else if (d is Bpl.GlobalVariable) {
+ Bpl.GlobalVariable v = (Bpl.GlobalVariable)d;
+ if (v.Name == "$Heap") {
+ heap = v;
+ }
+ }
+ }
+ if (seqTypeCtor == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of type Seq");
+ } 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 (refType == null) {
+ Console.WriteLine("Error: Dafny prelude is missing declaration of type ref");
+ } 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 {
+ return new PredefinedDecls(refType, seqTypeCtor, fieldNameType, heap, classNameType, allocField);
+ }
+ return null;
+ }
+
+ static Bpl.Program ReadPrelude() {
+ //using (System.IO.Stream stream = (!) 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 = (!) System.IO.Path.GetDirectoryName((!)System.Reflection.Assembly.GetExecutingAssembly().Location);
+ string! preludePath = System.IO.Path.Combine(codebase, "DafnyPrelude.bpl");
+ 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! program) {
+ 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 (ClassDecl c in program.Classes) {
+ AddClassMembers(c);
+ }
+ foreach (ClassDecl c in program.Classes) {
+ foreach (MemberDecl member in c.Members) {
+ if (member is Method) {
+ Method m = (Method)member;
+ if (m.Body != null) {
+ AddMethodImpl(m);
+ }
+ } else if (member is Function) {
+ Function f = (Function)member;
+ AddFrameAxiom(f);
+ // TODO: also need a well-formedness check for the preconditions
+ if (f.Body != null) {
+ AddWellformednessCheck(f);
+ }
+ }
+ }
+ }
+ return sink;
+ }
+
+ void AddClassMembers(ClassDecl! c)
+ requires sink != null && predef != null;
+ {
+ sink.TopLevelDeclarations.Add(GetClass(c));
+
+ foreach (MemberDecl member in c.Members) {
+ if (member is Field) {
+ Field f = (Field)member;
+ Bpl.Constant fc = GetField(f);
+ sink.TopLevelDeclarations.Add(fc);
+
+ AddAllocationAxiom(f);
+
+ } else if (member is Function) {
+ Function f = (Function)member;
+ Bpl.Function useFunc;
+ Bpl.Function func = GetFunction(f, out useFunc);
+ sink.TopLevelDeclarations.Add(func);
+ if (useFunc != null) {
+ sink.TopLevelDeclarations.Add(useFunc);
+ }
+ if (f.Body != null) {
+ ExpressionTranslator etran = new ExpressionTranslator(this, predef, f.tok);
+
+ // axiom (forall $Heap, formals :: f#use(formals) && this != null && $IsHeap($Heap) && Pre($Heap,formals) ==> f(formals) == body)
+ // The antecedent f#use(formals) is included only if the function has been declared as 'use'.
+ // 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, "$Heap", predef.HeapType));
+ formals.Add(bv);
+ args.Add(new Bpl.IdentifierExpr(f.tok, bv));
+ Bpl.BoundVariable bvThis = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "this", predef.RefType));
+ formals.Add(bvThis);
+ Bpl.Expr 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.Expr ante = Bpl.Expr.And(
+ Bpl.Expr.Neq(bvThisIdExpr, predef.Null),
+ FunctionCall(f.tok, BuiltinFunction.IsGoodHeap, null, etran.HeapExpr));
+ foreach (Expression req in f.Req) {
+ ante = Bpl.Expr.And(ante, etran.TrExpr(req));
+ }
+ Bpl.FunctionCall funcID = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullName, TrType(f.ResultType)));
+ Bpl.Expr funcAppl = new Bpl.NAryExpr(f.tok, funcID, args);
+ Bpl.Trigger tr;
+ if (f.Use) {
+ Bpl.FunctionCall useID = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullName + "#use", TrType(f.ResultType)));
+ Bpl.Expr useAppl = new Bpl.NAryExpr(f.tok, useID, args);
+ ante = Bpl.Expr.And(useAppl, ante);
+ tr = new Bpl.Trigger(f.tok, true, new Bpl.ExprSeq(useAppl));
+ } else {
+ tr = new Bpl.Trigger(f.tok, true, new Bpl.ExprSeq(funcAppl));
+ }
+ Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(f.TypeArgs);
+ Bpl.Expr ax = new Bpl.ForallExpr(f.tok, typeParams, formals, null, tr, Bpl.Expr.Imp(ante, Bpl.Expr.Eq(funcAppl, etran.TrExpr(f.Body))));
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(f.tok, ax));
+ }
+
+ } else if (member is Method) {
+ Method m = (Method)member;
+ Bpl.Procedure proc = GetMethod(m);
+ sink.TopLevelDeclarations.Add(proc);
+
+ } else {
+ assert false; // unexpected member
+ }
+ }
+ }
+
+ void AddAllocationAxiom(Field! f)
+ requires sink != null && predef != null;
+ {
+ if (f.Type is BoolType || f.Type is IntType || f.Type.IsTypeParameter) {
+ return;
+ }
+
+ 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 = Bpl.Expr.SelectTok(f.tok, h, o, new Bpl.IdentifierExpr(f.tok, GetField(f)));
+ // $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));
+
+ if (f.Type is SetType) {
+ SetType st = (SetType)f.Type;
+ if (st.Arg.IsRefType) {
+ // axiom (forall h: [ref, Field x]x, o: ref, t: ref ::
+ // { h[o,f][t] }
+ // $IsGoodHeap(h) && o != null && h[o,alloc] && h[o,f][t] ==> t == null || h[t, alloc]);
+ Bpl.BoundVariable tVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$t", predef.RefType));
+ Bpl.Expr t = new Bpl.IdentifierExpr(f.tok, tVar);
+ Bpl.Expr oDotFsubT = Bpl.Expr.SelectTok(f.tok, oDotF, t);
+
+ Bpl.Trigger tr = new Bpl.Trigger(f.tok, true, new Bpl.ExprSeq(oDotFsubT));
+
+ Bpl.Expr goodRef = etran.GoodRef(f.tok, t, st.Arg);
+ Bpl.Expr body = Bpl.Expr.Imp(Bpl.Expr.And(ante, oDotFsubT), Bpl.Expr.Or(Bpl.Expr.Eq(t, predef.Null), goodRef));
+ Bpl.Expr ax = new Bpl.ForallExpr(f.tok, new Bpl.VariableSeq(hVar, oVar, tVar), tr, body);
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(f.tok, ax));
+ } else {
+ // TODO: should also handle sets of sets, etc. This will probably require some extra predicates, and these predicates may even replace the above treatment of sets.
+ }
+
+ } else if (f.Type is SeqType) {
+ SeqType st = (SeqType)f.Type;
+ if (st.Arg.IsRefType) {
+ // axiom (forall h: [ref, Field x]x, o: ref, i: int ::
+ // { Seq#Index(h[o,f], i) }
+ // $IsGoodHeap(h) && o != null && h[o,alloc] && 0 <= i && i < Seq#Length(h[o,f]) ==> Seq#Index(h[o,f], i) == null || h[Seq#Index(h[o,f], i), alloc]);
+ Bpl.BoundVariable iVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$i", Bpl.Type.Int));
+ Bpl.Expr i = new Bpl.IdentifierExpr(f.tok, iVar);
+ Bpl.Expr oDotFsubI = FunctionCall(f.tok, BuiltinFunction.SeqIndex, predef.RefType, oDotF, i);
+
+ Bpl.Expr range = InSeqRange(f.tok, i, oDotF, null, false);
+
+ Bpl.Trigger tr = new Bpl.Trigger(f.tok, true, new Bpl.ExprSeq(oDotFsubI));
+
+ Bpl.Expr goodRef = etran.GoodRef(f.tok, oDotFsubI, st.Arg);
+ Bpl.Expr body = Bpl.Expr.Imp(Bpl.Expr.And(ante, range), Bpl.Expr.Or(Bpl.Expr.Eq(oDotFsubI, predef.Null), goodRef));
+ Bpl.Expr ax = new Bpl.ForallExpr(f.tok, new Bpl.VariableSeq(hVar, oVar, iVar), tr, body);
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(f.tok, ax));
+ } else {
+ // TODO: should also handle sequences of sequences, etc. This will probably require some extra predicates, and these predicates may even replace the above treatment of sequences.
+ }
+
+ } else {
+ // reference type:
+ // axiom (forall h: [ref, Field x]x, o: ref ::
+ // { h[o,f] }
+ // $IsGoodHeap(h) && o != null && h[o,alloc] ==> h[o,f] == null || h[h[o,f], alloc]);
+ Bpl.Trigger tr = new Bpl.Trigger(f.tok, true, new Bpl.ExprSeq(oDotF));
+
+ Bpl.Expr goodRef = etran.GoodRef(f.tok, oDotF, f.Type);
+ Bpl.Expr body = Bpl.Expr.Imp(ante, Bpl.Expr.Or(Bpl.Expr.Eq(oDotF, predef.Null), goodRef));
+ 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(Token! tok, Bpl.Expr! index, Bpl.Expr! seq, Bpl.Expr lowerBound, bool includeUpperBound) {
+ if (lowerBound == null) {
+ lowerBound = Bpl.Expr.Literal(0);
+ }
+ Bpl.Expr lower = Bpl.Expr.Le(lowerBound, index);
+ Bpl.Expr upper;
+ if (includeUpperBound) {
+ upper = Bpl.Expr.Le(index, FunctionCall(tok, BuiltinFunction.SeqLength, null, seq));
+ } else {
+ upper = Bpl.Expr.Lt(index, FunctionCall(tok, BuiltinFunction.SeqLength, null, seq));
+ }
+ return Bpl.Expr.And(lower, upper);
+ }
+
+ Method currentMethod = null; // the method whose implementation is currently being translated
+ int loopHeapVarCount = 0;
+ int otherTmpVarCount = 0;
+ Bpl.IdentifierExpr _phvie = null;
+ Bpl.IdentifierExpr! GetPrevHeapVar_IdExpr(Token! tok, Bpl.VariableSeq! locals) // local variable that's shared between statements that need it
+ requires predef != null;
+ {
+ if (_phvie == null) {
+ // the "tok" of the first request for this variable is the one we use
+ Bpl.LocalVariable prevHeapVar = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, "$prevHeap", predef.HeapType));
+ locals.Add(prevHeapVar);
+ _phvie = new Bpl.IdentifierExpr(tok, prevHeapVar);
+ }
+ return _phvie;
+ }
+ Bpl.IdentifierExpr _nwie = null;
+ Bpl.IdentifierExpr! GetNewVar_IdExpr(Token! tok, Bpl.VariableSeq! locals) // local variable that's shared between statements that need it
+ requires predef != null;
+ {
+ if (_nwie == null) {
+ // the "tok" of the first request for this variable is the one we use
+ Bpl.LocalVariable nwVar = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, "$nw", predef.RefType)); // important: no where clause (that's why we're going through the trouble of setting of this variable in the first place)
+ locals.Add(nwVar);
+ _nwie = new Bpl.IdentifierExpr(tok, nwVar);
+ }
+ return _nwie;
+ }
+
+ void AddMethodImpl(Method! m)
+ requires sink != null && predef != null && m.Body != null;
+ requires currentMethod == null && loopHeapVarCount == 0 && _phvie == null && _nwie == null;
+ ensures currentMethod == null && loopHeapVarCount == 0 && _phvie == null && _nwie == null;
+ {
+ Bpl.Procedure proc = GetMethod(m);
+ currentMethod = m;
+
+ Bpl.VariableSeq localVariables = new Bpl.VariableSeq();
+
+ Bpl.StmtList stmts = TrStmt2StmtList(m.Body, localVariables, new ExpressionTranslator(this, predef, m.tok));
+ Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(m.TypeArgs);
+ Bpl.Implementation impl = new Bpl.Implementation(m.tok, proc.Name,
+ typeParams,
+ Bpl.Formal.StripWhereClauses(proc.InParams),
+ Bpl.Formal.StripWhereClauses(proc.OutParams),
+ localVariables, stmts);
+ sink.TopLevelDeclarations.Add(impl);
+
+ currentMethod = null;
+ loopHeapVarCount = 0;
+ otherTmpVarCount = 0;
+ _phvie = null;
+ _nwie = null;
+ }
+
+ /// <summary>
+ /// Generates:
+ /// axiom (forall h0: [ref, Field x]x, h1: [ref, Field x]x, formals... ::
+ /// { F(h0,formals), F(h1,formals) }
+ /// (forall(alpha) o: ref, f: Field alpha :: o != null AND h0[o,alloc] AND o in reads clause of formals in h0 IMPLIES h0[o,f] == h1[o,f]) AND
+ /// (forall(alpha) o: ref, f: Field alpha :: o != null AND h1[o,alloc] AND o in reads clause of formals in h1 IMPLIES h0[o,f] == h1[o,f])
+ /// IMPLIES
+ /// F(h0,formals) == F(h1,formals)
+ /// );
+ /// </summary>
+ void AddFrameAxiom(Function! f)
+ 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.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 oNotNullAlloced0 = Bpl.Expr.And(oNotNull, etran0.IsAlloced(f.tok, o));
+ Bpl.Expr oNotNullAlloced1 = Bpl.Expr.And(oNotNull, etran1.IsAlloced(f.tok, o));
+ Bpl.Expr unchanged = Bpl.Expr.Eq(Bpl.Expr.SelectTok(f.tok, h0, o, field), Bpl.Expr.SelectTok(f.tok, h1, o, field));
+
+ Bpl.Expr r0 = InRWClause(f.tok, o, f.Reads, etran0);
+ Bpl.Expr r1 = InRWClause(f.tok, o, f.Reads, etran1);
+ Bpl.Expr q0 = new Bpl.ForallExpr(f.tok, new Bpl.TypeVariableSeq(alpha), new Bpl.VariableSeq(oVar, fieldVar),
+ Bpl.Expr.Imp(Bpl.Expr.And(oNotNullAlloced0, r0), unchanged));
+ Bpl.Expr q1 = new Bpl.ForallExpr(f.tok, new Bpl.TypeVariableSeq(alpha), new Bpl.VariableSeq(oVar, fieldVar),
+ Bpl.Expr.Imp(Bpl.Expr.And(oNotNullAlloced1, r1), 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();
+ 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(h0Var); bvars.Add(h1Var); bvars.Add(thVar);
+ f0args.Add(h0); f0args.Add(th);
+ f1args.Add(h1); f1args.Add(th);
+ 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.FunctionCall fn = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullName, TrType(f.ResultType)));
+ 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(F0, 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(q0, q1), eq));
+ sink.TopLevelDeclarations.Add(new Bpl.Axiom(f.tok, ax, "frame axiom for " + f.FullName));
+ }
+
+ Bpl.Expr! InRWClause(Token! tok, Bpl.Expr! o, List<Expression!>! rw, ExpressionTranslator! etran) {
+ Bpl.Expr disjunction = null;
+ foreach (Expression e in rw) {
+ Bpl.Expr disjunct;
+ if (e.Type is SetType) {
+ // old(e)[o]
+ disjunct = etran.TrInSet(tok, o, e, null);
+ } else if (e.Type is SeqType) {
+ // (exists i: int :: 0 <= i && i < Seq#Length(old(e)) ==> Seq#Index(old(e),i) == 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), null, false);
+ Bpl.Expr XsubI = FunctionCall(tok, BuiltinFunction.SeqIndex, TrType(((SeqType)e.Type).Arg), 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.Imp(iBounds, Bpl.Expr.Eq(XsubI, o)));
+ } else {
+ // o == old(e)
+ disjunct = Bpl.Expr.Eq(o, etran.TrExpr(e));
+ }
+ disjunct = Bpl.Expr.And(IsTotal(e, etran), disjunct);
+ 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)
+ requires sink != null && predef != null;
+ requires f.Body != null;
+ {
+ ExpressionTranslator etran = new ExpressionTranslator(this, predef, f.tok);
+ Bpl.VariableSeq inParams = new Bpl.VariableSeq();
+ 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.GetThisType(f.tok, (!)f.EnclosingClass)));
+ 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);
+ 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);
+ Bpl.Procedure proc = new Bpl.Procedure(f.tok, "CheckWellformed$$" + f.FullName, typeParams, inParams, new Bpl.VariableSeq(),
+ new Bpl.RequiresSeq(), new Bpl.IdentifierExprSeq(), new Bpl.EnsuresSeq());
+ sink.TopLevelDeclarations.Add(proc);
+
+ Bpl.VariableSeq localVariables = new Bpl.VariableSeq();
+ Bpl.StmtListBuilder builder = new Bpl.StmtListBuilder();
+
+ CheckWellformed(f.Body, f, Position.Positive, builder, etran);
+
+ Bpl.Implementation impl = new Bpl.Implementation(f.tok, proc.Name,
+ typeParams,
+ Bpl.Formal.StripWhereClauses(proc.InParams),
+ Bpl.Formal.StripWhereClauses(proc.OutParams),
+ localVariables, builder.Collect(f.tok));
+ sink.TopLevelDeclarations.Add(impl);
+ }
+
+ Bpl.Expr! IsTotal(Expression! expr, ExpressionTranslator! etran)
+ requires predef != null;
+ {
+ if (expr is LiteralExpr || expr is ThisExpr || expr is IdentifierExpr) {
+ 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 {
+ return Bpl.Expr.And(IsTotal(e.Obj, etran), Bpl.Expr.Neq(etran.TrExpr(e.Obj), predef.Null));
+ }
+ } else if (expr is SeqSelectExpr) {
+ SeqSelectExpr e = (SeqSelectExpr)expr;
+ 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, 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, e0, true));
+ }
+ return total;
+ } else if (expr is FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)expr;
+ Bpl.Expr r = IsTotal(e.Receiver, etran);
+ if (!(e.Receiver is ThisExpr)) {
+ r = BplAnd(r, Bpl.Expr.Neq(etran.TrExpr(e.Receiver), predef.Null));
+ }
+ // TODO: check reads permissions and check preconditions
+ return BplAnd(r, IsTotal(e.Args, etran));
+ } else if (expr is OldExpr) {
+ OldExpr e = (OldExpr)expr;
+ return new Bpl.OldExpr(expr.tok, 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;
+ return IsTotal(e.E, etran);
+ } 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:
+ 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 QuantifierExpr) {
+ QuantifierExpr e = (QuantifierExpr)expr;
+ Bpl.Expr total = IsTotal(e.Body, etran);
+ if (total != Bpl.Expr.True) {
+ Bpl.VariableSeq bvars = new Bpl.VariableSeq();
+ foreach (BoundVar bv in e.BoundVars) {
+ bvars.Add(new Bpl.BoundVariable(bv.tok, new Bpl.TypedIdent(bv.tok, bv.UniqueName, TrType(bv.Type))));
+ }
+ total = new Bpl.ForallExpr(expr.tok, bvars, total);
+ }
+ return total;
+ } 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 {
+ assert false; // unexpected expression
+ }
+ }
+
+ Bpl.Expr! IsTotal(List<Expression!>! exprs, ExpressionTranslator! etran) {
+ Bpl.Expr total = Bpl.Expr.True;
+ foreach (Expression e in exprs) {
+ total = BplAnd(total, IsTotal(e, etran));
+ }
+ return total;
+ }
+
+ Bpl.Expr! BplAnd(Bpl.Expr! a, Bpl.Expr! b) {
+ if (a == Bpl.Expr.True) {
+ return b;
+ } else if (b == Bpl.Expr.True) {
+ return a;
+ } else {
+ return Bpl.Expr.And(a, b);
+ }
+ }
+
+ enum Position { Positive, Negative, Neither }
+ Position Negate(Position pos) {
+ switch (pos) {
+ case Position.Positive: return Position.Negative;
+ case Position.Negative: return Position.Positive;
+ case Position.Neither: return Position.Neither;
+ default: assert false; // unexpected Position
+ }
+ }
+
+ void CheckNonNull(Token! tok, Expression! e, Bpl.StmtListBuilder! builder, ExpressionTranslator! etran)
+ 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"));
+ }
+ }
+
+ void CheckWellformed(Expression! expr, Function func, Position pos, Bpl.StmtListBuilder! builder, ExpressionTranslator! etran) {
+ if (expr is LiteralExpr || expr is ThisExpr || expr is IdentifierExpr) {
+ // always allowed
+ } else if (expr is DisplayExpression) {
+ DisplayExpression e = (DisplayExpression)expr;
+ foreach (Expression el in e.Elements) {
+ CheckWellformed(el, func, Position.Neither, builder, etran);
+ }
+ } else if (expr is FieldSelectExpr) {
+ FieldSelectExpr e = (FieldSelectExpr)expr;
+ CheckWellformed(e.Obj, func, Position.Neither, builder, etran);
+ CheckNonNull(expr.tok, e.Obj, builder, etran);
+ if (func != null) {
+ builder.Add(Assert(expr.tok, InRWClause(expr.tok, etran.TrExpr(e.Obj), func.Reads, etran), "insufficient reads clause to read field"));
+ }
+ } else if (expr is SeqSelectExpr) {
+ SeqSelectExpr e = (SeqSelectExpr)expr;
+ CheckWellformed(e.Seq, func, Position.Neither, builder, etran);
+ Bpl.Expr seq = etran.TrExpr(e.Seq);
+ Bpl.Expr e0 = null;
+ if (e.E0 != null) {
+ e0 = etran.TrExpr(e.E0);
+ CheckWellformed(e.E0, func, Position.Neither, builder, etran);
+ builder.Add(new Bpl.AssertCmd(expr.tok, InSeqRange(expr.tok, e0, seq, null, !e.SelectOne)));
+ }
+ if (e.E1 != null) {
+ CheckWellformed(e.E1, func, Position.Neither, builder, etran);
+ builder.Add(new Bpl.AssertCmd(expr.tok, InSeqRange(expr.tok, etran.TrExpr(e.E1), seq, e0, true)));
+ }
+ } else if (expr is FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)expr;
+ CheckWellformed(e.Receiver, func, Position.Neither, builder, etran);
+ CheckNonNull(expr.tok, e.Receiver, builder, etran);
+ foreach (Expression arg in e.Args) {
+ CheckWellformed(arg, func, Position.Neither, builder, etran);
+ }
+ // TODO: check wellformedness of call (call.reads is subset of reads, and either e.Function returns a boolean and is in a positive position, or e.Function returns something else and the subset is a proper subset)
+ // TODO: and check preconditions of call
+ } else if (expr is OldExpr || expr is FreshExpr) {
+ assert false; // unexpected expression in function body
+ } else if (expr is UnaryExpr) {
+ UnaryExpr e = (UnaryExpr)expr;
+ CheckWellformed(e.E, func, e.Op == UnaryExpr.Opcode.Not ? Negate(pos) : Position.Neither, builder, etran);
+ } else if (expr is BinaryExpr) {
+ BinaryExpr e = (BinaryExpr)expr;
+ CheckWellformed(e.E0, func, e.ResolvedOp == BinaryExpr.ResolvedOpcode.Imp ? Negate(pos) : pos, builder, etran);
+
+ switch (e.ResolvedOp) {
+ case BinaryExpr.ResolvedOpcode.And:
+ case BinaryExpr.ResolvedOpcode.Imp:
+ {
+ Bpl.StmtListBuilder b = new Bpl.StmtListBuilder();
+ CheckWellformed(e.E1, func, pos, 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, func, pos, b, etran);
+ builder.Add(new Bpl.IfCmd(expr.tok, Bpl.Expr.Not(etran.TrExpr(e.E0)), b.Collect(expr.tok), null, null));
+ }
+ break;
+ default:
+ CheckWellformed(e.E1, func, pos, builder, etran);
+ break;
+ }
+
+ } else if (expr is QuantifierExpr) {
+#if TODO
+ QuantifierExpr e = (QuantifierExpr)expr;
+ Bpl.Expr total = IsTotal(e.Body, etran);
+ if (total != Bpl.Expr.True) {
+ Bpl.VariableSeq bvars = new Bpl.VariableSeq();
+ foreach (BoundVar bv in e.BoundVars) {
+ // TODO: the following needs to take into account name clashes, for which the Boogie rules are different from the Dafny rules
+ bvars.Add(new Bpl.BoundVariable(bv.tok, new Bpl.TypedIdent(bv.tok, bv.UniqueName, TrType(bv.Type))));
+ }
+ if (expr is ForallExpr) {
+ total = new Bpl.ForallExpr(expr.tok, bvars, total);
+ } else {
+ total = new Bpl.ExistsExpr(expr.tok, bvars, total);
+ }
+ }
+ return total;
+#endif
+ } else if (expr is ITEExpr) {
+#if TODO
+ ITEExpr e = (ITEExpr)expr;
+ Bpl.Expr total = IsTotal(e.Test, etran);
+ total = BplAnd(total, IsTotal(e.Thn, etran));
+ total = BplAnd(total, IsTotal(e.Els, etran));
+ return total;
+#endif
+ } else {
+ assert false; // unexpected expression
+ }
+ }
+
+ Bpl.Constant! GetClass(ClassDecl! cl)
+ requires predef != null;
+ {
+ Bpl.Constant cc;
+ if (classes.TryGetValue(cl, out cc)) {
+ assert cc != null;
+ } else {
+ // TODO: make the following a function when the class has type parameters, so that different instantiations
+ // of a class can be distinguished
+ cc = new Bpl.Constant(cl.tok, new Bpl.TypedIdent(cl.tok, "class." + cl.Name, predef.ClassNameType), true);
+ classes.Add(cl, cc);
+ }
+ return cc;
+ }
+
+ Bpl.Expr GetTypeExpr(Token! tok, Type! type)
+ 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.bool", predef.ClassNameType);
+ } else if (type is IntType) {
+ return new Bpl.IdentifierExpr(tok, "class.int", predef.ClassNameType);
+ } else if (type is ObjectType) {
+ return new Bpl.IdentifierExpr(tok, "class.object", predef.ClassNameType);
+ } 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.set" : "class.seq", predef.ClassNameType);
+ return FunctionCall(tok, BuiltinFunction.TypeTuple, null, t, a);
+ } else {
+ ClassType ct = (ClassType)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)
+ requires predef != null;
+ {
+ Bpl.Constant fc;
+ if (fields.TryGetValue(f, out fc)) {
+ assert fc != null;
+ } else {
+ Bpl.Type ty = predef.FieldName(f.tok, TrType(f.Type));
+ fc = new Bpl.Constant(f.tok, new Bpl.TypedIdent(f.tok, f.FullName, ty), true);
+ fields.Add(f, fc);
+ }
+ return fc;
+ }
+
+ Bpl.Expr! GetField(FieldSelectExpr! fse)
+ requires fse.Field != null;
+ {
+ return new Bpl.IdentifierExpr(fse.tok, GetField(fse.Field));
+ }
+
+ Bpl.Function! GetFunction(Function! f, out Bpl.Function useF)
+ requires predef != null;
+ {
+ useF = null;
+ Bpl.Function func;
+ if (functions.TryGetValue(f, out func)) {
+ assert func != null;
+ } else {
+ 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));
+ 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);
+ func = new Bpl.Function(f.tok, f.FullName, typeParams, args, res);
+
+ functions.Add(f, func);
+
+ if (f.Use) {
+ Bpl.Formal boolRes = new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, Bpl.TypedIdent.NoName, Bpl.Type.Bool), false);
+ useF = new Bpl.Function(f.tok, f.FullName + "#use", args, boolRes);
+ }
+ }
+ return func;
+ }
+
+ Bpl.Procedure! GetMethod(Method! m)
+ requires predef != null;
+ {
+ Bpl.Procedure proc;
+ if (methods.TryGetValue(m, out proc)) {
+ assert proc != null;
+ } else {
+ ExpressionTranslator etran = new ExpressionTranslator(this, predef, m.tok);
+
+ Bpl.VariableSeq inParams = new Bpl.VariableSeq();
+ Bpl.VariableSeq outParams = new Bpl.VariableSeq();
+ Bpl.Expr wh = Bpl.Expr.And(
+ Bpl.Expr.Neq(new Bpl.IdentifierExpr(m.tok, "this", predef.RefType), predef.Null),
+ etran.GoodRef(m.tok, new Bpl.IdentifierExpr(m.tok, "this", predef.RefType), Resolver.GetThisType(m.tok, (!)m.EnclosingClass)));
+ Bpl.Formal thVar = new Bpl.Formal(m.tok, new Bpl.TypedIdent(m.tok, "this", predef.RefType, wh), true);
+ inParams.Add(thVar);
+ foreach (Formal p in m.Ins) {
+ Bpl.Type varType = TrType(p.Type);
+ 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));
+ }
+ foreach (Formal p in m.Outs) {
+ Bpl.Type varType = TrType(p.Type);
+ 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));
+ }
+
+ Bpl.RequiresSeq req = new Bpl.RequiresSeq();
+ Bpl.IdentifierExprSeq mod = new Bpl.IdentifierExprSeq();
+ Bpl.EnsuresSeq ens = new Bpl.EnsuresSeq();
+ mod.Add(etran.HeapExpr);
+ string comment = "user-defined preconditions";
+ foreach (MaybeFreeExpression p in m.Req) {
+ Bpl.RequiresSeq pieces = new Bpl.RequiresSeq();
+ if (!p.IsFree) {
+ foreach (Expression se in SplitExpr(p.E, true)) {
+ pieces.Add(Requires(se.tok, false, etran.TrExpr(se), null, null));
+ }
+ }
+ if (pieces.Length == 1) {
+ // add 1 checked precondition (the whole thing)
+ req.Add(Requires(p.E.tok, false, etran.TrExpr(p.E), null, comment));
+ } else {
+ // add 1 free precondition, followed by each piece (if any) as a checked precondition
+ req.Add(Requires(p.E.tok, true, etran.TrExpr(p.E), null, comment));
+ req.AddRange(pieces);
+ }
+ comment = null;
+ }
+ comment = "user-defined postconditions";
+ foreach (MaybeFreeExpression p in m.Ens) {
+ Bpl.EnsuresSeq pieces = new Bpl.EnsuresSeq();
+ if (!p.IsFree) {
+ foreach (Expression se in SplitExpr(p.E, true)) {
+ pieces.Add(Ensures(se.tok, false, etran.TrExpr(se), null, null));
+ }
+ }
+ if (pieces.Length == 1) {
+ ens.Add(Ensures(p.E.tok, false, etran.TrExpr(p.E), comment, null));
+ } else {
+ ens.Add(Ensures(p.E.tok, true, etran.TrExpr(p.E), comment, null));
+ ens.AddRange(pieces);
+ }
+ comment = null;
+ }
+
+ foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(m.tok, m, etran.Old, etran)) {
+ ens.Add(Ensures(tri.tok, tri.IsFree, tri.Expr, tri.ErrorMessage, tri.Comment));
+ }
+
+ Bpl.TypeVariableSeq typeParams = TrTypeParamDecls(m.TypeArgs);
+ proc = new Bpl.Procedure(m.tok, m.FullName, typeParams, inParams, outParams, req, mod, ens);
+
+ methods.Add(m, proc);
+ }
+ return proc;
+ }
+
+ class BoilerplateTriple { // a triple that is now a quintuple
+ public readonly Token! tok;
+ public readonly bool IsFree;
+ public readonly Bpl.Expr! Expr;
+ public readonly string ErrorMessage;
+ invariant IsFree || ErrorMessage != null;
+ public readonly string Comment;
+ public BoilerplateTriple(Token! tok, bool isFree, Bpl.Expr! expr, string errorMessage, string comment)
+ 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, 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 etran.Old denotes S0.
+ /// </summary>
+ List<BoilerplateTriple!>! GetTwoStateBoilerplate(Token! tok, Method! method, ExpressionTranslator! etranPre, ExpressionTranslator! etran)
+ {
+ List<BoilerplateTriple!> boilerplate = new List<BoilerplateTriple!>();
+
+ boilerplate.Add(new BoilerplateTriple(tok, false, FrameCondition(tok, method.Mod, etranPre, etran), "frame condition does not hold", "frame condition"));
+
+ // free specifications
+ 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 freame condition:
+ /// S0. the beginning of the method, 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 etran.Old denotes S0.
+ /// </summary>
+ Bpl.Expr! FrameCondition(Token! tok, List<Expression!>! modifiesClause, ExpressionTranslator! etranPre, ExpressionTranslator! etran)
+ requires predef != 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 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 = Bpl.Expr.SelectTok(tok, etran.HeapExpr, o, f);
+ Bpl.Expr preHeapOF = Bpl.Expr.SelectTok(tok, etranPre.HeapExpr, o, f);
+ Bpl.Expr ante = Bpl.Expr.And(Bpl.Expr.Neq(o, predef.Null), etran.Old.IsAlloced(tok, o));
+ Bpl.Expr consequent = Bpl.Expr.Eq(heapOF, preHeapOF);
+
+ consequent = Bpl.Expr.Or(consequent, InRWClause(tok, o, modifiesClause, etran.Old));
+
+ 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)
+ requires predef != 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.IsTypeParameter) {
+ return predef.RefType;
+ } else if (type.IsRefType) {
+ // object and class types translate to ref
+ return predef.RefType;
+ } else if (type is SetType) {
+ return new Bpl.MapType(Token.NoToken, new Bpl.TypeVariableSeq(), new Bpl.TypeSeq(TrType(((SetType)type).Arg)), Bpl.Type.Bool);
+ } else if (type is SeqType) {
+ return predef.SeqType(Token.NoToken, TrType(((SeqType)type).Arg));
+ } else {
+ assert false; // unexpected type
+ }
+ }
+
+ Bpl.TypeVariableSeq! TrTypeParamDecls(List<TypeParameter!>! tps)
+ {
+ Bpl.TypeVariableSeq typeParams = new Bpl.TypeVariableSeq();
+ return typeParams;
+ }
+
+ // ----- Statement ----------------------------------------------------------------------------
+
+ Bpl.AssertCmd! Assert(Token! tok, Bpl.Expr! condition, string! errorMessage)
+ {
+ Bpl.AssertCmd cmd = new Bpl.AssertCmd(tok, condition);
+ cmd.ErrorData = "Error: " + errorMessage;
+ return cmd;
+ }
+
+ Bpl.Ensures! Ensures(Token! tok, bool free, Bpl.Expr! condition, string errorMessage, string comment)
+ {
+ Bpl.Ensures ens = new Bpl.Ensures(tok, free, condition, comment);
+ if (errorMessage != null) {
+ ens.ErrorData = errorMessage;
+ }
+ return ens;
+ }
+
+ Bpl.Requires! Requires(Token! tok, bool free, Bpl.Expr! condition, string errorMessage, string comment)
+ {
+ Bpl.Requires req = new Bpl.Requires(tok, free, condition, comment);
+ if (errorMessage != null) {
+ req.ErrorData = errorMessage;
+ }
+ return req;
+ }
+
+ Bpl.StmtList! TrStmt2StmtList(Statement! block, Bpl.VariableSeq! locals, ExpressionTranslator! etran)
+ requires currentMethod != null && predef != null;
+ {
+ Bpl.StmtListBuilder builder = new Bpl.StmtListBuilder();
+ 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)
+ requires currentMethod != null && predef != null;
+ {
+ if (stmt is AssertStmt) {
+ AddComment(builder, stmt, "assert statement");
+ AssertStmt s = (AssertStmt)stmt;
+ int pieces = 0;
+ foreach (Expression p in SplitExpr(s.Expr, true)) {
+ builder.Add(Assert(stmt.Tok, IsTotal(p, etran), "assert condition must be well defined")); // totality check
+ builder.Add(new Bpl.AssertCmd(stmt.Tok, etran.TrExpr(p)));
+ pieces++;
+ }
+ if (2 <= pieces) {
+ 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;
+ builder.Add(Assert(stmt.Tok, IsTotal(s.Expr, etran), "assume condition must be well defined")); // totality check
+ builder.Add(new Bpl.AssumeCmd(stmt.Tok, etran.TrExpr(s.Expr)));
+ } else if (stmt is UseStmt) {
+ AddComment(builder, stmt, "use statement");
+ UseStmt s = (UseStmt)stmt;
+ builder.Add(Assert(stmt.Tok, IsTotal(s.Expr, etran), "use expression must be well defined")); // totality check
+ builder.Add(new Bpl.AssumeCmd(stmt.Tok, (s.EvalInOld ? etran.Old : etran).TrUseExpr(s.FunctionCallExpr)));
+ } else if (stmt is LabelStmt) {
+ AddComment(builder, stmt, "label statement"); // TODO: ouch, comments probably mess up what the label labels in the Boogie program
+ builder.AddLabelCmd(((LabelStmt)stmt).Label);
+ } else if (stmt is BreakStmt) {
+ AddComment(builder, stmt, "break statement");
+ builder.Add(new Bpl.BreakCmd(stmt.Tok, ((BreakStmt)stmt).TargetLabel)); // TODO: handle name clashes of labels
+ } else if (stmt is ReturnStmt) {
+ AddComment(builder, stmt, "return statement");
+ builder.Add(new Bpl.ReturnCmd(stmt.Tok));
+ } else if (stmt is AssignStmt) {
+ AddComment(builder, stmt, "assignment statement");
+ AssignStmt s = (AssignStmt)stmt;
+ TrAssignment(stmt.Tok, s.Lhs, 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);
+ if (s.Rhs != null) {
+ IdentifierExpr ide = new IdentifierExpr(stmt.Tok, var.Name); // allocate an expression for the assignment LHS...
+ ide.Var = s; ide.Type = s.Type; // ... and resolve it right here
+ TrAssignment(stmt.Tok, ide, s.Rhs, builder, locals, etran);
+ }
+
+ } else if (stmt is CallStmt) {
+ AddComment(builder, stmt, "call statement");
+ CallStmt s = (CallStmt)stmt;
+ Bpl.ExprSeq ins = new Bpl.ExprSeq();
+ ins.Add(etran.TrExpr(s.Receiver));
+ for (int i = 0; i < s.Args.Count; i++) {
+ Expression e = s.Args[i];
+ Type t = ((!)s.Method).Ins[i].Type;
+ ins.Add(etran.CondApplyBox(stmt.Tok, etran.TrExpr(e), (!)e.Type, t));
+ }
+ Bpl.IdentifierExprSeq outs = new Bpl.IdentifierExprSeq();
+ List<Bpl.IdentifierExpr> tmpOuts = new List<Bpl.IdentifierExpr>(s.Lhs.Count);
+ for (int i = 0; i < s.Lhs.Count; i++) {
+ Expression e = s.Lhs[i];
+ if (ExpressionTranslator.ModeledAsRef(((!)s.Method).Outs[i].Type) && !ExpressionTranslator.ModeledAsRef((!)e.Type)) {
+ // we need an Unbox
+ Bpl.LocalVariable var = new Bpl.LocalVariable(stmt.Tok, new Bpl.TypedIdent(stmt.Tok, "$tmp#" + otherTmpVarCount, predef.RefType));
+ otherTmpVarCount++;
+ locals.Add(var);
+ Bpl.IdentifierExpr varIdE = new Bpl.IdentifierExpr(stmt.Tok, var.Name, predef.RefType);
+ tmpOuts.Add(varIdE);
+ outs.Add(varIdE);
+ } else {
+ tmpOuts.Add(null);
+ outs.Add(etran.TrExpr(e));
+ }
+ }
+
+ Bpl.CallCmd call = new Bpl.CallCmd(stmt.Tok, ((!)s.Method).FullName, ins, outs);
+ builder.Add(call);
+ for (int i = 0; i < s.Lhs.Count; i++) {
+ Bpl.IdentifierExpr tmpVarIdE = tmpOuts[i];
+ if (tmpVarIdE != null) {
+ IdentifierExpr e = s.Lhs[i];
+ // e := UnBox(tmpVar);
+ Bpl.IdentifierExpr lhs = (Bpl.IdentifierExpr)etran.TrExpr(e); // TODO: is this cast always justified?
+ Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(stmt.Tok, lhs, FunctionSpecial(stmt.Tok, BuiltinFunction.Unbox, TrType((!)e.Type), tmpVarIdE));
+ builder.Add(cmd);
+ }
+ }
+
+ } else if (stmt is BlockStmt) {
+ foreach (Statement ss in ((BlockStmt)stmt).Body) {
+ TrStmt(ss, builder, locals, etran);
+ }
+ } else if (stmt is IfStmt) {
+ AddComment(builder, stmt, "if statement");
+ IfStmt s = (IfStmt)stmt;
+ Bpl.Expr guard = s.Guard == null ? null : etran.TrExpr(s.Guard);
+ Bpl.StmtList thn = TrStmt2StmtList(s.Thn, locals, etran);
+ Bpl.StmtList els = null;
+ Bpl.IfCmd elsIf = null;
+ if (s.Els != null) {
+ els = TrStmt2StmtList(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 WhileStmt) {
+ AddComment(builder, stmt, "while statement");
+ WhileStmt s = (WhileStmt)stmt;
+
+ Bpl.LocalVariable preLoopHeapVar = new Bpl.LocalVariable(stmt.Tok, new Bpl.TypedIdent(stmt.Tok, "$PreLoopHeap" + loopHeapVarCount, predef.HeapType));
+ locals.Add(preLoopHeapVar);
+ Bpl.IdentifierExpr preLoopHeap = new Bpl.IdentifierExpr(stmt.Tok, preLoopHeapVar);
+ ExpressionTranslator etranPreLoop = new ExpressionTranslator(this, predef, preLoopHeap);
+ builder.Add(Bpl.Cmd.SimpleAssign(stmt.Tok, preLoopHeap, etran.HeapExpr)); // TODO: does this screw up labeled breaks for this loop?
+
+ Bpl.Expr guard = s.Guard == null ? null : etran.TrExpr(s.Guard);
+ List<Bpl.PredicateCmd!> invariants = new List<Bpl.PredicateCmd!>();
+ foreach (MaybeFreeExpression loopInv in s.Invariants) {
+ int pieces = 0;
+ if (!loopInv.IsFree) {
+ foreach (Expression se in SplitExpr(loopInv.E, true)) {
+ invariants.Add(new Bpl.AssertCmd(se.tok, etran.TrExpr(se)));
+ pieces++;
+ }
+ }
+ if (pieces != 1) {
+ invariants.Add(new Bpl.AssumeCmd(loopInv.E.tok, etran.TrExpr(loopInv.E)));
+ }
+ }
+ foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(stmt.Tok, currentMethod, etranPreLoop, etran)) {
+ if (tri.IsFree) {
+ invariants.Add(new Bpl.AssumeCmd(stmt.Tok, tri.Expr));
+ } else {
+ assert tri.ErrorMessage != null; // follows from BoilerplateTriple invariant
+ invariants.Add(Assert(stmt.Tok, tri.Expr, tri.ErrorMessage));
+ }
+ }
+
+ Bpl.StmtList body;
+ if (s.Decreases.Count == 0) {
+ body = TrStmt2StmtList(s.Body, locals, etran);
+ } else {
+ Bpl.StmtListBuilder loopBodyBuilder = new Bpl.StmtListBuilder();
+
+ List<Bpl.IdentifierExpr!> oldBfs = new List<Bpl.IdentifierExpr!>();
+ int c = 0;
+ foreach (Expression e in s.Decreases) {
+ Bpl.LocalVariable bfVar = new Bpl.LocalVariable(e.tok, new Bpl.TypedIdent(e.tok, "$decr" + loopHeapVarCount + "$" + c, TrType((!)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
+ invariants.Add(Assert(e.tok, IsTotal(e, etran), "decreases expression must be well defined at top of each loop iteration")); // totality check
+ Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(e.tok, bf, etran.TrExpr(e));
+ loopBodyBuilder.Add(cmd);
+
+ c++;
+ }
+ // time for the actual loop body
+ TrStmt(s.Body, loopBodyBuilder, locals, etran);
+ // check definedness of decreases expressions
+ foreach (Expression e in s.Decreases) {
+ // The following totality check implies that the loop invariant stating the same property will hold;
+ // thus, an alternative (perhaps preferable) design would be: add the totality check also just before
+ // the loop, and change the loop invariant to be free.
+ loopBodyBuilder.Add(Assert(e.tok, IsTotal(e, etran), "decreases expression must be well defined at end of loop iteration")); // totality check
+ }
+ // compute eq and less for each component of the lexicographic pair
+ List<Bpl.Expr!> Eq = new List<Bpl.Expr!>();
+ List<Bpl.Expr!> Less = new List<Bpl.Expr!>();
+ for (int i = 0; i < s.Decreases.Count; i++) {
+ Expression e = s.Decreases[i];
+ Bpl.Expr d = etran.TrExpr(e);
+ Bpl.IdentifierExpr bf = oldBfs[i];
+
+ Bpl.Expr less;
+ Bpl.Expr eq;
+ Type ty = (!)e.Type;
+ if (ty is BoolType) {
+ eq = Bpl.Expr.Iff(d, bf);
+ less = Bpl.Expr.And(Bpl.Expr.Not(d), bf);
+ } else if (ty is IntType) {
+ eq = Bpl.Expr.Eq(d, bf);
+ less = Bpl.Expr.Lt(d, bf);
+ } else if (ty is SetType) {
+ eq = FunctionCall(stmt.Tok, BuiltinFunction.SetEqual, null, d, bf);
+ less = etran.ProperSubset(stmt.Tok, d, bf);
+ } else if (ty is SeqType) {
+ Bpl.Expr e0 = FunctionCall(stmt.Tok, BuiltinFunction.SeqLength, null, d);
+ Bpl.Expr e1 = FunctionCall(stmt.Tok, BuiltinFunction.SeqLength, null, bf);
+ eq = Bpl.Expr.Eq(e0, e1);
+ less = Bpl.Expr.Lt(e0, e1);
+ } else {
+ // reference type
+ Bpl.Expr e0 = Bpl.Expr.Neq(d, predef.Null);
+ Bpl.Expr e1 = Bpl.Expr.Neq(bf, predef.Null);
+ eq = Bpl.Expr.Iff(e0, e1);
+ less = Bpl.Expr.And(Bpl.Expr.Not(e0), e1);
+ }
+ Eq.Add(eq);
+ Less.Add(less);
+ }
+ // check: 0 <= old(decreases)
+ // more precisely, for component k of the lexicographic decreases function, check:
+ // 0 <= old(dec(k)) || dec0 < old(dec0) || dec1 < old(dec1) || ... || dec(k-1) < old(dec((k-1) || old(dec(k)) == dec(k)
+ for (int k = 0; k < s.Decreases.Count; k++) {
+ Expression e = s.Decreases[k];
+ // we only need to check lower bound for integers--sets, sequences, booleans, and references all have natural lower bounds
+ if (e.Type is IntType) {
+ Bpl.IdentifierExpr bf = oldBfs[k];
+ Bpl.Expr bounded = Bpl.Expr.Le(Bpl.Expr.Literal(0), bf);
+ for (int i = 0; i < k; i++) {
+ bounded = Bpl.Expr.Or(bounded, Less[i]);
+ }
+ Bpl.Cmd cmd = Assert(e.tok, Bpl.Expr.Or(bounded, Eq[k]), "decreases expression must be bounded below by 0");
+ loopBodyBuilder.Add(cmd);
+ }
+ }
+ // check: decreases < old(decreases)
+ Bpl.Expr decrCheck = null;
+ for (int i = s.Decreases.Count; 0 <= --i; )
+ invariant i != s.Decreases.Count ==> decrCheck != null;
+ {
+ Bpl.Expr less = Less[i];
+ Bpl.Expr eq = Eq[i];
+ if (decrCheck == null) {
+ decrCheck = less;
+ } else {
+ // decrCheck = less || (eq && decrCheck)
+ decrCheck = Bpl.Expr.Or(less, Bpl.Expr.And(eq, decrCheck));
+ }
+ }
+ assert decrCheck != null; // follows from loop invariant and the fact that s.Decreases.Count != 0
+ loopBodyBuilder.Add(Assert(stmt.Tok, decrCheck, "decreases expression might not decrease"));
+
+ body = loopBodyBuilder.Collect(stmt.Tok);
+ }
+
+ builder.Add(new Bpl.WhileCmd(stmt.Tok, guard, invariants, body));
+ loopHeapVarCount++;
+
+ } else if (stmt is ForeachStmt) {
+ AddComment(builder, stmt, "foreach statement");
+ ForeachStmt s = (ForeachStmt)stmt;
+ // assert/assume (forall o: ref :: o in S ==> Expr);
+ // var oldHeap := $Heap;
+ // havoc $Heap;
+ // assume $HeapSucc(oldHeap, $Heap);
+ // assume (forall o: ref, f: Field :: $Heap[o,f] = oldHeap[o,f] || (f = F && o in S));
+ // assume (forall o: ref :: o != null && o in S ==> $Heap[o,F] = RHS[$Heap := oldHeap]);
+ // Note, $Heap[o,alloc] is intentionally omitted from the antecedent of the quantifier in the previous line. That
+ // allocatedness property should hold automatically, because the set/seq quantified is a program expression, which
+ // will have been constructed from allocated objects.
+ // For sets, "o in S" means just that. For sequences, "o in S" is:
+ // (exists i :: { Seq#Index(S,i) } 0 <= i && i < Seq#Length(S) && Seq#Index(S,i) == o)
+
+ Bpl.BoundVariable oVar = new Bpl.BoundVariable(stmt.Tok, new Bpl.TypedIdent(stmt.Tok, s.BoundVar.UniqueName, TrType(s.BoundVar.Type)));
+ Bpl.IdentifierExpr o = new Bpl.IdentifierExpr(stmt.Tok, oVar);
+
+ Bpl.Expr oInS;
+ if (s.Collection.Type is SetType) {
+ oInS = etran.TrInSet(stmt.Tok, o, s.Collection, null);
+ } else {
+ Bpl.BoundVariable iVar = new Bpl.BoundVariable(stmt.Tok, new Bpl.TypedIdent(stmt.Tok, "$i", Bpl.Type.Int));
+ Bpl.IdentifierExpr i = new Bpl.IdentifierExpr(stmt.Tok, iVar);
+ Bpl.Expr S = etran.TrExpr(s.Collection);
+ Bpl.Expr range = InSeqRange(stmt.Tok, i, S, null, false);
+ Bpl.Expr Si = FunctionCall(stmt.Tok, BuiltinFunction.SeqIndex, TrType(((SeqType!)s.Collection.Type).Arg), S, i);
+ Bpl.Trigger tr = new Bpl.Trigger(stmt.Tok, true, new Bpl.ExprSeq(Si));
+ // TODO: in the next line, the == should be replaced by something that understands extensionality, for sets and sequences
+ oInS = new Bpl.ExistsExpr(stmt.Tok, new Bpl.VariableSeq(iVar), tr, Bpl.Expr.And(range, Bpl.Expr.Eq(Si, o)));
+ }
+
+ foreach (PredicateStmt ps in s.BodyPrefix) {
+ int pieces = 0;
+ if (ps is AssertStmt) {
+ foreach (Expression se in SplitExpr(ps.Expr, true)) {
+ Bpl.Expr e = etran.TrExpr(se);
+ Bpl.Expr q = new Bpl.ForallExpr(se.tok, new Bpl.VariableSeq(oVar), Bpl.Expr.Imp(oInS, e));
+ builder.Add(new Bpl.AssertCmd(se.tok, q));
+ pieces++;
+ }
+ }
+ if (pieces != 1) {
+ Bpl.Expr e;
+ if (ps is UseStmt) {
+ UseStmt us = (UseStmt)ps;
+ e = (us.EvalInOld ? etran.Old : etran).TrUseExpr(us.FunctionCallExpr);
+ } else {
+ e = etran.TrExpr(ps.Expr);
+ }
+ Bpl.Expr q = new Bpl.ForallExpr(ps.Expr.tok, new Bpl.VariableSeq(oVar), Bpl.Expr.Imp(oInS, e));
+ builder.Add(new Bpl.AssumeCmd(ps.Expr.tok, q));
+ }
+ }
+
+ Bpl.IdentifierExpr prevHeap = GetPrevHeapVar_IdExpr(stmt.Tok, locals);
+ builder.Add(Bpl.Cmd.SimpleAssign(stmt.Tok, prevHeap, etran.HeapExpr));
+ builder.Add(new Bpl.HavocCmd(stmt.Tok, new Bpl.IdentifierExprSeq((Bpl.IdentifierExpr/*TODO: this cast is rather dubious*/)etran.HeapExpr)));
+ builder.Add(new Bpl.AssumeCmd(stmt.Tok, FunctionCall(stmt.Tok, BuiltinFunction.HeapSucc, null, prevHeap, etran.HeapExpr)));
+
+ // Here comes: assume (forall<alpha> o: ref, f: Field alpha :: $Heap[o,f] = oldHeap[o,f] || (f = F && o in S));
+ Bpl.TypeVariable alpha = new Bpl.TypeVariable(stmt.Tok, "alpha");
+ Bpl.BoundVariable fVar = new Bpl.BoundVariable(stmt.Tok, new Bpl.TypedIdent(stmt.Tok, "$f", predef.FieldName(stmt.Tok, alpha)));
+ Bpl.IdentifierExpr f = new Bpl.IdentifierExpr(stmt.Tok, fVar);
+ Bpl.Expr heapOF = Bpl.Expr.SelectTok(stmt.Tok, etran.HeapExpr, o, f);
+ Bpl.Expr oldHeapOF = Bpl.Expr.SelectTok(stmt.Tok, prevHeap, o, f);
+ Bpl.Expr body = Bpl.Expr.Or(
+ Bpl.Expr.Eq(heapOF, oldHeapOF),
+ Bpl.Expr.And(
+ Bpl.Expr.Eq(f, GetField((FieldSelectExpr)((!)s.BodyAssign).Lhs)),
+ oInS));
+ Bpl.Expr qq = new Bpl.ForallExpr(stmt.Tok, new Bpl.TypeVariableSeq(alpha), new Bpl.VariableSeq(oVar, fVar), body);
+ builder.Add(new Bpl.AssumeCmd(stmt.Tok, qq));
+
+ // Here comes: assume (forall o: ref :: o != null && o in S ==> $Heap[o,F] = RHS[$Heap := oldHeap]);
+ Bpl.Expr heapOField = Bpl.Expr.SelectTok(stmt.Tok, etran.HeapExpr, o, GetField((FieldSelectExpr)(s.BodyAssign).Lhs));
+ ExpressionTranslator oldEtran = new ExpressionTranslator(this, predef, prevHeap);
+ body = Bpl.Expr.Imp(
+ Bpl.Expr.And(Bpl.Expr.Neq(o, predef.Null), oInS),
+ Bpl.Expr.Eq(heapOField, oldEtran.TrExpr(((ExprRhs)s.BodyAssign.Rhs).Expr)));
+ qq = new Bpl.ForallExpr(stmt.Tok, new Bpl.VariableSeq(oVar), body);
+ builder.Add(new Bpl.AssumeCmd(stmt.Tok, qq));
+
+ } else {
+ assert false; // unexpected statement
+ }
+ }
+
+ void AddComment(Bpl.StmtListBuilder! builder, Statement! stmt, string! comment) {
+ builder.Add(new Bpl.CommentCmd(string.Format("----- {0} ----- {1}({2},{3})", comment, stmt.Tok.filename, stmt.Tok.line, stmt.Tok.col)));
+ }
+
+ Bpl.Expr GetWhereClause(Token! tok, Bpl.Expr! x, Type! type, ExpressionTranslator! etran)
+ requires predef != null;
+ {
+ if (type is TypeProxy) {
+ // unresolved proxy
+ assert ((TypeProxy)type).T == null;
+ // 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 if (type is BoolType || type is IntType) {
+ return null;
+ } else if (type is SetType) {
+ SetType st = (SetType)type;
+ if (st.Arg.IsRefType) {
+ // (forall t: ref :: { x[t] } x[t] ==> t == null || $Heap[t,alloc])
+ Bpl.BoundVariable tVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$t", predef.RefType));
+ Bpl.Expr t = new Bpl.IdentifierExpr(tok, tVar);
+ Bpl.Expr xSubT = Bpl.Expr.SelectTok(tok, x, t);
+
+ Bpl.Trigger tr = new Bpl.Trigger(tok, true, new Bpl.ExprSeq(xSubT));
+
+ Bpl.Expr goodRef = etran.GoodRef(tok, t, st.Arg);
+ Bpl.Expr body = Bpl.Expr.Imp(xSubT, Bpl.Expr.Or(Bpl.Expr.Eq(t, predef.Null), goodRef));
+ return new Bpl.ForallExpr(tok, new Bpl.VariableSeq(tVar), tr, body);
+ } else {
+ // TODO: should also handle sets of sets, etc.
+ return null;
+ }
+
+ } else if (type is SeqType) {
+ SeqType st = (SeqType)type;
+ if (st.Arg.IsRefType) {
+ // (forall i: int :: { Seq#Index(x,i) }
+ // 0 <= i && i < Seq#Length(x) ==> Seq#Index(x,i) == null || $Heap[Seq#Index(x,i), alloc])
+ Bpl.BoundVariable iVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$i", Bpl.Type.Int));
+ Bpl.Expr i = new Bpl.IdentifierExpr(tok, iVar);
+ Bpl.Expr xSubI = FunctionCall(tok, BuiltinFunction.SeqIndex, predef.RefType, x, i);
+
+ Bpl.Expr range = InSeqRange(tok, i, x, null, false);
+
+ Bpl.Trigger tr = new Bpl.Trigger(tok, true, new Bpl.ExprSeq(xSubI));
+
+ Bpl.Expr goodRef = etran.GoodRef(tok, xSubI, st.Arg);
+ Bpl.Expr body = Bpl.Expr.Imp(range, Bpl.Expr.Or(Bpl.Expr.Eq(xSubI, predef.Null), goodRef));
+ return new Bpl.ForallExpr(tok, new Bpl.VariableSeq(iVar), tr, body);
+ } else {
+ // TODO: should also handle sequences or sequences, etc.
+ return null;
+ }
+
+ } else if (type.IsRefType) {
+ // reference type:
+ // x == null || $Heap[x,alloc]
+ return Bpl.Expr.Or(Bpl.Expr.Eq(x, predef.Null), etran.GoodRef(tok, x, type));
+ } else {
+ // type parameter
+ return null;
+ }
+ }
+
+ void TrAssignment(Token! tok, Expression! lhs, AssignmentRhs! rhs, Bpl.StmtListBuilder! builder, Bpl.VariableSeq! locals,
+ ExpressionTranslator! etran)
+ requires predef != null;
+ {
+ if (rhs is ExprRhs) {
+ builder.Add(Assert(tok, IsTotal(lhs, etran), "LHS expression must be well defined")); // totality check
+ builder.Add(Assert(tok, IsTotal(((ExprRhs)rhs).Expr, etran), "RHS expression must be well defined")); // totality check
+ Bpl.Expr bRhs = etran.TrExpr(((ExprRhs)rhs).Expr);
+ if (lhs is IdentifierExpr) {
+ Bpl.IdentifierExpr bLhs = (Bpl.IdentifierExpr)etran.TrExpr(lhs); // TODO: is this cast always justified?
+ Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(tok, bLhs, bRhs);
+ builder.Add(cmd);
+ } else {
+ Bpl.NAryExpr bLhs = (Bpl.NAryExpr)etran.TrExpr(lhs); // TODO: is this cast always justified?
+ assert bLhs.Args.Length == 3; // we're expecting h[o,f]
+ Bpl.IdentifierExpr h = (Bpl.IdentifierExpr!)bLhs.Args[0]; // TODO: is this cast always justified?
+ Bpl.Cmd cmd = Bpl.Cmd.MapAssign(tok, h, (!)bLhs.Args[1], (!)bLhs.Args[2], bRhs);
+ builder.Add(cmd);
+ // assume $IsGoodHeap($Heap);
+ builder.Add(AssumeGoodHeap(tok, etran));
+ }
+
+ } else if (rhs is HavocRhs) {
+ assert lhs is IdentifierExpr; // for this kind of RHS, the LHS is restricted to be a simple variable
+ Bpl.IdentifierExpr x = (Bpl.IdentifierExpr)etran.TrExpr(lhs); // TODO: is this cast always justified?
+ builder.Add(new Bpl.HavocCmd(tok, new Bpl.IdentifierExprSeq(x)));
+
+ } else {
+ assert rhs is TypeRhs; // otherwise, an unexpected AssignmentRhs
+ assert lhs is IdentifierExpr; // for this kind of RHS, the LHS is restricted to be a simple variable
+
+ 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);
+ builder.Add(new Bpl.AssumeCmd(tok, Bpl.Expr.And(nwNotNull, etran.GoodRef_Class(tok, nw, (ClassType)((TypeRhs)rhs).Type, true))));
+ // $Heap[$nw, alloc] := true;
+ Bpl.Expr alloc = predef.Alloc(tok);
+ Bpl.Cmd cmd = Bpl.Cmd.MapAssign(tok, (Bpl.IdentifierExpr/*TODO: this cast is dubious*/)etran.HeapExpr, nw, alloc, Bpl.Expr.True);
+ builder.Add(cmd);
+ // x := $nw;
+ Bpl.IdentifierExpr x = (Bpl.IdentifierExpr)etran.TrExpr(lhs); // TODO: is this cast always justified?
+ builder.Add(Bpl.Cmd.SimpleAssign(tok, x, nw));
+ // assume $IsGoodHeap($Heap);
+ builder.Add(AssumeGoodHeap(tok, etran));
+ }
+ }
+
+ Bpl.AssumeCmd! AssumeGoodHeap(Token! tok, ExpressionTranslator! etran) {
+ return new Bpl.AssumeCmd(tok, FunctionCall(tok, BuiltinFunction.IsGoodHeap, null, etran.HeapExpr));
+ }
+
+ // ----- Expression ---------------------------------------------------------------------------
+
+ internal class ExpressionTranslator {
+ public readonly Bpl.Expr! HeapExpr;
+ public readonly PredefinedDecls! predef;
+ public readonly Translator! translator;
+ public ExpressionTranslator(Translator! translator, PredefinedDecls! predef, Token! heapToken) {
+ this.translator = translator;
+ this.predef = predef;
+ HeapExpr = new Bpl.IdentifierExpr(heapToken, "$Heap", predef.HeapType);
+ }
+
+ public ExpressionTranslator(Translator! translator, PredefinedDecls! predef, Bpl.Expr! heap) {
+ this.translator = translator;
+ this.predef = predef;
+ this.HeapExpr = heap;
+ }
+
+ ExpressionTranslator oldEtran;
+ public ExpressionTranslator! Old {
+ get {
+ if (oldEtran == null) {
+ oldEtran = new ExpressionTranslator(translator, predef, new Bpl.OldExpr(HeapExpr.tok, HeapExpr));
+ }
+ return oldEtran;
+ }
+ }
+
+ public Bpl.Expr! TrExpr(Expression! expr)
+ 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 Bpl.Expr.Literal((bool)e.Value);
+ } else if (e.Value is int) {
+ return Bpl.Expr.Literal((int)e.Value);
+ } else {
+ assert false; // 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, (!)e.Var);
+
+ } else if (expr is SetDisplayExpr) {
+ SetDisplayExpr e = (SetDisplayExpr)expr;
+ Bpl.Expr s = translator.FunctionCall(expr.tok, BuiltinFunction.SetEmpty, translator.TrType((!)expr.Type));
+ foreach (Expression ee in e.Elements) {
+ Bpl.Expr ss = TrExpr(ee);
+ s = translator.FunctionCall(expr.tok, BuiltinFunction.SetUnionOne, translator.TrType(expr.Type), s, ss);
+ }
+ return s;
+
+ } else if (expr is SeqDisplayExpr) {
+ SeqDisplayExpr e = (SeqDisplayExpr)expr;
+ Bpl.Expr s = translator.FunctionCall(expr.tok, BuiltinFunction.SeqEmpty, translator.TrType((!)expr.Type));
+ int i = 0;
+ foreach (Expression ee in e.Elements) {
+ Bpl.Expr ss = TrExpr(ee);
+ s = translator.FunctionCall(expr.tok, BuiltinFunction.SeqBuild, translator.TrType(expr.Type), s, Bpl.Expr.Literal(i), ss, Bpl.Expr.Literal(i+1));
+ i++;
+ }
+ return s;
+
+ } else if (expr is FieldSelectExpr) {
+ FieldSelectExpr e = (FieldSelectExpr)expr;
+ return Bpl.Expr.SelectTok(expr.tok, HeapExpr, TrExpr(e.Obj), new Bpl.IdentifierExpr(expr.tok, translator.GetField((!)e.Field)));
+
+ } else if (expr is SeqSelectExpr) {
+ SeqSelectExpr e = (SeqSelectExpr)expr;
+ Bpl.Expr seq = TrExpr(e.Seq);
+ Bpl.Type elType = translator.TrType(((SeqType!)e.Seq.Type).Arg);
+ Bpl.Expr e0 = e.E0 == null ? null : TrExpr(e.E0);
+ Bpl.Expr e1 = e.E1 == null ? null : TrExpr(e.E1);
+ if (e.SelectOne) {
+ assert e1 == null;
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SeqIndex, elType, seq, e0);
+ } else {
+ 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);
+ }
+ return seq;
+ }
+
+ } else if (expr is FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)expr;
+ string nm = ((!)e.Function).FullName + (e is UseExpr ? "#use" : "");
+ Bpl.IdentifierExpr id = new Bpl.IdentifierExpr(expr.tok, nm, translator.TrType((!)e.Type));
+ Bpl.ExprSeq args = new Bpl.ExprSeq();
+ args.Add(HeapExpr);
+ 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(expr.tok, TrExpr(ee), (!)ee.Type, t));
+ }
+ 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 OldExpr) {
+ OldExpr e = (OldExpr)expr;
+ return new Bpl.OldExpr(expr.tok, TrExpr(e.E));
+
+ } else if (expr is FreshExpr) {
+ FreshExpr e = (FreshExpr)expr;
+ Bpl.Expr oldHeap = new Bpl.OldExpr(expr.tok, HeapExpr);
+ if (e.E.Type is SetType) {
+ // generate: (forall $o: ref :: $o != null && X[$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, null);
+ Bpl.Expr oIsFresh = Bpl.Expr.Not(IsAlloced(expr.tok, o, oldHeap));
+ 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) && 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), null, false);
+ Bpl.Expr XsubI = translator.FunctionCall(expr.tok, BuiltinFunction.SeqIndex, predef.RefType, TrExpr(e.E), i);
+ Bpl.Expr oIsFresh = Bpl.Expr.Not(IsAlloced(expr.tok, XsubI, oldHeap));
+ Bpl.Expr body = Bpl.Expr.Imp(Bpl.Expr.And(iBounds, XsubI), oIsFresh);
+ return new Bpl.ForallExpr(expr.tok, new Bpl.VariableSeq(iVar), body);
+ } else {
+ // generate: x == null || !old($Heap)[x]
+ Bpl.Expr oNotNull = Bpl.Expr.Neq(TrExpr(e.E), predef.Null);
+ Bpl.Expr oIsFresh = Bpl.Expr.Not(IsAlloced(expr.tok, TrExpr(e.E), oldHeap));
+ return Bpl.Expr.Or(oNotNull, 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.Not(arg);
+ case UnaryExpr.Opcode.SeqLength:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SeqLength, null, arg);
+ default:
+ assert false; // 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, e.E0.Type); // let TrInSet translate e.E1
+ }
+ Bpl.Expr e1 = TrExpr(e.E1);
+ switch (e.ResolvedOp) {
+ case BinaryExpr.ResolvedOpcode.Iff:
+ return Bpl.Expr.Iff(e0, e1);
+ case BinaryExpr.ResolvedOpcode.Imp:
+ return Bpl.Expr.Imp(e0, e1);
+ case BinaryExpr.ResolvedOpcode.And:
+ return Bpl.Expr.And(e0, e1);
+ case BinaryExpr.ResolvedOpcode.Or:
+ return Bpl.Expr.Or(e0, e1);
+
+ case BinaryExpr.ResolvedOpcode.EqCommon:
+ return Bpl.Expr.Eq(e0, e1);
+ case BinaryExpr.ResolvedOpcode.NeqCommon:
+ return Bpl.Expr.Neq(e0, e1);
+
+ case BinaryExpr.ResolvedOpcode.Lt:
+ return Bpl.Expr.Lt(e0, e1);
+ case BinaryExpr.ResolvedOpcode.Le:
+ return Bpl.Expr.Le(e0, e1);
+ case BinaryExpr.ResolvedOpcode.Ge:
+ return Bpl.Expr.Ge(e0, e1);
+ case BinaryExpr.ResolvedOpcode.Gt:
+ return Bpl.Expr.Gt(e0, e1);
+ case BinaryExpr.ResolvedOpcode.Add:
+ return Bpl.Expr.Add(e0, e1);
+ case BinaryExpr.ResolvedOpcode.Sub:
+ return Bpl.Expr.Sub(e0, e1);
+ case BinaryExpr.ResolvedOpcode.Mul:
+ return Bpl.Expr.Mul(e0, e1);
+ case BinaryExpr.ResolvedOpcode.Div:
+ return Bpl.Expr.Div(e0, e1);
+ case BinaryExpr.ResolvedOpcode.Mod:
+ return Bpl.Expr.Mod(e0, e1);
+
+ case BinaryExpr.ResolvedOpcode.SetEq:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SetEqual, null, e0, e1);
+ case BinaryExpr.ResolvedOpcode.SetNeq:
+ return Bpl.Expr.Not(translator.FunctionCall(expr.tok, BuiltinFunction.SetEqual, null, e0, e1));
+ case BinaryExpr.ResolvedOpcode.ProperSubset:
+ return ProperSubset(expr.tok, e0, e1);
+ case BinaryExpr.ResolvedOpcode.Subset:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SetSubset, null, e0, e1);
+ case BinaryExpr.ResolvedOpcode.Superset:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SetSubset, null, e1, e0);
+ case BinaryExpr.ResolvedOpcode.ProperSuperset:
+ return Bpl.Expr.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:
+ assert false; // this case handled above
+ case BinaryExpr.ResolvedOpcode.Union:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SetUnion, translator.TrType(((SetType!)expr.Type).Arg), e0, e1);
+ case BinaryExpr.ResolvedOpcode.Intersection:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SetIntersection, translator.TrType(((SetType!)expr.Type).Arg), e0, e1);
+ case BinaryExpr.ResolvedOpcode.SetDifference:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SetDifference, translator.TrType(((SetType!)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.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.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(((SeqType!)expr.Type).Arg), e0, e1);
+ case BinaryExpr.ResolvedOpcode.InSeq:
+ return translator.FunctionCall(expr.tok, BuiltinFunction.SeqContains, null, e1, e0);
+
+ default:
+ assert false; // unexpected binary expression
+ }
+
+ } else if (expr is QuantifierExpr) {
+ QuantifierExpr e = (QuantifierExpr)expr;
+ Bpl.VariableSeq bvars = new Bpl.VariableSeq();
+ foreach (BoundVar bv in e.BoundVars) {
+ bvars.Add(new Bpl.BoundVariable(bv.tok, new Bpl.TypedIdent(bv.tok, bv.UniqueName, translator.TrType(bv.Type))));
+ }
+ Bpl.QKeyValue kv = TrAttributes(e.Attributes);
+ Bpl.Trigger tr = null;
+ for (Triggers trigs = e.Trigs; trigs != null; trigs = trigs.Prev) {
+ Bpl.ExprSeq tt = new Bpl.ExprSeq();
+ foreach (Expression term in trigs.Terms) {
+ tt.Add(TrExpr(term));
+ }
+ tr = new Bpl.Trigger(expr.tok, true, tt, tr);
+ }
+ Bpl.Expr body = TrExpr(e.Body);
+
+ if (e is ForallExpr) {
+ return new Bpl.ForallExpr(expr.tok, new Bpl.TypeVariableSeq(), bvars, kv, tr, body);
+ } else {
+ assert e is ExistsExpr;
+ return new Bpl.ExistsExpr(expr.tok, new Bpl.TypeVariableSeq(), bvars, kv, tr, 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);
+ Bpl.Expr yea = Bpl.Expr.Imp(g, thn);
+ Bpl.Expr nay = Bpl.Expr.Imp(Bpl.Expr.Not(g), els);
+ return Bpl.Expr.And(yea, nay);
+
+ } else {
+ assert false; // unexpected expression
+ }
+ }
+
+ public Bpl.Expr! ProperSubset(Token! tok, Bpl.Expr! e0, Bpl.Expr! e1) {
+ return Bpl.Expr.And(
+ translator.FunctionCall(tok, BuiltinFunction.SetSubset, null, e0, e1),
+ Bpl.Expr.Not(translator.FunctionCall(tok, BuiltinFunction.SetEqual, null, e0, e1)));
+ }
+ public Bpl.Expr! ProperPrefix(Token! tok, Bpl.Expr! e0, Bpl.Expr! e1) {
+ 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! TrUseExpr(FunctionCallExpr! e) {
+ Bpl.IdentifierExpr id = new Bpl.IdentifierExpr(e.tok, ((!)e.Function).FullName + "#use", translator.TrType((!)e.Type));
+ Bpl.ExprSeq args = new Bpl.ExprSeq();
+ args.Add(HeapExpr);
+ args.Add(TrExpr(e.Receiver));
+ foreach (Expression ee in e.Args) {
+ args.Add(TrExpr(ee));
+ }
+ return new Bpl.NAryExpr(e.tok, new Bpl.FunctionCall(id), args);
+ }
+
+ public Bpl.Expr! CondApplyBox(Token! tok, Bpl.Expr! e, Type! fromType, Type! toType) {
+ if (!ModeledAsRef(fromType) && ModeledAsRef(toType)) {
+ return translator.FunctionCall(tok, BuiltinFunction.Box, null, e);
+ } else {
+ return e;
+ }
+ }
+
+ public Bpl.Expr! CondApplyUnbox(Token! tok, Bpl.Expr! e, Type! fromType, Type! toType) {
+ if (ModeledAsRef(fromType) && !ModeledAsRef(toType)) {
+ return translator.FunctionSpecial(tok, BuiltinFunction.Unbox, translator.TrType(toType), e);
+ } else {
+ return e;
+ }
+ }
+
+ public static bool ModeledAsRef(Type! t) {
+ return !(t is BoolType || t is IntType || t is CollectionType);
+ }
+
+ public Bpl.Expr! TrVar(Token! tok, IVariable! var) {
+ return new Bpl.IdentifierExpr(tok, var.UniqueName, translator.TrType(var.Type));
+ }
+
+ /// <summary>
+ /// Translate like s[elmt], but try to avoid as many set functions as possible in the
+ /// translation, because such functions can mess up triggering.
+ /// If elmtType is non-null, boxing according to elmtType is applied as appropriate.
+ /// </summary>
+ public Bpl.Expr! TrInSet(Token! tok, Bpl.Expr! elmt, Expression! s, Type elmtType) {
+ 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), elmt);
+ }
+
+ Bpl.QKeyValue TrAttributes(Attributes attrs) {
+ Bpl.QKeyValue kv = null;
+ while (attrs != null) {
+ 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((!)arg.S);
+ }
+ }
+ kv = new Bpl.QKeyValue(Token.NoToken, attrs.Name, parms, kv);
+ attrs = attrs.Prev;
+ }
+ return kv;
+ }
+
+ // --------------- help routines ---------------
+
+ public Bpl.Expr! IsAlloced(Token! tok, Bpl.Expr! e) {
+ return IsAlloced(tok, e, HeapExpr);
+ }
+
+ Bpl.Expr! IsAlloced(Token! tok, Bpl.Expr! e, Bpl.Expr! heap) {
+ return Bpl.Expr.SelectTok(tok, heap, e, predef.Alloc(tok));
+ }
+
+ public Bpl.Expr! GoodRef(Token! tok, Bpl.Expr! e, Type! type) {
+ Bpl.Expr goodRef;
+ if (type is ClassType && ((ClassType)type).ResolvedClass != null) {
+ // Heap[e, alloc] && dtype(e) == T
+ return GoodRef_Class(tok, e, (ClassType)type, false);
+ } else {
+ // Heap[e, alloc]
+ return IsAlloced(tok, e);
+ }
+ }
+
+ public Bpl.Expr! GoodRef_Class(Token! tok, Bpl.Expr! e, ClassType! type, bool isNew)
+ requires type.ResolvedClass != null;
+ {
+ // 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, new Bpl.IdentifierExpr(tok, translator.GetClass(type.ResolvedClass)));
+ r = Bpl.Expr.And(r, dtype);
+
+ // dtypeParams(e, #) == T
+ int n = 0;
+ foreach (Type arg in type.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 TypeAlloced(Token! tok, Bpl.Expr! e, Type! type) {
+ while (true) {
+ TypeProxy proxy = type as TypeProxy;
+ if (proxy == null) {
+ break;
+ } else if (proxy.T == null) {
+ return null;
+ } else {
+ type = proxy.T;
+ }
+ }
+
+ Bpl.BoundVariable bv = null;
+ Bpl.Expr ante = null;
+ if (type.IsRefType) { // object or class type
+ // e == null || $Heap[e, alloc]
+ // This is done below
+
+ } else if (type is SetType) {
+ // (forall tp: ref :: e[tp] ==> tp == null || $Heap[tp, alloc])
+ bv = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$tp", predef.RefType));
+ Bpl.Expr tp = new Bpl.IdentifierExpr(tok, bv);
+ ante = Bpl.Expr.SelectTok(tok, e, tp); // note, this means the set-inclusion does not undergo the set-inclusion expansion optimization done by TrInSet
+ e = tp;
+
+ } else if (type is SeqType) {
+ // (forall i: int :: Seq#Index(e,i) == null || $Heap[Seq#Index(e,i), alloc])
+ bv = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$i", Bpl.Type.Int));
+ e = translator.FunctionCall(tok, BuiltinFunction.SeqIndex, predef.RefType, e, new Bpl.IdentifierExpr(tok, bv));
+
+ } else {
+ return null;
+ }
+
+ Bpl.Expr r = Bpl.Expr.Or(Bpl.Expr.Eq(e, predef.Null), GoodRef(tok, e, type));
+ if (ante != null) {
+ r = Bpl.Expr.Imp(ante, r);
+ }
+ if (bv != null) {
+ r = new Bpl.ForallExpr(tok, new Bpl.VariableSeq(bv), r);
+ }
+ return r;
+ }
+ }
+
+ enum BuiltinFunction {
+ SetEmpty,
+ SetUnionOne,
+ SetUnion,
+ SetIntersection,
+ SetDifference,
+ SetEqual,
+ SetSubset,
+ SetDisjoint,
+
+ SeqLength,
+ SeqEmpty,
+ SeqBuild,
+ SeqAppend,
+ SeqIndex,
+ SeqContains,
+ SeqDrop,
+ SeqTake,
+ SeqEqual,
+ SeqSameUntil,
+
+ Box,
+ Unbox,
+
+ IsGoodHeap,
+ HeapSucc,
+
+ DynamicType, // allocated type
+ TypeParams, // type parameters to allocated type
+ TypeTuple
+ }
+
+ Bpl.NAryExpr! FunctionCall(Token! tok, BuiltinFunction f, Bpl.Type typeInstantiation, params Bpl.Expr[]! args)
+ requires predef != null;
+ {
+ switch (f) {
+ case BuiltinFunction.SetEmpty:
+ assert args.Length == 0;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Set#Empty",
+ new Bpl.MapType(tok, new Bpl.TypeVariableSeq(), new Bpl.TypeSeq(typeInstantiation), Bpl.Type.Bool), args);
+ case BuiltinFunction.SetUnionOne:
+ assert args.Length == 2;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Set#UnionOne",
+ new Bpl.MapType(tok, new Bpl.TypeVariableSeq(), new Bpl.TypeSeq(typeInstantiation), Bpl.Type.Bool), args);
+ case BuiltinFunction.SetUnion:
+ assert args.Length == 2;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Set#Union",
+ new Bpl.MapType(tok, new Bpl.TypeVariableSeq(), new Bpl.TypeSeq(typeInstantiation), Bpl.Type.Bool), args);
+ case BuiltinFunction.SetIntersection:
+ assert args.Length == 2;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Set#Intersection",
+ new Bpl.MapType(tok, new Bpl.TypeVariableSeq(), new Bpl.TypeSeq(typeInstantiation), Bpl.Type.Bool), args);
+ case BuiltinFunction.SetDifference:
+ assert args.Length == 2;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Set#Difference",
+ new Bpl.MapType(tok, new Bpl.TypeVariableSeq(), new Bpl.TypeSeq(typeInstantiation), Bpl.Type.Bool), args);
+ case BuiltinFunction.SetEqual:
+ assert args.Length == 2;
+ assert typeInstantiation == null;
+ return FunctionCall(tok, "Set#Equal", Bpl.Type.Bool, args);
+ case BuiltinFunction.SetSubset:
+ assert args.Length == 2;
+ assert typeInstantiation == null;
+ return FunctionCall(tok, "Set#Subset", Bpl.Type.Bool, args);
+ case BuiltinFunction.SetDisjoint:
+ assert args.Length == 2;
+ assert typeInstantiation == null;
+ return FunctionCall(tok, "Set#Disjoint", Bpl.Type.Bool, args);
+
+ case BuiltinFunction.SeqLength:
+ assert args.Length == 1;
+ assert typeInstantiation == null;
+ return FunctionCall(tok, "Seq#Length", Bpl.Type.Int, args);
+ case BuiltinFunction.SeqEmpty:
+ assert args.Length == 0;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Seq#Empty",
+ new Bpl.MapType(tok, new Bpl.TypeVariableSeq(), new Bpl.TypeSeq(typeInstantiation), Bpl.Type.Bool), args);
+ case BuiltinFunction.SeqBuild:
+ assert args.Length == 4;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Seq#Build", predef.SeqType(tok, typeInstantiation), args);
+ case BuiltinFunction.SeqAppend:
+ assert args.Length == 2;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Seq#Append", predef.SeqType(tok, typeInstantiation), args);
+ case BuiltinFunction.SeqIndex:
+ assert args.Length == 2;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Seq#Index", typeInstantiation, args);
+ case BuiltinFunction.SeqContains:
+ assert args.Length == 2;
+ assert typeInstantiation == null;
+ return FunctionCall(tok, "Seq#Contains", Bpl.Type.Bool, args);
+ case BuiltinFunction.SeqDrop:
+ assert args.Length == 2;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Seq#Drop", predef.SeqType(tok, typeInstantiation), args);
+ case BuiltinFunction.SeqTake:
+ assert args.Length == 2;
+ assert typeInstantiation != null;
+ return FunctionCall(tok, "Seq#Take", predef.SeqType(tok, typeInstantiation), args);
+ case BuiltinFunction.SeqEqual:
+ assert args.Length == 2;
+ assert typeInstantiation == null;
+ return FunctionCall(tok, "Seq#Equal", Bpl.Type.Bool, args);
+ case BuiltinFunction.SeqSameUntil:
+ assert args.Length == 3;
+ assert typeInstantiation == null;
+ return FunctionCall(tok, "Seq#SameUntil", Bpl.Type.Bool, args);
+
+ case BuiltinFunction.Box:
+ assert args.Length == 1;
+ assert typeInstantiation == null;
+ return FunctionCall(tok, "$Box", predef.RefType, args);
+ case BuiltinFunction.Unbox:
+ assert false; // Unbox should be handled by a call to FunctionSpecial
+
+ case BuiltinFunction.IsGoodHeap:
+ assert args.Length == 1;
+ assert typeInstantiation == null;
+ return FunctionCall(tok, "$IsGoodHeap", Bpl.Type.Bool, args);
+ case BuiltinFunction.HeapSucc:
+ assert args.Length == 2;
+ assert typeInstantiation == null;
+ return FunctionCall(tok, "$HeapSucc", Bpl.Type.Bool, args);
+
+ case BuiltinFunction.DynamicType:
+ assert args.Length == 1;
+ return FunctionCall(tok, "dtype", predef.ClassNameType, args);
+ case BuiltinFunction.TypeParams:
+ assert args.Length == 2;
+ return FunctionCall(tok, "TypeParams", predef.ClassNameType, args);
+ case BuiltinFunction.TypeTuple:
+ assert args.Length == 2;
+ return FunctionCall(tok, "TypeTuple", predef.ClassNameType, args);
+
+ default:
+ assert false; // unexpected built-in function
+ }
+ }
+
+ Bpl.NAryExpr! FunctionSpecial(Token! tok, BuiltinFunction f, Bpl.Type! resultType, params Bpl.Expr[]! args)
+ {
+ switch (f) {
+ case BuiltinFunction.Unbox:
+ assert args.Length == 1;
+ return Bpl.Expr.CoerceType(tok, FunctionCall(tok, "$Unbox", resultType, args), resultType);
+ default:
+ assert false; // unexpected enum value
+ }
+ }
+
+ Bpl.NAryExpr! FunctionCall(Token! tok, string! function, Bpl.Type! returnType, params Bpl.Expr[]! args)
+ {
+ return new Bpl.NAryExpr(tok, new Bpl.FunctionCall(new Bpl.IdentifierExpr(tok, function, returnType)), new Bpl.ExprSeq(args));
+ }
+
+ public IEnumerable<Expression!>! SplitExpr(Expression! expr, bool expandFunctions)
+ requires expr.Type is BoolType;
+ {
+ if (expr is BinaryExpr) {
+ BinaryExpr bin = (BinaryExpr)expr;
+ if (bin.ResolvedOp == BinaryExpr.ResolvedOpcode.And) {
+ foreach (Expression e in SplitExpr(bin.E0, expandFunctions)) {
+ yield return e;
+ }
+ assert bin != null; // the necessity of this cast is a compiler bug, but perhaps an irrepairable one
+ foreach (Expression e in SplitExpr(bin.E1, expandFunctions)) {
+ yield return e;
+ }
+ yield break;
+
+ } else if (bin.ResolvedOp == BinaryExpr.ResolvedOpcode.Imp) {
+ foreach (Expression e in SplitExpr(bin.E1, expandFunctions)) {
+ assert bin != null;
+ BinaryExpr redistributedExpr = new BinaryExpr(e.tok, bin.Op, bin.E0, e);
+ redistributedExpr.ResolvedOp = bin.ResolvedOp; redistributedExpr.Type = bin.Type; // resolve on the fly
+ yield return redistributedExpr;
+ }
+ yield break;
+ }
+
+ } else if (expr is ITEExpr) {
+ ITEExpr ite = (ITEExpr)expr;
+ foreach (Expression e in SplitExpr(ite.Thn, expandFunctions)) {
+ assert ite != null; // the necessity of this cast is a compiler bug, but perhaps an irrepairable one
+ BinaryExpr bin = new BinaryExpr(e.tok, BinaryExpr.Opcode.Imp, ite.Test, e);
+ bin.ResolvedOp = BinaryExpr.ResolvedOpcode.Imp; bin.Type = ite.Type; // resolve on the fly
+ yield return bin;
+ }
+ assert ite != null; // the necessity of this cast is a compiler bug, but perhaps an irrepairable one
+ Expression negatedGuard = new UnaryExpr(ite.Test.tok, UnaryExpr.Opcode.Not, ite.Test);
+ negatedGuard.Type = ite.Test.Type; // resolve on the fly
+ foreach (Expression e in SplitExpr(ite.Els, expandFunctions)) {
+ assert ite != null; // the necessity of this cast is a compiler bug, but perhaps an irrepairable one
+ assert negatedGuard != null; // the necessity of this cast is a compiler bug, but perhaps an irrepairable one
+ BinaryExpr bin = new BinaryExpr(e.tok, BinaryExpr.Opcode.Imp, negatedGuard, e);
+ bin.ResolvedOp = BinaryExpr.ResolvedOpcode.Imp; bin.Type = ite.Type; // resolve on the fly
+ yield return bin;
+ }
+ yield break;
+
+ } else if (expr is OldExpr) {
+ foreach (Expression se in SplitExpr(((OldExpr)expr).E, expandFunctions)) {
+ OldExpr oe = new OldExpr(expr.tok, se);
+ oe.Type = se.Type;
+ yield return oe;
+ }
+ yield break;
+
+ } else if (expandFunctions && expr is FunctionCallExpr) {
+ FunctionCallExpr fexp = (FunctionCallExpr)expr;
+ assert fexp.Function != null; // filled in during resolution
+ if (fexp.Function.Body != null) {
+ // inline this body
+ Dictionary<IVariable,Expression!> substMap = new Dictionary<IVariable,Expression!>();
+ assert fexp.Args.Count == fexp.Function.Formals.Count;
+ for (int i = 0; i < fexp.Function.Formals.Count; i++) {
+ substMap.Add(fexp.Function.Formals[i], fexp.Args[i]);
+ }
+ Expression body = Substitute(fexp.Function.Body, fexp.Receiver, substMap);
+ foreach (Expression se in SplitExpr(body, false)) {
+ assert fexp != null && fexp.Function != null; // already checked above, but the compiler seems to have forgotten that
+ if (fexp.Function.Use) {
+ BinaryExpr imp = new BinaryExpr(expr.tok, BinaryExpr.Opcode.Imp, new UseExpr(fexp), se);
+ imp.ResolvedOp = BinaryExpr.ResolvedOpcode.Imp;
+ imp.Type = Type.Bool;
+ yield return imp;
+ } else {
+ yield return se;
+ }
+ }
+ assert fexp != null && fexp.Function != null; // already checked above, but the compiler seems to have forgotten that
+ if (fexp.Function.Use) {
+ UnaryExpr ue = new UnaryExpr(expr.tok, UnaryExpr.Opcode.Not, new UseExpr(fexp));
+ ue.Type = Type.Bool;
+ BinaryExpr imp = new BinaryExpr(expr.tok, BinaryExpr.Opcode.Imp, ue, expr);
+ imp.ResolvedOp = BinaryExpr.ResolvedOpcode.Imp;
+ imp.Type = Type.Bool;
+ yield return imp;
+ }
+ yield break;
+ }
+ }
+
+ yield return expr;
+ }
+
+ static Expression! Substitute(Expression! expr, Expression! receiverReplacement, Dictionary<IVariable,Expression!>! substMap) {
+ 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) {
+ // nothing to substitute
+ } else if (expr is ThisExpr) {
+ return receiverReplacement;
+ } else if (expr is IdentifierExpr) {
+ IdentifierExpr e = (IdentifierExpr)expr;
+ Expression substExpr;
+ if (substMap.TryGetValue(e.Var, out substExpr)) {
+ return (!)substExpr;
+ }
+ } else if (expr is DisplayExpression) {
+ DisplayExpression e = (DisplayExpression)expr;
+ List<Expression!> newElements = SubstituteExprList(e.Elements, receiverReplacement, substMap);
+ DisplayExpression newDisplayExpr;
+ if (newElements != e.Elements) {
+ if (expr is SetDisplayExpr) {
+ newExpr = new SetDisplayExpr(expr.tok, newElements);
+ } else {
+ newExpr = new SeqDisplayExpr(expr.tok, newElements);
+ }
+ }
+
+ } else if (expr is FieldSelectExpr) {
+ FieldSelectExpr fse = (FieldSelectExpr)expr;
+ Expression substE = Substitute(fse.Obj, receiverReplacement, substMap);
+ 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, receiverReplacement, substMap);
+ Expression e0 = sse.E0 == null ? null : Substitute(sse.E0, receiverReplacement, substMap);
+ Expression e1 = sse.E1 == null ? null : Substitute(sse.E1, receiverReplacement, substMap);
+ if (seq != sse.Seq || e0 != sse.E0 || e1 != sse.E1) {
+ newExpr = new SeqSelectExpr(sse.tok, sse.SelectOne, seq, e0, e1);
+ }
+
+ } else if (expr is FunctionCallExpr) {
+ FunctionCallExpr e = (FunctionCallExpr)expr;
+ Expression receiver = Substitute(e.Receiver, receiverReplacement, substMap);
+ List<Expression!> newArgs = SubstituteExprList(e.Args, receiverReplacement, substMap);
+ if (receiver != e.Receiver || newArgs != e.Args) {
+ FunctionCallExpr newFce = new FunctionCallExpr(expr.tok, e.Name, receiver, newArgs);
+ newFce.Function = e.Function; // resolve on the fly (and set newFce.Type below, at end)
+ newExpr = newFce;
+ }
+
+ } else if (expr is OldExpr) {
+ OldExpr e = (OldExpr)expr;
+ Expression se = Substitute(e.E, receiverReplacement, substMap);
+ if (se != e.E) {
+ newExpr = new OldExpr(expr.tok, se);
+ }
+ } else if (expr is FreshExpr) {
+ FreshExpr e = (FreshExpr)expr;
+ Expression se = Substitute(e.E, receiverReplacement, substMap);
+ if (se != e.E) {
+ newExpr = new FreshExpr(expr.tok, se);
+ }
+ } else if (expr is UnaryExpr) {
+ UnaryExpr e = (UnaryExpr)expr;
+ Expression se = Substitute(e.E, receiverReplacement, substMap);
+ 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, receiverReplacement, substMap);
+ Expression e1 = Substitute(e.E1, receiverReplacement, substMap);
+ 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 QuantifierExpr) {
+ QuantifierExpr e = (QuantifierExpr)expr;
+ Expression newBody = Substitute(e.Body, receiverReplacement, substMap);
+ Triggers newTrigs = SubstTriggers(e.Trigs, receiverReplacement, substMap);
+ Attributes newAttrs = SubstAttributes(e.Attributes, receiverReplacement, substMap);
+ if (newBody != e.Body || newTrigs != e.Trigs || newAttrs != e.Attributes) {
+ if (expr is ForallExpr) {
+ newExpr = new ForallExpr(expr.tok, e.BoundVars, newBody, newTrigs, newAttrs);
+ } else {
+ newExpr = new ExistsExpr(expr.tok, e.BoundVars, newBody, newTrigs, newAttrs);
+ }
+ }
+
+ } else if (expr is ITEExpr) {
+ ITEExpr e = (ITEExpr)expr;
+ Expression test = Substitute(e.Test, receiverReplacement, substMap);
+ Expression thn = Substitute(e.Thn, receiverReplacement, substMap);
+ Expression els = Substitute(e.Els, receiverReplacement, substMap);
+ if (test != e.Test || thn != e.Thn || els != e.Els) {
+ newExpr = new ITEExpr(expr.tok, test, thn, els);
+ }
+ }
+
+ if (newExpr == null) {
+ return expr;
+ } else {
+ newExpr.Type = expr.Type; // resolve on the fly (any additional resolution must be done above)
+ return newExpr;
+ }
+ }
+
+ static List<Expression!>! SubstituteExprList(List<Expression!>! elist,
+ Expression! receiverReplacement, Dictionary<IVariable,Expression!>! substMap) {
+ List<Expression!> newElist = null; // initialized lazily
+ for (int i = 0; i < elist.Count; i++)
+ invariant newElist == null || newElist.Count == i;
+ {
+ Expression substE = Substitute(elist[i], receiverReplacement, substMap);
+ 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;
+ }
+ }
+
+ static Triggers SubstTriggers(Triggers trigs, Expression! receiverReplacement, Dictionary<IVariable,Expression!>! substMap) {
+ if (trigs != null) {
+ List<Expression!> terms = SubstituteExprList(trigs.Terms, receiverReplacement, substMap);
+ Triggers prev = SubstTriggers(trigs.Prev, receiverReplacement, substMap);
+ if (terms != trigs.Terms || prev != trigs.Prev) {
+ return new Triggers(terms, prev);
+ }
+ }
+ return trigs;
+ }
+
+ static Attributes SubstAttributes(Attributes attrs, Expression! receiverReplacement, Dictionary<IVariable,Expression!>! 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, receiverReplacement, substMap);
+ if (newE != arg.E) {
+ newArg = new Attributes.Argument(newE);
+ anyArgSubst = true;
+ }
+ }
+ newArgs.Add(newArg);
+ }
+ if (!anyArgSubst) {
+ newArgs = attrs.Args;
+ }
+
+ Attributes prev = SubstAttributes(attrs.Prev, receiverReplacement, substMap);
+ if (newArgs != attrs.Args || prev != attrs.Prev) {
+ return new Attributes(attrs.Name, newArgs, prev);
+ }
+ }
+ return attrs;
+ }
+
+ }
+}
diff --git a/Dafny/parser.frame b/Dafny/parser.frame
new file mode 100644
index 00000000..de0ba1fb
--- /dev/null
+++ b/Dafny/parser.frame
@@ -0,0 +1,103 @@
+
+using Microsoft.Contracts;
+
+namespace Microsoft.-->namespace {
+
+public class Parser {
+-->constants
+ const bool T = true;
+ const bool x = false;
+ const int minErrDist = 2;
+
+ static Token/*!*/ token; // last recognized token
+ static Token/*!*/ t; // lookahead token
+ static int errDist = minErrDist;
+
+ -->declarations
+
+ static void Error(int n) {
+ if (errDist >= minErrDist) Errors.SynErr(n, t.filename, t.line, t.col);
+ errDist = 0;
+ }
+
+ public static void SemErr(string! msg) {
+ if (errDist >= minErrDist) Errors.SemErr(token.filename, token.line, token.col, msg);
+ errDist = 0;
+ }
+
+ public static void SemErr(Token! tok, string! msg) {
+ if (errDist >= minErrDist) Errors.SemErr(tok.filename, tok.line, tok.col, msg);
+ errDist = 0;
+ }
+
+ static void Get() {
+ for (;;) {
+ token = t;
+ t = Scanner.Scan();
+ if (t.kind<=maxT) {errDist++; return;}
+-->pragmas
+ t = token;
+ }
+ }
+
+ static void Expect(int n) {
+ if (t.kind==n) Get(); else Error(n);
+ }
+
+ static bool StartOf(int s) {
+ return set[s, t.kind];
+ }
+
+ static void ExpectWeak(int n, int follow) {
+ if (t.kind == n) Get();
+ else {
+ Error(n);
+ while (!StartOf(follow)) Get();
+ }
+ }
+
+ static bool WeakSeparator(int n, int syFol, int repFol) {
+ bool[] s = new bool[maxT+1];
+ if (t.kind == n) {Get(); return true;}
+ else if (StartOf(repFol)) return false;
+ else {
+ for (int i=0; i <= maxT; i++) {
+ s[i] = set[syFol, i] || set[repFol, i] || set[0, i];
+ }
+ Error(n);
+ while (!s[t.kind]) Get();
+ return StartOf(syFol);
+ }
+ }
+
+-->productions
+
+ public static void Parse() {
+ Errors.SynErr = new ErrorProc(SynErr);
+ t = new Token();
+ Get();
+-->parseRoot
+ }
+
+ [Microsoft.Contracts.Verify(false)]
+ static void SynErr(int n, string filename, int line, int col) {
+ Errors.count++;
+ System.Console.Write("{0}({1},{2}): syntax error: ", filename, line, col);
+ string s;
+ switch (n) {
+-->errors
+ default: s = "error " + n; break;
+ }
+ System.Console.WriteLine(s);
+ }
+
+ static bool[,]! set = {
+-->initialization
+ };
+
+ [Microsoft.Contracts.Verify(false)]
+ static Parser() {}
+} // end Parser
+
+} // end namespace
+$$$ \ No newline at end of file
diff --git a/Dafny/scanner.frame b/Dafny/scanner.frame
new file mode 100644
index 00000000..a526c7f2
--- /dev/null
+++ b/Dafny/scanner.frame
@@ -0,0 +1,170 @@
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.Contracts;
+using Bpl = Microsoft.Boogie;
+using BoogiePL;
+
+
+namespace Microsoft.Dafny {
+
+ [Immutable]
+ public class Token : Bpl.Token {
+ public Token();
+ public Token(int linenum, int colnum) {
+ base(linenum, colnum);
+ }
+ public new static Token! NoToken = new Token();
+ }
+
+}
+
+namespace Microsoft.-->namespace {
+
+
+public class Scanner {
+ const char EOF = '\0';
+ const char CR = '\r';
+ const char LF = '\n';
+
+ [Microsoft.Contracts.Verify(false)]
+-->declarations
+
+
+ static Token/*!*/ t; // current token
+ static char ch; // current input character
+ static int pos; // column number of current character
+ static int line; // line number of current character
+ static int lineStart; // start position of current line
+ static Queue! oldEols; // EOLs that appeared in a comment;
+ static BitArray/*!*/ ignore; // set of characters to be ignored by the scanner
+ static string Filename;
+
+ ///<summary>
+ ///Initializes the scanner. Note: first fill the Buffer.
+ ///</summary>
+ ///<param name="filename">File name used for error reporting</param>
+ public static void Init (string filename) {
+ Filename = filename;
+ pos = -1; line = 1; lineStart = 0;
+ oldEols = new Queue();
+ NextCh();
+-->initialization
+ }
+
+ private static void NextCh() {
+ if (oldEols.Count > 0) {
+ ch = (char) ((!)oldEols.Dequeue());
+ } else {
+ while (true) {
+ ch = (char)BoogiePL.Buffer.Read(); pos++;
+ if (ch == BoogiePL.Buffer.eof) {
+ ch = EOF;
+ } else if (ch == LF) {
+ line++;
+ lineStart = pos + 1;
+
+ } else if (ch == '#' && pos == lineStart) {
+ int prLine = line;
+ int prColumn = pos - lineStart; // which is 0
+
+ string hashLine = BoogiePL.Buffer.ReadToEOL(); pos += hashLine.Length;
+ line++;
+ lineStart = pos + 1;
+
+ 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 (System.FormatException) {
+ // just fall down through to produce an error message
+ }
+ Errors.SemErr(Filename, prLine, prColumn, "Malformed (#line num [filename]) pragma: #" + hashLine);
+ continue;
+ }
+
+ Errors.SemErr(Filename, prLine, prColumn, "Unrecognized pragma: #" + hashLine);
+ continue;
+ }
+ return;
+ }
+ }
+ }
+
+-->comment
+
+ static void CheckLiteral() {
+ switch (t.val) {
+-->literals
+ }
+ }
+
+ public static Token/*!*/ Scan() {
+ while (ignore[ch]) { NextCh(); }
+-->scan1
+ t = new Token();
+ t.pos = pos; t.col = pos - lineStart + 1; t.line = line; t.filename = Filename;
+ int state = (/*^ (!) ^*/ start)[ch];
+ StringBuilder buf = new StringBuilder(16);
+ buf.Append(ch); NextCh();
+
+ switch (state) {
+ case 0: {t.kind = noSym; goto done;} // NextCh already done
+-->scan2
+ }
+ done:
+ t.val = buf.ToString();
+ return t;
+ }
+
+} // end Scanner
+
+
+public delegate void ErrorProc(int n, string filename, int line, int col);
+
+public class Errors {
+ public static int count = 0; // number of errors detected
+ public static ErrorProc/*!*/ SynErr; // syntactic errors
+
+ public static void SemErr(string filename, int line, int col, string! msg) { // semantic errors
+ System.ConsoleColor color = System.Console.ForegroundColor;
+ System.Console.ForegroundColor = System.ConsoleColor.Red;
+ System.Console.WriteLine("{0}({1},{2}): Error: {3}", filename, line, col, msg);
+ System.Console.ForegroundColor = color;
+ count++;
+ }
+
+ public static void SemErr(Bpl.IToken! tok, string! msg) { // semantic errors
+ SemErr(tok.filename, tok.line, tok.col, msg);
+ }
+
+ public static void Exception (string s) {
+ System.ConsoleColor color = System.Console.ForegroundColor;
+ System.Console.ForegroundColor = System.ConsoleColor.Red;
+ System.Console.WriteLine(s);
+ System.Console.ForegroundColor = color;
+ System.Environment.Exit(0);
+ }
+
+} // Errors
+
+} // end namespace
+$$$