Example #1
0
    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;
    }
Example #2
0
 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
     }
   }
 }
Example #3
0
        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
              }
        }