TextWriter TrStmt(Statement stmt, int indent) { Contract.Requires(stmt != null); TextWriter wr = new StringWriter(); if (stmt.IsGhost) { var v = new CheckHasNoAssumes_Visitor(this, wr); v.Visit(stmt); Indent(indent, wr); wr.WriteLine("{ }"); return wr; } if (stmt is PrintStmt) { PrintStmt s = (PrintStmt)stmt; foreach (var arg in s.Args) { Indent(indent, wr); wr.Write("System.Console.Write("); TrExpr(arg, wr, false); wr.WriteLine(");"); } } else if (stmt is BreakStmt) { var s = (BreakStmt)stmt; Indent(indent, wr); wr.WriteLine("goto after_{0};", s.TargetStmt.Labels.Data.AssignUniqueId("after_", idGenerator)); } else if (stmt is ProduceStmt) { var s = (ProduceStmt)stmt; if (s.hiddenUpdate != null) wr.Write(TrStmt(s.hiddenUpdate, indent).ToString()); Indent(indent, wr); if (s is YieldStmt) { wr.WriteLine("yield return null;"); } else { wr.WriteLine("return;"); } } else if (stmt is UpdateStmt) { var s = (UpdateStmt)stmt; var resolved = s.ResolvedStatements; if (resolved.Count == 1) { wr.Write(TrStmt(resolved[0], indent).ToString()); } else { // multi-assignment Contract.Assert(s.Lhss.Count == resolved.Count); Contract.Assert(s.Rhss.Count == resolved.Count); var lvalues = new List<string>(); var rhss = new List<string>(); for (int i = 0; i < resolved.Count; i++) { if (!resolved[i].IsGhost) { var lhs = s.Lhss[i]; var rhs = s.Rhss[i]; if (!(rhs is HavocRhs)) { lvalues.Add(CreateLvalue(lhs, indent, wr)); string target = idGenerator.FreshId("_rhs"); rhss.Add(target); TrRhs("var " + target, null, rhs, indent, wr); } } } Contract.Assert(lvalues.Count == rhss.Count); for (int i = 0; i < lvalues.Count; i++) { Indent(indent, wr); wr.WriteLine("{0} = {1};", lvalues[i], rhss[i]); } } } else if (stmt is AssignStmt) { AssignStmt s = (AssignStmt)stmt; Contract.Assert(!(s.Lhs is SeqSelectExpr) || ((SeqSelectExpr)s.Lhs).SelectOne); // multi-element array assignments are not allowed TrRhs(null, s.Lhs, s.Rhs, indent, wr); } else if (stmt is AssignSuchThatStmt) { var s = (AssignSuchThatStmt)stmt; if (s.AssumeToken != null) { // Note, a non-ghost AssignSuchThatStmt may contain an assume Error("an assume statement cannot be compiled (line {0})", wr, s.AssumeToken.line); } else if (s.MissingBounds != null) { foreach (var bv in s.MissingBounds) { Error("this assign-such-that statement is too advanced for the current compiler; Dafny's heuristics cannot find any bound for variable '{0}' (line {1})", wr, bv.Name, s.Tok.line); } } else { Contract.Assert(s.Bounds != null); // follows from s.MissingBounds == null TrAssignSuchThat(indent, s.Lhss.ConvertAll(lhs => ((IdentifierExpr)lhs.Resolved).Var), // the resolver allows only IdentifierExpr left-hand sides s.Expr, s.Bounds, s.Tok.line, wr, false); } } else if (stmt is CallStmt) { CallStmt s = (CallStmt)stmt; wr.Write(TrCallStmt(s, null, indent).ToString()); } else if (stmt is BlockStmt) { Indent(indent, wr); wr.WriteLine("{"); TrStmtList(((BlockStmt)stmt).Body, indent, wr); Indent(indent, wr); wr.WriteLine("}"); } else if (stmt is IfStmt) { IfStmt s = (IfStmt)stmt; if (s.Guard == null) { // we can compile the branch of our choice if (s.Els == null) { // let's compile the "else" branch, since that involves no work // (still, let's leave a marker in the source code to indicate that this is what we did) Indent(indent, wr); wr.WriteLine("if (!false) { }"); } else { // let's compile the "then" branch Indent(indent, wr); wr.WriteLine("if (true)"); wr.Write(TrStmt(s.Thn, indent).ToString()); } } else { Indent(indent, wr); wr.Write("if ("); TrExpr(s.IsExistentialGuard ? Translator.AlphaRename((ExistsExpr)s.Guard, "eg_d", new Translator(null)) : s.Guard, wr, false); wr.WriteLine(")"); // We'd like to do "TrStmt(s.Thn, indent)", except we want the scope of any existential variables to come inside the block Indent(indent, wr); wr.WriteLine("{"); if (s.IsExistentialGuard) { IntroduceAndAssignBoundVars(indent + IndentAmount, (ExistsExpr)s.Guard, wr); } TrStmtList(s.Thn.Body, indent, wr); Indent(indent, wr); wr.WriteLine("}"); if (s.Els != null) { Indent(indent, wr); wr.WriteLine("else"); wr.Write(TrStmt(s.Els, indent).ToString()); } } } else if (stmt is AlternativeStmt) { var s = (AlternativeStmt)stmt; Indent(indent, wr); foreach (var alternative in s.Alternatives) { wr.Write("if ("); TrExpr(alternative.IsExistentialGuard ? Translator.AlphaRename((ExistsExpr)alternative.Guard, "eg_d", new Translator(null)) : alternative.Guard, wr, false); wr.WriteLine(") {"); if (alternative.IsExistentialGuard) { IntroduceAndAssignBoundVars(indent + IndentAmount, (ExistsExpr)alternative.Guard, wr); } TrStmtList(alternative.Body, indent, wr); Indent(indent, wr); wr.Write("} else "); } wr.WriteLine("{ /*unreachable alternative*/ }"); } else if (stmt is WhileStmt) { WhileStmt s = (WhileStmt)stmt; if (s.Body == null) { return wr; } if (s.Guard == null) { Indent(indent, wr); wr.WriteLine("while (false) { }"); } else { Indent(indent, wr); wr.Write("while ("); TrExpr(s.Guard, wr, false); wr.WriteLine(")"); wr.Write(TrStmt(s.Body, indent).ToString()); } } else if (stmt is AlternativeLoopStmt) { var s = (AlternativeLoopStmt)stmt; if (s.Alternatives.Count != 0) { Indent(indent, wr); wr.WriteLine("while (true) {"); int ind = indent + IndentAmount; foreach (var alternative in s.Alternatives) { } Indent(ind, wr); foreach (var alternative in s.Alternatives) { wr.Write("if ("); TrExpr(alternative.Guard, wr, false); wr.WriteLine(") {"); TrStmtList(alternative.Body, ind, wr); Indent(ind, wr); wr.Write("} else "); } wr.WriteLine("{ break; }"); Indent(indent, wr); wr.WriteLine("}"); } } else if (stmt is ForallStmt) { var s = (ForallStmt)stmt; if (s.Kind != ForallStmt.ParBodyKind.Assign) { // Call and Proof have no side effects, so they can simply be optimized away. return wr; } else if (s.BoundVars.Count == 0) { // the bound variables just spell out a single point, so the forall statement is equivalent to one execution of the body wr.Write(TrStmt(s.Body, indent).ToString()); return wr; } var s0 = (AssignStmt)s.S0; if (s0.Rhs is HavocRhs) { // The forall statement says to havoc a bunch of things. This can be efficiently compiled // into doing nothing. return wr; } var rhs = ((ExprRhs)s0.Rhs).Expr; // Compile: // forall (w,x,y,z | Range(w,x,y,z)) { // LHS(w,x,y,z) := RHS(w,x,y,z); // } // where w,x,y,z have types seq<W>,set<X>,int,bool and LHS has L-1 top-level subexpressions // (that is, L denotes the number of top-level subexpressions of LHS plus 1), // into: // var ingredients = new List< L-Tuple >(); // foreach (W w in sq.UniqueElements) { // foreach (X x in st.Elements) { // for (BigInteger y = Lo; j < Hi; j++) { // for (bool z in Helper.AllBooleans) { // if (Range(w,x,y,z)) { // ingredients.Add(new L-Tuple( LHS0(w,x,y,z), LHS1(w,x,y,z), ..., RHS(w,x,y,z) )); // } // } // } // } // } // foreach (L-Tuple l in ingredients) { // LHS[ l0, l1, l2, ..., l(L-2) ] = l(L-1); // } // // Note, because the .NET Tuple class only supports up to 8 components, the compiler implementation // here supports arrays only up to 6 dimensions. This does not seem like a serious practical limitation. // However, it may be more noticeable if the forall statement supported forall assignments in its // body. To support cases where tuples would need more than 8 components, .NET Tuple's would have to // be nested. // Temporary names var c = idGenerator.FreshNumericId("_ingredients+_tup"); string ingredients = "_ingredients" + c; string tup = "_tup" + c; // Compute L int L; string tupleTypeArgs; if (s0.Lhs is MemberSelectExpr) { var lhs = (MemberSelectExpr)s0.Lhs; L = 2; tupleTypeArgs = TypeName(lhs.Obj.Type, wr); } else if (s0.Lhs is SeqSelectExpr) { var lhs = (SeqSelectExpr)s0.Lhs; L = 3; // note, we might as well do the BigInteger-to-int cast for array indices here, before putting things into the Tuple rather than when they are extracted from the Tuple tupleTypeArgs = TypeName(lhs.Seq.Type, wr) + ",int"; } else { var lhs = (MultiSelectExpr)s0.Lhs; L = 2 + lhs.Indices.Count; if (8 < L) { Error("compiler currently does not support assignments to more-than-6-dimensional arrays in forall statements", wr); return wr; } tupleTypeArgs = TypeName(lhs.Array.Type, wr); for (int i = 0; i < lhs.Indices.Count; i++) { // note, we might as well do the BigInteger-to-int cast for array indices here, before putting things into the Tuple rather than when they are extracted from the Tuple tupleTypeArgs += ",int"; } } tupleTypeArgs += "," + TypeName(rhs.Type, wr); // declare and construct "ingredients" Indent(indent, wr); wr.WriteLine("var {0} = new System.Collections.Generic.List<System.Tuple<{1}>>();", ingredients, tupleTypeArgs); var n = s.BoundVars.Count; Contract.Assert(s.Bounds.Count == n); for (int i = 0; i < n; i++) { var ind = indent + i * IndentAmount; var bound = s.Bounds[i]; var bv = s.BoundVars[i]; if (bound is ComprehensionExpr.BoolBoundedPool) { Indent(ind, wr); wr.Write("foreach (var @{0} in Dafny.Helpers.AllBooleans) {{ ", bv.CompileName); } else if (bound is ComprehensionExpr.CharBoundedPool) { Indent(ind, wr); wr.Write("foreach (var @{0} in Dafny.Helpers.AllChars) {{ ", bv.CompileName); } else if (bound is ComprehensionExpr.IntBoundedPool) { var b = (ComprehensionExpr.IntBoundedPool)bound; Indent(ind, wr); if (AsNativeType(bv.Type) != null) { wr.Write("foreach (var @{0} in @{1}.IntegerRange(", bv.CompileName, bv.Type.AsNewtype.FullCompileName); } else { wr.Write("foreach (var @{0} in Dafny.Helpers.IntegerRange(", bv.CompileName); } TrExpr(b.LowerBound, wr, false); wr.Write(", "); TrExpr(b.UpperBound, wr, false); wr.Write(")) { "); } else if (bound is ComprehensionExpr.SetBoundedPool) { var b = (ComprehensionExpr.SetBoundedPool)bound; Indent(ind, wr); wr.Write("foreach (var @{0} in (", bv.CompileName); TrExpr(b.Set, wr, false); wr.Write(").Elements) { "); } else if (bound is ComprehensionExpr.SeqBoundedPool) { var b = (ComprehensionExpr.SeqBoundedPool)bound; Indent(ind, wr); wr.Write("foreach (var @{0} in (", bv.CompileName); TrExpr(b.Seq, wr, false); wr.Write(").UniqueElements) { "); } else if (bound is ComprehensionExpr.DatatypeBoundedPool) { var b = (ComprehensionExpr.DatatypeBoundedPool)bound; wr.Write("foreach (var @{0} in {1}.AllSingletonConstructors) {{", bv.CompileName, TypeName(bv.Type, wr)); } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected BoundedPool type } wr.WriteLine(); } // if (range) { // ingredients.Add(new L-Tuple( LHS0(w,x,y,z), LHS1(w,x,y,z), ..., RHS(w,x,y,z) )); // } Indent(indent + n * IndentAmount, wr); wr.Write("if ("); foreach (var bv in s.BoundVars) { if (bv.Type.NormalizeExpand() is NatType) { wr.Write("0 <= {0} && ", bv.CompileName); } } TrExpr(s.Range, wr, false); wr.WriteLine(") {"); var indFinal = indent + (n + 1) * IndentAmount; Indent(indFinal, wr); wr.Write("{0}.Add(new System.Tuple<{1}>(", ingredients, tupleTypeArgs); if (s0.Lhs is MemberSelectExpr) { var lhs = (MemberSelectExpr)s0.Lhs; TrExpr(lhs.Obj, wr, false); } else if (s0.Lhs is SeqSelectExpr) { var lhs = (SeqSelectExpr)s0.Lhs; TrExpr(lhs.Seq, wr, false); wr.Write(", (int)("); TrExpr(lhs.E0, wr, false); wr.Write(")"); } else { var lhs = (MultiSelectExpr)s0.Lhs; TrExpr(lhs.Array, wr, false); for (int i = 0; i < lhs.Indices.Count; i++) { wr.Write(", (int)("); TrExpr(lhs.Indices[i], wr, false); wr.Write(")"); } } wr.Write(", "); TrExpr(rhs, wr, false); wr.WriteLine("));"); Indent(indent + n * IndentAmount, wr); wr.WriteLine("}"); for (int i = n; 0 <= --i; ) { Indent(indent + i * IndentAmount, wr); wr.WriteLine("}"); } // foreach (L-Tuple l in ingredients) { // LHS[ l0, l1, l2, ..., l(L-2) ] = l(L-1); // } Indent(indent, wr); wr.WriteLine("foreach (var {0} in {1}) {{", tup, ingredients); Indent(indent + IndentAmount, wr); if (s0.Lhs is MemberSelectExpr) { var lhs = (MemberSelectExpr)s0.Lhs; wr.WriteLine("{0}.Item1.@{1} = {0}.Item2;", tup, lhs.MemberName); } else if (s0.Lhs is SeqSelectExpr) { var lhs = (SeqSelectExpr)s0.Lhs; wr.WriteLine("{0}.Item1[{0}.Item2] = {0}.Item3;", tup); } else { var lhs = (MultiSelectExpr)s0.Lhs; wr.Write("{0}.Item1[", tup); string sep = ""; for (int i = 0; i < lhs.Indices.Count; i++) { wr.Write("{0}{1}.Item{2}", sep, tup, i + 2); sep = ", "; } wr.WriteLine("] = {0}.Item{1};", tup, L); } Indent(indent, wr); wr.WriteLine("}"); } else if (stmt is MatchStmt) { MatchStmt s = (MatchStmt)stmt; // Type source = e; // if (source.is_Ctor0) { // FormalType f0 = ((Dt_Ctor0)source._D).a0; // ... // Body0; // } else if (...) { // ... // } else if (true) { // ... // } if (s.Cases.Count != 0) { string source = idGenerator.FreshId("_source"); Indent(indent, wr); wr.Write("{0} {1} = ", TypeName(cce.NonNull(s.Source.Type), wr), source); TrExpr(s.Source, wr, false); wr.WriteLine(";"); int i = 0; var sourceType = (UserDefinedType)s.Source.Type.NormalizeExpand(); foreach (MatchCaseStmt mc in s.Cases) { MatchCasePrelude(source, sourceType, cce.NonNull(mc.Ctor), mc.Arguments, i, s.Cases.Count, indent, wr); TrStmtList(mc.Body, indent, wr); i++; } Indent(indent, wr); wr.WriteLine("}"); } } else if (stmt is VarDeclStmt) { var s = (VarDeclStmt)stmt; foreach (var local in s.Locals) { TrLocalVar(local, true, indent, wr); } if (s.Update != null) { wr.Write(TrStmt(s.Update, indent).ToString()); } } else if (stmt is LetStmt) { var s = (LetStmt)stmt; for (int i = 0; i < s.LHSs.Count; i++) { var lhs = s.LHSs[i]; if (Contract.Exists(lhs.Vars, bv => !bv.IsGhost)) { TrCasePatternOpt(lhs, s.RHSs[i], null, indent, wr, false); } } } else if (stmt is ModifyStmt) { var s = (ModifyStmt)stmt; if (s.Body != null) { wr.Write(TrStmt(s.Body, indent).ToString()); } } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement } return wr; }
void CompileClassMembers(ClassDecl c, bool forCompanionClass, int indent, TextWriter wr) { Contract.Requires(c != null); Contract.Requires(!forCompanionClass || c is TraitDecl); Contract.Requires(0 <= indent); foreach (var member in c.InheritedMembers) { Contract.Assert(!member.IsGhost && !member.IsStatic); // only non-ghost instance members should ever be added to .InheritedMembers if (member is Field) { var f = (Field)member; // every field is inherited Indent(indent, wr); wr.WriteLine("public {0} @_{1};", TypeName(f.Type, wr), f.CompileName); wr.Write("public {0} @{1}", TypeName(f.Type, wr), f.CompileName); wr.WriteLine(" {"); wr.WriteLine(" get { "); wr.Write("return this.@_{0};", f.CompileName); wr.WriteLine("}"); wr.WriteLine(" set { "); wr.WriteLine("this.@_{0} = value;", f.CompileName); wr.WriteLine("}"); wr.WriteLine("}"); } else if (member is Function) { var f = (Function)member; Contract.Assert(f.Body != null); CompileFunction(indent, f, wr); } else if (member is Method) { var method = (Method)member; Contract.Assert(method.Body != null); CompileMethod(c, indent, method, wr); } else { Contract.Assert(false); // unexpected member } } foreach (MemberDecl member in c.Members) { if (member is Field) { var f = (Field)member; if (f.IsGhost || forCompanionClass) { // emit nothing } else if (c is TraitDecl) { Indent(indent, wr); wr.Write("{0} @{1}", TypeName(f.Type, wr), f.CompileName); wr.WriteLine(" { get; set; }"); } else { Indent(indent, wr); wr.WriteLine("public {0} @{1} = {2};", TypeName(f.Type, wr), f.CompileName, DefaultValue(f.Type, wr)); } } else if (member is Function) { var f = (Function)member; if (f.Body == null && !(c is TraitDecl && !f.IsStatic)) { // A (ghost or non-ghost) function must always have a body, except if it's an instance function in a trait. if (forCompanionClass || Attributes.Contains(f.Attributes, "axiom")) { // suppress error message (in the case of "forCompanionClass", the non-forCompanionClass call will produce the error message) } else { Error("Function {0} has no body", wr, f.FullName); } } else if (f.IsGhost) { // nothing to compile, but we do check for assumes if (f.Body == null) { Contract.Assert(c is TraitDecl && !f.IsStatic); } else { var v = new CheckHasNoAssumes_Visitor(this, wr); v.Visit(f.Body); } } else if (c is TraitDecl && !forCompanionClass) { // include it, unless it's static if (!f.IsStatic) { Indent(indent, wr); wr.Write("{0} @{1}", TypeName(f.ResultType, wr), f.CompileName); wr.Write("("); WriteFormals("", f.Formals, wr); wr.WriteLine(");"); } } else if (forCompanionClass && !f.IsStatic) { // companion classes only has static members } else { CompileFunction(indent, f, wr); } } else if (member is Method) { var m = (Method)member; if (m.Body == null && !(c is TraitDecl && !m.IsStatic)) { // A (ghost or non-ghost) method must always have a body, except if it's an instance method in a trait. if (forCompanionClass || Attributes.Contains(m.Attributes, "axiom")) { // suppress error message (in the case of "forCompanionClass", the non-forCompanionClass call will produce the error message) } else { Error("Method {0} has no body", wr, m.FullName); } } else if (m.IsGhost) { // nothing to compile, but we do check for assumes if (m.Body == null) { Contract.Assert(c is TraitDecl && !m.IsStatic); } else { var v = new CheckHasNoAssumes_Visitor(this, wr); v.Visit(m.Body); } } else if (c is TraitDecl && !forCompanionClass) { // include it, unless it's static if (!m.IsStatic) { Indent(indent, wr); wr.Write("void @{0}", m.CompileName); wr.Write("("); int nIns = WriteFormals("", m.Ins, wr); WriteFormals(nIns == 0 ? "" : ", ", m.Outs, wr); wr.WriteLine(");"); } } else if (forCompanionClass && !m.IsStatic) { // companion classes only has static members } else { CompileMethod(c, indent, m, wr); } } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected member } } }
void TrStmt(Statement stmt) { Contract.Requires(stmt != null); TextWriter wr = new StringWriter(); if (stmt.IsGhost) { var v = new CheckHasNoAssumes_Visitor(this, wr); v.Visit(stmt); return; } if (stmt is PrintStmt) { WriteToken(stmt.Tok); PrintStmt s = (PrintStmt)stmt; using (WriteArray()) { j.WriteValue(KremlinAst.EApp); j.WriteValue("IO.debug_print_string"); // bugbug: is this the correct way to form the function name? using (WriteArray()) { foreach (var arg in s.Args) { TrExpr(arg, false); // bugbug: each argument needs to be converted to a string and concatenated } } } } else if (stmt is BreakStmt) { WriteToken(stmt.Tok); var s = (BreakStmt)stmt; WriteEAbort("BUGBUG BreakStmt is unsupported"); // bugbug: implement } else if (stmt is ProduceStmt) { WriteToken(stmt.Tok); var s = (ProduceStmt)stmt; if (s is YieldStmt) { WriteEAbort("BUGBUG ProduceStmt Yield unsupported"); // bugbug: implement. } else { using (WriteArray()) { j.WriteValue(KremlinAst.EReturn); // Dafny C# generates "return;" because methods are void. But // for Kremlin, if there is one [out] parameter it is translated // as a single return value. if (enclosingMethod.Outs.Count == 0) { WriteEUnit(); // No return value } else { var ReturnValue = enclosingMethod.Outs[0]; WriteEBound(ReturnValue); } } } } else if (stmt is UpdateStmt) { WriteToken(stmt.Tok); var s = (UpdateStmt)stmt; var resolved = s.ResolvedStatements; if (resolved.Count == 1) { TrStmt(resolved[0]); } else { Contract.Assert(s.Lhss.Count == resolved.Count); Contract.Assert(s.Rhss.Count == resolved.Count); // For each LHSS/RHSS pair, generate: // tmpN = rhsN // ... // lhssN = tmpN // so side effects to the LHSS's don't take place until after all // RHSS's have been evaluated. The complication is that the LHSS // may be an EBufWrite to an array element, or an EAssign to // a variable. // In Kremlin, this is: // let tmp0 = rhs0 in // let tmp1 = rhs1 in // ... in // { assign lhss0 = tmp0; // assign lhss1 = tmp1; // ... // } var rhss = new List<IVariable>(); for (int i = 0; i < resolved.Count; i++) { if (!resolved[i].IsGhost) { var lhs = s.Lhss[i]; var rhs = s.Rhss[i]; if (!(rhs is HavocRhs)) { var target = new BoundVar(resolved[i].Tok, idGenerator.FreshId("_rhs"), lhs.Type); rhss.Add(target); // ELet v in { Stmt j.WriteStartArray(); j.WriteValue(KremlinAst.ELet); j.WriteStartArray(); WriteBinder(target, target.Name, true); // lident TrRhs(target, null, rhs); // expr VarTracker.Push(target); // "in" is the contents that follow } } } using (WriteArray()) { j.WriteValue(KremlinAst.ESequence); using (WriteArray()) { for (int i = 0; i < rhss.Count; i++) { TrAssign(s.Lhss[i], rhss[i]); } } } rhss.Reverse(); foreach (var l in rhss) { VarTracker.Pop(l); j.WriteEndArray(); // Closing out the list of binder * expr * expr j.WriteEndArray(); // Closing out the array above ELet } } } else if (stmt is AssignStmt) { WriteToken(stmt.Tok); AssignStmt s = (AssignStmt)stmt; Contract.Assert(!(s.Lhs is SeqSelectExpr) || ((SeqSelectExpr)s.Lhs).SelectOne); // multi-element array assignments are not allowed TrRhs(null, s.Lhs, s.Rhs); } else if (stmt is AssignSuchThatStmt) { var s = (AssignSuchThatStmt)stmt; if (s.AssumeToken != null) { // Note, a non-ghost AssignSuchThatStmt may contain an assume Error("an assume statement cannot be compiled (line {0})", wr, s.AssumeToken.line); } else if (s.MissingBounds != null) { foreach (var bv in s.MissingBounds) { Error("this assign-such-that statement is too advanced for the current compiler; Dafny's heuristics cannot find any bound for variable '{0}' (line {1})", wr, bv.Name, s.Tok.line); } } else { Contract.Assert(s.Bounds != null); // follows from s.MissingBounds == null WriteToken(stmt.Tok); TrAssignSuchThat( s.Lhss.ConvertAll(lhs => ((IdentifierExpr)lhs.Resolved).Var), // the resolver allows only IdentifierExpr left-hand sides s.Expr, s.Bounds, s.Tok.line, false); } } else if (stmt is CallStmt) { WriteToken(stmt.Tok); CallStmt s = (CallStmt)stmt; TrCallStmt(s); } else if (stmt is BlockStmt) { WriteToken(stmt.Tok); using (WriteArray()) { j.WriteValue(KremlinAst.ESequence); using (WriteArray()) { WriteEUnit(); // in case the statement list is empty TrStmtList(((BlockStmt)stmt).Body); } } } else if (stmt is IfStmt) { WriteToken(stmt.Tok); IfStmt s = (IfStmt)stmt; if (s.Guard == null) { // we can compile the branch of our choice if (s.Els == null) { // let's compile the "else" branch, since that involves no work // (still, let's leave a marker in the source code to indicate that this is what we did) j.WriteComment("if (!false) { }"); } else { // let's compile the "then" branch j.WriteComment("if (true)"); TrStmt(s.Thn); } } else { using (WriteArray()) { j.WriteValue(KremlinAst.EIfThenElse); using (WriteArray()) { TrExpr(s.IsExistentialGuard ? Translator.AlphaRename((ExistsExpr)s.Guard, "eg_d", new Translator(null)) : s.Guard, false); // We'd like to do "TrStmt(s.Thn, indent)", except we want the scope of any existential variables to come inside the block using (WriteArray()) { j.WriteValue(KremlinAst.ESequence); using (WriteArray()) { WriteEUnit(); // in case the statement list is empty if (s.IsExistentialGuard) { IntroduceAndAssignBoundVars((ExistsExpr)s.Guard); } TrStmtList(s.Thn.Body); } } using (WriteArray()) { j.WriteValue(KremlinAst.ESequence); using (WriteArray()) { WriteEUnit(); // in case the statement list is empty if (s.Els != null) { TrStmt(s.Els); } } } } } } } else if (stmt is AlternativeStmt) { WriteToken(stmt.Tok); var s = (AlternativeStmt)stmt; WriteEAbort("BUGBUG AlternativeStmt is unsupported"); // bugbug: a cascade of if/else if/else. } else if (stmt is WhileStmt) { WhileStmt s = (WhileStmt)stmt; if (s.Body == null) { return; } WriteToken(stmt.Tok); if (s.Guard == null) { j.WriteComment("while (false) { }"); WriteEUnit(); } else { using (WriteArray()) { j.WriteValue(KremlinAst.EWhile); using (WriteArray()) { // of (expr * expr) TrExpr(s.Guard, false); TrStmt(s.Body); } } } } else if (stmt is AlternativeLoopStmt) { WriteToken(stmt.Tok); var s = (AlternativeLoopStmt)stmt; WriteEAbort("BUGBUG AlternativeLoopStmt is unsupported"); // bugbug: implement } else if (stmt is ForallStmt) { var s = (ForallStmt)stmt; if (s.Kind != ForallStmt.ParBodyKind.Assign) { // Call and Proof have no side effects, so they can simply be optimized away. return; } else if (s.BoundVars.Count == 0) { // the bound variables just spell out a single point, so the forall statement is equivalent to one execution of the body WriteToken(stmt.Tok); TrStmt(s.Body); return; } var s0 = (AssignStmt)s.S0; if (s0.Rhs is HavocRhs) { // The forall statement says to havoc a bunch of things. This can be efficiently compiled // into doing nothing. return; } WriteToken(stmt.Tok); var rhs = ((ExprRhs)s0.Rhs).Expr; WriteEAbort("BUGBUG Forall is unsupported"); // bugbug: implement } else if (stmt is MatchStmt) { WriteToken(stmt.Tok); MatchStmt s = (MatchStmt)stmt; WriteEAbort("BUGBUG MatchStmt is unsupported"); // bugbug: implement } else if (stmt is VarDeclStmt) { var s = (VarDeclStmt)stmt; foreach (var local in s.Locals) { if (!local.IsGhost) { // Note that a new local was introduced, and assume the caller // will inject an ELet in the correct order, to match the // VarTracker state. varDeclsList.Add(local); VarTracker.Push(local); } } if (s.Update != null) { TrStmt(s.Update); } } else if (stmt is LetStmt) { WriteToken(stmt.Tok); var s = (LetStmt)stmt; for (int i = 0; i < s.LHSs.Count; i++) { var lhs = s.LHSs[i]; if (Contract.Exists(lhs.Vars, bv => !bv.IsGhost)) { TrCasePatternOpt(lhs, s.RHSs[i], null, false); } } } else if (stmt is ModifyStmt) { WriteToken(stmt.Tok); var s = (ModifyStmt)stmt; if (s.Body != null) { TrStmt(s.Body); } } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement } }