From c17bdfd0330a18d20c0697394d40e0b2dc0288ec Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 29 May 2015 15:17:43 -0700 Subject: Changes to ComputeFreeVariables--bug fix as well as beautification --- Source/Dafny/Compiler.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'Source/Dafny/Compiler.cs') diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index 570ac30d..ef5ba69e 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -2863,11 +2863,8 @@ namespace Microsoft.Dafny { } else if (expr is LambdaExpr) { LambdaExpr e = (LambdaExpr)expr; - ISet fvs = new HashSet(); - bool dontCare = false; - Type dontCareT = null; - Translator.ComputeFreeVariables(expr, fvs, ref dontCare, ref dontCare, ref dontCareT, false); + var fvs = Translator.ComputeFreeVariables(expr); var sm = new Dictionary(); var bvars = new List(); -- cgit v1.2.3 From 67c4a534c033a239d47c7bffd9b632ff59ce42a0 Mon Sep 17 00:00:00 2001 From: Bryan Parno Date: Mon, 8 Jun 2015 14:39:25 -0700 Subject: Fix some issues when compiling generic types and generic function method calls --- Source/Dafny/Compiler.cs | 12 +++++++++++- Source/Dafny/Resolver.cs | 3 ++- 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'Source/Dafny/Compiler.cs') diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index ef5ba69e..4d0182dc 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -1116,7 +1116,13 @@ namespace Microsoft.Dafny { return string.Format("new {0}()", s); } else if (type.IsTypeParameter) { var udt = (UserDefinedType)type; - return "default(@" + udt.FullCompileName + ")"; + string s = "default(@" + udt.FullCompileName; + if (udt.TypeArgs.Count != 0) + { + s += "<" + TypeNames(udt.TypeArgs) + ">"; + } + s += ")"; + return s; } else if (type is SetType) { return DafnySetClass + "<" + TypeName(((SetType)type).Arg) + ">.Empty"; } else if (type is MultiSetType) { @@ -2959,6 +2965,10 @@ namespace Microsoft.Dafny { twr.Write(")"); } twr.Write(".@{0}", f.CompileName); + if (f.TypeArgs.Count != 0) { + List typeArgs = f.TypeArgs.ConvertAll(ta => e.TypeArgumentSubstitutions[ta]); + twr.Write("<" + TypeNames(typeArgs) + ">"); + } twr.Write("("); string sep = ""; for (int i = 0; i < e.Args.Count; i++) { diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 52b01d58..5f01f913 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -4129,7 +4129,8 @@ namespace Microsoft.Dafny } else if (r.Type is Resolver_IdentifierExpr.ResolverType_Type) { var d = r.Decl; if (d is OpaqueTypeDecl) { - t.ResolvedParam = ((OpaqueTypeDecl)d).TheType; // resolve like a type parameter, and it may have type parameters if it's an opaque type + t.ResolvedParam = ((OpaqueTypeDecl)d).TheType; // resolve like a type parameter, and it may have type parameters if it's an opaque type + t.ResolvedClass = d; // Store the decl, so the compiler will generate the fully qualified name } else if (d is NewtypeDecl) { var dd = (NewtypeDecl)d; var caller = context as ICallable; -- cgit v1.2.3 From 61d4f1a90833d66c5e959ae5c23b64f3fb23c510 Mon Sep 17 00:00:00 2001 From: Bryan Parno Date: Mon, 8 Jun 2015 17:52:01 -0700 Subject: Update the hash code for datatypes to use the djb2 hash algorithm, rather than xor. The latter produces pessimal performance if the datatype contains duplicate data. --- Source/Dafny/Compiler.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'Source/Dafny/Compiler.cs') diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index 4d0182dc..fd2392e6 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -441,19 +441,19 @@ namespace Microsoft.Dafny { wr.WriteLine(";"); Indent(ind); wr.WriteLine("}"); - // GetHashCode method + // GetHashCode method (Uses the djb2 algorithm) Indent(ind); wr.WriteLine("public override int GetHashCode() {"); - Indent(ind + IndentAmount); wr.Write("return " + constructorIndex); - + Indent(ind + IndentAmount); wr.WriteLine("ulong hash = 5381;"); + Indent(ind + IndentAmount); wr.WriteLine("hash = ((hash << 5) + hash) + {0};", constructorIndex); i = 0; foreach (Formal arg in ctor.Formals) { if (!arg.IsGhost) { string nm = FormalName(arg, i); - wr.Write(" ^ this.@{0}.GetHashCode()", nm); + Indent(ind + IndentAmount); wr.WriteLine("hash = ((hash << 5) + hash) + ((ulong)this.@{0}.GetHashCode());", nm); i++; } } - wr.WriteLine(";"); + Indent(ind + IndentAmount); wr.WriteLine("return (int) hash;"); Indent(ind); wr.WriteLine("}"); if (dt is IndDatatypeDecl) { -- cgit v1.2.3 From 50218b46904369dba030ed97067dacc87937003c Mon Sep 17 00:00:00 2001 From: Bryan Parno Date: Wed, 1 Jul 2015 12:42:55 -0700 Subject: Compile function methods into C# in a more efficient manner, at least for some common expressions. --- Source/Dafny/Compiler.cs | 137 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 133 insertions(+), 4 deletions(-) (limited to 'Source/Dafny/Compiler.cs') diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index fd2392e6..9f5b3e93 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -940,15 +940,144 @@ namespace Microsoft.Dafny { } } - void CompileReturnBody(Expression body, int indent) { - Contract.Requires(0 <= indent); - body = body.Resolved; + void TrCasePatternOpt(CasePattern pat, Expression rhs, string rhs_string, int indent) { + Contract.Requires(pat != null); + Contract.Requires(rhs != null); + if (pat.Var != null) { + // The trivial Dafny "pattern" expression + // var x := G + // is translated into C# as: + // var x := G; + var bv = pat.Var; + if (!bv.IsGhost) { + Indent(indent); + wr.Write("{0} {1} = ", TypeName(bv.Type), "@" + bv.CompileName); + if (rhs != null) { + TrExpr(rhs); + } else { + wr.Write(rhs_string); + } + wr.Write(";\n"); + } + } else if (pat.Arguments != null) { + // The Dafny "pattern" expression + // var Pattern(x,y) := G + // is translated into C# as: + // var tmp := G; + // var x := dtorX(tmp); + // var y := dtorY(tmp); + var ctor = pat.Ctor; + Contract.Assert(ctor != null); // follows from successful resolution + Contract.Assert(pat.Arguments.Count == ctor.Formals.Count); // follows from successful resolution + + // Create the temporary variable to hold G + var tmp_name = idGenerator.FreshId("_let_tmp_rhs"); + Indent(indent); + wr.Write("{0} {1} = ", TypeName(rhs.Type), tmp_name); + TrExpr(rhs); + wr.WriteLine(";"); + + var k = 0; // number of non-ghost formals processed + for (int i = 0; i < pat.Arguments.Count; i++) { + var arg = pat.Arguments[i]; + var formal = ctor.Formals[i]; + if (formal.IsGhost) { + // nothing to compile, but do a sanity check + Contract.Assert(!Contract.Exists(arg.Vars, bv => !bv.IsGhost)); + } else { + TrCasePatternOpt(arg, null, string.Format("(({0})({1})._D).@{2}", DtCtorName(ctor, ((DatatypeValue)pat.Expr).InferredTypeArgs), tmp_name, FormalName(formal, k)), indent); + k++; + } + } + } + } + + void ReturnExpr(Expression expr, int indent) { Indent(indent); wr.Write("return "); - TrExpr(body); + TrExpr(expr); wr.WriteLine(";"); } + void TrExprOpt(Expression expr, int indent) { + Contract.Requires(expr != null); + if (expr is LetExpr) { + var e = (LetExpr)expr; + if (e.Exact) { + for (int i = 0; i < e.LHSs.Count; i++) { + var lhs = e.LHSs[i]; + if (Contract.Exists(lhs.Vars, bv => !bv.IsGhost)) { + TrCasePatternOpt(lhs, e.RHSs[i], null, indent); + } + } + TrExprOpt(e.Body, indent); + } else { + // We haven't optimized the other cases, so fallback to normal compilation + ReturnExpr(e, indent); + } + } else if (expr is ITEExpr) { + ITEExpr e = (ITEExpr)expr; + Indent(indent); + wr.Write("if ("); + TrExpr(e.Test); + wr.Write(") {\n"); + TrExprOpt(e.Thn, indent + IndentAmount); + Indent(indent); + wr.WriteLine("} else {"); + TrExprOpt(e.Els, indent + IndentAmount); + Indent(indent); + wr.WriteLine("}"); + } else if (expr is MatchExpr) { + var e = (MatchExpr)expr; + // var _source = E; + // if (source.is_Ctor0) { + // FormalType f0 = ((Dt_Ctor0)source._D).a0; + // ... + // return Body0; + // } else if (...) { + // ... + // } else if (true) { + // ... + // } + string source = idGenerator.FreshId("_source"); + Indent(indent); + wr.Write("{0} {1} = ", TypeName(e.Source.Type), source); + TrExpr(e.Source); + wr.WriteLine(";"); + + if (e.Cases.Count == 0) { + // the verifier would have proved we never get here; still, we need some code that will compile + wr.Write("throw new System.Exception();"); + } else { + int i = 0; + var sourceType = (UserDefinedType)e.Source.Type.NormalizeExpand(); + foreach (MatchCaseExpr mc in e.Cases) { + //Indent(indent); + MatchCasePrelude(source, sourceType, cce.NonNull(mc.Ctor), mc.Arguments, i, e.Cases.Count, indent); + TrExprOpt(mc.Body, indent + IndentAmount); + i++; + } + Indent(indent); + wr.WriteLine("}"); + } + } else if (expr is StmtExpr) { + var e = (StmtExpr)expr; + TrExprOpt(e.E, indent); + } else { + // We haven't optimized any other cases, so fallback to normal compilation + ReturnExpr(expr, indent); + } + } + + void CompileReturnBody(Expression body, int indent) { + Contract.Requires(0 <= indent); + body = body.Resolved; + //Indent(indent); + //wr.Write("return "); + TrExprOpt(body, indent); + //wr.WriteLine(";"); + } + // ----- Type --------------------------------------------------------------------------------- readonly string DafnySetClass = "Dafny.Set"; -- cgit v1.2.3 From 0dd2dcd63d8daaa10ac532d4927acbf4f01ea186 Mon Sep 17 00:00:00 2001 From: Bryan Parno Date: Thu, 2 Jul 2015 10:10:59 -0700 Subject: Fixed a contract error provoked by one of the tests. --- Source/Dafny/Compiler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Source/Dafny/Compiler.cs') diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index 9f5b3e93..4fdd34f6 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -942,7 +942,7 @@ namespace Microsoft.Dafny { void TrCasePatternOpt(CasePattern pat, Expression rhs, string rhs_string, int indent) { Contract.Requires(pat != null); - Contract.Requires(rhs != null); + Contract.Requires(pat.Var != null || rhs != null); if (pat.Var != null) { // The trivial Dafny "pattern" expression // var x := G -- cgit v1.2.3 From 41d16e5a28b4eab7fb56f04c76759f8e24678b74 Mon Sep 17 00:00:00 2001 From: leino Date: Tue, 11 Aug 2015 23:08:17 -0700 Subject: Bug fixes and improvements in pretty printing --- Source/Dafny/Cloner.cs | 2 +- Source/Dafny/Compiler.cs | 8 +----- Source/Dafny/Printer.cs | 16 +++++++---- Source/Dafny/Resolver.cs | 61 ++++++++++++---------------------------- Source/Dafny/Rewriter.cs | 8 +++--- Source/Dafny/TriggerGenerator.cs | 10 +++---- 6 files changed, 39 insertions(+), 66 deletions(-) (limited to 'Source/Dafny/Compiler.cs') diff --git a/Source/Dafny/Cloner.cs b/Source/Dafny/Cloner.cs index 5f5a9a6e..0a5ca245 100644 --- a/Source/Dafny/Cloner.cs +++ b/Source/Dafny/Cloner.cs @@ -720,7 +720,7 @@ namespace Microsoft.Dafny { Contract.Requires(tok != null); Contract.Requires(s != null); - resolver.ReportAdditionalInformation(tok, s + suffix, s.Length); + resolver.ReportAdditionalInformation(tok, s + suffix); } } diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index 4fdd34f6..f02f7861 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -1201,13 +1201,7 @@ namespace Microsoft.Dafny { Contract.Requires(cce.NonNullElements(targs)); Contract.Ensures(Contract.Result() != null); - string s = ""; - string sep = ""; - foreach (TypeParameter tp in targs) { - s += sep + "@" + tp.CompileName; - sep = ","; - } - return s; + return Util.Comma(targs, tp => "@" + tp.CompileName); } string DefaultValue(Type type) diff --git a/Source/Dafny/Printer.cs b/Source/Dafny/Printer.cs index 6081f2c6..1f63d769 100644 --- a/Source/Dafny/Printer.cs +++ b/Source/Dafny/Printer.cs @@ -1443,7 +1443,7 @@ namespace Microsoft.Dafny { bool parensNeeded = ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext); if (parensNeeded) { wr.Write("("); } - PrintExpr(e.Seq, 0x00, false, false, !parensNeeded && isFollowedBySemicolon, indent); // BOGUS: fix me + PrintExpr(e.Seq, opBindingStrength, false, false, !parensNeeded && isFollowedBySemicolon, indent); wr.Write("["); if (e.SelectOne) { Contract.Assert( e.E0 != null); @@ -1467,7 +1467,7 @@ namespace Microsoft.Dafny { bool parensNeeded = ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext); if (parensNeeded) { wr.Write("("); } - PrintExpr(e.Array, 0x00, false, false, !parensNeeded && isFollowedBySemicolon, indent); // BOGUS: fix me + PrintExpr(e.Array, opBindingStrength, false, false, !parensNeeded && isFollowedBySemicolon, indent); string prefix = "["; foreach (Expression idx in e.Indices) { Contract.Assert(idx != null); @@ -1491,7 +1491,7 @@ namespace Microsoft.Dafny { bool parensNeeded = ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext); if (parensNeeded) { wr.Write("("); } - PrintExpr(e.Seq, 00, false, false, !parensNeeded && isFollowedBySemicolon, indent); // BOGUS: fix me + PrintExpr(e.Seq, opBindingStrength, false, false, !parensNeeded && isFollowedBySemicolon, indent); wr.Write("["); PrintExpression(e.Index, false); wr.Write(" := "); @@ -1804,9 +1804,12 @@ namespace Microsoft.Dafny { } else if (expr is LambdaExpr) { var e = (LambdaExpr)expr; - wr.Write("("); - wr.Write(Util.Comma(e.BoundVars, bv => bv.DisplayName + ":" + bv.Type)); - wr.Write(")"); + bool parensNeeded = !isRightmost; + if (parensNeeded) { wr.Write("("); } + var skipSignatureParens = e.BoundVars.Count == 1 && e.BoundVars[0].Type is InferredTypeProxy; + if (!skipSignatureParens) { wr.Write("("); } + wr.Write(Util.Comma(", ", e.BoundVars, bv => bv.DisplayName + (bv.Type is InferredTypeProxy ? "" : ": " + bv.Type))); + if (!skipSignatureParens) { wr.Write(")"); } if (e.Range != null) { wr.Write(" requires "); PrintExpression(e.Range, false); @@ -1817,6 +1820,7 @@ namespace Microsoft.Dafny { } wr.Write(e.OneShot ? " -> " : " => "); PrintExpression(e.Body, isFollowedBySemicolon); + if (parensNeeded) { wr.Write(")"); } } else if (expr is WildcardExpr) { wr.Write("*"); diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 75d171ac..2da365a9 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -101,13 +101,11 @@ namespace Microsoft.Dafny public Action AdditionalInformationReporter; - internal void ReportAdditionalInformation(IToken token, string text, int length) - { + internal void ReportAdditionalInformation(IToken token, string text) { Contract.Requires(token != null); Contract.Requires(text != null); - Contract.Requires(0 <= length); //FIXME: KRML: This should (probably) be the length of the token if (AdditionalInformationReporter != null) { - AdditionalInformationReporter(new AdditionalInformation { Token = token, Text = text, Length = length }); + AdditionalInformationReporter(new AdditionalInformation { Token = token, Text = text, Length = token.val.Length }); } } @@ -494,7 +492,7 @@ namespace Microsoft.Dafny } foreach (var module in prog.Modules) { foreach (var iter in ModuleDefinition.AllIteratorDecls(module.TopLevelDecls)) { - ReportAdditionalInformation(iter.tok, Printer.IteratorClassToString(iter), iter.Name.Length); + ReportAdditionalInformation(iter.tok, Printer.IteratorClassToString(iter)); } } // fill in other additional information @@ -563,17 +561,10 @@ namespace Microsoft.Dafny showIt = ((Method)m).IsRecursive; } if (showIt) { - s += "decreases "; - if (m.Decreases.Expressions.Count != 0) { - string sep = ""; - foreach (var d in m.Decreases.Expressions) { - s += sep + Printer.ExprToString(d); - sep = ", "; - } - } + s += "decreases " + Util.Comma(", ", m.Decreases.Expressions, Printer.ExprToString); // Note, in the following line, we use the location information for "clbl", not "m". These // are the same, except in the case where "clbl" is a CoLemma and "m" is a prefix lemma. - ReportAdditionalInformation(clbl.Tok, s, clbl.Tok.val.Length); + ReportAdditionalInformation(clbl.Tok, s); } } } @@ -1626,7 +1617,7 @@ namespace Microsoft.Dafny "Hint: try writing a newtype constraint of the form 'i:int | lowerBound <= i < upperBound && (...any additional constraints...)'"); } if (dd.NativeType != null && stringNativeType == null) { - ReportAdditionalInformation(dd.tok, "{:nativeType \"" + dd.NativeType.Name + "\"}", dd.tok.val.Length); + ReportAdditionalInformation(dd.tok, "{:nativeType \"" + dd.NativeType.Name + "\"}"); } } } @@ -1735,7 +1726,7 @@ namespace Microsoft.Dafny foreach (var c in coCandidates) { c.CandidateCall.CoCall = FunctionCallExpr.CoCallResolution.Yes; c.EnclosingCoConstructor.IsCoCall = true; - ReportAdditionalInformation(c.CandidateCall.tok, "co-recursive call", c.CandidateCall.Name.Length); + ReportAdditionalInformation(c.CandidateCall.tok, "co-recursive call"); } // Finally, fill in the CoClusterTarget field // Start by setting all the CoClusterTarget fields to CoRecursiveTargetAllTheWay. @@ -2002,10 +1993,10 @@ namespace Microsoft.Dafny Contract.Requires(args != null); Error(expr.tok, msg, args); } - protected void ReportAdditionalInformation(IToken tok, string text, int length) + protected void ReportAdditionalInformation(IToken tok, string text) { Contract.Requires(tok != null); - resolver.ReportAdditionalInformation(tok, text, length); + resolver.ReportAdditionalInformation(tok, text); } } #endregion Visitors @@ -2041,7 +2032,7 @@ namespace Microsoft.Dafny m.IsTailRecursive = true; if (tailCall != null) { // this means there was at least one recursive call - ReportAdditionalInformation(m.tok, "tail recursive", m.Name.Length); + ReportAdditionalInformation(m.tok, "tail recursive"); } } } @@ -2607,7 +2598,7 @@ namespace Microsoft.Dafny Error(e, msg); } else { e.CoCall = FunctionCallExpr.CoCallResolution.Yes; - ReportAdditionalInformation(e.tok, e.Function.Name + "#[_k - 1]", e.Function.Name.Length); + ReportAdditionalInformation(e.tok, e.Function.Name + "#[_k - 1]"); } } // do the sub-parts with cp := Neither @@ -3011,7 +3002,7 @@ namespace Microsoft.Dafny var substituter = new Translator.AlphaConverting_Substituter(cs.Receiver, argsSubstMap, new Dictionary(), new Translator()); foreach (var ens in cs.Method.Ens) { var p = substituter.Substitute(ens.E); // substitute the call's actuals for the method's formals - resolver.ReportAdditionalInformation(s.Tok, "ensures " + Printer.ExprToString(p) + ";", s.Tok.val.Length); + resolver.ReportAdditionalInformation(s.Tok, "ensures " + Printer.ExprToString(p)); } } } @@ -5217,16 +5208,8 @@ namespace Microsoft.Dafny ResolveStatement(s.Body, bodyMustBeSpecOnly, codeContext); loopStack.RemoveAt(loopStack.Count - 1); // pop } else { - string text = "havoc {"; - if (fvs.Count != 0) { - string sep = ""; - foreach (var fv in fvs) { - text += sep + fv.Name; - sep = ", "; - } - } - text += "};"; // always terminate with a semi-colon - ReportAdditionalInformation(s.Tok, text, s.Tok.val.Length); + string text = "havoc {" + Util.Comma(", ", fvs, fv => fv.Name) + "};"; // always terminate with a semi-colon + ReportAdditionalInformation(s.Tok, text); } } else if (stmt is AlternativeLoopStmt) { @@ -5324,7 +5307,7 @@ namespace Microsoft.Dafny // add the conclusion of the calc as a free postcondition var result = ((CalcStmt)s0).Result; s.Ens.Add(new MaybeFreeExpression(result, true)); - ReportAdditionalInformation(s.Tok, "ensures " + Printer.ExprToString(result) + ";", s.Tok.val.Length); + ReportAdditionalInformation(s.Tok, "ensures " + Printer.ExprToString(result)); } else { s.Kind = ForallStmt.ParBodyKind.Proof; if (s.Body is BlockStmt && ((BlockStmt)s.Body).Body.Count == 0) { @@ -5930,16 +5913,8 @@ namespace Microsoft.Dafny loopStmt.InferredDecreases = true; } if (loopStmt.InferredDecreases) { - string s = "decreases "; - if (theDecreases.Count != 0) { - string sep = ""; - foreach (var d in theDecreases) { - s += sep + Printer.ExprToString(d); - sep = ", "; - } - } - s += ";"; // always terminate with a semi-colon, even in the case of an empty decreases clause - ReportAdditionalInformation(loopStmt.Tok, s, loopStmt.Tok.val.Length); + string s = "decreases " + Util.Comma(", ", theDecreases, Printer.ExprToString); + ReportAdditionalInformation(loopStmt.Tok, s); } } private void ResolveConcreteUpdateStmt(ConcreteUpdateStatement s, bool specContextOnly, ICodeContext codeContext) { @@ -10486,7 +10461,7 @@ namespace Microsoft.Dafny // this call is disqualified from being a co-call, because it has a postcondition // (a postcondition could be allowed, as long as it does not get to be used with // co-recursive calls, because that could be unsound; for example, consider - // "ensures false;") + // "ensures false") if (!dealsWithCodatatypes) { e.CoCall = FunctionCallExpr.CoCallResolution.No; } else { diff --git a/Source/Dafny/Rewriter.cs b/Source/Dafny/Rewriter.cs index 7ab7dd36..4ac709f6 100644 --- a/Source/Dafny/Rewriter.cs +++ b/Source/Dafny/Rewriter.cs @@ -839,7 +839,7 @@ namespace Microsoft.Dafny } if (!tip.Equals("")) { - resolver.ReportAdditionalInformation(f.tok, tip, f.tok.val.Length); + resolver.ReportAdditionalInformation(f.tok, tip); if (DafnyOptions.O.AutoReqPrintFile != null) { using (System.IO.TextWriter writer = new System.IO.StreamWriter(DafnyOptions.O.AutoReqPrintFile, true)) { writer.WriteLine(f.Name); @@ -866,7 +866,7 @@ namespace Microsoft.Dafny } if (!tip.Equals("")) { - resolver.ReportAdditionalInformation(method.tok, tip, method.tok.val.Length); + resolver.ReportAdditionalInformation(method.tok, tip); if (DafnyOptions.O.AutoReqPrintFile != null) { using (System.IO.TextWriter writer = new System.IO.StreamWriter(DafnyOptions.O.AutoReqPrintFile, true)) { writer.WriteLine(method.Name); @@ -1061,7 +1061,7 @@ namespace Microsoft.Dafny Expression allReqsSatisfied = andify(e.Term.tok, auto_reqs); Expression allReqsSatisfiedAndTerm = Expression.CreateAnd(allReqsSatisfied, e.Term); e.UpdateTerm(allReqsSatisfiedAndTerm); - resolver.ReportAdditionalInformation(e.tok, "autoreq added (" + Printer.ExtendedExprToString(allReqsSatisfied) + ") &&", e.tok.val.Length); + resolver.ReportAdditionalInformation(e.tok, "autoreq added (" + Printer.ExtendedExprToString(allReqsSatisfied) + ") &&"); } } else if (expr is SetComprehension) { var e = (SetComprehension)expr; @@ -1410,7 +1410,7 @@ namespace Microsoft.Dafny attributes = new Attributes("_induction", inductionVariables, attributes); // And since we're inferring something, let's also report that in a hover text. var s = Printer.OneAttributeToString(attributes, "induction"); - Resolver.ReportAdditionalInformation(tok, s, s.Length); + Resolver.ReportAdditionalInformation(tok, s); } } class Induction_Visitor : BottomUpVisitor diff --git a/Source/Dafny/TriggerGenerator.cs b/Source/Dafny/TriggerGenerator.cs index cf677e0e..067336a3 100644 --- a/Source/Dafny/TriggerGenerator.cs +++ b/Source/Dafny/TriggerGenerator.cs @@ -126,9 +126,9 @@ namespace Microsoft.Dafny { List quantifiers; Dictionary annotations; - Action AdditionalInformationReporter; + Action AdditionalInformationReporter; - private TriggerGenerator(Action additionalInformationReporter) { + private TriggerGenerator(Action additionalInformationReporter) { Contract.Requires(additionalInformationReporter != null); this.quantifiers = new List(); this.annotations = new Dictionary(); @@ -469,12 +469,12 @@ namespace Microsoft.Dafny { if (multi_candidates.RejectedMultiCandidates.Any()) { var tooltip = JoinStringsWithHeader("Rejected: ", multi_candidates.RejectedMultiCandidates.Where(candidate => candidate.Tags != null) .Select(candidate => candidate.AsDafnyAttributeString(true, true))); - AdditionalInformationReporter(quantifier.tok, tooltip, quantifier.tok.val.Length); + AdditionalInformationReporter(quantifier.tok, tooltip); } if (multi_candidates.FinalMultiCandidates.Any()) { var tooltip = JoinStringsWithHeader("Triggers: ", multi_candidates.FinalMultiCandidates.Select(multi_candidate => multi_candidate.AsDafnyAttributeString())); - AdditionalInformationReporter(quantifier.tok, tooltip, quantifier.tok.val.Length); + AdditionalInformationReporter(quantifier.tok, tooltip); } string warning = multi_candidates.Warning(); @@ -484,7 +484,7 @@ namespace Microsoft.Dafny { } internal static bool IsTriggerKiller(Expression expr) { - var annotation = new TriggerGenerator((x, y, z) => { }).Annotate(expr); + var annotation = new TriggerGenerator((x, y) => { }).Annotate(expr); return annotation.IsTriggerKiller; } -- cgit v1.2.3 From a07b43ac03b38d4af575d1a1df48339ad228751a Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 14 Aug 2015 17:38:08 -0700 Subject: Start committing split quantifiers This requires rewriting the parts of the AST that contain these quantifiers. We unfortunately don't have facilities to do such rewrites at the moment (and there are loops in the AST, so it would be hard to write such a facility). As a workaround, this commit introduces a field in quantifier expressions, SplitQuantifiers. Code that manipulate triggers is expected to look for this field, and ignore the other fields if that one is found. --- Source/Dafny/Cloner.cs | 10 ++- Source/Dafny/Compiler.cs | 6 ++ Source/Dafny/DafnyAst.cs | 54 +++++++++++- Source/Dafny/Printer.cs | 7 ++ Source/Dafny/Resolver.cs | 16 +++- Source/Dafny/Rewriter.cs | 4 +- Source/Dafny/Translator.cs | 125 +++++++++++++++++----------- Source/Dafny/Triggers/QuantifierSplitter.cs | 26 ++++-- Source/Dafny/Triggers/TriggerExtensions.cs | 8 ++ Source/Dafny/Triggers/TriggerGenerator.cs | 10 ++- Source/Dafny/Triggers/TriggerUtils.cs | 1 + Source/Dafny/Triggers/TriggersCollector.cs | 5 +- 12 files changed, 203 insertions(+), 69 deletions(-) (limited to 'Source/Dafny/Compiler.cs') diff --git a/Source/Dafny/Cloner.cs b/Source/Dafny/Cloner.cs index 323abc70..961d4d14 100644 --- a/Source/Dafny/Cloner.cs +++ b/Source/Dafny/Cloner.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Numerics; using System.Diagnostics.Contracts; using IToken = Microsoft.Boogie.IToken; +using System.Linq; namespace Microsoft.Dafny { @@ -373,13 +374,18 @@ namespace Microsoft.Dafny if (e is QuantifierExpr) { var q = (QuantifierExpr)e; var tvs = q.TypeArgs.ConvertAll(CloneTypeParam); + QuantifierExpr cloned; if (e is ForallExpr) { - return new ForallExpr(tk, tvs, bvs, range, term, CloneAttributes(e.Attributes)); + cloned = new ForallExpr(tk, tvs, bvs, range, term, CloneAttributes(e.Attributes)); } else if (e is ExistsExpr) { - return new ExistsExpr(tk, tvs, bvs, range, term, CloneAttributes(e.Attributes)); + cloned = new ExistsExpr(tk, tvs, bvs, range, term, CloneAttributes(e.Attributes)); } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected quantifier expression } + if (q.SplitQuantifier != null) { //TODO CLEMENT TRIGGERS: Should we clone the quantifier as a quantifier in this case? Or should we just replace entirely? + cloned.SplitQuantifier = q.SplitQuantifier.Select(CloneExpr).ToList(); + } + return cloned; } else if (e is MapComprehension) { return new MapComprehension(tk, ((MapComprehension)e).Finite, bvs, range, term, CloneAttributes(e.Attributes)); } else if (e is LambdaExpr) { diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index 4fdd34f6..d19d2bed 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -2816,6 +2816,12 @@ namespace Microsoft.Dafny { } else if (expr is QuantifierExpr) { var e = (QuantifierExpr)expr; + + if (e.SplitQuantifier != null) { //TODO CLEMENT TRIGGERS: Do we compile a split quantifier in its original form, or in its split form? + TrExpr(e.SplitQuantifierExpression); + return; + } + Contract.Assert(e.Bounds != null); // for non-ghost quantifiers, the resolver would have insisted on finding bounds var n = e.BoundVars.Count; Contract.Assert(e.Bounds.Count == n); diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index efe94c66..05548f38 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -1555,7 +1555,7 @@ namespace Microsoft.Dafny { // BUGBUG: The following line is a workaround to tell the verifier that 'value' is not of an Immutable type. // A proper solution would be to be able to express that in the program (in a specification or attribute) or // to be able to declare 'parent' as [PeerOrImmutable]. - Contract.Requires(value is TopLevelDecl || value is Function || value is Method || value is DatatypeCtor || value is QuantifierExpr); + Contract.Requires(value is TopLevelDecl || value is Function || value is Method || value is DatatypeCtor || (value is QuantifierExpr && ((QuantifierExpr)value).SplitQuantifier == null)); //modifies parent; parent = value; } @@ -6552,6 +6552,15 @@ namespace Microsoft.Dafny { } } + /// + /// Returns a resolved binary expression + /// + public BinaryExpr(Boogie.IToken tok, BinaryExpr.ResolvedOpcode rop, Expression e0, Expression e1) + : this(tok, BinaryExpr.ResolvedOp2SyntacticOp(rop), e0, e1) { + ResolvedOp = rop; + Type = Type.Bool; + } + public override IEnumerable SubExpressions { get { yield return E0; @@ -6770,6 +6779,8 @@ namespace Microsoft.Dafny { public List TypeArgs; private static int currentQuantId = -1; + protected abstract BinaryExpr.ResolvedOpcode SplitResolvedOp { get; } + static int FreshQuantId() { return System.Threading.Interlocked.Increment(ref currentQuantId); } @@ -6800,10 +6811,47 @@ namespace Microsoft.Dafny { this.TypeArgs = tvars; this.UniqueId = FreshQuantId(); } + + private Expression SplitQuantifierToExpression() { + Contract.Requires(SplitQuantifier != null && SplitQuantifier.Any()); + Expression accumulator = SplitQuantifier[0]; + for (int tid = 1; tid < SplitQuantifier.Count; tid++) { + var newAcc = new BinaryExpr(Term.tok, SplitResolvedOp, accumulator, SplitQuantifier[tid]); + accumulator = newAcc; + } + return accumulator; + } + + private List _SplitQuantifier; + public List SplitQuantifier { + get { + return _SplitQuantifier; + } + set { + _SplitQuantifier = value; + SplitQuantifierExpression = SplitQuantifierToExpression(); + } + } + + internal Expression SplitQuantifierExpression { get; private set; } + public abstract Expression LogicalBody(); + + public override IEnumerable SubExpressions { + get { + if (SplitQuantifier == null) { + return base.SubExpressions; + } else { + return SplitQuantifier; + } + } + } } public class ForallExpr : QuantifierExpr { + protected override BinaryExpr.Opcode SplitOp { get { return BinaryExpr.Opcode.And; } } + protected override BinaryExpr.ResolvedOpcode SplitResolvedOp { get { return BinaryExpr.ResolvedOpcode.And; } } + public ForallExpr(IToken tok, List bvars, Expression range, Expression term, Attributes attrs) : this(tok, new List(), bvars, range, term, attrs) { Contract.Requires(cce.NonNullElements(bvars)); @@ -6817,6 +6865,7 @@ namespace Microsoft.Dafny { Contract.Requires(term != null); } public override Expression LogicalBody() { + Contract.Assert(SplitQuantifier == null); // Don't call this on a quantifier with a Split clause: it's not a real quantifier if (Range == null) { return Term; } @@ -6828,6 +6877,9 @@ namespace Microsoft.Dafny { } public class ExistsExpr : QuantifierExpr { + protected override BinaryExpr.Opcode SplitOp { get { return BinaryExpr.Opcode.Or; } } + protected override BinaryExpr.ResolvedOpcode SplitResolvedOp { get { return BinaryExpr.ResolvedOpcode.Or; } } + public ExistsExpr(IToken tok, List bvars, Expression range, Expression term, Attributes attrs) : this(tok, new List(), bvars, range, term, attrs) { Contract.Requires(cce.NonNullElements(bvars)); diff --git a/Source/Dafny/Printer.cs b/Source/Dafny/Printer.cs index 1c7508eb..43b69bbb 100644 --- a/Source/Dafny/Printer.cs +++ b/Source/Dafny/Printer.cs @@ -1729,6 +1729,13 @@ namespace Microsoft.Dafny { } else if (expr is QuantifierExpr) { QuantifierExpr e = (QuantifierExpr)expr; + + if (e.SplitQuantifier != null) { + // CLEMENT TODO TRIGGERS: Should (do) we have a setting to print the original forms instead of rewritten forms? + PrintExpr(e.SplitQuantifierExpression, contextBindingStrength, fragileContext, isRightmost, isFollowedBySemicolon, indent, resolv_count); + return; + } + bool parensNeeded = !isRightmost; if (parensNeeded) { wr.Write("("); } wr.Write(e is ForallExpr ? "forall" : "exists"); diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 7bf085f9..e1c3c63b 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -305,7 +305,7 @@ namespace Microsoft.Dafny Contract.Assert(!useCompileSignatures); useCompileSignatures = true; // set Resolver-global flag to indicate that Signatures should be followed to their CompiledSignature var oldErrorsOnly = reporter.ErrorsOnly; - reporter.ErrorsOnly = true; // turn off warning reporting for the clone + reporter.ErrorsOnly = true; // turn off warning reporter for the clone var nw = new Cloner().CloneModuleDefinition(m, m.CompileName + "_Compile"); var compileSig = RegisterTopLevelDecls(nw, true); compileSig.Refines = refinementTransformer.RefinedSig; @@ -338,7 +338,7 @@ namespace Microsoft.Dafny if (refinementTransformer.CheckIsRefinement(compileSig, p)) { abs.Signature.CompileSignature = compileSig; } else { - reporter.Error(MessageSource.Resolver, + reporter.Error(MessageSource.Resolver, abs.CompilePath[0], "module " + Util.Comma(".", abs.CompilePath, x => x.val) + " must be a refinement of " + Util.Comma(".", abs.Path, x => x.val)); @@ -2438,6 +2438,7 @@ namespace Microsoft.Dafny return false; } else if (expr is QuantifierExpr) { var e = (QuantifierExpr)expr; + Contract.Assert(e.SplitQuantifier == null); // No split quantifiers during resolution var cpos = IsCoContext ? cp : Invert(cp); if ((cpos == CallingPosition.Positive && e is ExistsExpr) || (cpos == CallingPosition.Negative && e is ForallExpr)) { if (e.MissingBounds != null && e.MissingBounds.Count != 0) { @@ -6962,7 +6963,7 @@ namespace Microsoft.Dafny reporter.Error(MessageSource.Resolver, expr, "'this' is not allowed in a 'static' context"); } if (currentClass != null) { - expr.Type = GetThisType(expr.tok, currentClass); // do this regardless of scope.AllowInstance, for better error reporting + expr.Type = GetThisType(expr.tok, currentClass); // do this regardless of scope.AllowInstance, for better error reporter } } else if (expr is IdentifierExpr) { @@ -7605,6 +7606,7 @@ namespace Microsoft.Dafny e.Type = e.Body.Type; } else if (expr is QuantifierExpr) { QuantifierExpr e = (QuantifierExpr)expr; + Contract.Assert(e.SplitQuantifier == null); // No split quantifiers during resolution int prevErrorCount = reporter.Count(ErrorLevel.Error); bool _val = true; bool typeQuantifier = Attributes.ContainsBool(e.Attributes, "typeQuantifier", ref _val) && _val; @@ -9177,6 +9179,7 @@ namespace Microsoft.Dafny return; } else if (expr is QuantifierExpr) { var e = (QuantifierExpr)expr; + Contract.Assert(e.SplitQuantifier == null); // No split quantifiers during resolution if (e.MissingBounds != null) { foreach (var bv in e.MissingBounds) { reporter.Error(MessageSource.Resolver, expr, "quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name); @@ -9774,10 +9777,13 @@ namespace Microsoft.Dafny if (expr is IdentifierExpr) { var e = (IdentifierExpr)expr; + return new HashSet() { e.Var }; } else if (expr is QuantifierExpr) { var e = (QuantifierExpr)expr; + Contract.Assert(e.SplitQuantifier == null); // No split quantifiers during resolution + var s = FreeVariables(e.LogicalBody()); foreach (var bv in e.BoundVars) { s.Remove(bv); @@ -10139,7 +10145,9 @@ namespace Microsoft.Dafny } else if (expr is NamedExpr) { return moduleInfo.IsGhost ? false : UsesSpecFeatures(((NamedExpr)expr).Body); } else if (expr is ComprehensionExpr) { - if (expr is QuantifierExpr && ((QuantifierExpr)expr).Bounds == null) { + var q = expr as QuantifierExpr; + Contract.Assert(q == null || q.SplitQuantifier == null); // No split quantifiers during resolution + if (q != null && q.Bounds == null) { return true; // the quantifier cannot be compiled if the resolver found no bounds } return Contract.Exists(expr.SubExpressions, se => UsesSpecFeatures(se)); diff --git a/Source/Dafny/Rewriter.cs b/Source/Dafny/Rewriter.cs index 0798510a..2c00e203 100644 --- a/Source/Dafny/Rewriter.cs +++ b/Source/Dafny/Rewriter.cs @@ -43,9 +43,9 @@ namespace Microsoft.Dafny Contract.Requires(reporter != null); } - internal override void PostResolve(ModuleDefinition m) { + internal override void PostCyclicityResolve(ModuleDefinition m) { var finder = new Triggers.QuantifierCollectionsFinder(reporter); - + foreach (var decl in ModuleDefinition.AllCallables(m.TopLevelDecls)) { if (decl is Function) { var function = (Function)decl; diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 14fca463..f0b7f276 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -4465,6 +4465,11 @@ namespace Microsoft.Dafny { var e = (ComprehensionExpr)expr; var canCall = CanCallAssumption(e.Term, etran); var q = e as QuantifierExpr; + + if (q != null && q.SplitQuantifier != null) { + return CanCallAssumption(q.SplitQuantifierExpression, etran); + } + var tyvars = MkTyParamBinders(q != null ? q.TypeArgs : new List()); if (e.Range != null) { canCall = BplAnd(CanCallAssumption(e.Range, etran), BplImp(etran.TrExpr(e.Range), canCall)); @@ -4758,6 +4763,12 @@ namespace Microsoft.Dafny { // If the quantifier is universal, then continue as: // assume (\forall x :: body(x)); // Create local variables corresponding to the type arguments: + + if (e.SplitQuantifier != null) { + CheckWellformedAndAssume(e.SplitQuantifierExpression, options, locals, builder, etran); + return; + } + var typeArgumentCopies = Map(e.TypeArgs, tp => e.Refresh(tp, CurrentIdGenerator)); var typeMap = Util.Dict(e.TypeArgs, Map(typeArgumentCopies, tp => (Type)new UserDefinedType(tp))); var newLocals = Map(typeArgumentCopies, tp => new Bpl.LocalVariable(tp.tok, new TypedIdent(tp.tok, nameTypeParam(tp), predef.Ty))); @@ -5293,6 +5304,10 @@ namespace Microsoft.Dafny { var q = e as QuantifierExpr; var lam = e as LambdaExpr; + if (q != null && q.SplitQuantifier != null) { + CheckWellformedWithResult(q.SplitQuantifierExpression, options, result, resultType, locals, builder, etran); + } + var typeMap = new Dictionary(); var copies = new List(); if (q != null) { @@ -11534,56 +11549,60 @@ namespace Microsoft.Dafny { return TrExpr(((NamedExpr)expr).Body); } else if (expr is QuantifierExpr) { QuantifierExpr e = (QuantifierExpr)expr; - List tyvars = translator.MkTyParamBinders(e.TypeArgs); - List bvars = new List(); - - var initEtran = this; - var bodyEtran = this; - bool _scratch = true; - Bpl.Expr antecedent = Bpl.Expr.True; - - if (Attributes.ContainsBool(e.Attributes, "layerQuantifier", ref _scratch)) { - // If this is a layer quantifier, quantify over layers here, and use $LS(ly) layers in the translation of the body - var ly = BplBoundVar(e.Refresh("q$ly#", translator.CurrentIdGenerator), predef.LayerType, bvars); - bodyEtran = new ExpressionTranslator(translator, predef, HeapExpr, This, applyLimited_CurrentFunction, new FuelSetting(translator, 1, ly), new FuelSetting(translator, 1, ly), modifiesFrame, stripLits); - } - if (Attributes.ContainsBool(e.Attributes, "heapQuantifier", ref _scratch)) { - var h = BplBoundVar(e.Refresh("q$heap#", translator.CurrentIdGenerator), predef.HeapType, bvars); - bodyEtran = new ExpressionTranslator(bodyEtran, h); - antecedent = BplAnd(new List { - antecedent, - translator.FunctionCall(e.tok, BuiltinFunction.IsGoodHeap, null, h), - translator.HeapSameOrSucc(initEtran.HeapExpr, h) - }); - } + if (e.SplitQuantifier != null) { + return TrExpr(e.SplitQuantifierExpression); + } else { + List tyvars = translator.MkTyParamBinders(e.TypeArgs); + List bvars = new List(); + + var initEtran = this; + var bodyEtran = this; + bool _scratch = true; + + Bpl.Expr antecedent = Bpl.Expr.True; + + if (Attributes.ContainsBool(e.Attributes, "layerQuantifier", ref _scratch)) { + // If this is a layer quantifier, quantify over layers here, and use $LS(ly) layers in the translation of the body + var ly = BplBoundVar(e.Refresh("q$ly#", translator.CurrentIdGenerator), predef.LayerType, bvars); + bodyEtran = new ExpressionTranslator(translator, predef, HeapExpr, This, applyLimited_CurrentFunction, new FuelSetting(translator, 1, ly), new FuelSetting(translator, 1, ly), modifiesFrame, stripLits); + } + if (Attributes.ContainsBool(e.Attributes, "heapQuantifier", ref _scratch)) { + var h = BplBoundVar(e.Refresh("q$heap#", translator.CurrentIdGenerator), predef.HeapType, bvars); + bodyEtran = new ExpressionTranslator(bodyEtran, h); + antecedent = BplAnd(new List { + antecedent, + translator.FunctionCall(e.tok, BuiltinFunction.IsGoodHeap, null, h), + translator.HeapSameOrSucc(initEtran.HeapExpr, h) + }); + } - antecedent = BplAnd(antecedent, bodyEtran.TrBoundVariables(e.BoundVars, bvars)); + antecedent = BplAnd(antecedent, bodyEtran.TrBoundVariables(e.BoundVars, bvars)); - Bpl.QKeyValue kv = TrAttributes(e.Attributes, "trigger"); - Bpl.Trigger tr = null; - var argsEtran = bodyEtran.WithNoLits(); - foreach (var aa in e.Attributes.AsEnumerable()) { - if (aa.Name == "trigger") { - List tt = new List(); - foreach (var arg in aa.Args) { - tt.Add(argsEtran.TrExpr(arg)); + Bpl.QKeyValue kv = TrAttributes(e.Attributes, "trigger"); + Bpl.Trigger tr = null; + var argsEtran = bodyEtran.WithNoLits(); + foreach (var aa in e.Attributes.AsEnumerable()) { + if (aa.Name == "trigger") { + List tt = new List(); + foreach (var arg in aa.Args) { + tt.Add(argsEtran.TrExpr(arg)); + } + tr = new Bpl.Trigger(expr.tok, true, tt, tr); } - tr = new Bpl.Trigger(expr.tok, true, tt, tr); } - } - if (e.Range != null) { - antecedent = BplAnd(antecedent, bodyEtran.TrExpr(e.Range)); - } - Bpl.Expr body = bodyEtran.TrExpr(e.Term); + if (e.Range != null) { + antecedent = BplAnd(antecedent, bodyEtran.TrExpr(e.Range)); + } + Bpl.Expr body = bodyEtran.TrExpr(e.Term); - if (e is ForallExpr) { - return new Bpl.ForallExpr(expr.tok, new List(), Concat(tyvars, bvars), kv, tr, Bpl.Expr.Imp(antecedent, body)); - } else { - Contract.Assert(e is ExistsExpr); - return new Bpl.ExistsExpr(expr.tok, new List(), Concat(tyvars, bvars), kv, tr, Bpl.Expr.And(antecedent, body)); + if (e is ForallExpr) { + return new Bpl.ForallExpr(expr.tok, new List(), Concat(tyvars, bvars), kv, tr, Bpl.Expr.Imp(antecedent, body)); + } else { + Contract.Assert(e is ExistsExpr); + return new Bpl.ExistsExpr(expr.tok, new List(), Concat(tyvars, bvars), kv, tr, Bpl.Expr.And(antecedent, body)); + } } - } else if (expr is SetComprehension) { var e = (SetComprehension)expr; // Translate "set xs | R :: T" into "lambda y: BoxType :: (exists xs :: CorrectType(xs) && R && y==Box(T))". @@ -12966,9 +12985,11 @@ namespace Microsoft.Dafny { } } + } else if (expr is QuantifierExpr && ((QuantifierExpr)expr).SplitQuantifier != null) { + return TrSplitExpr(((QuantifierExpr)expr).SplitQuantifierExpression, splits, position, heightLimit, apply_induction, etran); } else if (((position && expr is ForallExpr) || (!position && expr is ExistsExpr)) - /* NB: only for type arg less quantifiers for now: */ - && ((QuantifierExpr)expr).TypeArgs.Count == 0) { + /* NB: only for type arg less quantifiers for now: */ + && ((QuantifierExpr)expr).TypeArgs.Count == 0) { var e = (QuantifierExpr)expr; var inductionVariables = ApplyInduction(e); if (apply_induction && 2 <= DafnyOptions.O.Induction && inductionVariables.Count != 0) { @@ -13138,8 +13159,11 @@ namespace Microsoft.Dafny { protected override void VisitOneExpr(Expression expr) { if (expr is QuantifierExpr) { - foreach (var trigger in (expr as QuantifierExpr).Attributes.AsEnumerable().Where(a => a.Name == "trigger").SelectMany(a => a.Args)) { - collector.Visit(trigger); + var e = (QuantifierExpr)expr; + if (e.SplitQuantifier == null) { + foreach (var trigger in (expr as QuantifierExpr).Attributes.AsEnumerable().Where(a => a.Name == "trigger").SelectMany(a => a.Args)) { + collector.Visit(trigger); + } } } } @@ -13173,6 +13197,7 @@ namespace Microsoft.Dafny { } List ApplyInduction(QuantifierExpr e) { + Contract.Requires(e.SplitQuantifier == null); // Don't call this on a quantifier with a Split clause: it's not a real quantifier Contract.Requires(e.TypeArgs.Count == 0); return ApplyInduction(e.BoundVars, e.Attributes, new List() { e.LogicalBody() }, delegate(System.IO.TextWriter wr) { new Printer(wr).PrintExpression(e, true); }); @@ -13946,6 +13971,12 @@ namespace Microsoft.Dafny { var e = (ComprehensionExpr)expr; // For quantifiers and setComprehesion we want to make sure that we don't introduce name clashes with // the enclosing scopes. + + var q = e as QuantifierExpr; + if (q != null && q.SplitQuantifier != null) { + return Substitute(q.SplitQuantifierExpression); + } + var newBoundVars = CreateBoundVarSubstitutions(e.BoundVars, expr is ForallExpr || expr is ExistsExpr || expr is SetComprehension); Expression newRange = e.Range == null ? null : Substitute(e.Range); Expression newTerm = Substitute(e.Term); diff --git a/Source/Dafny/Triggers/QuantifierSplitter.cs b/Source/Dafny/Triggers/QuantifierSplitter.cs index 33be0da2..80381f0a 100644 --- a/Source/Dafny/Triggers/QuantifierSplitter.cs +++ b/Source/Dafny/Triggers/QuantifierSplitter.cs @@ -21,30 +21,37 @@ namespace Microsoft.Dafny.Triggers { // Unfortunately, this would cause ill-behaved quantifiers to produce // exponentially many smaller quantifiers. + private static UnaryOpExpr Not(Expression expr) { + var not = new UnaryOpExpr(expr.tok, UnaryOpExpr.Opcode.Not, expr) { Type = expr.Type }; + return not; + } + internal static IEnumerable SplitExpr(Expression expr, BinaryExpr.Opcode separator) { expr = expr.Resolved; var unary = expr as UnaryOpExpr; var binary = expr as BinaryExpr; if (unary != null && unary.Op == UnaryOpExpr.Opcode.Not) { - foreach (var e in SplitExpr(unary.E, FlipOpcode(separator))) { yield return new UnaryOpExpr(unary.tok, UnaryOpExpr.Opcode.Not, e); } + foreach (var e in SplitExpr(unary.E, FlipOpcode(separator))) { yield return Not(e); } } else if (binary != null && binary.Op == separator) { foreach (var e in SplitExpr(binary.E0, separator)) { yield return e; } foreach (var e in SplitExpr(binary.E1, separator)) { yield return e; } } else if (binary != null && binary.Op == BinaryExpr.Opcode.Imp && separator == BinaryExpr.Opcode.Or) { - foreach (var e in SplitExpr(new UnaryOpExpr(unary.tok, UnaryOpExpr.Opcode.Not, binary.E0), separator)) { yield return e; } + foreach (var e in SplitExpr(Not(binary.E0), separator)) { yield return e; } foreach (var e in SplitExpr(binary.E1, separator)) { yield return e; } + } else { + yield return expr; } } internal static IEnumerable SplitAndStich(BinaryExpr pair, BinaryExpr.Opcode separator) { - foreach (var e1 in SplitExpr(pair.E1, separator)) { - yield return new BinaryExpr(pair.tok, pair.Op, pair.E0, e1); + foreach (var e1 in SplitExpr(pair.E1, separator)) { + yield return new BinaryExpr(pair.tok, pair.Op, pair.E0, e1) { ResolvedOp = pair.ResolvedOp, Type = pair.Type }; } } internal static IEnumerable SplitQuantifier(QuantifierExpr quantifier) { - var body = quantifier.LogicalBody(); + var body = quantifier.Term; var binary = body as BinaryExpr; if (quantifier is ForallExpr) { @@ -55,7 +62,7 @@ namespace Microsoft.Dafny.Triggers { stream = SplitExpr(body, BinaryExpr.Opcode.And); } foreach (var e in stream) { - yield return new ForallExpr(quantifier.tok, quantifier.BoundVars, quantifier.Range, quantifier.Term, quantifier.Attributes); + yield return new ForallExpr(quantifier.tok, quantifier.BoundVars, quantifier.Range, e, quantifier.Attributes) { Type = quantifier.Type }; } } else if (quantifier is ExistsExpr) { IEnumerable stream; @@ -65,17 +72,18 @@ namespace Microsoft.Dafny.Triggers { stream = SplitExpr(body, BinaryExpr.Opcode.Or); } foreach (var e in stream) { - yield return new ExistsExpr(quantifier.tok, quantifier.BoundVars, quantifier.Range, quantifier.Term, quantifier.Attributes); + yield return new ExistsExpr(quantifier.tok, quantifier.BoundVars, quantifier.Range, e, quantifier.Attributes) { Type = quantifier.Type }; } } else { yield return quantifier; } } - protected override void VisitOneExpr(Expression expr) { //FIXME: This doesn't save the rewritten quantifier + protected override void VisitOneExpr(Expression expr) { var quantifier = expr as QuantifierExpr; if (quantifier != null) { - var rew = SplitQuantifier(quantifier); + var split = SplitQuantifier(quantifier).ToList(); + quantifier.SplitQuantifier = split; //Console.WriteLine("!!! {0} => {1}", Printer.ExprToString(expr), rew.MapConcat(Printer.ExprToString, " ||| ")); } } diff --git a/Source/Dafny/Triggers/TriggerExtensions.cs b/Source/Dafny/Triggers/TriggerExtensions.cs index da43abcc..9fbc8a8a 100644 --- a/Source/Dafny/Triggers/TriggerExtensions.cs +++ b/Source/Dafny/Triggers/TriggerExtensions.cs @@ -272,6 +272,14 @@ namespace Microsoft.Dafny.Triggers { } private static bool ShallowEq(QuantifierExpr expr1, QuantifierExpr expr2) { //FIXME are these TypeArgs still useful? + if (!TriggerUtils.SameNullity(expr1.SplitQuantifier, expr2.SplitQuantifier)) { + return false; + } + + if (expr1.SplitQuantifier != null && expr2.SplitQuantifier != null) { + return ShallowEq_Top(expr1.SplitQuantifierExpression, expr2.SplitQuantifierExpression); + } + if (expr1.TypeArgs.Count != expr2.TypeArgs.Count || !TriggerUtils.SameNullity(expr1.Range, expr2.Range)) { return false; diff --git a/Source/Dafny/Triggers/TriggerGenerator.cs b/Source/Dafny/Triggers/TriggerGenerator.cs index de4b212b..e218ad7b 100644 --- a/Source/Dafny/Triggers/TriggerGenerator.cs +++ b/Source/Dafny/Triggers/TriggerGenerator.cs @@ -19,9 +19,13 @@ namespace Microsoft.Dafny.Triggers { //FIXME rename this file protected override bool VisitOneExpr(Expression expr, ref object st) { var quantifier = expr as QuantifierExpr; if (quantifier != null) { - quantifierCollections.Add(new QuantifiersCollection(Enumerable.Repeat(quantifier, 1), reporter)); - } //FIXME handle the case of groups of quantifiers resulting from a split - + if (quantifier.SplitQuantifier != null) { + var collection = quantifier.SplitQuantifier.Select(q => q as QuantifierExpr).Where(q => q != null); + quantifierCollections.Add(new QuantifiersCollection(collection, reporter)); + } else { + quantifierCollections.Add(new QuantifiersCollection(Enumerable.Repeat(quantifier, 1), reporter)); + } + } return true; } } diff --git a/Source/Dafny/Triggers/TriggerUtils.cs b/Source/Dafny/Triggers/TriggerUtils.cs index dedbcbb1..6c6eede2 100644 --- a/Source/Dafny/Triggers/TriggerUtils.cs +++ b/Source/Dafny/Triggers/TriggerUtils.cs @@ -101,6 +101,7 @@ namespace Microsoft.Dafny.Triggers { } internal static bool NeedsAutoTriggers(QuantifierExpr quantifier) { + Contract.Requires(quantifier.SplitQuantifier == null); // Don't call this on a quantifier with a Split clause: it's not a real quantifier return quantifier.Attributes.AsEnumerable().All(aa => aa.Name != "trigger" && aa.Name != "no_trigger"); } } diff --git a/Source/Dafny/Triggers/TriggersCollector.cs b/Source/Dafny/Triggers/TriggersCollector.cs index e1f8a013..9f721d9a 100644 --- a/Source/Dafny/Triggers/TriggersCollector.cs +++ b/Source/Dafny/Triggers/TriggersCollector.cs @@ -44,11 +44,13 @@ namespace Microsoft.Dafny.Triggers { } internal IEnumerable LoopingSubterms(QuantifierExpr quantifier) { + Contract.Requires(quantifier.SplitQuantifier == null); // Don't call this on a quantifier with a Split clause: it's not a real quantifier var matchingSubterms = MatchingSubterms(quantifier); return matchingSubterms.Where(tm => tm.CouldCauseLoops(Terms)); } internal List MatchingSubterms(QuantifierExpr quantifier) { + Contract.Requires(quantifier.SplitQuantifier == null); // Don't call this on a quantifier with a Split clause: it's not a real quantifier //FIXME this will miss rewritten expressions (CleanupExpr). Should introduce an OriginalExpr to compare against. return Terms.SelectMany(term => quantifier.SubexpressionsMatchingTrigger(term.Expr)).Deduplicate(TriggerMatch.Eq); } @@ -139,7 +141,7 @@ namespace Microsoft.Dafny.Triggers { (expr is UnaryOpExpr && (((UnaryOpExpr)expr).Op == UnaryOpExpr.Opcode.Cardinality)) || // FIXME || ((UnaryOpExpr)expr).Op == UnaryOpExpr.Opcode.Fresh doesn't work, as fresh is a pretty tricky predicate when it's not about datatypes. See translator.cs:10944 (expr is BinaryExpr && (((BinaryExpr)expr).Op == BinaryExpr.Opcode.NotIn || ((BinaryExpr)expr).Op == BinaryExpr.Opcode.In))) { annotation = AnnotatePotentialCandidate(expr); - } else if (expr is QuantifierExpr) { + } else if (expr is QuantifierExpr && ((QuantifierExpr)expr).SplitQuantifier == null) { annotation = AnnotateQuantifier((QuantifierExpr)expr); } else if (expr is LetExpr) { annotation = AnnotateLetExpr((LetExpr)expr); @@ -259,6 +261,7 @@ namespace Microsoft.Dafny.Triggers { // FIXME document that this will contain duplicates internal List CollectTriggers(QuantifierExpr quantifier) { + Contract.Requires(quantifier.SplitQuantifier == null); // Don't call this on a quantifier with a Split clause: it's not a real quantifier // TODO could check for existing triggers and return that instead, but that require a bit of work to extract the expressions return Annotate(quantifier).PrivateTerms; } -- cgit v1.2.3 From db9821ac440cdfa817049ab83c2e94f861ff429d Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Mon, 17 Aug 2015 09:37:47 -0700 Subject: Review preceding commit with Rustan --- Source/Dafny/Cloner.cs | 9 ++------- Source/Dafny/Compiler.cs | 7 ++----- Source/Dafny/DafnyAst.cs | 23 +++++++++-------------- Source/Dafny/Printer.cs | 3 +-- Source/Dafny/Translator.cs | 1 + 5 files changed, 15 insertions(+), 28 deletions(-) (limited to 'Source/Dafny/Compiler.cs') diff --git a/Source/Dafny/Cloner.cs b/Source/Dafny/Cloner.cs index 961d4d14..e89a385f 100644 --- a/Source/Dafny/Cloner.cs +++ b/Source/Dafny/Cloner.cs @@ -374,18 +374,13 @@ namespace Microsoft.Dafny if (e is QuantifierExpr) { var q = (QuantifierExpr)e; var tvs = q.TypeArgs.ConvertAll(CloneTypeParam); - QuantifierExpr cloned; if (e is ForallExpr) { - cloned = new ForallExpr(tk, tvs, bvs, range, term, CloneAttributes(e.Attributes)); + return new ForallExpr(tk, tvs, bvs, range, term, CloneAttributes(e.Attributes)); } else if (e is ExistsExpr) { - cloned = new ExistsExpr(tk, tvs, bvs, range, term, CloneAttributes(e.Attributes)); + return new ExistsExpr(tk, tvs, bvs, range, term, CloneAttributes(e.Attributes)); } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected quantifier expression } - if (q.SplitQuantifier != null) { //TODO CLEMENT TRIGGERS: Should we clone the quantifier as a quantifier in this case? Or should we just replace entirely? - cloned.SplitQuantifier = q.SplitQuantifier.Select(CloneExpr).ToList(); - } - return cloned; } else if (e is MapComprehension) { return new MapComprehension(tk, ((MapComprehension)e).Finite, bvs, range, term, CloneAttributes(e.Attributes)); } else if (e is LambdaExpr) { diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index d19d2bed..c969ac1f 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -2817,10 +2817,7 @@ namespace Microsoft.Dafny { } else if (expr is QuantifierExpr) { var e = (QuantifierExpr)expr; - if (e.SplitQuantifier != null) { //TODO CLEMENT TRIGGERS: Do we compile a split quantifier in its original form, or in its split form? - TrExpr(e.SplitQuantifierExpression); - return; - } + // Compilation does not check whether a quantifier was split. Contract.Assert(e.Bounds != null); // for non-ghost quantifiers, the resolver would have insisted on finding bounds var n = e.BoundVars.Count; @@ -2864,7 +2861,7 @@ namespace Microsoft.Dafny { wr.Write("{0}, ", expr is ForallExpr ? "true" : "false"); wr.Write("@{0} => ", bv.CompileName); } - TrExpr(e.LogicalBody()); + TrExpr(e.LogicalBody(true)); for (int i = 0; i < n; i++) { wr.Write(")"); } diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index 05548f38..a902a18c 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -1552,11 +1552,6 @@ namespace Microsoft.Dafny { set { Contract.Requires(Parent == null); // set it only once Contract.Requires(value != null); - // BUGBUG: The following line is a workaround to tell the verifier that 'value' is not of an Immutable type. - // A proper solution would be to be able to express that in the program (in a specification or attribute) or - // to be able to declare 'parent' as [PeerOrImmutable]. - Contract.Requires(value is TopLevelDecl || value is Function || value is Method || value is DatatypeCtor || (value is QuantifierExpr && ((QuantifierExpr)value).SplitQuantifier == null)); - //modifies parent; parent = value; } } @@ -6816,8 +6811,7 @@ namespace Microsoft.Dafny { Contract.Requires(SplitQuantifier != null && SplitQuantifier.Any()); Expression accumulator = SplitQuantifier[0]; for (int tid = 1; tid < SplitQuantifier.Count; tid++) { - var newAcc = new BinaryExpr(Term.tok, SplitResolvedOp, accumulator, SplitQuantifier[tid]); - accumulator = newAcc; + accumulator = new BinaryExpr(Term.tok, SplitResolvedOp, accumulator, SplitQuantifier[tid]); } return accumulator; } @@ -6835,7 +6829,11 @@ namespace Microsoft.Dafny { internal Expression SplitQuantifierExpression { get; private set; } - public abstract Expression LogicalBody(); + public virtual Expression LogicalBody(bool bypassSplitQuantifier = false) { + // Don't call this on a quantifier with a Split clause: it's not a real quantifier. The only exception is the Compiler. + Contract.Requires(bypassSplitQuantifier || SplitQuantifier == null); + throw new cce.UnreachableException(); // This body is just here for the "Requires" clause + } public override IEnumerable SubExpressions { get { @@ -6847,9 +6845,8 @@ namespace Microsoft.Dafny { } } } - + public class ForallExpr : QuantifierExpr { - protected override BinaryExpr.Opcode SplitOp { get { return BinaryExpr.Opcode.And; } } protected override BinaryExpr.ResolvedOpcode SplitResolvedOp { get { return BinaryExpr.ResolvedOpcode.And; } } public ForallExpr(IToken tok, List bvars, Expression range, Expression term, Attributes attrs) @@ -6864,8 +6861,7 @@ namespace Microsoft.Dafny { Contract.Requires(tok != null); Contract.Requires(term != null); } - public override Expression LogicalBody() { - Contract.Assert(SplitQuantifier == null); // Don't call this on a quantifier with a Split clause: it's not a real quantifier + public override Expression LogicalBody(bool bypassSplitQuantifier = false) { if (Range == null) { return Term; } @@ -6877,7 +6873,6 @@ namespace Microsoft.Dafny { } public class ExistsExpr : QuantifierExpr { - protected override BinaryExpr.Opcode SplitOp { get { return BinaryExpr.Opcode.Or; } } protected override BinaryExpr.ResolvedOpcode SplitResolvedOp { get { return BinaryExpr.ResolvedOpcode.Or; } } public ExistsExpr(IToken tok, List bvars, Expression range, Expression term, Attributes attrs) @@ -6892,7 +6887,7 @@ namespace Microsoft.Dafny { Contract.Requires(tok != null); Contract.Requires(term != null); } - public override Expression LogicalBody() { + public override Expression LogicalBody(bool bypassSplitQuantifier = false) { if (Range == null) { return Term; } diff --git a/Source/Dafny/Printer.cs b/Source/Dafny/Printer.cs index 43b69bbb..7c684fde 100644 --- a/Source/Dafny/Printer.cs +++ b/Source/Dafny/Printer.cs @@ -1730,8 +1730,7 @@ namespace Microsoft.Dafny { } else if (expr is QuantifierExpr) { QuantifierExpr e = (QuantifierExpr)expr; - if (e.SplitQuantifier != null) { - // CLEMENT TODO TRIGGERS: Should (do) we have a setting to print the original forms instead of rewritten forms? + if (DafnyOptions.O.DafnyPrintResolvedFile != null && e.SplitQuantifier != null) { PrintExpr(e.SplitQuantifierExpression, contextBindingStrength, fragileContext, isRightmost, isFollowedBySemicolon, indent, resolv_count); return; } diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index f0b7f276..df816b6d 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -5306,6 +5306,7 @@ namespace Microsoft.Dafny { if (q != null && q.SplitQuantifier != null) { CheckWellformedWithResult(q.SplitQuantifierExpression, options, result, resultType, locals, builder, etran); + return; } var typeMap = new Dictionary(); -- cgit v1.2.3 From 69c320b225825eb2adf2ae899f88588a10fd27fe Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 19 Aug 2015 20:05:47 -0700 Subject: Refactored and improved bounds discovery --- Source/Dafny/Compiler.cs | 1 + Source/Dafny/DafnyAst.cs | 65 ++++++-- Source/Dafny/Resolver.cs | 386 ++++++++++++++++++--------------------------- Source/Dafny/Translator.cs | 64 ++++---- Test/dafny0/ISets.dfy | 5 +- 5 files changed, 242 insertions(+), 279 deletions(-) (limited to 'Source/Dafny/Compiler.cs') diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index f02f7861..619638ac 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -2978,6 +2978,7 @@ namespace Microsoft.Dafny { TrExpr(b.Seq); wr.Write(").Elements) { "); } else { + // TODO: handle ComprehensionExpr.SubSetBoundedPool Contract.Assert(false); throw new cce.UnreachableException(); // unexpected BoundedPool type } wr.Write("if ("); diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index 1b2af648..b998aa47 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -6688,24 +6688,56 @@ namespace Microsoft.Dafny { } public abstract int Preference(); // higher is better - public static BoundedPool GetBest(List bounds) { - Contract.Requires(bounds != null && bounds.Count > 0); - var ret = bounds[0]; + public static BoundedPool GetBest(List bounds, bool onlyFiniteBounds) { + Contract.Requires(bounds != null); + bounds = CombineIntegerBounds(bounds); + BoundedPool best = null; foreach (var bound in bounds) { - if (bound.Preference() > ret.Preference()) { - ret = bound; - } else { - var retInt = ret as ComprehensionExpr.IntBoundedPool; - if (retInt != null && (retInt.LowerBound == null || retInt.UpperBound == null)) { - var boundInt = bound as ComprehensionExpr.IntBoundedPool; - if (boundInt != null) { - ret = new ComprehensionExpr.IntBoundedPool(retInt.LowerBound ?? boundInt.LowerBound, - retInt.UpperBound ?? boundInt.UpperBound); - } - } + if (!onlyFiniteBounds || bound.IsFinite) { + if (best == null || bound.Preference() > best.Preference()) { + best = bound; + } + } + } + return best; + } + static List CombineIntegerBounds(List bounds) { + var lowerBounds = new List(); + var upperBounds = new List(); + var others = new List(); + foreach (var b in bounds) { + var ib = b as IntBoundedPool; + if (ib != null && ib.UpperBound == null) { + lowerBounds.Add(ib); + } else if (ib != null && ib.LowerBound == null) { + upperBounds.Add(ib); + } else { + others.Add(b); } } - return ret; + // pair up the bounds + var n = Math.Min(lowerBounds.Count, upperBounds.Count); + for (var i = 0; i < n; i++) { + others.Add(new IntBoundedPool(lowerBounds[i].LowerBound, upperBounds[i].UpperBound)); + } + for (var i = n; i < lowerBounds.Count; i++) { + others.Add(lowerBounds[i]); + } + for (var i = n; i < upperBounds.Count; i++) { + others.Add(upperBounds[i]); + } + return others; + } + } + public class ExactBoundedPool : BoundedPool + { + public readonly Expression E; + public ExactBoundedPool(Expression e) { + Contract.Requires(e != null); + E = e; + } + public override int Preference() { + return 20; // the best of all bounds } } public class BoolBoundedPool : BoundedPool @@ -6754,6 +6786,9 @@ namespace Microsoft.Dafny { public override int Preference() { return 0; } + public override bool IsFinite { + get { return false; } + } } public class MapBoundedPool : BoundedPool { diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 1bf96d7d..cd531cf1 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -1477,13 +1477,15 @@ namespace Microsoft.Dafny if (ErrorCount == prevErrorCount) { foreach (var e in needFiniteBoundsChecks_SetComprehension) { - var missingBounds = new List(); CheckTypeInference(e.Range); // we need to resolve operators before the call to DiscoverBounds - e.Bounds = DiscoverBounds(e.tok, e.BoundVars, e.Range, true, false, missingBounds); + List missingBounds; + e.Bounds = DiscoverBestBounds_MultipleVars(e.BoundVars, e.Range, true, true, out missingBounds); if (missingBounds.Count != 0) { e.MissingBounds = missingBounds; - foreach (var bv in e.MissingBounds) { - Error(e, "a set comprehension must produce a finite set, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name); + if (e.Finite) { + foreach (var bv in e.MissingBounds) { + Error(e, "a set comprehension must produce a finite set, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name); + } } } } @@ -1491,21 +1493,17 @@ namespace Microsoft.Dafny Contract.Assert(!e.Exact); // only let-such-that expressions are ever added to the list Contract.Assert(e.RHSs.Count == 1); // if we got this far, the resolver will have checked this condition successfully var constraint = e.RHSs[0]; - var missingBounds = new List(); CheckTypeInference(constraint); // we need to resolve operators before the call to DiscoverBounds - var allBounds = DiscoverBoundsAux(e.tok, new List(e.BoundVars), constraint, true, true, true, missingBounds); + List missingBounds; + var vars = new List(e.BoundVars); + var bestBounds = DiscoverBestBounds_MultipleVars(vars, constraint, true, false, out missingBounds); if (missingBounds.Count != 0) { e.Constraint_MissingBounds = missingBounds; foreach (var bv in e.Constraint_MissingBounds) { Error(e, "a non-ghost let-such-that constraint must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name); } } else { - e.Constraint_Bounds = new List(); - foreach (var pair in allBounds) { - Contract.Assert(1 <= pair.Item2.Count); - // TODO: The following could be improved by picking the bound that is most likely to give rise to an efficient compiled program - e.Constraint_Bounds.Add(pair.Item2[0]); - } + e.Constraint_Bounds = bestBounds; } } } @@ -1578,38 +1576,34 @@ namespace Microsoft.Dafny } return null; }; - var missingBounds = new List(); - var bounds = DiscoverBounds(dd.Constraint.tok, new List { dd.Var }, dd.Constraint, - true, true, missingBounds); + var bounds = DiscoverAllBounds_SingleVar(dd.Var, dd.Constraint); List potentialNativeTypes = (stringNativeType != null) ? new List { stringNativeType } : (boolNativeType == false) ? new List() : NativeTypes; foreach (var nt in potentialNativeTypes) { - if (missingBounds.Count == 0) { - bool lowerOk = false; - bool upperOk = false; - foreach (var bound in bounds) { - if (bound is ComprehensionExpr.IntBoundedPool) { - var bnd = (ComprehensionExpr.IntBoundedPool)bound; - if (bnd.LowerBound != null) { - BigInteger? lower = GetConst(bnd.LowerBound); - if (lower != null && nt.LowerBound <= lower) { - lowerOk = true; - } + bool lowerOk = false; + bool upperOk = false; + foreach (var bound in bounds) { + if (bound is ComprehensionExpr.IntBoundedPool) { + var bnd = (ComprehensionExpr.IntBoundedPool)bound; + if (bnd.LowerBound != null) { + BigInteger? lower = GetConst(bnd.LowerBound); + if (lower != null && nt.LowerBound <= lower) { + lowerOk = true; } - if (bnd.UpperBound != null) { - BigInteger? upper = GetConst(bnd.UpperBound); - if (upper != null && upper <= nt.UpperBound) { - upperOk = true; - } + } + if (bnd.UpperBound != null) { + BigInteger? upper = GetConst(bnd.UpperBound); + if (upper != null && upper <= nt.UpperBound) { + upperOk = true; } } } - if (lowerOk && upperOk) { - dd.NativeType = nt; - break; - } + } + if (lowerOk && upperOk) { + dd.NativeType = nt; + break; } } if (dd.NativeType == null && (boolNativeType == true || stringNativeType != null)) { @@ -5262,9 +5256,9 @@ namespace Microsoft.Dafny bool bodyMustBeSpecOnly = specContextOnly || (prevErrorCount == ErrorCount && UsesSpecFeatures(s.Range)); if (!bodyMustBeSpecOnly && prevErrorCount == ErrorCount) { - var missingBounds = new List(); CheckTypeInference(s.Range); // we need to resolve operators before the call to DiscoverBounds - s.Bounds = DiscoverBounds(s.Tok, s.BoundVars, s.Range, true, false, missingBounds); + List missingBounds; + s.Bounds = DiscoverBestBounds_MultipleVars(s.BoundVars, s.Range, true, true, out missingBounds); if (missingBounds.Count != 0) { bodyMustBeSpecOnly = true; } @@ -6082,18 +6076,14 @@ namespace Microsoft.Dafny if (ec == ErrorCount && !s.IsGhost && s.AssumeToken == null && !specContextOnly) { CheckIsNonGhost(s.Expr); - var missingBounds = new List(); CheckTypeInference(s.Expr); // we need to resolve operators before the call to DiscoverBoundsAux - var allBounds = DiscoverBoundsAux(s.Tok, varLhss, s.Expr, true, true, true, missingBounds); + List missingBounds; + var bestBounds = DiscoverBestBounds_MultipleVars(varLhss, s.Expr, true, false, out missingBounds); if (missingBounds.Count != 0) { s.MissingBounds = missingBounds; // so that an error message can be produced during compilation } else { - Contract.Assert(allBounds != null); - s.Bounds = new List(); - foreach (var pair in allBounds) { - Contract.Assert(1 <= pair.Item2.Count); - s.Bounds.Add(ComprehensionExpr.BoundedPool.GetBest(pair.Item2)); - } + Contract.Assert(bestBounds != null); + s.Bounds = bestBounds; } } } @@ -7694,7 +7684,7 @@ namespace Microsoft.Dafny if (e.Contract != null) ResolveExpression(e.Contract, opts); e.Type = e.Body.Type; } else if (expr is QuantifierExpr) { - QuantifierExpr e = (QuantifierExpr)expr; + var e = (QuantifierExpr)expr; int prevErrorCount = ErrorCount; bool _val = true; bool typeQuantifier = Attributes.ContainsBool(e.Attributes, "typeQuantifier", ref _val) && _val; @@ -7729,9 +7719,9 @@ namespace Microsoft.Dafny expr.Type = Type.Bool; if (prevErrorCount == ErrorCount) { - var missingBounds = new List(); CheckTypeInference(e.LogicalBody()); // we need to resolve operators before the call to DiscoverBounds - e.Bounds = DiscoverBounds(e.tok, e.BoundVars, e.LogicalBody(), e is ExistsExpr, false, missingBounds); + List missingBounds; + e.Bounds = DiscoverBestBounds_MultipleVars(e.BoundVars, e.LogicalBody(), e is ExistsExpr, true, out missingBounds); if (missingBounds.Count != 0) { // Report errors here about quantifications that depend on the allocation state. var mb = missingBounds; @@ -7801,9 +7791,9 @@ namespace Microsoft.Dafny expr.Type = new MapType(e.Finite, e.BoundVars[0].Type, e.Term.Type); if (prevErrorCount == ErrorCount) { - var missingBounds = new List(); CheckTypeInference(e.Range); // we need to resolve operators before the call to DiscoverBounds - e.Bounds = DiscoverBounds(e.tok, e.BoundVars, e.Range, true, false, missingBounds); + List missingBounds; + e.Bounds = DiscoverBestBounds_MultipleVars(e.BoundVars, e.Range, true, true, out missingBounds); if (missingBounds.Count != 0) { e.MissingBounds = missingBounds; if (e.Finite) { @@ -9408,202 +9398,138 @@ namespace Microsoft.Dafny } /// - /// For a description, see DiscoverBoundsAux. - /// - public static List DiscoverBounds(IToken tok, List bvars, Expression expr, bool polarity, bool returnAllBounds, List missingBounds) where VT : IVariable { - var pairs = DiscoverBoundsAux(tok, bvars, expr, polarity, returnAllBounds, false, missingBounds); - if (pairs == null) { - return null; - } - var bounds = new List(); - foreach (var pr in pairs) { - Contract.Assert(1 <= pr.Item2.Count); - bounds.AddRange(pr.Item2); - } - return bounds; - } - - /// - /// Tries to find a bounded pool for each of the bound variables "bvars" of "expr". If this process - /// fails, then "null" is returned and the bound variables for which the process fails are added to "missingBounds". - /// If "returnAllBounds" is false, then: - /// -- at most one BoundedPool per variable is returned - /// -- every IntBoundedPool returned has both a lower and an upper bound - /// -- no SubSetBoundedPool or SuperSetBoundedPool is returned - /// If "returnAllBounds" is true, then: - /// -- a variable may give rise to several BoundedPool's - /// -- IntBoundedPool bounds may have just one component - /// -- a non-null return value means that some bound were found for each variable (but, for example, perhaps one - /// variable only has lower bounds, no upper bounds) - /// Requires "expr" to be successfully resolved. - /// If "allowAnyIntegers", then integer variables will always be given a bound, but this bound may be WiggleWaggle if - /// there is no better bound. + /// For a list of variables "bvars", returns a list of best bounds for each respective variable. If no bound is found for a variable "v", then the bound for + /// "v" in the returned list is set to "null" and "v" is added to "missingBounds". /// - public static List>> DiscoverBoundsAux(IToken tok, List bvars, Expression expr, bool polarity, bool returnAllBounds, bool allowAnyIntegers, List missingBounds) where VT : IVariable { - Contract.Requires(tok != null); - Contract.Requires(bvars != null); - Contract.Requires(missingBounds != null); - Contract.Requires(expr != null); - Contract.Requires(expr.Type != null); // a sanity check (but not a complete proof) that "expr" has been resolved - Contract.Ensures( - (returnAllBounds && Contract.OldValue(missingBounds.Count) <= missingBounds.Count) || - (!returnAllBounds && - Contract.Result>>>() != null && - Contract.Result>>>().Count == bvars.Count && - Contract.OldValue(missingBounds.Count) == missingBounds.Count) || - (!returnAllBounds && - Contract.Result>>>() == null && - Contract.OldValue(missingBounds.Count) < missingBounds.Count)); - - var allBounds = new List>>(); - bool foundError = false; + public static List DiscoverBestBounds_MultipleVars(List bvars, Expression expr, bool polarity, bool onlyFiniteBounds, out List missingBounds) where VT : IVariable { foreach (var bv in bvars) { var c = TypeConstraint(bv, bv.Type); expr = polarity ? Expression.CreateAnd(c, expr) : Expression.CreateImplies(c, expr); } - for (int j = 0; j < bvars.Count; j++) { - VT bv; - List bounds; - foundError = DiscoverAuxSingle(bvars, expr, polarity, returnAllBounds, true, allowAnyIntegers, missingBounds, foundError, j, out bv, out bounds); - if (!returnAllBounds && bounds.Count > 1) { - var best = ComprehensionExpr.BoundedPool.GetBest(bounds); - bounds = new List() { best }; + var all = DiscoverAllBounds_Aux_MultipleVars(bvars, expr, polarity); + var bests = all.ConvertAll(tup => ComprehensionExpr.BoundedPool.GetBest(tup.Item2, onlyFiniteBounds)); + missingBounds = new List(); + for (var i = 0; i < bvars.Count; i++) { + if (bests[i] == null) { + missingBounds.Add(bvars[i]); } + } + return bests; + } - allBounds.Add(new Tuple>(bv, bounds)); + public static List DiscoverAllBounds_SingleVar(VT v, Expression expr) where VT : IVariable { + expr = Expression.CreateAnd(TypeConstraint(v, v.Type), expr); + return DiscoverAllBounds_Aux_SingleVar(new List { v }, 0, expr, true); + } + + private static List>> DiscoverAllBounds_Aux_MultipleVars(List bvars, Expression expr, bool polarity) where VT : IVariable { + Contract.Requires(bvars != null); + Contract.Requires(expr != null); + var bb = new List>>(); + for (var j = 0; j < bvars.Count; j++) { + var bounds = DiscoverAllBounds_Aux_SingleVar(bvars, j, expr, polarity); + bb.Add(new Tuple>(bvars[j], bounds)); } - return foundError ? null : allBounds; + return bb; } - private static bool DiscoverAuxSingle(List bvars, Expression expr, bool polarity, bool allowPartialBounds, bool returnAllBounds, bool allowAnyIntegers, List missingBounds, bool foundError, int j, out VT bv, out List bounds) where VT : IVariable { - bv = bvars[j]; - bounds = new List(); + /// + /// Returns a list of (possibly partial) bounds for "bvars[j]", each of which can be written without mentioning any variable in "bvars[j..]". + /// + private static List DiscoverAllBounds_Aux_SingleVar(List bvars, int j, Expression expr, bool polarity) where VT : IVariable { + Contract.Requires(bvars != null); + Contract.Requires(0 <= j && j < bvars.Count); + Contract.Requires(expr != null); + var bv = bvars[j]; + var bounds = new List(); + + // Maybe the type itself gives a bound if (bv.Type.IsBoolType) { // easy bounds.Add(new ComprehensionExpr.BoolBoundedPool()); - } else { - bool foundBoundsForBv = false; - if (bv.Type.IsIndDatatype && bv.Type.AsIndDatatype.HasFinitePossibleValues) { - bounds.Add(new ComprehensionExpr.DatatypeBoundedPool(bv.Type.AsIndDatatype)); - foundBoundsForBv = true; - } - // Go through the conjuncts of the range expression to look for bounds. - Expression lowerBound = null; - Expression upperBound = null; - if ((allowPartialBounds || returnAllBounds) && lowerBound != null) { - bounds.Add(new ComprehensionExpr.IntBoundedPool(lowerBound, upperBound)); - lowerBound = null; - foundBoundsForBv = true; - } - foreach (var conjunct in NormalizedConjuncts(expr, polarity)) { - var c = conjunct as BinaryExpr; - if (c == null) { - goto CHECK_NEXT_CONJUNCT; - } - var e0 = c.E0; - var e1 = c.E1; - int whereIsBv = SanitizeForBoundDiscovery(bvars, j, c.ResolvedOp, ref e0, ref e1); - if (whereIsBv < 0) { - goto CHECK_NEXT_CONJUNCT; - } - switch (c.ResolvedOp) { - case BinaryExpr.ResolvedOpcode.InSet: - if (whereIsBv == 0) { - bounds.Add(new ComprehensionExpr.SetBoundedPool(e1)); - foundBoundsForBv = true; - if (!returnAllBounds) goto CHECK_NEXT_BOUND_VARIABLE; - } - break; - case BinaryExpr.ResolvedOpcode.Subset: - if (returnAllBounds) { - if (whereIsBv == 0) { - bounds.Add(new ComprehensionExpr.SubSetBoundedPool(e1)); - } else { - bounds.Add(new ComprehensionExpr.SuperSetBoundedPool(e0)); - } - foundBoundsForBv = true; - } - break; - case BinaryExpr.ResolvedOpcode.InMultiSet: - if (whereIsBv == 0) { - bounds.Add(new ComprehensionExpr.SetBoundedPool(e1)); - foundBoundsForBv = true; - if (!returnAllBounds) goto CHECK_NEXT_BOUND_VARIABLE; - } - break; - case BinaryExpr.ResolvedOpcode.InSeq: - if (whereIsBv == 0) { - bounds.Add(new ComprehensionExpr.SeqBoundedPool(e1)); - foundBoundsForBv = true; - if (!returnAllBounds) goto CHECK_NEXT_BOUND_VARIABLE; - } - break; - case BinaryExpr.ResolvedOpcode.InMap: - if (whereIsBv == 0 && e1.Type.AsMapType.Finite) { - bounds.Add(new ComprehensionExpr.MapBoundedPool(e1)); - foundBoundsForBv = true; - if (!returnAllBounds) goto CHECK_NEXT_BOUND_VARIABLE; - } - break; - case BinaryExpr.ResolvedOpcode.EqCommon: - if (bv.Type is IntType) { - var otherOperand = whereIsBv == 0 ? e1 : e0; - bounds.Add(new ComprehensionExpr.IntBoundedPool(otherOperand, Expression.CreateIncrement(otherOperand, 1))); - foundBoundsForBv = true; - if (!returnAllBounds) goto CHECK_NEXT_BOUND_VARIABLE; - } else if (returnAllBounds && bv.Type is SetType) { - var otherOperand = whereIsBv == 0 ? e1 : e0; - bounds.Add(new ComprehensionExpr.SubSetBoundedPool(otherOperand)); - foundBoundsForBv = true; - } - break; - case BinaryExpr.ResolvedOpcode.Gt: - case BinaryExpr.ResolvedOpcode.Ge: - Contract.Assert(false); throw new cce.UnreachableException(); // promised by postconditions of NormalizedConjunct - case BinaryExpr.ResolvedOpcode.Lt: - if (e0.Type.IsNumericBased(Type.NumericPersuation.Int)) { - if (whereIsBv == 0 && upperBound == null) { - upperBound = e1; // bv < E - } else if (whereIsBv == 1 && lowerBound == null) { - lowerBound = Expression.CreateIncrement(e0, 1); // E < bv - } + } else if (bv.Type.IsIndDatatype && bv.Type.AsIndDatatype.HasFinitePossibleValues) { + bounds.Add(new ComprehensionExpr.DatatypeBoundedPool(bv.Type.AsIndDatatype)); + } else if (bv.Type.IsNumericBased(Type.NumericPersuation.Int)) { + bounds.Add(new AssignSuchThatStmt.WiggleWaggleBound()); + } + + // Go through the conjuncts of the range expression to look for bounds. + foreach (var conjunct in NormalizedConjuncts(expr, polarity)) { + var c = conjunct as BinaryExpr; + if (c == null) { + // We only know what to do with binary expressions + continue; + } + var e0 = c.E0; + var e1 = c.E1; + int whereIsBv = SanitizeForBoundDiscovery(bvars, j, c.ResolvedOp, ref e0, ref e1); + if (whereIsBv < 0) { + continue; + } + switch (c.ResolvedOp) { + case BinaryExpr.ResolvedOpcode.InSet: + if (whereIsBv == 0 && e1.Type.AsSetType.Finite) { + bounds.Add(new ComprehensionExpr.SetBoundedPool(e1)); + } + break; + case BinaryExpr.ResolvedOpcode.Subset: + if (whereIsBv == 0) { + bounds.Add(new ComprehensionExpr.SubSetBoundedPool(e1)); + } else { + bounds.Add(new ComprehensionExpr.SuperSetBoundedPool(e0)); + } + break; + case BinaryExpr.ResolvedOpcode.InMultiSet: + if (whereIsBv == 0) { + bounds.Add(new ComprehensionExpr.SetBoundedPool(e1)); + } + break; + case BinaryExpr.ResolvedOpcode.InSeq: + if (whereIsBv == 0) { + bounds.Add(new ComprehensionExpr.SeqBoundedPool(e1)); + } + break; + case BinaryExpr.ResolvedOpcode.InMap: + if (whereIsBv == 0 && e1.Type.AsMapType.Finite) { + bounds.Add(new ComprehensionExpr.MapBoundedPool(e1)); + } + break; + case BinaryExpr.ResolvedOpcode.EqCommon: + // TODO: Use the new ComprehensionExpr.ExactBoundedPool + if (bv.Type.IsNumericBased(Type.NumericPersuation.Int)) { + var otherOperand = whereIsBv == 0 ? e1 : e0; + bounds.Add(new ComprehensionExpr.IntBoundedPool(otherOperand, Expression.CreateIncrement(otherOperand, 1))); + } else if (bv.Type is SetType) { + var otherOperand = whereIsBv == 0 ? e1 : e0; + bounds.Add(new ComprehensionExpr.SubSetBoundedPool(otherOperand)); + } + break; + case BinaryExpr.ResolvedOpcode.Gt: + case BinaryExpr.ResolvedOpcode.Ge: + Contract.Assert(false); throw new cce.UnreachableException(); // promised by postconditions of NormalizedConjunct + case BinaryExpr.ResolvedOpcode.Lt: + if (e0.Type.IsNumericBased(Type.NumericPersuation.Int)) { + if (whereIsBv == 0) { // bv < E + bounds.Add(new ComprehensionExpr.IntBoundedPool(null, e1)); + } else { // E < bv + bounds.Add(new ComprehensionExpr.IntBoundedPool(Expression.CreateIncrement(e0, 1), null)); } - break; - case BinaryExpr.ResolvedOpcode.Le: - if (e0.Type.IsNumericBased(Type.NumericPersuation.Int)) { - if (whereIsBv == 0 && upperBound == null) { - upperBound = Expression.CreateIncrement(e1, 1); // bv <= E - } else if (whereIsBv == 1 && lowerBound == null) { - lowerBound = e0; // E <= bv - } + } + break; + case BinaryExpr.ResolvedOpcode.Le: + if (e0.Type.IsNumericBased(Type.NumericPersuation.Int)) { + if (whereIsBv == 0) { // bv <= E + bounds.Add(new ComprehensionExpr.IntBoundedPool(null, Expression.CreateIncrement(e1, 1))); + } else { // E <= bv + bounds.Add(new ComprehensionExpr.IntBoundedPool(e0, null)); } - break; - default: - break; - } - if ((lowerBound != null && upperBound != null) || - (allowPartialBounds && (lowerBound != null || upperBound != null))) { - // we have found two halves (or, in the case of returnAllBounds, we have found some bound) - bounds.Add(new ComprehensionExpr.IntBoundedPool(lowerBound, upperBound)); - lowerBound = null; - upperBound = null; - foundBoundsForBv = true; - if (!returnAllBounds) goto CHECK_NEXT_BOUND_VARIABLE; - } - CHECK_NEXT_CONJUNCT: ; - } - if (!foundBoundsForBv) { - // we have checked every conjunct in the range expression and still have not discovered good bounds - if (allowAnyIntegers && bv.Type is IntType) { - bounds.Add(new AssignSuchThatStmt.WiggleWaggleBound()); - } else { - missingBounds.Add(bv); // record failing bound variable - foundError = true; - } + } + break; + default: + break; } } - CHECK_NEXT_BOUND_VARIABLE: ; // should goto here only if the bound for the current variable has been discovered (otherwise, return with null from this method) - return foundError; + return bounds; } static Expression TypeConstraint(IVariable bv, Type ty) { @@ -10241,7 +10167,7 @@ namespace Microsoft.Dafny } else if (expr is NamedExpr) { return moduleInfo.IsGhost ? false : UsesSpecFeatures(((NamedExpr)expr).Body); } else if (expr is ComprehensionExpr) { - if (expr is QuantifierExpr && ((QuantifierExpr)expr).Bounds == null) { + if (expr is QuantifierExpr && ((QuantifierExpr)expr).Bounds.Contains(null)) { return true; // the quantifier cannot be compiled if the resolver found no bounds } return Contract.Exists(expr.SubExpressions, se => UsesSpecFeatures(se)); diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 59d93347..2ae03412 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -7653,39 +7653,37 @@ namespace Microsoft.Dafny { } var missingBounds = new List(); - var bounds = Resolver.DiscoverBounds(x.tok, new List() { x }, expr, true, true, missingBounds); - if (missingBounds.Count == 0) { - foreach (var bound in bounds) { - if (bound is ComprehensionExpr.IntBoundedPool) { - var bnd = (ComprehensionExpr.IntBoundedPool)bound; - if (bnd.LowerBound != null) yield return bnd.LowerBound; - if (bnd.UpperBound != null) yield return Expression.CreateDecrement(bnd.UpperBound, 1); - } else if (bound is ComprehensionExpr.SubSetBoundedPool) { - var bnd = (ComprehensionExpr.SubSetBoundedPool)bound; - yield return bnd.UpperBound; - } else if (bound is ComprehensionExpr.SuperSetBoundedPool) { - var bnd = (ComprehensionExpr.SuperSetBoundedPool)bound; - yield return bnd.LowerBound; - } else if (bound is ComprehensionExpr.SetBoundedPool) { - var st = ((ComprehensionExpr.SetBoundedPool)bound).Set.Resolved; - if (st is DisplayExpression) { - var display = (DisplayExpression)st; - foreach (var el in display.Elements) { - yield return el; - } - } else if (st is MapDisplayExpr) { - var display = (MapDisplayExpr)st; - foreach (var maplet in display.Elements) { - yield return maplet.A; - } + var bounds = Resolver.DiscoverAllBounds_SingleVar(x, expr); + foreach (var bound in bounds) { + if (bound is ComprehensionExpr.IntBoundedPool) { + var bnd = (ComprehensionExpr.IntBoundedPool)bound; + if (bnd.LowerBound != null) yield return bnd.LowerBound; + if (bnd.UpperBound != null) yield return Expression.CreateDecrement(bnd.UpperBound, 1); + } else if (bound is ComprehensionExpr.SubSetBoundedPool) { + var bnd = (ComprehensionExpr.SubSetBoundedPool)bound; + yield return bnd.UpperBound; + } else if (bound is ComprehensionExpr.SuperSetBoundedPool) { + var bnd = (ComprehensionExpr.SuperSetBoundedPool)bound; + yield return bnd.LowerBound; + } else if (bound is ComprehensionExpr.SetBoundedPool) { + var st = ((ComprehensionExpr.SetBoundedPool)bound).Set.Resolved; + if (st is DisplayExpression) { + var display = (DisplayExpression)st; + foreach (var el in display.Elements) { + yield return el; } - } else if (bound is ComprehensionExpr.SeqBoundedPool) { - var sq = ((ComprehensionExpr.SeqBoundedPool)bound).Seq.Resolved; - var display = sq as DisplayExpression; - if (display != null) { - foreach (var el in display.Elements) { - yield return el; - } + } else if (st is MapDisplayExpr) { + var display = (MapDisplayExpr)st; + foreach (var maplet in display.Elements) { + yield return maplet.A; + } + } + } else if (bound is ComprehensionExpr.SeqBoundedPool) { + var sq = ((ComprehensionExpr.SeqBoundedPool)bound).Seq.Resolved; + var display = sq as DisplayExpression; + if (display != null) { + foreach (var el in display.Elements) { + yield return el; } } } @@ -11603,8 +11601,8 @@ namespace Microsoft.Dafny { } else if (expr is LambdaExpr) { var e = (LambdaExpr)expr; - return TrLambdaExpr(e); + } else if (expr is StmtExpr) { var e = (StmtExpr)expr; return TrExpr(e.E); diff --git a/Test/dafny0/ISets.dfy b/Test/dafny0/ISets.dfy index bb0230f4..703039c8 100644 --- a/Test/dafny0/ISets.dfy +++ b/Test/dafny0/ISets.dfy @@ -4,7 +4,7 @@ ghost method M() { ghost var s := iset{2}; - // test "in + // test "in" if(2 in s) { } @@ -35,6 +35,9 @@ ghost method m1() { assert s2 - s3 == iset{3}; // set difference assert (iset x | x in s2 :: x+1) == iset{2,3,4}; // set comprehension + assert 17 in (iset x: int | true :: x); // set comprehension + + assert (imap x: int | true :: x+1)[14] == 15; } -- cgit v1.2.3 From 566fdf1676e0d7d6060767febbfa7a0378300e99 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 20 Aug 2015 17:23:24 -0700 Subject: Fixed compilation that involve enumeration over native-type newtype values. --- Binaries/DafnyRuntime.cs | 5 +++ Source/Dafny/Compiler.cs | 76 +++++++++++++++++++++------------ Source/Dafny/DafnyAst.cs | 6 ++- Test/dafny0/RangeCompilation.dfy | 25 +++++++++++ Test/dafny0/RangeCompilation.dfy.expect | 6 +++ 5 files changed, 89 insertions(+), 29 deletions(-) create mode 100644 Test/dafny0/RangeCompilation.dfy create mode 100644 Test/dafny0/RangeCompilation.dfy.expect (limited to 'Source/Dafny/Compiler.cs') diff --git a/Binaries/DafnyRuntime.cs b/Binaries/DafnyRuntime.cs index 7d3799d8..dfb8cf38 100644 --- a/Binaries/DafnyRuntime.cs +++ b/Binaries/DafnyRuntime.cs @@ -1060,6 +1060,11 @@ namespace Dafny } } } + public static IEnumerable IntegerRange(BigInteger lo, BigInteger hi) { + for (var j = lo; j < hi; j++) { + yield return j; + } + } // pre: b != 0 // post: result == a/b, as defined by Euclidean Division (http://en.wikipedia.org/wiki/Modulo_operation) public static sbyte EuclideanDivision_sbyte(sbyte a, sbyte b) { diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index f2cc5d23..8bfa5fa9 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -117,7 +117,19 @@ namespace Microsoft.Dafny { } else if (d is TypeSynonymDecl) { // do nothing, just bypass type synonyms in the compiler } else if (d is NewtypeDecl) { - // do nothing, just bypass newtypes in the compiler + var nt = (NewtypeDecl)d; + Indent(indent); + wr.WriteLine("public class @{0} {{", nt.CompileName); + if (nt.NativeType != null) { + Indent(indent + IndentAmount); + wr.WriteLine("public static System.Collections.Generic.IEnumerable<{0}> IntegerRange(BigInteger lo, BigInteger hi) {{", nt.NativeType.Name); + Indent(indent + 2 * IndentAmount); + wr.WriteLine("for (var j = lo; j < hi; j++) {{ yield return ({0})j; }}", nt.NativeType.Name); + Indent(indent + IndentAmount); + wr.WriteLine("}"); + } + Indent(indent); + wr.WriteLine("}"); } else if (d is DatatypeDecl) { var dt = (DatatypeDecl)d; Indent(indent); @@ -676,7 +688,7 @@ namespace Microsoft.Dafny { } string DtName(DatatypeDecl decl) { - return decl.Module.IsDefaultModule ? decl.CompileName : decl.FullCompileName; + return decl.FullCompileName; } string DtCtorName(DatatypeCtor ctor) { Contract.Requires(ctor != null); @@ -1560,11 +1572,15 @@ namespace Microsoft.Dafny { } else if (bound is ComprehensionExpr.IntBoundedPool) { var b = (ComprehensionExpr.IntBoundedPool)bound; Indent(ind); - wr.Write("for (var @{0} = ", bv.CompileName); + if (AsNativeType(bv.Type) != null) { + wr.Write("foreach (var @{0} in @{1}.IntegerRange(", bv.CompileName, bv.Type.AsNewtype.FullCompileName); + } else { + wr.Write("foreach (var @{0} in Dafny.Helpers.IntegerRange(", bv.CompileName); + } TrExpr(b.LowerBound); - wr.Write("; @{0} < ", bv.CompileName); + wr.Write(", "); TrExpr(b.UpperBound); - wr.Write("; @{0}++) {{ ", bv.CompileName); + wr.Write(")) { "); } else if (bound is ComprehensionExpr.SetBoundedPool) { var b = (ComprehensionExpr.SetBoundedPool)bound; Indent(ind); @@ -1766,27 +1782,23 @@ namespace Microsoft.Dafny { wr.WriteLine("foreach (var {0} in Dafny.Helpers.AllBooleans) {{ @{1} = {0};", tmpVar, bv.CompileName); } else if (bound is ComprehensionExpr.IntBoundedPool) { var b = (ComprehensionExpr.IntBoundedPool)bound; - // (tmpVar is not used in this case) - if (b.LowerBound != null) { - wr.Write("@{0} = ", bv.CompileName); + if (AsNativeType(bv.Type) != null) { + wr.Write("foreach (var @{0} in @{1}.IntegerRange(", tmpVar, bv.Type.AsNewtype.FullCompileName); + } else { + wr.Write("foreach (var @{0} in Dafny.Helpers.IntegerRange(", tmpVar); + } + if (b.LowerBound == null) { + wr.Write("null"); + } else { TrExpr(b.LowerBound); - wr.WriteLine(";"); - Indent(ind); - if (b.UpperBound != null) { - wr.Write("for (; @{0} < ", bv.CompileName); - TrExpr(b.UpperBound); - wr.WriteLine("; @{0}++) {{ ", bv.CompileName); - } else { - wr.WriteLine("for (;; @{0}++) {{ ", bv.CompileName); - } + } + wr.Write(", "); + if (b.UpperBound == null) { + wr.Write("null"); } else { - Contract.Assert(b.UpperBound != null); - wr.Write("@{0} = ", bv.CompileName); TrExpr(b.UpperBound); - wr.WriteLine(";"); - Indent(ind); - wr.WriteLine("for (;; @{0}--) {{ ", bv.CompileName); } + wr.WriteLine(")) {{ @{1} = {0};", tmpVar, bv.CompileName); } else if (bound is AssignSuchThatStmt.WiggleWaggleBound) { wr.WriteLine("foreach (var {0} in Dafny.Helpers.AllIntegers) {{ @{1} = {0};", tmpVar, bv.CompileName); } else if (bound is ComprehensionExpr.SetBoundedPool) { @@ -2891,11 +2903,15 @@ namespace Microsoft.Dafny { wr.Write("foreach (var @{0} in Dafny.Helpers.AllBooleans) {{ ", bv.CompileName); } else if (bound is ComprehensionExpr.IntBoundedPool) { var b = (ComprehensionExpr.IntBoundedPool)bound; - wr.Write("for (var @{0} = ", bv.CompileName); + 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.Write("; @{0} < ", bv.CompileName); + wr.Write(", "); TrExpr(b.UpperBound); - wr.Write("; @{0}++) {{ ", bv.CompileName); + wr.Write(")) { "); } else if (bound is ComprehensionExpr.SetBoundedPool) { var b = (ComprehensionExpr.SetBoundedPool)bound; wr.Write("foreach (var @{0} in (", bv.CompileName); @@ -2960,11 +2976,15 @@ namespace Microsoft.Dafny { wr.Write("foreach (var @{0} in Dafny.Helpers.AllBooleans) {{ ", bv.CompileName); } else if (bound is ComprehensionExpr.IntBoundedPool) { var b = (ComprehensionExpr.IntBoundedPool)bound; - wr.Write("for (var @{0} = ", bv.CompileName); + 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.Write("; @{0} < ", bv.CompileName); + wr.Write(", "); TrExpr(b.UpperBound); - wr.Write("; @{0}++) {{ ", bv.CompileName); + wr.Write(")) { "); } else if (bound is ComprehensionExpr.SetBoundedPool) { var b = (ComprehensionExpr.SetBoundedPool)bound; wr.Write("foreach (var @{0} in (", bv.CompileName); diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index 7328d8dd..d78ae170 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -1967,7 +1967,11 @@ namespace Microsoft.Dafny { } public string FullCompileName { get { - return Module.CompileName + ".@" + CompileName; + if (!Module.IsDefaultModule) { + return Module.CompileName + ".@" + CompileName; + } else { + return CompileName; + } } } } diff --git a/Test/dafny0/RangeCompilation.dfy b/Test/dafny0/RangeCompilation.dfy new file mode 100644 index 00000000..de8ca68e --- /dev/null +++ b/Test/dafny0/RangeCompilation.dfy @@ -0,0 +1,25 @@ +// RUN: %dafny /compile:3 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +newtype Byte = x | 0 <= x < 256 +predicate method GoodByte(b: Byte) { + b % 3 == 2 +} +predicate method GoodInteger(i: int) { + i % 5 == 4 +} + +method Main() { + assert GoodByte(11) && GoodInteger(24); + var b: Byte :| GoodByte(b); + var i: int :| 0 <= i < 256 && GoodInteger(i); + print "b=", b, " i=", i, "\n"; + var m0 := new MyClass; + var m17 := new M17.AnotherClass; +} + +class MyClass { } + +module M17 { + class AnotherClass { } +} diff --git a/Test/dafny0/RangeCompilation.dfy.expect b/Test/dafny0/RangeCompilation.dfy.expect new file mode 100644 index 00000000..c3275d12 --- /dev/null +++ b/Test/dafny0/RangeCompilation.dfy.expect @@ -0,0 +1,6 @@ + +Dafny program verifier finished with 5 verified, 0 errors +Program compiled successfully +Running... + +b=2 i=4 -- cgit v1.2.3 From b8d7d6d785a29fd948e9a9740fb96ed270ac19d8 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 20 Aug 2015 17:24:58 -0700 Subject: Minor refactoring --- Source/Dafny/Compiler.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'Source/Dafny/Compiler.cs') diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index 8bfa5fa9..477acabf 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -687,14 +687,11 @@ namespace Microsoft.Dafny { return formal.HasName ? formal.CompileName : "_a" + i; } - string DtName(DatatypeDecl decl) { - return decl.FullCompileName; - } string DtCtorName(DatatypeCtor ctor) { Contract.Requires(ctor != null); Contract.Ensures(Contract.Result() != null); - return DtName(ctor.EnclosingDatatype) + "_" + ctor.CompileName; + return ctor.EnclosingDatatype.FullCompileName + "_" + ctor.CompileName; } string DtCtorDeclartionName(DatatypeCtor ctor) { Contract.Requires(ctor != null); @@ -2390,7 +2387,7 @@ namespace Microsoft.Dafny { Contract.Assert(dtv.Ctor != null); // since dtv has been successfully resolved var typeParams = dtv.InferredTypeArgs.Count == 0 ? "" : string.Format("<{0}>", TypeNames(dtv.InferredTypeArgs)); - wr.Write("new {0}{1}(", DtName(dtv.Ctor.EnclosingDatatype), typeParams); + wr.Write("new {0}{1}(", dtv.Ctor.EnclosingDatatype.FullCompileName, typeParams); if (!dtv.IsCoCall) { // For an ordinary constructor (that is, one that does not guard any co-recursive calls), generate: // new Dt_Cons( args ) @@ -2860,7 +2857,7 @@ namespace Microsoft.Dafny { var b = (ComprehensionExpr.DatatypeBoundedPool)bound; wr.Write("Dafny.Helpers.QuantDatatype("); - wr.Write("{0}.AllSingletonConstructors, ", DtName(b.Decl)); + wr.Write("{0}.AllSingletonConstructors, ", b.Decl.FullCompileName); } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected BoundedPool type } -- cgit v1.2.3 From 045c50afc6261beeb83ab4a2f70e597157c9d796 Mon Sep 17 00:00:00 2001 From: Michael Lowell Roberts Date: Mon, 21 Sep 2015 13:51:16 -0700 Subject: merged IronDafny updates. two unit tests related to traits do not pass if ENABLE_IRONDAFNY is defined but this isn't critical and will be addressed shortly. --- Source/Dafny/Cloner.cs | 28 +++--- Source/Dafny/Compiler.cs | 152 +++++++++++++++++++++------------ Source/Dafny/DafnyAst.cs | 130 +++++++++++++++++++--------- Source/Dafny/DafnyPipeline.csproj | 10 +-- Source/Dafny/RefinementTransformer.cs | 18 ++-- Source/Dafny/Resolver.cs | 156 +++++++++++++++++++++++++++++----- Source/Dafny/Translator.cs | 68 ++++++++------- 7 files changed, 388 insertions(+), 174 deletions(-) (limited to 'Source/Dafny/Compiler.cs') diff --git a/Source/Dafny/Cloner.cs b/Source/Dafny/Cloner.cs index dd2eed69..7046f9a4 100644 --- a/Source/Dafny/Cloner.cs +++ b/Source/Dafny/Cloner.cs @@ -40,7 +40,7 @@ namespace Microsoft.Dafny } else if (d is TypeSynonymDecl) { var dd = (TypeSynonymDecl)d; var tps = dd.TypeArgs.ConvertAll(CloneTypeParam); - return new TypeSynonymDecl(Tok(dd.tok), dd.Name, tps, m, CloneType(dd.Rhs), CloneAttributes(dd.Attributes)); + return new TypeSynonymDecl(Tok(dd.tok), dd.Name, tps, m, CloneType(dd.Rhs), CloneAttributes(dd.Attributes), dd); } else if (d is NewtypeDecl) { var dd = (NewtypeDecl)d; if (dd.Var == null) { @@ -61,7 +61,7 @@ namespace Microsoft.Dafny var dd = (CoDatatypeDecl)d; var tps = dd.TypeArgs.ConvertAll(CloneTypeParam); var ctors = dd.Ctors.ConvertAll(CloneCtor); - var dt = new CoDatatypeDecl(Tok(dd.tok), dd.Name, m, tps, ctors, CloneAttributes(dd.Attributes)); + var dt = new CoDatatypeDecl(Tok(dd.tok), dd.Name, m, tps, ctors, CloneAttributes(dd.Attributes), dd); return dt; } else if (d is IteratorDecl) { var dd = (IteratorDecl)d; @@ -97,7 +97,7 @@ namespace Microsoft.Dafny var dd = (TraitDecl)d; var tps = dd.TypeArgs.ConvertAll(CloneTypeParam); var mm = dd.Members.ConvertAll(CloneMember); - var cl = new TraitDecl(Tok(dd.tok), dd.Name, m, tps, mm, CloneAttributes(dd.Attributes)); + var cl = new TraitDecl(Tok(dd.tok), dd.Name, m, tps, mm, CloneAttributes(dd.Attributes), dd); return cl; } } @@ -106,7 +106,7 @@ namespace Microsoft.Dafny var tps = dd.TypeArgs.ConvertAll(CloneTypeParam); var mm = dd.Members.ConvertAll(CloneMember); if (d is DefaultClassDecl) { - return new DefaultClassDecl(m, mm); + return new DefaultClassDecl(m, mm, ((DefaultClassDecl)d)); } else { return new ClassDecl(Tok(dd.tok), dd.Name, m, tps, mm, CloneAttributes(dd.Attributes), dd.TraitsTyp.ConvertAll(CloneType), dd); } @@ -137,7 +137,7 @@ namespace Microsoft.Dafny } public DatatypeCtor CloneCtor(DatatypeCtor ct) { - return new DatatypeCtor(Tok(ct.tok), ct.Name, ct.Formals.ConvertAll(CloneFormal), CloneAttributes(ct.Attributes)); + return new DatatypeCtor(Tok(ct.tok), ct.Name, ct.Formals.ConvertAll(CloneFormal), CloneAttributes(ct.Attributes), ct); } public TypeParameter CloneTypeParam(TypeParameter tp) { @@ -641,16 +641,16 @@ namespace Microsoft.Dafny if (f is Predicate) { return new Predicate(Tok(f.tok), newName, f.HasStaticKeyword, f.IsProtected, f.IsGhost, tps, formals, - req, reads, ens, decreases, body, Predicate.BodyOriginKind.OriginalOrInherited, CloneAttributes(f.Attributes), null); + req, reads, ens, decreases, body, Predicate.BodyOriginKind.OriginalOrInherited, CloneAttributes(f.Attributes), null, f); } else if (f is InductivePredicate) { return new InductivePredicate(Tok(f.tok), newName, f.HasStaticKeyword, f.IsProtected, tps, formals, - req, reads, ens, body, CloneAttributes(f.Attributes), null); + req, reads, ens, body, CloneAttributes(f.Attributes), null, f); } else if (f is CoPredicate) { return new CoPredicate(Tok(f.tok), newName, f.HasStaticKeyword, f.IsProtected, tps, formals, - req, reads, ens, body, CloneAttributes(f.Attributes), null); + req, reads, ens, body, CloneAttributes(f.Attributes), null, f); } else { return new Function(Tok(f.tok), newName, f.HasStaticKeyword, f.IsProtected, f.IsGhost, tps, formals, CloneType(f.ResultType), - req, reads, ens, decreases, body, CloneAttributes(f.Attributes), null); + req, reads, ens, decreases, body, CloneAttributes(f.Attributes), null, f); } } @@ -668,19 +668,19 @@ namespace Microsoft.Dafny var body = CloneBlockStmt(m.Body); if (m is Constructor) { return new Constructor(Tok(m.tok), m.Name, tps, ins, - req, mod, ens, decreases, body, CloneAttributes(m.Attributes), null); + req, mod, ens, decreases, body, CloneAttributes(m.Attributes), null, m); } else if (m is InductiveLemma) { return new InductiveLemma(Tok(m.tok), m.Name, m.HasStaticKeyword, tps, ins, m.Outs.ConvertAll(CloneFormal), - req, mod, ens, decreases, body, CloneAttributes(m.Attributes), null); + req, mod, ens, decreases, body, CloneAttributes(m.Attributes), null, m); } else if (m is CoLemma) { return new CoLemma(Tok(m.tok), m.Name, m.HasStaticKeyword, tps, ins, m.Outs.ConvertAll(CloneFormal), - req, mod, ens, decreases, body, CloneAttributes(m.Attributes), null); + req, mod, ens, decreases, body, CloneAttributes(m.Attributes), null, m); } else if (m is Lemma) { return new Lemma(Tok(m.tok), m.Name, m.HasStaticKeyword, tps, ins, m.Outs.ConvertAll(CloneFormal), - req, mod, ens, decreases, body, CloneAttributes(m.Attributes), null); + req, mod, ens, decreases, body, CloneAttributes(m.Attributes), null, m); } else { return new Method(Tok(m.tok), m.Name, m.HasStaticKeyword, m.IsGhost, tps, ins, m.Outs.ConvertAll(CloneFormal), - req, mod, ens, decreases, body, CloneAttributes(m.Attributes), null); + req, mod, ens, decreases, body, CloneAttributes(m.Attributes), null, m); } } public virtual IToken Tok(IToken tok) { diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index 477acabf..123433bc 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -102,7 +102,11 @@ namespace Microsoft.Dafny { } int indent = 0; if (!m.IsDefaultModule) { - wr.WriteLine("namespace @{0} {{", m.CompileName); + var m_prime = m; + while (DafnyOptions.O.IronDafny && m_prime.ClonedFrom != null) { + m_prime = m.ClonedFrom; + } + wr.WriteLine("namespace @{0} {{", m_prime.CompileName); indent += IndentAmount; } foreach (TopLevelDecl d in m.TopLevelDecls) { @@ -687,11 +691,18 @@ namespace Microsoft.Dafny { return formal.HasName ? formal.CompileName : "_a" + i; } + string DtName(DatatypeDecl decl) { + var d = (TopLevelDecl)decl; + while (DafnyOptions.O.IronDafny && d.ClonedFrom != null) { + d = (TopLevelDecl)d.ClonedFrom; + } + return d.Module.IsDefaultModule ? d.CompileName : d.FullCompileName; + } string DtCtorName(DatatypeCtor ctor) { Contract.Requires(ctor != null); Contract.Ensures(Contract.Result() != null); - return ctor.EnclosingDatatype.FullCompileName + "_" + ctor.CompileName; + return DtName(ctor.EnclosingDatatype) + "_" + ctor.CompileName; } string DtCtorDeclartionName(DatatypeCtor ctor) { Contract.Requires(ctor != null); @@ -1123,61 +1134,78 @@ namespace Microsoft.Dafny { Contract.Requires(type != null); Contract.Ensures(Contract.Result() != null); - type = type.NormalizeExpand(); - if (type is TypeProxy) { + var xType = type.NormalizeExpand(); + if (xType is TypeProxy) { // unresolved proxy; just treat as ref, since no particular type information is apparently needed for this type return "object"; } - if (type is BoolType) { + if (xType is BoolType) { return "bool"; - } else if (type is CharType) { + } else if (xType is CharType) { return "char"; - } else if (type is IntType) { + } else if (xType is IntType) { return "BigInteger"; - } else if (type is RealType) { + } else if (xType is RealType) { return "Dafny.BigRational"; - } else if (type.AsNewtype != null) { - NativeType nativeType = type.AsNewtype.NativeType; + } else if (xType.AsNewtype != null) { + NativeType nativeType = xType.AsNewtype.NativeType; if (nativeType != null) { return nativeType.Name; } - return TypeName(type.AsNewtype.BaseType); - } else if (type is ObjectType) { + return TypeName(xType.AsNewtype.BaseType); + } else if (xType is ObjectType) { return "object"; - } else if (type.IsArrayType) { - ArrayClassDecl at = type.AsArrayType; + } else if (xType.IsArrayType) { + ArrayClassDecl at = xType.AsArrayType; Contract.Assert(at != null); // follows from type.IsArrayType - Type elType = UserDefinedType.ArrayElementType(type); + Type elType = UserDefinedType.ArrayElementType(xType); string name = TypeName(elType) + "["; for (int i = 1; i < at.Dims; i++) { name += ","; } return name + "]"; - } else if (type is UserDefinedType) { - var udt = (UserDefinedType)type; - return TypeName_UDT(udt.FullCompileName, udt.TypeArgs); - } else if (type is SetType) { - Type argType = ((SetType)type).Arg; + } else if (xType is UserDefinedType) { + var udt = (UserDefinedType)xType; + var s = udt.FullCompileName; + var rc = udt.ResolvedClass; + if (DafnyOptions.O.IronDafny && + !(xType is ArrowType) && + rc != null && + rc.Module != null && + !rc.Module.IsDefaultModule) { + while (rc.ClonedFrom != null || rc.ExclusiveRefinement != null) { + if (rc.ClonedFrom != null) { + rc = (TopLevelDecl)rc.ClonedFrom; + } else { + Contract.Assert(rc.ExclusiveRefinement != null); + rc = rc.ExclusiveRefinement; + } + } + s = rc.FullCompileName; + } + return TypeName_UDT(s, udt.TypeArgs); + } else if (xType is SetType) { + Type argType = ((SetType)xType).Arg; if (argType is ObjectType) { Error("compilation of set is not supported; consider introducing a ghost"); } return DafnySetClass + "<" + TypeName(argType) + ">"; - } else if (type is SeqType) { - Type argType = ((SeqType)type).Arg; + } else if (xType is SeqType) { + Type argType = ((SeqType)xType).Arg; if (argType is ObjectType) { Error("compilation of seq is not supported; consider introducing a ghost"); } return DafnySeqClass + "<" + TypeName(argType) + ">"; - } else if (type is MultiSetType) { - Type argType = ((MultiSetType)type).Arg; + } else if (xType is MultiSetType) { + Type argType = ((MultiSetType)xType).Arg; if (argType is ObjectType) { Error("compilation of seq is not supported; consider introducing a ghost"); } return DafnyMultiSetClass + "<" + TypeName(argType) + ">"; - } else if (type is MapType) { - Type domType = ((MapType)type).Domain; - Type ranType = ((MapType)type).Range; + } else if (xType is MapType) { + Type domType = ((MapType)xType).Domain; + Type ranType = ((MapType)xType).Range; if (domType is ObjectType || ranType is ObjectType) { Error("compilation of map or map<_, object> is not supported; consider introducing a ghost"); } @@ -1218,36 +1246,52 @@ namespace Microsoft.Dafny { Contract.Requires(type != null); Contract.Ensures(Contract.Result() != null); - type = type.NormalizeExpand(); - if (type is TypeProxy) { + var xType = type.NormalizeExpand(); + if (xType is TypeProxy) { // unresolved proxy; just treat as ref, since no particular type information is apparently needed for this type return "null"; } - if (type is BoolType) { + if (xType is BoolType) { return "false"; - } else if (type is CharType) { + } else if (xType is CharType) { return "'D'"; - } else if (type is IntType) { + } else if (xType is IntType) { return "BigInteger.Zero"; - } else if (type is RealType) { + } else if (xType is RealType) { return "Dafny.BigRational.ZERO"; - } else if (type.AsNewtype != null) { - if (type.AsNewtype.NativeType != null) { + } else if (xType.AsNewtype != null) { + if (xType.AsNewtype.NativeType != null) { return "0"; } - return DefaultValue(type.AsNewtype.BaseType); - } else if (type.IsRefType) { - return string.Format("({0})null", TypeName(type)); - } else if (type.IsDatatype) { - UserDefinedType udt = (UserDefinedType)type; - string s = "@" + udt.FullCompileName; + return DefaultValue(xType.AsNewtype.BaseType); + } else if (xType.IsRefType) { + return string.Format("({0})null", TypeName(xType)); + } else if (xType.IsDatatype) { + var udt = (UserDefinedType)xType; + var s = "@" + udt.FullCompileName; + var rc = udt.ResolvedClass; + if (DafnyOptions.O.IronDafny && + !(xType is ArrowType) && + rc != null && + rc.Module != null && + !rc.Module.IsDefaultModule) { + while (rc.ClonedFrom != null || rc.ExclusiveRefinement != null) { + if (rc.ClonedFrom != null) { + rc = (TopLevelDecl)rc.ClonedFrom; + } else { + Contract.Assert(rc.ExclusiveRefinement != null); + rc = rc.ExclusiveRefinement; + } + } + s = "@" + rc.FullCompileName; + } if (udt.TypeArgs.Count != 0) { s += "<" + TypeNames(udt.TypeArgs) + ">"; } return string.Format("new {0}()", s); - } else if (type.IsTypeParameter) { - var udt = (UserDefinedType)type; + } else if (xType.IsTypeParameter) { + var udt = (UserDefinedType)xType; string s = "default(@" + udt.FullCompileName; if (udt.TypeArgs.Count != 0) { @@ -1255,15 +1299,15 @@ namespace Microsoft.Dafny { } s += ")"; return s; - } else if (type is SetType) { - return DafnySetClass + "<" + TypeName(((SetType)type).Arg) + ">.Empty"; - } else if (type is MultiSetType) { - return DafnyMultiSetClass + "<" + TypeName(((MultiSetType)type).Arg) + ">.Empty"; - } else if (type is SeqType) { - return DafnySeqClass + "<" + TypeName(((SeqType)type).Arg) + ">.Empty"; - } else if (type is MapType) { - return TypeName(type)+".Empty"; - } else if (type is ArrowType) { + } else if (xType is SetType) { + return DafnySetClass + "<" + TypeName(((SetType)xType).Arg) + ">.Empty"; + } else if (xType is MultiSetType) { + return DafnyMultiSetClass + "<" + TypeName(((MultiSetType)xType).Arg) + ">.Empty"; + } else if (xType is SeqType) { + return DafnySeqClass + "<" + TypeName(((SeqType)xType).Arg) + ">.Empty"; + } else if (xType is MapType) { + return TypeName(xType)+".Empty"; + } else if (xType is ArrowType) { return "null"; } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type @@ -2387,7 +2431,7 @@ namespace Microsoft.Dafny { Contract.Assert(dtv.Ctor != null); // since dtv has been successfully resolved var typeParams = dtv.InferredTypeArgs.Count == 0 ? "" : string.Format("<{0}>", TypeNames(dtv.InferredTypeArgs)); - wr.Write("new {0}{1}(", dtv.Ctor.EnclosingDatatype.FullCompileName, typeParams); + wr.Write("new {0}{1}(", DtName(dtv.Ctor.EnclosingDatatype), typeParams); if (!dtv.IsCoCall) { // For an ordinary constructor (that is, one that does not guard any co-recursive calls), generate: // new Dt_Cons( args ) @@ -2857,7 +2901,7 @@ namespace Microsoft.Dafny { var b = (ComprehensionExpr.DatatypeBoundedPool)bound; wr.Write("Dafny.Helpers.QuantDatatype("); - wr.Write("{0}.AllSingletonConstructors, ", b.Decl.FullCompileName); + wr.Write("{0}.AllSingletonConstructors, ", DtName(b.Decl)); } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected BoundedPool type } diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index 9bff2038..600f7473 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -183,7 +183,7 @@ namespace Microsoft.Dafny { null, null, null); readsIS.Function = reads; // just so we can really claim the member declarations are resolved readsIS.TypeArgumentSubstitutions = Util.Dict(tps, tys); // ditto - var arrowDecl = new ArrowTypeDecl(tps, req, reads, SystemModule, DontCompile()); + var arrowDecl = new ArrowTypeDecl(tps, req, reads, SystemModule, DontCompile(), null); arrowTypeDecls.Add(arity, arrowDecl); SystemModule.TopLevelDecls.Add(arrowDecl); } @@ -431,16 +431,33 @@ namespace Microsoft.Dafny { var pt = type as TypeProxy; if (pt != null && pt.T != null) { type = pt.T; - } else { + continue; + } var syn = type.AsTypeSynonym; if (syn != null) { var udt = (UserDefinedType)type; // correctness of cast follows from the AsTypeSynonym != null test. // Instantiate with the actual type arguments type = syn.RhsWithArgument(udt.TypeArgs); + continue; + } + if (DafnyOptions.O.IronDafny && type is UserDefinedType) { + var rc = ((UserDefinedType)type).ResolvedClass; + if (rc != null) { + while (rc.ClonedFrom != null || rc.ExclusiveRefinement != null) { + if (rc.ClonedFrom != null) { + rc = (TopLevelDecl)rc.ClonedFrom; } else { - return type; + Contract.Assert(rc.ExclusiveRefinement != null); + rc = rc.ExclusiveRefinement; + } + } + } + if (rc is TypeSynonymDecl) { + type = ((TypeSynonymDecl)rc).Rhs; + continue; } } + return type; } } @@ -1030,7 +1047,11 @@ namespace Microsoft.Dafny { public string FullCompanionCompileName { get { Contract.Requires(ResolvedClass is TraitDecl); - var s = ResolvedClass.Module.IsDefaultModule ? "" : ResolvedClass.Module.CompileName + "."; + var m = ResolvedClass.Module; + while (DafnyOptions.O.IronDafny && m.ClonedFrom != null) { + m = m.ClonedFrom; + } + var s = m.IsDefaultModule ? "" : m.CompileName + "."; return s + "@_Companion_" + CompileName; } } @@ -1802,6 +1823,16 @@ namespace Microsoft.Dafny { } } + public string RefinementCompileName { + get { + if (ExclusiveRefinement != null) { + return this.ExclusiveRefinement.RefinementCompileName; + } else { + return this.CompileName; + } + } + } + /// /// Determines if "a" and "b" are in the same strongly connected component of the call graph, that is, /// if "a" and "b" are mutually recursive. @@ -1946,6 +1977,7 @@ namespace Microsoft.Dafny { Contract.Requires(cce.NonNullElements(typeArgs)); Module = module; TypeArgs = typeArgs; + ExclusiveRefinement = null; } public string FullName { @@ -1958,6 +1990,13 @@ namespace Microsoft.Dafny { return Module.CompileName + "." + CompileName; } } + + public string FullSanitizedRefinementName { + get { + return Module.RefinementCompileName + "." + CompileName; + } + } + public string FullNameInContext(ModuleDefinition context) { if (Module == context) { return Name; @@ -1974,6 +2013,7 @@ namespace Microsoft.Dafny { } } } + public TopLevelDecl ExclusiveRefinement { get; set; } } public class TraitDecl : ClassDecl @@ -1981,8 +2021,8 @@ namespace Microsoft.Dafny { public override string WhatKind { get { return "trait"; } } public bool IsParent { set; get; } public TraitDecl(IToken tok, string name, ModuleDefinition module, - List typeArgs, [Captured] List members, Attributes attributes) - : base(tok, name, module, typeArgs, members, attributes, null) { } + List typeArgs, [Captured] List members, Attributes attributes, TraitDecl clonedFrom = null) + : base(tok, name, module, typeArgs, members, attributes, null, clonedFrom) { } } public class ClassDecl : TopLevelDecl { @@ -2024,8 +2064,8 @@ namespace Microsoft.Dafny { } public class DefaultClassDecl : ClassDecl { - public DefaultClassDecl(ModuleDefinition module, [Captured] List members) - : base(Token.NoToken, "_default", module, new List(), members, null, null) { + public DefaultClassDecl(ModuleDefinition module, [Captured] List members, DefaultClassDecl clonedFrom = null) + : base(Token.NoToken, "_default", module, new List(), members, null, null, clonedFrom) { Contract.Requires(module != null); Contract.Requires(cce.NonNullElements(members)); } @@ -2058,9 +2098,9 @@ namespace Microsoft.Dafny { public readonly Function Requires; public readonly Function Reads; - public ArrowTypeDecl(List tps, Function req, Function reads, ModuleDefinition module, Attributes attributes) + public ArrowTypeDecl(List tps, Function req, Function reads, ModuleDefinition module, Attributes attributes, ArrowTypeDecl clonedFrom) : base(Token.NoToken, ArrowType.ArrowTypeName(tps.Count - 1), module, tps, - new List { req, reads }, attributes, null) { + new List { req, reads }, attributes, null, clonedFrom) { Contract.Requires(tps != null && 1 <= tps.Count); Contract.Requires(req != null); Contract.Requires(reads != null); @@ -2186,8 +2226,8 @@ namespace Microsoft.Dafny { public CoDatatypeDecl SscRepr; // filled in during resolution public CoDatatypeDecl(IToken tok, string name, ModuleDefinition module, List typeArgs, - [Captured] List ctors, Attributes attributes) - : base(tok, name, module, typeArgs, ctors, attributes) { + [Captured] List ctors, Attributes attributes, CoDatatypeDecl clonedFrom = null) + : base(tok, name, module, typeArgs, ctors, attributes, clonedFrom) { Contract.Requires(tok != null); Contract.Requires(name != null); Contract.Requires(module != null); @@ -2214,8 +2254,8 @@ namespace Microsoft.Dafny { public SpecialField QueryField; // filled in during resolution public List Destructors = new List(); // contents filled in during resolution; includes both implicit (not mentionable in source) and explicit destructors - public DatatypeCtor(IToken tok, string name, [Captured] List formals, Attributes attributes) - : base(tok, name, attributes, null) { + public DatatypeCtor(IToken tok, string name, [Captured] List formals, Attributes attributes, DatatypeCtor clonedFrom = null) + : base(tok, name, attributes, clonedFrom) { Contract.Requires(tok != null); Contract.Requires(name != null); Contract.Requires(cce.NonNullElements(formals)); @@ -2426,8 +2466,8 @@ namespace Microsoft.Dafny { public readonly bool IsGhost; public TopLevelDecl EnclosingClass; // filled in during resolution public MemberDecl RefinementBase; // filled in during the pre-resolution refinement transformation; null if the member is new here - public MemberDecl(IToken tok, string name, bool hasStaticKeyword, bool isGhost, Attributes attributes) - : base(tok, name, attributes, null) { + public MemberDecl(IToken tok, string name, bool hasStaticKeyword, bool isGhost, Attributes attributes, Declaration clonedFrom = null) + : base(tok, name, attributes, clonedFrom) { Contract.Requires(tok != null); Contract.Requires(name != null); HasStaticKeyword = hasStaticKeyword; @@ -2452,6 +2492,14 @@ namespace Microsoft.Dafny { return EnclosingClass.FullSanitizedName + "." + CompileName; } } + public string FullSanitizedRefinementName { + get { + Contract.Requires(EnclosingClass != null); + Contract.Ensures(Contract.Result() != null); + + return EnclosingClass.FullSanitizedRefinementName + "." + CompileName; + } + } public string FullNameInContext(ModuleDefinition context) { Contract.Requires(EnclosingClass != null); Contract.Ensures(Contract.Result() != null); @@ -2658,8 +2706,8 @@ namespace Microsoft.Dafny { { public override string WhatKind { get { return "type synonym"; } } public readonly Type Rhs; - public TypeSynonymDecl(IToken tok, string name, List typeArgs, ModuleDefinition module, Type rhs, Attributes attributes) - : base(tok, name, module, typeArgs, attributes) { + public TypeSynonymDecl(IToken tok, string name, List typeArgs, ModuleDefinition module, Type rhs, Attributes attributes, TypeSynonymDecl clonedFrom = null) + : base(tok, name, module, typeArgs, attributes, clonedFrom) { Contract.Requires(tok != null); Contract.Requires(name != null); Contract.Requires(typeArgs != null); @@ -3043,8 +3091,8 @@ namespace Microsoft.Dafny { public Function(IToken tok, string name, bool hasStaticKeyword, bool isProtected, bool isGhost, List typeArgs, List formals, Type resultType, List req, List reads, List ens, Specification decreases, - Expression body, Attributes attributes, IToken signatureEllipsis) - : base(tok, name, hasStaticKeyword, isGhost, attributes) { + Expression body, Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom = null) + : base(tok, name, hasStaticKeyword, isGhost, attributes, clonedFrom) { Contract.Requires(tok != null); Contract.Requires(name != null); @@ -3099,8 +3147,8 @@ namespace Microsoft.Dafny { public Predicate(IToken tok, string name, bool hasStaticKeyword, bool isProtected, bool isGhost, List typeArgs, List formals, List req, List reads, List ens, Specification decreases, - Expression body, BodyOriginKind bodyOrigin, Attributes attributes, IToken signatureEllipsis) - : base(tok, name, hasStaticKeyword, isProtected, isGhost, typeArgs, formals, new BoolType(), req, reads, ens, decreases, body, attributes, signatureEllipsis) { + Expression body, BodyOriginKind bodyOrigin, Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom = null) + : base(tok, name, hasStaticKeyword, isProtected, isGhost, typeArgs, formals, new BoolType(), req, reads, ens, decreases, body, attributes, signatureEllipsis, clonedFrom) { Contract.Requires(bodyOrigin == Predicate.BodyOriginKind.OriginalOrInherited || body != null); BodyOrigin = bodyOrigin; } @@ -3118,7 +3166,7 @@ namespace Microsoft.Dafny { List typeArgs, Formal k, List formals, List req, List reads, List ens, Specification decreases, Expression body, Attributes attributes, FixpointPredicate fixpointPred) - : base(tok, name, hasStaticKeyword, isProtected, true, typeArgs, formals, new BoolType(), req, reads, ens, decreases, body, attributes, null) { + : base(tok, name, hasStaticKeyword, isProtected, true, typeArgs, formals, new BoolType(), req, reads, ens, decreases, body, attributes, null, null) { Contract.Requires(k != null); Contract.Requires(fixpointPred != null); Contract.Requires(formals != null && 1 <= formals.Count && formals[0] == k); @@ -3135,9 +3183,9 @@ namespace Microsoft.Dafny { public FixpointPredicate(IToken tok, string name, bool hasStaticKeyword, bool isProtected, List typeArgs, List formals, List req, List reads, List ens, - Expression body, Attributes attributes, IToken signatureEllipsis) + Expression body, Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom = null) : base(tok, name, hasStaticKeyword, isProtected, true, typeArgs, formals, new BoolType(), - req, reads, ens, new Specification(new List(), null), body, attributes, signatureEllipsis) { + req, reads, ens, new Specification(new List(), null), body, attributes, signatureEllipsis, clonedFrom) { } /// @@ -3176,9 +3224,9 @@ namespace Microsoft.Dafny { public InductivePredicate(IToken tok, string name, bool hasStaticKeyword, bool isProtected, List typeArgs, List formals, List req, List reads, List ens, - Expression body, Attributes attributes, IToken signatureEllipsis) + Expression body, Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom = null) : base(tok, name, hasStaticKeyword, isProtected, typeArgs, formals, - req, reads, ens, body, attributes, signatureEllipsis) { + req, reads, ens, body, attributes, signatureEllipsis, clonedFrom) { } } @@ -3188,9 +3236,9 @@ namespace Microsoft.Dafny { public CoPredicate(IToken tok, string name, bool hasStaticKeyword, bool isProtected, List typeArgs, List formals, List req, List reads, List ens, - Expression body, Attributes attributes, IToken signatureEllipsis) + Expression body, Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom = null) : base(tok, name, hasStaticKeyword, isProtected, typeArgs, formals, - req, reads, ens, body, attributes, signatureEllipsis) { + req, reads, ens, body, attributes, signatureEllipsis, clonedFrom) { } } @@ -3232,8 +3280,8 @@ namespace Microsoft.Dafny { [Captured] List ens, [Captured] Specification decreases, [Captured] BlockStmt body, - Attributes attributes, IToken signatureEllipsis) - : base(tok, name, hasStaticKeyword, isGhost, attributes) { + Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom = null) + : base(tok, name, hasStaticKeyword, isGhost, attributes, clonedFrom) { Contract.Requires(tok != null); Contract.Requires(name != null); Contract.Requires(cce.NonNullElements(typeArgs)); @@ -3305,8 +3353,8 @@ namespace Microsoft.Dafny { [Captured] List ens, [Captured] Specification decreases, [Captured] BlockStmt body, - Attributes attributes, IToken signatureEllipsis) - : base(tok, name, hasStaticKeyword, true, typeArgs, ins, outs, req, mod, ens, decreases, body, attributes, signatureEllipsis) { + Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom = null) + : base(tok, name, hasStaticKeyword, true, typeArgs, ins, outs, req, mod, ens, decreases, body, attributes, signatureEllipsis, clonedFrom) { } } @@ -3320,8 +3368,8 @@ namespace Microsoft.Dafny { [Captured] List ens, [Captured] Specification decreases, [Captured] BlockStmt body, - Attributes attributes, IToken signatureEllipsis) - : base(tok, name, false, false, typeArgs, ins, new List(), req, mod, ens, decreases, body, attributes, signatureEllipsis) { + Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom = null) + : base(tok, name, false, false, typeArgs, ins, new List(), req, mod, ens, decreases, body, attributes, signatureEllipsis, clonedFrom) { Contract.Requires(tok != null); Contract.Requires(name != null); Contract.Requires(cce.NonNullElements(typeArgs)); @@ -3372,8 +3420,8 @@ namespace Microsoft.Dafny { List ens, Specification decreases, BlockStmt body, - Attributes attributes, IToken signatureEllipsis) - : base(tok, name, hasStaticKeyword, true, typeArgs, ins, outs, req, mod, ens, decreases, body, attributes, signatureEllipsis) { + Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom) + : base(tok, name, hasStaticKeyword, true, typeArgs, ins, outs, req, mod, ens, decreases, body, attributes, signatureEllipsis, clonedFrom) { Contract.Requires(tok != null); Contract.Requires(name != null); Contract.Requires(cce.NonNullElements(typeArgs)); @@ -3398,8 +3446,8 @@ namespace Microsoft.Dafny { List ens, Specification decreases, BlockStmt body, - Attributes attributes, IToken signatureEllipsis) - : base(tok, name, hasStaticKeyword, typeArgs, ins, outs, req, mod, ens, decreases, body, attributes, signatureEllipsis) { + Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom = null) + : base(tok, name, hasStaticKeyword, typeArgs, ins, outs, req, mod, ens, decreases, body, attributes, signatureEllipsis, clonedFrom) { Contract.Requires(tok != null); Contract.Requires(name != null); Contract.Requires(cce.NonNullElements(typeArgs)); @@ -3424,8 +3472,8 @@ namespace Microsoft.Dafny { List ens, Specification decreases, BlockStmt body, - Attributes attributes, IToken signatureEllipsis) - : base(tok, name, hasStaticKeyword, typeArgs, ins, outs, req, mod, ens, decreases, body, attributes, signatureEllipsis) { + Attributes attributes, IToken signatureEllipsis, Declaration clonedFrom = null) + : base(tok, name, hasStaticKeyword, typeArgs, ins, outs, req, mod, ens, decreases, body, attributes, signatureEllipsis, clonedFrom) { Contract.Requires(tok != null); Contract.Requires(name != null); Contract.Requires(cce.NonNullElements(typeArgs)); diff --git a/Source/Dafny/DafnyPipeline.csproj b/Source/Dafny/DafnyPipeline.csproj index 13a1e53e..501a624c 100644 --- a/Source/Dafny/DafnyPipeline.csproj +++ b/Source/Dafny/DafnyPipeline.csproj @@ -1,4 +1,4 @@ - + Debug @@ -41,7 +41,7 @@ full false bin\Debug\ - DEBUG;TRACE + TRACE;DEBUG;NO_ENABLE_IRONDAFNY prompt 4 False @@ -83,7 +83,7 @@ pdbonly true bin\Release\ - TRACE + TRACE;NO_ENABLE_IRONDAFNY prompt 4 AllRules.ruleset @@ -91,7 +91,7 @@ true bin\Checked\ - DEBUG;TRACE + TRACE;DEBUG;NO_ENABLE_IRONDAFNY full AnyCPU prompt @@ -199,4 +199,4 @@ --> - + \ No newline at end of file diff --git a/Source/Dafny/RefinementTransformer.cs b/Source/Dafny/RefinementTransformer.cs index ba558ea6..24d1126f 100644 --- a/Source/Dafny/RefinementTransformer.cs +++ b/Source/Dafny/RefinementTransformer.cs @@ -547,16 +547,16 @@ namespace Microsoft.Dafny if (f is Predicate) { return new Predicate(tok, f.Name, f.HasStaticKeyword, f.IsProtected, isGhost, tps, formals, - req, reads, ens, decreases, body, bodyOrigin, refinementCloner.MergeAttributes(f.Attributes, moreAttributes), null); + req, reads, ens, decreases, body, bodyOrigin, refinementCloner.MergeAttributes(f.Attributes, moreAttributes), null, f); } else if (f is InductivePredicate) { return new InductivePredicate(tok, f.Name, f.HasStaticKeyword, f.IsProtected, tps, formals, - req, reads, ens, body, refinementCloner.MergeAttributes(f.Attributes, moreAttributes), null); + req, reads, ens, body, refinementCloner.MergeAttributes(f.Attributes, moreAttributes), null, f); } else if (f is CoPredicate) { return new CoPredicate(tok, f.Name, f.HasStaticKeyword, f.IsProtected, tps, formals, - req, reads, ens, body, refinementCloner.MergeAttributes(f.Attributes, moreAttributes), null); + req, reads, ens, body, refinementCloner.MergeAttributes(f.Attributes, moreAttributes), null, f); } else { return new Function(tok, f.Name, f.HasStaticKeyword, f.IsProtected, isGhost, tps, formals, refinementCloner.CloneType(f.ResultType), - req, reads, ens, decreases, body, refinementCloner.MergeAttributes(f.Attributes, moreAttributes), null); + req, reads, ens, decreases, body, refinementCloner.MergeAttributes(f.Attributes, moreAttributes), null, f); } } @@ -581,19 +581,19 @@ namespace Microsoft.Dafny var body = newBody ?? refinementCloner.CloneBlockStmt(m.Body); if (m is Constructor) { return new Constructor(new RefinementToken(m.tok, moduleUnderConstruction), m.Name, tps, ins, - req, mod, ens, decreases, body, refinementCloner.MergeAttributes(m.Attributes, moreAttributes), null); + req, mod, ens, decreases, body, refinementCloner.MergeAttributes(m.Attributes, moreAttributes), null, m); } else if (m is InductiveLemma) { return new InductiveLemma(new RefinementToken(m.tok, moduleUnderConstruction), m.Name, m.HasStaticKeyword, tps, ins, m.Outs.ConvertAll(refinementCloner.CloneFormal), - req, mod, ens, decreases, body, refinementCloner.MergeAttributes(m.Attributes, moreAttributes), null); + req, mod, ens, decreases, body, refinementCloner.MergeAttributes(m.Attributes, moreAttributes), null, m); } else if (m is CoLemma) { return new CoLemma(new RefinementToken(m.tok, moduleUnderConstruction), m.Name, m.HasStaticKeyword, tps, ins, m.Outs.ConvertAll(refinementCloner.CloneFormal), - req, mod, ens, decreases, body, refinementCloner.MergeAttributes(m.Attributes, moreAttributes), null); + req, mod, ens, decreases, body, refinementCloner.MergeAttributes(m.Attributes, moreAttributes), null, m); } else if (m is Lemma) { return new Lemma(new RefinementToken(m.tok, moduleUnderConstruction), m.Name, m.HasStaticKeyword, tps, ins, m.Outs.ConvertAll(refinementCloner.CloneFormal), - req, mod, ens, decreases, body, refinementCloner.MergeAttributes(m.Attributes, moreAttributes), null); + req, mod, ens, decreases, body, refinementCloner.MergeAttributes(m.Attributes, moreAttributes), null, m); } else { return new Method(new RefinementToken(m.tok, moduleUnderConstruction), m.Name, m.HasStaticKeyword, m.IsGhost, tps, ins, m.Outs.ConvertAll(refinementCloner.CloneFormal), - req, mod, ens, decreases, body, refinementCloner.MergeAttributes(m.Attributes, moreAttributes), null); + req, mod, ens, decreases, body, refinementCloner.MergeAttributes(m.Attributes, moreAttributes), null, m); } } diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 899d0a3d..a2ae401a 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -853,9 +853,34 @@ namespace Microsoft.Dafny if (useImports || string.Equals(kv.Key, "_default", StringComparison.InvariantCulture)) { TopLevelDecl d; if (sig.TopLevels.TryGetValue(kv.Key, out d)) { - if (DafnyOptions.O.IronDafny && kv.Value.ClonedFrom == d) { + bool resolved = false; + if (DafnyOptions.O.IronDafny) { + // sometimes, we need to compare two type synonyms in order to come up with a decision regarding substitution. + var aliased1 = Object.ReferenceEquals(kv.Value, d); + if (!aliased1) { + var a = d; + while (a.ExclusiveRefinement != null) { + a = a.ExclusiveRefinement; + } + var b = kv.Value; + while (b.ExclusiveRefinement != null) { + b = b.ExclusiveRefinement; + } + if (a is TypeSynonymDecl && b is TypeSynonymDecl) { + aliased1 = UnifyTypes(((TypeSynonymDecl)a).Rhs, ((TypeSynonymDecl)b).Rhs); + } else { + aliased1 = Object.ReferenceEquals(a, b); + } + } + if (aliased1 || + Object.ReferenceEquals(kv.Value.ClonedFrom, d) || + Object.ReferenceEquals(d.ClonedFrom, kv.Value) || + Object.ReferenceEquals(kv.Value.ExclusiveRefinement, d)) { sig.TopLevels[kv.Key] = kv.Value; - } else { + resolved = true; + } + } + if (!resolved) { sig.TopLevels[kv.Key] = AmbiguousTopLevelDecl.Create(moduleDef, d, kv.Value); } } else { @@ -872,7 +897,10 @@ namespace Microsoft.Dafny if (sig.Ctors.TryGetValue(kv.Key, out pair)) { // The same ctor can be imported from two different imports (e.g "diamond" imports), in which case, // they are not duplicates. - if (kv.Value.Item1 != pair.Item1) { + if (!Object.ReferenceEquals(kv.Value.Item1, pair.Item1) && + (!DafnyOptions.O.IronDafny || + (!Object.ReferenceEquals(kv.Value.Item1.ClonedFrom, pair.Item1) && + !Object.ReferenceEquals(kv.Value.Item1, pair.Item1.ClonedFrom)))) { // mark it as a duplicate sig.Ctors[kv.Key] = new Tuple(pair.Item1, true); } @@ -888,7 +916,48 @@ namespace Microsoft.Dafny foreach (var kv in s.StaticMembers) { MemberDecl md; if (sig.StaticMembers.TryGetValue(kv.Key, out md)) { + var resolved = false; + if (DafnyOptions.O.IronDafny) { + var aliased0 = Object.ReferenceEquals(kv.Value, md) || Object.ReferenceEquals(kv.Value.ClonedFrom, md) || Object.ReferenceEquals(md.ClonedFrom, kv.Value); + var aliased1 = aliased0; + if (!aliased0) { + var a = kv.Value.EnclosingClass; + while (a != null && + (a.ExclusiveRefinement != null || a.ClonedFrom != null)) { + if (a.ClonedFrom != null) { + a = (TopLevelDecl)a.ClonedFrom; + } else { + Contract.Assert(a.ExclusiveRefinement != null); + a = a.ExclusiveRefinement; + } + } + var b = md.EnclosingClass; + while (b != null && + (b.ExclusiveRefinement != null || b.ClonedFrom != null)) { + if (b.ClonedFrom != null) { + b = (TopLevelDecl)b.ClonedFrom; + } else { + Contract.Assert(b.ExclusiveRefinement != null); + b = b.ExclusiveRefinement; + } + } + aliased1 = Object.ReferenceEquals(a, b); + } + if (aliased0 || aliased1) { + if (kv.Value.EnclosingClass != null && + md.EnclosingClass != null && + md.EnclosingClass.ExclusiveRefinement != null && + !Object.ReferenceEquals( + kv.Value.EnclosingClass.ExclusiveRefinement, + md.EnclosingClass)) { + sig.StaticMembers[kv.Key] = kv.Value; + } + resolved = true; + } + } + if (!resolved) { sig.StaticMembers[kv.Key] = AmbiguousMemberDecl.Create(moduleDef, md, kv.Value); + } } else { // add new sig.StaticMembers.Add(kv.Key, kv.Value); @@ -1245,6 +1314,14 @@ namespace Microsoft.Dafny var typeRedirectionDependencies = new Graph(); foreach (TopLevelDecl d in declarations) { + if (DafnyOptions.O.IronDafny && d.Module.IsExclusiveRefinement) { + var refinementOf = + def.RefinementBase.TopLevelDecls.Find( + i => String.Equals(i.Name, d.Name, StringComparison.InvariantCulture)); + if (refinementOf != null && refinementOf.ExclusiveRefinement == null) { + refinementOf.ExclusiveRefinement = d; + } + } Contract.Assert(d != null); allTypeParameters.PushMarker(); ResolveTypeParameters(d.TypeArgs, true, d); @@ -1277,9 +1354,13 @@ namespace Microsoft.Dafny if (!def.IsAbstract) { if (decl.Signature.IsGhost) { - if (!(def.IsDefaultModule)) // _module is allowed to contain abstract modules, but not be abstract itself. Note this presents a challenge to + if (// _module is allowed to contain abstract modules, but not be abstract itself. Note this presents a challenge to // trusted verification, as toplevels can't be trusted if they invoke abstract module members. - reporter.Error(MessageSource.Resolver, d.tok, "an abstract module can only be imported into other abstract modules, not a concrete one."); + !def.IsDefaultModule + // [IronDafny] it's possbile for an abstract module to have an exclusive refinement, so it no longer makes sense to disallow this. + && !DafnyOptions.O.IronDafny) + + reporter.Error(MessageSource.Resolver, d.tok, "an abstract module can only be imported into other abstract modules, not a concrete one."); } else { // physical modules are allowed everywhere } @@ -4198,6 +4279,11 @@ namespace Microsoft.Dafny } } else if (d is NewtypeDecl) { var dd = (NewtypeDecl)d; + if (DafnyOptions.O.IronDafny) { + while (dd.ClonedFrom != null) { + dd = (NewtypeDecl)d.ClonedFrom; + } + } var caller = context as ICallable; if (caller != null) { caller.EnclosingModule.CallGraph.AddEdge(caller, dd); @@ -4313,28 +4399,60 @@ namespace Microsoft.Dafny UnifyTypes(((MapType)a).Domain, ((MapType)b).Domain) && UnifyTypes(((MapType)a).Range, ((MapType)b).Range); } else if (a is SeqType) { return b is SeqType && UnifyTypes(((SeqType)a).Arg, ((SeqType)b).Arg); - } else if (a is UserDefinedType) { - if (!(b is UserDefinedType)) { - return false; + } else if (a is UserDefinedType || b is UserDefinedType) { + if (!(a is UserDefinedType) && b is UserDefinedType) { + var x = a; + a = b; + b = x; } + var aa = (UserDefinedType)a; var rca = aa.ResolvedClass; + // traits are currently unfriendly to irondafny features. + if (DafnyOptions.O.IronDafny && !(rca is TraitDecl)) { + if (rca != null) { + while (rca.ClonedFrom != null || rca.ExclusiveRefinement != null) { + if (rca.ClonedFrom != null) { + rca = (TopLevelDecl)rca.ClonedFrom; + } else { + Contract.Assert(rca.ExclusiveRefinement != null); + rca = rca.ExclusiveRefinement; + } + } + } + } + + if (!(b is UserDefinedType)) { + return DafnyOptions.O.IronDafny && rca is TypeSynonymDecl && UnifyTypes(((TypeSynonymDecl)rca).Rhs, b); + } + var bb = (UserDefinedType)b; var rcb = bb.ResolvedClass; - if (DafnyOptions.O.IronDafny) - { - while (rca != null && rca.Module.IsAbstract && rca.ClonedFrom != null) - { - // todo: should ClonedFrom be a TopLevelDecl? - // todo: should ClonedFrom be moved to TopLevelDecl? - rca = (TopLevelDecl)rca.ClonedFrom; + // traits are currently unfriendly to irondafny features. + if (DafnyOptions.O.IronDafny && !(rca is TraitDecl) && !(rcb is TraitDecl)) { + if (rcb != null) { + while (rcb.ClonedFrom != null || rcb.ExclusiveRefinement != null) { + if (rcb.ClonedFrom != null) { + rcb = (TopLevelDecl)rcb.ClonedFrom; + } else { + Contract.Assert(rcb.ExclusiveRefinement != null); + rcb = rcb.ExclusiveRefinement; + } + } } - while (rcb != null && rcb.Module.IsAbstract && rcb.ClonedFrom != null) - { - rcb = (TopLevelDecl)rcb.ClonedFrom; + if (rca is TypeSynonymDecl || rcb is TypeSynonymDecl) { + var aaa = a; + var bbb = b; + if (rca is TypeSynonymDecl) { + aaa = ((TypeSynonymDecl)rca).Rhs; + } + if (rcb is TypeSynonymDecl) { + bbb = ((TypeSynonymDecl)rcb).Rhs; + } + return UnifyTypes(aaa, bbb); } } - if (rca != null && rca == rcb) { + if (rca != null && Object.ReferenceEquals(rca, rcb)) { // these are both resolved class/datatype types Contract.Assert(aa.TypeArgs.Count == bb.TypeArgs.Count); bool successSoFar = true; diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 09ca32e0..af8225da 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -9246,41 +9246,41 @@ namespace Microsoft.Dafny { Contract.Requires(type != null); Contract.Ensures(Contract.Result() != null); - type = type.NormalizeExpand(); - - if (type is SetType) { - bool finite = ((SetType)type).Finite; - return FunctionCall(Token.NoToken, finite ? "TSet" : "TISet", predef.Ty, TypeToTy(((CollectionType)type).Arg)); - } else if (type is MultiSetType) { - return FunctionCall(Token.NoToken, "TMultiSet", predef.Ty, TypeToTy(((CollectionType)type).Arg)); - } else if (type is SeqType) { - return FunctionCall(Token.NoToken, "TSeq", predef.Ty, TypeToTy(((CollectionType)type).Arg)); - } else if (type is MapType) { - bool finite = ((MapType)type).Finite; + var normType = type.NormalizeExpand(); + + if (normType is SetType) { + bool finite = ((SetType)normType).Finite; + return FunctionCall(Token.NoToken, finite ? "TSet" : "TISet", predef.Ty, TypeToTy(((CollectionType)normType).Arg)); + } else if (normType is MultiSetType) { + return FunctionCall(Token.NoToken, "TMultiSet", predef.Ty, TypeToTy(((CollectionType)normType).Arg)); + } else if (normType is SeqType) { + return FunctionCall(Token.NoToken, "TSeq", predef.Ty, TypeToTy(((CollectionType)normType).Arg)); + } else if (normType is MapType) { + bool finite = ((MapType)normType).Finite; return FunctionCall(Token.NoToken, finite ? "TMap" : "TIMap", predef.Ty, - TypeToTy(((MapType)type).Domain), - TypeToTy(((MapType)type).Range)); - } else if (type is BoolType) { + TypeToTy(((MapType)normType).Domain), + TypeToTy(((MapType)normType).Range)); + } else if (normType is BoolType) { return new Bpl.IdentifierExpr(Token.NoToken, "TBool", predef.Ty); - } else if (type is CharType) { + } else if (normType is CharType) { return new Bpl.IdentifierExpr(Token.NoToken, "TChar", predef.Ty); - } else if (type is RealType) { + } else if (normType is RealType) { return new Bpl.IdentifierExpr(Token.NoToken, "TReal", predef.Ty); - } else if (type is NatType) { + } else if (normType is NatType) { // (Nat needs to come before Int) return new Bpl.IdentifierExpr(Token.NoToken, "TNat", predef.Ty); - } else if (type is IntType) { + } else if (normType is IntType) { return new Bpl.IdentifierExpr(Token.NoToken, "TInt", predef.Ty); - } else if (type.IsTypeParameter) { - return trTypeParam(type.AsTypeParameter, type.TypeArgs); - } else if (type is ObjectType) { + } else if (normType.IsTypeParameter) { + return trTypeParam(normType.AsTypeParameter, normType.TypeArgs); + } else if (normType is ObjectType) { return ClassTyCon(program.BuiltIns.ObjectDecl, new List()); - } else if (type is UserDefinedType) { + } else if (normType is UserDefinedType) { // Classes, (co-)datatypes - var args = type.TypeArgs.ConvertAll(TypeToTy); - return ClassTyCon(((UserDefinedType)type), args); - } else if (type is ParamTypeProxy) { - return trTypeParam(((ParamTypeProxy)type).orig, null); + var args = normType.TypeArgs.ConvertAll(TypeToTy); + return ClassTyCon(((UserDefinedType)normType), args); + } else if (normType is ParamTypeProxy) { + return trTypeParam(((ParamTypeProxy)normType).orig, null); } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type } @@ -9383,19 +9383,19 @@ namespace Microsoft.Dafny { Contract.Requires(etran != null); Contract.Requires(predef != null); - type = type.NormalizeExpand(); - if (type is TypeProxy) { + var normType = type.NormalizeExpand(); + if (normType is TypeProxy) { // Unresolved proxy // Omit where clause (in other places, unresolved proxies are treated as a reference type; we could do that here too, but // we might as well leave out the where clause altogether). return null; } - if (type is NatType) { + if (normType is NatType) { // nat: // 0 <= x return Bpl.Expr.Le(Bpl.Expr.Literal(0), x); - } else if (type is BoolType || type is IntType || type is RealType) { + } else if (normType is BoolType || normType is IntType || normType is RealType) { // nothing to do return null; /* } else if (type is ArrowType) { @@ -9403,7 +9403,7 @@ namespace Microsoft.Dafny { return null; */ } else { - return BplAnd(MkIs(x, type), MkIsAlloc(x, type, etran.HeapExpr)); + return BplAnd(MkIs(x, normType), MkIsAlloc(x, normType, etran.HeapExpr)); } } @@ -11029,7 +11029,11 @@ namespace Microsoft.Dafny { } var ty = translator.TrType(e.Type); - var id = new Bpl.IdentifierExpr(e.tok, e.Function.FullSanitizedName, ty); + var name = e.Function.FullSanitizedName; + if (DafnyOptions.O.IronDafny) { + name = e.Function.FullSanitizedRefinementName; + } + var id = new Bpl.IdentifierExpr(e.tok, name, ty); bool argsAreLit; var args = FunctionInvocationArguments(e, layerArgument, out argsAreLit); -- cgit v1.2.3 From 0c1ec594e68dab4fcd458a00f9f7a1ac6de2e218 Mon Sep 17 00:00:00 2001 From: leino Date: Mon, 28 Sep 2015 13:16:15 -0700 Subject: Changed computation of ghosts until pass 2 of resolution. Other clean-up in resolution passes, like: Include everything of type "char" into bounds that are discovered, and likewise for reference types. Allow more set comprehensions, determining if they are finite based on their argument type. Changed CalcExpr.SubExpressions to not include computed expressions. --- Binaries/DafnyRuntime.cs | 13 + Source/Dafny/Cloner.cs | 14 +- Source/Dafny/Compiler.cs | 11 + Source/Dafny/DafnyAst.cs | 172 +++++++- Source/Dafny/Resolver.cs | 562 ++++++++++++++++----------- Test/dafny0/AssumptionVariables0.dfy.expect | 4 +- Test/dafny0/CallStmtTests.dfy | 34 +- Test/dafny0/CallStmtTests.dfy.expect | 4 +- Test/dafny0/DiscoverBounds.dfy.expect | 6 +- Test/dafny0/NonGhostQuantifiers.dfy | 7 + Test/dafny0/NonGhostQuantifiers.dfy.expect | 22 +- Test/dafny0/ParallelResolveErrors.dfy | 13 +- Test/dafny0/ParallelResolveErrors.dfy.expect | 36 +- Test/dafny0/ResolutionErrors.dfy | 278 +++++++++++-- Test/dafny0/ResolutionErrors.dfy.expect | 87 +++-- Test/dafny0/TypeTests.dfy | 50 +-- Test/dafny0/TypeTests.dfy.expect | 14 +- Test/dafny4/set-compr.dfy.expect | 4 +- 18 files changed, 925 insertions(+), 406 deletions(-) (limited to 'Source/Dafny/Compiler.cs') diff --git a/Binaries/DafnyRuntime.cs b/Binaries/DafnyRuntime.cs index d1a3c092..4dda19fe 100644 --- a/Binaries/DafnyRuntime.cs +++ b/Binaries/DafnyRuntime.cs @@ -1012,6 +1012,12 @@ namespace Dafny return pred(false) || pred(true); } } + public static bool QuantChar(bool frall, System.Predicate pred) { + for (int i = 0; i < 0x10000; i++) { + if (pred((char)i) != frall) { return !frall; } + } + return frall; + } public static bool QuantInt(BigInteger lo, BigInteger hi, bool frall, System.Predicate pred) { for (BigInteger i = lo; i < hi; i++) { if (pred(i) != frall) { return !frall; } @@ -1051,6 +1057,13 @@ namespace Dafny yield return true; } } + public static IEnumerable AllChars { + get { + for (int i = 0; i < 0x10000; i++) { + yield return (char)i; + } + } + } public static IEnumerable AllIntegers { get { yield return new BigInteger(0); diff --git a/Source/Dafny/Cloner.cs b/Source/Dafny/Cloner.cs index dd2eed69..032e30a0 100644 --- a/Source/Dafny/Cloner.cs +++ b/Source/Dafny/Cloner.cs @@ -550,13 +550,21 @@ namespace Microsoft.Dafny r = new ForallStmt(Tok(s.Tok), Tok(s.EndTok), s.BoundVars.ConvertAll(CloneBoundVar), null, CloneExpr(s.Range), s.Ens.ConvertAll(CloneMayBeFreeExpr), CloneStmt(s.Body)); } else if (stmt is CalcStmt) { - var s = (CalcStmt)stmt; - r = new CalcStmt(Tok(s.Tok), Tok(s.EndTok), CloneCalcOp(s.Op), s.Lines.ConvertAll(CloneExpr), s.Hints.ConvertAll(CloneBlockStmt), s.StepOps.ConvertAll(CloneCalcOp), CloneCalcOp(s.ResultOp), CloneAttributes(s.Attributes)); + var s = (CalcStmt)stmt; + // calc statements have the unusual property that the last line is duplicated. If that is the case (which + // we expect it to be here), we share the clone of that line as well. + var lineCount = s.Lines.Count; + var lines = new List(lineCount); + for (int i = 0; i < lineCount; i++) { + lines.Add(i == lineCount - 1 && 2 <= lineCount && s.Lines[i] == s.Lines[i - 1] ? lines[i - 1] : CloneExpr(s.Lines[i])); + } + Contract.Assert(lines.Count == lineCount); + r = new CalcStmt(Tok(s.Tok), Tok(s.EndTok), CloneCalcOp(s.Op), lines, s.Hints.ConvertAll(CloneBlockStmt), s.StepOps.ConvertAll(CloneCalcOp), CloneCalcOp(s.ResultOp), CloneAttributes(s.Attributes)); } else if (stmt is MatchStmt) { var s = (MatchStmt)stmt; r = new MatchStmt(Tok(s.Tok), Tok(s.EndTok), CloneExpr(s.Source), - s.Cases.ConvertAll(CloneMatchCaseStmt), s.UsesOptionalBraces); + s.Cases.ConvertAll(CloneMatchCaseStmt), s.UsesOptionalBraces); } else if (stmt is AssignSuchThatStmt) { var s = (AssignSuchThatStmt)stmt; diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index 477acabf..cdd968cf 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -1566,6 +1566,9 @@ namespace Microsoft.Dafny { if (bound is ComprehensionExpr.BoolBoundedPool) { Indent(ind); wr.Write("foreach (var @{0} in Dafny.Helpers.AllBooleans) {{ ", bv.CompileName); + } else if (bound is ComprehensionExpr.CharBoundedPool) { + Indent(ind); + wr.Write("foreach (var @{0} in Dafny.Helpers.AllChars) {{ ", bv.CompileName); } else if (bound is ComprehensionExpr.IntBoundedPool) { var b = (ComprehensionExpr.IntBoundedPool)bound; Indent(ind); @@ -1777,6 +1780,8 @@ namespace Microsoft.Dafny { Indent(ind); if (bound is ComprehensionExpr.BoolBoundedPool) { wr.WriteLine("foreach (var {0} in Dafny.Helpers.AllBooleans) {{ @{1} = {0};", tmpVar, bv.CompileName); + } else if (bound is ComprehensionExpr.CharBoundedPool) { + wr.WriteLine("foreach (var {0} in Dafny.Helpers.AllChars) {{ @{1} = {0};", tmpVar, bv.CompileName); } else if (bound is ComprehensionExpr.IntBoundedPool) { var b = (ComprehensionExpr.IntBoundedPool)bound; if (AsNativeType(bv.Type) != null) { @@ -2831,6 +2836,8 @@ namespace Microsoft.Dafny { // emit: Dafny.Helpers.QuantX(boundsInformation, isForall, bv => body) if (bound is ComprehensionExpr.BoolBoundedPool) { wr.Write("Dafny.Helpers.QuantBool("); + } else if (bound is ComprehensionExpr.CharBoundedPool) { + wr.Write("Dafny.Helpers.QuantChar("); } else if (bound is ComprehensionExpr.IntBoundedPool) { var b = (ComprehensionExpr.IntBoundedPool)bound; wr.Write("Dafny.Helpers.QuantInt("); @@ -2898,6 +2905,8 @@ namespace Microsoft.Dafny { var bv = e.BoundVars[i]; if (bound is ComprehensionExpr.BoolBoundedPool) { wr.Write("foreach (var @{0} in Dafny.Helpers.AllBooleans) {{ ", bv.CompileName); + } else if (bound is ComprehensionExpr.CharBoundedPool) { + wr.Write("foreach (var @{0} in Dafny.Helpers.AllChars) {{ ", bv.CompileName); } else if (bound is ComprehensionExpr.IntBoundedPool) { var b = (ComprehensionExpr.IntBoundedPool)bound; if (AsNativeType(bv.Type) != null) { @@ -2971,6 +2980,8 @@ namespace Microsoft.Dafny { var bv = e.BoundVars[0]; if (bound is ComprehensionExpr.BoolBoundedPool) { wr.Write("foreach (var @{0} in Dafny.Helpers.AllBooleans) {{ ", bv.CompileName); + } else if (bound is ComprehensionExpr.CharBoundedPool) { + wr.Write("foreach (var @{0} in Dafny.Helpers.AllChars) {{ ", bv.CompileName); } else if (bound is ComprehensionExpr.IntBoundedPool) { var b = (ComprehensionExpr.IntBoundedPool)bound; if (AsNativeType(bv.Type) != null) { diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index 2a98d5c2..b460d9b4 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -509,6 +509,27 @@ namespace Microsoft.Dafny { } } + public bool HasFinitePossibleValues { + get { + if (IsBoolType || IsCharType || IsRefType) { + return true; + } + var st = AsSetType; + if (st != null && st.Arg.HasFinitePossibleValues) { + return true; + } + var mt = AsMapType; + if (mt != null && mt.Domain.HasFinitePossibleValues) { + return true; + } + var dt = AsDatatype; + if (dt != null && dt.HasFinitePossibleValues) { + return true; + } + return false; + } + } + public CollectionType AsCollectionType { get { return NormalizeExpand() as CollectionType; } } public SetType AsSetType { get { return NormalizeExpand() as SetType; } } public MultiSetType AsMultiSetType { get { return NormalizeExpand() as MultiSetType; } } @@ -2376,6 +2397,48 @@ namespace Microsoft.Dafny { YieldCountVariable.type = YieldCountVariable.OptionalType; // resolve YieldCountVariable here } + /// + /// Returns the non-null expressions of this declaration proper (that is, do not include the expressions of substatements). + /// Does not include the generated class members. + /// + public virtual IEnumerable SubExpressions { + get { + foreach (var e in Attributes.SubExpressions(Attributes)) { + yield return e; + } + foreach (var e in Attributes.SubExpressions(Reads.Attributes)) { + yield return e; + } + foreach (var e in Reads.Expressions) { + yield return e.E; + } + foreach (var e in Attributes.SubExpressions(Modifies.Attributes)) { + yield return e; + } + foreach (var e in Modifies.Expressions) { + yield return e.E; + } + foreach (var e in Attributes.SubExpressions(Decreases.Attributes)) { + yield return e; + } + foreach (var e in Decreases.Expressions) { + yield return e; + } + foreach (var e in Requires) { + yield return e.E; + } + foreach (var e in Ensures) { + yield return e.E; + } + foreach (var e in YieldRequires) { + yield return e.E; + } + foreach (var e in YieldEnsures) { + yield return e.E; + } + } + } + /// /// This Dafny type exists only for the purpose of giving the yield-count variable a type, so /// that the type can be recognized during translation of Dafny into Boogie. It represents @@ -2475,6 +2538,11 @@ namespace Microsoft.Dafny { return EnclosingClass.FullCompileName + ".@" + CompileName; } } + public virtual IEnumerable SubExpressions { + get { + yield break; + } + } } public class Field : MemberDecl { @@ -2999,6 +3067,26 @@ namespace Microsoft.Dafny { public bool IsBuiltin; public Function OverriddenFunction; + public override IEnumerable SubExpressions { + get { + foreach (var e in Req) { + yield return e; + } + foreach (var e in Reads) { + yield return e.E; + } + foreach (var e in Ens) { + yield return e; + } + foreach (var e in Decreases.Expressions) { + yield return e; + } + if (Body != null) { + yield return Body; + } + } + } + public Type Type { get { // Note, the following returned type can contain type parameters from the function and its enclosing class @@ -3213,6 +3301,24 @@ namespace Microsoft.Dafny { public readonly ISet AssignedAssumptionVariables = new HashSet(); public Method OverriddenMethod; + public override IEnumerable SubExpressions { + get { + foreach (var e in Req) { + yield return e.E; + } + foreach (var e in Mod.Expressions) { + yield return e.E; + } + foreach (var e in Ens) { + yield return e.E; + } + foreach (var e in Decreases.Expressions) { + yield return e; + } + } + } + + [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(cce.NonNullElements(TypeArgs)); @@ -4821,18 +4927,32 @@ namespace Microsoft.Dafny { } public override IEnumerable SubExpressions { - get { // FIXME: This can return duplicates; this could confuse BottomUpVisitors that modify the AST in place + get { foreach (var e in base.SubExpressions) { yield return e; } foreach (var e in Attributes.SubExpressions(Attributes)) { yield return e; } - - foreach (var l in Lines) { - yield return l; + + for (int i = 0; i < Lines.Count - 1; i++) { // note, we skip the duplicated line at the end + yield return Lines[i]; } - foreach (var e in Steps) { - yield return e; + foreach (var calcop in AllCalcOps) { + var o3 = calcop as TernaryCalcOp; + if (o3 != null) { + yield return o3.Index; + } + } + } + } + + IEnumerable AllCalcOps { + get { + if (Op != null) { + yield return Op; } - if (Result != null) { - yield return Result; + foreach (var stepop in StepOps) { + yield return stepop; + } + if (ResultOp != null) { + yield return ResultOp; } } } @@ -6637,7 +6757,7 @@ namespace Microsoft.Dafny { public readonly Expression Body; public readonly bool Exact; // Exact==true means a regular let expression; Exact==false means an assign-such-that expression public readonly Attributes Attributes; - public List Constraint_Bounds; // initialized and filled in by resolver; null for Exact=true and for a ghost statement + public List Constraint_Bounds; // initialized and filled in by resolver; null for Exact=true and for when expression is in a ghost context // invariant Constraint_Bounds == null || Constraint_Bounds.Count == BoundVars.Count; public List Constraint_MissingBounds; // filled in during resolution; remains "null" if Exact==true or if bounds can be found // invariant Constraint_Bounds == null || Constraint_MissingBounds == null; @@ -6790,6 +6910,22 @@ namespace Microsoft.Dafny { return 5; } } + public class CharBoundedPool : BoundedPool + { + public override int Preference() { + return 4; + } + } + public class RefBoundedPool : BoundedPool + { + public Type Type; + public RefBoundedPool(Type t) { + Type = t; + } + public override int Preference() { + return 2; + } + } public class IntBoundedPool : BoundedPool { public readonly Expression LowerBound; @@ -6864,6 +7000,24 @@ namespace Microsoft.Dafny { public List MissingBounds; // filled in during resolution; remains "null" if bounds can be found // invariant Bounds == null || MissingBounds == null; + public List UncompilableBoundVars() { + var bvs = new List(); + if (MissingBounds != null) { + bvs.AddRange(MissingBounds); + } + if (Bounds != null) { + Contract.Assert(Bounds.Count == BoundVars.Count); + for (int i = 0; i < Bounds.Count; i++) { + var bound = Bounds[i]; + if (bound is RefBoundedPool) { + // yes, this is in principle a bound, but it's not one we'd like to compile + bvs.Add(BoundVars[i]); + } + } + } + return bvs; + } + public ComprehensionExpr(IToken tok, List bvars, Expression range, Expression term, Attributes attrs) : base(tok) { Contract.Requires(tok != null); diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index d82d7d1f..ec3a69c9 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -1313,16 +1313,6 @@ namespace Microsoft.Dafny } } - private readonly List needFiniteBoundsChecks_SetComprehension = new List(); - private readonly List needBoundsDiscovery_AssignSuchThatStmt = new List(); - private readonly List needFiniteBoundsChecks_LetSuchThatExpr = new List(); - public int NFBC_Count { - // provided just for the purpose of conveniently writing contracts for ResolveTopLevelDecl_Meat - get { - return needFiniteBoundsChecks_SetComprehension.Count + needFiniteBoundsChecks_LetSuchThatExpr.Count; - } - } - static readonly List NativeTypes = new List() { new NativeType("byte", 0, 0x100, "", true), new NativeType("sbyte", -0x80, 0x80, "", true), @@ -1338,13 +1328,23 @@ namespace Microsoft.Dafny Contract.Requires(declarations != null); Contract.Requires(cce.NonNullElements(datatypeDependencies)); Contract.Requires(cce.NonNullElements(codatatypeDependencies)); - Contract.Requires(NFBC_Count == 0); Contract.Requires(AllTypeConstraints.Count == 0); - Contract.Ensures(NFBC_Count == 0); Contract.Ensures(AllTypeConstraints.Count == 0); int prevErrorCount = reporter.Count(ErrorLevel.Error); + // ---------------------------------- Pass 0 ---------------------------------- + // This pass resolves names, introduces (and may solve) type constraints, and + // builds the module's call graph. + // Some bounds are discovered during this pass [is this necessary? can they be + // moved to pass 1 like the other bounds discovery? --KRML], namely: + // - forall statements + // - quantifier expressions + // - map comprehensions + // For 'newtype' declarations, it also checks that all types were fully + // determined. + // ---------------------------------------------------------------------------- + // Resolve the meat of classes and iterators, the definitions of type synonyms, and the type parameters of all top-level type declarations // First, resolve the newtype declarations and the constraint clauses, including filling in .ResolvedOp fields. This is needed for the // resolution of the other declarations, because those other declarations may invoke DiscoverBounds, which looks at the .Constraint field @@ -1403,172 +1403,80 @@ namespace Microsoft.Dafny allTypeParameters.PopMarker(); } + // ---------------------------------- Pass 1 ---------------------------------- + // This pass: + // * checks that type inference was able to determine all types + // * fills in the .ResolvedOp field of binary expressions + // * discovers bounds for: + // - set comprehensions + // - assign-such-that statements + // - compilable let-such-that expressions + // - newtype constraints + // For each statement body that it successfully typed, this pass also: + // * computes ghost interests + // * determines/checks tail-recursion. + // ---------------------------------------------------------------------------- + Dictionary nativeTypeMap = new Dictionary(); foreach (var nativeType in NativeTypes) { nativeTypeMap.Add(nativeType.Name, nativeType); } - if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { // Check that type inference went well everywhere; this will also fill in the .ResolvedOp field in binary expressions foreach (TopLevelDecl d in declarations) { if (d is IteratorDecl) { var iter = (IteratorDecl)d; + var prevErrCnt = reporter.Count(ErrorLevel.Error); iter.Members.Iter(CheckTypeInference_Member); + if (prevErrCnt == reporter.Count(ErrorLevel.Error)) { + iter.SubExpressions.Iter(e => CheckExpression(e, this, iter)); + } if (iter.Body != null) { CheckTypeInference(iter.Body); + if (prevErrCnt == reporter.Count(ErrorLevel.Error)) { + ComputeGhostInterest(iter.Body, false, iter); + CheckExpression(iter.Body, this, iter); + } } } else if (d is ClassDecl) { var cl = (ClassDecl)d; - cl.Members.Iter(CheckTypeInference_Member); foreach (var member in cl.Members) { - var m = member as Method; - if (m != null && m.Body != null) { - DetermineTailRecursion(m); + var prevErrCnt = reporter.Count(ErrorLevel.Error); + CheckTypeInference_Member(member); + if (prevErrCnt == reporter.Count(ErrorLevel.Error)) { + if (member is Method) { + var m = (Method)member; + if (m.Body != null) { + ComputeGhostInterest(m.Body, m.IsGhost, m); + CheckExpression(m.Body, this, m); + DetermineTailRecursion(m); + } + } else if (member is Function) { + var f = (Function)member; + if (!f.IsGhost && f.Body != null) { + CheckIsNonGhost(f.Body); + } + DetermineTailRecursion(f); + } + if (prevErrCnt == reporter.Count(ErrorLevel.Error) && member is ICodeContext) { + member.SubExpressions.Iter(e => CheckExpression(e, this, (ICodeContext)member)); + } } } } else if (d is NewtypeDecl) { var dd = (NewtypeDecl)d; - bool? boolNativeType = null; - NativeType stringNativeType = null; - object nativeTypeAttr = true; - bool hasNativeTypeAttr = Attributes.ContainsMatchingValue(dd.Attributes, "nativeType", ref nativeTypeAttr, - new Attributes.MatchingValueOption[] { - Attributes.MatchingValueOption.Empty, - Attributes.MatchingValueOption.Bool, - Attributes.MatchingValueOption.String }, - err => reporter.Error(MessageSource.Resolver, dd, err)); - if (hasNativeTypeAttr) { - if (nativeTypeAttr is bool) { - boolNativeType = (bool)nativeTypeAttr; - } else { - string keyString = (string)nativeTypeAttr; - if (nativeTypeMap.ContainsKey(keyString)) { - stringNativeType = nativeTypeMap[keyString]; - } else { - reporter.Error(MessageSource.Resolver, dd, "Unsupported nativeType {0}", keyString); - } - } - } - if (stringNativeType != null || boolNativeType == true) { - if (!dd.BaseType.IsNumericBased(Type.NumericPersuation.Int)) { - reporter.Error(MessageSource.Resolver, dd, "nativeType can only be used on integral types"); - } - if (dd.Var == null) { - reporter.Error(MessageSource.Resolver, dd, "nativeType can only be used if newtype specifies a constraint"); - } - } if (dd.Var != null) { Contract.Assert(dd.Constraint != null); - CheckTypeInference(dd.Constraint); - - Func GetConst = null; - GetConst = (Expression e) => { - int m = 1; - BinaryExpr bin = e as BinaryExpr; - if (bin != null && bin.Op == BinaryExpr.Opcode.Sub && GetConst(bin.E0) == BigInteger.Zero) { - m = -1; - e = bin.E1; - } - LiteralExpr l = e as LiteralExpr; - if (l != null && l.Value is BigInteger) { - return m * (BigInteger)l.Value; - } - return null; - }; - var bounds = DiscoverAllBounds_SingleVar(dd.Var, dd.Constraint); - List potentialNativeTypes = - (stringNativeType != null) ? new List { stringNativeType } : - (boolNativeType == false) ? new List() : - NativeTypes; - foreach (var nt in potentialNativeTypes) { - bool lowerOk = false; - bool upperOk = false; - foreach (var bound in bounds) { - if (bound is ComprehensionExpr.IntBoundedPool) { - var bnd = (ComprehensionExpr.IntBoundedPool)bound; - if (bnd.LowerBound != null) { - BigInteger? lower = GetConst(bnd.LowerBound); - if (lower != null && nt.LowerBound <= lower) { - lowerOk = true; - } - } - if (bnd.UpperBound != null) { - BigInteger? upper = GetConst(bnd.UpperBound); - if (upper != null && upper <= nt.UpperBound) { - upperOk = true; - } - } - } - } - if (lowerOk && upperOk) { - dd.NativeType = nt; - break; - } - } - if (dd.NativeType == null && (boolNativeType == true || stringNativeType != null)) { - reporter.Error(MessageSource.Resolver, dd, "Dafny's heuristics cannot find a compatible native type. " + - "Hint: try writing a newtype constraint of the form 'i:int | lowerBound <= i < upperBound && (...any additional constraints...)'"); - } - if (dd.NativeType != null && stringNativeType == null) { - reporter.Info(MessageSource.Resolver, dd.tok, "{:nativeType \"" + dd.NativeType.Name + "\"}"); - } + CheckExpression(dd.Constraint, this, dd); } + FigureOutNativeType(dd, nativeTypeMap); } } } - if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { - // At this point, it is necessary to know for each statement and expression if it is ghost or not - foreach (var e in needFiniteBoundsChecks_SetComprehension) { - CheckTypeInference(e.Range); // we need to resolve operators before the call to DiscoverBounds - List missingBounds; - e.Bounds = DiscoverBestBounds_MultipleVars(e.BoundVars, e.Range, true, true, out missingBounds); - if (missingBounds.Count != 0) { - e.MissingBounds = missingBounds; - if (e.Finite) { - foreach (var bv in e.MissingBounds) { - reporter.Error(MessageSource.Resolver, e, "a set comprehension must produce a finite set, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name); - } - } - } - } - foreach (AssignSuchThatStmt s in needBoundsDiscovery_AssignSuchThatStmt) { - Contract.Assert(!s.IsGhost); - var varLhss = new List(); - foreach (var lhs in s.Lhss) { - var ide = (IdentifierExpr)lhs.Resolved; // successful resolution above implies all LHS's are IdentifierExpr's - varLhss.Add(ide.Var); - } - - List missingBounds; - var bestBounds = DiscoverBestBounds_MultipleVars(varLhss, s.Expr, true, false, out missingBounds); - if (missingBounds.Count != 0) { - s.MissingBounds = missingBounds; // so that an error message can be produced during compilation - } else { - Contract.Assert(bestBounds != null); - s.Bounds = bestBounds; - } - } - foreach (var e in needFiniteBoundsChecks_LetSuchThatExpr) { - Contract.Assert(!e.Exact); // only let-such-that expressions are ever added to the list - Contract.Assert(e.RHSs.Count == 1); // if we got this far, the resolver will have checked this condition successfully - var constraint = e.RHSs[0]; - CheckTypeInference(constraint); // we need to resolve operators before the call to DiscoverBounds - List missingBounds; - var vars = new List(e.BoundVars); - var bestBounds = DiscoverBestBounds_MultipleVars(vars, constraint, true, false, out missingBounds); - if (missingBounds.Count != 0) { - e.Constraint_MissingBounds = missingBounds; - foreach (var bv in e.Constraint_MissingBounds) { - reporter.Error(MessageSource.Resolver, e, "a non-ghost let-such-that constraint must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name); - } - } else { - e.Constraint_Bounds = bestBounds; - } - } - } - needFiniteBoundsChecks_SetComprehension.Clear(); - needFiniteBoundsChecks_LetSuchThatExpr.Clear(); + // ---------------------------------- Pass 2 ---------------------------------- + // This pass fills in various additional information. + // ---------------------------------------------------------------------------- if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { // fill in the postconditions and bodies of prefix lemmas @@ -1894,6 +1802,93 @@ namespace Microsoft.Dafny } } + private void FigureOutNativeType(NewtypeDecl dd, Dictionary nativeTypeMap) { + Contract.Requires(dd != null); + Contract.Requires(nativeTypeMap != null); + bool? boolNativeType = null; + NativeType stringNativeType = null; + object nativeTypeAttr = true; + bool hasNativeTypeAttr = Attributes.ContainsMatchingValue(dd.Attributes, "nativeType", ref nativeTypeAttr, + new Attributes.MatchingValueOption[] { + Attributes.MatchingValueOption.Empty, + Attributes.MatchingValueOption.Bool, + Attributes.MatchingValueOption.String }, + err => reporter.Error(MessageSource.Resolver, dd, err)); + if (hasNativeTypeAttr) { + if (nativeTypeAttr is bool) { + boolNativeType = (bool)nativeTypeAttr; + } else { + string keyString = (string)nativeTypeAttr; + if (nativeTypeMap.ContainsKey(keyString)) { + stringNativeType = nativeTypeMap[keyString]; + } else { + reporter.Error(MessageSource.Resolver, dd, "Unsupported nativeType {0}", keyString); + } + } + } + if (stringNativeType != null || boolNativeType == true) { + if (!dd.BaseType.IsNumericBased(Type.NumericPersuation.Int)) { + reporter.Error(MessageSource.Resolver, dd, "nativeType can only be used on integral types"); + } + if (dd.Var == null) { + reporter.Error(MessageSource.Resolver, dd, "nativeType can only be used if newtype specifies a constraint"); + } + } + if (dd.Var != null) { + Func GetConst = null; + GetConst = (Expression e) => { + int m = 1; + BinaryExpr bin = e as BinaryExpr; + if (bin != null && bin.Op == BinaryExpr.Opcode.Sub && GetConst(bin.E0) == BigInteger.Zero) { + m = -1; + e = bin.E1; + } + LiteralExpr l = e as LiteralExpr; + if (l != null && l.Value is BigInteger) { + return m * (BigInteger)l.Value; + } + return null; + }; + var bounds = DiscoverAllBounds_SingleVar(dd.Var, dd.Constraint); + List potentialNativeTypes = + (stringNativeType != null) ? new List { stringNativeType } : + (boolNativeType == false) ? new List() : + NativeTypes; + foreach (var nt in potentialNativeTypes) { + bool lowerOk = false; + bool upperOk = false; + foreach (var bound in bounds) { + if (bound is ComprehensionExpr.IntBoundedPool) { + var bnd = (ComprehensionExpr.IntBoundedPool)bound; + if (bnd.LowerBound != null) { + BigInteger? lower = GetConst(bnd.LowerBound); + if (lower != null && nt.LowerBound <= lower) { + lowerOk = true; + } + } + if (bnd.UpperBound != null) { + BigInteger? upper = GetConst(bnd.UpperBound); + if (upper != null && upper <= nt.UpperBound) { + upperOk = true; + } + } + } + } + if (lowerOk && upperOk) { + dd.NativeType = nt; + break; + } + } + if (dd.NativeType == null && (boolNativeType == true || stringNativeType != null)) { + reporter.Error(MessageSource.Resolver, dd, "Dafny's heuristics cannot find a compatible native type. " + + "Hint: try writing a newtype constraint of the form 'i:int | lowerBound <= i < upperBound && (...any additional constraints...)'"); + } + if (dd.NativeType != null && stringNativeType == null) { + reporter.Info(MessageSource.Resolver, dd.tok, "{:nativeType \"" + dd.NativeType.Name + "\"}"); + } + } + } + /// /// Adds the type constraint ty==expected, eventually printing the error message "msg" if this is found to be /// untenable. If this is immediately known not to be untenable, false is returned. @@ -2123,16 +2118,60 @@ namespace Microsoft.Dafny } else if (stmt is ForallStmt) { var s = (ForallStmt)stmt; s.BoundVars.Iter(bv => CheckTypeIsDetermined(bv.tok, bv.Type, "bound variable")); + } else if (stmt is AssignSuchThatStmt) { + var s = (AssignSuchThatStmt)stmt; + if (s.AssumeToken == null) { + var varLhss = new List(); + foreach (var lhs in s.Lhss) { + var ide = (IdentifierExpr)lhs.Resolved; // successful resolution implies all LHS's are IdentifierExpr's + varLhss.Add(ide.Var); + } + List missingBounds; + var bestBounds = DiscoverBestBounds_MultipleVars(varLhss, s.Expr, true, false, out missingBounds); + if (missingBounds.Count != 0) { + s.MissingBounds = missingBounds; // so that an error message can be produced during compilation + } else { + Contract.Assert(bestBounds != null); + s.Bounds = bestBounds; + } + } + } else if (stmt is CalcStmt) { + var s = (CalcStmt)stmt; + // The resolution of the calc statement builds up .Steps and .Result, which are of the form E0 OP E1, where + // E0 and E1 are expressions from .Lines. These additional expressions still need to have their .ResolvedOp + // fields filled in, so we visit them (but not their subexpressions) here. + foreach (var e in s.Steps) { + VisitOneExpr(e); + } + VisitOneExpr(s.Result); } } protected override void VisitOneExpr(Expression expr) { if (expr is ComprehensionExpr) { var e = (ComprehensionExpr)expr; - if (e != null) { - foreach (var bv in e.BoundVars) { - if (!IsDetermined(bv.Type.Normalize())) { - resolver.reporter.Error(MessageSource.Resolver, bv.tok, "type of bound variable '{0}' could not be determined; please specify the type explicitly", - bv.Name); + foreach (var bv in e.BoundVars) { + if (!IsDetermined(bv.Type.Normalize())) { + resolver.reporter.Error(MessageSource.Resolver, bv.tok, "type of bound variable '{0}' could not be determined; please specify the type explicitly", bv.Name); + } + } + if (e is SetComprehension) { + var sc = (SetComprehension)e; + if (sc.Finite) { + // A set must be finite. Discover bounds for the Range expression, but report an error only if the Term is not + // of a finite-individuals type. + List missingBounds; + sc.Bounds = DiscoverBestBounds_MultipleVars(sc.BoundVars, sc.Range, true, true, out missingBounds); + if (missingBounds.Count != 0) { + sc.MissingBounds = missingBounds; + if (sc.Type.HasFinitePossibleValues) { + // This means the set is finite, regardless of if the Range is bounded. So, we don't give any error here. + // However, if this expression is used in a non-ghost context (which is not yet known at this stage of + // resolution), the resolver will generate an error about that later. + } else { + foreach (var bv in sc.MissingBounds) { + resolver.reporter.Error(MessageSource.Resolver, sc, "a set comprehension must produce a finite set, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name); + } + } } } } @@ -2172,6 +2211,7 @@ namespace Microsoft.Dafny var bin = expr as BinaryExpr; if (bin != null) { bin.ResolvedOp = ResolveOp(bin.Op, bin.E1.Type); + } } } @@ -2249,6 +2289,58 @@ namespace Microsoft.Dafny } #endregion CheckTypeInference + // ------------------------------------------------------------------------------------------------------ + // ----- CheckExpression -------------------------------------------------------------------------------- + // ------------------------------------------------------------------------------------------------------ + #region CheckExpression + /// + /// This method computes ghost interests in the statement portion of StmtExpr's and + /// checks for hint restrictions in any CalcStmt. + /// + void CheckExpression(Expression expr, Resolver resolver, ICodeContext codeContext) { + Contract.Requires(expr != null); + Contract.Requires(resolver != null); + Contract.Requires(codeContext != null); + var v = new CheckExpression_Visitor(resolver, codeContext); + v.Visit(expr); + } + /// + /// This method computes ghost interests in the statement portion of StmtExpr's and + /// checks for hint restrictions in any CalcStmt. + /// + void CheckExpression(Statement stmt, Resolver resolver, ICodeContext codeContext) { + Contract.Requires(stmt != null); + Contract.Requires(resolver != null); + Contract.Requires(codeContext != null); + var v = new CheckExpression_Visitor(resolver, codeContext); + v.Visit(stmt); + } + class CheckExpression_Visitor : ResolverBottomUpVisitor + { + readonly ICodeContext CodeContext; + public CheckExpression_Visitor(Resolver resolver, ICodeContext codeContext) + : base(resolver) { + Contract.Requires(resolver != null); + Contract.Requires(codeContext != null); + CodeContext = codeContext; + } + protected override void VisitOneExpr(Expression expr) { + if (expr is StmtExpr) { + var e = (StmtExpr)expr; + resolver.ComputeGhostInterest(e.S, true, CodeContext); + } + } + protected override void VisitOneStmt(Statement stmt) { + if (stmt is CalcStmt) { + var s = (CalcStmt)stmt; + foreach (var h in s.Hints) { + resolver.CheckHintRestrictions(h, new HashSet()); + } + } + } + } + #endregion + // ------------------------------------------------------------------------------------------------------ // ----- CheckTailRecursive ----------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------------------ @@ -3009,11 +3101,11 @@ namespace Microsoft.Dafny // ----- ComputeGhostInterest --------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------------------ #region ComputeGhostInterest - public void ComputeGhostInterest(Statement stmt, ICodeContext codeContext) { + public void ComputeGhostInterest(Statement stmt, bool mustBeErasable, ICodeContext codeContext) { Contract.Requires(stmt != null); Contract.Requires(codeContext != null); var visitor = new GhostInterest_Visitor(codeContext, this); - visitor.Visit(stmt, codeContext.IsGhost); + visitor.Visit(stmt, mustBeErasable); } class GhostInterest_Visitor { @@ -3037,12 +3129,6 @@ namespace Microsoft.Dafny Contract.Requires(msgArgs != null); resolver.reporter.Error(MessageSource.Resolver, expr, msg, msgArgs); } - protected void Error(IToken tok, string msg, params object[] msgArgs) { - Contract.Requires(tok != null); - Contract.Requires(msg != null); - Contract.Requires(msgArgs != null); - resolver.reporter.Error(MessageSource.Resolver, tok, msg, msgArgs); - } /// /// This method does three things: /// 0. Reports an error if "mustBeErasable" and the statement assigns to a non-ghost field @@ -3057,6 +3143,7 @@ namespace Microsoft.Dafny /// because break statements look at the ghost status of its enclosing statements. /// public void Visit(Statement stmt, bool mustBeErasable) { + Contract.Requires(stmt != null); Contract.Assume(!codeContext.IsGhost || mustBeErasable); // (this is really a precondition) codeContext.IsGhost ==> mustBeErasable if (stmt is PredicateStmt) { @@ -3107,9 +3194,6 @@ namespace Microsoft.Dafny } } } - if (!s.IsGhost) { - resolver.needBoundsDiscovery_AssignSuchThatStmt.Add(s); - } } else if (stmt is UpdateStmt) { var s = (UpdateStmt)stmt; @@ -3124,13 +3208,6 @@ namespace Microsoft.Dafny local.IsGhost = true; } } - foreach (var local in s.Locals) { - if (Attributes.Contains(local.Attributes, "assumption")) { - if (!local.IsGhost) { - Error(local.Tok, "assumption variable must be ghost"); - } - } - } s.IsGhost = (s.Update == null || s.Update.IsGhost) && s.Locals.All(v => v.IsGhost); if (s.Update != null) { Visit(s.Update, mustBeErasable); @@ -4040,14 +4117,10 @@ namespace Microsoft.Dafny var prevErrorCount = reporter.Count(ErrorLevel.Error); ResolveExpression(f.Body, new ResolveOpts(f, false)); SolveAllTypeConstraints(); - if (!f.IsGhost && prevErrorCount == reporter.Count(ErrorLevel.Error)) { - CheckIsNonGhost(f.Body); - } Contract.Assert(f.Body.Type != null); // follows from postcondition of ResolveExpression ConstrainTypes(f.Body.Type, f.ResultType, f, "Function body type mismatch (expected {0}, got {1})", f.ResultType, f.Body.Type); } scope.PopMarker(); - DetermineTailRecursion(f); } /// @@ -4187,7 +4260,7 @@ namespace Microsoft.Dafny ResolveBlockStatement(m.Body, m.IsGhost, m); SolveAllTypeConstraints(); if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { - ComputeGhostInterest(m.Body, m); +//KRML ComputeGhostInterest(m.Body, m); } } @@ -4308,7 +4381,7 @@ namespace Microsoft.Dafny if (iter.Body != null) { ResolveBlockStatement(iter.Body, false, iter); if (reporter.Count(ErrorLevel.Error) == postSpecErrorCount) { - ComputeGhostInterest(iter.Body, iter); + //KRML ComputeGhostInterest(iter.Body, iter); } } @@ -5290,7 +5363,10 @@ namespace Microsoft.Dafny } if (!(local.Type.IsBoolType)) { - reporter.Error(MessageSource.Resolver, s, "assumption variable must be of type 'bool'"); + reporter.Error(MessageSource.Resolver, local.Tok, "assumption variable must be of type 'bool'"); + } + if (!local.IsGhost) { + reporter.Error(MessageSource.Resolver, local.Tok, "assumption variable must be ghost"); } } } @@ -5599,7 +5675,6 @@ namespace Microsoft.Dafny loopStack = new List(); foreach (var h in s.Hints) { ResolveStatement(h, true, codeContext); - CheckHintRestrictions(h); } labeledStatements = prevLblStmts; loopStack = prevLoopStack; @@ -6347,9 +6422,6 @@ namespace Microsoft.Dafny if (!isInitCall && callee is Constructor) { reporter.Error(MessageSource.Resolver, s, "a constructor is allowed to be called only when an object is being allocated"); } - if (specContextOnly && !callee.IsGhost) { - reporter.Error(MessageSource.Resolver, s, "only ghost methods can be called from this context"); - } // resolve left-hand sides foreach (var lhs in s.Lhs) { @@ -6633,8 +6705,9 @@ namespace Microsoft.Dafny /// Check that a statment is a valid hint for a calculation. /// ToDo: generalize the part for compound statements to take a delegate? /// - public void CheckHintRestrictions(Statement stmt) { + public void CheckHintRestrictions(Statement stmt, ISet localsAllowedInUpdates) { Contract.Requires(stmt != null); + Contract.Requires(localsAllowedInUpdates != null); if (stmt is PredicateStmt) { // cool } else if (stmt is PrintStmt) { @@ -6648,11 +6721,11 @@ namespace Microsoft.Dafny } else if (stmt is AssignSuchThatStmt) { var s = (AssignSuchThatStmt)stmt; foreach (var lhs in s.Lhss) { - CheckHintLhs(s.Tok, lhs.Resolved); + CheckHintLhs(s.Tok, lhs.Resolved, localsAllowedInUpdates); } } else if (stmt is AssignStmt) { var s = (AssignStmt)stmt; - CheckHintLhs(s.Tok, s.Lhs.Resolved); + CheckHintLhs(s.Tok, s.Lhs.Resolved, localsAllowedInUpdates); } else if (stmt is CallStmt) { var s = (CallStmt)stmt; if (s.Method.Mod.Expressions.Count != 0) { @@ -6661,33 +6734,33 @@ namespace Microsoft.Dafny } else if (stmt is UpdateStmt) { var s = (UpdateStmt)stmt; foreach (var ss in s.ResolvedStatements) { - CheckHintRestrictions(ss); + CheckHintRestrictions(ss, localsAllowedInUpdates); } } else if (stmt is VarDeclStmt) { var s = (VarDeclStmt)stmt; + s.Locals.Iter(local => localsAllowedInUpdates.Add(local)); if (s.Update != null) { - CheckHintRestrictions(s.Update); + CheckHintRestrictions(s.Update, localsAllowedInUpdates); } } else if (stmt is BlockStmt) { var s = (BlockStmt)stmt; - scope.PushMarker(); + var newScopeForLocals = new HashSet(localsAllowedInUpdates); foreach (var ss in s.Body) { - CheckHintRestrictions(ss); + CheckHintRestrictions(ss, newScopeForLocals); } - scope.PopMarker(); } else if (stmt is IfStmt) { var s = (IfStmt)stmt; - CheckHintRestrictions(s.Thn); + CheckHintRestrictions(s.Thn, localsAllowedInUpdates); if (s.Els != null) { - CheckHintRestrictions(s.Els); + CheckHintRestrictions(s.Els, localsAllowedInUpdates); } } else if (stmt is AlternativeStmt) { var s = (AlternativeStmt)stmt; foreach (var alt in s.Alternatives) { foreach (var ss in alt.Body) { - CheckHintRestrictions(ss); + CheckHintRestrictions(ss, localsAllowedInUpdates); } } @@ -6697,14 +6770,14 @@ namespace Microsoft.Dafny reporter.Error(MessageSource.Resolver, s.Mod.Expressions[0].tok, "a while statement used inside a hint is not allowed to have a modifies clause"); } if (s.Body != null) { - CheckHintRestrictions(s.Body); + CheckHintRestrictions(s.Body, localsAllowedInUpdates); } } else if (stmt is AlternativeLoopStmt) { var s = (AlternativeLoopStmt)stmt; foreach (var alt in s.Alternatives) { foreach (var ss in alt.Body) { - CheckHintRestrictions(ss); + CheckHintRestrictions(ss, localsAllowedInUpdates); } } @@ -6726,14 +6799,14 @@ namespace Microsoft.Dafny } else if (stmt is CalcStmt) { var s = (CalcStmt)stmt; foreach (var h in s.Hints) { - CheckHintRestrictions(h); + CheckHintRestrictions(h, new HashSet()); } } else if (stmt is MatchStmt) { var s = (MatchStmt)stmt; foreach (var kase in s.Cases) { foreach (var ss in kase.Body) { - CheckHintRestrictions(ss); + CheckHintRestrictions(ss, localsAllowedInUpdates); } } @@ -6742,11 +6815,14 @@ namespace Microsoft.Dafny } } - void CheckHintLhs(IToken tok, Expression lhs) { + void CheckHintLhs(IToken tok, Expression lhs, ISet localsAllowedInUpdates) { + Contract.Requires(tok != null); + Contract.Requires(lhs != null); + Contract.Requires(localsAllowedInUpdates != null); var idExpr = lhs as IdentifierExpr; if (idExpr == null) { reporter.Error(MessageSource.Resolver, tok, "a hint is not allowed to update heap locations"); - } else if (scope.ContainsDecl(idExpr.Var)) { + } else if (!localsAllowedInUpdates.Contains(idExpr.Var)) { reporter.Error(MessageSource.Resolver, tok, "a hint is not allowed to update a variable declared outside the hint"); } } @@ -7768,9 +7844,6 @@ namespace Microsoft.Dafny ResolveExpression(rhs, opts); ConstrainTypes(rhs.Type, Type.Bool, rhs.tok, "type of RHS of let-such-that expression must be boolean (got {0})", rhs.Type); } - if (!opts.DontCareAboutCompilation && !e.BoundVars.All(bv => bv.IsGhost)) { - needFiniteBoundsChecks_LetSuchThatExpr.Add(e); - } } ResolveExpression(e.Body, opts); ResolveAttributes(e.Attributes, new ResolveOpts(opts, true)); @@ -7819,21 +7892,17 @@ namespace Microsoft.Dafny List missingBounds; e.Bounds = DiscoverBestBounds_MultipleVars(e.BoundVars, e.LogicalBody(), e is ExistsExpr, true, out missingBounds); if (missingBounds.Count != 0) { - // Report errors here about quantifications that depend on the allocation state. - var mb = missingBounds; - if (opts.codeContext is Function) { - mb = new List(); // (who cares if we allocate another array; this happens only in the case of a resolution error anyhow) - foreach (var bv in missingBounds) { - if (bv.Type.IsRefType) { - reporter.Error(MessageSource.Resolver, expr, "a quantifier involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of '{0}'", bv.Name); - } else { - mb.Add(bv); - } + e.MissingBounds = missingBounds; + } + if (opts.codeContext is Function && e.Bounds != null) { + Contract.Assert(e.Bounds.Count == e.BoundVars.Count); + for (int i = 0; i < e.Bounds.Count; i++) { + var bound = e.Bounds[i] as ComprehensionExpr.RefBoundedPool; + if (bound != null) { + var bv = e.BoundVars[i]; + reporter.Error(MessageSource.Resolver, expr, "a quantifier involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of '{0}'", bv.Name); } } - if (mb.Count != 0) { - e.MissingBounds = mb; - } } } @@ -7855,12 +7924,6 @@ namespace Microsoft.Dafny scope.PopMarker(); expr.Type = new SetType(e.Finite, e.Term.Type); - if (opts.DontCareAboutCompilation && (e.Term.Type.IsRefType || e.Term.Type.IsBoolType) || e.Term.Type.IsCharType) { - // ok, term type is finite and we're in a ghost context - } else { - needFiniteBoundsChecks_SetComprehension.Add(e); - } - } else if (expr is MapComprehension) { var e = (MapComprehension)expr; int prevErrorCount = reporter.Count(ErrorLevel.Error); @@ -9329,17 +9392,47 @@ namespace Microsoft.Dafny CheckIsNonGhost(e.RHSs[0]); } CheckIsNonGhost(e.Body); + + // fill in bounds for this to-be-compiled let-such-that expression + Contract.Assert(e.RHSs.Count == 1); // if we got this far, the resolver will have checked this condition successfully + var constraint = e.RHSs[0]; + List missingBounds; + var vars = new List(e.BoundVars); + var bestBounds = DiscoverBestBounds_MultipleVars(vars, constraint, true, false, out missingBounds); + if (missingBounds.Count != 0) { + e.Constraint_MissingBounds = missingBounds; + foreach (var bv in e.Constraint_MissingBounds) { + reporter.Error(MessageSource.Resolver, e, "a non-ghost let-such-that constraint must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name); + } + } else { + e.Constraint_Bounds = bestBounds; + } } return; - } else if (expr is QuantifierExpr) { - var e = (QuantifierExpr)expr; - Contract.Assert(e.SplitQuantifier == null); // No split quantifiers during resolution - if (e.MissingBounds != null) { - foreach (var bv in e.MissingBounds) { - reporter.Error(MessageSource.Resolver, expr, "quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name); + } else if (expr is LambdaExpr) { + var e = expr as LambdaExpr; + CheckIsNonGhost(e.Body); + return; + } else if (expr is ComprehensionExpr) { + var e = (ComprehensionExpr)expr; + var uncompilableBoundVars = e.UncompilableBoundVars(); + if (uncompilableBoundVars.Count != 0) { + string what; + if (e is SetComprehension) { + what = "set comprehensions"; + } else if (e is MapComprehension) { + what = "map comprehensions"; + } else { + Contract.Assume(e is QuantifierExpr); // otherwise, unexpected ComprehensionExpr (since LambdaExpr is handled separately above) + Contract.Assert(((QuantifierExpr)e).SplitQuantifier == null); // No split quantifiers during resolution + what = "quantifiers"; + } + foreach (var bv in uncompilableBoundVars) { + reporter.Error(MessageSource.Resolver, expr, "{0} in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for '{1}'", what, bv.Name); } return; } + } else if (expr is MapComprehension) { var e = (MapComprehension)expr; if (e.MissingBounds != null && !e.Finite) { @@ -9358,10 +9451,6 @@ namespace Microsoft.Dafny var e = (ChainingExpression)expr; e.Operands.ForEach(CheckIsNonGhost); return; - } else if (expr is LambdaExpr) { - var e = expr as LambdaExpr; - CheckIsNonGhost(e.Body); - return; } foreach (var ee in expr.SubExpressions) { @@ -9520,8 +9609,11 @@ namespace Microsoft.Dafny // Maybe the type itself gives a bound if (bv.Type.IsBoolType) { - // easy bounds.Add(new ComprehensionExpr.BoolBoundedPool()); + } else if (bv.Type.IsCharType) { + bounds.Add(new ComprehensionExpr.CharBoundedPool()); + } else if (bv.Type.IsRefType) { + bounds.Add(new ComprehensionExpr.RefBoundedPool(bv.Type)); } else if (bv.Type.IsIndDatatype && bv.Type.AsIndDatatype.HasFinitePossibleValues) { bounds.Add(new ComprehensionExpr.DatatypeBoundedPool(bv.Type.AsIndDatatype)); } else if (bv.Type.IsNumericBased(Type.NumericPersuation.Int)) { diff --git a/Test/dafny0/AssumptionVariables0.dfy.expect b/Test/dafny0/AssumptionVariables0.dfy.expect index 16572961..83eb8a73 100644 --- a/Test/dafny0/AssumptionVariables0.dfy.expect +++ b/Test/dafny0/AssumptionVariables0.dfy.expect @@ -1,13 +1,13 @@ AssumptionVariables0.dfy(6,29): Error: there may be at most one assignment to an assumption variable, the RHS of which must match the expression "a0 && " AssumptionVariables0.dfy(7,33): Error: there may be at most one assignment to an assumption variable, the RHS of which must match the expression "a2 && " -AssumptionVariables0.dfy(9,2): Error: assumption variable must be of type 'bool' +AssumptionVariables0.dfy(9,26): Error: assumption variable must be of type 'bool' AssumptionVariables0.dfy(15,5): Error: there may be at most one assignment to an assumption variable, the RHS of which must match the expression "a3 && " AssumptionVariables0.dfy(17,5): Error: there may be at most one assignment to an assumption variable, the RHS of which must match the expression "a3 && " AssumptionVariables0.dfy(27,5): Error: there may be at most one assignment to an assumption variable, the RHS of which must match the expression "a0 && " AssumptionVariables0.dfy(31,5): Error: there may be at most one assignment to an assumption variable, the RHS of which must match the expression "a0 && " AssumptionVariables0.dfy(53,9): Error: there may be at most one assignment to an assumption variable, the RHS of which must match the expression "a0 && " AssumptionVariables0.dfy(61,37): Error: there may be at most one assignment to an assumption variable, the RHS of which must match the expression "a0 && " -AssumptionVariables0.dfy(61,10): Error: assumption variable must be of type 'bool' +AssumptionVariables0.dfy(61,34): Error: assumption variable must be of type 'bool' AssumptionVariables0.dfy(69,15): Error: there may be at most one assignment to an assumption variable, the RHS of which must match the expression "a0 && " AssumptionVariables0.dfy(78,20): Error: assumption variable must be ghost 12 resolution/type errors detected in AssumptionVariables0.dfy diff --git a/Test/dafny0/CallStmtTests.dfy b/Test/dafny0/CallStmtTests.dfy index 67e66b34..46c466ff 100644 --- a/Test/dafny0/CallStmtTests.dfy +++ b/Test/dafny0/CallStmtTests.dfy @@ -1,23 +1,27 @@ // RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" // RUN: %diff "%s.expect" "%t" -method testing1(t: int) -{ - t := m(); // error: should be checked at the Dafny level, not fall to Boogie. -} +module M0 { + method testing1(t: int) + { + t := m(); // error: should be checked at the Dafny level, not fall to Boogie. + } -method m() returns (r: int) -{ - return 3; + method m() returns (r: int) + { + return 3; + } } -method testing2() -{ - var v; - v := m2(); // error: v needs to be ghost because r is. -} +module M1 { + method testing2() + { + var v; + v := m2(); // error: v needs to be ghost because r is. + } -method m2() returns (ghost r: int) -{ - r := 23; + method m2() returns (ghost r: int) + { + r := 23; + } } diff --git a/Test/dafny0/CallStmtTests.dfy.expect b/Test/dafny0/CallStmtTests.dfy.expect index 8a334754..246b89f8 100644 --- a/Test/dafny0/CallStmtTests.dfy.expect +++ b/Test/dafny0/CallStmtTests.dfy.expect @@ -1,3 +1,3 @@ -CallStmtTests.dfy(6,3): Error: LHS of assignment must denote a mutable variable -CallStmtTests.dfy(17,10): Error: actual out-parameter 0 is required to be a ghost variable +CallStmtTests.dfy(7,4): Error: LHS of assignment must denote a mutable variable +CallStmtTests.dfy(20,11): Error: actual out-parameter 0 is required to be a ghost variable 2 resolution/type errors detected in CallStmtTests.dfy diff --git a/Test/dafny0/DiscoverBounds.dfy.expect b/Test/dafny0/DiscoverBounds.dfy.expect index ee816683..34003053 100644 --- a/Test/dafny0/DiscoverBounds.dfy.expect +++ b/Test/dafny0/DiscoverBounds.dfy.expect @@ -1,4 +1,4 @@ -DiscoverBounds.dfy(36,7): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'o'' -DiscoverBounds.dfy(39,7): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'r' -DiscoverBounds.dfy(40,7): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'r'' +DiscoverBounds.dfy(36,7): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'o'' +DiscoverBounds.dfy(39,7): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'r' +DiscoverBounds.dfy(40,7): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'r'' 3 resolution/type errors detected in DiscoverBounds.dfy diff --git a/Test/dafny0/NonGhostQuantifiers.dfy b/Test/dafny0/NonGhostQuantifiers.dfy index bff1d65b..e522d0fc 100644 --- a/Test/dafny0/NonGhostQuantifiers.dfy +++ b/Test/dafny0/NonGhostQuantifiers.dfy @@ -181,6 +181,12 @@ module DependencyOnAllAllocatedObjects { forall c: SomeClass :: true // error: not allowed to dependend on which objects are allocated } + class SomeClass { + var f: int; + } +} + +module DependencyOnAllAllocatedObjects_More { method M() { var b := forall c: SomeClass :: c != null ==> c.f == 0; // error: non-ghost code requires bounds @@ -192,3 +198,4 @@ module DependencyOnAllAllocatedObjects { var f: int; } } + diff --git a/Test/dafny0/NonGhostQuantifiers.dfy.expect b/Test/dafny0/NonGhostQuantifiers.dfy.expect index 1e2fce17..6b737add 100644 --- a/Test/dafny0/NonGhostQuantifiers.dfy.expect +++ b/Test/dafny0/NonGhostQuantifiers.dfy.expect @@ -6,16 +6,16 @@ NonGhostQuantifiers.dfy(167,4): Error: a quantifier involved in a function defin NonGhostQuantifiers.dfy(171,4): Error: a quantifier involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of 'c' NonGhostQuantifiers.dfy(176,4): Error: a quantifier involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of 'c' NonGhostQuantifiers.dfy(181,4): Error: a quantifier involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of 'c' -NonGhostQuantifiers.dfy(186,13): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'c' -NonGhostQuantifiers.dfy(16,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'n' -NonGhostQuantifiers.dfy(45,4): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'n' -NonGhostQuantifiers.dfy(49,4): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'd' -NonGhostQuantifiers.dfy(53,4): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'n' -NonGhostQuantifiers.dfy(77,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'i' -NonGhostQuantifiers.dfy(81,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'j' -NonGhostQuantifiers.dfy(91,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'j' -NonGhostQuantifiers.dfy(106,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'j' -NonGhostQuantifiers.dfy(114,10): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'y' -NonGhostQuantifiers.dfy(123,8): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'x' +NonGhostQuantifiers.dfy(192,13): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'c' +NonGhostQuantifiers.dfy(16,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'n' +NonGhostQuantifiers.dfy(45,4): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'n' +NonGhostQuantifiers.dfy(49,4): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'd' +NonGhostQuantifiers.dfy(53,4): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'n' +NonGhostQuantifiers.dfy(77,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'i' +NonGhostQuantifiers.dfy(81,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'j' +NonGhostQuantifiers.dfy(91,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'j' +NonGhostQuantifiers.dfy(106,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'j' +NonGhostQuantifiers.dfy(114,10): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'y' +NonGhostQuantifiers.dfy(123,8): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'x' NonGhostQuantifiers.dfy(140,8): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression) 20 resolution/type errors detected in NonGhostQuantifiers.dfy diff --git a/Test/dafny0/ParallelResolveErrors.dfy b/Test/dafny0/ParallelResolveErrors.dfy index 61956651..8c48487d 100644 --- a/Test/dafny0/ParallelResolveErrors.dfy +++ b/Test/dafny0/ParallelResolveErrors.dfy @@ -7,7 +7,6 @@ class C { ghost method Init_ModifyNothing() { } ghost method Init_ModifyThis() modifies this; { - data := 6; // error: assignment to a non-ghost field gdata := 7; } ghost method Init_ModifyStuff(c: C) modifies this, c; { } @@ -120,3 +119,15 @@ method M3(c: C) c.GhostMethodWithModifies(x); // error: not allowed to call method with nonempty modifies clause } } + +module AnotherModule { + class C { + var data: int; + ghost var gdata: int; + ghost method Init_ModifyThis() modifies this; + { + data := 6; // error: assignment to a non-ghost field + gdata := 7; + } + } +} diff --git a/Test/dafny0/ParallelResolveErrors.dfy.expect b/Test/dafny0/ParallelResolveErrors.dfy.expect index 00030994..4d25ba11 100644 --- a/Test/dafny0/ParallelResolveErrors.dfy.expect +++ b/Test/dafny0/ParallelResolveErrors.dfy.expect @@ -1,23 +1,23 @@ -ParallelResolveErrors.dfy(10,9): Error: Assignment to non-ghost field is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression) -ParallelResolveErrors.dfy(21,4): Error: LHS of assignment must denote a mutable variable -ParallelResolveErrors.dfy(26,6): Error: body of forall statement is attempting to update a variable declared outside the forall statement +ParallelResolveErrors.dfy(129,11): Error: Assignment to non-ghost field is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression) +ParallelResolveErrors.dfy(20,4): Error: LHS of assignment must denote a mutable variable +ParallelResolveErrors.dfy(25,6): Error: body of forall statement is attempting to update a variable declared outside the forall statement +ParallelResolveErrors.dfy(42,6): Error: body of forall statement is attempting to update a variable declared outside the forall statement ParallelResolveErrors.dfy(43,6): Error: body of forall statement is attempting to update a variable declared outside the forall statement -ParallelResolveErrors.dfy(44,6): Error: body of forall statement is attempting to update a variable declared outside the forall statement -ParallelResolveErrors.dfy(56,13): Error: new allocation not supported in forall statements +ParallelResolveErrors.dfy(55,13): Error: new allocation not supported in forall statements +ParallelResolveErrors.dfy(60,13): Error: new allocation not allowed in ghost context ParallelResolveErrors.dfy(61,13): Error: new allocation not allowed in ghost context ParallelResolveErrors.dfy(62,13): Error: new allocation not allowed in ghost context ParallelResolveErrors.dfy(63,13): Error: new allocation not allowed in ghost context -ParallelResolveErrors.dfy(64,13): Error: new allocation not allowed in ghost context -ParallelResolveErrors.dfy(65,22): Error: the body of the enclosing forall statement is not allowed to update heap locations, so any call must be to a method with an empty modifies clause -ParallelResolveErrors.dfy(66,20): Error: the body of the enclosing forall statement is not allowed to call non-ghost methods -ParallelResolveErrors.dfy(73,19): Error: trying to break out of more loop levels than there are enclosing loops -ParallelResolveErrors.dfy(77,18): Error: return statement is not allowed inside a forall statement -ParallelResolveErrors.dfy(84,21): Error: trying to break out of more loop levels than there are enclosing loops -ParallelResolveErrors.dfy(85,20): Error: trying to break out of more loop levels than there are enclosing loops -ParallelResolveErrors.dfy(86,20): Error: break label is undefined or not in scope: OutsideLoop -ParallelResolveErrors.dfy(95,24): Error: trying to break out of more loop levels than there are enclosing loops -ParallelResolveErrors.dfy(96,24): Error: break label is undefined or not in scope: OutsideLoop -ParallelResolveErrors.dfy(107,9): Error: the body of the enclosing forall statement is not allowed to update heap locations -ParallelResolveErrors.dfy(115,29): Error: the body of the enclosing forall statement is not allowed to update heap locations, so any call must be to a method with an empty modifies clause -ParallelResolveErrors.dfy(120,29): Error: the body of the enclosing forall statement is not allowed to update heap locations, so any call must be to a method with an empty modifies clause +ParallelResolveErrors.dfy(64,22): Error: the body of the enclosing forall statement is not allowed to update heap locations, so any call must be to a method with an empty modifies clause +ParallelResolveErrors.dfy(65,20): Error: the body of the enclosing forall statement is not allowed to call non-ghost methods +ParallelResolveErrors.dfy(72,19): Error: trying to break out of more loop levels than there are enclosing loops +ParallelResolveErrors.dfy(76,18): Error: return statement is not allowed inside a forall statement +ParallelResolveErrors.dfy(83,21): Error: trying to break out of more loop levels than there are enclosing loops +ParallelResolveErrors.dfy(84,20): Error: trying to break out of more loop levels than there are enclosing loops +ParallelResolveErrors.dfy(85,20): Error: break label is undefined or not in scope: OutsideLoop +ParallelResolveErrors.dfy(94,24): Error: trying to break out of more loop levels than there are enclosing loops +ParallelResolveErrors.dfy(95,24): Error: break label is undefined or not in scope: OutsideLoop +ParallelResolveErrors.dfy(106,9): Error: the body of the enclosing forall statement is not allowed to update heap locations +ParallelResolveErrors.dfy(114,29): Error: the body of the enclosing forall statement is not allowed to update heap locations, so any call must be to a method with an empty modifies clause +ParallelResolveErrors.dfy(119,29): Error: the body of the enclosing forall statement is not allowed to update heap locations, so any call must be to a method with an empty modifies clause 22 resolution/type errors detected in ParallelResolveErrors.dfy diff --git a/Test/dafny0/ResolutionErrors.dfy b/Test/dafny0/ResolutionErrors.dfy index 2fab6bf3..d3514b2b 100644 --- a/Test/dafny0/ResolutionErrors.dfy +++ b/Test/dafny0/ResolutionErrors.dfy @@ -102,7 +102,7 @@ class EE { } // --------------- ghost tests ------------------------------------- - +module HereAreMoreGhostTests { datatype GhostDt = Nil(ghost extraInfo: int) | Cons(data: int, tail: GhostDt, ghost moreInfo: int) @@ -154,7 +154,7 @@ class GhostTests { ensures false; { while (true) - decreases *; // error: not allowed in ghost context +//KRML-confirmed decreases *; // error: not allowed in ghost context { } } @@ -228,7 +228,7 @@ class GhostTests { if (p == 67) { break break; // fine, since this is not a ghost context } else if (*) { - break break break; // error: tries to break out of more loop levels than there are +//KRML-confirmed break break break; // error: tries to break out of more loop levels than there are } q := q + 1; } @@ -238,7 +238,7 @@ class GhostTests { p := p + 1; } } -method BreakMayNotBeFineHere_Ghost(ghost t: int) + method BreakMayNotBeFineHere_Ghost(ghost t: int) { var n := 0; ghost var k := 0; @@ -297,7 +297,7 @@ method BreakMayNotBeFineHere_Ghost(ghost t: int) } } } - +} //HereAreMoreGhostTests method DuplicateLabels(n: int) { var x; if (n < 7) { @@ -364,17 +364,17 @@ method DatatypeDestructors(d: DTD_List) { } } method DatatypeDestructors_Ghost(d: DTD_List) { - var g1 := d.g; // error: cannot use ghost member in non-ghost code + var g1 := d.g; // error: cannot use ghost member in non-ghost code//KRML-confirmed } // ------------------- print statements --------------------------------------- - +module GhostPrintAttempts { method PrintOnlyNonGhosts(a: int, ghost b: int) { print "a: ", a, "\n"; print "b: ", b, "\n"; // error: print statement cannot take ghosts } - +} // ------------------- auto-added type arguments ------------------------------ class GenericClass { var data: T; } @@ -439,11 +439,11 @@ method TestCalc_Ghost(m: int, n: int, a: bool, b: bool) { calc { n + m; - { print n + m; } // error: non-ghost statements are not allowed in hints + { print n + m; } // error: non-ghost statements are not allowed in hints//KRML-confirmed m + n; } } - +module MyOwnModule { class SideEffectChecks { ghost var ycalc: int; @@ -461,11 +461,11 @@ class SideEffectChecks { var x: int; calc { 0; - { Mod(0); } // methods with side-effects are not allowed + { Mod(0); } // error: methods with side-effects are not allowed ycalc; - { ycalc := 1; } // heap updates are not allowed + { ycalc := 1; } // error: heap updates are not allowed 1; - { x := 1; } // updates to locals defined outside of the hint are not allowed + { x := 1; } // error: updates to locals defined outside of the hint are not allowed x; { var x: int; @@ -475,7 +475,7 @@ class SideEffectChecks { } } } - +} // ------------------- nameless constructors ------------------------------ class YHWH { @@ -523,13 +523,13 @@ method AssignSuchThatFromGhost() var x: int; ghost var g: int; - x := g; // error: ghost cannot flow into non-ghost + x := g; // error: ghost cannot flow into non-ghost//KRML-confirmed x := *; assume x == g; // this mix of ghosts and non-ghosts is cool (but, of course, // the compiler will complain) - x :| x == g; // error: left-side has non-ghost, so RHS must be non-ghost as well + x :| x == g; // error: left-side has non-ghost, so RHS must be non-ghost as well//KRML-confirmed x :| assume x == g; // this is cool, since it's an assume (but, of course, the // compiler will complain) @@ -605,7 +605,7 @@ method LetSuchThat(ghost z: int, n: nat) } method LetSuchThat_Ghost(ghost z: int, n: nat) { - var x := var y :| y < z; y; // error: contraint depend on ghost (z) + var x := var y :| y < z; y; // error: contraint depend on ghost (z)//KRML-confirmed } // ------------ quantified variables whose types are not inferred ---------- @@ -677,9 +677,9 @@ module GhostAllocationTests { 5; { var y := new G; } // error: 'new' not allowed in ghost contexts 2 + 3; - { if n != 0 { GhostNew4(n-1); } } // error: cannot call non-ghost method in a ghost context + { if n != 0 { GhostNew4(n-1); } } // error: cannot call non-ghost method in a ghost context//KRML-confirmed 1 + 4; - { GhostNew5(g); } // error: cannot call method with nonempty modifies + { GhostNew5(g); } // error: cannot call method with nonempty modifies//KRML-confirmed -5 + 10; } } @@ -735,7 +735,7 @@ module StatementsInExpressions { { calc { 5; - { SideEffect(); } // error: cannot call method with side effects + { SideEffect(); } // error: cannot call method with side effects//KRML 5; } } @@ -745,7 +745,7 @@ module StatementsInExpressions { calc { 6; { assert 6 < 8; } - { NonGhostMethod(); } // error: cannot call non-ghost method + { NonGhostMethod(); } // error: cannot call non-ghost method//KRML-confirmed { var x := 8; while x != 0 decreases *; // error: cannot use 'decreases *' in a ghost context @@ -759,12 +759,12 @@ module StatementsInExpressions { x := x - 1; } } - { MyField := 12; } // error: cannot assign to a field - { MyGhostField := 12; } // error: cannot assign to any field - { SideEffect(); } // error: cannot call (ghost) method with a modifies clause + { MyField := 12; } // error: cannot assign to a field//KRML-confirmed + { MyGhostField := 12; } // error: cannot assign to any field//KRML-confirmed + { SideEffect(); } // error: cannot call (ghost) method with a modifies clause//KRML-confirmed { var x := 8; while x != 0 - modifies this; // error: cannot use a modifies clause on a loop + modifies this; // error: cannot use a modifies clause on a loop//KRML-confirmed { x := x - 1; } @@ -783,7 +783,7 @@ module StatementsInExpressions { calc { 6; { assert 6 < 8; } - { NonGhostMethod(); } // error: cannot call non-ghost method + { NonGhostMethod(); } // error: cannot call non-ghost method//KRML-confirmed { var x := 8; while x != 0 decreases *; // error: cannot use 'decreases *' in a ghost context @@ -791,12 +791,12 @@ module StatementsInExpressions { x := x - 1; } } - { MyField := 12; } // error: cannot assign to a field - { MyGhostField := 12; } // error: cannot assign to any field - { M(); } // error: cannot call (ghost) method with a modifies clause + { MyField := 12; } // error: cannot assign to a field//KRML-confirmed + { MyGhostField := 12; } // error: cannot assign to any field//KRML-confirmed + { M(); } // error: cannot call (ghost) method with a modifies clause//KRML-confirmed { var x := 8; while x != 0 - modifies this; // error: cannot use a modifies clause on a loop + modifies this; // error: cannot use a modifies clause on a loop//KRML-confirmed { x := x - 1; } @@ -822,7 +822,7 @@ module StatementsInExpressions { { MyLemma(); MyGhostMethod(); // error: modifi2es state - OrdinaryMethod(); // error: not a ghost + OrdinaryMethod(); // error: not a ghost//KRML-confirmed OutParamMethod(); // error: has out-parameters 10 } @@ -1446,3 +1446,219 @@ module SuchThat { w } } + +// ---------------------- NEW STUFF ---------------------------------------- + +module GhostTests { + class G { } + + method GhostNew4(n: nat) + { + var g := new G; + calc { + 5; + 2 + 3; + { if n != 0 { GhostNew4(n-1); } } // error: cannot call non-ghost method in a ghost context//ADD:680 + 1 + 4; + { GhostNew5(g); } // error: cannot call method with nonempty modifies//ADD:682 + -5 + 10; + } + } + + ghost method GhostNew5(g: G) + modifies g; + { + } + + class MyClass { + ghost method SideEffect() + modifies this; + { + } + + method NonGhostMethod() + { + } + + ghost method M() + modifies this; + { + calc { + 5; + { SideEffect(); } // error: cannot call method with side effects//ADD:738 + 5; + } + } + function F(): int + { + calc { + 6; + { assert 6 < 8; } + { NonGhostMethod(); } // error: cannot call non-ghost method//ADD:748 + { var x := 8; + while x != 0 + { + x := x - 1; + } + } + { var x := 8; + while x != 0 + { + x := x - 1; + } + } + { MyField := 12; } // error: cannot assign to a field, and especially not a non-ghost field//ADD:762 + { MyGhostField := 12; } // error: cannot assign to any field//ADD:763 + { SideEffect(); } // error: cannot call (ghost) method with a modifies clause//ADD:764 + { var x := 8; + while x != 0 + modifies this; // error: cannot use a modifies clause on a loop//ADD:767 + { + x := x - 1; + } + } + 6; + } + 5 + } + var MyField: int; + ghost var MyGhostField: int; + method N() + { + var y := + calc { + 6; + { assert 6 < 8; } + { NonGhostMethod(); } // error: cannot call non-ghost method//ADD:786 + { var x := 8; + while x != 0 + { + x := x - 1; + } + } + { MyField := 12; } // error: cannot assign to a field, and especially not a non-ghost field//ADD:794 + { MyGhostField := 12; } // error: cannot assign to any field//ADD:795 + { M(); } // error: cannot call (ghost) method with a modifies clause//ADD:796 + { var x := 8; + while x != 0 + modifies this; // error: cannot use a modifies clause on a loop//ADD:799 + { + x := x - 1; + } + } + { var x := 8; + while x != 0 + { + x := x - 1; + } + } + 6; + } + 5; + } + ghost method MyLemma() + ghost method MyGhostMethod() + modifies this; + method OrdinaryMethod() + ghost method OutParamMethod() returns (y: int) + + function UseLemma(): int + { + MyLemma(); + OrdinaryMethod(); // error: not a ghost//ADD:825 + 10 + } + } +} + +module EvenMoreGhostTests { + ghost method NiceTry() + ensures false; + { + while (true) + decreases *; // error: not allowed in ghost context//ADD:157 + { + } + } + method BreakMayNotBeFineHere() + { + var n := 0; + var p := 0; + while (true) + { + var dontKnow; + if (n == 112) { + } else if (dontKnow == 708) { + while * { + label IfNest: + if (p == 67) { + break break; // fine, since this is not a ghost context + } else if (*) { + break break break; // error: tries to break out of more loop levels than there are//ADD:231 + } + } + } + } + } + ghost method Bad() + { + var x: int; + calc { + 1; +//****** { x := 1; } // error: updates to locals defined outside of the hint are not allowed + x; + { + var x: int; + x := 1; // this is OK + } + 1; + } + } +} + +module BadGhostTransfer { + datatype DTD_List = DTD_Nil | DTD_Cons(Car: int, Cdr: DTD_List, ghost g: int) + + method DatatypeDestructors_Ghost(d: DTD_List) { + var g1 := d.g; // error: cannot use ghost member in non-ghost code//ADD:367 + } + method AssignSuchThatFromGhost() + { + var x: int; + ghost var g: int; + + x := g; // error: ghost cannot flow into non-ghost//ADD:526 + + x := *; + assume x == g; // this mix of ghosts and non-ghosts is cool (but, of course, + // the compiler will complain) + + x :| x == g; // error: left-side has non-ghost, so RHS must be non-ghost as well//ADD:532 + + x :| assume x == g; // this is cool, since it's an assume (but, of course, the + // compiler will complain) + + x :| x == 5; + g :| g <= g; + g :| assume g < g; // the compiler will complain here, despite the LHS being + // ghost -- and rightly so, since an assume is used + } +} + +module MoreGhostPrintAttempts { + method TestCalc_Ghost(m: int, n: int, a: bool, b: bool) + { + calc { + n + m; + { print n + m; } // error: non-ghost statements are not allowed in hints//ADD:442 + m + n; + } + } +} + +module MoreLetSuchThatExpr { + method LetSuchThat_Ghost(ghost z: int, n: nat) + { + var x := var y :| y < z; y; // error: contraint depend on ghost (z)//ADD:608 + } +} diff --git a/Test/dafny0/ResolutionErrors.dfy.expect b/Test/dafny0/ResolutionErrors.dfy.expect index 0286778b..ee2dd5f7 100644 --- a/Test/dafny0/ResolutionErrors.dfy.expect +++ b/Test/dafny0/ResolutionErrors.dfy.expect @@ -1,3 +1,21 @@ +ResolutionErrors.dfy(113,9): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(114,9): Error: function calls are allowed only in specification contexts (consider declaring the function a 'function method') +ResolutionErrors.dfy(118,11): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(119,10): Error: actual out-parameter 0 is required to be a ghost variable +ResolutionErrors.dfy(126,15): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(130,23): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(137,4): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(141,21): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(142,35): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(151,10): Error: only ghost methods can be called from this context +ResolutionErrors.dfy(251,27): Error: ghost-context break statement is not allowed to break out of non-ghost structure +ResolutionErrors.dfy(274,12): Error: ghost-context break statement is not allowed to break out of non-ghost loop +ResolutionErrors.dfy(288,12): Error: ghost-context break statement is not allowed to break out of non-ghost loop +ResolutionErrors.dfy(293,8): Error: return statement is not allowed in this context (because it is guarded by a specification-only expression) +ResolutionErrors.dfy(375,15): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(464,11): Error: calls to methods with side-effects are not allowed inside a hint +ResolutionErrors.dfy(466,14): Error: a hint is not allowed to update heap locations +ResolutionErrors.dfy(468,10): Error: a hint is not allowed to update a variable declared outside the hint ResolutionErrors.dfy(558,7): Error: RHS (of type List) not assignable to LHS (of type List) ResolutionErrors.dfy(563,7): Error: RHS (of type List) not assignable to LHS (of type List) ResolutionErrors.dfy(577,23): Error: type of case bodies do not agree (found Tree<_T1,_T0>, previous types Tree<_T0,_T1>) @@ -13,26 +31,11 @@ ResolutionErrors.dfy(652,11): Error: the body of the enclosing forall statement ResolutionErrors.dfy(652,14): Error: new allocation not allowed in ghost context ResolutionErrors.dfy(662,23): Error: 'new' is not allowed in ghost contexts ResolutionErrors.dfy(669,15): Error: 'new' is not allowed in ghost contexts -ResolutionErrors.dfy(669,15): Error: only ghost methods can be called from this context ResolutionErrors.dfy(678,17): Error: 'new' is not allowed in ghost contexts -ResolutionErrors.dfy(680,29): Error: only ghost methods can be called from this context -ResolutionErrors.dfy(682,17): Error: calls to methods with side-effects are not allowed inside a hint ResolutionErrors.dfy(700,21): Error: the type of this variable is underspecified -ResolutionErrors.dfy(738,20): Error: calls to methods with side-effects are not allowed inside a hint -ResolutionErrors.dfy(748,24): Error: only ghost methods can be called from this context ResolutionErrors.dfy(751,22): Error: 'decreases *' is not allowed on ghost loops -ResolutionErrors.dfy(762,18): Error: a hint is not allowed to update heap locations -ResolutionErrors.dfy(763,23): Error: a hint is not allowed to update heap locations -ResolutionErrors.dfy(764,20): Error: calls to methods with side-effects are not allowed inside a hint -ResolutionErrors.dfy(767,21): Error: a while statement used inside a hint is not allowed to have a modifies clause -ResolutionErrors.dfy(786,24): Error: only ghost methods can be called from this context ResolutionErrors.dfy(789,22): Error: 'decreases *' is not allowed on ghost loops -ResolutionErrors.dfy(794,18): Error: a hint is not allowed to update heap locations -ResolutionErrors.dfy(795,23): Error: a hint is not allowed to update heap locations -ResolutionErrors.dfy(796,11): Error: calls to methods with side-effects are not allowed inside a hint -ResolutionErrors.dfy(799,21): Error: a while statement used inside a hint is not allowed to have a modifies clause ResolutionErrors.dfy(824,19): Error: calls to methods with side-effects are not allowed inside a statement expression -ResolutionErrors.dfy(825,20): Error: only ghost methods can be called from this context ResolutionErrors.dfy(826,20): Error: wrong number of method result arguments (got 0, expected 1) ResolutionErrors.dfy(838,23): Error: function calls are allowed only in specification contexts (consider declaring the function a 'function method') ResolutionErrors.dfy(848,4): Error: ghost variables are allowed only in specification contexts @@ -74,8 +77,8 @@ ResolutionErrors.dfy(1146,6): Error: RHS (of type P) not assignable to LHS ResolutionErrors.dfy(1151,13): Error: arguments must have the same type (got P and P) ResolutionErrors.dfy(1152,13): Error: arguments must have the same type (got P and P) ResolutionErrors.dfy(1153,13): Error: arguments must have the same type (got P and P) -ResolutionErrors.dfy(1176,38): Error: a set comprehension must produce a finite set, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'o' -ResolutionErrors.dfy(1178,24): Error: a set comprehension must produce a finite set, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'o' +ResolutionErrors.dfy(1176,38): Error: set comprehensions in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'o' +ResolutionErrors.dfy(1178,24): Error: set comprehensions in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'o' ResolutionErrors.dfy(1283,26): Error: the type of this variable is underspecified ResolutionErrors.dfy(1284,31): Error: the type of this variable is underspecified ResolutionErrors.dfy(1285,29): Error: the type of this variable is underspecified @@ -106,6 +109,29 @@ ResolutionErrors.dfy(1440,11): Error: type of RHS of assign-such-that statement ResolutionErrors.dfy(1441,9): Error: type of RHS of assign-such-that statement must be boolean (got int) ResolutionErrors.dfy(1442,13): Error: type of RHS of assign-such-that statement must be boolean (got int) ResolutionErrors.dfy(1445,15): Error: type of RHS of let-such-that expression must be boolean (got int) +ResolutionErrors.dfy(1488,20): Error: calls to methods with side-effects are not allowed inside a hint +ResolutionErrors.dfy(1510,18): Error: a hint is not allowed to update heap locations +ResolutionErrors.dfy(1511,23): Error: a hint is not allowed to update heap locations +ResolutionErrors.dfy(1512,20): Error: calls to methods with side-effects are not allowed inside a hint +ResolutionErrors.dfy(1515,21): Error: a while statement used inside a hint is not allowed to have a modifies clause +ResolutionErrors.dfy(1497,24): Error: only ghost methods can be called from this context +ResolutionErrors.dfy(1510,18): Error: Assignment to non-ghost field is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression) +ResolutionErrors.dfy(1539,18): Error: a hint is not allowed to update heap locations +ResolutionErrors.dfy(1540,23): Error: a hint is not allowed to update heap locations +ResolutionErrors.dfy(1541,11): Error: calls to methods with side-effects are not allowed inside a hint +ResolutionErrors.dfy(1544,21): Error: a while statement used inside a hint is not allowed to have a modifies clause +ResolutionErrors.dfy(1532,24): Error: only ghost methods can be called from this context +ResolutionErrors.dfy(1539,18): Error: Assignment to non-ghost field is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression) +ResolutionErrors.dfy(1568,20): Error: only ghost methods can be called from this context +ResolutionErrors.dfy(1461,29): Error: only ghost methods can be called from this context +ResolutionErrors.dfy(1463,17): Error: calls to methods with side-effects are not allowed inside a hint +ResolutionErrors.dfy(1579,16): Error: 'decreases *' is not allowed on ghost loops +ResolutionErrors.dfy(1597,12): Error: trying to break out of more loop levels than there are enclosing loops +ResolutionErrors.dfy(1623,16): Error: ghost fields are allowed only in specification contexts +ResolutionErrors.dfy(1630,9): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(1636,4): Error: non-ghost variable cannot be assigned a value that depends on a ghost +ResolutionErrors.dfy(1653,8): Error: print statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression) +ResolutionErrors.dfy(1662,26): Error: ghost variables are allowed only in specification contexts ResolutionErrors.dfy(488,2): Error: More than one anonymous constructor ResolutionErrors.dfy(50,13): Error: 'this' is not allowed in a 'static' context ResolutionErrors.dfy(87,14): Error: the name 'Benny' denotes a datatype constructor, but does not do so uniquely; add an explicit qualification (for example, 'Abc.Benny') @@ -113,25 +139,6 @@ ResolutionErrors.dfy(92,14): Error: the name 'David' denotes a datatype construc ResolutionErrors.dfy(93,14): Error: the name 'David' denotes a datatype constructor, but does not do so uniquely; add an explicit qualification (for example, 'Abc.David') ResolutionErrors.dfy(95,14): Error: the name 'David' denotes a datatype constructor, but does not do so uniquely; add an explicit qualification (for example, 'Abc.David') ResolutionErrors.dfy(97,18): Error: wrong number of arguments to datatype constructor David (found 2, expected 1) -ResolutionErrors.dfy(113,9): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(114,9): Error: function calls are allowed only in specification contexts (consider declaring the function a 'function method') -ResolutionErrors.dfy(118,11): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(119,10): Error: actual out-parameter 0 is required to be a ghost variable -ResolutionErrors.dfy(126,15): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(130,23): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(137,4): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(141,21): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(142,35): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(151,10): Error: only ghost methods can be called from this context -ResolutionErrors.dfy(157,16): Error: 'decreases *' is not allowed on ghost loops -ResolutionErrors.dfy(231,12): Error: trying to break out of more loop levels than there are enclosing loops -ResolutionErrors.dfy(251,27): Error: ghost-context break statement is not allowed to break out of non-ghost structure -ResolutionErrors.dfy(274,12): Error: ghost-context break statement is not allowed to break out of non-ghost loop -ResolutionErrors.dfy(288,12): Error: ghost-context break statement is not allowed to break out of non-ghost loop -ResolutionErrors.dfy(293,8): Error: return statement is not allowed in this context (because it is guarded by a specification-only expression) -ResolutionErrors.dfy(464,11): Error: calls to methods with side-effects are not allowed inside a hint -ResolutionErrors.dfy(466,14): Error: a hint is not allowed to update heap locations -ResolutionErrors.dfy(468,10): Error: a hint is not allowed to update a variable declared outside the hint ResolutionErrors.dfy(494,14): Error: when allocating an object of type 'YHWH', one of its constructor methods must be called ResolutionErrors.dfy(499,6): Error: when allocating an object of type 'Lucifer', one of its constructor methods must be called ResolutionErrors.dfy(500,6): Error: when allocating an object of type 'Lucifer', one of its constructor methods must be called @@ -168,8 +175,6 @@ ResolutionErrors.dfy(345,9): Error: a constructor is allowed to be called only w ResolutionErrors.dfy(359,16): Error: arguments must have the same type (got int and DTD_List) ResolutionErrors.dfy(360,16): Error: arguments must have the same type (got DTD_List and int) ResolutionErrors.dfy(361,25): Error: arguments must have the same type (got bool and int) -ResolutionErrors.dfy(367,14): Error: ghost fields are allowed only in specification contexts -ResolutionErrors.dfy(375,15): Error: ghost variables are allowed only in specification contexts ResolutionErrors.dfy(400,5): Error: incorrect type of method in-parameter 1 (expected GenericClass, got GenericClass) ResolutionErrors.dfy(412,18): Error: incorrect type of datatype constructor argument (found GList<_T0>, expected GList) ResolutionErrors.dfy(420,6): Error: arguments to + must be of a numeric type or a collection type (instead got bool) @@ -180,11 +185,7 @@ ResolutionErrors.dfy(429,10): Error: first argument to ==> must be of type bool ResolutionErrors.dfy(429,10): Error: second argument to ==> must be of type bool (instead got int) ResolutionErrors.dfy(434,10): Error: first argument to ==> must be of type bool (instead got int) ResolutionErrors.dfy(434,10): Error: second argument to ==> must be of type bool (instead got int) -ResolutionErrors.dfy(442,6): Error: print statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression) -ResolutionErrors.dfy(526,7): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(532,2): Error: non-ghost variable cannot be assigned a value that depends on a ghost ResolutionErrors.dfy(603,18): Error: unresolved identifier: w -ResolutionErrors.dfy(608,24): Error: ghost variables are allowed only in specification contexts ResolutionErrors.dfy(714,11): Error: lemmas are not allowed to have modifies clauses ResolutionErrors.dfy(976,9): Error: unresolved identifier: s ResolutionErrors.dfy(987,32): Error: RHS (of type (int,int,real)) not assignable to LHS (of type (int,real,int)) @@ -198,4 +199,4 @@ ResolutionErrors.dfy(1164,8): Error: new cannot be applied to a trait ResolutionErrors.dfy(1185,13): Error: first argument to / must be of numeric type (instead got set) ResolutionErrors.dfy(1192,18): Error: a call to a possibly non-terminating method is allowed only if the calling method is also declared (with 'decreases *') to be possibly non-terminating ResolutionErrors.dfy(1207,14): Error: a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating -200 resolution/type errors detected in ResolutionErrors.dfy +201 resolution/type errors detected in ResolutionErrors.dfy diff --git a/Test/dafny0/TypeTests.dfy b/Test/dafny0/TypeTests.dfy index b44f4d68..a9d473f6 100644 --- a/Test/dafny0/TypeTests.dfy +++ b/Test/dafny0/TypeTests.dfy @@ -39,7 +39,7 @@ datatype ReverseOrder_TheCounterpart = // --------------------- -class ArrayTests { +module ArrayTests { ghost method G(a: array) requires a != null && 10 <= a.Length; modifies a; @@ -167,31 +167,33 @@ module Expl_Module { // --------------------- more ghost tests, for assign-such-that statements -method M() -{ - ghost var b: bool; - ghost var k: int, l: int; - var m: int; - - k :| k < 10; - k, m :| 0 <= k < m; // error: LHS has non-ghost and RHS has ghost - m :| m < 10; - - // Because of the ghost guard, these 'if' statements are ghost contexts, so only - // assignments to ghosts are allowed. - if (b) { - k :| k < 10; // should be allowed - k, l :| 0 <= k < l; // ditto - } - if (b) { - m :| m < 10; // error: not allowed in ghost context - k, m :| 0 <= k < m; // error: not allowed in ghost context +module MoreGhostTests { + method M() + { + ghost var b: bool; + ghost var k: int, l: int; + var m: int; + + k :| k < 10; + k, m :| 0 <= k < m; // error: LHS has non-ghost and RHS has ghost + m :| m < 10; + + // Because of the ghost guard, these 'if' statements are ghost contexts, so only + // assignments to ghosts are allowed. + if (b) { + k :| k < 10; // should be allowed + k, l :| 0 <= k < l; // ditto + } + if (b) { + m :| m < 10; // error: not allowed in ghost context + k, m :| 0 <= k < m; // error: not allowed in ghost context + } } -} -ghost method GhostM() returns (x: int) -{ - x :| true; // no problem (but there once was a problem with this case, where an error was generated for no reason) + ghost method GhostM() returns (x: int) + { + x :| true; // no problem (but there once was a problem with this case, where an error was generated for no reason) + } } // ------------------ cycles that could arise from proxy assignments --------- diff --git a/Test/dafny0/TypeTests.dfy.expect b/Test/dafny0/TypeTests.dfy.expect index 8206fd43..de0bfbed 100644 --- a/Test/dafny0/TypeTests.dfy.expect +++ b/Test/dafny0/TypeTests.dfy.expect @@ -1,6 +1,10 @@ -TypeTests.dfy(205,15): Error: incorrect type of datatype constructor argument (found ? -> ?, expected ? -> Dt) -TypeTests.dfy(211,15): Error: incorrect type of datatype constructor argument (found ? -> ?, expected ? -> Dt) -TypeTests.dfy(218,6): Error: RHS (of type set) not assignable to LHS (of type ?) +TypeTests.dfy(47,9): Error: Assignment to array element is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression) +TypeTests.dfy(178,7): Error: non-ghost variable cannot be assigned a value that depends on a ghost +TypeTests.dfy(188,6): Error: cannot assign to non-ghost variable in a ghost context +TypeTests.dfy(189,9): Error: cannot assign to non-ghost variable in a ghost context +TypeTests.dfy(207,15): Error: incorrect type of datatype constructor argument (found ? -> ?, expected ? -> Dt) +TypeTests.dfy(213,15): Error: incorrect type of datatype constructor argument (found ? -> ?, expected ? -> Dt) +TypeTests.dfy(220,6): Error: RHS (of type set) not assignable to LHS (of type ?) TypeTests.dfy(7,17): Error: type mismatch for argument 0 (function expects C, got D) TypeTests.dfy(7,20): Error: type mismatch for argument 1 (function expects D, got C) TypeTests.dfy(8,15): Error: type mismatch for argument 0 (function expects C, got int) @@ -8,7 +12,6 @@ TypeTests.dfy(8,18): Error: type mismatch for argument 1 (function expects D, go TypeTests.dfy(14,16): Error: incorrect type of method in-parameter 0 (expected int, got bool) TypeTests.dfy(15,12): Error: incorrect type of method out-parameter 0 (expected int, got C) TypeTests.dfy(15,12): Error: incorrect type of method out-parameter 1 (expected C, got int) -TypeTests.dfy(47,9): Error: Assignment to array element is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression) TypeTests.dfy(56,6): Error: Duplicate local-variable name: z TypeTests.dfy(58,6): Error: Duplicate local-variable name: x TypeTests.dfy(61,8): Error: Duplicate local-variable name: x @@ -56,8 +59,5 @@ TypeTests.dfy(151,13): Error: sorry, cannot instantiate type parameter with a su TypeTests.dfy(152,2): Error: sorry, cannot instantiate type parameter with a subrange type TypeTests.dfy(153,16): Error: sorry, cannot instantiate type parameter with a subrange type TypeTests.dfy(154,14): Error: sorry, cannot instantiate type parameter with a subrange type -TypeTests.dfy(177,5): Error: non-ghost variable cannot be assigned a value that depends on a ghost -TypeTests.dfy(187,4): Error: cannot assign to non-ghost variable in a ghost context -TypeTests.dfy(188,7): Error: cannot assign to non-ghost variable in a ghost context TypeTests.dfy(21,9): Error: because of cyclic dependencies among constructor argument types, no instances of datatype 'NeverendingList' can be constructed 62 resolution/type errors detected in TypeTests.dfy diff --git a/Test/dafny4/set-compr.dfy.expect b/Test/dafny4/set-compr.dfy.expect index b31c6ac0..615ee2bc 100644 --- a/Test/dafny4/set-compr.dfy.expect +++ b/Test/dafny4/set-compr.dfy.expect @@ -1,3 +1,3 @@ -set-compr.dfy(25,7): Error: a set comprehension must produce a finite set, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'o' -set-compr.dfy(51,13): Error: a set comprehension must produce a finite set, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'o' +set-compr.dfy(25,7): Error: set comprehensions in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'o' +set-compr.dfy(51,13): Error: set comprehensions in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for 'o' 2 resolution/type errors detected in set-compr.dfy -- cgit v1.2.3 From 27120ddc7adb3a0c789c1ee784d73a4be08de118 Mon Sep 17 00:00:00 2001 From: leino Date: Mon, 5 Oct 2015 08:59:16 -0700 Subject: Implemented resolution, verification, and (poorly performing) compilation of existential if guards. Fixed bugs in ghost checks involving comprehensions and attributes. Added SubstituteBoundedPool method. --- Source/Dafny/Compiler.cs | 40 +++-- Source/Dafny/DafnyAst.cs | 13 +- Source/Dafny/Resolver.cs | 49 +++--- Source/Dafny/Translator.cs | 139 +++++++++++++++-- Test/dafny0/Compilation.dfy | 35 ++++- Test/dafny0/Compilation.dfy.expect | 13 +- Test/dafny0/ExistentialGuards.dfy | 86 ++++++++++- Test/dafny0/ExistentialGuards.dfy.expect | 111 ++++++++++++-- Test/dafny0/ExistentialGuardsResolution.dfy | 154 +++++++++++++++++++ Test/dafny0/ExistentialGuardsResolution.dfy.expect | 167 +++++++++++++++++++++ Test/dafny0/ResolutionErrors.dfy | 20 +++ Test/dafny0/ResolutionErrors.dfy.expect | 7 +- 12 files changed, 769 insertions(+), 65 deletions(-) create mode 100644 Test/dafny0/ExistentialGuardsResolution.dfy create mode 100644 Test/dafny0/ExistentialGuardsResolution.dfy.expect (limited to 'Source/Dafny/Compiler.cs') diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index 13381cc7..aa4ca3ec 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -1455,10 +1455,17 @@ namespace Microsoft.Dafny { } } else { Indent(indent); wr.Write("if ("); - TrExpr(s.Guard); + TrExpr(s.IsExistentialGuard ? Translator.AlphaRename((ExistsExpr)s.Guard, "eg_d", new Translator(null)) : s.Guard); wr.WriteLine(")"); - TrStmt(s.Thn, indent); + // We'd like to do "TrStmt(s.Thn, indent)", except we want the scope of any existential variables to come inside the block + Indent(indent); wr.WriteLine("{"); + if (s.IsExistentialGuard) { + IntroduceAndAssignBoundVars(indent + IndentAmount, (ExistsExpr)s.Guard); + } + TrStmtList(s.Thn.Body, indent); + Indent(indent); wr.WriteLine("}"); + if (s.Els != null) { Indent(indent); wr.WriteLine("else"); TrStmt(s.Els, indent); @@ -1467,13 +1474,14 @@ namespace Microsoft.Dafny { } else if (stmt is AlternativeStmt) { var s = (AlternativeStmt)stmt; - foreach (var alternative in s.Alternatives) { - } Indent(indent); foreach (var alternative in s.Alternatives) { wr.Write("if ("); - TrExpr(alternative.Guard); + TrExpr(alternative.IsExistentialGuard ? Translator.AlphaRename((ExistsExpr)alternative.Guard, "eg_d", new Translator(null)) : alternative.Guard); wr.WriteLine(") {"); + if (alternative.IsExistentialGuard) { + IntroduceAndAssignBoundVars(indent + IndentAmount, (ExistsExpr)alternative.Guard); + } TrStmtList(alternative.Body, indent); Indent(indent); wr.Write("} else "); @@ -1766,6 +1774,18 @@ namespace Microsoft.Dafny { } } + private void IntroduceAndAssignBoundVars(int indent, ExistsExpr exists) { + Contract.Requires(0 <= indent); + Contract.Requires(exists != null); + Contract.Assume(exists.Bounds != null); // follows from successful resolution + Contract.Assert(exists.Range == null); // follows from invariant of class IfStmt + foreach (var bv in exists.BoundVars) { + TrLocalVar(bv, false, indent); + } + var ivars = exists.BoundVars.ConvertAll(bv => (IVariable)bv); + TrAssignSuchThat(indent, ivars, exists.Term, exists.Bounds, exists.tok.line); + } + private void TrAssignSuchThat(int indent, List lhss, Expression constraint, List bounds, int debuginfoLine) { Contract.Requires(0 <= indent); Contract.Requires(lhss != null); @@ -2155,18 +2175,18 @@ namespace Microsoft.Dafny { } } - void TrLocalVar(LocalVariable s, bool alwaysInitialize, int indent) { - Contract.Requires(s != null); - if (s.IsGhost) { + void TrLocalVar(IVariable v, bool alwaysInitialize, int indent) { + Contract.Requires(v != null); + if (v.IsGhost) { // only emit non-ghosts (we get here only for local variables introduced implicitly by call statements) return; } Indent(indent); - wr.Write("{0} @{1}", TypeName(s.Type), s.CompileName); + wr.Write("{0} @{1}", TypeName(v.Type), v.CompileName); if (alwaysInitialize) { // produce a default value - wr.WriteLine(" = {0};", DefaultValue(s.Type)); + wr.WriteLine(" = {0};", DefaultValue(v.Type)); } else { wr.WriteLine(";"); } diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index 64af1425..667f8407 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -3903,7 +3903,7 @@ namespace Microsoft.Dafny { Contract.Invariant(Expr != null); } - public ExprRhs(Expression expr, Attributes attrs = null) + public ExprRhs(Expression expr, Attributes attrs = null) // TODO: these 'attrs' apparently aren't handled correctly in the Cloner, and perhaps not in various visitors either (for example, CheckIsCompilable should not go into attributes) : base(expr.tok, attrs) { Contract.Requires(expr != null); @@ -7175,9 +7175,16 @@ namespace Microsoft.Dafny { public override IEnumerable SubExpressions { get { if (SplitQuantifier == null) { - return base.SubExpressions; + foreach (var e in base.SubExpressions) { + yield return e; + } } else { - return SplitQuantifier; + foreach (var e in Attributes.SubExpressions(Attributes)) { + yield return e; + } + foreach (var e in SplitQuantifier) { + yield return e; + } } } } diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index bc94e491..1798243c 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -2264,12 +2264,14 @@ namespace Microsoft.Dafny what = "quantifier"; whereToLookForBounds = ((QuantifierExpr)e).LogicalBody(); polarity = e is ExistsExpr; - } else if (e is SetComprehension && ((SetComprehension)e).Finite) { + } else if (e is SetComprehension) { what = "set comprehension"; whereToLookForBounds = e.Range; - } else if (e is MapComprehension && ((MapComprehension)e).Finite) { + } else if (e is MapComprehension) { what = "map comprehension"; whereToLookForBounds = e.Range; + } else { + Contract.Assume(e is LambdaExpr); // otherwise, unexpected ComprehensionExpr } if (whereToLookForBounds != null) { List missingBounds; @@ -2278,9 +2280,9 @@ namespace Microsoft.Dafny e.MissingBounds = missingBounds; if ((e is SetComprehension && !((SetComprehension)e).Finite) || (e is MapComprehension && !((MapComprehension)e).Finite)) { - // a possibly infinite set/map has no restrictions on its range + // a possibly infinite set/map has no restrictions on its range (unless it's used in a compilable context, which is checked later) } else if (e is QuantifierExpr) { - // don't report any errors at this time (instead, wait to see if the quantifier is used in a non-ghost context) + // a quantifier has no restrictions on its range (unless it's used in a compilable context, which is checked later) } else if (e is SetComprehension && e.Type.HasFinitePossibleValues) { // This means the set is finite, regardless of if the Range is bounded. So, we don't give any error here. // However, if this expression is used in a non-ghost context (which is not yet known at this stage of @@ -2341,7 +2343,6 @@ namespace Microsoft.Dafny var bin = expr as BinaryExpr; if (bin != null) { bin.ResolvedOp = ResolveOp(bin.Op, bin.E1.Type); - } } } @@ -5647,7 +5648,17 @@ namespace Microsoft.Dafny Contract.Assert(s.Guard.Type != null); // follows from postcondition of ResolveExpression ConstrainTypes(s.Guard.Type, Type.Bool, s.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Guard.Type); } - ResolveStatement(s.Thn, codeContext); + + scope.PushMarker(); + if (s.IsExistentialGuard) { + var exists = (ExistsExpr)s.Guard; + foreach (var v in exists.BoundVars) { + ScopePushAndReport(scope, v, "bound-variable"); + } + } + ResolveBlockStatement(s.Thn, codeContext); + scope.PopMarker(); + if (s.Els != null) { ResolveStatement(s.Els, codeContext); } @@ -6525,7 +6536,7 @@ namespace Microsoft.Dafny Contract.Requires(alternatives != null); Contract.Requires(codeContext != null); - // first, resolve the guards, which tells us whether or not the entire statement is a ghost statement + // first, resolve the guards foreach (var alternative in alternatives) { int prevErrorCount = reporter.Count(ErrorLevel.Error); ResolveExpression(alternative.Guard, new ResolveOpts(codeContext, true)); @@ -6539,6 +6550,12 @@ namespace Microsoft.Dafny } foreach (var alternative in alternatives) { scope.PushMarker(); + if (alternative.IsExistentialGuard) { + var exists = (ExistsExpr)alternative.Guard; + foreach (var v in exists.BoundVars) { + ScopePushAndReport(scope, v, "bound-variable"); + } + } foreach (Statement ss in alternative.Body) { ResolveStatement(ss, codeContext); } @@ -9508,9 +9525,9 @@ namespace Microsoft.Dafny if (uncompilableBoundVars.Count != 0) { string what; if (e is SetComprehension) { - what = "set comprehensions"; + what = ((SetComprehension)e).Finite ? "set comprehensions" : "iset comprehensions"; } else if (e is MapComprehension) { - what = "map comprehensions"; + what = ((MapComprehension)e).Finite ? "map comprehensions" : "imap comprehensions"; } else { Contract.Assume(e is QuantifierExpr); // otherwise, unexpected ComprehensionExpr (since LambdaExpr is handled separately above) Contract.Assert(((QuantifierExpr)e).SplitQuantifier == null); // No split quantifiers during resolution @@ -9521,15 +9538,11 @@ namespace Microsoft.Dafny } return; } + // don't recurse down any attributes + if (e.Range != null) { CheckIsCompilable(e.Range); } + CheckIsCompilable(e.Term); + return; - } else if (expr is MapComprehension) { - var e = (MapComprehension)expr; - if (e.MissingBounds != null && !e.Finite) { - foreach (var bv in e.MissingBounds) { - reporter.Error(MessageSource.Resolver, expr, "imaps in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name); - } - return; - } } else if (expr is NamedExpr) { if (!moduleInfo.IsAbstract) CheckIsCompilable(((NamedExpr)expr).Body); @@ -10436,7 +10449,7 @@ namespace Microsoft.Dafny } else if (expr is QuantifierExpr) { var e = (QuantifierExpr)expr; Contract.Assert(e.SplitQuantifier == null); // No split quantifiers during resolution - return e.UncompilableBoundVars().Count != 0; + return e.UncompilableBoundVars().Count != 0 || UsesSpecFeatures(e.LogicalBody()); } else if (expr is SetComprehension) { var e = (SetComprehension)expr; return !e.Finite || e.UncompilableBoundVars().Count != 0 || (e.Range != null && UsesSpecFeatures(e.Range)) || (e.Term != null && UsesSpecFeatures(e.Term)); diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index c84f0cec..18b50686 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -4740,7 +4740,7 @@ namespace Microsoft.Dafny { var typeMap = Util.Dict(e.TypeArgs, Map(typeArgumentCopies, tp => (Type)new UserDefinedType(tp))); var newLocals = Map(typeArgumentCopies, tp => new Bpl.LocalVariable(tp.tok, new TypedIdent(tp.tok, nameTypeParam(tp), predef.Ty))); locals.AddRange(newLocals); - // Create local variables corresponding to the in-parameters: + // Create local variables corresponding to the bound variables: var substMap = SetupBoundVarsAsLocals(e.BoundVars, builder, locals, etran, typeMap); // Get the body of the quantifier and suitably substitute for the type variables and bound variables var body = Substitute(e.LogicalBody(true), null, substMap, typeMap); @@ -7244,24 +7244,30 @@ namespace Microsoft.Dafny { } else if (stmt is IfStmt) { AddComment(builder, stmt, "if statement"); IfStmt s = (IfStmt)stmt; - Bpl.Expr guard; + Expression guard; if (s.Guard == null) { guard = null; } else { - TrStmt_CheckWellformed(s.Guard, builder, locals, etran, true); - guard = etran.TrExpr(s.Guard); + guard = s.IsExistentialGuard ? AlphaRename((ExistsExpr)s.Guard, "eg$", this) : s.Guard; + TrStmt_CheckWellformed(guard, builder, locals, etran, true); } Bpl.StmtListBuilder b = new Bpl.StmtListBuilder(); CurrentIdGenerator.Push(); + if (s.IsExistentialGuard) { + var exists = (ExistsExpr)s.Guard; // the original (that is, not alpha-renamed) guard + IntroduceAndAssignExistentialVars(exists, b, builder, locals, etran); + } Bpl.StmtList thn = TrStmt2StmtList(b, s.Thn, locals, etran); CurrentIdGenerator.Pop(); Bpl.StmtList els; Bpl.IfCmd elsIf = null; + b = new Bpl.StmtListBuilder(); + if (s.IsExistentialGuard) { + b.Add(new Bpl.AssumeCmd(guard.tok, Bpl.Expr.Not(etran.TrExpr(guard)))); + } if (s.Els == null) { - b = new Bpl.StmtListBuilder(); els = b.Collect(s.Tok); } else { - b = new Bpl.StmtListBuilder(); els = TrStmt2StmtList(b, s.Els, locals, etran); if (els.BigBlocks.Count == 1) { Bpl.BigBlock bb = els.BigBlocks[0]; @@ -7271,7 +7277,7 @@ namespace Microsoft.Dafny { } } } - builder.Add(new Bpl.IfCmd(stmt.Tok, guard, thn, elsIf, els)); + builder.Add(new Bpl.IfCmd(stmt.Tok, guard == null || s.IsExistentialGuard ? null : etran.TrExpr(guard), thn, elsIf, els)); } else if (stmt is AlternativeStmt) { AddComment(builder, stmt, "alternative statement"); @@ -7551,6 +7557,26 @@ namespace Microsoft.Dafny { } } + private void IntroduceAndAssignExistentialVars(ExistsExpr exists, Bpl.StmtListBuilder builder, Bpl.StmtListBuilder builderOutsideIfConstruct, List locals, ExpressionTranslator etran) { + Contract.Requires(exists != null); + Contract.Requires(exists.Range == null); + Contract.Requires(builder != null); + Contract.Requires(builderOutsideIfConstruct != null); + Contract.Requires(locals != null); + Contract.Requires(etran != null); + // declare and havoc the bound variables of 'exists' as local variables + var iesForHavoc = new List(); + foreach (var bv in exists.BoundVars) { + Bpl.Type varType = TrType(bv.Type); + Bpl.Expr wh = GetWhereClause(bv.Tok, new Bpl.IdentifierExpr(bv.Tok, bv.AssignUniqueName(currentDeclaration.IdGenerator), varType), bv.Type, etran); + Bpl.Variable local = new Bpl.LocalVariable(bv.Tok, new Bpl.TypedIdent(bv.Tok, bv.AssignUniqueName(currentDeclaration.IdGenerator), varType, wh)); + locals.Add(local); + iesForHavoc.Add(new Bpl.IdentifierExpr(local.tok, local)); + } + builderOutsideIfConstruct.Add(new Bpl.HavocCmd(exists.tok, iesForHavoc)); + builder.Add(new Bpl.AssumeCmd(exists.tok, etran.TrExpr(exists.Term))); + } + void TrStmtList(List stmts, Bpl.StmtListBuilder builder, List locals, ExpressionTranslator etran) { Contract.Requires(stmts != null); Contract.Requires(builder != null); @@ -7564,6 +7590,46 @@ namespace Microsoft.Dafny { } } + /// + /// Returns an expression like 'exists' but where the bound variables have been renamed to have + /// 'prefix' as a prefix to their previous names. + /// Assumes the expression has been resolved. + /// + public static Expression AlphaRename(ExistsExpr exists, string prefix, Translator translator) { + Contract.Requires(exists != null); + Contract.Requires(prefix != null); + Contract.Requires(translator != null); + + if (exists.SplitQuantifier != null) { + // TODO: what to do? Substitute(exists.SplitQuantifierExpression); + } + + var substMap = new Dictionary(); + var var4var = new Dictionary(); + var bvars = new List(); + foreach (var bv in exists.BoundVars) { + var newBv = new BoundVar(bv.tok, prefix + bv.Name, bv.Type); + bvars.Add(newBv); + var4var.Add(bv, newBv); + var ie = new IdentifierExpr(newBv.tok, newBv.Name); + ie.Var = newBv; // resolve here + ie.Type = newBv.Type; // resolve here + substMap.Add(bv, ie); + } + var s = new Substituter(null, substMap, new Dictionary(), translator); + var range = exists.Range == null ? null : s.Substitute(exists.Range); + var term = s.Substitute(exists.Term); + var attrs = s.SubstAttributes(exists.Attributes); + var ex = new ExistsExpr(exists.tok, exists.TypeArgs, bvars, range, term, attrs); + if (exists.Bounds != null) { + ex.Bounds = exists.Bounds.ConvertAll(bound => s.SubstituteBoundedPool(bound)); + } + if (exists.MissingBounds != null) { + ex.MissingBounds = exists.MissingBounds.ConvertAll(bv => var4var[bv]); + } + return ex; + } + /// /// Generate: /// havoc Heap \ {this} \ _reads \ _new; @@ -8569,7 +8635,7 @@ namespace Microsoft.Dafny { void TrAlternatives(List alternatives, Bpl.Cmd elseCase0, Bpl.StructuredCmd elseCase1, Bpl.StmtListBuilder builder, List locals, ExpressionTranslator etran) { Contract.Requires(alternatives != null); - Contract.Requires((elseCase0 != null) == (elseCase1 == null)); // ugly way of doing a type union + Contract.Requires((elseCase0 == null) != (elseCase1 == null)); // ugly way of doing a type union Contract.Requires(builder != null); Contract.Requires(locals != null); Contract.Requires(etran != null); @@ -8583,10 +8649,13 @@ namespace Microsoft.Dafny { return; } + // alpha-rename any existential guards + var guards = alternatives.ConvertAll(alt => alt.IsExistentialGuard ? AlphaRename((ExistsExpr)alt.Guard, "eg$", this) : alt.Guard); + // build the negation of the disjunction of all guards (that is, the conjunction of their negations) Bpl.Expr noGuard = Bpl.Expr.True; - foreach (var alternative in alternatives) { - noGuard = BplAnd(noGuard, Bpl.Expr.Not(etran.TrExpr(alternative.Guard))); + foreach (var g in guards) { + noGuard = BplAnd(noGuard, Bpl.Expr.Not(etran.TrExpr(g))); } var b = new Bpl.StmtListBuilder(); @@ -8605,8 +8674,13 @@ namespace Microsoft.Dafny { CurrentIdGenerator.Push(); var alternative = alternatives[i]; b = new Bpl.StmtListBuilder(); - TrStmt_CheckWellformed(alternative.Guard, b, locals, etran, true); - b.Add(new AssumeCmd(alternative.Guard.tok, etran.TrExpr(alternative.Guard))); + TrStmt_CheckWellformed(guards[i], b, locals, etran, true); + if (alternative.IsExistentialGuard) { + var exists = (ExistsExpr)alternative.Guard; // the original (that is, not alpha-renamed) guard + IntroduceAndAssignExistentialVars(exists, b, builder, locals, etran); + } else { + b.Add(new AssumeCmd(alternative.Guard.tok, etran.TrExpr(alternative.Guard))); + } foreach (var s in alternative.Body) { TrStmt(s, b, locals, etran); } @@ -13705,6 +13779,9 @@ namespace Microsoft.Dafny { Contract.Assert(false); // unexpected ComprehensionExpr } } + if (e.Bounds != null) { + ((ComprehensionExpr)newExpr).Bounds = e.Bounds.ConvertAll(bound => SubstituteBoundedPool(bound)); + } // undo any changes to substMap (could be optimized to do this only if newBoundVars != e.BoundVars) foreach (var bv in e.BoundVars) { substMap.Remove(bv); @@ -13761,6 +13838,44 @@ namespace Microsoft.Dafny { } } + public ComprehensionExpr.BoundedPool SubstituteBoundedPool(ComprehensionExpr.BoundedPool bound) { + if (bound == null) { + return null; + } else if (bound is ComprehensionExpr.ExactBoundedPool) { + var b = (ComprehensionExpr.ExactBoundedPool)bound; + return new ComprehensionExpr.ExactBoundedPool(Substitute(b.E)); + } else if (bound is ComprehensionExpr.BoolBoundedPool) { + return bound; // nothing to substitute + } else if (bound is ComprehensionExpr.CharBoundedPool) { + return bound; // nothing to substitute + } else if (bound is ComprehensionExpr.RefBoundedPool) { + return bound; // nothing to substitute + } else if (bound is ComprehensionExpr.IntBoundedPool) { + var b = (ComprehensionExpr.IntBoundedPool)bound; + return new ComprehensionExpr.IntBoundedPool(b.LowerBound == null ? null : Substitute(b.LowerBound), b.UpperBound == null ? null : Substitute(b.UpperBound)); + } else if (bound is ComprehensionExpr.SetBoundedPool) { + var b = (ComprehensionExpr.SetBoundedPool)bound; + return new ComprehensionExpr.SetBoundedPool(Substitute(b.Set)); + } else if (bound is ComprehensionExpr.SubSetBoundedPool) { + var b = (ComprehensionExpr.SubSetBoundedPool)bound; + return new ComprehensionExpr.SubSetBoundedPool(Substitute(b.UpperBound)); + } else if (bound is ComprehensionExpr.SuperSetBoundedPool) { + var b = (ComprehensionExpr.SuperSetBoundedPool)bound; + return new ComprehensionExpr.SuperSetBoundedPool(Substitute(b.LowerBound)); + } else if (bound is ComprehensionExpr.MapBoundedPool) { + var b = (ComprehensionExpr.MapBoundedPool)bound; + return new ComprehensionExpr.MapBoundedPool(Substitute(b.Map)); + } else if (bound is ComprehensionExpr.SeqBoundedPool) { + var b = (ComprehensionExpr.SeqBoundedPool)bound; + return new ComprehensionExpr.SeqBoundedPool(Substitute(b.Seq)); + } else if (bound is ComprehensionExpr.DatatypeBoundedPool) { + return bound; // nothing to substitute + } else { + Contract.Assume(false); // unexpected ComprehensionExpr.BoundedPool + throw new cce.UnreachableException(); // to please compiler + } + } + /// /// Return a list of bound variables, of the same length as 'vars' but with possible substitutions. /// For any change necessary, update 'substMap' to reflect the new substitution; the caller is responsible for diff --git a/Test/dafny0/Compilation.dfy b/Test/dafny0/Compilation.dfy index a2b96996..7f9169da 100644 --- a/Test/dafny0/Compilation.dfy +++ b/Test/dafny0/Compilation.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny "%s" > "%t" +// RUN: %dafny /compile:3 "%s" > "%t" // RUN: %diff "%s.expect" "%t" // The tests in this file are designed to run through the compiler. They contain @@ -43,6 +43,8 @@ module CoRecursion { // 40 // 41 // 42 + // 9 + // 9 method Main() { var m := 17; var cell := new Cell; @@ -58,6 +60,37 @@ module CoRecursion { print l.car, "\n"; l := l.cdr; } + var nio := OneLess(0, 10); + print nio, "\n"; + nio := OneLess'(0, 10); + print nio, "\n"; + } + + method OneLess(lo: int, hi: int) returns (m: int) + requires lo < hi + // This method ensures m == hi - 1, but we don't care to prove it + decreases hi - lo + { + if y :| lo < y < hi { + m := OneLess(y, hi); + } else { + m := lo; + } + } + + method OneLess'(lo: int, hi: int) returns (m: int) + requires lo < hi + // This method ensures m == hi - 1, but we don't care to prove it + decreases hi - lo + { + if { + case y :| lo < y < hi => + m := OneLess'(y, hi); + case lo+1 < hi => + m := OneLess'(lo+1, hi); + case lo + 1 == hi => + m := lo; + } } } diff --git a/Test/dafny0/Compilation.dfy.expect b/Test/dafny0/Compilation.dfy.expect index 0a1938ae..2b76b107 100644 --- a/Test/dafny0/Compilation.dfy.expect +++ b/Test/dafny0/Compilation.dfy.expect @@ -1,3 +1,12 @@ -Dafny program verifier finished with 46 verified, 0 errors -Compiled assembly into Compilation.exe +Dafny program verifier finished with 50 verified, 0 errors +Program compiled successfully +Running... + +400 +320 +40 +41 +42 +9 +9 diff --git a/Test/dafny0/ExistentialGuards.dfy b/Test/dafny0/ExistentialGuards.dfy index 001acb55..5afb3979 100644 --- a/Test/dafny0/ExistentialGuards.dfy +++ b/Test/dafny0/ExistentialGuards.dfy @@ -2,12 +2,19 @@ // RUN: %diff "%s.expect" "%t" predicate P(n: int) +{ + n % 2 == 0 +} predicate R(r: real) +{ + 0.0 <= r +} method M0() { if x :| P(x) { + var y := x + 3; } } @@ -19,13 +26,19 @@ method M1() method M2() { - if x, y :| P(x) && R(y) { + var x := true; + if x, y :| P(x) && R(y) { // this declares a new 'x' + var z := x + 12; } + x := x && false; } method M3() { + var x := true; if x: int, y :| P(x) && R(y) { + var z := x + y.Trunc; + var w := real(x) + y; } } @@ -53,37 +66,94 @@ method M6() } } -method M7() +ghost method M7() returns (z: real, w: real) + ensures -2.0 <= z + ensures z == w // error: does not hold { + var k; if x :| P(x) { + k, z := 4, 18.0; } else if * { + z := z + -z; } else if y :| R(y) { + z := y; } else if y :| P(y) { + k := y; + } else { + z :| R(z); + } + if P(k) { + z := 18.0; + } +} + +ghost method M8(m: int, n: int) + requires forall y :: m <= y < n ==> P(y) +{ + var t := -1; + var u; + if y :| m <= y < n && P(y) { + u := y; + if * { + t := n - y; + } else if * { + t := y - m; + } else if P(y) { + t := 8; + } else { + t := -100; // will never happen + } + } + if t < 0 && m < n { + assert P(m) && !P(m); + assert false; } + assert t < 0 ==> n <= m; } method P0(m: int, n: int) requires m < n { + ghost var even, alsoEven := 4, 8; if { case x :| P(x) => + even := x; case x: int :| P(x) => + even := x; case x, y :| P(x) && R(y) => + even, alsoEven := x, y.Trunc; // this assigns to 'alsoEven' a possibly odd number case x: int, y :| P(x) && R(y) => + even := x; case m < n => // just to be different case x, y: real :| P(x) && R(y) => + even := x; case x: int, y: real :| P(x) && R(y) => + even := x; } + assert P(even); + assert P(alsoEven); // error: may not hold } method P1(m: int, n: int) - requires m < n { + if { // error: missing case + case x :| m <= x < n && P(x) => + } +} + +method P2(m: int, n: int) + requires forall y :: m <= y < n ==> P(y) +{ + if { // error: missing case + case x :| m <= x < n && P(x) => + } +} + +method P3(m: int, n: int) + requires m < n && forall y :: m <= y < n ==> P(y) +{ + assert P(m); // lemma that proves that the following 'if' covers all possibilities if { - case x {:myattribute x, "hello"} :| P(x) => - case x, y {:myattribute y, "sveika"} :| P(x) && R(y) => - case x: int {:myattribute x, "chello"} :| P(x) => - case x {:myattribute x, "hola"} {:yourattribute x + x, "hej"} :| P(x) => - case m < n => + case x :| m <= x < n && P(x) => } } diff --git a/Test/dafny0/ExistentialGuards.dfy.expect b/Test/dafny0/ExistentialGuards.dfy.expect index cf229553..21461067 100644 --- a/Test/dafny0/ExistentialGuards.dfy.expect +++ b/Test/dafny0/ExistentialGuards.dfy.expect @@ -3,12 +3,19 @@ // ExistentialGuards.dfy predicate P(n: int) +{ + n % 2 == 0 +} predicate R(r: real) +{ + 0.0 <= r +} method M0() { if x :| P(x) { + var y := x + 3; } } @@ -20,13 +27,19 @@ method M1() method M2() { + var x := true; if x, y :| P(x) && R(y) { + var z := x + 12; } + x := x && false; } method M3() { + var x := true; if x: int, y :| P(x) && R(y) { + var z := x + y.Trunc; + var w := real(x) + y; } } @@ -54,41 +67,119 @@ method M6() } } -method M7() +ghost method M7() returns (z: real, w: real) + ensures -2.0 <= z + ensures z == w { + var k; if x :| P(x) { + k, z := 4, 18.0; } else if * { + z := z + -z; } else if y :| R(y) { + z := y; } else if y :| P(y) { + k := y; + } else { + z :| R(z); + } + if P(k) { + z := 18.0; } } +ghost method M8(m: int, n: int) + requires forall y :: m <= y < n ==> P(y) +{ + var t := -1; + var u; + if y :| m <= y < n && P(y) { + u := y; + if * { + t := n - y; + } else if * { + t := y - m; + } else if P(y) { + t := 8; + } else { + t := -100; + } + } + if t < 0 && m < n { + assert P(m) && !P(m); + assert false; + } + assert t < 0 ==> n <= m; +} + method P0(m: int, n: int) requires m < n { + ghost var even, alsoEven := 4, 8; if { case x :| P(x) => + even := x; case x: int :| P(x) => + even := x; case x, y :| P(x) && R(y) => + even, alsoEven := x, y.Trunc; case x: int, y :| P(x) && R(y) => + even := x; case m < n => case x, y: real :| P(x) && R(y) => + even := x; case x: int, y: real :| P(x) && R(y) => + even := x; } + assert P(even); + assert P(alsoEven); } method P1(m: int, n: int) - requires m < n { if { - case x {:myattribute x, "hello"} :| P(x) => - case x, y {:myattribute y, "sveika"} :| P(x) && R(y) => - case x: int {:myattribute x, "chello"} :| P(x) => - case x {:myattribute x, "hola"} {:yourattribute x + x, "hej"} :| P(x) => - case m < n => + case x :| m <= x < n && P(x) => + } +} + +method P2(m: int, n: int) + requires forall y :: m <= y < n ==> P(y) +{ + if { + case x :| m <= x < n && P(x) => + } +} + +method P3(m: int, n: int) + requires m < n && forall y :: m <= y < n ==> P(y) +{ + assert P(m); + if { + case x :| m <= x < n && P(x) => } } +ExistentialGuards.dfy(85,10): Error BP5003: A postcondition might not hold on this return path. +ExistentialGuards.dfy(71,12): Related location: This is the postcondition that might not hold. +Execution trace: + (0,0): anon0 + (0,0): anon12_Then + (0,0): anon9 + (0,0): anon16_Then +ExistentialGuards.dfy(134,9): Error: assertion violation +ExistentialGuards.dfy(6,8): Related location +Execution trace: + (0,0): anon0 + (0,0): anon20_Then + (0,0): anon21_Then + (0,0): anon5 + (0,0): anon17 +ExistentialGuards.dfy(139,2): Error: alternative cases fail to cover all possibilties +Execution trace: + (0,0): anon0 + (0,0): anon7_Else +ExistentialGuards.dfy(147,2): Error: alternative cases fail to cover all possibilties +Execution trace: + (0,0): anon0 + (0,0): anon7_Else -Dafny program verifier finished with 22 verified, 0 errors -Compilation error: Function _module._default.P has no body -Compilation error: Function _module._default.R has no body +Dafny program verifier finished with 24 verified, 4 errors diff --git a/Test/dafny0/ExistentialGuardsResolution.dfy b/Test/dafny0/ExistentialGuardsResolution.dfy new file mode 100644 index 00000000..8fce1de5 --- /dev/null +++ b/Test/dafny0/ExistentialGuardsResolution.dfy @@ -0,0 +1,154 @@ +// RUN: %dafny /dprint:- "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +predicate P(n: int) + +predicate R(r: real) + +method M0() +{ + if x :| P(x) { + var y := x + 3; + var x := true; // error: 'x' is already declared in this scope + } +} + +method M1() +{ + if x: int :| P(x) { + x := x + 1; // error: 'x' is an immutable variable + } +} + +method M2() +{ + var x := true; + if x, y :| P(x) && R(y) { // this declares a new 'x' + var z := x + 12; + } + x := x && false; +} + +method M3() +{ + var x := true; + if x: int, y :| P(x) && R(y) { + var z := x + int(y); + var w := real(x) + y; + } + var x := 0.0; // error: 'x' is already declared in this scope +} + +method M4() +{ + if x, y: real :| P(x) && R(y) { + } +} + +method M5() +{ + if x: int, y: real :| P(x) && R(y) { + } +} + +method M6() +{ + if x {:myattribute x, "hello"} :| P(x) { + } + if x, y {:myattribute y, "sveika"} :| P(x) && R(y) { + } + if x: int {:myattribute x, "chello"} :| P(x) { + } + if x {:myattribute x, "hola"} {:yourattribute x + x, "hej"} :| P(x) { + } +} + +method M7() +{ + if x :| P(x) { + } else if * { + } else if y :| R(y) { + } else if y :| P(y) { + } +} + +method P0(m: int, n: int) + requires m < n +{ + var x := true; + if { + case x :| P(x) => + var t := 3 * x; + case x: int :| P(x) => + case x, y :| P(x) && R(y) => + y := y + 1.0; // error: 'y' is an immutable variable + case x: int, y :| P(x) && R(y) => + case m < n => + x := x || m + 5 == n; + case x, y: real :| P(x) && R(y) => + case x: int, y: real :| P(x) && R(y) => + } + assert x; +} + +method P1(m: int, n: int) + requires m < n +{ + if { + case x {:myattribute x, "hello"} :| P(x) => + case x, y {:myattribute y, "sveika"} :| P(x) && R(y) => + case x: int {:myattribute x, "chello"} :| P(x) => + case x {:myattribute x, "hola"} {:yourattribute x + x, "hej"} :| P(x) => + case m < n => + } +} + +module TypesNotFullyDetermined { + method T0() + { + if x :| true { // error: type not entirely resolved + } + } + method T1() + { + if x :| true { + var y := x + 3; + } + } +} + +module Ghost { + predicate P(x: int) // note, P is ghost + predicate method R(x: int) + method M7() returns (z: int, b: bool) + { + if * { + z := z + -z; + } else if y :| 1000 <= y < 2000 && R(y) { + z := y; + } else if y :| 0 <= y < 100 && P(y) { + z := y; // error: not allowed, because the P in the guard makes this a ghost context + } else if y :| 0 <= y < 100 && R(y) { + z := y; // error: this is also in a ghost context, because it depends on the P above + } + + if * { + z := z + -z; + } else if exists y :: 1000 <= y < 2000 && R(y) { + z := 0; + } else if exists y :: 0 <= y < 100 && P(y) { + z := 0; // error: not allowed, because the P in the guard makes this a ghost context + } else if exists y :: 0 <= y < 100 && R(y) { + z := 0; // error: this is also in a ghost context, because it depends on the P above + } + + if P(z) { + z := 20; // error: blatant ghost context + } + + b := exists y :: 0 <= y < 100 && P(y); // error: assignment to non-ghost of something that depends on ghost + ghost var c; + c := exists y :: 0 <= y < 100 && P(y); + b := exists y {:myattribute P(y)} :: 0 <= y < 100; + } +} diff --git a/Test/dafny0/ExistentialGuardsResolution.dfy.expect b/Test/dafny0/ExistentialGuardsResolution.dfy.expect new file mode 100644 index 00000000..681bb13d --- /dev/null +++ b/Test/dafny0/ExistentialGuardsResolution.dfy.expect @@ -0,0 +1,167 @@ +// Dafny program verifier version 1.9.5.20511, Copyright (c) 2003-2015, Microsoft. +// Command Line Options: -nologo -countVerificationErrors:0 -useBaseNameForFileName /dprint:- C:\dafny\Test\dafny0\ExistentialGuardsResolution.dfy +// ExistentialGuardsResolution.dfy + + +module TypesNotFullyDetermined { + method T0() + { + if x :| true { + } + } + + method T1() + { + if x :| true { + var y := x + 3; + } + } +} + +module Ghost { + predicate P(x: int) + + predicate method R(x: int) + + method M7() returns (z: int, b: bool) + { + if * { + z := z + -z; + } else if y :| 1000 <= y < 2000 && R(y) { + z := y; + } else if y :| 0 <= y < 100 && P(y) { + z := y; + } else if y :| 0 <= y < 100 && R(y) { + z := y; + } + if * { + z := z + -z; + } else if exists y :: 1000 <= y < 2000 && R(y) { + z := 0; + } else if exists y :: 0 <= y < 100 && P(y) { + z := 0; + } else if exists y :: 0 <= y < 100 && R(y) { + z := 0; + } + if P(z) { + z := 20; + } + b := exists y :: 0 <= y < 100 && P(y); + ghost var c; + c := exists y :: 0 <= y < 100 && P(y); + b := exists y {:myattribute P(y)} :: 0 <= y < 100; + } +} +predicate P(n: int) + +predicate R(r: real) + +method M0() +{ + if x :| P(x) { + var y := x + 3; + var x := true; + } +} + +method M1() +{ + if x: int :| P(x) { + x := x + 1; + } +} + +method M2() +{ + var x := true; + if x, y :| P(x) && R(y) { + var z := x + 12; + } + x := x && false; +} + +method M3() +{ + var x := true; + if x: int, y :| P(x) && R(y) { + var z := x + int(y); + var w := real(x) + y; + } + var x := 0.0; +} + +method M4() +{ + if x, y: real :| P(x) && R(y) { + } +} + +method M5() +{ + if x: int, y: real :| P(x) && R(y) { + } +} + +method M6() +{ + if x {:myattribute x, "hello"} :| P(x) { + } + if x, y {:myattribute y, "sveika"} :| P(x) && R(y) { + } + if x: int {:myattribute x, "chello"} :| P(x) { + } + if x {:myattribute x, "hola"} {:yourattribute x + x, "hej"} :| P(x) { + } +} + +method M7() +{ + if x :| P(x) { + } else if * { + } else if y :| R(y) { + } else if y :| P(y) { + } +} + +method P0(m: int, n: int) + requires m < n +{ + var x := true; + if { + case x :| P(x) => + var t := 3 * x; + case x: int :| P(x) => + case x, y :| P(x) && R(y) => + y := y + 1.0; + case x: int, y :| P(x) && R(y) => + case m < n => + x := x || m + 5 == n; + case x, y: real :| P(x) && R(y) => + case x: int, y: real :| P(x) && R(y) => + } + assert x; +} + +method P1(m: int, n: int) + requires m < n +{ + if { + case x {:myattribute x, "hello"} :| P(x) => + case x, y {:myattribute y, "sveika"} :| P(x) && R(y) => + case x: int {:myattribute x, "chello"} :| P(x) => + case x {:myattribute x, "hola"} {:yourattribute x + x, "hej"} :| P(x) => + case m < n => + } +} +ExistentialGuardsResolution.dfy(109,7): Error: type of bound variable 'x' could not be determined; please specify the type explicitly +ExistentialGuardsResolution.dfy(130,8): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression) +ExistentialGuardsResolution.dfy(132,8): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression) +ExistentialGuardsResolution.dfy(140,8): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression) +ExistentialGuardsResolution.dfy(142,8): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression) +ExistentialGuardsResolution.dfy(146,8): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression) +ExistentialGuardsResolution.dfy(149,37): Error: function calls are allowed only in specification contexts (consider declaring the function a 'function method') +ExistentialGuardsResolution.dfy(12,8): Error: Duplicate local-variable name: x +ExistentialGuardsResolution.dfy(19,4): Error: LHS of assignment must denote a mutable variable +ExistentialGuardsResolution.dfy(39,6): Error: Duplicate local-variable name: x +ExistentialGuardsResolution.dfy(84,6): Error: LHS of assignment must denote a mutable variable +11 resolution/type errors detected in ExistentialGuardsResolution.dfy diff --git a/Test/dafny0/ResolutionErrors.dfy b/Test/dafny0/ResolutionErrors.dfy index e935c83d..58ba6701 100644 --- a/Test/dafny0/ResolutionErrors.dfy +++ b/Test/dafny0/ResolutionErrors.dfy @@ -1692,3 +1692,23 @@ module LoopResolutionTests { } } } + +module UnderspecifiedTypesInAttributes { + function method P(x: T): int + method M() { + var {:myattr var u :| true; 6} v: int; // error: type of u is underspecified + var j {:myattr var u :| true; 6} :| 0 <= j < 100; // error: type of u is underspecified + + var a := new int[100]; + forall lp {:myattr var u :| true; 6} | 0 <= lp < 100 { // error: type of u is underspecified + a[lp] := 0; + } + + modify {:myattr P(10)} {:myattr var u :| true; 6} a; // error: type of u is underspecified + + calc {:myattr P(10)} {:myattr var u :| true; 6} // error: type of u is underspecified + { + 5; + } + } +} diff --git a/Test/dafny0/ResolutionErrors.dfy.expect b/Test/dafny0/ResolutionErrors.dfy.expect index be19eeac..b055184f 100644 --- a/Test/dafny0/ResolutionErrors.dfy.expect +++ b/Test/dafny0/ResolutionErrors.dfy.expect @@ -161,6 +161,11 @@ ResolutionErrors.dfy(1673,4): Error: 'decreases *' is not allowed on ghost loops ResolutionErrors.dfy(1677,8): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression) ResolutionErrors.dfy(1687,4): Error: 'decreases *' is not allowed on ghost loops ResolutionErrors.dfy(1691,29): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression) +ResolutionErrors.dfy(1699,17): Error: the type of the bound variable 'u' could not be determined +ResolutionErrors.dfy(1700,19): Error: the type of the bound variable 'u' could not be determined +ResolutionErrors.dfy(1703,23): Error: the type of the bound variable 'u' could not be determined +ResolutionErrors.dfy(1707,36): Error: the type of the bound variable 'u' could not be determined +ResolutionErrors.dfy(1709,34): Error: the type of the bound variable 'u' could not be determined ResolutionErrors.dfy(469,2): Error: More than one anonymous constructor ResolutionErrors.dfy(50,13): Error: 'this' is not allowed in a 'static' context ResolutionErrors.dfy(87,14): Error: the name 'Benny' denotes a datatype constructor, but does not do so uniquely; add an explicit qualification (for example, 'Abc.Benny') @@ -224,4 +229,4 @@ ResolutionErrors.dfy(1123,8): Error: new cannot be applied to a trait ResolutionErrors.dfy(1144,13): Error: first argument to / must be of numeric type (instead got set) ResolutionErrors.dfy(1151,18): Error: a call to a possibly non-terminating method is allowed only if the calling method is also declared (with 'decreases *') to be possibly non-terminating ResolutionErrors.dfy(1166,14): Error: a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating -226 resolution/type errors detected in ResolutionErrors.dfy +231 resolution/type errors detected in ResolutionErrors.dfy -- cgit v1.2.3 From 2b7d31bb3e0276028a8bc50c4933ea60fed5fb60 Mon Sep 17 00:00:00 2001 From: qunyanm Date: Wed, 4 Nov 2015 10:36:54 -0800 Subject: Fix issue89. Copy the out param to a local before use it in an anonymous method that is generated by LetExpr. Change the compiler so that each stmt writes to its own buffer before add it to the program's buffer so that we can insert the above mentioned copy instruction before the stmt. --- Source/Dafny/Compiler.cs | 1151 +++++++++++++++++++------------------ Source/DafnyDriver/DafnyDriver.cs | 4 +- Test/dafny4/Bug89.dfy | 15 + Test/dafny4/Bug89.dfy.expect | 6 + 4 files changed, 610 insertions(+), 566 deletions(-) create mode 100644 Test/dafny4/Bug89.dfy create mode 100644 Test/dafny4/Bug89.dfy.expect (limited to 'Source/Dafny/Compiler.cs') diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index aa4ca3ec..f992d6c0 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -14,18 +14,11 @@ using System.Text; namespace Microsoft.Dafny { public class Compiler { - public Compiler(TextWriter wr) { - Contract.Requires(wr != null); - this.wr = wr; + public Compiler() { + } - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(wr!=null); - } - - TextWriter wr; + StringBuilder copyInstrWriter = new StringBuilder(); // a buffer that stores copy instructions generated by letExpr that uses out param. Method enclosingMethod; // non-null when a method body is being translated FreshIdGenerator idGenerator = new FreshIdGenerator(); @@ -50,7 +43,7 @@ namespace Microsoft.Dafny { public int ErrorCount; public TextWriter ErrorWriter = Console.Out; - void Error(string msg, params object[] args) { + void Error(string msg, TextWriter wr, params object[] args) { Contract.Requires(msg != null); Contract.Requires(args != null); @@ -60,7 +53,7 @@ namespace Microsoft.Dafny { ErrorCount++; } - void ReadRuntimeSystem() { + void ReadRuntimeSystem(TextWriter wr) { string codebase = cce.NonNull( System.IO.Path.GetDirectoryName(cce.NonNull(System.Reflection.Assembly.GetExecutingAssembly().Location))); string path = System.IO.Path.Combine(codebase, "DafnyRuntime.cs"); using (TextReader rd = new StreamReader(new FileStream(path, System.IO.FileMode.Open, System.IO.FileAccess.Read))) @@ -75,16 +68,16 @@ namespace Microsoft.Dafny { } readonly int IndentAmount = 2; - void Indent(int ind) { + void Indent(int ind, TextWriter wr) { Contract.Requires(0 <= ind); string spaces = " "; for (; spaces.Length < ind; ind -= spaces.Length) { wr.Write(spaces); } - wr.Write(spaces.Substring(0, ind)); + wr.Write(spaces.Substring(0, ind)); } - public void Compile(Program program) { + public void Compile(Program program, TextWriter wr) { Contract.Requires(program != null); wr.WriteLine("// Dafny program {0} compiled into C#", program.Name); wr.WriteLine("// To recompile, use 'csc' with: /r:System.Numerics.dll"); @@ -92,8 +85,8 @@ namespace Microsoft.Dafny { wr.WriteLine("// You might also want to include compiler switches like:"); wr.WriteLine("// /debug /nowarn:0164 /nowarn:0219"); wr.WriteLine(); - ReadRuntimeSystem(); - CompileBuiltIns(program.BuiltIns); + ReadRuntimeSystem(wr); + CompileBuiltIns(program.BuiltIns, wr); foreach (ModuleDefinition m in program.CompileModules) { if (m.IsAbstract) { @@ -117,33 +110,33 @@ namespace Microsoft.Dafny { wr.WriteLine(); if (d is OpaqueTypeDecl) { var at = (OpaqueTypeDecl)d; - Error("Opaque type ('{0}') cannot be compiled", at.FullName); + Error("Opaque type ('{0}') cannot be compiled", wr, at.FullName); } else if (d is TypeSynonymDecl) { // do nothing, just bypass type synonyms in the compiler } else if (d is NewtypeDecl) { var nt = (NewtypeDecl)d; - Indent(indent); + Indent(indent, wr); wr.WriteLine("public class @{0} {{", nt.CompileName); if (nt.NativeType != null) { - Indent(indent + IndentAmount); + Indent(indent + IndentAmount, wr); wr.WriteLine("public static System.Collections.Generic.IEnumerable<{0}> IntegerRange(BigInteger lo, BigInteger hi) {{", nt.NativeType.Name); - Indent(indent + 2 * IndentAmount); + Indent(indent + 2 * IndentAmount, wr); wr.WriteLine("for (var j = lo; j < hi; j++) {{ yield return ({0})j; }}", nt.NativeType.Name); - Indent(indent + IndentAmount); + Indent(indent + IndentAmount, wr); wr.WriteLine("}"); } - Indent(indent); + Indent(indent, wr); wr.WriteLine("}"); } else if (d is DatatypeDecl) { var dt = (DatatypeDecl)d; - Indent(indent); + Indent(indent, wr); wr.Write("public abstract class Base_{0}", dt.CompileName); if (dt.TypeArgs.Count != 0) { wr.Write("<{0}>", TypeParameters(dt.TypeArgs)); } wr.WriteLine(" { }"); - CompileDatatypeConstructors(dt, indent); - CompileDatatypeStruct(dt, indent); + CompileDatatypeConstructors(dt, indent, wr); + CompileDatatypeStruct(dt, indent, wr); } else if (d is IteratorDecl) { var iter = (IteratorDecl)d; // An iterator is compiled as follows: @@ -169,7 +162,7 @@ namespace Microsoft.Dafny { // } // } - Indent(indent); + Indent(indent, wr); wr.Write("public class @{0}", iter.CompileName); if (iter.TypeArgs.Count != 0) { wr.Write("<{0}>", TypeParameters(iter.TypeArgs)); @@ -181,58 +174,58 @@ namespace Microsoft.Dafny { foreach (var member in iter.Members) { var f = member as Field; if (f != null && !f.IsGhost) { - Indent(ind); - wr.WriteLine("public {0} @{1} = {2};", TypeName(f.Type), f.CompileName, DefaultValue(f.Type)); + Indent(ind, wr); + wr.WriteLine("public {0} @{1} = {2};", TypeName(f.Type, wr), f.CompileName, DefaultValue(f.Type, wr)); } else if (member is Constructor) { Contract.Assert(ct == null); // we're expecting just one constructor ct = (Constructor)member; } } Contract.Assert(ct != null); // we do expect a constructor - Indent(ind); wr.WriteLine("System.Collections.Generic.IEnumerator __iter;"); + Indent(ind, wr); wr.WriteLine("System.Collections.Generic.IEnumerator __iter;"); // here's the initializer method - Indent(ind); wr.Write("public void @{0}(", ct.CompileName); + Indent(ind, wr); wr.Write("public void @{0}(", ct.CompileName); string sep = ""; foreach (var p in ct.Ins) { if (!p.IsGhost) { // here we rely on the parameters and the corresponding fields having the same names - wr.Write("{0}{1} @{2}", sep, TypeName(p.Type), p.CompileName); + wr.Write("{0}{1} @{2}", sep, TypeName(p.Type, wr), p.CompileName); sep = ", "; } } wr.WriteLine(") {"); foreach (var p in ct.Ins) { if (!p.IsGhost) { - Indent(ind + IndentAmount); + Indent(ind + IndentAmount, wr); wr.WriteLine("this.@{0} = @{0};", p.CompileName); } } - Indent(ind + IndentAmount); wr.WriteLine("__iter = TheIterator();"); - Indent(ind); wr.WriteLine("}"); + Indent(ind + IndentAmount, wr); wr.WriteLine("__iter = TheIterator();"); + Indent(ind, wr); wr.WriteLine("}"); // here are the enumerator methods - Indent(ind); wr.WriteLine("public void MoveNext(out bool more) { more = __iter.MoveNext(); }"); - Indent(ind); wr.WriteLine("private System.Collections.Generic.IEnumerator TheIterator() {"); + Indent(ind, wr); wr.WriteLine("public void MoveNext(out bool more) { more = __iter.MoveNext(); }"); + Indent(ind, wr); wr.WriteLine("private System.Collections.Generic.IEnumerator TheIterator() {"); if (iter.Body == null) { - Error("Iterator {0} has no body", iter.FullName); + Error("Iterator {0} has no body", wr, iter.FullName); } else { - TrStmt(iter.Body, ind + IndentAmount); + wr.Write(TrStmt(iter.Body, ind + IndentAmount).ToString()); } - Indent(ind + IndentAmount); wr.WriteLine("yield break;"); - Indent(ind); wr.WriteLine("}"); + Indent(ind + IndentAmount, wr); wr.WriteLine("yield break;"); + Indent(ind, wr); wr.WriteLine("}"); // end of the class - Indent(indent); wr.WriteLine("}"); + Indent(indent, wr); wr.WriteLine("}"); } else if (d is TraitDecl) { //writing the trait var trait = (TraitDecl)d; - Indent(indent); + Indent(indent, wr); wr.Write("public interface @{0}", trait.CompileName); wr.WriteLine(" {"); - CompileClassMembers(trait, false, indent + IndentAmount); - Indent(indent); wr.WriteLine("}"); + CompileClassMembers(trait, false, indent + IndentAmount, wr); + Indent(indent, wr); wr.WriteLine("}"); //writing the _Companion class List members = new List(); @@ -252,27 +245,27 @@ namespace Microsoft.Dafny { } } } - Indent(indent); + Indent(indent, wr); wr.Write("public class @_Companion_{0}", trait.CompileName); wr.WriteLine(" {"); - CompileClassMembers(trait, true, indent + IndentAmount); - Indent(indent); wr.WriteLine("}"); + CompileClassMembers(trait, true, indent + IndentAmount, wr); + Indent(indent, wr); wr.WriteLine("}"); } else if (d is ClassDecl) { var cl = (ClassDecl)d; - Indent(indent); + Indent(indent, wr); wr.Write("public class @{0}", cl.CompileName); if (cl.TypeArgs.Count != 0) { wr.Write("<{0}>", TypeParameters(cl.TypeArgs)); } string sep = " : "; foreach (var trait in cl.TraitsTyp) { - wr.Write("{0}{1}", sep, TypeName(trait)); + wr.Write("{0}{1}", sep, TypeName(trait, wr)); sep = ", "; } wr.WriteLine(" {"); - CompileClassMembers(cl, false, indent+IndentAmount); - Indent(indent); wr.WriteLine("}"); + CompileClassMembers(cl, false, indent+IndentAmount, wr); + Indent(indent, wr); wr.WriteLine("}"); } else if (d is ModuleDecl) { // nop } else { Contract.Assert(false); } @@ -283,15 +276,15 @@ namespace Microsoft.Dafny { } } - void CompileBuiltIns(BuiltIns builtIns) { + void CompileBuiltIns(BuiltIns builtIns, TextWriter wr) { wr.WriteLine("namespace Dafny {"); - Indent(IndentAmount); + Indent(IndentAmount, wr); wr.WriteLine("public partial class Helpers {"); foreach (var decl in builtIns.SystemModule.TopLevelDecls) { if (decl is ArrayClassDecl) { int dims = ((ArrayClassDecl)decl).Dims; // public static T[,] InitNewArray2(BigInteger size0, BigInteger size1) { - Indent(3 * IndentAmount); + Indent(3 * IndentAmount, wr); wr.Write("public static T["); RepeatWrite(wr, dims, "", ","); wr.Write("] InitNewArray{0}(", dims); @@ -299,52 +292,52 @@ namespace Microsoft.Dafny { wr.WriteLine(") {"); // int s0 = (int)size0; for (int i = 0; i < dims; i++) { - Indent(4 * IndentAmount); + Indent(4 * IndentAmount, wr); wr.WriteLine("int s{0} = (int)size{0};", i); } // T[,] a = new T[s0, s1]; - Indent(4 * IndentAmount); + Indent(4 * IndentAmount, wr); wr.Write("T["); RepeatWrite(wr, dims, "", ","); wr.Write("] a = new T["); RepeatWrite(wr, dims, "s{0}", ","); wr.WriteLine("];"); // BigInteger[,] b = a as BigInteger[,]; - Indent(4 * IndentAmount); + Indent(4 * IndentAmount, wr); wr.Write("BigInteger["); RepeatWrite(wr, dims, "", ","); wr.Write("] b = a as BigInteger["); RepeatWrite(wr, dims, "", ","); wr.WriteLine("];"); // if (b != null) { - Indent(4 * IndentAmount); + Indent(4 * IndentAmount, wr); wr.WriteLine("if (b != null) {"); // BigInteger z = new BigInteger(0); - Indent(5 * IndentAmount); + Indent(5 * IndentAmount, wr); wr.WriteLine("BigInteger z = new BigInteger(0);"); // for (int i0 = 0; i0 < s0; i0++) // for (int i1 = 0; i1 < s1; i1++) for (int i = 0; i < dims; i++) { - Indent((5+i) * IndentAmount); + Indent((5+i) * IndentAmount, wr); wr.WriteLine("for (int i{0} = 0; i{0} < s{0}; i{0}++)", i); } // b[i0,i1] = z; - Indent((5+dims) * IndentAmount); + Indent((5+dims) * IndentAmount, wr); wr.Write("b["); RepeatWrite(wr, dims, "i{0}", ","); wr.WriteLine("] = z;"); // } - Indent(4 * IndentAmount); + Indent(4 * IndentAmount, wr); wr.WriteLine("}"); // return a; - Indent(4 * IndentAmount); + Indent(4 * IndentAmount, wr); wr.WriteLine("return a;"); // } - Indent(3 * IndentAmount); + Indent(3 * IndentAmount, wr); wr.WriteLine("}"); // end of method } } - Indent(IndentAmount); + Indent(IndentAmount, wr); wr.WriteLine("}"); // end of class Helpers wr.WriteLine("}"); // end of namespace } @@ -359,7 +352,7 @@ namespace Microsoft.Dafny { } } - void CompileDatatypeConstructors(DatatypeDecl dt, int indent) + void CompileDatatypeConstructors(DatatypeDecl dt, int indent, TextWriter wr) { Contract.Requires(dt != null); @@ -372,20 +365,20 @@ namespace Microsoft.Dafny { // public Dt__Lazy(Computer c) { this.c = c; } // public Base_Dt Get() { return c(); } // } - Indent(indent); + Indent(indent, wr); wr.WriteLine("public class {0}__Lazy{1} : Base_{0}{1} {{", dt.CompileName, typeParams); int ind = indent + IndentAmount; - Indent(ind); + Indent(ind, wr); wr.WriteLine("public delegate Base_{0}{1} Computer();", dt.CompileName, typeParams); - Indent(ind); + Indent(ind, wr); wr.WriteLine("public delegate Computer ComputerComputer();"); - Indent(ind); + Indent(ind, wr); wr.WriteLine("Computer c;"); - Indent(ind); + Indent(ind, wr); wr.WriteLine("public {0}__Lazy(Computer c) {{ this.c = c; }}", dt.CompileName); - Indent(ind); + Indent(ind, wr); wr.WriteLine("public Base_{0}{1} Get() {{ return c(); }}", dt.CompileName, typeParams); - Indent(indent); + Indent(indent, wr); wr.WriteLine("}"); } @@ -407,7 +400,7 @@ namespace Microsoft.Dafny { // // ... // } // } - Indent(indent); + Indent(indent, wr); wr.Write("public class {0}", DtCtorDeclarationName(ctor, dt.TypeArgs)); wr.WriteLine(" : Base_{0}{1} {{", dt.CompileName, typeParams); int ind = indent + IndentAmount; @@ -415,32 +408,32 @@ namespace Microsoft.Dafny { int i = 0; foreach (Formal arg in ctor.Formals) { if (!arg.IsGhost) { - Indent(ind); - wr.WriteLine("public readonly {0} @{1};", TypeName(arg.Type), FormalName(arg, i)); + Indent(ind, wr); + wr.WriteLine("public readonly {0} @{1};", TypeName(arg.Type, wr), FormalName(arg, i)); i++; } } - Indent(ind); + Indent(ind, wr); wr.Write("public {0}(", DtCtorDeclartionName(ctor)); - WriteFormals("", ctor.Formals); + WriteFormals("", ctor.Formals, wr); wr.WriteLine(") {"); i = 0; foreach (Formal arg in ctor.Formals) { if (!arg.IsGhost) { - Indent(ind + IndentAmount); + Indent(ind + IndentAmount, wr); wr.WriteLine("this.@{0} = @{0};", FormalName(arg, i)); i++; } } - Indent(ind); wr.WriteLine("}"); + Indent(ind, wr); wr.WriteLine("}"); // Equals method - Indent(ind); wr.WriteLine("public override bool Equals(object other) {"); - Indent(ind + IndentAmount); + Indent(ind, wr); wr.WriteLine("public override bool Equals(object other) {"); + Indent(ind + IndentAmount, wr); wr.Write("var oth = other as {0}", DtCtorName(ctor, dt.TypeArgs)); wr.WriteLine(";"); - Indent(ind + IndentAmount); + Indent(ind + IndentAmount, wr); wr.Write("return oth != null"); i = 0; foreach (Formal arg in ctor.Formals) { @@ -455,56 +448,56 @@ namespace Microsoft.Dafny { } } wr.WriteLine(";"); - Indent(ind); wr.WriteLine("}"); + Indent(ind, wr); wr.WriteLine("}"); // GetHashCode method (Uses the djb2 algorithm) - Indent(ind); wr.WriteLine("public override int GetHashCode() {"); - Indent(ind + IndentAmount); wr.WriteLine("ulong hash = 5381;"); - Indent(ind + IndentAmount); wr.WriteLine("hash = ((hash << 5) + hash) + {0};", constructorIndex); + Indent(ind, wr); wr.WriteLine("public override int GetHashCode() {"); + Indent(ind + IndentAmount, wr); wr.WriteLine("ulong hash = 5381;"); + Indent(ind + IndentAmount, wr); wr.WriteLine("hash = ((hash << 5) + hash) + {0};", constructorIndex); i = 0; foreach (Formal arg in ctor.Formals) { if (!arg.IsGhost) { string nm = FormalName(arg, i); - Indent(ind + IndentAmount); wr.WriteLine("hash = ((hash << 5) + hash) + ((ulong)this.@{0}.GetHashCode());", nm); + Indent(ind + IndentAmount, wr); wr.WriteLine("hash = ((hash << 5) + hash) + ((ulong)this.@{0}.GetHashCode());", nm); i++; } } - Indent(ind + IndentAmount); wr.WriteLine("return (int) hash;"); - Indent(ind); wr.WriteLine("}"); + Indent(ind + IndentAmount, wr); wr.WriteLine("return (int) hash;"); + Indent(ind, wr); wr.WriteLine("}"); if (dt is IndDatatypeDecl) { - Indent(ind); wr.WriteLine("public override string ToString() {"); + Indent(ind, wr); wr.WriteLine("public override string ToString() {"); string nm; if (dt is TupleTypeDecl) { nm = ""; } else { nm = (dt.Module.IsDefaultModule ? "" : dt.Module.CompileName + ".") + dt.CompileName + "." + ctor.CompileName; } - Indent(ind + IndentAmount); wr.WriteLine("string s = \"{0}\";", nm); + Indent(ind + IndentAmount, wr); wr.WriteLine("string s = \"{0}\";", nm); if (ctor.Formals.Count != 0) { - Indent(ind + IndentAmount); wr.WriteLine("s += \"(\";"); + Indent(ind + IndentAmount, wr); wr.WriteLine("s += \"(\";"); i = 0; foreach (var arg in ctor.Formals) { if (!arg.IsGhost) { if (i != 0) { - Indent(ind + IndentAmount); wr.WriteLine("s += \", \";"); + Indent(ind + IndentAmount, wr); wr.WriteLine("s += \", \";"); } - Indent(ind + IndentAmount); wr.WriteLine("s += @{0}.ToString();", FormalName(arg, i)); + Indent(ind + IndentAmount, wr); wr.WriteLine("s += @{0}.ToString();", FormalName(arg, i)); i++; } } - Indent(ind + IndentAmount); wr.WriteLine("s += \")\";"); + Indent(ind + IndentAmount, wr); wr.WriteLine("s += \")\";"); } - Indent(ind + IndentAmount); wr.WriteLine("return s;"); - Indent(ind); wr.WriteLine("}"); + Indent(ind + IndentAmount, wr); wr.WriteLine("return s;"); + Indent(ind, wr); wr.WriteLine("}"); } - Indent(indent); wr.WriteLine("}"); + Indent(indent, wr); wr.WriteLine("}"); } constructorIndex++; } - void CompileDatatypeStruct(DatatypeDecl dt, int indent) { + void CompileDatatypeStruct(DatatypeDecl dt, int indent, TextWriter wr) { Contract.Requires(dt != null); // public struct Dt : IDatatype{ @@ -548,46 +541,46 @@ namespace Microsoft.Dafny { DtT += DtT_TypeArgs; } - Indent(indent); + Indent(indent, wr); wr.WriteLine("public struct @{0} {{", DtT); int ind = indent + IndentAmount; - Indent(ind); + Indent(ind, wr); wr.WriteLine("Base_{0} _d;", DtT); - Indent(ind); + Indent(ind, wr); wr.WriteLine("public Base_{0} _D {{", DtT); - Indent(ind + IndentAmount); + Indent(ind + IndentAmount, wr); wr.WriteLine("get {"); - Indent(ind + 2 * IndentAmount); + Indent(ind + 2 * IndentAmount, wr); wr.WriteLine("if (_d == null) {"); - Indent(ind + 3 * IndentAmount); + Indent(ind + 3 * IndentAmount, wr); wr.WriteLine("_d = Default;"); if (dt is CoDatatypeDecl) { string typeParams = dt.TypeArgs.Count == 0 ? "" : string.Format("<{0}>", TypeParameters(dt.TypeArgs)); - Indent(ind + 2 * IndentAmount); + Indent(ind + 2 * IndentAmount, wr); wr.WriteLine("}} else if (_d is {0}__Lazy{1}) {{", dt.CompileName, typeParams); - Indent(ind + 3 * IndentAmount); + Indent(ind + 3 * IndentAmount, wr); wr.WriteLine("_d = (({0}__Lazy{1})_d).Get();", dt.CompileName, typeParams); } - Indent(ind + 2 * IndentAmount); wr.WriteLine("}"); - Indent(ind + 2 * IndentAmount); wr.WriteLine("return _d;"); - Indent(ind + IndentAmount); wr.WriteLine("}"); - Indent(ind); wr.WriteLine("}"); + Indent(ind + 2 * IndentAmount, wr); wr.WriteLine("}"); + Indent(ind + 2 * IndentAmount, wr); wr.WriteLine("return _d;"); + Indent(ind + IndentAmount, wr); wr.WriteLine("}"); + Indent(ind, wr); wr.WriteLine("}"); - Indent(ind); + Indent(ind, wr); wr.WriteLine("public @{0}(Base_{1} d) {{ this._d = d; }}", dt.CompileName, DtT); - Indent(ind); + Indent(ind, wr); wr.WriteLine("static Base_{0} theDefault;", DtT); - Indent(ind); + Indent(ind, wr); wr.WriteLine("public static Base_{0} Default {{", DtT); - Indent(ind + IndentAmount); + Indent(ind + IndentAmount, wr); wr.WriteLine("get {"); - Indent(ind + 2 * IndentAmount); + Indent(ind + 2 * IndentAmount, wr); wr.WriteLine("if (theDefault == null) {"); - Indent(ind + 3 * IndentAmount); + Indent(ind + 3 * IndentAmount, wr); wr.Write("theDefault = "); DatatypeCtor defaultCtor; @@ -601,55 +594,55 @@ namespace Microsoft.Dafny { string sep = ""; foreach (Formal f in defaultCtor.Formals) { if (!f.IsGhost) { - wr.Write("{0}{1}", sep, DefaultValue(f.Type)); + wr.Write("{0}{1}", sep, DefaultValue(f.Type, wr)); sep = ", "; } } wr.Write(")"); wr.WriteLine(";"); - Indent(ind + 2 * IndentAmount); + Indent(ind + 2 * IndentAmount, wr); wr.WriteLine("}"); - Indent(ind + 2 * IndentAmount); + Indent(ind + 2 * IndentAmount, wr); wr.WriteLine("return theDefault;"); - Indent(ind + IndentAmount); wr.WriteLine("}"); + Indent(ind + IndentAmount, wr); wr.WriteLine("}"); - Indent(ind); wr.WriteLine("}"); + Indent(ind, wr); wr.WriteLine("}"); - Indent(ind); wr.WriteLine("public override bool Equals(object other) {"); - Indent(ind + IndentAmount); + Indent(ind, wr); wr.WriteLine("public override bool Equals(object other) {"); + Indent(ind + IndentAmount, wr); wr.WriteLine("return other is @{0} && _D.Equals(((@{0})other)._D);", DtT); - Indent(ind); wr.WriteLine("}"); + Indent(ind, wr); wr.WriteLine("}"); - Indent(ind); + Indent(ind, wr); wr.WriteLine("public override int GetHashCode() { return _D.GetHashCode(); }"); if (dt is IndDatatypeDecl) { - Indent(ind); + Indent(ind, wr); wr.WriteLine("public override string ToString() { return _D.ToString(); }"); } // query properties foreach (var ctor in dt.Ctors) { // public bool is_Ctor0 { get { return _D is Dt_Ctor0; } } - Indent(ind); + Indent(ind, wr); wr.WriteLine("public bool is_{0} {{ get {{ return _D is {1}_{0}{2}; }} }}", ctor.CompileName, dt.CompileName, DtT_TypeArgs); } if (dt.HasFinitePossibleValues) { - Indent(ind); + Indent(ind, wr); wr.WriteLine("public static System.Collections.Generic.IEnumerable<@{0}> AllSingletonConstructors {{", DtT); - Indent(ind + IndentAmount); + Indent(ind + IndentAmount, wr); wr.WriteLine("get {"); foreach (var ctr in dt.Ctors) { if (ctr.Formals.Count == 0) { - Indent(ind + IndentAmount + IndentAmount); + Indent(ind + IndentAmount + IndentAmount, wr); wr.WriteLine("yield return new @{0}(new {2}_{1}());", DtT, ctr.CompileName, dt.CompileName); } } - Indent(ind + IndentAmount + IndentAmount); + Indent(ind + IndentAmount + IndentAmount, wr); wr.WriteLine("yield break;"); - Indent(ind + IndentAmount); + Indent(ind + IndentAmount, wr); wr.WriteLine("}"); - Indent(ind); + Indent(ind, wr); wr.WriteLine("}"); } @@ -658,17 +651,17 @@ namespace Microsoft.Dafny { foreach (var arg in ctor.Formals) { if (!arg.IsGhost && arg.HasName) { // public T0 @Dtor0 { get { return ((DT_Ctor)_D).@Dtor0; } } - Indent(ind); - wr.WriteLine("public {0} dtor_{1} {{ get {{ return (({2}_{3}{4})_D).@{1}; }} }}", TypeName(arg.Type), arg.CompileName, dt.CompileName, ctor.CompileName, DtT_TypeArgs); + Indent(ind, wr); + wr.WriteLine("public {0} dtor_{1} {{ get {{ return (({2}_{3}{4})_D).@{1}; }} }}", TypeName(arg.Type, wr), arg.CompileName, dt.CompileName, ctor.CompileName, DtT_TypeArgs); } } } - Indent(indent); + Indent(indent, wr); wr.WriteLine("}"); } - int WriteFormals(string sep, List/*!*/ formals) + int WriteFormals(string sep, List/*!*/ formals, TextWriter wr) { Contract.Requires(sep != null); Contract.Requires(cce.NonNullElements(formals)); @@ -676,7 +669,7 @@ namespace Microsoft.Dafny { foreach (Formal arg in formals) { if (!arg.IsGhost) { string name = FormalName(arg, i); - wr.Write("{0}{1}{2} @{3}", sep, arg.InParam ? "" : "out ", TypeName(arg.Type), name); + wr.Write("{0}{1}{2} @{3}", sep, arg.InParam ? "" : "out ", TypeName(arg.Type, wr), name); sep = ", "; i++; } @@ -732,13 +725,13 @@ namespace Microsoft.Dafny { return s; } - string DtCtorName(DatatypeCtor ctor, List typeArgs) { + string DtCtorName(DatatypeCtor ctor, List typeArgs, TextWriter wr) { Contract.Requires(ctor != null); Contract.Ensures(Contract.Result() != null); var s = DtCtorName(ctor); if (typeArgs != null && typeArgs.Count != 0) { - s += "<" + TypeNames(typeArgs) + ">"; + s += "<" + TypeNames(typeArgs, wr) + ">"; } return s; } @@ -776,7 +769,7 @@ namespace Microsoft.Dafny { } } - void CompileClassMembers(ClassDecl c, bool forCompanionClass, int indent) { + void CompileClassMembers(ClassDecl c, bool forCompanionClass, int indent, TextWriter wr) { Contract.Requires(c != null); Contract.Requires(!forCompanionClass || c is TraitDecl); Contract.Requires(0 <= indent); @@ -785,9 +778,9 @@ namespace Microsoft.Dafny { if (member is Field) { var f = (Field)member; // every field is inherited - Indent(indent); - wr.WriteLine("public {0} @_{1};", TypeName(f.Type), f.CompileName); - wr.Write("public {0} @{1}", TypeName(f.Type), f.CompileName); + Indent(indent, wr); + wr.WriteLine("public {0} @_{1};", TypeName(f.Type, wr), f.CompileName); + wr.Write("public {0} @{1}", TypeName(f.Type, wr), f.CompileName); wr.WriteLine(" {"); wr.WriteLine(" get { "); wr.Write("return this.@_{0};", f.CompileName); @@ -799,11 +792,11 @@ namespace Microsoft.Dafny { } else if (member is Function) { var f = (Function)member; Contract.Assert(f.Body != null); - CompileFunction(indent, f); + CompileFunction(indent, f, wr); } else if (member is Method) { var method = (Method)member; Contract.Assert(method.Body != null); - CompileMethod(c, indent, method); + CompileMethod(c, indent, method, wr); } else { Contract.Assert(false); // unexpected member } @@ -814,12 +807,12 @@ namespace Microsoft.Dafny { if (f.IsGhost || forCompanionClass) { // emit nothing } else if (c is TraitDecl) { - Indent(indent); - wr.Write("{0} @{1}", TypeName(f.Type), f.CompileName); + Indent(indent, wr); + wr.Write("{0} @{1}", TypeName(f.Type, wr), f.CompileName); wr.WriteLine(" { get; set; }"); } else { - Indent(indent); - wr.WriteLine("public {0} @{1} = {2};", TypeName(f.Type), f.CompileName, DefaultValue(f.Type)); + Indent(indent, wr); + wr.WriteLine("public {0} @{1} = {2};", TypeName(f.Type, wr), f.CompileName, DefaultValue(f.Type, wr)); } } else if (member is Function) { var f = (Function)member; @@ -828,29 +821,29 @@ namespace Microsoft.Dafny { if (forCompanionClass || Attributes.Contains(f.Attributes, "axiom")) { // suppress error message (in the case of "forCompanionClass", the non-forCompanionClass call will produce the error message) } else { - Error("Function {0} has no body", f.FullName); + Error("Function {0} has no body", wr, f.FullName); } } else if (f.IsGhost) { // nothing to compile, but we do check for assumes if (f.Body == null) { Contract.Assert(c is TraitDecl && !f.IsStatic); } else { - var v = new CheckHasNoAssumes_Visitor(this); + var v = new CheckHasNoAssumes_Visitor(this, wr); v.Visit(f.Body); } } else if (c is TraitDecl && !forCompanionClass) { // include it, unless it's static if (!f.IsStatic) { - Indent(indent); - wr.Write("{0} @{1}", TypeName(f.ResultType), f.CompileName); + Indent(indent, wr); + wr.Write("{0} @{1}", TypeName(f.ResultType, wr), f.CompileName); wr.Write("("); - WriteFormals("", f.Formals); + WriteFormals("", f.Formals, wr); wr.WriteLine(");"); } } else if (forCompanionClass && !f.IsStatic) { // companion classes only has static members } else { - CompileFunction(indent, f); + CompileFunction(indent, f, wr); } } else if (member is Method) { var m = (Method)member; @@ -859,30 +852,30 @@ namespace Microsoft.Dafny { if (forCompanionClass || Attributes.Contains(m.Attributes, "axiom")) { // suppress error message (in the case of "forCompanionClass", the non-forCompanionClass call will produce the error message) } else { - Error("Method {0} has no body", m.FullName); + Error("Method {0} has no body", wr, m.FullName); } } else if (m.IsGhost) { // nothing to compile, but we do check for assumes if (m.Body == null) { Contract.Assert(c is TraitDecl && !m.IsStatic); } else { - var v = new CheckHasNoAssumes_Visitor(this); + var v = new CheckHasNoAssumes_Visitor(this, wr); v.Visit(m.Body); } } else if (c is TraitDecl && !forCompanionClass) { // include it, unless it's static if (!m.IsStatic) { - Indent(indent); + Indent(indent, wr); wr.Write("void @{0}", m.CompileName); wr.Write("("); - int nIns = WriteFormals("", m.Ins); - WriteFormals(nIns == 0 ? "" : ", ", m.Outs); + int nIns = WriteFormals("", m.Ins, wr); + WriteFormals(nIns == 0 ? "" : ", ", m.Outs, wr); wr.WriteLine(");"); } } else if (forCompanionClass && !m.IsStatic) { // companion classes only has static members } else { - CompileMethod(c, indent, m); + CompileMethod(c, indent, m, wr); } } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected member @@ -890,59 +883,59 @@ namespace Microsoft.Dafny { } } - private void CompileFunction(int indent, Function f) { - Indent(indent); - wr.Write("public {0}{1} @{2}", f.IsStatic ? "static " : "", TypeName(f.ResultType), f.CompileName); + private void CompileFunction(int indent, Function f, TextWriter wr) { + Indent(indent, wr); + wr.Write("public {0}{1} @{2}", f.IsStatic ? "static " : "", TypeName(f.ResultType, wr), f.CompileName); if (f.TypeArgs.Count != 0) { wr.Write("<{0}>", TypeParameters(f.TypeArgs)); } wr.Write("("); - WriteFormals("", f.Formals); + WriteFormals("", f.Formals, wr); wr.WriteLine(") {"); - CompileReturnBody(f.Body, indent + IndentAmount); - Indent(indent); wr.WriteLine("}"); + CompileReturnBody(f.Body, indent + IndentAmount, wr); + Indent(indent, wr); wr.WriteLine("}"); } - private void CompileMethod(ClassDecl c, int indent, Method m) { - Indent(indent); + private void CompileMethod(ClassDecl c, int indent, Method m, TextWriter wr) { + Indent(indent, wr); wr.Write("public {0}void @{1}", m.IsStatic ? "static " : "", m.CompileName); if (m.TypeArgs.Count != 0) { wr.Write("<{0}>", TypeParameters(m.TypeArgs)); } wr.Write("("); - int nIns = WriteFormals("", m.Ins); - WriteFormals(nIns == 0 ? "" : ", ", m.Outs); + int nIns = WriteFormals("", m.Ins, wr); + WriteFormals(nIns == 0 ? "" : ", ", m.Outs, wr); wr.WriteLine(")"); - Indent(indent); wr.WriteLine("{"); + Indent(indent, wr); wr.WriteLine("{"); foreach (Formal p in m.Outs) { if (!p.IsGhost) { - Indent(indent + IndentAmount); - wr.WriteLine("@{0} = {1};", p.CompileName, DefaultValue(p.Type)); + Indent(indent + IndentAmount, wr); + wr.WriteLine("@{0} = {1};", p.CompileName, DefaultValue(p.Type, wr)); } } if (m.Body == null) { - Error("Method {0} has no body", m.FullName); + Error("Method {0} has no body", wr, m.FullName); } else { if (m.IsTailRecursive) { - Indent(indent); wr.WriteLine("TAIL_CALL_START: ;"); + Indent(indent, wr); wr.WriteLine("TAIL_CALL_START: ;"); if (!m.IsStatic) { - Indent(indent + IndentAmount); wr.WriteLine("var _this = this;"); + Indent(indent + IndentAmount, wr); wr.WriteLine("var _this = this;"); } } Contract.Assert(enclosingMethod == null); enclosingMethod = m; - TrStmtList(m.Body.Body, indent); + TrStmtList(m.Body.Body, indent, wr); Contract.Assert(enclosingMethod == m); enclosingMethod = null; } - Indent(indent); wr.WriteLine("}"); + Indent(indent, wr); wr.WriteLine("}"); // allow the Main method to be an instance method if (!m.IsStatic && IsMain(m)) { - Indent(indent); + Indent(indent, wr); wr.WriteLine("public static void Main(string[] args) {"); Contract.Assert(m.EnclosingClass == c); - Indent(indent + IndentAmount); + Indent(indent + IndentAmount, wr); wr.Write("@{0} b = new @{0}", c.CompileName); if (c.TypeArgs.Count != 0) { // instantiate every parameter, it doesn't particularly matter how @@ -955,12 +948,12 @@ namespace Microsoft.Dafny { wr.Write(">"); } wr.WriteLine("();"); - Indent(indent + IndentAmount); wr.WriteLine("b.@Main();"); - Indent(indent); wr.WriteLine("}"); + Indent(indent + IndentAmount, wr); wr.WriteLine("b.@Main();"); + Indent(indent, wr); wr.WriteLine("}"); } } - void TrCasePatternOpt(CasePattern pat, Expression rhs, string rhs_string, int indent) { + void TrCasePatternOpt(CasePattern pat, Expression rhs, string rhs_string, int indent, TextWriter wr, bool inLetExprBody) { Contract.Requires(pat != null); Contract.Requires(pat.Var != null || rhs != null); if (pat.Var != null) { @@ -970,10 +963,10 @@ namespace Microsoft.Dafny { // var x := G; var bv = pat.Var; if (!bv.IsGhost) { - Indent(indent); - wr.Write("{0} {1} = ", TypeName(bv.Type), "@" + bv.CompileName); + Indent(indent, wr); + wr.Write("{0} {1} = ", TypeName(bv.Type, wr), "@" + bv.CompileName); if (rhs != null) { - TrExpr(rhs); + TrExpr(rhs, wr, inLetExprBody); } else { wr.Write(rhs_string); } @@ -992,9 +985,9 @@ namespace Microsoft.Dafny { // Create the temporary variable to hold G var tmp_name = idGenerator.FreshId("_let_tmp_rhs"); - Indent(indent); - wr.Write("{0} {1} = ", TypeName(rhs.Type), tmp_name); - TrExpr(rhs); + Indent(indent, wr); + wr.Write("{0} {1} = ", TypeName(rhs.Type, wr), tmp_name); + TrExpr(rhs, wr, inLetExprBody); wr.WriteLine(";"); var k = 0; // number of non-ghost formals processed @@ -1005,21 +998,21 @@ namespace Microsoft.Dafny { // nothing to compile, but do a sanity check Contract.Assert(!Contract.Exists(arg.Vars, bv => !bv.IsGhost)); } else { - TrCasePatternOpt(arg, null, string.Format("(({0})({1})._D).@{2}", DtCtorName(ctor, ((DatatypeValue)pat.Expr).InferredTypeArgs), tmp_name, FormalName(formal, k)), indent); + TrCasePatternOpt(arg, null, string.Format("(({0})({1})._D).@{2}", DtCtorName(ctor, ((DatatypeValue)pat.Expr).InferredTypeArgs, wr), tmp_name, FormalName(formal, k)), indent, wr, inLetExprBody); k++; } } } } - void ReturnExpr(Expression expr, int indent) { - Indent(indent); + void ReturnExpr(Expression expr, int indent, TextWriter wr, bool inLetExprBody) { + Indent(indent, wr); wr.Write("return "); - TrExpr(expr); + TrExpr(expr, wr, inLetExprBody); wr.WriteLine(";"); } - void TrExprOpt(Expression expr, int indent) { + void TrExprOpt(Expression expr, int indent, TextWriter wr, bool inLetExprBody) { Contract.Requires(expr != null); if (expr is LetExpr) { var e = (LetExpr)expr; @@ -1027,25 +1020,25 @@ namespace Microsoft.Dafny { for (int i = 0; i < e.LHSs.Count; i++) { var lhs = e.LHSs[i]; if (Contract.Exists(lhs.Vars, bv => !bv.IsGhost)) { - TrCasePatternOpt(lhs, e.RHSs[i], null, indent); + TrCasePatternOpt(lhs, e.RHSs[i], null, indent, wr, inLetExprBody); } } - TrExprOpt(e.Body, indent); + TrExprOpt(e.Body, indent, wr, inLetExprBody); } else { // We haven't optimized the other cases, so fallback to normal compilation - ReturnExpr(e, indent); + ReturnExpr(e, indent, wr, inLetExprBody); } } else if (expr is ITEExpr) { ITEExpr e = (ITEExpr)expr; - Indent(indent); + Indent(indent, wr); wr.Write("if ("); - TrExpr(e.Test); + TrExpr(e.Test, wr, inLetExprBody); wr.Write(") {\n"); - TrExprOpt(e.Thn, indent + IndentAmount); - Indent(indent); + TrExprOpt(e.Thn, indent + IndentAmount, wr, inLetExprBody); + Indent(indent, wr); wr.WriteLine("} else {"); - TrExprOpt(e.Els, indent + IndentAmount); - Indent(indent); + TrExprOpt(e.Els, indent + IndentAmount, wr, inLetExprBody); + Indent(indent, wr); wr.WriteLine("}"); } else if (expr is MatchExpr) { var e = (MatchExpr)expr; @@ -1060,9 +1053,9 @@ namespace Microsoft.Dafny { // ... // } string source = idGenerator.FreshId("_source"); - Indent(indent); - wr.Write("{0} {1} = ", TypeName(e.Source.Type), source); - TrExpr(e.Source); + Indent(indent, wr); + wr.Write("{0} {1} = ", TypeName(e.Source.Type, wr), source); + TrExpr(e.Source, wr, inLetExprBody); wr.WriteLine(";"); if (e.Cases.Count == 0) { @@ -1073,28 +1066,28 @@ namespace Microsoft.Dafny { var sourceType = (UserDefinedType)e.Source.Type.NormalizeExpand(); foreach (MatchCaseExpr mc in e.Cases) { //Indent(indent); - MatchCasePrelude(source, sourceType, cce.NonNull(mc.Ctor), mc.Arguments, i, e.Cases.Count, indent); - TrExprOpt(mc.Body, indent + IndentAmount); + MatchCasePrelude(source, sourceType, cce.NonNull(mc.Ctor), mc.Arguments, i, e.Cases.Count, indent, wr); + TrExprOpt(mc.Body, indent + IndentAmount, wr, inLetExprBody); i++; } - Indent(indent); + Indent(indent, wr); wr.WriteLine("}"); } } else if (expr is StmtExpr) { var e = (StmtExpr)expr; - TrExprOpt(e.E, indent); + TrExprOpt(e.E, indent, wr, inLetExprBody); } else { // We haven't optimized any other cases, so fallback to normal compilation - ReturnExpr(expr, indent); + ReturnExpr(expr, indent, wr, inLetExprBody); } } - void CompileReturnBody(Expression body, int indent) { + void CompileReturnBody(Expression body, int indent, TextWriter wr) { Contract.Requires(0 <= indent); body = body.Resolved; //Indent(indent); //wr.Write("return "); - TrExprOpt(body, indent); + TrExprOpt(body, indent, wr, false); //wr.WriteLine(";"); } @@ -1113,23 +1106,23 @@ namespace Microsoft.Dafny { return null; } - string TypeName_Companion(Type type) { + string TypeName_Companion(Type type, TextWriter wr) { Contract.Requires(type != null); var udt = type as UserDefinedType; if (udt != null && udt.ResolvedClass is TraitDecl) { string s = udt.FullCompanionCompileName; if (udt.TypeArgs.Count != 0) { if (udt.TypeArgs.Exists(argType => argType is ObjectType)) { - Error("compilation does not support type 'object' as a type parameter; consider introducing a ghost"); + Error("compilation does not support type 'object' as a type parameter; consider introducing a ghost", wr); } - s += "<" + TypeNames(udt.TypeArgs) + ">"; + s += "<" + TypeNames(udt.TypeArgs, wr) + ">"; } return s; } else { - return TypeName(type); + return TypeName(type, wr); } } - string TypeName(Type type) + string TypeName(Type type, TextWriter wr) { Contract.Requires(type != null); Contract.Ensures(Contract.Result() != null); @@ -1153,14 +1146,14 @@ namespace Microsoft.Dafny { if (nativeType != null) { return nativeType.Name; } - return TypeName(xType.AsNewtype.BaseType); + return TypeName(xType.AsNewtype.BaseType, wr); } else if (xType is ObjectType) { return "object"; } else if (xType.IsArrayType) { ArrayClassDecl at = xType.AsArrayType; Contract.Assert(at != null); // follows from type.IsArrayType Type elType = UserDefinedType.ArrayElementType(xType); - string name = TypeName(elType) + "["; + string name = TypeName(elType, wr) + "["; for (int i = 1; i < at.Dims; i++) { name += ","; } @@ -1184,54 +1177,60 @@ namespace Microsoft.Dafny { } s = rc.FullCompileName; } - return TypeName_UDT(s, udt.TypeArgs); + return TypeName_UDT(s, udt.TypeArgs, wr); } else if (xType is SetType) { Type argType = ((SetType)xType).Arg; if (argType is ObjectType) { - Error("compilation of set is not supported; consider introducing a ghost"); + Error("compilation of set is not supported; consider introducing a ghost", wr); } - return DafnySetClass + "<" + TypeName(argType) + ">"; + return DafnySetClass + "<" + TypeName(argType, wr) + ">"; } else if (xType is SeqType) { Type argType = ((SeqType)xType).Arg; if (argType is ObjectType) { - Error("compilation of seq is not supported; consider introducing a ghost"); + Error("compilation of seq is not supported; consider introducing a ghost", wr); } - return DafnySeqClass + "<" + TypeName(argType) + ">"; + return DafnySeqClass + "<" + TypeName(argType, wr) + ">"; } else if (xType is MultiSetType) { Type argType = ((MultiSetType)xType).Arg; if (argType is ObjectType) { - Error("compilation of seq is not supported; consider introducing a ghost"); + Error("compilation of seq is not supported; consider introducing a ghost", wr); } - return DafnyMultiSetClass + "<" + TypeName(argType) + ">"; + return DafnyMultiSetClass + "<" + TypeName(argType, wr) + ">"; } else if (xType is MapType) { Type domType = ((MapType)xType).Domain; Type ranType = ((MapType)xType).Range; if (domType is ObjectType || ranType is ObjectType) { - Error("compilation of map or map<_, object> is not supported; consider introducing a ghost"); + Error("compilation of map or map<_, object> is not supported; consider introducing a ghost", wr); } - return DafnyMapClass + "<" + TypeName(domType) + "," + TypeName(ranType) + ">"; + return DafnyMapClass + "<" + TypeName(domType, wr) + "," + TypeName(ranType, wr) + ">"; } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type } } - string TypeName_UDT(string fullCompileName, List typeArgs) { + string TypeName_UDT(string fullCompileName, List typeArgs, TextWriter wr) { Contract.Requires(fullCompileName != null); Contract.Requires(typeArgs != null); string s = "@" + fullCompileName; if (typeArgs.Count != 0) { if (typeArgs.Exists(argType => argType is ObjectType)) { - Error("compilation does not support type 'object' as a type parameter; consider introducing a ghost"); + Error("compilation does not support type 'object' as a type parameter; consider introducing a ghost", wr); } - s += "<" + TypeNames(typeArgs) + ">"; + s += "<" + TypeNames(typeArgs, wr) + ">"; } return s; } - string/*!*/ TypeNames(List/*!*/ types) { + string/*!*/ TypeNames(List/*!*/ types, TextWriter wr) { Contract.Requires(cce.NonNullElements(types)); Contract.Ensures(Contract.Result() != null); - return Util.Comma(types, TypeName); + string res = ""; + string c = ""; + foreach (var t in types) { + res += c + TypeName(t, wr); + c = ","; + } + return res; } string/*!*/ TypeParameters(List/*!*/ targs) { @@ -1241,7 +1240,7 @@ namespace Microsoft.Dafny { return Util.Comma(targs, tp => "@" + tp.CompileName); } - string DefaultValue(Type type) + string DefaultValue(Type type, TextWriter wr) { Contract.Requires(type != null); Contract.Ensures(Contract.Result() != null); @@ -1264,9 +1263,9 @@ namespace Microsoft.Dafny { if (xType.AsNewtype.NativeType != null) { return "0"; } - return DefaultValue(xType.AsNewtype.BaseType); + return DefaultValue(xType.AsNewtype.BaseType, wr); } else if (xType.IsRefType) { - return string.Format("({0})null", TypeName(xType)); + return string.Format("({0})null", TypeName(xType, wr)); } else if (xType.IsDatatype) { var udt = (UserDefinedType)xType; var s = "@" + udt.FullCompileName; @@ -1287,7 +1286,7 @@ namespace Microsoft.Dafny { s = "@" + rc.FullCompileName; } if (udt.TypeArgs.Count != 0) { - s += "<" + TypeNames(udt.TypeArgs) + ">"; + s += "<" + TypeNames(udt.TypeArgs, wr) + ">"; } return string.Format("new {0}()", s); } else if (xType.IsTypeParameter) { @@ -1295,18 +1294,18 @@ namespace Microsoft.Dafny { string s = "default(@" + udt.FullCompileName; if (udt.TypeArgs.Count != 0) { - s += "<" + TypeNames(udt.TypeArgs) + ">"; + s += "<" + TypeNames(udt.TypeArgs, wr) + ">"; } s += ")"; return s; } else if (xType is SetType) { - return DafnySetClass + "<" + TypeName(((SetType)xType).Arg) + ">.Empty"; + return DafnySetClass + "<" + TypeName(((SetType)xType).Arg, wr) + ">.Empty"; } else if (xType is MultiSetType) { - return DafnyMultiSetClass + "<" + TypeName(((MultiSetType)xType).Arg) + ">.Empty"; + return DafnyMultiSetClass + "<" + TypeName(((MultiSetType)xType).Arg, wr) + ">.Empty"; } else if (xType is SeqType) { - return DafnySeqClass + "<" + TypeName(((SeqType)xType).Arg) + ">.Empty"; + return DafnySeqClass + "<" + TypeName(((SeqType)xType).Arg, wr) + ">.Empty"; } else if (xType is MapType) { - return TypeName(xType)+".Empty"; + return TypeName(xType, wr) + ".Empty"; } else if (xType is ArrowType) { return "null"; } else { @@ -1319,59 +1318,61 @@ namespace Microsoft.Dafny { public class CheckHasNoAssumes_Visitor : BottomUpVisitor { readonly Compiler compiler; - public CheckHasNoAssumes_Visitor(Compiler c) { + TextWriter wr; + public CheckHasNoAssumes_Visitor(Compiler c, TextWriter wr) { Contract.Requires(c != null); compiler = c; + this.wr = wr; } protected override void VisitOneStmt(Statement stmt) { if (stmt is AssumeStmt) { - compiler.Error("an assume statement cannot be compiled (line {0})", stmt.Tok.line); + compiler.Error("an assume statement cannot be compiled (line {0})", wr, stmt.Tok.line); } else if (stmt is AssignSuchThatStmt) { var s = (AssignSuchThatStmt)stmt; if (s.AssumeToken != null) { - compiler.Error("an assume statement cannot be compiled (line {0})", s.AssumeToken.line); + compiler.Error("an assume statement cannot be compiled (line {0})", wr, s.AssumeToken.line); } } else if (stmt is ForallStmt) { var s = (ForallStmt)stmt; if (s.Body == null) { - compiler.Error("a forall statement without a body cannot be compiled (line {0})", stmt.Tok.line); + compiler.Error("a forall statement without a body cannot be compiled (line {0})", wr, stmt.Tok.line); } } else if (stmt is WhileStmt) { var s = (WhileStmt)stmt; if (s.Body == null) { - compiler.Error("a while statement without a body cannot be compiled (line {0})", stmt.Tok.line); + compiler.Error("a while statement without a body cannot be compiled (line {0})", wr, stmt.Tok.line); } } } } - void TrStmt(Statement stmt, int indent) + TextWriter TrStmt(Statement stmt, int indent) { Contract.Requires(stmt != null); + TextWriter wr = new StringWriter(); if (stmt.IsGhost) { - var v = new CheckHasNoAssumes_Visitor(this); + var v = new CheckHasNoAssumes_Visitor(this, wr); v.Visit(stmt); - Indent(indent); wr.WriteLine("{ }"); - return; + Indent(indent, wr); wr.WriteLine("{ }"); + return wr; } - if (stmt is PrintStmt) { PrintStmt s = (PrintStmt)stmt; foreach (var arg in s.Args) { - Indent(indent); + Indent(indent, wr); wr.Write("System.Console.Write("); - TrExpr(arg); + TrExpr(arg, wr, false); wr.WriteLine(");"); } } else if (stmt is BreakStmt) { var s = (BreakStmt)stmt; - Indent(indent); + Indent(indent, wr); wr.WriteLine("goto after_{0};", s.TargetStmt.Labels.Data.AssignUniqueId("after_", idGenerator)); } else if (stmt is ProduceStmt) { var s = (ProduceStmt)stmt; if (s.hiddenUpdate != null) - TrStmt(s.hiddenUpdate, indent); - Indent(indent); + wr.Write(TrStmt(s.hiddenUpdate, indent).ToString()); + Indent(indent, wr); if (s is YieldStmt) { wr.WriteLine("yield return null;"); } else { @@ -1381,7 +1382,7 @@ namespace Microsoft.Dafny { var s = (UpdateStmt)stmt; var resolved = s.ResolvedStatements; if (resolved.Count == 1) { - TrStmt(resolved[0], indent); + wr.Write(TrStmt(resolved[0], indent).ToString()); } else { // multi-assignment Contract.Assert(s.Lhss.Count == resolved.Count); @@ -1393,17 +1394,17 @@ namespace Microsoft.Dafny { var lhs = s.Lhss[i]; var rhs = s.Rhss[i]; if (!(rhs is HavocRhs)) { - lvalues.Add(CreateLvalue(lhs, indent)); + lvalues.Add(CreateLvalue(lhs, indent, wr)); string target = idGenerator.FreshId("_rhs"); rhss.Add(target); - TrRhs("var " + target, null, rhs, indent); + TrRhs("var " + target, null, rhs, indent, wr); } } } Contract.Assert(lvalues.Count == rhss.Count); for (int i = 0; i < lvalues.Count; i++) { - Indent(indent); + Indent(indent, wr); wr.WriteLine("{0} = {1};", lvalues[i], rhss[i]); } } @@ -1411,32 +1412,32 @@ namespace Microsoft.Dafny { } else if (stmt is AssignStmt) { AssignStmt s = (AssignStmt)stmt; Contract.Assert(!(s.Lhs is SeqSelectExpr) || ((SeqSelectExpr)s.Lhs).SelectOne); // multi-element array assignments are not allowed - TrRhs(null, s.Lhs, s.Rhs, indent); + TrRhs(null, s.Lhs, s.Rhs, indent, wr); } else if (stmt is AssignSuchThatStmt) { var s = (AssignSuchThatStmt)stmt; if (s.AssumeToken != null) { // Note, a non-ghost AssignSuchThatStmt may contain an assume - Error("an assume statement cannot be compiled (line {0})", s.AssumeToken.line); + Error("an assume statement cannot be compiled (line {0})", wr, s.AssumeToken.line); } else if (s.MissingBounds != null) { foreach (var bv in s.MissingBounds) { - Error("this assign-such-that statement is too advanced for the current compiler; Dafny's heuristics cannot find any bound for variable '{0}' (line {1})", bv.Name, s.Tok.line); + Error("this assign-such-that statement is too advanced for the current compiler; Dafny's heuristics cannot find any bound for variable '{0}' (line {1})", wr, bv.Name, s.Tok.line); } } else { Contract.Assert(s.Bounds != null); // follows from s.MissingBounds == null TrAssignSuchThat(indent, s.Lhss.ConvertAll(lhs => ((IdentifierExpr)lhs.Resolved).Var), // the resolver allows only IdentifierExpr left-hand sides - s.Expr, s.Bounds, s.Tok.line); + s.Expr, s.Bounds, s.Tok.line, wr, false); } } else if (stmt is CallStmt) { CallStmt s = (CallStmt)stmt; - TrCallStmt(s, null, indent); + wr.Write(TrCallStmt(s, null, indent).ToString()); } else if (stmt is BlockStmt) { - Indent(indent); wr.WriteLine("{"); - TrStmtList(((BlockStmt)stmt).Body, indent); - Indent(indent); wr.WriteLine("}"); + Indent(indent, wr); wr.WriteLine("{"); + TrStmtList(((BlockStmt)stmt).Body, indent, wr); + Indent(indent, wr); wr.WriteLine("}"); } else if (stmt is IfStmt) { IfStmt s = (IfStmt)stmt; @@ -1445,45 +1446,45 @@ namespace Microsoft.Dafny { if (s.Els == null) { // let's compile the "else" branch, since that involves no work // (still, let's leave a marker in the source code to indicate that this is what we did) - Indent(indent); + Indent(indent, wr); wr.WriteLine("if (!false) { }"); } else { // let's compile the "then" branch - Indent(indent); + Indent(indent, wr); wr.WriteLine("if (true)"); - TrStmt(s.Thn, indent); + wr.Write(TrStmt(s.Thn, indent).ToString()); } } else { - Indent(indent); wr.Write("if ("); - TrExpr(s.IsExistentialGuard ? Translator.AlphaRename((ExistsExpr)s.Guard, "eg_d", new Translator(null)) : s.Guard); + Indent(indent, wr); wr.Write("if ("); + TrExpr(s.IsExistentialGuard ? Translator.AlphaRename((ExistsExpr)s.Guard, "eg_d", new Translator(null)) : s.Guard, wr, false); wr.WriteLine(")"); // 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.WriteLine("{"); + Indent(indent, wr); wr.WriteLine("{"); if (s.IsExistentialGuard) { - IntroduceAndAssignBoundVars(indent + IndentAmount, (ExistsExpr)s.Guard); + IntroduceAndAssignBoundVars(indent + IndentAmount, (ExistsExpr)s.Guard, wr); } - TrStmtList(s.Thn.Body, indent); - Indent(indent); wr.WriteLine("}"); + TrStmtList(s.Thn.Body, indent, wr); + Indent(indent, wr); wr.WriteLine("}"); if (s.Els != null) { - Indent(indent); wr.WriteLine("else"); - TrStmt(s.Els, indent); + Indent(indent, wr); wr.WriteLine("else"); + wr.Write(TrStmt(s.Els, indent).ToString()); } } } else if (stmt is AlternativeStmt) { var s = (AlternativeStmt)stmt; - Indent(indent); + 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); + 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); + IntroduceAndAssignBoundVars(indent + IndentAmount, (ExistsExpr)alternative.Guard, wr); } - TrStmtList(alternative.Body, indent); - Indent(indent); + TrStmtList(alternative.Body, indent, wr); + Indent(indent, wr); wr.Write("} else "); } wr.WriteLine("{ /*unreachable alternative*/ }"); @@ -1491,38 +1492,38 @@ namespace Microsoft.Dafny { } else if (stmt is WhileStmt) { WhileStmt s = (WhileStmt)stmt; if (s.Body == null) { - return; + return wr; } if (s.Guard == null) { - Indent(indent); + Indent(indent, wr); wr.WriteLine("while (false) { }"); } else { - Indent(indent); + Indent(indent, wr); wr.Write("while ("); - TrExpr(s.Guard); + TrExpr(s.Guard, wr, false); wr.WriteLine(")"); - TrStmt(s.Body, indent); + wr.Write(TrStmt(s.Body, indent).ToString()); } } else if (stmt is AlternativeLoopStmt) { var s = (AlternativeLoopStmt)stmt; if (s.Alternatives.Count != 0) { - Indent(indent); + Indent(indent, wr); wr.WriteLine("while (true) {"); int ind = indent + IndentAmount; foreach (var alternative in s.Alternatives) { } - Indent(ind); + Indent(ind, wr); foreach (var alternative in s.Alternatives) { wr.Write("if ("); - TrExpr(alternative.Guard); + TrExpr(alternative.Guard, wr, false); wr.WriteLine(") {"); - TrStmtList(alternative.Body, ind); - Indent(ind); + TrStmtList(alternative.Body, ind, wr); + Indent(ind, wr); wr.Write("} else "); } wr.WriteLine("{ break; }"); - Indent(indent); + Indent(indent, wr); wr.WriteLine("}"); } @@ -1530,17 +1531,17 @@ namespace Microsoft.Dafny { var s = (ForallStmt)stmt; if (s.Kind != ForallStmt.ParBodyKind.Assign) { // Call and Proof have no side effects, so they can simply be optimized away. - return; + return wr; } else if (s.BoundVars.Count == 0) { // the bound variables just spell out a single point, so the forall statement is equivalent to one execution of the body - TrStmt(s.Body, indent); - return; + wr.Write(TrStmt(s.Body, indent).ToString()); + return wr; } var s0 = (AssignStmt)s.S0; if (s0.Rhs is HavocRhs) { // The forall statement says to havoc a bunch of things. This can be efficiently compiled // into doing nothing. - return; + return wr; } var rhs = ((ExprRhs)s0.Rhs).Expr; @@ -1584,29 +1585,29 @@ namespace Microsoft.Dafny { if (s0.Lhs is MemberSelectExpr) { var lhs = (MemberSelectExpr)s0.Lhs; L = 2; - tupleTypeArgs = TypeName(lhs.Obj.Type); + tupleTypeArgs = TypeName(lhs.Obj.Type, wr); } else if (s0.Lhs is SeqSelectExpr) { var lhs = (SeqSelectExpr)s0.Lhs; L = 3; // note, we might as well do the BigInteger-to-int cast for array indices here, before putting things into the Tuple rather than when they are extracted from the Tuple - tupleTypeArgs = TypeName(lhs.Seq.Type) + ",int"; + tupleTypeArgs = TypeName(lhs.Seq.Type, wr) + ",int"; } else { var lhs = (MultiSelectExpr)s0.Lhs; L = 2 + lhs.Indices.Count; if (8 < L) { - Error("compiler currently does not support assignments to more-than-6-dimensional arrays in forall statements"); - return; + Error("compiler currently does not support assignments to more-than-6-dimensional arrays in forall statements", wr); + return wr; } - tupleTypeArgs = TypeName(lhs.Array.Type); + tupleTypeArgs = TypeName(lhs.Array.Type, wr); for (int i = 0; i < lhs.Indices.Count; i++) { // note, we might as well do the BigInteger-to-int cast for array indices here, before putting things into the Tuple rather than when they are extracted from the Tuple tupleTypeArgs += ",int"; } } - tupleTypeArgs += "," + TypeName(rhs.Type); + tupleTypeArgs += "," + TypeName(rhs.Type, wr); // declare and construct "ingredients" - Indent(indent); + Indent(indent, wr); wr.WriteLine("var {0} = new System.Collections.Generic.List>();", ingredients, tupleTypeArgs); var n = s.BoundVars.Count; @@ -1616,38 +1617,38 @@ namespace Microsoft.Dafny { var bound = s.Bounds[i]; var bv = s.BoundVars[i]; if (bound is ComprehensionExpr.BoolBoundedPool) { - Indent(ind); + Indent(ind, wr); wr.Write("foreach (var @{0} in Dafny.Helpers.AllBooleans) {{ ", bv.CompileName); } else if (bound is ComprehensionExpr.CharBoundedPool) { - Indent(ind); + 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); + 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); + TrExpr(b.LowerBound, wr, false); wr.Write(", "); - TrExpr(b.UpperBound); + TrExpr(b.UpperBound, wr, false); wr.Write(")) { "); } else if (bound is ComprehensionExpr.SetBoundedPool) { var b = (ComprehensionExpr.SetBoundedPool)bound; - Indent(ind); + Indent(ind, wr); wr.Write("foreach (var @{0} in (", bv.CompileName); - TrExpr(b.Set); + TrExpr(b.Set, wr, false); wr.Write(").Elements) { "); } else if (bound is ComprehensionExpr.SeqBoundedPool) { var b = (ComprehensionExpr.SeqBoundedPool)bound; - Indent(ind); + Indent(ind, wr); wr.Write("foreach (var @{0} in (", bv.CompileName); - TrExpr(b.Seq); + TrExpr(b.Seq, wr, false); wr.Write(").UniqueElements) { "); } else if (bound is ComprehensionExpr.DatatypeBoundedPool) { var b = (ComprehensionExpr.DatatypeBoundedPool)bound; - wr.Write("foreach (var @{0} in {1}.AllSingletonConstructors) {{", bv.CompileName, TypeName(bv.Type)); + wr.Write("foreach (var @{0} in {1}.AllSingletonConstructors) {{", bv.CompileName, TypeName(bv.Type, wr)); } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected BoundedPool type } @@ -1657,55 +1658,55 @@ namespace Microsoft.Dafny { // if (range) { // ingredients.Add(new L-Tuple( LHS0(w,x,y,z), LHS1(w,x,y,z), ..., RHS(w,x,y,z) )); // } - Indent(indent + n * IndentAmount); + Indent(indent + n * IndentAmount, wr); wr.Write("if ("); foreach (var bv in s.BoundVars) { if (bv.Type.NormalizeExpand() is NatType) { wr.Write("0 <= {0} && ", bv.CompileName); } } - TrExpr(s.Range); + TrExpr(s.Range, wr, false); wr.WriteLine(") {"); var indFinal = indent + (n + 1) * IndentAmount; - Indent(indFinal); + Indent(indFinal, wr); wr.Write("{0}.Add(new System.Tuple<{1}>(", ingredients, tupleTypeArgs); if (s0.Lhs is MemberSelectExpr) { var lhs = (MemberSelectExpr)s0.Lhs; - TrExpr(lhs.Obj); + TrExpr(lhs.Obj, wr, false); } else if (s0.Lhs is SeqSelectExpr) { var lhs = (SeqSelectExpr)s0.Lhs; - TrExpr(lhs.Seq); + TrExpr(lhs.Seq, wr, false); wr.Write(", (int)("); - TrExpr(lhs.E0); + TrExpr(lhs.E0, wr, false); wr.Write(")"); } else { var lhs = (MultiSelectExpr)s0.Lhs; - TrExpr(lhs.Array); + TrExpr(lhs.Array, wr, false); for (int i = 0; i < lhs.Indices.Count; i++) { wr.Write(", (int)("); - TrExpr(lhs.Indices[i]); + TrExpr(lhs.Indices[i], wr, false); wr.Write(")"); } } wr.Write(", "); - TrExpr(rhs); + TrExpr(rhs, wr, false); wr.WriteLine("));"); - Indent(indent + n * IndentAmount); + Indent(indent + n * IndentAmount, wr); wr.WriteLine("}"); for (int i = n; 0 <= --i; ) { - Indent(indent + i * IndentAmount); + Indent(indent + i * IndentAmount, wr); wr.WriteLine("}"); } // foreach (L-Tuple l in ingredients) { // LHS[ l0, l1, l2, ..., l(L-2) ] = l(L-1); // } - Indent(indent); + Indent(indent, wr); wr.WriteLine("foreach (var {0} in {1}) {{", tup, ingredients); - Indent(indent + IndentAmount); + Indent(indent + IndentAmount, wr); if (s0.Lhs is MemberSelectExpr) { var lhs = (MemberSelectExpr)s0.Lhs; wr.WriteLine("{0}.Item1.@{1} = {0}.Item2;", tup, lhs.MemberName); @@ -1722,7 +1723,7 @@ namespace Microsoft.Dafny { } wr.WriteLine("] = {0}.Item{1};", tup, L); } - Indent(indent); + Indent(indent, wr); wr.WriteLine("}"); } else if (stmt is MatchStmt) { @@ -1739,54 +1740,56 @@ namespace Microsoft.Dafny { // } if (s.Cases.Count != 0) { string source = idGenerator.FreshId("_source"); - Indent(indent); - wr.Write("{0} {1} = ", TypeName(cce.NonNull(s.Source.Type)), source); - TrExpr(s.Source); + Indent(indent, wr); + wr.Write("{0} {1} = ", TypeName(cce.NonNull(s.Source.Type), wr), source); + TrExpr(s.Source, wr, false); wr.WriteLine(";"); int i = 0; var sourceType = (UserDefinedType)s.Source.Type.NormalizeExpand(); foreach (MatchCaseStmt mc in s.Cases) { - MatchCasePrelude(source, sourceType, cce.NonNull(mc.Ctor), mc.Arguments, i, s.Cases.Count, indent); - TrStmtList(mc.Body, indent); + MatchCasePrelude(source, sourceType, cce.NonNull(mc.Ctor), mc.Arguments, i, s.Cases.Count, indent, wr); + TrStmtList(mc.Body, indent, wr); i++; } - Indent(indent); wr.WriteLine("}"); + Indent(indent, wr); wr.WriteLine("}"); } } else if (stmt is VarDeclStmt) { var s = (VarDeclStmt)stmt; foreach (var local in s.Locals) { - TrLocalVar(local, true, indent); + TrLocalVar(local, true, indent, wr); } if (s.Update != null) { - TrStmt(s.Update, indent); + wr.Write(TrStmt(s.Update, indent).ToString()); } } else if (stmt is ModifyStmt) { var s = (ModifyStmt)stmt; if (s.Body != null) { - TrStmt(s.Body, indent); + wr.Write(TrStmt(s.Body, indent).ToString()); } } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement } + + return wr; } - private void IntroduceAndAssignBoundVars(int indent, ExistsExpr exists) { + private void IntroduceAndAssignBoundVars(int indent, ExistsExpr exists, TextWriter wr) { Contract.Requires(0 <= indent); Contract.Requires(exists != null); Contract.Assume(exists.Bounds != null); // follows from successful resolution Contract.Assert(exists.Range == null); // follows from invariant of class IfStmt foreach (var bv in exists.BoundVars) { - TrLocalVar(bv, false, indent); + TrLocalVar(bv, false, indent, wr); } var ivars = exists.BoundVars.ConvertAll(bv => (IVariable)bv); - TrAssignSuchThat(indent, ivars, exists.Term, exists.Bounds, exists.tok.line); + TrAssignSuchThat(indent, ivars, exists.Term, exists.Bounds, exists.tok.line, wr, false); } - private void TrAssignSuchThat(int indent, List lhss, Expression constraint, List bounds, int debuginfoLine) { + private void TrAssignSuchThat(int indent, List lhss, Expression constraint, List bounds, int debuginfoLine, TextWriter wr, bool inLetExprBody) { Contract.Requires(0 <= indent); Contract.Requires(lhss != null); Contract.Requires(constraint != null); @@ -1828,7 +1831,7 @@ namespace Microsoft.Dafny { int ind = indent; bool needIterLimit = lhss.Count != 1 && bounds.Exists(bnd => !bnd.IsFinite); if (needIterLimit) { - Indent(indent); + Indent(indent, wr); wr.WriteLine("for (var {0} = new BigInteger(5); ; {0} *= 2) {{", iterLimit); ind += IndentAmount; } @@ -1837,11 +1840,11 @@ namespace Microsoft.Dafny { var bound = bounds[i]; var bv = lhss[i]; if (needIterLimit) { - Indent(ind); + Indent(ind, wr); wr.WriteLine("var {0}_{1} = {0};", iterLimit, i); } var tmpVar = idGenerator.FreshId("_assign_such_that_"); - Indent(ind); + Indent(ind, wr); if (bound is ComprehensionExpr.BoolBoundedPool) { wr.WriteLine("foreach (var {0} in Dafny.Helpers.AllBooleans) {{ @{1} = {0};", tmpVar, bv.CompileName); } else if (bound is ComprehensionExpr.CharBoundedPool) { @@ -1856,13 +1859,13 @@ namespace Microsoft.Dafny { if (b.LowerBound == null) { wr.Write("null"); } else { - TrExpr(b.LowerBound); + TrExpr(b.LowerBound, wr, inLetExprBody); } wr.Write(", "); if (b.UpperBound == null) { wr.Write("null"); } else { - TrExpr(b.UpperBound); + TrExpr(b.UpperBound, wr, inLetExprBody); } wr.WriteLine(")) {{ @{1} = {0};", tmpVar, bv.CompileName); } else if (bound is AssignSuchThatStmt.WiggleWaggleBound) { @@ -1870,54 +1873,54 @@ namespace Microsoft.Dafny { } else if (bound is ComprehensionExpr.SetBoundedPool) { var b = (ComprehensionExpr.SetBoundedPool)bound; wr.Write("foreach (var {0} in (", tmpVar); - TrExpr(b.Set); + TrExpr(b.Set, wr, inLetExprBody); wr.WriteLine(").Elements) {{ @{0} = {1};", bv.CompileName, tmpVar); } else if (bound is ComprehensionExpr.SubSetBoundedPool) { var b = (ComprehensionExpr.SubSetBoundedPool)bound; wr.Write("foreach (var {0} in (", tmpVar); - TrExpr(b.UpperBound); + TrExpr(b.UpperBound, wr, inLetExprBody); wr.WriteLine(").AllSubsets) {{ @{0} = {1};", bv.CompileName, tmpVar); } else if (bound is ComprehensionExpr.MapBoundedPool) { var b = (ComprehensionExpr.MapBoundedPool)bound; wr.Write("foreach (var {0} in (", tmpVar); - TrExpr(b.Map); + TrExpr(b.Map, wr, inLetExprBody); wr.WriteLine(").Domain) {{ @{0} = {1};", bv.CompileName, tmpVar); } else if (bound is ComprehensionExpr.SeqBoundedPool) { var b = (ComprehensionExpr.SeqBoundedPool)bound; wr.Write("foreach (var {0} in (", tmpVar); - TrExpr(b.Seq); + TrExpr(b.Seq, wr, inLetExprBody); wr.WriteLine(").Elements) {{ @{0} = {1};", bv.CompileName, tmpVar); } else if (bound is ComprehensionExpr.DatatypeBoundedPool) { var b = (ComprehensionExpr.DatatypeBoundedPool)bound; - wr.WriteLine("foreach (var {0} in {1}.AllSingletonConstructors) {{ @{2} = {0};", tmpVar, TypeName(bv.Type), bv.CompileName); + wr.WriteLine("foreach (var {0} in {1}.AllSingletonConstructors) {{ @{2} = {0};", tmpVar, TypeName(bv.Type, wr), bv.CompileName); } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected BoundedPool type } if (needIterLimit) { - Indent(ind + IndentAmount); + Indent(ind + IndentAmount, wr); wr.WriteLine("if ({0}_{1} == 0) {{ break; }} {0}_{1}--;", iterLimit, i); } } - Indent(ind); + Indent(ind, wr); wr.Write("if ("); - TrExpr(constraint); + TrExpr(constraint, wr, inLetExprBody); wr.WriteLine(") {"); - Indent(ind + IndentAmount); + Indent(ind + IndentAmount, wr); wr.WriteLine("goto {0};", doneLabel); - Indent(ind); + Indent(ind, wr); wr.WriteLine("}"); - Indent(indent); + Indent(indent, wr); for (int i = 0; i < n; i++) { wr.Write(i == 0 ? "}" : " }"); } wr.WriteLine(needIterLimit ? " }" : ""); - Indent(indent); + Indent(indent, wr); wr.WriteLine("throw new System.Exception(\"assign-such-that search produced no value (line {0})\");", debuginfoLine); - Indent(indent); + Indent(indent, wr); wr.WriteLine("{0}: ;", doneLabel); } - string CreateLvalue(Expression lhs, int indent) { + string CreateLvalue(Expression lhs, int indent, TextWriter wr) { lhs = lhs.Resolved; if (lhs is IdentifierExpr) { var ll = (IdentifierExpr)lhs; @@ -1925,9 +1928,9 @@ namespace Microsoft.Dafny { } else if (lhs is MemberSelectExpr) { var ll = (MemberSelectExpr)lhs; string obj = idGenerator.FreshId("_obj"); - Indent(indent); + Indent(indent, wr); wr.Write("var {0} = ", obj); - TrExpr(ll.Obj); + TrExpr(ll.Obj, wr, false); wr.WriteLine(";"); return string.Format("{0}.@{1}", obj, ll.Member.CompileName); } else if (lhs is SeqSelectExpr) { @@ -1935,31 +1938,31 @@ namespace Microsoft.Dafny { var c = idGenerator.FreshNumericId("_arr+_index"); string arr = "_arr" + c; string index = "_index" + c; - Indent(indent); + Indent(indent, wr); wr.Write("var {0} = ", arr); - TrExpr(ll.Seq); + TrExpr(ll.Seq, wr, false); wr.WriteLine(";"); - Indent(indent); + Indent(indent, wr); wr.Write("var {0} = ", index); - TrExpr(ll.E0); + TrExpr(ll.E0, wr, false); wr.WriteLine(";"); return string.Format("{0}[(int){1}]", arr, index); } else { var ll = (MultiSelectExpr)lhs; var c = idGenerator.FreshNumericId("_arr+_index"); string arr = "_arr" + c; - Indent(indent); + Indent(indent, wr); wr.Write("var {0} = ", arr); - TrExpr(ll.Array); + TrExpr(ll.Array, wr, false); wr.WriteLine(";"); string fullString = arr + "["; string sep = ""; int i = 0; foreach (var idx in ll.Indices) { string index = "_index" + i + "_" + c; - Indent(indent); + Indent(indent, wr); wr.Write("var {0} = ", index); - TrExpr(idx); + TrExpr(idx, wr, false); wr.WriteLine(";"); fullString += sep + "(int)" + index; sep = ", "; @@ -1969,21 +1972,21 @@ namespace Microsoft.Dafny { } } - void TrRhs(string target, Expression targetExpr, AssignmentRhs rhs, int indent) { + void TrRhs(string target, Expression targetExpr, AssignmentRhs rhs, int indent, TextWriter wr) { Contract.Requires((target == null) != (targetExpr == null)); var tRhs = rhs as TypeRhs; if (tRhs != null && tRhs.InitCall != null) { string nw = idGenerator.FreshId("_nw"); - Indent(indent); + Indent(indent, wr); wr.Write("var {0} = ", nw); - TrAssignmentRhs(rhs); // in this case, this call will not require us to spill any let variables first + TrAssignmentRhs(rhs, wr); // in this case, this call will not require us to spill any let variables first wr.WriteLine(";"); - TrCallStmt(tRhs.InitCall, nw, indent); - Indent(indent); + wr.Write(TrCallStmt(tRhs.InitCall, nw, indent).ToString()); + Indent(indent, wr); if (target != null) { wr.Write(target); } else { - TrExpr(targetExpr); + TrExpr(targetExpr, wr, false); } wr.WriteLine(" = {0};", nw); } else if (rhs is HavocRhs) { @@ -1994,22 +1997,23 @@ namespace Microsoft.Dafny { foreach (Expression dim in tRhs.ArrayDimensions) { } } - Indent(indent); + Indent(indent, wr); if (target != null) { wr.Write(target); } else { - TrExpr(targetExpr); + TrExpr(targetExpr, wr, false); } wr.Write(" = "); - TrAssignmentRhs(rhs); + TrAssignmentRhs(rhs, wr); wr.WriteLine(";"); } } - void TrCallStmt(CallStmt s, string receiverReplacement, int indent) { + TextWriter TrCallStmt(CallStmt s, string receiverReplacement, int indent) { Contract.Requires(s != null); Contract.Assert(s.Method != null); // follows from the fact that stmt has been successfully resolved + StringWriter wr = new StringWriter(); if (s.Method == enclosingMethod && enclosingMethod.IsTailRecursive) { // compile call as tail-recursive @@ -2026,9 +2030,9 @@ namespace Microsoft.Dafny { string inTmp = idGenerator.FreshId("_in"); inTmps.Add(inTmp); - Indent(indent); + Indent(indent, wr); wr.Write("var {0} = ", inTmp); - TrExpr(s.Receiver); + TrExpr(s.Receiver, wr, false); wr.WriteLine(";"); } for (int i = 0; i < s.Method.Ins.Count; i++) { @@ -2036,29 +2040,29 @@ namespace Microsoft.Dafny { if (!p.IsGhost) { string inTmp = idGenerator.FreshId("_in"); inTmps.Add(inTmp); - Indent(indent); + Indent(indent, wr); wr.Write("var {0} = ", inTmp); - TrExpr(s.Args[i]); + TrExpr(s.Args[i], wr, false); wr.WriteLine(";"); } } // Now, assign to the formals int n = 0; if (!s.Method.IsStatic) { - Indent(indent); + Indent(indent, wr); wr.WriteLine("_this = {0};", inTmps[n]); n++; } foreach (var p in s.Method.Ins) { if (!p.IsGhost) { - Indent(indent); + Indent(indent, wr); wr.WriteLine("{0} = {1};", p.CompileName, inTmps[n]); n++; } } Contract.Assert(n == inTmps.Count); // finally, the jump back to the head of the method - Indent(indent); + Indent(indent, wr); wr.WriteLine("goto TAIL_CALL_START;"); } else { @@ -2069,7 +2073,7 @@ namespace Microsoft.Dafny { for (int i = 0; i < s.Method.Outs.Count; i++) { Formal p = s.Method.Outs[i]; if (!p.IsGhost) { - lvalues.Add(CreateLvalue(s.Lhs[i], indent)); + lvalues.Add(CreateLvalue(s.Lhs[i], indent, wr)); } } var outTmps = new List(); @@ -2078,8 +2082,8 @@ namespace Microsoft.Dafny { if (!p.IsGhost) { string target = idGenerator.FreshId("_out"); outTmps.Add(target); - Indent(indent); - wr.WriteLine("{0} {1};", TypeName(s.Lhs[i].Type), target); + Indent(indent, wr); + wr.WriteLine("{0} {1};", TypeName(s.Lhs[i].Type, wr), target); } } Contract.Assert(lvalues.Count == outTmps.Count); @@ -2090,14 +2094,14 @@ namespace Microsoft.Dafny { } } if (receiverReplacement != null) { - Indent(indent); + Indent(indent, wr); wr.Write("@" + receiverReplacement); } else if (s.Method.IsStatic) { - Indent(indent); - wr.Write(TypeName_Companion(s.Receiver.Type)); + Indent(indent, wr); + wr.Write(TypeName_Companion(s.Receiver.Type, wr)); } else { - Indent(indent); - TrParenExpr(s.Receiver); + Indent(indent, wr); + TrParenExpr(s.Receiver, wr, false); } wr.Write(".@{0}(", s.Method.CompileName); @@ -2106,7 +2110,7 @@ namespace Microsoft.Dafny { Formal p = s.Method.Ins[i]; if (!p.IsGhost) { wr.Write(sep); - TrExpr(s.Args[i]); + TrExpr(s.Args[i], wr, false); sep = ", "; } } @@ -2119,44 +2123,45 @@ namespace Microsoft.Dafny { // assign to the actual LHSs for (int j = 0; j < lvalues.Count; j++) { - Indent(indent); + Indent(indent, wr); wr.WriteLine("{0} = {1};", lvalues[j], outTmps[j]); } } + return wr; } /// /// Before calling TrAssignmentRhs(rhs), the caller must have spilled the let variables declared in "rhs". /// - void TrAssignmentRhs(AssignmentRhs rhs) { + void TrAssignmentRhs(AssignmentRhs rhs, TextWriter wr) { Contract.Requires(rhs != null); Contract.Requires(!(rhs is HavocRhs)); if (rhs is ExprRhs) { ExprRhs e = (ExprRhs)rhs; - TrExpr(e.Expr); + TrExpr(e.Expr, wr, false); } else { TypeRhs tp = (TypeRhs)rhs; if (tp.ArrayDimensions == null) { - wr.Write("new {0}()", TypeName(tp.EType)); + wr.Write("new {0}()", TypeName(tp.EType, wr)); } else { if (tp.EType.IsIntegerType || tp.EType.IsTypeParameter) { // Because the default constructor for BigInteger does not generate a valid BigInteger, we have // to excplicitly initialize the elements of an integer array. This is all done in a helper routine. - wr.Write("Dafny.Helpers.InitNewArray{0}<{1}>", tp.ArrayDimensions.Count, TypeName(tp.EType)); + wr.Write("Dafny.Helpers.InitNewArray{0}<{1}>", tp.ArrayDimensions.Count, TypeName(tp.EType, wr)); string prefix = "("; foreach (Expression dim in tp.ArrayDimensions) { wr.Write(prefix); - TrParenExpr(dim); + TrParenExpr(dim, wr, false); prefix = ", "; } wr.Write(")"); } else { - wr.Write("new {0}", TypeName(tp.EType)); + wr.Write("new {0}", TypeName(tp.EType, wr)); string prefix = "["; foreach (Expression dim in tp.ArrayDimensions) { wr.Write("{0}(int)", prefix); - TrParenExpr(dim); + TrParenExpr(dim, wr, false); prefix = ", "; } wr.Write("]"); @@ -2165,34 +2170,43 @@ namespace Microsoft.Dafny { } } - void TrStmtList(List/*!*/ stmts, int indent) {Contract.Requires(cce.NonNullElements(stmts)); + void TrStmtList(List/*!*/ stmts, int indent, TextWriter writer) {Contract.Requires(cce.NonNullElements(stmts)); foreach (Statement ss in stmts) { - TrStmt(ss, indent + IndentAmount); + copyInstrWriter.Clear(); + TextWriter wr = TrStmt(ss, indent + IndentAmount); + // write out any copy instructions that copies the out param + // used in letexpr to a local + string copyInstr = copyInstrWriter.ToString(); + if (copyInstr != "") { + Indent(indent + IndentAmount, writer); + writer.Write(copyInstrWriter.ToString()); + } + writer.Write(wr.ToString()); if (ss.Labels != null) { - Indent(indent); // labels are not indented as much as the statements - wr.WriteLine("after_{0}: ;", ss.Labels.Data.AssignUniqueId("after_", idGenerator)); + Indent(indent, writer); // labels are not indented as much as the statements + writer.WriteLine("after_{0}: ;", ss.Labels.Data.AssignUniqueId("after_", idGenerator)); } } } - void TrLocalVar(IVariable v, bool alwaysInitialize, int indent) { + void TrLocalVar(IVariable v, bool alwaysInitialize, int indent, TextWriter wr) { Contract.Requires(v != null); if (v.IsGhost) { // only emit non-ghosts (we get here only for local variables introduced implicitly by call statements) return; } - Indent(indent); - wr.Write("{0} @{1}", TypeName(v.Type), v.CompileName); + Indent(indent, wr); + wr.Write("{0} @{1}", TypeName(v.Type, wr), v.CompileName); if (alwaysInitialize) { // produce a default value - wr.WriteLine(" = {0};", DefaultValue(v.Type)); + wr.WriteLine(" = {0};", DefaultValue(v.Type, wr)); } else { wr.WriteLine(";"); } } - void MatchCasePrelude(string source, UserDefinedType sourceType, DatatypeCtor ctor, List/*!*/ arguments, int caseIndex, int caseCount, int indent) { + void MatchCasePrelude(string source, UserDefinedType sourceType, DatatypeCtor ctor, List/*!*/ arguments, int caseIndex, int caseCount, int indent, TextWriter wr) { Contract.Requires(source != null); Contract.Requires(sourceType != null); Contract.Requires(ctor != null); @@ -2201,7 +2215,7 @@ namespace Microsoft.Dafny { // if (source.is_Ctor0) { // FormalType f0 = ((Dt_Ctor0)source._D).a0; // ... - Indent(indent); + Indent(indent, wr); wr.Write("{0}if (", caseIndex == 0 ? "" : "} else "); if (caseIndex == caseCount - 1) { wr.Write("true"); @@ -2216,9 +2230,9 @@ namespace Microsoft.Dafny { if (!arg.IsGhost) { BoundVar bv = arguments[m]; // FormalType f0 = ((Dt_Ctor0)source._D).a0; - Indent(indent + IndentAmount); + Indent(indent + IndentAmount, wr); wr.WriteLine("{0} @{1} = (({2}){3}._D).@{4};", - TypeName(bv.Type), bv.CompileName, DtCtorName(ctor, sourceType.TypeArgs), source, FormalName(arg, k)); + TypeName(bv.Type, wr), bv.CompileName, DtCtorName(ctor, sourceType.TypeArgs, wr), source, FormalName(arg, k)); k++; } } @@ -2229,51 +2243,51 @@ namespace Microsoft.Dafny { /// /// Before calling TrParenExpr(expr), the caller must have spilled the let variables declared in "expr". /// - void TrParenExpr(string prefix, Expression expr) { + void TrParenExpr(string prefix, Expression expr, TextWriter wr, bool inLetExprBody) { Contract.Requires(prefix != null); Contract.Requires(expr != null); wr.Write(prefix); - TrParenExpr(expr); + TrParenExpr(expr, wr, inLetExprBody); } /// /// Before calling TrParenExpr(expr), the caller must have spilled the let variables declared in "expr". /// - void TrParenExpr(Expression expr) { + void TrParenExpr(Expression expr, TextWriter wr, bool inLetExprBody) { Contract.Requires(expr != null); wr.Write("("); - TrExpr(expr); + TrExpr(expr, wr, inLetExprBody); wr.Write(")"); } /// /// Before calling TrExprList(exprs), the caller must have spilled the let variables declared in expressions in "exprs". /// - void TrExprList(List/*!*/ exprs) { + void TrExprList(List/*!*/ exprs, TextWriter wr, bool inLetExprBody) { Contract.Requires(cce.NonNullElements(exprs)); wr.Write("("); string sep = ""; foreach (Expression e in exprs) { wr.Write(sep); - TrExpr(e); + TrExpr(e, wr, inLetExprBody); sep = ", "; } wr.Write(")"); } - void TrExprPairList(List/*!*/ exprs) { + void TrExprPairList(List/*!*/ exprs, TextWriter wr, bool inLetExprBody) { Contract.Requires(cce.NonNullElements(exprs)); wr.Write("("); string sep = ""; foreach (ExpressionPair p in exprs) { wr.Write(sep); wr.Write("new Dafny.Pair<"); - wr.Write(TypeName(p.A.Type)); + wr.Write(TypeName(p.A.Type, wr)); wr.Write(","); - wr.Write(TypeName(p.B.Type)); + wr.Write(TypeName(p.B.Type, wr)); wr.Write(">("); - TrExpr(p.A); + TrExpr(p.A, wr, inLetExprBody); wr.Write(","); - TrExpr(p.B); + TrExpr(p.B, wr, inLetExprBody); wr.Write(")"); sep = ", "; } @@ -2283,13 +2297,13 @@ namespace Microsoft.Dafny { /// /// Before calling TrExpr(expr), the caller must have spilled the let variables declared in "expr". /// - void TrExpr(Expression expr) + void TrExpr(Expression expr, TextWriter wr, bool inLetExprBody) { Contract.Requires(expr != null); if (expr is LiteralExpr) { LiteralExpr e = (LiteralExpr)expr; if (e.Value == null) { - wr.Write("({0})null", TypeName(e.Type)); + wr.Write("({0})null", TypeName(e.Type, wr)); } else if (e.Value is bool) { wr.Write((bool)e.Value ? "true" : "false"); } else if (e is CharLiteralExpr) { @@ -2330,41 +2344,49 @@ namespace Microsoft.Dafny { } else if (expr is IdentifierExpr) { var e = (IdentifierExpr)expr; - wr.Write("@" + e.Var.CompileName); - + if (e.Var is Formal && inLetExprBody && !((Formal)e.Var).InParam) { + // out param in letExpr body, need to copy it to a temp since + // letExpr body is translated to an anonymous function that doesn't + // allow out parameters + var name = string.Format("_pat_let_tv{0}", GetUniqueAstNumber(e)); + wr.Write("@" + name); + copyInstrWriter.Append("var @" + name + "= @" + e.Var.CompileName + ";\n"); + } else { + wr.Write("@" + e.Var.CompileName); + } } else if (expr is SetDisplayExpr) { var e = (SetDisplayExpr)expr; var elType = e.Type.AsSetType.Arg; - wr.Write("{0}<{1}>.FromElements", DafnySetClass, TypeName(elType)); - TrExprList(e.Elements); + wr.Write("{0}<{1}>.FromElements", DafnySetClass, TypeName(elType, wr)); + TrExprList(e.Elements, wr, inLetExprBody); } else if (expr is MultiSetDisplayExpr) { var e = (MultiSetDisplayExpr)expr; var elType = e.Type.AsMultiSetType.Arg; - wr.Write("{0}<{1}>.FromElements", DafnyMultiSetClass, TypeName(elType)); - TrExprList(e.Elements); + wr.Write("{0}<{1}>.FromElements", DafnyMultiSetClass, TypeName(elType, wr)); + TrExprList(e.Elements, wr, inLetExprBody); } else if (expr is SeqDisplayExpr) { var e = (SeqDisplayExpr)expr; var elType = e.Type.AsSeqType.Arg; - wr.Write("{0}<{1}>.FromElements", DafnySeqClass, TypeName(elType)); - TrExprList(e.Elements); + wr.Write("{0}<{1}>.FromElements", DafnySeqClass, TypeName(elType, wr)); + TrExprList(e.Elements, wr, inLetExprBody); } else if (expr is MapDisplayExpr) { MapDisplayExpr e = (MapDisplayExpr)expr; - wr.Write("{0}.FromElements", TypeName(e.Type)); - TrExprPairList(e.Elements); + wr.Write("{0}.FromElements", TypeName(e.Type, wr)); + TrExprPairList(e.Elements, wr, inLetExprBody); } else if (expr is MemberSelectExpr) { MemberSelectExpr e = (MemberSelectExpr)expr; SpecialField sf = e.Member as SpecialField; if (sf != null) { wr.Write(sf.PreString); - TrParenExpr(e.Obj); + TrParenExpr(e.Obj, wr, inLetExprBody); wr.Write(".@{0}", sf.CompiledName); wr.Write(sf.PostString); } else { - TrParenExpr(e.Obj); + TrParenExpr(e.Obj, wr, inLetExprBody); wr.Write(".@{0}", e.Member.CompileName); } @@ -2374,50 +2396,50 @@ namespace Microsoft.Dafny { if (e.Seq.Type.IsArrayType) { if (e.SelectOne) { Contract.Assert(e.E0 != null && e.E1 == null); - TrParenExpr(e.Seq); + TrParenExpr(e.Seq, wr, inLetExprBody); wr.Write("[(int)"); - TrParenExpr(e.E0); + TrParenExpr(e.E0, wr, inLetExprBody); wr.Write("]"); } else { - TrParenExpr("Dafny.Helpers.SeqFromArray", e.Seq); + TrParenExpr("Dafny.Helpers.SeqFromArray", e.Seq, wr, inLetExprBody); if (e.E1 != null) { - TrParenExpr(".Take", e.E1); + TrParenExpr(".Take", e.E1, wr, inLetExprBody); } if (e.E0 != null) { - TrParenExpr(".Drop", e.E0); + TrParenExpr(".Drop", e.E0, wr, inLetExprBody); } } } else if (e.SelectOne) { Contract.Assert(e.E0 != null && e.E1 == null); - TrParenExpr(e.Seq); - TrParenExpr(".Select", e.E0); + TrParenExpr(e.Seq, wr, inLetExprBody); + TrParenExpr(".Select", e.E0, wr, inLetExprBody); } else { - TrParenExpr(e.Seq); + TrParenExpr(e.Seq, wr, inLetExprBody); if (e.E1 != null) { - TrParenExpr(".Take", e.E1); + TrParenExpr(".Take", e.E1, wr, inLetExprBody); } if (e.E0 != null) { - TrParenExpr(".Drop", e.E0); + TrParenExpr(".Drop", e.E0, wr, inLetExprBody); } } } else if (expr is MultiSetFormingExpr) { var e = (MultiSetFormingExpr)expr; - wr.Write("{0}<{1}>", DafnyMultiSetClass, TypeName(e.E.Type.AsCollectionType.Arg)); + wr.Write("{0}<{1}>", DafnyMultiSetClass, TypeName(e.E.Type.AsCollectionType.Arg, wr)); var eeType = e.E.Type.NormalizeExpand(); if (eeType is SeqType) { - TrParenExpr(".FromSeq", e.E); + TrParenExpr(".FromSeq", e.E, wr, inLetExprBody); } else if (eeType is SetType) { - TrParenExpr(".FromSet", e.E); + TrParenExpr(".FromSet", e.E, wr, inLetExprBody); } else { Contract.Assert(false); throw new cce.UnreachableException(); } } else if (expr is MultiSelectExpr) { MultiSelectExpr e = (MultiSelectExpr)expr; - TrParenExpr(e.Array); + TrParenExpr(e.Array, wr, inLetExprBody); string prefix = "["; foreach (Expression idx in e.Indices) { wr.Write("{0}(int)", prefix); - TrParenExpr(idx); + TrParenExpr(idx, wr, inLetExprBody); prefix = ", "; } wr.Write("]"); @@ -2426,47 +2448,47 @@ namespace Microsoft.Dafny { SeqUpdateExpr e = (SeqUpdateExpr)expr; if (e.ResolvedUpdateExpr != null) { - TrExpr(e.ResolvedUpdateExpr); + TrExpr(e.ResolvedUpdateExpr, wr, inLetExprBody); } else { - TrParenExpr(e.Seq); + TrParenExpr(e.Seq, wr, inLetExprBody); wr.Write(".Update("); - TrExpr(e.Index); + TrExpr(e.Index, wr, inLetExprBody); wr.Write(", "); - TrExpr(e.Value); + TrExpr(e.Value, wr, inLetExprBody); wr.Write(")"); } } else if (expr is FunctionCallExpr) { FunctionCallExpr e = (FunctionCallExpr)expr; - CompileFunctionCallExpr(e, wr, TrExpr); + CompileFunctionCallExpr(e, wr, wr, inLetExprBody, TrExpr); } else if (expr is ApplyExpr) { var e = expr as ApplyExpr; wr.Write("Dafny.Helpers.Id<"); - wr.Write(TypeName(e.Function.Type)); + wr.Write(TypeName(e.Function.Type, wr)); wr.Write(">("); - TrExpr(e.Function); + TrExpr(e.Function, wr, inLetExprBody); wr.Write(")"); - TrExprList(e.Args); + TrExprList(e.Args, wr, inLetExprBody); } else if (expr is DatatypeValue) { DatatypeValue dtv = (DatatypeValue)expr; Contract.Assert(dtv.Ctor != null); // since dtv has been successfully resolved - var typeParams = dtv.InferredTypeArgs.Count == 0 ? "" : string.Format("<{0}>", TypeNames(dtv.InferredTypeArgs)); + var typeParams = dtv.InferredTypeArgs.Count == 0 ? "" : string.Format("<{0}>", TypeNames(dtv.InferredTypeArgs, wr)); wr.Write("new {0}{1}(", DtName(dtv.Ctor.EnclosingDatatype), typeParams); if (!dtv.IsCoCall) { // For an ordinary constructor (that is, one that does not guard any co-recursive calls), generate: // new Dt_Cons( args ) - wr.Write("new {0}(", DtCtorName(dtv.Ctor, dtv.InferredTypeArgs)); + wr.Write("new {0}(", DtCtorName(dtv.Ctor, dtv.InferredTypeArgs, wr)); string sep = ""; for (int i = 0; i < dtv.Arguments.Count; i++) { Formal formal = dtv.Ctor.Formals[i]; if (!formal.IsGhost) { wr.Write(sep); - TrExpr(dtv.Arguments[i]); + TrExpr(dtv.Arguments[i], wr, inLetExprBody); sep = ", "; } } @@ -2495,17 +2517,17 @@ namespace Microsoft.Dafny { arg = varName; wr.Write("var {0} = ", varName); - TrExpr(actual); + TrExpr(actual, wr, inLetExprBody); wr.Write("; "); } else { var sw = new StringWriter(); - CompileFunctionCallExpr(fce, sw, (exp) => { + CompileFunctionCallExpr(fce, sw, wr, inLetExprBody, (exp, wrr, inLetExpr) => { string varName = idGenerator.FreshId("_ac"); sw.Write(varName); - wr.Write("var {0} = ", varName); - TrExpr(exp); - wr.Write("; "); + wrr.Write("var {0} = ", varName); + TrExpr(exp, wrr, inLetExpr); + wrr.Write("; "); }); arg = sw.ToString(); @@ -2517,7 +2539,7 @@ namespace Microsoft.Dafny { wr.Write("return () => { return "); - wr.Write("new {0}({1}", DtCtorName(dtv.Ctor, dtv.InferredTypeArgs), args); + wr.Write("new {0}({1}", DtCtorName(dtv.Ctor, dtv.InferredTypeArgs, wr), args); wr.Write("); }; })())"); } wr.Write(")"); @@ -2530,11 +2552,11 @@ namespace Microsoft.Dafny { switch (e.Op) { case UnaryOpExpr.Opcode.Not: wr.Write("!"); - TrParenExpr(e.E); + TrParenExpr(e.E, wr, inLetExprBody); break; case UnaryOpExpr.Opcode.Cardinality: wr.Write("new BigInteger("); - TrParenExpr(e.E); + TrParenExpr(e.E, wr, inLetExprBody); wr.Write(".Length)"); break; default: @@ -2552,7 +2574,7 @@ namespace Microsoft.Dafny { if (AsNativeType(e.E.Type) != null) { wr.Write("new BigInteger"); } - TrParenExpr(e.E); + TrParenExpr(e.E, wr, inLetExprBody); }; Action toIntCast = () => { Contract.Assert(toInt); @@ -2568,7 +2590,7 @@ namespace Microsoft.Dafny { } else if (!fromInt && toInt) { // real -> int toIntCast(); - TrParenExpr(e.E); + TrParenExpr(e.E, wr, inLetExprBody); wr.Write(".ToBigInteger()"); } else if (AsNativeType(e.ToType) != null) { toIntCast(); @@ -2580,14 +2602,14 @@ namespace Microsoft.Dafny { wr.Write("(" + (BigInteger)lit.Value + AsNativeType(e.ToType).Suffix + ")"); } else if ((u != null && u.Op == UnaryOpExpr.Opcode.Cardinality) || (m != null && m.MemberName == "Length" && m.Obj.Type.IsArrayType)) { // Optimize .Length to avoid intermediate BigInteger - TrParenExpr((u != null) ? u.E : m.Obj); + TrParenExpr((u != null) ? u.E : m.Obj, wr, inLetExprBody); if (AsNativeType(e.ToType).UpperBound <= new BigInteger(0x80000000U)) { wr.Write(".Length"); } else { wr.Write(".LongLength"); } } else { - TrParenExpr(e.E); + TrParenExpr(e.E, wr, inLetExprBody); } } else if (e.ToType.IsIntegerType && AsNativeType(e.E.Type) != null) { fromIntAsBigInteger(); @@ -2595,7 +2617,7 @@ namespace Microsoft.Dafny { Contract.Assert(fromInt == toInt); Contract.Assert(AsNativeType(e.ToType) == null); Contract.Assert(AsNativeType(e.E.Type) == null); - TrParenExpr(e.E); + TrParenExpr(e.E, wr, inLetExprBody); } } else if (expr is BinaryExpr) { @@ -2664,9 +2686,9 @@ namespace Microsoft.Dafny { if (expr.Type.IsIntegerType || (AsNativeType(expr.Type) != null && AsNativeType(expr.Type).LowerBound < BigInteger.Zero)) { string suffix = AsNativeType(expr.Type) != null ? ("_" + AsNativeType(expr.Type).Name) : ""; wr.Write("Dafny.Helpers.EuclideanDivision" + suffix + "("); - TrParenExpr(e.E0); + TrParenExpr(e.E0, wr, inLetExprBody); wr.Write(", "); - TrExpr(e.E1); + TrExpr(e.E1, wr, inLetExprBody); wr.Write(")"); } else { opString = "/"; // for reals @@ -2676,9 +2698,9 @@ namespace Microsoft.Dafny { if (expr.Type.IsIntegerType || (AsNativeType(expr.Type) != null && AsNativeType(expr.Type).LowerBound < BigInteger.Zero)) { string suffix = AsNativeType(expr.Type) != null ? ("_" + AsNativeType(expr.Type).Name) : ""; wr.Write("Dafny.Helpers.EuclideanModulus" + suffix + "("); - TrParenExpr(e.E0); + TrParenExpr(e.E0, wr, inLetExprBody); wr.Write(", "); - TrExpr(e.E1); + TrExpr(e.E1, wr, inLetExprBody); wr.Write(")"); } else { opString = "%"; // for reals @@ -2713,18 +2735,18 @@ namespace Microsoft.Dafny { case BinaryExpr.ResolvedOpcode.InSet: case BinaryExpr.ResolvedOpcode.InMultiSet: case BinaryExpr.ResolvedOpcode.InMap: - TrParenExpr(e.E1); + TrParenExpr(e.E1, wr, inLetExprBody); wr.Write(".Contains("); - TrExpr(e.E0); + TrExpr(e.E0, wr, inLetExprBody); wr.Write(")"); break; case BinaryExpr.ResolvedOpcode.NotInSet: case BinaryExpr.ResolvedOpcode.NotInMultiSet: case BinaryExpr.ResolvedOpcode.NotInMap: wr.Write("!"); - TrParenExpr(e.E1); + TrParenExpr(e.E1, wr, inLetExprBody); wr.Write(".Contains("); - TrExpr(e.E0); + TrExpr(e.E0, wr, inLetExprBody); wr.Write(")"); break; case BinaryExpr.ResolvedOpcode.Union: @@ -2744,16 +2766,16 @@ namespace Microsoft.Dafny { case BinaryExpr.ResolvedOpcode.Concat: callString = "Concat"; break; case BinaryExpr.ResolvedOpcode.InSeq: - TrParenExpr(e.E1); + TrParenExpr(e.E1, wr, inLetExprBody); wr.Write(".Contains("); - TrExpr(e.E0); + TrExpr(e.E0, wr, inLetExprBody); wr.Write(")"); break; case BinaryExpr.ResolvedOpcode.NotInSeq: wr.Write("!"); - TrParenExpr(e.E1); + TrParenExpr(e.E1, wr, inLetExprBody); wr.Write(".Contains("); - TrExpr(e.E0); + TrExpr(e.E0, wr, inLetExprBody); wr.Write(")"); break; @@ -2767,17 +2789,17 @@ namespace Microsoft.Dafny { wr.Write("(" + nativeType.Name + ")("); } wr.Write(preOpString); - TrParenExpr(e.E0); + TrParenExpr(e.E0, wr, inLetExprBody); wr.Write(" {0} ", opString); - TrParenExpr(e.E1); + TrParenExpr(e.E1, wr, inLetExprBody); if (needsCast) { wr.Write(")"); } } else if (callString != null) { wr.Write(preOpString); - TrParenExpr(e.E0); + TrParenExpr(e.E0, wr, inLetExprBody); wr.Write(".@{0}(", callString); - TrExpr(e.E1); + TrExpr(e.E1, wr, inLetExprBody); wr.Write(")"); } @@ -2800,17 +2822,18 @@ namespace Microsoft.Dafny { if (Contract.Exists(lhs.Vars, bv => !bv.IsGhost)) { var rhsName = string.Format("_pat_let{0}_{1}", GetUniqueAstNumber(e), i); wr.Write("Dafny.Helpers.Let<"); - wr.Write(TypeName(e.RHSs[i].Type) + "," + TypeName(e.Body.Type)); + wr.Write(TypeName(e.RHSs[i].Type, wr) + "," + TypeName(e.Body.Type, wr)); wr.Write(">("); - TrExpr(e.RHSs[i]); + TrExpr(e.RHSs[i], wr, inLetExprBody); wr.Write(", " + rhsName + " => "); neededCloseParens++; - var c = TrCasePattern(lhs, rhsName, e.Body.Type); + var c = TrCasePattern(lhs, rhsName, e.Body.Type, wr); Contract.Assert(c != 0); // we already checked that there's at least one non-ghost neededCloseParens += c; } } - TrExpr(e.Body); + + TrExpr(e.Body, wr, true); for (int i = 0; i < neededCloseParens; i++) { wr.Write(")"); } @@ -2819,7 +2842,7 @@ namespace Microsoft.Dafny { // ghost var x,y :| Constraint; E // is compiled just like E is, because the resolver has already checked that x,y (or other ghost variables, for that matter) don't // occur in E (moreover, the verifier has checked that values for x,y satisfying Constraint exist). - TrExpr(e.Body); + TrExpr(e.Body, wr, inLetExprBody); } else { // The Dafny "let" expression // var x,y :| Constraint; E @@ -2834,17 +2857,17 @@ namespace Microsoft.Dafny { Contract.Assert(e.RHSs.Count == 1); // checked by resolution if (e.Constraint_MissingBounds != null) { foreach (var bv in e.Constraint_MissingBounds) { - Error("this let-such-that expression is too advanced for the current compiler; Dafny's heuristics cannot find any bound for variable '{0}' (line {1})", bv.Name, e.tok.line); + Error("this let-such-that expression is too advanced for the current compiler; Dafny's heuristics cannot find any bound for variable '{0}' (line {1})", wr, bv.Name, e.tok.line); } } else { - wr.Write("Dafny.Helpers.Let(0, _let_dummy_" + GetUniqueAstNumber(e) + " => {"); + wr.Write("Dafny.Helpers.Let(0, _let_dummy_" + GetUniqueAstNumber(e) + " => {"); foreach (var bv in e.BoundVars) { - wr.Write("{0} @{1}", TypeName(bv.Type), bv.CompileName); - wr.WriteLine(" = {0};", DefaultValue(bv.Type)); + wr.Write("{0} @{1}", TypeName(bv.Type, wr), bv.CompileName); + wr.WriteLine(" = {0};", DefaultValue(bv.Type, wr)); } - TrAssignSuchThat(0, new List(e.BoundVars).ConvertAll(bv => (IVariable)bv), e.RHSs[0], e.Constraint_Bounds, e.tok.line); + TrAssignSuchThat(0, new List(e.BoundVars).ConvertAll(bv => (IVariable)bv), e.RHSs[0], e.Constraint_Bounds, e.tok.line, wr, inLetExprBody); wr.Write(" return "); - TrExpr(e.Body); + TrExpr(e.Body, wr, true); wr.Write("; })"); } } @@ -2864,7 +2887,7 @@ namespace Microsoft.Dafny { // }(src) string source = idGenerator.FreshId("_source"); - wr.Write("new Dafny.Helpers.Function<{0}, {1}>(delegate ({0} {2}) {{ ", TypeName(e.Source.Type), TypeName(e.Type), source); + wr.Write("new Dafny.Helpers.Function<{0}, {1}>(delegate ({0} {2}) {{ ", TypeName(e.Source.Type, wr), TypeName(e.Type, wr), source); if (e.Cases.Count == 0) { // the verifier would have proved we never get here; still, we need some code that will compile @@ -2873,9 +2896,9 @@ namespace Microsoft.Dafny { int i = 0; var sourceType = (UserDefinedType)e.Source.Type.NormalizeExpand(); foreach (MatchCaseExpr mc in e.Cases) { - MatchCasePrelude(source, sourceType, cce.NonNull(mc.Ctor), mc.Arguments, i, e.Cases.Count, 0); + MatchCasePrelude(source, sourceType, cce.NonNull(mc.Ctor), mc.Arguments, i, e.Cases.Count, 0, wr); wr.Write("return "); - TrExpr(mc.Body); + TrExpr(mc.Body, wr, inLetExprBody); wr.Write("; "); i++; } @@ -2883,7 +2906,7 @@ namespace Microsoft.Dafny { } // We end with applying the source expression to the delegate we just built wr.Write("})("); - TrExpr(e.Source); + TrExpr(e.Source, wr, inLetExprBody); wr.Write(")"); } else if (expr is QuantifierExpr) { @@ -2905,24 +2928,24 @@ namespace Microsoft.Dafny { } else if (bound is ComprehensionExpr.IntBoundedPool) { var b = (ComprehensionExpr.IntBoundedPool)bound; wr.Write("Dafny.Helpers.QuantInt("); - TrExpr(b.LowerBound); + TrExpr(b.LowerBound, wr, inLetExprBody); wr.Write(", "); - TrExpr(b.UpperBound); + TrExpr(b.UpperBound, wr, inLetExprBody); wr.Write(", "); } else if (bound is ComprehensionExpr.SetBoundedPool) { var b = (ComprehensionExpr.SetBoundedPool)bound; wr.Write("Dafny.Helpers.QuantSet("); - TrExpr(b.Set); + TrExpr(b.Set, wr, inLetExprBody); wr.Write(", "); } else if (bound is ComprehensionExpr.MapBoundedPool) { var b = (ComprehensionExpr.MapBoundedPool)bound; wr.Write("Dafny.Helpers.QuantMap("); - TrExpr(b.Map); + TrExpr(b.Map, wr, inLetExprBody); wr.Write(", "); } else if (bound is ComprehensionExpr.SeqBoundedPool) { var b = (ComprehensionExpr.SeqBoundedPool)bound; wr.Write("Dafny.Helpers.QuantSeq("); - TrExpr(b.Seq); + TrExpr(b.Seq, wr, inLetExprBody); wr.Write(", "); } else if (bound is ComprehensionExpr.DatatypeBoundedPool) { var b = (ComprehensionExpr.DatatypeBoundedPool)bound; @@ -2935,7 +2958,7 @@ namespace Microsoft.Dafny { wr.Write("{0}, ", expr is ForallExpr ? "true" : "false"); wr.Write("@{0} => ", bv.CompileName); } - TrExpr(e.LogicalBody(true)); + TrExpr(e.LogicalBody(true), wr, inLetExprBody); for (int i = 0; i < n; i++) { wr.Write(")"); } @@ -2959,7 +2982,7 @@ namespace Microsoft.Dafny { // return Dafny.Set.FromCollection(_coll); // })() Contract.Assert(e.Bounds != null); // the resolver would have insisted on finding bounds - var typeName = TypeName(e.Type.AsSetType.Arg); + var typeName = TypeName(e.Type.AsSetType.Arg, wr); wr.Write("((Dafny.Helpers.ComprehensionDelegate<{0}>)delegate() {{ ", typeName); wr.Write("var _coll = new System.Collections.Generic.List<{0}>(); ", typeName); var n = e.BoundVars.Count; @@ -2978,36 +3001,36 @@ namespace Microsoft.Dafny { } else { wr.Write("foreach (var @{0} in Dafny.Helpers.IntegerRange(", bv.CompileName); } - TrExpr(b.LowerBound); + TrExpr(b.LowerBound, wr, inLetExprBody); wr.Write(", "); - TrExpr(b.UpperBound); + TrExpr(b.UpperBound, wr, inLetExprBody); wr.Write(")) { "); } else if (bound is ComprehensionExpr.SetBoundedPool) { var b = (ComprehensionExpr.SetBoundedPool)bound; wr.Write("foreach (var @{0} in (", bv.CompileName); - TrExpr(b.Set); + TrExpr(b.Set, wr, inLetExprBody); wr.Write(").Elements) { "); } else if (bound is ComprehensionExpr.MapBoundedPool) { var b = (ComprehensionExpr.MapBoundedPool)bound; wr.Write("foreach (var @{0} in (", bv.CompileName); - TrExpr(b.Map); + TrExpr(b.Map, wr, inLetExprBody); wr.Write(").Domain) { "); } else if (bound is ComprehensionExpr.SeqBoundedPool) { var b = (ComprehensionExpr.SeqBoundedPool)bound; wr.Write("foreach (var @{0} in (", bv.CompileName); - TrExpr(b.Seq); + TrExpr(b.Seq, wr, inLetExprBody); wr.Write(").Elements) { "); } else if (bound is ComprehensionExpr.DatatypeBoundedPool) { var b = (ComprehensionExpr.DatatypeBoundedPool)bound; - wr.Write("foreach (var @{0} in {1}.AllSingletonConstructors) {{", bv.CompileName, TypeName(bv.Type)); + wr.Write("foreach (var @{0} in {1}.AllSingletonConstructors) {{", bv.CompileName, TypeName(bv.Type, wr)); } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected BoundedPool type } } wr.Write("if ("); - TrExpr(e.Range); + TrExpr(e.Range, wr, inLetExprBody); wr.Write(") { _coll.Add("); - TrExpr(e.Term); + TrExpr(e.Term, wr, inLetExprBody); wr.Write("); }"); for (int i = 0; i < n; i++) { wr.Write("}"); @@ -3034,8 +3057,8 @@ namespace Microsoft.Dafny { // return Dafny.Map.FromElements(_coll); // })() Contract.Assert(e.Bounds != null); // the resolver would have insisted on finding bounds - var domtypeName = TypeName(e.Type.AsMapType.Domain); - var rantypeName = TypeName(e.Type.AsMapType.Range); + var domtypeName = TypeName(e.Type.AsMapType.Domain, wr); + var rantypeName = TypeName(e.Type.AsMapType.Range, wr); wr.Write("((Dafny.Helpers.MapComprehensionDelegate<{0},{1}>)delegate() {{ ", domtypeName, rantypeName); wr.Write("var _coll = new System.Collections.Generic.List>(); ", domtypeName, rantypeName); var n = e.BoundVars.Count; @@ -3053,34 +3076,34 @@ namespace Microsoft.Dafny { } else { wr.Write("foreach (var @{0} in Dafny.Helpers.IntegerRange(", bv.CompileName); } - TrExpr(b.LowerBound); + TrExpr(b.LowerBound, wr, inLetExprBody); wr.Write(", "); - TrExpr(b.UpperBound); + TrExpr(b.UpperBound, wr, inLetExprBody); wr.Write(")) { "); } else if (bound is ComprehensionExpr.SetBoundedPool) { var b = (ComprehensionExpr.SetBoundedPool)bound; wr.Write("foreach (var @{0} in (", bv.CompileName); - TrExpr(b.Set); + TrExpr(b.Set, wr, inLetExprBody); wr.Write(").Elements) { "); } else if (bound is ComprehensionExpr.MapBoundedPool) { var b = (ComprehensionExpr.MapBoundedPool)bound; wr.Write("foreach (var @{0} in (", bv.CompileName); - TrExpr(b.Map); + TrExpr(b.Map, wr, inLetExprBody); wr.Write(").Domain) { "); } else if (bound is ComprehensionExpr.SeqBoundedPool) { var b = (ComprehensionExpr.SeqBoundedPool)bound; wr.Write("foreach (var @{0} in (", bv.CompileName); - TrExpr(b.Seq); + TrExpr(b.Seq, wr, inLetExprBody); wr.Write(").Elements) { "); } else { // TODO: handle ComprehensionExpr.SubSetBoundedPool Contract.Assert(false); throw new cce.UnreachableException(); // unexpected BoundedPool type } wr.Write("if ("); - TrExpr(e.Range); + TrExpr(e.Range, wr, inLetExprBody); wr.Write(") { "); wr.Write("_coll.Add(new Dafny.Pair<{0},{1}>(@{2},", domtypeName, rantypeName, bv.CompileName); - TrExpr(e.Term); + TrExpr(e.Term, wr, inLetExprBody); wr.Write(")); }"); wr.Write("}"); wr.Write("return Dafny.Map<{0},{1}>.FromCollection(_coll); ", domtypeName, rantypeName); @@ -3109,46 +3132,46 @@ namespace Microsoft.Dafny { var su = new Translator.Substituter(null, sm, new Dictionary(), null); - BetaRedex(bvars, fexprs, expr.Type, () => { + BetaRedex(bvars, fexprs, expr.Type, wr, inLetExprBody, () => { wr.Write("("); wr.Write(Util.Comma(e.BoundVars, bv => "@" + bv.CompileName)); wr.Write(") => "); - TrExpr(su.Substitute(e.Body)); + TrExpr(su.Substitute(e.Body), wr, inLetExprBody); }); } else if (expr is StmtExpr) { var e = (StmtExpr)expr; - TrExpr(e.E); + TrExpr(e.E, wr, inLetExprBody); } else if (expr is ITEExpr) { ITEExpr e = (ITEExpr)expr; wr.Write("("); - TrExpr(e.Test); + TrExpr(e.Test, wr, inLetExprBody); wr.Write(") ? ("); - TrExpr(e.Thn); + TrExpr(e.Thn, wr, inLetExprBody); wr.Write(") : ("); - TrExpr(e.Els); + TrExpr(e.Els, wr, inLetExprBody); wr.Write(")"); } else if (expr is ConcreteSyntaxExpression) { var e = (ConcreteSyntaxExpression)expr; - TrExpr(e.ResolvedExpression); + TrExpr(e.ResolvedExpression, wr, inLetExprBody); } else if (expr is NamedExpr) { - TrExpr(((NamedExpr)expr).Body); + TrExpr(((NamedExpr)expr).Body, wr, inLetExprBody); } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression } } - int TrCasePattern(CasePattern pat, string rhsString, Type bodyType) { + int TrCasePattern(CasePattern pat, string rhsString, Type bodyType, TextWriter wr) { Contract.Requires(pat != null); Contract.Requires(rhsString != null); int c = 0; if (pat.Var != null) { var bv = pat.Var; if (!bv.IsGhost) { - wr.Write("Dafny.Helpers.Let<" + TypeName(bv.Type) + "," + TypeName(bodyType) + ">"); + wr.Write("Dafny.Helpers.Let<" + TypeName(bv.Type, wr) + "," + TypeName(bodyType, wr) + ">"); wr.Write("(" + rhsString + ", @" + bv.CompileName + " => "); c++; } @@ -3164,7 +3187,7 @@ namespace Microsoft.Dafny { // nothing to compile, but do a sanity check Contract.Assert(!Contract.Exists(arg.Vars, bv => !bv.IsGhost)); } else { - c += TrCasePattern(arg, string.Format("(({0})({1})._D).@{2}", DtCtorName(ctor, ((DatatypeValue)pat.Expr).InferredTypeArgs), rhsString, FormalName(formal, k)), bodyType); + c += TrCasePattern(arg, string.Format("(({0})({1})._D).@{2}", DtCtorName(ctor, ((DatatypeValue)pat.Expr).InferredTypeArgs, wr), rhsString, FormalName(formal, k)), bodyType, wr); k++; } } @@ -3172,40 +3195,40 @@ namespace Microsoft.Dafny { return c; } - delegate void FCE_Arg_Translator(Expression e); + delegate void FCE_Arg_Translator(Expression e, TextWriter wr, bool inLetExpr=false); - void CompileFunctionCallExpr(FunctionCallExpr e, TextWriter twr, FCE_Arg_Translator tr) { + void CompileFunctionCallExpr(FunctionCallExpr e, TextWriter twr, TextWriter wr, bool inLetExprBody, FCE_Arg_Translator tr) { Function f = cce.NonNull(e.Function); if (f.IsStatic) { - twr.Write(TypeName_Companion(e.Receiver.Type)); + twr.Write(TypeName_Companion(e.Receiver.Type, wr)); } else { twr.Write("("); - tr(e.Receiver); + tr(e.Receiver, wr, inLetExprBody); twr.Write(")"); } twr.Write(".@{0}", f.CompileName); if (f.TypeArgs.Count != 0) { List typeArgs = f.TypeArgs.ConvertAll(ta => e.TypeArgumentSubstitutions[ta]); - twr.Write("<" + TypeNames(typeArgs) + ">"); + twr.Write("<" + TypeNames(typeArgs, wr) + ">"); } twr.Write("("); string sep = ""; for (int i = 0; i < e.Args.Count; i++) { if (!e.Function.Formals[i].IsGhost) { twr.Write(sep); - tr(e.Args[i]); + tr(e.Args[i], wr); sep = ", "; } } twr.Write(")"); } - void BetaRedex(List bvars, List exprs, Type bodyType, Action makeBody) { + void BetaRedex(List bvars, List exprs, Type bodyType, TextWriter wr, bool inLetExprBody, Action makeBody) { Contract.Requires(bvars != null); Contract.Requires(exprs != null); Contract.Requires(bvars.Count == exprs.Count); wr.Write("Dafny.Helpers.Id<"); - wr.Write(TypeName_UDT(ArrowType.Arrow_FullCompileName, Util.Snoc(bvars.ConvertAll(bv => bv.Type), bodyType))); + wr.Write(TypeName_UDT(ArrowType.Arrow_FullCompileName, Util.Snoc(bvars.ConvertAll(bv => bv.Type), bodyType), wr)); wr.Write(">(("); wr.Write(Util.Comma(bvars, bv => "@" + bv.CompileName)); wr.Write(") => "); @@ -3213,7 +3236,7 @@ namespace Microsoft.Dafny { makeBody(); wr.Write(")"); - TrExprList(exprs); + TrExprList(exprs, wr, inLetExprBody); } } diff --git a/Source/DafnyDriver/DafnyDriver.cs b/Source/DafnyDriver/DafnyDriver.cs index 8282edc7..6cfbe7e1 100644 --- a/Source/DafnyDriver/DafnyDriver.cs +++ b/Source/DafnyDriver/DafnyDriver.cs @@ -300,14 +300,14 @@ namespace Microsoft.Dafny // Compile the Dafny program into a string that contains the C# program StringWriter sw = new StringWriter(); - Dafny.Compiler compiler = new Dafny.Compiler(sw); + Dafny.Compiler compiler = new Dafny.Compiler(); compiler.ErrorWriter = outputWriter; var hasMain = compiler.HasMain(dafnyProgram); if (DafnyOptions.O.RunAfterCompile && !hasMain) { // do no more return; } - compiler.Compile(dafnyProgram); + compiler.Compile(dafnyProgram, sw); var csharpProgram = sw.ToString(); bool completeProgram = compiler.ErrorCount == 0; diff --git a/Test/dafny4/Bug89.dfy b/Test/dafny4/Bug89.dfy new file mode 100644 index 00000000..12aec5f4 --- /dev/null +++ b/Test/dafny4/Bug89.dfy @@ -0,0 +1,15 @@ +// RUN: %dafny /compile:3 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +method F() returns(x:int) + ensures x == 6; +{ + x := 5; + x := (var y := 1; y + x); +} + +method Main() +{ + var x := F(); + print x; +} \ No newline at end of file diff --git a/Test/dafny4/Bug89.dfy.expect b/Test/dafny4/Bug89.dfy.expect new file mode 100644 index 00000000..5221a5d1 --- /dev/null +++ b/Test/dafny4/Bug89.dfy.expect @@ -0,0 +1,6 @@ + +Dafny program verifier finished with 4 verified, 0 errors +Program compiled successfully +Running... + +6 \ No newline at end of file -- cgit v1.2.3 From 7cc9a11d13a1d43d6e7beb4f874bb086f73804c4 Mon Sep 17 00:00:00 2001 From: leino Date: Wed, 11 Nov 2015 17:36:24 -0800 Subject: Fixed compilation of equality between reference types --- Source/Dafny/Compiler.cs | 14 +++++----- Test/dafny0/Compilation.dfy | 55 +++++++++++++++++++++++++++++++++++++- Test/dafny0/Compilation.dfy.expect | 6 ++++- 3 files changed, 66 insertions(+), 9 deletions(-) (limited to 'Source/Dafny/Compiler.cs') diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index f992d6c0..2786133e 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -2637,27 +2637,27 @@ namespace Microsoft.Dafny { opString = "&&"; break; case BinaryExpr.ResolvedOpcode.EqCommon: { - if (e.E0.Type.IsDatatype || e.E0.Type.IsTypeParameter || e.E0.Type.SupportsEquality) { - callString = "Equals"; - } else if (e.E0.Type.IsRefType) { + if (e.E0.Type.IsRefType) { // Dafny's type rules are slightly different C#, so we may need a cast here. // For example, Dafny allows x==y if x:array and y:array and T is some // type parameter. opString = "== (object)"; + } else if (e.E0.Type.IsDatatype || e.E0.Type.IsTypeParameter || e.E0.Type.SupportsEquality) { + callString = "Equals"; } else { opString = "=="; } break; } case BinaryExpr.ResolvedOpcode.NeqCommon: { - if (e.E0.Type.IsDatatype || e.E0.Type.IsTypeParameter || e.E0.Type.SupportsEquality) { - preOpString = "!"; - callString = "Equals"; - } else if (e.E0.Type.IsRefType) { + if (e.E0.Type.IsRefType) { // Dafny's type rules are slightly different C#, so we may need a cast here. // For example, Dafny allows x==y if x:array and y:array and T is some // type parameter. opString = "!= (object)"; + } else if (e.E0.Type.IsDatatype || e.E0.Type.IsTypeParameter || e.E0.Type.SupportsEquality) { + preOpString = "!"; + callString = "Equals"; } else { opString = "!="; } diff --git a/Test/dafny0/Compilation.dfy b/Test/dafny0/Compilation.dfy index 7f9169da..965f0787 100644 --- a/Test/dafny0/Compilation.dfy +++ b/Test/dafny0/Compilation.dfy @@ -45,7 +45,7 @@ module CoRecursion { // 42 // 9 // 9 - method Main() { + method TestMain() { var m := 17; var cell := new Cell; cell.data := 40; @@ -260,3 +260,56 @@ class DigitUnderscore_Names { this.10 := 20; } } + +// ------------------------------------------------------------------ + +method Main() +{ + CoRecursion.TestMain(); + EqualityTests.TestMain(); +} + +// ------------------------------------------------------------------ + +module EqualityTests { + class C { + } + + method TestMain() + { + // regression tests: + var a: C, b: C := null, null; + if a == null { + print "a is null\n"; + } + if a != null { + print "a is not null\n"; + } + if a == b { + print "a and b are equal\n"; + } + if a != b { + print "a and b are not equal\n"; + } + + var H := new real[10]; + ArrayTests(H); + } + + method ArrayTests(H: array) + { + var G := new int[10]; + if G == H { // this comparison is allowed in Dafny, but requires a cast in C# + print "this would be highly suspicious\n"; + } + if G != H { // this comparison is allowed in Dafny, but requires a cast in C# + print "good world order\n"; + } + if null == H { + print "given array is null\n"; + } + if null != H { + print "given array is non-null\n"; + } + } +} diff --git a/Test/dafny0/Compilation.dfy.expect b/Test/dafny0/Compilation.dfy.expect index 2b76b107..0a934a63 100644 --- a/Test/dafny0/Compilation.dfy.expect +++ b/Test/dafny0/Compilation.dfy.expect @@ -1,5 +1,5 @@ -Dafny program verifier finished with 50 verified, 0 errors +Dafny program verifier finished with 56 verified, 0 errors Program compiled successfully Running... @@ -10,3 +10,7 @@ Running... 42 9 9 +a is null +a and b are equal +good world order +given array is non-null -- cgit v1.2.3 From 854acf0f7f5aa105500c6d0ee0fcf0d4c918a81e Mon Sep 17 00:00:00 2001 From: qunyanm Date: Sat, 14 Nov 2015 14:50:26 -0800 Subject: Fix issue 94. Allow tuple-based assignment in statement contexts. --- Source/Dafny/Cloner.cs | 4 + Source/Dafny/Compiler.cs | 8 + Source/Dafny/Dafny.atg | 94 ++++-- Source/Dafny/DafnyAst.cs | 22 ++ Source/Dafny/Parser.cs | 459 +++++++++++++++------------ Source/Dafny/Printer.cs | 13 + Source/Dafny/Resolver.cs | 49 ++- Source/Dafny/Translator.cs | 29 +- Test/dafny4/Bug94.dfy | 35 ++ Test/dafny4/Bug94.dfy.expect | 6 + Test/server/simple-session.transcript.expect | 2 +- 11 files changed, 476 insertions(+), 245 deletions(-) create mode 100644 Test/dafny4/Bug94.dfy create mode 100644 Test/dafny4/Bug94.dfy.expect (limited to 'Source/Dafny/Compiler.cs') diff --git a/Source/Dafny/Cloner.cs b/Source/Dafny/Cloner.cs index 1c4275c5..1a6dfecb 100644 --- a/Source/Dafny/Cloner.cs +++ b/Source/Dafny/Cloner.cs @@ -583,6 +583,10 @@ namespace Microsoft.Dafny var lhss = s.Locals.ConvertAll(c => new LocalVariable(Tok(c.Tok), Tok(c.EndTok), c.Name, CloneType(c.OptionalType), c.IsGhost)); r = new VarDeclStmt(Tok(s.Tok), Tok(s.EndTok), lhss, (ConcreteUpdateStatement)CloneStmt(s.Update)); + } else if (stmt is LetStmt) { + var s = (LetStmt) stmt; + r = new LetStmt(Tok(s.Tok), Tok(s.EndTok), s.LHSs.ConvertAll(CloneCasePattern), s.RHSs.ConvertAll(CloneExpr)); + } else if (stmt is ModifyStmt) { var s = (ModifyStmt)stmt; var mod = CloneSpecFrameExpr(s.Mod); diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index 2786133e..82795480 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -1764,6 +1764,14 @@ namespace Microsoft.Dafny { 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) { diff --git a/Source/Dafny/Dafny.atg b/Source/Dafny/Dafny.atg index ff3b75a3..08c22db4 100644 --- a/Source/Dafny/Dafny.atg +++ b/Source/Dafny/Dafny.atg @@ -1628,46 +1628,74 @@ VarDeclStatement<.out Statement/*!*/ s.> Expression suchThat = null; Attributes attrs = null; IToken endTok; + s = dummyStmt; .) [ "ghost" (. isGhost = true; x = t; .) ] "var" (. if (!isGhost) { x = t; } .) - { Attribute } - LocalIdentTypeOptional (. lhss.Add(d); d.Attributes = attrs; attrs = null; .) - { "," - { Attribute } - LocalIdentTypeOptional (. lhss.Add(d); d.Attributes = attrs; attrs = null; .) - } - [ ":=" (. assignTok = t; .) - Rhs (. rhss.Add(r); .) - { "," Rhs (. rhss.Add(r); .) + ( { Attribute } + LocalIdentTypeOptional (. lhss.Add(d); d.Attributes = attrs; attrs = null; .) + { "," + { Attribute } + LocalIdentTypeOptional (. lhss.Add(d); d.Attributes = attrs; attrs = null; .) } - | { Attribute } - ":|" (. assignTok = t; .) - [ IF(la.kind == _assume) /* an Expression can also begin with an "assume", so this says to resolve it to pick up any "assume" here */ - "assume" (. suchThatAssume = t; .) + [ ":=" (. assignTok = t; .) + Rhs (. rhss.Add(r); .) + { "," Rhs (. rhss.Add(r); .) + } + | { Attribute } + ":|" (. assignTok = t; .) + [ IF(la.kind == _assume) /* an Expression can also begin with an "assume", so this says to resolve it to pick up any "assume" here */ + "assume" (. suchThatAssume = t; .) + ] + Expression ] - Expression - ] - SYNC ";" (. endTok = t; .) - (. ConcreteUpdateStatement update; - if (suchThat != null) { - var ies = new List(); - foreach (var lhs in lhss) { - ies.Add(new IdentifierExpr(lhs.Tok, lhs.Name)); - } - update = new AssignSuchThatStmt(assignTok, endTok, ies, suchThat, suchThatAssume, attrs); - } else if (rhss.Count == 0) { - update = null; - } else { - var ies = new List(); - foreach (var lhs in lhss) { - ies.Add(new AutoGhostIdentifierExpr(lhs.Tok, lhs.Name)); + SYNC ";" (. endTok = t; .) + (. ConcreteUpdateStatement update; + if (suchThat != null) { + var ies = new List(); + foreach (var lhs in lhss) { + ies.Add(new IdentifierExpr(lhs.Tok, lhs.Name)); + } + update = new AssignSuchThatStmt(assignTok, endTok, ies, suchThat, suchThatAssume, attrs); + } else if (rhss.Count == 0) { + update = null; + } else { + var ies = new List(); + foreach (var lhs in lhss) { + ies.Add(new AutoGhostIdentifierExpr(lhs.Tok, lhs.Name)); + } + update = new UpdateStmt(assignTok, endTok, ies, rhss); } - update = new UpdateStmt(assignTok, endTok, ies, rhss); - } - s = new VarDeclStmt(x, endTok, lhss, update); - .) + s = new VarDeclStmt(x, endTok, lhss, update); + .) + | "(" (. var letLHSs = new List(); + var letRHSs = new List(); + List arguments = new List(); + CasePattern pat; + Expression e = dummyExpr; + IToken id = t; + .) + [ CasePattern (. arguments.Add(pat); .) + { "," CasePattern (. arguments.Add(pat); .) + } + ] + ")" (. // Parse parenthesis without an identifier as a built in tuple type. + theBuiltIns.TupleType(id, arguments.Count, true); // make sure the tuple type exists + string ctor = BuiltIns.TupleTypeCtorName; //use the TupleTypeCtors + pat = new CasePattern(id, ctor, arguments); + if (isGhost) { pat.Vars.Iter(bv => bv.IsGhost = true); } + letLHSs.Add(pat); + .) + ( ":=" + | { Attribute } + ":|" (. SemErr(pat.tok, "LHS of assign-such-that expression must be variables, not general patterns"); .) + ) + Expression (. letRHSs.Add(e); .) + + ";" + (. s = new LetStmt(e.tok, e.tok, letLHSs, letRHSs); .) + ) . IfStmt = (. Contract.Ensures(Contract.ValueAtReturn(out ifStmt) != null); IToken/*!*/ x; diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index 9ed3b7e0..847617aa 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -4084,6 +4084,28 @@ namespace Microsoft.Dafny { } } + public class LetStmt : Statement + { + public readonly List LHSs; + public readonly List RHSs; + + public LetStmt(IToken tok, IToken endTok, List lhss, List rhss) + : base(tok, endTok) { + LHSs = lhss; + RHSs = rhss; + } + + public IEnumerable BoundVars { + get { + foreach (var lhs in LHSs) { + foreach (var bv in lhs.Vars) { + yield return bv; + } + } + } + } + } + /// /// Common superclass of UpdateStmt and AssignSuchThatStmt. /// diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs index b6a59f4e..922aad75 100644 --- a/Source/Dafny/Parser.cs +++ b/Source/Dafny/Parser.cs @@ -2339,6 +2339,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Expression suchThat = null; Attributes attrs = null; IToken endTok; + s = dummyStmt; if (la.kind == 73) { Get(); @@ -2346,64 +2347,104 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } Expect(78); if (!isGhost) { x = t; } - while (la.kind == 46) { - Attribute(ref attrs); - } - LocalIdentTypeOptional(out d, isGhost); - lhss.Add(d); d.Attributes = attrs; attrs = null; - while (la.kind == 22) { - Get(); + if (la.kind == 1 || la.kind == 46) { while (la.kind == 46) { Attribute(ref attrs); } LocalIdentTypeOptional(out d, isGhost); lhss.Add(d); d.Attributes = attrs; attrs = null; - } - if (la.kind == 25 || la.kind == 46 || la.kind == 95) { - if (la.kind == 95) { + while (la.kind == 22) { Get(); - assignTok = t; - Rhs(out r); - rhss.Add(r); - while (la.kind == 22) { + while (la.kind == 46) { + Attribute(ref attrs); + } + LocalIdentTypeOptional(out d, isGhost); + lhss.Add(d); d.Attributes = attrs; attrs = null; + } + if (la.kind == 25 || la.kind == 46 || la.kind == 95) { + if (la.kind == 95) { Get(); + assignTok = t; Rhs(out r); rhss.Add(r); + while (la.kind == 22) { + Get(); + Rhs(out r); + rhss.Add(r); + } + } else { + while (la.kind == 46) { + Attribute(ref attrs); + } + Expect(25); + assignTok = t; + if (la.kind == _assume) { + Expect(31); + suchThatAssume = t; + } + Expression(out suchThat, false, true); } + } + while (!(la.kind == 0 || la.kind == 28)) {SynErr(184); Get();} + Expect(28); + endTok = t; + ConcreteUpdateStatement update; + if (suchThat != null) { + var ies = new List(); + foreach (var lhs in lhss) { + ies.Add(new IdentifierExpr(lhs.Tok, lhs.Name)); + } + update = new AssignSuchThatStmt(assignTok, endTok, ies, suchThat, suchThatAssume, attrs); + } else if (rhss.Count == 0) { + update = null; } else { + var ies = new List(); + foreach (var lhs in lhss) { + ies.Add(new AutoGhostIdentifierExpr(lhs.Tok, lhs.Name)); + } + update = new UpdateStmt(assignTok, endTok, ies, rhss); + } + s = new VarDeclStmt(x, endTok, lhss, update); + + } else if (la.kind == 50) { + Get(); + var letLHSs = new List(); + var letRHSs = new List(); + List arguments = new List(); + CasePattern pat; + Expression e = dummyExpr; + IToken id = t; + + if (la.kind == 1 || la.kind == 50) { + CasePattern(out pat); + arguments.Add(pat); + while (la.kind == 22) { + Get(); + CasePattern(out pat); + arguments.Add(pat); + } + } + Expect(51); + theBuiltIns.TupleType(id, arguments.Count, true); // make sure the tuple type exists + string ctor = BuiltIns.TupleTypeCtorName; //use the TupleTypeCtors + pat = new CasePattern(id, ctor, arguments); + if (isGhost) { pat.Vars.Iter(bv => bv.IsGhost = true); } + letLHSs.Add(pat); + + if (la.kind == 95) { + Get(); + } else if (la.kind == 25 || la.kind == 46) { while (la.kind == 46) { Attribute(ref attrs); } Expect(25); - assignTok = t; - if (la.kind == _assume) { - Expect(31); - suchThatAssume = t; - } - Expression(out suchThat, false, true); - } - } - while (!(la.kind == 0 || la.kind == 28)) {SynErr(184); Get();} - Expect(28); - endTok = t; - ConcreteUpdateStatement update; - if (suchThat != null) { - var ies = new List(); - foreach (var lhs in lhss) { - ies.Add(new IdentifierExpr(lhs.Tok, lhs.Name)); - } - update = new AssignSuchThatStmt(assignTok, endTok, ies, suchThat, suchThatAssume, attrs); - } else if (rhss.Count == 0) { - update = null; - } else { - var ies = new List(); - foreach (var lhs in lhss) { - ies.Add(new AutoGhostIdentifierExpr(lhs.Tok, lhs.Name)); - } - update = new UpdateStmt(assignTok, endTok, ies, rhss); - } - s = new VarDeclStmt(x, endTok, lhss, update); - + SemErr(pat.tok, "LHS of assign-such-that expression must be variables, not general patterns"); + } else SynErr(185); + Expression(out e, false, true); + letRHSs.Add(e); + Expect(28); + s = new LetStmt(e.tok, e.tok, letLHSs, letRHSs); + } else SynErr(186); } void IfStmt(out Statement/*!*/ ifStmt) { @@ -2442,7 +2483,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } else if (la.kind == 46) { BlockStmt(out bs, out bodyStart, out bodyEnd); els = bs; endTok = bs.EndTok; - } else SynErr(185); + } else SynErr(187); } if (guardEllipsis != null) { ifStmt = new SkeletonStatement(new IfStmt(x, endTok, isExistentialGuard, guard, thn, els), guardEllipsis, null); @@ -2450,7 +2491,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo ifStmt = new IfStmt(x, endTok, isExistentialGuard, guard, thn, els); } - } else SynErr(186); + } else SynErr(188); } void WhileStmt(out Statement stmt) { @@ -2495,7 +2536,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Expect(59); bodyEllipsis = t; endTok = t; isDirtyLoop = false; } else if (StartOf(23)) { - } else SynErr(187); + } else SynErr(189); if (guardEllipsis != null || bodyEllipsis != null) { if (mod != null) { SemErr(mod[0].E.tok, "'modifies' clauses are not allowed on refining loops"); @@ -2513,7 +2554,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo stmt = new WhileStmt(x, endTok, guard, invariants, new Specification(decreases, decAttrs), new Specification(mod, modAttrs), body); } - } else SynErr(188); + } else SynErr(190); } void MatchStmt(out Statement/*!*/ s) { @@ -2538,7 +2579,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo CaseStatement(out c); cases.Add(c); } - } else SynErr(189); + } else SynErr(191); s = new MatchStmt(x, t, e, cases, usesOptionalBrace); } @@ -2563,7 +2604,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo x = t; errors.Warning(t, "the 'parallel' keyword has been deprecated; the comprehension statement now uses the keyword 'forall' (and the parentheses around the bound variables are now optional)"); - } else SynErr(190); + } else SynErr(192); if (la.kind == _openparen) { Expect(50); if (la.kind == 1) { @@ -2574,7 +2615,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo if (la.kind == _ident) { QuantifierDomain(out bvars, out attrs, out range); } - } else SynErr(191); + } else SynErr(193); if (bvars == null) { bvars = new List(); } if (range == null) { range = new LiteralExpr(x, true); } @@ -2664,7 +2705,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } else if (la.kind == 32) { CalcStmt(out subCalc); hintEnd = subCalc.EndTok; subhints.Add(subCalc); - } else SynErr(192); + } else SynErr(194); } var h = new BlockStmt(hintStart, hintEnd, subhints); // if the hint is empty, hintStart is the first token of the next line, but it doesn't matter because the block statement is just used as a container hints.Add(h); @@ -2706,14 +2747,14 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } else if (la.kind == 59) { Get(); ellipsisToken = t; - } else SynErr(193); + } else SynErr(195); if (la.kind == 46) { BlockStmt(out body, out bodyStart, out endTok); } else if (la.kind == 28) { - while (!(la.kind == 0 || la.kind == 28)) {SynErr(194); Get();} + while (!(la.kind == 0 || la.kind == 28)) {SynErr(196); Get();} Get(); endTok = t; - } else SynErr(195); + } else SynErr(197); s = new ModifyStmt(tok, endTok, mod, attrs, body); if (ellipsisToken != null) { s = new SkeletonStatement(s, ellipsisToken, null); @@ -2733,7 +2774,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } else if (la.kind == 90) { Get(); returnTok = t; isYield = true; - } else SynErr(196); + } else SynErr(198); if (StartOf(26)) { Rhs(out r); rhss = new List(); rhss.Add(r); @@ -2831,7 +2872,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } else if (StartOf(7)) { Expression(out e, false, true); r = new ExprRhs(e); - } else SynErr(197); + } else SynErr(199); while (la.kind == 46) { Attribute(ref attrs); } @@ -2852,7 +2893,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo while (la.kind == 27 || la.kind == 48 || la.kind == 50) { Suffix(ref e); } - } else SynErr(198); + } else SynErr(200); } void Expressions(List args) { @@ -2866,6 +2907,56 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } } + void CasePattern(out CasePattern pat) { + IToken id; List arguments; + BoundVar bv; + pat = null; + + if (IsIdentParen()) { + Ident(out id); + Expect(50); + arguments = new List(); + if (la.kind == 1 || la.kind == 50) { + CasePattern(out pat); + arguments.Add(pat); + while (la.kind == 22) { + Get(); + CasePattern(out pat); + arguments.Add(pat); + } + } + Expect(51); + pat = new CasePattern(id, id.val, arguments); + } else if (la.kind == 50) { + Get(); + id = t; + arguments = new List(); + + if (la.kind == 1 || la.kind == 50) { + CasePattern(out pat); + arguments.Add(pat); + while (la.kind == 22) { + Get(); + CasePattern(out pat); + arguments.Add(pat); + } + } + Expect(51); + theBuiltIns.TupleType(id, arguments.Count, true); // make sure the tuple type exists + string ctor = BuiltIns.TupleTypeCtorName; //use the TupleTypeCtors + pat = new CasePattern(id, ctor, arguments); + + } else if (la.kind == 1) { + IdentTypeOptional(out bv); + pat = new CasePattern(bv.tok, bv); + + } else SynErr(201); + if (pat == null) { + pat = new CasePattern(t, "_ParseError", new List()); + } + + } + void AlternativeBlock(bool allowExistentialGuards, out List alternatives, out IToken endTok) { alternatives = new List(); IToken x; @@ -2881,7 +2972,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo isExistentialGuard = true; } else if (StartOf(7)) { Expression(out e, true, false); - } else SynErr(199); + } else SynErr(202); Expect(29); body = new List(); while (StartOf(15)) { @@ -2927,7 +3018,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } else if (StartOf(7)) { Expression(out ee, true, true); e = ee; - } else SynErr(200); + } else SynErr(203); } void LoopSpec(List invariants, List decreases, ref List mod, ref Attributes decAttrs, ref Attributes modAttrs) { @@ -2935,7 +3026,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo bool isFree = false; Attributes attrs = null; if (la.kind == 37 || la.kind == 88) { - while (!(la.kind == 0 || la.kind == 37 || la.kind == 88)) {SynErr(201); Get();} + while (!(la.kind == 0 || la.kind == 37 || la.kind == 88)) {SynErr(204); Get();} if (la.kind == 88) { Get(); isFree = true; errors.Warning(t, "the 'free' keyword is soon to be deprecated"); @@ -2948,7 +3039,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo invariants.Add(new MaybeFreeExpression(e, isFree, attrs)); OldSemi(); } else if (la.kind == 36) { - while (!(la.kind == 0 || la.kind == 36)) {SynErr(202); Get();} + while (!(la.kind == 0 || la.kind == 36)) {SynErr(205); Get();} Get(); while (IsAttribute()) { Attribute(ref decAttrs); @@ -2956,7 +3047,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo DecreasesList(decreases, true, true); OldSemi(); } else if (la.kind == 43) { - while (!(la.kind == 0 || la.kind == 43)) {SynErr(203); Get();} + while (!(la.kind == 0 || la.kind == 43)) {SynErr(206); Get();} Get(); mod = mod ?? new List(); while (IsAttribute()) { @@ -2970,7 +3061,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo mod.Add(fe); } OldSemi(); - } else SynErr(204); + } else SynErr(207); } void CaseStatement(out MatchCaseStmt/*!*/ c) { @@ -3007,66 +3098,16 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo arguments.Add(pat); } Expect(51); - } else SynErr(205); + } else SynErr(208); Expect(29); - while (!(StartOf(28))) {SynErr(206); Get();} + while (!(StartOf(28))) {SynErr(209); Get();} while (IsNotEndOfCase()) { Stmt(body); - while (!(StartOf(28))) {SynErr(207); Get();} + while (!(StartOf(28))) {SynErr(210); Get();} } c = new MatchCaseStmt(x, name, arguments, body); } - void CasePattern(out CasePattern pat) { - IToken id; List arguments; - BoundVar bv; - pat = null; - - if (IsIdentParen()) { - Ident(out id); - Expect(50); - arguments = new List(); - if (la.kind == 1 || la.kind == 50) { - CasePattern(out pat); - arguments.Add(pat); - while (la.kind == 22) { - Get(); - CasePattern(out pat); - arguments.Add(pat); - } - } - Expect(51); - pat = new CasePattern(id, id.val, arguments); - } else if (la.kind == 50) { - Get(); - id = t; - arguments = new List(); - - if (la.kind == 1 || la.kind == 50) { - CasePattern(out pat); - arguments.Add(pat); - while (la.kind == 22) { - Get(); - CasePattern(out pat); - arguments.Add(pat); - } - } - Expect(51); - theBuiltIns.TupleType(id, arguments.Count, true); // make sure the tuple type exists - string ctor = BuiltIns.TupleTypeCtorName; //use the TupleTypeCtors - pat = new CasePattern(id, ctor, arguments); - - } else if (la.kind == 1) { - IdentTypeOptional(out bv); - pat = new CasePattern(bv.tok, bv); - - } else SynErr(208); - if (pat == null) { - pat = new CasePattern(t, "_ParseError", new List()); - } - - } - void QuantifierDomain(out List bvars, out Attributes attrs, out Expression range) { bvars = new List(); BoundVar/*!*/ bv; @@ -3161,7 +3202,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo x = t; binOp = BinaryExpr.Opcode.Exp; break; } - default: SynErr(209); break; + default: SynErr(211); break; } if (k == null) { op = new Microsoft.Dafny.CalcStmt.BinaryCalcOp(binOp); @@ -3176,7 +3217,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Get(); } else if (la.kind == 112) { Get(); - } else SynErr(210); + } else SynErr(212); } void ImpliesOp() { @@ -3184,7 +3225,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Get(); } else if (la.kind == 114) { Get(); - } else SynErr(211); + } else SynErr(213); } void ExpliesOp() { @@ -3192,7 +3233,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Get(); } else if (la.kind == 116) { Get(); - } else SynErr(212); + } else SynErr(214); } void AndOp() { @@ -3200,7 +3241,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Get(); } else if (la.kind == 118) { Get(); - } else SynErr(213); + } else SynErr(215); } void OrOp() { @@ -3208,7 +3249,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Get(); } else if (la.kind == 120) { Get(); - } else SynErr(214); + } else SynErr(216); } void NegOp() { @@ -3216,7 +3257,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Get(); } else if (la.kind == 122) { Get(); - } else SynErr(215); + } else SynErr(217); } void Forall() { @@ -3224,7 +3265,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Get(); } else if (la.kind == 123) { Get(); - } else SynErr(216); + } else SynErr(218); } void Exists() { @@ -3232,7 +3273,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Get(); } else if (la.kind == 125) { Get(); - } else SynErr(217); + } else SynErr(219); } void QSep() { @@ -3240,7 +3281,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Get(); } else if (la.kind == 26) { Get(); - } else SynErr(218); + } else SynErr(220); } void EquivExpression(out Expression e0, bool allowSemi, bool allowLambda) { @@ -3275,7 +3316,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo e0 = new BinaryExpr(x, BinaryExpr.Opcode.Exp, e1, e0); } - } else SynErr(219); + } else SynErr(221); } } @@ -3305,7 +3346,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo RelationalExpression(out e1, allowSemi, allowLambda); e0 = new BinaryExpr(x, BinaryExpr.Opcode.Or, e0, e1); } - } else SynErr(220); + } else SynErr(222); } } @@ -3523,7 +3564,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo x = t; op = BinaryExpr.Opcode.Ge; break; } - default: SynErr(221); break; + default: SynErr(223); break; } } @@ -3545,7 +3586,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } else if (la.kind == 128) { Get(); x = t; op = BinaryExpr.Opcode.Sub; - } else SynErr(222); + } else SynErr(224); } void UnaryExpression(out Expression e, bool allowSemi, bool allowLambda) { @@ -3605,7 +3646,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo while (IsSuffix()) { Suffix(ref e); } - } else SynErr(223); + } else SynErr(225); } void MulOp(out IToken x, out BinaryExpr.Opcode op) { @@ -3619,7 +3660,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } else if (la.kind == 130) { Get(); x = t; op = BinaryExpr.Opcode.Mod; - } else SynErr(224); + } else SynErr(226); } void MapDisplayExpr(IToken/*!*/ mapToken, bool finite, out Expression e) { @@ -3673,13 +3714,13 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } else if (la.kind == 106) { HashCall(id, out openParen, out typeArgs, out args); } else if (StartOf(31)) { - } else SynErr(225); + } else SynErr(227); e = new ExprDotName(id, e, id.val, typeArgs); if (openParen != null) { e = new ApplySuffix(openParen, e, args); } - } else SynErr(226); + } else SynErr(228); } else if (la.kind == 48) { Get(); x = t; @@ -3727,7 +3768,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo multipleIndices.Add(ee); } - } else SynErr(227); + } else SynErr(229); } else if (la.kind == 137) { Get(); anyDots = true; @@ -3735,7 +3776,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Expression(out ee, true, true); e1 = ee; } - } else SynErr(228); + } else SynErr(230); if (multipleIndices != null) { e = new MultiSelectExpr(x, e, multipleIndices); // make sure an array class with this dimensionality exists @@ -3779,7 +3820,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } Expect(51); e = new ApplySuffix(openParen, e, args); - } else SynErr(229); + } else SynErr(231); } void ISetDisplayExpr(IToken/*!*/ setToken, bool finite, out Expression e) { @@ -3821,7 +3862,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } } Expect(51); - } else SynErr(230); + } else SynErr(232); while (la.kind == 44 || la.kind == 45) { if (la.kind == 44) { Get(); @@ -3904,7 +3945,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo NamedExpr(out e, allowSemi, allowLambda); break; } - default: SynErr(231); break; + default: SynErr(233); break; } } @@ -3919,7 +3960,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } else if (la.kind == 106) { HashCall(id, out openParen, out typeArgs, out args); } else if (StartOf(31)) { - } else SynErr(232); + } else SynErr(234); e = new NameSegment(id, id.val, typeArgs); if (openParen != null) { e = new ApplySuffix(openParen, e, args); @@ -3948,7 +3989,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } e = new SeqDisplayExpr(x, elements); Expect(49); - } else SynErr(233); + } else SynErr(235); } void MultiSetExpr(out Expression e) { @@ -3972,7 +4013,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Expression(out e, true, true); e = new MultiSetFormingExpr(x, e); Expect(51); - } else SynErr(234); + } else SynErr(236); } void ConstAtomExpression(out Expression e, bool allowSemi, bool allowLambda) { @@ -4068,7 +4109,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo ParensExpression(out e, allowSemi, allowLambda); break; } - default: SynErr(235); break; + default: SynErr(237); break; } } @@ -4097,7 +4138,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo n = BigInteger.Zero; } - } else SynErr(236); + } else SynErr(238); } void Dec(out Basetypes.BigDec d) { @@ -4141,7 +4182,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } else if (la.kind == 30) { Get(); oneShot = true; - } else SynErr(237); + } else SynErr(239); } void MapLiteralExpressions(out List elements) { @@ -4204,7 +4245,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo CaseExpression(out c, allowSemi, allowLambda); cases.Add(c); } - } else SynErr(238); + } else SynErr(240); e = new MatchExpr(x, e, cases, usesOptionalBrace); } @@ -4222,7 +4263,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } else if (la.kind == 124 || la.kind == 125) { Exists(); x = t; - } else SynErr(239); + } else SynErr(241); QuantifierDomain(out bvars, out attrs, out range); QSep(); Expression(out body, allowSemi, allowLambda); @@ -4271,7 +4312,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo AssumeStmt(out s); } else if (la.kind == 32) { CalcStmt(out s); - } else SynErr(240); + } else SynErr(242); } void LetExpr(out Expression e, bool allowSemi, bool allowLambda) { @@ -4315,7 +4356,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } } - } else SynErr(241); + } else SynErr(243); Expression(out e, false, true); letRHSs.Add(e); while (la.kind == 22) { @@ -4375,7 +4416,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo arguments.Add(pat); } Expect(51); - } else SynErr(242); + } else SynErr(244); Expect(29); Expression(out body, allowSemi, allowLambda); c = new MatchCaseExpr(x, name, arguments, body); @@ -4409,7 +4450,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } else if (la.kind == 2) { Get(); id = t; - } else SynErr(243); + } else SynErr(245); Expect(95); Expression(out e, true, true); } @@ -4452,7 +4493,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } else if (la.kind == 44) { Get(); x = t; - } else SynErr(244); + } else SynErr(246); } @@ -4712,66 +4753,68 @@ public class Errors { case 182: s = "invalid UpdateStmt"; break; case 183: s = "invalid UpdateStmt"; break; case 184: s = "this symbol not expected in VarDeclStatement"; break; - case 185: s = "invalid IfStmt"; break; - case 186: s = "invalid IfStmt"; break; - case 187: s = "invalid WhileStmt"; break; - case 188: s = "invalid WhileStmt"; break; - case 189: s = "invalid MatchStmt"; break; - case 190: s = "invalid ForallStmt"; break; - case 191: s = "invalid ForallStmt"; break; - case 192: s = "invalid CalcStmt"; break; - case 193: s = "invalid ModifyStmt"; break; - case 194: s = "this symbol not expected in ModifyStmt"; break; + case 185: s = "invalid VarDeclStatement"; break; + case 186: s = "invalid VarDeclStatement"; break; + case 187: s = "invalid IfStmt"; break; + case 188: s = "invalid IfStmt"; break; + case 189: s = "invalid WhileStmt"; break; + case 190: s = "invalid WhileStmt"; break; + case 191: s = "invalid MatchStmt"; break; + case 192: s = "invalid ForallStmt"; break; + case 193: s = "invalid ForallStmt"; break; + case 194: s = "invalid CalcStmt"; break; case 195: s = "invalid ModifyStmt"; break; - case 196: s = "invalid ReturnStmt"; break; - case 197: s = "invalid Rhs"; break; - case 198: s = "invalid Lhs"; break; - case 199: s = "invalid AlternativeBlock"; break; - case 200: s = "invalid Guard"; break; - case 201: s = "this symbol not expected in LoopSpec"; break; - case 202: s = "this symbol not expected in LoopSpec"; break; - case 203: s = "this symbol not expected in LoopSpec"; break; - case 204: s = "invalid LoopSpec"; break; - case 205: s = "invalid CaseStatement"; break; - case 206: s = "this symbol not expected in CaseStatement"; break; - case 207: s = "this symbol not expected in CaseStatement"; break; - case 208: s = "invalid CasePattern"; break; - case 209: s = "invalid CalcOp"; break; - case 210: s = "invalid EquivOp"; break; - case 211: s = "invalid ImpliesOp"; break; - case 212: s = "invalid ExpliesOp"; break; - case 213: s = "invalid AndOp"; break; - case 214: s = "invalid OrOp"; break; - case 215: s = "invalid NegOp"; break; - case 216: s = "invalid Forall"; break; - case 217: s = "invalid Exists"; break; - case 218: s = "invalid QSep"; break; - case 219: s = "invalid ImpliesExpliesExpression"; break; - case 220: s = "invalid LogicalExpression"; break; - case 221: s = "invalid RelOp"; break; - case 222: s = "invalid AddOp"; break; - case 223: s = "invalid UnaryExpression"; break; - case 224: s = "invalid MulOp"; break; - case 225: s = "invalid Suffix"; break; - case 226: s = "invalid Suffix"; break; + case 196: s = "this symbol not expected in ModifyStmt"; break; + case 197: s = "invalid ModifyStmt"; break; + case 198: s = "invalid ReturnStmt"; break; + case 199: s = "invalid Rhs"; break; + case 200: s = "invalid Lhs"; break; + case 201: s = "invalid CasePattern"; break; + case 202: s = "invalid AlternativeBlock"; break; + case 203: s = "invalid Guard"; break; + case 204: s = "this symbol not expected in LoopSpec"; break; + case 205: s = "this symbol not expected in LoopSpec"; break; + case 206: s = "this symbol not expected in LoopSpec"; break; + case 207: s = "invalid LoopSpec"; break; + case 208: s = "invalid CaseStatement"; break; + case 209: s = "this symbol not expected in CaseStatement"; break; + case 210: s = "this symbol not expected in CaseStatement"; break; + case 211: s = "invalid CalcOp"; break; + case 212: s = "invalid EquivOp"; break; + case 213: s = "invalid ImpliesOp"; break; + case 214: s = "invalid ExpliesOp"; break; + case 215: s = "invalid AndOp"; break; + case 216: s = "invalid OrOp"; break; + case 217: s = "invalid NegOp"; break; + case 218: s = "invalid Forall"; break; + case 219: s = "invalid Exists"; break; + case 220: s = "invalid QSep"; break; + case 221: s = "invalid ImpliesExpliesExpression"; break; + case 222: s = "invalid LogicalExpression"; break; + case 223: s = "invalid RelOp"; break; + case 224: s = "invalid AddOp"; break; + case 225: s = "invalid UnaryExpression"; break; + case 226: s = "invalid MulOp"; break; case 227: s = "invalid Suffix"; break; case 228: s = "invalid Suffix"; break; case 229: s = "invalid Suffix"; break; - case 230: s = "invalid LambdaExpression"; break; - case 231: s = "invalid EndlessExpression"; break; - case 232: s = "invalid NameSegment"; break; - case 233: s = "invalid DisplayExpr"; break; - case 234: s = "invalid MultiSetExpr"; break; - case 235: s = "invalid ConstAtomExpression"; break; - case 236: s = "invalid Nat"; break; - case 237: s = "invalid LambdaArrow"; break; - case 238: s = "invalid MatchExpression"; break; - case 239: s = "invalid QuantifierGuts"; break; - case 240: s = "invalid StmtInExpr"; break; - case 241: s = "invalid LetExpr"; break; - case 242: s = "invalid CaseExpression"; break; - case 243: s = "invalid MemberBindingUpdate"; break; - case 244: s = "invalid DotSuffix"; break; + case 230: s = "invalid Suffix"; break; + case 231: s = "invalid Suffix"; break; + case 232: s = "invalid LambdaExpression"; break; + case 233: s = "invalid EndlessExpression"; break; + case 234: s = "invalid NameSegment"; break; + case 235: s = "invalid DisplayExpr"; break; + case 236: s = "invalid MultiSetExpr"; break; + case 237: s = "invalid ConstAtomExpression"; break; + case 238: s = "invalid Nat"; break; + case 239: s = "invalid LambdaArrow"; break; + case 240: s = "invalid MatchExpression"; break; + case 241: s = "invalid QuantifierGuts"; break; + case 242: s = "invalid StmtInExpr"; break; + case 243: s = "invalid LetExpr"; break; + case 244: s = "invalid CaseExpression"; break; + case 245: s = "invalid MemberBindingUpdate"; break; + case 246: s = "invalid DotSuffix"; break; default: s = "error " + n; break; } diff --git a/Source/Dafny/Printer.cs b/Source/Dafny/Printer.cs index 54de4905..145e82e7 100644 --- a/Source/Dafny/Printer.cs +++ b/Source/Dafny/Printer.cs @@ -963,6 +963,19 @@ namespace Microsoft.Dafny { } wr.Write(";"); + } else if (stmt is LetStmt) { + var s = (LetStmt)stmt; + wr.Write("var"); + string sep = ""; + foreach (var lhs in s.LHSs) { + wr.Write(sep); + PrintCasePattern(lhs); + sep = ", "; + } + wr.Write(" := "); + PrintExpressionList(s.RHSs, true); + wr.WriteLine(";"); + } else if (stmt is SkeletonStatement) { var s = (SkeletonStatement)stmt; if (s.S == null) { diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 21476ede..315b823a 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -2221,6 +2221,10 @@ namespace Microsoft.Dafny foreach (var local in s.Locals) { CheckTypeIsDetermined(local.Tok, local.Type, "local variable"); } + } else if (stmt is LetStmt) { + var s = (LetStmt)stmt; + s.BoundVars.Iter(bv => CheckTypeIsDetermined(bv.tok, bv.Type, "bound variable")); + } else if (stmt is ForallStmt) { var s = (ForallStmt)stmt; s.BoundVars.Iter(bv => CheckTypeIsDetermined(bv.tok, bv.Type, "bound variable")); @@ -2696,6 +2700,7 @@ namespace Microsoft.Dafny if (s.Update != null) { return CheckTailRecursive(s.Update, enclosingMethod, ref tailCall, reportErrors); } + } else if (stmt is LetStmt) { } else { Contract.Assert(false); // unexpected statement type } @@ -2980,6 +2985,11 @@ namespace Microsoft.Dafny foreach (var v in s.Locals) { CheckEqualityTypes_Type(v.Tok, v.Type); } + } else if (stmt is LetStmt) { + var s = (LetStmt)stmt; + foreach (var v in s.BoundVars) { + CheckEqualityTypes_Type(v.Tok, v.Type); + } } else if (stmt is WhileStmt) { var s = (WhileStmt)stmt; // don't recurse on the specification parts, which are ghost @@ -3362,6 +3372,15 @@ namespace Microsoft.Dafny Visit(s.Update, mustBeErasable); } + } else if (stmt is LetStmt) { + var s = (LetStmt)stmt; + if (mustBeErasable) { + foreach (var bv in s.BoundVars) { + bv.IsGhost = true; + } + } + s.IsGhost = s.BoundVars.All(v => v.IsGhost); + } else if (stmt is AssignStmt) { var s = (AssignStmt)stmt; var lhs = s.Lhs.Resolved; @@ -5566,7 +5585,30 @@ namespace Microsoft.Dafny } } } - + } else if (stmt is LetStmt) { + LetStmt s = (LetStmt)stmt; + foreach (var rhs in s.RHSs) { + ResolveExpression(rhs, new ResolveOpts(codeContext, true)); + } + if (s.LHSs.Count != s.RHSs.Count) { + reporter.Error(MessageSource.Resolver, stmt, "let statement must have same number of LHSs (found {0}) as RHSs (found {1})", s.LHSs.Count, s.RHSs.Count); + } + var i = 0; + foreach (var lhs in s.LHSs) { + var rhsType = i < s.RHSs.Count ? s.RHSs[i].Type : new InferredTypeProxy(); + ResolveCasePattern(lhs, rhsType, codeContext); + // Check for duplicate names now, because not until after resolving the case pattern do we know if identifiers inside it refer to bound variables or nullary constructors + var c = 0; + foreach (var bv in lhs.Vars) { + ScopePushAndReport(scope, bv, "local_variable"); + c++; + } + if (c == 0) { + // Every identifier-looking thing in the pattern resolved to a constructor; that is, this LHS is a constant literal + reporter.Error(MessageSource.Resolver, lhs.tok, "LHS is a constant literal; to be legal, it must introduce at least one bound variable"); + } + i++; + } } else if (stmt is AssignStmt) { AssignStmt s = (AssignStmt)stmt; int prevErrorCount = reporter.Count(ErrorLevel.Error); @@ -6748,6 +6790,8 @@ namespace Microsoft.Dafny if (s.Update != null) { CheckForallStatementBodyRestrictions(s.Update, kind); } + } else if (stmt is LetStmt) { + // Are we fine? } else if (stmt is AssignStmt) { var s = (AssignStmt)stmt; CheckForallStatementBodyLhs(s.Tok, s.Lhs.Resolved, kind); @@ -6903,6 +6947,8 @@ namespace Microsoft.Dafny if (s.Update != null) { CheckHintRestrictions(s.Update, localsAllowedInUpdates); } + } else if (stmt is LetStmt) { + // Are we fine? } else if (stmt is BlockStmt) { var s = (BlockStmt)stmt; var newScopeForLocals = new HashSet(localsAllowedInUpdates); @@ -7948,7 +7994,6 @@ namespace Microsoft.Dafny ResolveAttributes(e.Attributes, opts); scope.PopMarker(); expr.Type = e.Body.Type; - } else if (expr is NamedExpr) { var e = (NamedExpr)expr; ResolveExpression(e.Body, opts); diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 47dfb96a..51b800a7 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -5214,6 +5214,7 @@ namespace Microsoft.Dafny { } CheckWellformedWithResult(Substitute(e.Body, null, substMap), options, result, resultType, locals, builder, etran); result = null; + } else { // CheckWellformed(var b :| RHS(b); Body(b)) = // var b where typeAntecedent; @@ -7549,7 +7550,32 @@ namespace Microsoft.Dafny { if (s.Update != null) { TrStmt(s.Update, builder, locals, etran); } - + } else if (stmt is LetStmt) { + var s = (LetStmt)stmt; + foreach (var bv in s.BoundVars) { + Bpl.LocalVariable bvar = new Bpl.LocalVariable(bv.Tok, new Bpl.TypedIdent(bv.Tok, bv.AssignUniqueName(currentDeclaration.IdGenerator), TrType(bv.Type))); + locals.Add(bvar); + var bIe = new Bpl.IdentifierExpr(bvar.tok, bvar); + builder.Add(new Bpl.HavocCmd(bv.Tok, new List { bIe })); + Bpl.Expr wh = GetWhereClause(bv.Tok, bIe, bv.Type, etran); + if (wh != null) { + builder.Add(new Bpl.AssumeCmd(bv.Tok, wh)); + } + } + Contract.Assert(s.LHSs.Count == s.RHSs.Count); // checked by resolution + var varNameGen = CurrentIdGenerator.NestedFreshIdGenerator("let#"); + for (int i = 0; i < s.LHSs.Count; i++) { + var pat = s.LHSs[i]; + var rhs = s.RHSs[i]; + var nm = varNameGen.FreshId(string.Format("#{0}#", i)); + var r = new Bpl.LocalVariable(pat.tok, new Bpl.TypedIdent(pat.tok, nm, TrType(rhs.Type))); + locals.Add(r); + var rIe = new Bpl.IdentifierExpr(pat.tok, r); + TrStmt_CheckWellformed(s.RHSs[i], builder, locals, etran, false); + CheckWellformedWithResult(s.RHSs[i], new WFOptions(null, false, false), rIe, pat.Expr.Type, locals, builder, etran); + CheckCasePatternShape(pat, rIe, builder); + builder.Add(new Bpl.AssumeCmd(pat.tok, Bpl.Expr.Eq(etran.TrExpr(pat.Expr), rIe))); + } } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement } @@ -13700,6 +13726,7 @@ namespace Microsoft.Dafny { if (newCasePatterns != e.LHSs) { anythingChanged = true; } + var body = Substitute(e.Body); // undo any changes to substMap (could be optimized to do this only if newBoundVars != e.Vars) foreach (var bv in e.BoundVars) { diff --git a/Test/dafny4/Bug94.dfy b/Test/dafny4/Bug94.dfy new file mode 100644 index 00000000..2f437785 --- /dev/null +++ b/Test/dafny4/Bug94.dfy @@ -0,0 +1,35 @@ +// RUN: %dafny /compile:3 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +function foo() : (int, int) +{ + (5, 10) +} + +function bar() : int +{ + var (x, y) := foo(); + x + y +} + +lemma test() +{ + var (x, y) := foo(); +} + +function method foo2() : (int,int) +{ + (5, 10) +} + +method test2() +{ + var (x, y) := foo2(); +} + +method Main() +{ + var (x, y) := foo2(); + assert (x+y == 15); + print(x+y); +} diff --git a/Test/dafny4/Bug94.dfy.expect b/Test/dafny4/Bug94.dfy.expect new file mode 100644 index 00000000..6b337d5a --- /dev/null +++ b/Test/dafny4/Bug94.dfy.expect @@ -0,0 +1,6 @@ + +Dafny program verifier finished with 9 verified, 0 errors +Program compiled successfully +Running... + +15 \ No newline at end of file diff --git a/Test/server/simple-session.transcript.expect b/Test/server/simple-session.transcript.expect index 91429d8e..1aadca7f 100644 --- a/Test/server/simple-session.transcript.expect +++ b/Test/server/simple-session.transcript.expect @@ -136,7 +136,7 @@ Retrieving cached verification result for implementation Impl$$_module.__default [0 proof obligations] verified Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] -transcript(7,0): Error: ident expected +transcript(7,0): Error: invalid VarDeclStatement Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] -- cgit v1.2.3 From 2cde0265070d65f738b9ee236f7246e765536f08 Mon Sep 17 00:00:00 2001 From: qunyanm Date: Tue, 17 Nov 2015 13:13:40 -0800 Subject: Fix issue 107. Instead of writing out StaticReceiverExpr as null valued LiteralExpr, write out its type instead. --- Source/Dafny/Compiler.cs | 6 ++++-- Test/dafny4/Bug107.dfy | 16 ++++++++++++++++ Test/dafny4/Bug107.dfy.expect | 6 ++++++ 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 Test/dafny4/Bug107.dfy create mode 100644 Test/dafny4/Bug107.dfy.expect (limited to 'Source/Dafny/Compiler.cs') diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index 82795480..8f199da4 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -2310,7 +2310,9 @@ namespace Microsoft.Dafny { Contract.Requires(expr != null); if (expr is LiteralExpr) { LiteralExpr e = (LiteralExpr)expr; - if (e.Value == null) { + if (e is StaticReceiverExpr) { + wr.Write(TypeName(e.Type, wr)); + } else if (e.Value == null) { wr.Write("({0})null", TypeName(e.Type, wr)); } else if (e.Value is bool) { wr.Write((bool)e.Value ? "true" : "false"); @@ -2394,7 +2396,7 @@ namespace Microsoft.Dafny { wr.Write(".@{0}", sf.CompiledName); wr.Write(sf.PostString); } else { - TrParenExpr(e.Obj, wr, inLetExprBody); + TrExpr(e.Obj, wr, inLetExprBody); wr.Write(".@{0}", e.Member.CompileName); } diff --git a/Test/dafny4/Bug107.dfy b/Test/dafny4/Bug107.dfy new file mode 100644 index 00000000..56965d92 --- /dev/null +++ b/Test/dafny4/Bug107.dfy @@ -0,0 +1,16 @@ +// RUN: %dafny /compile:3 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +method Main() +{ + var f := Inc; + print(f(4)); +} + +function method Inc(x: int): int +{ + x + 2 +} + + + diff --git a/Test/dafny4/Bug107.dfy.expect b/Test/dafny4/Bug107.dfy.expect new file mode 100644 index 00000000..eaa3aa54 --- /dev/null +++ b/Test/dafny4/Bug107.dfy.expect @@ -0,0 +1,6 @@ + +Dafny program verifier finished with 3 verified, 0 errors +Program compiled successfully +Running... + +6 \ No newline at end of file -- cgit v1.2.3 From 9b2dea60e57be8ddb39b36ed8a282e3826cd4092 Mon Sep 17 00:00:00 2001 From: qunyanm Date: Wed, 18 Nov 2015 11:05:48 -0800 Subject: Fix issue 108. Use idGenerator to create a new collection name for each occurrence of Set/MapComprehension when translating it to c#. --- Source/Dafny/Compiler.cs | 15 +++++++++------ Test/dafny4/Bug108.dfy | 11 +++++++++++ Test/dafny4/Bug108.dfy.expect | 7 +++++++ 3 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 Test/dafny4/Bug108.dfy create mode 100644 Test/dafny4/Bug108.dfy.expect (limited to 'Source/Dafny/Compiler.cs') diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index 8f199da4..69b7a32d 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -2993,8 +2993,9 @@ namespace Microsoft.Dafny { // })() Contract.Assert(e.Bounds != null); // the resolver would have insisted on finding bounds var typeName = TypeName(e.Type.AsSetType.Arg, wr); + var collection_name = idGenerator.FreshId("_coll"); wr.Write("((Dafny.Helpers.ComprehensionDelegate<{0}>)delegate() {{ ", typeName); - wr.Write("var _coll = new System.Collections.Generic.List<{0}>(); ", typeName); + wr.Write("var {0} = new System.Collections.Generic.List<{1}>(); ", collection_name, typeName); var n = e.BoundVars.Count; Contract.Assert(e.Bounds.Count == n); for (int i = 0; i < n; i++) { @@ -3039,13 +3040,14 @@ namespace Microsoft.Dafny { } wr.Write("if ("); TrExpr(e.Range, wr, inLetExprBody); - wr.Write(") { _coll.Add("); + wr.Write(") {"); + wr.Write("{0}.Add(", collection_name); TrExpr(e.Term, wr, inLetExprBody); wr.Write("); }"); for (int i = 0; i < n; i++) { wr.Write("}"); } - wr.Write("return Dafny.Set<{0}>.FromCollection(_coll); ", typeName); + wr.Write("return Dafny.Set<{0}>.FromCollection({1}); ", typeName, collection_name); wr.Write("})()"); } else if (expr is MapComprehension) { @@ -3069,8 +3071,9 @@ namespace Microsoft.Dafny { Contract.Assert(e.Bounds != null); // the resolver would have insisted on finding bounds var domtypeName = TypeName(e.Type.AsMapType.Domain, wr); var rantypeName = TypeName(e.Type.AsMapType.Range, wr); + var collection_name = idGenerator.FreshId("_coll"); wr.Write("((Dafny.Helpers.MapComprehensionDelegate<{0},{1}>)delegate() {{ ", domtypeName, rantypeName); - wr.Write("var _coll = new System.Collections.Generic.List>(); ", domtypeName, rantypeName); + wr.Write("var {0} = new System.Collections.Generic.List>(); ", collection_name, domtypeName, rantypeName); var n = e.BoundVars.Count; Contract.Assert(e.Bounds.Count == n && n == 1); var bound = e.Bounds[0]; @@ -3112,11 +3115,11 @@ namespace Microsoft.Dafny { wr.Write("if ("); TrExpr(e.Range, wr, inLetExprBody); wr.Write(") { "); - wr.Write("_coll.Add(new Dafny.Pair<{0},{1}>(@{2},", domtypeName, rantypeName, bv.CompileName); + wr.Write("{0}.Add(new Dafny.Pair<{1},{2}>(@{3},", collection_name, domtypeName, rantypeName, bv.CompileName); TrExpr(e.Term, wr, inLetExprBody); wr.Write(")); }"); wr.Write("}"); - wr.Write("return Dafny.Map<{0},{1}>.FromCollection(_coll); ", domtypeName, rantypeName); + wr.Write("return Dafny.Map<{0},{1}>.FromCollection({2}); ", domtypeName, rantypeName, collection_name); wr.Write("})()"); } else if (expr is LambdaExpr) { diff --git a/Test/dafny4/Bug108.dfy b/Test/dafny4/Bug108.dfy new file mode 100644 index 00000000..17cb9f12 --- /dev/null +++ b/Test/dafny4/Bug108.dfy @@ -0,0 +1,11 @@ +// RUN: %dafny /compile:3 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +method Main() { + var A := map[0 := 1]; + var B := map x | x in (set y | y in A) :: A[x]; + print A, "\n"; + print B, "\n"; +} + + diff --git a/Test/dafny4/Bug108.dfy.expect b/Test/dafny4/Bug108.dfy.expect new file mode 100644 index 00000000..94e65ba2 --- /dev/null +++ b/Test/dafny4/Bug108.dfy.expect @@ -0,0 +1,7 @@ + +Dafny program verifier finished with 2 verified, 0 errors +Program compiled successfully +Running... + +map[0 := 1] +map[0 := 1] -- cgit v1.2.3 From e2bab8d4be0805bf22f7295be2014a39d1e18535 Mon Sep 17 00:00:00 2001 From: qunyanm Date: Thu, 3 Dec 2015 11:14:28 -0800 Subject: Fix issue 113. Make sure the tempVar name used in ToString() method doesn't collide with the names of its formals. --- Source/Dafny/Compiler.cs | 32 ++++++++++++++++++++++++++------ Test/dafny4/Bug113.dfy | 10 ++++++++++ Test/dafny4/Bug113.dfy.expect | 6 ++++++ 3 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 Test/dafny4/Bug113.dfy create mode 100644 Test/dafny4/Bug113.dfy.expect (limited to 'Source/Dafny/Compiler.cs') diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index 69b7a32d..264ecf9b 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -473,22 +473,23 @@ namespace Microsoft.Dafny { } else { nm = (dt.Module.IsDefaultModule ? "" : dt.Module.CompileName + ".") + dt.CompileName + "." + ctor.CompileName; } - Indent(ind + IndentAmount, wr); wr.WriteLine("string s = \"{0}\";", nm); + var tempVar = GenVarName("s", ctor.Formals); + Indent(ind + IndentAmount, wr); wr.WriteLine("string {0} = \"{1}\";", tempVar, nm); if (ctor.Formals.Count != 0) { - Indent(ind + IndentAmount, wr); wr.WriteLine("s += \"(\";"); + Indent(ind + IndentAmount, wr); wr.WriteLine("{0} += \"(\";", tempVar); i = 0; foreach (var arg in ctor.Formals) { if (!arg.IsGhost) { if (i != 0) { - Indent(ind + IndentAmount, wr); wr.WriteLine("s += \", \";"); + Indent(ind + IndentAmount, wr); wr.WriteLine("{0} += \", \";", tempVar); } - Indent(ind + IndentAmount, wr); wr.WriteLine("s += @{0}.ToString();", FormalName(arg, i)); + Indent(ind + IndentAmount, wr); wr.WriteLine("{0} += @{1}.ToString();", tempVar,FormalName(arg, i)); i++; } } - Indent(ind + IndentAmount, wr); wr.WriteLine("s += \")\";"); + Indent(ind + IndentAmount, wr); wr.WriteLine("{0} += \")\";", tempVar); } - Indent(ind + IndentAmount, wr); wr.WriteLine("return s;"); + Indent(ind + IndentAmount, wr); wr.WriteLine("return {0};", tempVar); Indent(ind, wr); wr.WriteLine("}"); } @@ -497,6 +498,25 @@ namespace Microsoft.Dafny { constructorIndex++; } + // create a varName that is not a duplicate of formals' name + string GenVarName(string root, List formals) { + bool finished = false; + while (!finished) { + finished = true; + int i = 0; + foreach (var arg in formals) { + if (!arg.IsGhost) { + if (root.Equals(FormalName(arg, i))) { + root += root; + finished = false; + } + i++; + } + } + } + return root; + } + void CompileDatatypeStruct(DatatypeDecl dt, int indent, TextWriter wr) { Contract.Requires(dt != null); diff --git a/Test/dafny4/Bug113.dfy b/Test/dafny4/Bug113.dfy new file mode 100644 index 00000000..8f5ddf9f --- /dev/null +++ b/Test/dafny4/Bug113.dfy @@ -0,0 +1,10 @@ +// RUN: %dafny /compile:3 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +datatype D = D(q:int, r:int, s:int, t:int) + +method Main() +{ + print D(10, 20, 30, 40); + print "\n"; +} \ No newline at end of file diff --git a/Test/dafny4/Bug113.dfy.expect b/Test/dafny4/Bug113.dfy.expect new file mode 100644 index 00000000..c4be010e --- /dev/null +++ b/Test/dafny4/Bug113.dfy.expect @@ -0,0 +1,6 @@ + +Dafny program verifier finished with 2 verified, 0 errors +Program compiled successfully +Running... + +D.D(10, 20, 30, 40) -- cgit v1.2.3 From bfd722fb0f5654fac4c3510625e5eec0a09cdfd1 Mon Sep 17 00:00:00 2001 From: qunyanm Date: Tue, 5 Jan 2016 10:14:00 -0800 Subject: Fix issue 116. Add the missing @ for the generated c# code. --- Source/Dafny/Compiler.cs | 3 +-- Test/dafny4/Bug116.dfy | 15 +++++++++++++++ Test/dafny4/Bug116.dfy.expect | 3 +++ 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 Test/dafny4/Bug116.dfy create mode 100644 Test/dafny4/Bug116.dfy.expect (limited to 'Source/Dafny/Compiler.cs') diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index 264ecf9b..1a99a8af 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -2507,8 +2507,7 @@ namespace Microsoft.Dafny { DatatypeValue dtv = (DatatypeValue)expr; Contract.Assert(dtv.Ctor != null); // since dtv has been successfully resolved var typeParams = dtv.InferredTypeArgs.Count == 0 ? "" : string.Format("<{0}>", TypeNames(dtv.InferredTypeArgs, wr)); - - wr.Write("new {0}{1}(", DtName(dtv.Ctor.EnclosingDatatype), typeParams); + wr.Write("new @{0}{1}(", DtName(dtv.Ctor.EnclosingDatatype), typeParams); if (!dtv.IsCoCall) { // For an ordinary constructor (that is, one that does not guard any co-recursive calls), generate: // new Dt_Cons( args ) diff --git a/Test/dafny4/Bug116.dfy b/Test/dafny4/Bug116.dfy new file mode 100644 index 00000000..9fd30597 --- /dev/null +++ b/Test/dafny4/Bug116.dfy @@ -0,0 +1,15 @@ +// RUN: %dafny "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +datatype struct = S // this is ok + +method Main() +{ + var s := S; // this line generates illegal C# code + print s; +} + + + + + diff --git a/Test/dafny4/Bug116.dfy.expect b/Test/dafny4/Bug116.dfy.expect new file mode 100644 index 00000000..b0cf7300 --- /dev/null +++ b/Test/dafny4/Bug116.dfy.expect @@ -0,0 +1,3 @@ + +Dafny program verifier finished with 2 verified, 0 errors +Compiled assembly into Bug116.exe -- cgit v1.2.3 From 17405efd598d2a8a2dac304ee9a7f7d9bd30a558 Mon Sep 17 00:00:00 2001 From: "Richard L. Ford" Date: Wed, 27 Jan 2016 14:09:16 -0800 Subject: Implement 'extern' declaration modifier. The 'extern' declaration modifier provides a more convenient way of interfacing Dafny code with C# source files and .Net DLLs. We support an 'extern' keyword on a module, class, function method, or method (cannot extern ghost). We check the CompileNames of all modules to make sure there are no duplicate names. Every Dafny-generated C# class is marked partial, so it can potentially be extended. The extern keyword could be accompanied by an optional string naming the corresponding C# method/class to connect to. If not given the name of the method/class is used. An 'extern' keyword implies an {:axiom} attribute for functions and methods, so their ensures clauses are assumed to be true without proof. In addition to the .dfy files, the user may supply C# files (.cs) and dynamic linked libraries (.dll) on command line. These will be passed onto the C# compiler, the .cs files as sources, and the .dll files as references. As part of this change the grammar was refactored some. New productions are - TopDecls - a list of top-level declarations. - TopDecl - a single top-level declaration - DeclModifiers - a list of declaration modifiers which are either 'abstract', 'ghost', 'static', 'protected', or 'extern'. They can be in any order and we diagnose duplicates. In addition, since they are not all allowed in all contexts, we check and give diagnostics if an DeclModifier appears where it is not allowed. --- Source/Dafny/Compiler.cs | 2 +- Source/Dafny/Dafny.atg | 356 +++++--- Source/Dafny/DafnyAst.cs | 31 +- Source/Dafny/Makefile | 4 +- Source/Dafny/Parser.cs | 1534 ++++++++++++++++++--------------- Source/Dafny/Resolver.cs | 29 + Source/Dafny/Scanner.cs | 169 ++-- Source/DafnyDriver/DafnyDriver.cs | 121 ++- Source/DafnyExtension/DafnyDriver.cs | 6 +- Test/dafny0/Extern.dfy | 27 + Test/dafny0/Extern.dfy.expect | 4 + Test/dafny0/Extern2.cs | 14 + Test/dafny0/ExternHelloLibrary.cs | 15 + Test/dafny0/ExternHelloLibrary.dll | Bin 0 -> 3072 bytes Test/dafny0/ExternNegative.dfy | 26 + Test/dafny0/ExternNegative.dfy.expect | 3 + Test/dafny0/ExternNegative2.dfy | 26 + 17 files changed, 1407 insertions(+), 960 deletions(-) create mode 100644 Test/dafny0/Extern.dfy create mode 100644 Test/dafny0/Extern.dfy.expect create mode 100644 Test/dafny0/Extern2.cs create mode 100644 Test/dafny0/ExternHelloLibrary.cs create mode 100644 Test/dafny0/ExternHelloLibrary.dll create mode 100644 Test/dafny0/ExternNegative.dfy create mode 100644 Test/dafny0/ExternNegative.dfy.expect create mode 100644 Test/dafny0/ExternNegative2.dfy (limited to 'Source/Dafny/Compiler.cs') diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index 1a99a8af..f5f95bd2 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -254,7 +254,7 @@ namespace Microsoft.Dafny { else if (d is ClassDecl) { var cl = (ClassDecl)d; Indent(indent, wr); - wr.Write("public class @{0}", cl.CompileName); + wr.Write("public partial class @{0}", cl.CompileName); if (cl.TypeArgs.Count != 0) { wr.Write("<{0}>", TypeParameters(cl.TypeArgs)); } diff --git a/Source/Dafny/Dafny.atg b/Source/Dafny/Dafny.atg index 08c22db4..87e75541 100644 --- a/Source/Dafny/Dafny.atg +++ b/Source/Dafny/Dafny.atg @@ -23,10 +23,113 @@ readonly BuiltIns theBuiltIns; readonly bool theVerifyThisFile; int anonymousIds = 0; -struct MemberModifiers { +/// +/// Holds the modifiers given for a declaration +/// +/// Not all modifiers are applicable to all kinds of declarations. +/// Errors are given when a modify does not apply. +/// We also record the tokens for the specified modifiers so that +/// they can be used in error messages. +/// +struct DeclModifierData { + public bool IsAbstract; + public IToken AbstractToken; public bool IsGhost; + public IToken GhostToken; public bool IsStatic; + public IToken StaticToken; public bool IsProtected; + public IToken ProtectedToken; + public bool IsExtern; + public IToken ExternToken; + public StringLiteralExpr ExternName; + +} + +// Check that token has not been set, then set it. +public void CheckAndSetToken(ref IToken token) +{ + if (token != null) { + SemErr(t, "Duplicate declaration modifier: " + t.val); + } + token = t; +} + +/// +// A flags type used to tell what declaration modifiers are allowed for a declaration. +/// +[Flags] +enum AllowedDeclModifiers { + None = 0, + Abstract = 1, + Ghost = 2, + + // Means ghost not allowed because already implicitly ghost. + AlreadyGhost = 4, + Static = 8, + Protected = 16, + Extern = 32 +}; + +/// +/// Check the declaration modifiers against those that are allowed. +/// +/// The 'allowed' parameter specifies which declaratio modifiers are allowed. +/// The 'declCaption' parameter should be a string describing the kind of declaration. +/// It is used in error messages. +/// Any declaration modifiers that are present but not allowed are cleared. +/// +void CheckDeclModifiers(DeclModifierData dmod, string declCaption, AllowedDeclModifiers allowed) +{ + if (dmod.IsAbstract && ((allowed & AllowedDeclModifiers.Abstract) == 0)) { + SemErr(dmod.AbstractToken, declCaption + " cannot be declared 'abstract'."); + dmod.IsAbstract = false; + } + if (dmod.IsGhost) { + if ((allowed & AllowedDeclModifiers.AlreadyGhost) != 0) { + SemErr(dmod.GhostToken, declCaption + " cannot be declared ghost (they are 'ghost' by default)."); + dmod.IsGhost = false; + } else if ((allowed & AllowedDeclModifiers.Ghost) == 0) { + SemErr(dmod.GhostToken, declCaption + " cannot be declared 'ghost'."); + dmod.IsGhost = false; + } + } + if (dmod.IsStatic && ((allowed & AllowedDeclModifiers.Static) == 0)) { + SemErr(dmod.StaticToken, declCaption + " cannot be declared 'static'."); + dmod.IsStatic = false; + } + if (dmod.IsProtected && ((allowed & AllowedDeclModifiers.Protected) == 0)) { + SemErr(dmod.ProtectedToken, declCaption + " cannot be declared 'protected'."); + dmod.IsProtected = false; + } + if (dmod.IsExtern && ((allowed & AllowedDeclModifiers.Extern) == 0)) { + SemErr(dmod.ExternToken, declCaption + " cannot be declared 'extern'."); + dmod.IsExtern = false; + } +} + +/// +/// Encode an 'extern' declaration modifier as an {:extern name} attribute. +/// +/// We also include an {:axiom} attribute since the specification of an +/// external entity is assumed to hold, but only for methods or functions. +/// +static void EncodeExternAsAttribute(DeclModifierData dmod, ref Attributes attrs, IToken/*!*/ id, bool needAxiom) { + if (dmod.IsExtern) { + StringLiteralExpr name = dmod.ExternName; + if (name == null) { + bool isVerbatimString = false; + name = new StringLiteralExpr(id, id.val, isVerbatimString); + } + var args = new List(); + args.Add(name); + attrs = new Attributes("extern", args, attrs); + + // Also 'extern' implies 'axiom' for methods or functions. + if (needAxiom) { + attrs = new Attributes("axiom", new List(), attrs); + } + } } /// @@ -516,13 +619,10 @@ IGNORE cr + lf + tab /*------------------------------------------------------------------------*/ PRODUCTIONS Dafny -= (. ClassDecl/*!*/ c; DatatypeDecl/*!*/ dt; TopLevelDecl td; IteratorDecl iter; - List membersDefaultClass = new List(); - ModuleDecl submodule; += (. List membersDefaultClass = new List(); // to support multiple files, create a default module only if theModule is null DefaultModuleDecl defaultModule = (DefaultModuleDecl)((LiteralModuleDecl)theModule).ModuleDef; // theModule should be a DefaultModuleDecl (actually, the singular DefaultModuleDecl) - TraitDecl/*!*/ trait; Contract.Assert(defaultModule != null); .) { "include" stringToken (. { @@ -540,15 +640,7 @@ Dafny } .) } - { SubModuleDecl (. defaultModule.TopLevelDecls.Add(submodule); .) - | ClassDecl (. defaultModule.TopLevelDecls.Add(c); .) - | DatatypeDecl (. defaultModule.TopLevelDecls.Add(dt); .) - | NewtypeDecl (. defaultModule.TopLevelDecls.Add(td); .) - | OtherTypeDecl (. defaultModule.TopLevelDecls.Add(td); .) - | IteratorDecl (. defaultModule.TopLevelDecls.Add(iter); .) - | TraitDecl (. defaultModule.TopLevelDecls.Add(trait); .) - | ClassMemberDecl - } + TopDecls (. // find the default class in the default module, then append membersDefaultClass to its member list DefaultClassDecl defaultClass = null; foreach (TopLevelDecl topleveldecl in defaultModule.TopLevelDecls) { @@ -564,43 +656,71 @@ Dafny } .) EOF . -SubModuleDecl -= (. ClassDecl/*!*/ c; DatatypeDecl/*!*/ dt; TopLevelDecl td; IteratorDecl iter; - Attributes attrs = null; IToken/*!*/ id; - TraitDecl/*!*/ trait; - List namedModuleDefaultClassMembers = new List();; + +TopDecls<. ModuleDefinition module, List membersDefaultClass, bool isTopLevel, bool isAbstract .> += { TopDecl } + . + +DeclModifiers += (. dmod = new DeclModifierData(); .) + { "abstract" (. dmod.IsAbstract = true; CheckAndSetToken(ref dmod.AbstractToken); .) + | "ghost" (. dmod.IsGhost = true; CheckAndSetToken(ref dmod.GhostToken); .) + | "static" (. dmod.IsStatic = true; CheckAndSetToken(ref dmod.StaticToken); .) + | "protected" (. dmod.IsProtected = true; CheckAndSetToken(ref dmod.ProtectedToken); .) + | "extern" (. dmod.IsExtern = true; CheckAndSetToken(ref dmod.ExternToken); .) + [ stringToken (. bool isVerbatimString; + string s = Util.RemoveParsedStringQuotes(t.val, out isVerbatimString); + dmod.ExternName = new StringLiteralExpr(t, s, isVerbatimString); + .) + ] + } + . + +TopDecl<. ModuleDefinition module, List membersDefaultClass, bool isTopLevel, bool isAbstract .> += (. DeclModifierData dmod; ModuleDecl submodule; + ClassDecl/*!*/ c; DatatypeDecl/*!*/ dt; TopLevelDecl td; IteratorDecl iter; + TraitDecl/*!*/ trait; + .) + DeclModifiers + ( SubModuleDecl (. module.TopLevelDecls.Add(submodule); .) + | ClassDecl (. module.TopLevelDecls.Add(c); .) + | DatatypeDecl (. module.TopLevelDecls.Add(dt); .) + | NewtypeDecl (. module.TopLevelDecls.Add(td); .) + | OtherTypeDecl (. module.TopLevelDecls.Add(td); .) + | IteratorDecl (. module.TopLevelDecls.Add(iter); .) + | TraitDecl (. module.TopLevelDecls.Add(trait); .) + | ClassMemberDecl + ) . + +SubModuleDecl += (. Attributes attrs = null; IToken/*!*/ id; + List namedModuleDefaultClassMembers = new List();; List idRefined = null, idPath = null, idAssignment = null; ModuleDefinition module; - ModuleDecl sm; submodule = null; // appease compiler - bool isAbstract = false; + bool isAbstract = dmod.IsAbstract; bool isExclusively = false; bool opened = false; + CheckDeclModifiers(dmod, "Modules", AllowedDeclModifiers.Abstract | AllowedDeclModifiers.Extern); .) - ( [ "abstract" (. isAbstract = true; .) ] - "module" + ( "module" { Attribute } NoUSIdent + (. EncodeExternAsAttribute(dmod, ref attrs, id, /* needAxiom */ false); .) [ "exclusively" "refines" QualifiedModuleName (. isExclusively = true; .) | "refines" QualifiedModuleName (. isExclusively = false; .) ] (. module = new ModuleDefinition(id, id.val, isAbstract, false, isExclusively, idRefined == null ? null : idRefined, parent, attrs, false, this); .) "{" (. module.BodyStartTok = t; .) - { SubModuleDecl (. module.TopLevelDecls.Add(sm); .) - | ClassDecl (. module.TopLevelDecls.Add(c); .) - | TraitDecl (. module.TopLevelDecls.Add(trait); .) - | DatatypeDecl (. module.TopLevelDecls.Add(dt); .) - | NewtypeDecl (. module.TopLevelDecls.Add(td); .) - | OtherTypeDecl (. module.TopLevelDecls.Add(td); .) - | IteratorDecl (. module.TopLevelDecls.Add(iter); .) - | ClassMemberDecl - } - "}" (. module.BodyEndTok = t; + TopDecls + "}" (. module.BodyEndTok = t; module.TopLevelDecls.Add(new DefaultClassDecl(module, namedModuleDefaultClassMembers)); submodule = new LiteralModuleDecl(module, parent); .) | "import" ["opened" (.opened = true;.)] NoUSIdent + (. EncodeExternAsAttribute(dmod, ref attrs, id, /* needAxiom */ false); .) [ "=" QualifiedModuleName (. submodule = new AliasModuleDecl(idPath, id, parent, opened); .) | "as" QualifiedModuleName ["default" QualifiedModuleName ] @@ -629,7 +749,7 @@ QualifiedModuleName<.out List ids.> } . -ClassDecl +ClassDecl = (. Contract.Requires(module != null); Contract.Ensures(Contract.ValueAtReturn(out c) != null); IToken/*!*/ id; @@ -639,18 +759,21 @@ ClassDecl List typeArgs = new List(); List members = new List(); IToken bodyStart; + CheckDeclModifiers(dmodClass, "Classes", AllowedDeclModifiers.Extern); + DeclModifierData dmod; .) SYNC "class" { Attribute } NoUSIdent + (. EncodeExternAsAttribute(dmodClass, ref attrs, id, /* needAxiom */ false); .) [ GenericParameters ] ["extends" Type (. traits.Add(trait); .) {"," Type (. traits.Add(trait); .) } ] "{" (. bodyStart = t; .) - { ClassMemberDecl + { DeclModifiers ClassMemberDecl } "}" (. c = new ClassDecl(id, id.val, module, typeArgs, members, attrs, traits); @@ -659,14 +782,16 @@ ClassDecl .) . - TraitDecl - = (. Contract.Requires(module != null); +TraitDecl + = (. Contract.Requires(module != null); + CheckDeclModifiers(dmodIn, "Traits", AllowedDeclModifiers.None); Contract.Ensures(Contract.ValueAtReturn(out trait) != null); IToken/*!*/ id; Attributes attrs = null; List typeArgs = new List(); //traits should not support type parameters at the moment List members = new List(); IToken bodyStart; + DeclModifierData dmod; .) SYNC "trait" @@ -674,7 +799,7 @@ ClassDecl NoUSIdent [ GenericParameters ] "{" (. bodyStart = t; .) - { ClassMemberDecl + { DeclModifiers ClassMemberDecl } "}" (. trait = new TraitDecl(id, id.val, module, typeArgs, members, attrs); @@ -683,44 +808,33 @@ ClassDecl .) . -ClassMemberDecl<.List mm, bool allowConstructors, bool moduleLevelDecl, bool isWithinAbstractModule.> +ClassMemberDecl<. DeclModifierData dmod, List mm, bool allowConstructors, bool moduleLevelDecl, bool isWithinAbstractModule.> = (. Contract.Requires(cce.NonNullElements(mm)); Method/*!*/ m; Function/*!*/ f; - MemberModifiers mmod = new MemberModifiers(); - IToken staticToken = null, protectedToken = null; .) - { "ghost" (. mmod.IsGhost = true; .) - | "static" (. mmod.IsStatic = true; staticToken = t; .) - | "protected" (. mmod.IsProtected = true; protectedToken = t; .) - } ( (. if (moduleLevelDecl) { SemErr(la, "fields are not allowed to be declared at the module level; instead, wrap the field in a 'class' declaration"); - mmod.IsStatic = false; - mmod.IsProtected = false; + dmod.IsStatic = false; } .) - FieldDecl + FieldDecl | IF(IsFunctionDecl()) - (. if (moduleLevelDecl && staticToken != null) { - errors.Warning(staticToken, "module-level functions are always non-instance, so the 'static' keyword is not allowed here"); - mmod.IsStatic = false; + (. if (moduleLevelDecl && dmod.StaticToken != null) { + errors.Warning(dmod.StaticToken, "module-level functions are always non-instance, so the 'static' keyword is not allowed here"); + dmod.IsStatic = false; } .) - FunctionDecl (. mm.Add(f); .) - | (. if (moduleLevelDecl && staticToken != null) { - errors.Warning(staticToken, "module-level methods are always non-instance, so the 'static' keyword is not allowed here"); - mmod.IsStatic = false; - } - if (protectedToken != null) { - SemErr(protectedToken, "only functions, not methods, can be declared 'protected'"); - mmod.IsProtected = false; + FunctionDecl (. mm.Add(f); .) + | (. if (moduleLevelDecl && dmod.StaticToken != null) { + errors.Warning(dmod.StaticToken, "module-level methods are always non-instance, so the 'static' keyword is not allowed here"); + dmod.IsStatic = false; } .) - MethodDecl (. mm.Add(m); .) + MethodDecl (. mm.Add(m); .) ) . -DatatypeDecl +DatatypeDecl = (. Contract.Requires(module != null); Contract.Ensures(Contract.ValueAtReturn(out dt)!=null); IToken/*!*/ id; @@ -729,6 +843,7 @@ DatatypeDecl List ctors = new List(); IToken bodyStart = Token.NoToken; // dummy assignment bool co = false; + CheckDeclModifiers(dmod, "Datatypes or codatatypes", AllowedDeclModifiers.None); .) SYNC ( "datatype" @@ -766,27 +881,27 @@ DatatypeMemberDecl<.List/*!*/ ctors.> [ FormalsOptionalIds ] (. ctors.Add(new DatatypeCtor(id, id.val, formals, attrs)); .) . -FieldDecl<.MemberModifiers mmod, List/*!*/ mm.> +FieldDecl<.DeclModifierData dmod, List/*!*/ mm.> = (. Contract.Requires(cce.NonNullElements(mm)); Attributes attrs = null; IToken/*!*/ id; Type/*!*/ ty; + CheckDeclModifiers(dmod, "Fields", AllowedDeclModifiers.Ghost); .) SYNC "var" - (. if (mmod.IsStatic) { SemErr(t, "fields cannot be declared 'static'"); } - .) { Attribute } - FIdentType (. mm.Add(new Field(id, id.val, mmod.IsGhost, ty, attrs)); .) - { "," FIdentType (. mm.Add(new Field(id, id.val, mmod.IsGhost, ty, attrs)); .) + FIdentType (. mm.Add(new Field(id, id.val, dmod.IsGhost, ty, attrs)); .) + { "," FIdentType (. mm.Add(new Field(id, id.val, dmod.IsGhost, ty, attrs)); .) } OldSemi . -NewtypeDecl +NewtypeDecl = (. IToken id, bvId; Attributes attrs = null; td = null; Type baseType = null; Expression wh; + CheckDeclModifiers(dmod, "Newtypes", AllowedDeclModifiers.None); .) "newtype" { Attribute } @@ -800,13 +915,14 @@ NewtypeDecl | Type (. td = new NewtypeDecl(theVerifyThisFile ? id : new IncludeToken(id), id.val, module, baseType, attrs); .) ) . -OtherTypeDecl +OtherTypeDecl = (. IToken id; Attributes attrs = null; var eqSupport = TypeParameter.EqualitySupportValue.Unspecified; var typeArgs = new List(); td = null; Type ty; + CheckDeclModifiers(dmod, "Type aliases", AllowedDeclModifiers.None); .) "type" { Attribute } @@ -902,7 +1018,7 @@ TypeIdentOptional +IteratorDecl = (. Contract.Ensures(Contract.ValueAtReturn(out iter) != null); IToken/*!*/ id; Attributes attrs = null; @@ -924,6 +1040,7 @@ IteratorDecl IToken signatureEllipsis = null; IToken bodyStart = Token.NoToken; IToken bodyEnd = Token.NoToken; + CheckDeclModifiers(dmod, "Iterators", AllowedDeclModifiers.None); .) SYNC "iterator" @@ -969,7 +1086,7 @@ GenericParameters<.List/*!*/ typeArgs.> ">" . /*------------------------------------------------------------------------*/ -MethodDecl +MethodDecl = (. Contract.Ensures(Contract.ValueAtReturn(out m) !=null); IToken/*!*/ id = Token.NoToken; bool hasName = false; IToken keywordToken; @@ -991,43 +1108,36 @@ MethodDecl } [ NoUSIdent (. hasName = true; .) ] @@ -1037,12 +1147,13 @@ MethodDecl ] - Formals + Formals [ "returns" (. if (isConstructor) { SemErr(t, "constructors cannot have out-parameters"); } .) - Formals + Formals ] | "..." (. signatureEllipsis = t; .) ) @@ -1059,16 +1170,16 @@ MethodDecl(mod, modAttrs), ens, new Specification(dec, decAttrs), body, attrs, signatureEllipsis); } else if (isIndLemma) { - m = new InductiveLemma(tok, id.val, mmod.IsStatic, typeArgs, ins, outs, + m = new InductiveLemma(tok, id.val, dmod.IsStatic, typeArgs, ins, outs, req, new Specification(mod, modAttrs), ens, new Specification(dec, decAttrs), body, attrs, signatureEllipsis); } else if (isCoLemma) { - m = new CoLemma(tok, id.val, mmod.IsStatic, typeArgs, ins, outs, + m = new CoLemma(tok, id.val, dmod.IsStatic, typeArgs, ins, outs, req, new Specification(mod, modAttrs), ens, new Specification(dec, decAttrs), body, attrs, signatureEllipsis); } else if (isLemma) { - m = new Lemma(tok, id.val, mmod.IsStatic, typeArgs, ins, outs, + m = new Lemma(tok, id.val, dmod.IsStatic, typeArgs, ins, outs, req, new Specification(mod, modAttrs), ens, new Specification(dec, decAttrs), body, attrs, signatureEllipsis); } else { - m = new Method(tok, id.val, mmod.IsStatic, mmod.IsGhost, typeArgs, ins, outs, + m = new Method(tok, id.val, dmod.IsStatic, dmod.IsGhost, typeArgs, ins, outs, req, new Specification(mod, modAttrs), ens, new Specification(dec, decAttrs), body, attrs, signatureEllipsis); } m.BodyStartTok = bodyStart; @@ -1273,7 +1384,7 @@ GenericInstantiation<.List/*!*/ gt.> ">" . /*------------------------------------------------------------------------*/ -FunctionDecl +FunctionDecl = (. Contract.Ensures(Contract.ValueAtReturn(out f)!=null); Attributes attrs = null; IToken/*!*/ id = Token.NoToken; // to please compiler @@ -1296,7 +1407,13 @@ FunctionDecl } NoUSIdent @@ -1312,7 +1429,13 @@ FunctionDecl } NoUSIdent @@ -1327,7 +1450,8 @@ FunctionDecl } NoUSIdent @@ -1341,7 +1465,8 @@ FunctionDecl } NoUSIdent @@ -1358,22 +1483,23 @@ FunctionDecl } [ FunctionBody ] - (. if (!isWithinAbstractModule && DafnyOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 && !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported")) { + (. if (!isWithinAbstractModule && DafnyOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 && + !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported")) { SemErr(t, "a function with an ensures clause must have a body, unless given the :axiom attribute"); } - + EncodeExternAsAttribute(dmod, ref attrs, id, /* needAxiom */ true); IToken tok = theVerifyThisFile ? id : new IncludeToken(id); if (isPredicate) { - f = new Predicate(tok, id.val, mmod.IsStatic, mmod.IsProtected, !isFunctionMethod, typeArgs, formals, + f = new Predicate(tok, id.val, dmod.IsStatic, dmod.IsProtected, !isFunctionMethod, typeArgs, formals, reqs, reads, ens, new Specification(decreases, null), body, Predicate.BodyOriginKind.OriginalOrInherited, attrs, signatureEllipsis); } else if (isIndPredicate) { - f = new InductivePredicate(tok, id.val, mmod.IsStatic, mmod.IsProtected, typeArgs, formals, + f = new InductivePredicate(tok, id.val, dmod.IsStatic, dmod.IsProtected, typeArgs, formals, reqs, reads, ens, body, attrs, signatureEllipsis); } else if (isCoPredicate) { - f = new CoPredicate(tok, id.val, mmod.IsStatic, mmod.IsProtected, typeArgs, formals, + f = new CoPredicate(tok, id.val, dmod.IsStatic, dmod.IsProtected, typeArgs, formals, reqs, reads, ens, body, attrs, signatureEllipsis); } else { - f = new Function(tok, id.val, mmod.IsStatic, mmod.IsProtected, !isFunctionMethod, typeArgs, formals, returnType, + f = new Function(tok, id.val, dmod.IsStatic, dmod.IsProtected, !isFunctionMethod, typeArgs, formals, returnType, reqs, reads, ens, new Specification(decreases, null), body, attrs, signatureEllipsis); } f.BodyStartTok = bodyStart; diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index 4c1e2bd7..a51e30de 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -1059,7 +1059,7 @@ namespace Microsoft.Dafny { public virtual string FullCompileName { get { if (ResolvedClass != null && !ResolvedClass.Module.IsDefaultModule) { - return ResolvedClass.Module.CompileName + ".@" + CompileName; + return ResolvedClass.Module.CompileName + ".@" + ResolvedClass.CompileName; } else { return CompileName; } @@ -1536,7 +1536,17 @@ namespace Microsoft.Dafny { public virtual string CompileName { get { if (compileName == null) { - compileName = NonglobalVariable.CompilerizeName(Name); + object externValue = ""; + string errorMessage = ""; + bool isExternal = Attributes.ContainsMatchingValue(this.Attributes, "extern", ref externValue, + new Attributes.MatchingValueOption[] { Attributes.MatchingValueOption.String }, + err => errorMessage = err); + if (isExternal) { + compileName = (string)externValue; + } + else { + compileName = NonglobalVariable.CompilerizeName(Name); + } } return compileName; } @@ -1835,10 +1845,19 @@ namespace Microsoft.Dafny { public string CompileName { get { if (compileName == null) { - if (IsBuiltinName) - compileName = Name; - else - compileName = "_" + Height.ToString() + "_" + NonglobalVariable.CompilerizeName(Name); + object externValue = ""; + string errorMessage = ""; + bool isExternal = Attributes.ContainsMatchingValue(this.Attributes, "extern", ref externValue, + new Attributes.MatchingValueOption[] { Attributes.MatchingValueOption.String }, + err => errorMessage = err); + if (isExternal) { + compileName = (string)externValue; + } else { + if (IsBuiltinName) + compileName = Name; + else + compileName = "_" + Height.ToString() + "_" + NonglobalVariable.CompilerizeName(Name); + } } return compileName; } diff --git a/Source/Dafny/Makefile b/Source/Dafny/Makefile index 68ab7a2d..a61539b0 100644 --- a/Source/Dafny/Makefile +++ b/Source/Dafny/Makefile @@ -4,8 +4,8 @@ # from http://boogiepartners.codeplex.com/. Update the FRAME_DIR variable to # point to whatever directory you install that into. # ############################################################################### -COCO_EXE_DIR = ..\..\..\boogie-partners\CocoR\bin -FRAME_DIR = ..\..\..\boogie-partners\CocoR\Modified +COCO_EXE_DIR = ..\..\..\coco +FRAME_DIR = ..\..\..\boogiepartners\CocoR\Modified COCO = $(COCO_EXE_DIR)\Coco.exe # "all" depends on 2 files, really (Parser.cs and Scanner.cs), but they diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs index 922aad75..dc661fc5 100644 --- a/Source/Dafny/Parser.cs +++ b/Source/Dafny/Parser.cs @@ -73,7 +73,7 @@ public class Parser { public const int _star = 57; public const int _notIn = 58; public const int _ellipsis = 59; - public const int maxT = 138; + public const int maxT = 139; const bool _T = true; const bool _x = false; @@ -95,10 +95,113 @@ readonly BuiltIns theBuiltIns; readonly bool theVerifyThisFile; int anonymousIds = 0; -struct MemberModifiers { +/// +/// Holds the modifiers given for a declaration +/// +/// Not all modifiers are applicable to all kinds of declarations. +/// Errors are given when a modify does not apply. +/// We also record the tokens for the specified modifiers so that +/// they can be used in error messages. +/// +struct DeclModifierData { + public bool IsAbstract; + public IToken AbstractToken; public bool IsGhost; + public IToken GhostToken; public bool IsStatic; + public IToken StaticToken; public bool IsProtected; + public IToken ProtectedToken; + public bool IsExtern; + public IToken ExternToken; + public StringLiteralExpr ExternName; + +} + +// Check that token has not been set, then set it. +public void CheckAndSetToken(ref IToken token) +{ + if (token != null) { + SemErr(t, "Duplicate declaration modifier: " + t.val); + } + token = t; +} + +/// +// A flags type used to tell what declaration modifiers are allowed for a declaration. +/// +[Flags] +enum AllowedDeclModifiers { + None = 0, + Abstract = 1, + Ghost = 2, + + // Means ghost not allowed because already implicitly ghost. + AlreadyGhost = 4, + Static = 8, + Protected = 16, + Extern = 32 +}; + +/// +/// Check the declaration modifiers against those that are allowed. +/// +/// The 'allowed' parameter specifies which declaratio modifiers are allowed. +/// The 'declCaption' parameter should be a string describing the kind of declaration. +/// It is used in error messages. +/// Any declaration modifiers that are present but not allowed are cleared. +/// +void CheckDeclModifiers(DeclModifierData dmod, string declCaption, AllowedDeclModifiers allowed) +{ + if (dmod.IsAbstract && ((allowed & AllowedDeclModifiers.Abstract) == 0)) { + SemErr(dmod.AbstractToken, declCaption + " cannot be declared 'abstract'."); + dmod.IsAbstract = false; + } + if (dmod.IsGhost) { + if ((allowed & AllowedDeclModifiers.AlreadyGhost) != 0) { + SemErr(dmod.GhostToken, declCaption + " cannot be declared ghost (they are 'ghost' by default)."); + dmod.IsGhost = false; + } else if ((allowed & AllowedDeclModifiers.Ghost) == 0) { + SemErr(dmod.GhostToken, declCaption + " cannot be declared 'ghost'."); + dmod.IsGhost = false; + } + } + if (dmod.IsStatic && ((allowed & AllowedDeclModifiers.Static) == 0)) { + SemErr(dmod.StaticToken, declCaption + " cannot be declared 'static'."); + dmod.IsStatic = false; + } + if (dmod.IsProtected && ((allowed & AllowedDeclModifiers.Protected) == 0)) { + SemErr(dmod.ProtectedToken, declCaption + " cannot be declared 'protected'."); + dmod.IsProtected = false; + } + if (dmod.IsExtern && ((allowed & AllowedDeclModifiers.Extern) == 0)) { + SemErr(dmod.ExternToken, declCaption + " cannot be declared 'extern'."); + dmod.IsExtern = false; + } +} + +/// +/// Encode an 'extern' declaration modifier as an {:extern name} attribute. +/// +/// We also include an {:axiom} attribute since the specification of an +/// external entity is assumed to hold, but only for methods or functions. +/// +static void EncodeExternAsAttribute(DeclModifierData dmod, ref Attributes attrs, IToken/*!*/ id, bool needAxiom) { + if (dmod.IsExtern) { + StringLiteralExpr name = dmod.ExternName; + if (name == null) { + bool isVerbatimString = false; + name = new StringLiteralExpr(id, id.val, isVerbatimString); + } + var args = new List(); + args.Add(name); + attrs = new Attributes("extern", args, attrs); + + // Also 'extern' implies 'axiom' for methods or functions. + if (needAxiom) { + attrs = new Attributes("axiom", new List(), attrs); + } + } } /// @@ -543,13 +646,10 @@ bool IsType(ref IToken pt) { void Dafny() { - ClassDecl/*!*/ c; DatatypeDecl/*!*/ dt; TopLevelDecl td; IteratorDecl iter; List membersDefaultClass = new List(); - ModuleDecl submodule; // to support multiple files, create a default module only if theModule is null DefaultModuleDecl defaultModule = (DefaultModuleDecl)((LiteralModuleDecl)theModule).ModuleDef; // theModule should be a DefaultModuleDecl (actually, the singular DefaultModuleDecl) - TraitDecl/*!*/ trait; Contract.Assert(defaultModule != null); while (la.kind == 60) { @@ -570,49 +670,7 @@ bool IsType(ref IToken pt) { } } - while (StartOf(1)) { - switch (la.kind) { - case 61: case 62: case 65: { - SubModuleDecl(defaultModule, out submodule); - defaultModule.TopLevelDecls.Add(submodule); - break; - } - case 70: { - ClassDecl(defaultModule, out c); - defaultModule.TopLevelDecls.Add(c); - break; - } - case 76: case 77: { - DatatypeDecl(defaultModule, out dt); - defaultModule.TopLevelDecls.Add(dt); - break; - } - case 79: { - NewtypeDecl(defaultModule, out td); - defaultModule.TopLevelDecls.Add(td); - break; - } - case 80: { - OtherTypeDecl(defaultModule, out td); - defaultModule.TopLevelDecls.Add(td); - break; - } - case 81: { - IteratorDecl(defaultModule, out iter); - defaultModule.TopLevelDecls.Add(iter); - break; - } - case 72: { - TraitDecl(defaultModule, out trait); - defaultModule.TopLevelDecls.Add(trait); - break; - } - case 38: case 39: case 40: case 41: case 42: case 73: case 74: case 75: case 78: case 84: case 85: case 86: case 87: { - ClassMemberDecl(membersDefaultClass, false, !DafnyOptions.O.AllowGlobals, false); - break; - } - } - } + TopDecls(defaultModule, membersDefaultClass, /* isTopLevel */ true, /* isAbstract */ false); DefaultClassDecl defaultClass = null; foreach (TopLevelDecl topleveldecl in defaultModule.TopLevelDecls) { defaultClass = topleveldecl as DefaultClassDecl; @@ -628,33 +686,114 @@ bool IsType(ref IToken pt) { Expect(0); } - void SubModuleDecl(ModuleDefinition parent, out ModuleDecl submodule) { + void TopDecls(ModuleDefinition module, List membersDefaultClass, bool isTopLevel, bool isAbstract ) { + while (StartOf(1)) { + TopDecl(module, membersDefaultClass, isTopLevel, isAbstract); + } + } + + void TopDecl(ModuleDefinition module, List membersDefaultClass, bool isTopLevel, bool isAbstract ) { + DeclModifierData dmod; ModuleDecl submodule; ClassDecl/*!*/ c; DatatypeDecl/*!*/ dt; TopLevelDecl td; IteratorDecl iter; - Attributes attrs = null; IToken/*!*/ id; TraitDecl/*!*/ trait; + + DeclModifiers(out dmod); + switch (la.kind) { + case 66: case 69: { + SubModuleDecl(dmod, module, out submodule); + module.TopLevelDecls.Add(submodule); + break; + } + case 74: { + ClassDecl(dmod, module, out c); + module.TopLevelDecls.Add(c); + break; + } + case 77: case 78: { + DatatypeDecl(dmod, module, out dt); + module.TopLevelDecls.Add(dt); + break; + } + case 80: { + NewtypeDecl(dmod, module, out td); + module.TopLevelDecls.Add(td); + break; + } + case 81: { + OtherTypeDecl(dmod, module, out td); + module.TopLevelDecls.Add(td); + break; + } + case 82: { + IteratorDecl(dmod, module, out iter); + module.TopLevelDecls.Add(iter); + break; + } + case 76: { + TraitDecl(dmod, module, out trait); + module.TopLevelDecls.Add(trait); + break; + } + case 38: case 39: case 40: case 41: case 42: case 79: case 85: case 86: case 87: case 88: { + ClassMemberDecl(dmod, membersDefaultClass, false, !DafnyOptions.O.AllowGlobals, +!isTopLevel && DafnyOptions.O.IronDafny && isAbstract); + break; + } + default: SynErr(140); break; + } + } + + void DeclModifiers(out DeclModifierData dmod) { + dmod = new DeclModifierData(); + while (StartOf(2)) { + if (la.kind == 61) { + Get(); + dmod.IsAbstract = true; CheckAndSetToken(ref dmod.AbstractToken); + } else if (la.kind == 62) { + Get(); + dmod.IsGhost = true; CheckAndSetToken(ref dmod.GhostToken); + } else if (la.kind == 63) { + Get(); + dmod.IsStatic = true; CheckAndSetToken(ref dmod.StaticToken); + } else if (la.kind == 64) { + Get(); + dmod.IsProtected = true; CheckAndSetToken(ref dmod.ProtectedToken); + } else { + Get(); + dmod.IsExtern = true; CheckAndSetToken(ref dmod.ExternToken); + if (la.kind == 20) { + Get(); + bool isVerbatimString; + string s = Util.RemoveParsedStringQuotes(t.val, out isVerbatimString); + dmod.ExternName = new StringLiteralExpr(t, s, isVerbatimString); + + } + } + } + } + + void SubModuleDecl(DeclModifierData dmod, ModuleDefinition parent, out ModuleDecl submodule) { + Attributes attrs = null; IToken/*!*/ id; List namedModuleDefaultClassMembers = new List();; List idRefined = null, idPath = null, idAssignment = null; ModuleDefinition module; - ModuleDecl sm; submodule = null; // appease compiler - bool isAbstract = false; + bool isAbstract = dmod.IsAbstract; bool isExclusively = false; bool opened = false; + CheckDeclModifiers(dmod, "Modules", AllowedDeclModifiers.Abstract | AllowedDeclModifiers.Extern); - if (la.kind == 61 || la.kind == 62) { - if (la.kind == 61) { - Get(); - isAbstract = true; - } - Expect(62); + if (la.kind == 66) { + Get(); while (la.kind == 46) { Attribute(ref attrs); } NoUSIdent(out id); - if (la.kind == 63 || la.kind == 64) { - if (la.kind == 63) { + EncodeExternAsAttribute(dmod, ref attrs, id, /* needAxiom */ false); + if (la.kind == 67 || la.kind == 68) { + if (la.kind == 67) { Get(); - Expect(64); + Expect(68); QualifiedModuleName(out idRefined); isExclusively = true; } else { @@ -666,69 +805,28 @@ bool IsType(ref IToken pt) { module = new ModuleDefinition(id, id.val, isAbstract, false, isExclusively, idRefined == null ? null : idRefined, parent, attrs, false, this); Expect(46); module.BodyStartTok = t; - while (StartOf(1)) { - switch (la.kind) { - case 61: case 62: case 65: { - SubModuleDecl(module, out sm); - module.TopLevelDecls.Add(sm); - break; - } - case 70: { - ClassDecl(module, out c); - module.TopLevelDecls.Add(c); - break; - } - case 72: { - TraitDecl(module, out trait); - module.TopLevelDecls.Add(trait); - break; - } - case 76: case 77: { - DatatypeDecl(module, out dt); - module.TopLevelDecls.Add(dt); - break; - } - case 79: { - NewtypeDecl(module, out td); - module.TopLevelDecls.Add(td); - break; - } - case 80: { - OtherTypeDecl(module, out td); - module.TopLevelDecls.Add(td); - break; - } - case 81: { - IteratorDecl(module, out iter); - module.TopLevelDecls.Add(iter); - break; - } - case 38: case 39: case 40: case 41: case 42: case 73: case 74: case 75: case 78: case 84: case 85: case 86: case 87: { - ClassMemberDecl(namedModuleDefaultClassMembers, false, !DafnyOptions.O.AllowGlobals, DafnyOptions.O.IronDafny && isAbstract); - break; - } - } - } + TopDecls(module, namedModuleDefaultClassMembers, /* isTopLevel */ false, isAbstract); Expect(47); module.BodyEndTok = t; module.TopLevelDecls.Add(new DefaultClassDecl(module, namedModuleDefaultClassMembers)); submodule = new LiteralModuleDecl(module, parent); - } else if (la.kind == 65) { + } else if (la.kind == 69) { Get(); - if (la.kind == 66) { + if (la.kind == 70) { Get(); opened = true; } NoUSIdent(out id); - if (la.kind == 67 || la.kind == 68) { - if (la.kind == 67) { + EncodeExternAsAttribute(dmod, ref attrs, id, /* needAxiom */ false); + if (la.kind == 71 || la.kind == 72) { + if (la.kind == 71) { Get(); QualifiedModuleName(out idPath); submodule = new AliasModuleDecl(idPath, id, parent, opened); } else { Get(); QualifiedModuleName(out idPath); - if (la.kind == 69) { + if (la.kind == 73) { Get(); QualifiedModuleName(out idAssignment); } @@ -736,7 +834,7 @@ bool IsType(ref IToken pt) { } } if (la.kind == 28) { - while (!(la.kind == 0 || la.kind == 28)) {SynErr(139); Get();} + while (!(la.kind == 0 || la.kind == 28)) {SynErr(141); Get();} Get(); errors.Warning(t, "the semi-colon that used to terminate a sub-module declaration has been deprecated; in the new syntax, just leave off the semi-colon"); } @@ -746,10 +844,10 @@ bool IsType(ref IToken pt) { submodule = new AliasModuleDecl(idPath, id, parent, opened); } - } else SynErr(140); + } else SynErr(142); } - void ClassDecl(ModuleDefinition/*!*/ module, out ClassDecl/*!*/ c) { + void ClassDecl(DeclModifierData dmodClass, ModuleDefinition/*!*/ module, out ClassDecl/*!*/ c) { Contract.Requires(module != null); Contract.Ensures(Contract.ValueAtReturn(out c) != null); IToken/*!*/ id; @@ -759,17 +857,20 @@ bool IsType(ref IToken pt) { List typeArgs = new List(); List members = new List(); IToken bodyStart; + CheckDeclModifiers(dmodClass, "Classes", AllowedDeclModifiers.Extern); + DeclModifierData dmod; - while (!(la.kind == 0 || la.kind == 70)) {SynErr(141); Get();} - Expect(70); + while (!(la.kind == 0 || la.kind == 74)) {SynErr(143); Get();} + Expect(74); while (la.kind == 46) { Attribute(ref attrs); } NoUSIdent(out id); + EncodeExternAsAttribute(dmodClass, ref attrs, id, /* needAxiom */ false); if (la.kind == 52) { GenericParameters(typeArgs); } - if (la.kind == 71) { + if (la.kind == 75) { Get(); Type(out trait); traits.Add(trait); @@ -781,8 +882,9 @@ bool IsType(ref IToken pt) { } Expect(46); bodyStart = t; - while (StartOf(2)) { - ClassMemberDecl(members, true, false, false); + while (StartOf(3)) { + DeclModifiers(out dmod); + ClassMemberDecl(dmod, members, true, false, false); } Expect(47); c = new ClassDecl(id, id.val, module, typeArgs, members, attrs, traits); @@ -791,7 +893,7 @@ bool IsType(ref IToken pt) { } - void DatatypeDecl(ModuleDefinition/*!*/ module, out DatatypeDecl/*!*/ dt) { + void DatatypeDecl(DeclModifierData dmod, ModuleDefinition/*!*/ module, out DatatypeDecl/*!*/ dt) { Contract.Requires(module != null); Contract.Ensures(Contract.ValueAtReturn(out dt)!=null); IToken/*!*/ id; @@ -800,14 +902,15 @@ bool IsType(ref IToken pt) { List ctors = new List(); IToken bodyStart = Token.NoToken; // dummy assignment bool co = false; + CheckDeclModifiers(dmod, "Datatypes or codatatypes", AllowedDeclModifiers.None); - while (!(la.kind == 0 || la.kind == 76 || la.kind == 77)) {SynErr(142); Get();} - if (la.kind == 76) { + while (!(la.kind == 0 || la.kind == 77 || la.kind == 78)) {SynErr(144); Get();} + if (la.kind == 77) { Get(); - } else if (la.kind == 77) { + } else if (la.kind == 78) { Get(); co = true; - } else SynErr(143); + } else SynErr(145); while (la.kind == 46) { Attribute(ref attrs); } @@ -815,7 +918,7 @@ bool IsType(ref IToken pt) { if (la.kind == 52) { GenericParameters(typeArgs); } - Expect(67); + Expect(71); bodyStart = t; DatatypeMemberDecl(ctors); while (la.kind == 23) { @@ -823,7 +926,7 @@ bool IsType(ref IToken pt) { DatatypeMemberDecl(ctors); } if (la.kind == 28) { - while (!(la.kind == 0 || la.kind == 28)) {SynErr(144); Get();} + while (!(la.kind == 0 || la.kind == 28)) {SynErr(146); Get();} Get(); errors.Warning(t, "the semi-colon that used to terminate a (co)datatype declaration has been deprecated; in the new syntax, just leave off the semi-colon"); } @@ -837,19 +940,20 @@ bool IsType(ref IToken pt) { } - void NewtypeDecl(ModuleDefinition module, out TopLevelDecl td) { + void NewtypeDecl(DeclModifierData dmod, ModuleDefinition module, out TopLevelDecl td) { IToken id, bvId; Attributes attrs = null; td = null; Type baseType = null; Expression wh; + CheckDeclModifiers(dmod, "Newtypes", AllowedDeclModifiers.None); - Expect(79); + Expect(80); while (la.kind == 46) { Attribute(ref attrs); } NoUSIdent(out id); - Expect(67); + Expect(71); if (IsIdentColonOrBar()) { NoUSIdent(out bvId); if (la.kind == 21) { @@ -860,21 +964,22 @@ bool IsType(ref IToken pt) { Expect(23); Expression(out wh, false, true); td = new NewtypeDecl(theVerifyThisFile ? id : new IncludeToken(id), id.val, module, new BoundVar(bvId, bvId.val, baseType), wh, attrs); - } else if (StartOf(3)) { + } else if (StartOf(4)) { Type(out baseType); td = new NewtypeDecl(theVerifyThisFile ? id : new IncludeToken(id), id.val, module, baseType, attrs); - } else SynErr(145); + } else SynErr(147); } - void OtherTypeDecl(ModuleDefinition module, out TopLevelDecl td) { + void OtherTypeDecl(DeclModifierData dmod, ModuleDefinition module, out TopLevelDecl td) { IToken id; Attributes attrs = null; var eqSupport = TypeParameter.EqualitySupportValue.Unspecified; var typeArgs = new List(); td = null; Type ty; + CheckDeclModifiers(dmod, "Type aliases", AllowedDeclModifiers.None); - Expect(80); + Expect(81); while (la.kind == 46) { Attribute(ref attrs); } @@ -887,28 +992,28 @@ bool IsType(ref IToken pt) { if (la.kind == 52) { GenericParameters(typeArgs); } - } else if (StartOf(4)) { + } else if (StartOf(5)) { if (la.kind == 52) { GenericParameters(typeArgs); } - if (la.kind == 67) { + if (la.kind == 71) { Get(); Type(out ty); td = new TypeSynonymDecl(id, id.val, typeArgs, module, ty, attrs); } - } else SynErr(146); + } else SynErr(148); if (td == null) { td = new OpaqueTypeDecl(id, id.val, module, eqSupport, typeArgs, attrs); } if (la.kind == 28) { - while (!(la.kind == 0 || la.kind == 28)) {SynErr(147); Get();} + while (!(la.kind == 0 || la.kind == 28)) {SynErr(149); Get();} Get(); errors.Warning(t, "the semi-colon that used to terminate an opaque-type declaration has been deprecated; in the new syntax, just leave off the semi-colon"); } } - void IteratorDecl(ModuleDefinition module, out IteratorDecl/*!*/ iter) { + void IteratorDecl(DeclModifierData dmod, ModuleDefinition module, out IteratorDecl/*!*/ iter) { Contract.Ensures(Contract.ValueAtReturn(out iter) != null); IToken/*!*/ id; Attributes attrs = null; @@ -930,9 +1035,10 @@ bool IsType(ref IToken pt) { IToken signatureEllipsis = null; IToken bodyStart = Token.NoToken; IToken bodyEnd = Token.NoToken; + CheckDeclModifiers(dmod, "Iterators", AllowedDeclModifiers.None); - while (!(la.kind == 0 || la.kind == 81)) {SynErr(148); Get();} - Expect(81); + while (!(la.kind == 0 || la.kind == 82)) {SynErr(150); Get();} + Expect(82); while (la.kind == 46) { Attribute(ref attrs); } @@ -942,8 +1048,8 @@ bool IsType(ref IToken pt) { GenericParameters(typeArgs); } Formals(true, true, ins); - if (la.kind == 82 || la.kind == 83) { - if (la.kind == 82) { + if (la.kind == 83 || la.kind == 84) { + if (la.kind == 83) { Get(); } else { Get(); @@ -954,8 +1060,8 @@ bool IsType(ref IToken pt) { } else if (la.kind == 59) { Get(); signatureEllipsis = t; - } else SynErr(149); - while (StartOf(5)) { + } else SynErr(151); + while (StartOf(6)) { IteratorSpec(reads, mod, decreases, req, ens, yieldReq, yieldEns, ref readsAttrs, ref modAttrs, ref decrAttrs); } if (la.kind == 46) { @@ -972,17 +1078,19 @@ bool IsType(ref IToken pt) { } - void TraitDecl(ModuleDefinition/*!*/ module, out TraitDecl/*!*/ trait) { - Contract.Requires(module != null); + void TraitDecl(DeclModifierData dmodIn, ModuleDefinition/*!*/ module, out TraitDecl/*!*/ trait) { + Contract.Requires(module != null); + CheckDeclModifiers(dmodIn, "Traits", AllowedDeclModifiers.None); Contract.Ensures(Contract.ValueAtReturn(out trait) != null); IToken/*!*/ id; Attributes attrs = null; List typeArgs = new List(); //traits should not support type parameters at the moment List members = new List(); IToken bodyStart; + DeclModifierData dmod; - while (!(la.kind == 0 || la.kind == 72)) {SynErr(150); Get();} - Expect(72); + while (!(la.kind == 0 || la.kind == 76)) {SynErr(152); Get();} + Expect(76); while (la.kind == 46) { Attribute(ref attrs); } @@ -992,8 +1100,9 @@ bool IsType(ref IToken pt) { } Expect(46); bodyStart = t; - while (StartOf(2)) { - ClassMemberDecl(members, true, false, false); + while (StartOf(3)) { + DeclModifiers(out dmod); + ClassMemberDecl(dmod, members, true, false, false); } Expect(47); trait = new TraitDecl(id, id.val, module, typeArgs, members, attrs); @@ -1002,54 +1111,35 @@ bool IsType(ref IToken pt) { } - void ClassMemberDecl(List mm, bool allowConstructors, bool moduleLevelDecl, bool isWithinAbstractModule) { + void ClassMemberDecl(DeclModifierData dmod, List mm, bool allowConstructors, bool moduleLevelDecl, bool isWithinAbstractModule) { Contract.Requires(cce.NonNullElements(mm)); Method/*!*/ m; Function/*!*/ f; - MemberModifiers mmod = new MemberModifiers(); - IToken staticToken = null, protectedToken = null; - while (la.kind == 73 || la.kind == 74 || la.kind == 75) { - if (la.kind == 73) { - Get(); - mmod.IsGhost = true; - } else if (la.kind == 74) { - Get(); - mmod.IsStatic = true; staticToken = t; - } else { - Get(); - mmod.IsProtected = true; protectedToken = t; - } - } - if (la.kind == 78) { + if (la.kind == 79) { if (moduleLevelDecl) { SemErr(la, "fields are not allowed to be declared at the module level; instead, wrap the field in a 'class' declaration"); - mmod.IsStatic = false; - mmod.IsProtected = false; + dmod.IsStatic = false; } - FieldDecl(mmod, mm); + FieldDecl(dmod, mm); } else if (IsFunctionDecl()) { - if (moduleLevelDecl && staticToken != null) { - errors.Warning(staticToken, "module-level functions are always non-instance, so the 'static' keyword is not allowed here"); - mmod.IsStatic = false; + if (moduleLevelDecl && dmod.StaticToken != null) { + errors.Warning(dmod.StaticToken, "module-level functions are always non-instance, so the 'static' keyword is not allowed here"); + dmod.IsStatic = false; } - FunctionDecl(mmod, isWithinAbstractModule, out f); + FunctionDecl(dmod, isWithinAbstractModule, out f); mm.Add(f); - } else if (StartOf(6)) { - if (moduleLevelDecl && staticToken != null) { - errors.Warning(staticToken, "module-level methods are always non-instance, so the 'static' keyword is not allowed here"); - mmod.IsStatic = false; - } - if (protectedToken != null) { - SemErr(protectedToken, "only functions, not methods, can be declared 'protected'"); - mmod.IsProtected = false; + } else if (StartOf(7)) { + if (moduleLevelDecl && dmod.StaticToken != null) { + errors.Warning(dmod.StaticToken, "module-level methods are always non-instance, so the 'static' keyword is not allowed here"); + dmod.IsStatic = false; } - MethodDecl(mmod, allowConstructors, isWithinAbstractModule, out m); + MethodDecl(dmod, allowConstructors, isWithinAbstractModule, out m); mm.Add(m); - } else SynErr(151); + } else SynErr(153); } void Attribute(ref Attributes attrs) { @@ -1060,7 +1150,7 @@ bool IsType(ref IToken pt) { Expect(21); NoUSIdent(out x); name = x.val; - if (StartOf(7)) { + if (StartOf(8)) { Expressions(args); } Expect(47); @@ -1129,29 +1219,28 @@ bool IsType(ref IToken pt) { TypeAndToken(out tok, out ty); } - void FieldDecl(MemberModifiers mmod, List/*!*/ mm) { + void FieldDecl(DeclModifierData dmod, List/*!*/ mm) { Contract.Requires(cce.NonNullElements(mm)); Attributes attrs = null; IToken/*!*/ id; Type/*!*/ ty; + CheckDeclModifiers(dmod, "Fields", AllowedDeclModifiers.Ghost); - while (!(la.kind == 0 || la.kind == 78)) {SynErr(152); Get();} - Expect(78); - if (mmod.IsStatic) { SemErr(t, "fields cannot be declared 'static'"); } - + while (!(la.kind == 0 || la.kind == 79)) {SynErr(154); Get();} + Expect(79); while (la.kind == 46) { Attribute(ref attrs); } FIdentType(out id, out ty); - mm.Add(new Field(id, id.val, mmod.IsGhost, ty, attrs)); + mm.Add(new Field(id, id.val, dmod.IsGhost, ty, attrs)); while (la.kind == 22) { Get(); FIdentType(out id, out ty); - mm.Add(new Field(id, id.val, mmod.IsGhost, ty, attrs)); + mm.Add(new Field(id, id.val, dmod.IsGhost, ty, attrs)); } OldSemi(); } - void FunctionDecl(MemberModifiers mmod, bool isWithinAbstractModule, out Function/*!*/ f) { + void FunctionDecl(DeclModifierData dmod, bool isWithinAbstractModule, out Function/*!*/ f) { Contract.Ensures(Contract.ValueAtReturn(out f)!=null); Attributes attrs = null; IToken/*!*/ id = Token.NoToken; // to please compiler @@ -1172,11 +1261,17 @@ bool IsType(ref IToken pt) { if (la.kind == 38) { Get(); - if (la.kind == 84) { + if (la.kind == 85) { Get(); isFunctionMethod = true; } - if (mmod.IsGhost) { SemErr(t, "functions cannot be declared 'ghost' (they are ghost by default)"); } + AllowedDeclModifiers allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static | AllowedDeclModifiers.Protected; + string caption = "Functions"; + if (isFunctionMethod) { + allowed |= AllowedDeclModifiers.Extern; + caption = "Function methods"; + } + CheckDeclModifiers(dmod, caption, allowed); while (la.kind == 46) { Attribute(ref attrs); @@ -1192,21 +1287,27 @@ bool IsType(ref IToken pt) { } else if (la.kind == 59) { Get(); signatureEllipsis = t; - } else SynErr(153); + } else SynErr(155); } else if (la.kind == 39) { Get(); isPredicate = true; - if (la.kind == 84) { + if (la.kind == 85) { Get(); isFunctionMethod = true; } - if (mmod.IsGhost) { SemErr(t, "predicates cannot be declared 'ghost' (they are ghost by default)"); } + AllowedDeclModifiers allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static | AllowedDeclModifiers.Protected; + string caption = "Predicates"; + if (isFunctionMethod) { + allowed |= AllowedDeclModifiers.Extern; + caption = "Predicate methods"; + } + CheckDeclModifiers(dmod, caption, allowed); while (la.kind == 46) { Attribute(ref attrs); } NoUSIdent(out id); - if (StartOf(8)) { + if (StartOf(9)) { if (la.kind == 52) { GenericParameters(typeArgs); } @@ -1223,12 +1324,13 @@ bool IsType(ref IToken pt) { } else if (la.kind == 59) { Get(); signatureEllipsis = t; - } else SynErr(154); + } else SynErr(156); } else if (la.kind == 40) { Get(); Expect(39); isIndPredicate = true; - if (mmod.IsGhost) { SemErr(t, "inductive predicates cannot be declared 'ghost' (they are ghost by default)"); } + CheckDeclModifiers(dmod, "Inductive predicates", + AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static | AllowedDeclModifiers.Protected); while (la.kind == 46) { Attribute(ref attrs); @@ -1246,11 +1348,12 @@ bool IsType(ref IToken pt) { } else if (la.kind == 59) { Get(); signatureEllipsis = t; - } else SynErr(155); + } else SynErr(157); } else if (la.kind == 42) { Get(); isCoPredicate = true; - if (mmod.IsGhost) { SemErr(t, "copredicates cannot be declared 'ghost' (they are ghost by default)"); } + CheckDeclModifiers(dmod, "Copredicates", + AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static | AllowedDeclModifiers.Protected); while (la.kind == 46) { Attribute(ref attrs); @@ -1268,31 +1371,32 @@ bool IsType(ref IToken pt) { } else if (la.kind == 59) { Get(); signatureEllipsis = t; - } else SynErr(156); - } else SynErr(157); + } else SynErr(158); + } else SynErr(159); decreases = isIndPredicate || isCoPredicate ? null : new List(); - while (StartOf(9)) { + while (StartOf(10)) { FunctionSpec(reqs, reads, ens, decreases); } if (la.kind == 46) { FunctionBody(out body, out bodyStart, out bodyEnd); } - if (!isWithinAbstractModule && DafnyOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 && !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported")) { + if (!isWithinAbstractModule && DafnyOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 && + !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported")) { SemErr(t, "a function with an ensures clause must have a body, unless given the :axiom attribute"); } - + EncodeExternAsAttribute(dmod, ref attrs, id, /* needAxiom */ true); IToken tok = theVerifyThisFile ? id : new IncludeToken(id); if (isPredicate) { - f = new Predicate(tok, id.val, mmod.IsStatic, mmod.IsProtected, !isFunctionMethod, typeArgs, formals, + f = new Predicate(tok, id.val, dmod.IsStatic, dmod.IsProtected, !isFunctionMethod, typeArgs, formals, reqs, reads, ens, new Specification(decreases, null), body, Predicate.BodyOriginKind.OriginalOrInherited, attrs, signatureEllipsis); } else if (isIndPredicate) { - f = new InductivePredicate(tok, id.val, mmod.IsStatic, mmod.IsProtected, typeArgs, formals, + f = new InductivePredicate(tok, id.val, dmod.IsStatic, dmod.IsProtected, typeArgs, formals, reqs, reads, ens, body, attrs, signatureEllipsis); } else if (isCoPredicate) { - f = new CoPredicate(tok, id.val, mmod.IsStatic, mmod.IsProtected, typeArgs, formals, + f = new CoPredicate(tok, id.val, dmod.IsStatic, dmod.IsProtected, typeArgs, formals, reqs, reads, ens, body, attrs, signatureEllipsis); } else { - f = new Function(tok, id.val, mmod.IsStatic, mmod.IsProtected, !isFunctionMethod, typeArgs, formals, returnType, + f = new Function(tok, id.val, dmod.IsStatic, dmod.IsProtected, !isFunctionMethod, typeArgs, formals, returnType, reqs, reads, ens, new Specification(decreases, null), body, attrs, signatureEllipsis); } f.BodyStartTok = bodyStart; @@ -1305,7 +1409,7 @@ bool IsType(ref IToken pt) { } - void MethodDecl(MemberModifiers mmod, bool allowConstructor, bool isWithinAbstractModule, out Method/*!*/ m) { + void MethodDecl(DeclModifierData dmod, bool allowConstructor, bool isWithinAbstractModule, out Method/*!*/ m) { Contract.Ensures(Contract.ValueAtReturn(out m) !=null); IToken/*!*/ id = Token.NoToken; bool hasName = false; IToken keywordToken; @@ -1327,26 +1431,37 @@ bool IsType(ref IToken pt) { IToken signatureEllipsis = null; IToken bodyStart = Token.NoToken; IToken bodyEnd = Token.NoToken; + AllowedDeclModifiers allowed = AllowedDeclModifiers.None; + string caption = ""; - while (!(StartOf(10))) {SynErr(158); Get();} + while (!(StartOf(11))) {SynErr(160); Get();} switch (la.kind) { - case 84: { + case 85: { Get(); + caption = "Methods"; + allowed = AllowedDeclModifiers.Ghost | AllowedDeclModifiers.Static + | AllowedDeclModifiers.Extern; break; } case 41: { Get(); - isLemma = true; + isLemma = true; caption = "Lemmas"; + allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static + | AllowedDeclModifiers.Protected; break; } - case 85: { + case 86: { Get(); - isCoLemma = true; + isCoLemma = true; caption = "Colemmas"; + allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static + | AllowedDeclModifiers.Protected; break; } - case 86: { + case 87: { Get(); - isCoLemma = true; + isCoLemma = true; caption = "Comethods"; + allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static + | AllowedDeclModifiers.Protected; errors.Warning(t, "the 'comethod' keyword has been deprecated; it has been renamed to 'colemma'"); break; @@ -1354,43 +1469,26 @@ bool IsType(ref IToken pt) { case 40: { Get(); Expect(41); - isIndLemma = true; + isIndLemma = true; caption = "Inductive lemmas"; + allowed = AllowedDeclModifiers.AlreadyGhost | AllowedDeclModifiers.Static; break; } - case 87: { + case 88: { Get(); if (allowConstructor) { isConstructor = true; } else { SemErr(t, "constructors are allowed only in classes"); - } + } + caption = "Constructors"; + allowed = AllowedDeclModifiers.None; break; } - default: SynErr(159); break; + default: SynErr(161); break; } keywordToken = t; - if (isLemma) { - if (mmod.IsGhost) { - SemErr(t, "lemmas cannot be declared 'ghost' (they are automatically 'ghost')"); - } - } else if (isConstructor) { - if (mmod.IsGhost) { - SemErr(t, "constructors cannot be declared 'ghost'"); - } - if (mmod.IsStatic) { - SemErr(t, "constructors cannot be declared 'static'"); - } - } else if (isIndLemma) { - if (mmod.IsGhost) { - SemErr(t, "inductive lemmas cannot be declared 'ghost' (they are automatically 'ghost')"); - } - } else if (isCoLemma) { - if (mmod.IsGhost) { - SemErr(t, "colemmas cannot be declared 'ghost' (they are automatically 'ghost')"); - } - } - + CheckDeclModifiers(dmod, caption, allowed); while (la.kind == 46) { Attribute(ref attrs); } @@ -1404,22 +1502,23 @@ bool IsType(ref IToken pt) { SemErr(la, "a method must be given a name (expecting identifier)"); } } + EncodeExternAsAttribute(dmod, ref attrs, id, /* needAxiom */ true); if (la.kind == 50 || la.kind == 52) { if (la.kind == 52) { GenericParameters(typeArgs); } - Formals(true, !mmod.IsGhost, ins); - if (la.kind == 83) { + Formals(true, !dmod.IsGhost, ins); + if (la.kind == 84) { Get(); if (isConstructor) { SemErr(t, "constructors cannot have out-parameters"); } - Formals(false, !mmod.IsGhost, outs); + Formals(false, !dmod.IsGhost, outs); } } else if (la.kind == 59) { Get(); signatureEllipsis = t; - } else SynErr(160); - while (StartOf(11)) { + } else SynErr(162); + while (StartOf(12)) { MethodSpec(req, mod, ens, dec, ref decAttrs, ref modAttrs); } if (la.kind == 46) { @@ -1434,16 +1533,16 @@ bool IsType(ref IToken pt) { m = new Constructor(tok, hasName ? id.val : "_ctor", typeArgs, ins, req, new Specification(mod, modAttrs), ens, new Specification(dec, decAttrs), body, attrs, signatureEllipsis); } else if (isIndLemma) { - m = new InductiveLemma(tok, id.val, mmod.IsStatic, typeArgs, ins, outs, + m = new InductiveLemma(tok, id.val, dmod.IsStatic, typeArgs, ins, outs, req, new Specification(mod, modAttrs), ens, new Specification(dec, decAttrs), body, attrs, signatureEllipsis); } else if (isCoLemma) { - m = new CoLemma(tok, id.val, mmod.IsStatic, typeArgs, ins, outs, + m = new CoLemma(tok, id.val, dmod.IsStatic, typeArgs, ins, outs, req, new Specification(mod, modAttrs), ens, new Specification(dec, decAttrs), body, attrs, signatureEllipsis); } else if (isLemma) { - m = new Lemma(tok, id.val, mmod.IsStatic, typeArgs, ins, outs, + m = new Lemma(tok, id.val, dmod.IsStatic, typeArgs, ins, outs, req, new Specification(mod, modAttrs), ens, new Specification(dec, decAttrs), body, attrs, signatureEllipsis); } else { - m = new Method(tok, id.val, mmod.IsStatic, mmod.IsGhost, typeArgs, ins, outs, + m = new Method(tok, id.val, dmod.IsStatic, dmod.IsGhost, typeArgs, ins, outs, req, new Specification(mod, modAttrs), ens, new Specification(dec, decAttrs), body, attrs, signatureEllipsis); } m.BodyStartTok = bodyStart; @@ -1470,7 +1569,7 @@ bool IsType(ref IToken pt) { void FormalsOptionalIds(List/*!*/ formals) { Contract.Requires(cce.NonNullElements(formals)); IToken/*!*/ id; Type/*!*/ ty; string/*!*/ name; bool isGhost; Expect(50); - if (StartOf(12)) { + if (StartOf(13)) { TypeIdentOptional(out id, out name, out ty, out isGhost); formals.Add(new Formal(id, name, ty, true, isGhost)); while (la.kind == 22) { @@ -1491,14 +1590,14 @@ bool IsType(ref IToken pt) { } else if (la.kind == 2) { Get(); id = t; - } else SynErr(161); + } else SynErr(163); Expect(21); Type(out ty); } void OldSemi() { if (la.kind == 28) { - while (!(la.kind == 0 || la.kind == 28)) {SynErr(162); Get();} + while (!(la.kind == 0 || la.kind == 28)) {SynErr(164); Get();} Get(); } } @@ -1521,7 +1620,7 @@ bool IsType(ref IToken pt) { Contract.Ensures(Contract.ValueAtReturn(out id)!=null); Contract.Ensures(Contract.ValueAtReturn(out ty)!=null); isGhost = false; - if (la.kind == 73) { + if (la.kind == 62) { Get(); if (allowGhostKeyword) { isGhost = true; } else { SemErr(t, "formal cannot be declared 'ghost' in this context"); } } @@ -1573,11 +1672,11 @@ bool IsType(ref IToken pt) { Contract.Ensures(Contract.ValueAtReturn(out ty)!=null); Contract.Ensures(Contract.ValueAtReturn(out identName)!=null); string name = null; id = Token.NoToken; ty = new BoolType()/*dummy*/; isGhost = false; - if (la.kind == 73) { + if (la.kind == 62) { Get(); isGhost = true; } - if (StartOf(3)) { + if (StartOf(4)) { TypeAndToken(out id, out ty); if (la.kind == 21) { Get(); @@ -1595,7 +1694,7 @@ bool IsType(ref IToken pt) { id = t; name = id.val; Expect(21); Type(out ty); - } else SynErr(163); + } else SynErr(165); if (name != null) { identName = name; } else { @@ -1746,7 +1845,7 @@ bool IsType(ref IToken pt) { case 50: { Get(); tok = t; tupleArgTypes = new List(); - if (StartOf(3)) { + if (StartOf(4)) { Type(out ty); tupleArgTypes.Add(ty); while (la.kind == 22) { @@ -1783,7 +1882,7 @@ bool IsType(ref IToken pt) { ty = new UserDefinedType(e.tok, e); break; } - default: SynErr(164); break; + default: SynErr(166); break; } if (la.kind == 30) { Type t2; @@ -1804,7 +1903,7 @@ bool IsType(ref IToken pt) { void Formals(bool incoming, bool allowGhostKeyword, List formals) { Contract.Requires(cce.NonNullElements(formals)); IToken id; Type ty; bool isGhost; Expect(50); - if (la.kind == 1 || la.kind == 73) { + if (la.kind == 1 || la.kind == 62) { GIdentType(allowGhostKeyword, out id, out ty, out isGhost); formals.Add(new Formal(id, id.val, ty, incoming, isGhost)); while (la.kind == 22) { @@ -1822,7 +1921,7 @@ List/*!*/ yieldReq, List/*!* ref Attributes readsAttrs, ref Attributes modAttrs, ref Attributes decrAttrs) { Expression/*!*/ e; FrameExpression/*!*/ fe; bool isFree = false; bool isYield = false; Attributes ensAttrs = null; - while (!(StartOf(13))) {SynErr(165); Get();} + while (!(StartOf(14))) {SynErr(167); Get();} if (la.kind == 44) { Get(); while (IsAttribute()) { @@ -1849,14 +1948,14 @@ ref Attributes readsAttrs, ref Attributes modAttrs, ref Attributes decrAttrs) { mod.Add(fe); } OldSemi(); - } else if (StartOf(14)) { - if (la.kind == 88) { + } else if (StartOf(15)) { + if (la.kind == 89) { Get(); isFree = true; errors.Warning(t, "the 'free' keyword is soon to be deprecated"); } - if (la.kind == 90) { + if (la.kind == 91) { Get(); isYield = true; } @@ -1870,7 +1969,7 @@ ref Attributes readsAttrs, ref Attributes modAttrs, ref Attributes decrAttrs) { req.Add(new MaybeFreeExpression(e, isFree)); } - } else if (la.kind == 89) { + } else if (la.kind == 90) { Get(); while (IsAttribute()) { Attribute(ref ensAttrs); @@ -1883,7 +1982,7 @@ ref Attributes readsAttrs, ref Attributes modAttrs, ref Attributes decrAttrs) { ens.Add(new MaybeFreeExpression(e, isFree, ensAttrs)); } - } else SynErr(166); + } else SynErr(168); } else if (la.kind == 36) { Get(); while (IsAttribute()) { @@ -1891,7 +1990,7 @@ ref Attributes readsAttrs, ref Attributes modAttrs, ref Attributes decrAttrs) { } DecreasesList(decreases, false, false); OldSemi(); - } else SynErr(167); + } else SynErr(169); } void BlockStmt(out BlockStmt/*!*/ block, out IToken bodyStart, out IToken bodyEnd) { @@ -1900,7 +1999,7 @@ ref Attributes readsAttrs, ref Attributes modAttrs, ref Attributes decrAttrs) { Expect(46); bodyStart = t; - while (StartOf(15)) { + while (StartOf(16)) { Stmt(body); } Expect(47); @@ -1913,7 +2012,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Contract.Requires(cce.NonNullElements(req)); Contract.Requires(cce.NonNullElements(mod)); Contract.Requires(cce.NonNullElements(ens)); Contract.Requires(cce.NonNullElements(decreases)); Expression/*!*/ e; FrameExpression/*!*/ fe; bool isFree = false; Attributes ensAttrs = null; - while (!(StartOf(16))) {SynErr(168); Get();} + while (!(StartOf(17))) {SynErr(170); Get();} if (la.kind == 43) { Get(); while (IsAttribute()) { @@ -1927,8 +2026,8 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo mod.Add(fe); } OldSemi(); - } else if (la.kind == 45 || la.kind == 88 || la.kind == 89) { - if (la.kind == 88) { + } else if (la.kind == 45 || la.kind == 89 || la.kind == 90) { + if (la.kind == 89) { Get(); isFree = true; errors.Warning(t, "the 'free' keyword is soon to be deprecated"); @@ -1939,7 +2038,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Expression(out e, false, false); OldSemi(); req.Add(new MaybeFreeExpression(e, isFree)); - } else if (la.kind == 89) { + } else if (la.kind == 90) { Get(); while (IsAttribute()) { Attribute(ref ensAttrs); @@ -1947,7 +2046,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Expression(out e, false, false); OldSemi(); ens.Add(new MaybeFreeExpression(e, isFree, ensAttrs)); - } else SynErr(169); + } else SynErr(171); } else if (la.kind == 36) { Get(); while (IsAttribute()) { @@ -1955,7 +2054,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } DecreasesList(decreases, true, false); OldSemi(); - } else SynErr(170); + } else SynErr(172); } void FrameExpression(out FrameExpression fe, bool allowSemi, bool allowLambda) { @@ -1965,21 +2064,21 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo string fieldName = null; IToken feTok = null; fe = null; - if (StartOf(7)) { + if (StartOf(8)) { Expression(out e, allowSemi, allowLambda); feTok = e.tok; - if (la.kind == 91) { + if (la.kind == 92) { Get(); Ident(out id); fieldName = id.val; feTok = id; } fe = new FrameExpression(feTok, e, fieldName); - } else if (la.kind == 91) { + } else if (la.kind == 92) { Get(); Ident(out id); fieldName = id.val; fe = new FrameExpression(id, new ImplicitThisExpr(id), fieldName); - } else SynErr(171); + } else SynErr(173); } void DecreasesList(List decreases, bool allowWildcard, bool allowLambda) { @@ -2034,7 +2133,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Contract.Requires(cce.NonNullElements(reads)); Contract.Requires(decreases == null || cce.NonNullElements(decreases)); Expression/*!*/ e; FrameExpression/*!*/ fe; - while (!(StartOf(17))) {SynErr(172); Get();} + while (!(StartOf(18))) {SynErr(174); Get();} if (la.kind == 45) { Get(); Expression(out e, false, false); @@ -2050,7 +2149,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo reads.Add(fe); } OldSemi(); - } else if (la.kind == 89) { + } else if (la.kind == 90) { Get(); Expression(out e, false, false); OldSemi(); @@ -2064,7 +2163,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo DecreasesList(decreases, false, false); OldSemi(); - } else SynErr(173); + } else SynErr(175); } void FunctionBody(out Expression/*!*/ e, out IToken bodyStart, out IToken bodyEnd) { @@ -2081,9 +2180,9 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo if (la.kind == 57) { Get(); fe = new FrameExpression(t, new WildcardExpr(t), null); - } else if (StartOf(18)) { + } else if (StartOf(19)) { FrameExpression(out fe, allowSemi, false); - } else SynErr(174); + } else SynErr(176); } void PossiblyWildExpression(out Expression e, bool allowLambda) { @@ -2092,9 +2191,9 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo if (la.kind == 57) { Get(); e = new WildcardExpr(t); - } else if (StartOf(7)) { + } else if (StartOf(8)) { Expression(out e, false, allowLambda); - } else SynErr(175); + } else SynErr(177); } void Stmt(List/*!*/ ss) { @@ -2111,14 +2210,14 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo IToken bodyStart, bodyEnd; int breakCount; - while (!(StartOf(19))) {SynErr(176); Get();} + while (!(StartOf(20))) {SynErr(178); Get();} switch (la.kind) { case 46: { BlockStmt(out bs, out bodyStart, out bodyEnd); s = bs; break; } - case 101: { + case 102: { AssertStmt(out s); break; } @@ -2126,31 +2225,31 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo AssumeStmt(out s); break; } - case 102: { + case 103: { PrintStmt(out s); break; } - case 1: case 2: case 3: case 4: case 8: case 10: case 19: case 20: case 23: case 50: case 131: case 132: case 133: case 134: case 135: case 136: { + case 1: case 2: case 3: case 4: case 8: case 10: case 19: case 20: case 23: case 50: case 132: case 133: case 134: case 135: case 136: case 137: { UpdateStmt(out s); break; } - case 73: case 78: { + case 62: case 79: { VarDeclStatement(out s); break; } - case 98: { + case 99: { IfStmt(out s); break; } - case 99: { + case 100: { WhileStmt(out s); break; } - case 100: { + case 101: { MatchStmt(out s); break; } - case 103: case 104: { + case 104: case 105: { ForallStmt(out s); break; } @@ -2158,11 +2257,11 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo CalcStmt(out s); break; } - case 105: { + case 106: { ModifyStmt(out s); break; } - case 92: { + case 93: { Get(); x = t; NoUSIdent(out id); @@ -2171,24 +2270,24 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo s.Labels = new LList