From 755a9ac2f8cbd07941dff34bf9e0f47c89471bc1 Mon Sep 17 00:00:00 2001 From: Nadia Polikarpova Date: Tue, 12 May 2015 14:34:25 -0400 Subject: Fixed typo in the Dafny Emacs mode. --- Util/Emacs/dafny-mode.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Util/Emacs/dafny-mode.el b/Util/Emacs/dafny-mode.el index 2846c824..8355817a 100644 --- a/Util/Emacs/dafny-mode.el +++ b/Util/Emacs/dafny-mode.el @@ -86,7 +86,7 @@ ;; commands (defun dafny-command-line (file) - (concat "boogie " file)) + (concat "dafny " file)) (defun dafny-run-verifier () "run Dafny verifier" -- cgit v1.2.3 From c13c4f3fbeae61ff152eaeeb4ae8bde9d01206be Mon Sep 17 00:00:00 2001 From: Bryan Parno Date: Wed, 13 May 2015 17:56:15 -0700 Subject: Update the configuration manager so the Debug build actually builds the Debug version of the projects (rather than Checked). The Checked build still builds the Checked version of the projects. --- Source/Dafny.sln | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Dafny.sln b/Source/Dafny.sln index 40e71952..e7ba8026 100644 --- a/Source/Dafny.sln +++ b/Source/Dafny.sln @@ -28,8 +28,8 @@ Global {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Debug|.NET.Build.0 = Debug|Any CPU {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Debug|Mixed Platforms.ActiveCfg = Checked|Any CPU - {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Debug|Mixed Platforms.Build.0 = Checked|Any CPU + {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Release|.NET.ActiveCfg = Release|Any CPU {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Release|Any CPU.ActiveCfg = Release|Any CPU {63400D1F-05B2-453E-9592-1EAB74B2C9CC}.Release|Any CPU.Build.0 = Release|Any CPU @@ -45,8 +45,8 @@ Global {FE44674A-1633-4917-99F4-57635E6FA740}.Debug|.NET.Build.0 = Debug|Any CPU {FE44674A-1633-4917-99F4-57635E6FA740}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FE44674A-1633-4917-99F4-57635E6FA740}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FE44674A-1633-4917-99F4-57635E6FA740}.Debug|Mixed Platforms.ActiveCfg = Checked|Any CPU - {FE44674A-1633-4917-99F4-57635E6FA740}.Debug|Mixed Platforms.Build.0 = Checked|Any CPU + {FE44674A-1633-4917-99F4-57635E6FA740}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {FE44674A-1633-4917-99F4-57635E6FA740}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {FE44674A-1633-4917-99F4-57635E6FA740}.Release|.NET.ActiveCfg = Release|Any CPU {FE44674A-1633-4917-99F4-57635E6FA740}.Release|Any CPU.ActiveCfg = Release|Any CPU {FE44674A-1633-4917-99F4-57635E6FA740}.Release|Any CPU.Build.0 = Release|Any CPU -- cgit v1.2.3 From 454909184e73582e425cad56a526956d91b88dcc Mon Sep 17 00:00:00 2001 From: qunyanm Date: Thu, 14 May 2015 15:44:39 -0700 Subject: Allow MatchExpr and MatchStmt to have nested patterns. Such as function last(xs: List): T requires xs != Nil { match xs case Cons(y, Nil) => y case Cons(y, Cons(z, zs)) => last(Cons(z, zs)) } And function minus(x: Nat, y: Nat): Nat { match (x, y) case (Zero, _) => Zero case (Suc(_), Zero) => x case (Suc(a), Suc(b)) => minus(a, b) } --- Source/Dafny/Cloner.cs | 37 +- Source/Dafny/Dafny.atg | 50 ++- Source/Dafny/DafnyAst.cs | 118 ++++- Source/Dafny/Parser.cs | 260 ++++++----- Source/Dafny/Printer.cs | 60 +-- Source/Dafny/Resolver.cs | 867 ++++++++++++++++++++++++++++++------- Source/Dafny/Rewriter.cs | 52 +++ Test/dafny0/NestedMatch.dfy | 59 +++ Test/dafny0/NestedMatch.dfy.expect | 2 + 9 files changed, 1157 insertions(+), 348 deletions(-) create mode 100644 Test/dafny0/NestedMatch.dfy create mode 100644 Test/dafny0/NestedMatch.dfy.expect diff --git a/Source/Dafny/Cloner.cs b/Source/Dafny/Cloner.cs index 9f83bf09..f729d411 100644 --- a/Source/Dafny/Cloner.cs +++ b/Source/Dafny/Cloner.cs @@ -300,9 +300,8 @@ namespace Microsoft.Dafny var e = (ExprDotName)expr; return new ExprDotName(Tok(e.tok), CloneExpr(e.Lhs), e.SuffixName, e.OptTypeArguments == null ? null : e.OptTypeArguments.ConvertAll(CloneType)); } else if (expr is ApplySuffix) { - var e = (ApplySuffix)expr; - return new ApplySuffix(Tok(e.tok), CloneExpr(e.Lhs), e.Args.ConvertAll(CloneExpr)); - + var e = (ApplySuffix) expr; + return CloneApplySuffix(e); } else if (expr is MemberSelectExpr) { var e = (MemberSelectExpr)expr; return new MemberSelectExpr(Tok(e.tok), CloneExpr(e.Obj), e.MemberName); @@ -411,7 +410,7 @@ namespace Microsoft.Dafny } else if (expr is MatchExpr) { var e = (MatchExpr)expr; return new MatchExpr(Tok(e.tok), CloneExpr(e.Source), - e.Cases.ConvertAll(c => new MatchCaseExpr(Tok(c.tok), c.Id, c.Arguments.ConvertAll(CloneBoundVar), CloneExpr(c.Body))), e.UsesOptionalBraces); + e.Cases.ConvertAll(CloneMatchCaseExpr), e.UsesOptionalBraces); } else if (expr is NegationExpression) { var e = (NegationExpression)expr; @@ -422,6 +421,22 @@ namespace Microsoft.Dafny } } + public MatchCaseExpr CloneMatchCaseExpr(MatchCaseExpr c) { + Contract.Requires(c != null); + if (c.Arguments != null) { + Contract.Assert(c.CasePatterns == null); + return new MatchCaseExpr(Tok(c.tok), c.Id, c.Arguments.ConvertAll(CloneBoundVar), CloneExpr(c.Body)); + } else { + Contract.Assert(c.Arguments == null); + Contract.Assert(c.CasePatterns != null); + return new MatchCaseExpr(Tok(c.tok), c.Id, c.CasePatterns.ConvertAll(CloneCasePattern), CloneExpr(c.Body)); + } + } + + public virtual Expression CloneApplySuffix(ApplySuffix e) { + return new ApplySuffix(Tok(e.tok), CloneExpr(e.Lhs), e.Args.ConvertAll(CloneExpr)); + } + public virtual CasePattern CloneCasePattern(CasePattern pat) { Contract.Requires(pat != null); if (pat.Var != null) { @@ -530,7 +545,7 @@ namespace Microsoft.Dafny } else if (stmt is MatchStmt) { var s = (MatchStmt)stmt; r = new MatchStmt(Tok(s.Tok), Tok(s.EndTok), CloneExpr(s.Source), - s.Cases.ConvertAll(c => new MatchCaseStmt(Tok(c.tok), c.Id, c.Arguments.ConvertAll(CloneBoundVar), c.Body.ConvertAll(CloneStmt))), s.UsesOptionalBraces); + s.Cases.ConvertAll(CloneMatchCaseStmt), s.UsesOptionalBraces); } else if (stmt is AssignSuchThatStmt) { var s = (AssignSuchThatStmt)stmt; @@ -562,6 +577,18 @@ namespace Microsoft.Dafny return r; } + public MatchCaseStmt CloneMatchCaseStmt(MatchCaseStmt c) { + Contract.Requires(c != null); + if (c.Arguments != null) { + Contract.Assert(c.CasePatterns == null); + return new MatchCaseStmt(Tok(c.tok), c.Id, c.Arguments.ConvertAll(CloneBoundVar), c.Body.ConvertAll(CloneStmt)); + } else { + Contract.Assert(c.Arguments == null); + Contract.Assert(c.CasePatterns != null); + return new MatchCaseStmt(Tok(c.tok), c.Id, c.CasePatterns.ConvertAll(CloneCasePattern), c.Body.ConvertAll(CloneStmt)); + } + } + public CalcStmt.CalcOp CloneCalcOp(CalcStmt.CalcOp op) { if (op is CalcStmt.BinaryCalcOp) { return new CalcStmt.BinaryCalcOp(((CalcStmt.BinaryCalcOp) op).Op); diff --git a/Source/Dafny/Dafny.atg b/Source/Dafny/Dafny.atg index c03f5ce0..3f684ff6 100644 --- a/Source/Dafny/Dafny.atg +++ b/Source/Dafny/Dafny.atg @@ -1794,17 +1794,24 @@ MatchStmt CaseStatement = (. Contract.Ensures(Contract.ValueAtReturn(out c) != null); IToken/*!*/ x, id; - List arguments = new List(); - BoundVar/*!*/ bv; + List arguments = new List(); + CasePattern/*!*/ pat; List body = new List(); + string/*!*/ name = ""; .) "case" (. x = t; .) - Ident - [ "(" - IdentTypeOptional (. arguments.Add(bv); .) - { "," IdentTypeOptional (. arguments.Add(bv); .) - } - ")" ] + ( Ident (. name = id.val; .) + [ "(" + CasePattern (. arguments.Add(pat); .) + { "," CasePattern (. arguments.Add(pat); .) + } + ")" ] + | "(" + CasePattern (. arguments.Add(pat); .) + { "," CasePattern (. arguments.Add(pat); .) + } + ")" + ) "=>" SYNC /* this SYNC and the one inside the loop below are used to avoid problems with the IsNotEndOfCase test. The SYNC will * skip until the next symbol that can legally occur here, which is either the beginning of a Stmt or whatever is allowed @@ -1814,7 +1821,7 @@ CaseStatement Stmt SYNC /* see comment about SYNC above */ } - (. c = new MatchCaseStmt(x, id.val, arguments, body); .) + (. c = new MatchCaseStmt(x, name, arguments, body); .) . /*------------------------------------------------------------------------*/ AssertStmt @@ -2584,19 +2591,26 @@ MatchExpression . CaseExpression = (. Contract.Ensures(Contract.ValueAtReturn(out c) != null); IToken/*!*/ x, id; - List arguments = new List(); - BoundVar/*!*/ bv; + List arguments = new List(); + CasePattern/*!*/ pat; Expression/*!*/ body; + string/*!*/ name = ""; .) "case" (. x = t; .) - Ident - [ "(" - IdentTypeOptional (. arguments.Add(bv); .) - { "," IdentTypeOptional (. arguments.Add(bv); .) - } - ")" ] + ( Ident (. name = id.val; .) + [ "(" + CasePattern (. arguments.Add(pat); .) + { "," CasePattern (. arguments.Add(pat); .) + } + ")" ] + | "(" + CasePattern (. arguments.Add(pat); .) + { "," CasePattern (. arguments.Add(pat); .) + } + ")" + ) "=>" - Expression (. c = new MatchCaseExpr(x, id.val, arguments, body); .) + Expression (. c = new MatchCaseExpr(x, name, arguments, body); .) . CasePattern = (. IToken id; List arguments; diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index 99dfecd6..a94b9a1b 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -4710,8 +4710,8 @@ namespace Microsoft.Dafny { Contract.Invariant(cce.NonNullElements(MissingCases)); } - public readonly Expression Source; - public readonly List Cases; + private Expression source; + private List cases; public readonly List MissingCases = new List(); // filled in during resolution public readonly bool UsesOptionalBraces; @@ -4721,14 +4721,31 @@ namespace Microsoft.Dafny { Contract.Requires(endTok != null); Contract.Requires(source != null); Contract.Requires(cce.NonNullElements(cases)); - this.Source = source; - this.Cases = cases; + this.source = source; + this.cases = cases; this.UsesOptionalBraces = usesOptionalBraces; } + public Expression Source { + get { return source; } + } + + public List Cases { + get { return cases; } + } + + // should only be used in desugar in resolve to change the cases of the matchexpr + public void UpdateSource(Expression source) { + this.source = source; + } + + public void UpdateCases(List cases) { + this.cases = cases; + } + public override IEnumerable SubStatements { get { - foreach (var kase in Cases) { + foreach (var kase in cases) { foreach (var s in kase.Body) { yield return s; } @@ -4745,7 +4762,7 @@ namespace Microsoft.Dafny { public class MatchCaseStmt : MatchCase { - public readonly List Body; + private List body; [ContractInvariantMethod] void ObjectInvariant() { @@ -4759,7 +4776,25 @@ namespace Microsoft.Dafny { Contract.Requires(id != null); Contract.Requires(cce.NonNullElements(arguments)); Contract.Requires(cce.NonNullElements(body)); - this.Body = body; + this.body = body; + } + + public MatchCaseStmt(IToken tok, string id, [Captured] List cps, [Captured] List body) + : base(tok, id, cps) { + Contract.Requires(tok != null); + Contract.Requires(id != null); + Contract.Requires(cce.NonNullElements(cps)); + Contract.Requires(cce.NonNullElements(body)); + this.body = body; + } + + public List Body { + get { return body; } + } + + // should only be called by resolve to reset the body of the MatchCaseExpr + public void UpdateBody(List body) { + this.body = body; } } @@ -6806,8 +6841,8 @@ namespace Microsoft.Dafny { } public class MatchExpr : Expression { // a MatchExpr is an "extended expression" and is only allowed in certain places - public readonly Expression Source; - public readonly List Cases; + private Expression source; + private List cases; public readonly List MissingCases = new List(); // filled in during resolution public readonly bool UsesOptionalBraces; @@ -6823,15 +6858,32 @@ namespace Microsoft.Dafny { Contract.Requires(tok != null); Contract.Requires(source != null); Contract.Requires(cce.NonNullElements(cases)); - this.Source = source; - this.Cases = cases; + this.source = source; + this.cases = cases; this.UsesOptionalBraces = usesOptionalBraces; } + public Expression Source { + get { return source; } + } + + public List Cases { + get { return cases; } + } + + // should only be used in desugar in resolve to change the source and cases of the matchexpr + public void UpdateSource(Expression source) { + this.source = source; + } + + public void UpdateCases(List cases) { + this.cases = cases; + } + public override IEnumerable SubExpressions { get { yield return Source; - foreach (var mc in Cases) { + foreach (var mc in cases) { yield return mc.Body; } } @@ -6913,12 +6965,13 @@ namespace Microsoft.Dafny { public readonly IToken tok; public readonly string Id; public DatatypeCtor Ctor; // filled in by resolution - public readonly List Arguments; + public List Arguments; // created by the resolver. + public List CasePatterns; // generated from parsers. It should be converted to List during resolver. Invariant: CasePatterns != null ==> Arguments == null [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(tok != null); Contract.Invariant(Id != null); - Contract.Invariant(cce.NonNullElements(Arguments)); + Contract.Invariant(cce.NonNullElements(Arguments) || cce.NonNullElements(CasePatterns)); } public MatchCase(IToken tok, string id, [Captured] List arguments) { @@ -6929,24 +6982,51 @@ namespace Microsoft.Dafny { this.Id = id; this.Arguments = arguments; } + + public MatchCase(IToken tok, string id, [Captured] List cps) { + Contract.Requires(tok != null); + Contract.Requires(id != null); + Contract.Requires(cce.NonNullElements(cps)); + this.tok = tok; + this.Id = id; + this.CasePatterns = cps; + } } public class MatchCaseExpr : MatchCase { - public readonly Expression Body; + private Expression body; [ContractInvariantMethod] void ObjectInvariant() { - Contract.Invariant(Body != null); + Contract.Invariant(body != null); } public MatchCaseExpr(IToken tok, string id, [Captured] List arguments, Expression body) - : base(tok, id, arguments) - { + : base(tok, id, arguments) { Contract.Requires(tok != null); Contract.Requires(id != null); Contract.Requires(cce.NonNullElements(arguments)); Contract.Requires(body != null); - this.Body = body; + this.body = body; + } + + public MatchCaseExpr(IToken tok, string id, [Captured] List cps, Expression body) + : base(tok, id, cps) + { + Contract.Requires(tok != null); + Contract.Requires(id != null); + Contract.Requires(cce.NonNullElements(cps)); + Contract.Requires(body != null); + this.body = body; + } + + public Expression Body { + get { return body; } + } + + // should only be called by resolve to reset the body of the MatchCaseExpr + public void UpdateBody(Expression body) { + this.body = body; } } diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs index 9e283ef5..d0924169 100644 --- a/Source/Dafny/Parser.cs +++ b/Source/Dafny/Parser.cs @@ -2884,31 +2884,76 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo void CaseStatement(out MatchCaseStmt/*!*/ c) { Contract.Ensures(Contract.ValueAtReturn(out c) != null); IToken/*!*/ x, id; - List arguments = new List(); - BoundVar/*!*/ bv; + List arguments = new List(); + CasePattern/*!*/ pat; List body = new List(); + string/*!*/ name = ""; Expect(31); x = t; - Ident(out id); - if (la.kind == 48) { + if (la.kind == 1) { + Ident(out id); + name = id.val; + if (la.kind == 48) { + Get(); + CasePattern(out pat); + arguments.Add(pat); + while (la.kind == 21) { + Get(); + CasePattern(out pat); + arguments.Add(pat); + } + Expect(49); + } + } else if (la.kind == 48) { Get(); - IdentTypeOptional(out bv); - arguments.Add(bv); + CasePattern(out pat); + arguments.Add(pat); while (la.kind == 21) { Get(); - IdentTypeOptional(out bv); - arguments.Add(bv); + CasePattern(out pat); + arguments.Add(pat); } Expect(49); - } + } else SynErr(202); Expect(27); - while (!(StartOf(28))) {SynErr(202); Get();} + while (!(StartOf(28))) {SynErr(203); Get();} while (IsNotEndOfCase()) { Stmt(body); - while (!(StartOf(28))) {SynErr(203); Get();} + while (!(StartOf(28))) {SynErr(204); 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(48); + arguments = new List(); + if (la.kind == 1) { + CasePattern(out pat); + arguments.Add(pat); + while (la.kind == 21) { + Get(); + CasePattern(out pat); + arguments.Add(pat); + } + } + Expect(49); + pat = new CasePattern(id, id.val, arguments); + } else if (la.kind == 1) { + IdentTypeOptional(out bv); + pat = new CasePattern(bv.tok, bv); + + } else SynErr(205); + if (pat == null) { + pat = new CasePattern(t, "_ParseError", new List()); } - c = new MatchCaseStmt(x, id.val, arguments, body); + } void QuantifierDomain(out List bvars, out Attributes attrs, out Expression range) { @@ -3005,7 +3050,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo x = t; binOp = BinaryExpr.Opcode.Exp; break; } - default: SynErr(204); break; + default: SynErr(206); break; } if (k == null) { op = new Microsoft.Dafny.CalcStmt.BinaryCalcOp(binOp); @@ -3020,7 +3065,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Get(); } else if (la.kind == 110) { Get(); - } else SynErr(205); + } else SynErr(207); } void ImpliesOp() { @@ -3028,7 +3073,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Get(); } else if (la.kind == 112) { Get(); - } else SynErr(206); + } else SynErr(208); } void ExpliesOp() { @@ -3036,7 +3081,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Get(); } else if (la.kind == 114) { Get(); - } else SynErr(207); + } else SynErr(209); } void AndOp() { @@ -3044,7 +3089,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Get(); } else if (la.kind == 116) { Get(); - } else SynErr(208); + } else SynErr(210); } void OrOp() { @@ -3052,7 +3097,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Get(); } else if (la.kind == 118) { Get(); - } else SynErr(209); + } else SynErr(211); } void NegOp() { @@ -3060,7 +3105,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Get(); } else if (la.kind == 120) { Get(); - } else SynErr(210); + } else SynErr(212); } void Forall() { @@ -3068,7 +3113,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Get(); } else if (la.kind == 121) { Get(); - } else SynErr(211); + } else SynErr(213); } void Exists() { @@ -3076,7 +3121,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Get(); } else if (la.kind == 123) { Get(); - } else SynErr(212); + } else SynErr(214); } void QSep() { @@ -3084,7 +3129,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Get(); } else if (la.kind == 24) { Get(); - } else SynErr(213); + } else SynErr(215); } void EquivExpression(out Expression e0, bool allowSemi, bool allowLambda) { @@ -3118,7 +3163,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo LogicalExpression(out e1, allowSemi, allowLambda); e0 = new BinaryExpr(x, BinaryExpr.Opcode.Exp, e0, e1); } - } else SynErr(214); + } else SynErr(216); } } @@ -3148,7 +3193,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(215); + } else SynErr(217); } } @@ -3366,7 +3411,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo x = t; op = BinaryExpr.Opcode.Ge; break; } - default: SynErr(216); break; + default: SynErr(218); break; } } @@ -3388,7 +3433,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } else if (la.kind == 126) { Get(); x = t; op = BinaryExpr.Opcode.Sub; - } else SynErr(217); + } else SynErr(219); } void UnaryExpression(out Expression e, bool allowSemi, bool allowLambda) { @@ -3441,7 +3486,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo while (IsSuffix()) { Suffix(ref e); } - } else SynErr(218); + } else SynErr(220); } void MulOp(out IToken x, out BinaryExpr.Opcode op) { @@ -3455,7 +3500,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } else if (la.kind == 128) { Get(); x = t; op = BinaryExpr.Opcode.Mod; - } else SynErr(219); + } else SynErr(221); } void MapDisplayExpr(IToken/*!*/ mapToken, bool finite, out Expression e) { @@ -3493,7 +3538,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } else if (la.kind == 104) { HashCall(id, out openParen, out typeArgs, out args); } else if (StartOf(30)) { - } else SynErr(220); + } else SynErr(222); e = new ExprDotName(id, e, id.val, typeArgs); if (openParen != null) { e = new ApplySuffix(openParen, e, args); @@ -3546,7 +3591,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo multipleIndices.Add(ee); } - } else SynErr(221); + } else SynErr(223); } else if (la.kind == 135) { Get(); anyDots = true; @@ -3554,7 +3599,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Expression(out ee, true, true); e1 = ee; } - } else SynErr(222); + } else SynErr(224); if (multipleIndices != null) { e = new MultiSelectExpr(x, e, multipleIndices); // make sure an array class with this dimensionality exists @@ -3598,7 +3643,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } Expect(49); e = new ApplySuffix(openParen, e, args); - } else SynErr(223); + } else SynErr(225); } void LambdaExpression(out Expression e, bool allowSemi) { @@ -3627,7 +3672,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } } Expect(49); - } else SynErr(224); + } else SynErr(226); while (la.kind == 42 || la.kind == 43) { if (la.kind == 42) { Get(); @@ -3702,7 +3747,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo NamedExpr(out e, allowSemi, allowLambda); break; } - default: SynErr(225); break; + default: SynErr(227); break; } } @@ -3717,7 +3762,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } else if (la.kind == 104) { HashCall(id, out openParen, out typeArgs, out args); } else if (StartOf(30)) { - } else SynErr(226); + } else SynErr(228); e = new NameSegment(id, id.val, typeArgs); if (openParen != null) { e = new ApplySuffix(openParen, e, args); @@ -3746,7 +3791,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } e = new SeqDisplayExpr(x, elements); Expect(47); - } else SynErr(227); + } else SynErr(229); } void MultiSetExpr(out Expression e) { @@ -3770,7 +3815,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Expression(out e, true, true); e = new MultiSetFormingExpr(x, e); Expect(49); - } else SynErr(228); + } else SynErr(230); } void ConstAtomExpression(out Expression e, bool allowSemi, bool allowLambda) { @@ -3866,7 +3911,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo ParensExpression(out e, allowSemi, allowLambda); break; } - default: SynErr(229); break; + default: SynErr(231); break; } } @@ -3895,7 +3940,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo n = BigInteger.Zero; } - } else SynErr(230); + } else SynErr(232); } void Dec(out Basetypes.BigDec d) { @@ -3939,7 +3984,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } else if (la.kind == 28) { Get(); oneShot = true; - } else SynErr(231); + } else SynErr(233); } void MapLiteralExpressions(out List elements) { @@ -4002,7 +4047,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo CaseExpression(out c, allowSemi, allowLambda); cases.Add(c); } - } else SynErr(232); + } else SynErr(234); e = new MatchExpr(x, e, cases, usesOptionalBrace); } @@ -4020,7 +4065,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } else if (la.kind == 122 || la.kind == 123) { Exists(); x = t; - } else SynErr(233); + } else SynErr(235); QuantifierDomain(out bvars, out attrs, out range); QSep(); Expression(out body, allowSemi, allowLambda); @@ -4072,7 +4117,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo AssumeStmt(out s); } else if (la.kind == 30) { CalcStmt(out s); - } else SynErr(234); + } else SynErr(236); } void LetExpr(out Expression e, bool allowSemi, bool allowLambda) { @@ -4116,7 +4161,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } } - } else SynErr(235); + } else SynErr(237); Expression(out e, false, true); letRHSs.Add(e); while (la.kind == 21) { @@ -4143,16 +4188,20 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo e = new NamedExpr(x, d.val, expr); } - void CasePattern(out CasePattern pat) { - IToken id; List arguments; - BoundVar bv; - pat = null; + void CaseExpression(out MatchCaseExpr c, bool allowSemi, bool allowLambda) { + Contract.Ensures(Contract.ValueAtReturn(out c) != null); IToken/*!*/ x, id; + List arguments = new List(); + CasePattern/*!*/ pat; + Expression/*!*/ body; + string/*!*/ name = ""; - if (IsIdentParen()) { + Expect(31); + x = t; + if (la.kind == 1) { Ident(out id); - Expect(48); - arguments = new List(); - if (la.kind == 1) { + name = id.val; + if (la.kind == 48) { + Get(); CasePattern(out pat); arguments.Add(pat); while (la.kind == 21) { @@ -4160,43 +4209,22 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo CasePattern(out pat); arguments.Add(pat); } + Expect(49); } - Expect(49); - pat = new CasePattern(id, id.val, arguments); - } else if (la.kind == 1) { - IdentTypeOptional(out bv); - pat = new CasePattern(bv.tok, bv); - - } else SynErr(236); - if (pat == null) { - pat = new CasePattern(t, "_ParseError", new List()); - } - - } - - void CaseExpression(out MatchCaseExpr c, bool allowSemi, bool allowLambda) { - Contract.Ensures(Contract.ValueAtReturn(out c) != null); IToken/*!*/ x, id; - List arguments = new List(); - BoundVar/*!*/ bv; - Expression/*!*/ body; - - Expect(31); - x = t; - Ident(out id); - if (la.kind == 48) { + } else if (la.kind == 48) { Get(); - IdentTypeOptional(out bv); - arguments.Add(bv); + CasePattern(out pat); + arguments.Add(pat); while (la.kind == 21) { Get(); - IdentTypeOptional(out bv); - arguments.Add(bv); + CasePattern(out pat); + arguments.Add(pat); } Expect(49); - } + } else SynErr(238); Expect(27); Expression(out body, allowSemi, allowLambda); - c = new MatchCaseExpr(x, id.val, arguments, body); + c = new MatchCaseExpr(x, name, arguments, body); } void HashCall(IToken id, out IToken openParen, out List typeArgs, out List args) { @@ -4258,7 +4286,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } else if (la.kind == 42) { Get(); x = t; - } else SynErr(237); + } else SynErr(239); } @@ -4532,42 +4560,44 @@ public class Errors { case 199: s = "this symbol not expected in LoopSpec"; break; case 200: s = "this symbol not expected in LoopSpec"; break; case 201: s = "invalid LoopSpec"; break; - case 202: s = "this symbol not expected in CaseStatement"; break; + case 202: s = "invalid CaseStatement"; break; case 203: s = "this symbol not expected in CaseStatement"; break; - case 204: s = "invalid CalcOp"; break; - case 205: s = "invalid EquivOp"; break; - case 206: s = "invalid ImpliesOp"; break; - case 207: s = "invalid ExpliesOp"; break; - case 208: s = "invalid AndOp"; break; - case 209: s = "invalid OrOp"; break; - case 210: s = "invalid NegOp"; break; - case 211: s = "invalid Forall"; break; - case 212: s = "invalid Exists"; break; - case 213: s = "invalid QSep"; break; - case 214: s = "invalid ImpliesExpliesExpression"; break; - case 215: s = "invalid LogicalExpression"; break; - case 216: s = "invalid RelOp"; break; - case 217: s = "invalid AddOp"; break; - case 218: s = "invalid UnaryExpression"; break; - case 219: s = "invalid MulOp"; break; - case 220: s = "invalid Suffix"; break; - case 221: s = "invalid Suffix"; break; + case 204: s = "this symbol not expected in CaseStatement"; break; + case 205: s = "invalid CasePattern"; break; + case 206: s = "invalid CalcOp"; break; + case 207: s = "invalid EquivOp"; break; + case 208: s = "invalid ImpliesOp"; break; + case 209: s = "invalid ExpliesOp"; break; + case 210: s = "invalid AndOp"; break; + case 211: s = "invalid OrOp"; break; + case 212: s = "invalid NegOp"; break; + case 213: s = "invalid Forall"; break; + case 214: s = "invalid Exists"; break; + case 215: s = "invalid QSep"; break; + case 216: s = "invalid ImpliesExpliesExpression"; break; + case 217: s = "invalid LogicalExpression"; break; + case 218: s = "invalid RelOp"; break; + case 219: s = "invalid AddOp"; break; + case 220: s = "invalid UnaryExpression"; break; + case 221: s = "invalid MulOp"; break; case 222: s = "invalid Suffix"; break; case 223: s = "invalid Suffix"; break; - case 224: s = "invalid LambdaExpression"; break; - case 225: s = "invalid EndlessExpression"; break; - case 226: s = "invalid NameSegment"; break; - case 227: s = "invalid DisplayExpr"; break; - case 228: s = "invalid MultiSetExpr"; break; - case 229: s = "invalid ConstAtomExpression"; break; - case 230: s = "invalid Nat"; break; - case 231: s = "invalid LambdaArrow"; break; - case 232: s = "invalid MatchExpression"; break; - case 233: s = "invalid QuantifierGuts"; break; - case 234: s = "invalid StmtInExpr"; break; - case 235: s = "invalid LetExpr"; break; - case 236: s = "invalid CasePattern"; break; - case 237: s = "invalid DotSuffix"; break; + case 224: s = "invalid Suffix"; break; + case 225: s = "invalid Suffix"; break; + case 226: s = "invalid LambdaExpression"; break; + case 227: s = "invalid EndlessExpression"; break; + case 228: s = "invalid NameSegment"; break; + case 229: s = "invalid DisplayExpr"; break; + case 230: s = "invalid MultiSetExpr"; break; + case 231: s = "invalid ConstAtomExpression"; break; + case 232: s = "invalid Nat"; break; + case 233: s = "invalid LambdaArrow"; break; + case 234: s = "invalid MatchExpression"; break; + case 235: s = "invalid QuantifierGuts"; break; + case 236: s = "invalid StmtInExpr"; break; + case 237: s = "invalid LetExpr"; break; + case 238: s = "invalid CaseExpression"; break; + case 239: s = "invalid DotSuffix"; break; default: s = "error " + n; break; } diff --git a/Source/Dafny/Printer.cs b/Source/Dafny/Printer.cs index bf42c71a..0259f12c 100644 --- a/Source/Dafny/Printer.cs +++ b/Source/Dafny/Printer.cs @@ -901,17 +901,7 @@ namespace Microsoft.Dafny { wr.WriteLine(); Indent(caseInd); wr.Write("case {0}", mc.Id); - if (mc.Arguments.Count != 0) { - string sep = "("; - foreach (BoundVar bv in mc.Arguments) { - wr.Write("{0}{1}", sep, bv.DisplayName); - if (bv.Type is NonProxyType) { - wr.Write(": {0}", bv.Type); - } - sep = ", "; - } - wr.Write(")"); - } + PrintMatchCaseArgument(mc); wr.Write(" =>"); foreach (Statement bs in mc.Body) { wr.WriteLine(); @@ -1199,17 +1189,7 @@ namespace Microsoft.Dafny { bool isLastCase = i == e.Cases.Count - 1; Indent(ind); wr.Write("case {0}", mc.Id); - if (mc.Arguments.Count != 0) { - string sep = "("; - foreach (BoundVar bv in mc.Arguments) { - wr.Write("{0}{1}", sep, bv.DisplayName); - if (bv.Type is NonProxyType) { - wr.Write(": {0}", bv.Type); - } - sep = ", "; - } - wr.Write(")"); - } + PrintMatchCaseArgument(mc); wr.WriteLine(" =>"); PrintExtendedExpr(mc.Body, ind + IndentAmount, isLastCase, isLastCase && (parensNeeded || endWithCloseParen)); i++; @@ -1246,6 +1226,33 @@ namespace Microsoft.Dafny { } } + public void PrintMatchCaseArgument(MatchCase mc) { + if (mc.Arguments != null) { + if (mc.Arguments.Count != 0) { + string sep = "("; + foreach (BoundVar bv in mc.Arguments) { + wr.Write("{0}{1}", sep, bv.DisplayName); + if (bv.Type is NonProxyType) { + wr.Write(": {0}", bv.Type); + } + sep = ", "; + } + wr.Write(")"); + } + } else { + Contract.Assert(mc.CasePatterns != null); + if (mc.CasePatterns.Count != 0) { + string sep = "("; + foreach (var cp in mc.CasePatterns) { + wr.Write(sep); + PrintCasePattern(cp); + sep = ", "; + } + wr.Write(")"); + } + } + } + public void PrintExpression(Expression expr, bool isFollowedBySemicolon) { Contract.Requires(expr != null); PrintExpr(expr, 0, false, true, isFollowedBySemicolon, -1); @@ -1859,14 +1866,7 @@ namespace Microsoft.Dafny { foreach (var mc in e.Cases) { bool isLastCase = i == e.Cases.Count - 1; wr.Write(" case {0}", mc.Id); - if (mc.Arguments.Count != 0) { - string sep = "("; - foreach (BoundVar bv in mc.Arguments) { - wr.Write("{0}{1}", sep, bv.DisplayName); - sep = ", "; - } - wr.Write(")"); - } + PrintMatchCaseArgument(mc); wr.Write(" => "); PrintExpression(mc.Body, isRightmost && isLastCase, !parensNeeded && isFollowedBySemicolon); i++; diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index f7ec135b..be348311 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -5185,106 +5185,381 @@ namespace Microsoft.Dafny Contract.Assert(prevErrorCount != ErrorCount || s.Steps.Count == s.Hints.Count); } else if (stmt is MatchStmt) { - MatchStmt s = (MatchStmt)stmt; - bool bodyIsSpecOnly = specContextOnly; - int prevErrorCount = ErrorCount; - ResolveExpression(s.Source, new ResolveOpts(codeContext, true, specContextOnly)); - Contract.Assert(s.Source.Type != null); // follows from postcondition of ResolveExpression - bool successfullyResolved = ErrorCount == prevErrorCount; - if (!specContextOnly && successfullyResolved) { - bodyIsSpecOnly = UsesSpecFeatures(s.Source); - } - UserDefinedType sourceType = null; - DatatypeDecl dtd = null; - if (s.Source.Type.IsDatatype) { - sourceType = (UserDefinedType)s.Source.Type.NormalizeExpand(); - dtd = cce.NonNull((DatatypeDecl)sourceType.ResolvedClass); + ResolveMatchStmt(stmt, specContextOnly, codeContext); + } else if (stmt is SkeletonStatement) { + var s = (SkeletonStatement)stmt; + Error(s.Tok, "skeleton statements are allowed only in refining methods"); + // nevertheless, resolve the underlying statement; hey, why not + if (s.S != null) { + ResolveStatement(s.S, specContextOnly, codeContext); } - var subst = new Dictionary(); - Dictionary ctors; - if (dtd == null) { - Error(s.Source, "the type of the match source expression must be a datatype (instead found {0})", s.Source.Type); - ctors = null; - } else { - Contract.Assert(sourceType != null); // dtd and sourceType are set together above - ctors = datatypeCtors[dtd]; - Contract.Assert(ctors != null); // dtd should have been inserted into datatypeCtors during a previous resolution stage + } else { + Contract.Assert(false); throw new cce.UnreachableException(); + } + } - // build the type-parameter substitution map for this use of the datatype - for (int i = 0; i < dtd.TypeArgs.Count; i++) { - subst.Add(dtd.TypeArgs[i], sourceType.TypeArgs[i]); - } + void ResolveMatchStmt(Statement stmt, bool specContextOnly, ICodeContext codeContext) { + MatchStmt s = (MatchStmt)stmt; + DesugarMatchStmtWithTupleExpression(s); + + bool bodyIsSpecOnly = specContextOnly; + int prevErrorCount = ErrorCount; + ResolveExpression(s.Source, new ResolveOpts(codeContext, true, specContextOnly)); + Contract.Assert(s.Source.Type != null); // follows from postcondition of ResolveExpression + bool successfullyResolved = ErrorCount == prevErrorCount; + if (!specContextOnly && successfullyResolved) { + bodyIsSpecOnly = UsesSpecFeatures(s.Source); + } + UserDefinedType sourceType = null; + DatatypeDecl dtd = null; + if (s.Source.Type.IsDatatype) { + sourceType = (UserDefinedType)s.Source.Type.NormalizeExpand(); + dtd = cce.NonNull((DatatypeDecl)sourceType.ResolvedClass); + } + var subst = new Dictionary(); + Dictionary ctors; + if (dtd == null) { + Error(s.Source, "the type of the match source expression must be a datatype (instead found {0})", s.Source.Type); + ctors = null; + } else { + Contract.Assert(sourceType != null); // dtd and sourceType are set together above + ctors = datatypeCtors[dtd]; + Contract.Assert(ctors != null); // dtd should have been inserted into datatypeCtors during a previous resolution stage + + // build the type-parameter substitution map for this use of the datatype + for (int i = 0; i < dtd.TypeArgs.Count; i++) { + subst.Add(dtd.TypeArgs[i], sourceType.TypeArgs[i]); } - s.IsGhost = bodyIsSpecOnly; + } + s.IsGhost = bodyIsSpecOnly; - ISet memberNamesUsed = new HashSet(); - foreach (MatchCaseStmt mc in s.Cases) { - DatatypeCtor ctor = null; - if (ctors != null) { - Contract.Assert(dtd != null); - if (!ctors.TryGetValue(mc.Id, out ctor)) { - Error(mc.tok, "member {0} does not exist in datatype {1}", mc.Id, dtd.Name); + // convert CasePattern in MatchCaseExpr to BoundVar and flatten the MatchCaseExpr. + Type type = new InferredTypeProxy(); + string name = FreshTempVarName("_mc#", codeContext); + BoundVar bv = new BoundVar(s.Tok, name, type); + List patternSubst = new List(); + DesugarMatchCaseStmt(s, dtd, bv, patternSubst); + + ISet memberNamesUsed = new HashSet(); + foreach (MatchCaseStmt mc in s.Cases) { + DatatypeCtor ctor = null; + if (ctors != null) { + Contract.Assert(dtd != null); + if (!ctors.TryGetValue(mc.Id, out ctor)) { + Error(mc.tok, "member {0} does not exist in datatype {1}", mc.Id, dtd.Name); + } else { + Contract.Assert(ctor != null); // follows from postcondition of TryGetValue + mc.Ctor = ctor; + if (ctor.Formals.Count != mc.Arguments.Count) { + Error(mc.tok, "member {0} has wrong number of formals (found {1}, expected {2})", mc.Id, mc.Arguments.Count, ctor.Formals.Count); + } + if (memberNamesUsed.Contains(mc.Id)) { + Error(mc.tok, "member {0} appears in more than one case", mc.Id); } else { - Contract.Assert(ctor != null); // follows from postcondition of TryGetValue - mc.Ctor = ctor; - if (ctor.Formals.Count != mc.Arguments.Count) { - Error(mc.tok, "member {0} has wrong number of formals (found {1}, expected {2})", mc.Id, mc.Arguments.Count, ctor.Formals.Count); - } - if (memberNamesUsed.Contains(mc.Id)) { - Error(mc.tok, "member {0} appears in more than one case", mc.Id); - } else { - memberNamesUsed.Add(mc.Id); // add mc.Id to the set of names used - } + memberNamesUsed.Add(mc.Id); // add mc.Id to the set of names used } } - scope.PushMarker(); - int i = 0; - foreach (BoundVar v in mc.Arguments) { - if (!scope.Push(v.Name, v)) { - Error(v, "Duplicate parameter name: {0}", v.Name); - } - ResolveType(v.tok, v.Type, codeContext, ResolveTypeOptionEnum.InferTypeProxies, null); - if (ctor != null && i < ctor.Formals.Count) { - Formal formal = ctor.Formals[i]; - Type st = SubstType(formal.Type, subst); - if (!UnifyTypes(v.Type, st)) { - Error(stmt, "the declared type of the formal ({0}) does not agree with the corresponding type in the constructor's signature ({1})", v.Type, st); - } - v.IsGhost = formal.IsGhost; + } + scope.PushMarker(); + int i = 0; + foreach (BoundVar v in mc.Arguments) { + if (!scope.Push(v.Name, v)) { + Error(v, "Duplicate parameter name: {0}", v.Name); + } + ResolveType(v.tok, v.Type, codeContext, ResolveTypeOptionEnum.InferTypeProxies, null); + if (ctor != null && i < ctor.Formals.Count) { + Formal formal = ctor.Formals[i]; + Type st = SubstType(formal.Type, subst); + if (!UnifyTypes(v.Type, st)) { + Error(stmt, "the declared type of the formal ({0}) does not agree with the corresponding type in the constructor's signature ({1})", v.Type, st); } - i++; + v.IsGhost = formal.IsGhost; } + i++; + } + foreach (Statement ss in mc.Body) { + ResolveStatement(ss, bodyIsSpecOnly, codeContext); + } + // substitute body to replace the case pat with v. This needs to happen + // after the body is resolved so we can scope the bv correctly. + if (patternSubst.Count > 0) { + MatchCaseExprSubstituteCloner cloner = new MatchCaseExprSubstituteCloner(patternSubst, bv); + List list = new List(); foreach (Statement ss in mc.Body) { - ResolveStatement(ss, bodyIsSpecOnly, codeContext); + Statement clone = cloner.CloneStmt(ss); + // resolve it again since we just cloned it. + ResolveStatement(clone, bodyIsSpecOnly, codeContext); + list.Add(clone); + } + mc.UpdateBody(list); + } + + scope.PopMarker(); + } + if (dtd != null && memberNamesUsed.Count != dtd.Ctors.Count) { + // We could complain about the syntactic omission of constructors: + // Error(stmt, "match statement does not cover all constructors"); + // but instead we let the verifier do a semantic check. + // So, for now, record the missing constructors: + foreach (var ctr in dtd.Ctors) { + if (!memberNamesUsed.Contains(ctr.Name)) { + s.MissingCases.Add(ctr); + } + } + Contract.Assert(memberNamesUsed.Count + s.MissingCases.Count == dtd.Ctors.Count); + } + if (!s.IsGhost) { + s.IsGhost = s.Cases.All(cs => cs.Body.All(ss => ss.IsGhost)); + } + } + + /* + * Convert + * match (x, y) + * case (Zero, _) => Zero + * case (Suc(_), Zero) => x + * case (Suc(a), Suc(b)) => minus(a, b) + * To: + * match x + * case Zero => match y + * case _ => zero + * case Suc(_) => match y + * case Zero => x + * case Suc(a) => match y + * case (b) => minus(a,b) + */ + void DesugarMatchStmtWithTupleExpression(MatchStmt me) { + // (x, y) is treated as a 2-tuple constructor + if (me.Source is DatatypeValue) { + var e = (DatatypeValue)me.Source; + Contract.Assert(e.Arguments.Count >= 1); + Expression source = e.Arguments[0]; + List cases = new List(); + foreach (MatchCaseStmt mc in me.Cases) { + Contract.Assert(mc.CasePatterns != null); + Contract.Assert(mc.CasePatterns.Count == e.Arguments.Count); + CasePattern cp = mc.CasePatterns[0]; + List patterns; + if (cp.Arguments != null) { + patterns = cp.Arguments; + } else { + patterns = new List(); + } + + List body = mc.Body; + for (int i = e.Arguments.Count; 1 <= --i; ) { + // others go into the body + body = CreateMatchCaseStmtBody(mc.tok, e.Arguments[i], mc.CasePatterns[i], body); } - scope.PopMarker(); + cases.Add(new MatchCaseStmt(cp.tok, cp.Id, patterns, body)); + } + me.UpdateSource(source); + me.UpdateCases(cases); + } + } + + List CreateMatchCaseStmtBody(Boogie.IToken tok, Expression source, CasePattern cp, List body) { + List cases = new List(); + List patterns; + if (cp.Var != null) { + var bv = cp.Var; + if (LocalVariable.HasWildcardName(bv)) { + return body; + } else { + patterns = new List(); } - if (dtd != null && memberNamesUsed.Count != dtd.Ctors.Count) { - // We could complain about the syntactic omission of constructors: - // Error(stmt, "match statement does not cover all constructors"); - // but instead we let the verifier do a semantic check. - // So, for now, record the missing constructors: - foreach (var ctr in dtd.Ctors) { - if (!memberNamesUsed.Contains(ctr.Name)) { - s.MissingCases.Add(ctr); + } else { + patterns = cp.Arguments; + } + cases.Add(new MatchCaseStmt(cp.tok, cp.Id, patterns, body)); + List list = new List(); + // endTok?? + list.Add(new MatchStmt(tok, tok, source, cases, false)); + return list; + } + + + /* + * Convert + * match xs + * case Cons(y, Cons(z, zs)) => last(Cons(z, zs)) + * case Cons(y, Nil) => y + * To + * match xs + * case Cons(y, ys) => match ys + * case Nil => y + * case Cons(z, zs) => last(ys) + */ + void DesugarMatchCaseStmt(MatchStmt s, DatatypeDecl dtd, BoundVar sourceVar, List patterns) { + Contract.Assert(dtd != null); + Dictionary ctors = datatypeCtors[dtd]; + foreach (MatchCaseStmt mc in s.Cases) { + if (mc.Arguments != null) { + // already desugared. This happens during the second pass resolver after cloning. + Contract.Assert(mc.CasePatterns == null); + return; + } + + Contract.Assert(mc.Arguments == null); + Contract.Assert(mc.CasePatterns != null); + DatatypeCtor ctor = null; + if (ctors != null) { + if (!ctors.TryGetValue(mc.Id, out ctor)) { + Error(mc.tok, "member {0} does not exist in datatype {1}", mc.Id, dtd.Name); + } else { + Contract.Assert(ctor != null); // follows from postcondition of TryGetValue + mc.Ctor = ctor; + if (ctor.Formals.Count != mc.CasePatterns.Count) { + Error(mc.tok, "member {0} has wrong number of formals (found {1}, expected {2})", mc.Id, mc.Arguments.Count, ctor.Formals.Count); } } - Contract.Assert(memberNamesUsed.Count + s.MissingCases.Count == dtd.Ctors.Count); } - if (!s.IsGhost) { - s.IsGhost = s.Cases.All(cs => cs.Body.All(ss => ss.IsGhost)); + scope.PushMarker(); + List arguments = new List(); + foreach (CasePattern pat in mc.CasePatterns) { + // Find the constructor in the given datatype + // If what was parsed was just an identifier, we will interpret it as a datatype constructor, if possible + ctor = null; + if (pat.Var == null || (pat.Var != null && pat.Var.Type is TypeProxy && dtd != null)) { + if (datatypeCtors[dtd].TryGetValue(pat.Id, out ctor)) { + pat.Ctor = ctor; + pat.Var = null; + } + } + if (pat.Var != null) { + BoundVar v = pat.Var; + arguments.Add(v); + if (!scope.Push(v.Name, v)) { + Error(v, "Duplicate name: {0}", v.Name); + } + } else { + DesugarMatchCasePattern(mc, pat, sourceVar); + patterns.Add(pat); + arguments.Add(sourceVar); + } } + mc.Arguments = arguments; + mc.CasePatterns = null; + scope.PopMarker(); + } - } else if (stmt is SkeletonStatement) { - var s = (SkeletonStatement)stmt; - Error(s.Tok, "skeleton statements are allowed only in refining methods"); - // nevertheless, resolve the underlying statement; hey, why not - if (s.S != null) { - ResolveStatement(s.S, specContextOnly, codeContext); + List newCases = new List(); + + // need to consolidate the cases. + // Convert + // match xs + // case Cons(y, #mc#0) => match #mc#0 + // case Cons((z, zs) => body + // case Cons(y, #mc#0) => match #mc#0 + // case Nil => y + // into + // match xs + // case Cons(y, #mc#0) => match #mc#0 + // case Cons((z, zs) => body + // case Nil => y + bool thingsChanged = false; + Dictionary caseMap = new Dictionary(); + List mcWithWildCard = new List(); + foreach (MatchCaseStmt mc in s.Cases) { + // check each CasePattern to see if it has wildcard. + if (CaseExprHasWildCard(mc)) { + mcWithWildCard.Add(mc); + } else { + thingsChanged |= CombineMatchCaseStmt(mc, newCases, caseMap); + } + } + + foreach (MatchCaseStmt mc in mcWithWildCard) { + // now process with cases with wildcard + thingsChanged |= CombineMatchCaseStmt(mc, newCases, caseMap); + } + + if (thingsChanged) { + s.UpdateCases(newCases); + } + } + + void DesugarMatchCasePattern(MatchCaseStmt mc, CasePattern pat, BoundVar v) { + // convert + // case Cons(y, Cons(z, zs)) => body + // to + // case Cons(y, #mc#) => match #mc# + // case Cons(z, zs) => body + + Expression source = new NameSegment(pat.tok, v.Name, null); + List cases = new List(); + cases.Add(new MatchCaseStmt(pat.tok, pat.Id, pat.Arguments == null ? new List() : pat.Arguments, mc.Body)); + List list = new List(); + // endTok?? + list.Add(new MatchStmt(pat.tok, pat.tok, source, cases, false)); + mc.UpdateBody(list); + } + + bool CombineMatchCaseStmt(MatchCaseStmt mc, List newCases, Dictionary caseMap) { + bool thingsChanged = false; + MatchCaseStmt old_mc; + if (caseMap.TryGetValue(mc.Id, out old_mc)) { + // already has a case with the same ctor, try to consolidate the body. + List oldBody = old_mc.Body; + List body = mc.Body; + if ((oldBody.Count == 1) && (oldBody[0] is MatchStmt) + && (body.Count == 1) && (body[0] is MatchStmt)) { + // both only have on statement and the statement is MatchStmt + MatchStmt old = (MatchStmt) oldBody[0]; + MatchStmt current = (MatchStmt) body[0]; + if (SameMatchCase(old_mc, mc)) { + foreach (MatchCaseStmt c in current.Cases) { + old.Cases.Add(c); + } + thingsChanged = true; + } + } else { + // duplicate cases, do nothing for now. The error will be reported during resolving } } else { - Contract.Assert(false); throw new cce.UnreachableException(); + // it is a new case. + newCases.Add(mc); + caseMap.Add(mc.Id, mc); + } + return thingsChanged; + } + + bool SameMatchCase(MatchCaseStmt one, MatchCaseStmt other) { + // this method is called after all the CasePattern in the match cases are converted + // into BoundVars. + Contract.Assert(one.CasePatterns == null && one.Arguments != null); + Contract.Assert(other.CasePatterns == null && other.Arguments != null); + // In order to combine the two match cases, the bodies need to be a MatchExpr and + // the arguments and the source of the body are the same. + // We do string equals since they should be in the same scope. + if (one.Arguments.Count != other.Arguments.Count) { + return false; + } + List body1 = one.Body; + List body2 = other.Body; + if ((body1.Count != 1) || (body2.Count != 1)) { + return false; + } + if (!(body1[0] is MatchStmt) || !(body2[0] is MatchStmt)) { + return false; + } + var source1 = ((MatchStmt)body1[0]).Source; + var source2 = ((MatchStmt)body2[0]).Source; + if (!(source1 is NameSegment) || !(source2 is NameSegment)) { + return false; } + if (!((NameSegment)source1).Name.Equals(((NameSegment)source2).Name)) { + return false; + } + for (int i = 0; i < one.Arguments.Count; i++) { + BoundVar bv1 = one.Arguments[i]; + BoundVar bv2 = other.Arguments[i]; + if (!LocalVariable.HasWildcardName(bv1) && !LocalVariable.HasWildcardName(bv2) && + !bv1.Name.Equals(bv2.Name)) { + return false; + } + } + return true; } void FillInDefaultLoopDecreases(LoopStmt loopStmt, Expression guard, List theDecreases, ICallable enclosingMethod) { @@ -7324,99 +7599,365 @@ namespace Microsoft.Dafny } } else if (expr is MatchExpr) { - var me = (MatchExpr)expr; - ResolveExpression(me.Source, opts); - Contract.Assert(me.Source.Type != null); // follows from postcondition of ResolveExpression - UserDefinedType sourceType = null; - DatatypeDecl dtd = null; - if (me.Source.Type.IsDatatype) { - sourceType = (UserDefinedType)me.Source.Type.NormalizeExpand(); - dtd = cce.NonNull((DatatypeDecl)sourceType.ResolvedClass); - } - var subst = new Dictionary(); - Dictionary ctors; - if (dtd == null) { - Error(me.Source, "the type of the match source expression must be a datatype (instead found {0})", me.Source.Type); - ctors = null; - } else { - Contract.Assert(sourceType != null); // dtd and sourceType are set together above - ctors = datatypeCtors[dtd]; - Contract.Assert(ctors != null); // dtd should have been inserted into datatypeCtors during a previous resolution stage + ResolveMatchExpr(expr, opts); + } else { + Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression + } - // build the type-parameter substitution map for this use of the datatype - for (int i = 0; i < dtd.TypeArgs.Count; i++) { - subst.Add(dtd.TypeArgs[i], sourceType.TypeArgs[i]); - } + if (expr.Type == null) { + // some resolution error occurred + expr.Type = new InferredTypeProxy(); + } + } + + void ResolveMatchExpr(Expression expr, ResolveOpts opts) { + var me = (MatchExpr)expr; + DesugarMatchExprWithTupleExpression(me); + + ResolveExpression(me.Source, opts); + Contract.Assert(me.Source.Type != null); // follows from postcondition of ResolveExpression + UserDefinedType sourceType = null; + DatatypeDecl dtd = null; + if (me.Source.Type.IsDatatype) { + sourceType = (UserDefinedType)me.Source.Type.NormalizeExpand(); + dtd = cce.NonNull((DatatypeDecl)sourceType.ResolvedClass); + } + var subst = new Dictionary(); + Dictionary ctors; + if (dtd == null) { + Error(me.Source, "the type of the match source expression must be a datatype (instead found {0})", me.Source.Type); + ctors = null; + } else { + Contract.Assert(sourceType != null); // dtd and sourceType are set together above + ctors = datatypeCtors[dtd]; + Contract.Assert(ctors != null); // dtd should have been inserted into datatypeCtors during a previous resolution stage + + // build the type-parameter substitution map for this use of the datatype + for (int i = 0; i < dtd.TypeArgs.Count; i++) { + subst.Add(dtd.TypeArgs[i], sourceType.TypeArgs[i]); } + } - ISet memberNamesUsed = new HashSet(); - expr.Type = new InferredTypeProxy(); - foreach (MatchCaseExpr mc in me.Cases) { - DatatypeCtor ctor = null; - if (ctors != null) { - Contract.Assert(dtd != null); - if (!ctors.TryGetValue(mc.Id, out ctor)) { - Error(mc.tok, "member {0} does not exist in datatype {1}", mc.Id, dtd.Name); + // convert CasePattern in MatchCaseExpr to BoundVar and flatten the MatchCaseExpr. + Type type = new InferredTypeProxy(); + string name = FreshTempVarName("_mc#", opts.codeContext); + BoundVar bv = new BoundVar(me.tok, name, type); + List patternSubst = new List(); + DesugarMatchCaseExpr(me, dtd, bv, patternSubst); + + ISet memberNamesUsed = new HashSet(); + expr.Type = new InferredTypeProxy(); + foreach (MatchCaseExpr mc in me.Cases) { + DatatypeCtor ctor = null; + if (ctors != null) { + Contract.Assert(dtd != null); + if (!ctors.TryGetValue(mc.Id, out ctor)) { + Error(mc.tok, "member {0} does not exist in datatype {1}", mc.Id, dtd.Name); + } else { + Contract.Assert(ctor != null); // follows from postcondition of TryGetValue + mc.Ctor = ctor; + if (ctor.Formals.Count != mc.Arguments.Count) { + Error(mc.tok, "member {0} has wrong number of formals (found {1}, expected {2})", mc.Id, mc.Arguments.Count, ctor.Formals.Count); + } + if (memberNamesUsed.Contains(mc.Id)) { + Error(mc.tok, "member {0} appears in more than one case", mc.Id); } else { - Contract.Assert(ctor != null); // follows from postcondition of TryGetValue - mc.Ctor = ctor; - if (ctor.Formals.Count != mc.Arguments.Count) { - Error(mc.tok, "member {0} has wrong number of formals (found {1}, expected {2})", mc.Id, mc.Arguments.Count, ctor.Formals.Count); - } - if (memberNamesUsed.Contains(mc.Id)) { - Error(mc.tok, "member {0} appears in more than one case", mc.Id); - } else { - memberNamesUsed.Add(mc.Id); // add mc.Id to the set of names used - } + memberNamesUsed.Add(mc.Id); // add mc.Id to the set of names used } } - scope.PushMarker(); - int i = 0; - foreach (BoundVar v in mc.Arguments) { - if (!scope.Push(v.Name, v)) { - Error(v, "Duplicate parameter name: {0}", v.Name); - } - ResolveType(v.tok, v.Type, opts.codeContext, ResolveTypeOptionEnum.InferTypeProxies, null); - if (ctor != null && i < ctor.Formals.Count) { - Formal formal = ctor.Formals[i]; - Type st = SubstType(formal.Type, subst); - if (!UnifyTypes(v.Type, st)) { - Error(expr, "the declared type of the formal ({0}) does not agree with the corresponding type in the constructor's signature ({1})", v.Type, st); - } - v.IsGhost = formal.IsGhost; + } + scope.PushMarker(); + int i = 0; + foreach (BoundVar v in mc.Arguments) { + if (!scope.Push(v.Name, v)) { + Error(v, "Duplicate parameter name: {0}", v.Name); + } + ResolveType(v.tok, v.Type, opts.codeContext, ResolveTypeOptionEnum.InferTypeProxies, null); + if (ctor != null && i < ctor.Formals.Count) { + Formal formal = ctor.Formals[i]; + Type st = SubstType(formal.Type, subst); + if (!UnifyTypes(v.Type, st)) { + Error(expr, "the declared type of the formal ({0}) does not agree with the corresponding type in the constructor's signature ({1})", v.Type, st); } - i++; + v.IsGhost = formal.IsGhost; } + i++; + } + ResolveExpression(mc.Body, opts); + // substitute body to replace the case pat with v. This needs to happen + // after the body is resolved so we can scope the bv correctly. + if (patternSubst.Count > 0) { + MatchCaseExprSubstituteCloner cloner = new MatchCaseExprSubstituteCloner(patternSubst, bv); + mc.UpdateBody(cloner.CloneExpr(mc.Body)); + // resolve it again since we just cloned it. ResolveExpression(mc.Body, opts); - Contract.Assert(mc.Body.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(expr.Type, mc.Body.Type)) { - Error(mc.Body.tok, "type of case bodies do not agree (found {0}, previous types {1})", mc.Body.Type, expr.Type); + } + + Contract.Assert(mc.Body.Type != null); // follows from postcondition of ResolveExpression + if (!UnifyTypes(expr.Type, mc.Body.Type)) { + Error(mc.Body.tok, "type of case bodies do not agree (found {0}, previous types {1})", mc.Body.Type, expr.Type); + } + scope.PopMarker(); + } + if (dtd != null && memberNamesUsed.Count != dtd.Ctors.Count) { + // We could complain about the syntactic omission of constructors: + // Error(expr, "match expression does not cover all constructors"); + // but instead we let the verifier do a semantic check. + // So, for now, record the missing constructors: + foreach (var ctr in dtd.Ctors) { + if (!memberNamesUsed.Contains(ctr.Name)) { + me.MissingCases.Add(ctr); + } + } + Contract.Assert(memberNamesUsed.Count + me.MissingCases.Count == dtd.Ctors.Count); + } + } + + /* + * Convert + * match (x, y) + * case (Zero, _) => Zero + * case (Suc(_), Zero) => x + * case (Suc(a), Suc(b)) => minus(a, b) + * To: + * match x + * case Zero => match y + * case _ => zero + * case Suc(_) => match y + * case Zero => x + * case Suc(a) => match y + * case (b) => minus(a,b) + */ + private void DesugarMatchExprWithTupleExpression(MatchExpr me) { + // (x, y) is treated as a 2-tuple constructor + if (me.Source is DatatypeValue) { + var e = (DatatypeValue)me.Source; + Contract.Assert(e.Arguments.Count >= 1); + Expression source = e.Arguments[0]; + List cases = new List(); + foreach (MatchCaseExpr mc in me.Cases) { + Contract.Assert(mc.CasePatterns != null); + Contract.Assert(mc.CasePatterns.Count == e.Arguments.Count); + CasePattern cp = mc.CasePatterns[0]; + List patterns; + if (cp.Arguments != null) { + patterns = cp.Arguments; + } else { + patterns = new List(); + } + + Expression body = mc.Body; + for (int i = e.Arguments.Count; 1 <= --i; ) { + // others go into the body + body = CreateMatchCaseExprBody(mc.tok, e.Arguments[i], mc.CasePatterns[i], body); + } + cases.Add(new MatchCaseExpr(cp.tok, cp.Id, patterns, body)); + } + me.UpdateSource(source); + me.UpdateCases(cases); + } + } + + Expression CreateMatchCaseExprBody(Boogie.IToken tok, Expression source, CasePattern cp, Expression body) { + List cases = new List(); + List patterns; + if (cp.Var != null) { + var bv = cp.Var; + if (LocalVariable.HasWildcardName(bv)) { + return body; + } else { + patterns = new List(); + } + } else { + patterns = cp.Arguments; + } + cases.Add(new MatchCaseExpr(cp.tok, cp.Id, patterns, body)); + return new MatchExpr(tok, source, cases, false); + } + + /* + * Convert + * match xs + * case Cons(y, Cons(z, zs)) => last(Cons(z, zs)) + * case Cons(y, Nil) => y + * To + * match xs + * case Cons(y, ys) => match ys + * case Nil => y + * case Cons(z, zs) => last(ys) + * */ + void DesugarMatchCaseExpr(MatchExpr me, DatatypeDecl dtd, BoundVar sourceVar, List patterns) { + Contract.Assert(dtd != null); + Dictionary ctors = datatypeCtors[dtd]; + foreach (MatchCaseExpr mc in me.Cases) { + if (mc.Arguments != null) { + // already desugared. This happens during the second pass resolver after cloning. + Contract.Assert(mc.CasePatterns == null); + return; + } + + Contract.Assert(mc.Arguments == null); + Contract.Assert(mc.CasePatterns != null); + DatatypeCtor ctor = null; + if (ctors != null) { + if (!ctors.TryGetValue(mc.Id, out ctor)) { + Error(mc.tok, "member {0} does not exist in datatype {1}", mc.Id, dtd.Name); + } else { + Contract.Assert(ctor != null); // follows from postcondition of TryGetValue + mc.Ctor = ctor; + if (ctor.Formals.Count != mc.CasePatterns.Count) { + Error(mc.tok, "member {0} has wrong number of formals (found {1}, expected {2})", mc.Id, mc.CasePatterns.Count, ctor.Formals.Count); + } } - scope.PopMarker(); } - if (dtd != null && memberNamesUsed.Count != dtd.Ctors.Count) { - // We could complain about the syntactic omission of constructors: - // Error(expr, "match expression does not cover all constructors"); - // but instead we let the verifier do a semantic check. - // So, for now, record the missing constructors: - foreach (var ctr in dtd.Ctors) { - if (!memberNamesUsed.Contains(ctr.Name)) { - me.MissingCases.Add(ctr); + scope.PushMarker(); + List arguments = new List(); + foreach (CasePattern pat in mc.CasePatterns) { + // Find the constructor in the given datatype + // If what was parsed was just an identifier, we will interpret it as a datatype constructor, if possible + ctor = null; + if (pat.Var == null || (pat.Var != null && pat.Var.Type is TypeProxy && dtd != null)) { + if (datatypeCtors[dtd].TryGetValue(pat.Id, out ctor)) { + pat.Ctor = ctor; + pat.Var = null; + } + } + if (pat.Var != null) { + BoundVar v = pat.Var; + arguments.Add(v); + if (!scope.Push(v.Name, v)) { + Error(v, "Duplicate name: {0}", v.Name); } + } else { + DesugarMatchCasePattern(mc, pat, sourceVar); + patterns.Add(pat); + arguments.Add(sourceVar); } - Contract.Assert(memberNamesUsed.Count + me.MissingCases.Count == dtd.Ctors.Count); } - } else { - Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression + mc.Arguments = arguments; + mc.CasePatterns = null; + scope.PopMarker(); } - if (expr.Type == null) { - // some resolution error occurred - expr.Type = new InferredTypeProxy(); + List newCases = new List(); + + // need to consolidate the cases. + // Convert + // match xs + // case Cons(y, #mc#0) => match #mc#0 + // case Cons((z, zs) => body + // case Cons(y, #mc#0) => match #mc#0 + // case Nil => y + // into + // match xs + // case Cons(y, #mc#0) => match #mc#0 + // case Cons((z, zs) => body + // case Nil => y + bool thingsChanged = false; + Dictionary caseMap = new Dictionary(); + List mcWithWildCard = new List(); + foreach (MatchCaseExpr mc in me.Cases) { + // check each CasePattern to see if it has wildcard. + if (CaseExprHasWildCard(mc)) { + mcWithWildCard.Add(mc); + } else { + thingsChanged |= CombineMatchCaseExpr(mc, newCases, caseMap); + } + } + + foreach (MatchCaseExpr mc in mcWithWildCard) { + // now process with cases with wildcard + thingsChanged |= CombineMatchCaseExpr(mc, newCases, caseMap); + } + + if (thingsChanged) { + me.UpdateCases(newCases); } } + void DesugarMatchCasePattern(MatchCaseExpr mc, CasePattern pat, BoundVar v) { + // convert + // case Cons(y, Cons(z, zs)) => body + // to + // case Cons(y, #mc#) => match #mc# + // case Cons(z, zs) => body + + Expression source = new NameSegment(pat.tok, v.Name, null); + List cases = new List(); + cases.Add(new MatchCaseExpr(pat.tok, pat.Id, pat.Arguments == null ? new List() : pat.Arguments, mc.Body)); + MatchExpr e = new MatchExpr(pat.tok, source, cases, false); + mc.UpdateBody(e); + } + + + bool CaseExprHasWildCard(MatchCase mc) { + foreach (BoundVar bv in mc.Arguments) { + if (LocalVariable.HasWildcardName(bv)) { + return true; + } + } + return false; + } + + bool CombineMatchCaseExpr(MatchCaseExpr mc, List newCases, Dictionary caseMap) { + bool thingsChanged = false; + MatchCaseExpr old_mc; + if (caseMap.TryGetValue(mc.Id, out old_mc)) { + // already has a case with the same ctor, try to consolidate the body. + Expression oldBody = old_mc.Body; + Expression body = mc.Body; + if (SameMatchCase(old_mc, mc)) { + MatchExpr old = (MatchExpr)oldBody; + MatchExpr current = (MatchExpr)body; + foreach (MatchCaseExpr c in current.Cases) { + old.Cases.Add(c); + } + thingsChanged = true; + } else { + // duplicate cases, do nothing for now. The error will be reported during resolving + } + } else { + // it is a new case. + newCases.Add(mc); + caseMap.Add(mc.Id, mc); + } + return thingsChanged; + } + + bool SameMatchCase(MatchCaseExpr one, MatchCaseExpr other) { + // this method is called after all the CasePattern in the match cases are converted + // into BoundVars. + Contract.Assert(one.CasePatterns == null && one.Arguments != null); + Contract.Assert(other.CasePatterns == null && other.Arguments != null); + // In order to combine the two match cases, the bodies need to be a MatchExpr and + // the arguments and the source of the body are the same. + // We do string equals since they should be in the same scope. + if (one.Arguments.Count != other.Arguments.Count) { + return false; + } + if (!(one.Body is MatchExpr) || !(other.Body is MatchExpr)) { + return false; + } + var source1 = ((MatchExpr)one.Body).Source; + var source2 = ((MatchExpr)other.Body).Source; + if (!(source1 is NameSegment) || !(source2 is NameSegment)) { + return false; + } + if (!((NameSegment)source1).Name.Equals(((NameSegment)source2).Name)) { + return false; + } + for (int i = 0; i < one.Arguments.Count; i++) { + BoundVar bv1 = one.Arguments[i]; + BoundVar bv2 = other.Arguments[i]; + if (!LocalVariable.HasWildcardName(bv1) && !LocalVariable.HasWildcardName(bv2) && + !bv1.Name.Equals(bv2.Name)) { + return false; + } + } + return true; + } + void ResolveCasePattern(CasePattern pat, Type sourceType, ICodeContext context) { Contract.Requires(pat != null); Contract.Requires(sourceType != null); @@ -7463,13 +8004,15 @@ namespace Microsoft.Dafny } // recursively call ResolveCasePattern on each of the arguments var j = 0; - foreach (var arg in pat.Arguments) { - if (j < ctor.Formals.Count) { - var formal = ctor.Formals[j]; - Type st = SubstType(formal.Type, subst); - ResolveCasePattern(arg, st, context); + if (pat.Arguments != null) { + foreach (var arg in pat.Arguments) { + if (j < ctor.Formals.Count) { + var formal = ctor.Formals[j]; + Type st = SubstType(formal.Type, subst); + ResolveCasePattern(arg, st, context); + } + j++; } - j++; } if (j == ctor.Formals.Count) { pat.AssembleExpr(udt.TypeArgs); @@ -9009,8 +9552,10 @@ namespace Microsoft.Dafny var s = FreeVariables(e.Source); foreach (MatchCaseExpr mc in e.Cases) { var t = FreeVariables(mc.Body); - foreach (var bv in mc.Arguments) { - t.Remove(bv); + foreach (var cp in mc.CasePatterns) { + foreach (var bv in cp.Vars) { + t.Remove(bv); + } } s.UnionWith(t); } diff --git a/Source/Dafny/Rewriter.cs b/Source/Dafny/Rewriter.cs index 72649b5f..1361ad85 100644 --- a/Source/Dafny/Rewriter.cs +++ b/Source/Dafny/Rewriter.cs @@ -1118,6 +1118,58 @@ namespace Microsoft.Dafny } } + + + class MatchCaseExprSubstituteCloner : Cloner + { + private List patternSubst; + private BoundVar var; + + // the cloner is called after resolving the body of matchexpr, trying + // to replace casepattern in the body that has been replaced by bv + public MatchCaseExprSubstituteCloner(List subst, BoundVar var) { + this.patternSubst = subst; + this.var = var; + } + + public override Expression CloneApplySuffix(ApplySuffix e) { + // if the ApplySuffix matches the CasePattern, then replace it with the BoundVar. + if (FindMatchingPattern(e)) { + return new NameSegment(e.tok, this.var.Name, null); + } else { + return new ApplySuffix(Tok(e.tok), CloneExpr(e.Lhs), e.Args.ConvertAll(CloneExpr)); + } + } + + private bool FindMatchingPattern(ApplySuffix e) { + Expression lhs = e.Lhs; + if (!(lhs is NameSegment)) { + return false; + } + string applyName = ((NameSegment)lhs).Name; + foreach (CasePattern cp in patternSubst) { + string ctorName = cp.Id; + if (!(applyName.Equals(ctorName)) || (e.Args.Count != cp.Arguments.Count)) { + continue; + } + bool found = true; + for (int i = 0; i < e.Args.Count; i++) { + var arg1 = e.Args[i]; + var arg2 = cp.Arguments[i]; + if (arg1.Resolved is IdentifierExpr) { + var bv1 = ((IdentifierExpr)arg1.Resolved).Var; + if (bv1 != arg2.Var) { + found = false; + } + } + } + if (found) { + return true; + } + } + return false; + } + } } diff --git a/Test/dafny0/NestedMatch.dfy b/Test/dafny0/NestedMatch.dfy new file mode 100644 index 00000000..e6e7c489 --- /dev/null +++ b/Test/dafny0/NestedMatch.dfy @@ -0,0 +1,59 @@ +// RUN: %dafny /compile:0 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +datatype Nat = Zero | Suc(Nat) + +predicate Even(n: Nat) +{ + match n + case Zero => true + case Suc(Zero) => false + case Suc(Suc(p)) => Even(p) +} + + +method checkEven(n: Nat) { + assert Even(Zero) == true; + assert Even(Suc(Zero)) == false; + assert Even(Suc(Suc(n))) == Even(n); +} + +datatype List = Nil | Cons(T, List) + +function last(xs: List): T + requires xs != Nil +{ + match xs + case Cons(y, Nil) => y + case Cons(y, Cons(z, zs)) => last(Cons(z, zs)) +} + +method checkLast(y: T) { + assert last(Cons(y, Nil)) == y; + assert last(Cons(y, Cons(y, Nil))) == last(Cons(y, Nil)); +} + + +function minus(x: Nat, y: Nat): Nat +{ + match (x, y) + case (Zero, _) => Zero + case (Suc(_), Zero) => x + case (Suc(a), Suc(b)) => minus(a, b) +} + +method checkMinus(x:Nat, y: Nat) { + assert minus(Suc(x), Suc(y)) == minus(x,y); +} + + +// nested match statement +method Last(xs: List) returns (x: T) + requires xs != Nil +{ + + match xs { + case Cons(y, Nil) => x:= y; + case Cons(y, Cons(z, zs)) => x:=Last(Cons(z, zs)); + } +} diff --git a/Test/dafny0/NestedMatch.dfy.expect b/Test/dafny0/NestedMatch.dfy.expect new file mode 100644 index 00000000..f3a9c95f --- /dev/null +++ b/Test/dafny0/NestedMatch.dfy.expect @@ -0,0 +1,2 @@ + +Dafny program verifier finished with 11 verified, 0 errors -- cgit v1.2.3 From be103552b0a81d48afcc7883ca48d9b5194e63f8 Mon Sep 17 00:00:00 2001 From: qunyanm Date: Fri, 15 May 2015 11:29:44 -0700 Subject: Fix issue #79. Allow tuple pattern matching with parenthesis only. --- Source/Dafny/Dafny.atg | 13 ++++++++++++- Source/Dafny/Parser.cs | 21 ++++++++++++++++++++- Test/dafny4/Bug79.dfy | 10 ++++++++++ Test/dafny4/Bug79.dfy.expect | 2 ++ 4 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 Test/dafny4/Bug79.dfy create mode 100644 Test/dafny4/Bug79.dfy.expect diff --git a/Source/Dafny/Dafny.atg b/Source/Dafny/Dafny.atg index 3f684ff6..16cc09eb 100644 --- a/Source/Dafny/Dafny.atg +++ b/Source/Dafny/Dafny.atg @@ -2625,7 +2625,18 @@ CasePattern } ] ")" (. pat = new CasePattern(id, id.val, arguments); .) - + | "(" (. id = t; + arguments = new List(); + .) + [ 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); + .) | IdentTypeOptional (. // This could be a BoundVar of a parameter-less constructor and we may not know until resolution. // Nevertheless, we do put the "bv" into the CasePattern here (even though it will get thrown out // later if resolution finds the CasePattern to denote a parameter-less constructor), because this diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs index d0924169..162e23a3 100644 --- a/Source/Dafny/Parser.cs +++ b/Source/Dafny/Parser.cs @@ -2934,7 +2934,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Ident(out id); Expect(48); arguments = new List(); - if (la.kind == 1) { + if (la.kind == 1 || la.kind == 48) { CasePattern(out pat); arguments.Add(pat); while (la.kind == 21) { @@ -2945,6 +2945,25 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } Expect(49); pat = new CasePattern(id, id.val, arguments); + } else if (la.kind == 48) { + Get(); + id = t; + arguments = new List(); + + if (la.kind == 1 || la.kind == 48) { + CasePattern(out pat); + arguments.Add(pat); + while (la.kind == 21) { + Get(); + CasePattern(out pat); + arguments.Add(pat); + } + } + Expect(49); + 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); diff --git a/Test/dafny4/Bug79.dfy b/Test/dafny4/Bug79.dfy new file mode 100644 index 00000000..49f2421b --- /dev/null +++ b/Test/dafny4/Bug79.dfy @@ -0,0 +1,10 @@ +// RUN: %dafny /compile:0 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +function foo(s:int) : (int, int) + +function bar(s:int) : bool +{ + var (x, rest) := foo(s); + x > 0 +} \ No newline at end of file diff --git a/Test/dafny4/Bug79.dfy.expect b/Test/dafny4/Bug79.dfy.expect new file mode 100644 index 00000000..069e7767 --- /dev/null +++ b/Test/dafny4/Bug79.dfy.expect @@ -0,0 +1,2 @@ + +Dafny program verifier finished with 2 verified, 0 errors -- cgit v1.2.3 From ff0d676f5202ebecdd25a5dcdbbcd2480860857d Mon Sep 17 00:00:00 2001 From: wuestholz Date: Sun, 17 May 2015 13:08:38 +0200 Subject: Updated test output after change in Boogie. --- Test/dafny0/snapshots/runtest.snapshot.expect | 45 ++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/Test/dafny0/snapshots/runtest.snapshot.expect b/Test/dafny0/snapshots/runtest.snapshot.expect index 8ad86f3b..f1050f62 100644 --- a/Test/dafny0/snapshots/runtest.snapshot.expect +++ b/Test/dafny0/snapshots/runtest.snapshot.expect @@ -6,12 +6,17 @@ Processing command (at Snapshots0.v0.dfy(4,10)) assert Lit(false); >>> DoNothingToAssert Dafny program verifier finished with 3 verified, 0 errors +Processing implementation CheckWellformed$$_module.__default.bar (at Snapshots0.v1.dfy(7,8)): + >>> added axiom: ##extracted_function##1() == (0 == $ModuleContextHeight && 0 == $FunctionContextHeight) + >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && ##extracted_function##1(); Processing call to procedure IntraModuleCall$$_module.__default.bar in implementation Impl$$_module.__default.foo (at Snapshots0.v1.dfy(3,6)): - >>> added axiom: (forall call0old#AT#$Heap: Heap, $Heap: Heap :: {:weight 30} { ##extracted_function##1(call0old#AT#$Heap, $Heap) } ##extracted_function##1(call0old#AT#$Heap, $Heap) == (true && Lit(false) && (forall $o: ref, $f: Field alpha :: { read($Heap, $o, $f) } $o != null && read(call0old#AT#$Heap, $o, alloc) ==> read($Heap, $o, $f) == read(call0old#AT#$Heap, $o, $f)) && $HeapSucc(call0old#AT#$Heap, $Heap))) - >>> added after: a##post##0 := a##post##0 && ##extracted_function##1(call0old#AT#$Heap, $Heap); + >>> added axiom: (forall call0old#AT#$Heap: Heap, $Heap: Heap :: {:weight 30} { ##extracted_function##2(call0old#AT#$Heap, $Heap) } ##extracted_function##2(call0old#AT#$Heap, $Heap) == (true && Lit(false) && (forall $o: ref, $f: Field alpha :: { read($Heap, $o, $f) } $o != null && read(call0old#AT#$Heap, $o, alloc) ==> read($Heap, $o, $f) == read(call0old#AT#$Heap, $o, $f)) && $HeapSucc(call0old#AT#$Heap, $Heap))) + >>> added after: a##cached##0 := a##cached##0 && ##extracted_function##2(call0old#AT#$Heap, $Heap); +Processing command (at ) a##cached##0 := a##cached##0 && ##extracted_function##1(); + >>> AssumeNegationOfAssumptionVariable Processing command (at Snapshots0.v1.dfy(3,6)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); >>> MarkAsFullyVerified -Processing command (at ) a##post##0 := a##post##0 && ##extracted_function##1(call0old#AT#$Heap, $Heap); +Processing command (at ) a##cached##0 := a##cached##0 && ##extracted_function##2(call0old#AT#$Heap, $Heap); >>> AssumeNegationOfAssumptionVariable Processing command (at Snapshots0.v1.dfy(4,10)) assert Lit(false); >>> MarkAsPartiallyVerified @@ -31,7 +36,7 @@ Processing command (at Snapshots1.v0.dfy(12,3)) assert true; Dafny program verifier finished with 4 verified, 0 errors Processing call to procedure IntraModuleCall$$_module.__default.N in implementation Impl$$_module.__default.M (at Snapshots1.v1.dfy(3,4)): - >>> added after: a##post##0 := a##post##0 && false; + >>> added after: a##cached##0 := a##cached##0 && false; Processing command (at Snapshots1.v1.dfy(12,3)) assert true; >>> MarkAsFullyVerified Processing command (at Snapshots1.v1.dfy(3,4)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); @@ -62,7 +67,11 @@ Processing command (at Snapshots2.v0.dfy(18,3)) assert true; Dafny program verifier finished with 6 verified, 0 errors Processing call to procedure IntraModuleCall$$_module.__default.N in implementation Impl$$_module.__default.M (at Snapshots2.v1.dfy(3,4)): - >>> added after: a##post##0 := a##post##0 && false; + >>> added after: a##cached##0 := a##cached##0 && false; +Processing implementation CheckWellformed$$_module.__default.P (at Snapshots2.v1.dfy(10,11)): + >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && false; +Processing implementation CheckWellformed$$_module.__default.Q (at Snapshots2.v1.dfy(13,11)): + >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && false; Processing command (at Snapshots2.v1.dfy(18,3)) assert true; >>> MarkAsFullyVerified Processing command (at Snapshots2.v1.dfy(3,4)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); @@ -73,11 +82,11 @@ Snapshots2.v1.dfy(4,10): Error: assertion violation Execution trace: (0,0): anon0 Processing command (at Snapshots2.v1.dfy(11,11)) assert true; - >>> MarkAsFullyVerified + >>> DoNothingToAssert Processing command (at Snapshots2.v1.dfy(11,15)) assert _module.__default.P($LS($LS($LZ)), $Heap) <==> _module.__default.Q($LS($LS($LZ)), $Heap); >>> DoNothingToAssert Processing command (at Snapshots2.v1.dfy(14,11)) assert true; - >>> MarkAsFullyVerified + >>> DoNothingToAssert Processing command (at Snapshots2.v1.dfy(14,15)) assert _module.__default.Q($LS($LS($LZ)), $Heap) <==> Lit(_module.__default.R($Heap)); >>> DoNothingToAssert @@ -171,8 +180,28 @@ Processing command (at Snapshots7.v0.dfy(19,14)) assert Lit(false); >>> DoNothingToAssert Dafny program verifier finished with 4 verified, 0 errors +Processing implementation CheckWellformed$$_0_M0.C.Foo (at Snapshots7.v1.dfy(5,12)): + >>> added axiom: ##extracted_function##1() == (0 == $ModuleContextHeight && 0 == $FunctionContextHeight) + >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && ##extracted_function##1(); +Processing implementation Impl$$_0_M0.C.Foo (at Snapshots7.v1.dfy(5,12)): + >>> added axiom: ##extracted_function##2() == (0 == $ModuleContextHeight && 0 == $FunctionContextHeight && Lit(false)) + >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && ##extracted_function##2(); +Processing implementation CheckWellformed$$_1_M1.C.Foo (at Snapshots7.v1.dfy[M1](5,12)): + >>> added axiom: ##extracted_function##3() == (1 == $ModuleContextHeight && 0 == $FunctionContextHeight) + >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && ##extracted_function##3(); +Processing implementation Impl$$_1_M1.C.Foo (at Snapshots7.v1.dfy[M1](5,12)): + >>> added axiom: ##extracted_function##4() == (1 == $ModuleContextHeight && 0 == $FunctionContextHeight && Lit(false)) + >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && ##extracted_function##4(); +Processing command (at ) a##cached##0 := a##cached##0 && ##extracted_function##1(); + >>> AssumeNegationOfAssumptionVariable +Processing command (at ) a##cached##0 := a##cached##0 && ##extracted_function##2(); + >>> AssumeNegationOfAssumptionVariable +Processing command (at ) a##cached##0 := a##cached##0 && ##extracted_function##3(); + >>> AssumeNegationOfAssumptionVariable +Processing command (at ) a##cached##0 := a##cached##0 && ##extracted_function##4(); + >>> AssumeNegationOfAssumptionVariable Processing command (at Snapshots7.v1.dfy(19,14)) assert Lit(false); - >>> DoNothingToAssert + >>> MarkAsPartiallyVerified Snapshots7.v1.dfy(19,14): Error: assertion violation Execution trace: (0,0): anon0 -- cgit v1.2.3 From 47e3c9e215f1c5f51d35a974fccb5bd612eaa8be Mon Sep 17 00:00:00 2001 From: wuestholz Date: Mon, 18 May 2015 23:41:08 +0200 Subject: DafnyExtension: Added experimental support for diagnosing timeouts. --- Source/DafnyExtension/DafnyDriver.cs | 5 ++++ Source/DafnyExtension/MenuProxy.cs | 17 ++++++++++++ Source/DafnyExtension/ProgressMargin.cs | 46 ++++++++++++++++++++++++--------- Source/DafnyMenu/DafnyMenu.vsct | 11 ++++++++ Source/DafnyMenu/DafnyMenuPackage.cs | 32 +++++++++++++++++++++++ Source/DafnyMenu/PkgCmdID.cs | 1 + 6 files changed, 100 insertions(+), 12 deletions(-) diff --git a/Source/DafnyExtension/DafnyDriver.cs b/Source/DafnyExtension/DafnyDriver.cs index 5b8cc943..7f39fe34 100644 --- a/Source/DafnyExtension/DafnyDriver.cs +++ b/Source/DafnyExtension/DafnyDriver.cs @@ -239,6 +239,11 @@ namespace DafnyLanguage return Dafny.DafnyOptions.Clo.VerifySnapshots; } + public static void SetDiagnoseTimeouts(bool v) + { + Dafny.DafnyOptions.Clo.RunDiagnosticsOnTimeout = v; + } + public static int ChangeIncrementalVerification(int mode) { var old = Dafny.DafnyOptions.Clo.VerifySnapshots; diff --git a/Source/DafnyExtension/MenuProxy.cs b/Source/DafnyExtension/MenuProxy.cs index 11e1287f..9ddc8344 100644 --- a/Source/DafnyExtension/MenuProxy.cs +++ b/Source/DafnyExtension/MenuProxy.cs @@ -67,6 +67,15 @@ namespace DafnyLanguage } } + public void DiagnoseTimeouts(IWpfTextView activeTextView) + { + DafnyLanguage.ProgressTagger tagger; + if (activeTextView != null && DafnyLanguage.ProgressTagger.ProgressTaggers.TryGetValue(activeTextView.TextBuffer, out tagger)) + { + tagger.StartVerification(false, true); + } + } + public bool MenuEnabled(IWpfTextView activeTextView) { return activeTextView != null && activeTextView.TextBuffer.ContentType.DisplayName == "dafny"; @@ -80,6 +89,14 @@ namespace DafnyLanguage && resolver.Program != null; } + public bool DiagnoseTimeoutsCommandEnabled(IWpfTextView activeTextView) + { + ResolverTagger resolver; + return activeTextView != null + && DafnyLanguage.ResolverTagger.ResolverTaggers.TryGetValue(activeTextView.TextBuffer, out resolver) + && resolver.VerificationErrors.Any(err => err.Message.Contains("timed out")); + } + public void Compile(IWpfTextView activeTextView) { ResolverTagger resolver; diff --git a/Source/DafnyExtension/ProgressMargin.cs b/Source/DafnyExtension/ProgressMargin.cs index b4e58d3d..c3f56259 100644 --- a/Source/DafnyExtension/ProgressMargin.cs +++ b/Source/DafnyExtension/ProgressMargin.cs @@ -197,6 +197,7 @@ namespace DafnyLanguage bool verificationInProgress; // this field is protected by "this". Invariant: !verificationInProgress ==> bufferChangesPreVerificationStart.Count == 0 System.Threading.Tasks.Task verificationTask; public bool VerificationDisabled { get; private set; } + bool isDiagnosingTimeouts; string lastRequestId; public static readonly IDictionary ProgressTaggers = new ConcurrentDictionary(); @@ -227,14 +228,21 @@ namespace DafnyLanguage if (prog == null || VerificationDisabled) return; // We have a successfully resolved program to verify - var resolvedVersion = snap.Version.VersionNumber; - if (bufferChangesPostVerificationStart.Count == 0) { - // Nothing new to verify. No reason to start a new verification. - return; - } else if (!bufferChangesPostVerificationStart.TrueForAll(span => span.Snapshot.Version.VersionNumber <= resolvedVersion)) { - // There have been buffer changes since the program that was resolved. Do nothing here, - // and instead just await the next resolved program. - return; + var dt = isDiagnosingTimeouts; + if (!dt) + { + var resolvedVersion = snap.Version.VersionNumber; + if (bufferChangesPostVerificationStart.Count == 0) + { + // Nothing new to verify. No reason to start a new verification. + return; + } + else if (!bufferChangesPostVerificationStart.TrueForAll(span => span.Snapshot.Version.VersionNumber <= resolvedVersion)) + { + // There have been buffer changes since the program that was resolved. Do nothing here, + // and instead just await the next resolved program. + return; + } } // at this time, we're committed to running the verifier @@ -254,10 +262,14 @@ namespace DafnyLanguage } verificationTask = System.Threading.Tasks.Task.Factory.StartNew( - () => RunVerifier(prog, snap, lastRequestId, resolver), + () => RunVerifier(prog, snap, lastRequestId, resolver, dt), TaskCreationOptions.LongRunning); verificationInProgress = true; + if (dt) + { + isDiagnosingTimeouts = false; + } // Change orange progress markers into yellow ones Contract.Assert(bufferChangesPreVerificationStart.Count == 0); // follows from monitor invariant @@ -293,7 +305,7 @@ namespace DafnyLanguage } } - public void StartVerification() + public void StartVerification(bool clearCache = true, bool diagnoseTimeouts = false) { lock (this) { @@ -301,7 +313,11 @@ namespace DafnyLanguage bufferChangesPostVerificationStart.Clear(); bufferChangesPostVerificationStart.Add(new SnapshotSpan(_buffer.CurrentSnapshot, 0, _buffer.CurrentSnapshot.Length)); VerificationDisabled = false; - ClearCachedVerificationResults(); + isDiagnosingTimeouts = diagnoseTimeouts; + if (clearCache) + { + ClearCachedVerificationResults(); + } NotifyAboutChangedTags(_buffer.CurrentSnapshot); } } @@ -314,7 +330,7 @@ namespace DafnyLanguage } } - void RunVerifier(Dafny.Program program, ITextSnapshot snapshot, string requestId, ResolverTagger errorListHolder) { + void RunVerifier(Dafny.Program program, ITextSnapshot snapshot, string requestId, ResolverTagger errorListHolder, bool diagnoseTimeouts) { Contract.Requires(program != null); Contract.Requires(snapshot != null); Contract.Requires(requestId != null); @@ -332,6 +348,8 @@ namespace DafnyLanguage _version++; } + DafnyDriver.SetDiagnoseTimeouts(diagnoseTimeouts); + try { bool success = DafnyDriver.Verify(program, errorListHolder, GetHashCode().ToString(), requestId, errorInfo => @@ -369,6 +387,10 @@ namespace DafnyLanguage { errorListHolder.AddError(new DafnyError("$$program$$", 0, 0, ErrorCategory.InternalError, "Verification process error: " + e.Message, snapshot, false), "$$program$$", requestId); } + finally + { + DafnyDriver.SetDiagnoseTimeouts(!diagnoseTimeouts); + } lock (this) { bufferChangesPreVerificationStart.Clear(); diff --git a/Source/DafnyMenu/DafnyMenu.vsct b/Source/DafnyMenu/DafnyMenu.vsct index 4c4b1403..4c9a4913 100644 --- a/Source/DafnyMenu/DafnyMenu.vsct +++ b/Source/DafnyMenu/DafnyMenu.vsct @@ -122,6 +122,16 @@ + + @@ -154,6 +164,7 @@ + thing association and returns "true", if name has not already been pushed since the last marker. - // If name already has been pushed since the last marker, does nothing and returns "false". - public bool Push(string name, Thing thing) { + public enum PushResult { Duplicate, Shadow, Success } + + /// + /// Pushes name-->thing association and returns "Success", if name has not already been pushed since the last marker. + /// If name already has been pushed since the last marker, does nothing and returns "Duplicate". + /// If the appropriate command-line option is supplied, then this method will also check if "name" shadows a previous + /// name; if it does, then it will return "Shadow" instead of "Success". + /// + public PushResult Push(string name, Thing thing) { Contract.Requires(name != null); Contract.Requires(thing != null); if (Find(name, true) != null) { - return false; + return PushResult.Duplicate; } else { + var r = PushResult.Success; + if (DafnyOptions.O.WarnShadowing && Find(name, false) != null) { + r = PushResult.Shadow; + } names.Add(name); things.Add(thing); - return true; + return r; } } diff --git a/Test/dafny0/Shadows.dfy b/Test/dafny0/Shadows.dfy new file mode 100644 index 00000000..da1e74d6 --- /dev/null +++ b/Test/dafny0/Shadows.dfy @@ -0,0 +1,42 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /warnShadowing "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +module Module0 { + class C { + method M(x: beta) // error: duplicate type parameter + method P(x: alpha) // shadowed type parameter + function F(x: beta): int // error: duplicate type parameter + function G(x: alpha): int // shadowed type parameter + + method Q0(x: int) returns (x: int) // error: duplicate variable name + } +} +module Module1 { + class D { + method Q1(x: int) returns (y: int) + { + var x; // shadowed + var y; // error: duplicate + } + + var f: int + method R() + { + var f; // okay + var f; // error: duplicate + } + method S() + { + var x; + { + var x; // shadow + } + } + method T() + { + var x; + ghost var b := forall x :: x < 10; // shadow + ghost var c := forall y :: forall y :: y != y + 1; // shadow + } + } +} diff --git a/Test/dafny0/Shadows.dfy.expect b/Test/dafny0/Shadows.dfy.expect new file mode 100644 index 00000000..5083ac64 --- /dev/null +++ b/Test/dafny0/Shadows.dfy.expect @@ -0,0 +1,12 @@ +Shadows.dfy(6,19): Error: Duplicate type-parameter name: beta +Shadows.dfy(7,13): Warning: Shadowed type-parameter name: alpha +Shadows.dfy(8,21): Error: Duplicate type-parameter name: beta +Shadows.dfy(9,15): Warning: Shadowed type-parameter name: alpha +Shadows.dfy(11,31): Error: Duplicate parameter name: x +Shadows.dfy(18,10): Warning: Shadowed local-variable name: x +Shadows.dfy(19,10): Error: Duplicate local-variable name: y +Shadows.dfy(26,10): Error: Duplicate local-variable name: f +Shadows.dfy(32,12): Warning: Shadowed local-variable name: x +Shadows.dfy(38,28): Warning: Shadowed bound-variable name: x +Shadows.dfy(39,40): Warning: Shadowed bound-variable name: y +5 resolution/type errors detected in Shadows.dfy -- cgit v1.2.3 From 732ae479be4409214aea62a1f67df0e6a45c2ab4 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Mon, 6 Jul 2015 10:06:45 -0700 Subject: Small refactoring of Printer.cs --- Source/Dafny/Cloner.cs | 2 +- Source/Dafny/DafnyAst.cs | 24 +++++++++++++++++---- Source/Dafny/Printer.cs | 52 ++++++++++++++++++++-------------------------- Source/Dafny/Resolver.cs | 14 ++++++------- Source/Dafny/Translator.cs | 2 +- 5 files changed, 52 insertions(+), 42 deletions(-) diff --git a/Source/Dafny/Cloner.cs b/Source/Dafny/Cloner.cs index f13aa2f4..be77f6b4 100644 --- a/Source/Dafny/Cloner.cs +++ b/Source/Dafny/Cloner.cs @@ -243,7 +243,7 @@ namespace Microsoft.Dafny var e = (LiteralExpr)expr; if (e is StaticReceiverExpr) { var ee = (StaticReceiverExpr)e; - return new StaticReceiverExpr(e.tok, CloneType(ee.UnresolvedType)); + return new StaticReceiverExpr(e.tok, CloneType(ee.UnresolvedType), ee.IsImplicit); } else if (e.Value == null) { return new LiteralExpr(Tok(e.tok)); } else if (e.Value is bool) { diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index b8660f98..eac64f80 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -5015,6 +5015,10 @@ namespace Microsoft.Dafny { get { yield break; } } + public virtual bool IsImplicit { + get { return false; } + } + public static IEnumerable Conjuncts(Expression expr) { Contract.Requires(expr != null); Contract.Requires(expr.Type.IsBoolType); @@ -5391,19 +5395,21 @@ namespace Microsoft.Dafny { public class StaticReceiverExpr : LiteralExpr { public readonly Type UnresolvedType; + private bool Implicit; - public StaticReceiverExpr(IToken tok, Type t) + public StaticReceiverExpr(IToken tok, Type t, bool isImplicit) : base(tok) { Contract.Requires(tok != null); Contract.Requires(t != null); UnresolvedType = t; + Implicit = isImplicit; } /// /// Constructs a resolved LiteralExpr representing the 'null' literal whose type is "cl" /// parameterized by the type arguments of "cl" itself. /// - public StaticReceiverExpr(IToken tok, ClassDecl cl) + public StaticReceiverExpr(IToken tok, ClassDecl cl, bool isImplicit) : base(tok) { Contract.Requires(tok != null); @@ -5411,6 +5417,7 @@ namespace Microsoft.Dafny { var typeArgs = cl.TypeArgs.ConvertAll(tp => (Type)new UserDefinedType(tp)); Type = new UserDefinedType(tok, cl.Name, cl, typeArgs); UnresolvedType = Type; + Implicit = isImplicit; } /// @@ -5427,7 +5434,7 @@ namespace Microsoft.Dafny { /// a trait that in turn extends trait "W(g(Y))". If "t" denotes type "C(G)" and "cl" denotes "W", /// then type of the StaticReceiverExpr will be "T(g(f(G)))". /// - public StaticReceiverExpr(IToken tok, UserDefinedType t, ClassDecl cl) + public StaticReceiverExpr(IToken tok, UserDefinedType t, ClassDecl cl, bool isImplicit) : base(tok) { Contract.Requires(tok != null); Contract.Requires(t.ResolvedClass != null); @@ -5441,6 +5448,11 @@ namespace Microsoft.Dafny { } Type = t; UnresolvedType = Type; + Implicit = isImplicit; + } + + public override bool IsImplicit { + get { return Implicit; } } } @@ -5597,6 +5609,10 @@ namespace Microsoft.Dafny { : base(tok) { Contract.Requires(tok != null); } + + public override bool IsImplicit { + get { return true; } + } } public class IdentifierExpr : Expression @@ -6534,7 +6550,7 @@ namespace Microsoft.Dafny { Contract.Invariant(Term != null); } - public readonly Attributes Attributes; + public Attributes Attributes; public abstract class BoundedPool { public virtual bool IsFinite { diff --git a/Source/Dafny/Printer.cs b/Source/Dafny/Printer.cs index cdcdb9e8..1c7508eb 100644 --- a/Source/Dafny/Printer.cs +++ b/Source/Dafny/Printer.cs @@ -1271,6 +1271,11 @@ namespace Microsoft.Dafny { PrintExpr(expr, 0, false, true, isFollowedBySemicolon, indent); } + private bool ParensNeeded(int opBindingStrength, int contextBindingStrength, bool fragileContext) { + return opBindingStrength < contextBindingStrength || + (fragileContext && opBindingStrength == contextBindingStrength); + } + /// /// An indent of -1 means print the entire expression on one line. /// @@ -1368,15 +1373,14 @@ namespace Microsoft.Dafny { PrintTypeInstantiation(e.OptTypeArguments); } else if (expr is ExprDotName) { - var e = (ExprDotName)expr; + var e = (ExprDotName)expr; //CLEMENT: Check the newly added Implicit parameter to make sure that we don't print "_default." DONE in FunctionCall. Where else? // determine if parens are needed int opBindingStrength = 0x70; - bool parensNeeded = !(e.Lhs is ImplicitThisExpr) && - opBindingStrength < contextBindingStrength || - (fragileContext && opBindingStrength == contextBindingStrength); + bool parensNeeded = !e.Lhs.IsImplicit && // KRML: I think that this never holds + ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext); if (parensNeeded) { wr.Write("("); } - if (!(e.Lhs is ImplicitThisExpr)) { + if (!e.Lhs.IsImplicit) { PrintExpr(e.Lhs, opBindingStrength, false, false, !parensNeeded && isFollowedBySemicolon, -1); wr.Write("."); } @@ -1388,9 +1392,8 @@ namespace Microsoft.Dafny { var e = (ApplySuffix)expr; // determine if parens are needed int opBindingStrength = 0x70; - bool parensNeeded = !(e.Lhs is ImplicitThisExpr) && - opBindingStrength < contextBindingStrength || - (fragileContext && opBindingStrength == contextBindingStrength); + bool parensNeeded = !e.Lhs.IsImplicit && // KRML: I think that this never holds + ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext); if (parensNeeded) { wr.Write("("); } if (ParensMayMatter(e.Lhs)) { @@ -1409,12 +1412,11 @@ namespace Microsoft.Dafny { MemberSelectExpr e = (MemberSelectExpr)expr; // determine if parens are needed int opBindingStrength = 0x70; - bool parensNeeded = !(e.Obj is ImplicitThisExpr) && - opBindingStrength < contextBindingStrength || - (fragileContext && opBindingStrength == contextBindingStrength); + bool parensNeeded = !e.Obj.IsImplicit && + ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext); if (parensNeeded) { wr.Write("("); } - if (!(e.Obj is ImplicitThisExpr)) { + if (!(e.Obj.IsImplicit)) { PrintExpr(e.Obj, opBindingStrength, false, false, !parensNeeded && isFollowedBySemicolon, -1); wr.Write("."); } @@ -1425,8 +1427,7 @@ namespace Microsoft.Dafny { SeqSelectExpr e = (SeqSelectExpr)expr; // determine if parens are needed int opBindingStrength = 0x70; - bool parensNeeded = opBindingStrength < contextBindingStrength || - (fragileContext && opBindingStrength == contextBindingStrength); + bool parensNeeded = ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext); if (parensNeeded) { wr.Write("("); } PrintExpr(e.Seq, 0x00, false, false, !parensNeeded && isFollowedBySemicolon, indent); // BOGUS: fix me @@ -1450,8 +1451,7 @@ namespace Microsoft.Dafny { MultiSelectExpr e = (MultiSelectExpr)expr; // determine if parens are needed int opBindingStrength = 0x70; - bool parensNeeded = opBindingStrength < contextBindingStrength || - (fragileContext && opBindingStrength == contextBindingStrength); + bool parensNeeded = ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext); if (parensNeeded) { wr.Write("("); } PrintExpr(e.Array, 0x00, false, false, !parensNeeded && isFollowedBySemicolon, indent); // BOGUS: fix me @@ -1475,8 +1475,7 @@ namespace Microsoft.Dafny { { // determine if parens are needed int opBindingStrength = 0x70; - bool parensNeeded = opBindingStrength < contextBindingStrength || - (fragileContext && opBindingStrength == contextBindingStrength); + bool parensNeeded = ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext); if (parensNeeded) { wr.Write("("); } PrintExpr(e.Seq, 00, false, false, !parensNeeded && isFollowedBySemicolon, indent); // BOGUS: fix me @@ -1491,9 +1490,7 @@ namespace Microsoft.Dafny { var e = (ApplyExpr)expr; // determine if parens are needed int opBindingStrength = 0x70; - bool parensNeeded = - opBindingStrength < contextBindingStrength || - (fragileContext && opBindingStrength == contextBindingStrength); + bool parensNeeded = ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext); if (parensNeeded) { wr.Write("("); } @@ -1508,12 +1505,11 @@ namespace Microsoft.Dafny { var e = (FunctionCallExpr)expr; // determine if parens are needed int opBindingStrength = 0x70; - bool parensNeeded = !(e.Receiver is ImplicitThisExpr) && - opBindingStrength < contextBindingStrength || - (fragileContext && opBindingStrength == contextBindingStrength); + bool parensNeeded = !(e.Receiver.IsImplicit) && + ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext); if (parensNeeded) { wr.Write("("); } - if (!(e.Receiver is ImplicitThisExpr)) { + if (!e.Receiver.IsImplicit) { PrintExpr(e.Receiver, opBindingStrength, false, false, !parensNeeded && isFollowedBySemicolon, -1); wr.Write("."); } @@ -1562,8 +1558,7 @@ namespace Microsoft.Dafny { default: Contract.Assert(false); throw new cce.UnreachableException(); // unexpected unary opcode } - bool parensNeeded = opBindingStrength < contextBindingStrength || - (fragileContext && opBindingStrength == contextBindingStrength); + bool parensNeeded = ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext); bool containsNestedNot = e.E is ParensExpression && ((ParensExpression)e.E).E is UnaryExpr && @@ -1849,8 +1844,7 @@ namespace Microsoft.Dafny { var e = (NegationExpression)expr; string op = "-"; int opBindingStrength = 0x60; - bool parensNeeded = opBindingStrength < contextBindingStrength || - (fragileContext && opBindingStrength == contextBindingStrength); + bool parensNeeded = ParensNeeded(opBindingStrength, contextBindingStrength, fragileContext); bool containsNestedNegation = e.E is ParensExpression && ((ParensExpression)e.E).E is NegationExpression; diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index ecb76ae8..5e08a38a 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -97,7 +97,7 @@ namespace Microsoft.Dafny { Contract.Requires(token != null); Contract.Requires(text != null); - Contract.Requires(0 <= length); + 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 }); } @@ -8172,7 +8172,7 @@ namespace Microsoft.Dafny // ----- 1. member of the enclosing class Expression receiver; if (member.IsStatic) { - receiver = new StaticReceiverExpr(expr.tok, (ClassDecl)member.EnclosingClass); + receiver = new StaticReceiverExpr(expr.tok, (ClassDecl)member.EnclosingClass, true); } else { if (!scope.AllowInstance) { Error(expr.tok, "'this' is not allowed in a 'static' context"); @@ -8220,7 +8220,7 @@ namespace Microsoft.Dafny var ambiguousMember = (AmbiguousMemberDecl)member; Error(expr.tok, "The name {0} ambiguously refers to a static member in one of the modules {1} (try qualifying the member name with the module name)", expr.Name, ambiguousMember.ModuleNames()); } else { - var receiver = new StaticReceiverExpr(expr.tok, (ClassDecl)member.EnclosingClass); + var receiver = new StaticReceiverExpr(expr.tok, (ClassDecl)member.EnclosingClass, true); r = ResolveExprDotCall(expr.tok, receiver, member, expr.OptTypeArguments, opts.codeContext, allowMethodCall); } @@ -8461,7 +8461,7 @@ namespace Microsoft.Dafny var ambiguousMember = (AmbiguousMemberDecl)member; Error(expr.tok, "The name {0} ambiguously refers to a static member in one of the modules {1} (try qualifying the member name with the module name)", expr.SuffixName, ambiguousMember.ModuleNames()); } else { - var receiver = new StaticReceiverExpr(expr.tok, (ClassDecl)member.EnclosingClass); + var receiver = new StaticReceiverExpr(expr.tok, (ClassDecl)member.EnclosingClass, true); r = ResolveExprDotCall(expr.tok, receiver, member, expr.OptTypeArguments, opts.codeContext, allowMethodCall); } } else { @@ -8484,10 +8484,10 @@ namespace Microsoft.Dafny Dictionary members; if (classMembers.TryGetValue(cd, out members) && members.TryGetValue(expr.SuffixName, out member)) { if (!member.IsStatic) { - Error(expr.tok, "accessing member '{0}' requires an instance expression", expr.SuffixName); + Error(expr.tok, "accessing member '{0}' requires an instance expression", expr.SuffixName); //FIXME Unify with similar error message // nevertheless, continue creating an expression that approximates a correct one } - var receiver = new StaticReceiverExpr(expr.tok, (UserDefinedType)ty.NormalizeExpand(), (ClassDecl)member.EnclosingClass); + var receiver = new StaticReceiverExpr(expr.tok, (UserDefinedType)ty.NormalizeExpand(), (ClassDecl)member.EnclosingClass, false); r = ResolveExprDotCall(expr.tok, receiver, member, expr.OptTypeArguments, opts.codeContext, allowMethodCall); } } else if (ty.IsDatatype) { @@ -8522,7 +8522,7 @@ namespace Microsoft.Dafny receiver = expr.Lhs; } else { Contract.Assert(expr.Lhs.Type.IsRefType); // only reference types have static methods - receiver = new StaticReceiverExpr(expr.tok, (UserDefinedType)expr.Lhs.Type.NormalizeExpand(), (ClassDecl)member.EnclosingClass); + receiver = new StaticReceiverExpr(expr.tok, (UserDefinedType)expr.Lhs.Type.NormalizeExpand(), (ClassDecl)member.EnclosingClass, false); } r = ResolveExprDotCall(expr.tok, receiver, member, expr.OptTypeArguments, opts.codeContext, allowMethodCall); } diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 4324b2b8..26412384 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -2781,7 +2781,7 @@ namespace Microsoft.Dafny { if (receiverReplacement != null) { recursiveCallReceiver = receiverReplacement; } else if (m.IsStatic) { - recursiveCallReceiver = new StaticReceiverExpr(m.tok, (ClassDecl)m.EnclosingClass); // this also resolves it + recursiveCallReceiver = new StaticReceiverExpr(m.tok, (ClassDecl)m.EnclosingClass, true); // this also resolves it } else { recursiveCallReceiver = new ImplicitThisExpr(m.tok); recursiveCallReceiver.Type = Resolver.GetThisType(m.tok, (ClassDecl)m.EnclosingClass); // resolve here -- cgit v1.2.3 From f177d7dc96b27f5ca3b777d31fcd9bad26be6ce5 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 6 Jul 2015 14:46:51 -0700 Subject: Report warnings only once. (This is the last step in fixing Issue #86.) --- Source/Dafny/Resolver.cs | 26 ++++++++++++++++++++------ Test/dafny0/JustWarnings.dfy | 19 +++++++++++++++++++ Test/dafny0/JustWarnings.dfy.expect | 4 ++++ 3 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 Test/dafny0/JustWarnings.dfy create mode 100644 Test/dafny0/JustWarnings.dfy.expect diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 66fd7959..3e308e76 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -50,15 +50,27 @@ namespace Microsoft.Dafny Contract.Requires(msg != null); Error(e.tok, msg, args); } + + private bool reportWarnings = true; + /// + /// Set whether or not to report warnings. Return the state of the previous behavior. + /// + public bool ReportWarnings(bool b) { + var old = reportWarnings; + reportWarnings = b; + return old; + } public void Warning(IToken tok, string msg, params object[] args) { Contract.Requires(tok != null); Contract.Requires(msg != null); - ConsoleColor col = Console.ForegroundColor; - Console.ForegroundColor = ConsoleColor.Yellow; - Console.WriteLine("{0}({1},{2}): Warning: {3}", - DafnyOptions.Clo.UseBaseNameForFileName ? System.IO.Path.GetFileName(tok.filename) : tok.filename, tok.line, tok.col - 1, - string.Format(msg, args)); - Console.ForegroundColor = col; + if (reportWarnings) { + ConsoleColor col = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine("{0}({1},{2}): Warning: {3}", + DafnyOptions.Clo.UseBaseNameForFileName ? System.IO.Path.GetFileName(tok.filename) : tok.filename, tok.line, tok.col - 1, + string.Format(msg, args)); + Console.ForegroundColor = col; + } } } @@ -366,6 +378,7 @@ namespace Microsoft.Dafny // compilation should only proceed if everything is good, including the signature (which preResolveErrorCount does not include); Contract.Assert(!useCompileSignatures); useCompileSignatures = true; // set Resolver-global flag to indicate that Signatures should be followed to their CompiledSignature + var oldWarnings = ReportWarnings(false); // turn off warning reporting for the clone var nw = new Cloner().CloneModuleDefinition(m, m.CompileName + "_Compile"); var compileSig = RegisterTopLevelDecls(nw, true); compileSig.Refines = refinementTransformer.RefinedSig; @@ -373,6 +386,7 @@ namespace Microsoft.Dafny ResolveModuleDefinition(nw, compileSig); prog.CompileModules.Add(nw); useCompileSignatures = false; // reset the flag + ReportWarnings(oldWarnings); } } else if (decl is AliasModuleDecl) { var alias = (AliasModuleDecl)decl; diff --git a/Test/dafny0/JustWarnings.dfy b/Test/dafny0/JustWarnings.dfy new file mode 100644 index 00000000..86523f5b --- /dev/null +++ b/Test/dafny0/JustWarnings.dfy @@ -0,0 +1,19 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /warnShadowing "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This file tests the behavior where the Resolver reports some warnings +// but no errors. In the case of errors, resolution does not continue +// to clone modules and resolve them, but the cloning does proceed if there +// are only warnings. Dafny should report only one copy of these warnings, +// and warnings are therefore turned off when processing the clones. This +// test file makes sure the warnings don't appear twice. + +method M(x: int) +{ + var x := 10; // warning: this shadows the parameter 'x' +} + +class C { + var u: T + method P(t: T) // warning: this shadows the type parameter 'T' +} diff --git a/Test/dafny0/JustWarnings.dfy.expect b/Test/dafny0/JustWarnings.dfy.expect new file mode 100644 index 00000000..5f0e66d8 --- /dev/null +++ b/Test/dafny0/JustWarnings.dfy.expect @@ -0,0 +1,4 @@ +JustWarnings.dfy(18,11): Warning: Shadowed type-parameter name: T +JustWarnings.dfy(13,6): Warning: Shadowed local-variable name: x + +Dafny program verifier finished with 3 verified, 0 errors -- cgit v1.2.3 From 57475c56804fad8c971f6c7280a08a0f0778b2c8 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Mon, 6 Jul 2015 17:52:19 -0700 Subject: Add a new test, with a FIXME --- Test/dafny0/IndexIntoUpdate.dfy | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Test/dafny0/IndexIntoUpdate.dfy diff --git a/Test/dafny0/IndexIntoUpdate.dfy b/Test/dafny0/IndexIntoUpdate.dfy new file mode 100644 index 00000000..01359e04 --- /dev/null +++ b/Test/dafny0/IndexIntoUpdate.dfy @@ -0,0 +1,9 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" +method M() { + var s := [1, 2, 3, 4]; + assert 3 in s; + s := s[0 := 1]; + if * { assert 3 in s; } // FIXME: This should verify + else { assert s[2] == 3; assert 3 in s; } +} -- cgit v1.2.3 From f235dfbc792bb885f3c76e4267658c1a9ef838d8 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 7 Jul 2015 15:27:58 -0700 Subject: Fixed crashes in overrides checking of function decreases clauses, and improved the error message reported to users --- Source/Dafny/DafnyAst.cs | 2 ++ Source/Dafny/Translator.cs | 46 ++++++---------------------- Test/dafny0/Trait/TraitsDecreases.dfy | 46 ++++++++++++++++++++++++++++ Test/dafny0/Trait/TraitsDecreases.dfy.expect | 20 +++++++++++- 4 files changed, 76 insertions(+), 38 deletions(-) diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index 134fb4c1..d14d82a3 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -2223,6 +2223,7 @@ namespace Microsoft.Dafny { public interface ICallable : ICodeContext { IToken Tok { get; } + string WhatKind { get; } string NameRelativeToModule { get; } Specification Decreases { get; } /// @@ -2234,6 +2235,7 @@ namespace Microsoft.Dafny { } public class DontUseICallable : ICallable { + public string WhatKind { get { throw new cce.UnreachableException(); } } public bool IsGhost { get { throw new cce.UnreachableException(); } } public List TypeArgs { get { throw new cce.UnreachableException(); } } public List Ins { get { throw new cce.UnreachableException(); } } diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index cefce6b6..a4cf3700 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -3018,7 +3018,7 @@ namespace Microsoft.Dafny { AddFunctionOverrideReqsChk(f, builder, etran, substMap); //adding assert R <= Rank’; - AddFunctionOverrideTerminationChk(f, builder, etran, substMap); + AddOverrideTerminationChk(f, f.OverriddenFunction, builder, etran, substMap); //adding assert W <= Frame’ AddFunctionOverrideSubsetChk(f, builder, etran, localVariables, substMap); @@ -3181,35 +3181,6 @@ namespace Microsoft.Dafny { builder.Add(Assert(tok, q, "expression may read an object not in the parent trait context's reads clause", kv)); } - private void AddFunctionOverrideTerminationChk(Function f, StmtListBuilder builder, ExpressionTranslator etran, Dictionary substMap) - { - var decrToks = new List(); - var decrTypes1 = new List(); - var decrTypes2 = new List(); - var decrClass = new List(); - var decrTrait = new List(); - if (f.Decreases != null) - { - foreach (var decC in f.Decreases.Expressions) - { - decrToks.Add(decC.tok); - decrTypes1.Add(decC.Type); - decrClass.Add(etran.TrExpr(decC)); - } - } - if (f.OverriddenFunction.Decreases != null) - { - foreach (var decT in f.OverriddenFunction.Decreases.Expressions) - { - var decCNew = Substitute(decT, null, substMap); - decrTypes2.Add(decCNew.Type); - decrTrait.Add(etran.TrExpr(decCNew)); - } - } - var decrChk = DecreasesCheck(decrToks, decrTypes1, decrTypes2, decrClass, decrTrait, null, null, true, false); - builder.Add(new Bpl.AssertCmd(f.tok, decrChk)); - } - private void AddFunctionOverrideReqsChk(Function f, StmtListBuilder builder, ExpressionTranslator etran, Dictionary substMap) { //generating trait pre-conditions with class variables @@ -3277,7 +3248,7 @@ namespace Microsoft.Dafny { AddMethodOverrideReqsChk(m, builder, etran, substMap); //adding assert R <= Rank’; - AddMethodOverrideTerminationChk(m, builder, etran, substMap); + AddOverrideTerminationChk(m, m.OverriddenMethod, builder, etran, substMap); //adding assert W <= Frame’ AddMethodOverrideSubsetChk(m, builder, etran, localVariables, substMap); @@ -3364,14 +3335,15 @@ namespace Microsoft.Dafny { } } - private void AddMethodOverrideTerminationChk(Method m, Bpl.StmtListBuilder builder, ExpressionTranslator etran, Dictionary substMap) { - Contract.Requires(m != null); + private void AddOverrideTerminationChk(ICallable original, ICallable overryd, Bpl.StmtListBuilder builder, ExpressionTranslator etran, Dictionary substMap) { + Contract.Requires(original != null); + Contract.Requires(overryd != null); Contract.Requires(builder != null); Contract.Requires(etran != null); Contract.Requires(substMap != null); // Note, it is as if the trait's method is calling the class's method. - var contextDecreases = m.OverriddenMethod.Decreases.Expressions; - var calleeDecreases = m.Decreases.Expressions; + var contextDecreases = overryd.Decreases.Expressions; + var calleeDecreases = original.Decreases.Expressions; // We want to check: calleeDecreases <= contextDecreases (note, we can allow equality, since there is a bounded, namely 1, number of dynamic dispatches) if (Contract.Exists(contextDecreases, e => e is WildcardExpr)) { // no check needed @@ -3392,7 +3364,7 @@ namespace Microsoft.Dafny { N = i; break; } - toks.Add(new NestedToken(m.tok, e1.tok)); + toks.Add(new NestedToken(original.Tok, e1.tok)); types0.Add(e0.Type.NormalizeExpand()); types1.Add(e1.Type.NormalizeExpand()); callee.Add(etran.TrExpr(e0)); @@ -3425,7 +3397,7 @@ namespace Microsoft.Dafny { // as "false". bool allowNoChange = N == decrCountT && decrCountT <= decrCountC; var decrChk = DecreasesCheck(toks, types0, types1, callee, caller, null, null, allowNoChange, false); - builder.Add(Assert(m.tok, decrChk, "method's decreases clause must be below or equal to that in the trait")); + builder.Add(Assert(original.Tok, decrChk, string.Format("{0}'s decreases clause must be below or equal to that in the trait", original.WhatKind))); } private void AddMethodOverrideSubsetChk(Method m, Bpl.StmtListBuilder builder, ExpressionTranslator etran, List localVariables, Dictionary substMap) diff --git a/Test/dafny0/Trait/TraitsDecreases.dfy b/Test/dafny0/Trait/TraitsDecreases.dfy index 53ce28be..8ab3672a 100644 --- a/Test/dafny0/Trait/TraitsDecreases.dfy +++ b/Test/dafny0/Trait/TraitsDecreases.dfy @@ -106,3 +106,49 @@ class CC extends TT { decreases * { } } + + +// The following module contains various regression tests +module More { + trait A0 { + predicate P() decreases 5 + } + class B0 extends A0 { + predicate P() // error: rank is not lower + } + + trait A1 { + predicate P() decreases 5 + } + class B1 extends A1 { + predicate P() reads this // error: rank is not lower + } + + trait A2 { + predicate P(x: int) + } + class B2 extends A2 { + predicate P(x: int) reads this // error: rank is not lower + } + + trait A3 { + predicate P() reads this + } + class B3 extends A3 { + predicate P() // error: rank is not lower + } + + trait A4 { + predicate P(x: int) decreases 5 + } + class B4 extends A4 { + predicate P(x: int) // error: rank is not lower + } + + trait A5 { + method M(x: int) decreases 5 + } + class B5 extends A5 { + method M(x: int) // error: rank is not lower + } +} diff --git a/Test/dafny0/Trait/TraitsDecreases.dfy.expect b/Test/dafny0/Trait/TraitsDecreases.dfy.expect index 6c76f9a8..2607a0c6 100644 --- a/Test/dafny0/Trait/TraitsDecreases.dfy.expect +++ b/Test/dafny0/Trait/TraitsDecreases.dfy.expect @@ -1,3 +1,21 @@ +TraitsDecreases.dfy(117,15): Error: predicate's decreases clause must be below or equal to that in the trait +Execution trace: + (0,0): anon0 +TraitsDecreases.dfy(124,15): Error: predicate's decreases clause must be below or equal to that in the trait +Execution trace: + (0,0): anon0 +TraitsDecreases.dfy(131,15): Error: predicate's decreases clause must be below or equal to that in the trait +Execution trace: + (0,0): anon0 +TraitsDecreases.dfy(138,15): Error: predicate's decreases clause must be below or equal to that in the trait +Execution trace: + (0,0): anon0 +TraitsDecreases.dfy(145,15): Error: predicate's decreases clause must be below or equal to that in the trait +Execution trace: + (0,0): anon0 +TraitsDecreases.dfy(152,12): Error: method's decreases clause must be below or equal to that in the trait +Execution trace: + (0,0): anon0 TraitsDecreases.dfy(57,10): Error: method's decreases clause must be below or equal to that in the trait Execution trace: (0,0): anon0 @@ -14,4 +32,4 @@ TraitsDecreases.dfy(88,10): Error: method's decreases clause must be below or eq Execution trace: (0,0): anon0 -Dafny program verifier finished with 63 verified, 5 errors +Dafny program verifier finished with 75 verified, 11 errors -- cgit v1.2.3 From fad74b96e5d9367960358b1c4cc9c2cce79e961a Mon Sep 17 00:00:00 2001 From: Michael Lowell Roberts Date: Wed, 8 Jul 2015 11:01:11 -0700 Subject: added unit tests for exclusive refinement. --- Test/irondafny0/FIFO.dfy | 43 ++++++++++++++++++ Test/irondafny0/FIFO.dfy.expect | 8 ++++ Test/irondafny0/LIFO.dfy | 43 ++++++++++++++++++ Test/irondafny0/LIFO.dfy.expect | 8 ++++ Test/irondafny0/Queue.dfyi | 22 ++++++++++ Test/irondafny0/inheritreqs0.dfy | 22 ++++++++++ Test/irondafny0/inheritreqs0.dfy.expect | 6 +++ Test/irondafny0/inheritreqs1.dfy | 22 ++++++++++ Test/irondafny0/inheritreqs1.dfy.expect | 6 +++ Test/irondafny0/xrefine0.dfy | 6 +++ Test/irondafny0/xrefine0.dfy.expect | 2 + Test/irondafny0/xrefine1.dfy | 77 +++++++++++++++++++++++++++++++++ Test/irondafny0/xrefine1.dfy.expect | 6 +++ Test/irondafny0/xrefine2.dfy | 77 +++++++++++++++++++++++++++++++++ Test/irondafny0/xrefine2.dfy.expect | 9 ++++ Test/irondafny0/xrefine3.dfy | 72 ++++++++++++++++++++++++++++++ Test/irondafny0/xrefine3.dfy.expect | 6 +++ 17 files changed, 435 insertions(+) create mode 100644 Test/irondafny0/FIFO.dfy create mode 100644 Test/irondafny0/FIFO.dfy.expect create mode 100644 Test/irondafny0/LIFO.dfy create mode 100644 Test/irondafny0/LIFO.dfy.expect create mode 100644 Test/irondafny0/Queue.dfyi create mode 100644 Test/irondafny0/inheritreqs0.dfy create mode 100644 Test/irondafny0/inheritreqs0.dfy.expect create mode 100644 Test/irondafny0/inheritreqs1.dfy create mode 100644 Test/irondafny0/inheritreqs1.dfy.expect create mode 100644 Test/irondafny0/xrefine0.dfy create mode 100644 Test/irondafny0/xrefine0.dfy.expect create mode 100644 Test/irondafny0/xrefine1.dfy create mode 100644 Test/irondafny0/xrefine1.dfy.expect create mode 100644 Test/irondafny0/xrefine2.dfy create mode 100644 Test/irondafny0/xrefine2.dfy.expect create mode 100644 Test/irondafny0/xrefine3.dfy create mode 100644 Test/irondafny0/xrefine3.dfy.expect diff --git a/Test/irondafny0/FIFO.dfy b/Test/irondafny0/FIFO.dfy new file mode 100644 index 00000000..1256b55d --- /dev/null +++ b/Test/irondafny0/FIFO.dfy @@ -0,0 +1,43 @@ +// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +include "Queue.dfyi" + +module FIFO exclusively refines Queue { + type Item = int + + method Init() returns (q: Queue) { + q := []; + } + + method Push(item: Item, q: Queue) returns (q': Queue) { + return q + [item]; + } + + method Pop(q: Queue) returns (item: Item, q': Queue) + ensures item == q[0] + { + item := q[0]; + q' := q[1..]; + } +} + +module MainImpl refines MainSpec { + import Q = FIFO + + method Main() + { + var q := Q.Init(); + q := Q.Push(0, q); + q := Q.Push(1, q); + q := Q.Push(2, q); + + var n: int; + n, q := Q.Pop(q); + print n, "\n"; + n, q := Q.Pop(q); + print n, "\n"; + n, q := Q.Pop(q); + print n, "\n"; + } +} diff --git a/Test/irondafny0/FIFO.dfy.expect b/Test/irondafny0/FIFO.dfy.expect new file mode 100644 index 00000000..25021947 --- /dev/null +++ b/Test/irondafny0/FIFO.dfy.expect @@ -0,0 +1,8 @@ + +Dafny program verifier finished with 8 verified, 0 errors +Program compiled successfully +Running... + +0 +1 +2 diff --git a/Test/irondafny0/LIFO.dfy b/Test/irondafny0/LIFO.dfy new file mode 100644 index 00000000..bac08fba --- /dev/null +++ b/Test/irondafny0/LIFO.dfy @@ -0,0 +1,43 @@ +// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +include "Queue.dfyi" + +module LIFO exclusively refines Queue { + type Item = int + + method Init() returns (q: Queue) { + q := []; + } + + method Push(item: Item, q: Queue) returns (q': Queue) { + return [item] + q; + } + + method Pop(q: Queue) returns (item: Item, q': Queue) + ensures item == q[0] + { + item := q[0]; + q' := q[1..]; + } +} + +module MainImpl refines MainSpec { + import Q = LIFO + + method Main() + { + var q := Q.Init(); + q := Q.Push(0, q); + q := Q.Push(1, q); + q := Q.Push(2, q); + + var n: int; + n, q := Q.Pop(q); + print n, "\n"; + n, q := Q.Pop(q); + print n, "\n"; + n, q := Q.Pop(q); + print n, "\n"; + } +} diff --git a/Test/irondafny0/LIFO.dfy.expect b/Test/irondafny0/LIFO.dfy.expect new file mode 100644 index 00000000..83f90a5b --- /dev/null +++ b/Test/irondafny0/LIFO.dfy.expect @@ -0,0 +1,8 @@ + +Dafny program verifier finished with 8 verified, 0 errors +Program compiled successfully +Running... + +2 +1 +0 diff --git a/Test/irondafny0/Queue.dfyi b/Test/irondafny0/Queue.dfyi new file mode 100644 index 00000000..9f7eb534 --- /dev/null +++ b/Test/irondafny0/Queue.dfyi @@ -0,0 +1,22 @@ +// Queue.dfyi + +abstract module Queue { + type Item + type Queue = seq + + method Init() returns (q: Queue) + ensures |q| == 0; + + method Push(item: Item, q: Queue) returns (q': Queue) + ensures |q'| == |q| + 1; + + method Pop(q: Queue) returns (item: Item, q': Queue) + requires |q| > 0; + ensures item in q; + ensures |q'| == |q| - 1; +} + +abstract module MainSpec { + import Q as Queue +} + diff --git a/Test/irondafny0/inheritreqs0.dfy b/Test/irondafny0/inheritreqs0.dfy new file mode 100644 index 00000000..a0117da0 --- /dev/null +++ b/Test/irondafny0/inheritreqs0.dfy @@ -0,0 +1,22 @@ +// RUN: %dafny /compile:3 /optimize /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +abstract module Spec { + method Greet(b: bool) + requires b; +} + +module Impl refines Spec { + method Greet(b: bool) { + print "o hai!\n"; + } + + method Xyzzy(b: bool) + requires b; + {} + + method Main() { + Greet(false); + Xyzzy(false); + } +} diff --git a/Test/irondafny0/inheritreqs0.dfy.expect b/Test/irondafny0/inheritreqs0.dfy.expect new file mode 100644 index 00000000..eaadc85a --- /dev/null +++ b/Test/irondafny0/inheritreqs0.dfy.expect @@ -0,0 +1,6 @@ +inheritreqs0.dfy(19,14): Error BP5002: A precondition for this call might not hold. +inheritreqs0.dfy[Impl](6,18): Related location: This is the precondition that might not hold. +Execution trace: + (0,0): anon0 + +Dafny program verifier finished with 6 verified, 1 error diff --git a/Test/irondafny0/inheritreqs1.dfy b/Test/irondafny0/inheritreqs1.dfy new file mode 100644 index 00000000..c83d04ac --- /dev/null +++ b/Test/irondafny0/inheritreqs1.dfy @@ -0,0 +1,22 @@ +// RUN: %dafny /compile:3 /optimize /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +abstract module Spec { + method Greet(b: bool) + requires b; +} + +module Impl refines Spec { + method Greet(b: bool) { + print "o hai!\n"; + } + + method Xyzzy(b: bool) + requires b; + {} + + method Main() { + Greet(true); + Xyzzy(false); + } +} diff --git a/Test/irondafny0/inheritreqs1.dfy.expect b/Test/irondafny0/inheritreqs1.dfy.expect new file mode 100644 index 00000000..27c76fee --- /dev/null +++ b/Test/irondafny0/inheritreqs1.dfy.expect @@ -0,0 +1,6 @@ +inheritreqs1.dfy(20,14): Error BP5002: A precondition for this call might not hold. +inheritreqs1.dfy(15,18): Related location: This is the precondition that might not hold. +Execution trace: + (0,0): anon0 + +Dafny program verifier finished with 6 verified, 1 error diff --git a/Test/irondafny0/xrefine0.dfy b/Test/irondafny0/xrefine0.dfy new file mode 100644 index 00000000..35993ce8 --- /dev/null +++ b/Test/irondafny0/xrefine0.dfy @@ -0,0 +1,6 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +abstract module Delicious {} +module Chocolate exclusively refines Delicious {} +module Strawberry exclusively refines Delicious {} diff --git a/Test/irondafny0/xrefine0.dfy.expect b/Test/irondafny0/xrefine0.dfy.expect new file mode 100644 index 00000000..136e06db --- /dev/null +++ b/Test/irondafny0/xrefine0.dfy.expect @@ -0,0 +1,2 @@ +xrefine0.dfy(6,7): Error: no more than one exclusive refinement may exist for a given module. +1 resolution/type errors detected in xrefine0.dfy diff --git a/Test/irondafny0/xrefine1.dfy b/Test/irondafny0/xrefine1.dfy new file mode 100644 index 00000000..10d25f53 --- /dev/null +++ b/Test/irondafny0/xrefine1.dfy @@ -0,0 +1,77 @@ +// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +abstract module ProtocolSpec { + type ProtoT + + predicate Init(p:ProtoT) +} + +abstract module HostSpec { + type HostT + import P as ProtocolSpec + + function method foo(h:HostT) : P.ProtoT +} + +module ProtocolImpl exclusively refines ProtocolSpec { + type ProtoT = bool + + predicate Init(p:ProtoT) { !p } + + method orange(i:nat) returns (j:nat) + { + j := i + 1; + } +} + +module HostImpl exclusively refines HostSpec { + import P = ProtocolImpl + + type HostT = int + + function method foo(h:HostT) : P.ProtoT + { + h > 0 + } + + method apple(i:nat) returns (j:nat) + { + j := i + 1; + } +} + +abstract module MainSpec { + import HI as HostSpec + import PI as ProtocolSpec + + method Test(h1:HI.HostT, h2:HI.HostT) + requires HI.foo(h1) == HI.foo(h2); + requires PI.Init(HI.foo(h1)) +} + +module MainImpl exclusively refines MainSpec { + import HI = HostImpl + import PI = ProtocolImpl + + method Test(h1:HI.HostT, h2:HI.HostT) + { + var a := HI.foo(h1); + print "HI.foo(h1) => ", a, "\n"; + var b := HI.foo(h2); + print "HI.foo(h2) => ", b, "\n"; + var i := PI.orange(1); + print "PI.orange(1) => ", i, "\n"; + var j := HI.apple(2); + print "PI.apple(2) => ", j, "\n"; + } + + method Main() + { + Test(-1, 1); + } +} + + + + diff --git a/Test/irondafny0/xrefine1.dfy.expect b/Test/irondafny0/xrefine1.dfy.expect new file mode 100644 index 00000000..ae844fc8 --- /dev/null +++ b/Test/irondafny0/xrefine1.dfy.expect @@ -0,0 +1,6 @@ +xrefine1.dfy(71,13): Error BP5002: A precondition for this call might not hold. +xrefine1.dfy[MainImpl](49,29): Related location: This is the precondition that might not hold. +Execution trace: + (0,0): anon0 + +Dafny program verifier finished with 12 verified, 1 error diff --git a/Test/irondafny0/xrefine2.dfy b/Test/irondafny0/xrefine2.dfy new file mode 100644 index 00000000..7578b424 --- /dev/null +++ b/Test/irondafny0/xrefine2.dfy @@ -0,0 +1,77 @@ +// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +abstract module ProtocolSpec { + type ProtoT + + predicate Init(p:ProtoT) +} + +abstract module HostSpec { + type HostT + import P as ProtocolSpec + + function method foo(h:HostT) : P.ProtoT +} + +module ProtocolImpl exclusively refines ProtocolSpec { + type ProtoT = bool + + predicate Init(p:ProtoT) { p } + + method orange(i:nat) returns (j:nat) + { + j := i + 1; + } +} + +module HostImpl exclusively refines HostSpec { + import P = ProtocolImpl + + type HostT = int + + function method foo(h:HostT) : P.ProtoT + { + h != 0 + } + + method apple(i:nat) returns (j:nat) + { + j := i + 1; + } +} + +abstract module MainSpec { + import HI as HostSpec + import PI as ProtocolSpec + + method Test(h1:HI.HostT, h2:HI.HostT) + requires HI.foo(h1) == HI.foo(h2); + requires PI.Init(HI.foo(h1)) +} + +module MainImpl exclusively refines MainSpec { + import HI = HostImpl + import PI = ProtocolImpl + + method Test(h1:HI.HostT, h2:HI.HostT) + { + var a := HI.foo(h1); + print "HI.foo(h1) => ", a, "\n"; + var b := HI.foo(h2); + print "HI.foo(h2) => ", b, "\n"; + var i := PI.orange(1); + print "PI.orange(1) => ", i, "\n"; + var j := HI.apple(2); + print "PI.apple(2) => ", j, "\n"; + } + + method Main() + { + Test(-1, 1); + } +} + + + + diff --git a/Test/irondafny0/xrefine2.dfy.expect b/Test/irondafny0/xrefine2.dfy.expect new file mode 100644 index 00000000..6d3fecd4 --- /dev/null +++ b/Test/irondafny0/xrefine2.dfy.expect @@ -0,0 +1,9 @@ + +Dafny program verifier finished with 13 verified, 0 errors +Program compiled successfully +Running... + +HI.foo(h1) => True +HI.foo(h2) => True +PI.orange(1) => 2 +PI.apple(2) => 3 diff --git a/Test/irondafny0/xrefine3.dfy b/Test/irondafny0/xrefine3.dfy new file mode 100644 index 00000000..69c5bc27 --- /dev/null +++ b/Test/irondafny0/xrefine3.dfy @@ -0,0 +1,72 @@ +// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +abstract module AlphaSpec { + type Alpha + + predicate IsValid(a:Alpha) + + method Init() returns (a:Alpha) + ensures IsValid(a); +} + +abstract module BetaSpec { + type Beta + import A as AlphaSpec + + predicate IsValid(b:Beta) + + method Init(ays:seq) returns (b:Beta) + requires forall i :: 0 <= i < |ays| ==> A.IsValid(ays[i]); + ensures IsValid(b); +} + +module AlphaImpl exclusively refines AlphaSpec { + type Alpha = bool + + predicate IsValid(a:Alpha) { + a + } + + method Init() returns (a:Alpha) + ensures IsValid(a); + { + a := true; + } +} + +module BetaImpl exclusively refines BetaSpec { + import A = AlphaImpl + type Beta = seq + + predicate IsValid(b:Beta) { + forall i :: 0 <= i < |b| ==> A.IsValid(b[i]) + } + + method Init(ays:seq) returns (b:Beta) { + b := ays; + } +} + +abstract module MainSpec { + import A as AlphaSpec + import B as BetaSpec + + method Main() + { + var a := A.Init(); + var ays := [a, a]; + assert forall i :: 0 <= i < |ays| ==> A.IsValid(ays[i]); + var b := B.Init(ays); + print "o hai!\n"; + } +} + +module MainImpl exclusively refines MainSpec { + import B = BetaImpl + import A = AlphaImpl +} + + + + diff --git a/Test/irondafny0/xrefine3.dfy.expect b/Test/irondafny0/xrefine3.dfy.expect new file mode 100644 index 00000000..1e5a5b4e --- /dev/null +++ b/Test/irondafny0/xrefine3.dfy.expect @@ -0,0 +1,6 @@ + +Dafny program verifier finished with 14 verified, 0 errors +Program compiled successfully +Running... + +o hai! -- cgit v1.2.3 From fe501d243c0413db8ae85bda174d0761da00d330 Mon Sep 17 00:00:00 2001 From: Michael Lowell Roberts Date: Mon, 13 Jul 2015 10:40:35 -0700 Subject: [IronDafny] implemented workaround for "import opened" bug(s). --- Source/Dafny/Cloner.cs | 8 ++-- Source/Dafny/Dafny.atg | 16 ++++---- Source/Dafny/DafnyAst.cs | 52 +++++++++++++++++++----- Source/Dafny/DafnyOptions.cs | 27 +++++++++++- Source/Dafny/Parser.cs | 16 ++++---- Source/Dafny/Resolver.cs | 61 ++++++++++++++++++++-------- Test/irondafny0/FIFO.dfy | 2 +- Test/irondafny0/LIFO.dfy | 2 +- Test/irondafny0/opened_workaround.dfy | 21 ++++++++++ Test/irondafny0/opened_workaround.dfy.expect | 3 ++ Test/irondafny0/xrefine0.dfy | 2 +- Test/irondafny0/xrefine1.dfy | 2 +- Test/irondafny0/xrefine2.dfy | 2 +- Test/irondafny0/xrefine3.dfy | 2 +- 14 files changed, 161 insertions(+), 55 deletions(-) create mode 100644 Test/irondafny0/opened_workaround.dfy create mode 100644 Test/irondafny0/opened_workaround.dfy.expect diff --git a/Source/Dafny/Cloner.cs b/Source/Dafny/Cloner.cs index 6e64c7ec..fe9795d3 100644 --- a/Source/Dafny/Cloner.cs +++ b/Source/Dafny/Cloner.cs @@ -44,9 +44,9 @@ namespace Microsoft.Dafny } else if (d is NewtypeDecl) { var dd = (NewtypeDecl)d; if (dd.Var == null) { - return new NewtypeDecl(Tok(dd.tok), dd.Name, m, CloneType(dd.BaseType), CloneAttributes(dd.Attributes)); + return new NewtypeDecl(Tok(dd.tok), dd.Name, m, CloneType(dd.BaseType), CloneAttributes(dd.Attributes), dd); } else { - return new NewtypeDecl(Tok(dd.tok), dd.Name, m, CloneBoundVar(dd.Var), CloneExpr(dd.Constraint), CloneAttributes(dd.Attributes)); + return new NewtypeDecl(Tok(dd.tok), dd.Name, m, CloneBoundVar(dd.Var), CloneExpr(dd.Constraint), CloneAttributes(dd.Attributes), dd); } } else if (d is TupleTypeDecl) { var dd = (TupleTypeDecl)d; @@ -55,7 +55,7 @@ namespace Microsoft.Dafny var dd = (IndDatatypeDecl)d; var tps = dd.TypeArgs.ConvertAll(CloneTypeParam); var ctors = dd.Ctors.ConvertAll(CloneCtor); - var dt = new IndDatatypeDecl(Tok(dd.tok), dd.Name, m, tps, ctors, CloneAttributes(dd.Attributes)); + var dt = new IndDatatypeDecl(Tok(dd.tok), dd.Name, m, tps, ctors, CloneAttributes(dd.Attributes), dd); return dt; } else if (d is CoDatatypeDecl) { var dd = (CoDatatypeDecl)d; @@ -108,7 +108,7 @@ namespace Microsoft.Dafny if (d is DefaultClassDecl) { return new DefaultClassDecl(m, mm); } else { - return new ClassDecl(Tok(dd.tok), dd.Name, m, tps, mm, CloneAttributes(dd.Attributes), dd.TraitsTyp.ConvertAll(CloneType)); + return new ClassDecl(Tok(dd.tok), dd.Name, m, tps, mm, CloneAttributes(dd.Attributes), dd.TraitsTyp.ConvertAll(CloneType), dd); } } else if (d is ModuleDecl) { if (d is LiteralModuleDecl) { diff --git a/Source/Dafny/Dafny.atg b/Source/Dafny/Dafny.atg index 7b51fb5e..6e939968 100644 --- a/Source/Dafny/Dafny.atg +++ b/Source/Dafny/Dafny.atg @@ -515,7 +515,7 @@ Dafny | OtherTypeDecl (. defaultModule.TopLevelDecls.Add(td); .) | IteratorDecl (. defaultModule.TopLevelDecls.Add(iter); .) | TraitDecl (. defaultModule.TopLevelDecls.Add(trait); .) - | ClassMemberDecl + | ClassMemberDecl } (. // find the default class in the default module, then append membersDefaultClass to its member list DefaultClassDecl defaultClass = null; @@ -561,7 +561,7 @@ SubModuleDecl | NewtypeDecl (. module.TopLevelDecls.Add(td); .) | OtherTypeDecl (. module.TopLevelDecls.Add(td); .) | IteratorDecl (. module.TopLevelDecls.Add(iter); .) - | ClassMemberDecl + | ClassMemberDecl } "}" (. module.BodyEndTok = t; module.TopLevelDecls.Add(new DefaultClassDecl(module, namedModuleDefaultClassMembers)); @@ -618,7 +618,7 @@ ClassDecl {"," Type (. traits.Add(trait); .) } ] "{" (. bodyStart = t; .) - { ClassMemberDecl + { ClassMemberDecl } "}" (. c = new ClassDecl(id, id.val, module, typeArgs, members, attrs, traits); @@ -642,7 +642,7 @@ ClassDecl NoUSIdent [ GenericParameters ] "{" (. bodyStart = t; .) - { ClassMemberDecl + { ClassMemberDecl } "}" (. trait = new TraitDecl(id, id.val, module, typeArgs, members, attrs); @@ -651,7 +651,7 @@ ClassDecl .) . -ClassMemberDecl<.List mm, bool allowConstructors, bool moduleLevelDecl.> +ClassMemberDecl<.List mm, bool allowConstructors, bool moduleLevelDecl, bool permitAbstractDecl.> = (. Contract.Requires(cce.NonNullElements(mm)); Method/*!*/ m; Function/*!*/ f; @@ -685,7 +685,7 @@ ClassMemberDecl<.List mm, bool allowConstructors, bool moduleLevelDe mmod.IsProtected = false; } .) - MethodDecl (. mm.Add(m); .) + MethodDecl (. mm.Add(m); .) ) . DatatypeDecl @@ -937,7 +937,7 @@ GenericParameters<.List/*!*/ typeArgs.> ">" . /*------------------------------------------------------------------------*/ -MethodDecl +MethodDecl = (. Contract.Ensures(Contract.ValueAtReturn(out m) !=null); IToken/*!*/ id = Token.NoToken; bool hasName = false; IToken keywordToken; @@ -1018,7 +1018,7 @@ MethodDecl [ BlockStmt ] (. - if (DafnyOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 && !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported") && !Attributes.Contains(attrs, "decl") && theVerifyThisFile) { + if (!permitAbstractDecl && DafnyOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 && !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported") && !Attributes.Contains(attrs, "decl") && theVerifyThisFile) { SemErr(t, "a method with an ensures clause must have a body, unless given the :axiom attribute"); } diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index d14d82a3..c90755a3 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -1777,6 +1777,14 @@ namespace Microsoft.Dafny { this.refinementBase = null; Includes = new List(); IsBuiltinName = isBuiltinName; + + if (isExclusiveRefinement && !DafnyOptions.O.IronDafny) { + parser.errors.SynErr( + tok.filename, + tok.line, + tok.col, + "The exclusively keyword is experimental and only available when IronDafny features are enabled (/ironDafny)."); + } } public virtual bool IsDefaultModule { get { @@ -1990,8 +1998,8 @@ namespace Microsoft.Dafny { } public ClassDecl(IToken tok, string name, ModuleDefinition module, - List typeArgs, [Captured] List members, Attributes attributes, List traits) - : base(tok, name, module, typeArgs, attributes) { + List typeArgs, [Captured] List members, Attributes attributes, List traits, ClassDecl clonedFrom = null) + : base(tok, name, module, typeArgs, attributes, clonedFrom) { Contract.Requires(tok != null); Contract.Requires(name != null); Contract.Requires(module != null); @@ -2005,6 +2013,12 @@ namespace Microsoft.Dafny { return false; } } + + public new ClassDecl ClonedFrom { + get { + return (ClassDecl)base.ClonedFrom; + } + } } public class DefaultClassDecl : ClassDecl { @@ -2067,8 +2081,8 @@ namespace Microsoft.Dafny { } public DatatypeDecl(IToken tok, string name, ModuleDefinition module, List typeArgs, - [Captured] List ctors, Attributes attributes) - : base(tok, name, module, typeArgs, attributes) { + [Captured] List ctors, Attributes attributes, DatatypeDecl clonedFrom = null) + : base(tok, name, module, typeArgs, attributes, clonedFrom) { Contract.Requires(tok != null); Contract.Requires(name != null); Contract.Requires(module != null); @@ -2082,6 +2096,12 @@ namespace Microsoft.Dafny { return (TypeArgs.Count == 0 && Ctors.TrueForAll(ctr => ctr.Formals.Count == 0)); } } + + public new DatatypeDecl ClonedFrom { + get { + return (DatatypeDecl)base.ClonedFrom; + } + } } public class IndDatatypeDecl : DatatypeDecl @@ -2094,8 +2114,8 @@ namespace Microsoft.Dafny { public ES EqualitySupport = ES.NotYetComputed; public IndDatatypeDecl(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, IndDatatypeDecl clonedFrom = null) + : base(tok, name, module, typeArgs, ctors, attributes, clonedFrom) { Contract.Requires(tok != null); Contract.Requires(name != null); Contract.Requires(module != null); @@ -2103,6 +2123,12 @@ namespace Microsoft.Dafny { Contract.Requires(cce.NonNullElements(ctors)); Contract.Requires(1 <= ctors.Count); } + + public new IndDatatypeDecl ClonedFrom { + get { + return (IndDatatypeDecl)base.ClonedFrom; + } + } } public class TupleTypeDecl : IndDatatypeDecl @@ -2578,16 +2604,16 @@ namespace Microsoft.Dafny { public readonly BoundVar Var; // can be null (if non-null, then object.ReferenceEquals(Var.Type, BaseType)) public readonly Expression Constraint; // is null iff Var is public NativeType NativeType; // non-null for fixed-size representations (otherwise, use BigIntegers for integers) - public NewtypeDecl(IToken tok, string name, ModuleDefinition module, Type baseType, Attributes attributes) - : base(tok, name, module, new List(), attributes) { + public NewtypeDecl(IToken tok, string name, ModuleDefinition module, Type baseType, Attributes attributes, NewtypeDecl clonedFrom = null) + : base(tok, name, module, new List(), attributes, clonedFrom) { Contract.Requires(tok != null); Contract.Requires(name != null); Contract.Requires(module != null); Contract.Requires(baseType != null); BaseType = baseType; } - public NewtypeDecl(IToken tok, string name, ModuleDefinition module, BoundVar bv, Expression constraint, Attributes attributes) - : base(tok, name, module, new List(), attributes) { + public NewtypeDecl(IToken tok, string name, ModuleDefinition module, BoundVar bv, Expression constraint, Attributes attributes, NewtypeDecl clonedFrom = null) + : base(tok, name, module, new List(), attributes, clonedFrom) { Contract.Requires(tok != null); Contract.Requires(name != null); Contract.Requires(module != null); @@ -2618,6 +2644,12 @@ namespace Microsoft.Dafny { get { throw new cce.UnreachableException(); } // see comment above about ICallable.Decreases set { throw new cce.UnreachableException(); } // see comment above about ICallable.Decreases } + + public new NewtypeDecl ClonedFrom { + get { + return (NewtypeDecl)base.ClonedFrom; + } + } } public class TypeSynonymDecl : TopLevelDecl, RedirectingTypeDecl diff --git a/Source/Dafny/DafnyOptions.cs b/Source/Dafny/DafnyOptions.cs index a3b26f2a..978525f3 100644 --- a/Source/Dafny/DafnyOptions.cs +++ b/Source/Dafny/DafnyOptions.cs @@ -15,7 +15,11 @@ namespace Microsoft.Dafny public override string VersionNumber { get { - return System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileVersion; + return System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileVersion +#if ENABLE_IRONDAFNY + + "[IronDafny]" +#endif + ; } } public override string VersionSuffix { @@ -59,6 +63,13 @@ namespace Microsoft.Dafny public bool PrintStats = false; public bool PrintFunctionCallGraph = false; public bool WarnShadowing = false; + public bool IronDafny = +#if ENABLE_IRONDAFNY + true +#else + false +#endif + ; protected override bool ParseOption(string name, Bpl.CommandLineOptionEngine.CommandLineParseState ps) { var args = ps.args; // convenient synonym @@ -201,6 +212,16 @@ namespace Microsoft.Dafny return true; } + case "noIronDafny": { + IronDafny = false; + return true; + } + + case "ironDafny": { + IronDafny = true; + return true; + } + default: break; } @@ -301,6 +322,10 @@ namespace Microsoft.Dafny /funcCallGraph Print out the function call graph. Format is: func,mod=callee* /warnShadowing Emits a warning if the name of a declared variable caused another variable to be shadowed + /ironDafny Enable experimental features needed to support Ironclad/Ironfleet. Use of + these features may cause your code to become incompatible with future + releases of Dafny. + /noIronDafny Disable Ironclad/Ironfleet features, if enabled by default. "); base.Usage(); // also print the Boogie options } diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs index a90e650a..0c88cc22 100644 --- a/Source/Dafny/Parser.cs +++ b/Source/Dafny/Parser.cs @@ -576,7 +576,7 @@ bool IsType(ref IToken pt) { break; } case 37: case 38: case 39: case 40: case 41: case 72: case 73: case 74: case 77: case 83: case 84: case 85: case 86: { - ClassMemberDecl(membersDefaultClass, false, !DafnyOptions.O.AllowGlobals); + ClassMemberDecl(membersDefaultClass, false, !DafnyOptions.O.AllowGlobals, false); break; } } @@ -672,7 +672,7 @@ bool IsType(ref IToken pt) { break; } case 37: case 38: case 39: case 40: case 41: case 72: case 73: case 74: case 77: case 83: case 84: case 85: case 86: { - ClassMemberDecl(namedModuleDefaultClassMembers, false, !DafnyOptions.O.AllowGlobals); + ClassMemberDecl(namedModuleDefaultClassMembers, false, !DafnyOptions.O.AllowGlobals, true); break; } } @@ -750,7 +750,7 @@ bool IsType(ref IToken pt) { Expect(45); bodyStart = t; while (StartOf(2)) { - ClassMemberDecl(members, true, false); + ClassMemberDecl(members, true, false, false); } Expect(46); c = new ClassDecl(id, id.val, module, typeArgs, members, attrs, traits); @@ -961,7 +961,7 @@ bool IsType(ref IToken pt) { Expect(45); bodyStart = t; while (StartOf(2)) { - ClassMemberDecl(members, true, false); + ClassMemberDecl(members, true, false, false); } Expect(46); trait = new TraitDecl(id, id.val, module, typeArgs, members, attrs); @@ -970,7 +970,7 @@ bool IsType(ref IToken pt) { } - void ClassMemberDecl(List mm, bool allowConstructors, bool moduleLevelDecl) { + void ClassMemberDecl(List mm, bool allowConstructors, bool moduleLevelDecl, bool permitAbstractDecl) { Contract.Requires(cce.NonNullElements(mm)); Method/*!*/ m; Function/*!*/ f; @@ -1015,7 +1015,7 @@ bool IsType(ref IToken pt) { mmod.IsProtected = false; } - MethodDecl(mmod, allowConstructors, out m); + MethodDecl(mmod, allowConstructors, permitAbstractDecl, out m); mm.Add(m); } else SynErr(151); } @@ -1273,7 +1273,7 @@ bool IsType(ref IToken pt) { } - void MethodDecl(MemberModifiers mmod, bool allowConstructor, out Method/*!*/ m) { + void MethodDecl(MemberModifiers mmod, bool allowConstructor, bool permitAbstractDecl, out Method/*!*/ m) { Contract.Ensures(Contract.ValueAtReturn(out m) !=null); IToken/*!*/ id = Token.NoToken; bool hasName = false; IToken keywordToken; @@ -1393,7 +1393,7 @@ bool IsType(ref IToken pt) { if (la.kind == 45) { BlockStmt(out body, out bodyStart, out bodyEnd); } - if (DafnyOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 && !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported") && !Attributes.Contains(attrs, "decl") && theVerifyThisFile) { + if (!permitAbstractDecl && DafnyOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 && !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported") && !Attributes.Contains(attrs, "decl") && theVerifyThisFile) { SemErr(t, "a method with an ensures clause must have a body, unless given the :axiom attribute"); } diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 3e308e76..460859db 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -919,20 +919,27 @@ namespace Microsoft.Dafny sig.IsGhost = moduleDef.IsAbstract; List declarations = moduleDef.TopLevelDecls; - if (useImports) { - // First go through and add anything from the opened imports - foreach (var im in declarations) { - if (im is ModuleDecl && ((ModuleDecl)im).Opened) { - var s = GetSignature(((ModuleDecl)im).Signature); + // First go through and add anything from the opened imports + foreach (var im in declarations) { + if (im is ModuleDecl && ((ModuleDecl)im).Opened) { + var s = GetSignature(((ModuleDecl)im).Signature); + + if (useImports || DafnyOptions.O.IronDafny) { // classes: foreach (var kv in s.TopLevels) { - TopLevelDecl d; - if (sig.TopLevels.TryGetValue(kv.Key, out d)) { - sig.TopLevels[kv.Key] = AmbiguousTopLevelDecl.Create(moduleDef, d, kv.Value); - } else { - sig.TopLevels.Add(kv.Key, kv.Value); + // IronDafny: we need to pull the members of the opened module's _default class in so that they can be merged. + if (useImports || string.Equals(kv.Key, "_default", StringComparison.InvariantCulture)) { + TopLevelDecl d; + if (sig.TopLevels.TryGetValue(kv.Key, out d)) { + sig.TopLevels[kv.Key] = AmbiguousTopLevelDecl.Create(moduleDef, d, kv.Value); + } else { + sig.TopLevels.Add(kv.Key, kv.Value); + } } } + } + + if (useImports) { // constructors: foreach (var kv in s.Ctors) { Tuple pair; @@ -948,6 +955,9 @@ namespace Microsoft.Dafny sig.Ctors.Add(kv.Key, kv.Value); } } + } + + if (useImports || DafnyOptions.O.IronDafny) { // static members: foreach (var kv in s.StaticMembers) { MemberDecl md; @@ -957,7 +967,7 @@ namespace Microsoft.Dafny // add new sig.StaticMembers.Add(kv.Key, kv.Value); } - } + } } } } @@ -4406,8 +4416,23 @@ namespace Microsoft.Dafny return false; } var aa = (UserDefinedType)a; + var rca = aa.ResolvedClass; var bb = (UserDefinedType)b; - if (aa.ResolvedClass != null && aa.ResolvedClass == bb.ResolvedClass) { + 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; + } + while (rcb != null && rcb.Module.IsAbstract && rcb.ClonedFrom != null) + { + rcb = (TopLevelDecl)rcb.ClonedFrom; + } + } + if (rca != null && rca == rcb) { // these are both resolved class/datatype types Contract.Assert(aa.TypeArgs.Count == bb.TypeArgs.Count); bool successSoFar = true; @@ -4416,12 +4441,12 @@ namespace Microsoft.Dafny } return successSoFar; } - else if ((bb.ResolvedClass is TraitDecl) && (aa.ResolvedClass is TraitDecl)) { - return ((TraitDecl)bb.ResolvedClass).FullCompileName == ((TraitDecl)aa.ResolvedClass).FullCompileName; - } else if ((bb.ResolvedClass is ClassDecl) && (aa.ResolvedClass is TraitDecl)) { - return ((ClassDecl)bb.ResolvedClass).TraitsObj.Any(tr => tr.FullCompileName == ((TraitDecl)aa.ResolvedClass).FullCompileName); - } else if ((aa.ResolvedClass is ClassDecl) && (bb.ResolvedClass is TraitDecl)) { - return ((ClassDecl)aa.ResolvedClass).TraitsObj.Any(tr => tr.FullCompileName == ((TraitDecl)bb.ResolvedClass).FullCompileName); + else if ((rcb is TraitDecl) && (rca is TraitDecl)) { + return ((TraitDecl)rcb).FullCompileName == ((TraitDecl)rca).FullCompileName; + } else if ((rcb is ClassDecl) && (rca is TraitDecl)) { + return ((ClassDecl)rcb).TraitsObj.Any(tr => tr.FullCompileName == ((TraitDecl)rca).FullCompileName); + } else if ((rca is ClassDecl) && (rcb is TraitDecl)) { + return ((ClassDecl)rca).TraitsObj.Any(tr => tr.FullCompileName == ((TraitDecl)rcb).FullCompileName); } else if (aa.ResolvedParam != null && aa.ResolvedParam == bb.ResolvedParam) { // type parameters if (aa.TypeArgs.Count != bb.TypeArgs.Count) { diff --git a/Test/irondafny0/FIFO.dfy b/Test/irondafny0/FIFO.dfy index 1256b55d..ded8f567 100644 --- a/Test/irondafny0/FIFO.dfy +++ b/Test/irondafny0/FIFO.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %dafny /ironDafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" // RUN: %diff "%s.expect" "%t" include "Queue.dfyi" diff --git a/Test/irondafny0/LIFO.dfy b/Test/irondafny0/LIFO.dfy index bac08fba..8c0a08e8 100644 --- a/Test/irondafny0/LIFO.dfy +++ b/Test/irondafny0/LIFO.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %dafny /ironDafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" // RUN: %diff "%s.expect" "%t" include "Queue.dfyi" diff --git a/Test/irondafny0/opened_workaround.dfy b/Test/irondafny0/opened_workaround.dfy new file mode 100644 index 00000000..7464c346 --- /dev/null +++ b/Test/irondafny0/opened_workaround.dfy @@ -0,0 +1,21 @@ +// RUN: %dafny /ironDafny /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +module A { + + predicate P() + + class C + { + static method{:axiom} M() + ensures P(); + } +} + +abstract module B { + import opened A +} + +abstract module C { + import Bee as B // Works +} diff --git a/Test/irondafny0/opened_workaround.dfy.expect b/Test/irondafny0/opened_workaround.dfy.expect new file mode 100644 index 00000000..0be94b4c --- /dev/null +++ b/Test/irondafny0/opened_workaround.dfy.expect @@ -0,0 +1,3 @@ + +Dafny program verifier finished with 3 verified, 0 errors +Compilation error: Function _0_A_Compile._default.P has no body diff --git a/Test/irondafny0/xrefine0.dfy b/Test/irondafny0/xrefine0.dfy index 35993ce8..b849111c 100644 --- a/Test/irondafny0/xrefine0.dfy +++ b/Test/irondafny0/xrefine0.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %dafny /ironDafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" // RUN: %diff "%s.expect" "%t" abstract module Delicious {} diff --git a/Test/irondafny0/xrefine1.dfy b/Test/irondafny0/xrefine1.dfy index 10d25f53..4b085e6b 100644 --- a/Test/irondafny0/xrefine1.dfy +++ b/Test/irondafny0/xrefine1.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %dafny /ironDafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" // RUN: %diff "%s.expect" "%t" abstract module ProtocolSpec { diff --git a/Test/irondafny0/xrefine2.dfy b/Test/irondafny0/xrefine2.dfy index 7578b424..1de4e201 100644 --- a/Test/irondafny0/xrefine2.dfy +++ b/Test/irondafny0/xrefine2.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %dafny /ironDafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" // RUN: %diff "%s.expect" "%t" abstract module ProtocolSpec { diff --git a/Test/irondafny0/xrefine3.dfy b/Test/irondafny0/xrefine3.dfy index 69c5bc27..44add7cc 100644 --- a/Test/irondafny0/xrefine3.dfy +++ b/Test/irondafny0/xrefine3.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %dafny /ironDafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" // RUN: %diff "%s.expect" "%t" abstract module AlphaSpec { -- cgit v1.2.3 From dfef591e33e79f03877c65e584521068a6114d50 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Mon, 13 Jul 2015 21:02:50 +0100 Subject: Make attributes enumerable. Use an extension method to properly deal with null attributes --- Source/Dafny/DafnyAst.cs | 49 +++++++++++++++++++++++----------------------- Source/Dafny/Resolver.cs | 6 +++--- Source/Dafny/Rewriter.cs | 12 ++++++------ Source/Dafny/Translator.cs | 30 +++++++++++++--------------- 4 files changed, 48 insertions(+), 49 deletions(-) diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index eac64f80..af51d650 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -10,6 +10,7 @@ using System.Diagnostics.Contracts; using System.Numerics; using System.Linq; using Microsoft.Boogie; +using System.Diagnostics; namespace Microsoft.Dafny { public class Program { @@ -232,21 +233,12 @@ namespace Microsoft.Dafny { } public static IEnumerable SubExpressions(Attributes attrs) { - for (; attrs != null; attrs = attrs.Prev) { - foreach (var arg in attrs.Args) { - yield return arg; - } - } + return attrs.AsEnumerable().SelectMany(aa => attrs.Args); } public static bool Contains(Attributes attrs, string nm) { Contract.Requires(nm != null); - for (; attrs != null; attrs = attrs.Prev) { - if (attrs.Name == nm) { - return true; - } - } - return false; + return attrs.AsEnumerable().Any(aa => aa.Name == nm); } /// @@ -258,10 +250,10 @@ namespace Microsoft.Dafny { [Pure] public static bool ContainsBool(Attributes attrs, string nm, ref bool value) { Contract.Requires(nm != null); - for (; attrs != null; attrs = attrs.Prev) { - if (attrs.Name == nm) { - if (attrs.Args.Count == 1) { - var arg = attrs.Args[0] as LiteralExpr; + foreach (var attr in attrs.AsEnumerable()) { + if (attr.Name == nm) { + if (attr.Args.Count == 1) { + var arg = attr.Args[0] as LiteralExpr; if (arg != null && arg.Value is bool) { value = (bool)arg.Value; } @@ -306,9 +298,9 @@ namespace Microsoft.Dafny { /// public static List FindExpressions(Attributes attrs, string nm) { Contract.Requires(nm != null); - for (; attrs != null; attrs = attrs.Prev) { - if (attrs.Name == nm) { - return attrs.Args; + foreach (var attr in attrs.AsEnumerable()) { + if (attr.Name == nm) { + return attr.Args; } } return null; @@ -366,6 +358,15 @@ namespace Microsoft.Dafny { } } + internal static class AttributesExtensions { + public static IEnumerable AsEnumerable(this Attributes attr) { + while (attr != null) { + yield return attr; + attr = attr.Prev; + } + } + } + // ------------------------------------------------------------------------------------------------------ public abstract class Type { @@ -3042,7 +3043,7 @@ namespace Microsoft.Dafny { prefixPredCall.TypeArgumentSubstitutions = new Dictionary(); var old_to_new = new Dictionary(); for (int i = 0; i < this.TypeArgs.Count; i++) { - old_to_new[this.TypeArgs[i]] = this.PrefixPredicate.TypeArgs[i]; + old_to_new[this.TypeArgs[i]] = this.PrefixPredicate.TypeArgs[i]; } foreach (var p in fexp.TypeArgumentSubstitutions) { prefixPredCall.TypeArgumentSubstitutions[old_to_new[p.Key]] = p.Value; @@ -4950,7 +4951,7 @@ namespace Microsoft.Dafny { } // ------------------------------------------------------------------------------------------------------ - + [DebuggerDisplay("{Printer.ExprToString(this)}")] public abstract class Expression { public readonly IToken tok; @@ -5568,8 +5569,8 @@ namespace Microsoft.Dafny { Contract.Invariant(cce.NonNullElements(Arguments)); Contract.Invariant(cce.NonNullElements(InferredTypeArgs)); Contract.Invariant( - Ctor == null || - InferredTypeArgs.Count == Ctor.EnclosingDatatype.TypeArgs.Count); + Ctor == null || + InferredTypeArgs.Count == Ctor.EnclosingDatatype.TypeArgs.Count); } public DatatypeValue(IToken tok, string datatypeName, string memberName, [Captured] List arguments) @@ -6003,10 +6004,10 @@ namespace Microsoft.Dafny { Function == null || TypeArgumentSubstitutions == null || Contract.ForAll( Function.TypeArgs, - a => TypeArgumentSubstitutions.ContainsKey(a)) && + a => TypeArgumentSubstitutions.ContainsKey(a)) && Contract.ForAll( TypeArgumentSubstitutions.Keys, - a => Function.TypeArgs.Contains(a) || Function.EnclosingClass.TypeArgs.Contains(a))); + a => Function.TypeArgs.Contains(a) || Function.EnclosingClass.TypeArgs.Contains(a))); } public Function Function; // filled in by resolution diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 5e08a38a..c5535808 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -3429,9 +3429,9 @@ namespace Microsoft.Dafny Contract.Requires(opts != null); Contract.Requires(opts.DontCareAboutCompilation); // attributes are never compiled // order does not matter much for resolution, so resolve them in reverse order - for (; attrs != null; attrs = attrs.Prev) { - if (attrs.Args != null) { - ResolveAttributeArgs(attrs.Args, opts, true); + foreach (var attr in attrs.AsEnumerable()) { + if (attr.Args != null) { + ResolveAttributeArgs(attr.Args, opts, true); } } } diff --git a/Source/Dafny/Rewriter.cs b/Source/Dafny/Rewriter.cs index 1dd985cb..c0a25a82 100644 --- a/Source/Dafny/Rewriter.cs +++ b/Source/Dafny/Rewriter.cs @@ -1087,15 +1087,15 @@ namespace Microsoft.Dafny // Check for the timeLimitMultiplier attribute if (Attributes.Contains(member.Attributes, "timeLimitMultiplier")) { Attributes attrs = member.Attributes; - for (; attrs != null; attrs = attrs.Prev) { - if (attrs.Name == "timeLimitMultiplier") { - if (attrs.Args.Count == 1 && attrs.Args[0] is LiteralExpr) { - var arg = attrs.Args[0] as LiteralExpr; + foreach (var attr in attrs.AsEnumerable()) { + if (attr.Name == "timeLimitMultiplier") { + if (attr.Args.Count == 1 && attr.Args[0] is LiteralExpr) { + var arg = attr.Args[0] as LiteralExpr; System.Numerics.BigInteger value = (System.Numerics.BigInteger)arg.Value; if (value.Sign > 0) { int current_limit = DafnyOptions.O.ProverKillTime > 0 ? DafnyOptions.O.ProverKillTime : 10; // Default to 10 seconds - attrs.Args[0] = new LiteralExpr(attrs.Args[0].tok, value * current_limit); - attrs.Name = "timeLimit"; + attr.Args[0] = new LiteralExpr(attr.Args[0].tok, value * current_limit); + attr.Name = "timeLimit"; } } } diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 26412384..d375c2bb 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -11774,13 +11774,13 @@ namespace Microsoft.Dafny { public Bpl.QKeyValue TrAttributes(Attributes attrs, string skipThisAttribute) { Bpl.QKeyValue kv = null; - for ( ; attrs != null; attrs = attrs.Prev) { - if (attrs.Name == skipThisAttribute - || attrs.Name == "axiom") { // Dafny's axiom attribute clashes with Boogie's axiom keyword + foreach (var attr in attrs.AsEnumerable()) { + if (attr.Name == skipThisAttribute + || attr.Name == "axiom") { // Dafny's axiom attribute clashes with Boogie's axiom keyword continue; } List parms = new List(); - foreach (var arg in attrs.Args) { + foreach (var arg in attr.Args) { var s = arg.AsStringLiteral(); if (s != null) { // pass string literals down to Boogie as string literals, not as their expression translation @@ -11791,7 +11791,7 @@ namespace Microsoft.Dafny { parms.Add(e); } } - kv = new Bpl.QKeyValue(Token.NoToken, attrs.Name, parms, kv); + kv = new Bpl.QKeyValue(Token.NoToken, attr.Name, parms, kv); } return kv; } @@ -12470,18 +12470,16 @@ namespace Microsoft.Dafny { Bpl.Trigger TrTrigger(ExpressionTranslator etran, Attributes attribs, IToken tok, Dictionary substMap = null) { Bpl.Trigger tr = null; - for (Attributes aa = attribs; aa != null; aa = aa.Prev) { - if (aa.Name == "trigger") { - List tt = new List(); - foreach (var arg in aa.Args) { - if (substMap == null) { - tt.Add(etran.TrExpr(arg)); - } else { - tt.Add(etran.TrExpr(Substitute(arg, null, substMap))); - } + foreach (var trigger in attribs.AsEnumerable().Where(aa => aa.Name == "trigger").Select(aa => aa.Args)) { + List tt = new List(); + foreach (var arg in trigger) { + if (substMap == null) { + tt.Add(etran.TrExpr(arg)); + } else { + tt.Add(etran.TrExpr(Substitute(arg, null, substMap))); } - tr = new Bpl.Trigger(tok, true, tt, tr); } + tr = new Bpl.Trigger(tok, true, tt, tr); } return tr; } @@ -12920,7 +12918,7 @@ namespace Microsoft.Dafny { return new List(); // don't apply induction } - for (var a = attributes; a != null; a = a.Prev) { + foreach (var a in attributes.AsEnumerable()) { if (a.Name == "induction") { // Here are the supported forms of the :induction attribute. // :induction -- apply induction to all bound variables -- cgit v1.2.3 From e90a947a54044487e8ccf984033b1113eac0d49e Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Mon, 13 Jul 2015 11:50:06 -0700 Subject: Initial import of the TriggerGenerator code. Includes trigger generation, basic cycle detection, and basic equality comparison for Dafny expressions. --- Source/Dafny/TriggerGenerator.cs | 971 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 971 insertions(+) create mode 100644 Source/Dafny/TriggerGenerator.cs diff --git a/Source/Dafny/TriggerGenerator.cs b/Source/Dafny/TriggerGenerator.cs new file mode 100644 index 00000000..1783a00c --- /dev/null +++ b/Source/Dafny/TriggerGenerator.cs @@ -0,0 +1,971 @@ +#define DEBUG_AUTO_TRIGGERS +#define THROW_UNSUPPORTED_COMPARISONS + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Boogie; +using System.Diagnostics.Contracts; +using System.Text; +using System.Diagnostics; + +//FIXME No LitInts in triggers +//FIXME Generated triggers should be _triggers, and if a predicate is called with something that can't be a trigger head we shouldn't inline it (see .\Test\dafny2\SnapshotableTrees.dfy) +//FIXME: When scoring, do not consider old(x) to be higher than x. + +/* High level note: There are really two processes going on here. One is finding quantifiers; + * the other is walking the subtree corresponding to each quantifier, and finding trigger candidates. + * These two processes are interleaved, because we can look for trigger candidates as we look for + * quantifiers. Since the visitor starts from the bottom of the tree, the recursive annotation + * procedure never causes deep recursion; every call it makes hits the cache that has been built + * during the visiting of lower level nodes. + * + * Note also that it wouldn't be enough to just use the recursive procedure: it doesn't visit + * statements, for example. + */ + +namespace Microsoft.Dafny { + class TriggerCandidate { // TODO Hashing is broken (duplicates can pop up) + internal Expression Expr; + internal ISet Variables; + internal Expression[] PotentialMatchingLoops; + + public override string ToString() { + return Printer.ExprToString(Expr); + } + } + + struct MultiTriggerCandidate { + internal ISet Candidates; + internal double Score; + + public override string ToString() { + return String.Format("[{0:G2}] {1}", Score, String.Join(", ", Candidates)); + } + } + + class TriggerAnnotation { + internal bool IsTriggerKiller; + internal ISet Variables; + internal readonly HashSet PrivateCandidates; + internal readonly HashSet ExportedCandidates; + + internal TriggerAnnotation(bool IsTriggerKiller, IEnumerable Variables, + IEnumerable AllCandidates, IEnumerable PrivateCandidates = null) { + this.IsTriggerKiller = IsTriggerKiller; + this.Variables = new HashSet(Variables); + + this.ExportedCandidates = new HashSet(AllCandidates == null ? Enumerable.Empty() : AllCandidates); + this.PrivateCandidates = new HashSet(PrivateCandidates == null ? Enumerable.Empty() : PrivateCandidates); + this.ExportedCandidates.ExceptWith(this.PrivateCandidates); + } + + public override string ToString() { + StringBuilder sb = new StringBuilder(); + string indent = " {0}", nindent = "\n - {0}", subindent = "\n * {0}"; + + sb.AppendFormat(indent, IsTriggerKiller); + + sb.AppendFormat(nindent, "Variables:"); + foreach (var bv in Variables) { + sb.AppendFormat(subindent, bv.Name); + } + + sb.AppendFormat(nindent, "Exported candidates:"); + foreach (var candidate in ExportedCandidates) { + sb.AppendFormat(subindent, candidate); + } + + if (PrivateCandidates.Any()) { + sb.AppendFormat(nindent, "Private candidates:"); + foreach (var candidate in PrivateCandidates) { + sb.AppendFormat(subindent, candidate); + } + } + + return sb.ToString(); + } + } + + public class TriggerGenerator : BottomUpVisitor { + List quantifiers; + Dictionary annotations; + + Resolver Resolver; + + private TriggerGenerator(Resolver resolver) { + Contract.Requires(Resolver != null); + this.quantifiers = new List(); + this.annotations = new Dictionary(); + this.Resolver = resolver; + } + + private ISet MergeAlterFirst(ISet a, ISet b) { + Contract.Requires(a != null); + Contract.Requires(b != null); + a.UnionWith(b); + return a; + } + + private ISet WithOneExtraElement(ISet set, T elem) { + Contract.Requires(set != null); + set.Add(elem); + return set; + } + + private T ReduceAnnotatedSubExpressions(Expression expr, T seed, Func map, Func reduce) { + return expr.SubExpressions.Select(e => map(Annotate(e))) + .Aggregate(seed, (acc, e) => reduce(acc, e)); + } + + private ISet CollectExportedCandidates(Expression expr) { + return ReduceAnnotatedSubExpressions>(expr, new HashSet(), a => a.ExportedCandidates, MergeAlterFirst); + } + + private ISet CollectVariables(Expression expr) { + return ReduceAnnotatedSubExpressions(expr, new HashSet(), a => a.Variables, MergeAlterFirst); + } + + private bool CollectIsKiller(Expression expr) { + return ReduceAnnotatedSubExpressions(expr, false, a => a.IsTriggerKiller, (a, b) => a || b); + } + + private IEnumerable OnlyPrivateCandidates(ISet candidates, IEnumerable privateVars) { + return candidates.Where(c => privateVars.Intersect(c.Variables).Any()); //TODO Check perf + } + + private TriggerAnnotation Annotate(Expression expr) { + TriggerAnnotation cached; + if (annotations.TryGetValue(expr, out cached)) { + return cached; + } + + expr.SubExpressions.Iter(e => Annotate(e)); //NOTE: These values are all cached + + TriggerAnnotation annotation; // TODO: Using ApplySuffix fixes the unresolved members problem in GenericSort + if (expr is FunctionCallExpr || expr is SeqSelectExpr || expr is MemberSelectExpr || expr is OldExpr || + (expr is UnaryOpExpr && (((UnaryOpExpr)expr).Op == UnaryOpExpr.Opcode.Cardinality)) || // FIXME || ((UnaryOpExpr)expr).Op == UnaryOpExpr.Opcode.Fresh oesn'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) { + annotation = AnnotateQuantifier((QuantifierExpr)expr); + } else if (expr is LetExpr) { + annotation = AnnotateLetExpr((LetExpr)expr); + } else if (expr is IdentifierExpr) { + annotation = AnnotateIdentifier((IdentifierExpr)expr); + } else if (expr is ApplySuffix) { + annotation = AnnotateApplySuffix((ApplySuffix)expr); + } else if (expr is ConcreteSyntaxExpression || + expr is LiteralExpr || + expr is OldExpr || + expr is ThisExpr || + expr is DatatypeValue) { + annotation = AnnotateOther(expr, false); + } else { + annotation = AnnotateOther(expr, true); + } + + DebugTriggers("{0} ({1})\n{2}", Printer.ExprToString(expr), expr.GetType(), annotation); + annotations[expr] = annotation; + return annotation; + } + + [Conditional("DEBUG_AUTO_TRIGGERS")] + private static void DebugTriggers(string format, params object[] more) { + Console.Error.WriteLine(format, more); + } + + private BinaryExpr.ResolvedOpcode RemoveNotInBinaryExprIn(BinaryExpr.ResolvedOpcode opcode) { + switch(opcode) { + case BinaryExpr.ResolvedOpcode.NotInMap: + return BinaryExpr.ResolvedOpcode.InMap; + case BinaryExpr.ResolvedOpcode.NotInSet: + return BinaryExpr.ResolvedOpcode.InSet; + case BinaryExpr.ResolvedOpcode.NotInSeq: + return BinaryExpr.ResolvedOpcode.InSeq; + case BinaryExpr.ResolvedOpcode.NotInMultiSet: + return BinaryExpr.ResolvedOpcode.InMultiSet; + } + + Contract.Assert(false); + throw new ArgumentException(); + } + + private Expression CleanupExpr(Expression expr, out bool isKiller) { + isKiller = false; + + if (!(expr is BinaryExpr)) { + return expr; + } + + var bexpr = expr as BinaryExpr; + + BinaryExpr new_expr = bexpr; + if (bexpr.Op == BinaryExpr.Opcode.NotIn) { + new_expr = new BinaryExpr(bexpr.tok, BinaryExpr.Opcode.In, bexpr.E0, bexpr.E1); + new_expr.ResolvedOp = RemoveNotInBinaryExprIn(bexpr.ResolvedOp); + new_expr.Type = bexpr.Type; + } + + Expression returned_expr = new_expr; + if (new_expr.ResolvedOp == BinaryExpr.ResolvedOpcode.InMultiSet) { + returned_expr = new SeqSelectExpr(new_expr.tok, true, new_expr.E1, new_expr.E0, null); + returned_expr.Type = bexpr.Type; + isKiller = true; // [a in s] becomes [s[a] > 0], which is a trigger killer + } + + return returned_expr; + } + + private TriggerAnnotation AnnotatePotentialCandidate(Expression expr) { + bool expr_is_killer = false; + var new_expr = CleanupExpr(expr, out expr_is_killer); + var new_candidate = new TriggerCandidate { Expr = new_expr, Variables = CollectVariables(expr) }; + + ISet collected_candidates = CollectExportedCandidates(expr); + var children_contain_killers = CollectIsKiller(expr); + + if (!children_contain_killers) { + // Add only if the children are not killers; the head has been cleaned up into non-killer form + collected_candidates = WithOneExtraElement(collected_candidates, new_candidate); + } + + // This new node is a killer if its children were killers, or if it's non-cleaned-up head is a killer + return new TriggerAnnotation(children_contain_killers || expr_is_killer, new_candidate.Variables, collected_candidates); + } + + private TriggerAnnotation AnnotateApplySuffix(ApplySuffix expr) { + // This is a bit tricky. A funcall node is generally meaningful as a trigger candidate, + // but when it's part of an ApplySuffix the function call itself may not resolve properly + // when the second round of resolving is done after modules are duplicated. + // Thus first we annotate expr and create a trigger candidate, and then we remove the + // candidate matching its direct subexpression if needed. Not that function calls are not the + // only possible child here; there can be DatatypeValue nodes, for example (see vstte2012/Combinators.dfy). + var annotation = AnnotatePotentialCandidate(expr); + annotation.ExportedCandidates.RemoveWhere(candidate => expr.SubExpressions.Contains(candidate.Expr)); + return annotation; + } + + private TriggerAnnotation AnnotateQuantifierOrLetExpr(Expression expr, IEnumerable boundVars) { + var candidates = CollectExportedCandidates(expr); + return new TriggerAnnotation(true, CollectVariables(expr), candidates, OnlyPrivateCandidates(candidates, boundVars)); + } + + private TriggerAnnotation AnnotateQuantifier(QuantifierExpr expr) { + quantifiers.Add(expr); + return AnnotateQuantifierOrLetExpr(expr, expr.BoundVars); + } + + private TriggerAnnotation AnnotateLetExpr(LetExpr expr) { + return AnnotateQuantifierOrLetExpr(expr, expr.BoundVars); + } + + private TriggerAnnotation AnnotateIdentifier(IdentifierExpr expr) { + return new TriggerAnnotation(false, Enumerable.Repeat(expr.Var, 1), null); + } + + private TriggerAnnotation AnnotateOther(Expression expr, bool isTriggerKiller) { + return new TriggerAnnotation(isTriggerKiller || CollectIsKiller(expr), CollectVariables(expr), CollectExportedCandidates(expr)); + } + + private static List CopyAndAdd(List seq, T elem) { + var copy = new List(seq); + copy.Add(elem); + return copy; + } + + private static IEnumerable> AllSubsets(IList source, int offset) { + if (offset >= source.Count) { + yield return new List(); + yield break; + } + + foreach (var subset in AllSubsets(source, offset + 1)) { + yield return CopyAndAdd(subset, source[offset]); + yield return new List(subset); + } + } + + private static IEnumerable> AllSubsets(IEnumerable source) { + List all = new List(source); + foreach (var subset in AllSubsets(all, 0)) { + yield return new HashSet(subset); + } + } + + private static bool MentionsAll(ISet multiCandidate, List vars) { + return vars.All(x => multiCandidate.Any(candidate => candidate.Variables.Contains(x))); //TODO Perfs? + } + + private static bool DefaultCandidateFilteringFunction(TriggerCandidate candidate, QuantifierExpr quantifier) { + //FIXME this will miss rewritten expressions (CleanupExpr) + //FIXME does PotentialMatchingLoops really need to be a field? + + return true; + + + candidate.PotentialMatchingLoops = ExprExtensions.CouldCauseCycle(candidate.Expr, quantifier).ToArray(); + if (candidate.PotentialMatchingLoops.Any()) { + DebugTriggers("Trigger {0} for quantifier {1} could cause a matching loop, due to terms {2}.", + Printer.ExprToString(candidate.Expr), Printer.ExprToString(quantifier), + String.Join(", ", candidate.PotentialMatchingLoops.Select(e => Printer.ExprToString(e)))); + } + return !candidate.PotentialMatchingLoops.Any(); + } + + private static bool DefaultMultiCandidateFilteringFunction(ISet multiCandidate, QuantifierExpr quantifier) { + return MentionsAll(multiCandidate, quantifier.BoundVars); + } + + private static double DefaultMultiCandidateScoringFunction(ISet multi_candidates) { + return 1; + } + + private static IEnumerable DefaultMultiCandidateSelectionFunction(List multi_candidates) { + return multi_candidates; + } + + // CLEMENT: Make these customizable + internal Func CandidateFilteringFunction = DefaultCandidateFilteringFunction; + internal Func, QuantifierExpr, bool> MultiCandidateFilteringFunction = DefaultMultiCandidateFilteringFunction; + internal Func, double> MultiCandidateScoringFunction = DefaultMultiCandidateScoringFunction; + internal Func, IEnumerable> MultiCandidateSelectionFunction = DefaultMultiCandidateSelectionFunction; + + struct MultiCandidatesCollection { + internal ISet AllCandidates; + internal List SelectedCandidates; + internal List RejectedCandidates; + internal List> FilteredMultiCandidates; + internal List ScoredMultiCandidates; + internal List SelectedMultiCandidates; + + public MultiCandidatesCollection(QuantifierExpr quantifier, + TriggerAnnotation annotation, + Func CandidateFilteringFunction, + Func, QuantifierExpr, bool> MultiCandidateFilteringFunction, + Func, double> MultiCandidateScoringFunction, + Func, IEnumerable> MultiCandidateSelectionFunction) { + + Contract.Requires(annotation != null); + Contract.Requires(quantifier != null); + Contract.Requires(CandidateFilteringFunction != null); + Contract.Requires(MultiCandidateFilteringFunction != null); + Contract.Requires(MultiCandidateScoringFunction != null); + Contract.Requires(MultiCandidateSelectionFunction != null); + + AllCandidates = annotation.PrivateCandidates; + Partition(AllCandidates, x => CandidateFilteringFunction(x, quantifier), out SelectedCandidates, out RejectedCandidates); + FilteredMultiCandidates = AllSubsets(SelectedCandidates).Where(t => MultiCandidateFilteringFunction(t, quantifier)).ToList(); + ScoredMultiCandidates = FilteredMultiCandidates.Select(candidates => new MultiTriggerCandidate { Candidates = candidates, Score = MultiCandidateScoringFunction(candidates) }).ToList(); + SelectedMultiCandidates = MultiCandidateSelectionFunction(ScoredMultiCandidates).ToList(); + } + + private static void Partition(IEnumerable AllCandidates, Func CandidateFilteringFunction, out List SelectedCandidates, out List RejectedCandidates) { + SelectedCandidates = new List(); + RejectedCandidates = new List(); + foreach (var c in AllCandidates) { + (CandidateFilteringFunction(c) ? SelectedCandidates : RejectedCandidates).Add(c); + } + } + + internal string Warning() { + if (AllCandidates.Count == 0) { + return "No triggers found in the body of this quantifier."; + } else if (SelectedCandidates.Count == 0) { + return String.Format("No suitable triggers found. Candidate building blocks for a good trigger where [{0}], but no subset of these terms passed the initial selection stage.", String.Join(", ", SelectedCandidates)); + } else if (FilteredMultiCandidates.Count == 0) { + return String.Format("No suitable set of triggers found. Candidate building blocks for a good trigger where [{0}], but no subset of these terms passed the subset selection stage.", String.Join(", ", SelectedCandidates)); + } else if (SelectedMultiCandidates.Count == 0) { + return String.Format("No suitable set of triggers found. Candidates where [{0}], but none passed the final selection stage.", String.Join(", ", ScoredMultiCandidates)); + } else { + return null; + } + } + + private void WriteIndented(StringBuilder builder, string indent, IEnumerable elements) { + foreach (var element in elements) { + builder.Append(indent).AppendLine(element.ToString()); + } + } + + private void WriteListOfCandidates(StringBuilder builder, string indent, IEnumerable elements) { + if (elements.Any()) { + builder.AppendLine(); + WriteIndented(builder, indent, elements); + } else { + builder.AppendLine(" (None)"); + } + } + + public override string ToString() { + var repr = new StringBuilder(); + var indent = " "; + repr.Append(" All:"); + WriteListOfCandidates(repr, indent, AllCandidates); + repr.Append(" PreFilter:"); + WriteListOfCandidates(repr, indent, AllSubsets(AllCandidates).Select(c => String.Join(", ", c))); + repr.Append(" Filtered:"); + WriteListOfCandidates(repr, indent, FilteredMultiCandidates.Select(c => String.Join(", ", c))); + repr.Append(" Scored:"); + WriteListOfCandidates(repr, indent, ScoredMultiCandidates); + repr.Append(" Selected:"); + WriteListOfCandidates(repr, indent, SelectedMultiCandidates); + return repr.ToString(); + } + } + + private MultiCandidatesCollection PickMultiTriggers(QuantifierExpr quantifier) { + var annotation = Annotate(quantifier); + DebugTriggers("Quantifier {0}:\n{1}", Printer.ExprToString(quantifier), annotation); + return new MultiCandidatesCollection(quantifier, annotation, CandidateFilteringFunction, MultiCandidateFilteringFunction, MultiCandidateScoringFunction, MultiCandidateSelectionFunction); + } + + private void AddTrigger(QuantifierExpr quantifier) { + // This call is elided when triggers debugging is disabled. + DebugTriggers(" Final results:\n{0}", PickMultiTriggers(quantifier)); + + if (quantifier.Attributes.AsEnumerable().Any(aa => aa.Name == "trigger" || aa.Name == "no_trigger")) { + DebugTriggers("Not generating triggers for {0}", Printer.ExprToString(quantifier)); //FIXME: no_trigger is passed down to Boogie + return; + } + + var multi_candidates = PickMultiTriggers(quantifier); + foreach (var multi_candidate in multi_candidates.SelectedMultiCandidates) { //TODO: error message for when no triggers found + quantifier.Attributes = new Attributes("trigger", multi_candidate.Candidates.Select(t => t.Expr).ToList(), quantifier.Attributes); + } + + // FIXME: Cleanup + if (multi_candidates.RejectedCandidates.Any()) { + var tooltip = "Rejected: " + String.Join(Environment.NewLine + " ", multi_candidates.RejectedCandidates.Select( + candidate => "{:trigger " + Printer.ExprToString(candidate.Expr) + "} (could loop with " + Printer.ExprToString(candidate.PotentialMatchingLoops[0]) + ")")); + Resolver.ReportAdditionalInformation(quantifier.tok, tooltip, quantifier.tok.val.Length); //CLEMENT Check this + } + + if (multi_candidates.SelectedMultiCandidates.Any()) { + var tooltip = "Triggers: " + String.Join(Environment.NewLine + " ", multi_candidates.SelectedMultiCandidates.Select( + multi_candidate => "{:trigger " + String.Join(", ", multi_candidate.Candidates.Select(t => Printer.ExprToString(t.Expr))) + "}")); + Resolver.ReportAdditionalInformation(quantifier.tok, tooltip, quantifier.tok.val.Length); //CLEMENT Check this + } + + string warning = multi_candidates.Warning(); + if (warning != null) { + // FIXME reenable Resolver.Warning(quantifier.tok, warning); + } + } + + private void AddTriggers_Internal() { + foreach (var quantifier in quantifiers) { + AddTrigger(quantifier); + } + } + + private void AddTriggers_Internal(Expression root) { + Visit(root); + AddTriggers_Internal(); + } + + private void AddTriggers_Internal(Statement root) { + Visit(root); + AddTriggers_Internal(); + } + + internal static void AddTriggers(Expression root, Resolver resolver) { + if (root == null) + return; + + DebugTriggers("== From {0} visiting expr: {1}", new StackFrame(1).GetMethod().Name, Printer.ExprToString(root)); + TriggerGenerator generator = new TriggerGenerator(resolver); + generator.AddTriggers_Internal(root); + } + + internal static void AddTriggers(Statement root, Resolver resolver) { + if (root == null) + return; + + DebugTriggers("== From {0} visiting statement: {1}", new StackFrame(1).GetMethod().Name, Printer.StatementToString(root)); + TriggerGenerator generator = new TriggerGenerator(resolver); + generator.AddTriggers_Internal(root); + } + + internal static void AddTriggers(IEnumerable roots, Resolver resolver) { + DebugTriggers("== From {0} visiting expressions: {1}", new StackFrame(1).GetMethod().Name, + String.Join(", ", roots.Select(root => Printer.ExprToString(root)))); + foreach (var expr in roots) { + AddTriggers(expr, resolver); + } + } + + internal static void AddTriggers(IEnumerable roots, Resolver resolver) { + DebugTriggers("== From {0} visiting expressions: {1}", new StackFrame(1).GetMethod().Name, + String.Join(", ", roots.Select(root => Printer.ExprToString(root.E)))); + foreach (var expr in roots) { + AddTriggers(expr.E, resolver); + } + } + + protected override void VisitOneExpr(Expression expr) { + Annotate(expr); + } + } + + static class ExprExtensions { + static IEnumerable AllSubExpressions(this Expression expr, bool strict = false) { + foreach (var subexpr in expr.SubExpressions) { + foreach (var r_subexpr in AllSubExpressions(subexpr, false)) { + yield return r_subexpr; + } + yield return subexpr; + } + + if (expr is StmtExpr) { + foreach (var r_subexpr in AllSubExpressions(((StmtExpr)expr).S, false)) { + yield return r_subexpr; + } + } + + if (!strict) { + yield return expr; + } + } + + private static IEnumerable AllSubExpressions(this Statement stmt, bool strict = false) { + foreach (var subexpr in stmt.SubExpressions) { + foreach (var r_subexpr in AllSubExpressions(subexpr, false)) { + yield return r_subexpr; + } + yield return subexpr; + } + + foreach (var substmt in stmt.SubStatements) { + foreach (var r_subexpr in AllSubExpressions(substmt, false)) { + yield return r_subexpr; + } + } + } + + private static bool ExpressionEq(this Expression expr1, Expression expr2) { + expr1 = GetResolved(expr1); + expr2 = GetResolved(expr2); + + return ShallowEq_Top(expr1, expr2) && SameLists(expr1.SubExpressions, expr2.SubExpressions, (e1, e2) => ExpressionEq(e1, e2)); + } + + private static bool MatchesTrigger(this Expression expr, Expression trigger, ISet holes, Dictionary bindings) { + expr = GetResolved(expr); + trigger = GetResolved(trigger); + + if (trigger is IdentifierExpr) { + var var = ((IdentifierExpr)trigger).Var; + + if (holes.Contains(var)) { + Expression existing_binding = null; + if (bindings.TryGetValue(var, out existing_binding)) { + return ExpressionEq(expr, existing_binding); + } else { + bindings[var] = expr; + return true; + } + } + } + + return ShallowEq_Top(expr, trigger) && SameLists(expr.SubExpressions, trigger.SubExpressions, (e1, e2) => MatchesTrigger(e1, e2, holes, bindings)); + } + + private static bool MatchesTrigger(this Expression expr, Expression trigger, ISet holes) { + return expr.MatchesTrigger(trigger, holes, new Dictionary()); + } + + internal static IEnumerable CouldCauseCycle(Expression trigger, QuantifierExpr quantifier) { //FIXME Term or bound? + //FIXME could be optimized by looking at the bindings instead of doing full equality + return quantifier.Term.AllSubExpressions().Where(e => e.MatchesTrigger(trigger, new HashSet(quantifier.BoundVars)) && !e.ExpressionEq(trigger)); + } + + private static bool SameLists(IEnumerable list1, IEnumerable list2, Func comparer) { + var it1 = list1.GetEnumerator(); + var it2 = list2.GetEnumerator(); + bool it1_has, it2_has; + bool acc = true; + + do { + it1_has = it1.MoveNext(); + it2_has = it2.MoveNext(); + + if (it1_has == true && it2_has == true) { + acc = acc && comparer(it1.Current, it2.Current); + } + } while (it1_has && it2_has); + + return it1_has == it2_has && acc; + } + + private static bool SameNullity(T x1, T x2) where T : class { + return (x1 == null) == (x2 == null); + } + + private static bool ShallowSameAttributes(Attributes attributes1, Attributes attributes2) { + return SameLists(attributes1.AsEnumerable(), attributes2.AsEnumerable(), ShallowSameSingleAttribute); + } + + private static bool ShallowSameSingleAttribute(Attributes arg1, Attributes arg2) { + return arg1.Name == arg2.Name; + } + + private static bool SameBoundVar(IVariable arg1, IVariable arg2) { + return arg1 == arg2 || + (arg1.Name == arg2.Name && + arg1.CompileName == arg2.CompileName && + arg1.DisplayName == arg2.DisplayName && + arg1.UniqueName == arg2.UniqueName && + arg1.IsGhost == arg2.IsGhost && + arg1.IsMutable == arg2.IsMutable); //FIXME compare types? + } + + private static Expression GetResolved(Expression expr) { + if (expr is ConcreteSyntaxExpression) { + return ((ConcreteSyntaxExpression)expr).ResolvedExpression; + } + return expr; + } + + /// + /// Compares two abstract syntax expressions, looking only at their direct members. Subexpressions and substatements are not compared. + /// + /// + internal static bool ShallowEq_Top(Expression expr1, Expression expr2) { + Contract.Requires(expr1 != null); + Contract.Requires(expr2 != null); + + // We never compare concrete expressions + Contract.Requires(!(expr1 is ConcreteSyntaxExpression)); + Contract.Requires(!(expr2 is ConcreteSyntaxExpression)); + + // CPC: Hey future editor: the following block of code is auto-generated. Just add your own cases at the end. + // This could be a visitor pattern, except I need to visit a pair of nodes. + // It could also be implemented in each individual class. I'd have a slight preference for that. + // This really just wants to use double dispatch. + if (expr1 is UnboxingCastExpr && expr2 is UnboxingCastExpr) { + return ShallowEq((UnboxingCastExpr)expr1, (UnboxingCastExpr)expr2); + } else if (expr1 is BoxingCastExpr && expr2 is BoxingCastExpr) { + return ShallowEq((BoxingCastExpr)expr1, (BoxingCastExpr)expr2); + } else if (expr1 is MatchExpr && expr2 is MatchExpr) { + return ShallowEq((MatchExpr)expr1, (MatchExpr)expr2); + } else if (expr1 is ITEExpr && expr2 is ITEExpr) { + return ShallowEq((ITEExpr)expr1, (ITEExpr)expr2); + } else if (expr1 is StmtExpr && expr2 is StmtExpr) { + return ShallowEq((StmtExpr)expr1, (StmtExpr)expr2); + } else if (expr1 is WildcardExpr && expr2 is WildcardExpr) { + return ShallowEq((WildcardExpr)expr1, (WildcardExpr)expr2); + } else if (expr1 is ComprehensionExpr && expr2 is ComprehensionExpr) { + return ShallowEq((ComprehensionExpr)expr1, (ComprehensionExpr)expr2); + } else if (expr1 is NamedExpr && expr2 is NamedExpr) { + return ShallowEq((NamedExpr)expr1, (NamedExpr)expr2); + } else if (expr1 is LetExpr && expr2 is LetExpr) { + return ShallowEq((LetExpr)expr1, (LetExpr)expr2); + } else if (expr1 is TernaryExpr && expr2 is TernaryExpr) { + return ShallowEq((TernaryExpr)expr1, (TernaryExpr)expr2); + } else if (expr1 is BinaryExpr && expr2 is BinaryExpr) { + return ShallowEq((BinaryExpr)expr1, (BinaryExpr)expr2); + } else if (expr1 is UnaryExpr && expr2 is UnaryExpr) { + return ShallowEq((UnaryExpr)expr1, (UnaryExpr)expr2); + } else if (expr1 is MultiSetFormingExpr && expr2 is MultiSetFormingExpr) { + return ShallowEq((MultiSetFormingExpr)expr1, (MultiSetFormingExpr)expr2); + } else if (expr1 is OldExpr && expr2 is OldExpr) { + return ShallowEq((OldExpr)expr1, (OldExpr)expr2); + } else if (expr1 is FunctionCallExpr && expr2 is FunctionCallExpr) { + return ShallowEq((FunctionCallExpr)expr1, (FunctionCallExpr)expr2); + } else if (expr1 is ApplyExpr && expr2 is ApplyExpr) { + return ShallowEq((ApplyExpr)expr1, (ApplyExpr)expr2); + } else if (expr1 is SeqUpdateExpr && expr2 is SeqUpdateExpr) { + return ShallowEq((SeqUpdateExpr)expr1, (SeqUpdateExpr)expr2); + } else if (expr1 is MultiSelectExpr && expr2 is MultiSelectExpr) { + return ShallowEq((MultiSelectExpr)expr1, (MultiSelectExpr)expr2); + } else if (expr1 is SeqSelectExpr && expr2 is SeqSelectExpr) { + return ShallowEq((SeqSelectExpr)expr1, (SeqSelectExpr)expr2); + } else if (expr1 is MemberSelectExpr && expr2 is MemberSelectExpr) { + return ShallowEq((MemberSelectExpr)expr1, (MemberSelectExpr)expr2); + } else if (expr1 is MapDisplayExpr && expr2 is MapDisplayExpr) { + return ShallowEq((MapDisplayExpr)expr1, (MapDisplayExpr)expr2); + } else if (expr1 is DisplayExpression && expr2 is DisplayExpression) { + return ShallowEq((DisplayExpression)expr1, (DisplayExpression)expr2); + } else if (expr1 is IdentifierExpr && expr2 is IdentifierExpr) { + return ShallowEq((IdentifierExpr)expr1, (IdentifierExpr)expr2); + } else if (expr1 is ThisExpr && expr2 is ThisExpr) { + return ShallowEq((ThisExpr)expr1, (ThisExpr)expr2); + } else if (expr1 is DatatypeValue && expr2 is DatatypeValue) { + return ShallowEq((DatatypeValue)expr1, (DatatypeValue)expr2); + } else if (expr1 is LiteralExpr && expr2 is LiteralExpr) { + return ShallowEq((LiteralExpr)expr1, (LiteralExpr)expr2); + } else { + // If this assertion fail, then a new abstract AST node was probably introduced but not registered here. + Contract.Assert(expr1.GetType() != expr2.GetType()); + return false; + } + } + + private static bool ShallowEq(UnboxingCastExpr expr1, UnboxingCastExpr expr2) { + Contract.Requires(false); + throw new InvalidOperationException(); + } + + private static bool ShallowEq(BoxingCastExpr expr1, BoxingCastExpr expr2) { + return expr1.FromType == expr2.FromType && + expr1.ToType == expr2.ToType; + } + + private static bool ShallowEq(MatchExpr expr1, MatchExpr expr2) { + return true; + } + + private static bool ShallowEq(ITEExpr expr1, ITEExpr expr2) { + return true; + } + + private static bool ShallowEq(StmtExpr expr1, StmtExpr expr2) { +#if THROW_UNSUPPORTED_COMPARISONS + Contract.Assume(false); // This kind of expression never appears in a trigger + throw new NotImplementedException(); +#else + return expr1.S == expr2.S; +#endif + } + + private static bool ShallowEq(WildcardExpr expr1, WildcardExpr expr2) { + return true; + } + + private static bool ShallowEq(LambdaExpr expr1, LambdaExpr expr2) { +#if THROW_UNSUPPORTED_COMPARISONS + Contract.Assume(false); // This kind of expression never appears in a trigger + throw new NotImplementedException(); +#else + return expr1.OneShot == expr2.OneShot && + SameLists(expr1.Reads, expr2.Reads, SameFrameExpression); +#endif + } + + private static bool ShallowEq(MapComprehension expr1, MapComprehension expr2) { + return expr1.Finite == expr2.Finite; + } + + private static bool ShallowEq(SetComprehension expr1, SetComprehension expr2) { + return expr1.TermIsImplicit == expr2.TermIsImplicit && //TODO + expr1.Finite == expr2.Finite; + } + + private static bool ShallowEq(ExistsExpr expr1, ExistsExpr expr2) { + return true; + } + + private static bool ShallowEq(ForallExpr expr1, ForallExpr expr2) { + return true; + } + + private static bool ShallowEq(QuantifierExpr expr1, QuantifierExpr expr2) { //FIXME are these TypeArgs still useful? + if (expr1.TypeArgs.Count != expr2.TypeArgs.Count || + !SameNullity(expr1.Range, expr2.Range)) { + return false; + } + + if (expr1 is ExistsExpr && expr2 is ExistsExpr) { + return ShallowEq((ExistsExpr)expr1, (ExistsExpr)expr2); + } else if (expr1 is ForallExpr && expr2 is ForallExpr) { + return ShallowEq((ForallExpr)expr1, (ForallExpr)expr2); + } else { + return false; + } + } + + private static bool ShallowEq(ComprehensionExpr expr1, ComprehensionExpr expr2) { + if (!SameLists(expr1.BoundVars, expr2.BoundVars, SameBoundVar) || + !ShallowSameAttributes(expr1.Attributes, expr2.Attributes) || + // Filled in during resolution: !SameLists(expr1.Bounds, expr2.Bounds, ReferenceCompare) || + // !SameLists(expr1.MissingBounds, expr2.MissingBounds, SameBoundVar) || + !SameNullity(expr1.Range, expr2.Range)) { //TODO Check + return false; + } + + if (expr1 is LambdaExpr && expr2 is LambdaExpr) { + return ShallowEq((LambdaExpr)expr1, (LambdaExpr)expr2); + } else if (expr1 is MapComprehension && expr2 is MapComprehension) { + return ShallowEq((MapComprehension)expr1, (MapComprehension)expr2); + } else if (expr1 is SetComprehension && expr2 is SetComprehension) { + return ShallowEq((SetComprehension)expr1, (SetComprehension)expr2); + } else if (expr1 is QuantifierExpr && expr2 is QuantifierExpr) { + return ShallowEq((QuantifierExpr)expr1, (QuantifierExpr)expr2); + } else { + return false; // ComprehensionExpr is abstract + } + } + + private static bool ShallowEq(NamedExpr expr1, NamedExpr expr2) { + return expr1.Name == expr2.Name && + SameNullity(expr1.Contract, expr2.Contract); + } + + private static bool ShallowEq(LetExpr expr1, LetExpr expr2) { + return expr1.Exact == expr2.Exact && + ShallowSameAttributes(expr1.Attributes, expr2.Attributes); + } + + private static bool ShallowEq(TernaryExpr expr1, TernaryExpr expr2) { + return expr1.Op == expr2.Op; + } + + private static bool ShallowEq(BinaryExpr expr1, BinaryExpr expr2) { + Contract.Requires(expr1.ResolvedOp != BinaryExpr.ResolvedOpcode.YetUndetermined); + Contract.Requires(expr2.ResolvedOp != BinaryExpr.ResolvedOpcode.YetUndetermined); + return expr1.ResolvedOp == expr2.ResolvedOp; + } + + private static bool ShallowEq(ConversionExpr expr1, ConversionExpr expr2) { + return expr1.Type == expr2.Type; //TODO equality on types? + } + + private static bool ShallowEq(UnaryOpExpr expr1, UnaryOpExpr expr2) { + return expr1.Op == expr2.Op; + } + + private static bool ShallowEq(UnaryExpr expr1, UnaryExpr expr2) { + if (expr1 is ConversionExpr && expr2 is ConversionExpr) { + return ShallowEq((ConversionExpr)expr1, (ConversionExpr)expr2); + } else if (expr1 is UnaryOpExpr && expr2 is UnaryOpExpr) { + return ShallowEq((UnaryOpExpr)expr1, (UnaryOpExpr)expr2); + } else { + return false; // UnaryExpr is abstract + } + } + + private static bool ShallowEq(MultiSetFormingExpr expr1, MultiSetFormingExpr expr2) { + return true; + } + + private static bool ShallowEq(OldExpr expr1, OldExpr expr2) { + return true; + } + + private static bool ShallowEq(FunctionCallExpr expr1, FunctionCallExpr expr2) { + return expr1.Name == expr2.Name && + expr1.CoCall == expr2.CoCall && //TODO + expr1.Function == expr2.Function; // TODO TypeArgumentSubstitutions? + } + + private static bool ShallowEq(ApplyExpr expr1, ApplyExpr expr2) { + return true; + } + + private static bool ShallowEq(SeqUpdateExpr expr1, SeqUpdateExpr expr2) { + Contract.Requires(expr1.ResolvedUpdateExpr != null && expr2.ResolvedUpdateExpr != null); + return true; + } + + private static bool ShallowEq(MultiSelectExpr expr1, MultiSelectExpr expr2) { + return true; + } + + private static bool ShallowEq(SeqSelectExpr expr1, SeqSelectExpr expr2) { + return expr1.SelectOne == expr2.SelectOne && + SameNullity(expr1.Seq, expr2.Seq) && + SameNullity(expr1.E0, expr2.E0) && + SameNullity(expr1.E1, expr2.E1); + } + + private static bool ShallowEq(MemberSelectExpr expr1, MemberSelectExpr expr2) { + return expr1.MemberName == expr2.MemberName && + expr1.Member == expr2.Member && + SameLists(expr1.TypeApplication, expr2.TypeApplication, Type.Equals); + } + + private static bool ShallowEq(SeqDisplayExpr expr1, SeqDisplayExpr expr2) { + return true; + } + + private static bool ShallowEq(MapDisplayExpr expr1, MapDisplayExpr expr2) { + return expr1.Finite == expr2.Finite; + } + + private static bool ShallowEq(MultiSetDisplayExpr expr1, MultiSetDisplayExpr expr2) { + return true; + } + + private static bool ShallowEq(SetDisplayExpr expr1, SetDisplayExpr expr2) { + return expr1.Finite == expr2.Finite; + } + + private static bool ShallowEq(DisplayExpression expr1, DisplayExpression expr2) { + if (expr1 is SeqDisplayExpr && expr2 is SeqDisplayExpr) { + return ShallowEq((SeqDisplayExpr)expr1, (SeqDisplayExpr)expr2); + } else if (expr1 is MultiSetDisplayExpr && expr2 is MultiSetDisplayExpr) { //FIXME MultiSetDisplayExpr is not a DisplayExpression ??! + return ShallowEq((MultiSetDisplayExpr)expr1, (MultiSetDisplayExpr)expr2); + } else if (expr1 is SetDisplayExpr && expr2 is SetDisplayExpr) { + return ShallowEq((SetDisplayExpr)expr1, (SetDisplayExpr)expr2); + } else { + return false; + } + } + + private static bool ShallowEq(AutoGhostIdentifierExpr expr1, AutoGhostIdentifierExpr expr2) { + return true; + } + + private static bool ShallowEq(IdentifierExpr expr1, IdentifierExpr expr2) { + if (expr1.Name != expr2.Name || + expr1.Var != expr2.Var) { + return false; + } + + if (expr1 is AutoGhostIdentifierExpr && expr2 is AutoGhostIdentifierExpr) { + return ShallowEq((AutoGhostIdentifierExpr)expr1, (AutoGhostIdentifierExpr)expr2); + } else { + return true; + } + } + + private static bool ShallowEq(ImplicitThisExpr expr1, ImplicitThisExpr expr2) { + return true; + } + + private static bool ShallowEq(ThisExpr expr1, ThisExpr expr2) { + if (expr1 is ImplicitThisExpr && expr2 is ImplicitThisExpr) { + return ShallowEq((ImplicitThisExpr)expr1, (ImplicitThisExpr)expr2); + } else { + return (expr1.GetType() == expr2.GetType()); // LiteralExpr is not abstract + } + } + + private static bool ShallowEq(DatatypeValue expr1, DatatypeValue expr2) { + return // Implied by Ctor equality: expr1.DatatypeName == expr2.DatatypeName && + // Implied by Ctor equality: expr1.MemberName == expr2.MemberName && + expr1.Ctor == expr2.Ctor && + // Contextual information: expr1.IsCoCall == expr2.IsCoCall && + SameLists(expr1.InferredTypeArgs, expr2.InferredTypeArgs, Type.Equals); + } + + private static bool ShallowEq(StringLiteralExpr expr1, StringLiteralExpr expr2) { + return true; + } + + private static bool ShallowEq(CharLiteralExpr expr1, CharLiteralExpr expr2) { + return true; + } + + private static bool ShallowEq(StaticReceiverExpr expr1, StaticReceiverExpr expr2) { + return true; + } + + private static bool ShallowEq(LiteralExpr expr1, LiteralExpr expr2) { + if (expr1.Value != expr2.Value) { + return false; + } + + if (expr1 is StringLiteralExpr && expr2 is StringLiteralExpr) { + return ShallowEq((StringLiteralExpr)expr1, (StringLiteralExpr)expr2); + } else if (expr1 is CharLiteralExpr && expr2 is CharLiteralExpr) { + return ShallowEq((CharLiteralExpr)expr1, (CharLiteralExpr)expr2); + } else if (expr1 is StaticReceiverExpr && expr2 is StaticReceiverExpr) { + return ShallowEq((StaticReceiverExpr)expr1, (StaticReceiverExpr)expr2); + } else { + return (expr1.GetType() == expr2.GetType()); // LiteralExpr is not abstract + } + } + } +} -- cgit v1.2.3 From e4cefb56b4312d7c2bf88d9ba3c3bfd2e00940e9 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Mon, 13 Jul 2015 11:51:04 -0700 Subject: Register the trigger generator as a a rewriter in the Resolver. --- Source/Dafny/DafnyPipeline.csproj | 5 +++-- Source/Dafny/Rewriter.cs | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/Source/Dafny/DafnyPipeline.csproj b/Source/Dafny/DafnyPipeline.csproj index f9540aa0..a1452b8a 100644 --- a/Source/Dafny/DafnyPipeline.csproj +++ b/Source/Dafny/DafnyPipeline.csproj @@ -1,4 +1,4 @@ - + Debug @@ -143,6 +143,7 @@ + @@ -192,4 +193,4 @@ --> - + \ No newline at end of file diff --git a/Source/Dafny/Rewriter.cs b/Source/Dafny/Rewriter.cs index c0a25a82..480b3f61 100644 --- a/Source/Dafny/Rewriter.cs +++ b/Source/Dafny/Rewriter.cs @@ -36,6 +36,35 @@ namespace Microsoft.Dafny } } + public class TriggersRewriter : IRewriter { + Resolver Resolver; + + internal TriggersRewriter(Resolver resolver) { + Contract.Requires(Resolver != null); + this.Resolver = resolver; + } + + public void PreResolve(ModuleDefinition m) { } + + public void PostResolve(ModuleDefinition m) { + foreach (var decl in ModuleDefinition.AllCallables(m.TopLevelDecls)) { + if (decl is Function) { + var function = (Function)decl; + TriggerGenerator.AddTriggers(function.Ens, Resolver); + TriggerGenerator.AddTriggers(function.Req, Resolver); + TriggerGenerator.AddTriggers(function.Body, Resolver); + } else if (decl is Method) { + var method = (Method)decl; + TriggerGenerator.AddTriggers(method.Ens, Resolver); + TriggerGenerator.AddTriggers(method.Req, Resolver); + TriggerGenerator.AddTriggers(method.Body, Resolver); + } + } + } + + public void PostCyclicityResolve(ModuleDefinition m) { } + } + /// /// AutoContracts is an experimental feature that will fill much of the dynamic-frames boilerplate /// into a class. From the user's perspective, what needs to be done is simply: -- cgit v1.2.3 From 64495ae998749da057b3a717aba6ef53a3e8006e Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Mon, 13 Jul 2015 21:04:11 +0100 Subject: Add /printTooltips and /autoTriggers to the CLI --- Source/Dafny/DafnyOptions.cs | 21 +++++++++++++++++++++ Source/Dafny/Resolver.cs | 22 ++++++++++++++++++---- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/Source/Dafny/DafnyOptions.cs b/Source/Dafny/DafnyOptions.cs index 125ab11e..b2593705 100644 --- a/Source/Dafny/DafnyOptions.cs +++ b/Source/Dafny/DafnyOptions.cs @@ -56,6 +56,8 @@ namespace Microsoft.Dafny public bool AllowGlobals = false; public bool CountVerificationErrors = true; public bool Optimize = false; + public bool AutoTriggers = true; + public bool PrintTooltips = false; protected override bool ParseOption(string name, Bpl.CommandLineOptionEngine.CommandLineParseState ps) { var args = ps.args; // convenient synonym @@ -181,6 +183,18 @@ namespace Microsoft.Dafny return true; } + case "printTooltips": + PrintTooltips = true; + return true; + + case "autoTriggers": { + int autoTriggers = 1; // defaults to reporting verification errors + if (ps.GetNumericArgument(ref autoTriggers, 2)) { + AutoTriggers = autoTriggers == 1; + } + return true; + } + case "optimize": { Optimize = true; return true; @@ -276,12 +290,19 @@ namespace Microsoft.Dafny of verification errors. 1 (default) - If preprocessing succeeds, set exit code to the number of verification errors. + /autoTriggers: + 0 - Do not generate {:trigger} annotations for user-level quantifiers. + 1 (default) - Add a {:trigger} to each user-level quantifier. Existing + annotations are preserved. /optimize Produce optimized C# code, meaning: - selects optimized C# prelude by passing /define:DAFNY_USE_SYSTEM_COLLECTIONS_IMMUTABLE to csc.exe (requires System.Collections.Immutable.dll in the source directory to successfully compile). - passes /optimize flag to csc.exe. + /printTooltips + Dump additional positional information (displayed as mouse-over tooltips by + the VS plugin) to stdout as 'Info' messages. "); base.Usage(); // also print the Boogie options } diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index c5535808..300d4985 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -25,7 +25,7 @@ namespace Microsoft.Dafny ConsoleColor col = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("{0}({1},{2}): Error: {3}", - DafnyOptions.Clo.UseBaseNameForFileName ? System.IO.Path.GetFileName(tok.filename) : tok.filename, tok.line, tok.col - 1, + DafnyOptions.Clo.UseBaseNameForFileName ? System.IO.Path.GetFileName(tok.filename) : tok.filename, tok.line, tok.col, string.Format(msg, args)); Console.ForegroundColor = col; ErrorCount++; @@ -254,6 +254,10 @@ namespace Microsoft.Dafny // Populate the members of the basic types var trunc = new SpecialField(Token.NoToken, "Trunc", "ToBigInteger()", "", "", false, false, false, Type.Int, null); basicTypeMembers[(int)BasicTypeVariety.Real].Add(trunc.Name, trunc); + + if (DafnyOptions.O.PrintTooltips) { + AdditionalInformationReporter = DefaultInformationReporter; + } } [ContractInvariantMethod] @@ -264,6 +268,12 @@ namespace Microsoft.Dafny Contract.Invariant(cce.NonNullDictionaryAndValues(datatypeCtors) && Contract.ForAll(datatypeCtors.Values, v => cce.NonNullDictionaryAndValues(v))); } + public void DefaultInformationReporter(AdditionalInformation info) { + Console.WriteLine("{0}({1},{2}): Info: {3}", + DafnyOptions.Clo.UseBaseNameForFileName ? System.IO.Path.GetFileName(info.Token.filename) : info.Token.filename, + info.Token.line, info.Token.col, info.Text); + } + public void ResolveProgram(Program prog) { Contract.Requires(prog != null); var origErrorCount = ErrorCount; @@ -303,6 +313,10 @@ namespace Microsoft.Dafny rewriters.Add(opaqueRewriter); rewriters.Add(new TimeLimitRewriter()); + if (DafnyOptions.O.AutoTriggers) { + rewriters.Add(new TriggersRewriter(this)); + } + systemNameInfo = RegisterTopLevelDecls(prog.BuiltIns.SystemModule, false); prog.CompileModules.Add(prog.BuiltIns.SystemModule); foreach (var decl in sortedDecls) { @@ -2048,7 +2062,7 @@ namespace Microsoft.Dafny foreach (var p in e.TypeArgumentSubstitutions) { if (!IsDetermined(p.Value.Normalize())) { Error(e.tok, "type variable '{0}' in the function call to '{1}' could not be determined{2}", p.Key.Name, e.Name, - (e.Name.Contains("reveal_") || e.Name.Contains("_FULL")) + (e.Name.Contains("reveal_") || e.Name.Contains("_FULL")) //CLEMENT should this be StartsWith and EndsWith? ? ". If you are making an opaque function, make sure that the function can be called." : "" ); @@ -8135,7 +8149,7 @@ namespace Microsoft.Dafny Contract.Requires(!expr.WasResolved()); Contract.Requires(opts != null); Contract.Ensures(Contract.Result() == null || args != null); - + if (expr.OptTypeArguments != null) { foreach (var ty in expr.OptTypeArguments) { ResolveType(expr.tok, ty, opts.codeContext, ResolveTypeOptionEnum.InferTypeProxies, null); @@ -8175,7 +8189,7 @@ namespace Microsoft.Dafny receiver = new StaticReceiverExpr(expr.tok, (ClassDecl)member.EnclosingClass, true); } else { if (!scope.AllowInstance) { - Error(expr.tok, "'this' is not allowed in a 'static' context"); + Error(expr.tok, "'this' is not allowed in a 'static' context"); //FIXME: Rephrase this // nevertheless, set "receiver" to a value so we can continue resolution } receiver = new ImplicitThisExpr(expr.tok); -- cgit v1.2.3 From 6dfa82655aa7cb35bae6904e05887cdf960c6319 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Mon, 13 Jul 2015 11:55:06 -0700 Subject: Fix multiple tests that relied on z3 triggering on $Box Found by enabling auto-generated triggers and looking for failing tests --- Test/dafny0/DeterministicPick.dfy | 1 + Test/dafny0/SmallTests.dfy | 1 + Test/dafny1/BDD.dfy | 1 + Test/dafny2/COST-verif-comp-2011-2-MaxTree-class.dfy | 9 +++++++++ Test/dafny4/CoqArt-InsertionSort.dfy | 1 + Test/dafny4/GHC-MergeSort.dfy | 7 ++----- Test/dafny4/NipkowKlein-chapter7.dfy | 1 + Test/dafny4/Primes.dfy | 7 +++++++ 8 files changed, 23 insertions(+), 5 deletions(-) diff --git a/Test/dafny0/DeterministicPick.dfy b/Test/dafny0/DeterministicPick.dfy index a7ec55fa..13db1bfc 100644 --- a/Test/dafny0/DeterministicPick.dfy +++ b/Test/dafny0/DeterministicPick.dfy @@ -29,6 +29,7 @@ module Attempt_Smallest refines Specification { var z :| z in s; if s != {z} { var s' := s - {z}; + assert forall y :: y in s ==> y in s' || y == z; ASmallestToPick(s'); } } diff --git a/Test/dafny0/SmallTests.dfy b/Test/dafny0/SmallTests.dfy index 45ef06f7..e9c2beb4 100644 --- a/Test/dafny0/SmallTests.dfy +++ b/Test/dafny0/SmallTests.dfy @@ -332,6 +332,7 @@ method TestSequences0() assert 1 !in s; } else { assert 2 in s; + assert 0 in s; assert exists n :: n in s && -3 <= n && n < 2; } assert 7 in s; // error diff --git a/Test/dafny1/BDD.dfy b/Test/dafny1/BDD.dfy index 252164db..59dc3092 100644 --- a/Test/dafny1/BDD.dfy +++ b/Test/dafny1/BDD.dfy @@ -55,6 +55,7 @@ module SimpleBDD node := if s[n-i] then node.t else node.f; i := i - 1; } + assert s[n-i..] == []; b := node.b; } } diff --git a/Test/dafny2/COST-verif-comp-2011-2-MaxTree-class.dfy b/Test/dafny2/COST-verif-comp-2011-2-MaxTree-class.dfy index c752bd38..f691384c 100644 --- a/Test/dafny2/COST-verif-comp-2011-2-MaxTree-class.dfy +++ b/Test/dafny2/COST-verif-comp-2011-2-MaxTree-class.dfy @@ -117,6 +117,11 @@ class Tree { Repr := lft.Repr + {this} + rgt.Repr; } + lemma exists_intro(P: T -> bool, x: T) + requires P.requires(x) + requires P(x) + ensures exists y :: P.requires(y) && P(y) { } + method ComputeMax() returns (mx: int) requires Valid() && !IsEmpty(); ensures forall x :: x in Contents ==> x <= mx; @@ -124,13 +129,17 @@ class Tree { decreases Repr; { mx := value; + if (!left.IsEmpty()) { var m := left.ComputeMax(); mx := if mx < m then m else mx; } + if (!right.IsEmpty()) { var m := right.ComputeMax(); mx := if mx < m then m else mx; } + + exists_intro(x reads this => x in Contents && x == mx, mx); } } diff --git a/Test/dafny4/CoqArt-InsertionSort.dfy b/Test/dafny4/CoqArt-InsertionSort.dfy index efd01537..99e0f0b1 100644 --- a/Test/dafny4/CoqArt-InsertionSort.dfy +++ b/Test/dafny4/CoqArt-InsertionSort.dfy @@ -151,6 +151,7 @@ lemma existence_proof(l: List) { match l { case Nil => + assert sorted(Nil); case Cons(x, m) => existence_proof(m); var m' :| equiv(m, m') && sorted(m'); diff --git a/Test/dafny4/GHC-MergeSort.dfy b/Test/dafny4/GHC-MergeSort.dfy index e06773eb..976b8a27 100644 --- a/Test/dafny4/GHC-MergeSort.dfy +++ b/Test/dafny4/GHC-MergeSort.dfy @@ -412,11 +412,8 @@ lemma sorted_replaceSuffix(xs: List, ys: List, zs: List) match xs { case Nil => case Cons(c, xs') => - forall a,b | a in multiset_of(xs') && b in multiset_of(Cons(c, zs)) - ensures Below(a, b); - { - sorted_reverse(xs', Cons(c, ys)); - } + sorted_reverse(xs, ys); + sorted_reverse(xs', Cons(c, ys)); sorted_replaceSuffix(xs', Cons(c, ys), Cons(c, zs)); } } diff --git a/Test/dafny4/NipkowKlein-chapter7.dfy b/Test/dafny4/NipkowKlein-chapter7.dfy index 4756f5b1..7db31cbd 100644 --- a/Test/dafny4/NipkowKlein-chapter7.dfy +++ b/Test/dafny4/NipkowKlein-chapter7.dfy @@ -117,6 +117,7 @@ inductive lemma lemma_7_6(b: bexp, c: com, c': com, s: state, t: state) } else { var s' :| big_step#[_k-1](c, s, s') && big_step#[_k-1](While(b, c), s', t); lemma_7_6(b, c, c', s', t); // induction hypothesis + assert big_step(While(b, c'), s', t); } } diff --git a/Test/dafny4/Primes.dfy b/Test/dafny4/Primes.dfy index 31e3a19b..b0bb7527 100644 --- a/Test/dafny4/Primes.dfy +++ b/Test/dafny4/Primes.dfy @@ -110,6 +110,13 @@ lemma RemoveFactor(x: int, s: set) x * y * product(s - {y} - {x}); { assert s - {y} - {x} == s - {x} - {y}; } x * y * product(s - {x} - {y}); + /* FIXME: This annotation wasn't needed before the introduction + * of auto-triggers. It's not needed if one adds {:no_trigger} + * to the forall y :: y in s ==> y <= x part of PickLargest, but that + * boils down to z3 picking $Box(...) as good trigger + */ + // FIXME: the parens shouldn't be needed around (s - {x}) + { assert y in (s - {x}); } { assert y == PickLargest(s - {x}); } x * product(s - {x}); } -- cgit v1.2.3 From 795682c4739dfc4f3fff4241108e8d5efd593265 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Mon, 13 Jul 2015 21:03:25 +0100 Subject: Start adding missing triggers to the translator --- Source/Dafny/Translator.cs | 136 ++++++++++++++++++++++++++++----------------- 1 file changed, 86 insertions(+), 50 deletions(-) diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index d375c2bb..8042033b 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -659,7 +659,8 @@ namespace Microsoft.Dafny { ie.Var = oVarDafny; ie.Type = ie.Var.Type; // resolve ie here var constraint = etran.TrExpr(Substitute(dd.Constraint, dd.Var, ie)); var heap = new Bpl.BoundVariable(dd.tok, new Bpl.TypedIdent(dd.tok, predef.HeapVarName, predef.HeapType)); - var ex = new Bpl.ExistsExpr(dd.tok, new List { heap }, BplAnd(FunctionCall(dd.tok, BuiltinFunction.IsGoodHeap, null, etran.HeapExpr), constraint)); + //TRIG (exists $Heap: Heap :: $IsGoodHeap($Heap) && LitInt(0) <= $o#0 && $o#0 < 100) + var ex = new Bpl.ExistsExpr(dd.tok, new List { heap }, BplAnd(FunctionCall(dd.tok, BuiltinFunction.IsGoodHeap, null, etran.HeapExpr), constraint)); // LL_TRIGGER rhs = BplAnd(rhs, ex); } body = BplIff(is_o, rhs); @@ -727,10 +728,10 @@ namespace Microsoft.Dafny { { // Add: axiom (forall params :: DatatypeCtorId(#dt.ctor(params)) == ##dt.ctor); CreateBoundVariables(ctor.Formals, out bvs, out args); - Bpl.Expr lhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); - lhs = FunctionCall(ctor.tok, BuiltinFunction.DatatypeCtorId, null, lhs); + var constructor_call = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); + var lhs = FunctionCall(ctor.tok, BuiltinFunction.DatatypeCtorId, null, constructor_call); Bpl.Expr q = Bpl.Expr.Eq(lhs, c); - sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, BplForall(bvs, q), "Constructor identifier")); + sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, BplForall(bvs, BplTrigger(constructor_call), q), "Constructor identifier")); // NEW_TRIGGER } { @@ -760,10 +761,11 @@ namespace Microsoft.Dafny { Bpl.Expr dId; var dBv = BplBoundVar("d", predef.DatatypeType, out dId); Bpl.Expr q = Bpl.Expr.Eq(dId, lhs); if (bvs.Count != 0) { - q = new Bpl.ExistsExpr(ctor.tok, bvs, q); + // TRIG (exists a#6#0#0: Box, a#6#1#0: DatatypeType :: d == #OnceBuggy.MyDt.Cons(a#6#0#0, a#6#1#0))' + q = new Bpl.ExistsExpr(ctor.tok, bvs, BplTrigger(lhs), q); // NEW_TRIGGER } Bpl.Expr dtq = FunctionCall(ctor.tok, ctor.QueryField.FullSanitizedName, Bpl.Type.Bool, dId); - q = BplForall(dBv, null, BplImp(dtq, q)); + q = BplForall(dBv, BplTrigger(dtq), BplImp(dtq, q)); // NEW_TRIGGER sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Constructor questionmark has arguments")); } @@ -854,7 +856,9 @@ namespace Microsoft.Dafny { */ Bpl.Expr rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs); - q = new Bpl.ForallExpr(ctor.tok, bvs, Bpl.Expr.Lt(lhs, rhs)); + // TRIG (forall a#11#0#0: Box, a#11#1#0: DatatypeType :: BoxRank(a#11#0#0) < DtRank(#_module.List.Cons(a#11#0#0, a#11#1#0))) + var trigger = new Bpl.Trigger(ctor.tok, true, new List { lhs, rhs }); // TRIGGERS: THIS IS BROKEN + q = new Bpl.ForallExpr(ctor.tok, bvs, null, Bpl.Expr.Lt(lhs, rhs)); // NEW_TRIGGER // CLEMENT: Trigger not use because breaks termination checks for match statements sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Inductive rank")); } else if (argType is SeqType) { // axiom (forall params, i: int :: 0 <= i && i < |arg| ==> DtRank(arg[i]) < DtRank(#dt.ctor(params))); @@ -879,7 +883,7 @@ namespace Microsoft.Dafny { lhs = FunctionCall(ctor.tok, BuiltinFunction.SeqRank, null, args[i]); rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs); - q = new Bpl.ForallExpr(ctor.tok, bvs, Bpl.Expr.Lt(lhs, rhs)); + q = new Bpl.ForallExpr(ctor.tok, bvs, new Trigger(lhs.tok, true, new List { lhs, rhs }), Bpl.Expr.Lt(lhs, rhs)); // NEW_TRIGGER sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Inductive seq rank")); } else if (argType is SetType) { // axiom (forall params, d: Datatype :: arg[d] ==> DtRank(d) < DtRank(#dt.ctor(params))); @@ -893,7 +897,9 @@ namespace Microsoft.Dafny { Bpl.Expr lhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, ie); Bpl.Expr rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs); - q = new Bpl.ForallExpr(ctor.tok, bvs, Bpl.Expr.Imp(ante, Bpl.Expr.Lt(lhs, rhs))); + // TRIG (forall a#50#0#0: Set Box, d: DatatypeType :: a#50#0#0[$Box(d)] ==> DtRank(d) < DtRank(#_module.d3.B3(a#50#0#0))) + var trigger = new Bpl.Trigger(ctor.tok, true, new List { lhs, rhs }); //TRIGGERS: Should this mention the precondition ("ante") too? + q = new Bpl.ForallExpr(ctor.tok, bvs, trigger, Bpl.Expr.Imp(ante, Bpl.Expr.Lt(lhs, rhs))); // NEW_TRIGGER sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Inductive set rank")); } else if (argType is MultiSetType) { // axiom (forall params, d: Datatype :: 0 < arg[d] ==> DtRank(d) < DtRank(#dt.ctor(params))); @@ -907,8 +913,10 @@ namespace Microsoft.Dafny { Bpl.Expr lhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, ie); Bpl.Expr rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs); - q = new Bpl.ForallExpr(ctor.tok, bvs, Bpl.Expr.Imp(ante, Bpl.Expr.Lt(lhs, rhs))); + // CLEMENT: Does the test suite cover this? + q = new Bpl.ForallExpr(ctor.tok, bvs, Bpl.Expr.Imp(ante, Bpl.Expr.Lt(lhs, rhs))); // W_TRIGGER sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Inductive multiset rank")); + // CLEMENT: I don't understand what this case disjunction does here; I don't think it's covered by the test suite } } @@ -1113,8 +1121,8 @@ namespace Microsoft.Dafny { var equal = Bpl.Expr.Eq(d0, d1); var PEq = CoEqualCall(codecl, lexprs, rexprs, k, LayerSucc(ly), d0, d1); sink.AddTopLevelDeclaration(new Axiom(dt.tok, - BplForall(vars, null, BplImp(BplAnd(equal, kGtZero), PEq)), - "Prefix equality shortcut")); + BplForall(vars, BplTrigger(PEq), BplImp(BplAnd(equal, kGtZero), PEq)), + "Prefix equality shortcut")); // NEW_TRIGGER }); } } @@ -2017,15 +2025,15 @@ namespace Microsoft.Dafny { Bpl.Trigger tr = new Bpl.Trigger(f.tok, true, new List { funcAppl }); var typeParams = TrTypeParamDecls(f.TypeArgs); - Bpl.Expr meat = Bpl.Expr.True; + Bpl.Expr post = Bpl.Expr.True; foreach (Expression p in ens) { Bpl.Expr q = etran.TrExpr(Substitute(p, null, substMap)); - meat = BplAnd(meat, q); + post = BplAnd(post, q); } Bpl.Expr whr = GetWhereClause(f.tok, funcAppl, f.ResultType, etran); - if (whr != null) { meat = Bpl.Expr.And(meat, whr); } + if (whr != null) { post = Bpl.Expr.And(post, whr); } - Bpl.Expr ax = new Bpl.ForallExpr(f.tok, typeParams, formals, null, tr, Bpl.Expr.Imp(ante, meat)); + Bpl.Expr ax = new Bpl.ForallExpr(f.tok, typeParams, formals, null, tr, Bpl.Expr.Imp(ante, post)); var activate = AxiomActivation(f, true, true, etran); string comment = "consequence axiom for " + f.FullSanitizedName; return new Bpl.Axiom(f.tok, Bpl.Expr.Imp(activate, ax), comment); @@ -2489,11 +2497,10 @@ namespace Microsoft.Dafny { moreBvs.Add(k); var z = Bpl.Expr.Eq(kId, Bpl.Expr.Literal(0)); funcID = new Bpl.IdentifierExpr(tok, pp.FullSanitizedName, TrType(pp.ResultType)); - Bpl.Expr prefixLimited = new Bpl.NAryExpr(tok, new Bpl.FunctionCall(funcID), prefixArgsLimited); - if (pp.FixpointPred is InductivePredicate) { - prefixLimited = Bpl.Expr.Not(prefixLimited); - } - var trueAtZero = new Bpl.ForallExpr(tok, moreBvs, BplImp(BplAnd(ante, z), prefixLimited)); + Bpl.Expr prefixLimitedBody = new Bpl.NAryExpr(tok, new Bpl.FunctionCall(funcID), prefixArgsLimited); + Bpl.Expr prefixLimited = pp.FixpointPred is InductivePredicate ? Bpl.Expr.Not(prefixLimitedBody) : prefixLimitedBody; + + var trueAtZero = new Bpl.ForallExpr(tok, moreBvs, BplTrigger(prefixLimitedBody), BplImp(BplAnd(ante, z), prefixLimited)); // NEW_TRIGGER sink.AddTopLevelDeclaration(new Bpl.Axiom(tok, Bpl.Expr.Imp(activation, trueAtZero), "3rd prefix predicate axiom")); } @@ -3946,7 +3953,8 @@ namespace Microsoft.Dafny { Bpl.Expr iBounds = InSeqRange(tok, i, etran.TrExpr(e), true, null, false); Bpl.Expr XsubI = FunctionCall(tok, BuiltinFunction.SeqIndex, predef.BoxType, etran.TrExpr(e), i); // TODO: the equality in the next line should be changed to one that understands extensionality - disjunct = new Bpl.ExistsExpr(tok, new List { iVar }, Bpl.Expr.And(iBounds, Bpl.Expr.Eq(XsubI, boxO))); + //TRIG (exists $i: int :: 0 <= $i && $i < Seq#Length(read($h0, this, _module.DoublyLinkedList.Nodes)) && Seq#Index(read($h0, this, _module.DoublyLinkedList.Nodes), $i) == $Box($o)) + disjunct = new Bpl.ExistsExpr(tok, new List { iVar }, Bpl.Expr.And(iBounds, Bpl.Expr.Eq(XsubI, boxO))); // LL_TRIGGER } else { // o == e disjunct = Bpl.Expr.Eq(o, etran.TrExpr(e)); @@ -4421,7 +4429,10 @@ namespace Microsoft.Dafny { var d = LetDesugaring(e); // call LetDesugaring to prepare the desugaring and populate letSuchThatExprInfo with something for e var info = letSuchThatExprInfo[e]; var canCallFunction = info.CanCallFunctionCall(this, etran); - var cc = new Bpl.ForallExpr(e.tok, bvars, Bpl.Expr.Imp(typeAntecedent, BplAnd(BplAnd(canCallRHS, canCallBody), canCallFunction))); + //TRIG (forall d#0: int :: true ==> _module.__default.DividesBoth#canCall($Heap, d#0, a#0, b#0) && (_module.__default.DividesBoth($Heap, d#0, a#0, b#0) ==> (forall m#1: int :: true ==> _module.__default.DividesBoth#canCall($Heap, m#1, a#0, b#0) && (_module.__default.DividesBoth($Heap, m#1, a#0, b#0) ==> true))) && (_module.__default.DividesBoth($Heap, d#0, a#0, b#0) && (forall m#1: int :: true ==> _module.__default.DividesBoth($Heap, m#1, a#0, b#0) ==> m#1 <= d#0) ==> true) && $let#0$canCall($Heap, a#0, b#0)) + //TRIG (forall g#0: int :: true ==> (x#0 == g#0 ==> true) && $let#0$canCall($Heap, x#0)) + //TRIGGERS: Not clear what would be good here + var cc = new Bpl.ForallExpr(e.tok, bvars, Bpl.Expr.Imp(typeAntecedent, BplAnd(BplAnd(canCallRHS, canCallBody), canCallFunction))); // LL_TRIGGER return cc; } @@ -4450,7 +4461,11 @@ namespace Microsoft.Dafny { ExpressionTranslator et = new ExpressionTranslator(etran, heap); var ebody = CanCallAssumption(Substitute(e.Body, null, subst), et); - return BplForall(bvars, ebody); + + //TRIG (forall $l#0#heap#0: Heap, $l#0#x#0: int :: true) + //TRIG (forall $l#0#heap#0: Heap, $l#0#t#0: DatatypeType :: _module.__default.TMap#canCall(_module._default.TMap$A, _module._default.TMap$B, $l#0#heap#0, $l#0#t#0, f#0)) + //TRIG (forall $l#4#heap#0: Heap, $l#4#x#0: Box :: _0_Monad.__default.Bind#canCall(Monad._default.Associativity$B, Monad._default.Associativity$C, $l#4#heap#0, Apply1(Monad._default.Associativity$A, #$M$B, f#0, $l#4#heap#0, $l#4#x#0), g#0)) + return BplForall(bvars, ebody); // L_TRIGGER } else if (expr is ComprehensionExpr) { var e = (ComprehensionExpr)expr; var canCall = CanCallAssumption(e.Term, etran); @@ -4466,7 +4481,7 @@ namespace Microsoft.Dafny { Bpl.Trigger tr = TrTrigger(etran, e.Attributes, expr.tok); canCall = new Bpl.ForallExpr(expr.tok, Concat(tyvars, bvars), tr, Bpl.Expr.Imp(typeAntecedent, canCall)); } else { - canCall = new Bpl.ForallExpr(expr.tok, Concat(tyvars, bvars), Bpl.Expr.Imp(typeAntecedent, canCall)); + canCall = new Bpl.ForallExpr(expr.tok, Concat(tyvars, bvars), Bpl.Expr.Imp(typeAntecedent, canCall)); // SMART_TRIGGER } } return canCall; @@ -4879,7 +4894,9 @@ namespace Microsoft.Dafny { var range = BplAnd(Bpl.Expr.Le(lowerBound, i), Bpl.Expr.Lt(i, upperBound)); var fieldName = FunctionCall(e.tok, BuiltinFunction.IndexField, null, i); var allowedToRead = Bpl.Expr.SelectTok(e.tok, etran.TheFrame(e.tok), seq, fieldName); - var qq = new Bpl.ForallExpr(e.tok, new List { iVar }, Bpl.Expr.Imp(range, allowedToRead)); + //TRIG (forall $i: int :: read($Heap, this, _module.RingBuffer.start) <= $i && $i < read($Heap, this, _module.RingBuffer.start) + read($Heap, this, _module.RingBuffer.len) ==> $_Frame[read($Heap, this, _module.RingBuffer.data), IndexField($i)]) + //TRIGGERS: Should this be more specific? + var qq = new Bpl.ForallExpr(e.tok, new List { iVar }, BplTrigger(fieldName), Bpl.Expr.Imp(range, allowedToRead)); // NEW_TRIGGER options.AssertSink(this, builder)(expr.tok, qq, "insufficient reads clause to read the indicated range of array elements", options.AssertKv); } } @@ -6106,8 +6123,9 @@ namespace Microsoft.Dafny { // axiom (forall o: Ref :: 0 <= array.Length(o)); Bpl.BoundVariable oVar = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "o", predef.RefType)); Bpl.IdentifierExpr o = new Bpl.IdentifierExpr(f.tok, oVar); - Bpl.Expr body = Bpl.Expr.Le(Bpl.Expr.Literal(0), new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(ff), new List { o })); - Bpl.Expr qq = new Bpl.ForallExpr(f.tok, new List { oVar }, body); + var rhs = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(ff), new List { o }); + Bpl.Expr body = Bpl.Expr.Le(Bpl.Expr.Literal(0), rhs); + Bpl.Expr qq = new Bpl.ForallExpr(f.tok, new List { oVar }, BplTrigger(rhs), body); // NEW_TRIGGER sink.AddTopLevelDeclaration(new Bpl.Axiom(f.tok, qq)); } } @@ -7890,7 +7908,8 @@ namespace Microsoft.Dafny { GetObjFieldDetails(s0.Lhs.Resolved, prevEtran, out xObj, out xField); xBody = BplAnd(xBody, Bpl.Expr.Eq(o, xObj)); xBody = BplAnd(xBody, Bpl.Expr.Eq(f, xField)); - Bpl.Expr xObjField = new Bpl.ExistsExpr(s.Tok, xBvars, xBody); + //TRIG (exists k#2: int :: (k#2 == LitInt(0 - 3) || k#2 == LitInt(4)) && $o == read($prevHeap, this, _module.MyClass.arr) && $f == MultiIndexField(IndexField(i#0), j#0)) + Bpl.Expr xObjField = new Bpl.ExistsExpr(s.Tok, xBvars, xBody); // LL_TRIGGER Bpl.Expr body = Bpl.Expr.Or(Bpl.Expr.Eq(heapOF, oldHeapOF), xObjField); var tr = new Trigger(s.Tok, true, new List() { heapOF }); Bpl.Expr qq = new Bpl.ForallExpr(s.Tok, new List { alpha }, new List { oVar, fVar }, null, tr, body); @@ -7957,6 +7976,13 @@ namespace Microsoft.Dafny { } } + //CLEMENT: Remove + //public static Expr PrintAndDie(Expr expr, [System.Runtime.CompilerServices.CallerMemberName] string caller = "", [System.Runtime.CompilerServices.CallerLineNumber] int linum = 0) { + // Console.Error.WriteLine("In {0} at line {1}: {2}", caller, linum, expr.ToString()); + // Environment.Exit(1); + // return expr; + //} + /// /// Generate: /// assume (forall x,y :: Range(x,y)[$Heap:=oldHeap] ==> @@ -8231,7 +8257,7 @@ namespace Microsoft.Dafny { ante = BplAnd(ante, additionalRange(substMap, initEtran)); } - // Note, in the following, we need to do a bit of a song and dance. The actual arguements of the + // Note, in the following, we need to do a bit of a song and dance. The actual arguments of the // call should be translated using "initEtran", whereas the method postcondition should be translated // using "callEtran". To accomplish this, we translate the argument and then tuck the resulting // Boogie expressions into BoogieExprWrappers that are used in the DafnyExpr-to-DafnyExpr substitution. @@ -8250,7 +8276,10 @@ namespace Microsoft.Dafny { post = BplAnd(post, callEtran.TrExpr(p)); } - Bpl.Expr qq = new Bpl.ForallExpr(tok, bvars, Bpl.Expr.Imp(ante, post)); + // TRIG (forall $ih#s0#0: Seq Box :: $Is($ih#s0#0, TSeq(TChar)) && $IsAlloc($ih#s0#0, TSeq(TChar), $initHeapForallStmt#0) && Seq#Length($ih#s0#0) != 0 && Seq#Rank($ih#s0#0) < Seq#Rank(s#0) ==> (forall i#2: int :: true ==> LitInt(0) <= i#2 && i#2 < Seq#Length($ih#s0#0) ==> char#ToInt(_module.CharChar.MinChar($LS($LZ), $Heap, this, $ih#s0#0)) <= char#ToInt($Unbox(Seq#Index($ih#s0#0, i#2)): char))) + // TRIG (forall $ih#pat0#0: Seq Box, $ih#a0#0: Seq Box :: $Is($ih#pat0#0, TSeq(_module._default.Same0$T)) && $IsAlloc($ih#pat0#0, TSeq(_module._default.Same0$T), $initHeapForallStmt#0) && $Is($ih#a0#0, TSeq(_module._default.Same0$T)) && $IsAlloc($ih#a0#0, TSeq(_module._default.Same0$T), $initHeapForallStmt#0) && Seq#Length($ih#pat0#0) <= Seq#Length($ih#a0#0) && Seq#SameUntil($ih#pat0#0, $ih#a0#0, Seq#Length($ih#pat0#0)) && (Seq#Rank($ih#pat0#0) < Seq#Rank(pat#0) || (Seq#Rank($ih#pat0#0) == Seq#Rank(pat#0) && Seq#Rank($ih#a0#0) < Seq#Rank(a#0))) ==> _module.__default.IsRelaxedPrefixAux(_module._default.Same0$T, $LS($LZ), $Heap, $ih#pat0#0, $ih#a0#0, LitInt(1)))' + // TRIG (forall $ih#m0#0: DatatypeType, $ih#n0#0: DatatypeType :: $Is($ih#m0#0, Tclass._module.Nat()) && $IsAlloc($ih#m0#0, Tclass._module.Nat(), $initHeapForallStmt#0) && $Is($ih#n0#0, Tclass._module.Nat()) && $IsAlloc($ih#n0#0, Tclass._module.Nat(), $initHeapForallStmt#0) && Lit(true) && (DtRank($ih#m0#0) < DtRank(m#0) || (DtRank($ih#m0#0) == DtRank(m#0) && DtRank($ih#n0#0) < DtRank(n#0))) ==> _module.__default.mult($LS($LZ), $Heap, $ih#m0#0, _module.__default.plus($LS($LZ), $Heap, $ih#n0#0, $ih#n0#0)) == _module.__default.mult($LS($LZ), $Heap, _module.__default.plus($LS($LZ), $Heap, $ih#m0#0, $ih#m0#0), $ih#n0#0)) + Bpl.Expr qq = new Bpl.ForallExpr(tok, bvars, Bpl.Expr.Imp(ante, post)); // SMART_TRIGGER exporter.Add(new Bpl.AssumeCmd(tok, qq)); } } @@ -10889,26 +10918,31 @@ namespace Microsoft.Dafny { var eeType = e.E.Type.NormalizeExpand(); if (eeType is SetType) { // generate: (forall $o: ref :: $o != null && X[Box($o)] ==> !old($Heap)[$o,alloc]) - // TODO: trigger? Bpl.Variable oVar = new Bpl.BoundVariable(expr.tok, new Bpl.TypedIdent(expr.tok, "$o", predef.RefType)); Bpl.Expr o = new Bpl.IdentifierExpr(expr.tok, oVar); Bpl.Expr oNotNull = Bpl.Expr.Neq(o, predef.Null); Bpl.Expr oInSet = TrInSet(expr.tok, o, e.E, ((SetType)eeType).Arg); - Bpl.Expr oIsFresh = Bpl.Expr.Not(Old.IsAlloced(expr.tok, o)); + Bpl.Expr oNotFresh = Old.IsAlloced(expr.tok, o); + Bpl.Expr oIsFresh = Bpl.Expr.Not(oNotFresh); Bpl.Expr body = Bpl.Expr.Imp(Bpl.Expr.And(oNotNull, oInSet), oIsFresh); - return new Bpl.ForallExpr(expr.tok, new List { oVar }, body); + // TRIGGERS: Does this make sense? VSI-Benchmarks\b7 + // TRIG (forall $o: ref :: $o != null && read($Heap, this, _module.List.Repr)[$Box($o)] && $o != this ==> !read(old($Heap), $o, alloc)) + // TRIG (forall $o: ref :: $o != null && read($Heap, this, _module.Stream.footprint)[$Box($o)] && $o != this ==> !read(old($Heap), $o, alloc)) + return new Bpl.ForallExpr(expr.tok, new List { oVar }, BplTrigger(oNotFresh), body); // NEW_TRIGGER } else if (eeType is SeqType) { // generate: (forall $i: int :: 0 <= $i && $i < Seq#Length(X) && Unbox(Seq#Index(X,$i)) != null ==> !old($Heap)[Unbox(Seq#Index(X,$i)),alloc]) - // TODO: trigger? Bpl.Variable iVar = new Bpl.BoundVariable(expr.tok, new Bpl.TypedIdent(expr.tok, "$i", Bpl.Type.Int)); Bpl.Expr i = new Bpl.IdentifierExpr(expr.tok, iVar); Bpl.Expr iBounds = translator.InSeqRange(expr.tok, i, TrExpr(e.E), true, null, false); Bpl.Expr XsubI = translator.FunctionCall(expr.tok, BuiltinFunction.SeqIndex, predef.RefType, TrExpr(e.E), i); XsubI = translator.FunctionCall(expr.tok, BuiltinFunction.Unbox, predef.RefType, XsubI); - Bpl.Expr oIsFresh = Bpl.Expr.Not(Old.IsAlloced(expr.tok, XsubI)); + Bpl.Expr oNotFresh = Old.IsAlloced(expr.tok, XsubI); + Bpl.Expr oIsFresh = Bpl.Expr.Not(oNotFresh); Bpl.Expr xsubiNotNull = Bpl.Expr.Neq(XsubI, predef.Null); Bpl.Expr body = Bpl.Expr.Imp(Bpl.Expr.And(iBounds, xsubiNotNull), oIsFresh); - return new Bpl.ForallExpr(expr.tok, new List { iVar }, body); + //TRIGGERS: Does this make sense? dafny0\SmallTests + //TRIG (forall $i: int :: 0 <= $i && $i < Seq#Length(Q#0) && $Unbox(Seq#Index(Q#0, $i)): ref != null ==> !read(old($Heap), $Unbox(Seq#Index(Q#0, $i)): ref, alloc)) + return new Bpl.ForallExpr(expr.tok, new List { iVar }, body); // NEW_TRIGGER } else if (eeType.IsDatatype) { // translator.FunctionCall(e.tok, BuiltinFunction.DtAlloc, null, TrExpr(e.E), Old.HeapExpr); Bpl.Expr alloc = translator.MkIsAlloc(TrExpr(e.E), eeType, Old.HeapExpr); @@ -11332,7 +11366,7 @@ namespace Microsoft.Dafny { Bpl.QKeyValue kv = TrAttributes(e.Attributes, "trigger"); Bpl.Trigger tr = null; - for (Attributes aa = e.Attributes; aa != null; aa = aa.Prev) { + foreach (var aa in e.Attributes.AsEnumerable()) { if (aa.Name == "trigger") { List tt = new List(); foreach (var arg in aa.Args) { @@ -11345,12 +11379,12 @@ namespace Microsoft.Dafny { 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)); + return new Bpl.ForallExpr(expr.tok, new List(), Concat(tyvars, bvars), kv, tr, Bpl.Expr.Imp(antecedent, body)); // SMART_TRIGGER } else { Contract.Assert(e is ExistsExpr); - return new Bpl.ExistsExpr(expr.tok, new List(), Concat(tyvars,bvars), kv, tr, Bpl.Expr.And(antecedent, body)); + return new Bpl.ExistsExpr(expr.tok, new List(), Concat(tyvars, bvars), kv, tr, Bpl.Expr.And(antecedent, body)); // SMART_TRIGGER } } else if (expr is SetComprehension) { @@ -11436,7 +11470,7 @@ namespace Microsoft.Dafny { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression } } - + private Expr TrLambdaExpr(LambdaExpr e) { var bvars = new List(); var bargs = new List(); @@ -12667,6 +12701,7 @@ namespace Microsoft.Dafny { // Don't inline opaque functions or foreign protected functions } else { // inline this body + // CLEMENT: This is a problem for triggers var body = GetSubstitutedBody(fexp, f, false); var typeSpecializedBody = GetSubstitutedBody(fexp, f, true); var typeSpecializedResultType = Resolver.SubstType(f.ResultType, fexp.TypeArgumentSubstitutions); @@ -12778,7 +12813,7 @@ namespace Microsoft.Dafny { Bpl.Trigger tr = TrTrigger(etran, e.Attributes, expr.tok, substMap); ih = new Bpl.ForallExpr(expr.tok, bvars, tr, Bpl.Expr.Imp(typeAntecedent, ihBody)); } else { - ih = new Bpl.ForallExpr(expr.tok, bvars, Bpl.Expr.Imp(typeAntecedent, ihBody)); + ih = new Bpl.ForallExpr(expr.tok, bvars, Bpl.Expr.Imp(typeAntecedent, ihBody)); // SMART_TRIGGER } // More precisely now: @@ -12815,10 +12850,10 @@ namespace Microsoft.Dafny { Bpl.Trigger tr = TrTrigger(etran, e.Attributes, expr.tok); q = new Bpl.ForallExpr(kase.tok, bvars, tr, Bpl.Expr.Imp(ante, bdy)); } else { - q = new Bpl.ForallExpr(kase.tok, bvars, Bpl.Expr.Imp(ante, bdy)); + q = new Bpl.ForallExpr(kase.tok, bvars, Bpl.Expr.Imp(ante, bdy)); // SMART_TRIGGER } } else { - q = new Bpl.ExistsExpr(kase.tok, bvars, Bpl.Expr.And(ante, bdy)); + q = new Bpl.ExistsExpr(kase.tok, bvars, Bpl.Expr.And(ante, bdy)); // SMART_TRIGGER } splits.Add(new SplitExprInfo(SplitExprInfo.K.Checked, q)); } @@ -13218,7 +13253,8 @@ namespace Microsoft.Dafny { } i++; } - q = new Bpl.ExistsExpr(ctor.tok, bvs, BplAnd(typeAntecedent, q)); + // TRIG (exists a#0#0#0: Box :: $IsBox(a#0#0#0, _module.DatatypeInduction$T) && $IsAllocBox(a#0#0#0, _module.DatatypeInduction$T, $Heap) && #_module.Tree.Leaf(a#0#0#0) == t#1) + q = new Bpl.ExistsExpr(ctor.tok, bvs, BplTrigger(ct), BplAnd(typeAntecedent, q)); // NEW_TRIGGER } yield return q; } @@ -14141,19 +14177,19 @@ namespace Microsoft.Dafny { // Bpl-making-utilities - static Bpl.Expr BplForall(IEnumerable args_in, Bpl.Expr body) { + static Bpl.Expr BplForall(IEnumerable args_in, Bpl.Expr body) { // NO_TRIGGER var args = new List(args_in); - if (args.Count == 0) { + if (args.Count == 0) { // CLEMENT don't add quantifiers if the body is trivial return body; } else { - return new Bpl.ForallExpr(body.tok, args, body); + return new Bpl.ForallExpr(body.tok, args, body); // NO_TRIGGER } } // Note: if the trigger is null, makes a forall without any triggers static Bpl.Expr BplForall(IEnumerable args_in, Bpl.Trigger trg, Bpl.Expr body) { if (trg == null) { - return BplForall(args_in, body); + return BplForall(args_in, body); // NO_TRIGGER } else { var args = new List(args_in); if (args.Count == 0) { -- cgit v1.2.3 From aa2bfc0930e932be72cfd2d796352a0aa4182477 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Mon, 13 Jul 2015 23:44:47 +0100 Subject: Detect matching loops at the mutli-trigger level Stop using HashSet, as we don't currently have a good HashCode implementation. Instead, excplicitly call distinct where needed. Improve reporting a bit. --- Source/Dafny/TriggerGenerator.cs | 238 +++++++++++++++++++++++++-------------- 1 file changed, 156 insertions(+), 82 deletions(-) diff --git a/Source/Dafny/TriggerGenerator.cs b/Source/Dafny/TriggerGenerator.cs index 1783a00c..4697b149 100644 --- a/Source/Dafny/TriggerGenerator.cs +++ b/Source/Dafny/TriggerGenerator.cs @@ -28,36 +28,90 @@ namespace Microsoft.Dafny { class TriggerCandidate { // TODO Hashing is broken (duplicates can pop up) internal Expression Expr; internal ISet Variables; - internal Expression[] PotentialMatchingLoops; + internal List PotentialMatchingLoops; public override string ToString() { return Printer.ExprToString(Expr); } } - - struct MultiTriggerCandidate { - internal ISet Candidates; + + class TriggerCandidateComparer : IEqualityComparer { + private static TriggerCandidateComparer singleton; + internal static TriggerCandidateComparer Instance { + get { return singleton == null ? (singleton = new TriggerCandidateComparer()) : singleton; } + } + + private TriggerCandidateComparer() { } + + public bool Equals(TriggerCandidate x, TriggerCandidate y) { + return x == null && y == null || + x != null && y != null && x.Expr.ExpressionEq(y.Expr); + } + + public int GetHashCode(TriggerCandidate obj) { + return 1; // FIXME: Force collisions. Use until we have a proper hashing strategy for expressions. + } + } + + class MultiTriggerCandidate { + internal List Candidates; + internal List Tags; internal double Score; + + private List potentialMatchingLoops; + internal List PotentialMatchingLoops { + get { + if (potentialMatchingLoops == null) { + //FIXME could be optimized by looking at the bindings instead of doing full equality + var candidates = Candidates.Distinct(TriggerCandidateComparer.Instance); + potentialMatchingLoops = candidates.SelectMany(candidate => candidate.PotentialMatchingLoops) + .Distinct(ExprExtensions.EqExpressionComparer.Instance).Where(e => !candidates.Any(c => c.Expr.ExpressionEq(e))).ToList(); + } + + return potentialMatchingLoops; + } + } + + internal MultiTriggerCandidate(List candidates) { + Candidates = candidates; + Tags = new List(); + } + + internal bool MentionsAll(List vars) { + var candidates = Candidates; + return vars.All(x => candidates.Any(candidate => candidate.Variables.Contains(x))); //TODO Perfs? + } public override string ToString() { return String.Format("[{0:G2}] {1}", Score, String.Join(", ", Candidates)); } + + public String AsDafnyAttributeString(bool wrap = true, bool includeTags = false) { + var repr = String.Join(", ", Candidates.Select(t => Printer.ExprToString(t.Expr))); + if (wrap) { + repr = "{:trigger " + repr + "}"; + } + if (includeTags && Tags != null) { + repr += " (" + String.Join("; ", Tags) + ")"; + } + return repr; + } } + class TriggerAnnotation { internal bool IsTriggerKiller; internal ISet Variables; - internal readonly HashSet PrivateCandidates; - internal readonly HashSet ExportedCandidates; + internal readonly List PrivateCandidates; + internal readonly List ExportedCandidates; //FIXME using a hashset is useless here internal TriggerAnnotation(bool IsTriggerKiller, IEnumerable Variables, IEnumerable AllCandidates, IEnumerable PrivateCandidates = null) { this.IsTriggerKiller = IsTriggerKiller; this.Variables = new HashSet(Variables); - this.ExportedCandidates = new HashSet(AllCandidates == null ? Enumerable.Empty() : AllCandidates); - this.PrivateCandidates = new HashSet(PrivateCandidates == null ? Enumerable.Empty() : PrivateCandidates); - this.ExportedCandidates.ExceptWith(this.PrivateCandidates); + this.PrivateCandidates = new List(PrivateCandidates == null ? Enumerable.Empty() : PrivateCandidates); + this.ExportedCandidates = new List(AllCandidates == null ? Enumerable.Empty() : AllCandidates.Except(this.PrivateCandidates)); } public override string ToString() { @@ -100,17 +154,18 @@ namespace Microsoft.Dafny { this.Resolver = resolver; } - private ISet MergeAlterFirst(ISet a, ISet b) { + private List MergeAlterFirst(List a, List b) { Contract.Requires(a != null); Contract.Requires(b != null); - a.UnionWith(b); + a.AddRange(b); return a; } - private ISet WithOneExtraElement(ISet set, T elem) { - Contract.Requires(set != null); - set.Add(elem); - return set; + private ISet MergeAlterFirst(ISet a, ISet b) { + Contract.Requires(a != null); + Contract.Requires(b != null); + a.UnionWith(b); + return a; } private T ReduceAnnotatedSubExpressions(Expression expr, T seed, Func map, Func reduce) { @@ -118,8 +173,8 @@ namespace Microsoft.Dafny { .Aggregate(seed, (acc, e) => reduce(acc, e)); } - private ISet CollectExportedCandidates(Expression expr) { - return ReduceAnnotatedSubExpressions>(expr, new HashSet(), a => a.ExportedCandidates, MergeAlterFirst); + private List CollectExportedCandidates(Expression expr) { + return ReduceAnnotatedSubExpressions>(expr, new List(), a => a.ExportedCandidates, MergeAlterFirst); } private ISet CollectVariables(Expression expr) { @@ -130,7 +185,7 @@ namespace Microsoft.Dafny { return ReduceAnnotatedSubExpressions(expr, false, a => a.IsTriggerKiller, (a, b) => a || b); } - private IEnumerable OnlyPrivateCandidates(ISet candidates, IEnumerable privateVars) { + private IEnumerable OnlyPrivateCandidates(List candidates, IEnumerable privateVars) { return candidates.Where(c => privateVars.Intersect(c.Variables).Any()); //TODO Check perf } @@ -222,12 +277,12 @@ namespace Microsoft.Dafny { var new_expr = CleanupExpr(expr, out expr_is_killer); var new_candidate = new TriggerCandidate { Expr = new_expr, Variables = CollectVariables(expr) }; - ISet collected_candidates = CollectExportedCandidates(expr); + List collected_candidates = CollectExportedCandidates(expr); var children_contain_killers = CollectIsKiller(expr); if (!children_contain_killers) { // Add only if the children are not killers; the head has been cleaned up into non-killer form - collected_candidates = WithOneExtraElement(collected_candidates, new_candidate); + collected_candidates.Add(new_candidate); } // This new node is a killer if its children were killers, or if it's non-cleaned-up head is a killer @@ -242,7 +297,8 @@ namespace Microsoft.Dafny { // candidate matching its direct subexpression if needed. Not that function calls are not the // only possible child here; there can be DatatypeValue nodes, for example (see vstte2012/Combinators.dfy). var annotation = AnnotatePotentialCandidate(expr); - annotation.ExportedCandidates.RemoveWhere(candidate => expr.SubExpressions.Contains(candidate.Expr)); + // Comparing by reference is fine here. Using sets could yield a small speedup + annotation.ExportedCandidates.RemoveAll(candidate => expr.SubExpressions.Contains(candidate.Expr)); return annotation; } @@ -286,39 +342,30 @@ namespace Microsoft.Dafny { } } - private static IEnumerable> AllSubsets(IEnumerable source) { + private static IEnumerable> AllNonEmptySubsets(IEnumerable source) { List all = new List(source); foreach (var subset in AllSubsets(all, 0)) { - yield return new HashSet(subset); + if (subset.Count > 0) { + yield return subset; + } } } - private static bool MentionsAll(ISet multiCandidate, List vars) { - return vars.All(x => multiCandidate.Any(candidate => candidate.Variables.Contains(x))); //TODO Perfs? - } - private static bool DefaultCandidateFilteringFunction(TriggerCandidate candidate, QuantifierExpr quantifier) { //FIXME this will miss rewritten expressions (CleanupExpr) - //FIXME does PotentialMatchingLoops really need to be a field? - + candidate.PotentialMatchingLoops = ExprExtensions.SubexpressionsMatchingTrigger(candidate.Expr, quantifier).ToList(); return true; - - - candidate.PotentialMatchingLoops = ExprExtensions.CouldCauseCycle(candidate.Expr, quantifier).ToArray(); - if (candidate.PotentialMatchingLoops.Any()) { - DebugTriggers("Trigger {0} for quantifier {1} could cause a matching loop, due to terms {2}.", - Printer.ExprToString(candidate.Expr), Printer.ExprToString(quantifier), - String.Join(", ", candidate.PotentialMatchingLoops.Select(e => Printer.ExprToString(e)))); - } - return !candidate.PotentialMatchingLoops.Any(); } - private static bool DefaultMultiCandidateFilteringFunction(ISet multiCandidate, QuantifierExpr quantifier) { - return MentionsAll(multiCandidate, quantifier.BoundVars); + private static bool DefaultMultiCandidateFilteringFunction(MultiTriggerCandidate multiCandidate, QuantifierExpr quantifier) { + if (multiCandidate.PotentialMatchingLoops.Any()) { + multiCandidate.Tags.Add(String.Format("matching loop with {0}", String.Join(", ", multiCandidate.PotentialMatchingLoops.Select(Printer.ExprToString)))); + } + return multiCandidate.MentionsAll(quantifier.BoundVars) && !multiCandidate.PotentialMatchingLoops.Any(); } - private static double DefaultMultiCandidateScoringFunction(ISet multi_candidates) { - return 1; + private static double DefaultMultiCandidateScoringFunction(MultiTriggerCandidate multi_candidate) { + return 1.0; } private static IEnumerable DefaultMultiCandidateSelectionFunction(List multi_candidates) { @@ -327,23 +374,23 @@ namespace Microsoft.Dafny { // CLEMENT: Make these customizable internal Func CandidateFilteringFunction = DefaultCandidateFilteringFunction; - internal Func, QuantifierExpr, bool> MultiCandidateFilteringFunction = DefaultMultiCandidateFilteringFunction; - internal Func, double> MultiCandidateScoringFunction = DefaultMultiCandidateScoringFunction; + internal Func MultiCandidateFilteringFunction = DefaultMultiCandidateFilteringFunction; + internal Func MultiCandidateScoringFunction = DefaultMultiCandidateScoringFunction; internal Func, IEnumerable> MultiCandidateSelectionFunction = DefaultMultiCandidateSelectionFunction; struct MultiCandidatesCollection { - internal ISet AllCandidates; + internal List AllCandidates; internal List SelectedCandidates; internal List RejectedCandidates; - internal List> FilteredMultiCandidates; - internal List ScoredMultiCandidates; internal List SelectedMultiCandidates; + internal List RejectedMultiCandidates; + internal List FinalMultiCandidates; public MultiCandidatesCollection(QuantifierExpr quantifier, TriggerAnnotation annotation, Func CandidateFilteringFunction, - Func, QuantifierExpr, bool> MultiCandidateFilteringFunction, - Func, double> MultiCandidateScoringFunction, + Func MultiCandidateFilteringFunction, + Func MultiCandidateScoringFunction, Func, IEnumerable> MultiCandidateSelectionFunction) { Contract.Requires(annotation != null); @@ -353,18 +400,20 @@ namespace Microsoft.Dafny { Contract.Requires(MultiCandidateScoringFunction != null); Contract.Requires(MultiCandidateSelectionFunction != null); - AllCandidates = annotation.PrivateCandidates; - Partition(AllCandidates, x => CandidateFilteringFunction(x, quantifier), out SelectedCandidates, out RejectedCandidates); - FilteredMultiCandidates = AllSubsets(SelectedCandidates).Where(t => MultiCandidateFilteringFunction(t, quantifier)).ToList(); - ScoredMultiCandidates = FilteredMultiCandidates.Select(candidates => new MultiTriggerCandidate { Candidates = candidates, Score = MultiCandidateScoringFunction(candidates) }).ToList(); - SelectedMultiCandidates = MultiCandidateSelectionFunction(ScoredMultiCandidates).ToList(); + AllCandidates = annotation.PrivateCandidates.Distinct(TriggerCandidateComparer.Instance).ToList(); + Partition(AllCandidates, + x => CandidateFilteringFunction(x, quantifier), out SelectedCandidates, out RejectedCandidates); + Partition(AllNonEmptySubsets(SelectedCandidates).Select(s => new MultiTriggerCandidate(s)), + x => MultiCandidateFilteringFunction(x, quantifier), out SelectedMultiCandidates, out RejectedMultiCandidates); + SelectedMultiCandidates.Iter(x => x.Score = MultiCandidateScoringFunction(x)); + FinalMultiCandidates = MultiCandidateSelectionFunction(SelectedMultiCandidates).ToList(); } - private static void Partition(IEnumerable AllCandidates, Func CandidateFilteringFunction, out List SelectedCandidates, out List RejectedCandidates) { - SelectedCandidates = new List(); - RejectedCandidates = new List(); - foreach (var c in AllCandidates) { - (CandidateFilteringFunction(c) ? SelectedCandidates : RejectedCandidates).Add(c); + private static void Partition(IEnumerable elements, Func predicate, out List positive, out List negative) { + positive = new List(); + negative = new List(); + foreach (var c in elements) { + (predicate(c) ? positive : negative).Add(c); } } @@ -372,11 +421,11 @@ namespace Microsoft.Dafny { if (AllCandidates.Count == 0) { return "No triggers found in the body of this quantifier."; } else if (SelectedCandidates.Count == 0) { - return String.Format("No suitable triggers found. Candidate building blocks for a good trigger where [{0}], but no subset of these terms passed the initial selection stage.", String.Join(", ", SelectedCandidates)); - } else if (FilteredMultiCandidates.Count == 0) { - return String.Format("No suitable set of triggers found. Candidate building blocks for a good trigger where [{0}], but no subset of these terms passed the subset selection stage.", String.Join(", ", SelectedCandidates)); + return String.Format("No suitable triggers found. Candidate building blocks for a good trigger where [{0}], but none these terms passed the initial selection stage.", String.Join(", ", AllCandidates)); } else if (SelectedMultiCandidates.Count == 0) { - return String.Format("No suitable set of triggers found. Candidates where [{0}], but none passed the final selection stage.", String.Join(", ", ScoredMultiCandidates)); + return String.Format("No suitable set of triggers found. Candidate building blocks for a good trigger where [{0}], but no subset of these terms passed the subset selection stage.", String.Join(", ", SelectedCandidates)); + } else if (FinalMultiCandidates.Count == 0) { + return String.Format("No suitable set of triggers found. Candidates where [{0}], but none passed the final selection stage.", String.Join(", ", SelectedMultiCandidates)); } else { return null; } @@ -402,14 +451,14 @@ namespace Microsoft.Dafny { var indent = " "; repr.Append(" All:"); WriteListOfCandidates(repr, indent, AllCandidates); + repr.Append(" Selected1:"); + WriteListOfCandidates(repr, indent, SelectedCandidates); repr.Append(" PreFilter:"); - WriteListOfCandidates(repr, indent, AllSubsets(AllCandidates).Select(c => String.Join(", ", c))); - repr.Append(" Filtered:"); - WriteListOfCandidates(repr, indent, FilteredMultiCandidates.Select(c => String.Join(", ", c))); - repr.Append(" Scored:"); - WriteListOfCandidates(repr, indent, ScoredMultiCandidates); - repr.Append(" Selected:"); - WriteListOfCandidates(repr, indent, SelectedMultiCandidates); + WriteListOfCandidates(repr, indent, AllNonEmptySubsets(AllCandidates).Select(c => String.Join(", ", c))); + repr.Append(" SelectedMulti:"); + WriteListOfCandidates(repr, indent, SelectedMultiCandidates.Select(c => String.Join(", ", c))); + repr.Append(" Final:"); + WriteListOfCandidates(repr, indent, FinalMultiCandidates); return repr.ToString(); } } @@ -430,20 +479,18 @@ namespace Microsoft.Dafny { } var multi_candidates = PickMultiTriggers(quantifier); - foreach (var multi_candidate in multi_candidates.SelectedMultiCandidates) { //TODO: error message for when no triggers found + foreach (var multi_candidate in multi_candidates.FinalMultiCandidates) { //TODO: error message for when no triggers found quantifier.Attributes = new Attributes("trigger", multi_candidate.Candidates.Select(t => t.Expr).ToList(), quantifier.Attributes); } - // FIXME: Cleanup - if (multi_candidates.RejectedCandidates.Any()) { - var tooltip = "Rejected: " + String.Join(Environment.NewLine + " ", multi_candidates.RejectedCandidates.Select( - candidate => "{:trigger " + Printer.ExprToString(candidate.Expr) + "} (could loop with " + Printer.ExprToString(candidate.PotentialMatchingLoops[0]) + ")")); + if (multi_candidates.RejectedMultiCandidates.Any()) { + var tooltip = JoinStringsWithHeader("Rejected: ", multi_candidates.RejectedMultiCandidates.Where(candidate => candidate.Tags != null) + .Select(candidate => candidate.AsDafnyAttributeString(true, true))); Resolver.ReportAdditionalInformation(quantifier.tok, tooltip, quantifier.tok.val.Length); //CLEMENT Check this } - if (multi_candidates.SelectedMultiCandidates.Any()) { - var tooltip = "Triggers: " + String.Join(Environment.NewLine + " ", multi_candidates.SelectedMultiCandidates.Select( - multi_candidate => "{:trigger " + String.Join(", ", multi_candidate.Candidates.Select(t => Printer.ExprToString(t.Expr))) + "}")); + if (multi_candidates.FinalMultiCandidates.Any()) { + var tooltip = JoinStringsWithHeader("Triggers: ", multi_candidates.FinalMultiCandidates.Select(multi_candidate => multi_candidate.AsDafnyAttributeString())); Resolver.ReportAdditionalInformation(quantifier.tok, tooltip, quantifier.tok.val.Length); //CLEMENT Check this } @@ -453,6 +500,10 @@ namespace Microsoft.Dafny { } } + private string JoinStringsWithHeader(string header, IEnumerable lines) { + return header + String.Join(Environment.NewLine + new String(' ', header.Length), lines); + } + private void AddTriggers_Internal() { foreach (var quantifier in quantifiers) { AddTrigger(quantifier); @@ -543,7 +594,25 @@ namespace Microsoft.Dafny { } } - private static bool ExpressionEq(this Expression expr1, Expression expr2) { + internal class EqExpressionComparer : IEqualityComparer { //FIXME + private static EqExpressionComparer singleton; + internal static EqExpressionComparer Instance { + get { return singleton == null ? (singleton = new EqExpressionComparer()) : singleton; } + } + + private EqExpressionComparer() { } + + public bool Equals(Expression x, Expression y) { + return x == null && y == null || + x != null && y != null && x.ExpressionEq(y); + } + + public int GetHashCode(Expression obj) { + return 1; + } + } + + internal static bool ExpressionEq(this Expression expr1, Expression expr2) { expr1 = GetResolved(expr1); expr2 = GetResolved(expr2); @@ -575,12 +644,17 @@ namespace Microsoft.Dafny { return expr.MatchesTrigger(trigger, holes, new Dictionary()); } - internal static IEnumerable CouldCauseCycle(Expression trigger, QuantifierExpr quantifier) { //FIXME Term or bound? - //FIXME could be optimized by looking at the bindings instead of doing full equality - return quantifier.Term.AllSubExpressions().Where(e => e.MatchesTrigger(trigger, new HashSet(quantifier.BoundVars)) && !e.ExpressionEq(trigger)); + internal static IEnumerable SubexpressionsMatchingTrigger(Expression trigger, QuantifierExpr quantifier) { + return quantifier.Term.AllSubExpressions().Where(e => e.MatchesTrigger(trigger, new HashSet(quantifier.BoundVars))); } private static bool SameLists(IEnumerable list1, IEnumerable list2, Func comparer) { + if (ReferenceEquals(list1, list2)) { + return true; + } else if ((list1 == null) != (list2 == null)) { + return false; + } + var it1 = list1.GetEnumerator(); var it2 = list2.GetEnumerator(); bool it1_has, it2_has; -- cgit v1.2.3 From dc721907edfd78d6e12ed4155b14c960416791c4 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Tue, 14 Jul 2015 19:46:49 -0700 Subject: Fix broken interaction between triggers and inlining of function calls The problem was that inlining would replace formals with arguments in triggers as well, causing invalid expressions ("trigger killers") to pop up in triggers after inlining. This fix disables inlining if it can't be determined that it won't lead to an invalid trigger. If that procedure is incomplete, then that's only by a narrow margin, as the checks actually ensure that the formals that are getting trigger killers are indeed used in triggers. --- Source/Dafny/Resolver.cs | 2 +- Source/Dafny/Translator.cs | 59 +++++++++++++++++++++++++- Source/Dafny/TriggerGenerator.cs | 24 ++++++----- Test/new-tests/trigger-in-predicate.dfy | 15 +++++++ Test/new-tests/trigger-in-predicate.dfy.expect | 4 ++ 5 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 Test/new-tests/trigger-in-predicate.dfy create mode 100644 Test/new-tests/trigger-in-predicate.dfy.expect diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 300d4985..c5b19d8c 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -268,7 +268,7 @@ namespace Microsoft.Dafny Contract.Invariant(cce.NonNullDictionaryAndValues(datatypeCtors) && Contract.ForAll(datatypeCtors.Values, v => cce.NonNullDictionaryAndValues(v))); } - public void DefaultInformationReporter(AdditionalInformation info) { + public static void DefaultInformationReporter(AdditionalInformation info) { Console.WriteLine("{0}({1},{2}): Info: {3}", DafnyOptions.Clo.UseBaseNameForFileName ? System.IO.Path.GetFileName(info.Token.filename) : info.Token.filename, info.Token.line, info.Token.col, info.Text); diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 8042033b..861e2859 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -12699,9 +12699,8 @@ namespace Microsoft.Dafny { // that needed to be proved about the function was proved already in the previous module, even without the body definition). } else if (!FunctionBodyIsAvailable(f, currentModule)) { // Don't inline opaque functions or foreign protected functions - } else { + } else if (CanSafelyInline(fexp, f)) { // inline this body - // CLEMENT: This is a problem for triggers var body = GetSubstitutedBody(fexp, f, false); var typeSpecializedBody = GetSubstitutedBody(fexp, f, true); var typeSpecializedResultType = Resolver.SubstType(f.ResultType, fexp.TypeArgumentSubstitutions); @@ -12762,6 +12761,15 @@ namespace Microsoft.Dafny { splits.Add(new SplitExprInfo(SplitExprInfo.K.Free, fr)); return true; + } else { + // Skip inlining, as it would cause arbitrary expressions to pop up in the trigger + // CLEMENT: Report inlining issue in a VS plugin friendly way + var info = new AdditionalInformation { + Token = fexp.tok, + Length = fexp.tok.val.Length, + Text = "This call cannot be safely inlined.", + }; + Resolver.DefaultInformationReporter(info); } } @@ -12902,6 +12910,53 @@ namespace Microsoft.Dafny { return splitHappened; } + private bool CanSafelyInline(FunctionCallExpr fexp, Function f) { + var visitor = new TriggersExplorer(); + + visitor.Visit(f.Body); + foreach (var expr in f.Ens) { visitor.Visit(expr); } + foreach (var expr in f.Req) { visitor.Visit(expr); } + // CLEMENT: Anything else? + + return f.Formals.Zip(fexp.Args).All(formal_concrete => CanSafelySubstitute(visitor.TriggerVariables, formal_concrete.Item1, formal_concrete.Item2)); + } + + private bool CanSafelySubstitute(ISet protectedVariables, IVariable variable, Expression substitution) { + return !(protectedVariables.Contains(variable) && TriggerGenerator.IsTriggerKiller(substitution)); + } + + private class VariablesCollector: BottomUpVisitor { + internal ISet variables; + + internal VariablesCollector() { + this.variables = new HashSet(); + } + + protected override void VisitOneExpr(Expression expr) { + if (expr is IdentifierExpr) { + variables.Add((expr as IdentifierExpr).Var); + } + } + } + + private class TriggersExplorer : BottomUpVisitor { + VariablesCollector collector; + + internal ISet TriggerVariables { get { return collector.variables; } } + + internal TriggersExplorer() { + collector = new VariablesCollector(); + } + + 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); + } + } + } + } + private Expression GetSubstitutedBody(FunctionCallExpr fexp, Function f, bool specializeTypeParameters) { Contract.Requires(fexp != null); Contract.Requires(f != null); diff --git a/Source/Dafny/TriggerGenerator.cs b/Source/Dafny/TriggerGenerator.cs index 4697b149..40cd67e4 100644 --- a/Source/Dafny/TriggerGenerator.cs +++ b/Source/Dafny/TriggerGenerator.cs @@ -145,13 +145,13 @@ namespace Microsoft.Dafny { List quantifiers; Dictionary annotations; - Resolver Resolver; + Action AdditionalInformationReporter; - private TriggerGenerator(Resolver resolver) { - Contract.Requires(Resolver != null); + private TriggerGenerator(Action additionalInformationReporter) { + Contract.Requires(additionalInformationReporter != null); this.quantifiers = new List(); this.annotations = new Dictionary(); - this.Resolver = resolver; + this.AdditionalInformationReporter = additionalInformationReporter; } private List MergeAlterFirst(List a, List b) { @@ -470,7 +470,6 @@ namespace Microsoft.Dafny { } private void AddTrigger(QuantifierExpr quantifier) { - // This call is elided when triggers debugging is disabled. DebugTriggers(" Final results:\n{0}", PickMultiTriggers(quantifier)); if (quantifier.Attributes.AsEnumerable().Any(aa => aa.Name == "trigger" || aa.Name == "no_trigger")) { @@ -486,12 +485,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))); - Resolver.ReportAdditionalInformation(quantifier.tok, tooltip, quantifier.tok.val.Length); //CLEMENT Check this + AdditionalInformationReporter(quantifier.tok, tooltip, quantifier.tok.val.Length); //CLEMENT Check this } if (multi_candidates.FinalMultiCandidates.Any()) { var tooltip = JoinStringsWithHeader("Triggers: ", multi_candidates.FinalMultiCandidates.Select(multi_candidate => multi_candidate.AsDafnyAttributeString())); - Resolver.ReportAdditionalInformation(quantifier.tok, tooltip, quantifier.tok.val.Length); //CLEMENT Check this + AdditionalInformationReporter(quantifier.tok, tooltip, quantifier.tok.val.Length); //CLEMENT Check this } string warning = multi_candidates.Warning(); @@ -500,6 +499,11 @@ namespace Microsoft.Dafny { } } + internal static bool IsTriggerKiller(Expression expr) { + var annotation = new TriggerGenerator((x, y, z) => { }).Annotate(expr); + return annotation.IsTriggerKiller; + } + private string JoinStringsWithHeader(string header, IEnumerable lines) { return header + String.Join(Environment.NewLine + new String(' ', header.Length), lines); } @@ -525,7 +529,7 @@ namespace Microsoft.Dafny { return; DebugTriggers("== From {0} visiting expr: {1}", new StackFrame(1).GetMethod().Name, Printer.ExprToString(root)); - TriggerGenerator generator = new TriggerGenerator(resolver); + TriggerGenerator generator = new TriggerGenerator(resolver.ReportAdditionalInformation); generator.AddTriggers_Internal(root); } @@ -534,7 +538,7 @@ namespace Microsoft.Dafny { return; DebugTriggers("== From {0} visiting statement: {1}", new StackFrame(1).GetMethod().Name, Printer.StatementToString(root)); - TriggerGenerator generator = new TriggerGenerator(resolver); + TriggerGenerator generator = new TriggerGenerator(resolver.ReportAdditionalInformation); generator.AddTriggers_Internal(root); } @@ -596,7 +600,7 @@ namespace Microsoft.Dafny { internal class EqExpressionComparer : IEqualityComparer { //FIXME private static EqExpressionComparer singleton; - internal static EqExpressionComparer Instance { + internal static EqExpressionComparer Instance { get { return singleton == null ? (singleton = new EqExpressionComparer()) : singleton; } } diff --git a/Test/new-tests/trigger-in-predicate.dfy b/Test/new-tests/trigger-in-predicate.dfy new file mode 100644 index 00000000..880d3d1d --- /dev/null +++ b/Test/new-tests/trigger-in-predicate.dfy @@ -0,0 +1,15 @@ +// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +predicate A(x: bool, y: bool) { x } + +predicate B(x: bool, z: bool) { forall y {:trigger A(x, y) } :: A(x, y) && z } + +// Inlining is disabled here to prevent pollution of the trigger in B +method C() requires B(true || false, true) {} + +// Inlining should work fine here +method C'() requires B(true, true) {} + +// Inlining should work fine here +method C''() requires B(true, true && false) {} diff --git a/Test/new-tests/trigger-in-predicate.dfy.expect b/Test/new-tests/trigger-in-predicate.dfy.expect new file mode 100644 index 00000000..211bc9a2 --- /dev/null +++ b/Test/new-tests/trigger-in-predicate.dfy.expect @@ -0,0 +1,4 @@ +c:/MSR/dafny-triggers/Test/new-tests/trigger-in-predicate.dfy(9,21): Info: This call cannot be safely inlined. +c:/MSR/dafny-triggers/Test/new-tests/trigger-in-predicate.dfy(9,21): Info: This call cannot be safely inlined. + +Dafny program verifier finished with 8 verified, 0 errors -- cgit v1.2.3 From 7a739b4e396554ee004abc42829351f126a224b7 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Wed, 15 Jul 2015 10:03:30 -0700 Subject: Weaken the trigger cycling check to allow more patterns --- Source/Dafny/TriggerGenerator.cs | 80 +++++++++++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 14 deletions(-) diff --git a/Source/Dafny/TriggerGenerator.cs b/Source/Dafny/TriggerGenerator.cs index 40cd67e4..dc99e609 100644 --- a/Source/Dafny/TriggerGenerator.cs +++ b/Source/Dafny/TriggerGenerator.cs @@ -1,4 +1,4 @@ -#define DEBUG_AUTO_TRIGGERS +// #define DEBUG_AUTO_TRIGGERS #define THROW_UNSUPPORTED_COMPARISONS using System; @@ -28,7 +28,7 @@ namespace Microsoft.Dafny { class TriggerCandidate { // TODO Hashing is broken (duplicates can pop up) internal Expression Expr; internal ISet Variables; - internal List PotentialMatchingLoops; + internal List MatchesInQuantifierBody; public override string ToString() { return Printer.ExprToString(Expr); @@ -57,15 +57,15 @@ namespace Microsoft.Dafny { internal List Candidates; internal List Tags; internal double Score; - - private List potentialMatchingLoops; - internal List PotentialMatchingLoops { + + private List potentialMatchingLoops; + internal List PotentialMatchingLoops { get { if (potentialMatchingLoops == null) { //FIXME could be optimized by looking at the bindings instead of doing full equality var candidates = Candidates.Distinct(TriggerCandidateComparer.Instance); - potentialMatchingLoops = candidates.SelectMany(candidate => candidate.PotentialMatchingLoops) - .Distinct(ExprExtensions.EqExpressionComparer.Instance).Where(e => !candidates.Any(c => c.Expr.ExpressionEq(e))).ToList(); + potentialMatchingLoops = candidates.SelectMany(candidate => candidate.MatchesInQuantifierBody) + .Distinct(ExprExtensions.TriggerMatchComparer.Instance).Where(tm => tm.CouldCauseLoops(candidates)).ToList(); } return potentialMatchingLoops; @@ -353,13 +353,13 @@ namespace Microsoft.Dafny { private static bool DefaultCandidateFilteringFunction(TriggerCandidate candidate, QuantifierExpr quantifier) { //FIXME this will miss rewritten expressions (CleanupExpr) - candidate.PotentialMatchingLoops = ExprExtensions.SubexpressionsMatchingTrigger(candidate.Expr, quantifier).ToList(); + candidate.MatchesInQuantifierBody = quantifier.SubexpressionsMatchingTrigger(candidate.Expr).ToList(); return true; } private static bool DefaultMultiCandidateFilteringFunction(MultiTriggerCandidate multiCandidate, QuantifierExpr quantifier) { if (multiCandidate.PotentialMatchingLoops.Any()) { - multiCandidate.Tags.Add(String.Format("matching loop with {0}", String.Join(", ", multiCandidate.PotentialMatchingLoops.Select(Printer.ExprToString)))); + multiCandidate.Tags.Add(String.Format("matching loop with {0}", String.Join(", ", multiCandidate.PotentialMatchingLoops.Select(tm => Printer.ExprToString(tm.Expr))))); } return multiCandidate.MentionsAll(quantifier.BoundVars) && !multiCandidate.PotentialMatchingLoops.Any(); } @@ -616,13 +616,41 @@ namespace Microsoft.Dafny { } } + internal class TriggerMatchComparer : IEqualityComparer { //FIXME + private static TriggerMatchComparer singleton; + internal static TriggerMatchComparer Instance { + get { return singleton == null ? (singleton = new TriggerMatchComparer()) : singleton; } + } + + private TriggerMatchComparer() { } + + public bool Equals(TriggerMatch x, TriggerMatch y) { + return ExpressionEq(x.Expr, y.Expr); + } + + public int GetHashCode(TriggerMatch obj) { + return 1; + } + } + internal static bool ExpressionEq(this Expression expr1, Expression expr2) { expr1 = GetResolved(expr1); expr2 = GetResolved(expr2); - + return ShallowEq_Top(expr1, expr2) && SameLists(expr1.SubExpressions, expr2.SubExpressions, (e1, e2) => ExpressionEq(e1, e2)); } + internal static bool ExpressionEqModuloVariableNames(this Expression expr1, Expression expr2) { + expr1 = GetResolved(expr1); + expr2 = GetResolved(expr2); + + if (expr1 is IdentifierExpr) { + return expr2 is IdentifierExpr; + } + + return ShallowEq_Top(expr1, expr2) && SameLists(expr1.SubExpressions, expr2.SubExpressions, (e1, e2) => ExpressionEqModuloVariableNames(e1, e2)); + } + private static bool MatchesTrigger(this Expression expr, Expression trigger, ISet holes, Dictionary bindings) { expr = GetResolved(expr); trigger = GetResolved(trigger); @@ -644,12 +672,36 @@ namespace Microsoft.Dafny { return ShallowEq_Top(expr, trigger) && SameLists(expr.SubExpressions, trigger.SubExpressions, (e1, e2) => MatchesTrigger(e1, e2, holes, bindings)); } - private static bool MatchesTrigger(this Expression expr, Expression trigger, ISet holes) { - return expr.MatchesTrigger(trigger, holes, new Dictionary()); + internal struct TriggerMatch { + internal Expression Expr; + internal Dictionary Bindings; + + internal bool CouldCauseLoops(IEnumerable candidates) { + // A match for a trigger in the body of a quantifier can be a problem if + // it yields to a matching loop: for example, f(x) is a bad trigger in + // forall x, y :: f(x) = f(f(x)) + // In general, any such match can lead to a loop, but two special cases + // will only lead to a finite number of instantiations: + // 1. The match equals one of the triggers in the set of triggers under + // consideration. For example, { f(x) } a bad trigger above, but the + // pair { f(x), f(f(x)) } is fine (instantiating won't yield new + // matches) + // 2. The match only differs from one of these triggers by variable + // names. This is a superset of the previous case. + var expr = Expr; + return !candidates.Any(c => c.Expr.ExpressionEqModuloVariableNames(expr)); + } + } + + private static TriggerMatch? MatchAgainst(this Expression expr, Expression trigger, ISet holes) { + var bindings = new Dictionary(); + return expr.MatchesTrigger(trigger, holes, bindings) ? new TriggerMatch { Expr = expr, Bindings = bindings } : (TriggerMatch?)null; } - internal static IEnumerable SubexpressionsMatchingTrigger(Expression trigger, QuantifierExpr quantifier) { - return quantifier.Term.AllSubExpressions().Where(e => e.MatchesTrigger(trigger, new HashSet(quantifier.BoundVars))); + internal static IEnumerable SubexpressionsMatchingTrigger(this QuantifierExpr quantifier, Expression trigger) { + return quantifier.Term.AllSubExpressions() + .Select(e => e.MatchAgainst(trigger, new HashSet(quantifier.BoundVars))) + .Where(e => e.HasValue).Select(e => e.Value); } private static bool SameLists(IEnumerable list1, IEnumerable list2, Func comparer) { -- cgit v1.2.3 From 6138ea13b5116eef41eeec5b59a13cc9c12ffcfa Mon Sep 17 00:00:00 2001 From: Michael Lowell Roberts Date: Wed, 15 Jul 2015 10:30:35 -0700 Subject: [IronDafny] fix for ambiguous identifier error. --- Source/Dafny/Resolver.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 460859db..78bc02ff 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -931,7 +931,11 @@ namespace Microsoft.Dafny if (useImports || string.Equals(kv.Key, "_default", StringComparison.InvariantCulture)) { TopLevelDecl d; if (sig.TopLevels.TryGetValue(kv.Key, out d)) { - sig.TopLevels[kv.Key] = AmbiguousTopLevelDecl.Create(moduleDef, d, kv.Value); + if (DafnyOptions.O.IronDafny && kv.Value.ClonedFrom == d) { + sig.TopLevels[kv.Key] = kv.Value; + } else { + sig.TopLevels[kv.Key] = AmbiguousTopLevelDecl.Create(moduleDef, d, kv.Value); + } } else { sig.TopLevels.Add(kv.Key, kv.Value); } -- cgit v1.2.3 From 2e93ec8ece4d029809bf092c779756e0b18d7a6c Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 16 Jul 2015 11:16:44 -0700 Subject: Add license text and Linux setup notes --- INSTALL | 34 ++++++++++++++++++++++++++++++++++ LICENSE | 22 ++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 INSTALL create mode 100644 LICENSE diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..2a70587e --- /dev/null +++ b/INSTALL @@ -0,0 +1,34 @@ +Building on Linux +================= + +1. Create an empty base directory + + mkdir BASE-DRIECTORY + cd BASE-DRIECTORY + +2. Download and build Boogie: + + git clone https://github.com/boogie-org/boogie + cd boogie + mozroots --import --sync + wget https://nuget.org/nuget.exe + mono ./nuget.exe restore Source/Boogie.sln + xbuild Source/Boogie.sln + +3. Download and build Dafny: + + hg clone https://hg.codeplex.com/dafny + cd dafny/Sources/ + xbuild Dafny.sln + +4. Download and build Z3 + + git clone https://github.com/Z3Prover/z3.git + cd z3 + ./configure && make && sudo make install + +5. Symlink the z3 binaries so that Boogie and Z3 can find them: + + cd BASE-DIRECTORY + ln -s /usr/bin/z3 boogie/Binaries/z3.exe + ln -s /usr/bin/z3 dafny/Binaries/z3.exe diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..327f6778 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +Microsoft Public License (MS-PL) + +This license governs use of the accompanying software. If you use the software, you +accept this license. If you do not accept the license, do not use the software. + +1. Definitions +The terms "reproduce," "reproduction," "derivative works," and "distribution" have the +same meaning here as under U.S. copyright law. +A "contribution" is the original software, or any additions or changes to the software. +A "contributor" is any person that distributes its contribution under this license. +"Licensed patents" are a contributor's patent claims that read directly on its contribution. + +2. Grant of Rights +(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. +(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. + +3. Conditions and Limitations +(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. +(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. +(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. +(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. +(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. -- cgit v1.2.3 From 9f8cb23ad0662cea28f681872236277c2af2432e Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 16 Jul 2015 14:27:28 -0700 Subject: Strip literals from triggers --- Source/Dafny/Translator.cs | 78 +++++++++++++++++++++++--------------- Test/dafny0/LitTriggers.dfy | 35 +++++++++++++++++ Test/dafny0/LitTriggers.dfy.expect | 2 + 3 files changed, 85 insertions(+), 30 deletions(-) create mode 100644 Test/dafny0/LitTriggers.dfy create mode 100644 Test/dafny0/LitTriggers.dfy.expect diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 861e2859..4a7ac006 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -10302,7 +10302,8 @@ namespace Microsoft.Dafny { public readonly Bpl.Expr layerInterCluster; public readonly Bpl.Expr layerIntraCluster = null; // a value of null says to do the same as for inter-cluster calls public int Statistics_CustomLayerFunctionCount = 0; - public readonly bool ProducingCoCertificates = false; + public readonly bool ProducingCoCertificates = false; // CLEMENT Where is this used? + public readonly bool stripLits = false; [ContractInvariantMethod] void ObjectInvariant() { @@ -10320,7 +10321,7 @@ namespace Microsoft.Dafny { /// This is a general constructor, but takes the layerInterCluster as an int. /// ExpressionTranslator(Translator translator, PredefinedDecls predef, Bpl.Expr heap, string thisVar, - Function applyLimited_CurrentFunction, int layerInterCluster, Bpl.Expr layerIntraCluster, string modifiesFrame) { + Function applyLimited_CurrentFunction, int layerInterCluster, Bpl.Expr layerIntraCluster, string modifiesFrame, bool stripLits) { Contract.Requires(translator != null); Contract.Requires(predef != null); @@ -10337,6 +10338,7 @@ namespace Microsoft.Dafny { this.layerInterCluster = LayerN(layerInterCluster); this.layerIntraCluster = layerIntraCluster; this.modifiesFrame = modifiesFrame; + this.stripLits = stripLits; } /// @@ -10344,7 +10346,7 @@ namespace Microsoft.Dafny { /// one ExpressionTranslator is constructed from another, unchanged parameters are just copied in. /// ExpressionTranslator(Translator translator, PredefinedDecls predef, Bpl.Expr heap, string thisVar, - Function applyLimited_CurrentFunction, Bpl.Expr layerInterCluster, Bpl.Expr layerIntraCluster, string modifiesFrame) { + Function applyLimited_CurrentFunction, Bpl.Expr layerInterCluster, Bpl.Expr layerIntraCluster, string modifiesFrame, bool stripLits) { Contract.Requires(translator != null); Contract.Requires(predef != null); @@ -10365,6 +10367,7 @@ namespace Microsoft.Dafny { this.layerIntraCluster = layerIntraCluster; } this.modifiesFrame = modifiesFrame; + this.stripLits = stripLits; } public ExpressionTranslator(Translator translator, PredefinedDecls predef, IToken heapToken) @@ -10394,7 +10397,7 @@ namespace Microsoft.Dafny { } public ExpressionTranslator(Translator translator, PredefinedDecls predef, Bpl.Expr heap, string thisVar) - : this(translator, predef, heap, thisVar, null, 1, null, "$_Frame") { + : this(translator, predef, heap, thisVar, null, 1, null, "$_Frame", false) { Contract.Requires(translator != null); Contract.Requires(predef != null); Contract.Requires(heap != null); @@ -10402,14 +10405,14 @@ namespace Microsoft.Dafny { } public ExpressionTranslator(ExpressionTranslator etran, Bpl.Expr heap) - : this(etran.translator, etran.predef, heap, etran.This, etran.applyLimited_CurrentFunction, etran.layerInterCluster, etran.layerIntraCluster, etran.modifiesFrame) + : this(etran.translator, etran.predef, heap, etran.This, etran.applyLimited_CurrentFunction, etran.layerInterCluster, etran.layerIntraCluster, etran.modifiesFrame, etran.stripLits) { Contract.Requires(etran != null); Contract.Requires(heap != null); } public ExpressionTranslator(ExpressionTranslator etran, string modifiesFrame) - : this(etran.translator, etran.predef, etran.HeapExpr, etran.This, etran.applyLimited_CurrentFunction, etran.layerInterCluster, etran.layerIntraCluster, modifiesFrame) { + : this(etran.translator, etran.predef, etran.HeapExpr, etran.This, etran.applyLimited_CurrentFunction, etran.layerInterCluster, etran.layerIntraCluster, modifiesFrame, etran.stripLits) { Contract.Requires(etran != null); Contract.Requires(modifiesFrame != null); } @@ -10420,7 +10423,7 @@ namespace Microsoft.Dafny { Contract.Ensures(Contract.Result() != null); if (oldEtran == null) { - oldEtran = new ExpressionTranslator(translator, predef, new Bpl.OldExpr(HeapExpr.tok, HeapExpr), This, applyLimited_CurrentFunction, layerInterCluster, layerIntraCluster, modifiesFrame); + oldEtran = new ExpressionTranslator(translator, predef, new Bpl.OldExpr(HeapExpr.tok, HeapExpr), This, applyLimited_CurrentFunction, layerInterCluster, layerIntraCluster, modifiesFrame, stripLits); oldEtran.oldEtran = oldEtran; } return oldEtran; @@ -10438,7 +10441,12 @@ namespace Microsoft.Dafny { Contract.Requires(layerArgument != null); Contract.Ensures(Contract.Result() != null); - return new ExpressionTranslator(translator, predef, HeapExpr, This, null, layerArgument, layerArgument, modifiesFrame); + return new ExpressionTranslator(translator, predef, HeapExpr, This, null, layerArgument, layerArgument, modifiesFrame, stripLits); + } + + public ExpressionTranslator WithNoLits() { + Contract.Ensures(Contract.Result() != null); + return new ExpressionTranslator(translator, predef, HeapExpr, This, null, layerInterCluster, layerIntraCluster, modifiesFrame, true); } public ExpressionTranslator LimitedFunctions(Function applyLimited_CurrentFunction, Bpl.Expr layerArgument) { @@ -10446,16 +10454,16 @@ namespace Microsoft.Dafny { Contract.Requires(layerArgument != null); Contract.Ensures(Contract.Result() != null); - return new ExpressionTranslator(translator, predef, HeapExpr, This, applyLimited_CurrentFunction, /* layerArgument */ layerInterCluster, layerArgument, modifiesFrame); + return new ExpressionTranslator(translator, predef, HeapExpr, This, applyLimited_CurrentFunction, /* layerArgument */ layerInterCluster, layerArgument, modifiesFrame, stripLits); } public ExpressionTranslator LayerOffset(int offset) { Contract.Requires(0 <= offset); Contract.Ensures(Contract.Result() != null); - var et = new ExpressionTranslator(translator, predef, HeapExpr, This, applyLimited_CurrentFunction, translator.LayerSucc(layerInterCluster, offset), layerIntraCluster, modifiesFrame); + var et = new ExpressionTranslator(translator, predef, HeapExpr, This, applyLimited_CurrentFunction, translator.LayerSucc(layerInterCluster, offset), layerIntraCluster, modifiesFrame, stripLits); if (this.oldEtran != null) { - var etOld = new ExpressionTranslator(translator, predef, Old.HeapExpr, This, applyLimited_CurrentFunction, translator.LayerSucc(layerInterCluster, offset), layerIntraCluster, modifiesFrame); + var etOld = new ExpressionTranslator(translator, predef, Old.HeapExpr, This, applyLimited_CurrentFunction, translator.LayerSucc(layerInterCluster, offset), layerIntraCluster, modifiesFrame, stripLits); etOld.oldEtran = etOld; et.oldEtran = etOld; } @@ -10538,6 +10546,14 @@ namespace Microsoft.Dafny { return translator.Substitute(e.Body, null, substMap); } + public Expr MaybeLit(Expr expr, Bpl.Type type) { + return stripLits ? expr : translator.Lit(expr, type); + } + + public Expr MaybeLit(Expr expr) { + return stripLits ? expr : translator.Lit(expr); + } + /// /// Translates Dafny expression "expr" into a Boogie expression. If the type of "expr" can be a boolean, then the /// token (source location) of the resulting expression is filled in (it wouldn't hurt if the token were always @@ -10554,7 +10570,7 @@ namespace Microsoft.Dafny { if (e.Value == null) { return predef.Null; } else if (e.Value is bool) { - return translator.Lit(new Bpl.LiteralExpr(e.tok, (bool)e.Value)); + return MaybeLit(new Bpl.LiteralExpr(e.tok, (bool)e.Value)); } else if (e is CharLiteralExpr) { // we expect e.Value to be a string representing exactly one char Bpl.Expr rawElement = null; // assignment to please compiler's definite assignment rule @@ -10563,7 +10579,7 @@ namespace Microsoft.Dafny { rawElement = translator.FunctionCall(expr.tok, BuiltinFunction.CharFromInt, null, Bpl.Expr.Literal((int)ch)); } Contract.Assert(rawElement != null); // there should have been an iteration of the loop above - return translator.Lit(rawElement, predef.CharType); + return MaybeLit(rawElement, predef.CharType); } else if (e is StringLiteralExpr) { var str = (StringLiteralExpr)e; Bpl.Expr seq = translator.FunctionCall(expr.tok, BuiltinFunction.SeqEmpty, predef.BoxType); @@ -10572,11 +10588,11 @@ namespace Microsoft.Dafny { Bpl.Expr elt = BoxIfNecessary(expr.tok, rawElement, Type.Char); seq = translator.FunctionCall(expr.tok, BuiltinFunction.SeqBuild, predef.BoxType, seq, elt); } - return translator.Lit(seq, translator.TrType(new SeqType(Type.Char))); + return MaybeLit(seq, translator.TrType(new SeqType(Type.Char))); } else if (e.Value is BigInteger) { - return translator.Lit(Bpl.Expr.Literal(Microsoft.Basetypes.BigNum.FromBigInt((BigInteger)e.Value))); + return MaybeLit(Bpl.Expr.Literal(Microsoft.Basetypes.BigNum.FromBigInt((BigInteger)e.Value))); } else if (e.Value is Basetypes.BigDec) { - return translator.Lit(Bpl.Expr.Literal((Basetypes.BigDec)e.Value)); + return MaybeLit(Bpl.Expr.Literal((Basetypes.BigDec)e.Value)); } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected literal } @@ -10642,7 +10658,7 @@ namespace Microsoft.Dafny { s = translator.FunctionCall(expr.tok, BuiltinFunction.SeqBuild, predef.BoxType, s, elt); } if (isLit) { - s = translator.Lit(s, predef.BoxType); + s = MaybeLit(s, predef.BoxType); } return s; @@ -10671,7 +10687,7 @@ namespace Microsoft.Dafny { new List {obj}); result = translator.CondApplyUnbox(expr.tok, result, field.Type, expr.Type); if (translator.IsLit(obj)) { - result = translator.Lit(result, translator.TrType(expr.Type)); + result = MaybeLit(result, translator.TrType(expr.Type)); } return result; } @@ -10746,7 +10762,7 @@ namespace Microsoft.Dafny { // if e0 == null && e1 == null, then we have the identity operation seq[..] == seq; if (isLit && (e0 != null || e1 != null)) { // Lit-lift the expression - seq = translator.Lit(seq, translator.TrType(expr.Type)); + seq = MaybeLit(seq, translator.TrType(expr.Type)); } return seq; } @@ -10857,7 +10873,7 @@ namespace Microsoft.Dafny { Expr result = new Bpl.NAryExpr(e.tok, new Bpl.FunctionCall(id), args); result = translator.CondApplyUnbox(e.tok, result, e.Function.ResultType, e.Type); if (returnLit && Translator.FunctionBodyIsAvailable(e.Function, translator.currentModule)) { - result = translator.Lit(result, ty); + result = MaybeLit(result, ty); } return result; } else if (expr is DatatypeValue) { @@ -10875,7 +10891,7 @@ namespace Microsoft.Dafny { Bpl.IdentifierExpr id = new Bpl.IdentifierExpr(dtv.tok, dtv.Ctor.FullName, predef.DatatypeType); Bpl.Expr ret = new Bpl.NAryExpr(dtv.tok, new Bpl.FunctionCall(id), args); if (isLit) { - ret = translator.Lit(ret, predef.DatatypeType); + ret = MaybeLit(ret, predef.DatatypeType); } return ret; } else if (expr is OldExpr) { @@ -10898,7 +10914,7 @@ namespace Microsoft.Dafny { Bpl.Expr arg = TrExpr(e.E); switch (e.Op) { case UnaryOpExpr.Opcode.Lit: - return translator.Lit(arg); + return MaybeLit(arg); case UnaryOpExpr.Opcode.Not: return Bpl.Expr.Unary(expr.tok, UnaryOperator.Opcode.Not, arg); case UnaryOpExpr.Opcode.Cardinality: @@ -10999,7 +11015,7 @@ namespace Microsoft.Dafny { bool liftLit = lit0 != null && lit1 != null; // NOTE(namin): We usually avoid keeping literals, because their presence might mess up triggers that do not expect them. // Still for equality-related operations, it's useful to keep them instead of lifting them, so that they can be propagated. - bool keepLits = false; + bool keepLits = false; // CLEMENT: I don't really understand this if (lit0 != null) { e0 = lit0; } @@ -11301,7 +11317,7 @@ namespace Microsoft.Dafny { var ae1 = keepLits ? oe1 : e1; Bpl.Expr re = Bpl.Expr.Binary(expr.tok, bOpcode, ae0, ae1); if (liftLit) { - re = translator.Lit(re, typ); + re = MaybeLit(re, typ); } return re; } else if (expr is TernaryExpr) { @@ -11350,7 +11366,7 @@ namespace Microsoft.Dafny { // 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); Expr layer = translator.LayerSucc(ly); - bodyEtran = new ExpressionTranslator(translator, predef, HeapExpr, This, applyLimited_CurrentFunction, layer, layer, modifiesFrame); + bodyEtran = new ExpressionTranslator(translator, predef, HeapExpr, This, applyLimited_CurrentFunction, layer, layer, modifiesFrame, stripLits); } if (Attributes.ContainsBool(e.Attributes, "heapQuantifier", ref _scratch)) { var h = BplBoundVar(e.Refresh("q$heap#", translator.CurrentIdGenerator), predef.HeapType, bvars); @@ -11366,11 +11382,12 @@ namespace Microsoft.Dafny { 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(bodyEtran.TrExpr(arg)); + tt.Add(argsEtran.TrExpr(arg)); } tr = new Bpl.Trigger(expr.tok, true, tt, tr); } @@ -11516,7 +11533,7 @@ namespace Microsoft.Dafny { new Bpl.LambdaExpr(e.tok, new List(), rdvars, null, BplImp(ante, consequent)); - return translator.Lit( + return MaybeLit( translator.FunctionCall(e.tok, BuiltinFunction.AtLayer, predef.HandleType, new Bpl.LambdaExpr(e.tok, new List(), lvars, null, translator.FunctionCall(e.tok, translator.Handle(e.BoundVars.Count), predef.BoxType, @@ -11821,7 +11838,7 @@ namespace Microsoft.Dafny { parms.Add(s); } else { var e = TrExpr(arg); - e = translator.RemoveLit(e); + e = translator.RemoveLit(e); // CLEMENT: Why? parms.Add(e); } } @@ -12503,14 +12520,15 @@ namespace Microsoft.Dafny { Bpl.Trigger TrTrigger(ExpressionTranslator etran, Attributes attribs, IToken tok, Dictionary substMap = null) { + var argsEtran = etran.WithNoLits(); Bpl.Trigger tr = null; foreach (var trigger in attribs.AsEnumerable().Where(aa => aa.Name == "trigger").Select(aa => aa.Args)) { List tt = new List(); foreach (var arg in trigger) { if (substMap == null) { - tt.Add(etran.TrExpr(arg)); + tt.Add(argsEtran.TrExpr(arg)); } else { - tt.Add(etran.TrExpr(Substitute(arg, null, substMap))); + tt.Add(argsEtran.TrExpr(Substitute(arg, null, substMap))); } } tr = new Bpl.Trigger(tok, true, tt, tr); diff --git a/Test/dafny0/LitTriggers.dfy b/Test/dafny0/LitTriggers.dfy new file mode 100644 index 00000000..14880d68 --- /dev/null +++ b/Test/dafny0/LitTriggers.dfy @@ -0,0 +1,35 @@ +// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// Imported from bug 76. LitInt would be triggered on, causing matching failures. + +predicate P(x:int, y:int) + +lemma L1(x:int, y:int) + requires y == 2; + requires forall i :: P(i, 3); +{ + assert P(x, y + 1); +} + +lemma L2(x:int, y:int) + requires y == 2; + requires forall i {:trigger P(i, 3)} :: P(i, 3); +{ + assert P(x, y + 1); +} + +lemma L3(x:int, y:int) + requires y == 2; + requires forall i :: P(i, 3); +{ + var dummy := 3; + assert P(x, y + 1); +} + +lemma L4(x:int, y:int) + requires y == 2; + requires forall i, j :: j == 3 ==> P(i, j); +{ + assert P(x, y + 1); +} diff --git a/Test/dafny0/LitTriggers.dfy.expect b/Test/dafny0/LitTriggers.dfy.expect new file mode 100644 index 00000000..249e77e5 --- /dev/null +++ b/Test/dafny0/LitTriggers.dfy.expect @@ -0,0 +1,2 @@ + +Dafny program verifier finished with 9 verified, 0 errors -- cgit v1.2.3 From 34bf8d39390f135c073ef8822fe2896d82b06477 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 16 Jul 2015 14:30:43 -0700 Subject: Minor fixes in .expect files --- Test/dafny0/SmallTests.dfy.expect | 46 +++++++++++----------- ...COST-verif-comp-2011-2-MaxTree-class.dfy.expect | 2 +- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Test/dafny0/SmallTests.dfy.expect b/Test/dafny0/SmallTests.dfy.expect index 3df8118a..b0605d8e 100644 --- a/Test/dafny0/SmallTests.dfy.expect +++ b/Test/dafny0/SmallTests.dfy.expect @@ -82,24 +82,24 @@ Execution trace: (0,0): anon0 SmallTests.dfy(256,19): anon3_Else (0,0): anon2 -SmallTests.dfy(366,12): Error: assertion violation +SmallTests.dfy(367,12): Error: assertion violation Execution trace: (0,0): anon0 -SmallTests.dfy(376,12): Error: assertion violation +SmallTests.dfy(377,12): Error: assertion violation Execution trace: (0,0): anon0 -SmallTests.dfy(386,6): Error: cannot prove termination; try supplying a decreases clause +SmallTests.dfy(387,6): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon4_Else -SmallTests.dfy(691,14): Error: assertion violation +SmallTests.dfy(692,14): Error: assertion violation Execution trace: (0,0): anon0 - SmallTests.dfy(688,5): anon7_LoopHead + SmallTests.dfy(689,5): anon7_LoopHead (0,0): anon7_LoopBody - SmallTests.dfy(688,5): anon8_Else + SmallTests.dfy(689,5): anon8_Else (0,0): anon9_Then -SmallTests.dfy(712,14): Error: assertion violation +SmallTests.dfy(713,14): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon7_Then @@ -114,29 +114,29 @@ Execution trace: (0,0): anon24_Then (0,0): anon15 (0,0): anon25_Else -SmallTests.dfy(337,12): Error: assertion violation +SmallTests.dfy(338,12): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon8_Then (0,0): anon7 -SmallTests.dfy(344,10): Error: assertion violation +SmallTests.dfy(345,10): Error: assertion violation Execution trace: (0,0): anon0 -SmallTests.dfy(354,4): Error: cannot prove termination; try supplying a decreases clause +SmallTests.dfy(355,4): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon4_Else -SmallTests.dfy(398,10): Error BP5003: A postcondition might not hold on this return path. -SmallTests.dfy(401,41): Related location: This is the postcondition that might not hold. +SmallTests.dfy(399,10): Error BP5003: A postcondition might not hold on this return path. +SmallTests.dfy(402,41): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon7_Else -SmallTests.dfy(562,12): Error: assertion violation +SmallTests.dfy(563,12): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Then (0,0): anon2 -SmallTests.dfy(576,20): Error: left-hand sides 0 and 1 may refer to the same location +SmallTests.dfy(577,20): Error: left-hand sides 0 and 1 may refer to the same location Execution trace: (0,0): anon0 (0,0): anon27_Then @@ -148,11 +148,11 @@ Execution trace: (0,0): anon31_Then (0,0): anon32_Then (0,0): anon12 -SmallTests.dfy(578,15): Error: left-hand sides 1 and 2 may refer to the same location +SmallTests.dfy(579,15): Error: left-hand sides 1 and 2 may refer to the same location Execution trace: (0,0): anon0 (0,0): anon27_Then - SmallTests.dfy(571,18): anon28_Else + SmallTests.dfy(572,18): anon28_Else (0,0): anon4 (0,0): anon29_Else (0,0): anon30_Then @@ -163,16 +163,16 @@ Execution trace: (0,0): anon37_Then (0,0): anon22 (0,0): anon38_Then -SmallTests.dfy(585,25): Error: target object may be null +SmallTests.dfy(586,25): Error: target object may be null Execution trace: (0,0): anon0 -SmallTests.dfy(598,10): Error: assertion violation +SmallTests.dfy(599,10): Error: assertion violation Execution trace: (0,0): anon0 -SmallTests.dfy(622,5): Error: cannot establish the existence of LHS values that satisfy the such-that predicate +SmallTests.dfy(623,5): Error: cannot establish the existence of LHS values that satisfy the such-that predicate Execution trace: (0,0): anon0 -SmallTests.dfy(645,23): Error: assertion violation +SmallTests.dfy(646,23): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon8_Then @@ -180,17 +180,17 @@ Execution trace: (0,0): anon4 (0,0): anon10_Then (0,0): anon7 -SmallTests.dfy(659,10): Error: cannot establish the existence of LHS values that satisfy the such-that predicate +SmallTests.dfy(660,10): Error: cannot establish the existence of LHS values that satisfy the such-that predicate Execution trace: (0,0): anon0 (0,0): anon5_Then (0,0): anon6_Then (0,0): anon3 -SmallTests.dfy(661,10): Error: cannot establish the existence of LHS values that satisfy the such-that predicate +SmallTests.dfy(662,10): Error: cannot establish the existence of LHS values that satisfy the such-that predicate Execution trace: (0,0): anon0 (0,0): anon5_Else -SmallTests.dfy(674,9): Error: cannot establish the existence of LHS values that satisfy the such-that predicate +SmallTests.dfy(675,9): Error: cannot establish the existence of LHS values that satisfy the such-that predicate Execution trace: (0,0): anon0 diff --git a/Test/dafny2/COST-verif-comp-2011-2-MaxTree-class.dfy.expect b/Test/dafny2/COST-verif-comp-2011-2-MaxTree-class.dfy.expect index 42fd56a5..c87e2af2 100644 --- a/Test/dafny2/COST-verif-comp-2011-2-MaxTree-class.dfy.expect +++ b/Test/dafny2/COST-verif-comp-2011-2-MaxTree-class.dfy.expect @@ -1,2 +1,2 @@ -Dafny program verifier finished with 8 verified, 0 errors +Dafny program verifier finished with 10 verified, 0 errors -- cgit v1.2.3 From 7950b3a320c9753cd1a05ce70c1a8db5df1f031b Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 16 Jul 2015 14:34:30 -0700 Subject: Use MapConcat instead of String.Join in TriggerGenerator --- Source/Dafny/TriggerGenerator.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Source/Dafny/TriggerGenerator.cs b/Source/Dafny/TriggerGenerator.cs index dc99e609..98354cad 100644 --- a/Source/Dafny/TriggerGenerator.cs +++ b/Source/Dafny/TriggerGenerator.cs @@ -87,7 +87,7 @@ namespace Microsoft.Dafny { } public String AsDafnyAttributeString(bool wrap = true, bool includeTags = false) { - var repr = String.Join(", ", Candidates.Select(t => Printer.ExprToString(t.Expr))); + var repr = Candidates.MapConcat(t => Printer.ExprToString(t.Expr), ", "); if (wrap) { repr = "{:trigger " + repr + "}"; } @@ -358,10 +358,12 @@ namespace Microsoft.Dafny { } private static bool DefaultMultiCandidateFilteringFunction(MultiTriggerCandidate multiCandidate, QuantifierExpr quantifier) { - if (multiCandidate.PotentialMatchingLoops.Any()) { - multiCandidate.Tags.Add(String.Format("matching loop with {0}", String.Join(", ", multiCandidate.PotentialMatchingLoops.Select(tm => Printer.ExprToString(tm.Expr))))); + var allowsLoops = quantifier.Attributes.AsEnumerable().Any(a => a.Name == "loop"); + + if (!allowsLoops && multiCandidate.PotentialMatchingLoops.Any()) { + multiCandidate.Tags.Add(String.Format("matching loop with {0}", multiCandidate.PotentialMatchingLoops.MapConcat(tm => Printer.ExprToString(tm.Expr), ", "))); } - return multiCandidate.MentionsAll(quantifier.BoundVars) && !multiCandidate.PotentialMatchingLoops.Any(); + return multiCandidate.MentionsAll(quantifier.BoundVars) && (allowsLoops || !multiCandidate.PotentialMatchingLoops.Any()); } private static double DefaultMultiCandidateScoringFunction(MultiTriggerCandidate multi_candidate) { @@ -544,7 +546,7 @@ namespace Microsoft.Dafny { internal static void AddTriggers(IEnumerable roots, Resolver resolver) { DebugTriggers("== From {0} visiting expressions: {1}", new StackFrame(1).GetMethod().Name, - String.Join(", ", roots.Select(root => Printer.ExprToString(root)))); + roots.MapConcat(Printer.ExprToString, ", ")); foreach (var expr in roots) { AddTriggers(expr, resolver); } @@ -552,7 +554,7 @@ namespace Microsoft.Dafny { internal static void AddTriggers(IEnumerable roots, Resolver resolver) { DebugTriggers("== From {0} visiting expressions: {1}", new StackFrame(1).GetMethod().Name, - String.Join(", ", roots.Select(root => Printer.ExprToString(root.E)))); + roots.MapConcat(root => Printer.ExprToString(root.E), ", ")); foreach (var expr in roots) { AddTriggers(expr.E, resolver); } -- cgit v1.2.3 From 6a2c5d90e90045ae972176935ea315fc6db8452d Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 16 Jul 2015 14:36:11 -0700 Subject: Force IsTriggerKiller to return false when /autoTriggers is off This is a temporary measure to ensure that the trigger related machinery is entirely disabled when /autoTriggers is off. --- Source/Dafny/TriggerGenerator.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Dafny/TriggerGenerator.cs b/Source/Dafny/TriggerGenerator.cs index 98354cad..d8a29d75 100644 --- a/Source/Dafny/TriggerGenerator.cs +++ b/Source/Dafny/TriggerGenerator.cs @@ -502,6 +502,10 @@ namespace Microsoft.Dafny { } internal static bool IsTriggerKiller(Expression expr) { + // CLEMENT: This should be removed once trigger generation becomes the default + if (!DafnyOptions.O.AutoTriggers) { + return false; + } var annotation = new TriggerGenerator((x, y, z) => { }).Annotate(expr); return annotation.IsTriggerKiller; } -- cgit v1.2.3 From 8f18456b7e89412855abc9993447512bdb835da9 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 16 Jul 2015 15:37:50 -0700 Subject: Fixed bugs in the parsing of explicit type arguments. Fixed resolution bug where some type arguments were not checked to have been determined. Fixed resolution bugs where checking for equality-supporting types was missing. --- Source/Dafny/Dafny.atg | 14 ++++- Source/Dafny/Parser.cs | 82 ++++++++++++++----------- Source/Dafny/Resolver.cs | 35 +++++++---- Test/dafny0/EqualityTypes.dfy | 112 +++++++++++++++++++++++++++++++++++ Test/dafny0/EqualityTypes.dfy.expect | 24 +++++++- 5 files changed, 220 insertions(+), 47 deletions(-) diff --git a/Source/Dafny/Dafny.atg b/Source/Dafny/Dafny.atg index 7b51fb5e..1710be05 100644 --- a/Source/Dafny/Dafny.atg +++ b/Source/Dafny/Dafny.atg @@ -299,6 +299,9 @@ bool IsGenericInstantiation() { return false; } } +/* Returns true if the next thing is of the form: + * "<" Type { "," Type } ">" + */ bool IsTypeList(ref IToken pt) { if (pt.kind != _openAngleBracket) { return false; @@ -306,6 +309,10 @@ bool IsTypeList(ref IToken pt) { pt = scanner.Peek(); return IsTypeSequence(ref pt, _closeAngleBracket); } +/* Returns true if the next thing is of the form: + * Type { "," Type } + * followed by an endBracketKind. + */ bool IsTypeSequence(ref IToken pt, int endBracketKind) { while (true) { if (!IsType(ref pt)) { @@ -343,7 +350,7 @@ bool IsType(ref IToken pt) { case _map: case _imap: pt = scanner.Peek(); - return IsTypeList(ref pt); + return pt.kind != _openAngleBracket || IsTypeList(ref pt); case _ident: while (true) { // invariant: next token is an ident @@ -362,6 +369,11 @@ bool IsType(ref IToken pt) { } case _openparen: pt = scanner.Peek(); + if (pt.kind == _closeparen) { + // end of type list + pt = scanner.Peek(); + return true; + } return IsTypeSequence(ref pt, _closeparen); default: return false; diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs index a90e650a..8d14b9df 100644 --- a/Source/Dafny/Parser.cs +++ b/Source/Dafny/Parser.cs @@ -74,8 +74,8 @@ public class Parser { public const int _ellipsis = 58; public const int maxT = 138; - const bool _T = true; - const bool _x = false; + const bool T = true; + const bool x = false; const int minErrDist = 2; public Scanner/*!*/ scanner; @@ -370,6 +370,9 @@ bool IsGenericInstantiation() { return false; } } +/* Returns true if the next thing is of the form: + * "<" Type { "," Type } ">" + */ bool IsTypeList(ref IToken pt) { if (pt.kind != _openAngleBracket) { return false; @@ -377,6 +380,10 @@ bool IsTypeList(ref IToken pt) { pt = scanner.Peek(); return IsTypeSequence(ref pt, _closeAngleBracket); } +/* Returns true if the next thing is of the form: + * Type { "," Type } + * followed by an endBracketKind. + */ bool IsTypeSequence(ref IToken pt, int endBracketKind) { while (true) { if (!IsType(ref pt)) { @@ -414,7 +421,7 @@ bool IsType(ref IToken pt) { case _map: case _imap: pt = scanner.Peek(); - return IsTypeList(ref pt); + return pt.kind != _openAngleBracket || IsTypeList(ref pt); case _ident: while (true) { // invariant: next token is an ident @@ -433,6 +440,11 @@ bool IsType(ref IToken pt) { } case _openparen: pt = scanner.Peek(); + if (pt.kind == _closeparen) { + // end of type list + pt = scanner.Peek(); + return true; + } return IsTypeSequence(ref pt, _closeparen); default: return false; @@ -4377,38 +4389,38 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } static readonly bool[,]/*!*/ set = { - {_T,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_T, _x,_x,_T,_T, _T,_x,_x,_T, _T,_x,_x,_T, _T,_x,_T,_T, _T,_T,_T,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _T,_x,_x,_T, _T,_T,_x,_x, _T,_x,_x,_T, _T,_T,_T,_T, _T,_T,_x,_T, _T,_x,_x,_T, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, - {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_x,_x, _T,_x,_x,_x, _x,_T,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_T,_x, _x,_T,_x,_x, _x,_x,_x,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_x,_T,_x,_x, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_x,_x, _x,_x,_T,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_x,_x, _T,_x,_T,_x, _x,_T,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_T,_T,_T, _x,_T,_T,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_x, _T,_T,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_x,_x, _T,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, - {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_T,_T,_T, _T,_T,_x,_T, _T,_T,_T,_x, _x,_T,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_x,_x, _T,_x,_x,_x, _x,_T,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_T, _T,_T,_T,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_x,_T,_T,_x, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _T,_x,_x,_T, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, - {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_T,_T,_T, _x,_T,_T,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_T,_x, _T,_T,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_x,_x, _T,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, - {_T,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _T,_x,_x,_T, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, - {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_T,_T,_T, _x,_T,_T,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _x,_T,_x,_x, _x,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_x, _T,_T,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_x,_x, _T,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, - {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_T,_T,_T, _x,_T,_T,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _x,_T,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_x, _T,_T,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_x,_x, _T,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, - {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _T,_x,_x,_T, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, - {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_x,_T, _T,_x,_x,_T, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, - {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_T,_T,_T, _x,_T,_T,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _x,_T,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_T,_T,_x, _T,_T,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_x,_x, _T,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, - {_x,_x,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, - {_T,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _T,_x,_x,_T, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, - {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_x, _x,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_x, _T,_T,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, - {_T,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _T,_T,_x,_x, _T,_x,_x,_x, _x,_T,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_T,_T, _T,_x,_T,_T, _T,_T,_T,_T, _T,_T,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x}, - {_T,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_T,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _T,_T,_x,_x, _T,_x,_x,_x, _x,_T,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_T,_T, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x} + {T,T,T,T, T,x,x,x, T,x,T,x, x,x,x,x, x,x,x,T, T,x,x,T, x,x,x,T, x,x,T,T, T,x,x,T, T,x,x,T, T,x,T,T, T,T,T,x, x,T,x,x, x,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,T,x,T, T,x,x,T, T,T,x,x, T,x,x,T, T,T,T,T, T,T,x,T, T,x,x,T, x,x,T,T, T,T,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,x,x,x}, + {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,T,x,x, T,x,x,x, x,T,x,T, T,T,T,T, T,T,T,T, T,x,x,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, + {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,T,T,x, x,T,x,x, x,x,x,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, + {x,T,x,x, x,T,T,T, T,T,T,T, T,T,T,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, + {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,x,x, x,T,T,T, T,T,x,x, x,x,T,x, x,x,x,T, x,x,x,x, x,x,x,x, T,T,x,x, T,x,T,x, x,T,x,T, T,T,T,T, T,T,T,T, T,x,x,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, + {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,T,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, + {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, + {x,T,T,T, T,x,x,x, T,x,T,x, x,T,T,T, x,T,T,T, T,x,x,T, x,x,x,x, x,x,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,T, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,T,x, T,T,x,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,T, T,T,x,x, T,x,x,T, T,T,T,T, T,x,x,x}, + {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,T,T,T, T,T,x,T, T,T,T,x, x,T,x,T, x,x,x,x, x,x,x,x, T,T,x,x, T,x,x,x, x,T,x,T, T,T,T,T, T,T,T,T, T,x,x,T, T,T,T,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, + {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,x,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, + {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, + {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,T,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, + {x,T,T,x, x,T,T,T, T,T,T,T, T,T,T,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, + {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,T,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, + {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, + {x,T,T,T, T,x,x,x, T,x,T,x, x,x,x,x, x,x,x,T, T,x,x,T, x,x,x,x, x,x,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,T,x,x, x,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,T,x,T, T,x,x,T, x,x,T,T, T,T,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,x,x,x}, + {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,T,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, + {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,x,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, + {x,T,T,T, T,x,x,x, T,x,T,x, x,T,T,T, x,T,T,T, T,x,x,T, x,x,x,x, x,x,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,T, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,T,T, x,x,x,x, x,x,T,x, T,T,x,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,T, T,T,x,x, T,x,x,T, T,T,T,T, T,x,x,x}, + {T,T,T,T, T,x,x,x, T,x,T,x, x,x,x,x, x,x,x,T, T,x,x,T, x,x,x,x, x,x,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,T,x,x, x,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,T,x,T, T,x,x,T, x,x,T,T, T,T,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,x,x,x}, + {x,T,T,T, T,x,x,x, T,x,T,x, x,T,T,T, x,T,T,T, T,x,x,T, x,x,x,x, x,x,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,T, x,T,x,x, x,x,x,x, T,x,T,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,T,x, T,T,x,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,T, T,T,x,x, T,x,x,T, T,T,T,T, T,x,x,x}, + {x,T,T,T, T,x,x,x, T,x,T,x, x,T,T,T, x,T,T,T, T,x,x,T, x,x,x,x, x,x,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,T, x,T,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,T,x, T,T,x,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,T, T,T,x,x, T,x,x,T, T,T,T,T, T,x,x,x}, + {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, + {x,T,T,T, T,x,x,x, T,x,T,x, x,x,x,x, x,x,x,T, T,x,x,T, x,x,x,x, x,x,T,T, T,x,x,x, x,x,x,x, x,x,x,x, x,T,T,x, x,T,x,x, x,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,T,x,T, T,x,x,T, x,x,T,T, T,T,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,x,x,x}, + {x,T,T,T, T,x,x,x, T,x,T,x, x,x,x,x, x,x,x,T, T,x,x,T, x,x,x,x, x,x,T,T, T,x,x,x, x,x,x,x, x,x,x,x, x,T,T,x, x,T,x,x, x,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,T, T,T,x,T, T,x,x,T, x,x,T,T, T,T,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,x,x,x}, + {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,T,T,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, + {x,T,T,T, T,x,x,x, T,x,T,x, x,T,T,T, x,T,T,T, T,x,x,T, x,x,x,x, x,x,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,T, x,T,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,T,T,x, T,T,x,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,T, T,T,x,x, T,x,x,T, T,T,T,T, T,x,x,x}, + {x,x,T,T, T,x,x,x, T,x,T,x, x,x,x,x, x,x,x,T, T,x,x,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,x,x,x}, + {T,T,T,T, T,x,x,x, T,x,T,x, x,x,x,x, x,x,x,T, T,x,x,T, x,x,x,x, x,x,T,T, T,x,x,x, x,x,x,x, x,x,x,x, x,T,T,x, x,T,x,x, x,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,T,x,T, T,x,x,T, x,x,T,T, T,T,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,x,x,x}, + {x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,x, x,T,T,x, x,x,x,x, x,x,x,x, x,x,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,T,x, T,T,x,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, + {T,T,T,T, T,x,x,x, T,x,T,x, x,x,x,x, x,x,x,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,x, T,T,x,x, T,x,x,x, x,T,x,T, T,T,T,T, T,T,T,T, T,x,x,T, T,T,T,T, T,T,T,T, T,x,T,T, T,x,T,T, T,T,T,T, T,T,x,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x,x, x,x,T,T, T,T,T,T, T,T,T,T, T,T,x,x}, + {T,T,T,T, T,x,x,x, T,x,T,x, x,x,x,x, x,x,x,T, T,T,T,T, T,T,x,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,x, T,T,T,T, T,T,T,T, T,T,T,x, T,T,x,x, T,x,x,x, x,T,x,T, T,T,T,T, T,T,T,T, T,x,x,T, T,T,T,T, T,T,T,T, T,x,T,T, x,x,T,T, T,T,T,T, T,T,x,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x,x, x,x,T,T, T,T,T,T, T,T,T,T, T,T,x,x} }; } // end Parser diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 3e308e76..99b8ba1e 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -2166,7 +2166,8 @@ namespace Microsoft.Dafny proxy.T = new ObjectType(); return true; } - return !(t is TypeProxy); // all other proxies indicate the type has not yet been determined + // all other proxies indicate the type has not yet been determined, provided their type parameters have been + return !(t is TypeProxy) && t.TypeArgs.All(tt => IsDetermined(tt.Normalize())); } ISet UnderspecifiedTypeProxies = new HashSet(); bool CheckTypeIsDetermined(IToken tok, Type t, string what) { @@ -2738,6 +2739,14 @@ namespace Microsoft.Dafny foreach (var v in s.BoundVars) { CheckEqualityTypes_Type(v.Tok, v.Type); } + // do substatements and subexpressions, except attributes and ensures clauses, since they are not compiled + foreach (var ss in s.SubStatements) { + Visit(ss, st); + } + if (s.Range != null) { + Visit(s.Range, st); + } + return false; // we're done } return true; } @@ -2796,6 +2805,18 @@ namespace Microsoft.Dafny foreach (var bv in e.BoundVars) { CheckEqualityTypes_Type(bv.tok, bv.Type); } + } else if (expr is MemberSelectExpr) { + var e = (MemberSelectExpr)expr; + if (e.Member is Function || e.Member is Method) { + var i = 0; + foreach (var tp in ((ICallable)e.Member).TypeArgs) { + var actualTp = e.TypeApplication[e.Member.EnclosingClass.TypeArgs.Count + i]; + if (tp.MustSupportEquality && !actualTp.SupportsEquality) { + Error(e.tok, "type parameter {0} ({1}) passed to {5} '{2}' must support equality (got {3}){4}", i, tp.Name, e.Member.Name, actualTp, TypeEqualityErrorMessageHint(actualTp), e.Member.WhatKind); + } + i++; + } + } } else if (expr is FunctionCallExpr) { var e = (FunctionCallExpr)expr; Contract.Assert(e.Function.TypeArgs.Count <= e.TypeArgumentSubstitutions.Count); @@ -2818,7 +2839,7 @@ namespace Microsoft.Dafny i++; } return false; // we've done what there is to be done - } else if (expr is SetDisplayExpr || expr is MultiSetDisplayExpr || expr is MapDisplayExpr || expr is MultiSetFormingExpr) { + } else if (expr is SetDisplayExpr || expr is MultiSetDisplayExpr || expr is MapDisplayExpr || expr is MultiSetFormingExpr || expr is StaticReceiverExpr) { // This catches other expressions whose type may potentially be illegal CheckEqualityTypes_Type(expr.tok, expr.Type); } @@ -2834,11 +2855,8 @@ namespace Microsoft.Dafny } else if (type is SetType) { var st = (SetType)type; var argType = st.Arg; - if (!st.Finite) { - Error(tok, "isets do not support equality: {0}", st); - } if (!argType.SupportsEquality) { - Error(tok, "set argument type must support equality (got {0}){1}", argType, TypeEqualityErrorMessageHint(argType)); + Error(tok, "{2}set argument type must support equality (got {0}){1}", argType, TypeEqualityErrorMessageHint(argType), st.Finite ? "" : "i"); } CheckEqualityTypes_Type(tok, argType); @@ -2851,11 +2869,8 @@ namespace Microsoft.Dafny } else if (type is MapType) { var mt = (MapType)type; - if (!mt.Finite) { - Error(tok, "imaps do not support equality: {0}", mt); - } if (!mt.Domain.SupportsEquality) { - Error(tok, "map domain type must support equality (got {0}){1}", mt.Domain, TypeEqualityErrorMessageHint(mt.Domain)); + Error(tok, "{2}map domain type must support equality (got {0}){1}", mt.Domain, TypeEqualityErrorMessageHint(mt.Domain), mt.Finite ? "" : "i"); } CheckEqualityTypes_Type(tok, mt.Domain); CheckEqualityTypes_Type(tok, mt.Range); diff --git a/Test/dafny0/EqualityTypes.dfy b/Test/dafny0/EqualityTypes.dfy index b2812759..c510cfb1 100644 --- a/Test/dafny0/EqualityTypes.dfy +++ b/Test/dafny0/EqualityTypes.dfy @@ -241,3 +241,115 @@ module Deep { var m1 := map[ko := 5]; // error: bad type } } + +//-------------------------- + +module UnderspecifiedTypeParameters { + method UP() + function method UG(): int + method Callee() + class TakesParam { } + + method MPG() + { + var g := UG(); // error: type parameter underspecified + UP(); // error: type parameter underspecified + } + method M() { + var zs: set; // error: type is underspecified + Callee<(int)>(); + Callee(); // error: type is underspecified + Callee<()>(); + // The following + Callee(); // error: type is underspecified + } +} + +module EqualitySupportingTypes { + method P() + function method G(): int + class AClass { + static function method H(): bool + static method Q() + } + + method Callee() + function method FCallee(): T + + datatype Dt = Dt(f: int -> int) + codatatype Stream = Cons(T, Stream) + + method M() + { + Callee
(); // error: Dt is not an equality-supporting type + Callee>(); // error: specified type does not support equality + + // set is allowed in a non-ghost context only if X is equality supporting. + // Ditto for multiset and map. + var s3x: set
; // error: this type not allowed in a non-ghost context + var is3x: iset
; // error: this type not allowed in a non-ghost context + var mast: multiset; // error: this type not allowed in a non-ghost context + var qt: seq>; // allowed + var mp0: map; // error: this type not allowed in a non-ghost context + var mp1: map; // allowed + var imp0: imap; // error: this type not allowed in a non-ghost context + var imp1: imap; // allowed + + var S := FCallee(); // this gives s:set + if 4 in S { // this constrains the type further to be s:set + } + + var xy: set>; + var xz: set>>; // error: set type argument must support equality + + Callee>>(); // bogus: a set shouldn't ever be allowed to take a Stream as an argument (this check seems to be missing for explicit type arguments) -- Note: language definition should be changed, because it doesn't make sense for it to talk about a type appearing in a ghost or non-ghost context. Instead, set/iset/multiset/map/imap should always be allowed to take any type argument, but these types may or may not support equality. + var xg := G>>(); + + var ac0: AClass; + var ac1: AClass,int>; // error: type parameter 0 is required to support equality + var ac2: AClass>; + var xd0 := ac0.H(); + var xd1 := ac1.H,real>(); // error (remnant of the fact that the type of ac1 is not allowed) + var xd2 := ac2.H>(); // error: type parameter 1 is required to support equality + var xe0 := ac0.H; + var xe1 := ac1.H,real>; // error (remnant of the fact that the type of ac1 is not allowed) + var xe2 := ac2.H>; // error: type parameter 1 is required to support equality + var xh0 := AClass.H(); + var xh1 := AClass.H,real>(); + var xh2 := AClass.H>(); // error: type parameter 1 is required to support equality + var xk0 := AClass.H; + var xk1 := AClass,real>.H; // error: class type param 0 wants an equality-supporting type + var xk2 := AClass>.H; + AClass,int>.Q(); // error: class type param 0 wants an equality-supporting type + AClass>.Q(); + AClass>.Q,real>(); + AClass>.Q>(); // error: method type param 1 wants an equality-supporting type + +/*************************** TESTS YET TO COME + var ac8: AClass; + var xd8 := (if 5/0 == 3 then ac0 else ac8).H(); // error: this should be checked by the verifier + + AClass>>.Q(); // error: cannot utter "set>" Or is that okay??? + AClass.Q>,real>(); // error: cannot utter "set>" Or is that okay??? + var xi0 := AClass>>.H(); // error: cannot utter "set>" Or is that okay??? + var xi1 := AClass.H>>(); // error: cannot utter "set>" Or is that okay??? + + var x, t, s: seq int>, fii: int -> int; + if s == t { + x := 5; // error: assigning to non-ghost variable in ghost context + } + if fii in s { + x := 4; // error: assigning to non-ghost variable in ghost context + } + if !(fii in s) { + x := 3; // error: assigning to non-ghost variable in ghost context + } + + ghost var ghostset: set> := {}; // fine, since this is ghost + forall u | 0 <= u < 100 + ensures var lets: set> := {}; lets == lets // this is ghost, so the equality requirement doesn't apply + { + } +*********************************************/ + } +} diff --git a/Test/dafny0/EqualityTypes.dfy.expect b/Test/dafny0/EqualityTypes.dfy.expect index 9f277582..1c02f3a0 100644 --- a/Test/dafny0/EqualityTypes.dfy.expect +++ b/Test/dafny0/EqualityTypes.dfy.expect @@ -35,4 +35,26 @@ EqualityTypes.dfy(238,24): Error: set argument type must support equality (got C EqualityTypes.dfy(239,21): Error: multiset argument type must support equality (got Co) EqualityTypes.dfy(241,8): Error: map domain type must support equality (got Co) EqualityTypes.dfy(241,14): Error: map domain type must support equality (got Co) -37 resolution/type errors detected in EqualityTypes.dfy +EqualityTypes.dfy(255,13): Error: type variable 'T' in the function call to 'UG' could not be determined +EqualityTypes.dfy(256,4): Error: type '?' to the method 'UP' is not determined +EqualityTypes.dfy(259,8): Error: the type of this local variable is underspecified +EqualityTypes.dfy(261,4): Error: type 'set' to the method 'Callee' is not determined +EqualityTypes.dfy(264,4): Error: type 'TakesParam' to the method 'Callee' is not determined +EqualityTypes.dfy(284,14): Error: type parameter 0 (T) passed to method Callee must support equality (got Dt) +EqualityTypes.dfy(285,23): Error: type parameter 0 (T) passed to method Callee must support equality (got Stream) +EqualityTypes.dfy(289,8): Error: set argument type must support equality (got Dt) +EqualityTypes.dfy(290,8): Error: iset argument type must support equality (got Dt) +EqualityTypes.dfy(291,8): Error: multiset argument type must support equality (got ArbitraryTypeArg) (perhaps try declaring type parameter 'ArbitraryTypeArg' on line 282 as 'ArbitraryTypeArg(==)', which says it can only be instantiated with a type that supports equality) +EqualityTypes.dfy(293,8): Error: map domain type must support equality (got Dt) +EqualityTypes.dfy(295,8): Error: imap domain type must support equality (got Dt) +EqualityTypes.dfy(303,8): Error: set argument type must support equality (got Stream) +EqualityTypes.dfy(309,8): Error: type parameter 0 (V) passed to type AClass must support equality (got Stream) +EqualityTypes.dfy(312,19): Error: type parameter 0 (V) passed to type AClass must support equality (got Stream) +EqualityTypes.dfy(313,19): Error: type parameter 1 (X) passed to function H must support equality (got Stream) +EqualityTypes.dfy(315,19): Error: type parameter 0 (V) passed to type AClass must support equality (got Stream) +EqualityTypes.dfy(316,19): Error: type parameter 1 (X) passed to function 'H' must support equality (got Stream) +EqualityTypes.dfy(319,31): Error: type parameter 1 (X) passed to function H must support equality (got Stream) +EqualityTypes.dfy(321,41): Error: type parameter 0 (V) passed to type AClass must support equality (got Stream) +EqualityTypes.dfy(323,28): Error: type parameter 0 (V) passed to type AClass must support equality (got Stream) +EqualityTypes.dfy(326,48): Error: type parameter 1 (B) passed to method Q must support equality (got Stream) +59 resolution/type errors detected in EqualityTypes.dfy -- cgit v1.2.3 From a22d071b3eac50ce1fd6c759c58873ae6c584ced Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 16 Jul 2015 17:44:17 -0700 Subject: Clean up a few thing and set proper defaults before merging --- Source/Dafny/DafnyOptions.cs | 8 ++++---- Source/Dafny/TriggerGenerator.cs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Dafny/DafnyOptions.cs b/Source/Dafny/DafnyOptions.cs index b2593705..a809cbd6 100644 --- a/Source/Dafny/DafnyOptions.cs +++ b/Source/Dafny/DafnyOptions.cs @@ -56,7 +56,7 @@ namespace Microsoft.Dafny public bool AllowGlobals = false; public bool CountVerificationErrors = true; public bool Optimize = false; - public bool AutoTriggers = true; + public bool AutoTriggers = false; public bool PrintTooltips = false; protected override bool ParseOption(string name, Bpl.CommandLineOptionEngine.CommandLineParseState ps) { @@ -188,7 +188,7 @@ namespace Microsoft.Dafny return true; case "autoTriggers": { - int autoTriggers = 1; // defaults to reporting verification errors + int autoTriggers = 0; if (ps.GetNumericArgument(ref autoTriggers, 2)) { AutoTriggers = autoTriggers == 1; } @@ -291,8 +291,8 @@ namespace Microsoft.Dafny 1 (default) - If preprocessing succeeds, set exit code to the number of verification errors. /autoTriggers: - 0 - Do not generate {:trigger} annotations for user-level quantifiers. - 1 (default) - Add a {:trigger} to each user-level quantifier. Existing + 0 (default) - Do not generate {:trigger} annotations for user-level quantifiers. + 1 - Add a {:trigger} to each user-level quantifier. Existing annotations are preserved. /optimize Produce optimized C# code, meaning: - selects optimized C# prelude by passing diff --git a/Source/Dafny/TriggerGenerator.cs b/Source/Dafny/TriggerGenerator.cs index d8a29d75..a777b4ae 100644 --- a/Source/Dafny/TriggerGenerator.cs +++ b/Source/Dafny/TriggerGenerator.cs @@ -25,7 +25,7 @@ using System.Diagnostics; */ namespace Microsoft.Dafny { - class TriggerCandidate { // TODO Hashing is broken (duplicates can pop up) + class TriggerCandidate { internal Expression Expr; internal ISet Variables; internal List MatchesInQuantifierBody; @@ -35,7 +35,7 @@ namespace Microsoft.Dafny { } } - class TriggerCandidateComparer : IEqualityComparer { + class TriggerCandidateComparer : IEqualityComparer { //FIXME: There is a bunch of these comparers. private static TriggerCandidateComparer singleton; internal static TriggerCandidateComparer Instance { get { return singleton == null ? (singleton = new TriggerCandidateComparer()) : singleton; } -- cgit v1.2.3 From c450e2718c6c047af95e948f87cad53ade07a1c5 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 16 Jul 2015 17:19:08 -0700 Subject: Fix column numbers in warning and info messages This brings them in line with error column numbers. --- Source/Dafny/Resolver.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index c5b19d8c..9d560561 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -25,7 +25,7 @@ namespace Microsoft.Dafny ConsoleColor col = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("{0}({1},{2}): Error: {3}", - DafnyOptions.Clo.UseBaseNameForFileName ? System.IO.Path.GetFileName(tok.filename) : tok.filename, tok.line, tok.col, + DafnyOptions.Clo.UseBaseNameForFileName ? System.IO.Path.GetFileName(tok.filename) : tok.filename, tok.line, tok.col - 1, string.Format(msg, args)); Console.ForegroundColor = col; ErrorCount++; @@ -271,7 +271,7 @@ namespace Microsoft.Dafny public static void DefaultInformationReporter(AdditionalInformation info) { Console.WriteLine("{0}({1},{2}): Info: {3}", DafnyOptions.Clo.UseBaseNameForFileName ? System.IO.Path.GetFileName(info.Token.filename) : info.Token.filename, - info.Token.line, info.Token.col, info.Text); + info.Token.line, info.Token.col - 1, info.Text); } public void ResolveProgram(Program prog) { -- cgit v1.2.3 From fde628b5153b86f187f353c6f79113932c309abe Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 17 Jul 2015 14:08:12 -0700 Subject: Add /autoTriggers to TriggerInPredicate's RUN specification --- Test/dafny0/TriggerInPredicate.dfy | 19 +++++++++++++++++++ Test/dafny0/TriggerInPredicate.dfy.expect | 4 ++++ Test/new-tests/trigger-in-predicate.dfy | 15 --------------- Test/new-tests/trigger-in-predicate.dfy.expect | 4 ---- 4 files changed, 23 insertions(+), 19 deletions(-) create mode 100644 Test/dafny0/TriggerInPredicate.dfy create mode 100644 Test/dafny0/TriggerInPredicate.dfy.expect delete mode 100644 Test/new-tests/trigger-in-predicate.dfy delete mode 100644 Test/new-tests/trigger-in-predicate.dfy.expect diff --git a/Test/dafny0/TriggerInPredicate.dfy b/Test/dafny0/TriggerInPredicate.dfy new file mode 100644 index 00000000..3f2eac2c --- /dev/null +++ b/Test/dafny0/TriggerInPredicate.dfy @@ -0,0 +1,19 @@ +// RUN: %dafny /compile:0 /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +predicate A(x: bool, y: bool) { x } + +predicate B(x: bool, z: bool) { forall y {:trigger A(x, y) } :: A(x, y) && z } + +// Inlining is disabled here to prevent pollution of the trigger in B +method C() requires B(true || false, true) {} + +// Inlining should work fine here +method C'() requires B(true, true) {} + +// Inlining should work fine here +method C''() requires B(true, true && false) {} + +// Local Variables: +// dafny-prover-local-args: ("/autoTriggers:1") +// End: diff --git a/Test/dafny0/TriggerInPredicate.dfy.expect b/Test/dafny0/TriggerInPredicate.dfy.expect new file mode 100644 index 00000000..47b3bdf9 --- /dev/null +++ b/Test/dafny0/TriggerInPredicate.dfy.expect @@ -0,0 +1,4 @@ +TriggerInPredicate.dfy(9,20): Info: This call cannot be safely inlined. +TriggerInPredicate.dfy(9,20): Info: This call cannot be safely inlined. + +Dafny program verifier finished with 8 verified, 0 errors diff --git a/Test/new-tests/trigger-in-predicate.dfy b/Test/new-tests/trigger-in-predicate.dfy deleted file mode 100644 index 880d3d1d..00000000 --- a/Test/new-tests/trigger-in-predicate.dfy +++ /dev/null @@ -1,15 +0,0 @@ -// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t" -// RUN: %diff "%s.expect" "%t" - -predicate A(x: bool, y: bool) { x } - -predicate B(x: bool, z: bool) { forall y {:trigger A(x, y) } :: A(x, y) && z } - -// Inlining is disabled here to prevent pollution of the trigger in B -method C() requires B(true || false, true) {} - -// Inlining should work fine here -method C'() requires B(true, true) {} - -// Inlining should work fine here -method C''() requires B(true, true && false) {} diff --git a/Test/new-tests/trigger-in-predicate.dfy.expect b/Test/new-tests/trigger-in-predicate.dfy.expect deleted file mode 100644 index 211bc9a2..00000000 --- a/Test/new-tests/trigger-in-predicate.dfy.expect +++ /dev/null @@ -1,4 +0,0 @@ -c:/MSR/dafny-triggers/Test/new-tests/trigger-in-predicate.dfy(9,21): Info: This call cannot be safely inlined. -c:/MSR/dafny-triggers/Test/new-tests/trigger-in-predicate.dfy(9,21): Info: This call cannot be safely inlined. - -Dafny program verifier finished with 8 verified, 0 errors -- cgit v1.2.3 From d9e4d4e4979ecb10f18583f51cd94e71a4dca9a4 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 17 Jul 2015 11:49:37 -0700 Subject: Enable autoTriggers in LitTriggers and SeqFromArray --- Test/dafny0/LitTriggers.dfy | 6 +++++- Test/dafny0/SeqFromArray.dfy | 8 +++++++- Test/dafny4/NumberRepresentations.dfy | 4 ++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Test/dafny0/LitTriggers.dfy b/Test/dafny0/LitTriggers.dfy index 14880d68..93e65643 100644 --- a/Test/dafny0/LitTriggers.dfy +++ b/Test/dafny0/LitTriggers.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t" +// RUN: %dafny /compile:0 /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" // RUN: %diff "%s.expect" "%t" // Imported from bug 76. LitInt would be triggered on, causing matching failures. @@ -33,3 +33,7 @@ lemma L4(x:int, y:int) { assert P(x, y + 1); } + +// Local Variables: +// dafny-prover-local-args: ("/autoTriggers:1") +// End: diff --git a/Test/dafny0/SeqFromArray.dfy b/Test/dafny0/SeqFromArray.dfy index 3a8760ba..629c5045 100644 --- a/Test/dafny0/SeqFromArray.dfy +++ b/Test/dafny0/SeqFromArray.dfy @@ -1,6 +1,8 @@ -// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" // RUN: %diff "%s.expect" "%t" +// /autoTriggers:1 added to suppress instabilities + method Main() { } method H(a: array, c: array, n: nat, j: nat) @@ -88,3 +90,7 @@ method M(a: array, c: array, m: nat, n: nat, k: nat, l: nat) assert a[k..k+m] == c[l+k..l+k+m]; } } + +// Local Variables: +// dafny-prover-local-args: ("/autoTriggers:1") +// End: diff --git a/Test/dafny4/NumberRepresentations.dfy b/Test/dafny4/NumberRepresentations.dfy index 8a94505c..f51ae924 100644 --- a/Test/dafny4/NumberRepresentations.dfy +++ b/Test/dafny4/NumberRepresentations.dfy @@ -293,3 +293,7 @@ lemma MulInverse(x: int, a: int, b: int, y: int) ensures a == b; { } + +// Local Variables: +// dafny-prover-local-args: ("/vcsMaxKeepGoingSplits:5") +// End: -- cgit v1.2.3 From 80ca1f0f28afa5f4b132ac0d64efa5d877f0d590 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 17 Jul 2015 13:48:42 -0700 Subject: Clean up runTests after 2015/07/17 meeting --- Test/runTests.py | 137 +++++++++++++++++++++++++++---------------------------- 1 file changed, 68 insertions(+), 69 deletions(-) diff --git a/Test/runTests.py b/Test/runTests.py index 044cf260..efabdc73 100644 --- a/Test/runTests.py +++ b/Test/runTests.py @@ -2,7 +2,6 @@ import os import re import sys import csv -import shlex import shutil import argparse import operator @@ -17,10 +16,15 @@ from subprocess import Popen, call, PIPE, TimeoutExpired # c:/Python34/python.exe runTests.py --compare ../TestStable/results/SequenceAxioms/2015-06-06-00-54-52--PrettyPrinted.report.csv ../TestStable/results/SequenceAxioms/*.csv +VERBOSITY = None +KILLED = False ANSI = False + try: import colorama - colorama.init() + no_native_ansi = os.name == 'nt' and os.environ.get("TERM") in [None, "cygwin"] + tty = all(hasattr(stream, 'isatty') and stream.isatty() for stream in (sys.stdout, sys.stderr)) + colorama.init(strip=no_native_ansi, convert=no_native_ansi and tty) ANSI = True except ImportError: try: @@ -29,25 +33,28 @@ except ImportError: except ImportError: pass -VERBOSITY = None -KILLED = False - -ALWAYS_EXCLUDED = ["Output", "snapshots", "sandbox"] - +class Defaults: + ALWAYS_EXCLUDED = ["Output", "snapshots", "sandbox", "desktop"] + DAFNY_BIN = os.path.realpath(os.path.join(os.path.dirname(__file__), "../Binaries/Dafny.exe")) + COMPILER = [DAFNY_BIN] + FLAGS = ["/useBaseNameForFileName", "/compile:1", "/nologo", "/timeLimit:120"] class Debug(Enum): ERROR = (-1, '\033[91m') - REPORT = (0, '\033[0m') - INFO = (1, '\033[0m') + REPORT = (0, '\033[0m', True) + INFO = (1, '\033[0m', True) DEBUG = (2, '\033[0m') TRACE = (3, '\033[0m') - def __init__(self, index, color): + def __init__(self, index, color, elide=False): self.index = index self.color = color + self.elide = elide -def wrap_color(string, color): - if ANSI: +def wrap_color(string, color, silent=False): + if silent: + return " " * len(string) + elif ANSI: return color + string + '\033[0m' else: return string @@ -60,10 +67,14 @@ def debug(level, *args, **kwargs): if isinstance(headers, Enum): headers = [headers] + silentheaders = kwargs.pop("silentheaders", False) + if level and level.index <= VERBOSITY: if level: - headers = [level] + headers - headers = tuple("[" + wrap_color("{: ^8}".format(h.name), h.color) + "]" for h in headers) + headers = [level] + headers + + headers = tuple(wrap_color("{: <8}".format("[" + h.name + "]"), h.color, silent = silentheaders) + for h in headers if not h.elide) print(*(headers + args), **kwargs) class TestStatus(Enum): @@ -76,13 +87,15 @@ class TestStatus(Enum): def __init__(self, index, color): self.index = index self.color = color + self.elide = False class Test: COLUMNS = ["name", "status", "start", "end", "duration", "returncodes", "suite_time", "njobs", "proc_info", "source_path", "temp_directory", "cmds", "expected", "output"] def __init__(self, name, source_path, cmds, timeout, compiler_id = 0): self.name = name - self.source_path = source_path + self.dfy = None if self.name is None else (self.name + ".dfy") + self.source_path = Test.uncygdrive(source_path) self.expect_path = Test.source_to_expect_path(self.source_path) self.source_directory, self.fname = os.path.split(self.source_path) self.temp_directory = os.path.join(self.source_directory, "Output") @@ -107,6 +120,10 @@ class Test: def source_to_expect_path(source): return source + ".expect" + @staticmethod + def uncygdrive(path): + return re.sub("^/cygdrive/([a-zA-Z])/", r"\1:/", path) + @staticmethod def read_normalize(path): with open(path, mode="rb") as reader: @@ -137,22 +154,26 @@ class Test: @staticmethod def summarize(results): - debug(None, "") + debug(Debug.INFO, "\nTesting complete ({} test(s))".format(len(results))) - debug(Debug.INFO, "** Testing complete ({} tests) **".format(len(results))) if results: grouped = defaultdict(list) - for t in results: - grouped[t.status].append(t) + for test in results: + grouped[test.status].append(test) + + for status, tests in sorted(grouped.items(), key=lambda x: x[0].index): + if tests: + debug(Debug.REPORT, "{} of {}".format(len(tests), len(results)), headers=status) + if status != TestStatus.PASSED: + for test in tests: + debug(Debug.REPORT, "* " + test.dfy, headers=status, silentheaders=True) + + debug(Debug.REPORT, "Testing took {:.2f}s on {} thread(s)".format(results[0].suite_time, results[0].njobs)) - for status, ts in sorted(grouped.items(), key=lambda x: x[0].index): - if ts: - debug(Debug.REPORT, "{}/{} -- {}".format(len(ts), len(results), " ".join(t.name for t in ts)), headers=status) - debug(Debug.REPORT, "Testing took {:.2f}s on {} threads".format(results[0].suite_time, results[0].njobs)) def run(self): - debug(Debug.DEBUG, "Starting {}".format(self.name)) - os.makedirs(self.temp_directory, exist_ok = True) + debug(Debug.DEBUG, "Starting {}".format(self.dfy)) + os.makedirs(self.temp_directory, exist_ok=True) # os.chdir(self.source_directory) stdout, stderr = b'', b'' @@ -162,7 +183,6 @@ class Test: for cmd in self.cmds: debug(Debug.DEBUG, "> {}".format(cmd)) try: - #, timeout = 60*60) proc = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=True) _stdout, _stderr = proc.communicate(timeout=self.timeout) stdout, stderr = stdout + _stdout, stderr + _stderr @@ -182,7 +202,7 @@ class Test: stdout, stderr = stdout.strip(), stderr.strip() if stdout != b"" or stderr != b"": - debug(Debug.TRACE, "Writing the output of {} to {}".format(self.name, self.temp_output_path)) + debug(Debug.TRACE, "Writing the output of {} to {}".format(self.dfy, self.temp_output_path)) with open(self.temp_output_path, mode='ab') as writer: writer.write(stdout + stderr) if stderr != b"": @@ -199,7 +219,7 @@ class Test: self.status = TestStatus.PASSED if self.expected == self.output else TestStatus.FAILED def report(self, tid, total): - debug(Debug.INFO, "[{}] [{:.2f}s] {} ({} of {})".format(self.compiler_id, self.duration, self.name, tid, total), headers=self.status) + debug(Debug.INFO, "[{:5.2f}s] {} ({} of {})".format(self.duration, self.dfy, tid, total), headers=self.status) @staticmethod def write_bytes(base_directory, relative_path, extension, contents): @@ -224,17 +244,20 @@ def setup_parser(): parser.add_argument('paths', type=str, action='store', nargs='+', help='Input files or folders. Folders are searched for .dfy files.') - parser.add_argument('--compiler', type=str, action='append', default=[], - help='Dafny executable.') + parser.add_argument('--compiler', type=str, action='append', default=None, + help='Dafny executable. Default: {}'.format(Defaults.DAFNY_BIN)) + + parser.add_argument('--base-flags', type=str, action='append', default=None, + help='Arguments to pass to dafny. Multiple --flags are concatenated. Default: {}'.format(Defaults.FLAGS)) parser.add_argument('--flags', '-f', type=str, action='append', default=[], - help='Arguments to pass to dafny. Multiple --flags are concatenated.') + help='Additional arguments to pass to dafny. Useful to override some of the defaults found in --base-flags.') parser.add_argument('--njobs', '-j', action='store', type=int, default=None, help='Number of test workers.') parser.add_argument('--exclude', action='append', type=str, default=[], - help='Excluded directories. {} are automatically added.'.format(ALWAYS_EXCLUDED)) + help='Excluded directories. {} are automatically added.'.format(Defaults.ALWAYS_EXCLUDED)) parser.add_argument('--verbosity', action='store', type=int, default=1, help='Set verbosity level. 0: Minimal; 1: Some info; 2: More info.') @@ -261,7 +284,8 @@ def setup_parser(): help="Used in conjuction with --diff, accept the new output.") parser.add_argument('--difftool', action='store', type=str, default="diff", - help='Diff command line.') + help='Diff program. Default: diff.') + return parser @@ -281,7 +305,7 @@ def run_one(test_args): # ignore further work once you receive a kill signal KILLED = True except Exception as e: - debug(Debug.ERROR, "For file {}".format(test.name), e) + debug(Debug.ERROR, "[{}] {}".format(test.dfy, e)) test.status = TestStatus.UNKNOWN return test @@ -331,22 +355,23 @@ def find_tests(paths, compiler_cmds, excluded, timeout): def run_tests(args): - if args.compiler == []: - base_directory = os.path.dirname(os.path.realpath(__file__)) - args.compiler = [os.path.normpath(os.path.join(base_directory, "../Binaries/Dafny.exe"))] + if args.compiler is None: + args.compiler = Defaults.COMPILER + if args.base_flags is None: + args.base_flags = Defaults.FLAGS for compiler in args.compiler: if not os.path.exists(compiler): debug(Debug.ERROR, "Compiler not found: {}".format(compiler)) return - tests = list(find_tests(args.paths, [compiler + ' ' + " ".join(args.flags) + tests = list(find_tests(args.paths, [compiler + ' ' + " ".join(args.base_flags + args.flags) for compiler in args.compiler], - args.exclude + ALWAYS_EXCLUDED, args.timeout)) + args.exclude + Defaults.ALWAYS_EXCLUDED, args.timeout)) tests.sort(key=operator.attrgetter("name")) - args.njobs = args.njobs or os.cpu_count() or 1 - debug(Debug.INFO, "** Running {} tests on {} testing threads, timeout is {:.2f}, started at {}**".format(len(tests), args.njobs, args.timeout, strftime("%H:%M:%S"))) + args.njobs = min(args.njobs or os.cpu_count() or 1, len(tests)) + debug(Debug.INFO, "\nRunning {} test(s) on {} testing thread(s), timeout is {:.2f}s, started at {}".format(len(tests), args.njobs, args.timeout, strftime("%H:%M:%S"))) try: pool = Pool(args.njobs) @@ -384,7 +409,7 @@ def diff(paths, accept, difftool): if not accept: call([difftool, test.expect_path, test.temp_output_path]) - if accept or input("Accept this change? (y/N)") == "y": + if accept or input("Accept this change? (y/N) ") == "y": debug(Debug.INFO, path, "Accepted") shutil.copy(test.temp_output_path, test.expect_path) @@ -392,7 +417,7 @@ def compare_results(globs, time_all): from glob import glob paths = [path for g in globs for path in glob(g)] reports = {path: Test.load_report(path) for path in paths} - resultsets = {path: {test.name: (test.status, test.duration) for test in report} + resultsets = {path: {test.dfy: (test.status, test.duration) for test in report} for path, report in reports.items()} all_tests = set(name for resultset in resultsets.values() for name in resultset.keys()) @@ -422,32 +447,6 @@ def compare_results(globs, time_all): csv_writer.writerow(row) - -# def compare_results(globs): -# from glob import glob -# baseline = dict() -# results = defaultdict(dict) -# paths = [p for g in globs for p in glob(g)] - -# for path in paths: -# report = Test.load_report(path) -# for test in report: # FIXME add return codes (once we have them) -# if test.duration: -# bl = baseline.setdefault(test.name, float(test.duration)) -# result = "timeout" if test.status == TestStatus.TIMEOUT else (float(test.duration) - bl) / bl -# else: -# result = "???" -# results[test.name][path] = result, test.duration, test.status.name - -# with open("compare.csv", mode='w', newline='') as writer: -# csv_writer = csv.writer(writer, dialect='excel') -# csv_writer.writerow(["Name"] + [os.path.split(path)[1].lstrip("0123456789-") for path in paths]) -# for name, tresults in sorted(results.items()): -# res = [tresults.get(path) for path in paths] -# csv_writer.writerow([name] + [r[0] for r in res]) -# csv_writer.writerow([name + "*"] + [r[1:] for r in res]) - - def main(): global VERBOSITY parser = setup_parser() -- cgit v1.2.3 From fc6ebea9b9ec614e4e014c64d9cad7940deb86fb Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 17 Jul 2015 11:50:46 -0700 Subject: Clean up new trigger declarations in Translator.cs --- Source/Dafny/Translator.cs | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 4a7ac006..4d35549c 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -731,7 +731,8 @@ namespace Microsoft.Dafny { var constructor_call = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); var lhs = FunctionCall(ctor.tok, BuiltinFunction.DatatypeCtorId, null, constructor_call); Bpl.Expr q = Bpl.Expr.Eq(lhs, c); - sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, BplForall(bvs, BplTrigger(constructor_call), q), "Constructor identifier")); // NEW_TRIGGER + var trigger = BplTrigger(constructor_call); // NEW_TRIGGER + sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, BplForall(bvs, trigger, q), "Constructor identifier")); } { @@ -762,10 +763,12 @@ namespace Microsoft.Dafny { Bpl.Expr q = Bpl.Expr.Eq(dId, lhs); if (bvs.Count != 0) { // TRIG (exists a#6#0#0: Box, a#6#1#0: DatatypeType :: d == #OnceBuggy.MyDt.Cons(a#6#0#0, a#6#1#0))' - q = new Bpl.ExistsExpr(ctor.tok, bvs, BplTrigger(lhs), q); // NEW_TRIGGER + var trigger = BplTrigger(lhs); // NEW_TRIGGER + q = new Bpl.ExistsExpr(ctor.tok, bvs, trigger, q); } Bpl.Expr dtq = FunctionCall(ctor.tok, ctor.QueryField.FullSanitizedName, Bpl.Type.Bool, dId); - q = BplForall(dBv, BplTrigger(dtq), BplImp(dtq, q)); // NEW_TRIGGER + var triggerb = BplTrigger(dtq); + q = BplForall(dBv, triggerb, BplImp(dtq, q)); // NEW_TRIGGER sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Constructor questionmark has arguments")); } @@ -857,8 +860,8 @@ namespace Microsoft.Dafny { Bpl.Expr rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs); // TRIG (forall a#11#0#0: Box, a#11#1#0: DatatypeType :: BoxRank(a#11#0#0) < DtRank(#_module.List.Cons(a#11#0#0, a#11#1#0))) - var trigger = new Bpl.Trigger(ctor.tok, true, new List { lhs, rhs }); // TRIGGERS: THIS IS BROKEN - q = new Bpl.ForallExpr(ctor.tok, bvs, null, Bpl.Expr.Lt(lhs, rhs)); // NEW_TRIGGER // CLEMENT: Trigger not use because breaks termination checks for match statements + var trigger = (Trigger)null; // FIXME new Bpl.Trigger(ctor.tok, true, new List { lhs, rhs }); // TRIGGERS: THIS IS BROKEN // NEW_TRIGGER + q = new Bpl.ForallExpr(ctor.tok, bvs, trigger, Bpl.Expr.Lt(lhs, rhs)); // CLEMENT: Trigger not is use, because it breaks termination checks for match statements sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Inductive rank")); } else if (argType is SeqType) { // axiom (forall params, i: int :: 0 <= i && i < |arg| ==> DtRank(arg[i]) < DtRank(#dt.ctor(params))); @@ -883,7 +886,8 @@ namespace Microsoft.Dafny { lhs = FunctionCall(ctor.tok, BuiltinFunction.SeqRank, null, args[i]); rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs); - q = new Bpl.ForallExpr(ctor.tok, bvs, new Trigger(lhs.tok, true, new List { lhs, rhs }), Bpl.Expr.Lt(lhs, rhs)); // NEW_TRIGGER + var trigger = new Trigger(lhs.tok, true, new List { lhs, rhs }); // NEW_TRIGGER + q = new Bpl.ForallExpr(ctor.tok, bvs, trigger, Bpl.Expr.Lt(lhs, rhs)); sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Inductive seq rank")); } else if (argType is SetType) { // axiom (forall params, d: Datatype :: arg[d] ==> DtRank(d) < DtRank(#dt.ctor(params))); @@ -898,8 +902,8 @@ namespace Microsoft.Dafny { Bpl.Expr rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs); // TRIG (forall a#50#0#0: Set Box, d: DatatypeType :: a#50#0#0[$Box(d)] ==> DtRank(d) < DtRank(#_module.d3.B3(a#50#0#0))) - var trigger = new Bpl.Trigger(ctor.tok, true, new List { lhs, rhs }); //TRIGGERS: Should this mention the precondition ("ante") too? - q = new Bpl.ForallExpr(ctor.tok, bvs, trigger, Bpl.Expr.Imp(ante, Bpl.Expr.Lt(lhs, rhs))); // NEW_TRIGGER + var trigger = new Bpl.Trigger(ctor.tok, true, new List { lhs, rhs }); // NEW_TRIGGER //TRIGGERS: Should this mention the precondition ("ante") too? + q = new Bpl.ForallExpr(ctor.tok, bvs, trigger, Bpl.Expr.Imp(ante, Bpl.Expr.Lt(lhs, rhs))); sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Inductive set rank")); } else if (argType is MultiSetType) { // axiom (forall params, d: Datatype :: 0 < arg[d] ==> DtRank(d) < DtRank(#dt.ctor(params))); @@ -1120,9 +1124,9 @@ namespace Microsoft.Dafny { CoAxHelper(true, (tyargs, vars, lexprs, rexprs, kVar, k, kGtZero, ly, d0, d1) => { var equal = Bpl.Expr.Eq(d0, d1); var PEq = CoEqualCall(codecl, lexprs, rexprs, k, LayerSucc(ly), d0, d1); + var trigger = BplTrigger(PEq); sink.AddTopLevelDeclaration(new Axiom(dt.tok, - BplForall(vars, BplTrigger(PEq), BplImp(BplAnd(equal, kGtZero), PEq)), - "Prefix equality shortcut")); // NEW_TRIGGER + BplForall(vars, trigger, BplImp(BplAnd(equal, kGtZero), PEq)), "Prefix equality shortcut")); // NEW_TRIGGER }); } } @@ -2499,8 +2503,9 @@ namespace Microsoft.Dafny { funcID = new Bpl.IdentifierExpr(tok, pp.FullSanitizedName, TrType(pp.ResultType)); Bpl.Expr prefixLimitedBody = new Bpl.NAryExpr(tok, new Bpl.FunctionCall(funcID), prefixArgsLimited); Bpl.Expr prefixLimited = pp.FixpointPred is InductivePredicate ? Bpl.Expr.Not(prefixLimitedBody) : prefixLimitedBody; - - var trueAtZero = new Bpl.ForallExpr(tok, moreBvs, BplTrigger(prefixLimitedBody), BplImp(BplAnd(ante, z), prefixLimited)); // NEW_TRIGGER + + var trigger = BplTrigger(prefixLimitedBody); // NEW_TRIGGER + var trueAtZero = new Bpl.ForallExpr(tok, moreBvs, trigger, BplImp(BplAnd(ante, z), prefixLimited)); sink.AddTopLevelDeclaration(new Bpl.Axiom(tok, Bpl.Expr.Imp(activation, trueAtZero), "3rd prefix predicate axiom")); } @@ -4896,7 +4901,8 @@ namespace Microsoft.Dafny { var allowedToRead = Bpl.Expr.SelectTok(e.tok, etran.TheFrame(e.tok), seq, fieldName); //TRIG (forall $i: int :: read($Heap, this, _module.RingBuffer.start) <= $i && $i < read($Heap, this, _module.RingBuffer.start) + read($Heap, this, _module.RingBuffer.len) ==> $_Frame[read($Heap, this, _module.RingBuffer.data), IndexField($i)]) //TRIGGERS: Should this be more specific? - var qq = new Bpl.ForallExpr(e.tok, new List { iVar }, BplTrigger(fieldName), Bpl.Expr.Imp(range, allowedToRead)); // NEW_TRIGGER + var trigger = BplTrigger(fieldName); // NEW_TRIGGER + var qq = new Bpl.ForallExpr(e.tok, new List { iVar }, trigger, Bpl.Expr.Imp(range, allowedToRead)); options.AssertSink(this, builder)(expr.tok, qq, "insufficient reads clause to read the indicated range of array elements", options.AssertKv); } } @@ -6125,7 +6131,8 @@ namespace Microsoft.Dafny { Bpl.IdentifierExpr o = new Bpl.IdentifierExpr(f.tok, oVar); var rhs = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(ff), new List { o }); Bpl.Expr body = Bpl.Expr.Le(Bpl.Expr.Literal(0), rhs); - Bpl.Expr qq = new Bpl.ForallExpr(f.tok, new List { oVar }, BplTrigger(rhs), body); // NEW_TRIGGER + var trigger = BplTrigger(rhs); // NEW_TRIGGER + Bpl.Expr qq = new Bpl.ForallExpr(f.tok, new List { oVar }, trigger, body); sink.AddTopLevelDeclaration(new Bpl.Axiom(f.tok, qq)); } } @@ -10944,7 +10951,8 @@ namespace Microsoft.Dafny { // TRIGGERS: Does this make sense? VSI-Benchmarks\b7 // TRIG (forall $o: ref :: $o != null && read($Heap, this, _module.List.Repr)[$Box($o)] && $o != this ==> !read(old($Heap), $o, alloc)) // TRIG (forall $o: ref :: $o != null && read($Heap, this, _module.Stream.footprint)[$Box($o)] && $o != this ==> !read(old($Heap), $o, alloc)) - return new Bpl.ForallExpr(expr.tok, new List { oVar }, BplTrigger(oNotFresh), body); // NEW_TRIGGER + var trigger = BplTrigger(oNotFresh); // NEW_TRIGGER + return new Bpl.ForallExpr(expr.tok, new List { oVar }, trigger, body); } else if (eeType is SeqType) { // generate: (forall $i: int :: 0 <= $i && $i < Seq#Length(X) && Unbox(Seq#Index(X,$i)) != null ==> !old($Heap)[Unbox(Seq#Index(X,$i)),alloc]) Bpl.Variable iVar = new Bpl.BoundVariable(expr.tok, new Bpl.TypedIdent(expr.tok, "$i", Bpl.Type.Int)); @@ -10957,8 +10965,9 @@ namespace Microsoft.Dafny { Bpl.Expr xsubiNotNull = Bpl.Expr.Neq(XsubI, predef.Null); Bpl.Expr body = Bpl.Expr.Imp(Bpl.Expr.And(iBounds, xsubiNotNull), oIsFresh); //TRIGGERS: Does this make sense? dafny0\SmallTests + // BROKEN // NEW_TRIGGER //TRIG (forall $i: int :: 0 <= $i && $i < Seq#Length(Q#0) && $Unbox(Seq#Index(Q#0, $i)): ref != null ==> !read(old($Heap), $Unbox(Seq#Index(Q#0, $i)): ref, alloc)) - return new Bpl.ForallExpr(expr.tok, new List { iVar }, body); // NEW_TRIGGER + return new Bpl.ForallExpr(expr.tok, new List { iVar }, body); } else if (eeType.IsDatatype) { // translator.FunctionCall(e.tok, BuiltinFunction.DtAlloc, null, TrExpr(e.E), Old.HeapExpr); Bpl.Expr alloc = translator.MkIsAlloc(TrExpr(e.E), eeType, Old.HeapExpr); @@ -13327,7 +13336,8 @@ namespace Microsoft.Dafny { i++; } // TRIG (exists a#0#0#0: Box :: $IsBox(a#0#0#0, _module.DatatypeInduction$T) && $IsAllocBox(a#0#0#0, _module.DatatypeInduction$T, $Heap) && #_module.Tree.Leaf(a#0#0#0) == t#1) - q = new Bpl.ExistsExpr(ctor.tok, bvs, BplTrigger(ct), BplAnd(typeAntecedent, q)); // NEW_TRIGGER + var trigger = BplTrigger(ct); // NEW_TRIGGER + q = new Bpl.ExistsExpr(ctor.tok, bvs, trigger, BplAnd(typeAntecedent, q)); } yield return q; } -- cgit v1.2.3 From 1f53d595ff0e4282ac51a68b124e94cd1af951ec Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Mon, 20 Jul 2015 09:48:50 -0700 Subject: Add missing .expect file + a small fix in runTests.py --- Test/dafny0/IndexIntoUpdate.dfy.expect | 6 ++++++ Test/runTests.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 Test/dafny0/IndexIntoUpdate.dfy.expect diff --git a/Test/dafny0/IndexIntoUpdate.dfy.expect b/Test/dafny0/IndexIntoUpdate.dfy.expect new file mode 100644 index 00000000..3423a20b --- /dev/null +++ b/Test/dafny0/IndexIntoUpdate.dfy.expect @@ -0,0 +1,6 @@ +IndexIntoUpdate.dfy(7,19): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon3_Then + +Dafny program verifier finished with 1 verified, 1 error diff --git a/Test/runTests.py b/Test/runTests.py index efabdc73..b8d8e6f4 100644 --- a/Test/runTests.py +++ b/Test/runTests.py @@ -370,7 +370,7 @@ def run_tests(args): args.exclude + Defaults.ALWAYS_EXCLUDED, args.timeout)) tests.sort(key=operator.attrgetter("name")) - args.njobs = min(args.njobs or os.cpu_count() or 1, len(tests)) + args.njobs = max(1, min(args.njobs or os.cpu_count() or 1, len(tests))) debug(Debug.INFO, "\nRunning {} test(s) on {} testing thread(s), timeout is {:.2f}s, started at {}".format(len(tests), args.njobs, args.timeout, strftime("%H:%M:%S"))) try: -- cgit v1.2.3 From 82cd6194369a376e51a6b525e577f7cc8852ebef Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Mon, 20 Jul 2015 13:00:10 -0700 Subject: Split snapshot tests into separate files and add support for %S in runTests.py --- Test/dafny0/snapshots/Inputs/Snapshots0.v0.dfy | 8 + Test/dafny0/snapshots/Inputs/Snapshots0.v1.dfy | 8 + Test/dafny0/snapshots/Inputs/Snapshots1.v0.dfy | 13 ++ Test/dafny0/snapshots/Inputs/Snapshots1.v1.dfy | 13 ++ Test/dafny0/snapshots/Inputs/Snapshots2.v0.dfy | 19 +++ Test/dafny0/snapshots/Inputs/Snapshots2.v1.dfy | 19 +++ Test/dafny0/snapshots/Inputs/Snapshots3.v0.dfy | 11 ++ Test/dafny0/snapshots/Inputs/Snapshots3.v1.dfy | 11 ++ Test/dafny0/snapshots/Inputs/Snapshots4.v0.dfy | 11 ++ Test/dafny0/snapshots/Inputs/Snapshots4.v1.dfy | 12 ++ Test/dafny0/snapshots/Inputs/Snapshots5.v0.dfy | 26 +++ Test/dafny0/snapshots/Inputs/Snapshots5.v1.dfy | 27 +++ Test/dafny0/snapshots/Inputs/Snapshots6.v0.dfy | 23 +++ Test/dafny0/snapshots/Inputs/Snapshots6.v1.dfy | 23 +++ Test/dafny0/snapshots/Inputs/Snapshots7.v0.dfy | 22 +++ Test/dafny0/snapshots/Inputs/Snapshots7.v1.dfy | 22 +++ Test/dafny0/snapshots/Snapshots0.run.dfy | 2 + Test/dafny0/snapshots/Snapshots0.run.dfy.expect | 25 +++ Test/dafny0/snapshots/Snapshots0.v0.dfy | 8 - Test/dafny0/snapshots/Snapshots0.v1.dfy | 8 - Test/dafny0/snapshots/Snapshots1.run.dfy | 2 + Test/dafny0/snapshots/Snapshots1.run.dfy.expect | 21 +++ Test/dafny0/snapshots/Snapshots1.v0.dfy | 13 -- Test/dafny0/snapshots/Snapshots1.v1.dfy | 13 -- Test/dafny0/snapshots/Snapshots2.run.dfy | 2 + Test/dafny0/snapshots/Snapshots2.run.dfy.expect | 41 +++++ Test/dafny0/snapshots/Snapshots2.v0.dfy | 19 --- Test/dafny0/snapshots/Snapshots2.v1.dfy | 19 --- Test/dafny0/snapshots/Snapshots3.run.dfy | 2 + Test/dafny0/snapshots/Snapshots3.run.dfy.expect | 18 ++ Test/dafny0/snapshots/Snapshots3.v0.dfy | 11 -- Test/dafny0/snapshots/Snapshots3.v1.dfy | 11 -- Test/dafny0/snapshots/Snapshots4.run.dfy | 2 + Test/dafny0/snapshots/Snapshots4.run.dfy.expect | 20 +++ Test/dafny0/snapshots/Snapshots4.v0.dfy | 11 -- Test/dafny0/snapshots/Snapshots4.v1.dfy | 12 -- Test/dafny0/snapshots/Snapshots5.run.dfy | 2 + Test/dafny0/snapshots/Snapshots5.run.dfy.expect | 26 +++ Test/dafny0/snapshots/Snapshots5.v0.dfy | 26 --- Test/dafny0/snapshots/Snapshots5.v1.dfy | 27 --- Test/dafny0/snapshots/Snapshots6.run.dfy | 2 + Test/dafny0/snapshots/Snapshots6.run.dfy.expect | 11 ++ Test/dafny0/snapshots/Snapshots6.v0.dfy | 23 --- Test/dafny0/snapshots/Snapshots6.v1.dfy | 23 --- Test/dafny0/snapshots/Snapshots7.run.dfy | 2 + Test/dafny0/snapshots/Snapshots7.run.dfy.expect | 31 ++++ Test/dafny0/snapshots/Snapshots7.v0.dfy | 22 --- Test/dafny0/snapshots/Snapshots7.v1.dfy | 22 --- Test/dafny0/snapshots/lit.local.cfg | 5 - Test/dafny0/snapshots/runtest.snapshot | 2 - Test/dafny0/snapshots/runtest.snapshot.expect | 209 ------------------------ Test/lit.site.cfg | 2 +- Test/runTests.py | 4 +- 53 files changed, 481 insertions(+), 486 deletions(-) create mode 100644 Test/dafny0/snapshots/Inputs/Snapshots0.v0.dfy create mode 100644 Test/dafny0/snapshots/Inputs/Snapshots0.v1.dfy create mode 100644 Test/dafny0/snapshots/Inputs/Snapshots1.v0.dfy create mode 100644 Test/dafny0/snapshots/Inputs/Snapshots1.v1.dfy create mode 100644 Test/dafny0/snapshots/Inputs/Snapshots2.v0.dfy create mode 100644 Test/dafny0/snapshots/Inputs/Snapshots2.v1.dfy create mode 100644 Test/dafny0/snapshots/Inputs/Snapshots3.v0.dfy create mode 100644 Test/dafny0/snapshots/Inputs/Snapshots3.v1.dfy create mode 100644 Test/dafny0/snapshots/Inputs/Snapshots4.v0.dfy create mode 100644 Test/dafny0/snapshots/Inputs/Snapshots4.v1.dfy create mode 100644 Test/dafny0/snapshots/Inputs/Snapshots5.v0.dfy create mode 100644 Test/dafny0/snapshots/Inputs/Snapshots5.v1.dfy create mode 100644 Test/dafny0/snapshots/Inputs/Snapshots6.v0.dfy create mode 100644 Test/dafny0/snapshots/Inputs/Snapshots6.v1.dfy create mode 100644 Test/dafny0/snapshots/Inputs/Snapshots7.v0.dfy create mode 100644 Test/dafny0/snapshots/Inputs/Snapshots7.v1.dfy create mode 100644 Test/dafny0/snapshots/Snapshots0.run.dfy create mode 100644 Test/dafny0/snapshots/Snapshots0.run.dfy.expect delete mode 100644 Test/dafny0/snapshots/Snapshots0.v0.dfy delete mode 100644 Test/dafny0/snapshots/Snapshots0.v1.dfy create mode 100644 Test/dafny0/snapshots/Snapshots1.run.dfy create mode 100644 Test/dafny0/snapshots/Snapshots1.run.dfy.expect delete mode 100644 Test/dafny0/snapshots/Snapshots1.v0.dfy delete mode 100644 Test/dafny0/snapshots/Snapshots1.v1.dfy create mode 100644 Test/dafny0/snapshots/Snapshots2.run.dfy create mode 100644 Test/dafny0/snapshots/Snapshots2.run.dfy.expect delete mode 100644 Test/dafny0/snapshots/Snapshots2.v0.dfy delete mode 100644 Test/dafny0/snapshots/Snapshots2.v1.dfy create mode 100644 Test/dafny0/snapshots/Snapshots3.run.dfy create mode 100644 Test/dafny0/snapshots/Snapshots3.run.dfy.expect delete mode 100644 Test/dafny0/snapshots/Snapshots3.v0.dfy delete mode 100644 Test/dafny0/snapshots/Snapshots3.v1.dfy create mode 100644 Test/dafny0/snapshots/Snapshots4.run.dfy create mode 100644 Test/dafny0/snapshots/Snapshots4.run.dfy.expect delete mode 100644 Test/dafny0/snapshots/Snapshots4.v0.dfy delete mode 100644 Test/dafny0/snapshots/Snapshots4.v1.dfy create mode 100644 Test/dafny0/snapshots/Snapshots5.run.dfy create mode 100644 Test/dafny0/snapshots/Snapshots5.run.dfy.expect delete mode 100644 Test/dafny0/snapshots/Snapshots5.v0.dfy delete mode 100644 Test/dafny0/snapshots/Snapshots5.v1.dfy create mode 100644 Test/dafny0/snapshots/Snapshots6.run.dfy create mode 100644 Test/dafny0/snapshots/Snapshots6.run.dfy.expect delete mode 100644 Test/dafny0/snapshots/Snapshots6.v0.dfy delete mode 100644 Test/dafny0/snapshots/Snapshots6.v1.dfy create mode 100644 Test/dafny0/snapshots/Snapshots7.run.dfy create mode 100644 Test/dafny0/snapshots/Snapshots7.run.dfy.expect delete mode 100644 Test/dafny0/snapshots/Snapshots7.v0.dfy delete mode 100644 Test/dafny0/snapshots/Snapshots7.v1.dfy delete mode 100644 Test/dafny0/snapshots/lit.local.cfg delete mode 100644 Test/dafny0/snapshots/runtest.snapshot delete mode 100644 Test/dafny0/snapshots/runtest.snapshot.expect diff --git a/Test/dafny0/snapshots/Inputs/Snapshots0.v0.dfy b/Test/dafny0/snapshots/Inputs/Snapshots0.v0.dfy new file mode 100644 index 00000000..73db9f9c --- /dev/null +++ b/Test/dafny0/snapshots/Inputs/Snapshots0.v0.dfy @@ -0,0 +1,8 @@ +method foo() +{ + bar(); + assert false; +} + +method bar() + ensures false; diff --git a/Test/dafny0/snapshots/Inputs/Snapshots0.v1.dfy b/Test/dafny0/snapshots/Inputs/Snapshots0.v1.dfy new file mode 100644 index 00000000..db9fc01a --- /dev/null +++ b/Test/dafny0/snapshots/Inputs/Snapshots0.v1.dfy @@ -0,0 +1,8 @@ +method foo() +{ + bar(); + assert false; // error +} + +method bar() + ensures true; diff --git a/Test/dafny0/snapshots/Inputs/Snapshots1.v0.dfy b/Test/dafny0/snapshots/Inputs/Snapshots1.v0.dfy new file mode 100644 index 00000000..34d066c3 --- /dev/null +++ b/Test/dafny0/snapshots/Inputs/Snapshots1.v0.dfy @@ -0,0 +1,13 @@ +method M() +{ + N(); + assert false; +} + +method N() + ensures P(); + +predicate P() +{ + false +} diff --git a/Test/dafny0/snapshots/Inputs/Snapshots1.v1.dfy b/Test/dafny0/snapshots/Inputs/Snapshots1.v1.dfy new file mode 100644 index 00000000..184ac65d --- /dev/null +++ b/Test/dafny0/snapshots/Inputs/Snapshots1.v1.dfy @@ -0,0 +1,13 @@ +method M() +{ + N(); + assert false; // error +} + +method N() + ensures P(); + +predicate P() +{ + true +} diff --git a/Test/dafny0/snapshots/Inputs/Snapshots2.v0.dfy b/Test/dafny0/snapshots/Inputs/Snapshots2.v0.dfy new file mode 100644 index 00000000..727e177d --- /dev/null +++ b/Test/dafny0/snapshots/Inputs/Snapshots2.v0.dfy @@ -0,0 +1,19 @@ +method M() +{ + N(); + assert false; +} + +method N() + ensures P(); + +predicate P() + ensures P() == Q(); + +predicate Q() + ensures Q() == R(); + +predicate R() +{ + false +} diff --git a/Test/dafny0/snapshots/Inputs/Snapshots2.v1.dfy b/Test/dafny0/snapshots/Inputs/Snapshots2.v1.dfy new file mode 100644 index 00000000..02a91b52 --- /dev/null +++ b/Test/dafny0/snapshots/Inputs/Snapshots2.v1.dfy @@ -0,0 +1,19 @@ +method M() +{ + N(); + assert false; // error +} + +method N() + ensures P(); + +predicate P() + ensures P() == Q(); + +predicate Q() + ensures Q() == R(); + +predicate R() +{ + true +} diff --git a/Test/dafny0/snapshots/Inputs/Snapshots3.v0.dfy b/Test/dafny0/snapshots/Inputs/Snapshots3.v0.dfy new file mode 100644 index 00000000..72607412 --- /dev/null +++ b/Test/dafny0/snapshots/Inputs/Snapshots3.v0.dfy @@ -0,0 +1,11 @@ +method M() +{ + if (*) + { + + } + else + { + assert 0 != 0; // error + } +} diff --git a/Test/dafny0/snapshots/Inputs/Snapshots3.v1.dfy b/Test/dafny0/snapshots/Inputs/Snapshots3.v1.dfy new file mode 100644 index 00000000..3b186318 --- /dev/null +++ b/Test/dafny0/snapshots/Inputs/Snapshots3.v1.dfy @@ -0,0 +1,11 @@ +method M() +{ + if (*) + { + assert true; + } + else + { + assert 0 != 0; // error + } +} diff --git a/Test/dafny0/snapshots/Inputs/Snapshots4.v0.dfy b/Test/dafny0/snapshots/Inputs/Snapshots4.v0.dfy new file mode 100644 index 00000000..beaadfeb --- /dev/null +++ b/Test/dafny0/snapshots/Inputs/Snapshots4.v0.dfy @@ -0,0 +1,11 @@ +method M() +{ + if (*) + { + + } + else + { + assert 0 == 0; + } +} diff --git a/Test/dafny0/snapshots/Inputs/Snapshots4.v1.dfy b/Test/dafny0/snapshots/Inputs/Snapshots4.v1.dfy new file mode 100644 index 00000000..cf9ae753 --- /dev/null +++ b/Test/dafny0/snapshots/Inputs/Snapshots4.v1.dfy @@ -0,0 +1,12 @@ +method M() +{ + if (*) + { + assert 1 != 1; // error + } + else + { + assert 0 == 0; + assert 2 != 2; // error + } +} diff --git a/Test/dafny0/snapshots/Inputs/Snapshots5.v0.dfy b/Test/dafny0/snapshots/Inputs/Snapshots5.v0.dfy new file mode 100644 index 00000000..b81c1a2b --- /dev/null +++ b/Test/dafny0/snapshots/Inputs/Snapshots5.v0.dfy @@ -0,0 +1,26 @@ +method M() +{ + N(); + if (*) + { + + } + else + { + assert (forall b: bool :: b || !b) || 0 != 0; + } + N(); + assert (forall b: bool :: b || !b) || 3 != 3; + if (*) + { + + } + else + { + assert (forall b: bool :: b || !b) || 1 != 1; + } +} + + +method N() + ensures (forall b: bool :: b || !b) || 2 != 2; diff --git a/Test/dafny0/snapshots/Inputs/Snapshots5.v1.dfy b/Test/dafny0/snapshots/Inputs/Snapshots5.v1.dfy new file mode 100644 index 00000000..05dbced0 --- /dev/null +++ b/Test/dafny0/snapshots/Inputs/Snapshots5.v1.dfy @@ -0,0 +1,27 @@ +method M() +{ + N(); + if (*) + { + + } + else + { + assert (forall b: bool :: b || !b) || 0 != 0; + } + N(); + assert (forall b: bool :: b || !b) || 3 != 3; + if (*) + { + + } + else + { + assert (exists b: bool :: b || !b) || 4 != 4; + } + assert (exists b: bool :: b || !b) || 5 != 5; +} + + +method N() + ensures (forall b: bool :: b || !b) || 2 != 2; diff --git a/Test/dafny0/snapshots/Inputs/Snapshots6.v0.dfy b/Test/dafny0/snapshots/Inputs/Snapshots6.v0.dfy new file mode 100644 index 00000000..c3742f4b --- /dev/null +++ b/Test/dafny0/snapshots/Inputs/Snapshots6.v0.dfy @@ -0,0 +1,23 @@ +module M0 +{ + class C + { + method Foo() + { + assume false; + } + } +} + + +module M1 refines M0 +{ + class C + { + method Foo() + { + ...; + assert false; + } + } +} diff --git a/Test/dafny0/snapshots/Inputs/Snapshots6.v1.dfy b/Test/dafny0/snapshots/Inputs/Snapshots6.v1.dfy new file mode 100644 index 00000000..aeb520cb --- /dev/null +++ b/Test/dafny0/snapshots/Inputs/Snapshots6.v1.dfy @@ -0,0 +1,23 @@ +module M0 +{ + class C + { + method Foo() + { + assume true; + } + } +} + + +module M1 refines M0 +{ + class C + { + method Foo() + { + ...; + assert false; + } + } +} diff --git a/Test/dafny0/snapshots/Inputs/Snapshots7.v0.dfy b/Test/dafny0/snapshots/Inputs/Snapshots7.v0.dfy new file mode 100644 index 00000000..27c7da5f --- /dev/null +++ b/Test/dafny0/snapshots/Inputs/Snapshots7.v0.dfy @@ -0,0 +1,22 @@ +module M0 +{ + class C + { + method Foo() + requires false; + { + } + } +} + + +module M1 refines M0 +{ + class C + { + method Foo... + { + assert false; + } + } +} diff --git a/Test/dafny0/snapshots/Inputs/Snapshots7.v1.dfy b/Test/dafny0/snapshots/Inputs/Snapshots7.v1.dfy new file mode 100644 index 00000000..b45dfe78 --- /dev/null +++ b/Test/dafny0/snapshots/Inputs/Snapshots7.v1.dfy @@ -0,0 +1,22 @@ +module M0 +{ + class C + { + method Foo() + requires true; + { + } + } +} + + +module M1 refines M0 +{ + class C + { + method Foo... + { + assert false; + } + } +} diff --git a/Test/dafny0/snapshots/Snapshots0.run.dfy b/Test/dafny0/snapshots/Snapshots0.run.dfy new file mode 100644 index 00000000..cb96468e --- /dev/null +++ b/Test/dafny0/snapshots/Snapshots0.run.dfy @@ -0,0 +1,2 @@ +// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 %S/Inputs/Snapshots0.dfy > "%t" +// RUN: %diff "%s.expect" "%t" diff --git a/Test/dafny0/snapshots/Snapshots0.run.dfy.expect b/Test/dafny0/snapshots/Snapshots0.run.dfy.expect new file mode 100644 index 00000000..96c280d9 --- /dev/null +++ b/Test/dafny0/snapshots/Snapshots0.run.dfy.expect @@ -0,0 +1,25 @@ +Processing command (at Snapshots0.v0.dfy(3,6)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); + >>> DoNothingToAssert +Processing command (at Snapshots0.v0.dfy(4,10)) assert Lit(false); + >>> DoNothingToAssert + +Dafny program verifier finished with 3 verified, 0 errors +Processing implementation CheckWellformed$$_module.__default.bar (at Snapshots0.v1.dfy(7,8)): + >>> added axiom: ##extracted_function##1() == (0 == $ModuleContextHeight && 0 == $FunctionContextHeight) + >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && ##extracted_function##1(); +Processing call to procedure IntraModuleCall$$_module.__default.bar in implementation Impl$$_module.__default.foo (at Snapshots0.v1.dfy(3,6)): + >>> added axiom: (forall call0old#AT#$Heap: Heap, $Heap: Heap :: {:weight 30} { ##extracted_function##2(call0old#AT#$Heap, $Heap) } ##extracted_function##2(call0old#AT#$Heap, $Heap) == (true && Lit(false) && (forall $o: ref, $f: Field alpha :: { read($Heap, $o, $f) } $o != null && read(call0old#AT#$Heap, $o, alloc) ==> read($Heap, $o, $f) == read(call0old#AT#$Heap, $o, $f)) && $HeapSucc(call0old#AT#$Heap, $Heap))) + >>> added after: a##cached##0 := a##cached##0 && ##extracted_function##2(call0old#AT#$Heap, $Heap); +Processing command (at ) a##cached##0 := a##cached##0 && ##extracted_function##1(); + >>> AssumeNegationOfAssumptionVariable +Processing command (at Snapshots0.v1.dfy(3,6)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); + >>> MarkAsFullyVerified +Processing command (at ) a##cached##0 := a##cached##0 && ##extracted_function##2(call0old#AT#$Heap, $Heap); + >>> AssumeNegationOfAssumptionVariable +Processing command (at Snapshots0.v1.dfy(4,10)) assert Lit(false); + >>> MarkAsPartiallyVerified +Snapshots0.v1.dfy(4,10): Error: assertion violation +Execution trace: + (0,0): anon0 + +Dafny program verifier finished with 2 verified, 1 error diff --git a/Test/dafny0/snapshots/Snapshots0.v0.dfy b/Test/dafny0/snapshots/Snapshots0.v0.dfy deleted file mode 100644 index 73db9f9c..00000000 --- a/Test/dafny0/snapshots/Snapshots0.v0.dfy +++ /dev/null @@ -1,8 +0,0 @@ -method foo() -{ - bar(); - assert false; -} - -method bar() - ensures false; diff --git a/Test/dafny0/snapshots/Snapshots0.v1.dfy b/Test/dafny0/snapshots/Snapshots0.v1.dfy deleted file mode 100644 index db9fc01a..00000000 --- a/Test/dafny0/snapshots/Snapshots0.v1.dfy +++ /dev/null @@ -1,8 +0,0 @@ -method foo() -{ - bar(); - assert false; // error -} - -method bar() - ensures true; diff --git a/Test/dafny0/snapshots/Snapshots1.run.dfy b/Test/dafny0/snapshots/Snapshots1.run.dfy new file mode 100644 index 00000000..7c277b3e --- /dev/null +++ b/Test/dafny0/snapshots/Snapshots1.run.dfy @@ -0,0 +1,2 @@ +// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 %S/Inputs/Snapshots1.dfy > "%t" +// RUN: %diff "%s.expect" "%t" diff --git a/Test/dafny0/snapshots/Snapshots1.run.dfy.expect b/Test/dafny0/snapshots/Snapshots1.run.dfy.expect new file mode 100644 index 00000000..878f9905 --- /dev/null +++ b/Test/dafny0/snapshots/Snapshots1.run.dfy.expect @@ -0,0 +1,21 @@ +Processing command (at Snapshots1.v0.dfy(3,4)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); + >>> DoNothingToAssert +Processing command (at Snapshots1.v0.dfy(4,10)) assert Lit(false); + >>> DoNothingToAssert +Processing command (at Snapshots1.v0.dfy(12,3)) assert true; + >>> DoNothingToAssert + +Dafny program verifier finished with 4 verified, 0 errors +Processing call to procedure IntraModuleCall$$_module.__default.N in implementation Impl$$_module.__default.M (at Snapshots1.v1.dfy(3,4)): + >>> added after: a##cached##0 := a##cached##0 && false; +Processing command (at Snapshots1.v1.dfy(12,3)) assert true; + >>> MarkAsFullyVerified +Processing command (at Snapshots1.v1.dfy(3,4)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); + >>> MarkAsFullyVerified +Processing command (at Snapshots1.v1.dfy(4,10)) assert Lit(false); + >>> DoNothingToAssert +Snapshots1.v1.dfy(4,10): Error: assertion violation +Execution trace: + (0,0): anon0 + +Dafny program verifier finished with 3 verified, 1 error diff --git a/Test/dafny0/snapshots/Snapshots1.v0.dfy b/Test/dafny0/snapshots/Snapshots1.v0.dfy deleted file mode 100644 index 34d066c3..00000000 --- a/Test/dafny0/snapshots/Snapshots1.v0.dfy +++ /dev/null @@ -1,13 +0,0 @@ -method M() -{ - N(); - assert false; -} - -method N() - ensures P(); - -predicate P() -{ - false -} diff --git a/Test/dafny0/snapshots/Snapshots1.v1.dfy b/Test/dafny0/snapshots/Snapshots1.v1.dfy deleted file mode 100644 index 184ac65d..00000000 --- a/Test/dafny0/snapshots/Snapshots1.v1.dfy +++ /dev/null @@ -1,13 +0,0 @@ -method M() -{ - N(); - assert false; // error -} - -method N() - ensures P(); - -predicate P() -{ - true -} diff --git a/Test/dafny0/snapshots/Snapshots2.run.dfy b/Test/dafny0/snapshots/Snapshots2.run.dfy new file mode 100644 index 00000000..889a8153 --- /dev/null +++ b/Test/dafny0/snapshots/Snapshots2.run.dfy @@ -0,0 +1,2 @@ +// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 %S/Inputs/Snapshots2.dfy > "%t" +// RUN: %diff "%s.expect" "%t" diff --git a/Test/dafny0/snapshots/Snapshots2.run.dfy.expect b/Test/dafny0/snapshots/Snapshots2.run.dfy.expect new file mode 100644 index 00000000..a6a9bc4c --- /dev/null +++ b/Test/dafny0/snapshots/Snapshots2.run.dfy.expect @@ -0,0 +1,41 @@ +Processing command (at Snapshots2.v0.dfy(3,4)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); + >>> DoNothingToAssert +Processing command (at Snapshots2.v0.dfy(4,10)) assert Lit(false); + >>> DoNothingToAssert +Processing command (at Snapshots2.v0.dfy(11,11)) assert true; + >>> DoNothingToAssert +Processing command (at Snapshots2.v0.dfy(11,15)) assert _module.__default.P($LS($LS($LZ)), $Heap) <==> _module.__default.Q($LS($LS($LZ)), $Heap); + >>> DoNothingToAssert +Processing command (at Snapshots2.v0.dfy(14,11)) assert true; + >>> DoNothingToAssert +Processing command (at Snapshots2.v0.dfy(14,15)) assert _module.__default.Q($LS($LS($LZ)), $Heap) <==> Lit(_module.__default.R($Heap)); + >>> DoNothingToAssert +Processing command (at Snapshots2.v0.dfy(18,3)) assert true; + >>> DoNothingToAssert + +Dafny program verifier finished with 6 verified, 0 errors +Processing call to procedure IntraModuleCall$$_module.__default.N in implementation Impl$$_module.__default.M (at Snapshots2.v1.dfy(3,4)): + >>> added after: a##cached##0 := a##cached##0 && false; +Processing implementation CheckWellformed$$_module.__default.P (at Snapshots2.v1.dfy(10,11)): + >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && false; +Processing implementation CheckWellformed$$_module.__default.Q (at Snapshots2.v1.dfy(13,11)): + >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && false; +Processing command (at Snapshots2.v1.dfy(18,3)) assert true; + >>> MarkAsFullyVerified +Processing command (at Snapshots2.v1.dfy(3,4)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); + >>> MarkAsFullyVerified +Processing command (at Snapshots2.v1.dfy(4,10)) assert Lit(false); + >>> DoNothingToAssert +Snapshots2.v1.dfy(4,10): Error: assertion violation +Execution trace: + (0,0): anon0 +Processing command (at Snapshots2.v1.dfy(11,11)) assert true; + >>> DoNothingToAssert +Processing command (at Snapshots2.v1.dfy(11,15)) assert _module.__default.P($LS($LS($LZ)), $Heap) <==> _module.__default.Q($LS($LS($LZ)), $Heap); + >>> DoNothingToAssert +Processing command (at Snapshots2.v1.dfy(14,11)) assert true; + >>> DoNothingToAssert +Processing command (at Snapshots2.v1.dfy(14,15)) assert _module.__default.Q($LS($LS($LZ)), $Heap) <==> Lit(_module.__default.R($Heap)); + >>> DoNothingToAssert + +Dafny program verifier finished with 5 verified, 1 error diff --git a/Test/dafny0/snapshots/Snapshots2.v0.dfy b/Test/dafny0/snapshots/Snapshots2.v0.dfy deleted file mode 100644 index 727e177d..00000000 --- a/Test/dafny0/snapshots/Snapshots2.v0.dfy +++ /dev/null @@ -1,19 +0,0 @@ -method M() -{ - N(); - assert false; -} - -method N() - ensures P(); - -predicate P() - ensures P() == Q(); - -predicate Q() - ensures Q() == R(); - -predicate R() -{ - false -} diff --git a/Test/dafny0/snapshots/Snapshots2.v1.dfy b/Test/dafny0/snapshots/Snapshots2.v1.dfy deleted file mode 100644 index 02a91b52..00000000 --- a/Test/dafny0/snapshots/Snapshots2.v1.dfy +++ /dev/null @@ -1,19 +0,0 @@ -method M() -{ - N(); - assert false; // error -} - -method N() - ensures P(); - -predicate P() - ensures P() == Q(); - -predicate Q() - ensures Q() == R(); - -predicate R() -{ - true -} diff --git a/Test/dafny0/snapshots/Snapshots3.run.dfy b/Test/dafny0/snapshots/Snapshots3.run.dfy new file mode 100644 index 00000000..3df182d6 --- /dev/null +++ b/Test/dafny0/snapshots/Snapshots3.run.dfy @@ -0,0 +1,2 @@ +// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 %S/Inputs/Snapshots3.dfy > "%t" +// RUN: %diff "%s.expect" "%t" diff --git a/Test/dafny0/snapshots/Snapshots3.run.dfy.expect b/Test/dafny0/snapshots/Snapshots3.run.dfy.expect new file mode 100644 index 00000000..07e2d063 --- /dev/null +++ b/Test/dafny0/snapshots/Snapshots3.run.dfy.expect @@ -0,0 +1,18 @@ +Processing command (at Snapshots3.v0.dfy(9,14)) assert Lit(0 != 0); + >>> DoNothingToAssert +Snapshots3.v0.dfy(9,14): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon3_Else + +Dafny program verifier finished with 1 verified, 1 error +Processing command (at Snapshots3.v1.dfy(5,12)) assert Lit(true); + >>> DoNothingToAssert +Processing command (at Snapshots3.v1.dfy(9,14)) assert Lit(0 != 0); + >>> RecycleError +Snapshots3.v0.dfy(9,14): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon3_Else + +Dafny program verifier finished with 1 verified, 1 error diff --git a/Test/dafny0/snapshots/Snapshots3.v0.dfy b/Test/dafny0/snapshots/Snapshots3.v0.dfy deleted file mode 100644 index 72607412..00000000 --- a/Test/dafny0/snapshots/Snapshots3.v0.dfy +++ /dev/null @@ -1,11 +0,0 @@ -method M() -{ - if (*) - { - - } - else - { - assert 0 != 0; // error - } -} diff --git a/Test/dafny0/snapshots/Snapshots3.v1.dfy b/Test/dafny0/snapshots/Snapshots3.v1.dfy deleted file mode 100644 index 3b186318..00000000 --- a/Test/dafny0/snapshots/Snapshots3.v1.dfy +++ /dev/null @@ -1,11 +0,0 @@ -method M() -{ - if (*) - { - assert true; - } - else - { - assert 0 != 0; // error - } -} diff --git a/Test/dafny0/snapshots/Snapshots4.run.dfy b/Test/dafny0/snapshots/Snapshots4.run.dfy new file mode 100644 index 00000000..fd6bef41 --- /dev/null +++ b/Test/dafny0/snapshots/Snapshots4.run.dfy @@ -0,0 +1,2 @@ +// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 %S/Inputs/Snapshots4.dfy > "%t" +// RUN: %diff "%s.expect" "%t" diff --git a/Test/dafny0/snapshots/Snapshots4.run.dfy.expect b/Test/dafny0/snapshots/Snapshots4.run.dfy.expect new file mode 100644 index 00000000..fdc97775 --- /dev/null +++ b/Test/dafny0/snapshots/Snapshots4.run.dfy.expect @@ -0,0 +1,20 @@ +Processing command (at Snapshots4.v0.dfy(9,14)) assert LitInt(0) == LitInt(0); + >>> DoNothingToAssert + +Dafny program verifier finished with 2 verified, 0 errors +Processing command (at Snapshots4.v1.dfy(5,14)) assert Lit(1 != 1); + >>> DoNothingToAssert +Processing command (at Snapshots4.v1.dfy(9,14)) assert LitInt(0) == LitInt(0); + >>> MarkAsFullyVerified +Processing command (at Snapshots4.v1.dfy(10,14)) assert Lit(2 != 2); + >>> DoNothingToAssert +Snapshots4.v1.dfy(5,14): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon3_Then +Snapshots4.v1.dfy(10,14): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon3_Else + +Dafny program verifier finished with 1 verified, 2 errors diff --git a/Test/dafny0/snapshots/Snapshots4.v0.dfy b/Test/dafny0/snapshots/Snapshots4.v0.dfy deleted file mode 100644 index beaadfeb..00000000 --- a/Test/dafny0/snapshots/Snapshots4.v0.dfy +++ /dev/null @@ -1,11 +0,0 @@ -method M() -{ - if (*) - { - - } - else - { - assert 0 == 0; - } -} diff --git a/Test/dafny0/snapshots/Snapshots4.v1.dfy b/Test/dafny0/snapshots/Snapshots4.v1.dfy deleted file mode 100644 index cf9ae753..00000000 --- a/Test/dafny0/snapshots/Snapshots4.v1.dfy +++ /dev/null @@ -1,12 +0,0 @@ -method M() -{ - if (*) - { - assert 1 != 1; // error - } - else - { - assert 0 == 0; - assert 2 != 2; // error - } -} diff --git a/Test/dafny0/snapshots/Snapshots5.run.dfy b/Test/dafny0/snapshots/Snapshots5.run.dfy new file mode 100644 index 00000000..4f26aac4 --- /dev/null +++ b/Test/dafny0/snapshots/Snapshots5.run.dfy @@ -0,0 +1,2 @@ +// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 %S/Inputs/Snapshots5.dfy > "%t" +// RUN: %diff "%s.expect" "%t" diff --git a/Test/dafny0/snapshots/Snapshots5.run.dfy.expect b/Test/dafny0/snapshots/Snapshots5.run.dfy.expect new file mode 100644 index 00000000..2ad73416 --- /dev/null +++ b/Test/dafny0/snapshots/Snapshots5.run.dfy.expect @@ -0,0 +1,26 @@ +Processing command (at Snapshots5.v0.dfy(3,4)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); + >>> DoNothingToAssert +Processing command (at Snapshots5.v0.dfy(10,40)) assert (forall b#1: bool :: true ==> b#1 || !b#1) || 0 != 0; + >>> DoNothingToAssert +Processing command (at Snapshots5.v0.dfy(12,4)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); + >>> DoNothingToAssert +Processing command (at Snapshots5.v0.dfy(13,38)) assert (forall b#3: bool :: true ==> b#3 || !b#3) || 3 != 3; + >>> DoNothingToAssert +Processing command (at Snapshots5.v0.dfy(20,40)) assert (forall b#5: bool :: true ==> b#5 || !b#5) || 1 != 1; + >>> DoNothingToAssert + +Dafny program verifier finished with 3 verified, 0 errors +Processing command (at Snapshots5.v1.dfy(3,4)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); + >>> MarkAsFullyVerified +Processing command (at Snapshots5.v1.dfy(10,40)) assert (forall b#1: bool :: true ==> b#1 || !b#1) || 0 != 0; + >>> MarkAsFullyVerified +Processing command (at Snapshots5.v1.dfy(12,4)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); + >>> MarkAsFullyVerified +Processing command (at Snapshots5.v1.dfy(13,38)) assert (forall b#3: bool :: true ==> b#3 || !b#3) || 3 != 3; + >>> MarkAsFullyVerified +Processing command (at Snapshots5.v1.dfy(20,40)) assert (exists b#5: bool :: b#5 || !b#5) || 4 != 4; + >>> DoNothingToAssert +Processing command (at Snapshots5.v1.dfy(22,38)) assert (exists b#7: bool :: b#7 || !b#7) || 5 != 5; + >>> DoNothingToAssert + +Dafny program verifier finished with 3 verified, 0 errors diff --git a/Test/dafny0/snapshots/Snapshots5.v0.dfy b/Test/dafny0/snapshots/Snapshots5.v0.dfy deleted file mode 100644 index b81c1a2b..00000000 --- a/Test/dafny0/snapshots/Snapshots5.v0.dfy +++ /dev/null @@ -1,26 +0,0 @@ -method M() -{ - N(); - if (*) - { - - } - else - { - assert (forall b: bool :: b || !b) || 0 != 0; - } - N(); - assert (forall b: bool :: b || !b) || 3 != 3; - if (*) - { - - } - else - { - assert (forall b: bool :: b || !b) || 1 != 1; - } -} - - -method N() - ensures (forall b: bool :: b || !b) || 2 != 2; diff --git a/Test/dafny0/snapshots/Snapshots5.v1.dfy b/Test/dafny0/snapshots/Snapshots5.v1.dfy deleted file mode 100644 index 05dbced0..00000000 --- a/Test/dafny0/snapshots/Snapshots5.v1.dfy +++ /dev/null @@ -1,27 +0,0 @@ -method M() -{ - N(); - if (*) - { - - } - else - { - assert (forall b: bool :: b || !b) || 0 != 0; - } - N(); - assert (forall b: bool :: b || !b) || 3 != 3; - if (*) - { - - } - else - { - assert (exists b: bool :: b || !b) || 4 != 4; - } - assert (exists b: bool :: b || !b) || 5 != 5; -} - - -method N() - ensures (forall b: bool :: b || !b) || 2 != 2; diff --git a/Test/dafny0/snapshots/Snapshots6.run.dfy b/Test/dafny0/snapshots/Snapshots6.run.dfy new file mode 100644 index 00000000..157fc5b7 --- /dev/null +++ b/Test/dafny0/snapshots/Snapshots6.run.dfy @@ -0,0 +1,2 @@ +// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 %S/Inputs/Snapshots6.dfy > "%t" +// RUN: %diff "%s.expect" "%t" diff --git a/Test/dafny0/snapshots/Snapshots6.run.dfy.expect b/Test/dafny0/snapshots/Snapshots6.run.dfy.expect new file mode 100644 index 00000000..af440327 --- /dev/null +++ b/Test/dafny0/snapshots/Snapshots6.run.dfy.expect @@ -0,0 +1,11 @@ +Processing command (at Snapshots6.v0.dfy(20,14)) assert Lit(false); + >>> DoNothingToAssert + +Dafny program verifier finished with 4 verified, 0 errors +Processing command (at Snapshots6.v1.dfy(20,14)) assert Lit(false); + >>> DoNothingToAssert +Snapshots6.v1.dfy(20,14): Error: assertion violation +Execution trace: + (0,0): anon0 + +Dafny program verifier finished with 3 verified, 1 error diff --git a/Test/dafny0/snapshots/Snapshots6.v0.dfy b/Test/dafny0/snapshots/Snapshots6.v0.dfy deleted file mode 100644 index c3742f4b..00000000 --- a/Test/dafny0/snapshots/Snapshots6.v0.dfy +++ /dev/null @@ -1,23 +0,0 @@ -module M0 -{ - class C - { - method Foo() - { - assume false; - } - } -} - - -module M1 refines M0 -{ - class C - { - method Foo() - { - ...; - assert false; - } - } -} diff --git a/Test/dafny0/snapshots/Snapshots6.v1.dfy b/Test/dafny0/snapshots/Snapshots6.v1.dfy deleted file mode 100644 index aeb520cb..00000000 --- a/Test/dafny0/snapshots/Snapshots6.v1.dfy +++ /dev/null @@ -1,23 +0,0 @@ -module M0 -{ - class C - { - method Foo() - { - assume true; - } - } -} - - -module M1 refines M0 -{ - class C - { - method Foo() - { - ...; - assert false; - } - } -} diff --git a/Test/dafny0/snapshots/Snapshots7.run.dfy b/Test/dafny0/snapshots/Snapshots7.run.dfy new file mode 100644 index 00000000..b192f090 --- /dev/null +++ b/Test/dafny0/snapshots/Snapshots7.run.dfy @@ -0,0 +1,2 @@ +// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 %S/Inputs/Snapshots7.dfy > "%t" +// RUN: %diff "%s.expect" "%t" diff --git a/Test/dafny0/snapshots/Snapshots7.run.dfy.expect b/Test/dafny0/snapshots/Snapshots7.run.dfy.expect new file mode 100644 index 00000000..7c073a9a --- /dev/null +++ b/Test/dafny0/snapshots/Snapshots7.run.dfy.expect @@ -0,0 +1,31 @@ +Processing command (at Snapshots7.v0.dfy(19,14)) assert Lit(false); + >>> DoNothingToAssert + +Dafny program verifier finished with 4 verified, 0 errors +Processing implementation CheckWellformed$$_0_M0.C.Foo (at Snapshots7.v1.dfy(5,12)): + >>> added axiom: ##extracted_function##1() == (0 == $ModuleContextHeight && 0 == $FunctionContextHeight) + >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && ##extracted_function##1(); +Processing implementation Impl$$_0_M0.C.Foo (at Snapshots7.v1.dfy(5,12)): + >>> added axiom: ##extracted_function##2() == (0 == $ModuleContextHeight && 0 == $FunctionContextHeight && Lit(false)) + >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && ##extracted_function##2(); +Processing implementation CheckWellformed$$_1_M1.C.Foo (at Snapshots7.v1.dfy[M1](5,12)): + >>> added axiom: ##extracted_function##3() == (1 == $ModuleContextHeight && 0 == $FunctionContextHeight) + >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && ##extracted_function##3(); +Processing implementation Impl$$_1_M1.C.Foo (at Snapshots7.v1.dfy[M1](5,12)): + >>> added axiom: ##extracted_function##4() == (1 == $ModuleContextHeight && 0 == $FunctionContextHeight && Lit(false)) + >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && ##extracted_function##4(); +Processing command (at ) a##cached##0 := a##cached##0 && ##extracted_function##1(); + >>> AssumeNegationOfAssumptionVariable +Processing command (at ) a##cached##0 := a##cached##0 && ##extracted_function##2(); + >>> AssumeNegationOfAssumptionVariable +Processing command (at ) a##cached##0 := a##cached##0 && ##extracted_function##3(); + >>> AssumeNegationOfAssumptionVariable +Processing command (at ) a##cached##0 := a##cached##0 && ##extracted_function##4(); + >>> AssumeNegationOfAssumptionVariable +Processing command (at Snapshots7.v1.dfy(19,14)) assert Lit(false); + >>> MarkAsPartiallyVerified +Snapshots7.v1.dfy(19,14): Error: assertion violation +Execution trace: + (0,0): anon0 + +Dafny program verifier finished with 3 verified, 1 error diff --git a/Test/dafny0/snapshots/Snapshots7.v0.dfy b/Test/dafny0/snapshots/Snapshots7.v0.dfy deleted file mode 100644 index 27c7da5f..00000000 --- a/Test/dafny0/snapshots/Snapshots7.v0.dfy +++ /dev/null @@ -1,22 +0,0 @@ -module M0 -{ - class C - { - method Foo() - requires false; - { - } - } -} - - -module M1 refines M0 -{ - class C - { - method Foo... - { - assert false; - } - } -} diff --git a/Test/dafny0/snapshots/Snapshots7.v1.dfy b/Test/dafny0/snapshots/Snapshots7.v1.dfy deleted file mode 100644 index b45dfe78..00000000 --- a/Test/dafny0/snapshots/Snapshots7.v1.dfy +++ /dev/null @@ -1,22 +0,0 @@ -module M0 -{ - class C - { - method Foo() - requires true; - { - } - } -} - - -module M1 refines M0 -{ - class C - { - method Foo... - { - assert false; - } - } -} diff --git a/Test/dafny0/snapshots/lit.local.cfg b/Test/dafny0/snapshots/lit.local.cfg deleted file mode 100644 index 07cb869f..00000000 --- a/Test/dafny0/snapshots/lit.local.cfg +++ /dev/null @@ -1,5 +0,0 @@ -# This test is unusual in that we don't use the .bpl files -# directly on the command line. So instead we'll invoke -# files in this directory with extension '.snapshot'. There -# will only be one for now -config.suffixes = ['.snapshot'] diff --git a/Test/dafny0/snapshots/runtest.snapshot b/Test/dafny0/snapshots/runtest.snapshot deleted file mode 100644 index 62ccabb3..00000000 --- a/Test/dafny0/snapshots/runtest.snapshot +++ /dev/null @@ -1,2 +0,0 @@ -// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 /verifySeparately Snapshots0.dfy Snapshots1.dfy Snapshots2.dfy Snapshots3.dfy Snapshots4.dfy Snapshots5.dfy Snapshots6.dfy Snapshots7.dfy > "%t" -// RUN: %diff "%s.expect" "%t" diff --git a/Test/dafny0/snapshots/runtest.snapshot.expect b/Test/dafny0/snapshots/runtest.snapshot.expect deleted file mode 100644 index f1050f62..00000000 --- a/Test/dafny0/snapshots/runtest.snapshot.expect +++ /dev/null @@ -1,209 +0,0 @@ - --------------------- Snapshots0.dfy -------------------- -Processing command (at Snapshots0.v0.dfy(3,6)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); - >>> DoNothingToAssert -Processing command (at Snapshots0.v0.dfy(4,10)) assert Lit(false); - >>> DoNothingToAssert - -Dafny program verifier finished with 3 verified, 0 errors -Processing implementation CheckWellformed$$_module.__default.bar (at Snapshots0.v1.dfy(7,8)): - >>> added axiom: ##extracted_function##1() == (0 == $ModuleContextHeight && 0 == $FunctionContextHeight) - >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && ##extracted_function##1(); -Processing call to procedure IntraModuleCall$$_module.__default.bar in implementation Impl$$_module.__default.foo (at Snapshots0.v1.dfy(3,6)): - >>> added axiom: (forall call0old#AT#$Heap: Heap, $Heap: Heap :: {:weight 30} { ##extracted_function##2(call0old#AT#$Heap, $Heap) } ##extracted_function##2(call0old#AT#$Heap, $Heap) == (true && Lit(false) && (forall $o: ref, $f: Field alpha :: { read($Heap, $o, $f) } $o != null && read(call0old#AT#$Heap, $o, alloc) ==> read($Heap, $o, $f) == read(call0old#AT#$Heap, $o, $f)) && $HeapSucc(call0old#AT#$Heap, $Heap))) - >>> added after: a##cached##0 := a##cached##0 && ##extracted_function##2(call0old#AT#$Heap, $Heap); -Processing command (at ) a##cached##0 := a##cached##0 && ##extracted_function##1(); - >>> AssumeNegationOfAssumptionVariable -Processing command (at Snapshots0.v1.dfy(3,6)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); - >>> MarkAsFullyVerified -Processing command (at ) a##cached##0 := a##cached##0 && ##extracted_function##2(call0old#AT#$Heap, $Heap); - >>> AssumeNegationOfAssumptionVariable -Processing command (at Snapshots0.v1.dfy(4,10)) assert Lit(false); - >>> MarkAsPartiallyVerified -Snapshots0.v1.dfy(4,10): Error: assertion violation -Execution trace: - (0,0): anon0 - -Dafny program verifier finished with 2 verified, 1 error - --------------------- Snapshots1.dfy -------------------- -Processing command (at Snapshots1.v0.dfy(3,4)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); - >>> DoNothingToAssert -Processing command (at Snapshots1.v0.dfy(4,10)) assert Lit(false); - >>> DoNothingToAssert -Processing command (at Snapshots1.v0.dfy(12,3)) assert true; - >>> DoNothingToAssert - -Dafny program verifier finished with 4 verified, 0 errors -Processing call to procedure IntraModuleCall$$_module.__default.N in implementation Impl$$_module.__default.M (at Snapshots1.v1.dfy(3,4)): - >>> added after: a##cached##0 := a##cached##0 && false; -Processing command (at Snapshots1.v1.dfy(12,3)) assert true; - >>> MarkAsFullyVerified -Processing command (at Snapshots1.v1.dfy(3,4)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); - >>> MarkAsFullyVerified -Processing command (at Snapshots1.v1.dfy(4,10)) assert Lit(false); - >>> DoNothingToAssert -Snapshots1.v1.dfy(4,10): Error: assertion violation -Execution trace: - (0,0): anon0 - -Dafny program verifier finished with 3 verified, 1 error - --------------------- Snapshots2.dfy -------------------- -Processing command (at Snapshots2.v0.dfy(3,4)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); - >>> DoNothingToAssert -Processing command (at Snapshots2.v0.dfy(4,10)) assert Lit(false); - >>> DoNothingToAssert -Processing command (at Snapshots2.v0.dfy(11,11)) assert true; - >>> DoNothingToAssert -Processing command (at Snapshots2.v0.dfy(11,15)) assert _module.__default.P($LS($LS($LZ)), $Heap) <==> _module.__default.Q($LS($LS($LZ)), $Heap); - >>> DoNothingToAssert -Processing command (at Snapshots2.v0.dfy(14,11)) assert true; - >>> DoNothingToAssert -Processing command (at Snapshots2.v0.dfy(14,15)) assert _module.__default.Q($LS($LS($LZ)), $Heap) <==> Lit(_module.__default.R($Heap)); - >>> DoNothingToAssert -Processing command (at Snapshots2.v0.dfy(18,3)) assert true; - >>> DoNothingToAssert - -Dafny program verifier finished with 6 verified, 0 errors -Processing call to procedure IntraModuleCall$$_module.__default.N in implementation Impl$$_module.__default.M (at Snapshots2.v1.dfy(3,4)): - >>> added after: a##cached##0 := a##cached##0 && false; -Processing implementation CheckWellformed$$_module.__default.P (at Snapshots2.v1.dfy(10,11)): - >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && false; -Processing implementation CheckWellformed$$_module.__default.Q (at Snapshots2.v1.dfy(13,11)): - >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && false; -Processing command (at Snapshots2.v1.dfy(18,3)) assert true; - >>> MarkAsFullyVerified -Processing command (at Snapshots2.v1.dfy(3,4)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); - >>> MarkAsFullyVerified -Processing command (at Snapshots2.v1.dfy(4,10)) assert Lit(false); - >>> DoNothingToAssert -Snapshots2.v1.dfy(4,10): Error: assertion violation -Execution trace: - (0,0): anon0 -Processing command (at Snapshots2.v1.dfy(11,11)) assert true; - >>> DoNothingToAssert -Processing command (at Snapshots2.v1.dfy(11,15)) assert _module.__default.P($LS($LS($LZ)), $Heap) <==> _module.__default.Q($LS($LS($LZ)), $Heap); - >>> DoNothingToAssert -Processing command (at Snapshots2.v1.dfy(14,11)) assert true; - >>> DoNothingToAssert -Processing command (at Snapshots2.v1.dfy(14,15)) assert _module.__default.Q($LS($LS($LZ)), $Heap) <==> Lit(_module.__default.R($Heap)); - >>> DoNothingToAssert - -Dafny program verifier finished with 5 verified, 1 error - --------------------- Snapshots3.dfy -------------------- -Processing command (at Snapshots3.v0.dfy(9,14)) assert Lit(0 != 0); - >>> DoNothingToAssert -Snapshots3.v0.dfy(9,14): Error: assertion violation -Execution trace: - (0,0): anon0 - (0,0): anon3_Else - -Dafny program verifier finished with 1 verified, 1 error -Processing command (at Snapshots3.v1.dfy(5,12)) assert Lit(true); - >>> DoNothingToAssert -Processing command (at Snapshots3.v1.dfy(9,14)) assert Lit(0 != 0); - >>> RecycleError -Snapshots3.v0.dfy(9,14): Error: assertion violation -Execution trace: - (0,0): anon0 - (0,0): anon3_Else - -Dafny program verifier finished with 1 verified, 1 error - --------------------- Snapshots4.dfy -------------------- -Processing command (at Snapshots4.v0.dfy(9,14)) assert LitInt(0) == LitInt(0); - >>> DoNothingToAssert - -Dafny program verifier finished with 2 verified, 0 errors -Processing command (at Snapshots4.v1.dfy(5,14)) assert Lit(1 != 1); - >>> DoNothingToAssert -Processing command (at Snapshots4.v1.dfy(9,14)) assert LitInt(0) == LitInt(0); - >>> MarkAsFullyVerified -Processing command (at Snapshots4.v1.dfy(10,14)) assert Lit(2 != 2); - >>> DoNothingToAssert -Snapshots4.v1.dfy(5,14): Error: assertion violation -Execution trace: - (0,0): anon0 - (0,0): anon3_Then -Snapshots4.v1.dfy(10,14): Error: assertion violation -Execution trace: - (0,0): anon0 - (0,0): anon3_Else - -Dafny program verifier finished with 1 verified, 2 errors - --------------------- Snapshots5.dfy -------------------- -Processing command (at Snapshots5.v0.dfy(3,4)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); - >>> DoNothingToAssert -Processing command (at Snapshots5.v0.dfy(10,40)) assert (forall b#1: bool :: true ==> b#1 || !b#1) || 0 != 0; - >>> DoNothingToAssert -Processing command (at Snapshots5.v0.dfy(12,4)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); - >>> DoNothingToAssert -Processing command (at Snapshots5.v0.dfy(13,38)) assert (forall b#3: bool :: true ==> b#3 || !b#3) || 3 != 3; - >>> DoNothingToAssert -Processing command (at Snapshots5.v0.dfy(20,40)) assert (forall b#5: bool :: true ==> b#5 || !b#5) || 1 != 1; - >>> DoNothingToAssert - -Dafny program verifier finished with 3 verified, 0 errors -Processing command (at Snapshots5.v1.dfy(3,4)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); - >>> MarkAsFullyVerified -Processing command (at Snapshots5.v1.dfy(10,40)) assert (forall b#1: bool :: true ==> b#1 || !b#1) || 0 != 0; - >>> MarkAsFullyVerified -Processing command (at Snapshots5.v1.dfy(12,4)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); - >>> MarkAsFullyVerified -Processing command (at Snapshots5.v1.dfy(13,38)) assert (forall b#3: bool :: true ==> b#3 || !b#3) || 3 != 3; - >>> MarkAsFullyVerified -Processing command (at Snapshots5.v1.dfy(20,40)) assert (exists b#5: bool :: b#5 || !b#5) || 4 != 4; - >>> DoNothingToAssert -Processing command (at Snapshots5.v1.dfy(22,38)) assert (exists b#7: bool :: b#7 || !b#7) || 5 != 5; - >>> DoNothingToAssert - -Dafny program verifier finished with 3 verified, 0 errors - --------------------- Snapshots6.dfy -------------------- -Processing command (at Snapshots6.v0.dfy(20,14)) assert Lit(false); - >>> DoNothingToAssert - -Dafny program verifier finished with 4 verified, 0 errors -Processing command (at Snapshots6.v1.dfy(20,14)) assert Lit(false); - >>> DoNothingToAssert -Snapshots6.v1.dfy(20,14): Error: assertion violation -Execution trace: - (0,0): anon0 - -Dafny program verifier finished with 3 verified, 1 error - --------------------- Snapshots7.dfy -------------------- -Processing command (at Snapshots7.v0.dfy(19,14)) assert Lit(false); - >>> DoNothingToAssert - -Dafny program verifier finished with 4 verified, 0 errors -Processing implementation CheckWellformed$$_0_M0.C.Foo (at Snapshots7.v1.dfy(5,12)): - >>> added axiom: ##extracted_function##1() == (0 == $ModuleContextHeight && 0 == $FunctionContextHeight) - >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && ##extracted_function##1(); -Processing implementation Impl$$_0_M0.C.Foo (at Snapshots7.v1.dfy(5,12)): - >>> added axiom: ##extracted_function##2() == (0 == $ModuleContextHeight && 0 == $FunctionContextHeight && Lit(false)) - >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && ##extracted_function##2(); -Processing implementation CheckWellformed$$_1_M1.C.Foo (at Snapshots7.v1.dfy[M1](5,12)): - >>> added axiom: ##extracted_function##3() == (1 == $ModuleContextHeight && 0 == $FunctionContextHeight) - >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && ##extracted_function##3(); -Processing implementation Impl$$_1_M1.C.Foo (at Snapshots7.v1.dfy[M1](5,12)): - >>> added axiom: ##extracted_function##4() == (1 == $ModuleContextHeight && 0 == $FunctionContextHeight && Lit(false)) - >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && ##extracted_function##4(); -Processing command (at ) a##cached##0 := a##cached##0 && ##extracted_function##1(); - >>> AssumeNegationOfAssumptionVariable -Processing command (at ) a##cached##0 := a##cached##0 && ##extracted_function##2(); - >>> AssumeNegationOfAssumptionVariable -Processing command (at ) a##cached##0 := a##cached##0 && ##extracted_function##3(); - >>> AssumeNegationOfAssumptionVariable -Processing command (at ) a##cached##0 := a##cached##0 && ##extracted_function##4(); - >>> AssumeNegationOfAssumptionVariable -Processing command (at Snapshots7.v1.dfy(19,14)) assert Lit(false); - >>> MarkAsPartiallyVerified -Snapshots7.v1.dfy(19,14): Error: assertion violation -Execution trace: - (0,0): anon0 - -Dafny program verifier finished with 3 verified, 1 error diff --git a/Test/lit.site.cfg b/Test/lit.site.cfg index 1fe593f4..a960bdbc 100644 --- a/Test/lit.site.cfg +++ b/Test/lit.site.cfg @@ -22,7 +22,7 @@ config.suffixes = ['.dfy'] # excludes: A list of directories to exclude from the testsuite. The 'Inputs' # subdirectories contain auxiliary inputs for various tests in their parent # directories. -config.excludes = [] +config.excludes = ['Inputs'] # test_source_root: The root path where tests are located. config.test_source_root = os.path.dirname(os.path.abspath(__file__)) diff --git a/Test/runTests.py b/Test/runTests.py index b8d8e6f4..d2bb61c9 100644 --- a/Test/runTests.py +++ b/Test/runTests.py @@ -34,7 +34,7 @@ except ImportError: pass class Defaults: - ALWAYS_EXCLUDED = ["Output", "snapshots", "sandbox", "desktop"] + ALWAYS_EXCLUDED = ["Inputs", "Output", "sandbox", "desktop"] DAFNY_BIN = os.path.realpath(os.path.join(os.path.dirname(__file__), "../Binaries/Dafny.exe")) COMPILER = [DAFNY_BIN] FLAGS = ["/useBaseNameForFileName", "/compile:1", "/nologo", "/timeLimit:120"] @@ -108,7 +108,9 @@ class Test: self.timeout = timeout self.compiler_id = compiler_id self.cmds = [cmd.replace("%s", self.source_path) for cmd in self.cmds] + self.cmds = [cmd.replace("%S", self.source_directory) for cmd in self.cmds] self.cmds = [cmd.replace("%t", self.temp_output_path) for cmd in self.cmds] + self.cmds = [cmd.replace("%T", self.temp_directory) for cmd in self.cmds] self.status = TestStatus.PENDING self.proc_info = platform.processor() -- cgit v1.2.3 From 458175ab09b00e1fd39851e1b411bd3cafa6edc5 Mon Sep 17 00:00:00 2001 From: Michael Lowell Roberts Date: Mon, 20 Jul 2015 13:59:38 -0700 Subject: clarified error message that occurs when the "opened" keyword is left off of a module import. --- Source/Dafny/Resolver.cs | 2 +- Test/dafny0/Modules0.dfy.expect | 20 ++++++++++---------- Test/dafny0/ResolutionErrors.dfy.expect | 10 +++++----- Test/dafny0/Trait/TraitBasix.dfy.expect | 2 +- Test/dafny0/UserSpecifiedTypeParameters.dfy.expect | 4 ++-- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 8d23f374..26349ef5 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -8593,7 +8593,7 @@ namespace Microsoft.Dafny #endif } else { // ----- None of the above - Error(expr.tok, "Undeclared top-level type or type parameter: {0} (did you forget to qualify a name?)", expr.Name); + Error(expr.tok, "Undeclared top-level type or type parameter: {0} (did you forget to qualify a name or declare a module import 'opened?')", expr.Name); } if (r == null) { diff --git a/Test/dafny0/Modules0.dfy.expect b/Test/dafny0/Modules0.dfy.expect index d2f0bcc8..c63ed937 100644 --- a/Test/dafny0/Modules0.dfy.expect +++ b/Test/dafny0/Modules0.dfy.expect @@ -6,17 +6,17 @@ Modules0.dfy(10,7): Error: Duplicate name of top-level declaration: WazzupA Modules0.dfy(13,7): Error: Duplicate name of top-level declaration: WazzupB Modules0.dfy(14,8): Error: Duplicate name of top-level declaration: WazzupB Modules0.dfy(15,11): Error: Duplicate name of top-level declaration: WazzupB -Modules0.dfy(56,21): Error: Undeclared top-level type or type parameter: MyClass1 (did you forget to qualify a name?) -Modules0.dfy(57,21): Error: Undeclared top-level type or type parameter: MyClass2 (did you forget to qualify a name?) -Modules0.dfy(68,21): Error: Undeclared top-level type or type parameter: MyClass2 (did you forget to qualify a name?) -Modules0.dfy(84,24): Error: Undeclared top-level type or type parameter: MyClassY (did you forget to qualify a name?) -Modules0.dfy(93,19): Error: Undeclared top-level type or type parameter: ClassG (did you forget to qualify a name?) -Modules0.dfy(226,15): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name?) +Modules0.dfy(56,21): Error: Undeclared top-level type or type parameter: MyClass1 (did you forget to qualify a name or declare a module import 'opened?') +Modules0.dfy(57,21): Error: Undeclared top-level type or type parameter: MyClass2 (did you forget to qualify a name or declare a module import 'opened?') +Modules0.dfy(68,21): Error: Undeclared top-level type or type parameter: MyClass2 (did you forget to qualify a name or declare a module import 'opened?') +Modules0.dfy(84,24): Error: Undeclared top-level type or type parameter: MyClassY (did you forget to qualify a name or declare a module import 'opened?') +Modules0.dfy(93,19): Error: Undeclared top-level type or type parameter: ClassG (did you forget to qualify a name or declare a module import 'opened?') +Modules0.dfy(226,15): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') Modules0.dfy(226,8): Error: new can be applied only to reference types (got X) Modules0.dfy(235,13): Error: module 'B' does not declare a type 'X' Modules0.dfy(245,13): Error: unresolved identifier: X Modules0.dfy(246,15): Error: member DoesNotExist does not exist in class X -Modules0.dfy(285,19): Error: Undeclared top-level type or type parameter: D (did you forget to qualify a name?) +Modules0.dfy(285,19): Error: Undeclared top-level type or type parameter: D (did you forget to qualify a name or declare a module import 'opened?') Modules0.dfy(285,12): Error: new can be applied only to reference types (got D) Modules0.dfy(288,25): Error: type of the receiver is not fully determined at this program point Modules0.dfy(289,16): Error: type of the receiver is not fully determined at this program point @@ -25,10 +25,10 @@ Modules0.dfy(290,16): Error: type of the receiver is not fully determined at thi Modules0.dfy(290,17): Error: expected method call, found expression Modules0.dfy(314,18): Error: second argument to "in" must be a set, multiset, or sequence with elements of type Q_Imp.Node, or a map with domain Q_Imp.Node (instead got set) Modules0.dfy(318,13): Error: arguments must have the same type (got Q_Imp.Node and Node) -Modules0.dfy(319,11): Error: Undeclared top-level type or type parameter: LongLostModule (did you forget to qualify a name?) -Modules0.dfy(320,11): Error: Undeclared top-level type or type parameter: Wazzup (did you forget to qualify a name?) +Modules0.dfy(319,11): Error: Undeclared top-level type or type parameter: LongLostModule (did you forget to qualify a name or declare a module import 'opened?') +Modules0.dfy(320,11): Error: Undeclared top-level type or type parameter: Wazzup (did you forget to qualify a name or declare a module import 'opened?') Modules0.dfy(321,17): Error: module 'Q_Imp' does not declare a type 'Edon' Modules0.dfy(323,10): Error: new can be applied only to reference types (got Q_Imp.List) Modules0.dfy(324,30): Error: member Create does not exist in class Klassy -Modules0.dfy(101,14): Error: Undeclared top-level type or type parameter: MyClassY (did you forget to qualify a name?) +Modules0.dfy(101,14): Error: Undeclared top-level type or type parameter: MyClassY (did you forget to qualify a name or declare a module import 'opened?') 31 resolution/type errors detected in Modules0.dfy diff --git a/Test/dafny0/ResolutionErrors.dfy.expect b/Test/dafny0/ResolutionErrors.dfy.expect index c215d354..ae3e8cbf 100644 --- a/Test/dafny0/ResolutionErrors.dfy.expect +++ b/Test/dafny0/ResolutionErrors.dfy.expect @@ -58,8 +58,8 @@ ResolutionErrors.dfy(987,11): Error: Wrong number of type arguments (2 instead o ResolutionErrors.dfy(988,11): Error: Wrong number of type arguments (2 instead of 1) passed to class: C ResolutionErrors.dfy(999,7): Error: Duplicate name of top-level declaration: BadSyn2 ResolutionErrors.dfy(996,17): Error: Wrong number of type arguments (0 instead of 1) passed to datatype: List -ResolutionErrors.dfy(997,17): Error: Undeclared top-level type or type parameter: badName (did you forget to qualify a name?) -ResolutionErrors.dfy(998,22): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name?) +ResolutionErrors.dfy(997,17): Error: Undeclared top-level type or type parameter: badName (did you forget to qualify a name or declare a module import 'opened?') +ResolutionErrors.dfy(998,22): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') ResolutionErrors.dfy(1005,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> A ResolutionErrors.dfy(1008,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A ResolutionErrors.dfy(1012,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A @@ -68,7 +68,7 @@ ResolutionErrors.dfy(1024,7): Error: Cycle among redirecting types (newtypes, ty ResolutionErrors.dfy(1029,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A ResolutionErrors.dfy(1048,21): Error: unresolved identifier: x ResolutionErrors.dfy(1055,35): Error: Wrong number of type arguments (2 instead of 1) passed to opaque type: P -ResolutionErrors.dfy(1067,13): Error: Undeclared top-level type or type parameter: BX (did you forget to qualify a name?) +ResolutionErrors.dfy(1067,13): Error: Undeclared top-level type or type parameter: BX (did you forget to qualify a name or declare a module import 'opened?') ResolutionErrors.dfy(1077,6): Error: RHS (of type P) not assignable to LHS (of type P) ResolutionErrors.dfy(1082,6): Error: RHS (of type P) not assignable to LHS (of type P) ResolutionErrors.dfy(1087,6): Error: RHS (of type P) not assignable to LHS (of type P) @@ -82,8 +82,8 @@ ResolutionErrors.dfy(1225,26): Error: the type of this variable is underspecifie ResolutionErrors.dfy(1226,31): Error: the type of this variable is underspecified ResolutionErrors.dfy(1227,29): Error: the type of this variable is underspecified ResolutionErrors.dfy(1237,34): Error: the type of this variable is underspecified -ResolutionErrors.dfy(1253,21): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name?) -ResolutionErrors.dfy(1254,24): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name?) +ResolutionErrors.dfy(1253,21): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') +ResolutionErrors.dfy(1254,24): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') ResolutionErrors.dfy(1291,16): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (y) ResolutionErrors.dfy(1301,18): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) ResolutionErrors.dfy(1329,15): Error: The name Inner ambiguously refers to a type in one of the modules A, B (try qualifying the type name with the module name) diff --git a/Test/dafny0/Trait/TraitBasix.dfy.expect b/Test/dafny0/Trait/TraitBasix.dfy.expect index 69af0dc5..dbb11c21 100644 --- a/Test/dafny0/Trait/TraitBasix.dfy.expect +++ b/Test/dafny0/Trait/TraitBasix.dfy.expect @@ -1,4 +1,4 @@ -TraitBasix.dfy(91,24): Error: Undeclared top-level type or type parameter: IX (did you forget to qualify a name?) +TraitBasix.dfy(91,24): Error: Undeclared top-level type or type parameter: IX (did you forget to qualify a name or declare a module import 'opened?') TraitBasix.dfy(77,8): Error: field 'x' is inherited from trait 'I2' and is not allowed to be re-declared TraitBasix.dfy(70,8): Error: class 'I0Child' does not implement trait method 'I2.Customizable' TraitBasix.dfy(80,8): Error: class 'I0Child2' does not implement trait function 'I2.F' diff --git a/Test/dafny0/UserSpecifiedTypeParameters.dfy.expect b/Test/dafny0/UserSpecifiedTypeParameters.dfy.expect index 2504fbfb..347252aa 100644 --- a/Test/dafny0/UserSpecifiedTypeParameters.dfy.expect +++ b/Test/dafny0/UserSpecifiedTypeParameters.dfy.expect @@ -1,8 +1,8 @@ UserSpecifiedTypeParameters.dfy(27,12): Error: the type of this variable is underspecified UserSpecifiedTypeParameters.dfy(27,26): Error: type variable 'T' in the function call to 'H' could not be determined UserSpecifiedTypeParameters.dfy(27,26): Error: type variable 'U' in the function call to 'H' could not be determined -UserSpecifiedTypeParameters.dfy(48,22): Error: Undeclared top-level type or type parameter: b (did you forget to qualify a name?) -UserSpecifiedTypeParameters.dfy(48,26): Error: Undeclared top-level type or type parameter: c (did you forget to qualify a name?) +UserSpecifiedTypeParameters.dfy(48,22): Error: Undeclared top-level type or type parameter: b (did you forget to qualify a name or declare a module import 'opened?') +UserSpecifiedTypeParameters.dfy(48,26): Error: Undeclared top-level type or type parameter: c (did you forget to qualify a name or declare a module import 'opened?') UserSpecifiedTypeParameters.dfy(48,18): Error: variable 'a' does not take any type parameters UserSpecifiedTypeParameters.dfy(48,30): Error: non-function expression (of type int) is called with parameters UserSpecifiedTypeParameters.dfy(48,16): Error: wrong number of arguments to function application (function 'F' expects 2, got 1) -- cgit v1.2.3 From 4dd4262daa0247cd78eef104ddf6083d90ee8972 Mon Sep 17 00:00:00 2001 From: Michael Lowell Roberts Date: Mon, 20 Jul 2015 14:05:57 -0700 Subject: fix for apparent typo in Rewriter.cs --- Source/Dafny/Rewriter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Dafny/Rewriter.cs b/Source/Dafny/Rewriter.cs index d6f54aa7..4223fb7f 100644 --- a/Source/Dafny/Rewriter.cs +++ b/Source/Dafny/Rewriter.cs @@ -41,7 +41,7 @@ namespace Microsoft.Dafny Resolver Resolver; internal TriggersRewriter(Resolver resolver) { - Contract.Requires(Resolver != null); + Contract.Requires(resolver != null); this.Resolver = resolver; } -- cgit v1.2.3 From 701741248e5831c1976b8cb2a2cc412987a1332e Mon Sep 17 00:00:00 2001 From: Michael Lowell Roberts Date: Mon, 20 Jul 2015 14:06:06 -0700 Subject: clarified error message that occurs when the "opened" keyword is left off of a module import. --- Source/Dafny/Resolver.cs | 2 +- Test/dafny0/Modules0.dfy.expect | 20 ++++++++++---------- Test/dafny0/ResolutionErrors.dfy.expect | 10 +++++----- Test/dafny0/Trait/TraitBasix.dfy.expect | 2 +- Test/dafny0/UserSpecifiedTypeParameters.dfy.expect | 4 ++-- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 8d23f374..26349ef5 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -8593,7 +8593,7 @@ namespace Microsoft.Dafny #endif } else { // ----- None of the above - Error(expr.tok, "Undeclared top-level type or type parameter: {0} (did you forget to qualify a name?)", expr.Name); + Error(expr.tok, "Undeclared top-level type or type parameter: {0} (did you forget to qualify a name or declare a module import 'opened?')", expr.Name); } if (r == null) { diff --git a/Test/dafny0/Modules0.dfy.expect b/Test/dafny0/Modules0.dfy.expect index d2f0bcc8..c63ed937 100644 --- a/Test/dafny0/Modules0.dfy.expect +++ b/Test/dafny0/Modules0.dfy.expect @@ -6,17 +6,17 @@ Modules0.dfy(10,7): Error: Duplicate name of top-level declaration: WazzupA Modules0.dfy(13,7): Error: Duplicate name of top-level declaration: WazzupB Modules0.dfy(14,8): Error: Duplicate name of top-level declaration: WazzupB Modules0.dfy(15,11): Error: Duplicate name of top-level declaration: WazzupB -Modules0.dfy(56,21): Error: Undeclared top-level type or type parameter: MyClass1 (did you forget to qualify a name?) -Modules0.dfy(57,21): Error: Undeclared top-level type or type parameter: MyClass2 (did you forget to qualify a name?) -Modules0.dfy(68,21): Error: Undeclared top-level type or type parameter: MyClass2 (did you forget to qualify a name?) -Modules0.dfy(84,24): Error: Undeclared top-level type or type parameter: MyClassY (did you forget to qualify a name?) -Modules0.dfy(93,19): Error: Undeclared top-level type or type parameter: ClassG (did you forget to qualify a name?) -Modules0.dfy(226,15): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name?) +Modules0.dfy(56,21): Error: Undeclared top-level type or type parameter: MyClass1 (did you forget to qualify a name or declare a module import 'opened?') +Modules0.dfy(57,21): Error: Undeclared top-level type or type parameter: MyClass2 (did you forget to qualify a name or declare a module import 'opened?') +Modules0.dfy(68,21): Error: Undeclared top-level type or type parameter: MyClass2 (did you forget to qualify a name or declare a module import 'opened?') +Modules0.dfy(84,24): Error: Undeclared top-level type or type parameter: MyClassY (did you forget to qualify a name or declare a module import 'opened?') +Modules0.dfy(93,19): Error: Undeclared top-level type or type parameter: ClassG (did you forget to qualify a name or declare a module import 'opened?') +Modules0.dfy(226,15): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') Modules0.dfy(226,8): Error: new can be applied only to reference types (got X) Modules0.dfy(235,13): Error: module 'B' does not declare a type 'X' Modules0.dfy(245,13): Error: unresolved identifier: X Modules0.dfy(246,15): Error: member DoesNotExist does not exist in class X -Modules0.dfy(285,19): Error: Undeclared top-level type or type parameter: D (did you forget to qualify a name?) +Modules0.dfy(285,19): Error: Undeclared top-level type or type parameter: D (did you forget to qualify a name or declare a module import 'opened?') Modules0.dfy(285,12): Error: new can be applied only to reference types (got D) Modules0.dfy(288,25): Error: type of the receiver is not fully determined at this program point Modules0.dfy(289,16): Error: type of the receiver is not fully determined at this program point @@ -25,10 +25,10 @@ Modules0.dfy(290,16): Error: type of the receiver is not fully determined at thi Modules0.dfy(290,17): Error: expected method call, found expression Modules0.dfy(314,18): Error: second argument to "in" must be a set, multiset, or sequence with elements of type Q_Imp.Node, or a map with domain Q_Imp.Node (instead got set) Modules0.dfy(318,13): Error: arguments must have the same type (got Q_Imp.Node and Node) -Modules0.dfy(319,11): Error: Undeclared top-level type or type parameter: LongLostModule (did you forget to qualify a name?) -Modules0.dfy(320,11): Error: Undeclared top-level type or type parameter: Wazzup (did you forget to qualify a name?) +Modules0.dfy(319,11): Error: Undeclared top-level type or type parameter: LongLostModule (did you forget to qualify a name or declare a module import 'opened?') +Modules0.dfy(320,11): Error: Undeclared top-level type or type parameter: Wazzup (did you forget to qualify a name or declare a module import 'opened?') Modules0.dfy(321,17): Error: module 'Q_Imp' does not declare a type 'Edon' Modules0.dfy(323,10): Error: new can be applied only to reference types (got Q_Imp.List) Modules0.dfy(324,30): Error: member Create does not exist in class Klassy -Modules0.dfy(101,14): Error: Undeclared top-level type or type parameter: MyClassY (did you forget to qualify a name?) +Modules0.dfy(101,14): Error: Undeclared top-level type or type parameter: MyClassY (did you forget to qualify a name or declare a module import 'opened?') 31 resolution/type errors detected in Modules0.dfy diff --git a/Test/dafny0/ResolutionErrors.dfy.expect b/Test/dafny0/ResolutionErrors.dfy.expect index c215d354..ae3e8cbf 100644 --- a/Test/dafny0/ResolutionErrors.dfy.expect +++ b/Test/dafny0/ResolutionErrors.dfy.expect @@ -58,8 +58,8 @@ ResolutionErrors.dfy(987,11): Error: Wrong number of type arguments (2 instead o ResolutionErrors.dfy(988,11): Error: Wrong number of type arguments (2 instead of 1) passed to class: C ResolutionErrors.dfy(999,7): Error: Duplicate name of top-level declaration: BadSyn2 ResolutionErrors.dfy(996,17): Error: Wrong number of type arguments (0 instead of 1) passed to datatype: List -ResolutionErrors.dfy(997,17): Error: Undeclared top-level type or type parameter: badName (did you forget to qualify a name?) -ResolutionErrors.dfy(998,22): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name?) +ResolutionErrors.dfy(997,17): Error: Undeclared top-level type or type parameter: badName (did you forget to qualify a name or declare a module import 'opened?') +ResolutionErrors.dfy(998,22): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') ResolutionErrors.dfy(1005,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> A ResolutionErrors.dfy(1008,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A ResolutionErrors.dfy(1012,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A @@ -68,7 +68,7 @@ ResolutionErrors.dfy(1024,7): Error: Cycle among redirecting types (newtypes, ty ResolutionErrors.dfy(1029,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A ResolutionErrors.dfy(1048,21): Error: unresolved identifier: x ResolutionErrors.dfy(1055,35): Error: Wrong number of type arguments (2 instead of 1) passed to opaque type: P -ResolutionErrors.dfy(1067,13): Error: Undeclared top-level type or type parameter: BX (did you forget to qualify a name?) +ResolutionErrors.dfy(1067,13): Error: Undeclared top-level type or type parameter: BX (did you forget to qualify a name or declare a module import 'opened?') ResolutionErrors.dfy(1077,6): Error: RHS (of type P) not assignable to LHS (of type P) ResolutionErrors.dfy(1082,6): Error: RHS (of type P) not assignable to LHS (of type P) ResolutionErrors.dfy(1087,6): Error: RHS (of type P) not assignable to LHS (of type P) @@ -82,8 +82,8 @@ ResolutionErrors.dfy(1225,26): Error: the type of this variable is underspecifie ResolutionErrors.dfy(1226,31): Error: the type of this variable is underspecified ResolutionErrors.dfy(1227,29): Error: the type of this variable is underspecified ResolutionErrors.dfy(1237,34): Error: the type of this variable is underspecified -ResolutionErrors.dfy(1253,21): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name?) -ResolutionErrors.dfy(1254,24): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name?) +ResolutionErrors.dfy(1253,21): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') +ResolutionErrors.dfy(1254,24): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') ResolutionErrors.dfy(1291,16): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (y) ResolutionErrors.dfy(1301,18): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) ResolutionErrors.dfy(1329,15): Error: The name Inner ambiguously refers to a type in one of the modules A, B (try qualifying the type name with the module name) diff --git a/Test/dafny0/Trait/TraitBasix.dfy.expect b/Test/dafny0/Trait/TraitBasix.dfy.expect index 69af0dc5..dbb11c21 100644 --- a/Test/dafny0/Trait/TraitBasix.dfy.expect +++ b/Test/dafny0/Trait/TraitBasix.dfy.expect @@ -1,4 +1,4 @@ -TraitBasix.dfy(91,24): Error: Undeclared top-level type or type parameter: IX (did you forget to qualify a name?) +TraitBasix.dfy(91,24): Error: Undeclared top-level type or type parameter: IX (did you forget to qualify a name or declare a module import 'opened?') TraitBasix.dfy(77,8): Error: field 'x' is inherited from trait 'I2' and is not allowed to be re-declared TraitBasix.dfy(70,8): Error: class 'I0Child' does not implement trait method 'I2.Customizable' TraitBasix.dfy(80,8): Error: class 'I0Child2' does not implement trait function 'I2.F' diff --git a/Test/dafny0/UserSpecifiedTypeParameters.dfy.expect b/Test/dafny0/UserSpecifiedTypeParameters.dfy.expect index 2504fbfb..347252aa 100644 --- a/Test/dafny0/UserSpecifiedTypeParameters.dfy.expect +++ b/Test/dafny0/UserSpecifiedTypeParameters.dfy.expect @@ -1,8 +1,8 @@ UserSpecifiedTypeParameters.dfy(27,12): Error: the type of this variable is underspecified UserSpecifiedTypeParameters.dfy(27,26): Error: type variable 'T' in the function call to 'H' could not be determined UserSpecifiedTypeParameters.dfy(27,26): Error: type variable 'U' in the function call to 'H' could not be determined -UserSpecifiedTypeParameters.dfy(48,22): Error: Undeclared top-level type or type parameter: b (did you forget to qualify a name?) -UserSpecifiedTypeParameters.dfy(48,26): Error: Undeclared top-level type or type parameter: c (did you forget to qualify a name?) +UserSpecifiedTypeParameters.dfy(48,22): Error: Undeclared top-level type or type parameter: b (did you forget to qualify a name or declare a module import 'opened?') +UserSpecifiedTypeParameters.dfy(48,26): Error: Undeclared top-level type or type parameter: c (did you forget to qualify a name or declare a module import 'opened?') UserSpecifiedTypeParameters.dfy(48,18): Error: variable 'a' does not take any type parameters UserSpecifiedTypeParameters.dfy(48,30): Error: non-function expression (of type int) is called with parameters UserSpecifiedTypeParameters.dfy(48,16): Error: wrong number of arguments to function application (function 'F' expects 2, got 1) -- cgit v1.2.3 From 91654565e2a5a6f36c582fe0c5d0b7eaf57caa09 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Mon, 20 Jul 2015 14:08:47 -0700 Subject: Add calc's attributes to its SubExpressions. --- Source/Dafny/DafnyAst.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index 21f5fadd..8d68e3e2 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -4807,6 +4807,8 @@ namespace Microsoft.Dafny { { 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; } -- cgit v1.2.3 From 10876f2eb49891b6d58280b7d6d9121424e20727 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Mon, 20 Jul 2015 15:50:20 -0700 Subject: runTests.py: Improve reports (show oldest thread) and fix colors on cygwin The color fix is only partial. It's rather tricky to support all combinations of cygwin/cmd and posix/nt Python. At the moment things work well everywhere with native Python. --- Test/runTests.py | 106 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 37 deletions(-) diff --git a/Test/runTests.py b/Test/runTests.py index d2bb61c9..f32b0e8d 100644 --- a/Test/runTests.py +++ b/Test/runTests.py @@ -8,8 +8,8 @@ import operator import platform from enum import Enum from time import time, strftime -from multiprocessing import Pool from collections import defaultdict +from multiprocessing import Pool, Manager from subprocess import Popen, call, PIPE, TimeoutExpired # C:/Python34/python.exe runTests.py --compiler "c:/MSR/dafny/Binaries/Dafny.exe /useBaseNameForFileName /compile:1 /nologo" --difftool "C:\Program Files (x86)\Meld\Meld.exe" -j4 -f "/dprelude preludes\AlmostAllTriggers.bpl" dafny0\SeqFromArray.dfy @@ -39,12 +39,21 @@ class Defaults: COMPILER = [DAFNY_BIN] FLAGS = ["/useBaseNameForFileName", "/compile:1", "/nologo", "/timeLimit:120"] +class Colors: + RED = '\033[91m' + GREEN = '\033[92m' + YELLOW = '\033[93m' + BRIGHT = '\033[1m' + DIM = '\033[2m' + RESET = '\033[0m' + class Debug(Enum): - ERROR = (-1, '\033[91m') - REPORT = (0, '\033[0m', True) - INFO = (1, '\033[0m', True) - DEBUG = (2, '\033[0m') - TRACE = (3, '\033[0m') + ERROR = (-1, Colors.RED) + WARNING = (-1, Colors.YELLOW) + REPORT = (0, Colors.RESET, True) + INFO = (1, Colors.RESET, True) + DEBUG = (2, Colors.RESET) + TRACE = (3, Colors.RESET) def __init__(self, index, color, elide=False): self.index = index @@ -55,7 +64,7 @@ def wrap_color(string, color, silent=False): if silent: return " " * len(string) elif ANSI: - return color + string + '\033[0m' + return color + string + Colors.RESET else: return string @@ -78,11 +87,11 @@ def debug(level, *args, **kwargs): print(*(headers + args), **kwargs) class TestStatus(Enum): - PENDING = (0, '\033[0m') - PASSED = (1, '\033[92m') - FAILED = (2, '\033[91m') - UNKNOWN = (3, '\033[91m') - TIMEOUT = (4, '\033[91m') + PENDING = (0, Colors.RESET) + PASSED = (1, Colors.GREEN) + FAILED = (2, Colors.RED) + UNKNOWN = (3, Colors.RED) + TIMEOUT = (4, Colors.RED) def __init__(self, index, color): self.index = index @@ -117,6 +126,7 @@ class Test: self.time, self.suite_time = None, None self.njobs, self.returncodes = None, [] + self.start, self.end, self.duration = None, None, None @staticmethod def source_to_expect_path(source): @@ -142,7 +152,7 @@ class Test: with open(name + ".csv", mode='w', newline='') as writer: csv_writer = csv.DictWriter(writer, Test.COLUMNS, dialect='excel') - csv_writer.writeheader() # TODO enable later + csv_writer.writeheader() for test in tests: test.serialize(csv_writer) @@ -220,8 +230,17 @@ class Test: self.output = Test.read_normalize(self.temp_output_path) self.status = TestStatus.PASSED if self.expected == self.output else TestStatus.FAILED - def report(self, tid, total): - debug(Debug.INFO, "[{:5.2f}s] {} ({} of {})".format(self.duration, self.dfy, tid, total), headers=self.status) + def report(self, tid, running, alltests): + running = [alltests[rid].fname for rid in running] + # running = ", ".join(running if len(running) <= 2 else (running[:2] + ["..."])) + if running: + running = "; oldest thread: {}".format(wrap_color(running[0], Colors.DIM)) + + fstring = "[{:5.2f}s] {} ({} of {}{})" + message = fstring.format(self.duration, wrap_color(self.dfy, Colors.BRIGHT), + tid, len(alltests), running) + + debug(Debug.INFO, message, headers=self.status) @staticmethod def write_bytes(base_directory, relative_path, extension, contents): @@ -290,27 +309,31 @@ def setup_parser(): return parser - -def run_one(test_args): +def run_one_internal(test, test_id, args, running): global KILLED global VERBOSITY - - test, args = test_args VERBOSITY = args.verbosity - try: - if not KILLED: + if not KILLED: + try: + running.append(test_id) test.run() - except KeyboardInterrupt: - # There's no reliable way to handle this cleanly on Windows: if one - # of the worker dies, it gets respawned. The reliable solution is to - # ignore further work once you receive a kill signal - KILLED = True - except Exception as e: - debug(Debug.ERROR, "[{}] {}".format(test.dfy, e)) - test.status = TestStatus.UNKNOWN + except KeyboardInterrupt: + # There's no reliable way to handle this cleanly on Windows: if one + # of the worker dies, it gets respawned. The reliable solution is to + # ignore further work once you receive a kill signal + KILLED = True + except Exception as e: + debug(Debug.ERROR, "[{}] {}".format(test.dfy, e)) + test.status = TestStatus.UNKNOWN + finally: + running.remove(test_id) + return test +def run_one(args): + return run_one_internal(*args) + def read_one_test(name, fname, compiler_cmds, timeout): for cid, compiler_cmd in enumerate(compiler_cmds): source_path = os.path.realpath(fname) @@ -378,13 +401,16 @@ def run_tests(args): try: pool = Pool(args.njobs) - start = time() results = [] - for tid, test in enumerate(pool.imap_unordered(run_one, [(t, args) for t in tests], 1)): - test.report(tid + 1, len(tests)) - results.append(test) - pool.close() - pool.join() + start = time() + with Manager() as manager: + running = manager.list() + payloads = [(t, tid, args, running) for (tid, t) in enumerate(tests)] + for tid, test in enumerate(pool.imap_unordered(run_one, payloads, 1)): + test.report(tid + 1, running, tests) + results.append(test) + pool.close() + pool.join() suite_time = time() - start for t in results: @@ -394,8 +420,11 @@ def run_tests(args): Test.summarize(results) Test.build_report(results, args.report) except KeyboardInterrupt: - pool.terminate() - pool.join() + try: + pool.terminate() + pool.join() + except (FileNotFoundError, EOFError, ConnectionAbortedError): + pass debug(Debug.ERROR, "Testing interrupted") @@ -455,6 +484,9 @@ def main(): args = parser.parse_args() VERBOSITY = args.verbosity + if os.name != 'nt' and os.environ.get("TERM") == "cygwin": + debug(Debug.WARNING, "If you run into issues, try using Windows' Python instead of Cygwin's") + if args.diff: diff(args.paths, args.accept, args.difftool) elif args.open: -- cgit v1.2.3 From 3ad94e1e638f727a7b862183414effc61dc1b781 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Mon, 20 Jul 2015 15:52:20 -0700 Subject: Fix encoding in Dijkstra.py --- Test/dafny3/Dijkstra.dfy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/dafny3/Dijkstra.dfy b/Test/dafny3/Dijkstra.dfy index 1fbd9b7c..42aa3b9e 100644 --- a/Test/dafny3/Dijkstra.dfy +++ b/Test/dafny3/Dijkstra.dfy @@ -3,7 +3,7 @@ // Example taken from: // Edsger W. Dijkstra: Heuristics for a Calculational Proof. Inf. Process. Lett. (IPL) 53(3):141-143 (1995) -// Transcribed into Dafny by Valentin Wstholz and Nadia Polikarpova. +// Transcribed into Dafny by Valentin Wüstholz and Nadia Polikarpova. // f is an arbitrary function on the natural numbers function f(n: nat) : nat -- cgit v1.2.3 From 0d94c8f5795b092372df5a06d6d0f9306d696d9b Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Tue, 21 Jul 2015 11:38:51 -0700 Subject: Update z3 to 4.4. One test had to be edited. --- Binaries/z3.exe | Bin 6343680 -> 11018752 bytes Test/VerifyThis2015/Problem3.dfy | 20 +++++++++++++++++++- Test/VerifyThis2015/Problem3.dfy.expect | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Binaries/z3.exe b/Binaries/z3.exe index 72aa6ccd..4e29a0a7 100644 Binary files a/Binaries/z3.exe and b/Binaries/z3.exe differ diff --git a/Test/VerifyThis2015/Problem3.dfy b/Test/VerifyThis2015/Problem3.dfy index 4205035d..21bdd4ed 100644 --- a/Test/VerifyThis2015/Problem3.dfy +++ b/Test/VerifyThis2015/Problem3.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" /vcsMaxKeepGoingSplits:5 "%s" > "%t" +// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" // RUN: %diff "%s.expect" "%t" // Rustan Leino @@ -92,6 +92,21 @@ class DoublyLinkedList { } Nodes := nodes; } + + function PopMiddle(s: seq, k: nat) : seq + requires k < |s| { + s[..k] + s[k+1..] + } + + predicate Injective(s: seq) { + forall j, k :: 0 <= j < k < |s| ==> s[j] != s[k] + } + + lemma InjectiveAfterPop(s: seq, k: nat) + requires k < |s| + requires Injective(s) + ensures Injective(PopMiddle(s, k)) { } + method Remove(x: Node) returns (ghost k: int) requires Valid() requires x in Nodes && x != Nodes[0] && x != Nodes[|Nodes|-1] // not allowed to remove end nodes; you may think of them as a sentinel nodes @@ -103,8 +118,11 @@ class DoublyLinkedList { k :| 1 <= k < |Nodes|-1 && Nodes[k] == x; x.R.L := x.L; x.L.R := x.R; + + InjectiveAfterPop(Nodes, k); Nodes := Nodes[..k] + Nodes[k+1..]; } + // One might consider have a precondition that says there exists a "k" with the properties given here. // However, we want to be able to refer to "k" in the postcondition as well, so it's convenient to // burden the client with having to pass in "k" as a ghost parameter. This, however, is really no diff --git a/Test/VerifyThis2015/Problem3.dfy.expect b/Test/VerifyThis2015/Problem3.dfy.expect index 4035605c..d3a9554b 100644 --- a/Test/VerifyThis2015/Problem3.dfy.expect +++ b/Test/VerifyThis2015/Problem3.dfy.expect @@ -1,5 +1,5 @@ -Dafny program verifier finished with 15 verified, 0 errors +Dafny program verifier finished with 19 verified, 0 errors Program compiled successfully Running... -- cgit v1.2.3 From 4fc4d641bb9f90538cd4bde0ad83012bc8622236 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Tue, 21 Jul 2015 13:14:16 -0700 Subject: Small fix in runTests.py --- Test/runTests.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Test/runTests.py b/Test/runTests.py index f32b0e8d..02a6f039 100644 --- a/Test/runTests.py +++ b/Test/runTests.py @@ -233,8 +233,7 @@ class Test: def report(self, tid, running, alltests): running = [alltests[rid].fname for rid in running] # running = ", ".join(running if len(running) <= 2 else (running[:2] + ["..."])) - if running: - running = "; oldest thread: {}".format(wrap_color(running[0], Colors.DIM)) + running = "; oldest thread: {}".format(wrap_color(running[0], Colors.DIM)) if running else "" fstring = "[{:5.2f}s] {} ({} of {}{})" message = fstring.format(self.duration, wrap_color(self.dfy, Colors.BRIGHT), -- cgit v1.2.3 From 2a1e21d5f74d8ea47136de5e6558e9a6abe31468 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Tue, 21 Jul 2015 13:20:13 -0700 Subject: Add Test/sandbox/* to .hgignore --- .hgignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgignore b/.hgignore index ecf4ed7b..22173264 100644 --- a/.hgignore +++ b/.hgignore @@ -6,6 +6,7 @@ syntax: regexp Test/.*/Output Test/desktop/.* Test/([^/]*)/([^/]*)\.sx +^Test/sandbox/.* syntax: glob *.exe *.pdb -- cgit v1.2.3 From 95db5928541c9e19532e474269c68b8c446427c1 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 21 Jul 2015 16:51:19 -0700 Subject: Code reviewed some of the triggers added by changeset 1dacb6d3cc3c --- Source/Dafny/Translator.cs | 59 ++++++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 34 deletions(-) diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index e295d25a..b1298784 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -738,7 +738,7 @@ namespace Microsoft.Dafny { var constructor_call = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); var lhs = FunctionCall(ctor.tok, BuiltinFunction.DatatypeCtorId, null, constructor_call); Bpl.Expr q = Bpl.Expr.Eq(lhs, c); - var trigger = BplTrigger(constructor_call); // NEW_TRIGGER + var trigger = BplTrigger(constructor_call); sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, BplForall(bvs, trigger, q), "Constructor identifier")); } @@ -765,17 +765,15 @@ namespace Microsoft.Dafny { { // Add: axiom (forall d: DatatypeType :: dt.ctor?(d) ==> (exists params :: d == #dt.ctor(params)); CreateBoundVariables(ctor.Formals, out bvs, out args); - Bpl.Expr lhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); + Bpl.Expr rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); Bpl.Expr dId; var dBv = BplBoundVar("d", predef.DatatypeType, out dId); - Bpl.Expr q = Bpl.Expr.Eq(dId, lhs); + Bpl.Expr q = Bpl.Expr.Eq(dId, rhs); if (bvs.Count != 0) { - // TRIG (exists a#6#0#0: Box, a#6#1#0: DatatypeType :: d == #OnceBuggy.MyDt.Cons(a#6#0#0, a#6#1#0))' - var trigger = BplTrigger(lhs); // NEW_TRIGGER - q = new Bpl.ExistsExpr(ctor.tok, bvs, trigger, q); + q = new Bpl.ExistsExpr(ctor.tok, bvs, null/*always in a Skolemization context*/, q); } Bpl.Expr dtq = FunctionCall(ctor.tok, ctor.QueryField.FullSanitizedName, Bpl.Type.Bool, dId); - var triggerb = BplTrigger(dtq); - q = BplForall(dBv, triggerb, BplImp(dtq, q)); // NEW_TRIGGER + var trigger = BplTrigger(dtq); + q = BplForall(dBv, trigger, BplImp(dtq, q)); sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Constructor questionmark has arguments")); } @@ -893,7 +891,7 @@ namespace Microsoft.Dafny { lhs = FunctionCall(ctor.tok, BuiltinFunction.SeqRank, null, args[i]); rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs); - var trigger = new Trigger(lhs.tok, true, new List { lhs, rhs }); // NEW_TRIGGER + var trigger = new Trigger(lhs.tok, true, new List { lhs, rhs }); q = new Bpl.ForallExpr(ctor.tok, bvs, trigger, Bpl.Expr.Lt(lhs, rhs)); sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Inductive seq rank")); } else if (argType is SetType) { @@ -908,8 +906,7 @@ namespace Microsoft.Dafny { Bpl.Expr lhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, ie); Bpl.Expr rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs); - // TRIG (forall a#50#0#0: Set Box, d: DatatypeType :: a#50#0#0[$Box(d)] ==> DtRank(d) < DtRank(#_module.d3.B3(a#50#0#0))) - var trigger = new Bpl.Trigger(ctor.tok, true, new List { lhs, rhs }); // NEW_TRIGGER //TRIGGERS: Should this mention the precondition ("ante") too? + var trigger = new Bpl.Trigger(ctor.tok, true, new List { lhs, rhs }); q = new Bpl.ForallExpr(ctor.tok, bvs, trigger, Bpl.Expr.Imp(ante, Bpl.Expr.Lt(lhs, rhs))); sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Inductive set rank")); } else if (argType is MultiSetType) { @@ -1133,7 +1130,7 @@ namespace Microsoft.Dafny { var PEq = CoEqualCall(codecl, lexprs, rexprs, k, LayerSucc(ly), d0, d1); var trigger = BplTrigger(PEq); sink.AddTopLevelDeclaration(new Axiom(dt.tok, - BplForall(vars, trigger, BplImp(BplAnd(equal, kGtZero), PEq)), "Prefix equality shortcut")); // NEW_TRIGGER + BplForall(vars, trigger, BplImp(BplAnd(equal, kGtZero), PEq)), "Prefix equality shortcut")); }); } } @@ -2522,7 +2519,7 @@ namespace Microsoft.Dafny { Bpl.Expr prefixLimitedBody = new Bpl.NAryExpr(tok, new Bpl.FunctionCall(funcID), prefixArgsLimited); Bpl.Expr prefixLimited = pp.FixpointPred is InductivePredicate ? Bpl.Expr.Not(prefixLimitedBody) : prefixLimitedBody; - var trigger = BplTrigger(prefixLimitedBody); // NEW_TRIGGER + var trigger = BplTrigger(prefixLimitedBody); var trueAtZero = new Bpl.ForallExpr(tok, moreBvs, trigger, BplImp(BplAnd(ante, z), prefixLimited)); sink.AddTopLevelDeclaration(new Bpl.Axiom(tok, Bpl.Expr.Imp(activation, trueAtZero), "3rd prefix predicate axiom")); @@ -4889,9 +4886,7 @@ namespace Microsoft.Dafny { var range = BplAnd(Bpl.Expr.Le(lowerBound, i), Bpl.Expr.Lt(i, upperBound)); var fieldName = FunctionCall(e.tok, BuiltinFunction.IndexField, null, i); var allowedToRead = Bpl.Expr.SelectTok(e.tok, etran.TheFrame(e.tok), seq, fieldName); - //TRIG (forall $i: int :: read($Heap, this, _module.RingBuffer.start) <= $i && $i < read($Heap, this, _module.RingBuffer.start) + read($Heap, this, _module.RingBuffer.len) ==> $_Frame[read($Heap, this, _module.RingBuffer.data), IndexField($i)]) - //TRIGGERS: Should this be more specific? - var trigger = BplTrigger(fieldName); // NEW_TRIGGER + var trigger = BplTrigger(allowedToRead); // Note, the assertion we're about to produce only seems useful in the check-only mode (that is, with subsumption 0), but if it were to be assumed, we'll use this entire RHS as the trigger var qq = new Bpl.ForallExpr(e.tok, new List { iVar }, trigger, Bpl.Expr.Imp(range, allowedToRead)); options.AssertSink(this, builder)(expr.tok, qq, "insufficient reads clause to read the indicated range of array elements", options.AssertKv); } @@ -6107,7 +6102,7 @@ namespace Microsoft.Dafny { Bpl.IdentifierExpr o = new Bpl.IdentifierExpr(f.tok, oVar); var rhs = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(ff), new List { o }); Bpl.Expr body = Bpl.Expr.Le(Bpl.Expr.Literal(0), rhs); - var trigger = BplTrigger(rhs); // NEW_TRIGGER + var trigger = BplTrigger(rhs); Bpl.Expr qq = new Bpl.ForallExpr(f.tok, new List { oVar }, trigger, body); sink.AddTopLevelDeclaration(new Bpl.Axiom(f.tok, qq)); } @@ -11572,10 +11567,10 @@ namespace Microsoft.Dafny { 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)); // SMART_TRIGGER + 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)); // SMART_TRIGGER + return new Bpl.ExistsExpr(expr.tok, new List(), Concat(tyvars, bvars), kv, tr, Bpl.Expr.And(antecedent, body)); } } else if (expr is SetComprehension) { @@ -12691,6 +12686,8 @@ namespace Microsoft.Dafny { Bpl.Trigger TrTrigger(ExpressionTranslator etran, Attributes attribs, IToken tok, Dictionary substMap = null) { + Contract.Requires(etran != null); + Contract.Requires(tok != null); var argsEtran = etran.WithNoLits(); Bpl.Trigger tr = null; foreach (var trigger in attribs.AsEnumerable().Where(aa => aa.Name == "trigger").Select(aa => aa.Args)) { @@ -13005,12 +13002,8 @@ namespace Microsoft.Dafny { List bvars = new List(); Bpl.Expr typeAntecedent = etran.TrBoundVariables(kvars, bvars); Bpl.Expr ih; - if (Attributes.Contains(e.Attributes, "trigger")) { - Bpl.Trigger tr = TrTrigger(etran, e.Attributes, expr.tok, substMap); - ih = new Bpl.ForallExpr(expr.tok, bvars, tr, Bpl.Expr.Imp(typeAntecedent, ihBody)); - } else { - ih = new Bpl.ForallExpr(expr.tok, bvars, Bpl.Expr.Imp(typeAntecedent, ihBody)); // SMART_TRIGGER - } + var tr = TrTrigger(etran, e.Attributes, expr.tok, substMap); + ih = new Bpl.ForallExpr(expr.tok, bvars, tr, Bpl.Expr.Imp(typeAntecedent, ihBody)); // More precisely now: // (forall n :: n-has-expected-type && (forall k :: k < n ==> P(k)) && case0(n) ==> P(n)) @@ -13041,15 +13034,11 @@ namespace Microsoft.Dafny { var ante = BplAnd(BplAnd(typeAntecedent, ih), kase); var bdy = etran.LayerOffset(1).TrExpr(e.LogicalBody()); Bpl.Expr q; + var trig = TrTrigger(etran, e.Attributes, expr.tok); if (position) { - if (Attributes.Contains(e.Attributes, "trigger")) { - Bpl.Trigger tr = TrTrigger(etran, e.Attributes, expr.tok); - q = new Bpl.ForallExpr(kase.tok, bvars, tr, Bpl.Expr.Imp(ante, bdy)); - } else { - q = new Bpl.ForallExpr(kase.tok, bvars, Bpl.Expr.Imp(ante, bdy)); // SMART_TRIGGER - } + q = new Bpl.ForallExpr(kase.tok, bvars, trig, Bpl.Expr.Imp(ante, bdy)); } else { - q = new Bpl.ExistsExpr(kase.tok, bvars, Bpl.Expr.And(ante, bdy)); // SMART_TRIGGER + q = new Bpl.ExistsExpr(kase.tok, bvars, trig, Bpl.Expr.And(ante, bdy)); } splits.Add(new SplitExprInfo(SplitExprInfo.K.Checked, q)); } @@ -13496,8 +13485,7 @@ namespace Microsoft.Dafny { } i++; } - // TRIG (exists a#0#0#0: Box :: $IsBox(a#0#0#0, _module.DatatypeInduction$T) && $IsAllocBox(a#0#0#0, _module.DatatypeInduction$T, $Heap) && #_module.Tree.Leaf(a#0#0#0) == t#1) - var trigger = BplTrigger(ct); // NEW_TRIGGER + var trigger = BplTrigger(ct); // this is probably never used, because this quantifier is not expected ever to appear in a context where it needs to be instantiated q = new Bpl.ExistsExpr(ctor.tok, bvs, trigger, BplAnd(typeAntecedent, q)); } yield return q; @@ -14422,6 +14410,9 @@ namespace Microsoft.Dafny { // Bpl-making-utilities static Bpl.Expr BplForall(IEnumerable args_in, Bpl.Expr body) { // NO_TRIGGER + Contract.Requires(args_in != null); + Contract.Requires(body != null); + Contract.Ensures(Contract.Result() != null); var args = new List(args_in); if (args.Count == 0) { // CLEMENT don't add quantifiers if the body is trivial return body; -- cgit v1.2.3 From 8e25cc2331e4f90d7674f3b3bb218f65f7e93797 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 21 Jul 2015 18:55:28 -0700 Subject: Improved rank axioms for containers --- Source/Dafny/Translator.cs | 111 +++++++++++++++++----------------- Test/dafny0/ContainerRanks.dfy | 33 ++++++++++ Test/dafny0/ContainerRanks.dfy.expect | 2 + 3 files changed, 90 insertions(+), 56 deletions(-) create mode 100644 Test/dafny0/ContainerRanks.dfy create mode 100644 Test/dafny0/ContainerRanks.dfy.expect diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index b1298784..b2c688d9 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -706,8 +706,6 @@ namespace Microsoft.Dafny { sink.AddTopLevelDeclaration(dt_const); foreach (DatatypeCtor ctor in dt.Ctors) { - int i; - // Add: function #dt.ctor(tyVars, paramTypes) returns (DatatypeType); List argTypes = new List(); @@ -794,14 +792,13 @@ namespace Microsoft.Dafny { Bpl.Expr h; var hVar = BplBoundVar("$h", predef.HeapType, out h); Bpl.Expr conj = Bpl.Expr.True; - i = 0; - foreach (Formal arg in ctor.Formals) { + for (var i = 0; i < ctor.Formals.Count; i++) { + var arg = ctor.Formals[i]; if (is_alloc) { conj = BplAnd(conj, MkIsAlloc(args[i], arg.Type, h)); } else { conj = BplAnd(conj, MkIs(args[i], arg.Type)); } - i++; } var c_params = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); var c_ty = ClassTyCon((TopLevelDecl)dt, tyexprs); @@ -837,8 +834,8 @@ namespace Microsoft.Dafny { } // Injectivity axioms for normal arguments - i = 0; - foreach (Formal arg in ctor.Formals) { + for (int i = 0; i < ctor.Formals.Count; i++) { + var arg = ctor.Formals[i]; // function ##dt.ctor#i(DatatypeType) returns (Ti); var sf = ctor.Destructors[i]; Contract.Assert(sf != null); @@ -854,81 +851,83 @@ namespace Microsoft.Dafny { if (dt is IndDatatypeDecl) { var argType = arg.Type.NormalizeExpand(); if (argType.IsDatatype || argType.IsTypeParameter) { - // for datatype: axiom (forall params :: DtRank(params_i) < DtRank(#dt.ctor(params))); - // for type-parameter type: axiom (forall params :: DtRank(Unbox(params_i)) < DtRank(#dt.ctor(params))); + // for datatype: axiom (forall params :: {#dt.ctor(params)} DtRank(params_i) < DtRank(#dt.ctor(params))); + // for type-parameter type: axiom (forall params :: {#dt.ctor(params)} BoxRank(params_i) < DtRank(#dt.ctor(params))); CreateBoundVariables(ctor.Formals, out bvs, out args); Bpl.Expr lhs = FunctionCall(ctor.tok, arg.Type.IsDatatype ? BuiltinFunction.DtRank : BuiltinFunction.BoxRank, null, args[i]); /* CHECK Bpl.Expr lhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, argType.IsDatatype ? args[i] : FunctionCall(ctor.tok, BuiltinFunction.Unbox, predef.DatatypeType, args[i])); - */ - Bpl.Expr rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); - rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs); - // TRIG (forall a#11#0#0: Box, a#11#1#0: DatatypeType :: BoxRank(a#11#0#0) < DtRank(#_module.List.Cons(a#11#0#0, a#11#1#0))) - var trigger = (Trigger)null; // FIXME new Bpl.Trigger(ctor.tok, true, new List { lhs, rhs }); // TRIGGERS: THIS IS BROKEN // NEW_TRIGGER - q = new Bpl.ForallExpr(ctor.tok, bvs, trigger, Bpl.Expr.Lt(lhs, rhs)); // CLEMENT: Trigger not is use, because it breaks termination checks for match statements + */ + Bpl.Expr ct = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); + var rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, ct); + var trigger = BplTrigger(ct); + q = new Bpl.ForallExpr(ctor.tok, bvs, trigger, Bpl.Expr.Lt(lhs, rhs)); sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Inductive rank")); } else if (argType is SeqType) { - // axiom (forall params, i: int :: 0 <= i && i < |arg| ==> DtRank(arg[i]) < DtRank(#dt.ctor(params))); + // axiom (forall params, i: int {#dt.ctor(params)} :: 0 <= i && i < |arg| ==> DtRank(arg[i]) < DtRank(#dt.ctor(params))); // that is: - // axiom (forall params, i: int :: 0 <= i && i < |arg| ==> DtRank(Unbox(Seq#Index(arg,i))) < DtRank(#dt.ctor(params))); - CreateBoundVariables(ctor.Formals, out bvs, out args); - Bpl.Variable iVar = new Bpl.BoundVariable(arg.tok, new Bpl.TypedIdent(arg.tok, "i", Bpl.Type.Int)); - bvs.Add(iVar); - Bpl.IdentifierExpr ie = new Bpl.IdentifierExpr(arg.tok, iVar); - Bpl.Expr ante = Bpl.Expr.And( - Bpl.Expr.Le(Bpl.Expr.Literal(0), ie), - Bpl.Expr.Lt(ie, FunctionCall(arg.tok, BuiltinFunction.SeqLength, null, args[i]))); - Bpl.Expr lhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, - FunctionCall(arg.tok, BuiltinFunction.Unbox, predef.DatatypeType, - FunctionCall(arg.tok, BuiltinFunction.SeqIndex, predef.DatatypeType, args[i], ie))); - Bpl.Expr rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); - rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs); - q = new Bpl.ForallExpr(ctor.tok, bvs, new Trigger(lhs.tok, true, new List { lhs, rhs }), Bpl.Expr.Imp(ante, Bpl.Expr.Lt(lhs, rhs))); - sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q)); - // axiom (forall params, SeqRank(arg) < DtRank(#dt.ctor(params))); - CreateBoundVariables(ctor.Formals, out bvs, out args); - lhs = FunctionCall(ctor.tok, BuiltinFunction.SeqRank, null, args[i]); - rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); - rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs); - var trigger = new Trigger(lhs.tok, true, new List { lhs, rhs }); - q = new Bpl.ForallExpr(ctor.tok, bvs, trigger, Bpl.Expr.Lt(lhs, rhs)); - sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Inductive seq rank")); + // axiom (forall params, i: int {#dt.ctor(params)} :: 0 <= i && i < |arg| ==> DtRank(Unbox(Seq#Index(arg,i))) < DtRank(#dt.ctor(params))); + { + CreateBoundVariables(ctor.Formals, out bvs, out args); + Bpl.Variable iVar = new Bpl.BoundVariable(arg.tok, new Bpl.TypedIdent(arg.tok, "i", Bpl.Type.Int)); + bvs.Add(iVar); + Bpl.IdentifierExpr ie = new Bpl.IdentifierExpr(arg.tok, iVar); + Bpl.Expr ante = Bpl.Expr.And( + Bpl.Expr.Le(Bpl.Expr.Literal(0), ie), + Bpl.Expr.Lt(ie, FunctionCall(arg.tok, BuiltinFunction.SeqLength, null, args[i]))); + var seqIndex = FunctionCall(arg.tok, BuiltinFunction.SeqIndex, predef.DatatypeType, args[i], ie); + Bpl.Expr lhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, + FunctionCall(arg.tok, BuiltinFunction.Unbox, predef.DatatypeType, seqIndex)); + var ct = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); + var rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, ct); + q = new Bpl.ForallExpr(ctor.tok, bvs, new Trigger(lhs.tok, true, new List { seqIndex, ct }), Bpl.Expr.Imp(ante, Bpl.Expr.Lt(lhs, rhs))); + sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q)); + } + + // axiom (forall params {#dt.ctor(params)} :: SeqRank(arg) < DtRank(#dt.ctor(params))); + { + CreateBoundVariables(ctor.Formals, out bvs, out args); + var lhs = FunctionCall(ctor.tok, BuiltinFunction.SeqRank, null, args[i]); + var ct = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); + var rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, ct); + var trigger = BplTrigger(ct); + q = new Bpl.ForallExpr(ctor.tok, bvs, trigger, Bpl.Expr.Lt(lhs, rhs)); + sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Inductive seq rank")); + } } else if (argType is SetType) { - // axiom (forall params, d: Datatype :: arg[d] ==> DtRank(d) < DtRank(#dt.ctor(params))); + // axiom (forall params, d: Datatype {arg[d], #dt.ctor(params)} :: arg[d] ==> DtRank(d) < DtRank(#dt.ctor(params))); // that is: - // axiom (forall params, d: Datatype :: arg[Box(d)] ==> DtRank(d) < DtRank(#dt.ctor(params))); + // axiom (forall params, d: Datatype {arg[Box(d)], #dt.ctor(params)} :: arg[Box(d)] ==> DtRank(d) < DtRank(#dt.ctor(params))); CreateBoundVariables(ctor.Formals, out bvs, out args); Bpl.Variable dVar = new Bpl.BoundVariable(arg.tok, new Bpl.TypedIdent(arg.tok, "d", predef.DatatypeType)); bvs.Add(dVar); Bpl.IdentifierExpr ie = new Bpl.IdentifierExpr(arg.tok, dVar); - Bpl.Expr ante = Bpl.Expr.SelectTok(arg.tok, args[i], FunctionCall(arg.tok, BuiltinFunction.Box, null, ie)); + Bpl.Expr inSet = Bpl.Expr.SelectTok(arg.tok, args[i], FunctionCall(arg.tok, BuiltinFunction.Box, null, ie)); Bpl.Expr lhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, ie); - Bpl.Expr rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); - rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs); - var trigger = new Bpl.Trigger(ctor.tok, true, new List { lhs, rhs }); - q = new Bpl.ForallExpr(ctor.tok, bvs, trigger, Bpl.Expr.Imp(ante, Bpl.Expr.Lt(lhs, rhs))); + var ct = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); + var rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, ct); + var trigger = new Bpl.Trigger(ctor.tok, true, new List { inSet, ct }); + q = new Bpl.ForallExpr(ctor.tok, bvs, trigger, Bpl.Expr.Imp(inSet, Bpl.Expr.Lt(lhs, rhs))); sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Inductive set rank")); } else if (argType is MultiSetType) { - // axiom (forall params, d: Datatype :: 0 < arg[d] ==> DtRank(d) < DtRank(#dt.ctor(params))); + // axiom (forall params, d: Datatype {arg[d], #dt.ctor(params)} :: 0 < arg[d] ==> DtRank(d) < DtRank(#dt.ctor(params))); // that is: - // axiom (forall params, d: Datatype :: 0 < arg[Box(d)] ==> DtRank(d) < DtRank(#dt.ctor(params))); + // axiom (forall params, d: Datatype {arg[Box(d)], #dt.ctor(params)} :: 0 < arg[Box(d)] ==> DtRank(d) < DtRank(#dt.ctor(params))); CreateBoundVariables(ctor.Formals, out bvs, out args); Bpl.Variable dVar = new Bpl.BoundVariable(arg.tok, new Bpl.TypedIdent(arg.tok, "d", predef.DatatypeType)); bvs.Add(dVar); Bpl.IdentifierExpr ie = new Bpl.IdentifierExpr(arg.tok, dVar); - Bpl.Expr ante = Bpl.Expr.Gt(Bpl.Expr.SelectTok(arg.tok, args[i], FunctionCall(arg.tok, BuiltinFunction.Box, null, ie)), Bpl.Expr.Literal(0)); + var inMultiset = Bpl.Expr.SelectTok(arg.tok, args[i], FunctionCall(arg.tok, BuiltinFunction.Box, null, ie)); + Bpl.Expr ante = Bpl.Expr.Gt(inMultiset, Bpl.Expr.Literal(0)); Bpl.Expr lhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, ie); - Bpl.Expr rhs = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); - rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, rhs); - // CLEMENT: Does the test suite cover this? - q = new Bpl.ForallExpr(ctor.tok, bvs, Bpl.Expr.Imp(ante, Bpl.Expr.Lt(lhs, rhs))); // W_TRIGGER + var ct = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); + var rhs = FunctionCall(ctor.tok, BuiltinFunction.DtRank, null, ct); + var trigger = new Bpl.Trigger(ctor.tok, true, new List { inMultiset, ct }); + q = new Bpl.ForallExpr(ctor.tok, bvs, trigger, Bpl.Expr.Imp(ante, Bpl.Expr.Lt(lhs, rhs))); sink.AddTopLevelDeclaration(new Bpl.Axiom(ctor.tok, q, "Inductive multiset rank")); - // CLEMENT: I don't understand what this case disjunction does here; I don't think it's covered by the test suite } } - - i++; } } diff --git a/Test/dafny0/ContainerRanks.dfy b/Test/dafny0/ContainerRanks.dfy new file mode 100644 index 00000000..df35e214 --- /dev/null +++ b/Test/dafny0/ContainerRanks.dfy @@ -0,0 +1,33 @@ +// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +datatype Abc = End | Wrapper(seq) + +lemma SeqRank0(a: Abc) + ensures a != Wrapper([a]) +{ + assert [a][0] == a; // TODO: one could consider strengthening axioms to eliminate the need for this assert + // The reason we need the assert is to match the trigger in the rank axioms produced + // for datatypes containing sequences. + // See "is SeqType" case of AddDatatype in Translator.cs +} + +lemma SeqRank1(s: seq) + requires s != [] + ensures s[0] != Wrapper(s) +{ +} + +datatype Def = End | MultiWrapper(multiset) + +lemma MultisetRank(a: Def) + ensures a != MultiWrapper(multiset{a}) +{ +} + +datatype Ghi = End | SetWrapper(set) + +lemma SetRank(a: Ghi) + ensures a != SetWrapper({a}) +{ +} diff --git a/Test/dafny0/ContainerRanks.dfy.expect b/Test/dafny0/ContainerRanks.dfy.expect new file mode 100644 index 00000000..42fd56a5 --- /dev/null +++ b/Test/dafny0/ContainerRanks.dfy.expect @@ -0,0 +1,2 @@ + +Dafny program verifier finished with 8 verified, 0 errors -- cgit v1.2.3 From c45904b0dcab8ecdf94b031c47684e19708e9608 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Tue, 21 Jul 2015 19:27:25 -0700 Subject: Simplify some of the deduplication code in TriggerGenerator.cs --- Source/Dafny/TriggerGenerator.cs | 93 +++++++++++----------------------------- 1 file changed, 25 insertions(+), 68 deletions(-) diff --git a/Source/Dafny/TriggerGenerator.cs b/Source/Dafny/TriggerGenerator.cs index a777b4ae..cf677e0e 100644 --- a/Source/Dafny/TriggerGenerator.cs +++ b/Source/Dafny/TriggerGenerator.cs @@ -9,8 +9,7 @@ using System.Diagnostics.Contracts; using System.Text; using System.Diagnostics; -//FIXME No LitInts in triggers -//FIXME Generated triggers should be _triggers, and if a predicate is called with something that can't be a trigger head we shouldn't inline it (see .\Test\dafny2\SnapshotableTrees.dfy) +//FIXME Generated triggers should be _triggers //FIXME: When scoring, do not consider old(x) to be higher than x. /* High level note: There are really two processes going on here. One is finding quantifiers; @@ -35,24 +34,6 @@ namespace Microsoft.Dafny { } } - class TriggerCandidateComparer : IEqualityComparer { //FIXME: There is a bunch of these comparers. - private static TriggerCandidateComparer singleton; - internal static TriggerCandidateComparer Instance { - get { return singleton == null ? (singleton = new TriggerCandidateComparer()) : singleton; } - } - - private TriggerCandidateComparer() { } - - public bool Equals(TriggerCandidate x, TriggerCandidate y) { - return x == null && y == null || - x != null && y != null && x.Expr.ExpressionEq(y.Expr); - } - - public int GetHashCode(TriggerCandidate obj) { - return 1; // FIXME: Force collisions. Use until we have a proper hashing strategy for expressions. - } - } - class MultiTriggerCandidate { internal List Candidates; internal List Tags; @@ -63,9 +44,9 @@ namespace Microsoft.Dafny { get { if (potentialMatchingLoops == null) { //FIXME could be optimized by looking at the bindings instead of doing full equality - var candidates = Candidates.Distinct(TriggerCandidateComparer.Instance); + var candidates = Candidates.Deduplicate((x, y) => ExprExtensions.ExpressionEq(x.Expr, y.Expr)); potentialMatchingLoops = candidates.SelectMany(candidate => candidate.MatchesInQuantifierBody) - .Distinct(ExprExtensions.TriggerMatchComparer.Instance).Where(tm => tm.CouldCauseLoops(candidates)).ToList(); + .Deduplicate((x, y) => ExprExtensions.ExpressionEq(x.Expr, y.Expr)).Where(tm => tm.CouldCauseLoops(candidates)).ToList(); } return potentialMatchingLoops; @@ -103,7 +84,7 @@ namespace Microsoft.Dafny { internal bool IsTriggerKiller; internal ISet Variables; internal readonly List PrivateCandidates; - internal readonly List ExportedCandidates; //FIXME using a hashset is useless here + internal readonly List ExportedCandidates; internal TriggerAnnotation(bool IsTriggerKiller, IEnumerable Variables, IEnumerable AllCandidates, IEnumerable PrivateCandidates = null) { @@ -214,6 +195,7 @@ namespace Microsoft.Dafny { expr is LiteralExpr || expr is OldExpr || expr is ThisExpr || + expr is BoxingCastExpr || expr is DatatypeValue) { annotation = AnnotateOther(expr, false); } else { @@ -352,7 +334,7 @@ namespace Microsoft.Dafny { } private static bool DefaultCandidateFilteringFunction(TriggerCandidate candidate, QuantifierExpr quantifier) { - //FIXME this will miss rewritten expressions (CleanupExpr) + //FIXME this will miss rewritten expressions (CleanupExpr). Should introduce an OriginalExpr to compare. candidate.MatchesInQuantifierBody = quantifier.SubexpressionsMatchingTrigger(candidate.Expr).ToList(); return true; } @@ -402,7 +384,7 @@ namespace Microsoft.Dafny { Contract.Requires(MultiCandidateScoringFunction != null); Contract.Requires(MultiCandidateSelectionFunction != null); - AllCandidates = annotation.PrivateCandidates.Distinct(TriggerCandidateComparer.Instance).ToList(); + AllCandidates = annotation.PrivateCandidates.Deduplicate((x, y) => ExprExtensions.ExpressionEq(x.Expr, y.Expr)); Partition(AllCandidates, x => CandidateFilteringFunction(x, quantifier), out SelectedCandidates, out RejectedCandidates); Partition(AllNonEmptySubsets(SelectedCandidates).Select(s => new MultiTriggerCandidate(s)), @@ -487,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); //CLEMENT Check this + AdditionalInformationReporter(quantifier.tok, tooltip, quantifier.tok.val.Length); } 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); //CLEMENT Check this + AdditionalInformationReporter(quantifier.tok, tooltip, quantifier.tok.val.Length); } string warning = multi_candidates.Warning(); @@ -502,10 +484,6 @@ namespace Microsoft.Dafny { } internal static bool IsTriggerKiller(Expression expr) { - // CLEMENT: This should be removed once trigger generation becomes the default - if (!DafnyOptions.O.AutoTriggers) { - return false; - } var annotation = new TriggerGenerator((x, y, z) => { }).Annotate(expr); return annotation.IsTriggerKiller; } @@ -569,6 +547,20 @@ namespace Microsoft.Dafny { } } + internal static class DeduplicateExtension { + public static List Deduplicate(this IEnumerable seq, Func eq) { + List deduplicated = new List(); + + foreach (var elem in seq) { + if (!deduplicated.Any(other => eq(elem, other))) { + deduplicated.Add(elem); + } + } + + return deduplicated; + } + } + static class ExprExtensions { static IEnumerable AllSubExpressions(this Expression expr, bool strict = false) { foreach (var subexpr in expr.SubExpressions) { @@ -604,41 +596,6 @@ namespace Microsoft.Dafny { } } - internal class EqExpressionComparer : IEqualityComparer { //FIXME - private static EqExpressionComparer singleton; - internal static EqExpressionComparer Instance { - get { return singleton == null ? (singleton = new EqExpressionComparer()) : singleton; } - } - - private EqExpressionComparer() { } - - public bool Equals(Expression x, Expression y) { - return x == null && y == null || - x != null && y != null && x.ExpressionEq(y); - } - - public int GetHashCode(Expression obj) { - return 1; - } - } - - internal class TriggerMatchComparer : IEqualityComparer { //FIXME - private static TriggerMatchComparer singleton; - internal static TriggerMatchComparer Instance { - get { return singleton == null ? (singleton = new TriggerMatchComparer()) : singleton; } - } - - private TriggerMatchComparer() { } - - public bool Equals(TriggerMatch x, TriggerMatch y) { - return ExpressionEq(x.Expr, y.Expr); - } - - public int GetHashCode(TriggerMatch obj) { - return 1; - } - } - internal static bool ExpressionEq(this Expression expr1, Expression expr2) { expr1 = GetResolved(expr1); expr2 = GetResolved(expr2); @@ -819,7 +776,7 @@ namespace Microsoft.Dafny { return ShallowEq((SeqSelectExpr)expr1, (SeqSelectExpr)expr2); } else if (expr1 is MemberSelectExpr && expr2 is MemberSelectExpr) { return ShallowEq((MemberSelectExpr)expr1, (MemberSelectExpr)expr2); - } else if (expr1 is MapDisplayExpr && expr2 is MapDisplayExpr) { + } else if (expr1 is MapDisplayExpr && expr2 is MapDisplayExpr) { //Note: MapDisplayExpr is not a DisplayExpression return ShallowEq((MapDisplayExpr)expr1, (MapDisplayExpr)expr2); } else if (expr1 is DisplayExpression && expr2 is DisplayExpression) { return ShallowEq((DisplayExpression)expr1, (DisplayExpression)expr2); @@ -1030,7 +987,7 @@ namespace Microsoft.Dafny { private static bool ShallowEq(DisplayExpression expr1, DisplayExpression expr2) { if (expr1 is SeqDisplayExpr && expr2 is SeqDisplayExpr) { return ShallowEq((SeqDisplayExpr)expr1, (SeqDisplayExpr)expr2); - } else if (expr1 is MultiSetDisplayExpr && expr2 is MultiSetDisplayExpr) { //FIXME MultiSetDisplayExpr is not a DisplayExpression ??! + } else if (expr1 is MultiSetDisplayExpr && expr2 is MultiSetDisplayExpr) { return ShallowEq((MultiSetDisplayExpr)expr1, (MultiSetDisplayExpr)expr2); } else if (expr1 is SetDisplayExpr && expr2 is SetDisplayExpr) { return ShallowEq((SetDisplayExpr)expr1, (SetDisplayExpr)expr2); -- cgit v1.2.3 From fb4915bc1b95d182b08a45c85d92046489ed1078 Mon Sep 17 00:00:00 2001 From: Michael Lowell Roberts Date: Wed, 22 Jul 2015 11:13:14 -0700 Subject: IronDafny related changes: - diabled error message related to ensures clauses requiring function bodies in the case of abstract modules. - fixed bug in similar error message related to bodyless methods in abstract modules. --- Source/Dafny/Dafny.atg | 16 +++++----- Source/Dafny/Parser.cs | 84 +++++++++++++++++++++++++------------------------- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/Source/Dafny/Dafny.atg b/Source/Dafny/Dafny.atg index f30542d0..c4f038d4 100644 --- a/Source/Dafny/Dafny.atg +++ b/Source/Dafny/Dafny.atg @@ -573,7 +573,7 @@ SubModuleDecl | NewtypeDecl (. module.TopLevelDecls.Add(td); .) | OtherTypeDecl (. module.TopLevelDecls.Add(td); .) | IteratorDecl (. module.TopLevelDecls.Add(iter); .) - | ClassMemberDecl + | ClassMemberDecl } "}" (. module.BodyEndTok = t; module.TopLevelDecls.Add(new DefaultClassDecl(module, namedModuleDefaultClassMembers)); @@ -663,7 +663,7 @@ ClassDecl .) . -ClassMemberDecl<.List mm, bool allowConstructors, bool moduleLevelDecl, bool permitAbstractDecl.> +ClassMemberDecl<.List mm, bool allowConstructors, bool moduleLevelDecl, bool isWithinAbstractModule.> = (. Contract.Requires(cce.NonNullElements(mm)); Method/*!*/ m; Function/*!*/ f; @@ -687,7 +687,7 @@ ClassMemberDecl<.List mm, bool allowConstructors, bool moduleLevelDe mmod.IsStatic = false; } .) - FunctionDecl (. mm.Add(f); .) + 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; @@ -697,7 +697,7 @@ ClassMemberDecl<.List mm, bool allowConstructors, bool moduleLevelDe mmod.IsProtected = false; } .) - MethodDecl (. mm.Add(m); .) + MethodDecl (. mm.Add(m); .) ) . DatatypeDecl @@ -949,7 +949,7 @@ GenericParameters<.List/*!*/ typeArgs.> ">" . /*------------------------------------------------------------------------*/ -MethodDecl +MethodDecl = (. Contract.Ensures(Contract.ValueAtReturn(out m) !=null); IToken/*!*/ id = Token.NoToken; bool hasName = false; IToken keywordToken; @@ -1030,7 +1030,7 @@ MethodDecl ] (. - if (!permitAbstractDecl && DafnyOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 && !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported") && !Attributes.Contains(attrs, "decl") && theVerifyThisFile) { + if (!isWithinAbstractModule && DafnyOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 && !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported") && !Attributes.Contains(attrs, "decl") && theVerifyThisFile) { SemErr(t, "a method with an ensures clause must have a body, unless given the :axiom attribute"); } @@ -1253,7 +1253,7 @@ GenericInstantiation<.List/*!*/ gt.> ">" . /*------------------------------------------------------------------------*/ -FunctionDecl +FunctionDecl = (. Contract.Ensures(Contract.ValueAtReturn(out f)!=null); Attributes attrs = null; IToken/*!*/ id = Token.NoToken; // to please compiler @@ -1338,7 +1338,7 @@ FunctionDecl { FunctionSpec } [ FunctionBody ] - (. if (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"); } diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs index 76e0ebba..01438f68 100644 --- a/Source/Dafny/Parser.cs +++ b/Source/Dafny/Parser.cs @@ -74,8 +74,8 @@ public class Parser { public const int _ellipsis = 58; public const int maxT = 138; - const bool T = true; - const bool x = false; + const bool _T = true; + const bool _x = false; const int minErrDist = 2; public Scanner/*!*/ scanner; @@ -684,7 +684,7 @@ bool IsType(ref IToken pt) { break; } case 37: case 38: case 39: case 40: case 41: case 72: case 73: case 74: case 77: case 83: case 84: case 85: case 86: { - ClassMemberDecl(namedModuleDefaultClassMembers, false, !DafnyOptions.O.AllowGlobals, true); + ClassMemberDecl(namedModuleDefaultClassMembers, false, !DafnyOptions.O.AllowGlobals, DafnyOptions.O.IronDafny && isAbstract); break; } } @@ -982,7 +982,7 @@ bool IsType(ref IToken pt) { } - void ClassMemberDecl(List mm, bool allowConstructors, bool moduleLevelDecl, bool permitAbstractDecl) { + void ClassMemberDecl(List mm, bool allowConstructors, bool moduleLevelDecl, bool isWithinAbstractModule) { Contract.Requires(cce.NonNullElements(mm)); Method/*!*/ m; Function/*!*/ f; @@ -1015,7 +1015,7 @@ bool IsType(ref IToken pt) { mmod.IsStatic = false; } - FunctionDecl(mmod, out f); + FunctionDecl(mmod, isWithinAbstractModule, out f); mm.Add(f); } else if (StartOf(6)) { if (moduleLevelDecl && staticToken != null) { @@ -1027,7 +1027,7 @@ bool IsType(ref IToken pt) { mmod.IsProtected = false; } - MethodDecl(mmod, allowConstructors, permitAbstractDecl, out m); + MethodDecl(mmod, allowConstructors, isWithinAbstractModule, out m); mm.Add(m); } else SynErr(151); } @@ -1131,7 +1131,7 @@ bool IsType(ref IToken pt) { OldSemi(); } - void FunctionDecl(MemberModifiers mmod, out Function/*!*/ f) { + void FunctionDecl(MemberModifiers mmod, bool isWithinAbstractModule, out Function/*!*/ f) { Contract.Ensures(Contract.ValueAtReturn(out f)!=null); Attributes attrs = null; IToken/*!*/ id = Token.NoToken; // to please compiler @@ -1257,7 +1257,7 @@ bool IsType(ref IToken pt) { if (la.kind == 45) { FunctionBody(out body, out bodyStart, out bodyEnd); } - if (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"); } @@ -1285,7 +1285,7 @@ bool IsType(ref IToken pt) { } - void MethodDecl(MemberModifiers mmod, bool allowConstructor, bool permitAbstractDecl, out Method/*!*/ m) { + void MethodDecl(MemberModifiers mmod, bool allowConstructor, bool isWithinAbstractModule, out Method/*!*/ m) { Contract.Ensures(Contract.ValueAtReturn(out m) !=null); IToken/*!*/ id = Token.NoToken; bool hasName = false; IToken keywordToken; @@ -1405,7 +1405,7 @@ bool IsType(ref IToken pt) { if (la.kind == 45) { BlockStmt(out body, out bodyStart, out bodyEnd); } - if (!permitAbstractDecl && DafnyOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 && !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported") && !Attributes.Contains(attrs, "decl") && theVerifyThisFile) { + if (!isWithinAbstractModule && DafnyOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 && !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported") && !Attributes.Contains(attrs, "decl") && theVerifyThisFile) { SemErr(t, "a method with an ensures clause must have a body, unless given the :axiom attribute"); } @@ -4389,38 +4389,38 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo } static readonly bool[,]/*!*/ set = { - {T,T,T,T, T,x,x,x, T,x,T,x, x,x,x,x, x,x,x,T, T,x,x,T, x,x,x,T, x,x,T,T, T,x,x,T, T,x,x,T, T,x,T,T, T,T,T,x, x,T,x,x, x,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,T,x,T, T,x,x,T, T,T,x,x, T,x,x,T, T,T,T,T, T,T,x,T, T,x,x,T, x,x,T,T, T,T,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,x,x,x}, - {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,T,x,x, T,x,x,x, x,T,x,T, T,T,T,T, T,T,T,T, T,x,x,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, - {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,T,T,x, x,T,x,x, x,x,x,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, - {x,T,x,x, x,T,T,T, T,T,T,T, T,T,T,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, - {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,x,x, x,T,T,T, T,T,x,x, x,x,T,x, x,x,x,T, x,x,x,x, x,x,x,x, T,T,x,x, T,x,T,x, x,T,x,T, T,T,T,T, T,T,T,T, T,x,x,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, - {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,T,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, - {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, - {x,T,T,T, T,x,x,x, T,x,T,x, x,T,T,T, x,T,T,T, T,x,x,T, x,x,x,x, x,x,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,T, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,T,x, T,T,x,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,T, T,T,x,x, T,x,x,T, T,T,T,T, T,x,x,x}, - {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,T,T,T, T,T,x,T, T,T,T,x, x,T,x,T, x,x,x,x, x,x,x,x, T,T,x,x, T,x,x,x, x,T,x,T, T,T,T,T, T,T,T,T, T,x,x,T, T,T,T,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, - {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,x,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, - {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, - {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,T,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, - {x,T,T,x, x,T,T,T, T,T,T,T, T,T,T,T, T,T,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, - {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,T,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, - {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, - {x,T,T,T, T,x,x,x, T,x,T,x, x,x,x,x, x,x,x,T, T,x,x,T, x,x,x,x, x,x,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,T,x,x, x,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,T,x,T, T,x,x,T, x,x,T,T, T,T,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,x,x,x}, - {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,T,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, - {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,x,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, - {x,T,T,T, T,x,x,x, T,x,T,x, x,T,T,T, x,T,T,T, T,x,x,T, x,x,x,x, x,x,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,T, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,T,T, x,x,x,x, x,x,T,x, T,T,x,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,T, T,T,x,x, T,x,x,T, T,T,T,T, T,x,x,x}, - {T,T,T,T, T,x,x,x, T,x,T,x, x,x,x,x, x,x,x,T, T,x,x,T, x,x,x,x, x,x,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,T,x,x, x,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,T,x,T, T,x,x,T, x,x,T,T, T,T,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,x,x,x}, - {x,T,T,T, T,x,x,x, T,x,T,x, x,T,T,T, x,T,T,T, T,x,x,T, x,x,x,x, x,x,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,T, x,T,x,x, x,x,x,x, T,x,T,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,T,x, T,T,x,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,T, T,T,x,x, T,x,x,T, T,T,T,T, T,x,x,x}, - {x,T,T,T, T,x,x,x, T,x,T,x, x,T,T,T, x,T,T,T, T,x,x,T, x,x,x,x, x,x,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,T, x,T,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,T,x, T,T,x,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,T, T,T,x,x, T,x,x,T, T,T,T,T, T,x,x,x}, - {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, - {x,T,T,T, T,x,x,x, T,x,T,x, x,x,x,x, x,x,x,T, T,x,x,T, x,x,x,x, x,x,T,T, T,x,x,x, x,x,x,x, x,x,x,x, x,T,T,x, x,T,x,x, x,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,T,x,T, T,x,x,T, x,x,T,T, T,T,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,x,x,x}, - {x,T,T,T, T,x,x,x, T,x,T,x, x,x,x,x, x,x,x,T, T,x,x,T, x,x,x,x, x,x,T,T, T,x,x,x, x,x,x,x, x,x,x,x, x,T,T,x, x,T,x,x, x,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,T, T,T,x,T, T,x,x,T, x,x,T,T, T,T,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,x,x,x}, - {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,T,T,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, - {x,T,T,T, T,x,x,x, T,x,T,x, x,T,T,T, x,T,T,T, T,x,x,T, x,x,x,x, x,x,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,T, x,T,x,x, x,x,x,x, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,T,T,x, T,T,x,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,T, T,T,x,x, T,x,x,T, T,T,T,T, T,x,x,x}, - {x,x,T,T, T,x,x,x, T,x,T,x, x,x,x,x, x,x,x,T, T,x,x,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,x,x,x}, - {T,T,T,T, T,x,x,x, T,x,T,x, x,x,x,x, x,x,x,T, T,x,x,T, x,x,x,x, x,x,T,T, T,x,x,x, x,x,x,x, x,x,x,x, x,T,T,x, x,T,x,x, x,x,x,x, x,x,T,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,T,x,T, T,x,x,T, x,x,T,T, T,T,T,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,T,T, T,x,x,x}, - {x,x,x,x, x,x,x,x, x,x,x,x, x,T,T,x, x,T,T,x, x,x,x,x, x,x,x,x, x,x,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, T,x,x,x, x,T,x,x, x,x,x,x, x,x,x,x, x,x,x,T, x,x,x,x, x,x,T,x, T,T,x,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,T, T,T,x,x, x,x,x,x, x,x,x,x, x,x,x,x}, - {T,T,T,T, T,x,x,x, T,x,T,x, x,x,x,x, x,x,x,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,x, T,T,x,x, T,x,x,x, x,T,x,T, T,T,T,T, T,T,T,T, T,x,x,T, T,T,T,T, T,T,T,T, T,x,T,T, T,x,T,T, T,T,T,T, T,T,x,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x,x, x,x,T,T, T,T,T,T, T,T,T,T, T,T,x,x}, - {T,T,T,T, T,x,x,x, T,x,T,x, x,x,x,x, x,x,x,T, T,T,T,T, T,T,x,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,T,x, T,T,T,T, T,T,T,T, T,T,T,x, T,T,x,x, T,x,x,x, x,T,x,T, T,T,T,T, T,T,T,T, T,x,x,T, T,T,T,T, T,T,T,T, T,x,T,T, x,x,T,T, T,T,T,T, T,T,x,T, T,T,T,T, T,T,T,T, T,T,T,T, T,T,x,x, x,x,T,T, T,T,T,T, T,T,T,T, T,T,x,x} + {_T,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_T, _x,_x,_T,_T, _T,_x,_x,_T, _T,_x,_x,_T, _T,_x,_T,_T, _T,_T,_T,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _T,_x,_x,_T, _T,_T,_x,_x, _T,_x,_x,_T, _T,_T,_T,_T, _T,_T,_x,_T, _T,_x,_x,_T, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, + {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_x,_x, _T,_x,_x,_x, _x,_T,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, + {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_T,_x, _x,_T,_x,_x, _x,_x,_x,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, + {_x,_T,_x,_x, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, + {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_x,_x, _x,_x,_T,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_x,_x, _T,_x,_T,_x, _x,_T,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, + {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, + {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, + {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_T,_T,_T, _x,_T,_T,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_x, _T,_T,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_x,_x, _T,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, + {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_T,_T,_T, _T,_T,_x,_T, _T,_T,_T,_x, _x,_T,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_x,_x, _T,_x,_x,_x, _x,_T,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_T, _T,_T,_T,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, + {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, + {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, + {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, + {_x,_T,_T,_x, _x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, + {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, + {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, + {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _T,_x,_x,_T, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, + {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, + {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, + {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_T,_T,_T, _x,_T,_T,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_T,_x, _T,_T,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_x,_x, _T,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, + {_T,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _T,_x,_x,_T, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, + {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_T,_T,_T, _x,_T,_T,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _x,_T,_x,_x, _x,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_x, _T,_T,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_x,_x, _T,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, + {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_T,_T,_T, _x,_T,_T,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _x,_T,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_x, _T,_T,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_x,_x, _T,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, + {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, + {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _T,_x,_x,_T, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, + {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_x,_T, _T,_x,_x,_T, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, + {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, + {_x,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_T,_T,_T, _x,_T,_T,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _x,_T,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_T,_T,_x, _T,_T,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_x,_x, _T,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, + {_x,_x,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, + {_T,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_T, _T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _T,_x,_x,_T, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, + {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_x, _x,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_x,_x,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_T,_x, _T,_T,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, + {_T,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _T,_T,_x,_x, _T,_x,_x,_x, _x,_T,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_T,_T, _T,_x,_T,_T, _T,_T,_T,_T, _T,_T,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x}, + {_T,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_T,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x, _T,_T,_x,_x, _T,_x,_x,_x, _x,_T,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_T,_T, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x} }; } // end Parser -- cgit v1.2.3 From 1763ad8e4d6b631fe966b394ae2dbafa7d803627 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 23 Jul 2015 10:26:47 -0700 Subject: Fix: Visual studio did not show warnings. To check if the fix works, try declaring a static top level function. Initial work on this patch by Rustan --- Source/Dafny/Resolver.cs | 4 ++-- Source/DafnyExtension/DafnyDriver.cs | 7 +++++++ Source/DafnyExtension/ResolverTagger.cs | 32 +++++++++++++++++--------------- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 26349ef5..44990d7d 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -51,7 +51,7 @@ namespace Microsoft.Dafny Error(e.tok, msg, args); } - private bool reportWarnings = true; + protected bool reportWarnings = true; /// /// Set whether or not to report warnings. Return the state of the previous behavior. /// @@ -60,7 +60,7 @@ namespace Microsoft.Dafny reportWarnings = b; return old; } - public void Warning(IToken tok, string msg, params object[] args) { + public virtual void Warning(IToken tok, string msg, params object[] args) { Contract.Requires(tok != null); Contract.Requires(msg != null); if (reportWarnings) { diff --git a/Source/DafnyExtension/DafnyDriver.cs b/Source/DafnyExtension/DafnyDriver.cs index 7f39fe34..8300213c 100644 --- a/Source/DafnyExtension/DafnyDriver.cs +++ b/Source/DafnyExtension/DafnyDriver.cs @@ -182,6 +182,13 @@ namespace DafnyLanguage dd.RecordError(tok.filename, tok.line - 1, tok.col - 1, ErrorCategory.ResolveError, s); ErrorCount++; } + + public override void Warning(IToken tok, string msg, params object[] args) { + if (reportWarnings) { + string s = string.Format(msg, args); + dd.RecordError(tok.filename, tok.line - 1, tok.col - 1, ErrorCategory.ResolveWarning, s); + } + } } #endregion diff --git a/Source/DafnyExtension/ResolverTagger.cs b/Source/DafnyExtension/ResolverTagger.cs index 1fdd3827..4237041f 100644 --- a/Source/DafnyExtension/ResolverTagger.cs +++ b/Source/DafnyExtension/ResolverTagger.cs @@ -68,26 +68,27 @@ namespace DafnyLanguage { Error = error; } - - private static string ConvertToErrorType(DafnyError err) - { - string ty; // the COLORs below indicate what I see on my machine - switch (err.Category) - { - default: // unexpected category + + private static string ConvertToErrorType(DafnyError err) { + // the COLORs below indicate what I see on my machine + switch (err.Category) { + case ErrorCategory.ProcessError: case ErrorCategory.ParseError: case ErrorCategory.ParseWarning: - ty = "syntax error"; break; // COLOR: red + return "syntax error"; // COLOR: red case ErrorCategory.ResolveError: - ty = "compiler error"; break; // COLOR: blue + return "compiler error"; // COLOR: blue + case ErrorCategory.ResolveWarning: + return "compiler warning"; // COLOR: blue + case ErrorCategory.InternalError: case ErrorCategory.VerificationError: - ty = "error"; break; // COLOR: red + return "error"; // COLOR: red case ErrorCategory.AuxInformation: - ty = "other error"; break; // COLOR: purple red - case ErrorCategory.InternalError: - ty = "error"; break; // COLOR: red + return "other error"; // COLOR: purple red + default: + Contract.Assert(false); + throw new InvalidOperationException(); } - return ty; } } @@ -412,6 +413,7 @@ namespace DafnyLanguage case ErrorCategory.InternalError: return TaskErrorCategory.Error; case ErrorCategory.ParseWarning: + case ErrorCategory.ResolveWarning: return TaskErrorCategory.Warning; case ErrorCategory.AuxInformation: return TaskErrorCategory.Message; @@ -477,7 +479,7 @@ namespace DafnyLanguage public enum ErrorCategory { - ProcessError, ParseWarning, ParseError, ResolveError, VerificationError, AuxInformation, InternalError + ProcessError, ParseWarning, ParseError, ResolveWarning, ResolveError, VerificationError, AuxInformation, InternalError } public class DafnyError -- cgit v1.2.3 From 3f886d1789d50400ffba2befdc2ae0e8d5c79cbe Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 23 Jul 2015 11:57:19 -0700 Subject: Fix: Unify column numbers in Dafny's errors Dafny counts from 0, but Boogie counts from 1. Tokens are 1-based. Thus when we print tokens, we need to decrement the column number. This was done for resolver errors, but not for verification or parsing errors. In addition, parsing errors were inconsistent with resolution errors case-wise. Unfortunately, the fix affects the output of many tests. --- Source/Dafny/Parser.cs | 12 ++- Source/DafnyDriver/DafnyDriver.cs | 8 +- Test/dafny0/AdvancedLHS.dfy.expect | 2 +- Test/dafny0/Array.dfy.expect | 46 +++++----- Test/dafny0/AutoReq.dfy.expect | 38 ++++----- Test/dafny0/Backticks.dfy.expect | 4 +- Test/dafny0/BadFunction.dfy.expect | 2 +- Test/dafny0/Basics.dfy.expect | 36 ++++---- Test/dafny0/Calculations.dfy.expect | 12 +-- Test/dafny0/Char.dfy.expect | 6 +- Test/dafny0/CoPrefix.dfy.expect | 30 +++---- Test/dafny0/CoinductiveProofs.dfy.expect | 42 +++++----- Test/dafny0/Comprehensions.dfy.expect | 2 +- Test/dafny0/ComputationsLoop.dfy.expect | 4 +- Test/dafny0/ComputationsLoop2.dfy.expect | 6 +- Test/dafny0/ComputationsNeg.dfy.expect | 14 ++-- Test/dafny0/ControlStructures.dfy.expect | 20 ++--- Test/dafny0/Corecursion.dfy.expect | 16 ++-- Test/dafny0/DTypes.dfy.expect | 24 +++--- Test/dafny0/Datatypes.dfy.expect | 26 +++--- Test/dafny0/Definedness.dfy.expect | 98 +++++++++++----------- Test/dafny0/DeterministicPick.dfy.expect | 2 +- Test/dafny0/DiamondImports.dfy.expect | 10 +-- Test/dafny0/Fuel.dfy.expect | 50 +++++------ Test/dafny0/FunctionSpecifications.dfy.expect | 38 ++++----- Test/dafny0/IMaps.dfy.expect | 2 +- Test/dafny0/Include.dfy.expect | 10 +-- Test/dafny0/Includee.dfy.expect | 10 +-- Test/dafny0/IndexIntoUpdate.dfy.expect | 2 +- Test/dafny0/InductivePredicates.dfy.expect | 4 +- Test/dafny0/Inverses.dfy.expect | 8 +- Test/dafny0/Iterators.dfy.expect | 44 +++++----- Test/dafny0/LetExpr.dfy.expect | 18 ++-- Test/dafny0/LhsDuplicates.dfy.expect | 12 +-- Test/dafny0/LoopModifies.dfy.expect | 18 ++-- Test/dafny0/Maps.dfy.expect | 4 +- Test/dafny0/ModifyStmt.dfy.expect | 22 ++--- Test/dafny0/Modules0.dfy.expect | 4 +- Test/dafny0/Modules1.dfy.expect | 12 +-- Test/dafny0/MultiDimArray.dfy.expect | 4 +- Test/dafny0/MultiSets.dfy.expect | 14 ++-- Test/dafny0/NatTypes.dfy.expect | 18 ++-- Test/dafny0/Newtypes.dfy.expect | 26 +++--- Test/dafny0/OpaqueFunctions.dfy.expect | 52 ++++++------ Test/dafny0/Parallel.dfy.expect | 20 ++--- Test/dafny0/ParseErrors.dfy.expect | 32 +++---- Test/dafny0/PredExpr.dfy.expect | 8 +- Test/dafny0/Predicates.dfy.expect | 26 +++--- Test/dafny0/Protected.dfy.expect | 10 +-- Test/dafny0/RankNeg.dfy.expect | 8 +- Test/dafny0/Reads.dfy.expect | 18 ++-- Test/dafny0/RealCompare.dfy.expect | 10 +-- Test/dafny0/RealTypes.dfy.expect | 10 +-- Test/dafny0/Refinement.dfy.expect | 36 ++++---- Test/dafny0/Skeletons.dfy.expect | 4 +- Test/dafny0/SmallTests.dfy.expect | 76 ++++++++--------- Test/dafny0/SplitExpr.dfy.expect | 4 +- Test/dafny0/StatementExpressions.dfy.expect | 10 +-- Test/dafny0/Superposition.dfy.expect | 12 +-- Test/dafny0/Termination.dfy.expect | 16 ++-- .../Trait/TraitUsingParentMembers.dfy.expect | 2 +- Test/dafny0/Trait/TraitsDecreases.dfy.expect | 22 ++--- Test/dafny0/Tuples.dfy.expect | 4 +- Test/dafny0/TypeAntecedents.dfy.expect | 8 +- Test/dafny0/TypeParameters.dfy.expect | 30 +++---- Test/dafny0/columns.dfy | 10 +++ Test/dafny0/columns.dfy.expect | 18 ++++ Test/dafny0/snapshots/Snapshots0.run.dfy.expect | 2 +- Test/dafny0/snapshots/Snapshots1.run.dfy.expect | 2 +- Test/dafny0/snapshots/Snapshots2.run.dfy.expect | 2 +- Test/dafny0/snapshots/Snapshots3.run.dfy.expect | 4 +- Test/dafny0/snapshots/Snapshots4.run.dfy.expect | 4 +- Test/dafny0/snapshots/Snapshots6.run.dfy.expect | 2 +- Test/dafny0/snapshots/Snapshots7.run.dfy.expect | 2 +- Test/dafny1/MoreInduction.dfy.expect | 16 ++-- Test/dafny2/SnapshotableTrees.dfy.expect | 4 +- Test/dafny4/BinarySearch.dfy.expect | 2 +- Test/dafny4/Bug73.dfy.expect | 4 +- Test/dafny4/SoftwareFoundations-Basics.dfy.expect | 2 +- Test/hofs/Apply.dfy.expect | 2 +- Test/hofs/Classes.dfy.expect | 4 +- Test/hofs/Field.dfy.expect | 8 +- Test/hofs/FnRef.dfy.expect | 8 +- Test/hofs/Frame.dfy.expect | 14 ++-- Test/hofs/Lambda.dfy.expect | 2 +- Test/hofs/LambdaParsefail.dfy.expect | 10 +-- Test/hofs/LambdaParsefail2.dfy.expect | 2 +- Test/hofs/Naked.dfy.expect | 24 +++--- Test/hofs/OneShot.dfy.expect | 6 +- Test/hofs/ReadsReads.dfy.expect | 16 ++-- Test/hofs/Simple.dfy.expect | 12 +-- Test/hofs/Twice.dfy.expect | 4 +- Test/irondafny0/inheritreqs0.dfy.expect | 4 +- Test/irondafny0/inheritreqs1.dfy.expect | 4 +- Test/irondafny0/xrefine1.dfy.expect | 4 +- 95 files changed, 714 insertions(+), 682 deletions(-) create mode 100644 Test/dafny0/columns.dfy create mode 100644 Test/dafny0/columns.dfy.expect diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs index 01438f68..d50a4dd6 100644 --- a/Source/Dafny/Parser.cs +++ b/Source/Dafny/Parser.cs @@ -4429,8 +4429,8 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo public class Errors { public int count = 0; // number of errors detected public System.IO.TextWriter/*!*/ errorStream = Console.Out; // error messages go to this stream - public string errMsgFormat = "{0}({1},{2}): error: {3}"; // 0=filename, 1=line, 2=column, 3=text - public string warningMsgFormat = "{0}({1},{2}): warning: {3}"; // 0=filename, 1=line, 2=column, 3=text + public string errMsgFormat = "{0}({1},{2}): Error: {3}"; // 0=filename, 1=line, 2=column, 3=text + public string warningMsgFormat = "{0}({1},{2}): Warning: {3}"; // 0=filename, 1=line, 2=column, 3=text public void SynErr(string filename, int line, int col, int n) { SynErr(filename, line, col, GetSyntaxErrorString(n)); @@ -4438,7 +4438,7 @@ public class Errors { public virtual void SynErr(string filename, int line, int col, string/*!*/ msg) { Contract.Requires(msg != null); - errorStream.WriteLine(errMsgFormat, filename, line, col, msg); + errorStream.WriteLine(errMsgFormat, filename, line, col - 1, msg); count++; } @@ -4701,7 +4701,7 @@ public class Errors { public virtual void SemErr(string filename, int line, int col, string/*!*/ msg) { Contract.Requires(msg != null); - errorStream.WriteLine(errMsgFormat, filename, line, col, msg); + errorStream.WriteLine(errMsgFormat, filename, line, col - 1, msg); count++; } @@ -4713,7 +4713,7 @@ public class Errors { public virtual void Warning(string filename, int line, int col, string msg) { Contract.Requires(msg != null); - errorStream.WriteLine(warningMsgFormat, filename, line, col, msg); + errorStream.WriteLine(warningMsgFormat, filename, line, col - 1, msg); } } // Errors @@ -4721,6 +4721,4 @@ public class Errors { public class FatalError: Exception { public FatalError(string m): base(m) {} } - - } \ No newline at end of file diff --git a/Source/DafnyDriver/DafnyDriver.cs b/Source/DafnyDriver/DafnyDriver.cs index 9fdc9320..d22899ab 100644 --- a/Source/DafnyDriver/DafnyDriver.cs +++ b/Source/DafnyDriver/DafnyDriver.cs @@ -273,7 +273,13 @@ namespace Microsoft.Dafny { public override void ReportBplError(IToken tok, string message, bool error, TextWriter tw, string category = null) { - base.ReportBplError(tok, message, error, tw, category); + // Dafny has 0-indexed columns, but Boogie counts from 1 + var realigned_tok = new Token(tok.line, tok.col - 1); + realigned_tok.kind = tok.kind; + realigned_tok.pos = tok.pos; + realigned_tok.val = tok.val; + realigned_tok.filename = tok.filename; + base.ReportBplError(realigned_tok, message, error, tw, category); if (tok is Dafny.NestedToken) { diff --git a/Test/dafny0/AdvancedLHS.dfy.expect b/Test/dafny0/AdvancedLHS.dfy.expect index cb817605..aab12cfc 100644 --- a/Test/dafny0/AdvancedLHS.dfy.expect +++ b/Test/dafny0/AdvancedLHS.dfy.expect @@ -1,4 +1,4 @@ -AdvancedLHS.dfy(34,23): Error: target object may be null +AdvancedLHS.dfy(34,22): Error: target object may be null Execution trace: (0,0): anon0 (0,0): anon15_Else diff --git a/Test/dafny0/Array.dfy.expect b/Test/dafny0/Array.dfy.expect index 59dcb4bf..40fb318d 100644 --- a/Test/dafny0/Array.dfy.expect +++ b/Test/dafny0/Array.dfy.expect @@ -1,16 +1,16 @@ -Array.dfy(13,8): Error: assignment may update an array element not in the enclosing context's modifies clause +Array.dfy(13,7): Error: assignment may update an array element not in the enclosing context's modifies clause Execution trace: (0,0): anon0 (0,0): anon5_Then (0,0): anon2 (0,0): anon6_Then -Array.dfy(20,16): Error: target object may be null +Array.dfy(20,15): Error: target object may be null Execution trace: (0,0): anon0 -Array.dfy(27,6): Error: index out of range +Array.dfy(27,5): Error: index out of range Execution trace: (0,0): anon0 -Array.dfy(51,20): Error: assertion violation +Array.dfy(51,19): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon12_Then @@ -21,19 +21,19 @@ Execution trace: (0,0): anon16_Then (0,0): anon9 (0,0): anon11 -Array.dfy(59,8): Error: assignment may update an array element not in the enclosing context's modifies clause +Array.dfy(59,7): Error: assignment may update an array element not in the enclosing context's modifies clause Execution trace: (0,0): anon0 (0,0): anon5_Then (0,0): anon2 (0,0): anon6_Then -Array.dfy(66,8): Error: assignment may update an array element not in the enclosing context's modifies clause +Array.dfy(66,7): Error: assignment may update an array element not in the enclosing context's modifies clause Execution trace: (0,0): anon0 (0,0): anon5_Then (0,0): anon2 (0,0): anon6_Then -Array.dfy(110,21): Error: upper bound below lower bound or above length of array +Array.dfy(110,20): Error: upper bound below lower bound or above length of array Execution trace: (0,0): anon0 (0,0): anon14_Else @@ -41,7 +41,7 @@ Execution trace: (0,0): anon19_Then (0,0): anon20_Then (0,0): anon11 -Array.dfy(120,8): Error: insufficient reads clause to read the indicated range of array elements +Array.dfy(120,7): Error: insufficient reads clause to read the indicated range of array elements Execution trace: (0,0): anon0 (0,0): anon10_Else @@ -49,7 +49,7 @@ Execution trace: (0,0): anon12_Then (0,0): anon13_Then (0,0): anon9 -Array.dfy(122,8): Error: insufficient reads clause to read the indicated range of array elements +Array.dfy(122,7): Error: insufficient reads clause to read the indicated range of array elements Execution trace: (0,0): anon0 (0,0): anon10_Else @@ -57,7 +57,7 @@ Execution trace: (0,0): anon12_Then (0,0): anon13_Else (0,0): anon9 -Array.dfy(123,8): Error: insufficient reads clause to read the indicated range of array elements +Array.dfy(123,7): Error: insufficient reads clause to read the indicated range of array elements Execution trace: (0,0): anon0 (0,0): anon10_Else @@ -65,7 +65,7 @@ Execution trace: (0,0): anon12_Then (0,0): anon13_Else (0,0): anon9 -Array.dfy(124,8): Error: insufficient reads clause to read the indicated range of array elements +Array.dfy(124,7): Error: insufficient reads clause to read the indicated range of array elements Execution trace: (0,0): anon0 (0,0): anon10_Else @@ -73,45 +73,45 @@ Execution trace: (0,0): anon12_Then (0,0): anon13_Else (0,0): anon9 -Array.dfy(163,6): Error: insufficient reads clause to read array element +Array.dfy(163,5): Error: insufficient reads clause to read array element Execution trace: (0,0): anon0 (0,0): anon8_Else (0,0): anon9_Then (0,0): anon10_Then (0,0): anon7 -Array.dfy(171,6): Error: insufficient reads clause to read array element +Array.dfy(171,5): Error: insufficient reads clause to read array element Execution trace: (0,0): anon0 (0,0): anon8_Else (0,0): anon9_Then (0,0): anon10_Then (0,0): anon7 -Array.dfy(187,6): Error: assignment may update an array element not in the enclosing context's modifies clause +Array.dfy(187,5): Error: assignment may update an array element not in the enclosing context's modifies clause Execution trace: (0,0): anon0 -Array.dfy(194,6): Error: assignment may update an array element not in the enclosing context's modifies clause +Array.dfy(194,5): Error: assignment may update an array element not in the enclosing context's modifies clause Execution trace: (0,0): anon0 -Array.dfy(219,1): Error BP5003: A postcondition might not hold on this return path. -Array.dfy(218,11): Related location: This is the postcondition that might not hold. +Array.dfy(219,0): Error BP5003: A postcondition might not hold on this return path. +Array.dfy(218,10): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 -Array.dfy(243,1): Error BP5003: A postcondition might not hold on this return path. -Array.dfy(242,11): Related location: This is the postcondition that might not hold. +Array.dfy(243,0): Error BP5003: A postcondition might not hold on this return path. +Array.dfy(242,10): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 -Array.dfy(249,1): Error BP5003: A postcondition might not hold on this return path. -Array.dfy(248,11): Related location: This is the postcondition that might not hold. +Array.dfy(249,0): Error BP5003: A postcondition might not hold on this return path. +Array.dfy(248,10): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 -Array.dfy(264,10): Error: value assigned to a nat must be non-negative +Array.dfy(264,9): Error: value assigned to a nat must be non-negative Execution trace: (0,0): anon0 (0,0): anon5_Then (0,0): anon2 (0,0): anon6_Then -Array.dfy(265,5): Error: value assigned to a nat must be non-negative +Array.dfy(265,4): Error: value assigned to a nat must be non-negative Execution trace: (0,0): anon0 (0,0): anon5_Then diff --git a/Test/dafny0/AutoReq.dfy.expect b/Test/dafny0/AutoReq.dfy.expect index 8486716d..b4b34e14 100644 --- a/Test/dafny0/AutoReq.dfy.expect +++ b/Test/dafny0/AutoReq.dfy.expect @@ -1,43 +1,43 @@ -AutoReq.dfy(247,5): Error: possible violation of function precondition -AutoReq.dfy(239,14): Related location +AutoReq.dfy(247,4): Error: possible violation of function precondition +AutoReq.dfy(239,13): Related location Execution trace: (0,0): anon0 (0,0): anon4_Else -AutoReq.dfy(13,3): Error: possible violation of function precondition -AutoReq.dfy(5,14): Related location +AutoReq.dfy(13,2): Error: possible violation of function precondition +AutoReq.dfy(5,13): Related location Execution trace: (0,0): anon0 (0,0): anon4_Else -AutoReq.dfy(25,3): Error: possible violation of function precondition -AutoReq.dfy(5,14): Related location +AutoReq.dfy(25,2): Error: possible violation of function precondition +AutoReq.dfy(5,13): Related location Execution trace: (0,0): anon0 (0,0): anon4_Else -AutoReq.dfy(38,12): Error: assertion violation -AutoReq.dfy(31,13): Related location -AutoReq.dfy(7,5): Related location +AutoReq.dfy(38,11): Error: assertion violation +AutoReq.dfy(31,12): Related location +AutoReq.dfy(7,4): Related location Execution trace: (0,0): anon0 (0,0): anon9_Then -AutoReq.dfy(38,12): Error: possible violation of function precondition -AutoReq.dfy(5,14): Related location +AutoReq.dfy(38,11): Error: possible violation of function precondition +AutoReq.dfy(5,13): Related location Execution trace: (0,0): anon0 (0,0): anon9_Then -AutoReq.dfy(40,12): Error: assertion violation -AutoReq.dfy(31,27): Related location -AutoReq.dfy(7,5): Related location +AutoReq.dfy(40,11): Error: assertion violation +AutoReq.dfy(31,26): Related location +AutoReq.dfy(7,4): Related location Execution trace: (0,0): anon0 (0,0): anon10_Then -AutoReq.dfy(40,12): Error: possible violation of function precondition -AutoReq.dfy(5,14): Related location +AutoReq.dfy(40,11): Error: possible violation of function precondition +AutoReq.dfy(5,13): Related location Execution trace: (0,0): anon0 (0,0): anon10_Then -AutoReq.dfy(45,12): Error: assertion violation -AutoReq.dfy(31,13): Related location -AutoReq.dfy(7,5): Related location +AutoReq.dfy(45,11): Error: assertion violation +AutoReq.dfy(31,12): Related location +AutoReq.dfy(7,4): Related location Execution trace: (0,0): anon0 (0,0): anon11_Then diff --git a/Test/dafny0/Backticks.dfy.expect b/Test/dafny0/Backticks.dfy.expect index 57761ab4..58977413 100644 --- a/Test/dafny0/Backticks.dfy.expect +++ b/Test/dafny0/Backticks.dfy.expect @@ -1,10 +1,10 @@ -Backticks.dfy(38,5): Error: insufficient reads clause to invoke function +Backticks.dfy(38,4): Error: insufficient reads clause to invoke function Execution trace: (0,0): anon0 (0,0): anon7_Else (0,0): anon8_Else (0,0): anon6 -Backticks.dfy(77,8): Error: call may violate context's modifies clause +Backticks.dfy(77,7): Error: call may violate context's modifies clause Execution trace: (0,0): anon0 (0,0): anon3_Then diff --git a/Test/dafny0/BadFunction.dfy.expect b/Test/dafny0/BadFunction.dfy.expect index 9c4ae81d..1af2608d 100644 --- a/Test/dafny0/BadFunction.dfy.expect +++ b/Test/dafny0/BadFunction.dfy.expect @@ -1,4 +1,4 @@ -BadFunction.dfy(9,3): Error: failure to decrease termination measure +BadFunction.dfy(9,2): Error: failure to decrease termination measure Execution trace: (0,0): anon0 (0,0): anon4_Else diff --git a/Test/dafny0/Basics.dfy.expect b/Test/dafny0/Basics.dfy.expect index f28df20a..65d5d101 100644 --- a/Test/dafny0/Basics.dfy.expect +++ b/Test/dafny0/Basics.dfy.expect @@ -1,8 +1,8 @@ -Basics.dfy(45,14): Error: assertion violation +Basics.dfy(45,13): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Else -Basics.dfy(69,42): Error: assertion violation +Basics.dfy(69,41): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon13_Then @@ -12,7 +12,7 @@ Execution trace: Basics.dfy(69,82): anon17_Else Basics.dfy(69,95): anon18_Else (0,0): anon12 -Basics.dfy(93,14): Error: assertion violation +Basics.dfy(93,13): Error: assertion violation Execution trace: (0,0): anon0 Basics.dfy(83,14): anon27_Else @@ -27,7 +27,7 @@ Execution trace: Basics.dfy(91,13): anon34_Else (0,0): anon35_Then (0,0): anon15 -Basics.dfy(99,14): Error: assertion violation +Basics.dfy(99,13): Error: assertion violation Execution trace: (0,0): anon0 Basics.dfy(83,14): anon27_Else @@ -42,7 +42,7 @@ Execution trace: Basics.dfy(97,19): anon40_Else (0,0): anon41_Then (0,0): anon26 -Basics.dfy(112,28): Error: target object may be null +Basics.dfy(112,27): Error: target object may be null Execution trace: (0,0): anon0 Basics.dfy(105,20): anon13_Else @@ -52,7 +52,7 @@ Execution trace: Basics.dfy(107,24): anon15_Else (0,0): anon6 (0,0): anon16_Then -Basics.dfy(114,14): Error: target object may be null +Basics.dfy(114,13): Error: target object may be null Execution trace: (0,0): anon0 Basics.dfy(105,20): anon13_Else @@ -62,11 +62,11 @@ Execution trace: Basics.dfy(107,24): anon15_Else (0,0): anon6 (0,0): anon16_Else -Basics.dfy(149,16): Error: assertion violation +Basics.dfy(149,15): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon10_Then -Basics.dfy(168,10): Error: when left-hand sides 0 and 1 may refer to the same location, they must be assigned the same value +Basics.dfy(168,9): Error: when left-hand sides 0 and 1 may refer to the same location, they must be assigned the same value Execution trace: (0,0): anon0 (0,0): anon10_Then @@ -75,28 +75,28 @@ Execution trace: (0,0): anon6 (0,0): anon12_Then (0,0): anon9 -Basics.dfy(182,10): Error: when left-hand sides 0 and 1 refer to the same location, they must be assigned the same value +Basics.dfy(182,9): Error: when left-hand sides 0 and 1 refer to the same location, they must be assigned the same value Execution trace: (0,0): anon0 -Basics.dfy(194,19): Error: assertion violation +Basics.dfy(194,18): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon11_Then -Basics.dfy(196,10): Error: assignment may update an object not in the enclosing context's modifies clause +Basics.dfy(196,9): Error: assignment may update an object not in the enclosing context's modifies clause Execution trace: (0,0): anon0 (0,0): anon3 -Basics.dfy(196,10): Error: target object may be null +Basics.dfy(196,9): Error: target object may be null Execution trace: (0,0): anon0 (0,0): anon3 -Basics.dfy(201,12): Error: left-hand sides 0 and 1 may refer to the same location +Basics.dfy(201,11): Error: left-hand sides 0 and 1 may refer to the same location Execution trace: (0,0): anon0 (0,0): anon11_Then (0,0): anon3 (0,0): anon12_Then -Basics.dfy(212,15): Error: assertion violation +Basics.dfy(212,14): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon11_Then @@ -106,19 +106,19 @@ Execution trace: (0,0): anon13_Then (0,0): anon8 (0,0): anon14_Then -Basics.dfy(274,10): Error: when left-hand sides 0 and 1 refer to the same location, they must be assigned the same value +Basics.dfy(274,9): Error: when left-hand sides 0 and 1 refer to the same location, they must be assigned the same value Execution trace: (0,0): anon0 -Basics.dfy(465,12): Error: assertion violation +Basics.dfy(465,11): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon4_Then (0,0): anon3 -Basics.dfy(476,19): Error: assertion violation +Basics.dfy(476,18): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon4_Else -Basics.dfy(478,12): Error: assertion violation +Basics.dfy(478,11): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon4_Then diff --git a/Test/dafny0/Calculations.dfy.expect b/Test/dafny0/Calculations.dfy.expect index 3427a5cb..d4559f53 100644 --- a/Test/dafny0/Calculations.dfy.expect +++ b/Test/dafny0/Calculations.dfy.expect @@ -1,24 +1,24 @@ -Calculations.dfy(7,6): Error: index out of range +Calculations.dfy(7,5): Error: index out of range Execution trace: (0,0): anon0 (0,0): anon24_Then -Calculations.dfy(12,15): Error: index out of range +Calculations.dfy(12,14): Error: index out of range Execution trace: (0,0): anon0 (0,0): anon26_Then -Calculations.dfy(12,19): Error: assertion violation +Calculations.dfy(12,18): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon26_Then -Calculations.dfy(56,12): Error: assertion violation +Calculations.dfy(56,11): Error: assertion violation Execution trace: (0,0): anon0 Calculations.dfy(51,3): anon5_Else -Calculations.dfy(79,15): Error: index out of range +Calculations.dfy(79,14): Error: index out of range Execution trace: (0,0): anon0 (0,0): anon12_Then -Calculations.dfy(79,19): Error: assertion violation +Calculations.dfy(79,18): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon12_Then diff --git a/Test/dafny0/Char.dfy.expect b/Test/dafny0/Char.dfy.expect index 55418934..874aaa65 100644 --- a/Test/dafny0/Char.dfy.expect +++ b/Test/dafny0/Char.dfy.expect @@ -1,14 +1,14 @@ -Char.dfy(48,21): Error: assertion violation +Char.dfy(48,20): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon9_Then (0,0): anon10_Then -Char.dfy(52,21): Error: assertion violation +Char.dfy(52,20): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon9_Then (0,0): anon11_Else -Char.dfy(63,17): Error: assertion violation +Char.dfy(63,16): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon5_Else diff --git a/Test/dafny0/CoPrefix.dfy.expect b/Test/dafny0/CoPrefix.dfy.expect index c92a09c1..a7295367 100644 --- a/Test/dafny0/CoPrefix.dfy.expect +++ b/Test/dafny0/CoPrefix.dfy.expect @@ -1,48 +1,48 @@ -CoPrefix.dfy(164,3): Error BP5003: A postcondition might not hold on this return path. -CoPrefix.dfy(163,15): Related location: This is the postcondition that might not hold. +CoPrefix.dfy(164,2): Error BP5003: A postcondition might not hold on this return path. +CoPrefix.dfy(163,14): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon3_Else -CoPrefix.dfy(169,3): Error BP5003: A postcondition might not hold on this return path. -CoPrefix.dfy(168,15): Related location: This is the postcondition that might not hold. +CoPrefix.dfy(169,2): Error BP5003: A postcondition might not hold on this return path. +CoPrefix.dfy(168,14): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon3_Else -CoPrefix.dfy(176,11): Error: cannot prove termination; try supplying a decreases clause +CoPrefix.dfy(176,10): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon3_Then -CoPrefix.dfy(63,57): Error: failure to decrease termination measure +CoPrefix.dfy(63,56): Error: failure to decrease termination measure Execution trace: (0,0): anon0 (0,0): anon7_Then (0,0): anon8_Else (0,0): anon9_Then -CoPrefix.dfy(76,56): Error: cannot prove termination; try supplying a decreases clause +CoPrefix.dfy(76,55): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon7_Then (0,0): anon8_Else (0,0): anon9_Then -CoPrefix.dfy(114,1): Error BP5003: A postcondition might not hold on this return path. -CoPrefix.dfy(113,11): Related location: This is the postcondition that might not hold. -CoPrefix.dfy(101,17): Related location +CoPrefix.dfy(114,0): Error BP5003: A postcondition might not hold on this return path. +CoPrefix.dfy(113,10): Related location: This is the postcondition that might not hold. +CoPrefix.dfy(101,16): Related location Execution trace: (0,0): anon0 (0,0): anon3_Then -CoPrefix.dfy(138,25): Error: assertion violation +CoPrefix.dfy(138,24): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon9_Then (0,0): anon10_Then -CoPrefix.dfy(142,25): Error: assertion violation -CoPrefix.dfy(117,23): Related location +CoPrefix.dfy(142,24): Error: assertion violation +CoPrefix.dfy(117,22): Related location Execution trace: (0,0): anon0 (0,0): anon9_Then (0,0): anon12_Then -CoPrefix.dfy(151,1): Error BP5003: A postcondition might not hold on this return path. -CoPrefix.dfy(150,11): Related location: This is the postcondition that might not hold. +CoPrefix.dfy(151,0): Error BP5003: A postcondition might not hold on this return path. +CoPrefix.dfy(150,10): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon3_Else diff --git a/Test/dafny0/CoinductiveProofs.dfy.expect b/Test/dafny0/CoinductiveProofs.dfy.expect index 12ce2f01..2a5a2b0b 100644 --- a/Test/dafny0/CoinductiveProofs.dfy.expect +++ b/Test/dafny0/CoinductiveProofs.dfy.expect @@ -1,48 +1,48 @@ -CoinductiveProofs.dfy(29,12): Error: assertion violation -CoinductiveProofs.dfy(13,17): Related location +CoinductiveProofs.dfy(29,11): Error: assertion violation +CoinductiveProofs.dfy(13,16): Related location Execution trace: (0,0): anon0 (0,0): anon5_Then (0,0): anon6_Then -CoinductiveProofs.dfy(59,1): Error BP5003: A postcondition might not hold on this return path. -CoinductiveProofs.dfy(58,11): Related location: This is the postcondition that might not hold. -CoinductiveProofs.dfy(54,3): Related location +CoinductiveProofs.dfy(59,0): Error BP5003: A postcondition might not hold on this return path. +CoinductiveProofs.dfy(58,10): Related location: This is the postcondition that might not hold. +CoinductiveProofs.dfy(54,2): Related location Execution trace: (0,0): anon0 (0,0): anon3_Then -CoinductiveProofs.dfy(74,12): Error: assertion violation -CoinductiveProofs.dfy(54,3): Related location +CoinductiveProofs.dfy(74,11): Error: assertion violation +CoinductiveProofs.dfy(54,2): Related location Execution trace: (0,0): anon0 (0,0): anon5_Then (0,0): anon6_Then -CoinductiveProofs.dfy(91,1): Error BP5003: A postcondition might not hold on this return path. -CoinductiveProofs.dfy(90,11): Related location: This is the postcondition that might not hold. -CoinductiveProofs.dfy(80,3): Related location +CoinductiveProofs.dfy(91,0): Error BP5003: A postcondition might not hold on this return path. +CoinductiveProofs.dfy(90,10): Related location: This is the postcondition that might not hold. +CoinductiveProofs.dfy(80,2): Related location Execution trace: (0,0): anon0 (0,0): anon3_Then -CoinductiveProofs.dfy(100,12): Error: assertion violation -CoinductiveProofs.dfy(80,3): Related location +CoinductiveProofs.dfy(100,11): Error: assertion violation +CoinductiveProofs.dfy(80,2): Related location Execution trace: (0,0): anon0 (0,0): anon5_Then (0,0): anon6_Then -CoinductiveProofs.dfy(111,1): Error BP5003: A postcondition might not hold on this return path. -CoinductiveProofs.dfy(110,11): Related location: This is the postcondition that might not hold. -CoinductiveProofs.dfy(106,3): Related location +CoinductiveProofs.dfy(111,0): Error BP5003: A postcondition might not hold on this return path. +CoinductiveProofs.dfy(110,10): Related location: This is the postcondition that might not hold. +CoinductiveProofs.dfy(106,2): Related location Execution trace: (0,0): anon0 (0,0): anon3_Then -CoinductiveProofs.dfy(150,1): Error BP5003: A postcondition might not hold on this return path. -CoinductiveProofs.dfy(149,22): Related location: This is the postcondition that might not hold. -CoinductiveProofs.dfy(4,24): Related location +CoinductiveProofs.dfy(150,0): Error BP5003: A postcondition might not hold on this return path. +CoinductiveProofs.dfy(149,21): Related location: This is the postcondition that might not hold. +CoinductiveProofs.dfy(4,23): Related location Execution trace: (0,0): anon0 (0,0): anon3_Then -CoinductiveProofs.dfy(156,1): Error BP5003: A postcondition might not hold on this return path. -CoinductiveProofs.dfy(155,22): Related location: This is the postcondition that might not hold. -CoinductiveProofs.dfy(4,24): Related location +CoinductiveProofs.dfy(156,0): Error BP5003: A postcondition might not hold on this return path. +CoinductiveProofs.dfy(155,21): Related location: This is the postcondition that might not hold. +CoinductiveProofs.dfy(4,23): Related location Execution trace: (0,0): anon0 (0,0): anon3_Then diff --git a/Test/dafny0/Comprehensions.dfy.expect b/Test/dafny0/Comprehensions.dfy.expect index 88873fd8..887a3249 100644 --- a/Test/dafny0/Comprehensions.dfy.expect +++ b/Test/dafny0/Comprehensions.dfy.expect @@ -1,4 +1,4 @@ -Comprehensions.dfy(12,14): Error: assertion violation +Comprehensions.dfy(12,13): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon9_Then diff --git a/Test/dafny0/ComputationsLoop.dfy.expect b/Test/dafny0/ComputationsLoop.dfy.expect index 91dc2af9..84674030 100644 --- a/Test/dafny0/ComputationsLoop.dfy.expect +++ b/Test/dafny0/ComputationsLoop.dfy.expect @@ -1,8 +1,8 @@ -ComputationsLoop.dfy(7,3): Error: failure to decrease termination measure +ComputationsLoop.dfy(7,2): Error: failure to decrease termination measure Execution trace: (0,0): anon0 (0,0): anon4_Else -ComputationsLoop.dfy(12,26): Error: assertion violation +ComputationsLoop.dfy(12,25): Error: assertion violation Execution trace: (0,0): anon0 diff --git a/Test/dafny0/ComputationsLoop2.dfy.expect b/Test/dafny0/ComputationsLoop2.dfy.expect index 816cbd31..48fc618f 100644 --- a/Test/dafny0/ComputationsLoop2.dfy.expect +++ b/Test/dafny0/ComputationsLoop2.dfy.expect @@ -1,12 +1,12 @@ -ComputationsLoop2.dfy(6,3): Error: cannot prove termination; try supplying a decreases clause +ComputationsLoop2.dfy(6,2): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon4_Else -ComputationsLoop2.dfy(11,3): Error: cannot prove termination; try supplying a decreases clause +ComputationsLoop2.dfy(11,2): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon4_Else -ComputationsLoop2.dfy(16,26): Error: assertion violation +ComputationsLoop2.dfy(16,25): Error: assertion violation Execution trace: (0,0): anon0 diff --git a/Test/dafny0/ComputationsNeg.dfy.expect b/Test/dafny0/ComputationsNeg.dfy.expect index a6318087..598e9fa5 100644 --- a/Test/dafny0/ComputationsNeg.dfy.expect +++ b/Test/dafny0/ComputationsNeg.dfy.expect @@ -1,19 +1,19 @@ -ComputationsNeg.dfy(7,3): Error: failure to decrease termination measure +ComputationsNeg.dfy(7,2): Error: failure to decrease termination measure Execution trace: (0,0): anon0 (0,0): anon4_Else -ComputationsNeg.dfy(11,1): Error BP5003: A postcondition might not hold on this return path. -ComputationsNeg.dfy(10,17): Related location: This is the postcondition that might not hold. +ComputationsNeg.dfy(11,0): Error BP5003: A postcondition might not hold on this return path. +ComputationsNeg.dfy(10,16): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 -ComputationsNeg.dfy(23,1): Error BP5003: A postcondition might not hold on this return path. -ComputationsNeg.dfy(22,11): Related location: This is the postcondition that might not hold. +ComputationsNeg.dfy(23,0): Error BP5003: A postcondition might not hold on this return path. +ComputationsNeg.dfy(22,10): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 -ComputationsNeg.dfy(36,13): Error: assertion violation +ComputationsNeg.dfy(36,12): Error: assertion violation Execution trace: (0,0): anon0 -ComputationsNeg.dfy(45,13): Error: assertion violation +ComputationsNeg.dfy(45,12): Error: assertion violation Execution trace: (0,0): anon0 diff --git a/Test/dafny0/ControlStructures.dfy.expect b/Test/dafny0/ControlStructures.dfy.expect index 3f4dce92..5638bcbc 100644 --- a/Test/dafny0/ControlStructures.dfy.expect +++ b/Test/dafny0/ControlStructures.dfy.expect @@ -1,29 +1,29 @@ -ControlStructures.dfy(8,3): Error: missing case in case statement: Purple +ControlStructures.dfy(8,2): Error: missing case in case statement: Purple Execution trace: (0,0): anon0 (0,0): anon6_Else (0,0): anon7_Else (0,0): anon8_Then -ControlStructures.dfy(8,3): Error: missing case in case statement: Blue +ControlStructures.dfy(8,2): Error: missing case in case statement: Blue Execution trace: (0,0): anon0 (0,0): anon6_Else (0,0): anon7_Else (0,0): anon8_Else (0,0): anon9_Then -ControlStructures.dfy(17,3): Error: missing case in case statement: Purple +ControlStructures.dfy(17,2): Error: missing case in case statement: Purple Execution trace: (0,0): anon0 (0,0): anon6_Else (0,0): anon7_Else (0,0): anon8_Then -ControlStructures.dfy(46,5): Error: missing case in case statement: Red +ControlStructures.dfy(46,4): Error: missing case in case statement: Red Execution trace: (0,0): anon0 (0,0): anon8_Then (0,0): anon9_Else (0,0): anon10_Then -ControlStructures.dfy(54,3): Error: missing case in case statement: Red +ControlStructures.dfy(54,2): Error: missing case in case statement: Red Execution trace: (0,0): anon0 (0,0): anon9_Else @@ -31,11 +31,11 @@ Execution trace: (0,0): anon11_Else (0,0): anon12_Else (0,0): anon13_Then -ControlStructures.dfy(75,3): Error: alternative cases fail to cover all possibilties +ControlStructures.dfy(75,2): Error: alternative cases fail to cover all possibilties Execution trace: (0,0): anon0 (0,0): anon5_Else -ControlStructures.dfy(218,18): Error: assertion violation +ControlStructures.dfy(218,17): Error: assertion violation Execution trace: (0,0): anon0 ControlStructures.dfy(197,3): anon59_LoopHead @@ -51,7 +51,7 @@ Execution trace: (0,0): anon69_LoopBody ControlStructures.dfy(213,9): anon70_Else (0,0): anon71_Then -ControlStructures.dfy(235,21): Error: assertion violation +ControlStructures.dfy(235,20): Error: assertion violation Execution trace: (0,0): anon0 ControlStructures.dfy(197,3): anon59_LoopHead @@ -77,7 +77,7 @@ Execution trace: (0,0): anon38 (0,0): anon83_Then (0,0): anon52 -ControlStructures.dfy(238,30): Error: assertion violation +ControlStructures.dfy(238,29): Error: assertion violation Execution trace: (0,0): anon0 ControlStructures.dfy(197,3): anon59_LoopHead @@ -92,7 +92,7 @@ Execution trace: (0,0): anon84_Then (0,0): anon85_Then (0,0): anon56 -ControlStructures.dfy(241,17): Error: assertion violation +ControlStructures.dfy(241,16): Error: assertion violation Execution trace: (0,0): anon0 ControlStructures.dfy(197,3): anon59_LoopHead diff --git a/Test/dafny0/Corecursion.dfy.expect b/Test/dafny0/Corecursion.dfy.expect index 619a9c84..a6b3fdce 100644 --- a/Test/dafny0/Corecursion.dfy.expect +++ b/Test/dafny0/Corecursion.dfy.expect @@ -1,34 +1,34 @@ -Corecursion.dfy(17,13): Error: cannot prove termination; try supplying a decreases clause (note that only functions without side effects can be called co-recursively) +Corecursion.dfy(17,12): Error: cannot prove termination; try supplying a decreases clause (note that only functions without side effects can be called co-recursively) Execution trace: (0,0): anon0 (0,0): anon4_Else -Corecursion.dfy(23,13): Error: cannot prove termination; try supplying a decreases clause (note that only functions without any ensures clause can be called co-recursively) +Corecursion.dfy(23,12): Error: cannot prove termination; try supplying a decreases clause (note that only functions without any ensures clause can be called co-recursively) Execution trace: (0,0): anon0 (0,0): anon4_Else -Corecursion.dfy(58,5): Error: cannot prove termination; try supplying a decreases clause +Corecursion.dfy(58,4): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon4_Else -Corecursion.dfy(71,16): Error: cannot prove termination; try supplying a decreases clause (note that calls cannot be co-recursive in this context) +Corecursion.dfy(71,15): Error: cannot prove termination; try supplying a decreases clause (note that calls cannot be co-recursive in this context) Execution trace: (0,0): anon0 (0,0): anon7_Else -Corecursion.dfy(93,15): Error: cannot prove termination; try supplying a decreases clause (note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts) +Corecursion.dfy(93,14): Error: cannot prove termination; try supplying a decreases clause (note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts) Execution trace: (0,0): anon0 (0,0): anon7_Else (0,0): anon8_Then -Corecursion.dfy(103,15): Error: cannot prove termination; try supplying a decreases clause (note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts) +Corecursion.dfy(103,14): Error: cannot prove termination; try supplying a decreases clause (note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts) Execution trace: (0,0): anon0 (0,0): anon7_Else (0,0): anon8_Then -Corecursion.dfy(148,13): Error: failure to decrease termination measure (note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts) +Corecursion.dfy(148,12): Error: failure to decrease termination measure (note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts) Execution trace: (0,0): anon0 (0,0): anon4_Else -Corecursion.dfy(161,13): Error: failure to decrease termination measure (note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts) +Corecursion.dfy(161,12): Error: failure to decrease termination measure (note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts) Execution trace: (0,0): anon0 (0,0): anon4_Else diff --git a/Test/dafny0/DTypes.dfy.expect b/Test/dafny0/DTypes.dfy.expect index 9b4288e9..76088e9b 100644 --- a/Test/dafny0/DTypes.dfy.expect +++ b/Test/dafny0/DTypes.dfy.expect @@ -1,27 +1,27 @@ -DTypes.dfy(182,3): Error BP5003: A postcondition might not hold on this return path. -DTypes.dfy(181,15): Related location: This is the postcondition that might not hold. +DTypes.dfy(182,2): Error BP5003: A postcondition might not hold on this return path. +DTypes.dfy(181,14): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 -DTypes.dfy(18,14): Error: assertion violation +DTypes.dfy(18,13): Error: assertion violation Execution trace: (0,0): anon0 -DTypes.dfy(56,18): Error: assertion violation +DTypes.dfy(56,17): Error: assertion violation Execution trace: (0,0): anon0 -DTypes.dfy(121,13): Error: assertion violation -DTypes.dfy(93,30): Related location +DTypes.dfy(121,12): Error: assertion violation +DTypes.dfy(93,29): Related location Execution trace: (0,0): anon0 -DTypes.dfy(127,13): Error: assertion violation -DTypes.dfy(93,20): Related location +DTypes.dfy(127,12): Error: assertion violation +DTypes.dfy(93,19): Related location Execution trace: (0,0): anon0 -DTypes.dfy(137,12): Error: assertion violation -DTypes.dfy(132,6): Related location -DTypes.dfy(93,20): Related location +DTypes.dfy(137,11): Error: assertion violation +DTypes.dfy(132,5): Related location +DTypes.dfy(93,19): Related location Execution trace: (0,0): anon0 -DTypes.dfy(158,12): Error: assertion violation +DTypes.dfy(158,11): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon5_Then diff --git a/Test/dafny0/Datatypes.dfy.expect b/Test/dafny0/Datatypes.dfy.expect index 4c0b1e96..7147ca60 100644 --- a/Test/dafny0/Datatypes.dfy.expect +++ b/Test/dafny0/Datatypes.dfy.expect @@ -1,43 +1,43 @@ -Datatypes.dfy(297,10): Error BP5003: A postcondition might not hold on this return path. -Datatypes.dfy(295,15): Related location: This is the postcondition that might not hold. +Datatypes.dfy(297,9): Error BP5003: A postcondition might not hold on this return path. +Datatypes.dfy(295,14): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon13_Then (0,0): anon14_Else (0,0): anon15_Then (0,0): anon6 -Datatypes.dfy(298,12): Error: missing case in case statement: Appendix +Datatypes.dfy(298,11): Error: missing case in case statement: Appendix Execution trace: (0,0): anon0 (0,0): anon13_Then (0,0): anon14_Else (0,0): anon15_Else (0,0): anon16_Then -Datatypes.dfy(349,5): Error: missing case in case statement: Cons +Datatypes.dfy(349,4): Error: missing case in case statement: Cons Execution trace: (0,0): anon0 (0,0): anon7_Else (0,0): anon8_Then -Datatypes.dfy(349,5): Error: missing case in case statement: Nil +Datatypes.dfy(349,4): Error: missing case in case statement: Nil Execution trace: (0,0): anon0 (0,0): anon7_Else (0,0): anon8_Else (0,0): anon9_Then -Datatypes.dfy(356,8): Error: missing case in case statement: Cons +Datatypes.dfy(356,7): Error: missing case in case statement: Cons Execution trace: (0,0): anon0 (0,0): anon10_Else (0,0): anon11_Then (0,0): anon12_Then -Datatypes.dfy(356,8): Error: missing case in case statement: Nil +Datatypes.dfy(356,7): Error: missing case in case statement: Nil Execution trace: (0,0): anon0 (0,0): anon10_Else (0,0): anon11_Then (0,0): anon12_Else (0,0): anon13_Then -Datatypes.dfy(82,20): Error: assertion violation +Datatypes.dfy(82,19): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon20_Else @@ -47,23 +47,23 @@ Execution trace: (0,0): anon23_Then (0,0): anon24_Else (0,0): anon25_Then -Datatypes.dfy(170,16): Error: assertion violation +Datatypes.dfy(170,15): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon4_Then -Datatypes.dfy(172,16): Error: assertion violation +Datatypes.dfy(172,15): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon4_Else (0,0): anon5_Then -Datatypes.dfy(201,13): Error: destructor 'Car' can only be applied to datatype values constructed by 'XCons' +Datatypes.dfy(201,12): Error: destructor 'Car' can only be applied to datatype values constructed by 'XCons' Execution trace: (0,0): anon0 -Datatypes.dfy(204,17): Error: destructor 'Car' can only be applied to datatype values constructed by 'XCons' +Datatypes.dfy(204,16): Error: destructor 'Car' can only be applied to datatype values constructed by 'XCons' Execution trace: (0,0): anon0 (0,0): anon6_Then -Datatypes.dfy(225,17): Error: destructor 'c' can only be applied to datatype values constructed by 'T'' +Datatypes.dfy(225,16): Error: destructor 'c' can only be applied to datatype values constructed by 'T'' Execution trace: (0,0): anon0 (0,0): anon5_Then diff --git a/Test/dafny0/Definedness.dfy.expect b/Test/dafny0/Definedness.dfy.expect index af5b62b9..b5b015ad 100644 --- a/Test/dafny0/Definedness.dfy.expect +++ b/Test/dafny0/Definedness.dfy.expect @@ -1,87 +1,87 @@ -Definedness.dfy(11,7): Error: possible division by zero +Definedness.dfy(11,6): Error: possible division by zero Execution trace: (0,0): anon0 (0,0): anon4_Else -Definedness.dfy(18,16): Error: possible division by zero +Definedness.dfy(18,15): Error: possible division by zero Execution trace: (0,0): anon0 -Definedness.dfy(27,16): Error: target object may be null +Definedness.dfy(27,15): Error: target object may be null Execution trace: (0,0): anon0 -Definedness.dfy(28,21): Error: target object may be null +Definedness.dfy(28,20): Error: target object may be null Execution trace: (0,0): anon0 (0,0): anon3_Then -Definedness.dfy(29,17): Error: possible division by zero +Definedness.dfy(29,16): Error: possible division by zero Execution trace: (0,0): anon0 -Definedness.dfy(36,16): Error: target object may be null +Definedness.dfy(36,15): Error: target object may be null Execution trace: (0,0): anon0 -Definedness.dfy(45,16): Error: target object may be null +Definedness.dfy(45,15): Error: target object may be null Execution trace: (0,0): anon0 -Definedness.dfy(53,18): Error: target object may be null +Definedness.dfy(53,17): Error: target object may be null Execution trace: (0,0): anon0 -Definedness.dfy(54,3): Error BP5003: A postcondition might not hold on this return path. -Definedness.dfy(53,22): Related location: This is the postcondition that might not hold. +Definedness.dfy(54,2): Error BP5003: A postcondition might not hold on this return path. +Definedness.dfy(53,21): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 -Definedness.dfy(60,18): Error: target object may be null +Definedness.dfy(60,17): Error: target object may be null Execution trace: (0,0): anon0 -Definedness.dfy(61,3): Error BP5003: A postcondition might not hold on this return path. -Definedness.dfy(60,22): Related location: This is the postcondition that might not hold. +Definedness.dfy(61,2): Error BP5003: A postcondition might not hold on this return path. +Definedness.dfy(60,21): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 -Definedness.dfy(68,3): Error BP5003: A postcondition might not hold on this return path. -Definedness.dfy(67,22): Related location: This is the postcondition that might not hold. +Definedness.dfy(68,2): Error BP5003: A postcondition might not hold on this return path. +Definedness.dfy(67,21): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 -Definedness.dfy(88,7): Error: target object may be null +Definedness.dfy(88,6): Error: target object may be null Execution trace: (0,0): anon0 -Definedness.dfy(89,5): Error: possible violation of function precondition -Definedness.dfy(79,16): Related location +Definedness.dfy(89,4): Error: possible violation of function precondition +Definedness.dfy(79,15): Related location Execution trace: (0,0): anon0 -Definedness.dfy(89,10): Error: assignment may update an object not in the enclosing context's modifies clause +Definedness.dfy(89,9): Error: assignment may update an object not in the enclosing context's modifies clause Execution trace: (0,0): anon0 -Definedness.dfy(89,10): Error: target object may be null +Definedness.dfy(89,9): Error: target object may be null Execution trace: (0,0): anon0 -Definedness.dfy(90,10): Error: possible violation of function precondition -Definedness.dfy(79,16): Related location +Definedness.dfy(90,9): Error: possible violation of function precondition +Definedness.dfy(79,15): Related location Execution trace: (0,0): anon0 -Definedness.dfy(95,14): Error: possible division by zero +Definedness.dfy(95,13): Error: possible division by zero Execution trace: (0,0): anon0 -Definedness.dfy(95,23): Error: possible division by zero +Definedness.dfy(95,22): Error: possible division by zero Execution trace: (0,0): anon0 -Definedness.dfy(96,15): Error: possible division by zero +Definedness.dfy(96,14): Error: possible division by zero Execution trace: (0,0): anon0 -Definedness.dfy(101,12): Error: possible division by zero +Definedness.dfy(101,11): Error: possible division by zero Execution trace: (0,0): anon0 -Definedness.dfy(108,15): Error: possible division by zero +Definedness.dfy(108,14): Error: possible division by zero Execution trace: Definedness.dfy(108,5): anon7_LoopHead (0,0): anon7_LoopBody Definedness.dfy(108,5): anon8_Else -Definedness.dfy(117,23): Error: possible violation of function precondition -Definedness.dfy(79,16): Related location +Definedness.dfy(117,22): Error: possible violation of function precondition +Definedness.dfy(79,15): Related location Execution trace: (0,0): anon0 Definedness.dfy(116,5): anon12_LoopHead (0,0): anon12_LoopBody (0,0): anon13_Then -Definedness.dfy(123,17): Error: possible violation of function precondition -Definedness.dfy(79,16): Related location +Definedness.dfy(123,16): Error: possible violation of function precondition +Definedness.dfy(79,15): Related location Execution trace: (0,0): anon0 Definedness.dfy(116,5): anon12_LoopHead @@ -91,30 +91,30 @@ Execution trace: Definedness.dfy(122,5): anon15_LoopHead (0,0): anon15_LoopBody (0,0): anon16_Then -Definedness.dfy(133,17): Error: possible violation of function precondition -Definedness.dfy(79,16): Related location +Definedness.dfy(133,16): Error: possible violation of function precondition +Definedness.dfy(79,15): Related location Execution trace: (0,0): anon0 Definedness.dfy(132,5): anon6_LoopHead (0,0): anon6_LoopBody (0,0): anon7_Then -Definedness.dfy(133,22): Error BP5004: This loop invariant might not hold on entry. +Definedness.dfy(133,21): Error BP5004: This loop invariant might not hold on entry. Execution trace: (0,0): anon0 -Definedness.dfy(134,17): Error: possible violation of function precondition -Definedness.dfy(79,16): Related location +Definedness.dfy(134,16): Error: possible violation of function precondition +Definedness.dfy(79,15): Related location Execution trace: (0,0): anon0 Definedness.dfy(132,5): anon6_LoopHead (0,0): anon6_LoopBody (0,0): anon7_Then -Definedness.dfy(143,15): Error: possible division by zero +Definedness.dfy(143,14): Error: possible division by zero Execution trace: (0,0): anon0 Definedness.dfy(143,5): anon8_LoopHead (0,0): anon8_LoopBody Definedness.dfy(143,5): anon9_Else -Definedness.dfy(162,15): Error: possible division by zero +Definedness.dfy(162,14): Error: possible division by zero Execution trace: (0,0): anon0 Definedness.dfy(156,5): anon16_LoopHead @@ -126,11 +126,11 @@ Execution trace: Definedness.dfy(162,5): anon20_LoopHead (0,0): anon20_LoopBody Definedness.dfy(162,5): anon21_Else -Definedness.dfy(175,28): Error BP5004: This loop invariant might not hold on entry. +Definedness.dfy(175,27): Error BP5004: This loop invariant might not hold on entry. Execution trace: (0,0): anon0 -Definedness.dfy(181,17): Error: possible violation of function precondition -Definedness.dfy(79,16): Related location +Definedness.dfy(181,16): Error: possible violation of function precondition +Definedness.dfy(79,15): Related location Execution trace: (0,0): anon0 Definedness.dfy(173,5): anon18_LoopHead @@ -142,32 +142,32 @@ Execution trace: (0,0): anon22_Then (0,0): anon23_Then (0,0): anon11 -Definedness.dfy(196,19): Error: possible division by zero +Definedness.dfy(196,18): Error: possible division by zero Execution trace: (0,0): anon0 Definedness.dfy(194,5): anon6_LoopHead (0,0): anon6_LoopBody (0,0): anon7_Then -Definedness.dfy(196,23): Error BP5004: This loop invariant might not hold on entry. +Definedness.dfy(196,22): Error BP5004: This loop invariant might not hold on entry. Execution trace: (0,0): anon0 -Definedness.dfy(196,28): Error: possible division by zero +Definedness.dfy(196,27): Error: possible division by zero Execution trace: (0,0): anon0 Definedness.dfy(194,5): anon6_LoopHead (0,0): anon6_LoopBody (0,0): anon7_Then -Definedness.dfy(215,10): Error BP5003: A postcondition might not hold on this return path. -Definedness.dfy(217,46): Related location: This is the postcondition that might not hold. +Definedness.dfy(215,9): Error BP5003: A postcondition might not hold on this return path. +Definedness.dfy(217,45): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon4_Else -Definedness.dfy(224,22): Error: target object may be null +Definedness.dfy(224,21): Error: target object may be null Execution trace: (0,0): anon0 (0,0): anon4_Then -Definedness.dfy(237,10): Error BP5003: A postcondition might not hold on this return path. -Definedness.dfy(240,24): Related location: This is the postcondition that might not hold. +Definedness.dfy(237,9): Error BP5003: A postcondition might not hold on this return path. +Definedness.dfy(240,23): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon4_Else diff --git a/Test/dafny0/DeterministicPick.dfy.expect b/Test/dafny0/DeterministicPick.dfy.expect index 0999294e..aef97ebd 100644 --- a/Test/dafny0/DeterministicPick.dfy.expect +++ b/Test/dafny0/DeterministicPick.dfy.expect @@ -1,4 +1,4 @@ -DeterministicPick.dfy(13,5): Error: to be compilable, the value of a let-such-that expression must be uniquely determined +DeterministicPick.dfy(13,4): Error: to be compilable, the value of a let-such-that expression must be uniquely determined Execution trace: (0,0): anon0 (0,0): anon4_Else diff --git a/Test/dafny0/DiamondImports.dfy.expect b/Test/dafny0/DiamondImports.dfy.expect index e9e8c2b9..1acca075 100644 --- a/Test/dafny0/DiamondImports.dfy.expect +++ b/Test/dafny0/DiamondImports.dfy.expect @@ -1,12 +1,12 @@ -DiamondImports.dfy(34,16): Error: assertion violation +DiamondImports.dfy(34,15): Error: assertion violation Execution trace: (0,0): anon0 -DiamondImports.dfy(50,16): Error: assertion violation +DiamondImports.dfy(50,15): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Then (0,0): anon2 -DiamondImports.dfy(101,16): Error: assertion violation +DiamondImports.dfy(101,15): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon7_Then @@ -14,7 +14,7 @@ Execution trace: (0,0): anon8_Then (0,0): anon9_Then (0,0): anon6 -DiamondImports.dfy(120,16): Error: assertion violation +DiamondImports.dfy(120,15): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon9_Then @@ -25,7 +25,7 @@ Execution trace: (0,0): anon6 (0,0): anon12_Then (0,0): anon8 -DiamondImports.dfy(140,26): Error: assertion violation +DiamondImports.dfy(140,25): Error: assertion violation Execution trace: (0,0): anon0 diff --git a/Test/dafny0/Fuel.dfy.expect b/Test/dafny0/Fuel.dfy.expect index 4c180a9c..90fe877d 100644 --- a/Test/dafny0/Fuel.dfy.expect +++ b/Test/dafny0/Fuel.dfy.expect @@ -1,94 +1,94 @@ -Fuel.dfy(17,23): Error: assertion violation +Fuel.dfy(17,22): Error: assertion violation Execution trace: (0,0): anon0 -Fuel.dfy(65,28): Error: assertion violation +Fuel.dfy(65,27): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon6_Else -Fuel.dfy(69,28): Error: assertion violation +Fuel.dfy(69,27): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon6_Then (0,0): anon7_Then -Fuel.dfy(92,23): Error: assertion violation +Fuel.dfy(92,22): Error: assertion violation Execution trace: (0,0): anon0 -Fuel.dfy(94,23): Error: assertion violation +Fuel.dfy(94,22): Error: assertion violation Execution trace: (0,0): anon0 -Fuel.dfy(120,23): Error: assertion violation +Fuel.dfy(120,22): Error: assertion violation Execution trace: (0,0): anon0 -Fuel.dfy(122,23): Error: assertion violation +Fuel.dfy(122,22): Error: assertion violation Execution trace: (0,0): anon0 -Fuel.dfy(129,39): Error: assertion violation +Fuel.dfy(129,38): Error: assertion violation Execution trace: (0,0): anon0 -Fuel.dfy(132,27): Error: assertion violation +Fuel.dfy(132,26): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Then -Fuel.dfy(133,27): Error: assertion violation +Fuel.dfy(133,26): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Then -Fuel.dfy(157,23): Error: assertion violation +Fuel.dfy(157,22): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon10_Else (0,0): anon9 -Fuel.dfy(200,56): Error: assertion violation +Fuel.dfy(200,55): Error: assertion violation Execution trace: (0,0): anon0 -Fuel.dfy(245,23): Error: assertion violation +Fuel.dfy(245,22): Error: assertion violation Execution trace: (0,0): anon0 -Fuel.dfy(247,23): Error: assertion violation +Fuel.dfy(247,22): Error: assertion violation Execution trace: (0,0): anon0 -Fuel.dfy(280,27): Error: assertion violation +Fuel.dfy(280,26): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon7_Then -Fuel.dfy(335,27): Error: possible violation of function precondition -Fuel.dfy(324,22): Related location +Fuel.dfy(335,26): Error: possible violation of function precondition +Fuel.dfy(324,21): Related location Execution trace: (0,0): anon0 (0,0): anon7_Else (0,0): anon8_Then -Fuel.dfy(335,50): Error: destructor 't' can only be applied to datatype values constructed by 'VTuple' +Fuel.dfy(335,49): Error: destructor 't' can only be applied to datatype values constructed by 'VTuple' Execution trace: (0,0): anon0 (0,0): anon7_Else (0,0): anon8_Then -Fuel.dfy(335,51): Error: index out of range +Fuel.dfy(335,50): Error: index out of range Execution trace: (0,0): anon0 (0,0): anon7_Else (0,0): anon8_Then -Fuel.dfy(336,39): Error: index out of range +Fuel.dfy(336,38): Error: index out of range Execution trace: (0,0): anon0 (0,0): anon7_Else (0,0): anon8_Then -Fuel.dfy(336,43): Error: destructor 'u' can only be applied to datatype values constructed by 'VUint64' +Fuel.dfy(336,42): Error: destructor 'u' can only be applied to datatype values constructed by 'VUint64' Execution trace: (0,0): anon0 (0,0): anon7_Else (0,0): anon8_Then -Fuel.dfy(346,43): Error: destructor 'u' can only be applied to datatype values constructed by 'VUint64' +Fuel.dfy(346,42): Error: destructor 'u' can only be applied to datatype values constructed by 'VUint64' Execution trace: (0,0): anon0 (0,0): anon7_Else (0,0): anon8_Then -Fuel.dfy(397,23): Error: assertion violation +Fuel.dfy(397,22): Error: assertion violation Execution trace: (0,0): anon0 -Fuel.dfy(398,23): Error: assertion violation +Fuel.dfy(398,22): Error: assertion violation Execution trace: (0,0): anon0 -Fuel.dfy(407,39): Error: assertion violation +Fuel.dfy(407,38): Error: assertion violation Execution trace: (0,0): anon0 diff --git a/Test/dafny0/FunctionSpecifications.dfy.expect b/Test/dafny0/FunctionSpecifications.dfy.expect index 9f76313a..078afaef 100644 --- a/Test/dafny0/FunctionSpecifications.dfy.expect +++ b/Test/dafny0/FunctionSpecifications.dfy.expect @@ -1,5 +1,5 @@ -FunctionSpecifications.dfy(29,10): Error BP5003: A postcondition might not hold on this return path. -FunctionSpecifications.dfy(31,13): Related location: This is the postcondition that might not hold. +FunctionSpecifications.dfy(29,9): Error BP5003: A postcondition might not hold on this return path. +FunctionSpecifications.dfy(31,12): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon10_Else @@ -7,64 +7,64 @@ Execution trace: (0,0): anon12_Then (0,0): anon13_Else (0,0): anon9 -FunctionSpecifications.dfy(38,10): Error BP5003: A postcondition might not hold on this return path. -FunctionSpecifications.dfy(40,24): Related location: This is the postcondition that might not hold. +FunctionSpecifications.dfy(38,9): Error BP5003: A postcondition might not hold on this return path. +FunctionSpecifications.dfy(40,23): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon15_Else (0,0): anon18_Else (0,0): anon19_Then (0,0): anon14 -FunctionSpecifications.dfy(53,11): Error: cannot prove termination; try supplying a decreases clause +FunctionSpecifications.dfy(53,10): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon11_Then (0,0): anon5 -FunctionSpecifications.dfy(59,10): Error BP5003: A postcondition might not hold on this return path. -FunctionSpecifications.dfy(60,22): Related location: This is the postcondition that might not hold. +FunctionSpecifications.dfy(59,9): Error BP5003: A postcondition might not hold on this return path. +FunctionSpecifications.dfy(60,21): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon7_Else -FunctionSpecifications.dfy(108,23): Error: assertion violation +FunctionSpecifications.dfy(108,22): Error: assertion violation Execution trace: (0,0): anon0 -FunctionSpecifications.dfy(111,23): Error: assertion violation +FunctionSpecifications.dfy(111,22): Error: assertion violation Execution trace: (0,0): anon0 -FunctionSpecifications.dfy(126,27): Error: assertion violation +FunctionSpecifications.dfy(126,26): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Then -FunctionSpecifications.dfy(130,27): Error: assertion violation +FunctionSpecifications.dfy(130,26): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Else -FunctionSpecifications.dfy(158,3): Error: cannot prove termination; try supplying a decreases clause +FunctionSpecifications.dfy(158,2): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon4_Else -FunctionSpecifications.dfy(167,11): Error: cannot prove termination; try supplying a decreases clause +FunctionSpecifications.dfy(167,10): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon4_Else -FunctionSpecifications.dfy(135,20): Error BP5003: A postcondition might not hold on this return path. -FunctionSpecifications.dfy(137,29): Related location: This is the postcondition that might not hold. +FunctionSpecifications.dfy(135,19): Error BP5003: A postcondition might not hold on this return path. +FunctionSpecifications.dfy(137,28): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon4_Else -FunctionSpecifications.dfy(146,3): Error: failure to decrease termination measure +FunctionSpecifications.dfy(146,2): Error: failure to decrease termination measure Execution trace: (0,0): anon0 (0,0): anon4_Else -FunctionSpecifications.dfy(153,3): Error: failure to decrease termination measure +FunctionSpecifications.dfy(153,2): Error: failure to decrease termination measure Execution trace: (0,0): anon0 (0,0): anon4_Else -FunctionSpecifications.dfy(174,3): Error: cannot prove termination; try supplying a decreases clause +FunctionSpecifications.dfy(174,2): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon4_Else -FunctionSpecifications.dfy(171,20): Error: cannot prove termination; try supplying a decreases clause +FunctionSpecifications.dfy(171,19): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 diff --git a/Test/dafny0/IMaps.dfy.expect b/Test/dafny0/IMaps.dfy.expect index c2da9505..28ca8ca3 100644 --- a/Test/dafny0/IMaps.dfy.expect +++ b/Test/dafny0/IMaps.dfy.expect @@ -1,4 +1,4 @@ -IMaps.dfy(52,8): Error: element may not be in domain +IMaps.dfy(52,7): Error: element may not be in domain Execution trace: (0,0): anon0 (0,0): anon5_Then diff --git a/Test/dafny0/Include.dfy.expect b/Test/dafny0/Include.dfy.expect index 0921cec9..d4543afe 100644 --- a/Test/dafny0/Include.dfy.expect +++ b/Test/dafny0/Include.dfy.expect @@ -1,13 +1,13 @@ -Include.dfy(19,19): Error BP5003: A postcondition might not hold on this return path. -Includee.dfy(17,20): Related location: This is the postcondition that might not hold. +Include.dfy(19,18): Error BP5003: A postcondition might not hold on this return path. +Includee.dfy(17,19): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon4_Else -Includee.dfy[Concrete](22,16): Error: assertion violation +Includee.dfy[Concrete](22,15): Error: assertion violation Execution trace: (0,0): anon0 -Include.dfy(27,7): Error BP5003: A postcondition might not hold on this return path. -Includee.dfy[Concrete](20,15): Related location: This is the postcondition that might not hold. +Include.dfy(27,6): Error BP5003: A postcondition might not hold on this return path. +Includee.dfy[Concrete](20,14): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon6_Then diff --git a/Test/dafny0/Includee.dfy.expect b/Test/dafny0/Includee.dfy.expect index e0f0689c..ce61e32a 100644 --- a/Test/dafny0/Includee.dfy.expect +++ b/Test/dafny0/Includee.dfy.expect @@ -1,12 +1,12 @@ -Includee.dfy(21,3): Error BP5003: A postcondition might not hold on this return path. -Includee.dfy(20,15): Related location: This is the postcondition that might not hold. +Includee.dfy(21,2): Error BP5003: A postcondition might not hold on this return path. +Includee.dfy(20,14): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 -Includee.dfy(24,18): Error: assertion violation +Includee.dfy(24,17): Error: assertion violation Execution trace: (0,0): anon0 -Includee.dfy(6,1): Error BP5003: A postcondition might not hold on this return path. -Includee.dfy(5,13): Related location: This is the postcondition that might not hold. +Includee.dfy(6,0): Error BP5003: A postcondition might not hold on this return path. +Includee.dfy(5,12): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 diff --git a/Test/dafny0/IndexIntoUpdate.dfy.expect b/Test/dafny0/IndexIntoUpdate.dfy.expect index 3423a20b..2db3aa0a 100644 --- a/Test/dafny0/IndexIntoUpdate.dfy.expect +++ b/Test/dafny0/IndexIntoUpdate.dfy.expect @@ -1,4 +1,4 @@ -IndexIntoUpdate.dfy(7,19): Error: assertion violation +IndexIntoUpdate.dfy(7,18): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Then diff --git a/Test/dafny0/InductivePredicates.dfy.expect b/Test/dafny0/InductivePredicates.dfy.expect index b09b7903..ccf30643 100644 --- a/Test/dafny0/InductivePredicates.dfy.expect +++ b/Test/dafny0/InductivePredicates.dfy.expect @@ -1,8 +1,8 @@ -InductivePredicates.dfy(64,10): Error: assertion violation +InductivePredicates.dfy(64,9): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Then -InductivePredicates.dfy(76,11): Error: assertion violation +InductivePredicates.dfy(76,10): Error: assertion violation Execution trace: (0,0): anon0 diff --git a/Test/dafny0/Inverses.dfy.expect b/Test/dafny0/Inverses.dfy.expect index a04f21dc..29c67e5d 100644 --- a/Test/dafny0/Inverses.dfy.expect +++ b/Test/dafny0/Inverses.dfy.expect @@ -1,10 +1,10 @@ -Inverses.dfy(70,1): Error BP5003: A postcondition might not hold on this return path. -Inverses.dfy(69,11): Related location: This is the postcondition that might not hold. +Inverses.dfy(70,0): Error BP5003: A postcondition might not hold on this return path. +Inverses.dfy(69,10): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon6_Else -Inverses.dfy(83,1): Error BP5003: A postcondition might not hold on this return path. -Inverses.dfy(82,11): Related location: This is the postcondition that might not hold. +Inverses.dfy(83,0): Error BP5003: A postcondition might not hold on this return path. +Inverses.dfy(82,10): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon9_Else diff --git a/Test/dafny0/Iterators.dfy.expect b/Test/dafny0/Iterators.dfy.expect index f0c6e400..d9129e3e 100644 --- a/Test/dafny0/Iterators.dfy.expect +++ b/Test/dafny0/Iterators.dfy.expect @@ -1,55 +1,55 @@ -Iterators.dfy(251,10): Error: failure to decrease termination measure +Iterators.dfy(251,9): Error: failure to decrease termination measure Execution trace: (0,0): anon0 (0,0): anon5_Else (0,0): anon6_Else -Iterators.dfy(274,10): Error: failure to decrease termination measure +Iterators.dfy(274,9): Error: failure to decrease termination measure Execution trace: (0,0): anon0 (0,0): anon5_Else (0,0): anon6_Else -Iterators.dfy(284,32): Error: failure to decrease termination measure +Iterators.dfy(284,31): Error: failure to decrease termination measure Execution trace: (0,0): anon0 -Iterators.dfy(296,10): Error: cannot prove termination; try supplying a decreases clause +Iterators.dfy(296,9): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon5_Else (0,0): anon6_Else -Iterators.dfy(317,10): Error: cannot prove termination; try supplying a decreases clause +Iterators.dfy(317,9): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon5_Else (0,0): anon6_Else -Iterators.dfy(326,32): Error: cannot prove termination; try supplying a decreases clause +Iterators.dfy(326,31): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 -Iterators.dfy(343,10): Error: failure to decrease termination measure +Iterators.dfy(343,9): Error: failure to decrease termination measure Execution trace: (0,0): anon0 (0,0): anon5_Else (0,0): anon6_Else -Iterators.dfy(353,32): Error: cannot prove termination; try supplying a decreases clause +Iterators.dfy(353,31): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 -Iterators.dfy(370,10): Error: failure to decrease termination measure +Iterators.dfy(370,9): Error: failure to decrease termination measure Execution trace: (0,0): anon0 (0,0): anon5_Else (0,0): anon6_Else -Iterators.dfy(103,22): Error: assertion violation +Iterators.dfy(103,21): Error: assertion violation Execution trace: (0,0): anon0 -Iterators.dfy(106,14): Error: assertion violation +Iterators.dfy(106,13): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon4_Then (0,0): anon3 -Iterators.dfy(177,28): Error: assertion violation +Iterators.dfy(177,27): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon15_Then -Iterators.dfy(208,7): Error: an assignment to _new is only allowed to shrink the set +Iterators.dfy(208,6): Error: an assignment to _new is only allowed to shrink the set Execution trace: (0,0): anon0 Iterators.dfy(197,3): anon16_LoopHead @@ -57,7 +57,7 @@ Execution trace: Iterators.dfy(197,3): anon17_Else Iterators.dfy(197,3): anon19_Else (0,0): anon20_Then -Iterators.dfy(212,21): Error: assertion violation +Iterators.dfy(212,20): Error: assertion violation Execution trace: (0,0): anon0 Iterators.dfy(197,3): anon16_LoopHead @@ -65,8 +65,8 @@ Execution trace: Iterators.dfy(197,3): anon17_Else Iterators.dfy(197,3): anon19_Else (0,0): anon21_Then -Iterators.dfy(40,22): Error BP5002: A precondition for this call might not hold. -Iterators.dfy(4,10): Related location: This is the precondition that might not hold. +Iterators.dfy(40,21): Error BP5002: A precondition for this call might not hold. +Iterators.dfy(4,9): Related location: This is the precondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon35_Then @@ -74,24 +74,24 @@ Execution trace: (0,0): anon36_Then (0,0): anon5 (0,0): anon37_Then -Iterators.dfy(89,14): Error: assertion violation +Iterators.dfy(89,13): Error: assertion violation Execution trace: (0,0): anon0 -Iterators.dfy(119,16): Error: assertion violation +Iterators.dfy(119,15): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Else -Iterators.dfy(150,16): Error: assertion violation +Iterators.dfy(150,15): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon4_Else -Iterators.dfy(155,24): Error BP5002: A precondition for this call might not hold. -Iterators.dfy(125,10): Related location: This is the precondition that might not hold. +Iterators.dfy(155,23): Error BP5002: A precondition for this call might not hold. +Iterators.dfy(125,9): Related location: This is the precondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon4_Then (0,0): anon3 -Iterators.dfy(234,21): Error: assertion violation +Iterators.dfy(234,20): Error: assertion violation Execution trace: (0,0): anon0 Iterators.dfy(225,3): anon14_LoopHead diff --git a/Test/dafny0/LetExpr.dfy.expect b/Test/dafny0/LetExpr.dfy.expect index 66dc2764..f0f51274 100644 --- a/Test/dafny0/LetExpr.dfy.expect +++ b/Test/dafny0/LetExpr.dfy.expect @@ -1,35 +1,35 @@ -LetExpr.dfy(109,23): Error: assertion violation +LetExpr.dfy(109,22): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon11_Then -LetExpr.dfy(9,12): Error: assertion violation +LetExpr.dfy(9,11): Error: assertion violation Execution trace: (0,0): anon0 -LetExpr.dfy(254,19): Error: value assigned to a nat must be non-negative +LetExpr.dfy(254,18): Error: value assigned to a nat must be non-negative Execution trace: (0,0): anon0 (0,0): anon5_Then -LetExpr.dfy(257,19): Error: value assigned to a nat must be non-negative +LetExpr.dfy(257,18): Error: value assigned to a nat must be non-negative Execution trace: (0,0): anon0 (0,0): anon6_Then -LetExpr.dfy(259,24): Error: value assigned to a nat must be non-negative +LetExpr.dfy(259,23): Error: value assigned to a nat must be non-negative Execution trace: (0,0): anon0 (0,0): anon6_Else -LetExpr.dfy(288,14): Error: RHS is not certain to look like the pattern 'Agnes' +LetExpr.dfy(288,13): Error: RHS is not certain to look like the pattern 'Agnes' Execution trace: (0,0): anon0 (0,0): anon3_Else -LetExpr.dfy(305,42): Error: value assigned to a nat must be non-negative +LetExpr.dfy(305,41): Error: value assigned to a nat must be non-negative Execution trace: (0,0): anon0 (0,0): anon7_Else -LetExpr.dfy(307,12): Error: assertion violation +LetExpr.dfy(307,11): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon7_Else -LetExpr.dfy(317,12): Error: to be compilable, the value of a let-such-that expression must be uniquely determined +LetExpr.dfy(317,11): Error: to be compilable, the value of a let-such-that expression must be uniquely determined Execution trace: (0,0): anon0 (0,0): anon10_Then diff --git a/Test/dafny0/LhsDuplicates.dfy.expect b/Test/dafny0/LhsDuplicates.dfy.expect index a864390f..d6689047 100644 --- a/Test/dafny0/LhsDuplicates.dfy.expect +++ b/Test/dafny0/LhsDuplicates.dfy.expect @@ -1,27 +1,27 @@ -LhsDuplicates.dfy(18,10): Error: left-hand sides for different forall-statement bound variables may refer to the same location +LhsDuplicates.dfy(18,9): Error: left-hand sides for different forall-statement bound variables may refer to the same location Execution trace: (0,0): anon0 (0,0): anon16_Else (0,0): anon18_Else (0,0): anon21_Then (0,0): anon13 -LhsDuplicates.dfy(34,12): Error: left-hand sides for different forall-statement bound variables may refer to the same location +LhsDuplicates.dfy(34,11): Error: left-hand sides for different forall-statement bound variables may refer to the same location Execution trace: (0,0): anon0 (0,0): anon16_Else (0,0): anon18_Else (0,0): anon21_Then (0,0): anon13 -LhsDuplicates.dfy(42,12): Error: when left-hand sides 1 and 3 refer to the same location, they must be assigned the same value +LhsDuplicates.dfy(42,11): Error: when left-hand sides 1 and 3 refer to the same location, they must be assigned the same value Execution trace: (0,0): anon0 -LhsDuplicates.dfy(51,18): Error: when left-hand sides 0 and 2 refer to the same location, they must be assigned the same value +LhsDuplicates.dfy(51,17): Error: when left-hand sides 0 and 2 refer to the same location, they must be assigned the same value Execution trace: (0,0): anon0 -LhsDuplicates.dfy(60,16): Error: when left-hand sides 1 and 2 may refer to the same location, they must be assigned the same value +LhsDuplicates.dfy(60,15): Error: when left-hand sides 1 and 2 may refer to the same location, they must be assigned the same value Execution trace: (0,0): anon0 -LhsDuplicates.dfy(69,20): Error: when left-hand sides 1 and 2 refer to the same location, they must be assigned the same value +LhsDuplicates.dfy(69,19): Error: when left-hand sides 1 and 2 refer to the same location, they must be assigned the same value Execution trace: (0,0): anon0 diff --git a/Test/dafny0/LoopModifies.dfy.expect b/Test/dafny0/LoopModifies.dfy.expect index 682975fb..a7ded8a4 100644 --- a/Test/dafny0/LoopModifies.dfy.expect +++ b/Test/dafny0/LoopModifies.dfy.expect @@ -1,38 +1,38 @@ -LoopModifies.dfy(8,5): Error: assignment may update an array element not in the enclosing context's modifies clause +LoopModifies.dfy(8,4): Error: assignment may update an array element not in the enclosing context's modifies clause Execution trace: (0,0): anon0 -LoopModifies.dfy(19,8): Error: assignment may update an array element not in the enclosing context's modifies clause +LoopModifies.dfy(19,7): Error: assignment may update an array element not in the enclosing context's modifies clause Execution trace: (0,0): anon0 LoopModifies.dfy(16,4): anon8_LoopHead (0,0): anon8_LoopBody LoopModifies.dfy(16,4): anon9_Else LoopModifies.dfy(16,4): anon11_Else -LoopModifies.dfy(48,8): Error: assignment may update an array element not in the enclosing context's modifies clause +LoopModifies.dfy(48,7): Error: assignment may update an array element not in the enclosing context's modifies clause Execution trace: (0,0): anon0 LoopModifies.dfy(44,4): anon8_LoopHead (0,0): anon8_LoopBody LoopModifies.dfy(44,4): anon9_Else LoopModifies.dfy(44,4): anon11_Else -LoopModifies.dfy(63,8): Error: assignment may update an array element not in the enclosing context's modifies clause +LoopModifies.dfy(63,7): Error: assignment may update an array element not in the enclosing context's modifies clause Execution trace: (0,0): anon0 LoopModifies.dfy(59,4): anon9_LoopHead (0,0): anon9_LoopBody LoopModifies.dfy(59,4): anon10_Else LoopModifies.dfy(59,4): anon12_Else -LoopModifies.dfy(76,4): Error: loop modifies clause may violate context's modifies clause +LoopModifies.dfy(76,3): Error: loop modifies clause may violate context's modifies clause Execution trace: (0,0): anon0 -LoopModifies.dfy(100,8): Error: assignment may update an array element not in the enclosing context's modifies clause +LoopModifies.dfy(100,7): Error: assignment may update an array element not in the enclosing context's modifies clause Execution trace: (0,0): anon0 LoopModifies.dfy(92,4): anon8_LoopHead (0,0): anon8_LoopBody LoopModifies.dfy(92,4): anon9_Else LoopModifies.dfy(92,4): anon11_Else -LoopModifies.dfy(148,11): Error: assignment may update an array element not in the enclosing context's modifies clause +LoopModifies.dfy(148,10): Error: assignment may update an array element not in the enclosing context's modifies clause Execution trace: (0,0): anon0 LoopModifies.dfy(136,4): anon17_LoopHead @@ -43,14 +43,14 @@ Execution trace: (0,0): anon21_LoopBody LoopModifies.dfy(141,7): anon22_Else LoopModifies.dfy(141,7): anon24_Else -LoopModifies.dfy(199,10): Error: assignment may update an array element not in the enclosing context's modifies clause +LoopModifies.dfy(199,9): Error: assignment may update an array element not in the enclosing context's modifies clause Execution trace: (0,0): anon0 LoopModifies.dfy(195,4): anon8_LoopHead (0,0): anon8_LoopBody LoopModifies.dfy(195,4): anon9_Else LoopModifies.dfy(195,4): anon11_Else -LoopModifies.dfy(287,13): Error: assignment may update an array element not in the enclosing context's modifies clause +LoopModifies.dfy(287,12): Error: assignment may update an array element not in the enclosing context's modifies clause Execution trace: (0,0): anon0 LoopModifies.dfy(275,4): anon16_LoopHead diff --git a/Test/dafny0/Maps.dfy.expect b/Test/dafny0/Maps.dfy.expect index f46549dd..8b4a6a36 100644 --- a/Test/dafny0/Maps.dfy.expect +++ b/Test/dafny0/Maps.dfy.expect @@ -1,7 +1,7 @@ -Maps.dfy(78,8): Error: element may not be in domain +Maps.dfy(78,7): Error: element may not be in domain Execution trace: (0,0): anon0 -Maps.dfy(128,13): Error: assertion violation +Maps.dfy(128,12): Error: assertion violation Execution trace: (0,0): anon0 diff --git a/Test/dafny0/ModifyStmt.dfy.expect b/Test/dafny0/ModifyStmt.dfy.expect index 4ea872e0..019453d1 100644 --- a/Test/dafny0/ModifyStmt.dfy.expect +++ b/Test/dafny0/ModifyStmt.dfy.expect @@ -1,19 +1,19 @@ -ModifyStmt.dfy(27,14): Error: assertion violation +ModifyStmt.dfy(27,13): Error: assertion violation Execution trace: (0,0): anon0 -ModifyStmt.dfy(42,5): Error: modify statement may violate context's modifies clause +ModifyStmt.dfy(42,4): Error: modify statement may violate context's modifies clause Execution trace: (0,0): anon0 -ModifyStmt.dfy(48,5): Error: modify statement may violate context's modifies clause +ModifyStmt.dfy(48,4): Error: modify statement may violate context's modifies clause Execution trace: (0,0): anon0 -ModifyStmt.dfy(61,5): Error: modify statement may violate context's modifies clause +ModifyStmt.dfy(61,4): Error: modify statement may violate context's modifies clause Execution trace: (0,0): anon0 -ModifyStmt.dfy(70,14): Error: assertion violation +ModifyStmt.dfy(70,13): Error: assertion violation Execution trace: (0,0): anon0 -ModifyStmt.dfy(89,14): Error: assertion violation +ModifyStmt.dfy(89,13): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon9_Then @@ -22,20 +22,20 @@ Execution trace: ModifyStmt.dfy(81,7): anon11_Else (0,0): anon12_Then (0,0): anon8 -ModifyStmt.dfy(99,14): Error: assertion violation +ModifyStmt.dfy(99,13): Error: assertion violation Execution trace: (0,0): anon0 -ModifyStmt.dfy(110,14): Error: assertion violation +ModifyStmt.dfy(110,13): Error: assertion violation Execution trace: (0,0): anon0 -ModifyStmt.dfy(122,16): Error: assertion violation +ModifyStmt.dfy(122,15): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Then -ModifyStmt.dfy(134,7): Error: assignment may update an object not in the enclosing context's modifies clause +ModifyStmt.dfy(134,6): Error: assignment may update an object not in the enclosing context's modifies clause Execution trace: (0,0): anon0 -ModifyStmt.dfy(172,15): Error: assertion violation +ModifyStmt.dfy(172,14): Error: assertion violation Execution trace: (0,0): anon0 diff --git a/Test/dafny0/Modules0.dfy.expect b/Test/dafny0/Modules0.dfy.expect index c63ed937..e4b46cce 100644 --- a/Test/dafny0/Modules0.dfy.expect +++ b/Test/dafny0/Modules0.dfy.expect @@ -1,5 +1,5 @@ -Modules0.dfy(333,3): warning: module-level functions are always non-instance, so the 'static' keyword is not allowed here -Modules0.dfy(335,3): warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +Modules0.dfy(333,2): Warning: module-level functions are always non-instance, so the 'static' keyword is not allowed here +Modules0.dfy(335,2): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here Modules0.dfy(8,8): Error: Duplicate name of top-level declaration: WazzupA Modules0.dfy(9,11): Error: Duplicate name of top-level declaration: WazzupA Modules0.dfy(10,7): Error: Duplicate name of top-level declaration: WazzupA diff --git a/Test/dafny0/Modules1.dfy.expect b/Test/dafny0/Modules1.dfy.expect index 342b5808..feddf46a 100644 --- a/Test/dafny0/Modules1.dfy.expect +++ b/Test/dafny0/Modules1.dfy.expect @@ -1,20 +1,20 @@ -Modules1.dfy(79,16): Error: assertion violation +Modules1.dfy(79,15): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Then -Modules1.dfy(92,16): Error: assertion violation +Modules1.dfy(92,15): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Then -Modules1.dfy(94,18): Error: assertion violation +Modules1.dfy(94,17): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Else -Modules1.dfy(56,9): Error: decreases expression must be bounded below by 0 -Modules1.dfy(54,13): Related location +Modules1.dfy(56,8): Error: decreases expression must be bounded below by 0 +Modules1.dfy(54,12): Related location Execution trace: (0,0): anon0 -Modules1.dfy(62,9): Error: failure to decrease termination measure +Modules1.dfy(62,8): Error: failure to decrease termination measure Execution trace: (0,0): anon0 diff --git a/Test/dafny0/MultiDimArray.dfy.expect b/Test/dafny0/MultiDimArray.dfy.expect index 597ade30..f2bf74de 100644 --- a/Test/dafny0/MultiDimArray.dfy.expect +++ b/Test/dafny0/MultiDimArray.dfy.expect @@ -1,9 +1,9 @@ -MultiDimArray.dfy(56,21): Error: assertion violation +MultiDimArray.dfy(56,20): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon11_Then (0,0): anon12_Then -MultiDimArray.dfy(83,25): Error: assertion violation +MultiDimArray.dfy(83,24): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon5_Then diff --git a/Test/dafny0/MultiSets.dfy.expect b/Test/dafny0/MultiSets.dfy.expect index 30534b11..aed70bd2 100644 --- a/Test/dafny0/MultiSets.dfy.expect +++ b/Test/dafny0/MultiSets.dfy.expect @@ -1,24 +1,24 @@ -MultiSets.dfy(159,3): Error BP5003: A postcondition might not hold on this return path. -MultiSets.dfy(158,15): Related location: This is the postcondition that might not hold. +MultiSets.dfy(159,2): Error BP5003: A postcondition might not hold on this return path. +MultiSets.dfy(158,14): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 -MultiSets.dfy(165,3): Error BP5003: A postcondition might not hold on this return path. -MultiSets.dfy(164,15): Related location: This is the postcondition that might not hold. +MultiSets.dfy(165,2): Error BP5003: A postcondition might not hold on this return path. +MultiSets.dfy(164,14): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 -MultiSets.dfy(178,11): Error: new number of occurrences might be negative +MultiSets.dfy(178,10): Error: new number of occurrences might be negative Execution trace: (0,0): anon0 (0,0): anon4_Then (0,0): anon3 -MultiSets.dfy(269,24): Error: assertion violation +MultiSets.dfy(269,23): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon11_Then (0,0): anon3 (0,0): anon12_Then (0,0): anon14_Else -MultiSets.dfy(292,16): Error: assertion violation +MultiSets.dfy(292,15): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon8_Then diff --git a/Test/dafny0/NatTypes.dfy.expect b/Test/dafny0/NatTypes.dfy.expect index 5af90253..2bc00e95 100644 --- a/Test/dafny0/NatTypes.dfy.expect +++ b/Test/dafny0/NatTypes.dfy.expect @@ -1,41 +1,41 @@ -NatTypes.dfy(35,12): Error: value assigned to a nat must be non-negative +NatTypes.dfy(35,11): Error: value assigned to a nat must be non-negative Execution trace: (0,0): anon0 NatTypes.dfy(23,5): anon10_LoopHead (0,0): anon10_LoopBody NatTypes.dfy(23,5): anon11_Else (0,0): anon12_Then -NatTypes.dfy(10,5): Error: value assigned to a nat must be non-negative +NatTypes.dfy(10,4): Error: value assigned to a nat must be non-negative Execution trace: (0,0): anon0 -NatTypes.dfy(43,14): Error: assertion violation +NatTypes.dfy(43,13): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon4_Then -NatTypes.dfy(45,14): Error: assertion violation +NatTypes.dfy(45,13): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon4_Then -NatTypes.dfy(62,16): Error: assertion violation +NatTypes.dfy(62,15): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Then -NatTypes.dfy(76,16): Error: assertion violation +NatTypes.dfy(76,15): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon5_Else (0,0): anon6_Then -NatTypes.dfy(94,22): Error: value assigned to a nat must be non-negative +NatTypes.dfy(94,21): Error: value assigned to a nat must be non-negative Execution trace: (0,0): anon0 (0,0): anon3_Then -NatTypes.dfy(109,45): Error: value assigned to a nat must be non-negative +NatTypes.dfy(109,44): Error: value assigned to a nat must be non-negative Execution trace: (0,0): anon0 (0,0): anon8_Else (0,0): anon9_Else (0,0): anon10_Then -NatTypes.dfy(132,35): Error: value assigned to a nat must be non-negative +NatTypes.dfy(132,34): Error: value assigned to a nat must be non-negative Execution trace: (0,0): anon0 (0,0): anon4_Then diff --git a/Test/dafny0/Newtypes.dfy.expect b/Test/dafny0/Newtypes.dfy.expect index 8e6ff4c5..425ee9a9 100644 --- a/Test/dafny0/Newtypes.dfy.expect +++ b/Test/dafny0/Newtypes.dfy.expect @@ -1,54 +1,54 @@ -Newtypes.dfy(74,11): Error: cannot find witness that shows type is inhabited (sorry, for now, only tried 0) +Newtypes.dfy(74,10): Error: cannot find witness that shows type is inhabited (sorry, for now, only tried 0) Execution trace: (0,0): anon0 -Newtypes.dfy(76,45): Error: possible division by zero +Newtypes.dfy(76,44): Error: possible division by zero Execution trace: (0,0): anon0 (0,0): anon3_Then -Newtypes.dfy(87,14): Error: result of operation might violate newtype constraint +Newtypes.dfy(87,13): Error: result of operation might violate newtype constraint Execution trace: (0,0): anon0 -Newtypes.dfy(95,12): Error: result of operation might violate newtype constraint +Newtypes.dfy(95,11): Error: result of operation might violate newtype constraint Execution trace: (0,0): anon0 (0,0): anon3_Then -Newtypes.dfy(97,14): Error: result of operation might violate newtype constraint +Newtypes.dfy(97,13): Error: result of operation might violate newtype constraint Execution trace: (0,0): anon0 (0,0): anon3_Else -Newtypes.dfy(104,16): Error: result of operation might violate newtype constraint +Newtypes.dfy(104,15): Error: result of operation might violate newtype constraint Execution trace: (0,0): anon0 -Newtypes.dfy(177,14): Error: result of operation might violate newtype constraint +Newtypes.dfy(177,13): Error: result of operation might violate newtype constraint Execution trace: (0,0): anon0 -Newtypes.dfy(193,64): Error: index 0 out of range +Newtypes.dfy(193,63): Error: index 0 out of range Execution trace: (0,0): anon0 (0,0): anon32_Then (0,0): anon33_Then (0,0): anon16 -Newtypes.dfy(194,67): Error: index 1 out of range +Newtypes.dfy(194,66): Error: index 1 out of range Execution trace: (0,0): anon0 (0,0): anon34_Then (0,0): anon35_Then (0,0): anon19 -Newtypes.dfy(222,16): Error: new number of occurrences might be negative +Newtypes.dfy(222,15): Error: new number of occurrences might be negative Execution trace: (0,0): anon0 (0,0): anon6_Then -Newtypes.dfy(225,40): Error: result of operation might violate newtype constraint +Newtypes.dfy(225,39): Error: result of operation might violate newtype constraint Execution trace: (0,0): anon0 (0,0): anon8_Then -Newtypes.dfy(237,19): Error: result of operation might violate newtype constraint +Newtypes.dfy(237,18): Error: result of operation might violate newtype constraint Execution trace: (0,0): anon0 Newtypes.dfy(236,5): anon9_LoopHead (0,0): anon9_LoopBody (0,0): anon10_Then -Newtypes.dfy(277,19): Error: result of operation might violate newtype constraint +Newtypes.dfy(277,18): Error: result of operation might violate newtype constraint Execution trace: (0,0): anon0 Newtypes.dfy(276,5): anon9_LoopHead diff --git a/Test/dafny0/OpaqueFunctions.dfy.expect b/Test/dafny0/OpaqueFunctions.dfy.expect index 2fb1701f..e9f6e60c 100644 --- a/Test/dafny0/OpaqueFunctions.dfy.expect +++ b/Test/dafny0/OpaqueFunctions.dfy.expect @@ -1,86 +1,86 @@ -OpaqueFunctions.dfy(27,16): Error: assertion violation +OpaqueFunctions.dfy(27,15): Error: assertion violation Execution trace: (0,0): anon0 -OpaqueFunctions.dfy(52,8): Error BP5002: A precondition for this call might not hold. -OpaqueFunctions.dfy(24,16): Related location: This is the precondition that might not hold. +OpaqueFunctions.dfy(52,7): Error BP5002: A precondition for this call might not hold. +OpaqueFunctions.dfy(24,15): Related location: This is the precondition that might not hold. Execution trace: (0,0): anon0 -OpaqueFunctions.dfy(58,20): Error: assertion violation +OpaqueFunctions.dfy(58,19): Error: assertion violation Execution trace: (0,0): anon0 -OpaqueFunctions.dfy(60,21): Error: assertion violation +OpaqueFunctions.dfy(60,20): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon5_Then -OpaqueFunctions.dfy(63,21): Error: assertion violation +OpaqueFunctions.dfy(63,20): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon6_Then -OpaqueFunctions.dfy(66,21): Error: assertion violation +OpaqueFunctions.dfy(66,20): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon6_Else -OpaqueFunctions.dfy(77,21): Error: assertion violation +OpaqueFunctions.dfy(77,20): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Then -OpaqueFunctions.dfy(79,10): Error BP5002: A precondition for this call might not hold. -OpaqueFunctions.dfy[A'](24,16): Related location: This is the precondition that might not hold. +OpaqueFunctions.dfy(79,9): Error BP5002: A precondition for this call might not hold. +OpaqueFunctions.dfy[A'](24,15): Related location: This is the precondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon3_Else -OpaqueFunctions.dfy(86,20): Error: assertion violation +OpaqueFunctions.dfy(86,19): Error: assertion violation Execution trace: (0,0): anon0 -OpaqueFunctions.dfy(88,21): Error: assertion violation +OpaqueFunctions.dfy(88,20): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon5_Then -OpaqueFunctions.dfy(91,21): Error: assertion violation +OpaqueFunctions.dfy(91,20): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon6_Then -OpaqueFunctions.dfy(94,21): Error: assertion violation +OpaqueFunctions.dfy(94,20): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon6_Else -OpaqueFunctions.dfy(105,21): Error: assertion violation +OpaqueFunctions.dfy(105,20): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Then -OpaqueFunctions.dfy(107,10): Error BP5002: A precondition for this call might not hold. -OpaqueFunctions.dfy[A'](24,16): Related location: This is the precondition that might not hold. +OpaqueFunctions.dfy(107,9): Error BP5002: A precondition for this call might not hold. +OpaqueFunctions.dfy[A'](24,15): Related location: This is the precondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon3_Else -OpaqueFunctions.dfy(114,20): Error: assertion violation +OpaqueFunctions.dfy(114,19): Error: assertion violation Execution trace: (0,0): anon0 -OpaqueFunctions.dfy(116,21): Error: assertion violation +OpaqueFunctions.dfy(116,20): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon5_Then -OpaqueFunctions.dfy(119,21): Error: assertion violation +OpaqueFunctions.dfy(119,20): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon6_Then -OpaqueFunctions.dfy(122,21): Error: assertion violation +OpaqueFunctions.dfy(122,20): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon6_Else -OpaqueFunctions.dfy(138,13): Error: assertion violation +OpaqueFunctions.dfy(138,12): Error: assertion violation Execution trace: (0,0): anon0 -OpaqueFunctions.dfy(202,12): Error: assertion violation +OpaqueFunctions.dfy(202,11): Error: assertion violation Execution trace: (0,0): anon0 -OpaqueFunctions.dfy(218,12): Error: assertion violation +OpaqueFunctions.dfy(218,11): Error: assertion violation Execution trace: (0,0): anon0 -OpaqueFunctions.dfy(170,16): Error: assertion violation +OpaqueFunctions.dfy(170,15): Error: assertion violation Execution trace: (0,0): anon0 -OpaqueFunctions.dfy(185,20): Error: assertion violation +OpaqueFunctions.dfy(185,19): Error: assertion violation Execution trace: (0,0): anon0 diff --git a/Test/dafny0/Parallel.dfy.expect b/Test/dafny0/Parallel.dfy.expect index db551bba..5d9b044f 100644 --- a/Test/dafny0/Parallel.dfy.expect +++ b/Test/dafny0/Parallel.dfy.expect @@ -1,9 +1,9 @@ -Parallel.dfy(297,22): Error: assertion violation +Parallel.dfy(297,21): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon4_Else -Parallel.dfy(34,10): Error BP5002: A precondition for this call might not hold. -Parallel.dfy(60,14): Related location: This is the precondition that might not hold. +Parallel.dfy(34,9): Error BP5002: A precondition for this call might not hold. +Parallel.dfy(60,13): Related location: This is the precondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon29_Else @@ -12,7 +12,7 @@ Execution trace: (0,0): anon34_Then (0,0): anon35_Then (0,0): anon14 -Parallel.dfy(38,5): Error: target object may be null +Parallel.dfy(38,4): Error: target object may be null Execution trace: (0,0): anon0 (0,0): anon29_Else @@ -22,7 +22,7 @@ Execution trace: (0,0): anon37_Then (0,0): anon38_Then (0,0): anon20 -Parallel.dfy(42,18): Error: possible violation of postcondition of forall statement +Parallel.dfy(42,17): Error: possible violation of postcondition of forall statement Execution trace: (0,0): anon0 (0,0): anon29_Else @@ -32,7 +32,7 @@ Execution trace: (0,0): anon39_Then (0,0): anon40_Then (0,0): anon26 -Parallel.dfy(47,19): Error: assertion violation +Parallel.dfy(47,18): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon29_Else @@ -41,24 +41,24 @@ Execution trace: (0,0): anon36_Else (0,0): anon39_Then (0,0): anon40_Then -Parallel.dfy(93,19): Error: assertion violation +Parallel.dfy(93,18): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon10_Else (0,0): anon11_Then -Parallel.dfy(99,20): Error: possible violation of postcondition of forall statement +Parallel.dfy(99,19): Error: possible violation of postcondition of forall statement Execution trace: (0,0): anon0 (0,0): anon10_Else (0,0): anon11_Then (0,0): anon12_Then -Parallel.dfy(122,12): Error: value assigned to a nat must be non-negative +Parallel.dfy(122,11): Error: value assigned to a nat must be non-negative Execution trace: (0,0): anon0 (0,0): anon6_Then (0,0): anon7_Then (0,0): anon3 -Parallel.dfy(185,12): Error: left-hand sides for different forall-statement bound variables may refer to the same location +Parallel.dfy(185,11): Error: left-hand sides for different forall-statement bound variables may refer to the same location Execution trace: (0,0): anon0 (0,0): anon19_Then diff --git a/Test/dafny0/ParseErrors.dfy.expect b/Test/dafny0/ParseErrors.dfy.expect index 30898479..660ed926 100644 --- a/Test/dafny0/ParseErrors.dfy.expect +++ b/Test/dafny0/ParseErrors.dfy.expect @@ -1,17 +1,17 @@ -ParseErrors.dfy(7,19): error: a chain cannot have more than one != operator -ParseErrors.dfy(9,37): error: this operator chain cannot continue with a descending operator -ParseErrors.dfy(10,38): error: this operator chain cannot continue with an ascending operator -ParseErrors.dfy(15,24): error: this operator chain cannot continue with a descending operator -ParseErrors.dfy(18,18): error: this operator cannot be part of a chain -ParseErrors.dfy(19,19): error: this operator cannot be part of a chain -ParseErrors.dfy(20,18): error: this operator cannot be part of a chain -ParseErrors.dfy(21,18): error: chaining not allowed from the previous operator -ParseErrors.dfy(28,19): error: chaining not allowed from the previous operator -ParseErrors.dfy(31,20): error: can only chain disjoint (!!) with itself. -ParseErrors.dfy(58,8): error: the main operator of a calculation must be transitive -ParseErrors.dfy(74,2): error: this operator cannot continue this calculation -ParseErrors.dfy(75,2): error: this operator cannot continue this calculation -ParseErrors.dfy(80,2): error: this operator cannot continue this calculation -ParseErrors.dfy(81,2): error: this operator cannot continue this calculation -ParseErrors.dfy(87,2): error: this operator cannot continue this calculation +ParseErrors.dfy(7,18): Error: a chain cannot have more than one != operator +ParseErrors.dfy(9,36): Error: this operator chain cannot continue with a descending operator +ParseErrors.dfy(10,37): Error: this operator chain cannot continue with an ascending operator +ParseErrors.dfy(15,23): Error: this operator chain cannot continue with a descending operator +ParseErrors.dfy(18,17): Error: this operator cannot be part of a chain +ParseErrors.dfy(19,18): Error: this operator cannot be part of a chain +ParseErrors.dfy(20,17): Error: this operator cannot be part of a chain +ParseErrors.dfy(21,17): Error: chaining not allowed from the previous operator +ParseErrors.dfy(28,18): Error: chaining not allowed from the previous operator +ParseErrors.dfy(31,19): Error: can only chain disjoint (!!) with itself. +ParseErrors.dfy(58,7): Error: the main operator of a calculation must be transitive +ParseErrors.dfy(74,1): Error: this operator cannot continue this calculation +ParseErrors.dfy(75,1): Error: this operator cannot continue this calculation +ParseErrors.dfy(80,1): Error: this operator cannot continue this calculation +ParseErrors.dfy(81,1): Error: this operator cannot continue this calculation +ParseErrors.dfy(87,1): Error: this operator cannot continue this calculation 16 parse errors detected in ParseErrors.dfy diff --git a/Test/dafny0/PredExpr.dfy.expect b/Test/dafny0/PredExpr.dfy.expect index 18d5d73f..80f311cb 100644 --- a/Test/dafny0/PredExpr.dfy.expect +++ b/Test/dafny0/PredExpr.dfy.expect @@ -1,16 +1,16 @@ -PredExpr.dfy(7,12): Error: assertion violation +PredExpr.dfy(7,11): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon4_Else -PredExpr.dfy(39,15): Error: value assigned to a nat must be non-negative +PredExpr.dfy(39,14): Error: value assigned to a nat must be non-negative Execution trace: (0,0): anon0 (0,0): anon7_Else (0,0): anon8_Else -PredExpr.dfy(52,17): Error: assertion violation +PredExpr.dfy(52,16): Error: assertion violation Execution trace: (0,0): anon0 -PredExpr.dfy(77,14): Error: assertion violation +PredExpr.dfy(77,13): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon8_Else diff --git a/Test/dafny0/Predicates.dfy.expect b/Test/dafny0/Predicates.dfy.expect index dac4eb3c..2d7ea6f1 100644 --- a/Test/dafny0/Predicates.dfy.expect +++ b/Test/dafny0/Predicates.dfy.expect @@ -1,26 +1,26 @@ -Predicates.dfy[B](21,5): Error BP5003: A postcondition might not hold on this return path. -Predicates.dfy[B](20,15): Related location: This is the postcondition that might not hold. -Predicates.dfy(31,9): Related location +Predicates.dfy[B](21,4): Error BP5003: A postcondition might not hold on this return path. +Predicates.dfy[B](20,14): Related location: This is the postcondition that might not hold. +Predicates.dfy(31,8): Related location Execution trace: (0,0): anon0 -Predicates.dfy(88,16): Error: assertion violation +Predicates.dfy(88,15): Error: assertion violation Execution trace: (0,0): anon0 -Predicates.dfy(92,14): Error: assertion violation +Predicates.dfy(92,13): Error: assertion violation Execution trace: (0,0): anon0 -Predicates.dfy[Tricky_Full](126,5): Error BP5003: A postcondition might not hold on this return path. -Predicates.dfy[Tricky_Full](125,15): Related location: This is the postcondition that might not hold. -Predicates.dfy(136,7): Related location -Predicates.dfy[Tricky_Full](116,9): Related location +Predicates.dfy[Tricky_Full](126,4): Error BP5003: A postcondition might not hold on this return path. +Predicates.dfy[Tricky_Full](125,14): Related location: This is the postcondition that might not hold. +Predicates.dfy(136,6): Related location +Predicates.dfy[Tricky_Full](116,8): Related location Execution trace: (0,0): anon0 -Predicates.dfy(164,5): Error BP5003: A postcondition might not hold on this return path. -Predicates.dfy(163,15): Related location: This is the postcondition that might not hold. +Predicates.dfy(164,4): Error BP5003: A postcondition might not hold on this return path. +Predicates.dfy(163,14): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 -Predicates.dfy[Q1](154,5): Error BP5003: A postcondition might not hold on this return path. -Predicates.dfy[Q1](153,15): Related location: This is the postcondition that might not hold. +Predicates.dfy[Q1](154,4): Error BP5003: A postcondition might not hold on this return path. +Predicates.dfy[Q1](153,14): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 diff --git a/Test/dafny0/Protected.dfy.expect b/Test/dafny0/Protected.dfy.expect index d50f2dd5..6796e847 100644 --- a/Test/dafny0/Protected.dfy.expect +++ b/Test/dafny0/Protected.dfy.expect @@ -1,20 +1,20 @@ -Protected.dfy(17,20): Error: assertion violation +Protected.dfy(17,19): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon9_Then -Protected.dfy(31,18): Error: assertion violation +Protected.dfy(31,17): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon12_Then (0,0): anon6 (0,0): anon13_Else -Protected.dfy(35,16): Error: assertion violation +Protected.dfy(35,15): Error: assertion violation Execution trace: (0,0): anon0 -Protected.dfy(48,20): Error: assertion violation +Protected.dfy(48,19): Error: assertion violation Execution trace: (0,0): anon0 -Protected.dfy(55,20): Error: assertion violation +Protected.dfy(55,19): Error: assertion violation Execution trace: (0,0): anon0 diff --git a/Test/dafny0/RankNeg.dfy.expect b/Test/dafny0/RankNeg.dfy.expect index b2686b43..33cd4f1e 100644 --- a/Test/dafny0/RankNeg.dfy.expect +++ b/Test/dafny0/RankNeg.dfy.expect @@ -1,19 +1,19 @@ -RankNeg.dfy(10,26): Error: cannot prove termination; try supplying a decreases clause +RankNeg.dfy(10,25): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon7_Else (0,0): anon8_Then -RankNeg.dfy(15,28): Error: cannot prove termination; try supplying a decreases clause +RankNeg.dfy(15,27): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon7_Else (0,0): anon8_Then -RankNeg.dfy(22,31): Error: cannot prove termination; try supplying a decreases clause +RankNeg.dfy(22,30): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon7_Else (0,0): anon8_Then -RankNeg.dfy(32,25): Error: cannot prove termination; try supplying a decreases clause +RankNeg.dfy(32,24): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon7_Else diff --git a/Test/dafny0/Reads.dfy.expect b/Test/dafny0/Reads.dfy.expect index 1199797f..0ef90aec 100644 --- a/Test/dafny0/Reads.dfy.expect +++ b/Test/dafny0/Reads.dfy.expect @@ -1,32 +1,32 @@ -Reads.dfy(133,11): Error: insufficient reads clause to read field +Reads.dfy(133,10): Error: insufficient reads clause to read field Execution trace: (0,0): anon0 -Reads.dfy(9,30): Error: insufficient reads clause to read field +Reads.dfy(9,29): Error: insufficient reads clause to read field Execution trace: (0,0): anon0 -Reads.dfy(18,30): Error: insufficient reads clause to read field +Reads.dfy(18,29): Error: insufficient reads clause to read field Execution trace: (0,0): anon0 -Reads.dfy(28,50): Error: insufficient reads clause to read field +Reads.dfy(28,49): Error: insufficient reads clause to read field Execution trace: (0,0): anon0 -Reads.dfy(37,43): Error: insufficient reads clause to read field +Reads.dfy(37,42): Error: insufficient reads clause to read field Execution trace: (0,0): anon0 (0,0): anon7_Then (0,0): anon4 -Reads.dfy(51,30): Error: insufficient reads clause to read field +Reads.dfy(51,29): Error: insufficient reads clause to read field Execution trace: (0,0): anon0 (0,0): anon10_Then (0,0): anon4 -Reads.dfy(117,36): Error: insufficient reads clause to invoke function +Reads.dfy(117,35): Error: insufficient reads clause to invoke function Execution trace: (0,0): anon0 -Reads.dfy(117,36): Error: possible violation of function precondition +Reads.dfy(117,35): Error: possible violation of function precondition Execution trace: (0,0): anon0 -Reads.dfy(120,38): Error: insufficient reads clause to invoke function +Reads.dfy(120,37): Error: insufficient reads clause to invoke function Execution trace: (0,0): anon0 diff --git a/Test/dafny0/RealCompare.dfy.expect b/Test/dafny0/RealCompare.dfy.expect index 5b25fa25..48524bdf 100644 --- a/Test/dafny0/RealCompare.dfy.expect +++ b/Test/dafny0/RealCompare.dfy.expect @@ -1,19 +1,19 @@ -RealCompare.dfy(35,6): Error: failure to decrease termination measure +RealCompare.dfy(35,5): Error: failure to decrease termination measure Execution trace: (0,0): anon0 (0,0): anon3_Then -RealCompare.dfy(50,4): Error: decreases expression must be bounded below by 0.0 -RealCompare.dfy(48,13): Related location +RealCompare.dfy(50,3): Error: decreases expression must be bounded below by 0.0 +RealCompare.dfy(48,12): Related location Execution trace: (0,0): anon0 -RealCompare.dfy(141,12): Error: assertion violation +RealCompare.dfy(141,11): Error: assertion violation Execution trace: (0,0): anon0 RealCompare.dfy(133,3): anon7_LoopHead (0,0): anon7_LoopBody RealCompare.dfy(133,3): anon8_Else (0,0): anon9_Then -RealCompare.dfy(156,12): Error: assertion violation +RealCompare.dfy(156,11): Error: assertion violation Execution trace: (0,0): anon0 RealCompare.dfy(147,3): anon9_LoopHead diff --git a/Test/dafny0/RealTypes.dfy.expect b/Test/dafny0/RealTypes.dfy.expect index 0d132948..0fce4634 100644 --- a/Test/dafny0/RealTypes.dfy.expect +++ b/Test/dafny0/RealTypes.dfy.expect @@ -1,22 +1,22 @@ -RealTypes.dfy(12,16): Error: the real-based number must be an integer (if you want truncation, apply .Trunc to the real-based number) +RealTypes.dfy(12,15): Error: the real-based number must be an integer (if you want truncation, apply .Trunc to the real-based number) Execution trace: (0,0): anon0 (0,0): anon6_Then -RealTypes.dfy(14,28): Error: assertion violation +RealTypes.dfy(14,27): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon7_Then -RealTypes.dfy(21,12): Error: possible division by zero +RealTypes.dfy(21,11): Error: possible division by zero Execution trace: (0,0): anon0 RealTypes.dfy(20,23): anon3_Else (0,0): anon2 -RealTypes.dfy(21,20): Error: assertion violation +RealTypes.dfy(21,19): Error: assertion violation Execution trace: (0,0): anon0 RealTypes.dfy(20,23): anon3_Else (0,0): anon2 -RealTypes.dfy(29,12): Error: assertion violation +RealTypes.dfy(29,11): Error: assertion violation Execution trace: (0,0): anon0 diff --git a/Test/dafny0/Refinement.dfy.expect b/Test/dafny0/Refinement.dfy.expect index d03b9412..339c86b4 100644 --- a/Test/dafny0/Refinement.dfy.expect +++ b/Test/dafny0/Refinement.dfy.expect @@ -1,40 +1,40 @@ -Refinement.dfy(15,5): Error BP5003: A postcondition might not hold on this return path. -Refinement.dfy(14,17): Related location: This is the postcondition that might not hold. +Refinement.dfy(15,4): Error BP5003: A postcondition might not hold on this return path. +Refinement.dfy(14,16): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 -Refinement.dfy[B](15,5): Error BP5003: A postcondition might not hold on this return path. -Refinement.dfy(33,20): Related location: This is the postcondition that might not hold. +Refinement.dfy[B](15,4): Error BP5003: A postcondition might not hold on this return path. +Refinement.dfy(33,19): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 -Refinement.dfy(69,16): Error: assertion violation +Refinement.dfy(69,15): Error: assertion violation Execution trace: (0,0): anon0 -Refinement.dfy(80,17): Error: assertion violation +Refinement.dfy(80,16): Error: assertion violation Execution trace: (0,0): anon0 -Refinement.dfy(99,12): Error BP5003: A postcondition might not hold on this return path. -Refinement.dfy(78,15): Related location: This is the postcondition that might not hold. +Refinement.dfy(99,11): Error BP5003: A postcondition might not hold on this return path. +Refinement.dfy(78,14): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon4_Else -Refinement.dfy(102,3): Error BP5003: A postcondition might not hold on this return path. -Refinement.dfy(83,15): Related location: This is the postcondition that might not hold. +Refinement.dfy(102,2): Error BP5003: A postcondition might not hold on this return path. +Refinement.dfy(83,14): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 -Refinement.dfy(189,5): Error BP5003: A postcondition might not hold on this return path. -Refinement.dfy[IncorrectConcrete](121,15): Related location: This is the postcondition that might not hold. -Refinement.dfy(186,9): Related location +Refinement.dfy(189,4): Error BP5003: A postcondition might not hold on this return path. +Refinement.dfy[IncorrectConcrete](121,14): Related location: This is the postcondition that might not hold. +Refinement.dfy(186,8): Related location Execution trace: (0,0): anon0 -Refinement.dfy(193,5): Error BP5003: A postcondition might not hold on this return path. -Refinement.dfy[IncorrectConcrete](129,15): Related location: This is the postcondition that might not hold. -Refinement.dfy(186,9): Related location +Refinement.dfy(193,4): Error BP5003: A postcondition might not hold on this return path. +Refinement.dfy[IncorrectConcrete](129,14): Related location: This is the postcondition that might not hold. +Refinement.dfy(186,8): Related location Execution trace: (0,0): anon0 (0,0): anon4_Then (0,0): anon3 -Refinement.dfy(199,7): Error: assertion violation -Refinement.dfy[IncorrectConcrete](137,24): Related location +Refinement.dfy(199,6): Error: assertion violation +Refinement.dfy[IncorrectConcrete](137,23): Related location Execution trace: (0,0): anon0 diff --git a/Test/dafny0/Skeletons.dfy.expect b/Test/dafny0/Skeletons.dfy.expect index 43b372c3..4b48bad0 100644 --- a/Test/dafny0/Skeletons.dfy.expect +++ b/Test/dafny0/Skeletons.dfy.expect @@ -1,5 +1,5 @@ -Skeletons.dfy(45,3): Error BP5003: A postcondition might not hold on this return path. -Skeletons.dfy(44,15): Related location: This is the postcondition that might not hold. +Skeletons.dfy(45,2): Error BP5003: A postcondition might not hold on this return path. +Skeletons.dfy(44,14): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 Skeletons.dfy[C0](32,5): anon11_LoopHead diff --git a/Test/dafny0/SmallTests.dfy.expect b/Test/dafny0/SmallTests.dfy.expect index b0605d8e..eee0d4f1 100644 --- a/Test/dafny0/SmallTests.dfy.expect +++ b/Test/dafny0/SmallTests.dfy.expect @@ -1,41 +1,41 @@ -SmallTests.dfy(34,11): Error: index out of range +SmallTests.dfy(34,10): Error: index out of range Execution trace: (0,0): anon0 -SmallTests.dfy(65,36): Error: possible division by zero +SmallTests.dfy(65,35): Error: possible division by zero Execution trace: (0,0): anon0 (0,0): anon13_Then -SmallTests.dfy(66,51): Error: possible division by zero +SmallTests.dfy(66,50): Error: possible division by zero Execution trace: (0,0): anon0 (0,0): anon13_Else (0,0): anon14_Else -SmallTests.dfy(67,22): Error: target object may be null +SmallTests.dfy(67,21): Error: target object may be null Execution trace: (0,0): anon0 (0,0): anon13_Then (0,0): anon14_Then (0,0): anon15_Then -SmallTests.dfy(86,24): Error: target object may be null +SmallTests.dfy(86,23): Error: target object may be null Execution trace: (0,0): anon0 SmallTests.dfy(85,5): anon8_LoopHead (0,0): anon8_LoopBody (0,0): anon9_Then -SmallTests.dfy(120,6): Error: call may violate context's modifies clause +SmallTests.dfy(120,5): Error: call may violate context's modifies clause Execution trace: (0,0): anon0 (0,0): anon4_Else (0,0): anon3 -SmallTests.dfy(133,10): Error: call may violate context's modifies clause +SmallTests.dfy(133,9): Error: call may violate context's modifies clause Execution trace: (0,0): anon0 (0,0): anon3_Then -SmallTests.dfy(135,10): Error: call may violate context's modifies clause +SmallTests.dfy(135,9): Error: call may violate context's modifies clause Execution trace: (0,0): anon0 (0,0): anon3_Else -SmallTests.dfy(175,9): Error: assignment may update an object field not in the enclosing context's modifies clause +SmallTests.dfy(175,8): Error: assignment may update an object field not in the enclosing context's modifies clause Execution trace: (0,0): anon0 (0,0): anon22_Else @@ -44,23 +44,23 @@ Execution trace: (0,0): anon28_Then (0,0): anon29_Then (0,0): anon19 -SmallTests.dfy(199,14): Error: assertion violation +SmallTests.dfy(199,13): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon9_Then -SmallTests.dfy(206,14): Error: assertion violation +SmallTests.dfy(206,13): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon9_Else (0,0): anon3 (0,0): anon10_Then -SmallTests.dfy(208,14): Error: assertion violation +SmallTests.dfy(208,13): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon9_Else (0,0): anon3 (0,0): anon10_Else -SmallTests.dfy(213,14): Error: assertion violation +SmallTests.dfy(213,13): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon9_Else @@ -68,7 +68,7 @@ Execution trace: (0,0): anon10_Then (0,0): anon6 (0,0): anon11_Then -SmallTests.dfy(215,14): Error: assertion violation +SmallTests.dfy(215,13): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon9_Else @@ -76,37 +76,37 @@ Execution trace: (0,0): anon10_Then (0,0): anon6 (0,0): anon11_Else -SmallTests.dfy(261,24): Error BP5002: A precondition for this call might not hold. -SmallTests.dfy(239,30): Related location: This is the precondition that might not hold. +SmallTests.dfy(261,23): Error BP5002: A precondition for this call might not hold. +SmallTests.dfy(239,29): Related location: This is the precondition that might not hold. Execution trace: (0,0): anon0 SmallTests.dfy(256,19): anon3_Else (0,0): anon2 -SmallTests.dfy(367,12): Error: assertion violation +SmallTests.dfy(367,11): Error: assertion violation Execution trace: (0,0): anon0 -SmallTests.dfy(377,12): Error: assertion violation +SmallTests.dfy(377,11): Error: assertion violation Execution trace: (0,0): anon0 -SmallTests.dfy(387,6): Error: cannot prove termination; try supplying a decreases clause +SmallTests.dfy(387,5): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon4_Else -SmallTests.dfy(692,14): Error: assertion violation +SmallTests.dfy(692,13): Error: assertion violation Execution trace: (0,0): anon0 SmallTests.dfy(689,5): anon7_LoopHead (0,0): anon7_LoopBody SmallTests.dfy(689,5): anon8_Else (0,0): anon9_Then -SmallTests.dfy(713,14): Error: assertion violation +SmallTests.dfy(713,13): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon7_Then (0,0): anon8_Then (0,0): anon3 -SmallTests.dfy(296,3): Error BP5003: A postcondition might not hold on this return path. -SmallTests.dfy(290,11): Related location: This is the postcondition that might not hold. +SmallTests.dfy(296,2): Error BP5003: A postcondition might not hold on this return path. +SmallTests.dfy(290,10): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon18_Else @@ -114,29 +114,29 @@ Execution trace: (0,0): anon24_Then (0,0): anon15 (0,0): anon25_Else -SmallTests.dfy(338,12): Error: assertion violation +SmallTests.dfy(338,11): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon8_Then (0,0): anon7 -SmallTests.dfy(345,10): Error: assertion violation +SmallTests.dfy(345,9): Error: assertion violation Execution trace: (0,0): anon0 -SmallTests.dfy(355,4): Error: cannot prove termination; try supplying a decreases clause +SmallTests.dfy(355,3): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon4_Else -SmallTests.dfy(399,10): Error BP5003: A postcondition might not hold on this return path. -SmallTests.dfy(402,41): Related location: This is the postcondition that might not hold. +SmallTests.dfy(399,9): Error BP5003: A postcondition might not hold on this return path. +SmallTests.dfy(402,40): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon7_Else -SmallTests.dfy(563,12): Error: assertion violation +SmallTests.dfy(563,11): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Then (0,0): anon2 -SmallTests.dfy(577,20): Error: left-hand sides 0 and 1 may refer to the same location +SmallTests.dfy(577,19): Error: left-hand sides 0 and 1 may refer to the same location Execution trace: (0,0): anon0 (0,0): anon27_Then @@ -148,7 +148,7 @@ Execution trace: (0,0): anon31_Then (0,0): anon32_Then (0,0): anon12 -SmallTests.dfy(579,15): Error: left-hand sides 1 and 2 may refer to the same location +SmallTests.dfy(579,14): Error: left-hand sides 1 and 2 may refer to the same location Execution trace: (0,0): anon0 (0,0): anon27_Then @@ -163,16 +163,16 @@ Execution trace: (0,0): anon37_Then (0,0): anon22 (0,0): anon38_Then -SmallTests.dfy(586,25): Error: target object may be null +SmallTests.dfy(586,24): Error: target object may be null Execution trace: (0,0): anon0 -SmallTests.dfy(599,10): Error: assertion violation +SmallTests.dfy(599,9): Error: assertion violation Execution trace: (0,0): anon0 -SmallTests.dfy(623,5): Error: cannot establish the existence of LHS values that satisfy the such-that predicate +SmallTests.dfy(623,4): Error: cannot establish the existence of LHS values that satisfy the such-that predicate Execution trace: (0,0): anon0 -SmallTests.dfy(646,23): Error: assertion violation +SmallTests.dfy(646,22): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon8_Then @@ -180,17 +180,17 @@ Execution trace: (0,0): anon4 (0,0): anon10_Then (0,0): anon7 -SmallTests.dfy(660,10): Error: cannot establish the existence of LHS values that satisfy the such-that predicate +SmallTests.dfy(660,9): Error: cannot establish the existence of LHS values that satisfy the such-that predicate Execution trace: (0,0): anon0 (0,0): anon5_Then (0,0): anon6_Then (0,0): anon3 -SmallTests.dfy(662,10): Error: cannot establish the existence of LHS values that satisfy the such-that predicate +SmallTests.dfy(662,9): Error: cannot establish the existence of LHS values that satisfy the such-that predicate Execution trace: (0,0): anon0 (0,0): anon5_Else -SmallTests.dfy(675,9): Error: cannot establish the existence of LHS values that satisfy the such-that predicate +SmallTests.dfy(675,8): Error: cannot establish the existence of LHS values that satisfy the such-that predicate Execution trace: (0,0): anon0 diff --git a/Test/dafny0/SplitExpr.dfy.expect b/Test/dafny0/SplitExpr.dfy.expect index b7ef524f..29dd6eda 100644 --- a/Test/dafny0/SplitExpr.dfy.expect +++ b/Test/dafny0/SplitExpr.dfy.expect @@ -1,5 +1,5 @@ -SplitExpr.dfy(92,15): Error: loop invariant violation -SplitExpr.dfy(86,44): Related location +SplitExpr.dfy(92,14): Error: loop invariant violation +SplitExpr.dfy(86,43): Related location Execution trace: SplitExpr.dfy(91,3): anon7_LoopHead diff --git a/Test/dafny0/StatementExpressions.dfy.expect b/Test/dafny0/StatementExpressions.dfy.expect index 9de6a5d1..936a3954 100644 --- a/Test/dafny0/StatementExpressions.dfy.expect +++ b/Test/dafny0/StatementExpressions.dfy.expect @@ -1,22 +1,22 @@ -StatementExpressions.dfy(55,12): Error: cannot prove termination; try supplying a decreases clause +StatementExpressions.dfy(55,11): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon6_Then (0,0): anon8_Then -StatementExpressions.dfy(59,14): Error: assertion violation +StatementExpressions.dfy(59,13): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon6_Then StatementExpressions.dfy(53,7): anon8_Else -StatementExpressions.dfy(77,6): Error: possible division by zero +StatementExpressions.dfy(77,5): Error: possible division by zero Execution trace: (0,0): anon0 (0,0): anon4_Else -StatementExpressions.dfy(88,5): Error: value assigned to a nat must be non-negative +StatementExpressions.dfy(88,4): Error: value assigned to a nat must be non-negative Execution trace: (0,0): anon0 (0,0): anon4_Else -StatementExpressions.dfy(98,18): Error: cannot prove termination; try supplying a decreases clause +StatementExpressions.dfy(98,17): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon6_Then diff --git a/Test/dafny0/Superposition.dfy.expect b/Test/dafny0/Superposition.dfy.expect index 6497c712..04ec2f7d 100644 --- a/Test/dafny0/Superposition.dfy.expect +++ b/Test/dafny0/Superposition.dfy.expect @@ -10,16 +10,16 @@ Verifying CheckWellformed$$_0_M0.C.P ... Verifying CheckWellformed$$_0_M0.C.Q ... [5 proof obligations] error -Superposition.dfy(27,15): Error BP5003: A postcondition might not hold on this return path. -Superposition.dfy(28,26): Related location: This is the postcondition that might not hold. +Superposition.dfy(27,14): Error BP5003: A postcondition might not hold on this return path. +Superposition.dfy(28,25): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon7_Else Verifying CheckWellformed$$_0_M0.C.R ... [5 proof obligations] error -Superposition.dfy(33,15): Error BP5003: A postcondition might not hold on this return path. -Superposition.dfy(34,26): Related location: This is the postcondition that might not hold. +Superposition.dfy(33,14): Error BP5003: A postcondition might not hold on this return path. +Superposition.dfy(34,25): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon7_Else @@ -32,8 +32,8 @@ Verifying Impl$$_1_M1.C.M ... Verifying CheckWellformed$$_1_M1.C.P ... [2 proof obligations] error -Superposition.dfy(50,25): Error BP5003: A postcondition might not hold on this return path. -Superposition.dfy[M1](22,26): Related location: This is the postcondition that might not hold. +Superposition.dfy(50,24): Error BP5003: A postcondition might not hold on this return path. +Superposition.dfy[M1](22,25): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon9_Else diff --git a/Test/dafny0/Termination.dfy.expect b/Test/dafny0/Termination.dfy.expect index 77a9e70e..69cb360d 100644 --- a/Test/dafny0/Termination.dfy.expect +++ b/Test/dafny0/Termination.dfy.expect @@ -1,20 +1,20 @@ -Termination.dfy[TerminationRefinement1](441,6): Error: failure to decrease termination measure +Termination.dfy[TerminationRefinement1](441,5): Error: failure to decrease termination measure Execution trace: (0,0): anon0 -Termination.dfy(361,47): Error: failure to decrease termination measure +Termination.dfy(361,46): Error: failure to decrease termination measure Execution trace: (0,0): anon0 (0,0): anon9_Else (0,0): anon10_Then (0,0): anon11_Else -Termination.dfy(108,3): Error: cannot prove termination; try supplying a decreases clause for the loop +Termination.dfy(108,2): Error: cannot prove termination; try supplying a decreases clause for the loop Execution trace: (0,0): anon0 Termination.dfy(108,3): anon6_LoopHead (0,0): anon6_LoopBody Termination.dfy(108,3): anon7_Else Termination.dfy(108,3): anon8_Else -Termination.dfy(116,3): Error: cannot prove termination; try supplying a decreases clause for the loop +Termination.dfy(116,2): Error: cannot prove termination; try supplying a decreases clause for the loop Execution trace: (0,0): anon0 Termination.dfy(116,3): anon8_LoopHead @@ -23,7 +23,7 @@ Execution trace: (0,0): anon10_Then (0,0): anon5 Termination.dfy(116,3): anon11_Else -Termination.dfy(125,3): Error: decreases expression might not decrease +Termination.dfy(125,2): Error: decreases expression might not decrease Execution trace: (0,0): anon0 Termination.dfy(125,3): anon8_LoopHead @@ -32,7 +32,7 @@ Execution trace: (0,0): anon10_Then (0,0): anon5 Termination.dfy(125,3): anon11_Else -Termination.dfy(126,17): Error: decreases expression must be bounded below by 0 at end of loop iteration +Termination.dfy(126,16): Error: decreases expression must be bounded below by 0 at end of loop iteration Execution trace: (0,0): anon0 Termination.dfy(125,3): anon8_LoopHead @@ -41,13 +41,13 @@ Execution trace: (0,0): anon10_Then (0,0): anon5 Termination.dfy(125,3): anon11_Else -Termination.dfy(255,35): Error: cannot prove termination; try supplying a decreases clause +Termination.dfy(255,34): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon8_Else (0,0): anon9_Else (0,0): anon10_Then -Termination.dfy(296,3): Error: decreases expression might not decrease +Termination.dfy(296,2): Error: decreases expression might not decrease Execution trace: Termination.dfy(296,3): anon9_LoopHead (0,0): anon9_LoopBody diff --git a/Test/dafny0/Trait/TraitUsingParentMembers.dfy.expect b/Test/dafny0/Trait/TraitUsingParentMembers.dfy.expect index 9960c1d9..1517dee4 100644 --- a/Test/dafny0/Trait/TraitUsingParentMembers.dfy.expect +++ b/Test/dafny0/Trait/TraitUsingParentMembers.dfy.expect @@ -1,4 +1,4 @@ -TraitUsingParentMembers.dfy(10,8): Error: assignment may update an array element not in the enclosing context's modifies clause +TraitUsingParentMembers.dfy(10,7): Error: assignment may update an array element not in the enclosing context's modifies clause Execution trace: (0,0): anon0 (0,0): anon5_Then diff --git a/Test/dafny0/Trait/TraitsDecreases.dfy.expect b/Test/dafny0/Trait/TraitsDecreases.dfy.expect index 2607a0c6..7d646bd1 100644 --- a/Test/dafny0/Trait/TraitsDecreases.dfy.expect +++ b/Test/dafny0/Trait/TraitsDecreases.dfy.expect @@ -1,34 +1,34 @@ -TraitsDecreases.dfy(117,15): Error: predicate's decreases clause must be below or equal to that in the trait +TraitsDecreases.dfy(117,14): Error: predicate's decreases clause must be below or equal to that in the trait Execution trace: (0,0): anon0 -TraitsDecreases.dfy(124,15): Error: predicate's decreases clause must be below or equal to that in the trait +TraitsDecreases.dfy(124,14): Error: predicate's decreases clause must be below or equal to that in the trait Execution trace: (0,0): anon0 -TraitsDecreases.dfy(131,15): Error: predicate's decreases clause must be below or equal to that in the trait +TraitsDecreases.dfy(131,14): Error: predicate's decreases clause must be below or equal to that in the trait Execution trace: (0,0): anon0 -TraitsDecreases.dfy(138,15): Error: predicate's decreases clause must be below or equal to that in the trait +TraitsDecreases.dfy(138,14): Error: predicate's decreases clause must be below or equal to that in the trait Execution trace: (0,0): anon0 -TraitsDecreases.dfy(145,15): Error: predicate's decreases clause must be below or equal to that in the trait +TraitsDecreases.dfy(145,14): Error: predicate's decreases clause must be below or equal to that in the trait Execution trace: (0,0): anon0 -TraitsDecreases.dfy(152,12): Error: method's decreases clause must be below or equal to that in the trait +TraitsDecreases.dfy(152,11): Error: method's decreases clause must be below or equal to that in the trait Execution trace: (0,0): anon0 -TraitsDecreases.dfy(57,10): Error: method's decreases clause must be below or equal to that in the trait +TraitsDecreases.dfy(57,9): Error: method's decreases clause must be below or equal to that in the trait Execution trace: (0,0): anon0 -TraitsDecreases.dfy(69,10): Error: method's decreases clause must be below or equal to that in the trait +TraitsDecreases.dfy(69,9): Error: method's decreases clause must be below or equal to that in the trait Execution trace: (0,0): anon0 -TraitsDecreases.dfy(72,10): Error: method's decreases clause must be below or equal to that in the trait +TraitsDecreases.dfy(72,9): Error: method's decreases clause must be below or equal to that in the trait Execution trace: (0,0): anon0 -TraitsDecreases.dfy(78,10): Error: method's decreases clause must be below or equal to that in the trait +TraitsDecreases.dfy(78,9): Error: method's decreases clause must be below or equal to that in the trait Execution trace: (0,0): anon0 -TraitsDecreases.dfy(88,10): Error: method's decreases clause must be below or equal to that in the trait +TraitsDecreases.dfy(88,9): Error: method's decreases clause must be below or equal to that in the trait Execution trace: (0,0): anon0 diff --git a/Test/dafny0/Tuples.dfy.expect b/Test/dafny0/Tuples.dfy.expect index 13c706d3..9b5f3a83 100644 --- a/Test/dafny0/Tuples.dfy.expect +++ b/Test/dafny0/Tuples.dfy.expect @@ -1,7 +1,7 @@ -Tuples.dfy(22,19): Error: assertion violation +Tuples.dfy(22,18): Error: assertion violation Execution trace: (0,0): anon0 -Tuples.dfy(24,21): Error: possible division by zero +Tuples.dfy(24,20): Error: possible division by zero Execution trace: (0,0): anon0 diff --git a/Test/dafny0/TypeAntecedents.dfy.expect b/Test/dafny0/TypeAntecedents.dfy.expect index d6eb08e4..2e2f606d 100644 --- a/Test/dafny0/TypeAntecedents.dfy.expect +++ b/Test/dafny0/TypeAntecedents.dfy.expect @@ -1,8 +1,8 @@ -TypeAntecedents.dfy(35,13): Error: assertion violation +TypeAntecedents.dfy(35,12): Error: assertion violation Execution trace: (0,0): anon0 -TypeAntecedents.dfy(58,1): Error BP5003: A postcondition might not hold on this return path. -TypeAntecedents.dfy(57,15): Related location: This is the postcondition that might not hold. +TypeAntecedents.dfy(58,0): Error BP5003: A postcondition might not hold on this return path. +TypeAntecedents.dfy(57,14): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon25_Then @@ -16,7 +16,7 @@ Execution trace: (0,0): anon34_Then (0,0): anon35_Then (0,0): anon24 -TypeAntecedents.dfy(66,16): Error: assertion violation +TypeAntecedents.dfy(66,15): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon25_Else diff --git a/Test/dafny0/TypeParameters.dfy.expect b/Test/dafny0/TypeParameters.dfy.expect index 3d00e89a..aca0694d 100644 --- a/Test/dafny0/TypeParameters.dfy.expect +++ b/Test/dafny0/TypeParameters.dfy.expect @@ -1,43 +1,43 @@ -TypeParameters.dfy(47,22): Error: assertion violation +TypeParameters.dfy(47,21): Error: assertion violation Execution trace: (0,0): anon0 -TypeParameters.dfy(69,27): Error: assertion violation +TypeParameters.dfy(69,26): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Then (0,0): anon2 -TypeParameters.dfy(156,12): Error: assertion violation -TypeParameters.dfy(156,28): Related location +TypeParameters.dfy(156,11): Error: assertion violation +TypeParameters.dfy(156,27): Related location Execution trace: (0,0): anon0 (0,0): anon20_Then TypeParameters.dfy(156,32): anon21_Else (0,0): anon5 -TypeParameters.dfy(158,12): Error: assertion violation -TypeParameters.dfy(158,33): Related location +TypeParameters.dfy(158,11): Error: assertion violation +TypeParameters.dfy(158,32): Related location Execution trace: (0,0): anon0 (0,0): anon23_Then TypeParameters.dfy(158,37): anon24_Else (0,0): anon11 -TypeParameters.dfy(160,12): Error: assertion violation -TypeParameters.dfy(160,20): Related location +TypeParameters.dfy(160,11): Error: assertion violation +TypeParameters.dfy(160,19): Related location Execution trace: (0,0): anon0 (0,0): anon25_Then -TypeParameters.dfy(162,12): Error: assertion violation -TypeParameters.dfy(147,5): Related location -TypeParameters.dfy(162,21): Related location +TypeParameters.dfy(162,11): Error: assertion violation +TypeParameters.dfy(147,4): Related location +TypeParameters.dfy(162,20): Related location Execution trace: (0,0): anon0 (0,0): anon26_Then -TypeParameters.dfy(164,12): Error: assertion violation -TypeParameters.dfy(149,8): Related location +TypeParameters.dfy(164,11): Error: assertion violation +TypeParameters.dfy(149,7): Related location Execution trace: (0,0): anon0 (0,0): anon27_Then -TypeParameters.dfy(178,15): Error BP5005: This loop invariant might not be maintained by the loop. -TypeParameters.dfy(178,38): Related location +TypeParameters.dfy(178,14): Error BP5005: This loop invariant might not be maintained by the loop. +TypeParameters.dfy(178,37): Related location Execution trace: (0,0): anon0 TypeParameters.dfy(171,3): anon16_LoopHead diff --git a/Test/dafny0/columns.dfy b/Test/dafny0/columns.dfy new file mode 100644 index 00000000..e36142be --- /dev/null +++ b/Test/dafny0/columns.dfy @@ -0,0 +1,10 @@ +// RUN: %dafny "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// Dafny counts columns from 0, but Boogie from one, so for a while there were small bugs with that. + +static method A(x:int) requires x > 0 { // error os 's' + assert (forall y :: y > x ==> y > 100); // error on '(' + assert x != 1; // error on '!' + assert x in {}; // error on 'i' +} diff --git a/Test/dafny0/columns.dfy.expect b/Test/dafny0/columns.dfy.expect new file mode 100644 index 00000000..295ca351 --- /dev/null +++ b/Test/dafny0/columns.dfy.expect @@ -0,0 +1,18 @@ +columns.dfy(6,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +columns.dfy(7,9): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon3_Then + (0,0): anon2 +columns.dfy(8,11): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon3_Then + (0,0): anon2 +columns.dfy(9,11): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon3_Then + (0,0): anon2 + +Dafny program verifier finished with 1 verified, 3 errors diff --git a/Test/dafny0/snapshots/Snapshots0.run.dfy.expect b/Test/dafny0/snapshots/Snapshots0.run.dfy.expect index 96c280d9..d32cd9bb 100644 --- a/Test/dafny0/snapshots/Snapshots0.run.dfy.expect +++ b/Test/dafny0/snapshots/Snapshots0.run.dfy.expect @@ -18,7 +18,7 @@ Processing command (at ) a##cached##0 := a##cached##0 && ##ext >>> AssumeNegationOfAssumptionVariable Processing command (at Snapshots0.v1.dfy(4,10)) assert Lit(false); >>> MarkAsPartiallyVerified -Snapshots0.v1.dfy(4,10): Error: assertion violation +Snapshots0.v1.dfy(4,9): Error: assertion violation Execution trace: (0,0): anon0 diff --git a/Test/dafny0/snapshots/Snapshots1.run.dfy.expect b/Test/dafny0/snapshots/Snapshots1.run.dfy.expect index 878f9905..6d5e43f8 100644 --- a/Test/dafny0/snapshots/Snapshots1.run.dfy.expect +++ b/Test/dafny0/snapshots/Snapshots1.run.dfy.expect @@ -14,7 +14,7 @@ Processing command (at Snapshots1.v1.dfy(3,4)) assert (forall $o: ref, $f >>> MarkAsFullyVerified Processing command (at Snapshots1.v1.dfy(4,10)) assert Lit(false); >>> DoNothingToAssert -Snapshots1.v1.dfy(4,10): Error: assertion violation +Snapshots1.v1.dfy(4,9): Error: assertion violation Execution trace: (0,0): anon0 diff --git a/Test/dafny0/snapshots/Snapshots2.run.dfy.expect b/Test/dafny0/snapshots/Snapshots2.run.dfy.expect index a6a9bc4c..ee2ceecd 100644 --- a/Test/dafny0/snapshots/Snapshots2.run.dfy.expect +++ b/Test/dafny0/snapshots/Snapshots2.run.dfy.expect @@ -26,7 +26,7 @@ Processing command (at Snapshots2.v1.dfy(3,4)) assert (forall $o: ref, $f >>> MarkAsFullyVerified Processing command (at Snapshots2.v1.dfy(4,10)) assert Lit(false); >>> DoNothingToAssert -Snapshots2.v1.dfy(4,10): Error: assertion violation +Snapshots2.v1.dfy(4,9): Error: assertion violation Execution trace: (0,0): anon0 Processing command (at Snapshots2.v1.dfy(11,11)) assert true; diff --git a/Test/dafny0/snapshots/Snapshots3.run.dfy.expect b/Test/dafny0/snapshots/Snapshots3.run.dfy.expect index 07e2d063..accacd90 100644 --- a/Test/dafny0/snapshots/Snapshots3.run.dfy.expect +++ b/Test/dafny0/snapshots/Snapshots3.run.dfy.expect @@ -1,6 +1,6 @@ Processing command (at Snapshots3.v0.dfy(9,14)) assert Lit(0 != 0); >>> DoNothingToAssert -Snapshots3.v0.dfy(9,14): Error: assertion violation +Snapshots3.v0.dfy(9,13): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Else @@ -10,7 +10,7 @@ Processing command (at Snapshots3.v1.dfy(5,12)) assert Lit(true); >>> DoNothingToAssert Processing command (at Snapshots3.v1.dfy(9,14)) assert Lit(0 != 0); >>> RecycleError -Snapshots3.v0.dfy(9,14): Error: assertion violation +Snapshots3.v0.dfy(9,13): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Else diff --git a/Test/dafny0/snapshots/Snapshots4.run.dfy.expect b/Test/dafny0/snapshots/Snapshots4.run.dfy.expect index fdc97775..d56eb9d0 100644 --- a/Test/dafny0/snapshots/Snapshots4.run.dfy.expect +++ b/Test/dafny0/snapshots/Snapshots4.run.dfy.expect @@ -8,11 +8,11 @@ Processing command (at Snapshots4.v1.dfy(9,14)) assert LitInt(0) == LitInt(0); >>> MarkAsFullyVerified Processing command (at Snapshots4.v1.dfy(10,14)) assert Lit(2 != 2); >>> DoNothingToAssert -Snapshots4.v1.dfy(5,14): Error: assertion violation +Snapshots4.v1.dfy(5,13): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Then -Snapshots4.v1.dfy(10,14): Error: assertion violation +Snapshots4.v1.dfy(10,13): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon3_Else diff --git a/Test/dafny0/snapshots/Snapshots6.run.dfy.expect b/Test/dafny0/snapshots/Snapshots6.run.dfy.expect index af440327..bef5a87d 100644 --- a/Test/dafny0/snapshots/Snapshots6.run.dfy.expect +++ b/Test/dafny0/snapshots/Snapshots6.run.dfy.expect @@ -4,7 +4,7 @@ Processing command (at Snapshots6.v0.dfy(20,14)) assert Lit(false); Dafny program verifier finished with 4 verified, 0 errors Processing command (at Snapshots6.v1.dfy(20,14)) assert Lit(false); >>> DoNothingToAssert -Snapshots6.v1.dfy(20,14): Error: assertion violation +Snapshots6.v1.dfy(20,13): Error: assertion violation Execution trace: (0,0): anon0 diff --git a/Test/dafny0/snapshots/Snapshots7.run.dfy.expect b/Test/dafny0/snapshots/Snapshots7.run.dfy.expect index 7c073a9a..b90a6034 100644 --- a/Test/dafny0/snapshots/Snapshots7.run.dfy.expect +++ b/Test/dafny0/snapshots/Snapshots7.run.dfy.expect @@ -24,7 +24,7 @@ Processing command (at ) a##cached##0 := a##cached##0 && ##ext >>> AssumeNegationOfAssumptionVariable Processing command (at Snapshots7.v1.dfy(19,14)) assert Lit(false); >>> MarkAsPartiallyVerified -Snapshots7.v1.dfy(19,14): Error: assertion violation +Snapshots7.v1.dfy(19,13): Error: assertion violation Execution trace: (0,0): anon0 diff --git a/Test/dafny1/MoreInduction.dfy.expect b/Test/dafny1/MoreInduction.dfy.expect index c8785e56..5de0ace6 100644 --- a/Test/dafny1/MoreInduction.dfy.expect +++ b/Test/dafny1/MoreInduction.dfy.expect @@ -1,17 +1,17 @@ -MoreInduction.dfy(78,1): Error BP5003: A postcondition might not hold on this return path. -MoreInduction.dfy(77,11): Related location: This is the postcondition that might not hold. +MoreInduction.dfy(78,0): Error BP5003: A postcondition might not hold on this return path. +MoreInduction.dfy(77,10): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 -MoreInduction.dfy(83,1): Error BP5003: A postcondition might not hold on this return path. -MoreInduction.dfy(82,21): Related location: This is the postcondition that might not hold. +MoreInduction.dfy(83,0): Error BP5003: A postcondition might not hold on this return path. +MoreInduction.dfy(82,20): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 -MoreInduction.dfy(88,1): Error BP5003: A postcondition might not hold on this return path. -MoreInduction.dfy(87,11): Related location: This is the postcondition that might not hold. +MoreInduction.dfy(88,0): Error BP5003: A postcondition might not hold on this return path. +MoreInduction.dfy(87,10): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 -MoreInduction.dfy(93,1): Error BP5003: A postcondition might not hold on this return path. -MoreInduction.dfy(92,22): Related location: This is the postcondition that might not hold. +MoreInduction.dfy(93,0): Error BP5003: A postcondition might not hold on this return path. +MoreInduction.dfy(92,21): Related location: This is the postcondition that might not hold. Execution trace: (0,0): anon0 diff --git a/Test/dafny2/SnapshotableTrees.dfy.expect b/Test/dafny2/SnapshotableTrees.dfy.expect index 849b9e38..808fe0f9 100644 --- a/Test/dafny2/SnapshotableTrees.dfy.expect +++ b/Test/dafny2/SnapshotableTrees.dfy.expect @@ -1,5 +1,5 @@ -SnapshotableTrees.dfy(68,24): Error BP5002: A precondition for this call might not hold. -SnapshotableTrees.dfy(648,16): Related location: This is the precondition that might not hold. +SnapshotableTrees.dfy(68,23): Error BP5002: A precondition for this call might not hold. +SnapshotableTrees.dfy(648,15): Related location: This is the precondition that might not hold. Execution trace: (0,0): anon0 (0,0): anon3_Then diff --git a/Test/dafny4/BinarySearch.dfy.expect b/Test/dafny4/BinarySearch.dfy.expect index 944f677a..a9f834b7 100644 --- a/Test/dafny4/BinarySearch.dfy.expect +++ b/Test/dafny4/BinarySearch.dfy.expect @@ -1,4 +1,4 @@ -BinarySearch.dfy(44,20): Error: result of operation might violate newtype constraint +BinarySearch.dfy(44,19): Error: result of operation might violate newtype constraint Execution trace: (0,0): anon0 BinarySearch.dfy(40,3): anon18_LoopHead diff --git a/Test/dafny4/Bug73.dfy.expect b/Test/dafny4/Bug73.dfy.expect index 6cf5c156..8beaa18c 100644 --- a/Test/dafny4/Bug73.dfy.expect +++ b/Test/dafny4/Bug73.dfy.expect @@ -1,9 +1,9 @@ -Bug73.dfy(7,14): Error: assertion violation +Bug73.dfy(7,13): Error: assertion violation Execution trace: (0,0): anon0 Bug73.dfy(7,19): anon3_Else (0,0): anon2 -Bug73.dfy(13,14): Error: assertion violation +Bug73.dfy(13,13): Error: assertion violation Execution trace: (0,0): anon0 Bug73.dfy(13,20): anon3_Else diff --git a/Test/dafny4/SoftwareFoundations-Basics.dfy.expect b/Test/dafny4/SoftwareFoundations-Basics.dfy.expect index 0f9eb8d0..f07b068f 100644 --- a/Test/dafny4/SoftwareFoundations-Basics.dfy.expect +++ b/Test/dafny4/SoftwareFoundations-Basics.dfy.expect @@ -1,4 +1,4 @@ -SoftwareFoundations-Basics.dfy(41,12): Error: assertion violation +SoftwareFoundations-Basics.dfy(41,11): Error: assertion violation Execution trace: (0,0): anon0 diff --git a/Test/hofs/Apply.dfy.expect b/Test/hofs/Apply.dfy.expect index 77d34c4c..0a923143 100644 --- a/Test/hofs/Apply.dfy.expect +++ b/Test/hofs/Apply.dfy.expect @@ -1,4 +1,4 @@ -Apply.dfy(27,16): Error: assertion violation +Apply.dfy(27,15): Error: assertion violation Execution trace: (0,0): anon0 Apply.dfy(26,27): anon15_Else diff --git a/Test/hofs/Classes.dfy.expect b/Test/hofs/Classes.dfy.expect index 1c9e31f0..a5b33522 100644 --- a/Test/hofs/Classes.dfy.expect +++ b/Test/hofs/Classes.dfy.expect @@ -1,7 +1,7 @@ -Classes.dfy(64,12): Error: assertion violation +Classes.dfy(64,11): Error: assertion violation Execution trace: (0,0): anon0 -Classes.dfy(40,6): Error: possible violation of function precondition +Classes.dfy(40,5): Error: possible violation of function precondition Execution trace: (0,0): anon0 (0,0): anon7_Else diff --git a/Test/hofs/Field.dfy.expect b/Test/hofs/Field.dfy.expect index 9f6998f5..0859d83c 100644 --- a/Test/hofs/Field.dfy.expect +++ b/Test/hofs/Field.dfy.expect @@ -1,13 +1,13 @@ -Field.dfy(12,12): Error: possible violation of function precondition +Field.dfy(12,11): Error: possible violation of function precondition Execution trace: (0,0): anon0 -Field.dfy(12,15): Error: assertion violation +Field.dfy(12,14): Error: assertion violation Execution trace: (0,0): anon0 -Field.dfy(21,12): Error: possible violation of function precondition +Field.dfy(21,11): Error: possible violation of function precondition Execution trace: (0,0): anon0 -Field.dfy(21,14): Error: assertion violation +Field.dfy(21,13): Error: assertion violation Execution trace: (0,0): anon0 diff --git a/Test/hofs/FnRef.dfy.expect b/Test/hofs/FnRef.dfy.expect index 0f6f2aa9..e665c830 100644 --- a/Test/hofs/FnRef.dfy.expect +++ b/Test/hofs/FnRef.dfy.expect @@ -1,19 +1,19 @@ -FnRef.dfy(17,45): Error: possible violation of function precondition +FnRef.dfy(17,44): Error: possible violation of function precondition Execution trace: (0,0): anon0 FnRef.dfy(15,12): anon5_Else (0,0): anon6_Then -FnRef.dfy(32,8): Error: possible violation of function precondition +FnRef.dfy(32,7): Error: possible violation of function precondition Execution trace: (0,0): anon0 FnRef.dfy(26,12): anon9_Else FnRef.dfy(28,8): anon10_Else -FnRef.dfy(46,12): Error: assertion violation +FnRef.dfy(46,11): Error: assertion violation Execution trace: (0,0): anon0 FnRef.dfy(43,12): anon7_Else (0,0): anon9_Then -FnRef.dfy(65,14): Error: assertion violation +FnRef.dfy(65,13): Error: assertion violation Execution trace: (0,0): anon0 FnRef.dfy(56,12): anon8_Else diff --git a/Test/hofs/Frame.dfy.expect b/Test/hofs/Frame.dfy.expect index 0ee2eadb..9964deb4 100644 --- a/Test/hofs/Frame.dfy.expect +++ b/Test/hofs/Frame.dfy.expect @@ -1,35 +1,35 @@ -Frame.dfy(23,16): Error: assertion violation +Frame.dfy(23,15): Error: assertion violation Execution trace: (0,0): anon0 Frame.dfy(19,12): anon5_Else (0,0): anon6_Then -Frame.dfy(37,14): Error: assertion violation +Frame.dfy(37,13): Error: assertion violation Execution trace: (0,0): anon0 Frame.dfy(33,12): anon3_Else -Frame.dfy(63,23): Error: assertion violation +Frame.dfy(63,22): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon13_Then Frame.dfy(55,12): anon14_Else (0,0): anon15_Then (0,0): anon5 -Frame.dfy(66,19): Error: insufficient reads clause to read array element +Frame.dfy(66,18): Error: insufficient reads clause to read array element Execution trace: (0,0): anon0 (0,0): anon16_Then (0,0): anon17_Then -Frame.dfy(68,28): Error: insufficient reads clause to read array element +Frame.dfy(68,27): Error: insufficient reads clause to read array element Execution trace: (0,0): anon0 (0,0): anon16_Else (0,0): anon18_Then -Frame.dfy(123,14): Error: possible violation of function precondition +Frame.dfy(123,13): Error: possible violation of function precondition Execution trace: (0,0): anon0 (0,0): anon5_Then (0,0): anon6_Else -Frame.dfy(123,19): Error: assertion violation +Frame.dfy(123,18): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon5_Then diff --git a/Test/hofs/Lambda.dfy.expect b/Test/hofs/Lambda.dfy.expect index 4fe8275f..ab57fbe0 100644 --- a/Test/hofs/Lambda.dfy.expect +++ b/Test/hofs/Lambda.dfy.expect @@ -1,4 +1,4 @@ -Lambda.dfy(24,12): Error: assertion violation +Lambda.dfy(24,11): Error: assertion violation Execution trace: (0,0): anon0 Lambda.dfy(6,24): anon31_Else diff --git a/Test/hofs/LambdaParsefail.dfy.expect b/Test/hofs/LambdaParsefail.dfy.expect index 11deb9b0..a72fc978 100644 --- a/Test/hofs/LambdaParsefail.dfy.expect +++ b/Test/hofs/LambdaParsefail.dfy.expect @@ -1,6 +1,6 @@ -LambdaParsefail.dfy(5,19): error: this symbol not expected in VarDeclStatement -LambdaParsefail.dfy(6,19): error: this symbol not expected in VarDeclStatement -LambdaParsefail.dfy(7,21): error: this symbol not expected in VarDeclStatement -LambdaParsefail.dfy(8,15): error: cannot declare identifier beginning with underscore -LambdaParsefail.dfy(9,17): error: this symbol not expected in VarDeclStatement +LambdaParsefail.dfy(5,18): Error: this symbol not expected in VarDeclStatement +LambdaParsefail.dfy(6,18): Error: this symbol not expected in VarDeclStatement +LambdaParsefail.dfy(7,20): Error: this symbol not expected in VarDeclStatement +LambdaParsefail.dfy(8,14): Error: cannot declare identifier beginning with underscore +LambdaParsefail.dfy(9,16): Error: this symbol not expected in VarDeclStatement 5 parse errors detected in LambdaParsefail.dfy diff --git a/Test/hofs/LambdaParsefail2.dfy.expect b/Test/hofs/LambdaParsefail2.dfy.expect index 0c9ecb83..1a6a65dc 100644 --- a/Test/hofs/LambdaParsefail2.dfy.expect +++ b/Test/hofs/LambdaParsefail2.dfy.expect @@ -1,2 +1,2 @@ -LambdaParsefail2.dfy(6,39): error: invalid LambdaArrow +LambdaParsefail2.dfy(6,38): Error: invalid LambdaArrow 1 parse errors detected in LambdaParsefail2.dfy diff --git a/Test/hofs/Naked.dfy.expect b/Test/hofs/Naked.dfy.expect index 514952a1..9794478d 100644 --- a/Test/hofs/Naked.dfy.expect +++ b/Test/hofs/Naked.dfy.expect @@ -1,45 +1,45 @@ -Naked.dfy(9,16): Error: cannot use naked function in recursive setting. Possible solution: eta expansion. +Naked.dfy(9,15): Error: cannot use naked function in recursive setting. Possible solution: eta expansion. Execution trace: (0,0): anon0 (0,0): anon9_Else (0,0): anon10_Else (0,0): anon11_Then -Naked.dfy(12,8): Error: cannot use naked function in recursive setting. Possible solution: eta expansion. +Naked.dfy(12,7): Error: cannot use naked function in recursive setting. Possible solution: eta expansion. Execution trace: (0,0): anon0 (0,0): anon9_Else (0,0): anon10_Else (0,0): anon11_Else -Naked.dfy(17,53): Error: cannot use naked function in recursive setting. Possible solution: eta expansion. +Naked.dfy(17,52): Error: cannot use naked function in recursive setting. Possible solution: eta expansion. Execution trace: (0,0): anon0 (0,0): anon7_Else (0,0): anon8_Else -Naked.dfy(22,13): Error: cannot use naked function in recursive setting. Possible solution: eta expansion. +Naked.dfy(22,12): Error: cannot use naked function in recursive setting. Possible solution: eta expansion. Execution trace: (0,0): anon0 -Naked.dfy(26,14): Error: cannot use naked function in recursive setting. Possible solution: eta expansion. +Naked.dfy(26,13): Error: cannot use naked function in recursive setting. Possible solution: eta expansion. Execution trace: (0,0): anon0 -Naked.dfy(30,45): Error: possible violation of function precondition -Naked.dfy(32,14): Related location +Naked.dfy(30,44): Error: possible violation of function precondition +Naked.dfy(32,13): Related location Execution trace: (0,0): anon0 (0,0): anon4_Else -Naked.dfy(32,15): Error: cannot use naked function in recursive setting. Possible solution: eta expansion. +Naked.dfy(32,14): Error: cannot use naked function in recursive setting. Possible solution: eta expansion. Execution trace: (0,0): anon0 -Naked.dfy(38,9): Error: cannot use naked function in recursive setting. Possible solution: eta expansion. +Naked.dfy(38,8): Error: cannot use naked function in recursive setting. Possible solution: eta expansion. Execution trace: (0,0): anon0 -Naked.dfy(42,10): Error: cannot use naked function in recursive setting. Possible solution: eta expansion. +Naked.dfy(42,9): Error: cannot use naked function in recursive setting. Possible solution: eta expansion. Execution trace: (0,0): anon0 -Naked.dfy(45,30): Error: cannot prove termination; try supplying a decreases clause +Naked.dfy(45,29): Error: cannot prove termination; try supplying a decreases clause Execution trace: (0,0): anon0 (0,0): anon4_Else -Naked.dfy(48,11): Error: cannot use naked function in recursive setting. Possible solution: eta expansion. +Naked.dfy(48,10): Error: cannot use naked function in recursive setting. Possible solution: eta expansion. Execution trace: (0,0): anon0 diff --git a/Test/hofs/OneShot.dfy.expect b/Test/hofs/OneShot.dfy.expect index 91b931b8..0b4a2bb8 100644 --- a/Test/hofs/OneShot.dfy.expect +++ b/Test/hofs/OneShot.dfy.expect @@ -1,16 +1,16 @@ -OneShot.dfy(20,12): Error: possible violation of function precondition +OneShot.dfy(20,11): Error: possible violation of function precondition Execution trace: (0,0): anon0 (0,0): anon5_Then OneShot.dfy(13,8): anon5_Else (0,0): anon6_Then -OneShot.dfy(22,12): Error: assertion violation +OneShot.dfy(22,11): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon5_Then OneShot.dfy(13,8): anon5_Else (0,0): anon6_Else -OneShot.dfy(22,13): Error: possible violation of function precondition +OneShot.dfy(22,12): Error: possible violation of function precondition Execution trace: (0,0): anon0 (0,0): anon5_Then diff --git a/Test/hofs/ReadsReads.dfy.expect b/Test/hofs/ReadsReads.dfy.expect index cd013630..0a374c44 100644 --- a/Test/hofs/ReadsReads.dfy.expect +++ b/Test/hofs/ReadsReads.dfy.expect @@ -1,33 +1,33 @@ -ReadsReads.dfy(31,7): Error: insufficient reads clause to invoke function +ReadsReads.dfy(31,6): Error: insufficient reads clause to invoke function Execution trace: (0,0): anon0 (0,0): anon4_Else -ReadsReads.dfy(36,5): Error: insufficient reads clause to invoke function +ReadsReads.dfy(36,4): Error: insufficient reads clause to invoke function Execution trace: (0,0): anon0 (0,0): anon4_Else -ReadsReads.dfy(47,12): Error: insufficient reads clause to invoke function +ReadsReads.dfy(47,11): Error: insufficient reads clause to invoke function Execution trace: (0,0): anon0 (0,0): anon4_Else -ReadsReads.dfy(58,7): Error: insufficient reads clause to invoke function +ReadsReads.dfy(58,6): Error: insufficient reads clause to invoke function Execution trace: (0,0): anon0 (0,0): anon4_Else -ReadsReads.dfy(87,50): Error: assertion violation +ReadsReads.dfy(87,49): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon16_Then -ReadsReads.dfy(89,29): Error: assertion violation +ReadsReads.dfy(89,28): Error: assertion violation Execution trace: (0,0): anon0 (0,0): anon18_Then -ReadsReads.dfy(99,37): Error: assertion violation +ReadsReads.dfy(99,36): Error: assertion violation Execution trace: (0,0): anon0 ReadsReads.dfy(96,14): anon15_Else (0,0): anon19_Then -ReadsReads.dfy(101,29): Error: assertion violation +ReadsReads.dfy(101,28): Error: assertion violation Execution trace: (0,0): anon0 ReadsReads.dfy(96,14): anon15_Else diff --git a/Test/hofs/Simple.dfy.expect b/Test/hofs/Simple.dfy.expect index e2f16ef3..c0123c80 100644 --- a/Test/hofs/Simple.dfy.expect +++ b/Test/hofs/Simple.dfy.expect @@ -1,26 +1,26 @@ -Simple.dfy(14,10): Error: possible division by zero +Simple.dfy(14,9): Error: possible division by zero Execution trace: (0,0): anon0 (0,0): anon6_Else (0,0): anon7_Then -Simple.dfy(27,10): Error: possible division by zero +Simple.dfy(27,9): Error: possible division by zero Execution trace: (0,0): anon0 (0,0): anon6_Else (0,0): anon7_Then -Simple.dfy(37,9): Error: possible violation of function precondition +Simple.dfy(37,8): Error: possible violation of function precondition Execution trace: (0,0): anon0 Simple.dfy(35,13): anon5_Else -Simple.dfy(49,9): Error: possible violation of function precondition +Simple.dfy(49,8): Error: possible violation of function precondition Execution trace: (0,0): anon0 (0,0): anon3_Then (0,0): anon2 -Simple.dfy(61,10): Error: possible violation of function precondition +Simple.dfy(61,9): Error: possible violation of function precondition Execution trace: (0,0): anon0 -Simple.dfy(73,10): Error: assertion violation +Simple.dfy(73,9): Error: assertion violation Execution trace: (0,0): anon0 Simple.dfy(72,38): anon5_Else diff --git a/Test/hofs/Twice.dfy.expect b/Test/hofs/Twice.dfy.expect index 2476b945..0ce2450c 100644 --- a/Test/hofs/Twice.dfy.expect +++ b/Test/hofs/Twice.dfy.expect @@ -1,8 +1,8 @@ -Twice.dfy(27,22): Error: assertion violation +Twice.dfy(27,21): Error: assertion violation Execution trace: (0,0): anon0 Twice.dfy(23,12): anon3_Else -Twice.dfy(35,32): Error: possible violation of function precondition +Twice.dfy(35,31): Error: possible violation of function precondition Execution trace: (0,0): anon0 (0,0): anon10_Else diff --git a/Test/irondafny0/inheritreqs0.dfy.expect b/Test/irondafny0/inheritreqs0.dfy.expect index eaadc85a..44e33bc0 100644 --- a/Test/irondafny0/inheritreqs0.dfy.expect +++ b/Test/irondafny0/inheritreqs0.dfy.expect @@ -1,5 +1,5 @@ -inheritreqs0.dfy(19,14): Error BP5002: A precondition for this call might not hold. -inheritreqs0.dfy[Impl](6,18): Related location: This is the precondition that might not hold. +inheritreqs0.dfy(19,13): Error BP5002: A precondition for this call might not hold. +inheritreqs0.dfy[Impl](6,17): Related location: This is the precondition that might not hold. Execution trace: (0,0): anon0 diff --git a/Test/irondafny0/inheritreqs1.dfy.expect b/Test/irondafny0/inheritreqs1.dfy.expect index 27c76fee..a07d179d 100644 --- a/Test/irondafny0/inheritreqs1.dfy.expect +++ b/Test/irondafny0/inheritreqs1.dfy.expect @@ -1,5 +1,5 @@ -inheritreqs1.dfy(20,14): Error BP5002: A precondition for this call might not hold. -inheritreqs1.dfy(15,18): Related location: This is the precondition that might not hold. +inheritreqs1.dfy(20,13): Error BP5002: A precondition for this call might not hold. +inheritreqs1.dfy(15,17): Related location: This is the precondition that might not hold. Execution trace: (0,0): anon0 diff --git a/Test/irondafny0/xrefine1.dfy.expect b/Test/irondafny0/xrefine1.dfy.expect index ae844fc8..ec946cda 100644 --- a/Test/irondafny0/xrefine1.dfy.expect +++ b/Test/irondafny0/xrefine1.dfy.expect @@ -1,5 +1,5 @@ -xrefine1.dfy(71,13): Error BP5002: A precondition for this call might not hold. -xrefine1.dfy[MainImpl](49,29): Related location: This is the precondition that might not hold. +xrefine1.dfy(71,12): Error BP5002: A precondition for this call might not hold. +xrefine1.dfy[MainImpl](49,28): Related location: This is the precondition that might not hold. Execution trace: (0,0): anon0 -- cgit v1.2.3 From c2092abbb945b55c87976d6d57ec2f728306af89 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 23 Jul 2015 11:59:33 -0700 Subject: Let runTests.py generate expect files That is, missing expect files now raise a warning, not an error. --- Test/runTests.bat | 2 +- Test/runTests.py | 39 ++++++++++++++++++++++----------------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/Test/runTests.bat b/Test/runTests.bat index ced3bd27..4856faf8 100644 --- a/Test/runTests.bat +++ b/Test/runTests.bat @@ -1,2 +1,2 @@ @REM runTests.bat -f "/dprelude PRELUDE_FILE" -r REPORT_NAME INPUT_FILES -C:/Python34/python.exe runTests.py --flags "/useBaseNameForFileName /compile:1 /nologo" --difftool "C:/Program Files (x86)/Meld/Meld.exe" %* +C:/Python34/python.exe runTests.py --difftool "C:/Program Files (x86)/Meld/Meld.exe" %* diff --git a/Test/runTests.py b/Test/runTests.py index 02a6f039..34a9c4de 100644 --- a/Test/runTests.py +++ b/Test/runTests.py @@ -138,8 +138,12 @@ class Test: @staticmethod def read_normalize(path): - with open(path, mode="rb") as reader: - return reader.read().replace(b'\r\n', b'\n').replace(b'\r', b'\n') + try: + with open(path, mode="rb") as reader: + return reader.read().replace(b'\r\n', b'\n').replace(b'\r', b'\n') + except FileNotFoundError: + debug(Debug.WARNING, "{} not found".format(path)) + return "" @staticmethod def build_report(tests, name): @@ -232,12 +236,12 @@ class Test: def report(self, tid, running, alltests): running = [alltests[rid].fname for rid in running] - # running = ", ".join(running if len(running) <= 2 else (running[:2] + ["..."])) - running = "; oldest thread: {}".format(wrap_color(running[0], Colors.DIM)) if running else "" + running = "; oldest thread: {}".format(running[0]) if running else "" - fstring = "[{:5.2f}s] {} ({} of {}{})" + fstring = "[{:5.2f}s] {} ({}{})" + progress = "{} of {}".format(tid, len(alltests)) message = fstring.format(self.duration, wrap_color(self.dfy, Colors.BRIGHT), - tid, len(alltests), running) + wrap_color(progress, Colors.BRIGHT), running) debug(Debug.INFO, message, headers=self.status) @@ -295,14 +299,14 @@ def setup_parser(): help="When comparing, include all timings.") parser.add_argument('--diff', '-d', action='store_const', const=True, default=False, - help="Don't run tests; show differences for one file.") + help="Don't run tests; show differences between outputs and .expect files, optionally overwritting .expect files.") + + parser.add_argument('--accept', '-a', action='store_const', const=True, default=False, + help="Don't run tests; copy outputs to .expect files.") parser.add_argument('--open', '-o', action='store_const', const=True, default=False, help="Don't run tests; open one file.") - parser.add_argument('--accept', '-a', action='store_const', const=True, default=False, - help="Used in conjuction with --diff, accept the new output.") - parser.add_argument('--difftool', action='store', type=str, default="diff", help='Diff program. Default: diff.') @@ -346,10 +350,7 @@ def read_one_test(name, fname, compiler_cmds, timeout): else: break if cmds: - if os.path.exists(Test.source_to_expect_path(source_path)): - yield Test(name, source_path, cmds, timeout, cid) - else: - debug(Debug.DEBUG, "Test file {} has no .expect".format(fname)) + yield Test(name, source_path, cmds, timeout, cid) else: debug(Debug.INFO, "Test file {} has no RUN specification".format(fname)) @@ -436,12 +437,16 @@ def diff(paths, accept, difftool): debug(Debug.ERROR, "Not found: {}".format(path)) else: test = Test(None, path, [], None) + if not accept: call([difftool, test.expect_path, test.temp_output_path]) + accept = input("Accept this change? (y/N) ") == "y" - if accept or input("Accept this change? (y/N) ") == "y": - debug(Debug.INFO, path, "Accepted") + if accept: + debug(Debug.INFO, path, "accepted.") shutil.copy(test.temp_output_path, test.expect_path) + else: + debug(Debug.INFO, path, "not accepted.") def compare_results(globs, time_all): from glob import glob @@ -486,7 +491,7 @@ def main(): if os.name != 'nt' and os.environ.get("TERM") == "cygwin": debug(Debug.WARNING, "If you run into issues, try using Windows' Python instead of Cygwin's") - if args.diff: + if args.diff or args.accept: diff(args.paths, args.accept, args.difftool) elif args.open: os.startfile(args.paths[0]) -- cgit v1.2.3 From 31005fdf54359aad3f33a54228d61d82e10bc679 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 23 Jul 2015 11:35:01 -0700 Subject: Fix a bug with includes and /useBaseNameForFileName If /useBasenameForFilename is specified, tokens just contain file names, which is not enough to locate included files. Solution based on Rustan's advice. --- Source/Dafny/Dafny.atg | 16 ++++++++-------- Source/Dafny/Parser.cs | 16 ++++++++-------- Source/Dafny/Scanner.cs | 9 +++++++-- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/Source/Dafny/Dafny.atg b/Source/Dafny/Dafny.atg index c4f038d4..8a3bdb0e 100644 --- a/Source/Dafny/Dafny.atg +++ b/Source/Dafny/Dafny.atg @@ -41,11 +41,11 @@ public static int Parse (string/*!*/ filename, ModuleDecl module, BuiltIns built string s; if (filename == "stdin.dfy") { s = Microsoft.Boogie.ParserHelper.Fill(System.Console.In, new List()); - return Parse(s, filename, module, builtIns, errors, verifyThisFile); + return Parse(s, filename, filename, module, builtIns, errors, verifyThisFile); } else { using (System.IO.StreamReader reader = new System.IO.StreamReader(filename)) { s = Microsoft.Boogie.ParserHelper.Fill(reader, new List()); - return Parse(s, DafnyOptions.Clo.UseBaseNameForFileName ? Path.GetFileName(filename) : filename, module, builtIns, errors, verifyThisFile); + return Parse(s, filename, DafnyOptions.Clo.UseBaseNameForFileName ? Path.GetFileName(filename) : filename, module, builtIns, errors, verifyThisFile); } } } @@ -55,12 +55,12 @@ public static int Parse (string/*!*/ filename, ModuleDecl module, BuiltIns built /// Returns the number of parsing errors encountered. /// Note: first initialize the Scanner. ///
-public static int Parse (string/*!*/ s, string/*!*/ filename, ModuleDecl module, BuiltIns builtIns, bool verifyThisFile=true) { +public static int Parse (string/*!*/ s, string/*!*/ fullFilename, string/*!*/ filename, ModuleDecl module, BuiltIns builtIns, bool verifyThisFile=true) { Contract.Requires(s != null); Contract.Requires(filename != null); Contract.Requires(module != null); Errors errors = new Errors(); - return Parse(s, filename, module, builtIns, errors, verifyThisFile); + return Parse(s, fullFilename, filename, module, builtIns, errors, verifyThisFile); } /// /// Parses top-level things (modules, classes, datatypes, class members) @@ -68,15 +68,15 @@ public static int Parse (string/*!*/ s, string/*!*/ filename, ModuleDecl module, /// Returns the number of parsing errors encountered. /// Note: first initialize the Scanner with the given Errors sink. /// -public static int Parse (string/*!*/ s, string/*!*/ filename, ModuleDecl module, BuiltIns builtIns, - Errors/*!*/ errors, bool verifyThisFile=true) { +public static int Parse (string/*!*/ s, string/*!*/ fullFilename, string/*!*/ filename, ModuleDecl module, + BuiltIns builtIns, Errors/*!*/ errors, bool verifyThisFile=true) { Contract.Requires(s != null); Contract.Requires(filename != null); Contract.Requires(module != null); Contract.Requires(errors != null); byte[]/*!*/ buffer = cce.NonNull( UTF8Encoding.Default.GetBytes(s)); MemoryStream ms = new MemoryStream(buffer,false); - Scanner scanner = new Scanner(ms, errors, filename); + Scanner scanner = new Scanner(ms, errors, fullFilename, filename); Parser parser = new Parser(scanner, errors, module, builtIns, verifyThisFile); parser.Parse(); return parser.errors.count; @@ -506,7 +506,7 @@ Dafny Contract.Assert(defaultModule != null); .) { "include" stringToken (. { - string parsedFile = t.filename; + string parsedFile = scanner.FullFilename; bool isVerbatimString; string includedFile = Util.RemoveParsedStringQuotes(t.val, out isVerbatimString); includedFile = Util.RemoveEscaping(includedFile, isVerbatimString); diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs index d50a4dd6..dae1b95e 100644 --- a/Source/Dafny/Parser.cs +++ b/Source/Dafny/Parser.cs @@ -112,11 +112,11 @@ public static int Parse (string/*!*/ filename, ModuleDecl module, BuiltIns built string s; if (filename == "stdin.dfy") { s = Microsoft.Boogie.ParserHelper.Fill(System.Console.In, new List()); - return Parse(s, filename, module, builtIns, errors, verifyThisFile); + return Parse(s, filename, filename, module, builtIns, errors, verifyThisFile); } else { using (System.IO.StreamReader reader = new System.IO.StreamReader(filename)) { s = Microsoft.Boogie.ParserHelper.Fill(reader, new List()); - return Parse(s, DafnyOptions.Clo.UseBaseNameForFileName ? Path.GetFileName(filename) : filename, module, builtIns, errors, verifyThisFile); + return Parse(s, filename, DafnyOptions.Clo.UseBaseNameForFileName ? Path.GetFileName(filename) : filename, module, builtIns, errors, verifyThisFile); } } } @@ -126,12 +126,12 @@ public static int Parse (string/*!*/ filename, ModuleDecl module, BuiltIns built /// Returns the number of parsing errors encountered. /// Note: first initialize the Scanner. ///
-public static int Parse (string/*!*/ s, string/*!*/ filename, ModuleDecl module, BuiltIns builtIns, bool verifyThisFile=true) { +public static int Parse (string/*!*/ s, string/*!*/ fullFilename, string/*!*/ filename, ModuleDecl module, BuiltIns builtIns, bool verifyThisFile=true) { Contract.Requires(s != null); Contract.Requires(filename != null); Contract.Requires(module != null); Errors errors = new Errors(); - return Parse(s, filename, module, builtIns, errors, verifyThisFile); + return Parse(s, fullFilename, filename, module, builtIns, errors, verifyThisFile); } /// /// Parses top-level things (modules, classes, datatypes, class members) @@ -139,15 +139,15 @@ public static int Parse (string/*!*/ s, string/*!*/ filename, ModuleDecl module, /// Returns the number of parsing errors encountered. /// Note: first initialize the Scanner with the given Errors sink. /// -public static int Parse (string/*!*/ s, string/*!*/ filename, ModuleDecl module, BuiltIns builtIns, - Errors/*!*/ errors, bool verifyThisFile=true) { +public static int Parse (string/*!*/ s, string/*!*/ fullFilename, string/*!*/ filename, ModuleDecl module, + BuiltIns builtIns, Errors/*!*/ errors, bool verifyThisFile=true) { Contract.Requires(s != null); Contract.Requires(filename != null); Contract.Requires(module != null); Contract.Requires(errors != null); byte[]/*!*/ buffer = cce.NonNull( UTF8Encoding.Default.GetBytes(s)); MemoryStream ms = new MemoryStream(buffer,false); - Scanner scanner = new Scanner(ms, errors, filename); + Scanner scanner = new Scanner(ms, errors, fullFilename, filename); Parser parser = new Parser(scanner, errors, module, builtIns, verifyThisFile); parser.Parse(); return parser.errors.count; @@ -536,7 +536,7 @@ bool IsType(ref IToken pt) { Get(); Expect(20); { - string parsedFile = t.filename; + string parsedFile = scanner.FullFilename; bool isVerbatimString; string includedFile = Util.RemoveParsedStringQuotes(t.val, out isVerbatimString); includedFile = Util.RemoveEscaping(includedFile, isVerbatimString); diff --git a/Source/Dafny/Scanner.cs b/Source/Dafny/Scanner.cs index 325a2f2c..a73f510d 100644 --- a/Source/Dafny/Scanner.cs +++ b/Source/Dafny/Scanner.cs @@ -224,6 +224,7 @@ public class Scanner { Contract.Invariant(pt != null); Contract.Invariant(tval != null); Contract.Invariant(Filename != null); + Contract.Invariant(FullFilename != null); Contract.Invariant(errorHandler != null); } @@ -258,6 +259,8 @@ public class Scanner { private string/*!*/ Filename; private Errors/*!*/ errorHandler; + internal string/*!*/ FullFilename { get; private set; } + static Scanner() { start = new Hashtable(128); for (int i = 63; i <= 63; ++i) start[i] = 1; @@ -310,7 +313,7 @@ public class Scanner { } // [NotDelayed] - public Scanner (string/*!*/ fileName, Errors/*!*/ errorHandler, bool useBaseName = false) : base() { + public Scanner (string/*!*/ fullFilename, string/*!*/ fileName, Errors/*!*/ errorHandler, bool useBaseName = false) : base() { Contract.Requires(fileName != null); Contract.Requires(errorHandler != null); this.errorHandler = errorHandler; @@ -319,6 +322,7 @@ public class Scanner { try { Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); this._buffer = new Buffer(stream, false); + this.FullFilename = fullFilename; Filename = useBaseName? GetBaseName(fileName): fileName; Init(); } catch (IOException) { @@ -327,7 +331,7 @@ public class Scanner { } // [NotDelayed] - public Scanner (Stream/*!*/ s, Errors/*!*/ errorHandler, string/*!*/ fileName, bool useBaseName = false) : base() { + public Scanner (Stream/*!*/ s, Errors/*!*/ errorHandler, string/*!*/ fullFilename, string/*!*/ fileName, bool useBaseName = false) : base() { Contract.Requires(s != null); Contract.Requires(errorHandler != null); Contract.Requires(fileName != null); @@ -335,6 +339,7 @@ public class Scanner { t = new Token(); // dummy because t is a non-null field this._buffer = new Buffer(s, true); this.errorHandler = errorHandler; + this.FullFilename = fullFilename; this.Filename = useBaseName? GetBaseName(fileName) : fileName; Init(); } -- cgit v1.2.3 From 4bcc3e82425441b077ff53a9c2b2a8442ff8936f Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 23 Jul 2015 15:03:40 -0700 Subject: Fix: Read clauses should be checked before lit lifting The function IgnoreFuel in UnfoldingPerformance.dfy used to be lit-wrapped. It shouldn't be, because its reads clause is non-empty. This is not a soundness bug, but fixing it improves performance in cases where a function call would be incorrectly unrolled. Test case written by Rustan. --- Source/Dafny/Translator.cs | 30 ++++++++------ Test/dafny0/UnfoldingPerformance.dfy | 61 +++++++++++++++++++++++++++++ Test/dafny0/UnfoldingPerformance.dfy.expect | 11 ++++++ 3 files changed, 91 insertions(+), 11 deletions(-) create mode 100644 Test/dafny0/UnfoldingPerformance.dfy create mode 100644 Test/dafny0/UnfoldingPerformance.dfy.expect diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index b2c688d9..05633742 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -10824,6 +10824,7 @@ namespace Microsoft.Dafny { s = translator.FunctionCall(expr.tok, BuiltinFunction.SeqBuild, predef.BoxType, s, elt); } if (isLit) { + // Lit-lifting: All elements are lit, so the sequence is Lit too s = MaybeLit(s, predef.BoxType); } return s; @@ -11033,30 +11034,37 @@ namespace Microsoft.Dafny { var ty = translator.TrType(e.Type); var id = new Bpl.IdentifierExpr(e.tok, e.Function.FullSanitizedName, ty); - bool returnLit; - var args = FunctionInvocationArguments(e, layerArgument, out returnLit); + bool argsAreLit; + var args = FunctionInvocationArguments(e, layerArgument, out argsAreLit); Expr result = new Bpl.NAryExpr(e.tok, new Bpl.FunctionCall(id), args); result = translator.CondApplyUnbox(e.tok, result, e.Function.ResultType, e.Type); - if (returnLit && Translator.FunctionBodyIsAvailable(e.Function, translator.currentModule)) { + + bool callIsLit = argsAreLit + && Translator.FunctionBodyIsAvailable(e.Function, translator.currentModule) + && !e.Function.Reads.Any(); // Function could depend on external values + if (callIsLit) { result = MaybeLit(result, ty); } + return result; } else if (expr is DatatypeValue) { DatatypeValue dtv = (DatatypeValue)expr; Contract.Assert(dtv.Ctor != null); // since dtv has been successfully resolved List args = new List(); - bool isLit = true; + + bool argsAreLit = true; for (int i = 0; i < dtv.Arguments.Count; i++) { Expression arg = dtv.Arguments[i]; Type t = dtv.Ctor.Formals[i].Type; var bArg = TrExpr(arg); - isLit = isLit && translator.IsLit(bArg); + argsAreLit = argsAreLit && translator.IsLit(bArg); args.Add(translator.CondApplyBox(expr.tok, bArg, cce.NonNull(arg.Type), t)); } Bpl.IdentifierExpr id = new Bpl.IdentifierExpr(dtv.tok, dtv.Ctor.FullName, predef.DatatypeType); Bpl.Expr ret = new Bpl.NAryExpr(dtv.tok, new Bpl.FunctionCall(id), args); - if (isLit) { + if (argsAreLit) { + // If all arguments are Lit, so is the whole expression ret = MaybeLit(ret, predef.DatatypeType); } return ret; @@ -11806,11 +11814,11 @@ namespace Microsoft.Dafny { } public List FunctionInvocationArguments(FunctionCallExpr e, Bpl.Expr layerArgument) { - bool returnLit; - return FunctionInvocationArguments(e, layerArgument, out returnLit); + bool dummy; + return FunctionInvocationArguments(e, layerArgument, out dummy); } - public List FunctionInvocationArguments(FunctionCallExpr e, Bpl.Expr layerArgument, out bool returnLit) { + public List FunctionInvocationArguments(FunctionCallExpr e, Bpl.Expr layerArgument, out bool argsAreLit) { Contract.Requires(e != null); Contract.Ensures(Contract.Result>() != null); @@ -11829,12 +11837,12 @@ namespace Microsoft.Dafny { if (!e.Function.IsStatic) { args.Add(TrExpr(e.Receiver)); } - returnLit = true; + argsAreLit = true; for (int i = 0; i < e.Args.Count; i++) { Expression ee = e.Args[i]; Type t = e.Function.Formals[i].Type; Expr tr_ee = TrExpr(ee); - returnLit = returnLit && translator.IsLit(tr_ee); + argsAreLit = argsAreLit && translator.IsLit(tr_ee); args.Add(translator.CondApplyBox(e.tok, tr_ee, cce.NonNull(ee.Type), t)); } return args; diff --git a/Test/dafny0/UnfoldingPerformance.dfy b/Test/dafny0/UnfoldingPerformance.dfy new file mode 100644 index 00000000..3eed689a --- /dev/null +++ b/Test/dafny0/UnfoldingPerformance.dfy @@ -0,0 +1,61 @@ +// RUN: %dafny /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +class C { + var x: nat + function method IgnoreFuel(): nat + reads this + { + x + } +} + +function method Fib(n: int): int +{ + if n < 2 then n else Fib(n-2) + Fib(n-1) +} + +method Test0() { + var c := new C; + var f := Fib(c.IgnoreFuel()); + // with the bug, the following wwould take a long time before it reports an error + // after the bug fix, this still fails, but quickly + assert 0 <= f; +} + +method Test1() { + var c := new C; + var f := Fib(c.x); + // the following assert will also fail, but quickly + assert 0 <= f; +} + +method Test2() { + var c := new C; + c.x := 10; + var f := Fib(c.IgnoreFuel()); + assert 0 <= f; // passes +} + +method Test3() { + var c := new C; + c.x := 10; + var f := Fib(c.x); + assert 0 <= f; // passes +} + +method Test4() { + var c := new C; + c.x := 10; + var f := Fib(c.x - 2); + assert 0 <= f; // fails +} + +method Test5(x: int) + requires 9 <= x - 1 && x + 1 <= 11 +{ + var c := new C; + c.x := x; + var f := Fib(c.x); + assert 0 <= f; // succeeds? +} diff --git a/Test/dafny0/UnfoldingPerformance.dfy.expect b/Test/dafny0/UnfoldingPerformance.dfy.expect new file mode 100644 index 00000000..220fecc5 --- /dev/null +++ b/Test/dafny0/UnfoldingPerformance.dfy.expect @@ -0,0 +1,11 @@ +UnfoldingPerformance.dfy(23,11): Error: assertion violation +Execution trace: + (0,0): anon0 +UnfoldingPerformance.dfy(30,11): Error: assertion violation +Execution trace: + (0,0): anon0 +UnfoldingPerformance.dfy(51,11): Error: assertion violation +Execution trace: + (0,0): anon0 + +Dafny program verifier finished with 11 verified, 3 errors -- cgit v1.2.3 From 259a1283e5636fc99a454ea414dc71008f71a571 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 23 Jul 2015 15:03:21 -0700 Subject: Bump up the time limit in runTests.py and save a bit of space in output --- Test/runTests.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Test/runTests.py b/Test/runTests.py index 34a9c4de..62ab205b 100644 --- a/Test/runTests.py +++ b/Test/runTests.py @@ -12,7 +12,7 @@ from collections import defaultdict from multiprocessing import Pool, Manager from subprocess import Popen, call, PIPE, TimeoutExpired -# C:/Python34/python.exe runTests.py --compiler "c:/MSR/dafny/Binaries/Dafny.exe /useBaseNameForFileName /compile:1 /nologo" --difftool "C:\Program Files (x86)\Meld\Meld.exe" -j4 -f "/dprelude preludes\AlmostAllTriggers.bpl" dafny0\SeqFromArray.dfy +# C:/Python34/python.exe runTests.py --compiler "c:/MSR/dafny/Binaries/Dafny.exe" --flags "/useBaseNameForFileName /compile:1 /nologo" --difftool "C:\Program Files (x86)\Meld\Meld.exe" -j4 --flags "/dprelude preludes\AlmostAllTriggers.bpl" dafny0\SeqFromArray.dfy # c:/Python34/python.exe runTests.py --compare ../TestStable/results/SequenceAxioms/2015-06-06-00-54-52--PrettyPrinted.report.csv ../TestStable/results/SequenceAxioms/*.csv @@ -37,7 +37,7 @@ class Defaults: ALWAYS_EXCLUDED = ["Inputs", "Output", "sandbox", "desktop"] DAFNY_BIN = os.path.realpath(os.path.join(os.path.dirname(__file__), "../Binaries/Dafny.exe")) COMPILER = [DAFNY_BIN] - FLAGS = ["/useBaseNameForFileName", "/compile:1", "/nologo", "/timeLimit:120"] + FLAGS = ["/useBaseNameForFileName", "/compile:1", "/nologo", "/timeLimit:300"] class Colors: RED = '\033[91m' @@ -236,10 +236,10 @@ class Test: def report(self, tid, running, alltests): running = [alltests[rid].fname for rid in running] - running = "; oldest thread: {}".format(running[0]) if running else "" + running = "; oldest: {}".format(running[0]) if running else "" fstring = "[{:5.2f}s] {} ({}{})" - progress = "{} of {}".format(tid, len(alltests)) + progress = "{}/{}".format(tid, len(alltests)) message = fstring.format(self.duration, wrap_color(self.dfy, Colors.BRIGHT), wrap_color(progress, Colors.BRIGHT), running) -- cgit v1.2.3 From 8a26fae8810b3a0419df1704de2b23926b95e92e Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 24 Jul 2015 17:33:56 -0700 Subject: Update DafnyExtension to use the parser constructor introduced in d2e394fc4b93 --- Source/DafnyExtension/DafnyDriver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/DafnyExtension/DafnyDriver.cs b/Source/DafnyExtension/DafnyDriver.cs index 8300213c..61328fbf 100644 --- a/Source/DafnyExtension/DafnyDriver.cs +++ b/Source/DafnyExtension/DafnyDriver.cs @@ -116,7 +116,7 @@ namespace DafnyLanguage Dafny.ModuleDecl module = new Dafny.LiteralModuleDecl(new Dafny.DefaultModuleDecl(), null); Dafny.BuiltIns builtIns = new Dafny.BuiltIns(); Dafny.Errors parseErrors = new VSErrors(this); - int errorCount = Dafny.Parser.Parse(_snapshot.GetText(), _filename, module, builtIns, parseErrors); + int errorCount = Dafny.Parser.Parse(_snapshot.GetText(), _filename, _filename, module, builtIns, parseErrors); string errString = Dafny.Main.ParseIncludes(module, builtIns, new List(), parseErrors); if (errorCount != 0 || errString != null) return false; -- cgit v1.2.3 From 1ca106492d5680c29bfa83b34ff79f7ff8407db7 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 24 Jul 2015 20:49:43 -0700 Subject: Translate triggers with the same ExpressionTranslator as the body Signed-off-by: Clement Pit--Claudel --- Source/Dafny/Translator.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 05633742..3821e5df 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -10623,7 +10623,7 @@ namespace Microsoft.Dafny { public ExpressionTranslator WithNoLits() { Contract.Ensures(Contract.Result() != null); - return new ExpressionTranslator(translator, predef, HeapExpr, This, null, layerInterCluster, layerIntraCluster, modifiesFrame, true); + return new ExpressionTranslator(translator, predef, HeapExpr, This, applyLimited_CurrentFunction, layerInterCluster, layerIntraCluster, modifiesFrame, true); } public ExpressionTranslator LimitedFunctions(Function applyLimited_CurrentFunction, Bpl.Expr layerArgument) { @@ -13039,9 +13039,10 @@ namespace Microsoft.Dafny { typeAntecedent = etran.TrBoundVariables(e.BoundVars, bvars); foreach (var kase in caseProduct) { var ante = BplAnd(BplAnd(typeAntecedent, ih), kase); - var bdy = etran.LayerOffset(1).TrExpr(e.LogicalBody()); + var etranBody = etran.LayerOffset(1); + var bdy = etranBody.TrExpr(e.LogicalBody()); Bpl.Expr q; - var trig = TrTrigger(etran, e.Attributes, expr.tok); + var trig = TrTrigger(etranBody, e.Attributes, expr.tok); if (position) { q = new Bpl.ForallExpr(kase.tok, bvars, trig, Bpl.Expr.Imp(ante, bdy)); } else { -- cgit v1.2.3 From 2b2050060b9eb8cb123af6df942ebebe7fe6d52c Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 24 Jul 2015 21:12:30 -0700 Subject: Renamed "ghost method" to "lemma" in a couple of test files --- Test/dafny1/Induction.dfy | 24 +++++------ Test/dafny1/Rippling.dfy | 108 +++++++++++++++++++++++----------------------- 2 files changed, 66 insertions(+), 66 deletions(-) diff --git a/Test/dafny1/Induction.dfy b/Test/dafny1/Induction.dfy index 28171896..3445dab9 100644 --- a/Test/dafny1/Induction.dfy +++ b/Test/dafny1/Induction.dfy @@ -22,7 +22,7 @@ class IntegerInduction { // Here is one proof. It uses a lemma, which is proved separately. - ghost method Theorem0(n: int) + lemma Theorem0(n: int) requires 0 <= n; ensures SumOfCubes(n) == Gauss(n) * Gauss(n); { @@ -32,7 +32,7 @@ class IntegerInduction { } } - ghost method Lemma(n: int) + lemma Lemma(n: int) requires 0 <= n; ensures 2 * Gauss(n) == n*(n+1); { @@ -42,7 +42,7 @@ class IntegerInduction { // Here is another proof. It states the lemma as part of the theorem, and // thus proves the two together. - ghost method Theorem1(n: int) + lemma Theorem1(n: int) requires 0 <= n; ensures SumOfCubes(n) == Gauss(n) * Gauss(n); ensures 2 * Gauss(n) == n*(n+1); @@ -52,24 +52,24 @@ class IntegerInduction { } } - ghost method DoItAllInOneGo() + lemma DoItAllInOneGo() ensures (forall n :: 0 <= n ==> SumOfCubes(n) == Gauss(n) * Gauss(n) && 2 * Gauss(n) == n*(n+1)); { } - // The following two ghost methods are the same as the previous two, but + // The following two lemmas are the same as the previous two, but // here no body is given--and the proof still goes through (thanks to // Dafny's ghost-method induction tactic). - ghost method Lemma_Auto(n: int) + lemma Lemma_Auto(n: int) requires 0 <= n; ensures 2 * Gauss(n) == n*(n+1); { } - ghost method Theorem1_Auto(n: int) + lemma Theorem1_Auto(n: int) requires 0 <= n; ensures SumOfCubes(n) == Gauss(n) * Gauss(n); ensures 2 * Gauss(n) == n*(n+1); @@ -79,7 +79,7 @@ class IntegerInduction { // Here is another proof. It makes use of Dafny's induction heuristics to // prove the lemma. - ghost method Theorem2(n: int) + lemma Theorem2(n: int) requires 0 <= n; ensures SumOfCubes(n) == Gauss(n) * Gauss(n); { @@ -90,7 +90,7 @@ class IntegerInduction { } } - ghost method M(n: int) + lemma M(n: int) requires 0 <= n; { assume (forall k :: 0 <= k && k < n ==> 2 * Gauss(k) == k*(k+1)); // manually assume the induction hypothesis @@ -99,7 +99,7 @@ class IntegerInduction { // Another way to prove the lemma is to supply a postcondition on the Gauss function - ghost method Theorem3(n: int) + lemma Theorem3(n: int) requires 0 <= n; ensures SumOfCubes(n) == GaussWithPost(n) * GaussWithPost(n); { @@ -117,14 +117,14 @@ class IntegerInduction { // Finally, with the postcondition of GaussWithPost, one can prove the entire theorem by induction - ghost method Theorem4() + lemma Theorem4() ensures (forall n :: 0 <= n ==> SumOfCubes(n) == GaussWithPost(n) * GaussWithPost(n)); { // look ma, no hints! } - ghost method Theorem5(n: int) + lemma Theorem5(n: int) requires 0 <= n; ensures SumOfCubes(n) == GaussWithPost(n) * GaussWithPost(n); { diff --git a/Test/dafny1/Rippling.dfy b/Test/dafny1/Rippling.dfy index 55701a93..4d1761b1 100644 --- a/Test/dafny1/Rippling.dfy +++ b/Test/dafny1/Rippling.dfy @@ -300,244 +300,244 @@ function AlwaysTrueFunction(): FunctionValue // The theorems to be proved // ----------------------------------------------------------------------------------- -ghost method P1() +lemma P1() ensures forall n, xs :: concat(take(n, xs), drop(n, xs)) == xs; { } -ghost method P2() +lemma P2() ensures forall n, xs, ys :: add(count(n, xs), count(n, ys)) == count(n, concat(xs, ys)); { } -ghost method P3() +lemma P3() ensures forall n, xs, ys :: leq(count(n, xs), count(n, concat(xs, ys))) == True; { } -ghost method P4() +lemma P4() ensures forall n, xs :: add(Suc(Zero), count(n, xs)) == count(n, Cons(n, xs)); { } -ghost method P5() +lemma P5() ensures forall n, xs, x :: add(Suc(Zero), count(n, xs)) == count(n, Cons(x, xs)) ==> n == x; { } -ghost method P6() +lemma P6() ensures forall m, n :: minus(n, add(n, m)) == Zero; { } -ghost method P7() +lemma P7() ensures forall m, n :: minus(add(n, m), n) == m; { } -ghost method P8() +lemma P8() ensures forall k, m, n :: minus(add(k, m), add(k, n)) == minus(m, n); { } -ghost method P9() +lemma P9() ensures forall i, j, k :: minus(minus(i, j), k) == minus(i, add(j, k)); { } -ghost method P10() +lemma P10() ensures forall m :: minus(m, m) == Zero; { } -ghost method P11() +lemma P11() ensures forall xs :: drop(Zero, xs) == xs; { } -ghost method P12() +lemma P12() ensures forall n, xs, f :: drop(n, apply(f, xs)) == apply(f, drop(n, xs)); { } -ghost method P13() +lemma P13() ensures forall n, x, xs :: drop(Suc(n), Cons(x, xs)) == drop(n, xs); { } -ghost method P14() +lemma P14() ensures forall xs, ys, p :: filter(p, concat(xs, ys)) == concat(filter(p, xs), filter(p, ys)); { } -ghost method P15() +lemma P15() ensures forall x, xs :: len(ins(x, xs)) == Suc(len(xs)); { } -ghost method P16() +lemma P16() ensures forall x, xs :: xs == Nil ==> last(Cons(x, xs)) == x; { } -ghost method P17() +lemma P17() ensures forall n :: leq(n, Zero) == True <==> n == Zero; { } -ghost method P18() +lemma P18() ensures forall i, m :: less(i, Suc(add(i, m))) == True; { } -ghost method P19() +lemma P19() ensures forall n, xs :: len(drop(n, xs)) == minus(len(xs), n); { } -ghost method P20() +lemma P20() ensures forall xs :: len(sort(xs)) == len(xs); { // the proof of this theorem requires a lemma about "insort" assert forall x, xs :: len(insort(x, xs)) == Suc(len(xs)); } -ghost method P21() +lemma P21() ensures forall n, m :: leq(n, add(n, m)) == True; { } -ghost method P22() +lemma P22() ensures forall a, b, c :: max(max(a, b), c) == max(a, max(b, c)); { } -ghost method P23() +lemma P23() ensures forall a, b :: max(a, b) == max(b, a); { } -ghost method P24() +lemma P24() ensures forall a, b :: max(a, b) == a <==> leq(b, a) == True; { } -ghost method P25() +lemma P25() ensures forall a, b :: max(a, b) == b <==> leq(a, b) == True; { } -ghost method P26() +lemma P26() ensures forall x, xs, ys :: mem(x, xs) == True ==> mem(x, concat(xs, ys)) == True; { } -ghost method P27() +lemma P27() ensures forall x, xs, ys :: mem(x, ys) == True ==> mem(x, concat(xs, ys)) == True; { } -ghost method P28() +lemma P28() ensures forall x, xs :: mem(x, concat(xs, Cons(x, Nil))) == True; { } -ghost method P29() +lemma P29() ensures forall x, xs :: mem(x, ins1(x, xs)) == True; { } -ghost method P30() +lemma P30() ensures forall x, xs :: mem(x, ins(x, xs)) == True; { } -ghost method P31() +lemma P31() ensures forall a, b, c :: min(min(a, b), c) == min(a, min(b, c)); { } -ghost method P32() +lemma P32() ensures forall a, b :: min(a, b) == min(b, a); { } -ghost method P33() +lemma P33() ensures forall a, b :: min(a, b) == a <==> leq(a, b) == True; { } -ghost method P34() +lemma P34() ensures forall a, b :: min(a, b) == b <==> leq(b, a) == True; { } -ghost method P35() +lemma P35() ensures forall xs :: dropWhileAlways(AlwaysFalseFunction(), xs) == xs; { } -ghost method P36() +lemma P36() ensures forall xs :: takeWhileAlways(AlwaysTrueFunction(), xs) == xs; { } -ghost method P37() +lemma P37() ensures forall x, xs :: not(mem(x, delete(x, xs))) == True; { } -ghost method P38() +lemma P38() ensures forall n, xs :: count(n, concat(xs, Cons(n, Nil))) == Suc(count(n, xs)); { } -ghost method P39() +lemma P39() ensures forall n, x, xs :: add(count(n, Cons(x, Nil)), count(n, xs)) == count(n, Cons(x, xs)); { } -ghost method P40() +lemma P40() ensures forall xs :: take(Zero, xs) == Nil; { } -ghost method P41() +lemma P41() ensures forall n, xs, f :: take(n, apply(f, xs)) == apply(f, take(n, xs)); { } -ghost method P42() +lemma P42() ensures forall n, x, xs :: take(Suc(n), Cons(x, xs)) == Cons(x, take(n, xs)); { } -ghost method P43(p: FunctionValue) +lemma P43(p: FunctionValue) ensures forall xs :: concat(takeWhileAlways(p, xs), dropWhileAlways(p, xs)) == xs; { } -ghost method P44() +lemma P44() ensures forall x, xs, ys :: zip(Cons(x, xs), ys) == zipConcat(x, xs, ys); { } -ghost method P45() +lemma P45() ensures forall x, xs, y, ys :: zip(Cons(x, xs), Cons(y, ys)) == PCons(Pair.Pair(x, y), zip(xs, ys)); { } -ghost method P46() +lemma P46() ensures forall ys :: zip(Nil, ys) == PNil; { } -ghost method P47() +lemma P47() ensures forall a :: height(mirror(a)) == height(a); { // proving this theorem requires a previously proved lemma: @@ -546,7 +546,7 @@ ghost method P47() // ... -ghost method P54() +lemma P54() ensures forall m, n :: minus(add(m, n), n) == m; { // the proof of this theorem follows from two lemmas: @@ -554,7 +554,7 @@ ghost method P54() assert forall m, n :: add(m, n) == add(n, m); } -ghost method P65() +lemma P65() ensures forall i, m :: less(i, Suc(add(m, i))) == True; { if (*) { @@ -567,7 +567,7 @@ ghost method P65() } } -ghost method P67() +lemma P67() ensures forall m, n :: leq(n, add(m, n)) == True; { if (*) { @@ -583,19 +583,19 @@ ghost method P67() // --------- // Here is a alternate way of writing down the proof obligations: -ghost method P1_alt(n: Nat, xs: List) +lemma P1_alt(n: Nat, xs: List) ensures concat(take(n, xs), drop(n, xs)) == xs; { } -ghost method P2_alt(n: Nat, xs: List, ys: List) +lemma P2_alt(n: Nat, xs: List, ys: List) ensures add(count(n, xs), count(n, ys)) == count(n, (concat(xs, ys))); { } // --------- -ghost method Lemma_RevConcat(xs: List, ys: List) +lemma Lemma_RevConcat(xs: List, ys: List) ensures reverse(concat(xs, ys)) == concat(reverse(ys), reverse(xs)); { match (xs) { @@ -606,7 +606,7 @@ ghost method Lemma_RevConcat(xs: List, ys: List) } } -ghost method Theorem(xs: List) +lemma Theorem(xs: List) ensures reverse(reverse(xs)) == xs; { match (xs) { -- cgit v1.2.3 From c69d99690d9a25c448455df2bc8afee276b7b64c Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Mon, 27 Jul 2015 11:25:10 -0700 Subject: Clean up error reporting. There now is one main entry point for reporting errors, warnings, or information. Next step is to make the VS extension use that. --- Source/Dafny/Parser.cs | 21 +++++++-------------- Source/Dafny/Resolver.cs | 12 +++--------- Source/Dafny/Translator.cs | 2 +- Source/Dafny/Util.cs | 23 +++++++++++++++++++++++ 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs index dae1b95e..53921bb1 100644 --- a/Source/Dafny/Parser.cs +++ b/Source/Dafny/Parser.cs @@ -4428,9 +4428,6 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo public class Errors { public int count = 0; // number of errors detected - public System.IO.TextWriter/*!*/ errorStream = Console.Out; // error messages go to this stream - public string errMsgFormat = "{0}({1},{2}): Error: {3}"; // 0=filename, 1=line, 2=column, 3=text - public string warningMsgFormat = "{0}({1},{2}): Warning: {3}"; // 0=filename, 1=line, 2=column, 3=text public void SynErr(string filename, int line, int col, int n) { SynErr(filename, line, col, GetSyntaxErrorString(n)); @@ -4438,7 +4435,7 @@ public class Errors { public virtual void SynErr(string filename, int line, int col, string/*!*/ msg) { Contract.Requires(msg != null); - errorStream.WriteLine(errMsgFormat, filename, line, col - 1, msg); + Dafny.Util.ReportIssue("Error", filename, line, col, msg); count++; } @@ -4693,27 +4690,23 @@ public class Errors { return s; } - public void SemErr(IToken/*!*/ tok, string/*!*/ msg) { // semantic errors + public void SemErr(IToken/*!*/ tok, string/*!*/ msg) { // semantic errors Contract.Requires(tok != null); Contract.Requires(msg != null); - SemErr(tok.filename, tok.line, tok.col, msg); + Dafny.Util.ReportIssue("Error", tok, msg); + count++; } public virtual void SemErr(string filename, int line, int col, string/*!*/ msg) { Contract.Requires(msg != null); - errorStream.WriteLine(errMsgFormat, filename, line, col - 1, msg); + Dafny.Util.ReportIssue("Error", filename, line, col, msg); count++; } - public void Warning(IToken/*!*/ tok, string/*!*/ msg) { // warnings + public void Warning(IToken/*!*/ tok, string/*!*/ msg) { // warnings Contract.Requires(tok != null); Contract.Requires(msg != null); - Warning(tok.filename, tok.line, tok.col, msg); - } - - public virtual void Warning(string filename, int line, int col, string msg) { - Contract.Requires(msg != null); - errorStream.WriteLine(warningMsgFormat, filename, line, col - 1, msg); + Dafny.Util.ReportIssue("Warning", tok, msg); } } // Errors diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 44990d7d..e3083133 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -24,9 +24,7 @@ namespace Microsoft.Dafny Contract.Requires(msg != null); ConsoleColor col = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("{0}({1},{2}): Error: {3}", - DafnyOptions.Clo.UseBaseNameForFileName ? System.IO.Path.GetFileName(tok.filename) : tok.filename, tok.line, tok.col - 1, - string.Format(msg, args)); + Dafny.Util.ReportIssue("Error", tok, msg, args); Console.ForegroundColor = col; ErrorCount++; } @@ -66,9 +64,7 @@ namespace Microsoft.Dafny if (reportWarnings) { ConsoleColor col = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.Yellow; - Console.WriteLine("{0}({1},{2}): Warning: {3}", - DafnyOptions.Clo.UseBaseNameForFileName ? System.IO.Path.GetFileName(tok.filename) : tok.filename, tok.line, tok.col - 1, - string.Format(msg, args)); + Dafny.Util.ReportIssue("Warning", tok, msg, args); Console.ForegroundColor = col; } } @@ -281,9 +277,7 @@ namespace Microsoft.Dafny } public static void DefaultInformationReporter(AdditionalInformation info) { - Console.WriteLine("{0}({1},{2}): Info: {3}", - DafnyOptions.Clo.UseBaseNameForFileName ? System.IO.Path.GetFileName(info.Token.filename) : info.Token.filename, - info.Token.line, info.Token.col - 1, info.Text); + Dafny.Util.ReportIssue("Info", info.Token, info.Text); } public void ResolveProgram(Program prog) { diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 05633742..e2c45558 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -3609,7 +3609,7 @@ namespace Microsoft.Dafny { Contract.Requires(tok != null); Contract.Ensures(Contract.Result() != null); var col = tok.col + (isEndToken ? tok.val.Length : 0); - string description = string.Format("{0}({1},{2}){3}{4}", tok.filename, tok.line, col, additionalInfo == null ? "" : ": ", additionalInfo ?? ""); + string description = Util.ReportIssueToString_Bare(additionalInfo == null ? "" : ": ", tok.filename, tok.line, tok.col, additionalInfo ?? ""); QKeyValue kv = new QKeyValue(tok, "captureState", new List() { description }, null); return new Bpl.AssumeCmd(tok, Bpl.Expr.True, kv); } diff --git a/Source/Dafny/Util.cs b/Source/Dafny/Util.cs index 63659696..6162d32c 100644 --- a/Source/Dafny/Util.cs +++ b/Source/Dafny/Util.cs @@ -67,6 +67,29 @@ namespace Microsoft.Dafny { return res; } + internal static void ReportIssue(string header, IToken tok, string msg, params object[] args) { + ReportIssue(header, tok, String.Format(msg, args)); + } + + internal static void ReportIssue(string header, IToken tok, string msg) { + ReportIssue(header, tok.filename, tok.line, tok.col, msg); + } + + internal static void ReportIssue(string header, string filename, int line, int column, string msg) { + Console.WriteLine(ReportIssueToString(header, filename, line, column, msg)); + } + + internal static string ReportIssueToString(string header, string filename, int line, int column, string msg) { + Contract.Requires(header != null); + Contract.Requires(filename != null); + Contract.Requires(msg != null); + return ReportIssueToString_Bare(": " + header, filename, line, column, ": " + msg); + } + + internal static string ReportIssueToString_Bare(string header, string filename, int line, int column, string msg) { + return String.Format("{0}({1},{2}){3}{4}", filename, line, column - 1, header, msg ?? ""); + } + /// /// Returns s but with all occurrences of '_' removed. /// -- cgit v1.2.3 From b61088661bea206390f487ed813fd350a50eb084 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Mon, 27 Jul 2015 11:25:38 -0700 Subject: Small refactoring --- Source/Dafny/Util.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Source/Dafny/Util.cs b/Source/Dafny/Util.cs index 6162d32c..a0a7fe36 100644 --- a/Source/Dafny/Util.cs +++ b/Source/Dafny/Util.cs @@ -95,13 +95,7 @@ namespace Microsoft.Dafny { /// public static string RemoveUnderscores(string s) { Contract.Requires(s != null); - while (true) { - var j = s.IndexOf('_'); - if (j == -1) { - return s; - } - s = s.Substring(0, j) + s.Substring(j + 1); - } + return s.Replace("_", ""); } /// -- cgit v1.2.3 From 8d1676a6d30e295d57e7e64fc58d103771459974 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Mon, 27 Jul 2015 11:28:39 -0700 Subject: Clarify the inlining warning. --- Source/Dafny/Translator.cs | 8 ++------ Test/dafny0/TriggerInPredicate.dfy.expect | 4 ++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index e2c45558..83078fb5 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -12956,12 +12956,8 @@ namespace Microsoft.Dafny { } else { // Skip inlining, as it would cause arbitrary expressions to pop up in the trigger // CLEMENT: Report inlining issue in a VS plugin friendly way - var info = new AdditionalInformation { - Token = fexp.tok, - Length = fexp.tok.val.Length, - Text = "This call cannot be safely inlined.", - }; - Resolver.DefaultInformationReporter(info); + //CLEMENT this should appear at the outmost call site, not at the innermost. See SnapshotableTrees.dfy + Dafny.Util.ReportIssue("Info", fexp.tok, "Some instances of this call cannot safely be inlined."); } } diff --git a/Test/dafny0/TriggerInPredicate.dfy.expect b/Test/dafny0/TriggerInPredicate.dfy.expect index 47b3bdf9..7f6e0268 100644 --- a/Test/dafny0/TriggerInPredicate.dfy.expect +++ b/Test/dafny0/TriggerInPredicate.dfy.expect @@ -1,4 +1,4 @@ -TriggerInPredicate.dfy(9,20): Info: This call cannot be safely inlined. -TriggerInPredicate.dfy(9,20): Info: This call cannot be safely inlined. +TriggerInPredicate.dfy(9,20): Info: Some instances of this call cannot safely be inlined. +TriggerInPredicate.dfy(9,20): Info: Some instances of this call cannot safely be inlined. Dafny program verifier finished with 8 verified, 0 errors -- cgit v1.2.3 From d5eb6e9e4c8e4f71e3bb48e0a40fc412e9a5e65e Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 27 Jul 2015 18:59:02 -0700 Subject: Updated parser generation to work with latest update in boogiepartners. Note that Coco.exe is now included in boogiepartners. --- Source/Dafny/Makefile | 2 +- Source/Dafny/Parser.cs | 29 ++++++++++++++--------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/Source/Dafny/Makefile b/Source/Dafny/Makefile index e8c0f5e0..68ab7a2d 100644 --- a/Source/Dafny/Makefile +++ b/Source/Dafny/Makefile @@ -4,7 +4,7 @@ # from http://boogiepartners.codeplex.com/. Update the FRAME_DIR variable to # point to whatever directory you install that into. # ############################################################################### -COCO_EXE_DIR = ..\..\..\boogie-partners\CocoRdownload +COCO_EXE_DIR = ..\..\..\boogie-partners\CocoR\bin FRAME_DIR = ..\..\..\boogie-partners\CocoR\Modified COCO = $(COCO_EXE_DIR)\Coco.exe diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs index dae1b95e..b4f45159 100644 --- a/Source/Dafny/Parser.cs +++ b/Source/Dafny/Parser.cs @@ -78,11 +78,11 @@ public class Parser { const bool _x = false; const int minErrDist = 2; - public Scanner/*!*/ scanner; - public Errors/*!*/ errors; + public Scanner scanner; + public Errors errors; - public Token/*!*/ t; // last recognized token - public Token/*!*/ la; // lookahead token + public Token t; // last recognized token + public Token la; // lookahead token int errDist = minErrDist; readonly Expression/*!*/ dummyExpr; @@ -454,10 +454,10 @@ bool IsType(ref IToken pt) { /*--------------------------------------------------------------------------*/ - public Parser(Scanner/*!*/ scanner, Errors/*!*/ errors) { + public Parser(Scanner scanner, Errors errors) { this.scanner = scanner; this.errors = errors; - Token/*!*/ tok = new Token(); + Token tok = new Token(); tok.val = ""; this.la = tok; this.t = new Token(); // just to satisfy its non-null constraint @@ -468,13 +468,13 @@ bool IsType(ref IToken pt) { errDist = 0; } - public void SemErr (string/*!*/ msg) { + public void SemErr (string msg) { Contract.Requires(msg != null); if (errDist >= minErrDist) errors.SemErr(t, msg); errDist = 0; } - public void SemErr(IToken/*!*/ tok, string/*!*/ msg) { + public void SemErr(IToken tok, string msg) { Contract.Requires(tok != null); Contract.Requires(msg != null); errors.SemErr(tok, msg); @@ -4385,10 +4385,9 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Dafny(); Expect(0); - Expect(0); } - static readonly bool[,]/*!*/ set = { + static readonly bool[,] set = { {_T,_T,_T,_T, _T,_x,_x,_x, _T,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_x,_x,_T, _x,_x,_x,_T, _x,_x,_T,_T, _T,_x,_x,_T, _T,_x,_x,_T, _T,_x,_T,_T, _T,_T,_T,_x, _x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_x,_T, _T,_x,_x,_T, _T,_T,_x,_x, _T,_x,_x,_T, _T,_T,_T,_T, _T,_T,_x,_T, _T,_x,_x,_T, _x,_x,_T,_T, _T,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_x,_x,_x}, {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_x,_x, _T,_x,_x,_x, _x,_T,_x,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_x,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, {_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_T,_T,_T, _T,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _T,_T,_T,_x, _x,_T,_x,_x, _x,_x,_x,_T, _T,_T,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, @@ -4428,7 +4427,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo public class Errors { public int count = 0; // number of errors detected - public System.IO.TextWriter/*!*/ errorStream = Console.Out; // error messages go to this stream + public System.IO.TextWriter errorStream = Console.Out; // error messages go to this stream public string errMsgFormat = "{0}({1},{2}): Error: {3}"; // 0=filename, 1=line, 2=column, 3=text public string warningMsgFormat = "{0}({1},{2}): Warning: {3}"; // 0=filename, 1=line, 2=column, 3=text @@ -4436,7 +4435,7 @@ public class Errors { SynErr(filename, line, col, GetSyntaxErrorString(n)); } - public virtual void SynErr(string filename, int line, int col, string/*!*/ msg) { + public virtual void SynErr(string filename, int line, int col, string msg) { Contract.Requires(msg != null); errorStream.WriteLine(errMsgFormat, filename, line, col - 1, msg); count++; @@ -4693,19 +4692,19 @@ public class Errors { return s; } - public void SemErr(IToken/*!*/ tok, string/*!*/ msg) { // semantic errors + public void SemErr(IToken tok, string msg) { // semantic errors Contract.Requires(tok != null); Contract.Requires(msg != null); SemErr(tok.filename, tok.line, tok.col, msg); } - public virtual void SemErr(string filename, int line, int col, string/*!*/ msg) { + public virtual void SemErr(string filename, int line, int col, string msg) { Contract.Requires(msg != null); errorStream.WriteLine(errMsgFormat, filename, line, col - 1, msg); count++; } - public void Warning(IToken/*!*/ tok, string/*!*/ msg) { // warnings + public void Warning(IToken tok, string msg) { // warnings Contract.Requires(tok != null); Contract.Requires(msg != null); Warning(tok.filename, tok.line, tok.col, msg); -- cgit v1.2.3 From 3cfa0049262a9d547f061937d5c452afb2033401 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 28 Jul 2015 14:27:29 -0700 Subject: Renamed "ghost method" to "lemma" whenever appropriate (which is most of the time) in the test suite. Removed some assertions that have been rendered unnecessary because of the computations that Dafny instructs the SMT solver to do. --- Test/cloudmake/CloudMake-ConsistentBuilds.dfy | 24 ++++++++--------- Test/dafny1/FindZero.dfy | 2 +- Test/dafny1/MoreInduction.dfy | 14 +++++----- Test/dafny2/Calculations.dfy | 24 ++++++++--------- Test/dafny2/MajorityVote.dfy | 4 +-- Test/dafny3/CalcExample.dfy | 6 ++--- Test/dafny3/SimpleInduction.dfy | 14 +++++----- Test/dafny4/NipkowKlein-chapter3.dfy | 7 +++-- Test/vstte2012/Combinators.dfy | 12 ++++----- Test/vstte2012/Tree.dfy | 39 ++++++++------------------- 10 files changed, 66 insertions(+), 80 deletions(-) diff --git a/Test/cloudmake/CloudMake-ConsistentBuilds.dfy b/Test/cloudmake/CloudMake-ConsistentBuilds.dfy index 815352f6..6d86607b 100644 --- a/Test/cloudmake/CloudMake-ConsistentBuilds.dfy +++ b/Test/cloudmake/CloudMake-ConsistentBuilds.dfy @@ -23,7 +23,7 @@ function Union(st: State, st': State): State (p in DomSt(st) ==> GetSt(p, result) == GetSt(p, st)) && (p in DomSt(st') ==> GetSt(p, result) == GetSt(p, st')); -ghost method StateEqualityProperty(st: State, st': State) +lemma StateEqualityProperty(st: State, st': State) requires DomSt(st) == DomSt(st'); requires forall p :: p in DomSt(st) ==> GetSt(p, st) == GetSt(p, st'); ensures st == st'; @@ -60,7 +60,7 @@ function UpdateC(cmd: Expression, deps: Expression, exts: Expression, stC: State UpdateC(cmd, deps, exts', S(stC.st, c')) } -ghost method UpdateCLemma(cmd: Expression, deps: Expression, exts: Expression, stC: StateC) +lemma UpdateCLemma(cmd: Expression, deps: Expression, exts: Expression, stC: StateC) requires cmd.exprLiteral? && cmd.lit.litString? && deps.exprLiteral? && deps.lit.litArrOfPaths? && @@ -136,7 +136,7 @@ function CombineC(stsC: set): StateC UnionC(stC, CombineC(stsC - {stC})) } -ghost method CombineCLemma(stsC: set) +lemma CombineCLemma(stsC: set) requires stsC != {}; requires forall stC :: stC in stsC ==> ConsistentCache(stC); ensures @@ -174,7 +174,7 @@ function SetEnv(id: Identifier, expr: Expression, env: Env): Env /******* Primitive function 'exec' *******/ function exec(cmd: Expression, deps: Expression, exts: Expression, st: State): Tuple -ghost method ExecProperty(cmd: Expression, deps: Expression, exts: Expression, st: State) +lemma ExecProperty(cmd: Expression, deps: Expression, exts: Expression, st: State) requires cmd.exprLiteral? && cmd.lit.litString? && deps.exprLiteral? && deps.lit.litArrOfPaths? && @@ -244,7 +244,7 @@ function execC(cmd: Expression, deps: Expression, exts: Expression, stC: StateC) Pair(expr', stC') } -ghost method ExecCProperty(cmd: Expression, deps: Expression, exts: Expression, stC: StateC) +lemma ExecCProperty(cmd: Expression, deps: Expression, exts: Expression, stC: StateC) requires cmd.exprLiteral? && cmd.lit.litString? && deps.exprLiteral? && deps.lit.litArrOfPaths? && @@ -305,7 +305,7 @@ predicate PostC(cmd: Expression, deps: Expression, exts: Expression, stC: StateC function Hash(p: Path): HashValue -ghost method HashProperty(cmd: Expression, deps: Expression, ext: string, cmd': Expression, deps': Expression, ext': string) +lemma HashProperty(cmd: Expression, deps: Expression, ext: string, cmd': Expression, deps': Expression, ext': string) requires Hash(Loc(cmd, deps, ext)) == Hash(Loc(cmd', deps', ext')); ensures cmd == cmd' && deps == deps' && ext == ext'; @@ -509,7 +509,7 @@ predicate ValidArgsC(prim: Primitive, args: seq, stC: StateC) } /******* {consistent_cache} buildC {no_bad_cache_error /\ consistent_cache} *******/ -ghost method CachedBuildsTheorem(prog: Program, stC: StateC) +lemma CachedBuildsTheorem(prog: Program, stC: StateC) requires Legal(prog.stmts); requires ConsistentCache(stC); ensures @@ -521,7 +521,7 @@ ghost method CachedBuildsTheorem(prog: Program, stC: StateC) BuildCLemma(prog, stC); } -ghost method BuildCLemma(prog: Program, stC: StateC) +lemma BuildCLemma(prog: Program, stC: StateC) requires Legal(prog.stmts); requires ConsistentCache(stC); ensures @@ -532,7 +532,7 @@ ghost method BuildCLemma(prog: Program, stC: StateC) DoCLemma(prog.stmts, stC, EmptyEnv()); } -ghost method DoCLemma(stmts: seq, stC: StateC, env: Env) +lemma DoCLemma(stmts: seq, stC: StateC, env: Env) requires Legal(stmts); requires ConsistentCache(stC); ensures @@ -558,7 +558,7 @@ ghost method DoCLemma(stmts: seq, stC: StateC, env: Env) } } -ghost method {:induction expr} EvalCLemma(expr: Expression, stC: StateC, env: Env) +lemma {:induction expr} EvalCLemma(expr: Expression, stC: StateC, env: Env) requires ConsistentCache(stC); ensures var result := evalC(expr, stC, env); @@ -627,7 +627,7 @@ ghost method {:induction expr} EvalCLemma(expr: Expression, stC: StateC, env: En } else { } } -ghost method EvalArgsCLemma(expr: Expression, args: seq, stC: StateC, env: Env) +lemma EvalArgsCLemma(expr: Expression, args: seq, stC: StateC, env: Env) requires ConsistentCache(stC); requires forall arg :: arg in args ==> arg < expr; ensures @@ -640,7 +640,7 @@ ghost method EvalArgsCLemma(expr: Expression, args: seq, stC: StateC EvalArgsC'Lemma(expr, args, stC, env, [], {}); } -ghost method EvalArgsC'Lemma(expr: Expression, args: seq, stC: StateC, env: Env, +lemma EvalArgsC'Lemma(expr: Expression, args: seq, stC: StateC, env: Env, args': seq, stsC': set) requires ConsistentCache(stC); requires forall stC' :: stC' in stsC' ==> ConsistentCache(stC'); diff --git a/Test/dafny1/FindZero.dfy b/Test/dafny1/FindZero.dfy index f0eb6a60..0940d9e7 100644 --- a/Test/dafny1/FindZero.dfy +++ b/Test/dafny1/FindZero.dfy @@ -18,7 +18,7 @@ method FindZero(a: array) returns (r: int) r := -1; } -ghost method Lemma(a: array, k: int, m: int) +lemma Lemma(a: array, k: int, m: int) requires a != null && forall i :: 0 <= i < a.Length ==> 0 <= a[i]; requires forall i :: 0 <= i && i+1 < a.Length ==> a[i]-1 <= a[i+1]; requires 0 <= k; diff --git a/Test/dafny1/MoreInduction.dfy b/Test/dafny1/MoreInduction.dfy index 41adcf50..319bb8d0 100644 --- a/Test/dafny1/MoreInduction.dfy +++ b/Test/dafny1/MoreInduction.dfy @@ -42,13 +42,13 @@ function ToSeq(list: List): seq case Nary(nn) => ToSeq(nn) + ToSeq(rest) } -ghost method Theorem(list: List) +lemma Theorem(list: List) ensures ToSeq(list) == ToSeq(FlattenMain(list)); { Lemma(list, Nil); } -ghost method Lemma(list: List, ext: List) +lemma Lemma(list: List, ext: List) requires IsFlat(ext); ensures ToSeq(list) + ToSeq(ext) == ToSeq(Flatten(list, ext)); { @@ -73,27 +73,27 @@ function NegFac(n: int): int if -1 <= n then -1 else - NegFac(n+1) * n } -ghost method LemmaAll() +lemma LemmaAll() ensures forall n :: NegFac(n) <= -1; // error: induction heuristic does not give a useful well-founded order, and thus this fails to verify { } -ghost method LemmaOne(n: int) +lemma LemmaOne(n: int) ensures NegFac(n) <= -1; // error: induction heuristic does not give a useful well-founded order, and thus this fails to verify { } -ghost method LemmaAll_Neg() +lemma LemmaAll_Neg() ensures forall n :: NegFac(-n) <= -1; // error: fails to verify because of the minus in the trigger { } -ghost method LemmaOne_Neg(n: int) +lemma LemmaOne_Neg(n: int) ensures NegFac(-n) <= -1; // error: fails to verify because of the minus in the trigger { } -ghost method LemmaOneWithDecreases(n: int) +lemma LemmaOneWithDecreases(n: int) ensures NegFac(n) <= -1; // here, the programmer gives a good well-founded order, so this verifies decreases -n; { diff --git a/Test/dafny2/Calculations.dfy b/Test/dafny2/Calculations.dfy index 8af0afe9..3870490f 100644 --- a/Test/dafny2/Calculations.dfy +++ b/Test/dafny2/Calculations.dfy @@ -41,12 +41,12 @@ function qreverse(l: List): List // Here are two lemmas about the List functions. -ghost method Lemma_ConcatNil(xs : List) +lemma Lemma_ConcatNil(xs : List) ensures concat(xs, Nil) == xs; { } -ghost method Lemma_RevCatCommute(xs : List) +lemma Lemma_RevCatCommute(xs : List) ensures forall ys, zs :: revacc(xs, concat(ys, zs)) == concat(revacc(xs, ys), zs); { } @@ -55,7 +55,7 @@ ghost method Lemma_RevCatCommute(xs : List) // is given in a calculational style. The proof is not minimal--some lines can be omitted // and Dafny will still fill in the details. -ghost method Theorem_QReverseIsCorrect_Calc(l: List) +lemma Theorem_QReverseIsCorrect_Calc(l: List) ensures qreverse(l) == reverse(l); { calc { @@ -69,7 +69,7 @@ ghost method Theorem_QReverseIsCorrect_Calc(l: List) } } -ghost method Lemma_Revacc_calc(xs: List, ys: List) +lemma Lemma_Revacc_calc(xs: List, ys: List) ensures revacc(xs, ys) == concat(reverse(xs), ys); { match (xs) { @@ -93,7 +93,7 @@ ghost method Lemma_Revacc_calc(xs: List, ys: List) // Here is a version of the same proof, as it was constructed before Dafny's "calc" construct. -ghost method Theorem_QReverseIsCorrect(l: List) +lemma Theorem_QReverseIsCorrect(l: List) ensures qreverse(l) == reverse(l); { assert qreverse(l) @@ -105,7 +105,7 @@ ghost method Theorem_QReverseIsCorrect(l: List) Lemma_ConcatNil(reverse(l)); } -ghost method Lemma_Revacc(xs: List, ys: List) +lemma Lemma_Revacc(xs: List, ys: List) ensures revacc(xs, ys) == concat(reverse(xs), ys); { match (xs) { @@ -140,7 +140,7 @@ function Fib(n: nat): nat if n < 2 then n else Fib(n - 2) + Fib(n - 1) } -ghost method Lemma_Fib() +lemma Lemma_Fib() ensures Fib(5) < 6; { calc { @@ -160,11 +160,11 @@ ghost method Lemma_Fib() /* List length */ // Here are some proofs that show the use of nested calculations. -ghost method Lemma_Concat_Length(xs: List, ys: List) +lemma Lemma_Concat_Length(xs: List, ys: List) ensures length(concat(xs, ys)) == length(xs) + length(ys); {} -ghost method Lemma_Reverse_Length(xs: List) +lemma Lemma_Reverse_Length(xs: List) ensures length(xs) == length(reverse(xs)); { match (xs) { @@ -193,7 +193,7 @@ ghost method Lemma_Reverse_Length(xs: List) } } -ghost method Window(xs: List, ys: List) +lemma Window(xs: List, ys: List) ensures length(xs) == length(ys) ==> length(reverse(xs)) == length(reverse(ys)); { calc { @@ -221,11 +221,11 @@ function ith(xs: List, i: nat): a case Cons(x, xrest) => if i == 0 then x else ith(xrest, i - 1) } -ghost method lemma_zero_length(xs: List) +lemma lemma_zero_length(xs: List) ensures length(xs) == 0 <==> xs.Nil?; {} -ghost method lemma_extensionality(xs: List, ys: List) +lemma lemma_extensionality(xs: List, ys: List) requires length(xs) == length(ys); // (0) requires forall i: nat | i < length(xs) :: ith(xs, i) == ith(ys, i); // (1) ensures xs == ys; diff --git a/Test/dafny2/MajorityVote.dfy b/Test/dafny2/MajorityVote.dfy index 51e5b968..f1c3b485 100644 --- a/Test/dafny2/MajorityVote.dfy +++ b/Test/dafny2/MajorityVote.dfy @@ -165,7 +165,7 @@ method SearchForWinner(a: seq, ghost hasWinner: bool, // Here are two lemmas about Count that are used in the methods above. -ghost method Lemma_Split(a: seq, s: int, t: int, u: int, x: T) +lemma Lemma_Split(a: seq, s: int, t: int, u: int, x: T) requires 0 <= s <= t <= u <= |a|; ensures Count(a, s, t, x) + Count(a, t, u, x) == Count(a, s, u, x); { @@ -178,7 +178,7 @@ ghost method Lemma_Split(a: seq, s: int, t: int, u: int, x: T) */ } -ghost method Lemma_Unique(a: seq, s: int, t: int, x: T, y: T) +lemma Lemma_Unique(a: seq, s: int, t: int, x: T, y: T) requires 0 <= s <= t <= |a|; ensures x != y ==> Count(a, s, t, x) + Count(a, s, t, y) <= t - s; { diff --git a/Test/dafny3/CalcExample.dfy b/Test/dafny3/CalcExample.dfy index 2782d049..b9d3260b 100644 --- a/Test/dafny3/CalcExample.dfy +++ b/Test/dafny3/CalcExample.dfy @@ -3,14 +3,14 @@ function f(x: int, y: int): int -ghost method Associativity(x: int, y: int, z: int) +lemma Associativity(x: int, y: int, z: int) ensures f(x, f(y, z)) == f(f(x, y), z); -ghost method Monotonicity(y: int, z: int) +lemma Monotonicity(y: int, z: int) requires y <= z; ensures forall x :: f(x, y) <= f(x, z); -ghost method DiagonalIdentity(x: int) +lemma DiagonalIdentity(x: int) ensures f(x, x) == x; method M(a: int, b: int, c: int, x: int) diff --git a/Test/dafny3/SimpleInduction.dfy b/Test/dafny3/SimpleInduction.dfy index 83ea6d14..8cf937e1 100644 --- a/Test/dafny3/SimpleInduction.dfy +++ b/Test/dafny3/SimpleInduction.dfy @@ -13,7 +13,7 @@ function Fib(n: nat): nat decreases n; { if n < 2 then n else Fib(n-2) + Fib(n-1) } -ghost method FibLemma(n: nat) +lemma FibLemma(n: nat) ensures Fib(n) % 2 == 0 <==> n % 3 == 0; decreases n; { @@ -30,7 +30,7 @@ ghost method FibLemma(n: nat) satisfying 0 <= k < n, and in the second example, to all non-negative n. */ -ghost method FibLemma_Alternative(n: nat) +lemma FibLemma_Alternative(n: nat) ensures Fib(n) % 2 == 0 <==> n % 3 == 0; { forall k | 0 <= k < n { @@ -38,7 +38,7 @@ ghost method FibLemma_Alternative(n: nat) } } -ghost method FibLemma_All() +lemma FibLemma_All() ensures forall n :: 0 <= n ==> (Fib(n) % 2 == 0 <==> n % 3 == 0); { forall n | 0 <= n { @@ -48,8 +48,8 @@ ghost method FibLemma_All() /* A standard inductive definition of a generic List type and a function Append - that concatenates two lists. The ghost method states the lemma that Append - is associative, and its recursive body gives the inductive proof. + that concatenates two lists. The lemma states that Append is associative, + and its recursive body gives the inductive proof. We omitted the explicit declaration and uses of the List type parameter in the signature of the method, since in simple cases like this, Dafny is able @@ -68,7 +68,7 @@ function Append(xs: List, ys: List): List // The {:induction false} attribute disables automatic induction tactic, // so we can make the proof explicit. -ghost method {:induction false} AppendIsAssociative(xs: List, ys: List, zs: List) +lemma {:induction false} AppendIsAssociative(xs: List, ys: List, zs: List) ensures Append(Append(xs, ys), zs) == Append(xs, Append(ys, zs)); decreases xs; { @@ -81,7 +81,7 @@ ghost method {:induction false} AppendIsAssociative(xs: List, ys: List, zs: List // Here the proof is fully automatic - the body of the method is empty, // yet still verifies. -ghost method AppendIsAssociative_Auto(xs: List, ys: List, zs: List) +lemma AppendIsAssociative_Auto(xs: List, ys: List, zs: List) ensures Append(Append(xs, ys), zs) == Append(xs, Append(ys, zs)); { } diff --git a/Test/dafny4/NipkowKlein-chapter3.dfy b/Test/dafny4/NipkowKlein-chapter3.dfy index 725d68f6..3de6a5fc 100644 --- a/Test/dafny4/NipkowKlein-chapter3.dfy +++ b/Test/dafny4/NipkowKlein-chapter3.dfy @@ -195,9 +195,12 @@ lemma BsimpCorrect(b: bexp, s: state) ensures bval(bsimp(b), s) == bval(b, s) { /* Here is one proof, which uses the induction hypothesis any anything smaller than b and also invokes - the lemma AsimpCorrect on anything smaller than b. + the lemma AsimpCorrect on every arithmetic expression. forall b' | b' < b { BsimpCorrect(b', s); } - forall a' | a' < b { AsimpCorrect(a', s); } + forall a { AsimpCorrect(a, s); } + Yet another possibility is to mark the lemma with {:induction b} and to use the following line in + the body: + forall a { AsimpCorrect(a, s); } */ // Here is another proof, which makes explicit the uses of the induction hypothesis and the other lemma. match b diff --git a/Test/vstte2012/Combinators.dfy b/Test/vstte2012/Combinators.dfy index be7bc25f..ba4a4141 100644 --- a/Test/vstte2012/Combinators.dfy +++ b/Test/vstte2012/Combinators.dfy @@ -170,7 +170,7 @@ function IsTerminal(t: Term): bool // The following theorem states the correctness of the FindAndStep function: -ghost method Theorem_FindAndStep(t: Term) +lemma Theorem_FindAndStep(t: Term) // If FindAndStep returns the term it started from, then there is no // way to take a step. More precisely, there is no C[u] == t for which the // Step applies to "u". @@ -194,7 +194,7 @@ ghost method Theorem_FindAndStep(t: Term) // computes the value of FindAndStep(t) as it goes along and it returns // that value. -ghost method Lemma_FindAndStep(t: Term) returns (r: Term, C: Context, u: Term) +lemma Lemma_FindAndStep(t: Term) returns (r: Term, C: Context, u: Term) ensures r == FindAndStep(t); ensures r == t ==> IsTerminal(t); ensures r != t ==> @@ -255,7 +255,7 @@ ghost method Lemma_FindAndStep(t: Term) returns (r: Term, C: Context, u: Term) // The proof of the lemma above used one more lemma, namely one that enumerates // lays out the options for how to represent a term as a C[u] pair. -ghost method Lemma_ContextPossibilities(t: Term) +lemma Lemma_ContextPossibilities(t: Term) ensures forall C,u :: IsContext(C) && t == EvalExpr(C, u) ==> (C == Hole && t == u) || (t.Apply? && exists D :: C == C_term(D, t.cdr) && t.car == EvalExpr(D, u)) || @@ -442,7 +442,7 @@ function method ks(n: nat): Term // VerificationTask2) it computes the same thing as method VerificationTask2 // does. -ghost method VerificationTask3() +lemma VerificationTask3() ensures forall n: nat :: TerminatingReduction(ks(n)) == if n % 2 == 0 then K else Apply(K, K); { @@ -451,13 +451,13 @@ ghost method VerificationTask3() } } -ghost method VT3(n: nat) +lemma VT3(n: nat) ensures TerminatingReduction(ks(n)) == if n % 2 == 0 then K else Apply(K, K); { // Dafny's (way cool) induction tactic kicks in and proves the following // assertion automatically: assert forall p :: 2 <= p ==> FindAndStep(ks(p)) == ks(p-2); - // And then Dafny's (cool beyond words) induction tactic for ghost methods kicks + // And then Dafny's (cool beyond words) induction tactic for lemmas kicks // in to prove the postcondition. (If this got you curious, scope out Leino's // VMCAI 2012 paper "Automating Induction with an SMT Solver".) } diff --git a/Test/vstte2012/Tree.dfy b/Test/vstte2012/Tree.dfy index 4a45d011..a346aac5 100644 --- a/Test/vstte2012/Tree.dfy +++ b/Test/vstte2012/Tree.dfy @@ -75,7 +75,7 @@ function method build(s: seq): Result } -// This ghost methods encodes the main lemma for the +// This is the main lemma for the // completeness theorem. If a sequence s starts with a // valid encoding of a tree t then build_rec yields a // result (i.e., does not fail) and the rest of the sequence. @@ -83,8 +83,8 @@ function method build(s: seq): Result // induction on t. Dafny proves termination (using the // height of the term t as termination measure), which // ensures that the induction hypothesis is applied -// correctly (encoded by calls to this ghost method). -ghost method lemma0(t: Tree, d: int, s: seq) +// correctly (encoded by calls to this lemma). +lemma lemma0(t: Tree, d: int, s: seq) ensures build_rec(d, toList(d, t) + s).Res? && build_rec(d, toList(d, t) + s).sOut == s; { @@ -100,13 +100,13 @@ ghost method lemma0(t: Tree, d: int, s: seq) } -// This ghost method encodes a lemma that states the +// This lemma states the // completeness property. It is proved by applying the // main lemma (lemma0). In this lemma, the bound variables // of the completeness theorem are passed as arguments; -// the following two ghost methods replace these arguments +// the following two lemmas replace these arguments // by quantified variables. -ghost method lemma1(t: Tree, s:seq) +lemma lemma1(t: Tree, s:seq) requires s == toList(0, t) + []; ensures build(s).Res?; { @@ -114,9 +114,9 @@ ghost method lemma1(t: Tree, s:seq) } -// This ghost method encodes a lemma that introduces the -// existential quantifier in the completeness property. -ghost method lemma2(s: seq) +// This lemma introduces the existential quantifier in the completeness +// property. +lemma lemma2(s: seq) ensures (exists t: Tree :: toList(0,t) == s) ==> build(s).Res?; { forall t | toList(0,t) == s { @@ -125,12 +125,12 @@ ghost method lemma2(s: seq) } -// This ghost method encodes the completeness theorem. +// This lemma encodes the completeness theorem. // For each sequence for which there is a corresponding // tree, function build yields a result different from Fail. // The body of the method converts the argument of lemma2 // into a universally quantified variable. -ghost method completeness() +lemma completeness() ensures forall s: seq :: ((exists t: Tree :: toList(0,t) == s) ==> build(s).Res?); { forall s { @@ -147,19 +147,6 @@ method harness0() ensures build([1,3,3,2]).Res? && build([1,3,3,2]).t == Node(Leaf, Node(Node(Leaf, Leaf), Leaf)); { - assert build_rec(2, [2]) == - Res(Leaf, []); - assert build_rec(2, [3,3,2]) == - Res(Node(Leaf, Leaf), [2]); - assert build_rec(1, [3,3,2]) == - Res(Node(Node(Leaf, Leaf), Leaf), []); - assert build_rec(1, [1,3,3,2]) == - Res(Leaf, [3,3,2]); - assert build_rec(0, [1,3,3,2]) == - Res( - Node(build_rec(1, [1,3,3,2]).t, - build_rec(1, [3,3,2]).t), - []); } @@ -170,8 +157,4 @@ method harness0() method harness1() ensures build([1,3,2,2]).Fail?; { - assert build_rec(1,[1,3,2,2]) == Res(Leaf, [3,2,2]); - assert build_rec(3,[2,2]).Fail?; - assert build_rec(2,[3,2,2]).Fail?; - assert build_rec(1,[3,2,2]).Fail?; } -- cgit v1.2.3 From 1258fd132d80cfdba5e59cfd76c517a091269d2d Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Tue, 28 Jul 2015 17:56:40 -0700 Subject: Save failing tests to failing.lst, making it easy to re-run them --- Test/runTests.py | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/Test/runTests.py b/Test/runTests.py index 62ab205b..f9443426 100644 --- a/Test/runTests.py +++ b/Test/runTests.py @@ -184,6 +184,15 @@ class Test: for test in tests: debug(Debug.REPORT, "* " + test.dfy, headers=status, silentheaders=True) + debug(Debug.REPORT) + + failing = [t for t in results if t.status != TestStatus.PASSED] + if failing: + with open("failing.lst", mode='w') as writer: + for t in failing: + writer.write("{}\t{}\n".format(t.name, t.source_path)) + debug(Debug.REPORT, "Some tests failed: use [runTests.py failing.lst] to rerun the failing tests") + debug(Debug.REPORT, "Testing took {:.2f}s on {} thread(s)".format(results[0].suite_time, results[0].njobs)) @@ -265,8 +274,8 @@ class Test: def setup_parser(): parser = argparse.ArgumentParser(description='Run the Dafny test suite.') - parser.add_argument('paths', type=str, action='store', nargs='+', - help='Input files or folders. Folders are searched for .dfy files.') + parser.add_argument('path', type=str, action='store', nargs='+', + help='Input files or folders. Folders are searched for .dfy files. Lists of files can also be specified by passing a .lst file (for an example of such a file, look at failing.lst after running failing tests.') parser.add_argument('--compiler', type=str, action='append', default=None, help='Dafny executable. Default: {}'.format(Defaults.DAFNY_BIN)) @@ -286,6 +295,9 @@ def setup_parser(): parser.add_argument('--verbosity', action='store', type=int, default=1, help='Set verbosity level. 0: Minimal; 1: Some info; 2: More info.') + parser.add_argument('-v', action='store_const', default=1, dest="verbosity", const=2, + help='Short for --verbosity 2.') + parser.add_argument('--report', '-r', action='store', type=str, default=None, help='Give an explicit name to the report file. Defaults to the current date and time.') @@ -298,13 +310,13 @@ def setup_parser(): parser.add_argument('--time-all', action='store_true', help="When comparing, include all timings.") - parser.add_argument('--diff', '-d', action='store_const', const=True, default=False, + parser.add_argument('--diff', '-d', action='store_true', help="Don't run tests; show differences between outputs and .expect files, optionally overwritting .expect files.") - parser.add_argument('--accept', '-a', action='store_const', const=True, default=False, + parser.add_argument('--accept', '-a', action='store_true', help="Don't run tests; copy outputs to .expect files.") - parser.add_argument('--open', '-o', action='store_const', const=True, default=False, + parser.add_argument('--open', '-o', action='store_true', help="Don't run tests; open one file.") parser.add_argument('--difftool', action='store', type=str, default="diff", @@ -363,6 +375,12 @@ def find_one(name, fname, compiler_cmds, timeout): yield from read_one_test(name, fname, compiler_cmds, timeout) else: debug(Debug.ERROR, "Test file {} not found".format(fname)) + elif ext == ".lst": + debug(Debug.INFO, "Loading tests from {}".format(fname)) + with open(fname) as reader: + for line in reader: + _name, _path = line.strip().split() + yield from find_one(_name, _path, compiler_cmds, timeout) else: debug(Debug.TRACE, "Ignoring {}".format(fname)) @@ -390,7 +408,7 @@ def run_tests(args): debug(Debug.ERROR, "Compiler not found: {}".format(compiler)) return - tests = list(find_tests(args.paths, [compiler + ' ' + " ".join(args.base_flags + args.flags) + tests = list(find_tests(args.path, [compiler + ' ' + " ".join(args.base_flags + args.flags) for compiler in args.compiler], args.exclude + Defaults.ALWAYS_EXCLUDED, args.timeout)) tests.sort(key=operator.attrgetter("name")) @@ -492,11 +510,11 @@ def main(): debug(Debug.WARNING, "If you run into issues, try using Windows' Python instead of Cygwin's") if args.diff or args.accept: - diff(args.paths, args.accept, args.difftool) + diff(args.path, args.accept, args.difftool) elif args.open: - os.startfile(args.paths[0]) + os.startfile(args.path[0]) elif args.compare: - compare_results(args.paths, args.time_all) + compare_results(args.path, args.time_all) else: run_tests(args) -- cgit v1.2.3 From 13bb45d5350cb6b0ce6b731d6552780b0d147265 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Wed, 29 Jul 2015 16:55:49 -0700 Subject: Update the VS extension to use the error interface defined in 576eac2e17ff --- Source/Dafny/Parser.cs | 2 +- Source/Dafny/Util.cs | 10 +++++----- Source/DafnyExtension/DafnyDriver.cs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs index 86a185a4..fd6fb026 100644 --- a/Source/Dafny/Parser.cs +++ b/Source/Dafny/Parser.cs @@ -4702,7 +4702,7 @@ public class Errors { count++; } - public void Warning(IToken tok, string msg) { // warnings + public virtual void Warning(IToken tok, string msg) { // warnings Contract.Requires(tok != null); Contract.Requires(msg != null); Dafny.Util.ReportIssue("Warning", tok, msg); diff --git a/Source/Dafny/Util.cs b/Source/Dafny/Util.cs index a0a7fe36..508d23c6 100644 --- a/Source/Dafny/Util.cs +++ b/Source/Dafny/Util.cs @@ -67,26 +67,26 @@ namespace Microsoft.Dafny { return res; } - internal static void ReportIssue(string header, IToken tok, string msg, params object[] args) { + public static void ReportIssue(string header, IToken tok, string msg, params object[] args) { ReportIssue(header, tok, String.Format(msg, args)); } - internal static void ReportIssue(string header, IToken tok, string msg) { + public static void ReportIssue(string header, IToken tok, string msg) { ReportIssue(header, tok.filename, tok.line, tok.col, msg); } - internal static void ReportIssue(string header, string filename, int line, int column, string msg) { + public static void ReportIssue(string header, string filename, int line, int column, string msg) { Console.WriteLine(ReportIssueToString(header, filename, line, column, msg)); } - internal static string ReportIssueToString(string header, string filename, int line, int column, string msg) { + public static string ReportIssueToString(string header, string filename, int line, int column, string msg) { Contract.Requires(header != null); Contract.Requires(filename != null); Contract.Requires(msg != null); return ReportIssueToString_Bare(": " + header, filename, line, column, ": " + msg); } - internal static string ReportIssueToString_Bare(string header, string filename, int line, int column, string msg) { + public static string ReportIssueToString_Bare(string header, string filename, int line, int column, string msg) { return String.Format("{0}({1},{2}){3}{4}", filename, line, column - 1, header, msg ?? ""); } diff --git a/Source/DafnyExtension/DafnyDriver.cs b/Source/DafnyExtension/DafnyDriver.cs index 61328fbf..640ef67c 100644 --- a/Source/DafnyExtension/DafnyDriver.cs +++ b/Source/DafnyExtension/DafnyDriver.cs @@ -151,8 +151,8 @@ namespace DafnyLanguage dd.RecordError(filename, line - 1, col - 1, ErrorCategory.ResolveError, msg); count++; } - public override void Warning(string filename, int line, int col, string msg) { - dd.RecordError(filename, line - 1, col - 1, ErrorCategory.ParseWarning, msg); + public override void Warning(IToken tok, string msg) { + dd.RecordError(tok.filename, tok.line - 1, tok.col - 1, ErrorCategory.ParseWarning, msg); } } -- cgit v1.2.3 From 7e7eb1a4177e03f6bb22411b7246db1d26f5ed27 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 29 Jul 2015 17:35:18 -0700 Subject: Fixed crash in resolution where, after reporting an error, the cases #type and #module were not handled --- Source/Dafny/Resolver.cs | 4 ++++ Test/dafny0/ResolutionErrors.dfy | 24 ++++++++++++++++++++++++ Test/dafny0/ResolutionErrors.dfy.expect | 16 +++++++++++++++- 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index e3083133..e977dbd5 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -4491,6 +4491,10 @@ namespace Microsoft.Dafny // something is wrong; either aa or bb wasn't properly resolved, or they don't unify return false; } + } else if (a is Resolver_IdentifierExpr.ResolverType_Type) { + return b is Resolver_IdentifierExpr.ResolverType_Type; + } else if (a is Resolver_IdentifierExpr.ResolverType_Module) { + return b is Resolver_IdentifierExpr.ResolverType_Module; } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type } diff --git a/Test/dafny0/ResolutionErrors.dfy b/Test/dafny0/ResolutionErrors.dfy index 8c910959..900c7459 100644 --- a/Test/dafny0/ResolutionErrors.dfy +++ b/Test/dafny0/ResolutionErrors.dfy @@ -1349,3 +1349,27 @@ module TupleEqualitySupport { datatype GoodRecord = GoodRecord(set<(int,int)>) datatype BadRecord = BadRecord(set<(int, int->bool)>) // error: this tuple type does not support equality } + +// ------------------- non-type variable names ------------------- + +module NonTypeVariableNames { + type X = int + + module Y { } + + method M(m: map) + { + assert X == X; // error (x2): type name used as variable + assert Y == Y; // error (x2): module name used as variable + assert X in m; // error (x2): type name used as variable + assert Y in m; // error (x2): module name used as variable + } + + method N(k: int) + { + assert k == X; // error (x2): type name used as variable + assert k == Y; // error (x2): module name used as variable + X := k; // error: type name used as variable + Y := k; // error: module name used as variable + } +} diff --git a/Test/dafny0/ResolutionErrors.dfy.expect b/Test/dafny0/ResolutionErrors.dfy.expect index ae3e8cbf..481b47e0 100644 --- a/Test/dafny0/ResolutionErrors.dfy.expect +++ b/Test/dafny0/ResolutionErrors.dfy.expect @@ -90,6 +90,20 @@ ResolutionErrors.dfy(1329,15): Error: The name Inner ambiguously refers to a typ ResolutionErrors.dfy(1339,29): Error: ghost variables are allowed only in specification contexts ResolutionErrors.dfy(1341,49): Error: ghost variables are allowed only in specification contexts ResolutionErrors.dfy(1341,54): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(1362,11): Error: name of type (X) is used as a variable +ResolutionErrors.dfy(1362,16): Error: name of type (X) is used as a variable +ResolutionErrors.dfy(1363,11): Error: name of module (Y) is used as a variable +ResolutionErrors.dfy(1363,16): Error: name of module (Y) is used as a variable +ResolutionErrors.dfy(1364,11): Error: name of type (X) is used as a variable +ResolutionErrors.dfy(1364,13): Error: second argument to "in" must be a set, multiset, or sequence with elements of type #type, or a map with domain #type (instead got map) +ResolutionErrors.dfy(1365,11): Error: name of module (Y) is used as a variable +ResolutionErrors.dfy(1365,13): Error: second argument to "in" must be a set, multiset, or sequence with elements of type #module, or a map with domain #module (instead got map) +ResolutionErrors.dfy(1370,16): Error: name of type (X) is used as a variable +ResolutionErrors.dfy(1370,13): Error: arguments must have the same type (got int and #type) +ResolutionErrors.dfy(1371,16): Error: name of module (Y) is used as a variable +ResolutionErrors.dfy(1371,13): Error: arguments must have the same type (got int and #module) +ResolutionErrors.dfy(1372,4): Error: name of type (X) is used as a variable +ResolutionErrors.dfy(1373,4): Error: name of module (Y) is used as a variable ResolutionErrors.dfy(432,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') @@ -182,4 +196,4 @@ ResolutionErrors.dfy(1106,8): Error: new cannot be applied to a trait ResolutionErrors.dfy(1127,13): Error: first argument to / must be of numeric type (instead got set) ResolutionErrors.dfy(1134,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(1149,14): Error: a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating -184 resolution/type errors detected in ResolutionErrors.dfy +198 resolution/type errors detected in ResolutionErrors.dfy -- cgit v1.2.3 From e93d4804f9d99150079728572c00a518af20a7c5 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 30 Jul 2015 12:04:16 -0700 Subject: Implement a Dafny server. The Dafny server is a command line utility that allows non-.Net editors to take advantage of Dafny's caching facilities, as used by the Dafny extension for Visual Studio. The server is essentially a REPL, which produces output in the same format as the Dafny CLI; clients thus do not need to understand the internals of Dafny's caching. A typical editing session proceeds as follows: * When a new Dafny file is opened, the editor starts a new instance of the Dafny server. The cache is blank at that point. * The editor sends a copy of the buffer for initial verification. This takes some time, after which the server returns a list of errors. * The user makes modifications; the editor periodically sends a new copy of the buffer's contents to the Dafny server, which quickly returns an updated list of errors. The client-server protocol is sequential, uses JSON, and works over ASCII pipes by base64-encoding queries. It defines one type of query, and two types of responses: Queries are of the following form: verify [[DAFNY-CLIENT: EOM]] Responses are of the following form: [SUCCESS] [[DAFNY-SERVER: EOM]] or [FAILURE] [[DAFNY-SERVER: EOM]] The JSON payload is an array with 4 fields: * args: An array of Dafny arguments, as passed to the Dafny CLI * source: A Dafny program, or the path to a Dafny source file. * sourceIsFile: A boolean indicating whether the 'source' argument is a Dafny program or the path to one. * filename: The name of the original source file, to be used in error messages For small files, embedding the Dafny source directly into a message is convenient; for larger files, however, it is generally better for performance to write the source snapshot to a separate file, and to pass that to Dafny by setting the 'sourceIsFile' flag to true. --- Source/DafnyServer.sln | 20 ++ Source/DafnyServer/App.config | 6 + Source/DafnyServer/DafnyServer.csproj | 75 +++++++ Source/DafnyServer/Properties/AssemblyInfo.cs | 36 ++++ Source/DafnyServer/Server.cs | 269 ++++++++++++++++++++++++++ 5 files changed, 406 insertions(+) create mode 100644 Source/DafnyServer.sln create mode 100644 Source/DafnyServer/App.config create mode 100644 Source/DafnyServer/DafnyServer.csproj create mode 100644 Source/DafnyServer/Properties/AssemblyInfo.cs create mode 100644 Source/DafnyServer/Server.cs diff --git a/Source/DafnyServer.sln b/Source/DafnyServer.sln new file mode 100644 index 00000000..217b1c74 --- /dev/null +++ b/Source/DafnyServer.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DafnyServer", "DafnyServer\DafnyServer.csproj", "{AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Source/DafnyServer/App.config b/Source/DafnyServer/App.config new file mode 100644 index 00000000..fad249e4 --- /dev/null +++ b/Source/DafnyServer/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Source/DafnyServer/DafnyServer.csproj b/Source/DafnyServer/DafnyServer.csproj new file mode 100644 index 00000000..0aac88b2 --- /dev/null +++ b/Source/DafnyServer/DafnyServer.csproj @@ -0,0 +1,75 @@ + + + + + Debug + AnyCPU + {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2} + Exe + Properties + DafnyServer + DafnyServer + v4.5 + 512 + Microsoft.Dafny.Server + + + true + full + false + ..\..\Binaries\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + ..\..\Binaries\ + TRACE + prompt + 4 + + + + ..\..\..\boogie\Binaries\Core.dll + + + ..\..\..\boogie\Binaries\ExecutionEngine.dll + + + ..\..\..\boogie\Binaries\ParserHelper.dll + + + ..\..\..\boogie\Binaries\Provers.SMTLib.dll + + + + + + + + ..\..\Binaries\DafnyPipeline.dll + + + + + + + + + + + PreserveNewest + + + + + diff --git a/Source/DafnyServer/Properties/AssemblyInfo.cs b/Source/DafnyServer/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..5b996543 --- /dev/null +++ b/Source/DafnyServer/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Dafny Server")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft Research")] +[assembly: AssemblyProduct("Dafny Server")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("44d9c1c1-773b-47c1-b876-2de17e70152e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Source/DafnyServer/Server.cs b/Source/DafnyServer/Server.cs new file mode 100644 index 00000000..36c28627 --- /dev/null +++ b/Source/DafnyServer/Server.cs @@ -0,0 +1,269 @@ +using System; +using System.IO; +using System.Text; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Json; + +using Dafny = Microsoft.Dafny; +using Bpl = Microsoft.Boogie; +using Microsoft.Boogie; + +namespace Microsoft.Dafny { + class Interaction { + internal static string SUCCESS = "SUCCESS"; + internal static string FAILURE = "FAILURE"; + internal static string SERVER_EOM_TAG = "[[DAFNY-SERVER: EOM]]"; + internal static string CLIENT_EOM_TAG = "[[DAFNY-CLIENT: EOM]]"; + + internal static void EOM(string header, string msg) { + var trailer = (msg == null) ? "" : "\n"; + Console.Write("{0}{1}[{2}] {3}\n", msg ?? "", trailer, header, SERVER_EOM_TAG); + Console.Out.Flush(); + } + + internal static void EOM(string header, Exception ex, string subHeader="") { + var aggregate = ex as AggregateException; + subHeader = String.IsNullOrEmpty(subHeader) ? "" : subHeader + " "; + + if (aggregate == null) { + EOM(header, subHeader + ex.Message); + } else { + EOM(header, subHeader + aggregate.InnerExceptions.MapConcat(exn => exn.Message, ", ")); + } + } + } + + // FIXME: This should not be duplicated here + class DafnyConsolePrinter : ConsolePrinter { + public override void ReportBplError(IToken tok, string message, bool error, TextWriter tw, string category = null) { + // Dafny has 0-indexed columns, but Boogie counts from 1 + var realigned_tok = new Token(tok.line, tok.col - 1); + realigned_tok.kind = tok.kind; + realigned_tok.pos = tok.pos; + realigned_tok.val = tok.val; + realigned_tok.filename = tok.filename; + base.ReportBplError(realigned_tok, message, error, tw, category); + + if (tok is Dafny.NestedToken) { + var nt = (Dafny.NestedToken)tok; + ReportBplError(nt.Inner, "Related location", false, tw); + } + } + } + + [Serializable] + class VerificationTask { + [DataMember] + string[] args = null; + + [DataMember] + string filename = null; + + [DataMember] + string source = null; + + [DataMember] + bool sourceIsFile = false; + + string ProgramSource { get { return sourceIsFile ? File.ReadAllText(source) : source; } } + + internal static VerificationTask ReadTask(string b64_repr) { + try { + var json = Encoding.UTF8.GetString(System.Convert.FromBase64String(b64_repr)); + using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json))) { + DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(VerificationTask)); + return (VerificationTask)serializer.ReadObject(ms); + } + } catch (Exception ex) { + throw new ServerException("Deserialization failed: {0}.", ex.Message); + } + } + + internal static void SelfTest() { + var task = new VerificationTask() { + filename = "", + sourceIsFile = false, + args = new string[] { }, + source = "method selftest() { assert true; }" + }; + try { + task.Run(); + Interaction.EOM(Interaction.SUCCESS, null); + } catch (Exception ex) { + Interaction.EOM(Interaction.FAILURE, ex); + } + } + + internal void Run() { + Server.ApplyArgs(args); + new DafnyHelper(filename, ProgramSource).Verify(); + } + } + + class ServerException : Exception { + internal ServerException(string message) : base(message) { } + internal ServerException(string message, params object[] args) : base(String.Format(message, args)) { } + } + + class Server { + private bool running; + + static void Main(string[] args) { + Server server = new Server(); + if (args.Length > 0) { + if (args[0] == "selftest") { + VerificationTask.SelfTest(); + } else { + Console.WriteLine("Not sure what to do with '{0}'", String.Join(" ", args)); + } + } else { + server.Loop(); + } + } + + public Server() { + this.running = true; + Console.CancelKeyPress += this.CancelKeyPress; + ExecutionEngine.printer = new DafnyConsolePrinter(); + } + + internal static void ApplyArgs(string[] args) { + Dafny.DafnyOptions.Install(new Dafny.DafnyOptions()); + if (CommandLineOptions.Clo.Parse(args)) { + // Dafny.DafnyOptions.O.ErrorTrace = 0; //FIXME + // Dafny.DafnyOptions.O.ModelViewFile = "-"; + Dafny.DafnyOptions.O.ProverKillTime = 10; + Dafny.DafnyOptions.O.VcsCores = Math.Max(1, System.Environment.ProcessorCount - 1); + Dafny.DafnyOptions.O.VerifySnapshots = 2; + } else { + throw new ServerException("Invalid command line options"); + } + } + + void CancelKeyPress(object sender, ConsoleCancelEventArgs e) { + e.Cancel = true; + // FIXME TerminateProver and RunningProverFromCommandLine + // Cancel the current verification? TerminateProver() ? Or kill entirely? + } + + static bool EndOfPayload(out string line) { + line = Console.ReadLine(); + return line == null || line == Interaction.CLIENT_EOM_TAG; + } + + static string ReadPayload() { + StringBuilder buffer = new StringBuilder(); + string line = null; + while (!EndOfPayload(out line)) { + buffer.Append(line); + } + return buffer.ToString(); + } + + void Loop() { + for (int cycle = 0; running; cycle++) { + var line = Console.ReadLine() ?? "quit"; + var command = line.Split(); + Respond(command); + } + } + + void Respond(string[] command) { + try { + if (command.Length == 0) { + throw new ServerException("Empty command"); + } + + var verb = command[0]; + if (verb == "verify") { + checkArgs(command, 0); + var payload = ReadPayload(); + VerificationTask.ReadTask(payload).Run(); + } else if (verb == "quit") { + checkArgs(command, 0); + Exit(); + } else { + throw new ServerException("Unknown verb '{0}'", verb); + } + + Interaction.EOM(Interaction.SUCCESS, "Verification completed successfully!"); + } catch (ServerException ex) { + Interaction.EOM(Interaction.FAILURE, ex); + } catch (Exception ex) { + Interaction.EOM(Interaction.FAILURE, ex, "[FATAL]"); + running = false; + } + } + + void checkArgs(string[] command, int expectedLength) { + if (command.Length - 1 != expectedLength) { + throw new ServerException("Invalid argument count (got {0}, expected {1})", command.Length - 1, expectedLength); + } + } + + void Exit() { + this.running = false; + } + } + + class DafnyHelper { + private string fname; + private string source; + + private Dafny.Errors errors; + private Dafny.Program dafnyProgram; + private Bpl.Program boogieProgram; + + public DafnyHelper(string fname, string source) { + this.fname = fname; + this.source = source; + this.errors = new Dafny.Errors(); + } + + public bool Verify() { + return Parse() && Resolve() && Translate() && Boogie(); + } + + private bool Parse() { + Dafny.ModuleDecl module = new Dafny.LiteralModuleDecl(new Dafny.DefaultModuleDecl(), null); + Dafny.BuiltIns builtIns = new Dafny.BuiltIns(); + var success = (Dafny.Parser.Parse(source, fname, fname, module, builtIns, errors) == 0 && + Dafny.Main.ParseIncludes(module, builtIns, new List(), errors) == null); + if (success) { + dafnyProgram = new Dafny.Program(fname, module, builtIns); + } + return success; + } + + private bool Resolve() { + var resolver = new Dafny.Resolver(dafnyProgram); + resolver.ResolveProgram(dafnyProgram); + return resolver.ErrorCount == 0; + } + + private bool Translate() { + var translator = new Dafny.Translator() { InsertChecksums = true, UniqueIdPrefix = null }; //FIXME check if null is OK for UniqueIdPrefix + boogieProgram = translator.Translate(dafnyProgram); // FIXME how are translation errors reported? + return true; + } + + // FIXME var boogieFilename = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Path.GetFileName(fname), "bpl")); + private bool Boogie() { + if (boogieProgram.Resolve() == 0 && boogieProgram.Typecheck() == 0) { + ExecutionEngine.EliminateDeadVariables(boogieProgram); + ExecutionEngine.CollectModSets(boogieProgram); + ExecutionEngine.CoalesceBlocks(boogieProgram); + ExecutionEngine.Inline(boogieProgram); + + switch (ExecutionEngine.InferAndVerify(boogieProgram, new PipelineStatistics(), null, null, DateTime.UtcNow.Ticks.ToString())) { // FIXME check if null is ok for programId and error delegate + case PipelineOutcome.Done: + case PipelineOutcome.VerificationCompleted: + return true; + } + } + + return false; + } + } +} \ No newline at end of file -- cgit v1.2.3 From 8c1a52e085aed20f62a5d24fb2af9bbd5cb3e469 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 30 Jul 2015 14:56:08 -0700 Subject: Add quotes in snapshot tests. Thanks Valentin for elucidating this issue! --- Test/dafny0/snapshots/Snapshots0.run.dfy | 2 +- Test/dafny0/snapshots/Snapshots1.run.dfy | 2 +- Test/dafny0/snapshots/Snapshots2.run.dfy | 2 +- Test/dafny0/snapshots/Snapshots3.run.dfy | 2 +- Test/dafny0/snapshots/Snapshots4.run.dfy | 2 +- Test/dafny0/snapshots/Snapshots5.run.dfy | 2 +- Test/dafny0/snapshots/Snapshots6.run.dfy | 2 +- Test/dafny0/snapshots/Snapshots7.run.dfy | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Test/dafny0/snapshots/Snapshots0.run.dfy b/Test/dafny0/snapshots/Snapshots0.run.dfy index cb96468e..5e016c12 100644 --- a/Test/dafny0/snapshots/Snapshots0.run.dfy +++ b/Test/dafny0/snapshots/Snapshots0.run.dfy @@ -1,2 +1,2 @@ -// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 %S/Inputs/Snapshots0.dfy > "%t" +// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 "%S/Inputs/Snapshots0.dfy" > "%t" // RUN: %diff "%s.expect" "%t" diff --git a/Test/dafny0/snapshots/Snapshots1.run.dfy b/Test/dafny0/snapshots/Snapshots1.run.dfy index 7c277b3e..1907f4a0 100644 --- a/Test/dafny0/snapshots/Snapshots1.run.dfy +++ b/Test/dafny0/snapshots/Snapshots1.run.dfy @@ -1,2 +1,2 @@ -// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 %S/Inputs/Snapshots1.dfy > "%t" +// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 "%S/Inputs/Snapshots1.dfy" > "%t" // RUN: %diff "%s.expect" "%t" diff --git a/Test/dafny0/snapshots/Snapshots2.run.dfy b/Test/dafny0/snapshots/Snapshots2.run.dfy index 889a8153..71f3e18a 100644 --- a/Test/dafny0/snapshots/Snapshots2.run.dfy +++ b/Test/dafny0/snapshots/Snapshots2.run.dfy @@ -1,2 +1,2 @@ -// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 %S/Inputs/Snapshots2.dfy > "%t" +// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 "%S/Inputs/Snapshots2.dfy" > "%t" // RUN: %diff "%s.expect" "%t" diff --git a/Test/dafny0/snapshots/Snapshots3.run.dfy b/Test/dafny0/snapshots/Snapshots3.run.dfy index 3df182d6..40dd1012 100644 --- a/Test/dafny0/snapshots/Snapshots3.run.dfy +++ b/Test/dafny0/snapshots/Snapshots3.run.dfy @@ -1,2 +1,2 @@ -// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 %S/Inputs/Snapshots3.dfy > "%t" +// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 "%S/Inputs/Snapshots3.dfy" > "%t" // RUN: %diff "%s.expect" "%t" diff --git a/Test/dafny0/snapshots/Snapshots4.run.dfy b/Test/dafny0/snapshots/Snapshots4.run.dfy index fd6bef41..803403cf 100644 --- a/Test/dafny0/snapshots/Snapshots4.run.dfy +++ b/Test/dafny0/snapshots/Snapshots4.run.dfy @@ -1,2 +1,2 @@ -// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 %S/Inputs/Snapshots4.dfy > "%t" +// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 "%S/Inputs/Snapshots4.dfy" > "%t" // RUN: %diff "%s.expect" "%t" diff --git a/Test/dafny0/snapshots/Snapshots5.run.dfy b/Test/dafny0/snapshots/Snapshots5.run.dfy index 4f26aac4..e0f3b16b 100644 --- a/Test/dafny0/snapshots/Snapshots5.run.dfy +++ b/Test/dafny0/snapshots/Snapshots5.run.dfy @@ -1,2 +1,2 @@ -// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 %S/Inputs/Snapshots5.dfy > "%t" +// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 "%S/Inputs/Snapshots5.dfy" > "%t" // RUN: %diff "%s.expect" "%t" diff --git a/Test/dafny0/snapshots/Snapshots6.run.dfy b/Test/dafny0/snapshots/Snapshots6.run.dfy index 157fc5b7..8f958cb9 100644 --- a/Test/dafny0/snapshots/Snapshots6.run.dfy +++ b/Test/dafny0/snapshots/Snapshots6.run.dfy @@ -1,2 +1,2 @@ -// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 %S/Inputs/Snapshots6.dfy > "%t" +// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 "%S/Inputs/Snapshots6.dfy" > "%t" // RUN: %diff "%s.expect" "%t" diff --git a/Test/dafny0/snapshots/Snapshots7.run.dfy b/Test/dafny0/snapshots/Snapshots7.run.dfy index b192f090..c84c41d2 100644 --- a/Test/dafny0/snapshots/Snapshots7.run.dfy +++ b/Test/dafny0/snapshots/Snapshots7.run.dfy @@ -1,2 +1,2 @@ -// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 %S/Inputs/Snapshots7.dfy > "%t" +// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 "%S/Inputs/Snapshots7.dfy" > "%t" // RUN: %diff "%s.expect" "%t" -- cgit v1.2.3 From 48fed349a2fc592e7f015ecaa6cf98582446278f Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 30 Jul 2015 17:43:13 -0700 Subject: Fix an issue where the server would reverify the same file multiple times. The confusing part here is that if one passes null as the ProgramId for two consecutive calls to Boogie, then Boogie will return the same results twice, regardless of what the second program was. --- Source/DafnyServer/Server.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Source/DafnyServer/Server.cs b/Source/DafnyServer/Server.cs index 36c28627..660188ba 100644 --- a/Source/DafnyServer/Server.cs +++ b/Source/DafnyServer/Server.cs @@ -114,6 +114,9 @@ namespace Microsoft.Dafny { if (args.Length > 0) { if (args[0] == "selftest") { VerificationTask.SelfTest(); + } else if (File.Exists(args[0])) { + Console.SetIn(new StreamReader(args[0])); + server.Loop(); } else { Console.WriteLine("Not sure what to do with '{0}'", String.Join(" ", args)); } @@ -248,15 +251,14 @@ namespace Microsoft.Dafny { return true; } - // FIXME var boogieFilename = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Path.GetFileName(fname), "bpl")); private bool Boogie() { - if (boogieProgram.Resolve() == 0 && boogieProgram.Typecheck() == 0) { + if (boogieProgram.Resolve() == 0 && boogieProgram.Typecheck() == 0) { //FIXME ResolveAndTypecheck? ExecutionEngine.EliminateDeadVariables(boogieProgram); ExecutionEngine.CollectModSets(boogieProgram); ExecutionEngine.CoalesceBlocks(boogieProgram); ExecutionEngine.Inline(boogieProgram); - switch (ExecutionEngine.InferAndVerify(boogieProgram, new PipelineStatistics(), null, null, DateTime.UtcNow.Ticks.ToString())) { // FIXME check if null is ok for programId and error delegate + switch (ExecutionEngine.InferAndVerify(boogieProgram, new PipelineStatistics(), "ServerProgram", null, DateTime.UtcNow.Ticks.ToString())) { // FIXME check if null is ok for error delegate case PipelineOutcome.Done: case PipelineOutcome.VerificationCompleted: return true; -- cgit v1.2.3 From 990966db7dcea00045c5c104ded084348e8f7dde Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 31 Jul 2015 14:26:53 -0700 Subject: Split the server source into multiple files --- Source/DafnyServer/DafnyHelper.cs | 87 ++++++++++++++ Source/DafnyServer/DafnyServer.csproj | 7 +- Source/DafnyServer/Server.cs | 202 +++------------------------------ Source/DafnyServer/Utilities.cs | 60 ++++++++++ Source/DafnyServer/VerificationTask.cs | 59 ++++++++++ 5 files changed, 227 insertions(+), 188 deletions(-) create mode 100644 Source/DafnyServer/DafnyHelper.cs create mode 100644 Source/DafnyServer/Utilities.cs create mode 100644 Source/DafnyServer/VerificationTask.cs diff --git a/Source/DafnyServer/DafnyHelper.cs b/Source/DafnyServer/DafnyHelper.cs new file mode 100644 index 00000000..10d98677 --- /dev/null +++ b/Source/DafnyServer/DafnyHelper.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Boogie; +using Bpl = Microsoft.Boogie; + +namespace Microsoft.Dafny { + // FIXME: This should not be duplicated here + class DafnyConsolePrinter : ConsolePrinter { + public override void ReportBplError(IToken tok, string message, bool error, TextWriter tw, string category = null) { + // Dafny has 0-indexed columns, but Boogie counts from 1 + var realigned_tok = new Token(tok.line, tok.col - 1); + realigned_tok.kind = tok.kind; + realigned_tok.pos = tok.pos; + realigned_tok.val = tok.val; + realigned_tok.filename = tok.filename; + base.ReportBplError(realigned_tok, message, error, tw, category); + + if (tok is Dafny.NestedToken) { + var nt = (Dafny.NestedToken)tok; + ReportBplError(nt.Inner, "Related location", false, tw); + } + } + } + + class DafnyHelper { + private string fname; + private string source; + + private Dafny.Errors errors; + private Dafny.Program dafnyProgram; + private Bpl.Program boogieProgram; + + public DafnyHelper(string fname, string source) { + this.fname = fname; + this.source = source; + this.errors = new Dafny.Errors(); + } + + public bool Verify() { + return Parse() && Resolve() && Translate() && Boogie(); + } + + private bool Parse() { + Dafny.ModuleDecl module = new Dafny.LiteralModuleDecl(new Dafny.DefaultModuleDecl(), null); + Dafny.BuiltIns builtIns = new Dafny.BuiltIns(); + var success = (Dafny.Parser.Parse(source, fname, fname, module, builtIns, errors) == 0 && + Dafny.Main.ParseIncludes(module, builtIns, new List(), errors) == null); + if (success) { + dafnyProgram = new Dafny.Program(fname, module, builtIns); + } + return success; + } + + private bool Resolve() { + var resolver = new Dafny.Resolver(dafnyProgram); + resolver.ResolveProgram(dafnyProgram); + return resolver.ErrorCount == 0; + } + + private bool Translate() { + var translator = new Dafny.Translator() { InsertChecksums = true, UniqueIdPrefix = null }; //FIXME check if null is OK for UniqueIdPrefix + boogieProgram = translator.Translate(dafnyProgram); // FIXME how are translation errors reported? + return true; + } + + private bool Boogie() { + if (boogieProgram.Resolve() == 0 && boogieProgram.Typecheck() == 0) { //FIXME ResolveAndTypecheck? + ExecutionEngine.EliminateDeadVariables(boogieProgram); + ExecutionEngine.CollectModSets(boogieProgram); + ExecutionEngine.CoalesceBlocks(boogieProgram); + ExecutionEngine.Inline(boogieProgram); + + switch (ExecutionEngine.InferAndVerify(boogieProgram, new PipelineStatistics(), "ServerProgram", null, DateTime.UtcNow.Ticks.ToString())) { // FIXME check if null is ok for error delegate + case PipelineOutcome.Done: + case PipelineOutcome.VerificationCompleted: + return true; + } + } + + return false; + } + } +} diff --git a/Source/DafnyServer/DafnyServer.csproj b/Source/DafnyServer/DafnyServer.csproj index 0aac88b2..3da33f16 100644 --- a/Source/DafnyServer/DafnyServer.csproj +++ b/Source/DafnyServer/DafnyServer.csproj @@ -1,4 +1,4 @@ - + @@ -55,6 +55,9 @@ + + + @@ -72,4 +75,4 @@ --> - + \ No newline at end of file diff --git a/Source/DafnyServer/Server.cs b/Source/DafnyServer/Server.cs index 660188ba..c6f619d3 100644 --- a/Source/DafnyServer/Server.cs +++ b/Source/DafnyServer/Server.cs @@ -10,116 +10,21 @@ using Bpl = Microsoft.Boogie; using Microsoft.Boogie; namespace Microsoft.Dafny { - class Interaction { - internal static string SUCCESS = "SUCCESS"; - internal static string FAILURE = "FAILURE"; - internal static string SERVER_EOM_TAG = "[[DAFNY-SERVER: EOM]]"; - internal static string CLIENT_EOM_TAG = "[[DAFNY-CLIENT: EOM]]"; - - internal static void EOM(string header, string msg) { - var trailer = (msg == null) ? "" : "\n"; - Console.Write("{0}{1}[{2}] {3}\n", msg ?? "", trailer, header, SERVER_EOM_TAG); - Console.Out.Flush(); - } - - internal static void EOM(string header, Exception ex, string subHeader="") { - var aggregate = ex as AggregateException; - subHeader = String.IsNullOrEmpty(subHeader) ? "" : subHeader + " "; - - if (aggregate == null) { - EOM(header, subHeader + ex.Message); - } else { - EOM(header, subHeader + aggregate.InnerExceptions.MapConcat(exn => exn.Message, ", ")); - } - } - } - - // FIXME: This should not be duplicated here - class DafnyConsolePrinter : ConsolePrinter { - public override void ReportBplError(IToken tok, string message, bool error, TextWriter tw, string category = null) { - // Dafny has 0-indexed columns, but Boogie counts from 1 - var realigned_tok = new Token(tok.line, tok.col - 1); - realigned_tok.kind = tok.kind; - realigned_tok.pos = tok.pos; - realigned_tok.val = tok.val; - realigned_tok.filename = tok.filename; - base.ReportBplError(realigned_tok, message, error, tw, category); - - if (tok is Dafny.NestedToken) { - var nt = (Dafny.NestedToken)tok; - ReportBplError(nt.Inner, "Related location", false, tw); - } - } - } - - [Serializable] - class VerificationTask { - [DataMember] - string[] args = null; - - [DataMember] - string filename = null; - - [DataMember] - string source = null; - - [DataMember] - bool sourceIsFile = false; - - string ProgramSource { get { return sourceIsFile ? File.ReadAllText(source) : source; } } - - internal static VerificationTask ReadTask(string b64_repr) { - try { - var json = Encoding.UTF8.GetString(System.Convert.FromBase64String(b64_repr)); - using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json))) { - DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(VerificationTask)); - return (VerificationTask)serializer.ReadObject(ms); - } - } catch (Exception ex) { - throw new ServerException("Deserialization failed: {0}.", ex.Message); - } - } - - internal static void SelfTest() { - var task = new VerificationTask() { - filename = "", - sourceIsFile = false, - args = new string[] { }, - source = "method selftest() { assert true; }" - }; - try { - task.Run(); - Interaction.EOM(Interaction.SUCCESS, null); - } catch (Exception ex) { - Interaction.EOM(Interaction.FAILURE, ex); - } - } - - internal void Run() { - Server.ApplyArgs(args); - new DafnyHelper(filename, ProgramSource).Verify(); - } - } - - class ServerException : Exception { - internal ServerException(string message) : base(message) { } - internal ServerException(string message, params object[] args) : base(String.Format(message, args)) { } - } - class Server { private bool running; static void Main(string[] args) { Server server = new Server(); - if (args.Length > 0) { - if (args[0] == "selftest") { - VerificationTask.SelfTest(); - } else if (File.Exists(args[0])) { - Console.SetIn(new StreamReader(args[0])); - server.Loop(); - } else { - Console.WriteLine("Not sure what to do with '{0}'", String.Join(" ", args)); - } + + var hasArg = args.Length > 0; + var arg = hasArg ? args[0] : null; + + if (hasArg && args[0] == "selftest") { + VerificationTask.SelfTest(); + } else if (hasArg && File.Exists(arg)) { + Console.WriteLine("# Reading from {0}", arg); + Console.SetIn(new StreamReader(arg)); + server.Loop(); } else { server.Loop(); } @@ -131,19 +36,6 @@ namespace Microsoft.Dafny { ExecutionEngine.printer = new DafnyConsolePrinter(); } - internal static void ApplyArgs(string[] args) { - Dafny.DafnyOptions.Install(new Dafny.DafnyOptions()); - if (CommandLineOptions.Clo.Parse(args)) { - // Dafny.DafnyOptions.O.ErrorTrace = 0; //FIXME - // Dafny.DafnyOptions.O.ModelViewFile = "-"; - Dafny.DafnyOptions.O.ProverKillTime = 10; - Dafny.DafnyOptions.O.VcsCores = Math.Max(1, System.Environment.ProcessorCount - 1); - Dafny.DafnyOptions.O.VerifySnapshots = 2; - } else { - throw new ServerException("Invalid command line options"); - } - } - void CancelKeyPress(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; // FIXME TerminateProver and RunningProverFromCommandLine @@ -167,8 +59,10 @@ namespace Microsoft.Dafny { void Loop() { for (int cycle = 0; running; cycle++) { var line = Console.ReadLine() ?? "quit"; - var command = line.Split(); - Respond(command); + if (line != String.Empty && !line.StartsWith("#")) { + var command = line.Split(); + Respond(command); + } } } @@ -180,11 +74,11 @@ namespace Microsoft.Dafny { var verb = command[0]; if (verb == "verify") { - checkArgs(command, 0); + ServerUtils.checkArgs(command, 0); var payload = ReadPayload(); VerificationTask.ReadTask(payload).Run(); } else if (verb == "quit") { - checkArgs(command, 0); + ServerUtils.checkArgs(command, 0); Exit(); } else { throw new ServerException("Unknown verb '{0}'", verb); @@ -199,73 +93,9 @@ namespace Microsoft.Dafny { } } - void checkArgs(string[] command, int expectedLength) { - if (command.Length - 1 != expectedLength) { - throw new ServerException("Invalid argument count (got {0}, expected {1})", command.Length - 1, expectedLength); - } - } - void Exit() { this.running = false; } } - class DafnyHelper { - private string fname; - private string source; - - private Dafny.Errors errors; - private Dafny.Program dafnyProgram; - private Bpl.Program boogieProgram; - - public DafnyHelper(string fname, string source) { - this.fname = fname; - this.source = source; - this.errors = new Dafny.Errors(); - } - - public bool Verify() { - return Parse() && Resolve() && Translate() && Boogie(); - } - - private bool Parse() { - Dafny.ModuleDecl module = new Dafny.LiteralModuleDecl(new Dafny.DefaultModuleDecl(), null); - Dafny.BuiltIns builtIns = new Dafny.BuiltIns(); - var success = (Dafny.Parser.Parse(source, fname, fname, module, builtIns, errors) == 0 && - Dafny.Main.ParseIncludes(module, builtIns, new List(), errors) == null); - if (success) { - dafnyProgram = new Dafny.Program(fname, module, builtIns); - } - return success; - } - - private bool Resolve() { - var resolver = new Dafny.Resolver(dafnyProgram); - resolver.ResolveProgram(dafnyProgram); - return resolver.ErrorCount == 0; - } - - private bool Translate() { - var translator = new Dafny.Translator() { InsertChecksums = true, UniqueIdPrefix = null }; //FIXME check if null is OK for UniqueIdPrefix - boogieProgram = translator.Translate(dafnyProgram); // FIXME how are translation errors reported? - return true; - } - - private bool Boogie() { - if (boogieProgram.Resolve() == 0 && boogieProgram.Typecheck() == 0) { //FIXME ResolveAndTypecheck? - ExecutionEngine.EliminateDeadVariables(boogieProgram); - ExecutionEngine.CollectModSets(boogieProgram); - ExecutionEngine.CoalesceBlocks(boogieProgram); - ExecutionEngine.Inline(boogieProgram); - - switch (ExecutionEngine.InferAndVerify(boogieProgram, new PipelineStatistics(), "ServerProgram", null, DateTime.UtcNow.Ticks.ToString())) { // FIXME check if null is ok for error delegate - case PipelineOutcome.Done: - case PipelineOutcome.VerificationCompleted: - return true; - } - } - - return false; - } - } } \ No newline at end of file diff --git a/Source/DafnyServer/Utilities.cs b/Source/DafnyServer/Utilities.cs new file mode 100644 index 00000000..8fb03b05 --- /dev/null +++ b/Source/DafnyServer/Utilities.cs @@ -0,0 +1,60 @@ +using System; +using System.IO; +using System.Text; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Json; +using Microsoft.Boogie; + +namespace Microsoft.Dafny { + class Interaction { + internal static string SUCCESS = "SUCCESS"; + internal static string FAILURE = "FAILURE"; + internal static string SERVER_EOM_TAG = "[[DAFNY-SERVER: EOM]]"; + internal static string CLIENT_EOM_TAG = "[[DAFNY-CLIENT: EOM]]"; + + internal static void EOM(string header, string msg) { + var trailer = (msg == null) ? "" : "\n"; + Console.Write("{0}{1}[{2}] {3}\n", msg ?? "", trailer, header, SERVER_EOM_TAG); + Console.Out.Flush(); + } + + internal static void EOM(string header, Exception ex, string subHeader = "") { + var aggregate = ex as AggregateException; + subHeader = String.IsNullOrEmpty(subHeader) ? "" : subHeader + " "; + + if (aggregate == null) { + EOM(header, subHeader + ex.Message); + } else { + EOM(header, subHeader + aggregate.InnerExceptions.MapConcat(exn => exn.Message, ", ")); + } + } + } + + class ServerException : Exception { + internal ServerException(string message) : base(message) { } + internal ServerException(string message, params object[] args) : base(String.Format(message, args)) { } + } + + class ServerUtils { + internal static void checkArgs(string[] command, int expectedLength) { + if (command.Length - 1 != expectedLength) { + throw new ServerException("Invalid argument count (got {0}, expected {1})", command.Length - 1, expectedLength); + } + } + + internal static void ApplyArgs(string[] args) { + Dafny.DafnyOptions.Install(new Dafny.DafnyOptions()); + + if (CommandLineOptions.Clo.Parse(args)) { + // Dafny.DafnyOptions.O.ErrorTrace = 0; //FIXME + // Dafny.DafnyOptions.O.ModelViewFile = "-"; + Dafny.DafnyOptions.O.ProverKillTime = 10; + Dafny.DafnyOptions.O.VcsCores = Math.Max(1, System.Environment.ProcessorCount - 1); + Dafny.DafnyOptions.O.VerifySnapshots = 2; + } else { + throw new ServerException("Invalid command line options"); + } + } + } +} diff --git a/Source/DafnyServer/VerificationTask.cs b/Source/DafnyServer/VerificationTask.cs new file mode 100644 index 00000000..a00688b1 --- /dev/null +++ b/Source/DafnyServer/VerificationTask.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Json; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.Dafny { + [Serializable] + class VerificationTask { + [DataMember] + string[] args = null; + + [DataMember] + string filename = null; + + [DataMember] + string source = null; + + [DataMember] + bool sourceIsFile = false; + + string ProgramSource { get { return sourceIsFile ? File.ReadAllText(source) : source; } } + + internal static VerificationTask ReadTask(string b64_repr) { + try { + var json = Encoding.UTF8.GetString(System.Convert.FromBase64String(b64_repr)); + using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json))) { + DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(VerificationTask)); + return (VerificationTask)serializer.ReadObject(ms); + } + } catch (Exception ex) { + throw new ServerException("Deserialization failed: {0}.", ex.Message); + } + } + + internal static void SelfTest() { + var task = new VerificationTask() { + filename = "", + sourceIsFile = false, + args = new string[] { }, + source = "method selftest() { assert true; }" + }; + try { + task.Run(); + Interaction.EOM(Interaction.SUCCESS, null); + } catch (Exception ex) { + Interaction.EOM(Interaction.FAILURE, ex); + } + } + + internal void Run() { + ServerUtils.ApplyArgs(args); + new DafnyHelper(filename, ProgramSource).Verify(); + } + } +} -- cgit v1.2.3 From 594809c6668c26c3b838153ba4a4222ebef3312d Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 31 Jul 2015 14:30:48 -0700 Subject: Update .ignore --- .hgignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.hgignore b/.hgignore index 22173264..de39514d 100644 --- a/.hgignore +++ b/.hgignore @@ -7,6 +7,7 @@ Test/.*/Output Test/desktop/.* Test/([^/]*)/([^/]*)\.sx ^Test/sandbox/.* +^Test/.*\.csv syntax: glob *.exe *.pdb @@ -14,3 +15,4 @@ syntax: glob *.tmp *.tmp.dfy Source/DafnyExtension/DafnyRuntime.cs +Test/failing.lst -- cgit v1.2.3 From 65334f8f33c92a1e37376d6484d60ee45b55ca1d Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 31 Jul 2015 15:11:03 -0700 Subject: Add tests for the server --- Source/DafnyServer/Server.cs | 2 +- Test/lit.site.cfg | 8 + Test/runTests.py | 89 ++-- Test/server/simple-session.transcript | 637 +++++++++++++++++++++++++++ Test/server/simple-session.transcript.expect | 299 +++++++++++++ 5 files changed, 1000 insertions(+), 35 deletions(-) create mode 100644 Test/server/simple-session.transcript create mode 100644 Test/server/simple-session.transcript.expect diff --git a/Source/DafnyServer/Server.cs b/Source/DafnyServer/Server.cs index c6f619d3..74cdd8c2 100644 --- a/Source/DafnyServer/Server.cs +++ b/Source/DafnyServer/Server.cs @@ -22,7 +22,7 @@ namespace Microsoft.Dafny { if (hasArg && args[0] == "selftest") { VerificationTask.SelfTest(); } else if (hasArg && File.Exists(arg)) { - Console.WriteLine("# Reading from {0}", arg); + Console.WriteLine("# Reading from {0}", Path.GetFileName(arg)); Console.SetIn(new StreamReader(arg)); server.Loop(); } else { diff --git a/Test/lit.site.cfg b/Test/lit.site.cfg index a960bdbc..c5718f86 100644 --- a/Test/lit.site.cfg +++ b/Test/lit.site.cfg @@ -79,14 +79,21 @@ lit_config.note('Repository root is {}'.format(repositoryRoot)) binaryDir = os.path.join( repositoryRoot, 'Binaries') dafnyExecutable = os.path.join( binaryDir, 'Dafny.exe') +serverExecutable = os.path.join( binaryDir, 'DafnyServer.exe') if not os.path.exists(dafnyExecutable): lit_config.fatal('Could not find Dafny.exe at {}'.format(dafnyExecutable)) +if not os.path.exists(serverExecutable): + lit_config.warning('Could not find DafnyServer.exe at {}'.format(serverExecutable)) +else: + config.suffixes.append('.transcript') + dafnyExecutable = quotePath(dafnyExecutable) if os.name == 'posix': dafnyExecutable = 'mono ' + dafnyExecutable + serverExecutable = 'mono ' + serverExecutable if lit.util.which('mono') == None: lit_config.fatal('Cannot find mono. Make sure it is your PATH') @@ -105,6 +112,7 @@ if len(dafnyParams) > 0: lit_config.note('Using Dafny: {}\n'.format(dafnyExecutable)) config.substitutions.append( ('%dafny', dafnyExecutable) ) +config.substitutions.append( ('%server', serverExecutable) ) # Sanity check: Check solver executable is available # FIXME: Should this check be removed entirely? diff --git a/Test/runTests.py b/Test/runTests.py index fc9c20e7..ebdb0655 100644 --- a/Test/runTests.py +++ b/Test/runTests.py @@ -38,6 +38,7 @@ class Defaults: DAFNY_BIN = os.path.realpath(os.path.join(os.path.dirname(__file__), "../Binaries/Dafny.exe")) COMPILER = [DAFNY_BIN] FLAGS = ["/useBaseNameForFileName", "/compile:1", "/nologo", "/timeLimit:300"] + EXTENSIONS = [".dfy", ".transcript"] class Colors: RED = '\033[91m' @@ -103,7 +104,6 @@ class Test: def __init__(self, name, source_path, cmds, timeout, compiler_id = 0): self.name = name - self.dfy = None if self.name is None else (self.name + ".dfy") self.source_path = Test.uncygdrive(source_path) self.expect_path = Test.source_to_expect_path(self.source_path) self.source_directory, self.fname = os.path.split(self.source_path) @@ -182,7 +182,7 @@ class Test: debug(Debug.REPORT, "{} of {}".format(len(tests), len(results)), headers=status) if status != TestStatus.PASSED: for test in tests: - debug(Debug.REPORT, "* " + test.dfy, headers=status, silentheaders=True) + debug(Debug.REPORT, "* " + test.name, headers=status, silentheaders=True) debug(Debug.REPORT) @@ -190,14 +190,14 @@ class Test: if failing: with open("failing.lst", mode='w') as writer: for t in failing: - writer.write("{}\t{}\n".format(t.name, t.source_path)) + writer.write("{}\n".format(t.name)) debug(Debug.REPORT, "Some tests failed: use [runTests.py failing.lst] to rerun the failing tests") debug(Debug.REPORT, "Testing took {:.2f}s on {} thread(s)".format(results[0].suite_time, results[0].njobs)) def run(self): - debug(Debug.DEBUG, "Starting {}".format(self.dfy)) + debug(Debug.DEBUG, "Starting {}".format(self.name)) os.makedirs(self.temp_directory, exist_ok=True) # os.chdir(self.source_directory) @@ -227,7 +227,7 @@ class Test: stdout, stderr = stdout.strip(), stderr.strip() if stdout != b"" or stderr != b"": - debug(Debug.TRACE, "Writing the output of {} to {}".format(self.dfy, self.temp_output_path)) + debug(Debug.TRACE, "Writing the output of {} to {}".format(self.name, self.temp_output_path)) with open(self.temp_output_path, mode='ab') as writer: writer.write(stdout + stderr) if stderr != b"": @@ -249,7 +249,7 @@ class Test: fstring = "[{:5.2f}s] {} ({}{})" progress = "{}/{}".format(tid, len(alltests)) - message = fstring.format(self.duration, wrap_color(self.dfy, Colors.BRIGHT), + message = fstring.format(self.duration, wrap_color(self.name, Colors.BRIGHT), wrap_color(progress, Colors.BRIGHT), running) debug(Debug.INFO, message, headers=self.status) @@ -275,7 +275,7 @@ def setup_parser(): parser = argparse.ArgumentParser(description='Run the Dafny test suite.') parser.add_argument('path', type=str, action='store', nargs='+', - help='Input files or folders. Folders are searched for .dfy files. Lists of files can also be specified by passing a .lst file (for an example of such a file, look at failing.lst after running failing tests.') + help='Input files or folders. Folders are searched for test files. Lists of files can also be specified by passing a .lst file (for an example of such a file, look at failing.lst after running failing tests.') parser.add_argument('--compiler', type=str, action='append', default=None, help='Dafny executable. Default: {}'.format(Defaults.DAFNY_BIN)) @@ -293,11 +293,14 @@ def setup_parser(): help='Excluded directories. {} are automatically added.'.format(Defaults.ALWAYS_EXCLUDED)) parser.add_argument('--verbosity', action='store', type=int, default=1, - help='Set verbosity level. 0: Minimal; 1: Some info; 2: More info.') + help='Set verbosity level. 0: Minimal; 1: Some info; 2: More info; 3: Trace.') parser.add_argument('-v', action='store_const', default=1, dest="verbosity", const=2, help='Short for --verbosity 2.') + parser.add_argument('-vv', action='store_const', default=1, dest="verbosity", const=3, + help='Short for --verbosity 3.') + parser.add_argument('--report', '-r', action='store', type=str, default=None, help='Give an explicit name to the report file. Defaults to the current date and time.') @@ -339,7 +342,7 @@ def run_one_internal(test, test_id, args, running): # ignore further work once you receive a kill signal KILLED = True except Exception as e: - debug(Debug.ERROR, "[{}] {}".format(test.dfy, e)) + debug(Debug.ERROR, "[{}] {}".format(test.name, e)) test.status = TestStatus.UNKNOWN finally: running.remove(test_id) @@ -349,53 +352,71 @@ def run_one_internal(test, test_id, args, running): def run_one(args): return run_one_internal(*args) -def read_one_test(name, fname, compiler_cmds, timeout): +def get_server_path(compiler): + REGEXP = r"\bDafny.exe\b.*" + if re.search(REGEXP, compiler): + return re.sub(REGEXP, "DafnyServer.exe", compiler) + else: + return None + +def substitute_binaries(cmd, compiler): + cmd = cmd.replace("%dafny", compiler) + cmd = cmd.replace("%server", get_server_path(compiler)) + return cmd + +def read_one_test(fname, compiler_cmds, timeout): for cid, compiler_cmd in enumerate(compiler_cmds): source_path = os.path.realpath(fname) with open(source_path, mode='r') as reader: cmds = [] for line in reader: line = line.strip() - match = re.match("^// *RUN: *(?!%diff)([^ ].*)$", line) + match = re.match("^[/# ]*RUN: *(?!%diff)([^ ].*)$", line) if match: - cmds.append(match.groups()[0].replace("%dafny", compiler_cmd)) + debug(Debug.TRACE, "Found RUN spec: {}".format(line)) + cmds.append(substitute_binaries(match.groups()[0], compiler_cmd)) else: break if cmds: - yield Test(name, source_path, cmds, timeout, cid) + yield Test(fname, source_path, cmds, timeout, cid) else: - debug(Debug.INFO, "Test file {} has no RUN specification".format(fname)) + debug(Debug.WARNING, "Test file {} has no RUN specification".format(fname)) -def find_one(name, fname, compiler_cmds, timeout, allow_lst=False): - name, ext = os.path.splitext(fname) - if ext == ".dfy": +def find_one(fname, compiler_cmds, timeout): + _, ext = os.path.splitext(fname) + if ext in Defaults.EXTENSIONS: if os.path.exists(fname): debug(Debug.TRACE, "Found test file: {}".format(fname)) - yield from read_one_test(name, fname, compiler_cmds, timeout) + yield from read_one_test(fname, compiler_cmds, timeout) else: debug(Debug.ERROR, "Test file {} not found".format(fname)) - elif ext == ".lst" and allow_lst: #lst files are only read if explicitly listed on the CLI - debug(Debug.INFO, "Loading tests from {}".format(fname)) - with open(fname) as reader: - for line in reader: - _name, _path = line.strip().split() - yield from find_one(_name, _path, compiler_cmds, timeout) else: debug(Debug.TRACE, "Ignoring {}".format(fname)) -def find_tests(paths, compiler_cmds, excluded, timeout): +def expand_lsts(paths): for path in paths: + _, ext = os.path.splitext(path) + if ext == ".lst": #lst files are only read if explicitly listed on the CLI + debug(Debug.INFO, "Loading tests from {}".format(path)) + with open(path) as reader: + for line in reader: + _path = line.strip() + yield _path + else: + yield path + +def find_tests(paths, compiler_cmds, excluded, timeout): + for path in expand_lsts(paths): if os.path.isdir(path): debug(Debug.TRACE, "Searching for tests in {}".format(path)) for base, dirnames, fnames in os.walk(path): dirnames[:] = [d for d in dirnames if d not in excluded] for fname in fnames: - yield from find_one(fname, os.path.join(base, fname), compiler_cmds, timeout) + yield from find_one(os.path.join(base, fname), compiler_cmds, timeout) else: - yield from find_one(path, path, compiler_cmds, timeout, True) - + yield from find_one(path, compiler_cmds, timeout) def run_tests(args): if args.compiler is None: @@ -404,12 +425,15 @@ def run_tests(args): args.base_flags = Defaults.FLAGS for compiler in args.compiler: + server = get_server_path(compiler) if not os.path.exists(compiler): debug(Debug.ERROR, "Compiler not found: {}".format(compiler)) return + if not os.path.exists(server): + debug(Debug.WARNING, "Server not found") tests = list(find_tests(args.path, [compiler + ' ' + " ".join(args.base_flags + args.flags) - for compiler in args.compiler], + for compiler in args.compiler], args.exclude + Defaults.ALWAYS_EXCLUDED, args.timeout)) tests.sort(key=operator.attrgetter("name")) @@ -447,10 +471,7 @@ def run_tests(args): def diff(paths, accept, difftool): - for path in paths: - if not path.endswith(".dfy"): - path += ".dfy" - + for path in expand_lsts(paths): if not os.path.exists(path): debug(Debug.ERROR, "Not found: {}".format(path)) else: @@ -470,7 +491,7 @@ def compare_results(globs, time_all): from glob import glob paths = [path for g in globs for path in glob(g)] reports = {path: Test.load_report(path) for path in paths} - resultsets = {path: {test.dfy: (test.status, test.duration) for test in report} + resultsets = {path: {test.name: (test.status, test.duration) for test in report} for path, report in reports.items()} all_tests = set(name for resultset in resultsets.values() for name in resultset.keys()) diff --git a/Test/server/simple-session.transcript b/Test/server/simple-session.transcript new file mode 100644 index 00000000..26539267 --- /dev/null +++ b/Test/server/simple-session.transcript @@ -0,0 +1,637 @@ +# RUN: %server "%s" > "%t" +# RUN: %diff "%s.expect" "%t" +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xuICBhc3NlcnQgZmFsc2U7XG59XG4iLCJz +b3VyY2VJc0ZpbGUiOmZhbHNlfQ== +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xuICBhc3NlcnQgZmFsc2U7XG4gfVxuIiwi +c291cmNlSXNGaWxlIjpmYWxzZX0= +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xuICBhc3NlcnQgZmFsc2U7XG59XG4iLCJz +b3VyY2VJc0ZpbGUiOmZhbHNlfQ== +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xuICBhc3NlcnQgZmFsc2U7XG59XG4iLCJz +b3VyY2VJc0ZpbGUiOmZhbHNlfQ== +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG4iLCJzb3VyY2VJc0ZpbGUiOmZh +bHNlfQ== +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7Iiwic291 +cmNlSXNGaWxlIjpmYWxzZX0= +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG4gICIs +InNvdXJjZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG59Iiwi +c291cmNlSXNGaWxlIjpmYWxzZX0= +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG5cbn0i +LCJzb3VyY2VJc0ZpbGUiOmZhbHNlfQ== +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG4gIFxu +fSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG4gIHhc +bn0iLCJzb3VyY2VJc0ZpbGUiOmZhbHNlfQ== +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG4gIFxu +fSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG4gIHZh +clxufSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG4gIHZh +ciB4IDo9IDE7XG59Iiwic291cmNlSXNGaWxlIjpmYWxzZX0= +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG4gIHZh +ciB4IDo9IDE7XG4gIFxufSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG4gIHZh +ciB4IDo9IDE7XG4gIHZhciB5LCB6IDo9IDI7XG59Iiwic291cmNlSXNGaWxlIjpmYWxzZX0= +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG4gIHZh +ciB4IDo9IDE7XG4gIHZhciB5LCB6IDo9IDI7XG4gIFxufSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5tZXRob2QgTScoKSB7XG4gIHZh +ciB4IDo9IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIFxufSIsInNvdXJjZUlzRmlsZSI6ZmFs +c2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKCkg +e1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAyLCAzO1xuICBcbn0iLCJzb3VyY2VJc0Zp +bGUiOmZhbHNlfQ== +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKCkg +e1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAyLCAzO1xuICBcbn0iLCJzb3VyY2VJc0Zp +bGUiOmZhbHNlfQ== +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcblxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAyLCAzO1xuICBcbn0iLCJz +b3VyY2VJc0ZpbGUiOmZhbHNlfQ== +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcblxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAyLCAzO1xuICBcbn0iLCJz +b3VyY2VJc0ZpbGUiOmZhbHNlfQ== +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgXG57XG4gIHZhciB4IDo9IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIFxufSIs +InNvdXJjZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxXG57XG4gIHZhciB4IDo9IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIFxu +fSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxXG57XG4gIHZhciB4IDo9IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIFxu +fSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgXG59Iiwic291cmNlSXNGaWxlIjpmYWxzZX0= +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzXG59Iiwic291cmNlSXNGaWxlIjpmYWxzZX0= +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvclxufSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JyA6OiBcbn0iLCJzb3VyY2VJc0ZpbGUiOmZhbHNlfQ== +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JyA6OiBpbnQsIHgnICogeCcgPiAwO1xufSIsInNvdXJj +ZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JyA6OiBpbnQsIHgnICogeCcgPiAwO1xufSIsInNvdXJj +ZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4J2ludCwgeCcgKiB4JyA+IDA7XG59Iiwic291cmNlSXNG +aWxlIjpmYWxzZX0= +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50LCB4JyAqIHgnID4gMDtcbn0iLCJzb3VyY2VJ +c0ZpbGUiOmZhbHNlfQ== +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6ICwgeCcgKiB4JyA+IDA7XG59Iiwic291 +cmNlSXNGaWxlIjpmYWxzZX0= +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPiAwO1xufSIsInNvdXJj +ZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbn0iLCJzb3Vy +Y2VJc0ZpbGUiOmZhbHNlfQ== +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgXG59Iiwi +c291cmNlSXNGaWxlIjpmYWxzZX0= +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzXG59 +Iiwic291cmNlSXNGaWxlIjpmYWxzZX0= +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn0iLCJzb3VyY2VJc0ZpbGUiOmZhbHNlfQ== +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuIiwic291cmNlSXNGaWxlIjpmYWxzZX0= +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn0iLCJz +b3VyY2VJc0ZpbGUiOmZhbHNlfQ== +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn0iLCJz +b3VyY2VJc0ZpbGUiOmZhbHNlfQ== +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cbiIs +InNvdXJjZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu +c3RhdGljIG1ldGhvZCBNJyh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9 +IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq +IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59Iiwic291cmNlSXNGaWxlIjpmYWxzZX0= +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu +c3RhdGljIG1ldGhvZCBNJyh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9 +IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq +IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59Iiwic291cmNlSXNGaWxlIjpmYWxzZX0= +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu +c3RhdGljIG1ldGhvZCBNJyh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9 +IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq +IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG4iLCJzb3VyY2VJc0ZpbGUiOmZhbHNl +fQ== +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu +c3RhdGljIG1ldGhvZCBNJyh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9 +IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq +IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTScodDog +bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy +LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiB4JyA+PSAwO1xuICBhc3NlcnQg +eCAqIHggPiAwO1xufSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0odDogbmF0KVxuICByZXF1aXJlcyB0 +ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAyLCAzO1xuICBhc3NlcnQgZm9y +YWxsIHgnOiBpbnQgOjogeCcgKiB4JyA+PSAwO1xuICBhc3NlcnQgeCAqIHggPiAwO1xufVxuXG5z +dGF0aWMgbWV0aG9kIE0odDogbmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAx +O1xuICB2YXIgeSwgeiA6PSAyLCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiB4 +JyA+PSAwO1xuICBhc3NlcnQgeCAqIHggPiAwO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0odDogbmF0 +KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAyLCAz +O1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiB4JyA+PSAwO1xuICBhc3NlcnQgeCAq +IHggPiAwO1xufSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu +c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9 +IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq +IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog +bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy +LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiB4JyA+PSAwO1xuICBhc3NlcnQg +eCAqIHggPiAwO1xufSIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu +c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9 +IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq +IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog +bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy +LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiB4JyA+PSAwO1xuICBhc3NlcnQg +eCAqIHggPiAwO1xufVxuIiwic291cmNlSXNGaWxlIjpmYWxzZX0= +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu +c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9 +IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq +IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog +bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy +LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnID49IDA7XG4gIGFzc2Vy +dCB4ICogeCA+IDA7XG59XG4iLCJzb3VyY2VJc0ZpbGUiOmZhbHNlfQ== +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu +c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9 +IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq +IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog +bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy +LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnICAwO1xuICBhc3NlcnQg +eCAqIHggPiAwO1xufVxuIiwic291cmNlSXNGaWxlIjpmYWxzZX0= +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu +c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9 +IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq +IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog +bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy +LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDwgMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cbiIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu +c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9 +IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq +IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog +bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy +LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIGFzc2Vy +dCB4ICogeCA+IDA7XG59XG4iLCJzb3VyY2VJc0ZpbGUiOmZhbHNlfQ== +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu +c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9 +IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq +IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog +bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy +LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIFxuICBh +c3NlcnQgeCAqIHggPiAwO1xufVxuIiwic291cmNlSXNGaWxlIjpmYWxzZX0= +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu +c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9 +IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq +IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog +bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy +LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIGFzc1xu +ICBhc3NlcnQgeCAqIHggPiAwO1xufVxuIiwic291cmNlSXNGaWxlIjpmYWxzZX0= +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu +c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9 +IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq +IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog +bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy +LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIGFzc2Vy +XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG4iLCJzb3VyY2VJc0ZpbGUiOmZhbHNlfQ== +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu +c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9 +IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq +IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog +bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy +LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIGFzc1xu +ICBhc3NlcnQgeCAqIHggPiAwO1xufVxuIiwic291cmNlSXNGaWxlIjpmYWxzZX0= +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu +c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9 +IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq +IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog +bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy +LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIGFzc3Vt +ZSB5IDwgMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cbiIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu +c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9 +IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq +IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog +bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy +LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIGFzc3Vt +ZSB5IDwgMDtcbiAgYXNzZXJ0IDtcbn1cbiIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu +c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9 +IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq +IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog +bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy +LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIGFzc3Vt +ZSB5IDwgMDtcbiAgYXNzZXJ0IDtcbn1cbiIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu +c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9 +IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq +IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog +bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy +LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIGFzc3Vt +ZSB5IDwgMDtcbiAgYXNzZXJ0IGZhbDtcbn1cbiIsInNvdXJjZUlzRmlsZSI6ZmFsc2V9 +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu +c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9 +IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq +IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog +bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy +LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIGFzc3Vt +ZSB5IDwgMDtcbiAgYXNzZXJ0IGZhbHNlO1xufVxuIiwic291cmNlSXNGaWxlIjpmYWxzZX0= +[[DAFNY-CLIENT: EOM]] +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xufVxuXG5zdGF0aWMgbWV0aG9kIE0nKHQ6 +IG5hdClcbiAgcmVxdWlyZXMgdCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0g +MiwgMztcbiAgYXNzZXJ0IGZvcmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0 +IHggKiB4ID4gMDtcbn1cblxuXG5zdGF0aWMgbWV0aG9kIE0wKHQ6IG5hdClcbiAgcmVxdWlyZXMg +dCA+IDBcbntcbiAgdmFyIHggOj0gMTtcbiAgdmFyIHksIHogOj0gMiwgMztcbiAgYXNzZXJ0IGZv +cmFsbCB4JzogaW50IDo6IHgnICogeCcgPj0gMDtcbiAgYXNzZXJ0IHggKiB4ID4gMDtcbn1cblxu +c3RhdGljIG1ldGhvZCBNMSh0OiBuYXQpXG4gIHJlcXVpcmVzIHQgPiAwXG57XG4gIHZhciB4IDo9 +IDE7XG4gIHZhciB5LCB6IDo9IDIsIDM7XG4gIGFzc2VydCBmb3JhbGwgeCc6IGludCA6OiB4JyAq +IHgnID49IDA7XG4gIGFzc2VydCB4ICogeCA+IDA7XG59XG5cbnN0YXRpYyBtZXRob2QgTTIodDog +bmF0KVxuICByZXF1aXJlcyB0ID4gMFxue1xuICB2YXIgeCA6PSAxO1xuICB2YXIgeSwgeiA6PSAy +LCAzO1xuICBhc3NlcnQgZm9yYWxsIHgnOiBpbnQgOjogeCcgKiAtIHgnIDw9IDA7XG4gIGFzc3Vt +ZSB5IDwgMDtcbiAgYXNzZXJ0IGZhbHNlO1xufVxuIiwic291cmNlSXNGaWxlIjpmYWxzZX0= +[[DAFNY-CLIENT: EOM]] diff --git a/Test/server/simple-session.transcript.expect b/Test/server/simple-session.transcript.expect new file mode 100644 index 00000000..89c3351d --- /dev/null +++ b/Test/server/simple-session.transcript.expect @@ -0,0 +1,299 @@ +# Reading from simple-session.transcript +transcript(3,9): Error: assertion violation +Execution trace: + (0,0): anon0 +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(3,9): Error: assertion violation +Execution trace: + (0,0): anon0 +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(3,9): Error: assertion violation +Execution trace: + (0,0): anon0 +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(3,9): Error: assertion violation +Execution trace: + (0,0): anon0 +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,13): Error: rbrace expected +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(6,2): Error: rbrace expected +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(7,0): Error: invalid UpdateStmt +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(7,0): Error: ident expected +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(7,11): Error: the number of left-hand sides (2) and right-hand sides (1) must match for a multi-assignment +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(7,11): Error: the number of left-hand sides (2) and right-hand sides (1) must match for a multi-assignment +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(6,2): Error: EOF expected +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(6,2): Error: EOF expected +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(11,0): Error: invalid UpdateStmt +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(11,0): Error: semi expected +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(11,0): Error: invalid UnaryExpression +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(10,25): Error: openparen expected +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(10,25): Error: openparen expected +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(10,26): Error: invalid QSep +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(10,28): Error: invalid QSep +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(10,27): Error: invalid UnaryExpression +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(10,9): Error: assertion violation +Execution trace: + (0,0): anon0 +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(12,0): Error: invalid UpdateStmt +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,14): Error: Duplicate member name: M' +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,14): Error: Duplicate member name: M' +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,14): Error: Duplicate member name: M' +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,14): Error: Duplicate member name: M' +transcript(24,14): Error: Duplicate member name: M' +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,14): Error: Duplicate member name: M' +transcript(24,14): Error: Duplicate member name: M' +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,14): Error: Duplicate member name: M' +transcript(24,14): Error: Duplicate member name: M' +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,14): Error: Duplicate member name: M' +transcript(24,14): Error: Duplicate member name: M' +transcript(33,14): Error: Duplicate member name: M' +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,14): Error: Duplicate member name: M +transcript(33,14): Error: Duplicate member name: M +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(38,9): Error: assertion violation +Execution trace: + (0,0): anon0 +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(38,38): Error: semi expected +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(38,9): Error: assertion violation +Execution trace: + (0,0): anon0 +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(40,2): Error: invalid UpdateStmt +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(40,2): Error: invalid UpdateStmt +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(40,2): Error: invalid UpdateStmt +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(40,9): Error: invalid AssertStmt +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(40,9): Error: invalid AssertStmt +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(40,9): Error: unresolved identifier: fal +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] -- cgit v1.2.3 From a8953bef9bebfaa4afb56a914060360c7453e8b8 Mon Sep 17 00:00:00 2001 From: leino Date: Fri, 31 Jul 2015 16:43:34 -0700 Subject: Allow forall statements in refinements --- Source/Dafny/RefinementTransformer.cs | 3 --- Test/dafny0/RefinementErrors.dfy | 37 +++++++++++++++++++++++++++++++++ Test/dafny0/RefinementErrors.dfy.expect | 3 ++- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/Source/Dafny/RefinementTransformer.cs b/Source/Dafny/RefinementTransformer.cs index 2d32f78a..f430933b 100644 --- a/Source/Dafny/RefinementTransformer.cs +++ b/Source/Dafny/RefinementTransformer.cs @@ -1532,9 +1532,6 @@ namespace Microsoft.Dafny }); } else if (s is CallStmt) { reporter.Error(s.Tok, "cannot have call statement"); - } else if (s is ForallStmt) { - if (((ForallStmt)s).Kind == ForallStmt.ParBodyKind.Assign) // allow Proof and Call (as neither touch any existing state) - reporter.Error(s.Tok, "cannot have forall statement"); } else { if (s is WhileStmt || s is AlternativeLoopStmt) { loopLevels++; diff --git a/Test/dafny0/RefinementErrors.dfy b/Test/dafny0/RefinementErrors.dfy index 121b33aa..8d60a8e4 100644 --- a/Test/dafny0/RefinementErrors.dfy +++ b/Test/dafny0/RefinementErrors.dfy @@ -59,3 +59,40 @@ module BB refines B { { 10 } } } + +module Forall0 { + class C { + var a: int + method M() + modifies this + { + } + lemma Lemma(x: int) + { + } + } +} +module Forall1 refines Forall0 { + class C { + var b: int + method M... + { + forall x { Lemma(x); } // allowed + var s := {4}; + forall x | x in s ensures x == 4 { } // allowed + forall x { // allowed + calc { + x in s; + == + x == 4; + } + } + forall c | c in {this} { + c.b := 17; // allowed + } + forall c | c in {this} { + c.a := 17; // error: not allowed to update previously defined field + } + } + } +} diff --git a/Test/dafny0/RefinementErrors.dfy.expect b/Test/dafny0/RefinementErrors.dfy.expect index 40cdb081..bac6612d 100644 --- a/Test/dafny0/RefinementErrors.dfy.expect +++ b/Test/dafny0/RefinementErrors.dfy.expect @@ -9,4 +9,5 @@ RefinementErrors.dfy(38,13): Error: type parameters are not allowed to be rename RefinementErrors.dfy(39,23): Error: the type of parameter 'z' is different from the type of the same parameter in the corresponding function in the module it refines ('seq' instead of 'set') RefinementErrors.dfy(40,9): Error: there is a difference in name of parameter 3 ('k' versus 'b') of function F compared to corresponding function in the module it refines RefinementErrors.dfy(57,20): Error: a function can be changed into a function method in a refining module only if the function has not yet been given a body: G -11 resolution/type errors detected in RefinementErrors.dfy +RefinementErrors.dfy(94,10): Error: refinement method cannot assign to a field defined in parent module ('a') +12 resolution/type errors detected in RefinementErrors.dfy -- cgit v1.2.3 From 6eeaf689c0ae81bf9df46f975b014b2b9e465f0a Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 31 Jul 2015 16:45:02 -0700 Subject: Add a Linux z3 binary to the repo, and use that or z3.exe based on the OS --- Binaries/z3 | Bin 0 -> 16438468 bytes Source/Dafny/DafnyOptions.cs | 10 ++++++++++ 2 files changed, 10 insertions(+) create mode 100644 Binaries/z3 diff --git a/Binaries/z3 b/Binaries/z3 new file mode 100644 index 00000000..7c60feb4 Binary files /dev/null and b/Binaries/z3 differ diff --git a/Source/Dafny/DafnyOptions.cs b/Source/Dafny/DafnyOptions.cs index 8972c490..66cf639f 100644 --- a/Source/Dafny/DafnyOptions.cs +++ b/Source/Dafny/DafnyOptions.cs @@ -11,6 +11,7 @@ namespace Microsoft.Dafny { public DafnyOptions() : base("Dafny", "Dafny program verifier") { + SetZ3ExecutableName(); } public override string VersionNumber { @@ -255,6 +256,15 @@ namespace Microsoft.Dafny // TODO: provide attribute help here } + private void SetZ3ExecutableName() { + var platform = (int)System.Environment.OSVersion.Platform; + var isLinux = platform == 4 || platform == 128; // http://www.mono-project.com/docs/faq/technical/ + + //TODO should we also vendor an OSX build? + var binDir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); + Z3ExecutablePath = System.IO.Path.Combine(binDir, isLinux ? "z3" : "z3.exe"); + } + public override void Usage() { Console.WriteLine(@" ---- Dafny options --------------------------------------------------------- -- cgit v1.2.3 From ce07a29685a27f01c596271e03d6a39a7090d12e Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 31 Jul 2015 16:45:25 -0700 Subject: Update install notes and test them --- INSTALL | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/INSTALL b/INSTALL index 2a70587e..830a89f4 100644 --- a/INSTALL +++ b/INSTALL @@ -1,6 +1,12 @@ Building on Linux ================= +(Tested on a fresh Linux Mint 17.2) + +0. Dependencies + + apt install mono-devel g++ + 1. Create an empty base directory mkdir BASE-DRIECTORY @@ -14,21 +20,27 @@ Building on Linux wget https://nuget.org/nuget.exe mono ./nuget.exe restore Source/Boogie.sln xbuild Source/Boogie.sln + cd .. 3. Download and build Dafny: hg clone https://hg.codeplex.com/dafny - cd dafny/Sources/ - xbuild Dafny.sln + xbuild dafny/Source/Dafny.sln + xbuild dafny/Source/DafnyServer.sln 4. Download and build Z3 git clone https://github.com/Z3Prover/z3.git cd z3 - ./configure && make && sudo make install + git checkout z3-4.4.0 + ./configure + cd build + make -j4 + cd ../.. -5. Symlink the z3 binaries so that Boogie and Z3 can find them: +5. Copy (or symlink) the z3 binary so that Dafny and Boogie can find it: cd BASE-DIRECTORY - ln -s /usr/bin/z3 boogie/Binaries/z3.exe - ln -s /usr/bin/z3 dafny/Binaries/z3.exe + rm dafny/Binaries/z3 # Dafny already packages z3 + cp z3/build/z3 dafny/Binaries/z3 + cp z3/build/z3 boogie/Binaries/z3.exe -- cgit v1.2.3 From d023b0e19f7bf886801a3a059511b8449d8ab223 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 31 Jul 2015 16:58:36 -0700 Subject: Bug fix: check that assign-such-that constraint is of type boolean --- Source/Dafny/Resolver.cs | 3 +++ Test/dafny0/ResolutionErrors.dfy | 15 +++++++++++++++ Test/dafny0/ResolutionErrors.dfy.expect | 6 +++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index e977dbd5..a58d6e6c 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -6100,6 +6100,9 @@ namespace Microsoft.Dafny s.IsGhost = s.Lhss.TrueForAll(AssignStmt.LhsIsToGhost); var ec = ErrorCount; ResolveExpression(s.Expr, new ResolveOpts(codeContext, true, specContextOnly)); + if (!UnifyTypes(s.Expr.Type, Type.Bool)) { + Error(s.Expr, "type of RHS of assign-such-that statement must be boolean (got {0})", s.Expr.Type); + } if (ec == ErrorCount && !s.IsGhost && s.AssumeToken == null && !specContextOnly) { CheckIsNonGhost(s.Expr); diff --git a/Test/dafny0/ResolutionErrors.dfy b/Test/dafny0/ResolutionErrors.dfy index 900c7459..1354e533 100644 --- a/Test/dafny0/ResolutionErrors.dfy +++ b/Test/dafny0/ResolutionErrors.dfy @@ -1373,3 +1373,18 @@ module NonTypeVariableNames { Y := k; // error: module name used as variable } } + +// ------------------- assign-such-that and let-such-that ------------------- + +module SuchThat { + method M() { + var x: int; + x :| 5 + 7; // error: constraint should be boolean + x :| x; // error: constraint should be boolean + var y :| 4; // error: constraint should be boolean + } + function F(): int { + var w :| 6 + 8; // error: constraint should be boolean + w + } +} diff --git a/Test/dafny0/ResolutionErrors.dfy.expect b/Test/dafny0/ResolutionErrors.dfy.expect index 481b47e0..b5c93ac1 100644 --- a/Test/dafny0/ResolutionErrors.dfy.expect +++ b/Test/dafny0/ResolutionErrors.dfy.expect @@ -104,6 +104,10 @@ ResolutionErrors.dfy(1371,16): Error: name of module (Y) is used as a variable ResolutionErrors.dfy(1371,13): Error: arguments must have the same type (got int and #module) ResolutionErrors.dfy(1372,4): Error: name of type (X) is used as a variable ResolutionErrors.dfy(1373,4): Error: name of module (Y) is used as a variable +ResolutionErrors.dfy(1382,11): Error: type of RHS of assign-such-that statement must be boolean (got int) +ResolutionErrors.dfy(1383,9): Error: type of RHS of assign-such-that statement must be boolean (got int) +ResolutionErrors.dfy(1384,13): Error: type of RHS of assign-such-that statement must be boolean (got int) +ResolutionErrors.dfy(1387,15): Error: type of RHS of let-such-that expression must be boolean (got int) ResolutionErrors.dfy(432,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') @@ -196,4 +200,4 @@ ResolutionErrors.dfy(1106,8): Error: new cannot be applied to a trait ResolutionErrors.dfy(1127,13): Error: first argument to / must be of numeric type (instead got set) ResolutionErrors.dfy(1134,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(1149,14): Error: a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating -198 resolution/type errors detected in ResolutionErrors.dfy +202 resolution/type errors detected in ResolutionErrors.dfy -- cgit v1.2.3 From 3243c7ac086991a801f6bca711387d36fc601999 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 31 Jul 2015 17:22:52 -0700 Subject: Add path to DafnyPrelude.bpl from DafnyServer project --- Source/DafnyServer/DafnyServer.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/DafnyServer/DafnyServer.csproj b/Source/DafnyServer/DafnyServer.csproj index 3da33f16..a173f810 100644 --- a/Source/DafnyServer/DafnyServer.csproj +++ b/Source/DafnyServer/DafnyServer.csproj @@ -1,4 +1,4 @@ - + @@ -63,7 +63,7 @@ - + PreserveNewest @@ -75,4 +75,4 @@ --> - \ No newline at end of file + -- cgit v1.2.3 From 0579ef0552cf496b7c7b2246586b91b5bb42d406 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 31 Jul 2015 17:55:23 -0700 Subject: Integrate the DafnyServer project into the main Dafny solution --- Source/Dafny.sln | 17 +++++++++++++++++ Source/DafnyServer.sln | 20 -------------------- Source/DafnyServer/DafnyServer.csproj | 3 --- 3 files changed, 17 insertions(+), 23 deletions(-) delete mode 100644 Source/DafnyServer.sln diff --git a/Source/Dafny.sln b/Source/Dafny.sln index e7ba8026..570922fa 100644 --- a/Source/Dafny.sln +++ b/Source/Dafny.sln @@ -5,6 +5,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DafnyDriver", "DafnyDriver\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DafnyPipeline", "Dafny\DafnyPipeline.csproj", "{FE44674A-1633-4917-99F4-57635E6FA740}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DafnyServer", "DafnyServer\DafnyServer.csproj", "{AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Checked|.NET = Checked|.NET @@ -52,6 +54,21 @@ Global {FE44674A-1633-4917-99F4-57635E6FA740}.Release|Any CPU.Build.0 = Release|Any CPU {FE44674A-1633-4917-99F4-57635E6FA740}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {FE44674A-1633-4917-99F4-57635E6FA740}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Checked|.NET.ActiveCfg = Release|Any CPU + {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Checked|Any CPU.ActiveCfg = Release|Any CPU + {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Checked|Any CPU.Build.0 = Release|Any CPU + {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Checked|Mixed Platforms.ActiveCfg = Release|Any CPU + {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Checked|Mixed Platforms.Build.0 = Release|Any CPU + {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Debug|.NET.ActiveCfg = Debug|Any CPU + {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Release|.NET.ActiveCfg = Release|Any CPU + {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Release|Any CPU.Build.0 = Release|Any CPU + {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Release|Mixed Platforms.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Source/DafnyServer.sln b/Source/DafnyServer.sln deleted file mode 100644 index 217b1c74..00000000 --- a/Source/DafnyServer.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DafnyServer", "DafnyServer\DafnyServer.csproj", "{AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/Source/DafnyServer/DafnyServer.csproj b/Source/DafnyServer/DafnyServer.csproj index 3da33f16..d1171d69 100644 --- a/Source/DafnyServer/DafnyServer.csproj +++ b/Source/DafnyServer/DafnyServer.csproj @@ -63,9 +63,6 @@ - - PreserveNewest - - \ No newline at end of file + -- cgit v1.2.3 From 8a0df70ffb8d57d1bd210ce2e1c9522ba0967365 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Tue, 18 Aug 2015 18:58:40 -0700 Subject: Refactor the error reporting code The new error reporting system has a simpler interface, isn't tied to the resolver, and contains error source information. --- Source/Dafny/Cloner.cs | 33 +- Source/Dafny/Dafny.atg | 6 +- Source/Dafny/DafnyAst.cs | 20 +- Source/Dafny/DafnyMain.cs | 12 +- Source/Dafny/DafnyPipeline.csproj | 7 +- Source/Dafny/Parser.cs | 34 +- Source/Dafny/RefinementTransformer.cs | 293 ++++---- Source/Dafny/Reporting.cs | 164 +++++ Source/Dafny/Resolver.cs | 1146 +++++++++++++---------------- Source/Dafny/Rewriter.cs | 119 ++- Source/Dafny/Scanner.cs | 18 +- Source/Dafny/Translator.cs | 15 +- Source/Dafny/TriggerGenerator.cs | 4 +- Source/Dafny/Util.cs | 23 - Source/DafnyDriver/DafnyDriver.cs | 35 +- Source/DafnyExtension/DafnyDriver.cs | 77 +- Source/DafnyExtension/IdentifierTagger.cs | 4 +- Source/DafnyExtension/ResolverTagger.cs | 4 +- Source/DafnyServer/DafnyHelper.cs | 17 +- 19 files changed, 1006 insertions(+), 1025 deletions(-) create mode 100644 Source/Dafny/Reporting.cs diff --git a/Source/Dafny/Cloner.cs b/Source/Dafny/Cloner.cs index f959b537..323abc70 100644 --- a/Source/Dafny/Cloner.cs +++ b/Source/Dafny/Cloner.cs @@ -365,6 +365,7 @@ namespace Microsoft.Dafny return new NamedExpr(Tok(e.tok), e.Name, CloneExpr(e.Body)); } else if (expr is ComprehensionExpr) { var e = (ComprehensionExpr)expr; + var tk = Tok(e.tok); var bvs = e.BoundVars.ConvertAll(CloneBoundVar); var range = CloneExpr(e.Range); @@ -703,22 +704,16 @@ namespace Microsoft.Dafny abstract class FixpointCloner : Cloner { protected readonly Expression k; - readonly Resolver resolver; + protected readonly ErrorReporter reporter; readonly string suffix; - protected FixpointCloner(Expression k, Resolver resolver) + protected FixpointCloner(Expression k, ErrorReporter reporter) { Contract.Requires(k != null); - Contract.Requires(resolver != null); + Contract.Requires(reporter != null); this.k = k; - this.resolver = resolver; + this.reporter = reporter; this.suffix = string.Format("#[{0}]", Printer.ExprToString(k)); } - protected void ReportAdditionalInformation(IToken tok, string s) - { - Contract.Requires(tok != null); - Contract.Requires(s != null); - resolver.ReportAdditionalInformation(tok, s + suffix, s.Length); - } } /// @@ -733,12 +728,12 @@ namespace Microsoft.Dafny { readonly bool isCoContext; readonly ISet friendlyCalls; - public FixpointLemmaSpecificationSubstituter(ISet friendlyCalls, Expression k, Resolver resolver, bool isCoContext) - : base(k, resolver) + public FixpointLemmaSpecificationSubstituter(ISet friendlyCalls, Expression k, ErrorReporter reporter, bool isCoContext) + : base(k, reporter) { Contract.Requires(friendlyCalls != null); Contract.Requires(k != null); - Contract.Requires(resolver != null); + Contract.Requires(reporter != null); this.isCoContext = isCoContext; this.friendlyCalls = friendlyCalls; } @@ -758,7 +753,7 @@ namespace Microsoft.Dafny args.Add(CloneExpr(arg)); } var fexp = new FunctionCallExpr(Tok(e.tok), e.Name + "#", receiver, e.OpenParen, args); - ReportAdditionalInformation(e.tok, e.Name); + reporter.Info(MessageSource.Cloner, e.tok, e.Name); return fexp; } } else if (expr is BinaryExpr && isCoContext) { @@ -769,7 +764,7 @@ namespace Microsoft.Dafny var B = CloneExpr(e.E1); var teq = new TernaryExpr(Tok(e.tok), op, k, A, B); var opString = op == TernaryExpr.Opcode.PrefixEqOp ? "==" : "!="; - ReportAdditionalInformation(e.tok, opString); + reporter.Info(MessageSource.Cloner, e.tok, opString); return teq; } } @@ -804,12 +799,12 @@ namespace Microsoft.Dafny class FixpointLemmaBodyCloner : FixpointCloner { readonly FixpointLemma context; - public FixpointLemmaBodyCloner(FixpointLemma context, Expression k, Resolver resolver) - : base(k, resolver) + public FixpointLemmaBodyCloner(FixpointLemma context, Expression k, ErrorReporter reporter) + : base(k, reporter) { Contract.Requires(context != null); Contract.Requires(k != null); - Contract.Requires(resolver != null); + Contract.Requires(reporter != null); this.context = context; } public override AssignmentRhs CloneRHS(AssignmentRhs rhs) { @@ -834,7 +829,7 @@ namespace Microsoft.Dafny apply.Args.ForEach(arg => args.Add(CloneExpr(arg))); var applyClone = new ApplySuffix(Tok(apply.tok), lhsClone, args); var c = new ExprRhs(applyClone); - ReportAdditionalInformation(apply.tok, mse.Member.Name); + reporter.Info(MessageSource.Cloner, apply.tok, mse.Member.Name); return c; } } diff --git a/Source/Dafny/Dafny.atg b/Source/Dafny/Dafny.atg index 8a3bdb0e..07f8e1c4 100644 --- a/Source/Dafny/Dafny.atg +++ b/Source/Dafny/Dafny.atg @@ -55,11 +55,11 @@ public static int Parse (string/*!*/ filename, ModuleDecl module, BuiltIns built /// Returns the number of parsing errors encountered. /// Note: first initialize the Scanner. /// -public static int Parse (string/*!*/ s, string/*!*/ fullFilename, string/*!*/ filename, ModuleDecl module, BuiltIns builtIns, bool verifyThisFile=true) { +public static int Parse (string/*!*/ s, string/*!*/ fullFilename, string/*!*/ filename, ModuleDecl module, BuiltIns builtIns, ErrorReporter reporter, bool verifyThisFile=true) { Contract.Requires(s != null); Contract.Requires(filename != null); Contract.Requires(module != null); - Errors errors = new Errors(); + Errors errors = new Errors(reporter); return Parse(s, fullFilename, filename, module, builtIns, errors, verifyThisFile); } /// @@ -79,7 +79,7 @@ public static int Parse (string/*!*/ s, string/*!*/ fullFilename, string/*!*/ fi Scanner scanner = new Scanner(ms, errors, fullFilename, filename); Parser parser = new Parser(scanner, errors, module, builtIns, verifyThisFile); parser.Parse(); - return parser.errors.count; + return parser.errors.ErrorCount; } public Parser(Scanner/*!*/ scanner, Errors/*!*/ errors, ModuleDecl module, BuiltIns builtIns, bool verifyThisFile=true) : this(scanner, errors) // the real work diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index 66b91ac2..efe94c66 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -27,20 +27,22 @@ namespace Microsoft.Dafny { public List CompileModules; // filled in during resolution. // Contains the definitions to be used for compilation. - List _additionalInformation = new List(); - public List AdditionalInformation { get { return _additionalInformation; } } public readonly ModuleDecl DefaultModule; public readonly ModuleDefinition DefaultModuleDef; public readonly BuiltIns BuiltIns; public readonly List TranslationTasks; - public Program(string name, [Captured] ModuleDecl module, [Captured] BuiltIns builtIns) { + public readonly ErrorReporter reporter; + + public Program(string name, [Captured] ModuleDecl module, [Captured] BuiltIns builtIns, ErrorReporter reporter) { Contract.Requires(name != null); Contract.Requires(module != null); Contract.Requires(module is LiteralModuleDecl); + Contract.Requires(reporter != null); FullName = name; DefaultModule = module; DefaultModuleDef = (DefaultModuleDecl)((LiteralModuleDecl)module).ModuleDef; BuiltIns = builtIns; + this.reporter = reporter; Modules = new List(); CompileModules = new List(); TranslationTasks = new List(); @@ -330,13 +332,13 @@ namespace Microsoft.Dafny { /// - if "allowed" contains Int and Args contains one BigInteger literal, return true and set value to the BigInteger literal. Otherwise, /// - if "allowed" contains String and Args contains one string literal, return true and set value to the string literal. Otherwise, /// - if "allowed" contains Expression and Args contains one element, return true and set value to the one element (of type Expression). Otherwise, - /// - return false, leave value unmodified, and call errorReporter with an error string. + /// - return false, leave value unmodified, and call reporter with an error string. /// public enum MatchingValueOption { Empty, Bool, Int, String, Expression } - public static bool ContainsMatchingValue(Attributes attrs, string nm, ref object value, IEnumerable allowed, Action errorReporter) { + public static bool ContainsMatchingValue(Attributes attrs, string nm, ref object value, IEnumerable allowed, Action reporter) { Contract.Requires(nm != null); Contract.Requires(allowed != null); - Contract.Requires(errorReporter != null); + Contract.Requires(reporter != null); List args = FindExpressions(attrs, nm); if (args == null) { return false; @@ -344,7 +346,7 @@ namespace Microsoft.Dafny { if (allowed.Contains(MatchingValueOption.Empty)) { return true; } else { - errorReporter("Attribute " + nm + " requires one argument"); + reporter("Attribute " + nm + " requires one argument"); return false; } } else if (args.Count == 1) { @@ -364,11 +366,11 @@ namespace Microsoft.Dafny { value = arg; return true; } else { - errorReporter("Attribute " + nm + " expects an argument in one of the following categories: " + String.Join(", ", allowed)); + reporter("Attribute " + nm + " expects an argument in one of the following categories: " + String.Join(", ", allowed)); return false; } } else { - errorReporter("Attribute " + nm + " cannot have more than one argument"); + reporter("Attribute " + nm + " cannot have more than one argument"); return false; } } diff --git a/Source/Dafny/DafnyMain.cs b/Source/Dafny/DafnyMain.cs index 012ca4df..251d8656 100644 --- a/Source/Dafny/DafnyMain.cs +++ b/Source/Dafny/DafnyMain.cs @@ -29,7 +29,7 @@ namespace Microsoft.Dafny { /// /// Returns null on success, or an error string otherwise. /// - public static string ParseCheck(IList/*!*/ fileNames, string/*!*/ programName, out Program program) + public static string ParseCheck(IList/*!*/ fileNames, string/*!*/ programName, ErrorReporter reporter, out Program program) //modifies Bpl.CommandLineOptions.Clo.XmlSink.*; { Contract.Requires(programName != null); @@ -47,20 +47,20 @@ namespace Microsoft.Dafny { Console.WriteLine("Parsing " + dafnyFileName); } - string err = ParseFile(dafnyFileName, Bpl.Token.NoToken, module, builtIns, new Errors()); + string err = ParseFile(dafnyFileName, Bpl.Token.NoToken, module, builtIns, new Errors(reporter)); if (err != null) { return err; } } if (!DafnyOptions.O.DisallowIncludes) { - string errString = ParseIncludes(module, builtIns, fileNames, new Errors()); + string errString = ParseIncludes(module, builtIns, fileNames, new Errors(reporter)); if (errString != null) { return errString; } } - program = new Program(programName, module, builtIns); + program = new Program(programName, module, builtIns, reporter); MaybePrintProgram(program, DafnyOptions.O.DafnyPrintFile); @@ -70,8 +70,8 @@ namespace Microsoft.Dafny { r.ResolveProgram(program); MaybePrintProgram(program, DafnyOptions.O.DafnyPrintResolvedFile); - if (r.ErrorCount != 0) { - return string.Format("{0} resolution/type errors detected in {1}", r.ErrorCount, program.Name); + if (reporter.Count(ErrorLevel.Error) != 0) { + return string.Format("{0} resolution/type errors detected in {1}", reporter.Count(ErrorLevel.Error), program.Name); } return null; // success diff --git a/Source/Dafny/DafnyPipeline.csproj b/Source/Dafny/DafnyPipeline.csproj index a1452b8a..626bb26a 100644 --- a/Source/Dafny/DafnyPipeline.csproj +++ b/Source/Dafny/DafnyPipeline.csproj @@ -1,4 +1,4 @@ - + Debug @@ -8,7 +8,7 @@ {FE44674A-1633-4917-99F4-57635E6FA740} Library Properties - DafnyPipeline + Microsoft.Dafny DafnyPipeline v4.0 512 @@ -143,6 +143,7 @@ + @@ -193,4 +194,4 @@ --> - \ No newline at end of file + diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs index fd6fb026..2507cacc 100644 --- a/Source/Dafny/Parser.cs +++ b/Source/Dafny/Parser.cs @@ -126,11 +126,11 @@ public static int Parse (string/*!*/ filename, ModuleDecl module, BuiltIns built /// Returns the number of parsing errors encountered. /// Note: first initialize the Scanner. /// -public static int Parse (string/*!*/ s, string/*!*/ fullFilename, string/*!*/ filename, ModuleDecl module, BuiltIns builtIns, bool verifyThisFile=true) { +public static int Parse (string/*!*/ s, string/*!*/ fullFilename, string/*!*/ filename, ModuleDecl module, BuiltIns builtIns, ErrorReporter reporter, bool verifyThisFile=true) { Contract.Requires(s != null); Contract.Requires(filename != null); Contract.Requires(module != null); - Errors errors = new Errors(); + Errors errors = new Errors(reporter); return Parse(s, fullFilename, filename, module, builtIns, errors, verifyThisFile); } /// @@ -150,7 +150,7 @@ public static int Parse (string/*!*/ s, string/*!*/ fullFilename, string/*!*/ fi Scanner scanner = new Scanner(ms, errors, fullFilename, filename); Parser parser = new Parser(scanner, errors, module, builtIns, verifyThisFile); parser.Parse(); - return parser.errors.count; + return parser.errors.ErrorCount; } public Parser(Scanner/*!*/ scanner, Errors/*!*/ errors, ModuleDecl module, BuiltIns builtIns, bool verifyThisFile=true) : this(scanner, errors) // the real work @@ -4426,16 +4426,22 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo public class Errors { - public int count = 0; // number of errors detected + readonly ErrorReporter reporter; + public int ErrorCount; + + public Errors(ErrorReporter reporter) { + Contract.Requires(reporter != null); + this.reporter = reporter; + } public void SynErr(string filename, int line, int col, int n) { SynErr(filename, line, col, GetSyntaxErrorString(n)); } - public virtual void SynErr(string filename, int line, int col, string msg) { + public void SynErr(string filename, int line, int col, string msg) { Contract.Requires(msg != null); - Dafny.Util.ReportIssue("Error", filename, line, col, msg); - count++; + ErrorCount++; + reporter.Error(MessageSource.Parser, filename, line, col, msg); } string GetSyntaxErrorString(int n) { @@ -4692,20 +4698,20 @@ public class Errors { public void SemErr(IToken tok, string msg) { // semantic errors Contract.Requires(tok != null); Contract.Requires(msg != null); - Dafny.Util.ReportIssue("Error", tok, msg); - count++; + ErrorCount++; + reporter.Error(MessageSource.Parser, tok, msg); } - public virtual void SemErr(string filename, int line, int col, string msg) { + public void SemErr(string filename, int line, int col, string msg) { Contract.Requires(msg != null); - Dafny.Util.ReportIssue("Error", filename, line, col, msg); - count++; + ErrorCount++; + reporter.Error(MessageSource.Parser, filename, line, col, msg); } - public virtual void Warning(IToken tok, string msg) { // warnings + public void Warning(IToken tok, string msg) { Contract.Requires(tok != null); Contract.Requires(msg != null); - Dafny.Util.ReportIssue("Warning", tok, msg); + reporter.Warning(MessageSource.Parser, tok, msg); } } // Errors diff --git a/Source/Dafny/RefinementTransformer.cs b/Source/Dafny/RefinementTransformer.cs index f430933b..ba558ea6 100644 --- a/Source/Dafny/RefinementTransformer.cs +++ b/Source/Dafny/RefinementTransformer.cs @@ -52,16 +52,19 @@ namespace Microsoft.Dafny public class RefinementTransformer : IRewriter { - ResolutionErrorReporter reporter; - Action additionalInformationReporter; Cloner rawCloner; // This cloner just gives exactly the same thing back. RefinementCloner refinementCloner; // This cloner wraps things in a RefinementTransformer + Program program; - public RefinementTransformer(ResolutionErrorReporter reporter, Action additionalInformationReporter, Program p) { - Contract.Requires(reporter != null); - this.reporter = reporter; - this.additionalInformationReporter = additionalInformationReporter; + + public RefinementTransformer(ErrorReporter reporter) + : base(reporter) { rawCloner = new Cloner(); + } + + public RefinementTransformer(Program p) + : this(p.reporter) { + Contract.Requires(p != null); program = p; } @@ -71,17 +74,7 @@ namespace Microsoft.Dafny private Method currentMethod; public ModuleSignature RefinedSig; // the intention is to use this field only after a successful PreResolve - void ReportAdditionalInformation(IToken token, string text, int length) - { - Contract.Requires(token != null); - Contract.Requires(text != null); - Contract.Requires(0 <= length); - if (additionalInformationReporter != null) { - additionalInformationReporter(new AdditionalInformation { Token = token, Text = text, Length = length }); - } - } - - public void PreResolve(ModuleDefinition m) { + internal override void PreResolve(ModuleDefinition m) { if (m.RefinementBaseRoot != null) { if (Resolver.ResolvePath(m.RefinementBaseRoot, m.RefinementBaseName, out RefinedSig, reporter)) { if (RefinedSig.ModuleDef != null) { @@ -90,17 +83,17 @@ namespace Microsoft.Dafny if (null == m.RefinementBase.ExclusiveRefinement) { m.RefinementBase.ExclusiveRefinement = m; } else { - this.reporter.Error( + reporter.Error(MessageSource.RefinementTransformer, m.tok, "no more than one exclusive refinement may exist for a given module."); } } PreResolveWorker(m); } else { - reporter.Error(m.RefinementBaseName[0], "module ({0}) named as refinement base is not a literal module or simple reference to a literal module", Util.Comma(".", m.RefinementBaseName, x => x.val)); + reporter.Error(MessageSource.RefinementTransformer, m.RefinementBaseName[0], "module ({0}) named as refinement base is not a literal module or simple reference to a literal module", Util.Comma(".", m.RefinementBaseName, x => x.val)); } } else { - reporter.Error(m.RefinementBaseName[0], "module ({0}) named as refinement base does not exist", Util.Comma(".", m.RefinementBaseName, x => x.val)); + reporter.Error(MessageSource.RefinementTransformer, m.RefinementBaseName[0], "module ({0}) named as refinement base does not exist", Util.Comma(".", m.RefinementBaseName, x => x.val)); } } } @@ -135,9 +128,9 @@ namespace Microsoft.Dafny var nw = m.TopLevelDecls[index]; if (d is ModuleDecl) { if (!(nw is ModuleDecl)) { - reporter.Error(nw, "a module ({0}) must refine another module", nw.Name); + reporter.Error(MessageSource.RefinementTransformer, nw, "a module ({0}) must refine another module", nw.Name); } else if (!(d is ModuleFacadeDecl)) { - reporter.Error(nw, "a module ({0}) can only refine a module facade", nw.Name); + reporter.Error(MessageSource.RefinementTransformer, nw, "a module ({0}) can only refine a module facade", nw.Name); } else { ModuleSignature original = ((ModuleFacadeDecl)d).OriginalSignature; ModuleSignature derived = null; @@ -146,72 +139,72 @@ namespace Microsoft.Dafny } else if (nw is ModuleFacadeDecl) { derived = ((ModuleFacadeDecl)nw).Signature; } else { - reporter.Error(nw, "a module ({0}) can only be refined by an alias module or a module facade", d.Name); + reporter.Error(MessageSource.RefinementTransformer, nw, "a module ({0}) can only be refined by an alias module or a module facade", d.Name); } if (derived != null) { // check that the new module refines the previous declaration if (!CheckIsRefinement(derived, original)) - reporter.Error(nw.tok, "a module ({0}) can only be replaced by a refinement of the original module", d.Name); + reporter.Error(MessageSource.RefinementTransformer, nw.tok, "a module ({0}) can only be replaced by a refinement of the original module", d.Name); } } } else if (d is OpaqueTypeDecl) { if (nw is ModuleDecl) { - reporter.Error(nw, "a module ({0}) must refine another module", nw.Name); + reporter.Error(MessageSource.RefinementTransformer, nw, "a module ({0}) must refine another module", nw.Name); } else { bool dDemandsEqualitySupport = ((OpaqueTypeDecl)d).MustSupportEquality; if (nw is OpaqueTypeDecl) { if (dDemandsEqualitySupport != ((OpaqueTypeDecl)nw).MustSupportEquality) { - reporter.Error(nw, "type declaration '{0}' is not allowed to change the requirement of supporting equality", nw.Name); + reporter.Error(MessageSource.RefinementTransformer, nw, "type declaration '{0}' is not allowed to change the requirement of supporting equality", nw.Name); } if (nw.TypeArgs.Count != d.TypeArgs.Count) { - reporter.Error(nw, "type '{0}' is not allowed to change its number of type parameters (got {1}, expected {2})", nw.Name, nw.TypeArgs.Count, d.TypeArgs.Count); + reporter.Error(MessageSource.RefinementTransformer, nw, "type '{0}' is not allowed to change its number of type parameters (got {1}, expected {2})", nw.Name, nw.TypeArgs.Count, d.TypeArgs.Count); } } else if (dDemandsEqualitySupport) { if (nw is ClassDecl) { // fine, as long as "nw" takes the right number of type parameters if (nw.TypeArgs.Count != d.TypeArgs.Count) { - reporter.Error(nw, "opaque type '{0}' is not allowed to be replaced by a class that takes a different number of type parameters (got {1}, expected {2})", nw.Name, nw.TypeArgs.Count, d.TypeArgs.Count); + reporter.Error(MessageSource.RefinementTransformer, nw, "opaque type '{0}' is not allowed to be replaced by a class that takes a different number of type parameters (got {1}, expected {2})", nw.Name, nw.TypeArgs.Count, d.TypeArgs.Count); } } else if (nw is NewtypeDecl) { // fine, as long as "nw" does not take any type parameters if (nw.TypeArgs.Count != 0) { - reporter.Error(nw, "opaque type '{0}', which has {1} type argument{2}, is not allowed to be replaced by a newtype, which takes none", nw.Name, d.TypeArgs.Count, d.TypeArgs.Count == 1 ? "" : "s"); + reporter.Error(MessageSource.RefinementTransformer, nw, "opaque type '{0}', which has {1} type argument{2}, is not allowed to be replaced by a newtype, which takes none", nw.Name, d.TypeArgs.Count, d.TypeArgs.Count == 1 ? "" : "s"); } } else if (nw is CoDatatypeDecl) { - reporter.Error(nw, "a type declaration that requires equality support cannot be replaced by a codatatype"); + reporter.Error(MessageSource.RefinementTransformer, nw, "a type declaration that requires equality support cannot be replaced by a codatatype"); } else { Contract.Assert(nw is IndDatatypeDecl || nw is TypeSynonymDecl); if (nw.TypeArgs.Count != d.TypeArgs.Count) { - reporter.Error(nw, "opaque type '{0}' is not allowed to be replaced by a type that takes a different number of type parameters (got {1}, expected {2})", nw.Name, nw.TypeArgs.Count, d.TypeArgs.Count); + reporter.Error(MessageSource.RefinementTransformer, nw, "opaque type '{0}' is not allowed to be replaced by a type that takes a different number of type parameters (got {1}, expected {2})", nw.Name, nw.TypeArgs.Count, d.TypeArgs.Count); } else { // Here, we need to figure out if the new type supports equality. But we won't know about that until resolution has // taken place, so we defer it until the PostResolve phase. var udt = UserDefinedType.FromTopLevelDecl(nw.tok, nw); postTasks.Enqueue(() => { if (!udt.SupportsEquality) { - reporter.Error(udt.tok, "type '{0}' is used to refine an opaque type with equality support, but '{0}' does not support equality", udt.Name); + reporter.Error(MessageSource.RefinementTransformer, udt.tok, "type '{0}' is used to refine an opaque type with equality support, but '{0}' does not support equality", udt.Name); } }); } } } else if (d.TypeArgs.Count != nw.TypeArgs.Count) { - reporter.Error(nw, "opaque type '{0}' is not allowed to be replaced by a type that takes a different number of type parameters (got {1}, expected {2})", nw.Name, nw.TypeArgs.Count, d.TypeArgs.Count); + reporter.Error(MessageSource.RefinementTransformer, nw, "opaque type '{0}' is not allowed to be replaced by a type that takes a different number of type parameters (got {1}, expected {2})", nw.Name, nw.TypeArgs.Count, d.TypeArgs.Count); } } } else if (nw is OpaqueTypeDecl) { - reporter.Error(nw, "an opaque type declaration ({0}) in a refining module cannot replace a more specific type declaration in the refinement base", nw.Name); + reporter.Error(MessageSource.RefinementTransformer, nw, "an opaque type declaration ({0}) in a refining module cannot replace a more specific type declaration in the refinement base", nw.Name); } else if (nw is DatatypeDecl) { - reporter.Error(nw, "a datatype declaration ({0}) in a refinement module can only replace an opaque type declaration", nw.Name); + reporter.Error(MessageSource.RefinementTransformer, nw, "a datatype declaration ({0}) in a refinement module can only replace an opaque type declaration", nw.Name); } else if (nw is IteratorDecl) { if (d is IteratorDecl) { m.TopLevelDecls[index] = MergeIterator((IteratorDecl)nw, (IteratorDecl)d); } else { - reporter.Error(nw, "an iterator declaration ({0}) is a refining module cannot replace a different kind of declaration in the refinement base", nw.Name); + reporter.Error(MessageSource.RefinementTransformer, nw, "an iterator declaration ({0}) is a refining module cannot replace a different kind of declaration in the refinement base", nw.Name); } } else { Contract.Assert(nw is ClassDecl); if (d is DatatypeDecl) { - reporter.Error(nw, "a class declaration ({0}) in a refining module cannot replace a different kind of declaration in the refinement base", nw.Name); + reporter.Error(MessageSource.RefinementTransformer, nw, "a class declaration ({0}) in a refining module cannot replace a different kind of declaration in the refinement base", nw.Name); } else { m.TopLevelDecls[index] = MergeClass((ClassDecl)nw, (ClassDecl)d); } @@ -238,42 +231,42 @@ namespace Microsoft.Dafny // Second, we need to determine whether the specifications will be compatible // (i.e. substitutable), by translating to Boogie. - var errorCount = reporter.ErrorCount; + var errorCount = reporter.Count(ErrorLevel.Error); foreach (var kv in original.TopLevels) { var d = kv.Value; TopLevelDecl nw; if (derived.TopLevels.TryGetValue(kv.Key, out nw)) { if (d is ModuleDecl) { if (!(nw is ModuleDecl)) { - reporter.Error(nw, "a module ({0}) must refine another module", nw.Name); + reporter.Error(MessageSource.RefinementTransformer, nw, "a module ({0}) must refine another module", nw.Name); } else { CheckIsRefinement(((ModuleDecl)nw).Signature, ((ModuleDecl)d).Signature); } } else if (d is OpaqueTypeDecl) { if (nw is ModuleDecl) { - reporter.Error(nw, "a module ({0}) must refine another module", nw.Name); + reporter.Error(MessageSource.RefinementTransformer, nw, "a module ({0}) must refine another module", nw.Name); } else { bool dDemandsEqualitySupport = ((OpaqueTypeDecl)d).MustSupportEquality; if (nw is OpaqueTypeDecl) { if (dDemandsEqualitySupport != ((OpaqueTypeDecl)nw).MustSupportEquality) { - reporter.Error(nw, "type declaration '{0}' is not allowed to change the requirement of supporting equality", nw.Name); + reporter.Error(MessageSource.RefinementTransformer, nw, "type declaration '{0}' is not allowed to change the requirement of supporting equality", nw.Name); } } else if (dDemandsEqualitySupport) { if (nw is ClassDecl) { // fine, as long as "nw" does not take any type parameters if (nw.TypeArgs.Count != 0) { - reporter.Error(nw, "opaque type '{0}' is not allowed to be replaced by a class that takes type parameters", nw.Name); + reporter.Error(MessageSource.RefinementTransformer, nw, "opaque type '{0}' is not allowed to be replaced by a class that takes type parameters", nw.Name); } } else if (nw is CoDatatypeDecl) { - reporter.Error(nw, "a type declaration that requires equality support cannot be replaced by a codatatype"); + reporter.Error(MessageSource.RefinementTransformer, nw, "a type declaration that requires equality support cannot be replaced by a codatatype"); } else { Contract.Assert(nw is IndDatatypeDecl); if (nw.TypeArgs.Count != 0) { - reporter.Error(nw, "opaque type '{0}' is not allowed to be replaced by a datatype that takes type parameters", nw.Name); + reporter.Error(MessageSource.RefinementTransformer, nw, "opaque type '{0}' is not allowed to be replaced by a datatype that takes type parameters", nw.Name); } else { var udt = new UserDefinedType(nw.tok, nw.Name, nw, new List()); if (!(udt.SupportsEquality)) { - reporter.Error(nw.tok, "datatype '{0}' is used to refine an opaque type with equality support, but '{0}' does not support equality", nw.Name); + reporter.Error(MessageSource.RefinementTransformer, nw.tok, "datatype '{0}' is used to refine an opaque type with equality support, but '{0}' does not support equality", nw.Name); } } } @@ -282,18 +275,18 @@ namespace Microsoft.Dafny } else if (d is DatatypeDecl) { if (nw is DatatypeDecl) { if (d is IndDatatypeDecl && !(nw is IndDatatypeDecl)) { - reporter.Error(nw, "a datatype ({0}) must be replaced by a datatype, not a codatatype", d.Name); + reporter.Error(MessageSource.RefinementTransformer, nw, "a datatype ({0}) must be replaced by a datatype, not a codatatype", d.Name); } else if (d is CoDatatypeDecl && !(nw is CoDatatypeDecl)) { - reporter.Error(nw, "a codatatype ({0}) must be replaced by a codatatype, not a datatype", d.Name); + reporter.Error(MessageSource.RefinementTransformer, nw, "a codatatype ({0}) must be replaced by a codatatype, not a datatype", d.Name); } // check constructors, formals, etc. CheckDatatypesAreRefinements((DatatypeDecl)d, (DatatypeDecl)nw); } else { - reporter.Error(nw, "a {0} ({1}) must be refined by a {0}", d is IndDatatypeDecl ? "datatype" : "codatatype", d.Name); + reporter.Error(MessageSource.RefinementTransformer, nw, "a {0} ({1}) must be refined by a {0}", d is IndDatatypeDecl ? "datatype" : "codatatype", d.Name); } } else if (d is ClassDecl) { if (!(nw is ClassDecl)) { - reporter.Error(nw, "a class declaration ({0}) must be refined by another class declaration", nw.Name); + reporter.Error(MessageSource.RefinementTransformer, nw, "a class declaration ({0}) must be refined by another class declaration", nw.Name); } else { CheckClassesAreRefinements((ClassDecl)nw, (ClassDecl)d); } @@ -301,15 +294,15 @@ namespace Microsoft.Dafny Contract.Assert(false); throw new cce.UnreachableException(); // unexpected toplevel } } else { - reporter.Error(d, "declaration {0} must have a matching declaration in the refining module", d.Name); + reporter.Error(MessageSource.RefinementTransformer, d, "declaration {0} must have a matching declaration in the refining module", d.Name); } } - return errorCount == reporter.ErrorCount; + return errorCount == reporter.Count(ErrorLevel.Error); } private void CheckClassesAreRefinements(ClassDecl nw, ClassDecl d) { if (nw.TypeArgs.Count != d.TypeArgs.Count) { - reporter.Error(nw, "a refining class ({0}) must have the same number of type parameters", nw.Name); + reporter.Error(MessageSource.RefinementTransformer, nw, "a refining class ({0}) must have the same number of type parameters", nw.Name); } else { var map = new Dictionary(); foreach (var mem in nw.Members) { @@ -319,33 +312,33 @@ namespace Microsoft.Dafny MemberDecl newMem; if (map.TryGetValue(m.Name, out newMem)) { if (m.HasStaticKeyword != newMem.HasStaticKeyword) { - reporter.Error(newMem, "member {0} must {1}", m.Name, m.HasStaticKeyword ? "be static" : "not be static"); + reporter.Error(MessageSource.RefinementTransformer, newMem, "member {0} must {1}", m.Name, m.HasStaticKeyword ? "be static" : "not be static"); } if (m is Field) { if (newMem is Field) { var newField = (Field)newMem; if (!ResolvedTypesAreTheSame(newField.Type, ((Field)m).Type)) - reporter.Error(newMem, "field must be refined by a field with the same type (got {0}, expected {1})", newField.Type, ((Field)m).Type); + reporter.Error(MessageSource.RefinementTransformer, newMem, "field must be refined by a field with the same type (got {0}, expected {1})", newField.Type, ((Field)m).Type); if (m.IsGhost || !newField.IsGhost) - reporter.Error(newField, "a field re-declaration ({0}) must be to ghostify the field", newField.Name, nw.Name); + reporter.Error(MessageSource.RefinementTransformer, newField, "a field re-declaration ({0}) must be to ghostify the field", newField.Name, nw.Name); } else { - reporter.Error(newMem, "a field declaration ({1}) must be replaced by a field in the refinement base (not {0})", newMem.Name, nw.Name); + reporter.Error(MessageSource.RefinementTransformer, newMem, "a field declaration ({1}) must be replaced by a field in the refinement base (not {0})", newMem.Name, nw.Name); } } else if (m is Method) { if (newMem is Method) { CheckMethodsAreRefinements((Method)newMem, (Method)m); } else { - reporter.Error(newMem, "method must be refined by a method"); + reporter.Error(MessageSource.RefinementTransformer, newMem, "method must be refined by a method"); } } else if (m is Function) { if (newMem is Function) { CheckFunctionsAreRefinements((Function)newMem, (Function)m); } else { - reporter.Error(newMem, "{0} must be refined by a {0}", m.WhatKind); + reporter.Error(MessageSource.RefinementTransformer, newMem, "{0} must be refined by a {0}", m.WhatKind); } } } else { - reporter.Error(nw is DefaultClassDecl ? nw.Module.tok : nw.tok, "refining {0} must have member {1}", nw is DefaultClassDecl ? "module" : "class", m.Name); + reporter.Error(MessageSource.RefinementTransformer, nw is DefaultClassDecl ? nw.Module.tok : nw.tok, "refining {0} must have member {1}", nw is DefaultClassDecl ? "module" : "class", m.Name); } } } @@ -358,17 +351,17 @@ namespace Microsoft.Dafny Contract.Requires(thing != null); Contract.Requires(parameterKind != null); if (old.Count != nw.Count) { - reporter.Error(tok, "{0} '{1}' is declared with a different number of {2} ({3} instead of {4}) than the corresponding {0} in the module it refines", thing, name, parameterKind, nw.Count, old.Count); + reporter.Error(MessageSource.RefinementTransformer, tok, "{0} '{1}' is declared with a different number of {2} ({3} instead of {4}) than the corresponding {0} in the module it refines", thing, name, parameterKind, nw.Count, old.Count); } else { for (int i = 0; i < old.Count; i++) { var o = old[i]; var n = nw[i]; if (!o.IsGhost && n.IsGhost) { - reporter.Error(n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it refines, from non-ghost to ghost", parameterKind, n.Name, thing, name); + reporter.Error(MessageSource.RefinementTransformer, n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it refines, from non-ghost to ghost", parameterKind, n.Name, thing, name); } else if (o.IsGhost && !n.IsGhost) { - reporter.Error(n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it refines, from ghost to non-ghost", parameterKind, n.Name, thing, name); + reporter.Error(MessageSource.RefinementTransformer, n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it refines, from ghost to non-ghost", parameterKind, n.Name, thing, name); } else if (!ResolvedTypesAreTheSame(o.Type, n.Type)) { - reporter.Error(n.tok, "the type of {0} '{1}' is different from the type of the same {0} in the corresponding {2} in the module it refines ('{3}' instead of '{4}')", parameterKind, n.Name, thing, n.Type, o.Type); + reporter.Error(MessageSource.RefinementTransformer, n.tok, "the type of {0} '{1}' is different from the type of the same {0} in the corresponding {2} in the module it refines ('{3}' instead of '{4}')", parameterKind, n.Name, thing, n.Type, o.Type); } } } @@ -382,22 +375,22 @@ namespace Microsoft.Dafny private void CheckFunctionsAreRefinements(Function nw, Function f) { if (f is Predicate) { if (!(nw is Predicate)) { - reporter.Error(nw, "a predicate declaration ({0}) can only be refined by a predicate", nw.Name); + reporter.Error(MessageSource.RefinementTransformer, nw, "a predicate declaration ({0}) can only be refined by a predicate", nw.Name); } else { CheckAgreement_TypeParameters(nw.tok, f.TypeArgs, nw.TypeArgs, nw.Name, "predicate", false); CheckAgreementResolvedParameters(nw.tok, f.Formals, nw.Formals, nw.Name, "predicate", "parameter"); } } else if (f is FixpointPredicate) { - reporter.Error(nw, "refinement of {0}s is not supported", f.WhatKind); + reporter.Error(MessageSource.RefinementTransformer, nw, "refinement of {0}s is not supported", f.WhatKind); } else { // f is a plain Function if (nw is Predicate || nw is FixpointPredicate) { - reporter.Error(nw, "a {0} declaration ({1}) can only be refined by a function or function method", nw.IsGhost ? "function" : "function method", nw.Name); + reporter.Error(MessageSource.RefinementTransformer, nw, "a {0} declaration ({1}) can only be refined by a function or function method", nw.IsGhost ? "function" : "function method", nw.Name); } else { CheckAgreement_TypeParameters(nw.tok, f.TypeArgs, nw.TypeArgs, nw.Name, "function", false); CheckAgreementResolvedParameters(nw.tok, f.Formals, nw.Formals, nw.Name, "function", "parameter"); if (!ResolvedTypesAreTheSame(nw.ResultType, f.ResultType)) { - reporter.Error(nw, "the result type of function '{0}' ({1}) differs from the result type of the corresponding function in the module it refines ({2})", nw.Name, nw.ResultType, f.ResultType); + reporter.Error(MessageSource.RefinementTransformer, nw, "the result type of function '{0}' ({1}) differs from the result type of the corresponding function in the module it refines ({2})", nw.Name, nw.ResultType, f.ResultType); } } } @@ -408,7 +401,7 @@ namespace Microsoft.Dafny private void CheckDatatypesAreRefinements(DatatypeDecl dd, DatatypeDecl nn) { CheckAgreement_TypeParameters(nn.tok, dd.TypeArgs, nn.TypeArgs, dd.Name, "datatype", false); if (dd.Ctors.Count != nn.Ctors.Count) { - reporter.Error(nn.tok, "a refining datatype must have the same number of constructors"); + reporter.Error(MessageSource.RefinementTransformer, nn.tok, "a refining datatype must have the same number of constructors"); } else { var map = new Dictionary(); foreach (var ctor in nn.Ctors) { @@ -418,21 +411,21 @@ namespace Microsoft.Dafny DatatypeCtor newCtor; if (map.TryGetValue(ctor.Name, out newCtor)) { if (newCtor.Formals.Count != ctor.Formals.Count) { - reporter.Error(newCtor, "the constructor ({0}) must have the same number of formals as in the refined module", newCtor.Name); + reporter.Error(MessageSource.RefinementTransformer, newCtor, "the constructor ({0}) must have the same number of formals as in the refined module", newCtor.Name); } else { for (int i = 0; i < newCtor.Formals.Count; i++) { var a = ctor.Formals[i]; var b = newCtor.Formals[i]; if (a.HasName) { if (!b.HasName || a.Name != b.Name) - reporter.Error(b, "formal argument {0} in constructor {1} does not have the same name as in the refined module (should be {2})", i, ctor.Name, a.Name); + reporter.Error(MessageSource.RefinementTransformer, b, "formal argument {0} in constructor {1} does not have the same name as in the refined module (should be {2})", i, ctor.Name, a.Name); } if (!ResolvedTypesAreTheSame(a.Type, b.Type)) { - reporter.Error(b, "formal argument {0} in constructor {1} does not have the same type as in the refined module (should be {2}, not {3})", i, ctor.Name, a.Type.ToString(), b.Type.ToString()); + reporter.Error(MessageSource.RefinementTransformer, b, "formal argument {0} in constructor {1} does not have the same type as in the refined module (should be {2}, not {3})", i, ctor.Name, a.Type.ToString(), b.Type.ToString()); } } } } else { - reporter.Error(nn, "the constructor {0} must be present in the refining datatype", ctor.Name); + reporter.Error(MessageSource.RefinementTransformer, nn, "the constructor {0} must be present in the refining datatype", ctor.Name); } } } @@ -501,7 +494,8 @@ namespace Microsoft.Dafny Contract.Assert(false); throw new cce.UnreachableException(); // unexpected type } } - public void PostResolve(ModuleDefinition m) { + + internal override void PostResolve(ModuleDefinition m) { if (m == moduleUnderConstruction) { while (this.postTasks.Count != 0) { var a = postTasks.Dequeue(); @@ -512,8 +506,7 @@ namespace Microsoft.Dafny } moduleUnderConstruction = null; } - public void PostCyclicityResolve(ModuleDefinition m) { - } + Function CloneFunction(IToken tok, Function f, bool isGhost, List moreEnsures, Expression moreBody, Expression replacementBody, bool checkPrevPostconditions, Attributes moreAttributes) { Contract.Requires(tok != null); Contract.Requires(moreBody == null || f is Predicate); @@ -611,26 +604,26 @@ namespace Microsoft.Dafny Contract.Requires(prev != null); if (nw.Requires.Count != 0) { - reporter.Error(nw.Requires[0].E.tok, "a refining iterator is not allowed to add preconditions"); + reporter.Error(MessageSource.RefinementTransformer, nw.Requires[0].E.tok, "a refining iterator is not allowed to add preconditions"); } if (nw.YieldRequires.Count != 0) { - reporter.Error(nw.YieldRequires[0].E.tok, "a refining iterator is not allowed to add yield preconditions"); + reporter.Error(MessageSource.RefinementTransformer, nw.YieldRequires[0].E.tok, "a refining iterator is not allowed to add yield preconditions"); } if (nw.Reads.Expressions.Count != 0) { - reporter.Error(nw.Reads.Expressions[0].E.tok, "a refining iterator is not allowed to extend the reads clause"); + reporter.Error(MessageSource.RefinementTransformer, nw.Reads.Expressions[0].E.tok, "a refining iterator is not allowed to extend the reads clause"); } if (nw.Modifies.Expressions.Count != 0) { - reporter.Error(nw.Modifies.Expressions[0].E.tok, "a refining iterator is not allowed to extend the modifies clause"); + reporter.Error(MessageSource.RefinementTransformer, nw.Modifies.Expressions[0].E.tok, "a refining iterator is not allowed to extend the modifies clause"); } if (nw.Decreases.Expressions.Count != 0) { - reporter.Error(nw.Decreases.Expressions[0].tok, "a refining iterator is not allowed to extend the decreases clause"); + reporter.Error(MessageSource.RefinementTransformer, nw.Decreases.Expressions[0].tok, "a refining iterator is not allowed to extend the decreases clause"); } if (nw.SignatureIsOmitted) { Contract.Assert(nw.TypeArgs.Count == 0); Contract.Assert(nw.Ins.Count == 0); Contract.Assert(nw.Outs.Count == 0); - ReportAdditionalInformation(nw.SignatureEllipsis, Printer.IteratorSignatureToString(prev), 3); + reporter.Info(MessageSource.RefinementTransformer, nw.SignatureEllipsis, Printer.IteratorSignatureToString(prev)); } else { CheckAgreement_TypeParameters(nw.tok, prev.TypeArgs, nw.TypeArgs, nw.Name, "iterator"); CheckAgreement_Parameters(nw.tok, prev.Ins, nw.Ins, nw.Name, "iterator", "in-parameter"); @@ -694,9 +687,9 @@ namespace Microsoft.Dafny if (nwMember is Field) { if (member is Field && TypesAreSyntacticallyEqual(((Field)nwMember).Type, ((Field)member).Type)) { if (member.IsGhost || !nwMember.IsGhost) - reporter.Error(nwMember, "a field re-declaration ({0}) must be to ghostify the field", nwMember.Name, nw.Name); + reporter.Error(MessageSource.RefinementTransformer, nwMember, "a field re-declaration ({0}) must be to ghostify the field", nwMember.Name, nw.Name); } else { - reporter.Error(nwMember, "a field declaration ({0}) in a refining class ({1}) must replace a field in the refinement base", nwMember.Name, nw.Name); + reporter.Error(MessageSource.RefinementTransformer, nwMember, "a field declaration ({0}) in a refining class ({1}) must replace a field in the refinement base", nwMember.Name, nw.Name); } nwMember.RefinementBase = member; @@ -709,38 +702,38 @@ namespace Microsoft.Dafny (isPredicate && !(member is Predicate)) || (isIndPredicate && !(member is InductivePredicate)) || (isCoPredicate && !(member is CoPredicate))) { - reporter.Error(nwMember, "a {0} declaration ({1}) can only refine a {0}", f.WhatKind, nwMember.Name); + reporter.Error(MessageSource.RefinementTransformer, nwMember, "a {0} declaration ({1}) can only refine a {0}", f.WhatKind, nwMember.Name); } else if (f.IsProtected != ((Function)member).IsProtected) { - reporter.Error(f, "a {0} in a refinement module must be declared 'protected' if and only if the refined {0} is", f.WhatKind); + reporter.Error(MessageSource.RefinementTransformer, f, "a {0} in a refinement module must be declared 'protected' if and only if the refined {0} is", f.WhatKind); } else { var prevFunction = (Function)member; if (f.Req.Count != 0) { - reporter.Error(f.Req[0].tok, "a refining {0} is not allowed to add preconditions", f.WhatKind); + reporter.Error(MessageSource.RefinementTransformer, f.Req[0].tok, "a refining {0} is not allowed to add preconditions", f.WhatKind); } if (f.Reads.Count != 0) { - reporter.Error(f.Reads[0].E.tok, "a refining {0} is not allowed to extend the reads clause", f.WhatKind); + reporter.Error(MessageSource.RefinementTransformer, f.Reads[0].E.tok, "a refining {0} is not allowed to extend the reads clause", f.WhatKind); } if (f.Decreases.Expressions.Count != 0) { - reporter.Error(f.Decreases.Expressions[0].tok, "decreases clause on refining {0} not supported", f.WhatKind); + reporter.Error(MessageSource.RefinementTransformer, f.Decreases.Expressions[0].tok, "decreases clause on refining {0} not supported", f.WhatKind); } if (prevFunction.HasStaticKeyword != f.HasStaticKeyword) { - reporter.Error(f, "a function in a refining module cannot be changed from static to non-static or vice versa: {0}", f.Name); + reporter.Error(MessageSource.RefinementTransformer, f, "a function in a refining module cannot be changed from static to non-static or vice versa: {0}", f.Name); } if (!prevFunction.IsGhost && f.IsGhost) { - reporter.Error(f, "a function method cannot be changed into a (ghost) function in a refining module: {0}", f.Name); + reporter.Error(MessageSource.RefinementTransformer, f, "a function method cannot be changed into a (ghost) function in a refining module: {0}", f.Name); } else if (prevFunction.IsGhost && !f.IsGhost && prevFunction.Body != null) { - reporter.Error(f, "a function can be changed into a function method in a refining module only if the function has not yet been given a body: {0}", f.Name); + reporter.Error(MessageSource.RefinementTransformer, f, "a function can be changed into a function method in a refining module only if the function has not yet been given a body: {0}", f.Name); } if (f.SignatureIsOmitted) { Contract.Assert(f.TypeArgs.Count == 0); Contract.Assert(f.Formals.Count == 0); - ReportAdditionalInformation(f.SignatureEllipsis, Printer.FunctionSignatureToString(prevFunction), 3); + reporter.Info(MessageSource.RefinementTransformer, f.SignatureEllipsis, Printer.FunctionSignatureToString(prevFunction)); } else { CheckAgreement_TypeParameters(f.tok, prevFunction.TypeArgs, f.TypeArgs, f.Name, "function"); CheckAgreement_Parameters(f.tok, prevFunction.Formals, f.Formals, f.Name, "function", "parameter"); if (!TypesAreSyntacticallyEqual(prevFunction.ResultType, f.ResultType)) { - reporter.Error(f, "the result type of function '{0}' ({1}) differs from the result type of the corresponding function in the module it refines ({2})", f.Name, f.ResultType, prevFunction.ResultType); + reporter.Error(MessageSource.RefinementTransformer, f, "the result type of function '{0}' ({1}) differs from the result type of the corresponding function in the module it refines ({2})", f.Name, f.ResultType, prevFunction.ResultType); } } @@ -752,9 +745,9 @@ namespace Microsoft.Dafny if (isPredicate && f.IsProtected) { moreBody = f.Body; } else if (isPredicate) { - reporter.Error(nwMember, "a refining predicate is not allowed to extend/change the body unless it is declared 'protected'"); + reporter.Error(MessageSource.RefinementTransformer, nwMember, "a refining predicate is not allowed to extend/change the body unless it is declared 'protected'"); } else { - reporter.Error(nwMember, "a refining function is not allowed to extend/change the body"); + reporter.Error(MessageSource.RefinementTransformer, nwMember, "a refining function is not allowed to extend/change the body"); } } var newF = CloneFunction(f.tok, prevFunction, f.IsGhost, f.Ens, moreBody, replacementBody, prevFunction.Body == null, f.Attributes); @@ -765,14 +758,14 @@ namespace Microsoft.Dafny } else { var m = (Method)nwMember; if (!(member is Method)) { - reporter.Error(nwMember, "a method declaration ({0}) can only refine a method", nwMember.Name); + reporter.Error(MessageSource.RefinementTransformer, nwMember, "a method declaration ({0}) can only refine a method", nwMember.Name); } else { var prevMethod = (Method)member; if (m.Req.Count != 0) { - reporter.Error(m.Req[0].E.tok, "a refining method is not allowed to add preconditions"); + reporter.Error(MessageSource.RefinementTransformer, m.Req[0].E.tok, "a refining method is not allowed to add preconditions"); } if (m.Mod.Expressions.Count != 0) { - reporter.Error(m.Mod.Expressions[0].E.tok, "a refining method is not allowed to extend the modifies clause"); + reporter.Error(MessageSource.RefinementTransformer, m.Mod.Expressions[0].E.tok, "a refining method is not allowed to extend the modifies clause"); } // If the previous method was not specified with "decreases *", then the new method is not allowed to provide any "decreases" clause. // Any "decreases *" clause is not inherited, so if the previous method was specified with "decreases *", then the new method needs @@ -785,23 +778,23 @@ namespace Microsoft.Dafny } else { if (!Contract.Exists(prevMethod.Decreases.Expressions, e => e is WildcardExpr)) { // If the previous loop was not specified with "decreases *", then the new loop is not allowed to provide any "decreases" clause. - reporter.Error(m.Decreases.Expressions[0].tok, "decreases clause on refining method not supported, unless the refined method was specified with 'decreases *'"); + reporter.Error(MessageSource.RefinementTransformer, m.Decreases.Expressions[0].tok, "decreases clause on refining method not supported, unless the refined method was specified with 'decreases *'"); } decreases = m.Decreases; } if (prevMethod.HasStaticKeyword != m.HasStaticKeyword) { - reporter.Error(m, "a method in a refining module cannot be changed from static to non-static or vice versa: {0}", m.Name); + reporter.Error(MessageSource.RefinementTransformer, m, "a method in a refining module cannot be changed from static to non-static or vice versa: {0}", m.Name); } if (prevMethod.IsGhost && !m.IsGhost) { - reporter.Error(m, "a method cannot be changed into a ghost method in a refining module: {0}", m.Name); + reporter.Error(MessageSource.RefinementTransformer, m, "a method cannot be changed into a ghost method in a refining module: {0}", m.Name); } else if (!prevMethod.IsGhost && m.IsGhost) { - reporter.Error(m, "a ghost method cannot be changed into a non-ghost method in a refining module: {0}", m.Name); + reporter.Error(MessageSource.RefinementTransformer, m, "a ghost method cannot be changed into a non-ghost method in a refining module: {0}", m.Name); } if (m.SignatureIsOmitted) { Contract.Assert(m.TypeArgs.Count == 0); Contract.Assert(m.Ins.Count == 0); Contract.Assert(m.Outs.Count == 0); - ReportAdditionalInformation(m.SignatureEllipsis, Printer.MethodSignatureToString(prevMethod), 3); + reporter.Info(MessageSource.RefinementTransformer, m.SignatureEllipsis, Printer.MethodSignatureToString(prevMethod)); } else { CheckAgreement_TypeParameters(m.tok, prevMethod.TypeArgs, m.TypeArgs, m.Name, "method"); CheckAgreement_Parameters(m.tok, prevMethod.Ins, m.Ins, m.Name, "method", "in-parameter"); @@ -833,13 +826,13 @@ namespace Microsoft.Dafny Contract.Requires(name != null); Contract.Requires(thing != null); if (old.Count != nw.Count) { - reporter.Error(tok, "{0} '{1}' is declared with a different number of type parameters ({2} instead of {3}) than the corresponding {0} in the module it refines", thing, name, nw.Count, old.Count); + reporter.Error(MessageSource.RefinementTransformer, tok, "{0} '{1}' is declared with a different number of type parameters ({2} instead of {3}) than the corresponding {0} in the module it refines", thing, name, nw.Count, old.Count); } else { for (int i = 0; i < old.Count; i++) { var o = old[i]; var n = nw[i]; if (o.Name != n.Name && checkNames) { // if checkNames is false, then just treat the parameters positionally. - reporter.Error(n.tok, "type parameters are not allowed to be renamed from the names given in the {0} in the module being refined (expected '{1}', found '{2}')", thing, o.Name, n.Name); + reporter.Error(MessageSource.RefinementTransformer, n.tok, "type parameters are not allowed to be renamed from the names given in the {0} in the module being refined (expected '{1}', found '{2}')", thing, o.Name, n.Name); } else { // This explains what we want to do and why: // switch (o.EqualitySupport) { @@ -859,7 +852,7 @@ namespace Microsoft.Dafny // } // Here's how we actually compute it: if (o.EqualitySupport != TypeParameter.EqualitySupportValue.InferredRequired && o.EqualitySupport != n.EqualitySupport) { - reporter.Error(n.tok, "type parameter '{0}' is not allowed to change the requirement of supporting equality", n.Name); + reporter.Error(MessageSource.RefinementTransformer, n.tok, "type parameter '{0}' is not allowed to change the requirement of supporting equality", n.Name); } } } @@ -872,7 +865,7 @@ namespace Microsoft.Dafny CheckOverrideResolvedParameters(nw.tok, f.Formals, nw.Formals, nw.Name, "function", "parameter"); if (!ResolvedTypesAreTheSame(nw.ResultType, f.ResultType)) { - reporter.Error(nw, "the result type of function '{0}' ({1}) differs from the result type of the corresponding function in the module it overrides ({2})", nw.Name, nw.ResultType, f.ResultType); + reporter.Error(MessageSource.RefinementTransformer, nw, "the result type of function '{0}' ({1}) differs from the result type of the corresponding function in the module it overrides ({2})", nw.Name, nw.ResultType, f.ResultType); } } @@ -892,7 +885,7 @@ namespace Microsoft.Dafny Contract.Requires(thing != null); if (old.Count != nw.Count) { - reporter.Error(tok, "{0} '{1}' is declared with a different number of type parameters ({2} instead of {3}) than the corresponding {0} in the module it overrides", thing, name, nw.Count, old.Count); + reporter.Error(MessageSource.RefinementTransformer, tok, "{0} '{1}' is declared with a different number of type parameters ({2} instead of {3}) than the corresponding {0} in the module it overrides", thing, name, nw.Count, old.Count); } else { @@ -902,14 +895,14 @@ namespace Microsoft.Dafny var n = nw[i]; if (o.Name != n.Name && checkNames) { // if checkNames is false, then just treat the parameters positionally. - reporter.Error(n.tok, "type parameters are not allowed to be renamed from the names given in the {0} in the module being overriden (expected '{1}', found '{2}')", thing, o.Name, n.Name); + reporter.Error(MessageSource.RefinementTransformer, n.tok, "type parameters are not allowed to be renamed from the names given in the {0} in the module being overriden (expected '{1}', found '{2}')", thing, o.Name, n.Name); } else { // Here's how we actually compute it: if (o.EqualitySupport != TypeParameter.EqualitySupportValue.InferredRequired && o.EqualitySupport != n.EqualitySupport) { - reporter.Error(n.tok, "type parameter '{0}' is not allowed to change the requirement of supporting equality", n.Name); + reporter.Error(MessageSource.RefinementTransformer, n.tok, "type parameter '{0}' is not allowed to change the requirement of supporting equality", n.Name); } } } @@ -926,7 +919,7 @@ namespace Microsoft.Dafny Contract.Requires(parameterKind != null); if (old.Count != nw.Count) { - reporter.Error(tok, "{0} '{1}' is declared with a different number of {2} ({3} instead of {4}) than the corresponding {0} in the module it overrides", thing, name, parameterKind, nw.Count, old.Count); + reporter.Error(MessageSource.RefinementTransformer, tok, "{0} '{1}' is declared with a different number of {2} ({3} instead of {4}) than the corresponding {0} in the module it overrides", thing, name, parameterKind, nw.Count, old.Count); } else { @@ -936,15 +929,15 @@ namespace Microsoft.Dafny var n = nw[i]; if (!o.IsGhost && n.IsGhost) { - reporter.Error(n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it overrides, from non-ghost to ghost", parameterKind, n.Name, thing, name); + reporter.Error(MessageSource.RefinementTransformer, n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it overrides, from non-ghost to ghost", parameterKind, n.Name, thing, name); } else if (o.IsGhost && !n.IsGhost) { - reporter.Error(n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it overrides, from ghost to non-ghost", parameterKind, n.Name, thing, name); + reporter.Error(MessageSource.RefinementTransformer, n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it overrides, from ghost to non-ghost", parameterKind, n.Name, thing, name); } else if (!ResolvedTypesAreTheSame(o.Type, n.Type)) { - reporter.Error(n.tok, "the type of {0} '{1}' is different from the type of the same {0} in the corresponding {2} in the module it overrides ('{3}' instead of '{4}')", parameterKind, n.Name, thing, n.Type, o.Type); + reporter.Error(MessageSource.RefinementTransformer, n.tok, "the type of {0} '{1}' is different from the type of the same {0} in the corresponding {2} in the module it overrides ('{3}' instead of '{4}')", parameterKind, n.Name, thing, n.Type, o.Type); } } } @@ -958,19 +951,19 @@ namespace Microsoft.Dafny Contract.Requires(thing != null); Contract.Requires(parameterKind != null); if (old.Count != nw.Count) { - reporter.Error(tok, "{0} '{1}' is declared with a different number of {2} ({3} instead of {4}) than the corresponding {0} in the module it refines", thing, name, parameterKind, nw.Count, old.Count); + reporter.Error(MessageSource.RefinementTransformer, tok, "{0} '{1}' is declared with a different number of {2} ({3} instead of {4}) than the corresponding {0} in the module it refines", thing, name, parameterKind, nw.Count, old.Count); } else { for (int i = 0; i < old.Count; i++) { var o = old[i]; var n = nw[i]; if (o.Name != n.Name) { - reporter.Error(n.tok, "there is a difference in name of {0} {1} ('{2}' versus '{3}') of {4} {5} compared to corresponding {4} in the module it refines", parameterKind, i, n.Name, o.Name, thing, name); + reporter.Error(MessageSource.RefinementTransformer, n.tok, "there is a difference in name of {0} {1} ('{2}' versus '{3}') of {4} {5} compared to corresponding {4} in the module it refines", parameterKind, i, n.Name, o.Name, thing, name); } else if (!o.IsGhost && n.IsGhost) { - reporter.Error(n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it refines, from non-ghost to ghost", parameterKind, n.Name, thing, name); + reporter.Error(MessageSource.RefinementTransformer, n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it refines, from non-ghost to ghost", parameterKind, n.Name, thing, name); } else if (o.IsGhost && !n.IsGhost) { - reporter.Error(n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it refines, from ghost to non-ghost", parameterKind, n.Name, thing, name); + reporter.Error(MessageSource.RefinementTransformer, n.tok, "{0} '{1}' of {2} {3} cannot be changed, compared to the corresponding {2} in the module it refines, from ghost to non-ghost", parameterKind, n.Name, thing, name); } else if (!TypesAreSyntacticallyEqual(o.Type, n.Type)) { - reporter.Error(n.tok, "the type of {0} '{1}' is different from the type of the same {0} in the corresponding {2} in the module it refines ('{3}' instead of '{4}')", parameterKind, n.Name, thing, n.Type, o.Type); + reporter.Error(MessageSource.RefinementTransformer, n.tok, "the type of {0} '{1}' is different from the type of the same {0} in the corresponding {2} in the module it refines ('{3}' instead of '{4}')", parameterKind, n.Name, thing, n.Type, o.Type); } } } @@ -996,7 +989,7 @@ namespace Microsoft.Dafny } else if (((SkeletonStatement)cur).S == null) { // the "..." matches the empty statement sequence } else { - reporter.Error(cur.Tok, "skeleton statement does not match old statement"); + reporter.Error(MessageSource.RefinementTransformer, cur.Tok, "skeleton statement does not match old statement"); } i++; } else { @@ -1050,7 +1043,7 @@ namespace Microsoft.Dafny Contract.Assert(c.NameReplacements.Count == c.ExprReplacements.Count); for (int k = 0; k < c.NameReplacements.Count; k++) { if (subExprs.ContainsKey(c.NameReplacements[k].val)) { - reporter.Error(c.NameReplacements[k], "replacement definition must contain at most one definition for a given label"); + reporter.Error(MessageSource.RefinementTransformer, c.NameReplacements[k], "replacement definition must contain at most one definition for a given label"); } else subExprs.Add(c.NameReplacements[k].val, c.ExprReplacements[k]); } subber = new SubstitutionCloner(subExprs, rawCloner); @@ -1071,12 +1064,12 @@ namespace Microsoft.Dafny oldS = oldStmt.Body[j]; } if (hoverTextA.Length != 0) { - ReportAdditionalInformation(c.Tok, hoverTextA, 3); + reporter.Info(MessageSource.RefinementTransformer, c.Tok, hoverTextA); } if (subber != null && subber.SubstitutionsMade.Count < subber.Exprs.Count) { foreach (var s in subber.SubstitutionsMade) subber.Exprs.Remove(s); - reporter.Error(c.Tok, "could not find labeled expression(s): " + Util.Comma(", ", subber.Exprs.Keys, x => x)); + reporter.Error(MessageSource.RefinementTransformer, c.Tok, "could not find labeled expression(s): " + Util.Comma(", ", subber.Exprs.Keys, x => x)); } } i++; @@ -1086,7 +1079,7 @@ namespace Microsoft.Dafny Contract.Assert(c.ConditionOmitted); var oldAssume = oldS as PredicateStmt; if (oldAssume == null) { - reporter.Error(cur.Tok, "assert template does not match inherited statement"); + reporter.Error(MessageSource.RefinementTransformer, cur.Tok, "assert template does not match inherited statement"); i++; } else { // Clone the expression, but among the new assert's attributes, indicate @@ -1097,7 +1090,7 @@ namespace Microsoft.Dafny var attrs = refinementCloner.MergeAttributes(oldAssume.Attributes, skel.Attributes); body.Add(new AssertStmt(new Translator.ForceCheckToken(skel.Tok), new Translator.ForceCheckToken(skel.EndTok), e, new Attributes("prependAssertToken", new List(), attrs))); - ReportAdditionalInformation(c.ConditionEllipsis, "assume->assert: " + Printer.ExprToString(e), 3); + reporter.Info(MessageSource.RefinementTransformer, c.ConditionEllipsis, "assume->assert: " + Printer.ExprToString(e)); i++; j++; } @@ -1106,13 +1099,13 @@ namespace Microsoft.Dafny Contract.Assert(c.ConditionOmitted); var oldAssume = oldS as AssumeStmt; if (oldAssume == null) { - reporter.Error(cur.Tok, "assume template does not match inherited statement"); + reporter.Error(MessageSource.RefinementTransformer, cur.Tok, "assume template does not match inherited statement"); i++; } else { var e = refinementCloner.CloneExpr(oldAssume.Expr); var attrs = refinementCloner.MergeAttributes(oldAssume.Attributes, skel.Attributes); body.Add(new AssumeStmt(skel.Tok, skel.EndTok, e, attrs)); - ReportAdditionalInformation(c.ConditionEllipsis, Printer.ExprToString(e), 3); + reporter.Info(MessageSource.RefinementTransformer, c.ConditionEllipsis, Printer.ExprToString(e)); i++; j++; } @@ -1121,7 +1114,7 @@ namespace Microsoft.Dafny Contract.Assert(c.ConditionOmitted); var oldIf = oldS as IfStmt; if (oldIf == null) { - reporter.Error(cur.Tok, "if-statement template does not match inherited statement"); + reporter.Error(MessageSource.RefinementTransformer, cur.Tok, "if-statement template does not match inherited statement"); i++; } else { var resultingThen = MergeBlockStmt(skel.Thn, oldIf.Thn); @@ -1129,7 +1122,7 @@ namespace Microsoft.Dafny var e = refinementCloner.CloneExpr(oldIf.Guard); var r = new IfStmt(skel.Tok, skel.EndTok, e, resultingThen, resultingElse); body.Add(r); - ReportAdditionalInformation(c.ConditionEllipsis, Printer.GuardToString(e), 3); + reporter.Info(MessageSource.RefinementTransformer, c.ConditionEllipsis, Printer.GuardToString(e)); i++; j++; } @@ -1137,16 +1130,16 @@ namespace Microsoft.Dafny var skel = (WhileStmt)S; var oldWhile = oldS as WhileStmt; if (oldWhile == null) { - reporter.Error(cur.Tok, "while-statement template does not match inherited statement"); + reporter.Error(MessageSource.RefinementTransformer, cur.Tok, "while-statement template does not match inherited statement"); i++; } else { Expression guard; if (c.ConditionOmitted) { guard = refinementCloner.CloneExpr(oldWhile.Guard); - ReportAdditionalInformation(c.ConditionEllipsis, Printer.GuardToString(oldWhile.Guard), 3); + reporter.Info(MessageSource.RefinementTransformer, c.ConditionEllipsis, Printer.GuardToString(oldWhile.Guard)); } else { if (oldWhile.Guard != null) { - reporter.Error(skel.Guard.tok, "a skeleton while statement with a guard can only replace a while statement with a non-deterministic guard"); + reporter.Error(MessageSource.RefinementTransformer, skel.Guard.tok, "a skeleton while statement with a guard can only replace a while statement with a non-deterministic guard"); } guard = skel.Guard; } @@ -1162,7 +1155,7 @@ namespace Microsoft.Dafny Contract.Assert(c.ConditionOmitted); var oldModifyStmt = oldS as ModifyStmt; if (oldModifyStmt == null) { - reporter.Error(cur.Tok, "modify template does not match inherited statement"); + reporter.Error(MessageSource.RefinementTransformer, cur.Tok, "modify template does not match inherited statement"); i++; } else { var mod = refinementCloner.CloneSpecFrameExpr(oldModifyStmt.Mod); @@ -1172,13 +1165,13 @@ namespace Microsoft.Dafny } else if (oldModifyStmt.Body == null) { mbody = skel.Body; } else if (skel.Body == null) { - reporter.Error(cur.Tok, "modify template must have a body if the inherited modify statement does"); + reporter.Error(MessageSource.RefinementTransformer, cur.Tok, "modify template must have a body if the inherited modify statement does"); mbody = null; } else { mbody = MergeBlockStmt(skel.Body, oldModifyStmt.Body); } body.Add(new ModifyStmt(skel.Tok, skel.EndTok, mod.Expressions, mod.Attributes, mbody)); - ReportAdditionalInformation(c.ConditionEllipsis, Printer.FrameExprListToString(mod.Expressions), 3); + reporter.Info(MessageSource.RefinementTransformer, c.ConditionEllipsis, Printer.FrameExprListToString(mod.Expressions)); i++; j++; } @@ -1341,7 +1334,7 @@ namespace Microsoft.Dafny sep = "\n"; } if (hoverText.Length != 0) { - ReportAdditionalInformation(skeleton.EndTok, hoverText, 3); + reporter.Info(MessageSource.RefinementTransformer, skeleton.EndTok, hoverText); } return new BlockStmt(skeleton.Tok, skeleton.EndTok, body); } @@ -1447,7 +1440,7 @@ namespace Microsoft.Dafny } else { if (!Contract.Exists(cOld.Decreases.Expressions, e => e is WildcardExpr)) { // If the previous loop was not specified with "decreases *", then the new loop is not allowed to provide any "decreases" clause. - reporter.Error(cNew.Decreases.Expressions[0].tok, "a refining loop can provide a decreases clause only if the loop being refined was declared with 'decreases *'"); + reporter.Error(MessageSource.RefinementTransformer, cNew.Decreases.Expressions[0].tok, "a refining loop can provide a decreases clause only if the loop being refined was declared with 'decreases *'"); } decr = cNew.Decreases; } @@ -1489,9 +1482,9 @@ namespace Microsoft.Dafny void MergeAddStatement(Statement s, List stmtList) { Contract.Requires(s != null); Contract.Requires(stmtList != null); - var prevErrorCount = reporter.ErrorCount; + var prevErrorCount = reporter.Count(ErrorLevel.Error); CheckIsOkayNewStatement(s, new Stack(), 0); - if (reporter.ErrorCount == prevErrorCount) { + if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { stmtList.Add(s); } } @@ -1508,30 +1501,30 @@ namespace Microsoft.Dafny labels.Push(n.Data.Name); } if (s is SkeletonStatement) { - reporter.Error(s, "skeleton statement may not be used here; it does not have a matching statement in what is being replaced"); + reporter.Error(MessageSource.RefinementTransformer, s, "skeleton statement may not be used here; it does not have a matching statement in what is being replaced"); } else if (s is ReturnStmt) { // allow return statements, but make note of that this requires verifying the postcondition ((ReturnStmt)s).ReverifyPost = true; } else if (s is YieldStmt) { - reporter.Error(s, "yield statements are not allowed in skeletons"); + reporter.Error(MessageSource.RefinementTransformer, s, "yield statements are not allowed in skeletons"); } else if (s is BreakStmt) { var b = (BreakStmt)s; if (b.TargetLabel != null ? !labels.Contains(b.TargetLabel) : loopLevels < b.BreakCount) { - reporter.Error(s, "break statement in skeleton is not allowed to break outside the skeleton fragment"); + reporter.Error(MessageSource.RefinementTransformer, s, "break statement in skeleton is not allowed to break outside the skeleton fragment"); } } else if (s is AssignStmt) { // TODO: To be a refinement automatically (that is, without any further verification), only variables and fields defined // in this module are allowed. This needs to be checked. If the LHS refers to an l-value that was not declared within // this module, then either an error should be reported or the Translator needs to know to translate new proof obligations. var a = (AssignStmt)s; - reporter.Error(a.Tok, "cannot have assignment statement"); + reporter.Error(MessageSource.RefinementTransformer, a.Tok, "cannot have assignment statement"); } else if (s is ConcreteUpdateStatement) { postTasks.Enqueue(() => { - CheckIsOkayUpdateStmt((ConcreteUpdateStatement)s, moduleUnderConstruction, reporter); + CheckIsOkayUpdateStmt((ConcreteUpdateStatement)s, moduleUnderConstruction); }); } else if (s is CallStmt) { - reporter.Error(s.Tok, "cannot have call statement"); + reporter.Error(MessageSource.RefinementTransformer, s.Tok, "cannot have call statement"); } else { if (s is WhileStmt || s is AlternativeLoopStmt) { loopLevels++; @@ -1547,7 +1540,7 @@ namespace Microsoft.Dafny } // Checks that statement stmt, defined in the constructed module m, is a refinement of skip in the parent module - void CheckIsOkayUpdateStmt(ConcreteUpdateStatement stmt, ModuleDefinition m, ResolutionErrorReporter reporter) { + void CheckIsOkayUpdateStmt(ConcreteUpdateStatement stmt, ModuleDefinition m) { foreach (var lhs in stmt.Lhss) { var l = lhs.Resolved; if (l is IdentifierExpr) { @@ -1555,23 +1548,23 @@ namespace Microsoft.Dafny Contract.Assert(ident.Var is LocalVariable || ident.Var is Formal); // LHS identifier expressions must be locals or out parameters (ie. formals) if ((ident.Var is LocalVariable && RefinementToken.IsInherited(((LocalVariable)ident.Var).Tok, m)) || ident.Var is Formal) { // for some reason, formals are not considered to be inherited. - reporter.Error(l.tok, "refinement method cannot assign to variable defined in parent module ('{0}')", ident.Var.Name); + reporter.Error(MessageSource.RefinementTransformer, l.tok, "refinement method cannot assign to variable defined in parent module ('{0}')", ident.Var.Name); } } else if (l is MemberSelectExpr) { var member = ((MemberSelectExpr)l).Member; if (RefinementToken.IsInherited(member.tok, m)) { - reporter.Error(l.tok, "refinement method cannot assign to a field defined in parent module ('{0}')", member.Name); + reporter.Error(MessageSource.RefinementTransformer, l.tok, "refinement method cannot assign to a field defined in parent module ('{0}')", member.Name); } } else { // must be an array element - reporter.Error(l.tok, "new assignments in a refinement method can only assign to state that the module defines (which never includes array elements)"); + reporter.Error(MessageSource.RefinementTransformer, l.tok, "new assignments in a refinement method can only assign to state that the module defines (which never includes array elements)"); } } if (stmt is UpdateStmt) { var s = (UpdateStmt)stmt; foreach (var rhs in s.Rhss) { if (rhs.CanAffectPreviouslyKnownExpressions) { - reporter.Error(rhs.Tok, "assignment RHS in refinement method is not allowed to affect previously defined state"); + reporter.Error(MessageSource.RefinementTransformer, rhs.Tok, "assignment RHS in refinement method is not allowed to affect previously defined state"); } } } diff --git a/Source/Dafny/Reporting.cs b/Source/Dafny/Reporting.cs new file mode 100644 index 00000000..c3797574 --- /dev/null +++ b/Source/Dafny/Reporting.cs @@ -0,0 +1,164 @@ +using Microsoft.Boogie; +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; +using System.Text; + +namespace Microsoft.Dafny { + public enum ErrorLevel { + Info, Warning, Error + } + + public enum MessageSource { + Parser, Resolver, Translator, Rewriter, Other, + RefinementTransformer, + Cloner + } + + public struct ErrorMessage { + public IToken token; + public string message; + public MessageSource source; + } + + public abstract class ErrorReporter { + public bool ErrorsOnly { get; set; } + public Dictionary> AllMessages { get; private set; } + + protected ErrorReporter() { + ErrorsOnly = false; + AllMessages = new Dictionary>(); + AllMessages[ErrorLevel.Error] = new List(); + AllMessages[ErrorLevel.Warning] = new List(); + AllMessages[ErrorLevel.Info] = new List(); + } + + protected bool ShouldDiscard(MessageSource source, ErrorLevel level) { + return ((ErrorsOnly && level != ErrorLevel.Error) || + (!DafnyOptions.O.PrintTooltips && level == ErrorLevel.Info)); + } + + // This is the only thing that needs to be overriden + public virtual bool Message(MessageSource source, ErrorLevel level, IToken tok, string msg) { + var discard = ShouldDiscard(source, level); + + if (!discard) { + AllMessages[level].Add(new ErrorMessage { token = tok, message = msg }); + return true; + } + + return false; + } + + public int Count(ErrorLevel level) { + return AllMessages[level].Count; + } + + public void Error(MessageSource source, IToken tok, string msg) { + Contract.Requires(tok != null); + Contract.Requires(msg != null); + Message(source, ErrorLevel.Error, tok, msg); + } + + // This method required by the Parser + internal void Error(MessageSource source, string filename, int line, int col, string msg) { + var tok = new Token(line, col); + tok.filename = filename; + Error(source, tok, msg); + } + + public void Error(MessageSource source, IToken tok, string msg, params object[] args) { + Contract.Requires(tok != null); + Contract.Requires(msg != null); + Error(source, tok, String.Format(msg, args)); + } + + public void Error(MessageSource source, Declaration d, string msg, params object[] args) { + Contract.Requires(d != null); + Contract.Requires(msg != null); + Error(source, d.tok, msg, args); + } + + public void Error(MessageSource source, Statement s, string msg, params object[] args) { + Contract.Requires(s != null); + Contract.Requires(msg != null); + Error(source, s.Tok, msg, args); + } + + public void Error(MessageSource source, NonglobalVariable v, string msg, params object[] args) { + Contract.Requires(v != null); + Contract.Requires(msg != null); + Error(source, v.tok, msg, args); + } + + public void Error(MessageSource source, Expression e, string msg, params object[] args) { + Contract.Requires(e != null); + Contract.Requires(msg != null); + Error(source, e.tok, msg, args); + } + + public void Warning(MessageSource source, IToken tok, string msg) { + Contract.Requires(tok != null); + Contract.Requires(msg != null); + Message(source, ErrorLevel.Warning, tok, msg); + } + + public void Warning(MessageSource source, IToken tok, string msg, params object[] args) { + Contract.Requires(tok != null); + Contract.Requires(msg != null); + Warning(source, tok, String.Format(msg, args)); + } + + public void Info(MessageSource source, IToken tok, string msg) { + Contract.Requires(tok != null); + Contract.Requires(msg != null); + Message(source, ErrorLevel.Info, tok, msg); + } + + public void Info(MessageSource source, IToken tok, string msg, params object[] args) { + Contract.Requires(tok != null); + Contract.Requires(msg != null); + Info(source, tok, String.Format(msg, args)); + } + + public string ErrorToString(ErrorLevel header, IToken tok, string msg) { + return ErrorToString_Internal(": " + header.ToString(), tok.filename, tok.line, tok.col, ": " + msg); + } + + public string ErrorToString(ErrorLevel header, string filename, int oneBasedLine, int oneBasedColumn, string msg) { + return ErrorToString_Internal(": " + header.ToString(), filename, oneBasedLine, oneBasedColumn, ": " + msg); + } + + public string ErrorToString_Internal(string header, string filename, int oneBasedLine, int oneBasedColumn, string msg) { + return String.Format("{0}({1},{2}){3}{4}", filename, oneBasedLine, oneBasedColumn - 1, header, msg ?? ""); + } + } + + public class ConsoleErrorReporter : ErrorReporter { + private ConsoleColor ColorForLevel(ErrorLevel level) { + switch (level) { + case ErrorLevel.Error: + return ConsoleColor.Red; + case ErrorLevel.Warning: + return ConsoleColor.Yellow; + case ErrorLevel.Info: + return ConsoleColor.Green; + default: + throw new cce.UnreachableException(); + } + } + + public override bool Message(MessageSource source, ErrorLevel level, IToken tok, string msg) { + if (base.Message(source, level, tok, msg)) { + ConsoleColor previousColor = Console.ForegroundColor; + Console.ForegroundColor = ColorForLevel(level); + Console.WriteLine(ErrorToString(level, tok, msg)); + Console.ForegroundColor = previousColor; + return true; + } else { + return false; + } + } + } +} diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index a58d6e6c..db087109 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -12,75 +12,11 @@ using Microsoft.Boogie; namespace Microsoft.Dafny { - public class ResolutionErrorReporter - { - public int ErrorCount = 0; - - /// - /// This method is virtual, because it is overridden in the VSX plug-in for Dafny. - /// - public virtual void Error(IToken tok, string msg, params object[] args) { - Contract.Requires(tok != null); - Contract.Requires(msg != null); - ConsoleColor col = Console.ForegroundColor; - Console.ForegroundColor = ConsoleColor.Red; - Dafny.Util.ReportIssue("Error", tok, msg, args); - Console.ForegroundColor = col; - ErrorCount++; - } - public void Error(Declaration d, string msg, params object[] args) { - Contract.Requires(d != null); - Contract.Requires(msg != null); - Error(d.tok, msg, args); - } - public void Error(Statement s, string msg, params object[] args) { - Contract.Requires(s != null); - Contract.Requires(msg != null); - Error(s.Tok, msg, args); - } - public void Error(NonglobalVariable v, string msg, params object[] args) { - Contract.Requires(v != null); - Contract.Requires(msg != null); - Error(v.tok, msg, args); - } - public void Error(Expression e, string msg, params object[] args) { - Contract.Requires(e != null); - Contract.Requires(msg != null); - Error(e.tok, msg, args); - } - - protected bool reportWarnings = true; - /// - /// Set whether or not to report warnings. Return the state of the previous behavior. - /// - public bool ReportWarnings(bool b) { - var old = reportWarnings; - reportWarnings = b; - return old; - } - public virtual void Warning(IToken tok, string msg, params object[] args) { - Contract.Requires(tok != null); - Contract.Requires(msg != null); - if (reportWarnings) { - ConsoleColor col = Console.ForegroundColor; - Console.ForegroundColor = ConsoleColor.Yellow; - Dafny.Util.ReportIssue("Warning", tok, msg, args); - Console.ForegroundColor = col; - } - } - } - - public struct AdditionalInformation - { - public IToken Token; - public string Text; - public int Length; - } - - public class Resolver : ResolutionErrorReporter + public class Resolver { readonly BuiltIns builtIns; + readonly ErrorReporter reporter; ModuleSignature moduleInfo = null; FreshIdGenerator defaultTempVarIdGenerator; @@ -99,18 +35,6 @@ namespace Microsoft.Dafny return defaultTempVarIdGenerator.FreshId(prefix); } - public Action AdditionalInformationReporter; - - internal void ReportAdditionalInformation(IToken token, string text, int length) - { - 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 }); - } - } - interface IAmbiguousThing { /// @@ -259,13 +183,10 @@ namespace Microsoft.Dafny public Resolver(Program prog) { Contract.Requires(prog != null); builtIns = prog.BuiltIns; + reporter = prog.reporter; // Populate the members of the basic types var trunc = new SpecialField(Token.NoToken, "Trunc", "ToBigInteger()", "", "", false, false, false, Type.Int, null); basicTypeMembers[(int)BasicTypeVariety.Real].Add(trunc.Name, trunc); - - if (DafnyOptions.O.PrintTooltips) { - AdditionalInformationReporter = DefaultInformationReporter; - } } [ContractInvariantMethod] @@ -276,26 +197,22 @@ namespace Microsoft.Dafny Contract.Invariant(cce.NonNullDictionaryAndValues(datatypeCtors) && Contract.ForAll(datatypeCtors.Values, v => cce.NonNullDictionaryAndValues(v))); } - public static void DefaultInformationReporter(AdditionalInformation info) { - Dafny.Util.ReportIssue("Info", info.Token, info.Text); - } - public void ResolveProgram(Program prog) { Contract.Requires(prog != null); - var origErrorCount = ErrorCount; + var origErrorCount = reporter.Count(ErrorLevel.Error); //FIXME (Clement): This is used further below, but not in the >0 comparisons in the next few lines. Is that right? var bindings = new ModuleBindings(null); var b = BindModuleNames(prog.DefaultModuleDef, bindings); bindings.BindName("_module", prog.DefaultModule, b); - if (ErrorCount > 0) { return; } // if there were errors, then the implict ModuleBindings data structure invariant + if (reporter.Count(ErrorLevel.Error) > 0) { return; } // if there were errors, then the implict ModuleBindings data structure invariant // is violated, so Processing dependencies will not succeed. ProcessDependencies(prog.DefaultModule, b, dependencies); // check for cycles in the import graph List cycle = dependencies.TryFindCycle(); if (cycle != null) { var cy = Util.Comma(" -> ", cycle, m => m.Name); - Error(cycle[0], "module definition contains a cycle (note: parent modules implicitly depend on submodules): {0}", cy); + reporter.Error(MessageSource.Resolver, cycle[0], "module definition contains a cycle (note: parent modules implicitly depend on submodules): {0}", cy); } - if (ErrorCount > 0) { return; } // give up on trying to resolve anything else + if (reporter.Count(ErrorLevel.Error) > 0) { return; } // give up on trying to resolve anything else // fill in module heights List sortedDecls = dependencies.TopologicallySortedComponents(); @@ -311,16 +228,16 @@ namespace Microsoft.Dafny } var rewriters = new List(); - var refinementTransformer = new RefinementTransformer(this, AdditionalInformationReporter, prog); + var refinementTransformer = new RefinementTransformer(prog); rewriters.Add(refinementTransformer); - rewriters.Add(new AutoContractsRewriter()); - var opaqueRewriter = new OpaqueFunctionRewriter(this); - rewriters.Add(new AutoReqFunctionRewriter(this, opaqueRewriter)); + rewriters.Add(new AutoContractsRewriter(reporter)); + var opaqueRewriter = new OpaqueFunctionRewriter(this.reporter); + rewriters.Add(new AutoReqFunctionRewriter(this.reporter, opaqueRewriter)); rewriters.Add(opaqueRewriter); - rewriters.Add(new TimeLimitRewriter()); + rewriters.Add(new TimeLimitRewriter(reporter)); if (DafnyOptions.O.AutoTriggers) { - rewriters.Add(new TriggersRewriter(this)); + rewriters.Add(new TriggerGeneratingRewriter(this.reporter)); } systemNameInfo = RegisterTopLevelDecls(prog.BuiltIns.SystemModule, false); @@ -365,7 +282,7 @@ namespace Microsoft.Dafny var literalDecl = (LiteralModuleDecl)decl; var m = literalDecl.ModuleDef; - var errorCount = ErrorCount; + var errorCount = reporter.Count(ErrorLevel.Error); foreach (var r in rewriters) { r.PreResolve(m); } @@ -374,19 +291,20 @@ namespace Microsoft.Dafny literalDecl.Signature.Refines = refinementTransformer.RefinedSig; var sig = literalDecl.Signature; // set up environment - var preResolveErrorCount = ErrorCount; + var preResolveErrorCount = reporter.Count(ErrorLevel.Error); ResolveModuleDefinition(m, sig); foreach (var r in rewriters) { - if (ErrorCount != preResolveErrorCount) { + if (reporter.Count(ErrorLevel.Error) != preResolveErrorCount) { break; } r.PostResolve(m); } - if (ErrorCount == errorCount && !m.IsAbstract) { + if (reporter.Count(ErrorLevel.Error) == errorCount && !m.IsAbstract) { // compilation should only proceed if everything is good, including the signature (which preResolveErrorCount does not include); Contract.Assert(!useCompileSignatures); useCompileSignatures = true; // set Resolver-global flag to indicate that Signatures should be followed to their CompiledSignature - var oldWarnings = ReportWarnings(false); // turn off warning reporting for the clone + var oldErrorsOnly = reporter.ErrorsOnly; + reporter.ErrorsOnly = true; // turn off warning reporting for the clone var nw = new Cloner().CloneModuleDefinition(m, m.CompileName + "_Compile"); var compileSig = RegisterTopLevelDecls(nw, true); compileSig.Refines = refinementTransformer.RefinedSig; @@ -394,13 +312,13 @@ namespace Microsoft.Dafny ResolveModuleDefinition(nw, compileSig); prog.CompileModules.Add(nw); useCompileSignatures = false; // reset the flag - ReportWarnings(oldWarnings); + reporter.ErrorsOnly = oldErrorsOnly; } } else if (decl is AliasModuleDecl) { var alias = (AliasModuleDecl)decl; // resolve the path ModuleSignature p; - if (ResolvePath(alias.Root, alias.Path, out p, this)) { + if (ResolvePath(alias.Root, alias.Path, out p, reporter)) { alias.Signature = p; } else { alias.Signature = new ModuleSignature(); // there was an error, give it a valid but empty signature @@ -408,18 +326,18 @@ namespace Microsoft.Dafny } else if (decl is ModuleFacadeDecl) { var abs = (ModuleFacadeDecl)decl; ModuleSignature p; - if (ResolvePath(abs.Root, abs.Path, out p, this)) { + if (ResolvePath(abs.Root, abs.Path, out p, reporter)) { abs.OriginalSignature = p; // ModuleDefinition.ExclusiveRefinement may not be set at this point but ExclusiveRefinementCount will be. if (0 == abs.Root.Signature.ModuleDef.ExclusiveRefinementCount) { abs.Signature = MakeAbstractSignature(p, abs.FullCompileName, abs.Height, prog.Modules); ModuleSignature compileSig; if (abs.CompilePath != null) { - if (ResolvePath(abs.CompileRoot, abs.CompilePath, out compileSig, this)) { + if (ResolvePath(abs.CompileRoot, abs.CompilePath, out compileSig, reporter)) { if (refinementTransformer.CheckIsRefinement(compileSig, p)) { abs.Signature.CompileSignature = compileSig; } else { - Error( + 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)); @@ -437,7 +355,7 @@ namespace Microsoft.Dafny } else { Contract.Assert(false); } Contract.Assert(decl.Signature != null); } - if (ErrorCount != origErrorCount) { + if (reporter.Count(ErrorLevel.Error) != origErrorCount) { // do nothing else return; } @@ -493,7 +411,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); + reporter.Info(MessageSource.Resolver, iter.tok, Printer.IteratorClassToString(iter)); } } // fill in other additional information @@ -514,19 +432,19 @@ namespace Microsoft.Dafny // Determine, for each function, whether someone tries to adjust its fuel parameter foreach (var module in prog.Modules) { - CheckForFuelAdjustments(module.tok, module.Attributes, module, this); + CheckForFuelAdjustments(module.tok, module.Attributes, module); foreach (var clbl in ModuleDefinition.AllItersAndCallables(module.TopLevelDecls)) { Statement body = null; if (clbl is Method) { body = ((Method)clbl).Body; - CheckForFuelAdjustments(clbl.Tok,((Method)clbl).Attributes, module, this); + CheckForFuelAdjustments(clbl.Tok,((Method)clbl).Attributes, module); } else if (clbl is IteratorDecl) { body = ((IteratorDecl)clbl).Body; - CheckForFuelAdjustments(clbl.Tok, ((IteratorDecl)clbl).Attributes, module, this); + CheckForFuelAdjustments(clbl.Tok, ((IteratorDecl)clbl).Attributes, module); } if (body != null) { var c = new FuelAdjustment_Visitor(this); - c.Visit(body, new FuelAdjustment_Context(module, this)); + c.Visit(body, new FuelAdjustment_Context(module)); } } } @@ -572,7 +490,7 @@ namespace Microsoft.Dafny } // 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); + reporter.Info(MessageSource.Resolver, clbl.Tok, s); } } } @@ -595,7 +513,7 @@ namespace Microsoft.Dafny var decr = clbl.Decreases.Expressions; if (DafnyOptions.O.Dafnycc) { if (decr.Count > 1) { - Error(decr[1].tok, "In dafnycc mode, only one decreases expression is allowed"); + reporter.Error(MessageSource.Resolver, decr[1].tok, "In dafnycc mode, only one decreases expression is allowed"); } // In dafnycc mode, only consider first argument if (decr.Count == 0 && clbl.Ins.Count > 0) { @@ -757,11 +675,11 @@ namespace Microsoft.Dafny // resolve var datatypeDependencies = new Graph(); var codatatypeDependencies = new Graph(); - int prevErrorCount = ErrorCount; + int prevErrorCount = reporter.Count(ErrorLevel.Error); ResolveTopLevelDecls_Signatures(m, m.TopLevelDecls, datatypeDependencies, codatatypeDependencies); ResolveAttributes(m.Attributes, new ResolveOpts(new NoContext(m.Module), false)); // Must follow ResolveTopLevelDecls_Signatures, in case attributes refer to members - if (ErrorCount == prevErrorCount) { - ResolveTopLevelDecls_Meat(m.TopLevelDecls, datatypeDependencies, codatatypeDependencies); + if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { + ResolveTopLevelDecls_Core(m.TopLevelDecls, datatypeDependencies, codatatypeDependencies); } } @@ -819,17 +737,17 @@ namespace Microsoft.Dafny var subdecl = (LiteralModuleDecl)tld; var subBindings = BindModuleNames(subdecl.ModuleDef, bindings); if (!bindings.BindName(subdecl.Name, subdecl, subBindings)) { - Error(subdecl.tok, "Duplicate module name: {0}", subdecl.Name); + reporter.Error(MessageSource.Resolver, subdecl.tok, "Duplicate module name: {0}", subdecl.Name); } } else if (tld is ModuleFacadeDecl) { var subdecl = (ModuleFacadeDecl)tld; if (!bindings.BindName(subdecl.Name, subdecl, null)) { - Error(subdecl.tok, "Duplicate module name: {0}", subdecl.Name); + reporter.Error(MessageSource.Resolver, subdecl.tok, "Duplicate module name: {0}", subdecl.Name); } } else if (tld is AliasModuleDecl) { var subdecl = (AliasModuleDecl)tld; if (!bindings.BindName(subdecl.Name, subdecl, null)) { - Error(subdecl.tok, "Duplicate module name: {0}", subdecl.Name); + reporter.Error(MessageSource.Resolver, subdecl.tok, "Duplicate module name: {0}", subdecl.Name); } } } @@ -840,9 +758,9 @@ namespace Microsoft.Dafny if (m.RefinementBaseName != null) { ModuleDecl other; if (!bindings.TryLookup(m.RefinementBaseName[0], out other)) { - Error(m.RefinementBaseName[0], "module {0} named as refinement base does not exist", m.RefinementBaseName[0].val); + reporter.Error(MessageSource.Resolver, m.RefinementBaseName[0], "module {0} named as refinement base does not exist", m.RefinementBaseName[0].val); } else if (other is LiteralModuleDecl && ((LiteralModuleDecl)other).ModuleDef == m) { - Error(m.RefinementBaseName[0], "module cannot refine itself: {0}", m.RefinementBaseName[0].val); + reporter.Error(MessageSource.Resolver, m.RefinementBaseName[0], "module cannot refine itself: {0}", m.RefinementBaseName[0].val); } else { Contract.Assert(other != null); // follows from postcondition of TryGetValue dependencies.AddEdge(decl, other); @@ -866,7 +784,7 @@ namespace Microsoft.Dafny var alias = moduleDecl as AliasModuleDecl; ModuleDecl root; if (!bindings.TryLookupIgnore(alias.Path[0], out root, alias)) - Error(alias.tok, ModuleNotFoundErrorMessage(0, alias.Path)); + reporter.Error(MessageSource.Resolver, alias.tok, ModuleNotFoundErrorMessage(0, alias.Path)); else { dependencies.AddEdge(moduleDecl, root); alias.Root = root; @@ -875,14 +793,14 @@ namespace Microsoft.Dafny var abs = moduleDecl as ModuleFacadeDecl; ModuleDecl root; if (!bindings.TryLookup(abs.Path[0], out root)) - Error(abs.tok, ModuleNotFoundErrorMessage(0, abs.Path)); + reporter.Error(MessageSource.Resolver, abs.tok, ModuleNotFoundErrorMessage(0, abs.Path)); else { dependencies.AddEdge(moduleDecl, root); abs.Root = root; } if (abs.CompilePath != null) { if (!bindings.TryLookup(abs.CompilePath[0], out root)) - Error(abs.tok, ModuleNotFoundErrorMessage(0, abs.CompilePath)); + reporter.Error(MessageSource.Resolver, abs.tok, ModuleNotFoundErrorMessage(0, abs.CompilePath)); else { dependencies.AddEdge(moduleDecl, root); abs.CompileRoot = root; @@ -990,7 +908,7 @@ namespace Microsoft.Dafny Contract.Assert(d != null); // register the class/datatype/module name if (toplevels.ContainsKey(d.Name)) { - Error(d, "Duplicate name of top-level declaration: {0}", d.Name); + reporter.Error(MessageSource.Resolver, d, "Duplicate name of top-level declaration: {0}", d.Name); } else { toplevels[d.Name] = d; sig.TopLevels[d.Name] = d; @@ -1014,7 +932,7 @@ namespace Microsoft.Dafny // First, register the iterator's in- and out-parameters as readonly fields foreach (var p in iter.Ins) { if (members.ContainsKey(p.Name)) { - Error(p, "Name of in-parameter is used by another member of the iterator: {0}", p.Name); + reporter.Error(MessageSource.Resolver, p, "Name of in-parameter is used by another member of the iterator: {0}", p.Name); } else { var field = new SpecialField(p.tok, p.Name, p.CompileName, "", "", p.IsGhost, false, false, p.Type, null); field.EnclosingClass = iter; // resolve here @@ -1024,7 +942,7 @@ namespace Microsoft.Dafny } foreach (var p in iter.Outs) { if (members.ContainsKey(p.Name)) { - Error(p, "Name of yield-parameter is used by another member of the iterator: {0}", p.Name); + reporter.Error(MessageSource.Resolver, p, "Name of yield-parameter is used by another member of the iterator: {0}", p.Name); } else { var field = new SpecialField(p.tok, p.Name, p.CompileName, "", "", p.IsGhost, true, true, p.Type, null); field.EnclosingClass = iter; // resolve here @@ -1036,7 +954,7 @@ namespace Microsoft.Dafny foreach (var p in iter.Outs) { var nm = p.Name + "s"; if (members.ContainsKey(nm)) { - Error(p.tok, "Name of implicit yield-history variable '{0}' is already used by another member of the iterator", p.Name); + reporter.Error(MessageSource.Resolver, p.tok, "Name of implicit yield-history variable '{0}' is already used by another member of the iterator", p.Name); } else { var tp = new SeqType(p.Type.IsSubrangeType ? new IntType() : p.Type); var field = new SpecialField(p.tok, nm, nm, "", "", true, true, false, tp, null); @@ -1108,7 +1026,7 @@ namespace Microsoft.Dafny iter.Member_MoveNext = moveNext; MemberDecl member; if (members.TryGetValue(init.Name, out member)) { - Error(member.tok, "member name '{0}' is already predefined for this iterator", init.Name); + reporter.Error(MessageSource.Resolver, member.tok, "member name '{0}' is already predefined for this iterator", init.Name); } else { members.Add(init.Name, init); iter.Members.Add(init); @@ -1116,13 +1034,13 @@ namespace Microsoft.Dafny // If the name of the iterator is "Valid" or "MoveNext", one of the following will produce an error message. That // error message may not be as clear as it could be, but the situation also seems unlikely to ever occur in practice. if (members.TryGetValue("Valid", out member)) { - Error(member.tok, "member name 'Valid' is already predefined for iterators"); + reporter.Error(MessageSource.Resolver, member.tok, "member name 'Valid' is already predefined for iterators"); } else { members.Add(valid.Name, valid); iter.Members.Add(valid); } if (members.TryGetValue("MoveNext", out member)) { - Error(member.tok, "member name 'MoveNext' is already predefined for iterators"); + reporter.Error(MessageSource.Resolver, member.tok, "member name 'MoveNext' is already predefined for iterators"); } else { members.Add(moveNext.Name, moveNext); iter.Members.Add(moveNext); @@ -1140,7 +1058,7 @@ namespace Microsoft.Dafny members.Add(m.Name, m); if (m is Constructor) { if (cl is TraitDecl) { - Error(m.tok, "a trait is not allowed to declare a constructor"); + reporter.Error(MessageSource.Resolver, m.tok, "a trait is not allowed to declare a constructor"); } else { cl.HasConstructor = true; } @@ -1196,9 +1114,9 @@ namespace Microsoft.Dafny members.Add(extraName, extraMember); } } else if (m is Constructor && !((Constructor)m).HasName) { - Error(m, "More than one anonymous constructor"); + reporter.Error(MessageSource.Resolver, m, "More than one anonymous constructor"); } else { - Error(m, "Duplicate member name: {0}", m.Name); + reporter.Error(MessageSource.Resolver, m, "Duplicate member name: {0}", m.Name); } } if (cl.IsDefaultClass) { @@ -1222,9 +1140,9 @@ namespace Microsoft.Dafny foreach (DatatypeCtor ctor in dt.Ctors) { if (ctor.Name.EndsWith("?")) { - Error(ctor, "a datatype constructor name is not allowed to end with '?'"); + reporter.Error(MessageSource.Resolver, ctor, "a datatype constructor name is not allowed to end with '?'"); } else if (ctors.ContainsKey(ctor.Name)) { - Error(ctor, "Duplicate datatype constructor name: {0}", ctor.Name); + reporter.Error(MessageSource.Resolver, ctor, "Duplicate datatype constructor name: {0}", ctor.Name); } else { ctors.Add(ctor.Name, ctor); @@ -1251,7 +1169,7 @@ namespace Microsoft.Dafny foreach (var formal in ctor.Formals) { bool nameError = false; if (formal.HasName && members.ContainsKey(formal.Name)) { - Error(ctor, "Name of deconstructor is used by another member of the datatype: {0}", formal.Name); + reporter.Error(MessageSource.Resolver, ctor, "Name of deconstructor is used by another member of the datatype: {0}", formal.Name); nameError = true; } var dtor = new DatatypeDestructor(formal.tok, ctor, formal, formal.Name, "dtor_" + formal.CompileName, "", "", formal.IsGhost, formal.Type, null); @@ -1301,8 +1219,7 @@ namespace Microsoft.Dafny } - public static bool ResolvePath(ModuleDecl root, List Path, out ModuleSignature p, ResolutionErrorReporter reporter) { - Contract.Requires(reporter != null); + public static bool ResolvePath(ModuleDecl root, List Path, out ModuleSignature p, ErrorReporter reporter) { p = root.Signature; int i = 1; while (i < Path.Count) { @@ -1311,7 +1228,7 @@ namespace Microsoft.Dafny p = pp; i++; } else { - reporter.Error(Path[i], ModuleNotFoundErrorMessage(i, Path)); + reporter.Error(MessageSource.Resolver, Path[i], ModuleNotFoundErrorMessage(i, Path)); break; } } @@ -1366,7 +1283,7 @@ namespace Microsoft.Dafny { if (!(def.IsDefaultModule)) // _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. - Error(d.tok, "an abstract module can only be imported into other abstract modules, not a concrete one."); + 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 } @@ -1392,7 +1309,7 @@ namespace Microsoft.Dafny if (cycle != null) { Contract.Assert(cycle.Count != 0); var erste = cycle[0]; - Error(erste.Tok, "Cycle among redirecting types (newtypes, type synonyms): {0} -> {1}", Util.Comma(" -> ", cycle, syn => syn.Name), erste.Name); + reporter.Error(MessageSource.Resolver, erste.Tok, "Cycle among redirecting types (newtypes, type synonyms): {0} -> {1}", Util.Comma(" -> ", cycle, syn => syn.Name), erste.Name); } } @@ -1416,14 +1333,14 @@ namespace Microsoft.Dafny new NativeType("long", Int64.MinValue, 0x8000000000000000, "L", false), }; - public void ResolveTopLevelDecls_Meat(List/*!*/ declarations, Graph/*!*/ datatypeDependencies, Graph/*!*/ codatatypeDependencies) { + public void ResolveTopLevelDecls_Core(List/*!*/ declarations, Graph/*!*/ datatypeDependencies, Graph/*!*/ codatatypeDependencies) { Contract.Requires(declarations != null); Contract.Requires(cce.NonNullElements(datatypeDependencies)); Contract.Requires(cce.NonNullElements(codatatypeDependencies)); Contract.Requires(NFBC_Count == 0); Contract.Ensures(NFBC_Count == 0); - int prevErrorCount = ErrorCount; + int prevErrorCount = reporter.Count(ErrorLevel.Error); // 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 @@ -1438,7 +1355,7 @@ namespace Microsoft.Dafny ResolveAttributes(d.Attributes, new ResolveOpts(new NoContext(d.Module), false)); // this check can be done only after it has been determined that the redirected types do not involve cycles if (!dd.BaseType.IsNumericBased()) { - Error(dd.tok, "newtypes must be based on some numeric type (got {0})", dd.BaseType); + reporter.Error(MessageSource.Resolver, dd.tok, "newtypes must be based on some numeric type (got {0})", dd.BaseType); } // type check the constraint, if any if (dd.Var != null) { @@ -1451,10 +1368,10 @@ namespace Microsoft.Dafny ResolveExpression(dd.Constraint, new ResolveOpts(dd, false, true)); Contract.Assert(dd.Constraint.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(dd.Constraint.Type, Type.Bool)) { - Error(dd.Constraint, "newtype constraint must be of type bool (instead got {0})", dd.Constraint.Type); + reporter.Error(MessageSource.Resolver, dd.Constraint, "newtype constraint must be of type bool (instead got {0})", dd.Constraint.Type); } if (!CheckTypeInference_Visitor.IsDetermined(dd.BaseType.NormalizeExpand())) { - Error(dd.tok, "newtype's base type is not fully determined; add an explicit type for '{0}'", dd.Var.Name); + reporter.Error(MessageSource.Resolver, dd.tok, "newtype's base type is not fully determined; add an explicit type for '{0}'", dd.Var.Name); } CheckTypeInference(dd.Constraint); scope.PopMarker(); @@ -1464,7 +1381,7 @@ namespace Microsoft.Dafny // Now, we're ready for the other declarations. foreach (TopLevelDecl d in declarations) { if (d is TraitDecl && d.TypeArgs.Count > 0) { - Error(d, "sorry, traits with type parameters are not supported"); + reporter.Error(MessageSource.Resolver, d, "sorry, traits with type parameters are not supported"); } allTypeParameters.PushMarker(); ResolveTypeParameters(d.TypeArgs, false, d); @@ -1483,15 +1400,15 @@ namespace Microsoft.Dafny allTypeParameters.PopMarker(); } - if (ErrorCount == prevErrorCount) { + if (reporter.Count(ErrorLevel.Error) == 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); + e.Bounds = DiscoverBounds(e.tok, e.BoundVars, e.Range, true, false, missingBounds, reporter); 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); + 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); } } } @@ -1501,11 +1418,11 @@ namespace Microsoft.Dafny 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); + var allBounds = DiscoverBoundsAux(e.tok, new List(e.BoundVars), constraint, true, true, true, missingBounds, reporter); 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); + 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 = new List(); @@ -1525,7 +1442,7 @@ namespace Microsoft.Dafny nativeTypeMap.Add(nativeType.Name, nativeType); } - if (ErrorCount == prevErrorCount) { + 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) { @@ -1547,7 +1464,7 @@ namespace Microsoft.Dafny Attributes.MatchingValueOption.Empty, Attributes.MatchingValueOption.Bool, Attributes.MatchingValueOption.String }, - err => Error(dd, err)); + err => reporter.Error(MessageSource.Resolver, dd, err)); if (hasNativeTypeAttr) { if (nativeTypeAttr is bool) { boolNativeType = (bool)nativeTypeAttr; @@ -1556,16 +1473,16 @@ namespace Microsoft.Dafny if (nativeTypeMap.ContainsKey(keyString)) { stringNativeType = nativeTypeMap[keyString]; } else { - Error(dd, "Unsupported nativeType {0}", keyString); + reporter.Error(MessageSource.Resolver, dd, "Unsupported nativeType {0}", keyString); } } } if (stringNativeType != null || boolNativeType == true) { if (!dd.BaseType.IsNumericBased(Type.NumericPersuation.Int)) { - Error(dd, "nativeType can only be used on integral types"); + reporter.Error(MessageSource.Resolver, dd, "nativeType can only be used on integral types"); } if (dd.Var == null) { - Error(dd, "nativeType can only be used if newtype specifies a constraint"); + reporter.Error(MessageSource.Resolver, dd, "nativeType can only be used if newtype specifies a constraint"); } } if (dd.Var != null) { @@ -1588,7 +1505,7 @@ namespace Microsoft.Dafny }; var missingBounds = new List(); var bounds = DiscoverBounds(dd.Constraint.tok, new List { dd.Var }, dd.Constraint, - true, true, missingBounds); + true, true, missingBounds, reporter); List potentialNativeTypes = (stringNativeType != null) ? new List { stringNativeType } : (boolNativeType == false) ? new List() : @@ -1621,18 +1538,18 @@ namespace Microsoft.Dafny } } if (dd.NativeType == null && (boolNativeType == true || stringNativeType != null)) { - Error(dd, "Dafny's heuristics cannot find a compatible native type. " + + 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) { - ReportAdditionalInformation(dd.tok, "{:nativeType \"" + dd.NativeType.Name + "\"}", dd.tok.val.Length); + reporter.Info(MessageSource.Resolver, dd.tok, "{:nativeType \"" + dd.NativeType.Name + "\"}"); } } } } } - if (ErrorCount == prevErrorCount) { + if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { // fill in the postconditions and bodies of prefix lemmas foreach (var com in ModuleDefinition.AllFixpointLemmas(declarations)) { var prefixLemma = com.PrefixLemma; @@ -1646,7 +1563,7 @@ namespace Microsoft.Dafny foreach (var p in com.Ens) { var coConclusions = new HashSet(); CollectFriendlyCallsInFixpointLemmaSpecification(p.E, true, coConclusions, true); - var subst = new FixpointLemmaSpecificationSubstituter(coConclusions, new IdentifierExpr(k.tok, k.Name), this, true); + var subst = new FixpointLemmaSpecificationSubstituter(coConclusions, new IdentifierExpr(k.tok, k.Name), this.reporter, true); var post = subst.CloneExpr(p.E); prefixLemma.Ens.Add(new MaybeFreeExpression(post, p.IsFree)); } @@ -1656,7 +1573,7 @@ namespace Microsoft.Dafny foreach (var p in com.Req) { var antecedents = new HashSet(); CollectFriendlyCallsInFixpointLemmaSpecification(p.E, true, antecedents, false); - var subst = new FixpointLemmaSpecificationSubstituter(antecedents, new IdentifierExpr(k.tok, k.Name), this, false); + var subst = new FixpointLemmaSpecificationSubstituter(antecedents, new IdentifierExpr(k.tok, k.Name), this.reporter, false); var pre = subst.CloneExpr(p.E); prefixLemma.Req.Add(new MaybeFreeExpression(pre, p.IsFree)); } @@ -1665,7 +1582,7 @@ namespace Microsoft.Dafny Contract.Assume(prefixLemma.Body == null); // this is not supposed to have been filled in before if (com.Body != null) { var kMinusOne = new BinaryExpr(com.tok, BinaryExpr.Opcode.Sub, new IdentifierExpr(k.tok, k.Name), new LiteralExpr(com.tok, 1)); - var subst = new FixpointLemmaBodyCloner(com, kMinusOne, this); + var subst = new FixpointLemmaBodyCloner(com, kMinusOne, this.reporter); var mainBody = subst.CloneBlockStmt(com.Body); var kPositive = new BinaryExpr(com.tok, BinaryExpr.Opcode.Lt, new LiteralExpr(com.tok, 0), new IdentifierExpr(k.tok, k.Name)); var condBody = new IfStmt(com.BodyStartTok, mainBody.EndTok, kPositive, mainBody, null); @@ -1699,7 +1616,7 @@ namespace Microsoft.Dafny } } - if (ErrorCount == prevErrorCount) { // because CheckCoCalls requires the given expression to have been successfully resolved + if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { // because CheckCoCalls requires the given expression to have been successfully resolved // Perform the guardedness check on co-datatypes foreach (var repr in ModuleDefinition.AllFunctionSCCs(declarations)) { var module = repr.EnclosingModule; @@ -1734,7 +1651,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); + reporter.Info(MessageSource.Resolver, c.CandidateCall.tok, "co-recursive call"); } // Finally, fill in the CoClusterTarget field // Start by setting all the CoClusterTarget fields to CoRecursiveTargetAllTheWay. @@ -1929,11 +1846,11 @@ namespace Microsoft.Dafny // Check here for the presence of any 'ensures' clauses, which are not allowed (because we're not sure // of their soundness) if (fn.Ens.Count != 0) { - Error(fn.Ens[0].tok, "a {0} is not allowed to declare any ensures clause", member.WhatKind); + reporter.Error(MessageSource.Resolver, fn.Ens[0].tok, "a {0} is not allowed to declare any ensures clause", member.WhatKind); } // Also check for 'reads' clauses if (fn.Reads.Count != 0) { - Error(fn.Reads[0].tok, "a {0} is not allowed to declare any reads clause", member.WhatKind); // (why?) + reporter.Error(MessageSource.Resolver, fn.Reads[0].tok, "a {0} is not allowed to declare any reads clause", member.WhatKind); // (why?) } if (fn.Body != null) { FixpointPredicateChecks(fn.Body, fn, CallingPosition.Positive); @@ -1949,7 +1866,7 @@ namespace Microsoft.Dafny var dd = (NewtypeDecl)d; if (dd.Module.CallGraph.GetSCCSize(dd) != 1) { var cycle = Util.Comma(" -> ", dd.Module.CallGraph.GetSCC(dd), clbl => clbl.NameRelativeToModule); - Error(dd.tok, "recursive dependency involving a newtype: " + cycle); + reporter.Error(MessageSource.Resolver, dd.tok, "recursive dependency involving a newtype: " + cycle); } } } @@ -1967,45 +1884,14 @@ namespace Microsoft.Dafny Contract.Requires(resolver != null); this.resolver = resolver; } - public void Error(IToken tok, string msg, params object[] args) { - Contract.Requires(tok != null); - Contract.Requires(msg != null); - Contract.Requires(args != null); - resolver.Error(tok, msg, args); - } - public void Error(Expression expr, string msg, params object[] args) { - Contract.Requires(expr != null); - Contract.Requires(msg != null); - Contract.Requires(args != null); - Error(expr.tok, msg, args); - } } abstract class ResolverTopDownVisitor : TopDownVisitor { - Resolver resolver; + protected Resolver resolver; public ResolverTopDownVisitor(Resolver resolver) { Contract.Requires(resolver != null); this.resolver = resolver; } - protected void Error(IToken tok, string msg, params object[] args) - { - Contract.Requires(tok != null); - Contract.Requires(msg != null); - Contract.Requires(args != null); - resolver.Error(tok, msg, args); - } - protected void Error(Expression expr, string msg, params object[] args) - { - Contract.Requires(expr != null); - Contract.Requires(msg != null); - Contract.Requires(args != null); - Error(expr.tok, msg, args); - } - protected void ReportAdditionalInformation(IToken tok, string text, int length) - { - Contract.Requires(tok != null); - resolver.ReportAdditionalInformation(tok, text, length); - } } #endregion Visitors @@ -2027,12 +1913,12 @@ namespace Microsoft.Dafny if (hasTailRecursionPreference && !tail) { // the user specifically requested no tail recursion, so do nothing else } else if (hasTailRecursionPreference && tail && m.IsGhost) { - Error(m.tok, "tail recursion can be specified only for methods that will be compiled, not for ghost methods"); + reporter.Error(MessageSource.Resolver, m.tok, "tail recursion can be specified only for methods that will be compiled, not for ghost methods"); } else { var module = m.EnclosingClass.Module; var sccSize = module.CallGraph.GetSCCSize(m); if (hasTailRecursionPreference && 2 <= sccSize) { - Error(m.tok, "sorry, tail-call optimizations are not supported for mutually recursive methods"); + reporter.Error(MessageSource.Resolver, m.tok, "sorry, tail-call optimizations are not supported for mutually recursive methods"); } else if (hasTailRecursionPreference || sccSize == 1) { CallStmt tailCall = null; var status = CheckTailRecursive(m.Body.Body, m, ref tailCall, hasTailRecursionPreference); @@ -2040,7 +1926,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); + reporter.Info(MessageSource.Resolver, m.tok, "tail recursive"); } } } @@ -2048,7 +1934,7 @@ namespace Microsoft.Dafny } } else if (member is Function) { var f = (Function)member; - var errorCount = ErrorCount; + var errorCount = reporter.Count(ErrorLevel.Error); f.Req.Iter(CheckTypeInference); f.Ens.Iter(CheckTypeInference); f.Reads.Iter(fe => CheckTypeInference(fe.E)); @@ -2057,10 +1943,10 @@ namespace Microsoft.Dafny CheckTypeInference(f.Body); bool tail = true; if (Attributes.ContainsBool(f.Attributes, "tailrecursion", ref tail) && tail) { - Error(f.tok, "sorry, tail-call functions are not supported"); + reporter.Error(MessageSource.Resolver, f.tok, "sorry, tail-call functions are not supported"); } } - if (errorCount == ErrorCount && f is FixpointPredicate) { + if (errorCount == reporter.Count(ErrorLevel.Error) && f is FixpointPredicate) { var cop = (FixpointPredicate)f; CheckTypeInference_Member(cop.PrefixPredicate); } @@ -2120,7 +2006,7 @@ namespace Microsoft.Dafny if (e != null) { foreach (var bv in e.BoundVars) { if (!IsDetermined(bv.Type.Normalize())) { - Error(bv.tok, "type of bound variable '{0}' could not be determined; please specify the type explicitly", + resolver.reporter.Error(MessageSource.Resolver, bv.tok, "type of bound variable '{0}' could not be determined; please specify the type explicitly", bv.Name); } } @@ -2130,7 +2016,7 @@ namespace Microsoft.Dafny if (e.Member is Function || e.Member is Method) { foreach (var p in e.TypeApplication) { if (!IsDetermined(p.Normalize())) { - Error(e.tok, "type '{0}' to the {2} '{1}' is not determined", p, e.Member.Name, e.Member.WhatKind); + resolver.reporter.Error(MessageSource.Resolver, e.tok, "type '{0}' to the {2} '{1}' is not determined", p, e.Member.Name, e.Member.WhatKind); } } } @@ -2138,7 +2024,7 @@ namespace Microsoft.Dafny var e = (FunctionCallExpr)expr; foreach (var p in e.TypeArgumentSubstitutions) { if (!IsDetermined(p.Value.Normalize())) { - Error(e.tok, "type variable '{0}' in the function call to '{1}' could not be determined{2}", p.Key.Name, e.Name, + resolver.reporter.Error(MessageSource.Resolver, e.tok, "type variable '{0}' in the function call to '{1}' could not be determined{2}", p.Key.Name, e.Name, (e.Name.Contains("reveal_") || e.Name.Contains("_FULL")) //CLEMENT should this be StartsWith and EndsWith? ? ". If you are making an opaque function, make sure that the function can be called." : "" @@ -2150,7 +2036,7 @@ namespace Microsoft.Dafny foreach (var p in e.LHSs) { foreach (var x in p.Vars) { if (!IsDetermined(x.Type.Normalize())) { - Error(e.tok, "the type of the bound variable '{0}' could not be determined", x.Name); + resolver.reporter.Error(MessageSource.Resolver, e.tok, "the type of the bound variable '{0}' could not be determined", x.Name); } } } @@ -2218,7 +2104,7 @@ namespace Microsoft.Dafny var proxy = (TypeProxy)t; if (!UnderspecifiedTypeProxies.Contains(proxy)) { // report an error for this TypeProxy only once - Error(tok, "the type of this {0} is underspecified", what); + resolver.reporter.Error(MessageSource.Resolver, tok, "the type of this {0} is underspecified", what); UnderspecifiedTypeProxies.Add(proxy); } return false; @@ -2274,7 +2160,7 @@ namespace Microsoft.Dafny if (status == TailRecursionStatus.TailCallSpent) { // a tail call cannot be followed by non-ghost code if (reportErrors) { - Error(tailCall.Tok, "this recursive call is not recognized as being tail recursive, because it is followed by non-ghost code"); + reporter.Error(MessageSource.Resolver, tailCall.Tok, "this recursive call is not recognized as being tail recursive, because it is followed by non-ghost code"); } return TailRecursionStatus.NotTailRecursive; } @@ -2322,7 +2208,7 @@ namespace Microsoft.Dafny // all is good } else { if (reportErrors) { - Error(s.Tok, "the recursive call to '{0}' is not tail recursive because the actual out-parameter {1} is not the formal out-parameter '{2}'", s.Method.Name, i, formal.Name); + reporter.Error(MessageSource.Resolver, s.Tok, "the recursive call to '{0}' is not tail recursive because the actual out-parameter {1} is not the formal out-parameter '{2}'", s.Method.Name, i, formal.Name); } return TailRecursionStatus.NotTailRecursive; } @@ -2368,7 +2254,7 @@ namespace Microsoft.Dafny if (status == TailRecursionStatus.NotTailRecursive) { // an error has already been reported } else if (reportErrors) { - Error(tailCall.Tok, "a recursive call inside a loop is not recognized as being a tail call"); + reporter.Error(MessageSource.Resolver, tailCall.Tok, "a recursive call inside a loop is not recognized as being a tail call"); } return TailRecursionStatus.NotTailRecursive; } @@ -2380,7 +2266,7 @@ namespace Microsoft.Dafny if (status == TailRecursionStatus.NotTailRecursive) { // an error has already been reported } else if (reportErrors) { - Error(tailCall.Tok, "a recursive call inside a loop is not recognized as being a tail call"); + reporter.Error(MessageSource.Resolver, tailCall.Tok, "a recursive call inside a loop is not recognized as being a tail call"); } return TailRecursionStatus.NotTailRecursive; } @@ -2395,7 +2281,7 @@ namespace Microsoft.Dafny if (status == TailRecursionStatus.NotTailRecursive) { // an error has already been reported } else if (reportErrors) { - Error(tailCall.Tok, "a recursive call inside a forall statement is not a tail call"); + reporter.Error(MessageSource.Resolver, tailCall.Tok, "a recursive call inside a forall statement is not a tail call"); } return TailRecursionStatus.NotTailRecursive; } @@ -2432,7 +2318,7 @@ namespace Microsoft.Dafny // ------------------------------------------------------------------------------------------------------ #region FuelAdjustmentChecks - protected static void CheckForFuelAdjustments(IToken tok, Attributes attrs, ModuleDefinition currentModule, ResolutionErrorReporter reporter) { + protected void CheckForFuelAdjustments(IToken tok, Attributes attrs, ModuleDefinition currentModule) { List> results = Attributes.FindAllExpressions(attrs, "fuel"); if (results != null) { @@ -2445,7 +2331,7 @@ namespace Microsoft.Dafny if (f != null) { f.IsFueled = true; if (f.IsProtected && currentModule != f.EnclosingClass.Module) { - reporter.Error(tok, "cannot adjust fuel for protected function {0} from another module", f.Name); + reporter.Error(MessageSource.Resolver, tok, "cannot adjust fuel for protected function {0} from another module", f.Name); } } } @@ -2457,10 +2343,8 @@ namespace Microsoft.Dafny public class FuelAdjustment_Context { public ModuleDefinition currentModule; - public ResolutionErrorReporter reporter; - public FuelAdjustment_Context(ModuleDefinition currentModule, ResolutionErrorReporter reporter) { + public FuelAdjustment_Context(ModuleDefinition currentModule) { this.currentModule = currentModule; - this.reporter = reporter; } } @@ -2472,7 +2356,7 @@ namespace Microsoft.Dafny } protected override bool VisitOneStmt(Statement stmt, ref FuelAdjustment_Context st) { - Resolver.CheckForFuelAdjustments(stmt.Tok, stmt.Attributes, st.currentModule, st.reporter); + resolver.CheckForFuelAdjustments(stmt.Tok, stmt.Attributes, st.currentModule); return true; } } @@ -2594,7 +2478,7 @@ namespace Microsoft.Dafny var article = context is InductivePredicate ? "an" : "a"; // we're looking at a recursive call if (!(context is InductivePredicate ? e.Function is InductivePredicate : e.Function is CoPredicate)) { - Error(e, "a recursive call from {0} {1} can go only to other {1}s", article, context.WhatKind); + resolver.reporter.Error(MessageSource.Resolver, e, "a recursive call from {0} {1} can go only to other {1}s", article, context.WhatKind); } else if (cp != CallingPosition.Positive) { var msg = string.Format("{0} {1} can be called recursively only in positive positions", article, context.WhatKind); if (cp == CallingPosition.Neither) { @@ -2603,10 +2487,10 @@ namespace Microsoft.Dafny } else { // the fixpoint-call is not inside an quantifier, so don't bother mentioning the part of existentials/universals in the error message } - Error(e, msg); + resolver.reporter.Error(MessageSource.Resolver, e, msg); } else { e.CoCall = FunctionCallExpr.CoCallResolution.Yes; - ReportAdditionalInformation(e.tok, e.Function.Name + "#[_k - 1]", e.Function.Name.Length); + resolver.reporter.Info(MessageSource.Resolver, e.tok, e.Function.Name + "#[_k - 1]"); } } // do the sub-parts with cp := Neither @@ -2621,7 +2505,7 @@ namespace Microsoft.Dafny if (ModuleDefinition.InSameSCC(context, s.Method)) { // we're looking at a recursive call var article = context is InductivePredicate ? "an" : "a"; - Error(stmt.Tok, "a recursive call from {0} {1} can go only to other {1}s", article, context.WhatKind); + resolver.reporter.Error(MessageSource.Resolver, stmt.Tok, "a recursive call from {0} {1} can go only to other {1}s", article, context.WhatKind); } // do the sub-parts with the same "cp" return true; @@ -2662,7 +2546,7 @@ namespace Microsoft.Dafny if (ModuleDefinition.InSameSCC(context, s.Method)) { // we're looking at a recursive call (to a non-fixpoint-lemma) var article = context is InductiveLemma ? "an" : "a"; - Error(s.Tok, "a recursive call from {0} {1} can go only to other {1}s and prefix lemmas", article, context.WhatKind); + resolver.reporter.Error(MessageSource.Resolver, s.Tok, "a recursive call from {0} {1} can go only to other {1}s and prefix lemmas", article, context.WhatKind); } } } @@ -2674,7 +2558,7 @@ namespace Microsoft.Dafny // the call goes from a colemma context to a non-colemma callee if (ModuleDefinition.InSameSCC(context, e.Function)) { // we're looking at a recursive call (to a non-colemma) - Error(e.tok, "a recursive call from a colemma can go only to other colemmas and prefix lemmas"); + resolver.reporter.Error(MessageSource.Resolver, e.tok, "a recursive call from a colemma can go only to other colemmas and prefix lemmas"); } } } @@ -2733,7 +2617,7 @@ namespace Microsoft.Dafny foreach (var formalTypeArg in s.Method.TypeArgs) { var actualTypeArg = s.MethodSelect.TypeArgumentSubstitutions()[formalTypeArg]; if (formalTypeArg.MustSupportEquality && !actualTypeArg.SupportsEquality) { - Error(s.Tok, "type parameter {0} ({1}) passed to method {2} must support equality (got {3}){4}", i, formalTypeArg.Name, s.Method.Name, actualTypeArg, TypeEqualityErrorMessageHint(actualTypeArg)); + resolver.reporter.Error(MessageSource.Resolver, s.Tok, "type parameter {0} ({1}) passed to method {2} must support equality (got {3}){4}", i, formalTypeArg.Name, s.Method.Name, actualTypeArg, TypeEqualityErrorMessageHint(actualTypeArg)); } i++; } @@ -2788,9 +2672,9 @@ namespace Microsoft.Dafny } else if (e1 != null && e1.Arguments.Count == 0) { // oh yeah! } else if (!t0.SupportsEquality) { - Error(e.E0, "{0} can only be applied to expressions of types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t0, TypeEqualityErrorMessageHint(t0)); + resolver.reporter.Error(MessageSource.Resolver, e.E0, "{0} can only be applied to expressions of types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t0, TypeEqualityErrorMessageHint(t0)); } else if (!t1.SupportsEquality) { - Error(e.E1, "{0} can only be applied to expressions of types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t1, TypeEqualityErrorMessageHint(t1)); + resolver.reporter.Error(MessageSource.Resolver, e.E1, "{0} can only be applied to expressions of types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t1, TypeEqualityErrorMessageHint(t1)); } break; default: @@ -2803,12 +2687,12 @@ namespace Microsoft.Dafny case BinaryExpr.ResolvedOpcode.Prefix: case BinaryExpr.ResolvedOpcode.ProperPrefix: if (!t1.SupportsEquality) { - Error(e.E1, "{0} can only be applied to expressions of sequence types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t1, TypeEqualityErrorMessageHint(t1)); + resolver.reporter.Error(MessageSource.Resolver, e.E1, "{0} can only be applied to expressions of sequence types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t1, TypeEqualityErrorMessageHint(t1)); } else if (!t0.SupportsEquality) { if (e.ResolvedOp == BinaryExpr.ResolvedOpcode.InSet || e.ResolvedOp == BinaryExpr.ResolvedOpcode.NotInSeq) { - Error(e.E0, "{0} can only be applied to expressions of types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t0, TypeEqualityErrorMessageHint(t0)); + resolver.reporter.Error(MessageSource.Resolver, e.E0, "{0} can only be applied to expressions of types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t0, TypeEqualityErrorMessageHint(t0)); } else { - Error(e.E0, "{0} can only be applied to expressions of sequence types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t0, TypeEqualityErrorMessageHint(t0)); + resolver.reporter.Error(MessageSource.Resolver, e.E0, "{0} can only be applied to expressions of sequence types that support equality (got {1}){2}", BinaryExpr.OpcodeString(e.Op), t0, TypeEqualityErrorMessageHint(t0)); } } break; @@ -2834,7 +2718,7 @@ namespace Microsoft.Dafny foreach (var tp in ((ICallable)e.Member).TypeArgs) { var actualTp = e.TypeApplication[e.Member.EnclosingClass.TypeArgs.Count + i]; if (tp.MustSupportEquality && !actualTp.SupportsEquality) { - Error(e.tok, "type parameter {0} ({1}) passed to {5} '{2}' must support equality (got {3}){4}", i, tp.Name, e.Member.Name, actualTp, TypeEqualityErrorMessageHint(actualTp), e.Member.WhatKind); + resolver.reporter.Error(MessageSource.Resolver, e.tok, "type parameter {0} ({1}) passed to {5} '{2}' must support equality (got {3}){4}", i, tp.Name, e.Member.Name, actualTp, TypeEqualityErrorMessageHint(actualTp), e.Member.WhatKind); } i++; } @@ -2846,7 +2730,7 @@ namespace Microsoft.Dafny foreach (var formalTypeArg in e.Function.TypeArgs) { var actualTypeArg = e.TypeArgumentSubstitutions[formalTypeArg]; if (formalTypeArg.MustSupportEquality && !actualTypeArg.SupportsEquality) { - Error(e.tok, "type parameter {0} ({1}) passed to function {2} must support equality (got {3}){4}", i, formalTypeArg.Name, e.Function.Name, actualTypeArg, TypeEqualityErrorMessageHint(actualTypeArg)); + resolver.reporter.Error(MessageSource.Resolver, e.tok, "type parameter {0} ({1}) passed to function {2} must support equality (got {3}){4}", i, formalTypeArg.Name, e.Function.Name, actualTypeArg, TypeEqualityErrorMessageHint(actualTypeArg)); } i++; } @@ -2878,21 +2762,21 @@ namespace Microsoft.Dafny var st = (SetType)type; var argType = st.Arg; if (!argType.SupportsEquality) { - Error(tok, "{2}set argument type must support equality (got {0}){1}", argType, TypeEqualityErrorMessageHint(argType), st.Finite ? "" : "i"); + resolver.reporter.Error(MessageSource.Resolver, tok, "{2}set argument type must support equality (got {0}){1}", argType, TypeEqualityErrorMessageHint(argType), st.Finite ? "" : "i"); } CheckEqualityTypes_Type(tok, argType); } else if (type is MultiSetType) { var argType = ((MultiSetType)type).Arg; if (!argType.SupportsEquality) { - Error(tok, "multiset argument type must support equality (got {0}){1}", argType, TypeEqualityErrorMessageHint(argType)); + resolver.reporter.Error(MessageSource.Resolver, tok, "multiset argument type must support equality (got {0}){1}", argType, TypeEqualityErrorMessageHint(argType)); } CheckEqualityTypes_Type(tok, argType); } else if (type is MapType) { var mt = (MapType)type; if (!mt.Domain.SupportsEquality) { - Error(tok, "{2}map domain type must support equality (got {0}){1}", mt.Domain, TypeEqualityErrorMessageHint(mt.Domain), mt.Finite ? "" : "i"); + resolver.reporter.Error(MessageSource.Resolver, tok, "{2}map domain type must support equality (got {0}){1}", mt.Domain, TypeEqualityErrorMessageHint(mt.Domain), mt.Finite ? "" : "i"); } CheckEqualityTypes_Type(tok, mt.Domain); CheckEqualityTypes_Type(tok, mt.Range); @@ -2918,7 +2802,7 @@ namespace Microsoft.Dafny foreach (var argType in udt.TypeArgs) { var formalTypeArg = formalTypeArgs[i]; if (formalTypeArg.MustSupportEquality && !argType.SupportsEquality) { - Error(tok, "type parameter {0} ({1}) passed to type {2} must support equality (got {3}){4}", i, formalTypeArg.Name, udt.ResolvedClass.Name, argType, TypeEqualityErrorMessageHint(argType)); + resolver.reporter.Error(MessageSource.Resolver, tok, "type parameter {0} ({1}) passed to type {2} must support equality (got {3}){4}", i, formalTypeArg.Name, udt.ResolvedClass.Name, argType, TypeEqualityErrorMessageHint(argType)); } CheckEqualityTypes_Type(tok, argType); i++; @@ -3007,10 +2891,10 @@ namespace Microsoft.Dafny for (int i = 0; i < cs.Method.Ins.Count; i++) { argsSubstMap.Add(cs.Method.Ins[i], cs.Args[i]); } - var substituter = new Translator.AlphaConverting_Substituter(cs.Receiver, argsSubstMap, new Dictionary(), new Translator()); + var substituter = new Translator.AlphaConverting_Substituter(cs.Receiver, argsSubstMap, new Dictionary(), new Translator(resolver.reporter)); 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.reporter.Info(MessageSource.Resolver, s.Tok, "ensures " + Printer.ExprToString(p) + ";"); } } } @@ -3088,26 +2972,26 @@ namespace Microsoft.Dafny currentClass = cl; if (cl.TraitsTyp.Count > 0 && cl.TypeArgs.Count > 0) { - Error(cl.tok, "sorry, traits are currently supported only for classes that take no type arguments"); // TODO: do the work to remove this limitation + reporter.Error(MessageSource.Resolver, cl.tok, "sorry, traits are currently supported only for classes that take no type arguments"); // TODO: do the work to remove this limitation } // Resolve names of traits extended foreach (var tt in cl.TraitsTyp) { - var prevErrorCount = ErrorCount; + var prevErrorCount = reporter.Count(ErrorLevel.Error); ResolveType(cl.tok, tt, new NoContext(cl.Module), ResolveTypeOptionEnum.DontInfer, null); - if (ErrorCount == prevErrorCount) { + if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { var udt = tt as UserDefinedType; if (udt != null && udt.ResolvedClass is TraitDecl) { var trait = (TraitDecl)udt.ResolvedClass; //disallowing inheritance in multi module case if (cl.Module != trait.Module) { - Error(udt.tok, "class '{0}' is in a different module than trait '{1}'. A class may only extend a trait in the same module.", cl.Name, trait.FullName); + reporter.Error(MessageSource.Resolver, udt.tok, "class '{0}' is in a different module than trait '{1}'. A class may only extend a trait in the same module.", cl.Name, trait.FullName); } else { // all is good cl.TraitsObj.Add(trait); } } else { - Error(udt != null ? udt.tok : cl.tok, "a class can only extend traits (found '{0}')", tt); + reporter.Error(MessageSource.Resolver, udt != null ? udt.tok : cl.tok, "a class can only extend traits (found '{0}')", tt); } } } @@ -3150,12 +3034,12 @@ namespace Microsoft.Dafny } else if (member is Function) { var f = (Function)member; - var ec = ErrorCount; + var ec = reporter.Count(ErrorLevel.Error); allTypeParameters.PushMarker(); ResolveTypeParameters(f.TypeArgs, true, f); ResolveFunctionSignature(f); allTypeParameters.PopMarker(); - if (f is FixpointPredicate && ec == ErrorCount) { + if (f is FixpointPredicate && ec == reporter.Count(ErrorLevel.Error)) { var ff = ((FixpointPredicate)f).PrefixPredicate; ff.EnclosingClass = cl; allTypeParameters.PushMarker(); @@ -3166,13 +3050,13 @@ namespace Microsoft.Dafny } else if (member is Method) { var m = (Method)member; - var ec = ErrorCount; + var ec = reporter.Count(ErrorLevel.Error); allTypeParameters.PushMarker(); ResolveTypeParameters(m.TypeArgs, true, m); ResolveMethodSignature(m); allTypeParameters.PopMarker(); var com = m as FixpointLemma; - if (com != null && com.PrefixLemma != null && ec == ErrorCount) { + if (com != null && com.PrefixLemma != null && ec == reporter.Count(ErrorLevel.Error)) { var mm = com.PrefixLemma; // resolve signature of the prefix lemma mm.EnclosingClass = cl; @@ -3193,7 +3077,7 @@ namespace Microsoft.Dafny void InheritTraitMembers(ClassDecl cl) { Contract.Requires(cl != null); - var refinementTransformer = new RefinementTransformer(this, AdditionalInformationReporter, null); + var refinementTransformer = new RefinementTransformer(reporter); //merging class members with parent members if any var clMembers = classMembers[cl]; foreach (TraitDecl trait in cl.TraitsObj) { @@ -3213,37 +3097,37 @@ namespace Microsoft.Dafny } else if (traitMember is Function) { var func = (Function)traitMember; if (func.Body == null) { - Error(cl.tok, "class '{0}' does not implement trait function '{1}.{2}'", cl.Name, trait.Name, traitMember.Name); + reporter.Error(MessageSource.Resolver, cl.tok, "class '{0}' does not implement trait function '{1}.{2}'", cl.Name, trait.Name, traitMember.Name); } else if (!func.IsGhost && !func.IsStatic) { cl.InheritedMembers.Add(func); } } else if (traitMember is Method) { var method = (Method)traitMember; if (method.Body == null) { - Error(cl.tok, "class '{0}' does not implement trait method '{1}.{2}'", cl.Name, trait.Name, traitMember.Name); + reporter.Error(MessageSource.Resolver, cl.tok, "class '{0}' does not implement trait method '{1}.{2}'", cl.Name, trait.Name, traitMember.Name); } else if (!method.IsGhost && !method.IsStatic) { cl.InheritedMembers.Add(method); } } } else if (clMember.EnclosingClass != cl) { // The class inherits the member from two places - Error(clMember.tok, "member name '{0}' in class '{1}' inherited from both traits '{2}' and '{3}'", traitMember.Name, cl.Name, clMember.EnclosingClass.Name, trait.Name); + reporter.Error(MessageSource.Resolver, clMember.tok, "member name '{0}' in class '{1}' inherited from both traits '{2}' and '{3}'", traitMember.Name, cl.Name, clMember.EnclosingClass.Name, trait.Name); } else if (traitMember is Field) { // The class is not allowed to do anything with the field other than silently inherit it. if (clMember is Field) { - Error(clMember.tok, "field '{0}' is inherited from trait '{1}' and is not allowed to be re-declared", traitMember.Name, trait.Name); + reporter.Error(MessageSource.Resolver, clMember.tok, "field '{0}' is inherited from trait '{1}' and is not allowed to be re-declared", traitMember.Name, trait.Name); } else { - Error(clMember.tok, "member name '{0}' in class '{1}' clashes with inherited field from trait '{2}'", traitMember.Name, cl.Name, trait.Name); + reporter.Error(MessageSource.Resolver, clMember.tok, "member name '{0}' in class '{1}' clashes with inherited field from trait '{2}'", traitMember.Name, cl.Name, trait.Name); } } else if (traitMember is Method) { var traitMethod = (Method)traitMember; if (traitMethod.Body != null) { // The method was defined in the trait, so the class is not allowed to do anything with the method other than silently inherit it. - Error(clMember.tok, "member '{0}' in class '{1}' overrides fully defined method inherited from trait '{2}'", clMember.Name, cl.Name, trait.Name); + reporter.Error(MessageSource.Resolver, clMember.tok, "member '{0}' in class '{1}' overrides fully defined method inherited from trait '{2}'", clMember.Name, cl.Name, trait.Name); } else if (!(clMember is Method)) { - Error(clMember.tok, "non-method member '{0}' overrides method '{1}' inherited from trait '{2}'", clMember.Name, traitMethod.Name, trait.Name); + reporter.Error(MessageSource.Resolver, clMember.tok, "non-method member '{0}' overrides method '{1}' inherited from trait '{2}'", clMember.Name, traitMethod.Name, trait.Name); } else { var classMethod = (Method)clMember; classMethod.OverriddenMethod = traitMethod; @@ -3255,7 +3139,7 @@ namespace Microsoft.Dafny var traitMethodAllowsNonTermination = Contract.Exists(traitMethod.Decreases.Expressions, e => e is WildcardExpr); var classMethodAllowsNonTermination = Contract.Exists(classMethod.Decreases.Expressions, e => e is WildcardExpr); if (classMethodAllowsNonTermination && !traitMethodAllowsNonTermination) { - Error(classMethod.tok, "not allowed to override a terminating method with a possibly non-terminating method ('{0}')", classMethod.Name); + reporter.Error(MessageSource.Resolver, classMethod.tok, "not allowed to override a terminating method with a possibly non-terminating method ('{0}')", classMethod.Name); } } @@ -3263,9 +3147,9 @@ namespace Microsoft.Dafny var traitFunction = (Function)traitMember; if (traitFunction.Body != null) { // The function was defined in the trait, so the class is not allowed to do anything with the function other than silently inherit it. - Error(clMember.tok, "member '{0}' in class '{1}' overrides fully defined function inherited from trait '{2}'", clMember.Name, cl.Name, trait.Name); + reporter.Error(MessageSource.Resolver, clMember.tok, "member '{0}' in class '{1}' overrides fully defined function inherited from trait '{2}'", clMember.Name, cl.Name, trait.Name); } else if (!(clMember is Function)) { - Error(clMember.tok, "non-function member '{0}' overrides function '{1}' inherited from trait '{2}'", clMember.Name, traitFunction.Name, trait.Name); + reporter.Error(MessageSource.Resolver, clMember.tok, "non-function member '{0}' overrides function '{1}' inherited from trait '{2}'", clMember.Name, traitFunction.Name, trait.Name); } else { var classFunction = (Function)clMember; classFunction.OverriddenFunction = traitFunction; @@ -3298,12 +3182,12 @@ namespace Microsoft.Dafny } else if (member is Function) { var f = (Function)member; - var ec = ErrorCount; + var ec = reporter.Count(ErrorLevel.Error); allTypeParameters.PushMarker(); ResolveTypeParameters(f.TypeArgs, false, f); ResolveFunction(f); allTypeParameters.PopMarker(); - if (f is FixpointPredicate && ec == ErrorCount) { + if (f is FixpointPredicate && ec == reporter.Count(ErrorLevel.Error)) { var ff = ((FixpointPredicate)f).PrefixPredicate; allTypeParameters.PushMarker(); ResolveTypeParameters(ff.TypeArgs, false, ff); @@ -3313,7 +3197,7 @@ namespace Microsoft.Dafny } else if (member is Method) { var m = (Method)member; - var ec = ErrorCount; + var ec = reporter.Count(ErrorLevel.Error); allTypeParameters.PushMarker(); ResolveTypeParameters(m.TypeArgs, false, m); ResolveMethod(m); @@ -3420,7 +3304,7 @@ namespace Microsoft.Dafny // whatever is in scc-cleared now failed to pass the test foreach (var dt in scc) { if (dt.DefaultCtor == null) { - Error(dt, "because of cyclic dependencies among constructor argument types, no instances of datatype '{0}' can be constructed", dt.Name); + reporter.Error(MessageSource.Resolver, dt, "because of cyclic dependencies among constructor argument types, no instances of datatype '{0}' can be constructed", dt.Name); } } return; @@ -3598,12 +3482,12 @@ namespace Microsoft.Dafny Contract.Requires(args != null); foreach (var arg in args) { Contract.Assert(arg != null); - int prevErrors = ErrorCount; + int prevErrors = reporter.Count(ErrorLevel.Error); ResolveExpression(arg, opts); if (!allowGhosts) { CheckIsNonGhost(arg); } - if (prevErrors == ErrorCount) { + if (prevErrors == reporter.Count(ErrorLevel.Error)) { CheckTypeInference(arg); } } @@ -3623,9 +3507,9 @@ namespace Microsoft.Dafny var r = allTypeParameters.Push(tp.Name, tp); if (emitErrors) { if (r == Scope.PushResult.Duplicate) { - Error(tp, "Duplicate type-parameter name: {0}", tp.Name); + reporter.Error(MessageSource.Resolver, tp, "Duplicate type-parameter name: {0}", tp.Name); } else if (r == Scope.PushResult.Shadow) { - Warning(tp.tok, "Shadowed type-parameter name: {0}", tp.Name); + reporter.Warning(MessageSource.Resolver, tp.tok, "Shadowed type-parameter name: {0}", tp.Name); } } } @@ -3649,10 +3533,10 @@ namespace Microsoft.Dafny case Scope.PushResult.Success: break; case Scope.PushResult.Duplicate: - Error(tok, "Duplicate {0} name: {1}", kind, name); + reporter.Error(MessageSource.Resolver, tok, "Duplicate {0} name: {1}", kind, name); break; case Scope.PushResult.Shadow: - Warning(tok, "Shadowed {0} name: {1}", kind, name); + reporter.Warning(MessageSource.Resolver, tok, "Shadowed {0} name: {1}", kind, name); break; } } @@ -3664,7 +3548,7 @@ namespace Microsoft.Dafny Contract.Requires(f != null); scope.PushMarker(); if (f.SignatureIsOmitted) { - Error(f, "function signature can be omitted only in refining functions"); + reporter.Error(MessageSource.Resolver, f, "function signature can be omitted only in refining functions"); } var option = f.TypeArgs.Count == 0 ? new ResolveTypeOption(f) : new ResolveTypeOption(ResolveTypeOptionEnum.AllowPrefix); foreach (Formal p in f.Formals) { @@ -3692,7 +3576,7 @@ namespace Microsoft.Dafny ResolveExpression(r, new ResolveOpts(f, false, true)); Contract.Assert(r.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(r.Type, Type.Bool)) { - Error(r, "Precondition must be a boolean (got {0})", r.Type); + reporter.Error(MessageSource.Resolver, r, "Precondition must be a boolean (got {0})", r.Type); } } foreach (FrameExpression fr in f.Reads) { @@ -3702,7 +3586,7 @@ namespace Microsoft.Dafny ResolveExpression(r, new ResolveOpts(f, false, true)); // since this is a function, the postcondition is still a one-state predicate Contract.Assert(r.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(r.Type, Type.Bool)) { - Error(r, "Postcondition must be a boolean (got {0})", r.Type); + reporter.Error(MessageSource.Resolver, r, "Postcondition must be a boolean (got {0})", r.Type); } } ResolveAttributes(f.Decreases.Attributes, new ResolveOpts(f, false, true)); @@ -3711,14 +3595,14 @@ namespace Microsoft.Dafny // any type is fine } if (f.Body != null) { - var prevErrorCount = ErrorCount; + var prevErrorCount = reporter.Count(ErrorLevel.Error); ResolveExpression(f.Body, new ResolveOpts(f, false)); - if (!f.IsGhost && prevErrorCount == ErrorCount) { + if (!f.IsGhost && prevErrorCount == reporter.Count(ErrorLevel.Error)) { CheckIsNonGhost(f.Body); } Contract.Assert(f.Body.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(f.Body.Type, f.ResultType)) { - Error(f, "Function body type mismatch (expected {0}, got {1})", f.ResultType, f.Body.Type); + reporter.Error(MessageSource.Resolver, f, "Function body type mismatch (expected {0}, got {1})", f.ResultType, f.Body.Type); } } scope.PopMarker(); @@ -3746,7 +3630,7 @@ namespace Microsoft.Dafny t = collType.Arg; } if (!UnifyTypes(t, new ObjectType())) { - Error(fe.E, "a {0}-clause expression must denote an object or a collection of objects (instead got {1})", readsFrame ? "reads" : "modifies", fe.E.Type); + reporter.Error(MessageSource.Resolver, fe.E, "a {0}-clause expression must denote an object or a collection of objects (instead got {1})", readsFrame ? "reads" : "modifies", fe.E.Type); } else if (fe.FieldName != null) { NonProxyType nptype; MemberDecl member = ResolveMember(fe.E.tok, t, fe.FieldName, out nptype); @@ -3754,9 +3638,9 @@ namespace Microsoft.Dafny if (member == null) { // error has already been reported by ResolveMember } else if (!(member is Field)) { - Error(fe.E, "member {0} in type {1} does not refer to a field", fe.FieldName, ctype.Name); + reporter.Error(MessageSource.Resolver, fe.E, "member {0} in type {1} does not refer to a field", fe.FieldName, ctype.Name); } else if (!readsFrame && isGhostContext && !member.IsGhost) { - Error(fe.E, "in a ghost context, only ghost fields can be mentioned as modifies frame targets ({0})", fe.FieldName); + reporter.Error(MessageSource.Resolver, fe.E, "in a ghost context, only ghost fields can be mentioned as modifies frame targets ({0})", fe.FieldName); } else { Contract.Assert(ctype != null && ctype.ResolvedClass != null); // follows from postcondition of ResolveMember fe.Field = (Field)member; @@ -3771,7 +3655,7 @@ namespace Microsoft.Dafny Contract.Requires(m != null); scope.PushMarker(); if (m.SignatureIsOmitted) { - Error(m, "method signature can be omitted only in refining methods"); + reporter.Error(MessageSource.Resolver, m, "method signature can be omitted only in refining methods"); } var option = m.TypeArgs.Count == 0 ? new ResolveTypeOption(m) : new ResolveTypeOption(ResolveTypeOptionEnum.AllowPrefix); // resolve in-parameters @@ -3812,14 +3696,14 @@ namespace Microsoft.Dafny ResolveExpression(e.E, new ResolveOpts(m, false, true)); Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(e.E.Type, Type.Bool)) { - Error(e.E, "Precondition must be a boolean (got {0})", e.E.Type); + reporter.Error(MessageSource.Resolver, e.E, "Precondition must be a boolean (got {0})", e.E.Type); } } ResolveAttributes(m.Mod.Attributes, new ResolveOpts(m, false, true)); foreach (FrameExpression fe in m.Mod.Expressions) { ResolveFrameExpression(fe, false, m.IsGhost, m); if (m is Lemma || m is FixpointLemma) { - Error(fe.tok, "{0}s are not allowed to have modifies clauses", m.WhatKind); + reporter.Error(MessageSource.Resolver, fe.tok, "{0}s are not allowed to have modifies clauses", m.WhatKind); } } ResolveAttributes(m.Decreases.Attributes, new ResolveOpts(m, false, true)); @@ -3827,7 +3711,7 @@ namespace Microsoft.Dafny ResolveExpression(e, new ResolveOpts(m, false, true)); // any type is fine if (m.IsGhost && e is WildcardExpr) { - Error(e, "'decreases *' is not allowed on ghost methods"); + reporter.Error(MessageSource.Resolver, e, "'decreases *' is not allowed on ghost methods"); } } @@ -3835,7 +3719,7 @@ namespace Microsoft.Dafny // Don't care about any duplication errors among the out-parameters, since they have already been reported scope.PushMarker(); if (m is FixpointLemma && m.Outs.Count != 0) { - Error(m.Outs[0].tok, "{0}s are not allowed to have out-parameters", m.WhatKind); + reporter.Error(MessageSource.Resolver, m.Outs[0].tok, "{0}s are not allowed to have out-parameters", m.WhatKind); } else { foreach (Formal p in m.Outs) { scope.Push(p.Name, p); @@ -3848,7 +3732,7 @@ namespace Microsoft.Dafny ResolveExpression(e.E, new ResolveOpts(m, true, true)); Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(e.E.Type, Type.Bool)) { - Error(e.E, "Postcondition must be a boolean (got {0})", e.E.Type); + reporter.Error(MessageSource.Resolver, e.E, "Postcondition must be a boolean (got {0})", e.E.Type); } } @@ -3896,7 +3780,7 @@ namespace Microsoft.Dafny Contract.Requires(iter != null); scope.PushMarker(); if (iter.SignatureIsOmitted) { - Error(iter, "iterator signature can be omitted only in refining methods"); + reporter.Error(MessageSource.Resolver, iter, "iterator signature can be omitted only in refining methods"); } var option = iter.TypeArgs.Count == 0 ? new ResolveTypeOption(iter) : new ResolveTypeOption(ResolveTypeOptionEnum.AllowPrefix); // resolve the types of the parameters @@ -3918,7 +3802,7 @@ namespace Microsoft.Dafny Contract.Requires(currentClass == null); Contract.Ensures(currentClass == null); - var initialErrorCount = ErrorCount; + var initialErrorCount = reporter.Count(ErrorLevel.Error); // Add in-parameters to the scope, but don't care about any duplication errors, since they have already been reported scope.PushMarker(); @@ -3937,7 +3821,7 @@ namespace Microsoft.Dafny var d = iter.DecreasesFields[i]; if (!UnifyTypes(d.Type, e.Type)) { // bummer, there was a use--and a bad use--of the field before, so this won't be the best of error messages - Error(e, "type of field {0} is {1}, but has been constrained elsewhere to be of type {2}", d.Name, e.Type, d.Type); + reporter.Error(MessageSource.Resolver, e, "type of field {0} is {1}, but has been constrained elsewhere to be of type {2}", d.Name, e.Type, d.Type); } } foreach (FrameExpression fe in iter.Reads.Expressions) { @@ -3950,7 +3834,7 @@ namespace Microsoft.Dafny ResolveExpression(e.E, new ResolveOpts(iter, false, true)); Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(e.E.Type, Type.Bool)) { - Error(e.E, "Precondition must be a boolean (got {0})", e.E.Type); + reporter.Error(MessageSource.Resolver, e.E, "Precondition must be a boolean (got {0})", e.E.Type); } } @@ -3966,27 +3850,27 @@ namespace Microsoft.Dafny ResolveExpression(e.E, new ResolveOpts(iter, false, true)); Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(e.E.Type, Type.Bool)) { - Error(e.E, "Yield precondition must be a boolean (got {0})", e.E.Type); + reporter.Error(MessageSource.Resolver, e.E, "Yield precondition must be a boolean (got {0})", e.E.Type); } } foreach (MaybeFreeExpression e in iter.YieldEnsures) { ResolveExpression(e.E, new ResolveOpts(iter, true, true)); Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(e.E.Type, Type.Bool)) { - Error(e.E, "Yield postcondition must be a boolean (got {0})", e.E.Type); + reporter.Error(MessageSource.Resolver, e.E, "Yield postcondition must be a boolean (got {0})", e.E.Type); } } foreach (MaybeFreeExpression e in iter.Ensures) { ResolveExpression(e.E, new ResolveOpts(iter, true, true)); Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(e.E.Type, Type.Bool)) { - Error(e.E, "Postcondition must be a boolean (got {0})", e.E.Type); + reporter.Error(MessageSource.Resolver, e.E, "Postcondition must be a boolean (got {0})", e.E.Type); } } ResolveAttributes(iter.Attributes, new ResolveOpts(iter, false, true)); - var postSpecErrorCount = ErrorCount; + var postSpecErrorCount = reporter.Count(ErrorLevel.Error); // Resolve body if (iter.Body != null) { @@ -4031,7 +3915,7 @@ namespace Microsoft.Dafny Expression frameSet = new SetDisplayExpr(iter.tok, true, modSetSingletons); foreach (var fr in iter.Reads.Expressions) { if (fr.FieldName != null) { - Error(fr.tok, "sorry, a reads clause for an iterator is not allowed to designate specific fields"); + reporter.Error(MessageSource.Resolver, fr.tok, "sorry, a reads clause for an iterator is not allowed to designate specific fields"); } else if (fr.E.Type.IsRefType) { modSetSingletons.Add(fr.E); } else { @@ -4046,7 +3930,7 @@ namespace Microsoft.Dafny frameSet = new SetDisplayExpr(iter.tok, true, modSetSingletons); foreach (var fr in iter.Modifies.Expressions) { if (fr.FieldName != null) { - Error(fr.tok, "sorry, a modifies clause for an iterator is not allowed to designate specific fields"); + reporter.Error(MessageSource.Resolver, fr.tok, "sorry, a modifies clause for an iterator is not allowed to designate specific fields"); } else if (fr.E.Type.IsRefType) { modSetSingletons.Add(fr.E); } else { @@ -4232,7 +4116,7 @@ namespace Microsoft.Dafny // nothing to resolve } else if (type is MapType) { var mt = (MapType)type; - var errorCount = ErrorCount; + var errorCount = reporter.Count(ErrorLevel.Error); int typeArgumentCount = 0; if (mt.HasTypeArg()) { ResolveType(tok, mt.Domain, context, option, defaultTypeArguments); @@ -4253,19 +4137,19 @@ namespace Microsoft.Dafny } // defaults and auto have been applied; check if we now have the right number of arguments if (2 != typeArgumentCount) { - Error(tok, "Wrong number of type arguments ({0} instead of 2) passed to type: {1}", typeArgumentCount, mt.CollectionTypeName); + reporter.Error(MessageSource.Resolver, tok, "Wrong number of type arguments ({0} instead of 2) passed to type: {1}", typeArgumentCount, mt.CollectionTypeName); // add proxy types, to make sure that MapType will have have a non-null Arg/Domain and Range if (typeArgumentCount == 0) { mt.SetTypeArg(new InferredTypeProxy()); } mt.SetRangetype(new InferredTypeProxy()); } - if (errorCount == ErrorCount && (mt.Domain.IsSubrangeType || mt.Range.IsSubrangeType)) { - Error(tok, "sorry, cannot instantiate collection type with a subrange type"); + if (errorCount == reporter.Count(ErrorLevel.Error) && (mt.Domain.IsSubrangeType || mt.Range.IsSubrangeType)) { + reporter.Error(MessageSource.Resolver, tok, "sorry, cannot instantiate collection type with a subrange type"); } } else if (type is CollectionType) { var t = (CollectionType)type; - var errorCount = ErrorCount; + var errorCount = reporter.Count(ErrorLevel.Error); if (t.HasTypeArg()) { ResolveType(tok, t.Arg, context, option, defaultTypeArguments); } else if (option.Opt != ResolveTypeOptionEnum.DontInfer) { @@ -4278,13 +4162,13 @@ namespace Microsoft.Dafny } if (!t.HasTypeArg()) { // defaults and auto have been applied; check if we now have the right number of arguments - Error(tok, "Wrong number of type arguments (0 instead of 1) passed to type: {0}", t.CollectionTypeName); + reporter.Error(MessageSource.Resolver, tok, "Wrong number of type arguments (0 instead of 1) passed to type: {0}", t.CollectionTypeName); // add a proxy type, to make sure that CollectionType will have have a non-null Arg t.SetTypeArg(new InferredTypeProxy()); } - if (errorCount == ErrorCount && t.Arg.IsSubrangeType) { - Error(tok, "sorry, cannot instantiate collection type with a subrange type"); + if (errorCount == reporter.Count(ErrorLevel.Error) && t.Arg.IsSubrangeType) { + reporter.Error(MessageSource.Resolver, tok, "sorry, cannot instantiate collection type with a subrange type"); } } else if (type is UserDefinedType) { @@ -4293,7 +4177,7 @@ namespace Microsoft.Dafny // Apparently, this type has already been resolved return null; } - var prevErrorCount = ErrorCount; + var prevErrorCount = reporter.Count(ErrorLevel.Error); if (t.NamePath is ExprDotName) { var ret = ResolveDotSuffix_Type((ExprDotName)t.NamePath, new ResolveOpts(context, true, true), allowDanglingDotName, option, defaultTypeArguments); if (ret != null) { @@ -4303,10 +4187,10 @@ namespace Microsoft.Dafny var s = (NameSegment)t.NamePath; ResolveNameSegment_Type(s, new ResolveOpts(context, true, true), option, defaultTypeArguments); } - if (ErrorCount == prevErrorCount) { + if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { var r = t.NamePath.Resolved as Resolver_IdentifierExpr; if (r == null || !(r.Type is Resolver_IdentifierExpr.ResolverType_Type)) { - Error(t.tok, "expected type"); + reporter.Error(MessageSource.Resolver, t.tok, "expected type"); } else if (r.Type is Resolver_IdentifierExpr.ResolverType_Type && r.TypeParamDecl != null) { t.ResolvedParam = r.TypeParamDecl; } else if (r.Type is Resolver_IdentifierExpr.ResolverType_Type) { @@ -4328,7 +4212,7 @@ namespace Microsoft.Dafny caller.EnclosingModule.CallGraph.AddEdge(caller, dd); if (caller == dd) { // detect self-loops here, since they don't show up in the graph's SSC methods - Error(dd.tok, "recursive dependency involving a newtype: {0} -> {0}", dd.Name); + reporter.Error(MessageSource.Resolver, dd.tok, "recursive dependency involving a newtype: {0} -> {0}", dd.Name); } } t.ResolvedClass = dd; @@ -4343,7 +4227,7 @@ namespace Microsoft.Dafny } // defaults and auto have been applied; check if we now have the right number of arguments if (d.TypeArgs.Count != t.TypeArgs.Count) { - Error(t.tok, "Wrong number of type arguments ({0} instead of {1}) passed to {2}: {3}", t.TypeArgs.Count, d.TypeArgs.Count, d.WhatKind, t.Name); + reporter.Error(MessageSource.Resolver, t.tok, "Wrong number of type arguments ({0} instead of {1}) passed to {2}: {3}", t.TypeArgs.Count, d.TypeArgs.Count, d.WhatKind, t.Name); } } @@ -4839,14 +4723,14 @@ namespace Microsoft.Dafny ResolveExpression(s.Expr, new ResolveOpts(codeContext, true, true)); Contract.Assert(s.Expr.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(s.Expr.Type, Type.Bool)) { - Error(s.Expr, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Expr.Type); + reporter.Error(MessageSource.Resolver, s.Expr, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Expr.Type); } } else if (stmt is PrintStmt) { PrintStmt s = (PrintStmt)stmt; ResolveAttributeArgs(s.Args, new ResolveOpts(codeContext, false, specContextOnly), false); if (specContextOnly) { - Error(stmt, "print statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); + reporter.Error(MessageSource.Resolver, stmt, "print statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); } } else if (stmt is BreakStmt) { @@ -4854,17 +4738,17 @@ namespace Microsoft.Dafny if (s.TargetLabel != null) { Statement target = labeledStatements.Find(s.TargetLabel); if (target == null) { - Error(s, "break label is undefined or not in scope: {0}", s.TargetLabel); + reporter.Error(MessageSource.Resolver, s, "break label is undefined or not in scope: {0}", s.TargetLabel); } else { s.TargetStmt = target; bool targetIsLoop = target is WhileStmt || target is AlternativeLoopStmt; if (specContextOnly && !s.TargetStmt.IsGhost && !inSpecOnlyContext[s.TargetStmt]) { - Error(stmt, "ghost-context break statement is not allowed to break out of non-ghost " + (targetIsLoop ? "loop" : "structure")); + reporter.Error(MessageSource.Resolver, stmt, "ghost-context break statement is not allowed to break out of non-ghost " + (targetIsLoop ? "loop" : "structure")); } } } else { if (loopStack.Count < s.BreakCount) { - Error(s, "trying to break out of more loop levels than there are enclosing loops"); + reporter.Error(MessageSource.Resolver, s, "trying to break out of more loop levels than there are enclosing loops"); } else { Statement target = loopStack[loopStack.Count - s.BreakCount]; if (target.Labels == null) { @@ -4873,7 +4757,7 @@ namespace Microsoft.Dafny } s.TargetStmt = target; if (specContextOnly && !target.IsGhost && !inSpecOnlyContext[target]) { - Error(stmt, "ghost-context break statement is not allowed to break out of non-ghost loop"); + reporter.Error(MessageSource.Resolver, stmt, "ghost-context break statement is not allowed to break out of non-ghost loop"); } } } @@ -4881,11 +4765,11 @@ namespace Microsoft.Dafny } else if (stmt is ProduceStmt) { var kind = stmt is YieldStmt ? "yield" : "return"; if (stmt is YieldStmt && !(codeContext is IteratorDecl)) { - Error(stmt, "yield statement is allowed only in iterators"); + reporter.Error(MessageSource.Resolver, stmt, "yield statement is allowed only in iterators"); } else if (stmt is ReturnStmt && !(codeContext is Method)) { - Error(stmt, "return statement is allowed only in method"); + reporter.Error(MessageSource.Resolver, stmt, "return statement is allowed only in method"); } else if (specContextOnly && !codeContext.IsGhost) { - Error(stmt, "{0} statement is not allowed in this context (because it is guarded by a specification-only expression)", kind); + reporter.Error(MessageSource.Resolver, stmt, "{0} statement is not allowed in this context (because it is guarded by a specification-only expression)", kind); } var s = (ProduceStmt)stmt; if (s.rhss != null) { @@ -4893,7 +4777,7 @@ namespace Microsoft.Dafny if (cmc == null) { // an error has already been reported above } else if (cmc.Outs.Count != s.rhss.Count) { - Error(s, "number of {2} parameters does not match declaration (found {0}, expected {1})", s.rhss.Count, cmc.Outs.Count, kind); + reporter.Error(MessageSource.Resolver, s, "number of {2} parameters does not match declaration (found {0}, expected {1})", s.rhss.Count, cmc.Outs.Count, kind); } else { Contract.Assert(s.rhss.Count > 0); // Create a hidden update statement using the out-parameter formals, resolve the RHS, and check that the RHS is good. @@ -4986,24 +4870,24 @@ namespace Microsoft.Dafny { if (currentMethod == null) { - Error(local.Tok, "assumption variable can only be declared in a method"); + reporter.Error(MessageSource.Resolver, local.Tok, "assumption variable can only be declared in a method"); } if (!local.IsGhost) { - Error(local.Tok, "assumption variable must be ghost"); + reporter.Error(MessageSource.Resolver, local.Tok, "assumption variable must be ghost"); } if (!(local.Type.IsBoolType)) { - Error(s, "assumption variable must be of type 'bool'"); + reporter.Error(MessageSource.Resolver, s, "assumption variable must be of type 'bool'"); } } } } else if (stmt is AssignStmt) { AssignStmt s = (AssignStmt)stmt; - int prevErrorCount = ErrorCount; + int prevErrorCount = reporter.Count(ErrorLevel.Error); ResolveExpression(s.Lhs, new ResolveOpts(codeContext, true, specContextOnly)); // allow ghosts for now, tighted up below - bool lhsResolvedSuccessfully = ErrorCount == prevErrorCount; + bool lhsResolvedSuccessfully = reporter.Count(ErrorLevel.Error) == prevErrorCount; Contract.Assert(s.Lhs.Type != null); // follows from postcondition of ResolveExpression // check that LHS denotes a mutable variable or a field bool lvalueIsGhost = false; @@ -5016,7 +4900,7 @@ namespace Microsoft.Dafny lvalueIsGhost = var.IsGhost || codeContext.IsGhost; CheckIsLvalue(lhs, codeContext); if (!lvalueIsGhost && specContextOnly) { - Error(stmt, "Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); + reporter.Error(MessageSource.Resolver, stmt, "Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); } var localVar = var as LocalVariable; @@ -5035,7 +4919,7 @@ namespace Microsoft.Dafny } else { - Error(stmt, string.Format("there may be at most one assignment to an assumption variable, the RHS of which must match the expression \"{0} && \"", localVar.Name)); + reporter.Error(MessageSource.Resolver, stmt, string.Format("there may be at most one assignment to an assumption variable, the RHS of which must match the expression \"{0} && \"", localVar.Name)); } } } @@ -5045,12 +4929,12 @@ namespace Microsoft.Dafny lvalueIsGhost = fse.Member.IsGhost; if (!lvalueIsGhost) { if (specContextOnly) { - Error(stmt, "Assignment to non-ghost field is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); + reporter.Error(MessageSource.Resolver, stmt, "Assignment to non-ghost field is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); } else { // It is now that we wish we would have resolved s.Lhs to not allow ghosts. Too late, so we do // the next best thing. if (lhsResolvedSuccessfully && UsesSpecFeatures(fse.Obj)) { - Error(stmt, "Assignment to non-ghost field is not allowed to use specification-only expressions in the receiver"); + reporter.Error(MessageSource.Resolver, stmt, "Assignment to non-ghost field is not allowed to use specification-only expressions in the receiver"); } } } @@ -5062,14 +4946,14 @@ namespace Microsoft.Dafny if (lhsResolvedSuccessfully) { Contract.Assert(slhs.Seq.Type != null); if (specContextOnly) { - Error(stmt, "Assignment to array element is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); + reporter.Error(MessageSource.Resolver, stmt, "Assignment to array element is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); } CheckIsLvalue(slhs, codeContext); } } else if (lhs is MultiSelectExpr) { if (specContextOnly) { - Error(stmt, "Assignment to array element is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); + reporter.Error(MessageSource.Resolver, stmt, "Assignment to array element is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); } CheckIsLvalue(lhs, codeContext); @@ -5087,7 +4971,7 @@ namespace Microsoft.Dafny } Contract.Assert(rr.Expr.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(lhsType, rr.Expr.Type)) { - Error(stmt, "RHS (of type {0}) not assignable to LHS (of type {1})", rr.Expr.Type, lhsType); + reporter.Error(MessageSource.Resolver, stmt, "RHS (of type {0}) not assignable to LHS (of type {1})", rr.Expr.Type, lhsType); } } else if (s.Rhs is TypeRhs) { TypeRhs rr = (TypeRhs)s.Rhs; @@ -5105,7 +4989,7 @@ namespace Microsoft.Dafny } } if (!UnifyTypes(lhsType, t)) { - Error(stmt, "type {0} is not assignable to LHS (of type {1})", t, lhsType); + reporter.Error(MessageSource.Resolver, stmt, "type {0} is not assignable to LHS (of type {1})", t, lhsType); } } else if (s.Rhs is HavocRhs) { // nothing else to do @@ -5130,12 +5014,12 @@ namespace Microsoft.Dafny IfStmt s = (IfStmt)stmt; bool branchesAreSpecOnly = specContextOnly; if (s.Guard != null) { - int prevErrorCount = ErrorCount; + int prevErrorCount = reporter.Count(ErrorLevel.Error); ResolveExpression(s.Guard, new ResolveOpts(codeContext, true, specContextOnly)); Contract.Assert(s.Guard.Type != null); // follows from postcondition of ResolveExpression - bool successfullyResolved = ErrorCount == prevErrorCount; + bool successfullyResolved = reporter.Count(ErrorLevel.Error) == prevErrorCount; if (!UnifyTypes(s.Guard.Type, Type.Bool)) { - Error(s.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Guard.Type); + reporter.Error(MessageSource.Resolver, s.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Guard.Type); } if (!specContextOnly && successfullyResolved) { branchesAreSpecOnly = UsesSpecFeatures(s.Guard); @@ -5163,13 +5047,13 @@ namespace Microsoft.Dafny bool bodyMustBeSpecOnly = specContextOnly; var fvs = new HashSet(); if (s.Guard != null) { - int prevErrorCount = ErrorCount; + int prevErrorCount = reporter.Count(ErrorLevel.Error); ResolveExpression(s.Guard, new ResolveOpts(codeContext, true, specContextOnly)); Contract.Assert(s.Guard.Type != null); // follows from postcondition of ResolveExpression - bool successfullyResolved = ErrorCount == prevErrorCount; + bool successfullyResolved = reporter.Count(ErrorLevel.Error) == prevErrorCount; Translator.ComputeFreeVariables(s.Guard, fvs); if (!UnifyTypes(s.Guard.Type, Type.Bool)) { - Error(s.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Guard.Type); + reporter.Error(MessageSource.Resolver, s.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Guard.Type); } if (!specContextOnly && successfullyResolved) { bodyMustBeSpecOnly = UsesSpecFeatures(s.Guard); @@ -5182,7 +5066,7 @@ namespace Microsoft.Dafny Contract.Assert(inv.E.Type != null); // follows from postcondition of ResolveExpression Translator.ComputeFreeVariables(inv.E, fvs); if (!UnifyTypes(inv.E.Type, Type.Bool)) { - Error(inv.E, "invariant is expected to be of type {0}, but is {1}", Type.Bool, inv.E.Type); + reporter.Error(MessageSource.Resolver, inv.E, "invariant is expected to be of type {0}, but is {1}", Type.Bool, inv.E.Type); } } @@ -5191,9 +5075,9 @@ namespace Microsoft.Dafny ResolveExpression(e, new ResolveOpts(codeContext, true, true)); if (e is WildcardExpr) { if (bodyMustBeSpecOnly) { - Error(e, "'decreases *' is not allowed on ghost loops"); + reporter.Error(MessageSource.Resolver, e, "'decreases *' is not allowed on ghost loops"); } else if (!codeContext.AllowsNontermination && !DafnyOptions.O.Dafnycc) { - Error(e, "a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating"); + reporter.Error(MessageSource.Resolver, e, "a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating"); } } // any type is fine @@ -5225,7 +5109,7 @@ namespace Microsoft.Dafny } } text += "};"; // always terminate with a semi-colon - ReportAdditionalInformation(s.Tok, text, s.Tok.val.Length); + reporter.Info(MessageSource.Resolver, s.Tok, text); } } else if (stmt is AlternativeLoopStmt) { @@ -5235,7 +5119,7 @@ namespace Microsoft.Dafny ResolveExpression(inv.E, new ResolveOpts(codeContext, true, true)); Contract.Assert(inv.E.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(inv.E.Type, Type.Bool)) { - Error(inv.E, "invariant is expected to be of type {0}, but is {1}", Type.Bool, inv.E.Type); + reporter.Error(MessageSource.Resolver, inv.E, "invariant is expected to be of type {0}, but is {1}", Type.Bool, inv.E.Type); } } @@ -5243,9 +5127,9 @@ namespace Microsoft.Dafny ResolveExpression(e, new ResolveOpts(codeContext, true, true)); if (e is WildcardExpr) { if (s.IsGhost) { - Error(e, "'decreases *' is not allowed on ghost loops"); + reporter.Error(MessageSource.Resolver, e, "'decreases *' is not allowed on ghost loops"); } else if (!codeContext.AllowsNontermination && !DafnyOptions.O.Dafnycc) { - Error(e, "a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating"); + reporter.Error(MessageSource.Resolver, e, "a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating"); } } // any type is fine @@ -5254,7 +5138,7 @@ namespace Microsoft.Dafny } else if (stmt is ForallStmt) { var s = (ForallStmt)stmt; - int prevErrorCount = ErrorCount; + int prevErrorCount = reporter.Count(ErrorLevel.Error); scope.PushMarker(); foreach (BoundVar v in s.BoundVars) { ScopePushAndReport(scope, v, "local-variable"); @@ -5263,24 +5147,24 @@ namespace Microsoft.Dafny ResolveExpression(s.Range, new ResolveOpts(codeContext, true, specContextOnly)); Contract.Assert(s.Range.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(s.Range.Type, Type.Bool)) { - Error(stmt, "range restriction in forall statement must be of type bool (instead got {0})", s.Range.Type); + reporter.Error(MessageSource.Resolver, stmt, "range restriction in forall statement must be of type bool (instead got {0})", s.Range.Type); } foreach (var ens in s.Ens) { ResolveExpression(ens.E, new ResolveOpts(codeContext, true, true)); Contract.Assert(ens.E.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(ens.E.Type, Type.Bool)) { - Error(ens.E, "ensures condition is expected to be of type {0}, but is {1}", Type.Bool, ens.E.Type); + reporter.Error(MessageSource.Resolver, ens.E, "ensures condition is expected to be of type {0}, but is {1}", Type.Bool, ens.E.Type); } } // Since the range and postconditions are more likely to infer the types of the bound variables, resolve them // first (above) and only then resolve the attributes (below). ResolveAttributes(s.Attributes, new ResolveOpts(codeContext, true, true)); - bool bodyMustBeSpecOnly = specContextOnly || (prevErrorCount == ErrorCount && UsesSpecFeatures(s.Range)); - if (!bodyMustBeSpecOnly && prevErrorCount == ErrorCount) { + bool bodyMustBeSpecOnly = specContextOnly || (prevErrorCount == reporter.Count(ErrorLevel.Error) && UsesSpecFeatures(s.Range)); + if (!bodyMustBeSpecOnly && prevErrorCount == reporter.Count(ErrorLevel.Error)) { 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); + s.Bounds = DiscoverBounds(s.Tok, s.BoundVars, s.Range, true, false, missingBounds, reporter); if (missingBounds.Count != 0) { bodyMustBeSpecOnly = true; } @@ -5299,7 +5183,7 @@ namespace Microsoft.Dafny } scope.PopMarker(); - if (prevErrorCount == ErrorCount) { + if (prevErrorCount == reporter.Count(ErrorLevel.Error)) { // determine the Kind and run some additional checks on the body if (s.Ens.Count != 0) { // The only supported kind with ensures clauses is Proof. @@ -5323,13 +5207,13 @@ 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); + reporter.Info(MessageSource.Resolver, s.Tok, "ensures " + Printer.ExprToString(result) + ";"); } else { s.Kind = ForallStmt.ParBodyKind.Proof; if (s.Body is BlockStmt && ((BlockStmt)s.Body).Body.Count == 0) { // an empty statement, so don't produce any warning } else { - Warning(s.Tok, "the conclusion of the body of this forall statement will not be known outside the forall statement; consider using an 'ensures' clause"); + reporter.Warning(MessageSource.Resolver, s.Tok, "the conclusion of the body of this forall statement will not be known outside the forall statement; consider using an 'ensures' clause"); } } } @@ -5351,7 +5235,7 @@ namespace Microsoft.Dafny s.IsGhost = specContextOnly; } else if (stmt is CalcStmt) { - var prevErrorCount = ErrorCount; + var prevErrorCount = reporter.Count(ErrorLevel.Error); CalcStmt s = (CalcStmt)stmt; s.IsGhost = true; if (s.Lines.Count > 0) { @@ -5359,12 +5243,12 @@ namespace Microsoft.Dafny ResolveExpression(e0, new ResolveOpts(codeContext, true, true)); Contract.Assert(e0.Type != null); // follows from postcondition of ResolveExpression for (int i = 1; i < s.Lines.Count; i++) { - if (i < s.Lines.Count - 1 || prevErrorCount == ErrorCount) { // do not resolve the dummy step if there were errors, it might generate more errors + if (i < s.Lines.Count - 1 || prevErrorCount == reporter.Count(ErrorLevel.Error)) { // do not resolve the dummy step if there were errors, it might generate more errors var e1 = s.Lines[i]; ResolveExpression(e1, new ResolveOpts(codeContext, true, true)); Contract.Assert(e1.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(e0.Type, e1.Type)) { - Error(e1, "all lines in a calculation must have the same type (got {0} after {1})", e1.Type, e0.Type); + reporter.Error(MessageSource.Resolver, e1, "all lines in a calculation must have the same type (got {0} after {1})", e1.Type, e0.Type); } else { var step = s.StepOps[i - 1].StepExpr(e0, e1); // Use custom line operator ResolveExpression(step, new ResolveOpts(codeContext, true, true)); @@ -5387,7 +5271,7 @@ namespace Microsoft.Dafny loopStack = prevLoopStack; } - if (prevErrorCount == ErrorCount && s.Lines.Count > 0) { + if (prevErrorCount == reporter.Count(ErrorLevel.Error) && s.Lines.Count > 0) { // do not build Result from the lines if there were errors, as it might be ill-typed and produce unnecessary resolution errors s.Result = s.ResultOp.StepExpr(s.Lines.First(), s.Lines.Last()); } else { @@ -5395,13 +5279,13 @@ namespace Microsoft.Dafny } ResolveExpression(s.Result, new ResolveOpts(codeContext, true, true)); Contract.Assert(s.Result != null); - Contract.Assert(prevErrorCount != ErrorCount || s.Steps.Count == s.Hints.Count); + Contract.Assert(prevErrorCount != reporter.Count(ErrorLevel.Error) || s.Steps.Count == s.Hints.Count); } else if (stmt is MatchStmt) { ResolveMatchStmt(stmt, specContextOnly, codeContext); } else if (stmt is SkeletonStatement) { var s = (SkeletonStatement)stmt; - Error(s.Tok, "skeleton statements are allowed only in refining methods"); + reporter.Error(MessageSource.Resolver, s.Tok, "skeleton statements are allowed only in refining methods"); // nevertheless, resolve the underlying statement; hey, why not if (s.S != null) { ResolveStatement(s.S, specContextOnly, codeContext); @@ -5416,10 +5300,10 @@ namespace Microsoft.Dafny DesugarMatchStmtWithTupleExpression(s); bool bodyIsSpecOnly = specContextOnly; - int prevErrorCount = ErrorCount; + int prevErrorCount = reporter.Count(ErrorLevel.Error); ResolveExpression(s.Source, new ResolveOpts(codeContext, true, specContextOnly)); Contract.Assert(s.Source.Type != null); // follows from postcondition of ResolveExpression - bool successfullyResolved = ErrorCount == prevErrorCount; + bool successfullyResolved = reporter.Count(ErrorLevel.Error) == prevErrorCount; if (!specContextOnly && successfullyResolved) { bodyIsSpecOnly = UsesSpecFeatures(s.Source); } @@ -5432,7 +5316,7 @@ namespace Microsoft.Dafny var subst = new Dictionary(); Dictionary ctors; if (dtd == null) { - Error(s.Source, "the type of the match source expression must be a datatype (instead found {0})", s.Source.Type); + reporter.Error(MessageSource.Resolver, s.Source, "the type of the match source expression must be a datatype (instead found {0})", s.Source.Type); ctors = null; } else { Contract.Assert(sourceType != null); // dtd and sourceType are set together above @@ -5462,15 +5346,15 @@ namespace Microsoft.Dafny if (ctors != null) { Contract.Assert(dtd != null); if (!ctors.TryGetValue(mc.Id, out ctor)) { - Error(mc.tok, "member {0} does not exist in datatype {1}", mc.Id, dtd.Name); + reporter.Error(MessageSource.Resolver, mc.tok, "member {0} does not exist in datatype {1}", mc.Id, dtd.Name); } else { Contract.Assert(ctor != null); // follows from postcondition of TryGetValue mc.Ctor = ctor; if (ctor.Formals.Count != mc.Arguments.Count) { - Error(mc.tok, "member {0} has wrong number of formals (found {1}, expected {2})", mc.Id, mc.Arguments.Count, ctor.Formals.Count); + reporter.Error(MessageSource.Resolver, mc.tok, "member {0} has wrong number of formals (found {1}, expected {2})", mc.Id, mc.Arguments.Count, ctor.Formals.Count); } if (memberNamesUsed.Contains(mc.Id)) { - Error(mc.tok, "member {0} appears in more than one case", mc.Id); + reporter.Error(MessageSource.Resolver, mc.tok, "member {0} appears in more than one case", mc.Id); } else { memberNamesUsed.Add(mc.Id); // add mc.Id to the set of names used } @@ -5486,7 +5370,7 @@ namespace Microsoft.Dafny Formal formal = ctor.Formals[i]; Type st = SubstType(formal.Type, subst); if (!UnifyTypes(v.Type, st)) { - Error(stmt, "the declared type of the formal ({0}) does not agree with the corresponding type in the constructor's signature ({1})", v.Type, st); + reporter.Error(MessageSource.Resolver, stmt, "the declared type of the formal ({0}) does not agree with the corresponding type in the constructor's signature ({1})", v.Type, st); } v.IsGhost = formal.IsGhost; @@ -5557,7 +5441,7 @@ namespace Microsoft.Dafny if (me.Source is DatatypeValue) { var e = (DatatypeValue)me.Source; if (e.Arguments.Count < 1) { - Error(me.Tok, "match source tuple needs at least 1 argument"); + reporter.Error(MessageSource.Resolver, me.Tok, "match source tuple needs at least 1 argument"); } else { Expression source = e.Arguments[0]; List cases = new List(); @@ -5566,7 +5450,7 @@ namespace Microsoft.Dafny bool keepOrigToken = true; foreach (MatchCaseStmt mc in me.Cases) { if (mc.CasePatterns == null || mc.CasePatterns.Count != e.Arguments.Count) { - Error(mc.tok, "case arguments count does not match source arguments count"); + reporter.Error(MessageSource.Resolver, mc.tok, "case arguments count does not match source arguments count"); } else { CasePattern cp = mc.CasePatterns[0]; List patterns; @@ -5723,7 +5607,7 @@ namespace Microsoft.Dafny ScopePushAndReport(scope, v, "parameter"); } else { if (scope.Find(v.Name) != null) { - Error(v, "Duplicate parameter name: {0}", v.Name); + reporter.Error(MessageSource.Resolver, v, "Duplicate parameter name: {0}", v.Name); } } } else { @@ -5938,26 +5822,26 @@ namespace Microsoft.Dafny } } s += ";"; // always terminate with a semi-colon, even in the case of an empty decreases clause - ReportAdditionalInformation(loopStmt.Tok, s, loopStmt.Tok.val.Length); + reporter.Info(MessageSource.Resolver, loopStmt.Tok, s); } } private void ResolveConcreteUpdateStmt(ConcreteUpdateStatement s, bool specContextOnly, ICodeContext codeContext) { Contract.Requires(codeContext != null); // First, resolve all LHS's and expression-looking RHS's. - int errorCountBeforeCheckingLhs = ErrorCount; + int errorCountBeforeCheckingLhs = reporter.Count(ErrorLevel.Error); var update = s as UpdateStmt; var lhsNameSet = new HashSet(); // used to check for duplicate identifiers on the left (full duplication checking for references and the like is done during verification) foreach (var lhs in s.Lhss) { - var ec = ErrorCount; + var ec = reporter.Count(ErrorLevel.Error); ResolveExpression(lhs, new ResolveOpts(codeContext, true, specContextOnly)); - if (ec == ErrorCount) { + if (ec == reporter.Count(ErrorLevel.Error)) { if (update == null && specContextOnly && !AssignStmt.LhsIsToGhost(lhs) && !codeContext.IsGhost) { - Error(lhs, "cannot assign to non-ghost variable in a ghost context"); + reporter.Error(MessageSource.Resolver, lhs, "cannot assign to non-ghost variable in a ghost context"); } if (lhs is SeqSelectExpr && !((SeqSelectExpr)lhs).SelectOne) { - Error(lhs, "cannot assign to a range of array elements (try the 'forall' statement)"); + reporter.Error(MessageSource.Resolver, lhs, "cannot assign to a range of array elements (try the 'forall' statement)"); } } } @@ -6018,10 +5902,10 @@ namespace Microsoft.Dafny if (firstEffectfulRhs == null) { if (update.Lhss.Count == 0) { Contract.Assert(update.Rhss.Count == 1); // guaranteed by the parser - Error(update, "expected method call, found expression"); + reporter.Error(MessageSource.Resolver, update, "expected method call, found expression"); } else if (update.Lhss.Count != update.Rhss.Count) { - Error(update, "the number of left-hand sides ({0}) and right-hand sides ({1}) must match for a multi-assignment", update.Lhss.Count, update.Rhss.Count); - } else if (ErrorCount == errorCountBeforeCheckingLhs) { + reporter.Error(MessageSource.Resolver, update, "the number of left-hand sides ({0}) and right-hand sides ({1}) must match for a multi-assignment", update.Lhss.Count, update.Rhss.Count); + } else if (reporter.Count(ErrorLevel.Error) == errorCountBeforeCheckingLhs) { // add the statements here in a sequence, but don't use that sequence later for translation (instead, should translate properly as multi-assignment) for (int i = 0; i < update.Lhss.Count; i++) { var a = new AssignStmt(update.Tok, update.EndTok, update.Lhss[i].Resolved, update.Rhss[i]); @@ -6031,19 +5915,19 @@ namespace Microsoft.Dafny } else if (update.CanMutateKnownState) { if (1 < update.Rhss.Count) { - Error(firstEffectfulRhs, "cannot have effectful parameter in multi-return statement."); + reporter.Error(MessageSource.Resolver, firstEffectfulRhs, "cannot have effectful parameter in multi-return statement."); } else { // it might be ok, if it is a TypeRhs Contract.Assert(update.Rhss.Count == 1); if (methodCallInfo != null) { - Error(methodCallInfo.Tok, "cannot have method call in return statement."); + reporter.Error(MessageSource.Resolver, methodCallInfo.Tok, "cannot have method call in return statement."); } else { // we have a TypeRhs Contract.Assert(update.Rhss[0] is TypeRhs); var tr = (TypeRhs)update.Rhss[0]; Contract.Assert(tr.InitCall != null); // there were effects, so this must have been a call. if (tr.CanAffectPreviouslyKnownExpressions) { - Error(tr.Tok, "can only have initialization methods which modify at most 'this'."); - } else if (ErrorCount == errorCountBeforeCheckingLhs) { + reporter.Error(MessageSource.Resolver, tr.Tok, "can only have initialization methods which modify at most 'this'."); + } else if (reporter.Count(ErrorLevel.Error) == errorCountBeforeCheckingLhs) { var a = new AssignStmt(update.Tok, update.EndTok, update.Lhss[0].Resolved, tr); update.ResolvedStatements.Add(a); } @@ -6053,17 +5937,17 @@ namespace Microsoft.Dafny } else { // if there was an effectful RHS, that must be the only RHS if (update.Rhss.Count != 1) { - Error(firstEffectfulRhs, "an update statement is allowed an effectful RHS only if there is just one RHS"); + reporter.Error(MessageSource.Resolver, firstEffectfulRhs, "an update statement is allowed an effectful RHS only if there is just one RHS"); } else if (methodCallInfo == null) { // must be a single TypeRhs if (update.Lhss.Count != 1) { Contract.Assert(2 <= update.Lhss.Count); // the parser allows 0 Lhss only if the whole statement looks like an expression (not a TypeRhs) - Error(update.Lhss[1].tok, "the number of left-hand sides ({0}) and right-hand sides ({1}) must match for a multi-assignment", update.Lhss.Count, update.Rhss.Count); - } else if (ErrorCount == errorCountBeforeCheckingLhs) { + reporter.Error(MessageSource.Resolver, update.Lhss[1].tok, "the number of left-hand sides ({0}) and right-hand sides ({1}) must match for a multi-assignment", update.Lhss.Count, update.Rhss.Count); + } else if (reporter.Count(ErrorLevel.Error) == errorCountBeforeCheckingLhs) { var a = new AssignStmt(update.Tok, update.EndTok, update.Lhss[0].Resolved, update.Rhss[0]); update.ResolvedStatements.Add(a); } - } else if (ErrorCount == errorCountBeforeCheckingLhs) { + } else if (reporter.Count(ErrorLevel.Error) == errorCountBeforeCheckingLhs) { // a call statement var resolvedLhss = new List(); foreach (var ll in update.Lhss) { @@ -6090,7 +5974,7 @@ namespace Microsoft.Dafny foreach (var lhs in s.Lhss) { var ide = lhs.Resolved as IdentifierExpr; if (ide == null) { - Error(lhs, "the assign-such-that statement currently only supports local-variable LHSs"); + reporter.Error(MessageSource.Resolver, lhs, "the assign-such-that statement currently only supports local-variable LHSs"); } else { varLhss.Add(ide.Var); } @@ -6098,17 +5982,17 @@ namespace Microsoft.Dafny } s.IsGhost = s.Lhss.TrueForAll(AssignStmt.LhsIsToGhost); - var ec = ErrorCount; + var ec = reporter.Count(ErrorLevel.Error); ResolveExpression(s.Expr, new ResolveOpts(codeContext, true, specContextOnly)); if (!UnifyTypes(s.Expr.Type, Type.Bool)) { - Error(s.Expr, "type of RHS of assign-such-that statement must be boolean (got {0})", s.Expr.Type); + reporter.Error(MessageSource.Resolver, s.Expr, "type of RHS of assign-such-that statement must be boolean (got {0})", s.Expr.Type); } - if (ec == ErrorCount && !s.IsGhost && s.AssumeToken == null && !specContextOnly) { + if (ec == reporter.Count(ErrorLevel.Error) && !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); + var allBounds = DiscoverBoundsAux(s.Tok, varLhss, s.Expr, true, true, true, missingBounds, reporter); if (missingBounds.Count != 0) { s.MissingBounds = missingBounds; // so that an error message can be produced during compilation } else { @@ -6130,12 +6014,12 @@ namespace Microsoft.Dafny bool isGhost = specContextOnly; // first, resolve the guards, which tells us whether or not the entire statement is a ghost statement foreach (var alternative in alternatives) { - int prevErrorCount = ErrorCount; + int prevErrorCount = reporter.Count(ErrorLevel.Error); ResolveExpression(alternative.Guard, new ResolveOpts(codeContext, true, specContextOnly)); Contract.Assert(alternative.Guard.Type != null); // follows from postcondition of ResolveExpression - bool successfullyResolved = ErrorCount == prevErrorCount; + bool successfullyResolved = reporter.Count(ErrorLevel.Error) == prevErrorCount; if (!UnifyTypes(alternative.Guard.Type, Type.Bool)) { - Error(alternative.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, alternative.Guard.Type); + reporter.Error(MessageSource.Resolver, alternative.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, alternative.Guard.Type); } if (!specContextOnly && successfullyResolved) { isGhost = isGhost || UsesSpecFeatures(alternative.Guard); @@ -6174,11 +6058,11 @@ namespace Microsoft.Dafny var callee = s.Method; Contract.Assert(callee != null); // follows from the invariant of CallStmt if (!isInitCall && callee is Constructor) { - Error(s, "a constructor is allowed to be called only when an object is being allocated"); + reporter.Error(MessageSource.Resolver, s, "a constructor is allowed to be called only when an object is being allocated"); } s.IsGhost = callee.IsGhost; if (specContextOnly && !callee.IsGhost) { - Error(s, "only ghost methods can be called from this context"); + reporter.Error(MessageSource.Resolver, s, "only ghost methods can be called from this context"); } // resolve left-hand sides @@ -6192,49 +6076,49 @@ namespace Microsoft.Dafny int j = 0; foreach (Expression e in s.Args) { bool allowGhost = s.IsGhost || callee.Ins.Count <= j || callee.Ins[j].IsGhost; - var ec = ErrorCount; + var ec = reporter.Count(ErrorLevel.Error); ResolveExpression(e, new ResolveOpts(codeContext, true, allowGhost)); - if (ec == ErrorCount && !allowGhost) { + if (ec == reporter.Count(ErrorLevel.Error) && !allowGhost) { CheckIsNonGhost(e); } j++; } if (callee.Ins.Count != s.Args.Count) { - Error(s, "wrong number of method arguments (got {0}, expected {1})", s.Args.Count, callee.Ins.Count); + reporter.Error(MessageSource.Resolver, s, "wrong number of method arguments (got {0}, expected {1})", s.Args.Count, callee.Ins.Count); } else if (callee.Outs.Count != s.Lhs.Count) { if (isInitCall) { - Error(s, "a method called as an initialization method must not have any result arguments"); + reporter.Error(MessageSource.Resolver, s, "a method called as an initialization method must not have any result arguments"); } else { - Error(s, "wrong number of method result arguments (got {0}, expected {1})", s.Lhs.Count, callee.Outs.Count); + reporter.Error(MessageSource.Resolver, s, "wrong number of method result arguments (got {0}, expected {1})", s.Lhs.Count, callee.Outs.Count); } } else { if (isInitCall) { if (callee.IsStatic) { - Error(s.Tok, "a method called as an initialization method must not be 'static'"); + reporter.Error(MessageSource.Resolver, s.Tok, "a method called as an initialization method must not be 'static'"); } } else if (!callee.IsStatic) { if (!scope.AllowInstance && s.Receiver is ThisExpr) { // The call really needs an instance, but that instance is given as 'this', which is not // available in this context. For more details, see comment in the resolution of a // FunctionCallExpr. - Error(s.Receiver, "'this' is not allowed in a 'static' context"); + reporter.Error(MessageSource.Resolver, s.Receiver, "'this' is not allowed in a 'static' context"); } else if (s.Receiver is StaticReceiverExpr) { - Error(s.Receiver, "call to instance method requires an instance"); + reporter.Error(MessageSource.Resolver, s.Receiver, "call to instance method requires an instance"); } } // type check the arguments for (int i = 0; i < callee.Ins.Count; i++) { Type st = SubstType(callee.Ins[i].Type, s.MethodSelect.TypeArgumentSubstitutions()); if (!UnifyTypes(cce.NonNull(s.Args[i].Type), st)) { - Error(s, "incorrect type of method in-parameter {0} (expected {1}, got {2})", i, st, s.Args[i].Type); + reporter.Error(MessageSource.Resolver, s, "incorrect type of method in-parameter {0} (expected {1}, got {2})", i, st, s.Args[i].Type); } } for (int i = 0; i < callee.Outs.Count; i++) { Type st = SubstType(callee.Outs[i].Type, s.MethodSelect.TypeArgumentSubstitutions()); var lhs = s.Lhs[i]; if (!UnifyTypes(cce.NonNull(lhs.Type), st)) { - Error(s, "incorrect type of method out-parameter {0} (expected {1}, got {2})", i, st, lhs.Type); + reporter.Error(MessageSource.Resolver, s, "incorrect type of method out-parameter {0} (expected {1}, got {2})", i, st, lhs.Type); } else { var resolvedLhs = lhs.Resolved; if (!specContextOnly && (s.IsGhost || callee.Outs[i].IsGhost)) { @@ -6246,17 +6130,17 @@ namespace Microsoft.Dafny // the variable was actually declared in this statement, so auto-declare it as ghost ((LocalVariable)ll.Var).MakeGhost(); } else { - Error(s, "actual out-parameter {0} is required to be a ghost variable", i); + reporter.Error(MessageSource.Resolver, s, "actual out-parameter {0} is required to be a ghost variable", i); } } } else if (resolvedLhs is MemberSelectExpr) { var ll = (MemberSelectExpr)resolvedLhs; if (!ll.Member.IsGhost) { - Error(s, "actual out-parameter {0} is required to be a ghost field", i); + reporter.Error(MessageSource.Resolver, s, "actual out-parameter {0} is required to be a ghost field", i); } } else { // this is an array update, and arrays are always non-ghost - Error(s, "actual out-parameter {0} is required to be a ghost variable", i); + reporter.Error(MessageSource.Resolver, s, "actual out-parameter {0} is required to be a ghost variable", i); } } // LHS must denote a mutable field. @@ -6283,7 +6167,7 @@ namespace Microsoft.Dafny } } if (Contract.Exists(callee.Decreases.Expressions, e => e is WildcardExpr) && !codeContext.AllowsNontermination) { - Error(s.Tok, "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"); + reporter.Error(MessageSource.Resolver, s.Tok, "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"); } } @@ -6298,26 +6182,26 @@ namespace Microsoft.Dafny if (lhs is IdentifierExpr) { var ll = (IdentifierExpr)lhs; if (!ll.Var.IsMutable) { - Error(lhs, "LHS of assignment must denote a mutable variable"); + reporter.Error(MessageSource.Resolver, lhs, "LHS of assignment must denote a mutable variable"); } } else if (lhs is MemberSelectExpr) { var ll = (MemberSelectExpr)lhs; var field = ll.Member as Field; if (field == null || !field.IsUserMutable) { - Error(lhs, "LHS of assignment must denote a mutable field"); + reporter.Error(MessageSource.Resolver, lhs, "LHS of assignment must denote a mutable field"); } } else if (lhs is SeqSelectExpr) { var ll = (SeqSelectExpr)lhs; if (!UnifyTypes(ll.Seq.Type, ResolvedArrayType(ll.Seq.tok, 1, new InferredTypeProxy(), codeContext))) { - Error(ll.Seq, "LHS of array assignment must denote an array element (found {0})", ll.Seq.Type); + reporter.Error(MessageSource.Resolver, ll.Seq, "LHS of array assignment must denote an array element (found {0})", ll.Seq.Type); } if (!ll.SelectOne) { - Error(ll.Seq, "cannot assign to a range of array elements (try the 'forall' statement)"); + reporter.Error(MessageSource.Resolver, ll.Seq, "cannot assign to a range of array elements (try the 'forall' statement)"); } } else if (lhs is MultiSelectExpr) { // nothing to check; this can only denote an array element } else { - Error(lhs, "LHS of assignment must denote a mutable variable or field"); + reporter.Error(MessageSource.Resolver, lhs, "LHS of assignment must denote a mutable variable or field"); } } @@ -6333,9 +6217,9 @@ namespace Microsoft.Dafny Contract.Assert(lnode.Name != null); // LabelNode's with .Label==null are added only during resolution of the break statements with 'stmt' as their target, which hasn't happened yet var prev = labeledStatements.Find(lnode.Name); if (prev == ss) { - Error(lnode.Tok, "duplicate label"); + reporter.Error(MessageSource.Resolver, lnode.Tok, "duplicate label"); } else if (prev != null) { - Error(lnode.Tok, "label shadows an enclosing label"); + reporter.Error(MessageSource.Resolver, lnode.Tok, "label shadows an enclosing label"); } else { var r = labeledStatements.Push(lnode.Name, ss); Contract.Assert(r == Scope.PushResult.Success); // since we just checked for duplicates, we expect the Push to succeed @@ -6357,13 +6241,13 @@ namespace Microsoft.Dafny if (stmt is PredicateStmt) { // cool } else if (stmt is PrintStmt) { - Error(stmt, "print statement is not allowed inside a forall statement"); + reporter.Error(MessageSource.Resolver, stmt, "print statement is not allowed inside a forall statement"); } else if (stmt is BreakStmt) { // this case is checked already in the first pass through the forall-statement body, by doing so from an empty set of labeled statements and resetting the loop-stack } else if (stmt is ReturnStmt) { - Error(stmt, "return statement is not allowed inside a forall statement"); + reporter.Error(MessageSource.Resolver, stmt, "return statement is not allowed inside a forall statement"); } else if (stmt is YieldStmt) { - Error(stmt, "yield statement is not allowed inside a forall statement"); + reporter.Error(MessageSource.Resolver, stmt, "yield statement is not allowed inside a forall statement"); } else if (stmt is AssignSuchThatStmt) { var s = (AssignSuchThatStmt)stmt; foreach (var lhs in s.Lhss) { @@ -6385,9 +6269,9 @@ namespace Microsoft.Dafny var rhs = s.Rhs; // ExprRhs and HavocRhs are fine, but TypeRhs is not if (rhs is TypeRhs) { if (kind == ForallStmt.ParBodyKind.Assign) { - Error(rhs.Tok, "new allocation not supported in forall statements"); + reporter.Error(MessageSource.Resolver, rhs.Tok, "new allocation not supported in forall statements"); } else { - Error(rhs.Tok, "new allocation not allowed in ghost context"); + reporter.Error(MessageSource.Resolver, rhs.Tok, "new allocation not allowed in ghost context"); } } } else if (stmt is CallStmt) { @@ -6396,14 +6280,14 @@ namespace Microsoft.Dafny var idExpr = lhs as IdentifierExpr; if (idExpr != null) { if (scope.ContainsDecl(idExpr.Var)) { - Error(stmt, "body of forall statement is attempting to update a variable declared outside the forall statement"); + reporter.Error(MessageSource.Resolver, stmt, "body of forall statement is attempting to update a variable declared outside the forall statement"); } } else { - Error(stmt, "the body of the enclosing forall statement is not allowed to update heap locations"); + reporter.Error(MessageSource.Resolver, stmt, "the body of the enclosing forall statement is not allowed to update heap locations"); } } if (s.Method.Mod.Expressions.Count != 0) { - Error(stmt, "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"); + reporter.Error(MessageSource.Resolver, stmt, "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"); } if (!s.Method.IsGhost) { // The reason for this restriction is that the compiler is going to omit the forall statement altogether--it has @@ -6411,7 +6295,7 @@ namespace Microsoft.Dafny // a method that prints something, all calls to non-ghost methods are disallowed. (Note, if this restriction // is somehow lifted in the future, then it is still necessary to enforce s.Method.Mod.Expressions.Count != 0 for // calls to non-ghost methods.) - Error(s, "the body of the enclosing forall statement is not allowed to call non-ghost methods"); + reporter.Error(MessageSource.Resolver, s, "the body of the enclosing forall statement is not allowed to call non-ghost methods"); } } else if (stmt is BlockStmt) { @@ -6455,7 +6339,7 @@ namespace Microsoft.Dafny var s = (ForallStmt)stmt; switch (s.Kind) { case ForallStmt.ParBodyKind.Assign: - Error(stmt, "a forall statement with heap updates is not allowed inside the body of another forall statement"); + reporter.Error(MessageSource.Resolver, stmt, "a forall statement with heap updates is not allowed inside the body of another forall statement"); break; case ForallStmt.ParBodyKind.Call: case ForallStmt.ParBodyKind.Proof: @@ -6486,10 +6370,10 @@ namespace Microsoft.Dafny var idExpr = lhs as IdentifierExpr; if (idExpr != null) { if (scope.ContainsDecl(idExpr.Var)) { - Error(tok, "body of forall statement is attempting to update a variable declared outside the forall statement"); + reporter.Error(MessageSource.Resolver, tok, "body of forall statement is attempting to update a variable declared outside the forall statement"); } } else if (kind != ForallStmt.ParBodyKind.Assign) { - Error(tok, "the body of the enclosing forall statement is not allowed to update heap locations"); + reporter.Error(MessageSource.Resolver, tok, "the body of the enclosing forall statement is not allowed to update heap locations"); } } @@ -6506,9 +6390,9 @@ namespace Microsoft.Dafny } else if (stmt is BreakStmt) { // already checked while resolving hints } else if (stmt is ReturnStmt) { - Error(stmt, "return statement is not allowed inside a hint"); + reporter.Error(MessageSource.Resolver, stmt, "return statement is not allowed inside a hint"); } else if (stmt is YieldStmt) { - Error(stmt, "yield statement is not allowed inside a hint"); + reporter.Error(MessageSource.Resolver, stmt, "yield statement is not allowed inside a hint"); } else if (stmt is AssignSuchThatStmt) { var s = (AssignSuchThatStmt)stmt; foreach (var lhs in s.Lhss) { @@ -6520,7 +6404,7 @@ namespace Microsoft.Dafny } else if (stmt is CallStmt) { var s = (CallStmt)stmt; if (s.Method.Mod.Expressions.Count != 0) { - Error(stmt, "calls to methods with side-effects are not allowed inside a hint"); + reporter.Error(MessageSource.Resolver, stmt, "calls to methods with side-effects are not allowed inside a hint"); } } else if (stmt is UpdateStmt) { var s = (UpdateStmt)stmt; @@ -6558,7 +6442,7 @@ namespace Microsoft.Dafny } else if (stmt is WhileStmt) { var s = (WhileStmt)stmt; if (s.Mod.Expressions != null && s.Mod.Expressions.Count != 0) { - Error(s.Mod.Expressions[0].tok, "a while statement used inside a hint is not allowed to have a modifies clause"); + 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); @@ -6576,7 +6460,7 @@ namespace Microsoft.Dafny var s = (ForallStmt)stmt; switch (s.Kind) { case ForallStmt.ParBodyKind.Assign: - Error(stmt, "a forall statement with heap updates is not allowed inside a hint"); + reporter.Error(MessageSource.Resolver, stmt, "a forall statement with heap updates is not allowed inside a hint"); break; case ForallStmt.ParBodyKind.Call: case ForallStmt.ParBodyKind.Proof: @@ -6609,9 +6493,9 @@ namespace Microsoft.Dafny void CheckHintLhs(IToken tok, Expression lhs) { var idExpr = lhs as IdentifierExpr; if (idExpr == null) { - Error(tok, "a hint is not allowed to update heap locations"); + reporter.Error(MessageSource.Resolver, tok, "a hint is not allowed to update heap locations"); } else if (scope.ContainsDecl(idExpr.Var)) { - Error(tok, "a hint is not allowed to update a variable declared outside the hint"); + reporter.Error(MessageSource.Resolver, tok, "a hint is not allowed to update a variable declared outside the hint"); } } @@ -6623,7 +6507,7 @@ namespace Microsoft.Dafny // "new" is not allowed in ghost contexts if (specContextOnly) { - Error(rr.Tok, "'new' is not allowed in ghost contexts"); + reporter.Error(MessageSource.Resolver, rr.Tok, "'new' is not allowed in ghost contexts"); } if (rr.Type == null) { if (rr.ArrayDimensions != null) { @@ -6635,7 +6519,7 @@ namespace Microsoft.Dafny Contract.Assert(dim != null); ResolveExpression(dim, new ResolveOpts(codeContext, true)); if (!UnifyTypes(dim.Type, new OperationTypeProxy(true, false, false, false, false, false))) { - Error(stmt, "new must use an integer-based expression for the array size (got {0} for index {1})", dim.Type, i); + reporter.Error(MessageSource.Resolver, stmt, "new must use an integer-based expression for the array size (got {0} for index {1})", dim.Type, i); } i++; } @@ -6645,7 +6529,7 @@ namespace Microsoft.Dafny if (rr.Arguments == null) { ResolveType(stmt.Tok, rr.EType, codeContext, ResolveTypeOptionEnum.InferTypeProxies, null); if (!rr.EType.IsRefType) { - Error(stmt, "new can be applied only to reference types (got {0})", rr.EType); + reporter.Error(MessageSource.Resolver, stmt, "new can be applied only to reference types (got {0})", rr.EType); } } else { string initCallName = null; @@ -6668,18 +6552,18 @@ namespace Microsoft.Dafny initCallTok = rr.Tok; } if (!rr.EType.IsRefType) { - Error(stmt, "new can be applied only to reference types (got {0})", rr.EType); + reporter.Error(MessageSource.Resolver, stmt, "new can be applied only to reference types (got {0})", rr.EType); } else { // ---------- new C.Init(EE) Contract.Assert(initCallName != null); - var prevErrorCount = ErrorCount; + var prevErrorCount = reporter.Count(ErrorLevel.Error); // We want to create a MemberSelectExpr for the initializing method. To do that, we create a throw-away receiver of the appropriate // type, create an dot-suffix expression around this receiver, and then resolve it in the usual way for dot-suffix expressions. var lhs = new ImplicitThisExpr(initCallTok) { Type = rr.EType }; var callLhs = new ExprDotName(initCallTok, lhs, initCallName, ret == null ? null : ret.LastComponent.OptTypeArguments); ResolveDotSuffix(callLhs, true, rr.Arguments, new ResolveOpts(codeContext, true, specContextOnly), true); - if (prevErrorCount == ErrorCount) { + if (prevErrorCount == reporter.Count(ErrorLevel.Error)) { Contract.Assert(callLhs.ResolvedExpression is MemberSelectExpr); // since ResolveApplySuffix succeeded and call.Lhs denotes an expression (not a module or a type) var methodSel = (MemberSelectExpr)callLhs.ResolvedExpression; if (methodSel.Member is Method) { @@ -6689,7 +6573,7 @@ namespace Microsoft.Dafny callsConstructor = true; } } else { - Error(initCallTok, "object initialization must denote an initializing method or constructor ({0})", initCallName); + reporter.Error(MessageSource.Resolver, initCallTok, "object initialization must denote an initializing method or constructor ({0})", initCallName); } } } @@ -6699,10 +6583,10 @@ namespace Microsoft.Dafny if (udt != null) { var cl = (ClassDecl)udt.ResolvedClass; // cast is guaranteed by the call to rr.EType.IsRefType above, together with the "rr.EType is UserDefinedType" test if (cl is TraitDecl) { - Error(stmt, "new cannot be applied to a trait"); + reporter.Error(MessageSource.Resolver, stmt, "new cannot be applied to a trait"); } if (!callsConstructor && cl.HasConstructor) { - Error(stmt, "when allocating an object of type '{0}', one of its constructor methods must be called", cl.Name); + reporter.Error(MessageSource.Resolver, stmt, "when allocating an object of type '{0}', one of its constructor methods must be called", cl.Name); } } } @@ -6731,7 +6615,7 @@ namespace Microsoft.Dafny } } if (receiverType is TypeProxy) { - Error(tok, "type of the receiver is not fully determined at this program point", receiverType); + reporter.Error(MessageSource.Resolver, tok, "type of the receiver is not fully determined at this program point", receiverType); return null; } Contract.Assert(receiverType is NonProxyType); // there are only two kinds of types: proxies and non-proxies @@ -6744,20 +6628,20 @@ namespace Microsoft.Dafny if (!classMembers[cd].TryGetValue(memberName, out member)) { var kind = cd is IteratorDecl ? "iterator" : "class"; if (memberName == "_ctor") { - Error(tok, "{0} {1} does not have an anonymous constructor", kind, ctype.Name); + reporter.Error(MessageSource.Resolver, tok, "{0} {1} does not have an anonymous constructor", kind, ctype.Name); } else { // search the static members of the enclosing module or its imports if (moduleInfo.StaticMembers.TryGetValue(memberName, out member)) { Contract.Assert(member.IsStatic); // moduleInfo.StaticMembers is supposed to contain only static members of the module's implicit class _default if (member is AmbiguousMemberDecl) { var ambiguousMember = (AmbiguousMemberDecl)member; - Error(tok, "The name {0} ambiguously refers to a static member in one of the modules {1} (try qualifying the member name with the module name)", memberName, ambiguousMember.ModuleNames()); + reporter.Error(MessageSource.Resolver, tok, "The name {0} ambiguously refers to a static member in one of the modules {1} (try qualifying the member name with the module name)", memberName, ambiguousMember.ModuleNames()); } else { nptype = GetReceiverType(tok, member); return member; } } else { - Error(tok, "member {0} does not exist in {2} {1}", memberName, ctype.Name, kind); + reporter.Error(MessageSource.Resolver, tok, "member {0} does not exist in {2} {1}", memberName, ctype.Name, kind); } } return null; @@ -6771,7 +6655,7 @@ namespace Microsoft.Dafny if (dtd != null) { MemberDecl member; if (!datatypeMembers[dtd].TryGetValue(memberName, out member)) { - Error(tok, "member {0} does not exist in datatype {1}", memberName, dtd.Name); + reporter.Error(MessageSource.Resolver, tok, "member {0} does not exist in datatype {1}", memberName, dtd.Name); return null; } else { nptype = (UserDefinedType)receiverType; @@ -6797,7 +6681,7 @@ namespace Microsoft.Dafny } } - Error(tok, "type {0} does not have a member {1}", receiverType, memberName); + reporter.Error(MessageSource.Resolver, tok, "type {0} does not have a member {1}", receiverType, memberName); return null; } @@ -7021,10 +6905,10 @@ namespace Microsoft.Dafny } else if (expr is NegationExpression) { var e = (NegationExpression)expr; - var errorCount = ErrorCount; + var errorCount = reporter.Count(ErrorLevel.Error); ResolveExpression(e.E, opts); e.Type = e.E.Type; - if (errorCount != ErrorCount) { + if (errorCount != reporter.Count(ErrorLevel.Error)) { // there were errors resolving the operand; take the quick way out and // just let the (already erronous) subexpression be what the negation expression // will resolve to @@ -7074,7 +6958,7 @@ namespace Microsoft.Dafny } } else if (expr is ThisExpr) { if (!scope.AllowInstance) { - Error(expr, "'this' is not allowed in a 'static' context"); + 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 @@ -7086,19 +6970,19 @@ namespace Microsoft.Dafny if (e.Var != null) { expr.Type = e.Var.Type; } else { - Error(expr, "Identifier does not denote a local variable, parameter, or bound variable: {0}", e.Name); + reporter.Error(MessageSource.Resolver, expr, "Identifier does not denote a local variable, parameter, or bound variable: {0}", e.Name); } } else if (expr is DatatypeValue) { DatatypeValue dtv = (DatatypeValue)expr; TopLevelDecl d; if (!moduleInfo.TopLevels.TryGetValue(dtv.DatatypeName, out d)) { - Error(expr.tok, "Undeclared datatype: {0}", dtv.DatatypeName); + reporter.Error(MessageSource.Resolver, expr.tok, "Undeclared datatype: {0}", dtv.DatatypeName); } else if (d is AmbiguousTopLevelDecl) { var ad = (AmbiguousTopLevelDecl)d; - Error(expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", dtv.DatatypeName, ad.ModuleNames()); + reporter.Error(MessageSource.Resolver, expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", dtv.DatatypeName, ad.ModuleNames()); } else if (!(d is DatatypeDecl)) { - Error(expr.tok, "Expected datatype: {0}", dtv.DatatypeName); + reporter.Error(MessageSource.Resolver, expr.tok, "Expected datatype: {0}", dtv.DatatypeName); } else { ResolveDatatypeValue(opts, dtv, (DatatypeDecl)d); } @@ -7110,7 +6994,7 @@ namespace Microsoft.Dafny ResolveExpression(ee, opts); Contract.Assert(ee.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(elementType, ee.Type)) { - Error(ee, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", ee.Type, elementType); + reporter.Error(MessageSource.Resolver, ee, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", ee.Type, elementType); } } if (expr is SetDisplayExpr) { @@ -7129,12 +7013,12 @@ namespace Microsoft.Dafny ResolveExpression(p.A, opts); Contract.Assert(p.A.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(domainType, p.A.Type)) { - Error(p.A, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", p.A.Type, domainType); + reporter.Error(MessageSource.Resolver, p.A, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", p.A.Type, domainType); } ResolveExpression(p.B, opts); Contract.Assert(p.B.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(rangeType, p.B.Type)) { - Error(p.B, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", p.B.Type, rangeType); + reporter.Error(MessageSource.Resolver, p.B, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", p.B.Type, rangeType); } } expr.Type = new MapType(e.Finite, domainType, rangeType); @@ -7142,18 +7026,18 @@ namespace Microsoft.Dafny var e = (NameSegment)expr; ResolveNameSegment(e, true, null, opts, false); if (e.Type is Resolver_IdentifierExpr.ResolverType_Module) { - Error(e.tok, "name of module ({0}) is used as a variable", e.Name); + reporter.Error(MessageSource.Resolver, e.tok, "name of module ({0}) is used as a variable", e.Name); } else if (e.Type is Resolver_IdentifierExpr.ResolverType_Type) { - Error(e.tok, "name of type ({0}) is used as a variable", e.Name); + reporter.Error(MessageSource.Resolver, e.tok, "name of type ({0}) is used as a variable", e.Name); } } else if (expr is ExprDotName) { var e = (ExprDotName)expr; ResolveDotSuffix(e, true, null, opts, false); if (e.Type is Resolver_IdentifierExpr.ResolverType_Module) { - Error(e.tok, "name of module ({0}) is used as a variable", e.SuffixName); + reporter.Error(MessageSource.Resolver, e.tok, "name of module ({0}) is used as a variable", e.SuffixName); } else if (e.Type is Resolver_IdentifierExpr.ResolverType_Type) { - Error(e.tok, "name of type ({0}) is used as a variable", e.SuffixName); + reporter.Error(MessageSource.Resolver, e.tok, "name of type ({0}) is used as a variable", e.SuffixName); } } else if (expr is ApplySuffix) { @@ -7193,7 +7077,7 @@ namespace Microsoft.Dafny var field = (Field)member; e.Member = field; if (e.Obj is StaticReceiverExpr) { - Error(expr, "a field must be selected via an object, not just a class name"); + reporter.Error(MessageSource.Resolver, expr, "a field must be selected via an object, not just a class name"); } var ctype = nptype as UserDefinedType; if (ctype == null) { @@ -7205,7 +7089,7 @@ namespace Microsoft.Dafny e.Type = SubstType(field.Type, subst); } } else { - Error(expr, "member {0} in type {1} does not refer to a field or a function", e.MemberName, nptype); + reporter.Error(MessageSource.Resolver, expr, "member {0} in type {1} does not refer to a field or a function", e.MemberName, nptype); } } else if (expr is SeqSelectExpr) { @@ -7219,7 +7103,7 @@ namespace Microsoft.Dafny Contract.Assert(e.Array.Type != null); // follows from postcondition of ResolveExpression Type elementType = new InferredTypeProxy(); if (!UnifyTypes(e.Array.Type, ResolvedArrayType(e.Array.tok, e.Indices.Count, elementType, opts.codeContext))) { - Error(e.Array, "array selection requires an array{0} (got {1})", e.Indices.Count, e.Array.Type); + reporter.Error(MessageSource.Resolver, e.Array, "array selection requires an array{0} (got {1})", e.Indices.Count, e.Array.Type); } int i = 0; foreach (Expression idx in e.Indices) { @@ -7227,7 +7111,7 @@ namespace Microsoft.Dafny ResolveExpression(idx, opts); Contract.Assert(idx.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(idx.Type, new OperationTypeProxy(true, false, false, false, false, false))) { - Error(idx, "array selection requires integer-based numeric indices (got {0} for index {1})", idx.Type, i); + reporter.Error(MessageSource.Resolver, idx, "array selection requires integer-based numeric indices (got {0} for index {1})", idx.Type, i); } i++; } @@ -7244,42 +7128,42 @@ namespace Microsoft.Dafny ResolveExpression(e.Index, opts); Contract.Assert(e.Index.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(e.Index.Type, Type.Int)) { - Error(e.Index, "sequence update requires integer index (got {0})", e.Index.Type); + reporter.Error(MessageSource.Resolver, e.Index, "sequence update requires integer index (got {0})", e.Index.Type); } ResolveExpression(e.Value, opts); Contract.Assert(e.Value.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(e.Value.Type, elementType)) { - Error(e.Value, "sequence update requires the value to have the element type of the sequence (got {0})", e.Value.Type); + reporter.Error(MessageSource.Resolver, e.Value, "sequence update requires the value to have the element type of the sequence (got {0})", e.Value.Type); } expr.Type = e.Seq.Type; } else if (UnifyTypes(e.Seq.Type, new MapType(true, domainType, rangeType))) { ResolveExpression(e.Index, opts); if (!UnifyTypes(e.Index.Type, domainType)) { - Error(e.Index, "map update requires domain element to be of type {0} (got {1})", domainType, e.Index.Type); + reporter.Error(MessageSource.Resolver, e.Index, "map update requires domain element to be of type {0} (got {1})", domainType, e.Index.Type); } ResolveExpression(e.Value, opts); if (!UnifyTypes(e.Value.Type, rangeType)) { - Error(e.Value, "map update requires the value to have the range type {0} (got {1})", rangeType, e.Value.Type); + reporter.Error(MessageSource.Resolver, e.Value, "map update requires the value to have the range type {0} (got {1})", rangeType, e.Value.Type); } expr.Type = e.Seq.Type; } else if (UnifyTypes(e.Seq.Type, new MapType(false, domainType, rangeType))) { ResolveExpression(e.Index, opts); if (!UnifyTypes(e.Index.Type, domainType)) { - Error(e.Index, "imap update requires domain element to be of type {0} (got {1})", domainType, e.Index.Type); + reporter.Error(MessageSource.Resolver, e.Index, "imap update requires domain element to be of type {0} (got {1})", domainType, e.Index.Type); } ResolveExpression(e.Value, opts); if (!UnifyTypes(e.Value.Type, rangeType)) { - Error(e.Value, "imap update requires the value to have the range type {0} (got {1})", rangeType, e.Value.Type); + reporter.Error(MessageSource.Resolver, e.Value, "imap update requires the value to have the range type {0} (got {1})", rangeType, e.Value.Type); } expr.Type = e.Seq.Type; } else if (UnifyTypes(e.Seq.Type, new MultiSetType(elementType))) { ResolveExpression(e.Index, opts); if (!UnifyTypes(e.Index.Type, elementType)) { - Error(e.Index, "multiset update requires domain element to be of type {0} (got {1})", elementType, e.Index.Type); + reporter.Error(MessageSource.Resolver, e.Index, "multiset update requires domain element to be of type {0} (got {1})", elementType, e.Index.Type); } ResolveExpression(e.Value, opts); if (!UnifyTypes(e.Value.Type, new OperationTypeProxy(true, false, false, false, false, false))) { - Error(e.Value, "multiset update requires integer-based numeric value (got {0})", e.Value.Type); + reporter.Error(MessageSource.Resolver, e.Value, "multiset update requires integer-based numeric value (got {0})", e.Value.Type); } expr.Type = e.Seq.Type; @@ -7297,7 +7181,7 @@ namespace Microsoft.Dafny var ei = (SeqUpdateExpr)eIter; if (!(ei.Index is NameSegment|| (ei.Index is LiteralExpr && ((LiteralExpr)ei.Index).Value is BigInteger))) { - Error(expr, "datatype updates must be to datatype destructors"); + reporter.Error(MessageSource.Resolver, expr, "datatype updates must be to datatype destructors"); } else { string destructor_str = null; @@ -7313,12 +7197,12 @@ namespace Microsoft.Dafny if (IndexToValue.ContainsKey(destructor_str)) { // Don't bother trying to optimize this case; we'd have to drop one of the updates, // which makes resolving the dropped update an annoyance. - Warning(ei.tok, "update to {0} overwritten by another update to {1}", destructor_str, destructor_str); + reporter.Warning(MessageSource.Resolver, ei.tok, "update to {0} overwritten by another update to {1}", destructor_str, destructor_str); break; } MemberDecl member; if (!datatypeMembers[dt].TryGetValue(destructor_str, out member)) { - Error(expr, "member {0} does not exist in datatype {1}", destructor_str, dt.Name); + reporter.Error(MessageSource.Resolver, expr, "member {0} does not exist in datatype {1}", destructor_str, dt.Name); } else { DatatypeDestructor destructor = (DatatypeDestructor)member; if (ctor != null && ctor != destructor.EnclosingCtor) { @@ -7367,7 +7251,7 @@ namespace Microsoft.Dafny expr.Type = e0.Type; } else { - Error(expr, "update requires a sequence, map, or datatype (got {0})", e.Seq.Type); + reporter.Error(MessageSource.Resolver, expr, "update requires a sequence, map, or datatype (got {0})", e.Seq.Type); } } else if (expr is FunctionCallExpr) { @@ -7382,13 +7266,13 @@ namespace Microsoft.Dafny } var fnType = e.Function.Type.AsArrowType; if (fnType == null) { - Error(e.tok, "non-function expression (of type {0}) is called with parameters", e.Function.Type); + reporter.Error(MessageSource.Resolver, e.tok, "non-function expression (of type {0}) is called with parameters", e.Function.Type); } else if (fnType.Arity != e.Args.Count) { - Error(e.tok, "wrong number of arguments to function application (function type '{0}' expects {1}, got {2})", fnType, fnType.Arity, e.Args.Count); + reporter.Error(MessageSource.Resolver, e.tok, "wrong number of arguments to function application (function type '{0}' expects {1}, got {2})", fnType, fnType.Arity, e.Args.Count); } else { for (var i = 0; i < fnType.Arity; i++) { if (!UnifyTypes(fnType.Args[i], e.Args[i].Type)) { - Error(e.Args[i].tok, "type mismatch for argument {0} (function expects {1}, got {2})", i, fnType.Args[i], e.Args[i].Type); + reporter.Error(MessageSource.Resolver, e.Args[i].tok, "type mismatch for argument {0} (function expects {1}, got {2})", i, fnType.Args[i], e.Args[i].Type); } } } @@ -7397,7 +7281,7 @@ namespace Microsoft.Dafny } else if (expr is OldExpr) { OldExpr e = (OldExpr)expr; if (!opts.twoState) { - Error(expr, "old expressions are not allowed in this context"); + reporter.Error(MessageSource.Resolver, expr, "old expressions are not allowed in this context"); } ResolveExpression(e.E, opts); expr.Type = e.E.Type; @@ -7406,7 +7290,7 @@ namespace Microsoft.Dafny MultiSetFormingExpr e = (MultiSetFormingExpr)expr; ResolveExpression(e.E, opts); if (!UnifyTypes(e.E.Type, new SetType(true, new InferredTypeProxy())) && !UnifyTypes(e.E.Type, new SeqType(new InferredTypeProxy()))) { - Error(e.tok, "can only form a multiset from a seq or set."); + reporter.Error(MessageSource.Resolver, e.tok, "can only form a multiset from a seq or set."); } expr.Type = new MultiSetType(e.E.Type.AsCollectionType.Arg); @@ -7417,19 +7301,19 @@ namespace Microsoft.Dafny switch (e.Op) { case UnaryOpExpr.Opcode.Not: if (!UnifyTypes(e.E.Type, Type.Bool)) { - Error(expr, "logical negation expects a boolean argument (instead got {0})", e.E.Type); + reporter.Error(MessageSource.Resolver, expr, "logical negation expects a boolean argument (instead got {0})", e.E.Type); } expr.Type = Type.Bool; break; case UnaryOpExpr.Opcode.Cardinality: if (!UnifyTypes(e.E.Type, new CollectionTypeProxy(new InferredTypeProxy(), false, false))) { - Error(expr, "size operator expects a collection argument (instead got {0})", e.E.Type); + reporter.Error(MessageSource.Resolver, expr, "size operator expects a collection argument (instead got {0})", e.E.Type); } expr.Type = Type.Int; break; case UnaryOpExpr.Opcode.Fresh: if (!opts.twoState) { - Error(expr, "fresh expressions are not allowed in this context"); + reporter.Error(MessageSource.Resolver, expr, "fresh expressions are not allowed in this context"); } // the type of e.E must be either an object or a collection of objects Type t = e.E.Type.NormalizeExpand(); @@ -7444,7 +7328,7 @@ namespace Microsoft.Dafny } else if (t.IsDatatype) { // fine, treat this as the datatype itself. } else { - Error(expr, "the argument of a fresh expression must denote an object or a collection of objects (instead got {0})", e.E.Type); + reporter.Error(MessageSource.Resolver, expr, "the argument of a fresh expression must denote an object or a collection of objects (instead got {0})", e.E.Type); } expr.Type = Type.Bool; break; @@ -7458,14 +7342,14 @@ namespace Microsoft.Dafny ResolveExpression(e.E, opts); if (e.ToType.IsNumericBased(Type.NumericPersuation.Int)) { if (!UnifyTypes(e.E.Type, new OperationTypeProxy(true, true, false, false, false, false))) { - Error(expr, "type conversion to an int-based type is allowed only from numeric types (got {0})", e.E.Type); + reporter.Error(MessageSource.Resolver, expr, "type conversion to an int-based type is allowed only from numeric types (got {0})", e.E.Type); } } else if (e.ToType.IsNumericBased(Type.NumericPersuation.Real)) { if (!UnifyTypes(e.E.Type, new OperationTypeProxy(true, true, false, false, false, false))) { - Error(expr, "type conversion to a real-based type is allowed only from numeric types (got {0})", e.E.Type); + reporter.Error(MessageSource.Resolver, expr, "type conversion to a real-based type is allowed only from numeric types (got {0})", e.E.Type); } } else { - Error(expr, "type conversions are not supported to this type (got {0})", e.ToType); + reporter.Error(MessageSource.Resolver, expr, "type conversions are not supported to this type (got {0})", e.ToType); } e.Type = e.ToType; @@ -7482,10 +7366,10 @@ namespace Microsoft.Dafny case BinaryExpr.Opcode.And: case BinaryExpr.Opcode.Or: if (!UnifyTypes(e.E0.Type, Type.Bool)) { - Error(expr, "first argument to {0} must be of type bool (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type); + reporter.Error(MessageSource.Resolver, expr, "first argument to {0} must be of type bool (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type); } if (!UnifyTypes(e.E1.Type, Type.Bool)) { - Error(expr, "second argument to {0} must be of type bool (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E1.Type); + reporter.Error(MessageSource.Resolver, expr, "second argument to {0} must be of type bool (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E1.Type); } expr.Type = Type.Bool; break; @@ -7506,7 +7390,7 @@ namespace Microsoft.Dafny // unification will succeed. } else { // The types are not comparable and do not unify. It's time for an error message. - Error(expr, "arguments must have the same type (got {0} and {1})", e.E0.Type, e.E1.Type); + reporter.Error(MessageSource.Resolver, expr, "arguments must have the same type (got {0} and {1})", e.E0.Type, e.E1.Type); } expr.Type = Type.Bool; break; @@ -7514,12 +7398,12 @@ namespace Microsoft.Dafny case BinaryExpr.Opcode.Disjoint: // TODO: the error messages are backwards from what (ideally) they should be. this is necessary because UnifyTypes can't backtrack. if (!UnifyTypes(e.E0.Type, e.E1.Type)) { - Error(expr, "arguments must have the same type (got {0} and {1})", e.E0.Type, e.E1.Type); + reporter.Error(MessageSource.Resolver, expr, "arguments must have the same type (got {0} and {1})", e.E0.Type, e.E1.Type); } if (!UnifyTypes(e.E0.Type, new SetType(true, new InferredTypeProxy())) && !UnifyTypes(e.E0.Type, new MultiSetType(new InferredTypeProxy())) && !UnifyTypes(e.E0.Type, new MapType(true, new InferredTypeProxy(), new InferredTypeProxy()))) { - Error(expr, "arguments must be of a [multi]set or map type (got {0})", e.E0.Type); + reporter.Error(MessageSource.Resolver, expr, "arguments must be of a [multi]set or map type (got {0})", e.E0.Type); } expr.Type = Type.Bool; break; @@ -7531,26 +7415,26 @@ namespace Microsoft.Dafny if (UnifyTypes(e.E1.Type, new DatatypeProxy(false, false))) { e.ResolvedOp = BinaryExpr.ResolvedOpcode.RankLt; } else { - Error(expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E1.Type); + reporter.Error(MessageSource.Resolver, expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E1.Type); } expr.Type = Type.Bool; } else if (e.Op == BinaryExpr.Opcode.Lt && e.E1.Type.NormalizeExpand().IsIndDatatype) { if (UnifyTypes(e.E0.Type, new DatatypeProxy(false, true))) { e.ResolvedOp = BinaryExpr.ResolvedOpcode.RankLt; } else { - Error(expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E0.Type); + reporter.Error(MessageSource.Resolver, expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E0.Type); } expr.Type = Type.Bool; } else { bool err = false; bool isComparison = e.Op != BinaryExpr.Opcode.Add; if (!UnifyTypes(e.E0.Type, new OperationTypeProxy(true, true, isComparison, true, true, true))) { - Error(expr, "arguments to {0} must be of a numeric type{2} or a collection type (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, + reporter.Error(MessageSource.Resolver, expr, "arguments to {0} must be of a numeric type{2} or a collection type (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, isComparison ? ", char," : ""); err = true; } if (!UnifyTypes(e.E1.Type, e.E0.Type)) { - Error(expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type); + reporter.Error(MessageSource.Resolver, expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type); err = true; } if (isComparison) { @@ -7570,26 +7454,26 @@ namespace Microsoft.Dafny if (UnifyTypes(e.E1.Type, new DatatypeProxy(false, true))) { e.ResolvedOp = BinaryExpr.ResolvedOpcode.RankGt; } else { - Error(expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E1.Type); + reporter.Error(MessageSource.Resolver, expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E1.Type); } expr.Type = Type.Bool; } else if (e.Op == BinaryExpr.Opcode.Gt && (e.E1.Type.NormalizeExpand().IsIndDatatype || e.E1.Type.IsTypeParameter)) { if (UnifyTypes(e.E0.Type, new DatatypeProxy(false, false))) { e.ResolvedOp = BinaryExpr.ResolvedOpcode.RankGt; } else { - Error(expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E0.Type); + reporter.Error(MessageSource.Resolver, expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E0.Type); } expr.Type = Type.Bool; } else { bool err = false; bool isComparison = e.Op == BinaryExpr.Opcode.Gt || e.Op == BinaryExpr.Opcode.Ge; if (!UnifyTypes(e.E0.Type, new OperationTypeProxy(true, true, isComparison, false, true, true))) { - Error(expr, "arguments to {0} must be of a numeric type{2} or a set type (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, + reporter.Error(MessageSource.Resolver, expr, "arguments to {0} must be of a numeric type{2} or a set type (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, isComparison ? ", char, " : ""); err = true; } if (!UnifyTypes(e.E1.Type, e.E0.Type)) { - Error(expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type); + reporter.Error(MessageSource.Resolver, expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type); err = true; } if (isComparison) { @@ -7604,27 +7488,27 @@ namespace Microsoft.Dafny case BinaryExpr.Opcode.In: case BinaryExpr.Opcode.NotIn: if (!UnifyTypes(e.E1.Type, new CollectionTypeProxy(e.E0.Type, true, true))) { - Error(expr, "second argument to \"{0}\" must be a set, multiset, or sequence with elements of type {1}, or a map with domain {1} (instead got {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type); + reporter.Error(MessageSource.Resolver, expr, "second argument to \"{0}\" must be a set, multiset, or sequence with elements of type {1}, or a map with domain {1} (instead got {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type); } expr.Type = Type.Bool; break; case BinaryExpr.Opcode.Div: if (!UnifyTypes(e.E0.Type, new OperationTypeProxy(true, true, false, false, false, false))) { - Error(expr, "first argument to {0} must be of numeric type (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type); + reporter.Error(MessageSource.Resolver, expr, "first argument to {0} must be of numeric type (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type); } if (!UnifyTypes(e.E1.Type, e.E0.Type)) { - Error(expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type); + reporter.Error(MessageSource.Resolver, expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type); } expr.Type = e.E0.Type; break; case BinaryExpr.Opcode.Mod: if (!UnifyTypes(e.E0.Type, new OperationTypeProxy(true, false, false, false, false, false))) { - Error(expr, "first argument to {0} must be of type int (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type); + reporter.Error(MessageSource.Resolver, expr, "first argument to {0} must be of type int (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type); } if (!UnifyTypes(e.E1.Type, e.E0.Type)) { - Error(expr, "second argument to {0} must be of type int (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E1.Type); + reporter.Error(MessageSource.Resolver, expr, "second argument to {0} must be of type int (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E1.Type); } expr.Type = e.E0.Type; break; @@ -7644,13 +7528,13 @@ namespace Microsoft.Dafny case TernaryExpr.Opcode.PrefixEqOp: case TernaryExpr.Opcode.PrefixNeqOp: if (!UnifyTypes(e.E0.Type, Type.Int)) { - Error(e.E0, "prefix-equality limit argument must be an integer expression (got {0})", e.E0.Type); + reporter.Error(MessageSource.Resolver, e.E0, "prefix-equality limit argument must be an integer expression (got {0})", e.E0.Type); } if (!UnifyTypes(e.E1.Type, new DatatypeProxy(true))) { - Error(expr, "arguments to prefix equality must be codatatypes (instead of {0})", e.E1.Type); + reporter.Error(MessageSource.Resolver, expr, "arguments to prefix equality must be codatatypes (instead of {0})", e.E1.Type); } if (!UnifyTypes(e.E1.Type, e.E2.Type)) { - Error(expr, "arguments must have the same type (got {0} and {1})", e.E1.Type, e.E2.Type); + reporter.Error(MessageSource.Resolver, expr, "arguments must have the same type (got {0} and {1})", e.E1.Type, e.E2.Type); } expr.Type = Type.Bool; break; @@ -7667,7 +7551,7 @@ namespace Microsoft.Dafny } scope.PushMarker(); if (e.LHSs.Count != e.RHSs.Count) { - Error(expr, "let expression must have same number of LHSs (found {0}) as RHSs (found {1})", e.LHSs.Count, e.RHSs.Count); + reporter.Error(MessageSource.Resolver, expr, "let expression must have same number of LHSs (found {0}) as RHSs (found {1})", e.LHSs.Count, e.RHSs.Count); } var i = 0; foreach (var lhs in e.LHSs) { @@ -7681,14 +7565,14 @@ namespace Microsoft.Dafny } if (c == 0) { // Every identifier-looking thing in the pattern resolved to a constructor; that is, this LHS is a constant literal - Error(lhs.tok, "LHS is a constant literal; to be legal, it must introduce at least one bound variable"); + reporter.Error(MessageSource.Resolver, lhs.tok, "LHS is a constant literal; to be legal, it must introduce at least one bound variable"); } i++; } } else { // let-such-that expression if (e.RHSs.Count != 1) { - Error(expr, "let-such-that expression must have just one RHS (found {0})", e.RHSs.Count); + reporter.Error(MessageSource.Resolver, expr, "let-such-that expression must have just one RHS (found {0})", e.RHSs.Count); } // the bound variables are in scope in the RHS of a let-such-that expression scope.PushMarker(); @@ -7701,7 +7585,7 @@ namespace Microsoft.Dafny foreach (var rhs in e.RHSs) { ResolveExpression(rhs, opts); if (!UnifyTypes(rhs.Type, Type.Bool)) { - Error(rhs.tok, "type of RHS of let-such-that expression must be boolean (got {0})", rhs.Type); + reporter.Error(MessageSource.Resolver, 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)) { @@ -7720,7 +7604,7 @@ namespace Microsoft.Dafny e.Type = e.Body.Type; } else if (expr is QuantifierExpr) { QuantifierExpr e = (QuantifierExpr)expr; - int prevErrorCount = ErrorCount; + int prevErrorCount = reporter.Count(ErrorLevel.Error); bool _val = true; bool typeQuantifier = Attributes.ContainsBool(e.Attributes, "typeQuantifier", ref _val) && _val; allTypeParameters.PushMarker(); @@ -7732,19 +7616,19 @@ namespace Microsoft.Dafny ResolveType(v.tok, v.Type, opts.codeContext, option, typeQuantifier ? e.TypeArgs : null); } if (e.TypeArgs.Count > 0 && !typeQuantifier) { - Error(expr, "a quantifier cannot quantify over types. Possible fix: use the experimental attribute :typeQuantifier"); + reporter.Error(MessageSource.Resolver, expr, "a quantifier cannot quantify over types. Possible fix: use the experimental attribute :typeQuantifier"); } if (e.Range != null) { ResolveExpression(e.Range, new ResolveOpts(opts, true)); Contract.Assert(e.Range.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(e.Range.Type, Type.Bool)) { - Error(expr, "range of quantifier must be of type bool (instead got {0})", e.Range.Type); + reporter.Error(MessageSource.Resolver, expr, "range of quantifier must be of type bool (instead got {0})", e.Range.Type); } } ResolveExpression(e.Term, new ResolveOpts(opts, true)); Contract.Assert(e.Term.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(e.Term.Type, Type.Bool)) { - Error(expr, "body of quantifier must be of type bool (instead got {0})", e.Term.Type); + reporter.Error(MessageSource.Resolver, expr, "body of quantifier must be of type bool (instead got {0})", e.Term.Type); } // Since the body is more likely to infer the types of the bound variables, resolve it // first (above) and only then resolve the attributes (below). @@ -7753,10 +7637,10 @@ namespace Microsoft.Dafny allTypeParameters.PopMarker(); expr.Type = Type.Bool; - if (prevErrorCount == ErrorCount) { + if (prevErrorCount == reporter.Count(ErrorLevel.Error)) { 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); + e.Bounds = DiscoverBounds(e.tok, e.BoundVars, e.LogicalBody(), e is ExistsExpr, false, missingBounds, reporter); if (missingBounds.Count != 0) { // Report errors here about quantifications that depend on the allocation state. var mb = missingBounds; @@ -7764,7 +7648,7 @@ namespace Microsoft.Dafny 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) { - Error(expr, "a quantifier involved in a function definition is not allowed to depend on the set of allocated references; Dafny's heuristics can't figure out a bound for the values of '{0}'", bv.Name); + 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); } @@ -7778,7 +7662,7 @@ namespace Microsoft.Dafny } else if (expr is SetComprehension) { var e = (SetComprehension)expr; - int prevErrorCount = ErrorCount; + int prevErrorCount = reporter.Count(ErrorLevel.Error); scope.PushMarker(); foreach (BoundVar v in e.BoundVars) { ScopePushAndReport(scope, v, "bound-variable"); @@ -7787,7 +7671,7 @@ namespace Microsoft.Dafny ResolveExpression(e.Range, opts); Contract.Assert(e.Range.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(e.Range.Type, Type.Bool)) { - Error(expr, "range of comprehension must be of type bool (instead got {0})", e.Range.Type); + reporter.Error(MessageSource.Resolver, expr, "range of comprehension must be of type bool (instead got {0})", e.Range.Type); } ResolveExpression(e.Term, opts); Contract.Assert(e.Term.Type != null); // follows from postcondition of ResolveExpression @@ -7804,10 +7688,10 @@ namespace Microsoft.Dafny } else if (expr is MapComprehension) { var e = (MapComprehension)expr; - int prevErrorCount = ErrorCount; + int prevErrorCount = reporter.Count(ErrorLevel.Error); scope.PushMarker(); if (e.BoundVars.Count != 1) { - Error(e.tok, "a map comprehension must have exactly one bound variable."); + reporter.Error(MessageSource.Resolver, e.tok, "a map comprehension must have exactly one bound variable."); } foreach (BoundVar v in e.BoundVars) { ScopePushAndReport(scope, v, "bound-variable"); @@ -7816,7 +7700,7 @@ namespace Microsoft.Dafny ResolveExpression(e.Range, opts); Contract.Assert(e.Range.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(e.Range.Type, Type.Bool)) { - Error(expr, "range of comprehension must be of type bool (instead got {0})", e.Range.Type); + reporter.Error(MessageSource.Resolver, expr, "range of comprehension must be of type bool (instead got {0})", e.Range.Type); } ResolveExpression(e.Term, opts); Contract.Assert(e.Term.Type != null); // follows from postcondition of ResolveExpression @@ -7825,22 +7709,22 @@ namespace Microsoft.Dafny scope.PopMarker(); expr.Type = new MapType(e.Finite, e.BoundVars[0].Type, e.Term.Type); - if (prevErrorCount == ErrorCount) { + if (prevErrorCount == reporter.Count(ErrorLevel.Error)) { 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); + e.Bounds = DiscoverBounds(e.tok, e.BoundVars, e.Range, true, false, missingBounds, reporter); if (missingBounds.Count != 0) { e.MissingBounds = missingBounds; if (e.Finite) { foreach (var bv in e.MissingBounds) { - Error(expr, "a map comprehension must produce a finite domain, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name); + reporter.Error(MessageSource.Resolver, expr, "a map comprehension must produce a finite domain, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name); } } } } } else if (expr is LambdaExpr) { var e = (LambdaExpr)expr; - int prevErrorCount = ErrorCount; + int prevErrorCount = reporter.Count(ErrorLevel.Error); scope.PushMarker(); foreach (BoundVar v in e.BoundVars) { ScopePushAndReport(scope, v, "bound-variable"); @@ -7851,7 +7735,7 @@ namespace Microsoft.Dafny ResolveExpression(e.Range, opts); Contract.Assert(e.Range.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(e.Range.Type, Type.Bool)) { - Error(expr, "Precondition must be boolean (got {0})", e.Range.Type); + reporter.Error(MessageSource.Resolver, expr, "Precondition must be boolean (got {0})", e.Range.Type); } } @@ -7867,14 +7751,14 @@ namespace Microsoft.Dafny expr.Type = new SetType(true, new ObjectType()); } else if (expr is StmtExpr) { var e = (StmtExpr)expr; - int prevErrorCount = ErrorCount; + int prevErrorCount = reporter.Count(ErrorLevel.Error); ResolveStatement(e.S, true, opts.codeContext); - if (ErrorCount == prevErrorCount) { + if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { var r = e.S as UpdateStmt; if (r != null && r.ResolvedStatements.Count == 1) { var call = r.ResolvedStatements[0] as CallStmt; if (call.Method.Mod.Expressions.Count != 0) { - Error(call, "calls to methods with side-effects are not allowed inside a statement expression"); + reporter.Error(MessageSource.Resolver, call, "calls to methods with side-effects are not allowed inside a statement expression"); } } } @@ -7891,12 +7775,12 @@ namespace Microsoft.Dafny ResolveExpression(e.Els, opts); Contract.Assert(e.Els.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(e.Test.Type, Type.Bool)) { - Error(expr, "guard condition in if-then-else expression must be a boolean (instead got {0})", e.Test.Type); + reporter.Error(MessageSource.Resolver, expr, "guard condition in if-then-else expression must be a boolean (instead got {0})", e.Test.Type); } if (UnifyTypes(e.Thn.Type, e.Els.Type)) { expr.Type = e.Thn.Type; } else { - Error(expr, "the two branches of an if-then-else expression must have the same type (got {0} and {1})", e.Thn.Type, e.Els.Type); + reporter.Error(MessageSource.Resolver, expr, "the two branches of an if-then-else expression must have the same type (got {0} and {1})", e.Thn.Type, e.Els.Type); } } else if (expr is MatchExpr) { @@ -7926,7 +7810,7 @@ namespace Microsoft.Dafny var subst = new Dictionary(); Dictionary ctors; if (dtd == null) { - Error(me.Source, "the type of the match source expression must be a datatype (instead found {0})", me.Source.Type); + reporter.Error(MessageSource.Resolver, me.Source, "the type of the match source expression must be a datatype (instead found {0})", me.Source.Type); ctors = null; } else { Contract.Assert(sourceType != null); // dtd and sourceType are set together above @@ -7955,15 +7839,15 @@ namespace Microsoft.Dafny if (ctors != null) { Contract.Assert(dtd != null); if (!ctors.TryGetValue(mc.Id, out ctor)) { - Error(mc.tok, "member {0} does not exist in datatype {1}", mc.Id, dtd.Name); + reporter.Error(MessageSource.Resolver, mc.tok, "member {0} does not exist in datatype {1}", mc.Id, dtd.Name); } else { Contract.Assert(ctor != null); // follows from postcondition of TryGetValue mc.Ctor = ctor; if (ctor.Formals.Count != mc.Arguments.Count) { - Error(mc.tok, "member {0} has wrong number of formals (found {1}, expected {2})", mc.Id, mc.Arguments.Count, ctor.Formals.Count); + reporter.Error(MessageSource.Resolver, mc.tok, "member {0} has wrong number of formals (found {1}, expected {2})", mc.Id, mc.Arguments.Count, ctor.Formals.Count); } if (memberNamesUsed.Contains(mc.Id)) { - Error(mc.tok, "member {0} appears in more than one case", mc.Id); + reporter.Error(MessageSource.Resolver, mc.tok, "member {0} appears in more than one case", mc.Id); } else { memberNamesUsed.Add(mc.Id); // add mc.Id to the set of names used } @@ -7979,7 +7863,7 @@ namespace Microsoft.Dafny Formal formal = ctor.Formals[i]; Type st = SubstType(formal.Type, subst); if (!UnifyTypes(v.Type, st)) { - Error(expr, "the declared type of the formal ({0}) does not agree with the corresponding type in the constructor's signature ({1})", v.Type, st); + reporter.Error(MessageSource.Resolver, expr, "the declared type of the formal ({0}) does not agree with the corresponding type in the constructor's signature ({1})", v.Type, st); } v.IsGhost = formal.IsGhost; @@ -8006,7 +7890,7 @@ namespace Microsoft.Dafny Contract.Assert(mc.Body.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(expr.Type, mc.Body.Type)) { - Error(mc.Body.tok, "type of case bodies do not agree (found {0}, previous types {1})", mc.Body.Type, expr.Type); + reporter.Error(MessageSource.Resolver, mc.Body.tok, "type of case bodies do not agree (found {0}, previous types {1})", mc.Body.Type, expr.Type); } scope.PopMarker(); } @@ -8044,13 +7928,13 @@ namespace Microsoft.Dafny if (me.Source is DatatypeValue) { var e = (DatatypeValue)me.Source; if (e.Arguments.Count < 1) { - Error(me.tok, "match source tuple needs at least 1 argument"); + reporter.Error(MessageSource.Resolver, me.tok, "match source tuple needs at least 1 argument"); } else { Expression source = e.Arguments[0]; List cases = new List(); foreach (MatchCaseExpr mc in me.Cases) { if (mc.CasePatterns == null || mc.CasePatterns.Count != e.Arguments.Count) { - Error(mc.tok, "case arguments count does not match source arguments count"); + reporter.Error(MessageSource.Resolver, mc.tok, "case arguments count does not match source arguments count"); } else { CasePattern cp = mc.CasePatterns[0]; List patterns; @@ -8342,17 +8226,17 @@ namespace Microsoft.Dafny var v = pat.Var; ResolveType(v.tok, v.Type, context, ResolveTypeOptionEnum.InferTypeProxies, null); if (!UnifyTypes(v.Type, sourceType)) { - Error(v.tok, "type of corresponding source/RHS ({0}) does not match type of bound variable ({1})", sourceType, v.Type); + reporter.Error(MessageSource.Resolver, v.tok, "type of corresponding source/RHS ({0}) does not match type of bound variable ({1})", sourceType, v.Type); } pat.AssembleExpr(null); } else if (dtd == null) { - Error(pat.tok, "to use a pattern, the type of the source/RHS expression must be a datatype (instead found {0})", sourceType); + reporter.Error(MessageSource.Resolver, pat.tok, "to use a pattern, the type of the source/RHS expression must be a datatype (instead found {0})", sourceType); } else if (ctor == null) { - Error(pat.tok, "constructor {0} does not exist in datatype {1}", pat.Id, dtd.Name); + reporter.Error(MessageSource.Resolver, pat.tok, "constructor {0} does not exist in datatype {1}", pat.Id, dtd.Name); } else { var argCount = pat.Arguments == null ? 0 : pat.Arguments.Count; if (ctor.Formals.Count != argCount) { - Error(pat.tok, "pattern for constructor {0} has wrong number of formals (found {1}, expected {2})", pat.Id, argCount, ctor.Formals.Count); + reporter.Error(MessageSource.Resolver, pat.tok, "pattern for constructor {0} has wrong number of formals (found {1}, expected {2})", pat.Id, argCount, ctor.Formals.Count); } // build the type-parameter substitution map for this use of the datatype Contract.Assert(dtd.TypeArgs.Count == udt.TypeArgs.Count); // follows from the type previously having been successfully resolved @@ -8408,7 +8292,7 @@ namespace Microsoft.Dafny foreach (var ty in expr.OptTypeArguments) { ResolveType(expr.tok, ty, opts.codeContext, ResolveTypeOptionEnum.InferTypeProxies, null); if (ty.IsSubrangeType) { - Error(expr.tok, "sorry, cannot instantiate type parameter with a subrange type"); + reporter.Error(MessageSource.Resolver, expr.tok, "sorry, cannot instantiate type parameter with a subrange type"); } } } @@ -8431,7 +8315,7 @@ namespace Microsoft.Dafny if (v != null) { // ----- 0. local variable, parameter, or bound variable if (expr.OptTypeArguments != null) { - Error(expr.tok, "variable '{0}' does not take any type parameters", expr.Name); + reporter.Error(MessageSource.Resolver, expr.tok, "variable '{0}' does not take any type parameters", expr.Name); } var rr = new IdentifierExpr(expr.tok, expr.Name); rr.Var = v; rr.Type = v.Type; @@ -8443,7 +8327,7 @@ namespace Microsoft.Dafny receiver = new StaticReceiverExpr(expr.tok, (ClassDecl)member.EnclosingClass, true); } else { if (!scope.AllowInstance) { - Error(expr.tok, "'this' is not allowed in a 'static' context"); //FIXME: Rephrase this + reporter.Error(MessageSource.Resolver, expr.tok, "'this' is not allowed in a 'static' context"); //FIXME: Rephrase this // nevertheless, set "receiver" to a value so we can continue resolution } receiver = new ImplicitThisExpr(expr.tok); @@ -8454,10 +8338,10 @@ namespace Microsoft.Dafny // ----- 2. datatype constructor if (pair.Item2) { // there is more than one constructor with this name - Error(expr.tok, "the name '{0}' denotes a datatype constructor, but does not do so uniquely; add an explicit qualification (for example, '{1}.{0}')", expr.Name, pair.Item1.EnclosingDatatype.Name); + reporter.Error(MessageSource.Resolver, expr.tok, "the name '{0}' denotes a datatype constructor, but does not do so uniquely; add an explicit qualification (for example, '{1}.{0}')", expr.Name, pair.Item1.EnclosingDatatype.Name); } else { if (expr.OptTypeArguments != null) { - Error(expr.tok, "datatype constructor does not take any type parameters ('{0}')", expr.Name); + reporter.Error(MessageSource.Resolver, expr.tok, "datatype constructor does not take any type parameters ('{0}')", expr.Name); } var rr = new DatatypeValue(expr.tok, pair.Item1.EnclosingDatatype.Name, expr.Name, args ?? new List()); ResolveDatatypeValue(opts, rr, pair.Item1.EnclosingDatatype); @@ -8472,7 +8356,7 @@ namespace Microsoft.Dafny // ----- 3. Member of the enclosing module if (decl is AmbiguousTopLevelDecl) { var ad = (AmbiguousTopLevelDecl)decl; - Error(expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", expr.Name, ad.ModuleNames()); + reporter.Error(MessageSource.Resolver, expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", expr.Name, ad.ModuleNames()); } else { // We have found a module name or a type name, neither of which is an expression. However, the NameSegment we're // looking at may be followed by a further suffix that makes this into an expresion. We postpone the rest of the @@ -8486,7 +8370,7 @@ namespace Microsoft.Dafny Contract.Assert(member.IsStatic); // moduleInfo.StaticMembers is supposed to contain only static members of the module's implicit class _default if (member is AmbiguousMemberDecl) { var ambiguousMember = (AmbiguousMemberDecl)member; - Error(expr.tok, "The name {0} ambiguously refers to a static member in one of the modules {1} (try qualifying the member name with the module name)", expr.Name, ambiguousMember.ModuleNames()); + reporter.Error(MessageSource.Resolver, expr.tok, "The name {0} ambiguously refers to a static member in one of the modules {1} (try qualifying the member name with the module name)", expr.Name, ambiguousMember.ModuleNames()); } else { var receiver = new StaticReceiverExpr(expr.tok, (ClassDecl)member.EnclosingClass, true); r = ResolveExprDotCall(expr.tok, receiver, member, expr.OptTypeArguments, opts.codeContext, allowMethodCall); @@ -8494,7 +8378,7 @@ namespace Microsoft.Dafny } else { // ----- None of the above - Error(expr.tok, "unresolved identifier: {0}", expr.Name); + reporter.Error(MessageSource.Resolver, expr.tok, "unresolved identifier: {0}", expr.Name); } if (r == null) { @@ -8526,7 +8410,7 @@ namespace Microsoft.Dafny foreach (var ty in expr.OptTypeArguments) { ResolveType(expr.tok, ty, opts.codeContext, option, defaultTypeArguments); if (ty.IsSubrangeType) { - Error(expr.tok, "sorry, cannot instantiate type parameter with a subrange type"); + reporter.Error(MessageSource.Resolver, expr.tok, "sorry, cannot instantiate type parameter with a subrange type"); } } } @@ -8550,7 +8434,7 @@ namespace Microsoft.Dafny if (expr.OptTypeArguments == null) { r = new Resolver_IdentifierExpr(expr.tok, tp); } else { - Error(expr.tok, "Type parameter expects no type arguments: {0}", expr.Name); + reporter.Error(MessageSource.Resolver, expr.tok, "Type parameter expects no type arguments: {0}", expr.Name); } #if ASYNC_TASK_TYPES // At the moment, there is no way for a class member to part of a type name, but this changes with async task types } else if (currentClass != null && classMembers.TryGetValue(currentClass, out members) && members.TryGetValue(expr.Name, out member)) { @@ -8572,7 +8456,7 @@ namespace Microsoft.Dafny // ----- 2. Member of the enclosing module if (decl is AmbiguousTopLevelDecl) { var ad = (AmbiguousTopLevelDecl)decl; - Error(expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", expr.Name, ad.ModuleNames()); + reporter.Error(MessageSource.Resolver, expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", expr.Name, ad.ModuleNames()); } else { // We have found a module name or a type name, neither of which is a type expression. However, the NameSegment we're // looking at may be followed by a further suffix that makes this into a type expresion. We postpone the rest of the @@ -8594,7 +8478,7 @@ namespace Microsoft.Dafny #endif } else { // ----- None of the above - Error(expr.tok, "Undeclared top-level type or type parameter: {0} (did you forget to qualify a name or declare a module import 'opened?')", expr.Name); + reporter.Error(MessageSource.Resolver, expr.tok, "Undeclared top-level type or type parameter: {0} (did you forget to qualify a name or declare a module import 'opened?')", expr.Name); } if (r == null) { @@ -8615,7 +8499,7 @@ namespace Microsoft.Dafny if (optTypeArguments != null) { // type arguments were supplied; they must be equal in number to those expected if (n != decl.TypeArgs.Count) { - Error(tok, "Wrong number of type arguments ({0} instead of {1}) passed to {2}: {3}", n, decl.TypeArgs.Count, decl.WhatKind, name); + reporter.Error(MessageSource.Resolver, tok, "Wrong number of type arguments ({0} instead of {1}) passed to {2}: {3}", n, decl.TypeArgs.Count, decl.WhatKind, name); } } List tpArgs = new List(); @@ -8667,7 +8551,7 @@ namespace Microsoft.Dafny foreach (var ty in expr.OptTypeArguments) { ResolveType(expr.tok, ty, opts.codeContext, ResolveTypeOptionEnum.InferTypeProxies, null); if (ty.IsSubrangeType) { - Error(expr.tok, "sorry, cannot instantiate type parameter with a subrange type"); + reporter.Error(MessageSource.Resolver, expr.tok, "sorry, cannot instantiate type parameter with a subrange type"); } } } @@ -8690,10 +8574,10 @@ namespace Microsoft.Dafny // ----- 0. datatype constructor if (pair.Item2) { // there is more than one constructor with this name - Error(expr.tok, "the name '{0}' denotes a datatype constructor in module {2}, but does not do so uniquely; add an explicit qualification (for example, '{1}.{0}')", expr.SuffixName, pair.Item1.EnclosingDatatype.Name, ((ModuleDecl)ri.Decl).Name); + reporter.Error(MessageSource.Resolver, expr.tok, "the name '{0}' denotes a datatype constructor in module {2}, but does not do so uniquely; add an explicit qualification (for example, '{1}.{0}')", expr.SuffixName, pair.Item1.EnclosingDatatype.Name, ((ModuleDecl)ri.Decl).Name); } else { if (expr.OptTypeArguments != null) { - Error(expr.tok, "datatype constructor does not take any type parameters ('{0}')", expr.SuffixName); + reporter.Error(MessageSource.Resolver, expr.tok, "datatype constructor does not take any type parameters ('{0}')", expr.SuffixName); } var rr = new DatatypeValue(expr.tok, pair.Item1.EnclosingDatatype.Name, expr.SuffixName, args ?? new List()); ResolveExpression(rr, opts); @@ -8708,7 +8592,7 @@ namespace Microsoft.Dafny // ----- 1. Member of the specified module if (decl is AmbiguousTopLevelDecl) { var ad = (AmbiguousTopLevelDecl)decl; - Error(expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", expr.SuffixName, ad.ModuleNames()); + reporter.Error(MessageSource.Resolver, expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", expr.SuffixName, ad.ModuleNames()); } else { // We have found a module name or a type name, neither of which is an expression. However, the ExprDotName we're // looking at may be followed by a further suffix that makes this into an expresion. We postpone the rest of the @@ -8721,13 +8605,13 @@ namespace Microsoft.Dafny Contract.Assert(member.IsStatic); // moduleInfo.StaticMembers is supposed to contain only static members of the module's implicit class _default if (member is AmbiguousMemberDecl) { var ambiguousMember = (AmbiguousMemberDecl)member; - Error(expr.tok, "The name {0} ambiguously refers to a static member in one of the modules {1} (try qualifying the member name with the module name)", expr.SuffixName, ambiguousMember.ModuleNames()); + reporter.Error(MessageSource.Resolver, expr.tok, "The name {0} ambiguously refers to a static member in one of the modules {1} (try qualifying the member name with the module name)", expr.SuffixName, ambiguousMember.ModuleNames()); } else { var receiver = new StaticReceiverExpr(expr.tok, (ClassDecl)member.EnclosingClass, true); r = ResolveExprDotCall(expr.tok, receiver, member, expr.OptTypeArguments, opts.codeContext, allowMethodCall); } } else { - Error(expr.tok, "unresolved identifier: {0}", expr.SuffixName); + reporter.Error(MessageSource.Resolver, expr.tok, "unresolved identifier: {0}", expr.SuffixName); } } else if (lhs != null && lhs.Type is Resolver_IdentifierExpr.ResolverType_Type) { @@ -8746,7 +8630,7 @@ namespace Microsoft.Dafny Dictionary members; if (classMembers.TryGetValue(cd, out members) && members.TryGetValue(expr.SuffixName, out member)) { if (!member.IsStatic) { - Error(expr.tok, "accessing member '{0}' requires an instance expression", expr.SuffixName); //FIXME Unify with similar error message + reporter.Error(MessageSource.Resolver, expr.tok, "accessing member '{0}' requires an instance expression", expr.SuffixName); //FIXME Unify with similar error message // nevertheless, continue creating an expression that approximates a correct one } var receiver = new StaticReceiverExpr(expr.tok, (UserDefinedType)ty.NormalizeExpand(), (ClassDecl)member.EnclosingClass, false); @@ -8759,7 +8643,7 @@ namespace Microsoft.Dafny DatatypeCtor ctor; if (datatypeCtors.TryGetValue(dt, out members) && members.TryGetValue(expr.SuffixName, out ctor)) { if (expr.OptTypeArguments != null) { - Error(expr.tok, "datatype constructor does not take any type parameters ('{0}')", expr.SuffixName); + reporter.Error(MessageSource.Resolver, expr.tok, "datatype constructor does not take any type parameters ('{0}')", expr.SuffixName); } var rr = new DatatypeValue(expr.tok, ctor.EnclosingDatatype.Name, expr.SuffixName, args ?? new List()); ResolveDatatypeValue(opts, rr, ctor.EnclosingDatatype); @@ -8772,7 +8656,7 @@ namespace Microsoft.Dafny } } if (r == null) { - Error(expr.tok, "member '{0}' does not exist in type '{1}'", expr.SuffixName, ri.TypeParamDecl != null ? ri.TypeParamDecl.Name : ri.Decl.Name); + reporter.Error(MessageSource.Resolver, expr.tok, "member '{0}' does not exist in type '{1}'", expr.SuffixName, ri.TypeParamDecl != null ? ri.TypeParamDecl.Name : ri.Decl.Name); } } else if (lhs != null) { // ----- 4. Look up name in the type of the Lhs @@ -8832,7 +8716,7 @@ namespace Microsoft.Dafny foreach (var ty in expr.OptTypeArguments) { ResolveType(expr.tok, ty, opts.codeContext, option, defaultTypeArguments); if (ty.IsSubrangeType) { - Error(expr.tok, "sorry, cannot instantiate type parameter with a subrange type"); + reporter.Error(MessageSource.Resolver, expr.tok, "sorry, cannot instantiate type parameter with a subrange type"); } } } @@ -8851,7 +8735,7 @@ namespace Microsoft.Dafny // ----- 0. Member of the specified module if (decl is AmbiguousTopLevelDecl) { var ad = (AmbiguousTopLevelDecl)decl; - Error(expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", expr.SuffixName, ad.ModuleNames()); + reporter.Error(MessageSource.Resolver, expr.tok, "The name {0} ambiguously refers to a type in one of the modules {1} (try qualifying the type name with the module name)", expr.SuffixName, ad.ModuleNames()); } else { // We have found a module name or a type name. We create a temporary expression that will never be seen by the compiler // or verifier, just to have a placeholder where we can recorded what we have found. @@ -8869,7 +8753,7 @@ namespace Microsoft.Dafny } #endif } else { - Error(expr.tok, "module '{0}' does not declare a type '{1}'", ri.Decl.Name, expr.SuffixName); + reporter.Error(MessageSource.Resolver, expr.tok, "module '{0}' does not declare a type '{1}'", ri.Decl.Name, expr.SuffixName); } } else if (lhs != null && lhs.Type is Resolver_IdentifierExpr.ResolverType_Type) { @@ -8885,7 +8769,7 @@ namespace Microsoft.Dafny return new ResolveTypeReturn(ty, expr); } if (r == null) { - Error(expr.tok, "member '{0}' does not exist in type '{1}' or cannot be part of type name", expr.SuffixName, ri.TypeParamDecl != null ? ri.TypeParamDecl.Name : ri.Decl.Name); + reporter.Error(MessageSource.Resolver, expr.tok, "member '{0}' does not exist in type '{1}' or cannot be part of type name", expr.SuffixName, ri.TypeParamDecl != null ? ri.TypeParamDecl.Name : ri.Decl.Name); } } @@ -8922,14 +8806,14 @@ namespace Microsoft.Dafny if (member is Field) { if (optTypeArguments != null) { - Error(tok, "a field ({0}) does not take any type arguments (got {1})", member.Name, optTypeArguments.Count); + reporter.Error(MessageSource.Resolver, tok, "a field ({0}) does not take any type arguments (got {1})", member.Name, optTypeArguments.Count); } rr.Type = SubstType(((Field)member).Type, subst); } else if (member is Function) { var fn = (Function)member; int suppliedTypeArguments = optTypeArguments == null ? 0 : optTypeArguments.Count; if (optTypeArguments != null && suppliedTypeArguments != fn.TypeArgs.Count) { - Error(tok, "function '{0}' expects {1} type arguments (got {2})", member.Name, fn.TypeArgs.Count, suppliedTypeArguments); + reporter.Error(MessageSource.Resolver, tok, "function '{0}' expects {1} type arguments (got {2})", member.Name, fn.TypeArgs.Count, suppliedTypeArguments); } rr.TypeApplication = new List(); if (udt != null && udt.ResolvedClass != null) { @@ -8947,11 +8831,11 @@ namespace Microsoft.Dafny var m = (Method)member; if (!allowMethodCall) { // it's a method and method calls are not allowed in the given context - Error(tok, "expression is not allowed to invoke a method ({0})", member.Name); + reporter.Error(MessageSource.Resolver, tok, "expression is not allowed to invoke a method ({0})", member.Name); } int suppliedTypeArguments = optTypeArguments == null ? 0 : optTypeArguments.Count; if (optTypeArguments != null && suppliedTypeArguments != m.TypeArgs.Count) { - Error(tok, "method '{0}' expects {1} type arguments (got {2})", member.Name, m.TypeArgs.Count, suppliedTypeArguments); + reporter.Error(MessageSource.Resolver, tok, "method '{0}' expects {1} type arguments (got {2})", member.Name, m.TypeArgs.Count, suppliedTypeArguments); } rr.TypeApplication = new List(); if (udt != null && udt.ResolvedClass != null) { @@ -8996,7 +8880,7 @@ namespace Microsoft.Dafny Contract.Requires(opts != null); Contract.Ensures(Contract.Result() == null || allowMethodCall); Expression r = null; // upon success, the expression to which the ApplySuffix resolves - var errorCount = ErrorCount; + var errorCount = reporter.Count(ErrorLevel.Error); if (e.Lhs is NameSegment) { r = ResolveNameSegment((NameSegment)e.Lhs, true, e.Args, opts, allowMethodCall); // note, if r is non-null, then e.Args have been resolved and r is a resolved expression that incorporates e.Args @@ -9014,18 +8898,18 @@ namespace Microsoft.Dafny if (fnType == null) { var lhs = e.Lhs.Resolved; if (lhs != null && lhs.Type is Resolver_IdentifierExpr.ResolverType_Module) { - Error(e.tok, "name of module ({0}) is used as a function", ((Resolver_IdentifierExpr)lhs).Decl.Name); + reporter.Error(MessageSource.Resolver, e.tok, "name of module ({0}) is used as a function", ((Resolver_IdentifierExpr)lhs).Decl.Name); } else if (lhs != null && lhs.Type is Resolver_IdentifierExpr.ResolverType_Type) { // It may be a conversion expression var ri = (Resolver_IdentifierExpr)lhs; if (ri.TypeParamDecl != null) { - Error(e.tok, "name of type parameter ({0}) is used as a function", ri.TypeParamDecl.Name); + reporter.Error(MessageSource.Resolver, e.tok, "name of type parameter ({0}) is used as a function", ri.TypeParamDecl.Name); } else { var decl = ri.Decl; var ty = new UserDefinedType(e.tok, decl.Name, decl, ri.TypeArgs); if (ty.AsNewtype != null) { if (e.Args.Count != 1) { - Error(e.tok, "conversion operation to {0} got wrong number of arguments (expected 1, got {1})", decl.Name, e.Args.Count); + reporter.Error(MessageSource.Resolver, e.tok, "conversion operation to {0} got wrong number of arguments (expected 1, got {1})", decl.Name, e.Args.Count); } var conversionArg = 1 <= e.Args.Count ? e.Args[0] : ty.IsNumericBased(Type.NumericPersuation.Int) ? LiteralExpr.CreateIntLiteral(e.tok, 0) : @@ -9037,7 +8921,7 @@ namespace Microsoft.Dafny ResolveExpression(e.Args[i], opts); } } else { - Error(e.tok, "name of type ({0}) is used as a function", decl.Name); + reporter.Error(MessageSource.Resolver, e.tok, "name of type ({0}) is used as a function", decl.Name); } } } else { @@ -9047,10 +8931,10 @@ namespace Microsoft.Dafny var cRhs = new MethodCallInformation(e.tok, mse, e.Args); return cRhs; } else { - Error(e.tok, "method call is not allowed to be used in an expression context ({0})", mse.Member.Name); + reporter.Error(MessageSource.Resolver, e.tok, "method call is not allowed to be used in an expression context ({0})", mse.Member.Name); } } else if (lhs != null) { // if e.Lhs.Resolved is null, then e.Lhs was not successfully resolved and an error has already been reported - Error(e.tok, "non-function expression (of type {0}) is called with parameters", e.Lhs.Type); + reporter.Error(MessageSource.Resolver, e.tok, "non-function expression (of type {0}) is called with parameters", e.Lhs.Type); } } } else { @@ -9058,14 +8942,14 @@ namespace Microsoft.Dafny var callee = mse == null ? null : mse.Member as Function; if (fnType.Arity != e.Args.Count) { var what = callee != null ? string.Format("function '{0}'", callee.Name) : string.Format("function type '{0}'", fnType); - Error(e.tok, "wrong number of arguments to function application ({0} expects {1}, got {2})", what, fnType.Arity, e.Args.Count); + reporter.Error(MessageSource.Resolver, e.tok, "wrong number of arguments to function application ({0} expects {1}, got {2})", what, fnType.Arity, e.Args.Count); } else { for (var i = 0; i < fnType.Arity; i++) { if (!UnifyTypes(fnType.Args[i], e.Args[i].Type)) { - Error(e.Args[i].tok, "type mismatch for argument {0} (function expects {1}, got {2})", i, fnType.Args[i], e.Args[i].Type); + reporter.Error(MessageSource.Resolver, e.Args[i].tok, "type mismatch for argument {0} (function expects {1}, got {2})", i, fnType.Args[i], e.Args[i].Type); } } - if (errorCount != ErrorCount) { + if (errorCount != reporter.Count(ErrorLevel.Error)) { // do nothing else; error has been reported } else if (callee != null) { // produce a FunctionCallExpr instead of an ApplyExpr(MemberSelectExpr) @@ -9090,7 +8974,7 @@ namespace Microsoft.Dafny Contract.Assert(farg.Type != null); // follows from postcondition of ResolveExpression Type s = SubstType(callee.Formals[i].Type, rr.TypeArgumentSubstitutions); if (!UnifyTypes(farg.Type, s)) { - Error(rr, "incorrect type of function argument {0} (expected {1}, got {2})", i, s, farg.Type); + reporter.Error(MessageSource.Resolver, rr, "incorrect type of function argument {0} (expected {1}, got {2})", i, s, farg.Type); } } rr.Type = SubstType(callee.ResultType, rr.TypeArgumentSubstitutions); @@ -9132,12 +9016,12 @@ namespace Microsoft.Dafny DatatypeCtor ctor; if (!datatypeCtors[dt].TryGetValue(dtv.MemberName, out ctor)) { - Error(dtv.tok, "undeclared constructor {0} in datatype {1}", dtv.MemberName, dtv.DatatypeName); + reporter.Error(MessageSource.Resolver, dtv.tok, "undeclared constructor {0} in datatype {1}", dtv.MemberName, dtv.DatatypeName); } else { Contract.Assert(ctor != null); // follows from postcondition of TryGetValue dtv.Ctor = ctor; if (ctor.Formals.Count != dtv.Arguments.Count) { - Error(dtv.tok, "wrong number of arguments to datatype constructor {0} (found {1}, expected {2})", ctor.Name, dtv.Arguments.Count, ctor.Formals.Count); + reporter.Error(MessageSource.Resolver, dtv.tok, "wrong number of arguments to datatype constructor {0} (found {1}, expected {2})", ctor.Name, dtv.Arguments.Count, ctor.Formals.Count); } } int j = 0; @@ -9148,7 +9032,7 @@ namespace Microsoft.Dafny if (formal != null) { Type st = SubstType(formal.Type, subst); if (!UnifyTypes(arg.Type, st)) { - Error(arg.tok, "incorrect type of datatype constructor argument (found {0}, expected {1})", arg.Type, st); + reporter.Error(MessageSource.Resolver, arg.tok, "incorrect type of datatype constructor argument (found {0}, expected {1})", arg.Type, st); } } j++; @@ -9192,14 +9076,14 @@ namespace Microsoft.Dafny if (expr is IdentifierExpr) { var e = (IdentifierExpr)expr; if (e.Var != null && e.Var.IsGhost) { - Error(expr, "ghost variables are allowed only in specification contexts"); + reporter.Error(MessageSource.Resolver, expr, "ghost variables are allowed only in specification contexts"); return; } } else if (expr is MemberSelectExpr) { var e = (MemberSelectExpr)expr; if (e.Member != null && e.Member.IsGhost) { - Error(expr, "ghost fields are allowed only in specification contexts"); + reporter.Error(MessageSource.Resolver, expr, "ghost fields are allowed only in specification contexts"); return; } @@ -9207,7 +9091,7 @@ namespace Microsoft.Dafny var e = (FunctionCallExpr)expr; if (e.Function != null) { if (e.Function.IsGhost) { - Error(expr, "function calls are allowed only in specification contexts (consider declaring the function a 'function method')"); + reporter.Error(MessageSource.Resolver, expr, "function calls are allowed only in specification contexts (consider declaring the function a 'function method')"); return; } // function is okay, so check all NON-ghost arguments @@ -9232,13 +9116,13 @@ namespace Microsoft.Dafny return; } else if (expr is OldExpr) { - Error(expr, "old expressions are allowed only in specification and ghost contexts"); + reporter.Error(MessageSource.Resolver, expr, "old expressions are allowed only in specification and ghost contexts"); return; } else if (expr is UnaryOpExpr) { var e = (UnaryOpExpr)expr; if (e.Op == UnaryOpExpr.Opcode.Fresh) { - Error(expr, "fresh expressions are allowed only in specification and ghost contexts"); + reporter.Error(MessageSource.Resolver, expr, "fresh expressions are allowed only in specification and ghost contexts"); return; } @@ -9253,7 +9137,7 @@ namespace Microsoft.Dafny switch (e.ResolvedOp_PossiblyStillUndetermined) { case BinaryExpr.ResolvedOpcode.RankGt: case BinaryExpr.ResolvedOpcode.RankLt: - Error(expr, "rank comparisons are allowed only in specification and ghost contexts"); + reporter.Error(MessageSource.Resolver, expr, "rank comparisons are allowed only in specification and ghost contexts"); return; default: break; @@ -9264,7 +9148,7 @@ namespace Microsoft.Dafny switch (e.Op) { case TernaryExpr.Opcode.PrefixEqOp: case TernaryExpr.Opcode.PrefixNeqOp: - Error(expr, "prefix equalities are allowed only in specification and ghost contexts"); + reporter.Error(MessageSource.Resolver, expr, "prefix equalities are allowed only in specification and ghost contexts"); return; default: break; @@ -9294,7 +9178,7 @@ namespace Microsoft.Dafny var e = (QuantifierExpr)expr; if (e.MissingBounds != null) { foreach (var bv in e.MissingBounds) { - Error(expr, "quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name); + 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); } return; } @@ -9302,7 +9186,7 @@ namespace Microsoft.Dafny var e = (MapComprehension)expr; if (e.MissingBounds != null && !e.Finite) { foreach (var bv in e.MissingBounds) { - Error(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); + 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; } @@ -9339,9 +9223,9 @@ namespace Microsoft.Dafny if (member == null) { // error has already been reported by ResolveMember } else if (member is Method) { - Error(e, "member {0} in type {1} refers to a method, but only functions can be used in this context", e.Name, cce.NonNull(ctype).Name); + reporter.Error(MessageSource.Resolver, e, "member {0} in type {1} refers to a method, but only functions can be used in this context", e.Name, cce.NonNull(ctype).Name); } else if (!(member is Function)) { - Error(e, "member {0} in type {1} does not refer to a function", e.Name, cce.NonNull(ctype).Name); + reporter.Error(MessageSource.Resolver, e, "member {0} in type {1} does not refer to a function", e.Name, cce.NonNull(ctype).Name); } else { Function function = (Function)member; e.Function = function; @@ -9349,10 +9233,10 @@ namespace Microsoft.Dafny ((FixpointPredicate)function).Uses.Add(e); } if (e.Receiver is StaticReceiverExpr && !function.IsStatic) { - Error(e, "an instance function must be selected via an object, not just a class name"); + reporter.Error(MessageSource.Resolver, e, "an instance function must be selected via an object, not just a class name"); } if (function.Formals.Count != e.Args.Count) { - Error(e, "wrong number of function arguments (got {0}, expected {1})", e.Args.Count, function.Formals.Count); + reporter.Error(MessageSource.Resolver, e, "wrong number of function arguments (got {0}, expected {1})", e.Args.Count, function.Formals.Count); } else { Contract.Assert(ctype != null); // follows from postcondition of ResolveMember if (!function.IsStatic) { @@ -9364,9 +9248,9 @@ namespace Microsoft.Dafny // in the event that a static function calls another static function (and note that we need the // type of the receiver in order to find the method, so we could not have made this check // earlier). - Error(e.Receiver, "'this' is not allowed in a 'static' context"); + reporter.Error(MessageSource.Resolver, e.Receiver, "'this' is not allowed in a 'static' context"); } else if (e.Receiver is StaticReceiverExpr) { - Error(e.Receiver, "call to instance function requires an instance"); + reporter.Error(MessageSource.Resolver, e.Receiver, "call to instance function requires an instance"); } } // build the type substitution map @@ -9384,7 +9268,7 @@ namespace Microsoft.Dafny Contract.Assert(farg.Type != null); // follows from postcondition of ResolveExpression Type s = SubstType(function.Formals[i].Type, e.TypeArgumentSubstitutions); if (!UnifyTypes(farg.Type, s)) { - Error(e, "incorrect type of function argument {0} (expected {1}, got {2})", i, s, farg.Type); + reporter.Error(MessageSource.Resolver, e, "incorrect type of function argument {0} (expected {1}, got {2})", i, s, farg.Type); } } e.Type = SubstType(function.ResultType, e.TypeArgumentSubstitutions); @@ -9435,8 +9319,8 @@ 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); + public static List DiscoverBounds(IToken tok, List bvars, Expression expr, bool polarity, bool returnAllBounds, List missingBounds, ErrorReporter reporter) where VT : IVariable { + var pairs = DiscoverBoundsAux(tok, bvars, expr, polarity, returnAllBounds, false, missingBounds, reporter); if (pairs == null) { return null; } @@ -9464,7 +9348,7 @@ namespace Microsoft.Dafny /// If "allowAnyIntegers", then integer variables will always be given a bound, but this bound may be WiggleWaggle if /// there is no better bound. /// - public static List>> DiscoverBoundsAux(IToken tok, List bvars, Expression expr, bool polarity, bool returnAllBounds, bool allowAnyIntegers, List missingBounds) where VT : IVariable { + public static List>> DiscoverBoundsAux(IToken tok, List bvars, Expression expr, bool polarity, bool returnAllBounds, bool allowAnyIntegers, List missingBounds, ErrorReporter reporter) where VT : IVariable { Contract.Requires(tok != null); Contract.Requires(bvars != null); Contract.Requires(missingBounds != null); @@ -9483,7 +9367,7 @@ namespace Microsoft.Dafny var allBounds = new List>>(); bool foundError = false; foreach (var bv in bvars) { - var c = TypeConstraint(bv, bv.Type); + var c = TypeConstraint(bv, bv.Type, reporter); expr = polarity ? Expression.CreateAnd(c, expr) : Expression.CreateImplies(c, expr); } for (int j = 0; j < bvars.Count; j++) { @@ -9619,15 +9503,15 @@ namespace Microsoft.Dafny return foundError ? null : allBounds; } - static Expression TypeConstraint(IVariable bv, Type ty) { + static Expression TypeConstraint(IVariable bv, Type ty, ErrorReporter reporter) { Contract.Requires(bv != null); Contract.Requires(ty != null); ty = ty.NormalizeExpand(); var dd = ty.AsNewtype; if (dd != null) { - var c = TypeConstraint(bv, dd.BaseType); + var c = TypeConstraint(bv, dd.BaseType, reporter); if (dd.Var != null) { - c = Expression.CreateAnd(c, new Translator().Substitute(dd.Constraint, dd.Var, Expression.CreateIdentExpr(bv))); + c = Expression.CreateAnd(c, new Translator(reporter).Substitute(dd.Constraint, dd.Var, Expression.CreateIdentExpr(bv))); } return c; } @@ -9971,7 +9855,7 @@ namespace Microsoft.Dafny IndexableTypeProxy expectedType = new IndexableTypeProxy(domainType, elementType, argType, true, true, true); if (!UnifyTypes(e.Seq.Type, expectedType)) { - Error(e, "sequence/array/multiset/map selection requires a sequence, array, multiset, or map (got {0})", e.Seq.Type); + reporter.Error(MessageSource.Resolver, e, "sequence/array/multiset/map selection requires a sequence, array, multiset, or map (got {0})", e.Seq.Type); seqErr = true; } if (!e.SelectOne) // require sequence or array @@ -9979,13 +9863,13 @@ namespace Microsoft.Dafny if (!allowNonUnitArraySelection) { // require seq if (!UnifyTypes(expectedType, new SeqType(new InferredTypeProxy()))) { - Error(e, "selection requires a sequence (got {0})", e.Seq.Type); + reporter.Error(MessageSource.Resolver, e, "selection requires a sequence (got {0})", e.Seq.Type); } } else { if (UnifyTypes(expectedType, new MapType(true, new InferredTypeProxy(), new InferredTypeProxy()))) { - Error(e, "cannot multiselect a map (got {0} as map type)", e.Seq.Type); + reporter.Error(MessageSource.Resolver, e, "cannot multiselect a map (got {0} as map type)", e.Seq.Type); } else if (UnifyTypes(expectedType, new MapType(false, new InferredTypeProxy(), new InferredTypeProxy()))) { - Error(e, "cannot multiselect an imap (got {0} as imap type)", e.Seq.Type); + reporter.Error(MessageSource.Resolver, e, "cannot multiselect an imap (got {0} as imap type)", e.Seq.Type); } } } @@ -9993,7 +9877,7 @@ namespace Microsoft.Dafny ResolveExpression(e.E0, opts); Contract.Assert(e.E0.Type != null); // follows from postcondition of ResolveExpression if (!UnifyTypes(e.E0.Type, domainType)) { - Error(e.E0, "sequence/array/multiset/map selection requires {1} indices (got {0})", e.E0.Type, domainType); + reporter.Error(MessageSource.Resolver, e.E0, "sequence/array/multiset/map selection requires {1} indices (got {0})", e.E0.Type, domainType); } } if (e.E1 != null) { @@ -10001,7 +9885,7 @@ namespace Microsoft.Dafny Contract.Assert(e.E1.Type != null); // follows from postcondition of ResolveExpression var domType = e.E0 == null ? domainType : new OperationTypeProxy(true, false, false, false, false, false); // reuse 'domainType' if .E0 did not use it; otherwise, create a new proxy to allow .E1 to be any integer-based numeric type, independent of the integer-based numeric type used by .E0 if (!UnifyTypes(e.E1.Type, domType)) { - Error(e.E1, "sequence/array/multiset/map selection requires {1} indices (got {0})", e.E1.Type, domType); + reporter.Error(MessageSource.Resolver, e.E1, "sequence/array/multiset/map selection requires {1} indices (got {0})", e.E1.Type, domType); } } if (!seqErr) { @@ -10193,7 +10077,7 @@ namespace Microsoft.Dafny } else { return false; } - } else if (expr is SeqSelectExpr) { + } else if (expr is SeqSelectExpr) { SeqSelectExpr e = (SeqSelectExpr)expr; return UsesSpecFeatures(e.Seq) || (e.E0 != null && UsesSpecFeatures(e.E0)) || diff --git a/Source/Dafny/Rewriter.cs b/Source/Dafny/Rewriter.cs index 4223fb7f..cb71b80d 100644 --- a/Source/Dafny/Rewriter.cs +++ b/Source/Dafny/Rewriter.cs @@ -6,24 +6,25 @@ using IToken = Microsoft.Boogie.IToken; namespace Microsoft.Dafny { - [ContractClass(typeof(IRewriterContracts))] - public interface IRewriter + public abstract class IRewriter { - void PreResolve(ModuleDefinition m); - void PostResolve(ModuleDefinition m); - // After SCC/Cyclicity/Recursivity analysis: - void PostCyclicityResolve(ModuleDefinition m); - } - [ContractClassFor(typeof(IRewriter))] - abstract class IRewriterContracts : IRewriter - { - public void PreResolve(ModuleDefinition m) { + protected readonly ErrorReporter reporter; + + public IRewriter(ErrorReporter reporter) { + Contract.Requires(reporter != null); + this.reporter = reporter; + } + + internal virtual void PreResolve(ModuleDefinition m) { Contract.Requires(m != null); } - public void PostResolve(ModuleDefinition m) { + + internal virtual void PostResolve(ModuleDefinition m) { Contract.Requires(m != null); } - public void PostCyclicityResolve(ModuleDefinition m) { + + // After SCC/Cyclicity/Recursivity analysis: + internal virtual void PostCyclicityResolve(ModuleDefinition m) { Contract.Requires(m != null); } } @@ -37,33 +38,26 @@ namespace Microsoft.Dafny } } - public class TriggersRewriter : IRewriter { - Resolver Resolver; - - internal TriggersRewriter(Resolver resolver) { - Contract.Requires(resolver != null); - this.Resolver = resolver; + public class TriggerGeneratingRewriter : IRewriter { + internal TriggerGeneratingRewriter(ErrorReporter reporter) : base(reporter) { + Contract.Requires(reporter != null); } - public void PreResolve(ModuleDefinition m) { } - - public void PostResolve(ModuleDefinition m) { + internal override void PostResolve(ModuleDefinition m) { foreach (var decl in ModuleDefinition.AllCallables(m.TopLevelDecls)) { if (decl is Function) { var function = (Function)decl; - TriggerGenerator.AddTriggers(function.Ens, Resolver); - TriggerGenerator.AddTriggers(function.Req, Resolver); - TriggerGenerator.AddTriggers(function.Body, Resolver); + //TriggerGenerator.AddTriggers(function.Ens, Resolver); + //TriggerGenerator.AddTriggers(function.Req, Resolver); + //TriggerGenerator.AddTriggers(function.Body, Resolver); } else if (decl is Method) { var method = (Method)decl; - TriggerGenerator.AddTriggers(method.Ens, Resolver); - TriggerGenerator.AddTriggers(method.Req, Resolver); - TriggerGenerator.AddTriggers(method.Body, Resolver); + //TriggerGenerator.AddTriggers(method.Ens, Resolver); + //TriggerGenerator.AddTriggers(method.Req, Resolver); + //TriggerGenerator.AddTriggers(method.Body, Resolver); } } } - - public void PostCyclicityResolve(ModuleDefinition m) { } } /// @@ -106,7 +100,12 @@ namespace Microsoft.Dafny /// public class AutoContractsRewriter : IRewriter { - public void PreResolve(ModuleDefinition m) { + public AutoContractsRewriter(ErrorReporter reporter) + : base(reporter) { + Contract.Requires(reporter != null); + } + + internal override void PreResolve(ModuleDefinition m) { foreach (var d in m.TopLevelDecls) { bool sayYes = true; if (d is ClassDecl && Attributes.ContainsBool(d.Attributes, "autocontracts", ref sayYes) && sayYes) { @@ -162,7 +161,7 @@ namespace Microsoft.Dafny } } - public void PostResolve(ModuleDefinition m) { + internal override void PostResolve(ModuleDefinition m) { foreach (var d in m.TopLevelDecls) { bool sayYes = true; if (d is ClassDecl && Attributes.ContainsBool(d.Attributes, "autocontracts", ref sayYes) && sayYes) { @@ -171,9 +170,6 @@ namespace Microsoft.Dafny } } - public void PostCyclicityResolve(ModuleDefinition m) { - } - void ProcessClassPostResolve(ClassDecl cl) { // Find all fields of a reference type, and make a note of whether or not the reference type has a Repr field. // Also, find the Repr field and the function Valid in class "cl" @@ -416,20 +412,20 @@ namespace Microsoft.Dafny /// specifically asks to see it via the reveal_foo() lemma /// public class OpaqueFunctionRewriter : IRewriter { - readonly ResolutionErrorReporter reporter; protected Dictionary fullVersion; // Given an opaque function, retrieve the full protected Dictionary original; // Given a full version of an opaque function, find the original opaque version protected Dictionary revealOriginal; // Map reveal_* lemmas back to their original functions - public OpaqueFunctionRewriter(ResolutionErrorReporter reporter) - : base() { - this.reporter = reporter; + public OpaqueFunctionRewriter(ErrorReporter reporter) + : base(reporter) { + Contract.Requires(reporter != null); + fullVersion = new Dictionary(); original = new Dictionary(); revealOriginal = new Dictionary(); } - public void PreResolve(ModuleDefinition m) { + internal override void PreResolve(ModuleDefinition m) { foreach (var d in m.TopLevelDecls) { if (d is ClassDecl) { DuplicateOpaqueClassFunctions((ClassDecl)d); @@ -437,7 +433,7 @@ namespace Microsoft.Dafny } } - public void PostResolve(ModuleDefinition m) { + internal override void PostResolve(ModuleDefinition m) { // Fix up the ensures clause of the full version of the function, // since it may refer to the original opaque function foreach (var fn in ModuleDefinition.AllFunctions(m.TopLevelDecls)) { @@ -462,7 +458,7 @@ namespace Microsoft.Dafny } } - public void PostCyclicityResolve(ModuleDefinition m) { + internal override void PostCyclicityResolve(ModuleDefinition m) { // Add layer quantifier if the function is recursive foreach (var decl in ModuleDefinition.AllCallables(m.TopLevelDecls)) { if (decl is Lemma) { @@ -507,7 +503,7 @@ namespace Microsoft.Dafny if (!Attributes.Contains(f.Attributes, "opaque")) { // Nothing to do } else if (f.IsProtected) { - reporter.Error(f.tok, ":opaque is not allowed to be applied to protected functions (this will be allowed when the language introduces 'opaque'/'reveal' as keywords)"); + reporter.Error(MessageSource.Rewriter, f.tok, ":opaque is not allowed to be applied to protected functions (this will be allowed when the language introduces 'opaque'/'reveal' as keywords)"); } else if (!RefinementToken.IsInherited(f.tok, c.Module)) { // Create a copy, which will be the internal version with a full body // which will allow us to verify that the ensures are true @@ -721,19 +717,16 @@ namespace Microsoft.Dafny /// public class AutoReqFunctionRewriter : IRewriter { Function parentFunction; - Resolver resolver; OpaqueFunctionRewriter opaqueInfo; bool containsMatch; // TODO: Track this per-requirement, rather than per-function - public AutoReqFunctionRewriter(Resolver r, OpaqueFunctionRewriter o) { - this.resolver = r; + public AutoReqFunctionRewriter(ErrorReporter reporter, OpaqueFunctionRewriter o) + : base(reporter) { + Contract.Requires(reporter != null); this.opaqueInfo = o; } - public void PreResolve(ModuleDefinition m) { - } - - public void PostResolve(ModuleDefinition m) { + internal override void PostResolve(ModuleDefinition m) { var components = m.CallGraph.TopologicallySortedComponents(); foreach (var scComponent in components) { // Visit the call graph bottom up, so anything we call already has its prequisites calculated @@ -806,9 +799,6 @@ namespace Microsoft.Dafny } } - public void PostCyclicityResolve(ModuleDefinition m) { - } - Expression subVars(List formals, List values, Expression e, Expression f_this) { Contract.Assert(formals != null); Contract.Assert(values != null); @@ -839,7 +829,7 @@ namespace Microsoft.Dafny } if (!tip.Equals("")) { - resolver.ReportAdditionalInformation(f.tok, tip, f.tok.val.Length); + reporter.Info(MessageSource.Rewriter, 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 +856,7 @@ namespace Microsoft.Dafny } if (!tip.Equals("")) { - resolver.ReportAdditionalInformation(method.tok, tip, method.tok.val.Length); + reporter.Info(MessageSource.Rewriter, 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 +1051,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); + reporter.Info(MessageSource.Rewriter, e.tok, "autoreq added (" + Printer.ExtendedExprToString(allReqsSatisfied) + ") &&"); } } else if (expr is SetComprehension) { var e = (SetComprehension)expr; @@ -1108,7 +1098,12 @@ namespace Microsoft.Dafny /// public class TimeLimitRewriter : IRewriter { - public void PreResolve(ModuleDefinition m) { + public TimeLimitRewriter(ErrorReporter reporter) + : base(reporter) { + Contract.Requires(reporter != null); + } + + internal override void PreResolve(ModuleDefinition m) { foreach (var d in m.TopLevelDecls) { if (d is ClassDecl) { var c = (ClassDecl)d; @@ -1136,16 +1131,6 @@ namespace Microsoft.Dafny } } } - - public void PostResolve(ModuleDefinition m) - { - // Nothing to do here - } - - public void PostCyclicityResolve(ModuleDefinition m) { - // Nothing to do here - } - } diff --git a/Source/Dafny/Scanner.cs b/Source/Dafny/Scanner.cs index a73f510d..e6c97f22 100644 --- a/Source/Dafny/Scanner.cs +++ b/Source/Dafny/Scanner.cs @@ -314,8 +314,8 @@ public class Scanner { // [NotDelayed] public Scanner (string/*!*/ fullFilename, string/*!*/ fileName, Errors/*!*/ errorHandler, bool useBaseName = false) : base() { - Contract.Requires(fileName != null); - Contract.Requires(errorHandler != null); + Contract.Requires(fileName != null); + Contract.Requires(errorHandler != null); this.errorHandler = errorHandler; pt = tokens = new Token(); // first token is a dummy t = new Token(); // dummy because t is a non-null field @@ -332,9 +332,9 @@ public class Scanner { // [NotDelayed] public Scanner (Stream/*!*/ s, Errors/*!*/ errorHandler, string/*!*/ fullFilename, string/*!*/ fileName, bool useBaseName = false) : base() { - Contract.Requires(s != null); - Contract.Requires(errorHandler != null); - Contract.Requires(fileName != null); + Contract.Requires(s != null); + Contract.Requires(errorHandler != null); + Contract.Requires(fileName != null); pt = tokens = new Token(); // first token is a dummy t = new Token(); // dummy because t is a non-null field this._buffer = new Buffer(s, true); @@ -344,9 +344,9 @@ public class Scanner { Init(); } - string GetBaseName(string fileName) { - return System.IO.Path.GetFileName(fileName); // Return basename - } + string GetBaseName(string fileName) { + return System.IO.Path.GetFileName(fileName); // Return basename + } void Init() { pos = -1; line = 1; col = 0; @@ -991,6 +991,4 @@ public class Scanner { } // end Scanner public delegate void ErrorProc(int n, string filename, int line, int col); - - } \ No newline at end of file diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index b8c4b8ec..039aa56f 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -93,9 +93,13 @@ namespace Microsoft.Dafny { } public class Translator { + readonly ErrorReporter reporter; [NotDelayed] - public Translator() { + public Translator(ErrorReporter reporter) { + Contract.Requires(reporter != null); + + this.reporter = reporter; InsertChecksums = 0 < CommandLineOptions.Clo.VerifySnapshots; Bpl.Program boogieProgram = ReadPrelude(); if (boogieProgram != null) { @@ -3609,7 +3613,7 @@ namespace Microsoft.Dafny { Contract.Requires(tok != null); Contract.Ensures(Contract.Result() != null); var col = tok.col + (isEndToken ? tok.val.Length : 0); - string description = Util.ReportIssueToString_Bare(additionalInfo == null ? "" : ": ", tok.filename, tok.line, tok.col, additionalInfo ?? ""); + string description = reporter.ErrorToString_Internal(additionalInfo == null ? "" : ": ", tok.filename, tok.line, tok.col, additionalInfo ?? ""); QKeyValue kv = new QKeyValue(tok, "captureState", new List() { description }, null); return new Bpl.AssumeCmd(tok, Bpl.Expr.True, kv); } @@ -7679,7 +7683,7 @@ namespace Microsoft.Dafny { } var missingBounds = new List(); - var bounds = Resolver.DiscoverBounds(x.tok, new List() { x }, expr, true, true, missingBounds); + var bounds = Resolver.DiscoverBounds(x.tok, new List() { x }, expr, true, true, missingBounds, reporter); if (missingBounds.Count == 0) { foreach (var bound in bounds) { if (bound is ComprehensionExpr.IntBoundedPool) { @@ -12955,9 +12959,8 @@ namespace Microsoft.Dafny { return true; } else { // Skip inlining, as it would cause arbitrary expressions to pop up in the trigger - // CLEMENT: Report inlining issue in a VS plugin friendly way - //CLEMENT this should appear at the outmost call site, not at the innermost. See SnapshotableTrees.dfy - Dafny.Util.ReportIssue("Info", fexp.tok, "Some instances of this call cannot safely be inlined."); + // CLEMENT this should appear at the outmost call site, not at the innermost. See SnapshotableTrees.dfy + reporter.Info(MessageSource.Translator, fexp.tok, "Some instances of this call cannot safely be inlined."); } } diff --git a/Source/Dafny/TriggerGenerator.cs b/Source/Dafny/TriggerGenerator.cs index cf677e0e..04d96b01 100644 --- a/Source/Dafny/TriggerGenerator.cs +++ b/Source/Dafny/TriggerGenerator.cs @@ -513,7 +513,7 @@ namespace Microsoft.Dafny { return; DebugTriggers("== From {0} visiting expr: {1}", new StackFrame(1).GetMethod().Name, Printer.ExprToString(root)); - TriggerGenerator generator = new TriggerGenerator(resolver.ReportAdditionalInformation); + TriggerGenerator generator = new TriggerGenerator(null); generator.AddTriggers_Internal(root); } @@ -522,7 +522,7 @@ namespace Microsoft.Dafny { return; DebugTriggers("== From {0} visiting statement: {1}", new StackFrame(1).GetMethod().Name, Printer.StatementToString(root)); - TriggerGenerator generator = new TriggerGenerator(resolver.ReportAdditionalInformation); + TriggerGenerator generator = new TriggerGenerator(null); generator.AddTriggers_Internal(root); } diff --git a/Source/Dafny/Util.cs b/Source/Dafny/Util.cs index 508d23c6..eaf599e3 100644 --- a/Source/Dafny/Util.cs +++ b/Source/Dafny/Util.cs @@ -67,29 +67,6 @@ namespace Microsoft.Dafny { return res; } - public static void ReportIssue(string header, IToken tok, string msg, params object[] args) { - ReportIssue(header, tok, String.Format(msg, args)); - } - - public static void ReportIssue(string header, IToken tok, string msg) { - ReportIssue(header, tok.filename, tok.line, tok.col, msg); - } - - public static void ReportIssue(string header, string filename, int line, int column, string msg) { - Console.WriteLine(ReportIssueToString(header, filename, line, column, msg)); - } - - public static string ReportIssueToString(string header, string filename, int line, int column, string msg) { - Contract.Requires(header != null); - Contract.Requires(filename != null); - Contract.Requires(msg != null); - return ReportIssueToString_Bare(": " + header, filename, line, column, ": " + msg); - } - - public static string ReportIssueToString_Bare(string header, string filename, int line, int column, string msg) { - return String.Format("{0}({1},{2}){3}{4}", filename, line, column - 1, header, msg ?? ""); - } - /// /// Returns s but with all occurrences of '_' removed. /// diff --git a/Source/DafnyDriver/DafnyDriver.cs b/Source/DafnyDriver/DafnyDriver.cs index d22899ab..4b5ae8d8 100644 --- a/Source/DafnyDriver/DafnyDriver.cs +++ b/Source/DafnyDriver/DafnyDriver.cs @@ -22,7 +22,6 @@ namespace Microsoft.Dafny public class DafnyDriver { - enum ExitValue { VERIFIED = 0, PREPROCESSING_ERROR, DAFNY_ERROR, NOT_VERIFIED } @@ -42,8 +41,7 @@ namespace Microsoft.Dafny { Contract.Requires(cce.NonNullElements(args)); - printer = new DafnyConsolePrinter(); - ExecutionEngine.printer = printer; + ExecutionEngine.printer = new DafnyConsolePrinter(); // For boogie errors DafnyOptions.Install(new DafnyOptions()); @@ -57,14 +55,14 @@ namespace Microsoft.Dafny if (CommandLineOptions.Clo.Files.Count == 0) { - printer.ErrorWriteLine(Console.Out, "*** Error: No input files were specified."); + ExecutionEngine.printer.ErrorWriteLine(Console.Out, "*** Error: No input files were specified."); exitValue = ExitValue.PREPROCESSING_ERROR; goto END; } if (CommandLineOptions.Clo.XmlSink != null) { string errMsg = CommandLineOptions.Clo.XmlSink.Open(); if (errMsg != null) { - printer.ErrorWriteLine(Console.Out, "*** Error: " + errMsg); + ExecutionEngine.printer.ErrorWriteLine(Console.Out, "*** Error: " + errMsg); exitValue = ExitValue.PREPROCESSING_ERROR; goto END; } @@ -87,12 +85,11 @@ namespace Microsoft.Dafny {Contract.Assert(file != null); string extension = Path.GetExtension(file); if (extension != null) { extension = extension.ToLower(); } - if (extension != ".dfy") - { - printer.ErrorWriteLine(Console.Out, "*** Error: '{0}': Filename extension '{1}' is not supported. Input files must be Dafny programs (.dfy).", file, - extension == null ? "" : extension); - exitValue = ExitValue.PREPROCESSING_ERROR; - goto END; + if (extension != ".dfy") { + ExecutionEngine.printer.ErrorWriteLine(Console.Out, "*** Error: '{0}': Filename extension '{1}' is not supported. Input files must be Dafny programs (.dfy).", file, + extension == null ? "" : extension); + exitValue = ExitValue.PREPROCESSING_ERROR; + goto END; } } exitValue = ProcessFiles(CommandLineOptions.Clo.Files); @@ -156,14 +153,15 @@ namespace Microsoft.Dafny using (XmlFileScope xf = new XmlFileScope(CommandLineOptions.Clo.XmlSink, fileNames[fileNames.Count-1])) { Dafny.Program dafnyProgram; + ErrorReporter reporter = new ConsoleErrorReporter(); string programName = fileNames.Count == 1 ? fileNames[0] : "the program"; - string err = Dafny.Main.ParseCheck(fileNames, programName, out dafnyProgram); + string err = Dafny.Main.ParseCheck(fileNames, programName, reporter, out dafnyProgram); if (err != null) { exitValue = ExitValue.DAFNY_ERROR; - printer.ErrorWriteLine(Console.Out, err); + ExecutionEngine.printer.ErrorWriteLine(Console.Out, err); } else if (dafnyProgram != null && !CommandLineOptions.Clo.NoResolve && !CommandLineOptions.Clo.NoTypecheck && DafnyOptions.O.DafnyVerify) { - Dafny.Translator translator = new Dafny.Translator(); + Dafny.Translator translator = new Dafny.Translator(dafnyProgram.reporter); Bpl.Program boogieProgram = translator.Translate(dafnyProgram); if (CommandLineOptions.Clo.PrintFile != null) { @@ -184,12 +182,12 @@ namespace Microsoft.Dafny var allOk = stats.ErrorCount == 0 && stats.InconclusiveCount == 0 && stats.TimeoutCount == 0 && stats.OutOfMemoryCount == 0; switch (oc) { case PipelineOutcome.VerificationCompleted: - printer.WriteTrailer(stats); + ExecutionEngine.printer.WriteTrailer(stats); if ((DafnyOptions.O.Compile && allOk && CommandLineOptions.Clo.ProcsToCheck == null) || DafnyOptions.O.ForceCompile) CompileDafnyProgram(dafnyProgram, fileNames[0]); break; case PipelineOutcome.Done: - printer.WriteTrailer(stats); + ExecutionEngine.printer.WriteTrailer(stats); if (DafnyOptions.O.ForceCompile) CompileDafnyProgram(dafnyProgram, fileNames[0]); break; @@ -265,10 +263,7 @@ namespace Microsoft.Dafny #region Output - - static OutputPrinter printer; - - + class DafnyConsolePrinter : ConsolePrinter { public override void ReportBplError(IToken tok, string message, bool error, TextWriter tw, string category = null) diff --git a/Source/DafnyExtension/DafnyDriver.cs b/Source/DafnyExtension/DafnyDriver.cs index 50d8c2e3..36664a9b 100644 --- a/Source/DafnyExtension/DafnyDriver.cs +++ b/Source/DafnyExtension/DafnyDriver.cs @@ -115,19 +115,21 @@ namespace DafnyLanguage bool ParseAndTypeCheck() { Dafny.ModuleDecl module = new Dafny.LiteralModuleDecl(new Dafny.DefaultModuleDecl(), null); Dafny.BuiltIns builtIns = new Dafny.BuiltIns(); - Dafny.Errors parseErrors = new VSErrors(this); + + var errorReporter = new VSErrorReporter(this); + var parseErrors = new Dafny.Errors(errorReporter); + int errorCount = Dafny.Parser.Parse(_snapshot.GetText(), _filename, _filename, module, builtIns, parseErrors); string errString = Dafny.Main.ParseIncludes(module, builtIns, new List(), parseErrors); if (errorCount != 0 || errString != null) return false; - Dafny.Program program = new Dafny.Program(_filename, module, builtIns); + Dafny.Program program = new Dafny.Program(_filename, module, builtIns, errorReporter); - var r = new VSResolver(program, this); + var r = new Resolver(program); r.ResolveProgram(program); - if (r.ErrorCount != 0) + if (errorReporter.Count(ErrorLevel.Error) != 0) return false; - program.AdditionalInformation.AddRange(r.AdditionalInformation); _program = program; return true; // success } @@ -137,56 +139,31 @@ namespace DafnyLanguage _errors.Add(new DafnyError(filename, line - 1, col - 1, cat, msg, _snapshot, isRecycled, null, System.IO.Path.GetFullPath(this._filename) == filename)); } - class VSErrors : Dafny.Errors - { - DafnyDriver dd; - public VSErrors(DafnyDriver dd) { - this.dd = dd; - } - public override void SynErr(string filename, int line, int col, string msg) { - dd.RecordError(filename, line, col, ErrorCategory.ParseError, msg); - count++; - } - public override void SemErr(string filename, int line, int col, string msg) { - dd.RecordError(filename, line, col, ErrorCategory.ResolveError, msg); - count++; - } - public override void Warning(IToken tok, string msg) { - dd.RecordError(tok.filename, tok.line, tok.col, ErrorCategory.ParseWarning, msg); - } - } - - class VSResolver : Dafny.Resolver + class VSErrorReporter : Dafny.ErrorReporter { DafnyDriver dd; - Dictionary> _additionalInformation = new Dictionary>(); - public List AdditionalInformation { get { return _additionalInformation.Values.SelectMany(i => i).ToList(); } } - public VSResolver(Dafny.Program program, DafnyDriver dd) - : base(program) { + public VSErrorReporter(DafnyDriver dd) { this.dd = dd; - - AdditionalInformationReporter = - (addinfo) - => - { - if (!_additionalInformation.ContainsKey(addinfo.Token)) { - _additionalInformation.Add(addinfo.Token, new HashSet()); - } - _additionalInformation[addinfo.Token].Add(addinfo); - }; - } - - public override void Error(Bpl.IToken tok, string msg, params object[] args) { - string s = string.Format(msg, args); - dd.RecordError(tok.filename, tok.line, tok.col, ErrorCategory.ResolveError, s); - ErrorCount++; } - public override void Warning(IToken tok, string msg, params object[] args) { - if (reportWarnings) { - string s = string.Format(msg, args); - dd.RecordError(tok.filename, tok.line, tok.col, ErrorCategory.ResolveWarning, s); + // TODO: The error tracking could be made better to track the full information returned by Dafny + public override bool Message(MessageSource source, ErrorLevel level, IToken tok, string msg) { + if (base.Message(source, level, tok, msg)) { + switch (level) { + case ErrorLevel.Error: + dd.RecordError(tok.filename, tok.line, tok.col, source == MessageSource.Parser ? ErrorCategory.ParseError : ErrorCategory.ResolveError, msg); + break; + case ErrorLevel.Warning: + dd.RecordError(tok.filename, tok.line, tok.col, source == MessageSource.Parser ? ErrorCategory.ParseWarning : ErrorCategory.ResolveWarning, msg); + break; + case ErrorLevel.Info: + // The AllMessages variable already keeps track of this + break; + } + return true; + } else { + return false; } } } @@ -273,7 +250,7 @@ namespace DafnyLanguage } public static bool Verify(Dafny.Program dafnyProgram, ResolverTagger resolver, string uniqueIdPrefix, string requestId, ErrorReporterDelegate er) { - Dafny.Translator translator = new Dafny.Translator(); + Dafny.Translator translator = new Dafny.Translator(dafnyProgram.reporter); translator.InsertChecksums = true; translator.UniqueIdPrefix = uniqueIdPrefix; Bpl.Program boogieProgram = translator.Translate(dafnyProgram); diff --git a/Source/DafnyExtension/IdentifierTagger.cs b/Source/DafnyExtension/IdentifierTagger.cs index 262dddcd..13991496 100644 --- a/Source/DafnyExtension/IdentifierTagger.cs +++ b/Source/DafnyExtension/IdentifierTagger.cs @@ -136,9 +136,9 @@ namespace DafnyLanguage List newRegions = new List(); - foreach (var addInfo in program.AdditionalInformation) + foreach (var info in program.reporter.AllMessages[ErrorLevel.Info]) { - IdRegion.Add(newRegions, addInfo.Token, addInfo.Text, addInfo.Length); + IdRegion.Add(newRegions, info.token, info.message, info.token.val.Length); } foreach (var module in program.Modules) { diff --git a/Source/DafnyExtension/ResolverTagger.cs b/Source/DafnyExtension/ResolverTagger.cs index 6eaec5a6..0ce68809 100644 --- a/Source/DafnyExtension/ResolverTagger.cs +++ b/Source/DafnyExtension/ResolverTagger.cs @@ -74,10 +74,10 @@ namespace DafnyLanguage switch (err.Category) { case ErrorCategory.ProcessError: case ErrorCategory.ParseError: - case ErrorCategory.ParseWarning: return "syntax error"; // COLOR: red case ErrorCategory.ResolveError: return "compiler error"; // COLOR: blue + case ErrorCategory.ParseWarning: case ErrorCategory.ResolveWarning: return "compiler warning"; // COLOR: blue case ErrorCategory.InternalError: @@ -403,7 +403,7 @@ namespace DafnyLanguage chng(this, new SnapshotSpanEventArgs(new SnapshotSpan(snapshot, 0, snapshot.Length))); } - static TaskErrorCategory CategoryConversion(ErrorCategory cat) + static TaskErrorCategory CategoryConversion(ErrorCategory cat) //CLEMENT: We've lost that info { switch (cat) { diff --git a/Source/DafnyServer/DafnyHelper.cs b/Source/DafnyServer/DafnyHelper.cs index 10d98677..d6c3f5c7 100644 --- a/Source/DafnyServer/DafnyHelper.cs +++ b/Source/DafnyServer/DafnyHelper.cs @@ -30,14 +30,14 @@ namespace Microsoft.Dafny { private string fname; private string source; - private Dafny.Errors errors; + private readonly Dafny.ErrorReporter reporter; private Dafny.Program dafnyProgram; private Bpl.Program boogieProgram; public DafnyHelper(string fname, string source) { this.fname = fname; this.source = source; - this.errors = new Dafny.Errors(); + this.reporter = new Dafny.ConsoleErrorReporter(); } public bool Verify() { @@ -47,10 +47,10 @@ namespace Microsoft.Dafny { private bool Parse() { Dafny.ModuleDecl module = new Dafny.LiteralModuleDecl(new Dafny.DefaultModuleDecl(), null); Dafny.BuiltIns builtIns = new Dafny.BuiltIns(); - var success = (Dafny.Parser.Parse(source, fname, fname, module, builtIns, errors) == 0 && - Dafny.Main.ParseIncludes(module, builtIns, new List(), errors) == null); + var success = (Dafny.Parser.Parse(source, fname, fname, module, builtIns, new Dafny.Errors(reporter)) == 0 && + Dafny.Main.ParseIncludes(module, builtIns, new List(), new Dafny.Errors(reporter)) == null); if (success) { - dafnyProgram = new Dafny.Program(fname, module, builtIns); + dafnyProgram = new Dafny.Program(fname, module, builtIns, reporter); } return success; } @@ -58,11 +58,11 @@ namespace Microsoft.Dafny { private bool Resolve() { var resolver = new Dafny.Resolver(dafnyProgram); resolver.ResolveProgram(dafnyProgram); - return resolver.ErrorCount == 0; + return reporter.Count(ErrorLevel.Error) == 0; } private bool Translate() { - var translator = new Dafny.Translator() { InsertChecksums = true, UniqueIdPrefix = null }; //FIXME check if null is OK for UniqueIdPrefix + var translator = new Dafny.Translator(reporter) { InsertChecksums = true, UniqueIdPrefix = null }; //FIXME check if null is OK for UniqueIdPrefix boogieProgram = translator.Translate(dafnyProgram); // FIXME how are translation errors reported? return true; } @@ -74,7 +74,8 @@ namespace Microsoft.Dafny { ExecutionEngine.CoalesceBlocks(boogieProgram); ExecutionEngine.Inline(boogieProgram); - switch (ExecutionEngine.InferAndVerify(boogieProgram, new PipelineStatistics(), "ServerProgram", null, DateTime.UtcNow.Ticks.ToString())) { // FIXME check if null is ok for error delegate + //FIXME Could capture errors instead of printing them (pass a delegate instead of null) + switch (ExecutionEngine.InferAndVerify(boogieProgram, new PipelineStatistics(), "ServerProgram", null, DateTime.UtcNow.Ticks.ToString())) { case PipelineOutcome.Done: case PipelineOutcome.VerificationCompleted: return true; -- cgit v1.2.3 From 674e30357980e1192ac532f4bd16c529cedc7fdc Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Wed, 19 Aug 2015 10:51:44 -0700 Subject: Draft out a more advanced version of trigger generation This new version will include a cleaner pipeline, and trigger splitting. --- Source/Dafny/DafnyPipeline.csproj | 7 +- Source/Dafny/Rewriter.cs | 46 +- Source/Dafny/Translator.cs | 4 +- Source/Dafny/TriggerGenerator.cs | 1064 ------------------------ Source/Dafny/Triggers/QuantifierSplitter.cs | 66 ++ Source/Dafny/Triggers/QuantifiersCollection.cs | 104 +++ Source/Dafny/Triggers/TriggerExtensions.cs | 489 +++++++++++ Source/Dafny/Triggers/TriggerGenerator.cs | 28 + Source/Dafny/Triggers/TriggerUtils.cs | 94 +++ Source/Dafny/Triggers/TriggersCollector.cs | 256 ++++++ Test/dafny0/SeqFromArray.dfy.expect | 3 + 11 files changed, 1089 insertions(+), 1072 deletions(-) delete mode 100644 Source/Dafny/TriggerGenerator.cs create mode 100644 Source/Dafny/Triggers/QuantifierSplitter.cs create mode 100644 Source/Dafny/Triggers/QuantifiersCollection.cs create mode 100644 Source/Dafny/Triggers/TriggerExtensions.cs create mode 100644 Source/Dafny/Triggers/TriggerGenerator.cs create mode 100644 Source/Dafny/Triggers/TriggerUtils.cs create mode 100644 Source/Dafny/Triggers/TriggersCollector.cs diff --git a/Source/Dafny/DafnyPipeline.csproj b/Source/Dafny/DafnyPipeline.csproj index 626bb26a..5a824c36 100644 --- a/Source/Dafny/DafnyPipeline.csproj +++ b/Source/Dafny/DafnyPipeline.csproj @@ -144,7 +144,12 @@ - + + + + + + diff --git a/Source/Dafny/Rewriter.cs b/Source/Dafny/Rewriter.cs index cb71b80d..4e274189 100644 --- a/Source/Dafny/Rewriter.cs +++ b/Source/Dafny/Rewriter.cs @@ -39,22 +39,56 @@ namespace Microsoft.Dafny } public class TriggerGeneratingRewriter : IRewriter { + Triggers.QuantifierCollectionsFinder finder; + internal TriggerGeneratingRewriter(ErrorReporter reporter) : base(reporter) { Contract.Requires(reporter != null); + this.finder = new Triggers.QuantifierCollectionsFinder(reporter); + } + + internal override void PostResolve(ModuleDefinition m) { + foreach (var decl in ModuleDefinition.AllCallables(m.TopLevelDecls)) { //CLEMENT + if (decl is Function) { + var function = (Function)decl; + finder.Visit(function.Ens, null); + finder.Visit(function.Req, null); + if (function.Body != null) { + finder.Visit(function.Body, null); + } + } else if (decl is Method) { + var method = (Method)decl; + finder.Visit(method.Ens, null); + finder.Visit(method.Req, null); + if (method.Body != null) { + finder.Visit(method.Body, null); + } + } + } + } + } + + internal class QuantifierSplittingRewriter : IRewriter { + internal QuantifierSplittingRewriter(ErrorReporter reporter) : base(reporter) { + Contract.Requires(reporter != null); } internal override void PostResolve(ModuleDefinition m) { + var splitter = new Triggers.QuantifierSplitter(); foreach (var decl in ModuleDefinition.AllCallables(m.TopLevelDecls)) { if (decl is Function) { var function = (Function)decl; - //TriggerGenerator.AddTriggers(function.Ens, Resolver); - //TriggerGenerator.AddTriggers(function.Req, Resolver); - //TriggerGenerator.AddTriggers(function.Body, Resolver); + splitter.Visit(function.Ens); + splitter.Visit(function.Req); + if (function.Body != null) { + splitter.Visit(function.Body); + } } else if (decl is Method) { var method = (Method)decl; - //TriggerGenerator.AddTriggers(method.Ens, Resolver); - //TriggerGenerator.AddTriggers(method.Req, Resolver); - //TriggerGenerator.AddTriggers(method.Body, Resolver); + splitter.Visit(method.Ens); + splitter.Visit(method.Req); + if (method.Body != null) { + splitter.Visit(method.Body); + } } } } diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 039aa56f..b5d89abd 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -12895,6 +12895,8 @@ namespace Microsoft.Dafny { // that needed to be proved about the function was proved already in the previous module, even without the body definition). } else if (!FunctionBodyIsAvailable(f, currentModule)) { // Don't inline opaque functions or foreign protected functions + } else if (Attributes.Contains(f.Attributes, "no_inline")) { + // User manually prevented inlining } else if (CanSafelyInline(fexp, f)) { // inline this body var body = GetSubstitutedBody(fexp, f, false); @@ -13106,7 +13108,7 @@ namespace Microsoft.Dafny { } private bool CanSafelySubstitute(ISet protectedVariables, IVariable variable, Expression substitution) { - return !(protectedVariables.Contains(variable) && TriggerGenerator.IsTriggerKiller(substitution)); + return !(protectedVariables.Contains(variable) && Dafny.Triggers.TriggersCollector.IsTriggerKiller(substitution)); } private class VariablesCollector: BottomUpVisitor { diff --git a/Source/Dafny/TriggerGenerator.cs b/Source/Dafny/TriggerGenerator.cs deleted file mode 100644 index 04d96b01..00000000 --- a/Source/Dafny/TriggerGenerator.cs +++ /dev/null @@ -1,1064 +0,0 @@ -// #define DEBUG_AUTO_TRIGGERS -#define THROW_UNSUPPORTED_COMPARISONS - -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Boogie; -using System.Diagnostics.Contracts; -using System.Text; -using System.Diagnostics; - -//FIXME Generated triggers should be _triggers -//FIXME: When scoring, do not consider old(x) to be higher than x. - -/* High level note: There are really two processes going on here. One is finding quantifiers; - * the other is walking the subtree corresponding to each quantifier, and finding trigger candidates. - * These two processes are interleaved, because we can look for trigger candidates as we look for - * quantifiers. Since the visitor starts from the bottom of the tree, the recursive annotation - * procedure never causes deep recursion; every call it makes hits the cache that has been built - * during the visiting of lower level nodes. - * - * Note also that it wouldn't be enough to just use the recursive procedure: it doesn't visit - * statements, for example. - */ - -namespace Microsoft.Dafny { - class TriggerCandidate { - internal Expression Expr; - internal ISet Variables; - internal List MatchesInQuantifierBody; - - public override string ToString() { - return Printer.ExprToString(Expr); - } - } - - class MultiTriggerCandidate { - internal List Candidates; - internal List Tags; - internal double Score; - - private List potentialMatchingLoops; - internal List PotentialMatchingLoops { - get { - if (potentialMatchingLoops == null) { - //FIXME could be optimized by looking at the bindings instead of doing full equality - var candidates = Candidates.Deduplicate((x, y) => ExprExtensions.ExpressionEq(x.Expr, y.Expr)); - potentialMatchingLoops = candidates.SelectMany(candidate => candidate.MatchesInQuantifierBody) - .Deduplicate((x, y) => ExprExtensions.ExpressionEq(x.Expr, y.Expr)).Where(tm => tm.CouldCauseLoops(candidates)).ToList(); - } - - return potentialMatchingLoops; - } - } - - internal MultiTriggerCandidate(List candidates) { - Candidates = candidates; - Tags = new List(); - } - - internal bool MentionsAll(List vars) { - var candidates = Candidates; - return vars.All(x => candidates.Any(candidate => candidate.Variables.Contains(x))); //TODO Perfs? - } - - public override string ToString() { - return String.Format("[{0:G2}] {1}", Score, String.Join(", ", Candidates)); - } - - public String AsDafnyAttributeString(bool wrap = true, bool includeTags = false) { - var repr = Candidates.MapConcat(t => Printer.ExprToString(t.Expr), ", "); - if (wrap) { - repr = "{:trigger " + repr + "}"; - } - if (includeTags && Tags != null) { - repr += " (" + String.Join("; ", Tags) + ")"; - } - return repr; - } - } - - - class TriggerAnnotation { - internal bool IsTriggerKiller; - internal ISet Variables; - internal readonly List PrivateCandidates; - internal readonly List ExportedCandidates; - - internal TriggerAnnotation(bool IsTriggerKiller, IEnumerable Variables, - IEnumerable AllCandidates, IEnumerable PrivateCandidates = null) { - this.IsTriggerKiller = IsTriggerKiller; - this.Variables = new HashSet(Variables); - - this.PrivateCandidates = new List(PrivateCandidates == null ? Enumerable.Empty() : PrivateCandidates); - this.ExportedCandidates = new List(AllCandidates == null ? Enumerable.Empty() : AllCandidates.Except(this.PrivateCandidates)); - } - - public override string ToString() { - StringBuilder sb = new StringBuilder(); - string indent = " {0}", nindent = "\n - {0}", subindent = "\n * {0}"; - - sb.AppendFormat(indent, IsTriggerKiller); - - sb.AppendFormat(nindent, "Variables:"); - foreach (var bv in Variables) { - sb.AppendFormat(subindent, bv.Name); - } - - sb.AppendFormat(nindent, "Exported candidates:"); - foreach (var candidate in ExportedCandidates) { - sb.AppendFormat(subindent, candidate); - } - - if (PrivateCandidates.Any()) { - sb.AppendFormat(nindent, "Private candidates:"); - foreach (var candidate in PrivateCandidates) { - sb.AppendFormat(subindent, candidate); - } - } - - return sb.ToString(); - } - } - - public class TriggerGenerator : BottomUpVisitor { - List quantifiers; - Dictionary annotations; - - Action AdditionalInformationReporter; - - private TriggerGenerator(Action additionalInformationReporter) { - Contract.Requires(additionalInformationReporter != null); - this.quantifiers = new List(); - this.annotations = new Dictionary(); - this.AdditionalInformationReporter = additionalInformationReporter; - } - - private List MergeAlterFirst(List a, List b) { - Contract.Requires(a != null); - Contract.Requires(b != null); - a.AddRange(b); - return a; - } - - private ISet MergeAlterFirst(ISet a, ISet b) { - Contract.Requires(a != null); - Contract.Requires(b != null); - a.UnionWith(b); - return a; - } - - private T ReduceAnnotatedSubExpressions(Expression expr, T seed, Func map, Func reduce) { - return expr.SubExpressions.Select(e => map(Annotate(e))) - .Aggregate(seed, (acc, e) => reduce(acc, e)); - } - - private List CollectExportedCandidates(Expression expr) { - return ReduceAnnotatedSubExpressions>(expr, new List(), a => a.ExportedCandidates, MergeAlterFirst); - } - - private ISet CollectVariables(Expression expr) { - return ReduceAnnotatedSubExpressions(expr, new HashSet(), a => a.Variables, MergeAlterFirst); - } - - private bool CollectIsKiller(Expression expr) { - return ReduceAnnotatedSubExpressions(expr, false, a => a.IsTriggerKiller, (a, b) => a || b); - } - - private IEnumerable OnlyPrivateCandidates(List candidates, IEnumerable privateVars) { - return candidates.Where(c => privateVars.Intersect(c.Variables).Any()); //TODO Check perf - } - - private TriggerAnnotation Annotate(Expression expr) { - TriggerAnnotation cached; - if (annotations.TryGetValue(expr, out cached)) { - return cached; - } - - expr.SubExpressions.Iter(e => Annotate(e)); //NOTE: These values are all cached - - TriggerAnnotation annotation; // TODO: Using ApplySuffix fixes the unresolved members problem in GenericSort - if (expr is FunctionCallExpr || expr is SeqSelectExpr || expr is MemberSelectExpr || expr is OldExpr || - (expr is UnaryOpExpr && (((UnaryOpExpr)expr).Op == UnaryOpExpr.Opcode.Cardinality)) || // FIXME || ((UnaryOpExpr)expr).Op == UnaryOpExpr.Opcode.Fresh oesn'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) { - annotation = AnnotateQuantifier((QuantifierExpr)expr); - } else if (expr is LetExpr) { - annotation = AnnotateLetExpr((LetExpr)expr); - } else if (expr is IdentifierExpr) { - annotation = AnnotateIdentifier((IdentifierExpr)expr); - } else if (expr is ApplySuffix) { - annotation = AnnotateApplySuffix((ApplySuffix)expr); - } else if (expr is ConcreteSyntaxExpression || - expr is LiteralExpr || - expr is OldExpr || - expr is ThisExpr || - expr is BoxingCastExpr || - expr is DatatypeValue) { - annotation = AnnotateOther(expr, false); - } else { - annotation = AnnotateOther(expr, true); - } - - DebugTriggers("{0} ({1})\n{2}", Printer.ExprToString(expr), expr.GetType(), annotation); - annotations[expr] = annotation; - return annotation; - } - - [Conditional("DEBUG_AUTO_TRIGGERS")] - private static void DebugTriggers(string format, params object[] more) { - Console.Error.WriteLine(format, more); - } - - private BinaryExpr.ResolvedOpcode RemoveNotInBinaryExprIn(BinaryExpr.ResolvedOpcode opcode) { - switch(opcode) { - case BinaryExpr.ResolvedOpcode.NotInMap: - return BinaryExpr.ResolvedOpcode.InMap; - case BinaryExpr.ResolvedOpcode.NotInSet: - return BinaryExpr.ResolvedOpcode.InSet; - case BinaryExpr.ResolvedOpcode.NotInSeq: - return BinaryExpr.ResolvedOpcode.InSeq; - case BinaryExpr.ResolvedOpcode.NotInMultiSet: - return BinaryExpr.ResolvedOpcode.InMultiSet; - } - - Contract.Assert(false); - throw new ArgumentException(); - } - - private Expression CleanupExpr(Expression expr, out bool isKiller) { - isKiller = false; - - if (!(expr is BinaryExpr)) { - return expr; - } - - var bexpr = expr as BinaryExpr; - - BinaryExpr new_expr = bexpr; - if (bexpr.Op == BinaryExpr.Opcode.NotIn) { - new_expr = new BinaryExpr(bexpr.tok, BinaryExpr.Opcode.In, bexpr.E0, bexpr.E1); - new_expr.ResolvedOp = RemoveNotInBinaryExprIn(bexpr.ResolvedOp); - new_expr.Type = bexpr.Type; - } - - Expression returned_expr = new_expr; - if (new_expr.ResolvedOp == BinaryExpr.ResolvedOpcode.InMultiSet) { - returned_expr = new SeqSelectExpr(new_expr.tok, true, new_expr.E1, new_expr.E0, null); - returned_expr.Type = bexpr.Type; - isKiller = true; // [a in s] becomes [s[a] > 0], which is a trigger killer - } - - return returned_expr; - } - - private TriggerAnnotation AnnotatePotentialCandidate(Expression expr) { - bool expr_is_killer = false; - var new_expr = CleanupExpr(expr, out expr_is_killer); - var new_candidate = new TriggerCandidate { Expr = new_expr, Variables = CollectVariables(expr) }; - - List collected_candidates = CollectExportedCandidates(expr); - var children_contain_killers = CollectIsKiller(expr); - - if (!children_contain_killers) { - // Add only if the children are not killers; the head has been cleaned up into non-killer form - collected_candidates.Add(new_candidate); - } - - // This new node is a killer if its children were killers, or if it's non-cleaned-up head is a killer - return new TriggerAnnotation(children_contain_killers || expr_is_killer, new_candidate.Variables, collected_candidates); - } - - private TriggerAnnotation AnnotateApplySuffix(ApplySuffix expr) { - // This is a bit tricky. A funcall node is generally meaningful as a trigger candidate, - // but when it's part of an ApplySuffix the function call itself may not resolve properly - // when the second round of resolving is done after modules are duplicated. - // Thus first we annotate expr and create a trigger candidate, and then we remove the - // candidate matching its direct subexpression if needed. Not that function calls are not the - // only possible child here; there can be DatatypeValue nodes, for example (see vstte2012/Combinators.dfy). - var annotation = AnnotatePotentialCandidate(expr); - // Comparing by reference is fine here. Using sets could yield a small speedup - annotation.ExportedCandidates.RemoveAll(candidate => expr.SubExpressions.Contains(candidate.Expr)); - return annotation; - } - - private TriggerAnnotation AnnotateQuantifierOrLetExpr(Expression expr, IEnumerable boundVars) { - var candidates = CollectExportedCandidates(expr); - return new TriggerAnnotation(true, CollectVariables(expr), candidates, OnlyPrivateCandidates(candidates, boundVars)); - } - - private TriggerAnnotation AnnotateQuantifier(QuantifierExpr expr) { - quantifiers.Add(expr); - return AnnotateQuantifierOrLetExpr(expr, expr.BoundVars); - } - - private TriggerAnnotation AnnotateLetExpr(LetExpr expr) { - return AnnotateQuantifierOrLetExpr(expr, expr.BoundVars); - } - - private TriggerAnnotation AnnotateIdentifier(IdentifierExpr expr) { - return new TriggerAnnotation(false, Enumerable.Repeat(expr.Var, 1), null); - } - - private TriggerAnnotation AnnotateOther(Expression expr, bool isTriggerKiller) { - return new TriggerAnnotation(isTriggerKiller || CollectIsKiller(expr), CollectVariables(expr), CollectExportedCandidates(expr)); - } - - private static List CopyAndAdd(List seq, T elem) { - var copy = new List(seq); - copy.Add(elem); - return copy; - } - - private static IEnumerable> AllSubsets(IList source, int offset) { - if (offset >= source.Count) { - yield return new List(); - yield break; - } - - foreach (var subset in AllSubsets(source, offset + 1)) { - yield return CopyAndAdd(subset, source[offset]); - yield return new List(subset); - } - } - - private static IEnumerable> AllNonEmptySubsets(IEnumerable source) { - List all = new List(source); - foreach (var subset in AllSubsets(all, 0)) { - if (subset.Count > 0) { - yield return subset; - } - } - } - - private static bool DefaultCandidateFilteringFunction(TriggerCandidate candidate, QuantifierExpr quantifier) { - //FIXME this will miss rewritten expressions (CleanupExpr). Should introduce an OriginalExpr to compare. - candidate.MatchesInQuantifierBody = quantifier.SubexpressionsMatchingTrigger(candidate.Expr).ToList(); - return true; - } - - private static bool DefaultMultiCandidateFilteringFunction(MultiTriggerCandidate multiCandidate, QuantifierExpr quantifier) { - var allowsLoops = quantifier.Attributes.AsEnumerable().Any(a => a.Name == "loop"); - - if (!allowsLoops && multiCandidate.PotentialMatchingLoops.Any()) { - multiCandidate.Tags.Add(String.Format("matching loop with {0}", multiCandidate.PotentialMatchingLoops.MapConcat(tm => Printer.ExprToString(tm.Expr), ", "))); - } - return multiCandidate.MentionsAll(quantifier.BoundVars) && (allowsLoops || !multiCandidate.PotentialMatchingLoops.Any()); - } - - private static double DefaultMultiCandidateScoringFunction(MultiTriggerCandidate multi_candidate) { - return 1.0; - } - - private static IEnumerable DefaultMultiCandidateSelectionFunction(List multi_candidates) { - return multi_candidates; - } - - // CLEMENT: Make these customizable - internal Func CandidateFilteringFunction = DefaultCandidateFilteringFunction; - internal Func MultiCandidateFilteringFunction = DefaultMultiCandidateFilteringFunction; - internal Func MultiCandidateScoringFunction = DefaultMultiCandidateScoringFunction; - internal Func, IEnumerable> MultiCandidateSelectionFunction = DefaultMultiCandidateSelectionFunction; - - struct MultiCandidatesCollection { - internal List AllCandidates; - internal List SelectedCandidates; - internal List RejectedCandidates; - internal List SelectedMultiCandidates; - internal List RejectedMultiCandidates; - internal List FinalMultiCandidates; - - public MultiCandidatesCollection(QuantifierExpr quantifier, - TriggerAnnotation annotation, - Func CandidateFilteringFunction, - Func MultiCandidateFilteringFunction, - Func MultiCandidateScoringFunction, - Func, IEnumerable> MultiCandidateSelectionFunction) { - - Contract.Requires(annotation != null); - Contract.Requires(quantifier != null); - Contract.Requires(CandidateFilteringFunction != null); - Contract.Requires(MultiCandidateFilteringFunction != null); - Contract.Requires(MultiCandidateScoringFunction != null); - Contract.Requires(MultiCandidateSelectionFunction != null); - - AllCandidates = annotation.PrivateCandidates.Deduplicate((x, y) => ExprExtensions.ExpressionEq(x.Expr, y.Expr)); - Partition(AllCandidates, - x => CandidateFilteringFunction(x, quantifier), out SelectedCandidates, out RejectedCandidates); - Partition(AllNonEmptySubsets(SelectedCandidates).Select(s => new MultiTriggerCandidate(s)), - x => MultiCandidateFilteringFunction(x, quantifier), out SelectedMultiCandidates, out RejectedMultiCandidates); - SelectedMultiCandidates.Iter(x => x.Score = MultiCandidateScoringFunction(x)); - FinalMultiCandidates = MultiCandidateSelectionFunction(SelectedMultiCandidates).ToList(); - } - - private static void Partition(IEnumerable elements, Func predicate, out List positive, out List negative) { - positive = new List(); - negative = new List(); - foreach (var c in elements) { - (predicate(c) ? positive : negative).Add(c); - } - } - - internal string Warning() { - if (AllCandidates.Count == 0) { - return "No triggers found in the body of this quantifier."; - } else if (SelectedCandidates.Count == 0) { - return String.Format("No suitable triggers found. Candidate building blocks for a good trigger where [{0}], but none these terms passed the initial selection stage.", String.Join(", ", AllCandidates)); - } else if (SelectedMultiCandidates.Count == 0) { - return String.Format("No suitable set of triggers found. Candidate building blocks for a good trigger where [{0}], but no subset of these terms passed the subset selection stage.", String.Join(", ", SelectedCandidates)); - } else if (FinalMultiCandidates.Count == 0) { - return String.Format("No suitable set of triggers found. Candidates where [{0}], but none passed the final selection stage.", String.Join(", ", SelectedMultiCandidates)); - } else { - return null; - } - } - - private void WriteIndented(StringBuilder builder, string indent, IEnumerable elements) { - foreach (var element in elements) { - builder.Append(indent).AppendLine(element.ToString()); - } - } - - private void WriteListOfCandidates(StringBuilder builder, string indent, IEnumerable elements) { - if (elements.Any()) { - builder.AppendLine(); - WriteIndented(builder, indent, elements); - } else { - builder.AppendLine(" (None)"); - } - } - - public override string ToString() { - var repr = new StringBuilder(); - var indent = " "; - repr.Append(" All:"); - WriteListOfCandidates(repr, indent, AllCandidates); - repr.Append(" Selected1:"); - WriteListOfCandidates(repr, indent, SelectedCandidates); - repr.Append(" PreFilter:"); - WriteListOfCandidates(repr, indent, AllNonEmptySubsets(AllCandidates).Select(c => String.Join(", ", c))); - repr.Append(" SelectedMulti:"); - WriteListOfCandidates(repr, indent, SelectedMultiCandidates.Select(c => String.Join(", ", c))); - repr.Append(" Final:"); - WriteListOfCandidates(repr, indent, FinalMultiCandidates); - return repr.ToString(); - } - } - - private MultiCandidatesCollection PickMultiTriggers(QuantifierExpr quantifier) { - var annotation = Annotate(quantifier); - DebugTriggers("Quantifier {0}:\n{1}", Printer.ExprToString(quantifier), annotation); - return new MultiCandidatesCollection(quantifier, annotation, CandidateFilteringFunction, MultiCandidateFilteringFunction, MultiCandidateScoringFunction, MultiCandidateSelectionFunction); - } - - private void AddTrigger(QuantifierExpr quantifier) { - DebugTriggers(" Final results:\n{0}", PickMultiTriggers(quantifier)); - - if (quantifier.Attributes.AsEnumerable().Any(aa => aa.Name == "trigger" || aa.Name == "no_trigger")) { - DebugTriggers("Not generating triggers for {0}", Printer.ExprToString(quantifier)); //FIXME: no_trigger is passed down to Boogie - return; - } - - var multi_candidates = PickMultiTriggers(quantifier); - foreach (var multi_candidate in multi_candidates.FinalMultiCandidates) { //TODO: error message for when no triggers found - quantifier.Attributes = new Attributes("trigger", multi_candidate.Candidates.Select(t => t.Expr).ToList(), quantifier.Attributes); - } - - 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); - } - - 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); - } - - string warning = multi_candidates.Warning(); - if (warning != null) { - // FIXME reenable Resolver.Warning(quantifier.tok, warning); - } - } - - internal static bool IsTriggerKiller(Expression expr) { - var annotation = new TriggerGenerator((x, y, z) => { }).Annotate(expr); - return annotation.IsTriggerKiller; - } - - private string JoinStringsWithHeader(string header, IEnumerable lines) { - return header + String.Join(Environment.NewLine + new String(' ', header.Length), lines); - } - - private void AddTriggers_Internal() { - foreach (var quantifier in quantifiers) { - AddTrigger(quantifier); - } - } - - private void AddTriggers_Internal(Expression root) { - Visit(root); - AddTriggers_Internal(); - } - - private void AddTriggers_Internal(Statement root) { - Visit(root); - AddTriggers_Internal(); - } - - internal static void AddTriggers(Expression root, Resolver resolver) { - if (root == null) - return; - - DebugTriggers("== From {0} visiting expr: {1}", new StackFrame(1).GetMethod().Name, Printer.ExprToString(root)); - TriggerGenerator generator = new TriggerGenerator(null); - generator.AddTriggers_Internal(root); - } - - internal static void AddTriggers(Statement root, Resolver resolver) { - if (root == null) - return; - - DebugTriggers("== From {0} visiting statement: {1}", new StackFrame(1).GetMethod().Name, Printer.StatementToString(root)); - TriggerGenerator generator = new TriggerGenerator(null); - generator.AddTriggers_Internal(root); - } - - internal static void AddTriggers(IEnumerable roots, Resolver resolver) { - DebugTriggers("== From {0} visiting expressions: {1}", new StackFrame(1).GetMethod().Name, - roots.MapConcat(Printer.ExprToString, ", ")); - foreach (var expr in roots) { - AddTriggers(expr, resolver); - } - } - - internal static void AddTriggers(IEnumerable roots, Resolver resolver) { - DebugTriggers("== From {0} visiting expressions: {1}", new StackFrame(1).GetMethod().Name, - roots.MapConcat(root => Printer.ExprToString(root.E), ", ")); - foreach (var expr in roots) { - AddTriggers(expr.E, resolver); - } - } - - protected override void VisitOneExpr(Expression expr) { - Annotate(expr); - } - } - - internal static class DeduplicateExtension { - public static List Deduplicate(this IEnumerable seq, Func eq) { - List deduplicated = new List(); - - foreach (var elem in seq) { - if (!deduplicated.Any(other => eq(elem, other))) { - deduplicated.Add(elem); - } - } - - return deduplicated; - } - } - - static class ExprExtensions { - static IEnumerable AllSubExpressions(this Expression expr, bool strict = false) { - foreach (var subexpr in expr.SubExpressions) { - foreach (var r_subexpr in AllSubExpressions(subexpr, false)) { - yield return r_subexpr; - } - yield return subexpr; - } - - if (expr is StmtExpr) { - foreach (var r_subexpr in AllSubExpressions(((StmtExpr)expr).S, false)) { - yield return r_subexpr; - } - } - - if (!strict) { - yield return expr; - } - } - - private static IEnumerable AllSubExpressions(this Statement stmt, bool strict = false) { - foreach (var subexpr in stmt.SubExpressions) { - foreach (var r_subexpr in AllSubExpressions(subexpr, false)) { - yield return r_subexpr; - } - yield return subexpr; - } - - foreach (var substmt in stmt.SubStatements) { - foreach (var r_subexpr in AllSubExpressions(substmt, false)) { - yield return r_subexpr; - } - } - } - - internal static bool ExpressionEq(this Expression expr1, Expression expr2) { - expr1 = GetResolved(expr1); - expr2 = GetResolved(expr2); - - return ShallowEq_Top(expr1, expr2) && SameLists(expr1.SubExpressions, expr2.SubExpressions, (e1, e2) => ExpressionEq(e1, e2)); - } - - internal static bool ExpressionEqModuloVariableNames(this Expression expr1, Expression expr2) { - expr1 = GetResolved(expr1); - expr2 = GetResolved(expr2); - - if (expr1 is IdentifierExpr) { - return expr2 is IdentifierExpr; - } - - return ShallowEq_Top(expr1, expr2) && SameLists(expr1.SubExpressions, expr2.SubExpressions, (e1, e2) => ExpressionEqModuloVariableNames(e1, e2)); - } - - private static bool MatchesTrigger(this Expression expr, Expression trigger, ISet holes, Dictionary bindings) { - expr = GetResolved(expr); - trigger = GetResolved(trigger); - - if (trigger is IdentifierExpr) { - var var = ((IdentifierExpr)trigger).Var; - - if (holes.Contains(var)) { - Expression existing_binding = null; - if (bindings.TryGetValue(var, out existing_binding)) { - return ExpressionEq(expr, existing_binding); - } else { - bindings[var] = expr; - return true; - } - } - } - - return ShallowEq_Top(expr, trigger) && SameLists(expr.SubExpressions, trigger.SubExpressions, (e1, e2) => MatchesTrigger(e1, e2, holes, bindings)); - } - - internal struct TriggerMatch { - internal Expression Expr; - internal Dictionary Bindings; - - internal bool CouldCauseLoops(IEnumerable candidates) { - // A match for a trigger in the body of a quantifier can be a problem if - // it yields to a matching loop: for example, f(x) is a bad trigger in - // forall x, y :: f(x) = f(f(x)) - // In general, any such match can lead to a loop, but two special cases - // will only lead to a finite number of instantiations: - // 1. The match equals one of the triggers in the set of triggers under - // consideration. For example, { f(x) } a bad trigger above, but the - // pair { f(x), f(f(x)) } is fine (instantiating won't yield new - // matches) - // 2. The match only differs from one of these triggers by variable - // names. This is a superset of the previous case. - var expr = Expr; - return !candidates.Any(c => c.Expr.ExpressionEqModuloVariableNames(expr)); - } - } - - private static TriggerMatch? MatchAgainst(this Expression expr, Expression trigger, ISet holes) { - var bindings = new Dictionary(); - return expr.MatchesTrigger(trigger, holes, bindings) ? new TriggerMatch { Expr = expr, Bindings = bindings } : (TriggerMatch?)null; - } - - internal static IEnumerable SubexpressionsMatchingTrigger(this QuantifierExpr quantifier, Expression trigger) { - return quantifier.Term.AllSubExpressions() - .Select(e => e.MatchAgainst(trigger, new HashSet(quantifier.BoundVars))) - .Where(e => e.HasValue).Select(e => e.Value); - } - - private static bool SameLists(IEnumerable list1, IEnumerable list2, Func comparer) { - if (ReferenceEquals(list1, list2)) { - return true; - } else if ((list1 == null) != (list2 == null)) { - return false; - } - - var it1 = list1.GetEnumerator(); - var it2 = list2.GetEnumerator(); - bool it1_has, it2_has; - bool acc = true; - - do { - it1_has = it1.MoveNext(); - it2_has = it2.MoveNext(); - - if (it1_has == true && it2_has == true) { - acc = acc && comparer(it1.Current, it2.Current); - } - } while (it1_has && it2_has); - - return it1_has == it2_has && acc; - } - - private static bool SameNullity(T x1, T x2) where T : class { - return (x1 == null) == (x2 == null); - } - - private static bool ShallowSameAttributes(Attributes attributes1, Attributes attributes2) { - return SameLists(attributes1.AsEnumerable(), attributes2.AsEnumerable(), ShallowSameSingleAttribute); - } - - private static bool ShallowSameSingleAttribute(Attributes arg1, Attributes arg2) { - return arg1.Name == arg2.Name; - } - - private static bool SameBoundVar(IVariable arg1, IVariable arg2) { - return arg1 == arg2 || - (arg1.Name == arg2.Name && - arg1.CompileName == arg2.CompileName && - arg1.DisplayName == arg2.DisplayName && - arg1.UniqueName == arg2.UniqueName && - arg1.IsGhost == arg2.IsGhost && - arg1.IsMutable == arg2.IsMutable); //FIXME compare types? - } - - private static Expression GetResolved(Expression expr) { - if (expr is ConcreteSyntaxExpression) { - return ((ConcreteSyntaxExpression)expr).ResolvedExpression; - } - return expr; - } - - /// - /// Compares two abstract syntax expressions, looking only at their direct members. Subexpressions and substatements are not compared. - /// - /// - internal static bool ShallowEq_Top(Expression expr1, Expression expr2) { - Contract.Requires(expr1 != null); - Contract.Requires(expr2 != null); - - // We never compare concrete expressions - Contract.Requires(!(expr1 is ConcreteSyntaxExpression)); - Contract.Requires(!(expr2 is ConcreteSyntaxExpression)); - - // CPC: Hey future editor: the following block of code is auto-generated. Just add your own cases at the end. - // This could be a visitor pattern, except I need to visit a pair of nodes. - // It could also be implemented in each individual class. I'd have a slight preference for that. - // This really just wants to use double dispatch. - if (expr1 is UnboxingCastExpr && expr2 is UnboxingCastExpr) { - return ShallowEq((UnboxingCastExpr)expr1, (UnboxingCastExpr)expr2); - } else if (expr1 is BoxingCastExpr && expr2 is BoxingCastExpr) { - return ShallowEq((BoxingCastExpr)expr1, (BoxingCastExpr)expr2); - } else if (expr1 is MatchExpr && expr2 is MatchExpr) { - return ShallowEq((MatchExpr)expr1, (MatchExpr)expr2); - } else if (expr1 is ITEExpr && expr2 is ITEExpr) { - return ShallowEq((ITEExpr)expr1, (ITEExpr)expr2); - } else if (expr1 is StmtExpr && expr2 is StmtExpr) { - return ShallowEq((StmtExpr)expr1, (StmtExpr)expr2); - } else if (expr1 is WildcardExpr && expr2 is WildcardExpr) { - return ShallowEq((WildcardExpr)expr1, (WildcardExpr)expr2); - } else if (expr1 is ComprehensionExpr && expr2 is ComprehensionExpr) { - return ShallowEq((ComprehensionExpr)expr1, (ComprehensionExpr)expr2); - } else if (expr1 is NamedExpr && expr2 is NamedExpr) { - return ShallowEq((NamedExpr)expr1, (NamedExpr)expr2); - } else if (expr1 is LetExpr && expr2 is LetExpr) { - return ShallowEq((LetExpr)expr1, (LetExpr)expr2); - } else if (expr1 is TernaryExpr && expr2 is TernaryExpr) { - return ShallowEq((TernaryExpr)expr1, (TernaryExpr)expr2); - } else if (expr1 is BinaryExpr && expr2 is BinaryExpr) { - return ShallowEq((BinaryExpr)expr1, (BinaryExpr)expr2); - } else if (expr1 is UnaryExpr && expr2 is UnaryExpr) { - return ShallowEq((UnaryExpr)expr1, (UnaryExpr)expr2); - } else if (expr1 is MultiSetFormingExpr && expr2 is MultiSetFormingExpr) { - return ShallowEq((MultiSetFormingExpr)expr1, (MultiSetFormingExpr)expr2); - } else if (expr1 is OldExpr && expr2 is OldExpr) { - return ShallowEq((OldExpr)expr1, (OldExpr)expr2); - } else if (expr1 is FunctionCallExpr && expr2 is FunctionCallExpr) { - return ShallowEq((FunctionCallExpr)expr1, (FunctionCallExpr)expr2); - } else if (expr1 is ApplyExpr && expr2 is ApplyExpr) { - return ShallowEq((ApplyExpr)expr1, (ApplyExpr)expr2); - } else if (expr1 is SeqUpdateExpr && expr2 is SeqUpdateExpr) { - return ShallowEq((SeqUpdateExpr)expr1, (SeqUpdateExpr)expr2); - } else if (expr1 is MultiSelectExpr && expr2 is MultiSelectExpr) { - return ShallowEq((MultiSelectExpr)expr1, (MultiSelectExpr)expr2); - } else if (expr1 is SeqSelectExpr && expr2 is SeqSelectExpr) { - return ShallowEq((SeqSelectExpr)expr1, (SeqSelectExpr)expr2); - } else if (expr1 is MemberSelectExpr && expr2 is MemberSelectExpr) { - return ShallowEq((MemberSelectExpr)expr1, (MemberSelectExpr)expr2); - } else if (expr1 is MapDisplayExpr && expr2 is MapDisplayExpr) { //Note: MapDisplayExpr is not a DisplayExpression - return ShallowEq((MapDisplayExpr)expr1, (MapDisplayExpr)expr2); - } else if (expr1 is DisplayExpression && expr2 is DisplayExpression) { - return ShallowEq((DisplayExpression)expr1, (DisplayExpression)expr2); - } else if (expr1 is IdentifierExpr && expr2 is IdentifierExpr) { - return ShallowEq((IdentifierExpr)expr1, (IdentifierExpr)expr2); - } else if (expr1 is ThisExpr && expr2 is ThisExpr) { - return ShallowEq((ThisExpr)expr1, (ThisExpr)expr2); - } else if (expr1 is DatatypeValue && expr2 is DatatypeValue) { - return ShallowEq((DatatypeValue)expr1, (DatatypeValue)expr2); - } else if (expr1 is LiteralExpr && expr2 is LiteralExpr) { - return ShallowEq((LiteralExpr)expr1, (LiteralExpr)expr2); - } else { - // If this assertion fail, then a new abstract AST node was probably introduced but not registered here. - Contract.Assert(expr1.GetType() != expr2.GetType()); - return false; - } - } - - private static bool ShallowEq(UnboxingCastExpr expr1, UnboxingCastExpr expr2) { - Contract.Requires(false); - throw new InvalidOperationException(); - } - - private static bool ShallowEq(BoxingCastExpr expr1, BoxingCastExpr expr2) { - return expr1.FromType == expr2.FromType && - expr1.ToType == expr2.ToType; - } - - private static bool ShallowEq(MatchExpr expr1, MatchExpr expr2) { - return true; - } - - private static bool ShallowEq(ITEExpr expr1, ITEExpr expr2) { - return true; - } - - private static bool ShallowEq(StmtExpr expr1, StmtExpr expr2) { -#if THROW_UNSUPPORTED_COMPARISONS - Contract.Assume(false); // This kind of expression never appears in a trigger - throw new NotImplementedException(); -#else - return expr1.S == expr2.S; -#endif - } - - private static bool ShallowEq(WildcardExpr expr1, WildcardExpr expr2) { - return true; - } - - private static bool ShallowEq(LambdaExpr expr1, LambdaExpr expr2) { -#if THROW_UNSUPPORTED_COMPARISONS - Contract.Assume(false); // This kind of expression never appears in a trigger - throw new NotImplementedException(); -#else - return expr1.OneShot == expr2.OneShot && - SameLists(expr1.Reads, expr2.Reads, SameFrameExpression); -#endif - } - - private static bool ShallowEq(MapComprehension expr1, MapComprehension expr2) { - return expr1.Finite == expr2.Finite; - } - - private static bool ShallowEq(SetComprehension expr1, SetComprehension expr2) { - return expr1.TermIsImplicit == expr2.TermIsImplicit && //TODO - expr1.Finite == expr2.Finite; - } - - private static bool ShallowEq(ExistsExpr expr1, ExistsExpr expr2) { - return true; - } - - private static bool ShallowEq(ForallExpr expr1, ForallExpr expr2) { - return true; - } - - private static bool ShallowEq(QuantifierExpr expr1, QuantifierExpr expr2) { //FIXME are these TypeArgs still useful? - if (expr1.TypeArgs.Count != expr2.TypeArgs.Count || - !SameNullity(expr1.Range, expr2.Range)) { - return false; - } - - if (expr1 is ExistsExpr && expr2 is ExistsExpr) { - return ShallowEq((ExistsExpr)expr1, (ExistsExpr)expr2); - } else if (expr1 is ForallExpr && expr2 is ForallExpr) { - return ShallowEq((ForallExpr)expr1, (ForallExpr)expr2); - } else { - return false; - } - } - - private static bool ShallowEq(ComprehensionExpr expr1, ComprehensionExpr expr2) { - if (!SameLists(expr1.BoundVars, expr2.BoundVars, SameBoundVar) || - !ShallowSameAttributes(expr1.Attributes, expr2.Attributes) || - // Filled in during resolution: !SameLists(expr1.Bounds, expr2.Bounds, ReferenceCompare) || - // !SameLists(expr1.MissingBounds, expr2.MissingBounds, SameBoundVar) || - !SameNullity(expr1.Range, expr2.Range)) { //TODO Check - return false; - } - - if (expr1 is LambdaExpr && expr2 is LambdaExpr) { - return ShallowEq((LambdaExpr)expr1, (LambdaExpr)expr2); - } else if (expr1 is MapComprehension && expr2 is MapComprehension) { - return ShallowEq((MapComprehension)expr1, (MapComprehension)expr2); - } else if (expr1 is SetComprehension && expr2 is SetComprehension) { - return ShallowEq((SetComprehension)expr1, (SetComprehension)expr2); - } else if (expr1 is QuantifierExpr && expr2 is QuantifierExpr) { - return ShallowEq((QuantifierExpr)expr1, (QuantifierExpr)expr2); - } else { - return false; // ComprehensionExpr is abstract - } - } - - private static bool ShallowEq(NamedExpr expr1, NamedExpr expr2) { - return expr1.Name == expr2.Name && - SameNullity(expr1.Contract, expr2.Contract); - } - - private static bool ShallowEq(LetExpr expr1, LetExpr expr2) { - return expr1.Exact == expr2.Exact && - ShallowSameAttributes(expr1.Attributes, expr2.Attributes); - } - - private static bool ShallowEq(TernaryExpr expr1, TernaryExpr expr2) { - return expr1.Op == expr2.Op; - } - - private static bool ShallowEq(BinaryExpr expr1, BinaryExpr expr2) { - Contract.Requires(expr1.ResolvedOp != BinaryExpr.ResolvedOpcode.YetUndetermined); - Contract.Requires(expr2.ResolvedOp != BinaryExpr.ResolvedOpcode.YetUndetermined); - return expr1.ResolvedOp == expr2.ResolvedOp; - } - - private static bool ShallowEq(ConversionExpr expr1, ConversionExpr expr2) { - return expr1.Type == expr2.Type; //TODO equality on types? - } - - private static bool ShallowEq(UnaryOpExpr expr1, UnaryOpExpr expr2) { - return expr1.Op == expr2.Op; - } - - private static bool ShallowEq(UnaryExpr expr1, UnaryExpr expr2) { - if (expr1 is ConversionExpr && expr2 is ConversionExpr) { - return ShallowEq((ConversionExpr)expr1, (ConversionExpr)expr2); - } else if (expr1 is UnaryOpExpr && expr2 is UnaryOpExpr) { - return ShallowEq((UnaryOpExpr)expr1, (UnaryOpExpr)expr2); - } else { - return false; // UnaryExpr is abstract - } - } - - private static bool ShallowEq(MultiSetFormingExpr expr1, MultiSetFormingExpr expr2) { - return true; - } - - private static bool ShallowEq(OldExpr expr1, OldExpr expr2) { - return true; - } - - private static bool ShallowEq(FunctionCallExpr expr1, FunctionCallExpr expr2) { - return expr1.Name == expr2.Name && - expr1.CoCall == expr2.CoCall && //TODO - expr1.Function == expr2.Function; // TODO TypeArgumentSubstitutions? - } - - private static bool ShallowEq(ApplyExpr expr1, ApplyExpr expr2) { - return true; - } - - private static bool ShallowEq(SeqUpdateExpr expr1, SeqUpdateExpr expr2) { - Contract.Requires(expr1.ResolvedUpdateExpr != null && expr2.ResolvedUpdateExpr != null); - return true; - } - - private static bool ShallowEq(MultiSelectExpr expr1, MultiSelectExpr expr2) { - return true; - } - - private static bool ShallowEq(SeqSelectExpr expr1, SeqSelectExpr expr2) { - return expr1.SelectOne == expr2.SelectOne && - SameNullity(expr1.Seq, expr2.Seq) && - SameNullity(expr1.E0, expr2.E0) && - SameNullity(expr1.E1, expr2.E1); - } - - private static bool ShallowEq(MemberSelectExpr expr1, MemberSelectExpr expr2) { - return expr1.MemberName == expr2.MemberName && - expr1.Member == expr2.Member && - SameLists(expr1.TypeApplication, expr2.TypeApplication, Type.Equals); - } - - private static bool ShallowEq(SeqDisplayExpr expr1, SeqDisplayExpr expr2) { - return true; - } - - private static bool ShallowEq(MapDisplayExpr expr1, MapDisplayExpr expr2) { - return expr1.Finite == expr2.Finite; - } - - private static bool ShallowEq(MultiSetDisplayExpr expr1, MultiSetDisplayExpr expr2) { - return true; - } - - private static bool ShallowEq(SetDisplayExpr expr1, SetDisplayExpr expr2) { - return expr1.Finite == expr2.Finite; - } - - private static bool ShallowEq(DisplayExpression expr1, DisplayExpression expr2) { - if (expr1 is SeqDisplayExpr && expr2 is SeqDisplayExpr) { - return ShallowEq((SeqDisplayExpr)expr1, (SeqDisplayExpr)expr2); - } else if (expr1 is MultiSetDisplayExpr && expr2 is MultiSetDisplayExpr) { - return ShallowEq((MultiSetDisplayExpr)expr1, (MultiSetDisplayExpr)expr2); - } else if (expr1 is SetDisplayExpr && expr2 is SetDisplayExpr) { - return ShallowEq((SetDisplayExpr)expr1, (SetDisplayExpr)expr2); - } else { - return false; - } - } - - private static bool ShallowEq(AutoGhostIdentifierExpr expr1, AutoGhostIdentifierExpr expr2) { - return true; - } - - private static bool ShallowEq(IdentifierExpr expr1, IdentifierExpr expr2) { - if (expr1.Name != expr2.Name || - expr1.Var != expr2.Var) { - return false; - } - - if (expr1 is AutoGhostIdentifierExpr && expr2 is AutoGhostIdentifierExpr) { - return ShallowEq((AutoGhostIdentifierExpr)expr1, (AutoGhostIdentifierExpr)expr2); - } else { - return true; - } - } - - private static bool ShallowEq(ImplicitThisExpr expr1, ImplicitThisExpr expr2) { - return true; - } - - private static bool ShallowEq(ThisExpr expr1, ThisExpr expr2) { - if (expr1 is ImplicitThisExpr && expr2 is ImplicitThisExpr) { - return ShallowEq((ImplicitThisExpr)expr1, (ImplicitThisExpr)expr2); - } else { - return (expr1.GetType() == expr2.GetType()); // LiteralExpr is not abstract - } - } - - private static bool ShallowEq(DatatypeValue expr1, DatatypeValue expr2) { - return // Implied by Ctor equality: expr1.DatatypeName == expr2.DatatypeName && - // Implied by Ctor equality: expr1.MemberName == expr2.MemberName && - expr1.Ctor == expr2.Ctor && - // Contextual information: expr1.IsCoCall == expr2.IsCoCall && - SameLists(expr1.InferredTypeArgs, expr2.InferredTypeArgs, Type.Equals); - } - - private static bool ShallowEq(StringLiteralExpr expr1, StringLiteralExpr expr2) { - return true; - } - - private static bool ShallowEq(CharLiteralExpr expr1, CharLiteralExpr expr2) { - return true; - } - - private static bool ShallowEq(StaticReceiverExpr expr1, StaticReceiverExpr expr2) { - return true; - } - - private static bool ShallowEq(LiteralExpr expr1, LiteralExpr expr2) { - if (expr1.Value != expr2.Value) { - return false; - } - - if (expr1 is StringLiteralExpr && expr2 is StringLiteralExpr) { - return ShallowEq((StringLiteralExpr)expr1, (StringLiteralExpr)expr2); - } else if (expr1 is CharLiteralExpr && expr2 is CharLiteralExpr) { - return ShallowEq((CharLiteralExpr)expr1, (CharLiteralExpr)expr2); - } else if (expr1 is StaticReceiverExpr && expr2 is StaticReceiverExpr) { - return ShallowEq((StaticReceiverExpr)expr1, (StaticReceiverExpr)expr2); - } else { - return (expr1.GetType() == expr2.GetType()); // LiteralExpr is not abstract - } - } - } -} diff --git a/Source/Dafny/Triggers/QuantifierSplitter.cs b/Source/Dafny/Triggers/QuantifierSplitter.cs new file mode 100644 index 00000000..e83feebb --- /dev/null +++ b/Source/Dafny/Triggers/QuantifierSplitter.cs @@ -0,0 +1,66 @@ +using Microsoft.Boogie; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.Dafny.Triggers { + class QuantifierSplitter : BottomUpVisitor { + internal enum Quantifier { Forall, Exists } + + internal static IEnumerable BreakQuantifier(Expression expr, Quantifier quantifier) { + expr = expr.Resolved; + var binary = expr as BinaryExpr; + + if (binary == null) { + yield return expr; + yield break; + } + + var e0 = binary.E0; + var e1 = binary.E1; + + if ((quantifier == Quantifier.Forall && binary.Op == BinaryExpr.Opcode.And) || + (quantifier == Quantifier.Exists && binary.Op == BinaryExpr.Opcode.Or)) { + foreach (var e in BreakQuantifier(e0, quantifier)) { yield return e; } + foreach (var e in BreakQuantifier(e1, quantifier)) { yield return e; } + } else if (binary.Op == BinaryExpr.Opcode.Imp) { + if (quantifier == Quantifier.Forall) { + foreach (var e in BreakImplication(e0, e1, quantifier, expr.tok)) { yield return e; } + } else { + yield return new UnaryOpExpr(e1.tok, UnaryOpExpr.Opcode.Not, e1); // FIXME should be broken further + foreach (var e in BreakQuantifier(e1, quantifier)) { yield return e; } + } + } else { + yield return expr; + } + } + + internal static IEnumerable BreakImplication(Expression ante, Expression post, Quantifier quantifier, IToken tok) { // FIXME: should work for exists and && + foreach (var small_post in BreakQuantifier(post, quantifier)) { + var bin_post = small_post as BinaryExpr; + if (bin_post == null || bin_post.Op != BinaryExpr.Opcode.Imp) { + yield return new BinaryExpr(tok, BinaryExpr.Opcode.Imp, ante, small_post); + } else { // bin_post is an implication + var large_ante = new BinaryExpr(ante.tok, BinaryExpr.Opcode.And, ante, bin_post.E0); + foreach (var imp in BreakImplication(large_ante, bin_post.E1, quantifier, tok)) { + yield return imp; + } + } + } + } + + protected override void VisitOneExpr(Expression expr) { //FIXME: This doesn't save the rewritten quantifier + var forall = expr as ForallExpr; + var exists = expr as ExistsExpr; + + if (forall != null && TriggerUtils.NeedsAutoTriggers(forall)) { + var rew = BreakQuantifier(forall.LogicalBody(), Quantifier.Forall); + //Console.WriteLine("!!! {0} => {1}", Printer.ExprToString(expr), rew.MapConcat(Printer.ExprToString, " ||| ")); + } else if (exists != null && TriggerUtils.NeedsAutoTriggers(exists)) { + var rew = BreakQuantifier(exists.LogicalBody(), Quantifier.Exists); + //Console.WriteLine("!!! {0} => {1}", Printer.ExprToString(expr), rew.MapConcat(Printer.ExprToString, " ||| ")); + } + } + } +} diff --git a/Source/Dafny/Triggers/QuantifiersCollection.cs b/Source/Dafny/Triggers/QuantifiersCollection.cs new file mode 100644 index 00000000..3cbe3bd9 --- /dev/null +++ b/Source/Dafny/Triggers/QuantifiersCollection.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +//FIXME Generated triggers should be _triggers +//FIXME: When scoring, do not consider old(x) to be higher than x. + +namespace Microsoft.Dafny.Triggers { + class QuantifierWithTriggers { + internal QuantifierExpr quantifier; + internal List triggers; + + internal QuantifierWithTriggers(QuantifierExpr quantifier) { + this.quantifier = quantifier; + this.triggers = null; + } + + internal void TrimInvalidTriggers() { + triggers = triggers.Where(tr => tr.MentionsAll(quantifier.BoundVars)).ToList(); + } + + public bool QuantifierAlreadyHadTriggers { get { return !TriggerUtils.NeedsAutoTriggers(quantifier); } } + } + + class QuantifiersCollection { + readonly ErrorReporter reporter; + readonly List quantifiers; + + internal QuantifiersCollection(IEnumerable quantifiers, ErrorReporter reporter) { + this.reporter = reporter; + this.quantifiers = quantifiers.Select(q => new QuantifierWithTriggers(q)).ToList(); + } + + void ComputeTriggers() { + CollectAndShareTriggers(); + TrimInvalidTriggers(); + BuildDependenciesGraph(); + SuppressMatchingLoops(); + SelectTriggers(); + } + + void CollectAndShareTriggers() { + var pool = quantifiers.SelectMany(q => TriggersCollector.CollectTriggers(q.quantifier)); + var distinctPool = pool.Deduplicate((x, y) => ExprExtensions.ExpressionEq(x.Expr, y.Expr)); + var multiPool = TriggerUtils.AllNonEmptySubsets(distinctPool).Select(candidates => new MultiTriggerCandidate(candidates)).ToList(); + + foreach (var q in quantifiers) { + if (q.QuantifierAlreadyHadTriggers) { + reporter.Info(MessageSource.Resolver, q.quantifier.tok, "Not generating triggers for this quantifier."); //FIXME: no_trigger is passed down to Boogie + return; + } else { + q.triggers = multiPool; + } + } + } + + private void TrimInvalidTriggers() { + foreach (var q in quantifiers) { + q.TrimInvalidTriggers(); + } + } + + void BuildDependenciesGraph() { + //FIXME + } + + void SuppressMatchingLoops() { + //FIXME + } + + void SelectTriggers() { + //FIXME + } + + private void CommitOne(QuantifierWithTriggers q) { + foreach (var multiCandidate in q.triggers) { //TODO: error message for when no triggers found + q.quantifier.Attributes = new Attributes("trigger", multiCandidate.terms.Select(t => t.Expr).ToList(), q.quantifier.Attributes); + } + + + //TriggerUtils.DebugTriggers(" Final results:\n{0}", PickMultiTriggers(quantifier)); + + //var multi_candidates = PickMultiTriggers(quantifier); + + //if (multi_candidates.RejectedMultiCandidates.Any()) { + // var tooltip = TriggerUtils.JoinStringsWithHeader("Rejected: ", multi_candidates.RejectedMultiCandidates.Where(candidate => candidate.Tags != null) + // .Select(candidate => candidate.AsDafnyAttributeString(true, true))); + // reporter.Info(ErrorSource.Resolver, quantifier.tok, tooltip, quantifier.tok.val.Length); + //} + + //if (multi_candidates.FinalMultiCandidates.Any()) { + // var tooltip = JoinStringsWithHeader("Triggers: ", multi_candidates.FinalMultiCandidates.Select(multi_candidate => multi_candidate.AsDafnyAttributeString())); + // reporter.Info(ErrorSource.Resolver, quantifier.tok, tooltip, quantifier.tok.val.Length); + //} + + //string warning = multi_candidates.Warning(); + //if (warning != null) { + // // FIXME reenable Resolver.Warning(quantifier.tok, warning); + //} + } + + } +} diff --git a/Source/Dafny/Triggers/TriggerExtensions.cs b/Source/Dafny/Triggers/TriggerExtensions.cs new file mode 100644 index 00000000..7bebe1ac --- /dev/null +++ b/Source/Dafny/Triggers/TriggerExtensions.cs @@ -0,0 +1,489 @@ +#define THROW_UNSUPPORTED_COMPARISONS + +using Microsoft.Dafny; +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; +using System.Text; + +namespace Microsoft.Dafny.Triggers { + internal static class DeduplicateExtension { + public static List Deduplicate(this IEnumerable seq, Func eq) { + List deduplicated = new List(); + + foreach (var elem in seq) { + if (!deduplicated.Any(other => eq(elem, other))) { + deduplicated.Add(elem); + } + } + + return deduplicated; + } + } + + internal static class ExprExtensions { + internal static IEnumerable AllSubExpressions(this Expression expr, bool strict = false) { + foreach (var subexpr in expr.SubExpressions) { + foreach (var r_subexpr in AllSubExpressions(subexpr, false)) { + yield return r_subexpr; + } + yield return subexpr; + } + + if (expr is StmtExpr) { + foreach (var r_subexpr in AllSubExpressions(((StmtExpr)expr).S, false)) { + yield return r_subexpr; + } + } + + if (!strict) { + yield return expr; + } + } + + internal static IEnumerable AllSubExpressions(this Statement stmt, bool strict = false) { + foreach (var subexpr in stmt.SubExpressions) { + foreach (var r_subexpr in AllSubExpressions(subexpr, false)) { + yield return r_subexpr; + } + yield return subexpr; + } + + foreach (var substmt in stmt.SubStatements) { + foreach (var r_subexpr in AllSubExpressions(substmt, false)) { + yield return r_subexpr; + } + } + } + + internal static bool ExpressionEq(this Expression expr1, Expression expr2) { + expr1 = expr1.Resolved; + expr2 = expr2.Resolved; + + return ShallowEq_Top(expr1, expr2) && TriggerUtils.SameLists(expr1.SubExpressions, expr2.SubExpressions, (e1, e2) => ExpressionEq(e1, e2)); + } + + internal static bool ExpressionEqModuloVariableNames(this Expression expr1, Expression expr2) { + expr1 = expr1.Resolved; + expr2 = expr2.Resolved; + + if (expr1 is IdentifierExpr) { + return expr2 is IdentifierExpr; + } + + return ShallowEq_Top(expr1, expr2) && TriggerUtils.SameLists(expr1.SubExpressions, expr2.SubExpressions, (e1, e2) => ExpressionEqModuloVariableNames(e1, e2)); + } + + private static bool MatchesTrigger(this Expression expr, Expression trigger, ISet holes, Dictionary bindings) { + expr = expr.Resolved; + trigger = trigger.Resolved; + + if (trigger is IdentifierExpr) { + var var = ((IdentifierExpr)trigger).Var; + + if (holes.Contains(var)) { + Expression existing_binding = null; + if (bindings.TryGetValue(var, out existing_binding)) { + return ExpressionEq(expr, existing_binding); + } else { + bindings[var] = expr; + return true; + } + } + } + + return ShallowEq_Top(expr, trigger) && TriggerUtils.SameLists(expr.SubExpressions, trigger.SubExpressions, (e1, e2) => MatchesTrigger(e1, e2, holes, bindings)); + } + + internal struct TriggerMatch { + internal Expression Expr; + internal Dictionary Bindings; + + internal bool CouldCauseLoops(IEnumerable candidates) { + // A match for a trigger in the body of a quantifier can be a problem if + // it yields to a matching loop: for example, f(x) is a bad trigger in + // forall x, y :: f(x) = f(f(x)) + // In general, any such match can lead to a loop, but two special cases + // will only lead to a finite number of instantiations: + // 1. The match equals one of the triggers in the set of triggers under + // consideration. For example, { f(x) } a bad trigger above, but the + // pair { f(x), f(f(x)) } is fine (instantiating won't yield new + // matches) + // 2. The match only differs from one of these triggers by variable + // names. This is a superset of the previous case. + var expr = Expr; + return !candidates.Any(c => c.Expr.ExpressionEqModuloVariableNames(expr)); + } + } + + private static TriggerMatch? MatchAgainst(this Expression expr, Expression trigger, ISet holes) { + var bindings = new Dictionary(); + return expr.MatchesTrigger(trigger, holes, bindings) ? new TriggerMatch { Expr = expr, Bindings = bindings } : (TriggerMatch?)null; + } + + internal static IEnumerable SubexpressionsMatchingTrigger(this QuantifierExpr quantifier, Expression trigger) { + return quantifier.Term.AllSubExpressions() + .Select(e => e.MatchAgainst(trigger, new HashSet(quantifier.BoundVars))) + .Where(e => e.HasValue).Select(e => e.Value); + } + + private static bool ShallowSameAttributes(Attributes attributes1, Attributes attributes2) { + return TriggerUtils.SameLists(attributes1.AsEnumerable(), attributes2.AsEnumerable(), ShallowSameSingleAttribute); + } + + private static bool ShallowSameSingleAttribute(Attributes arg1, Attributes arg2) { + return arg1.Name == arg2.Name; + } + + private static bool SameBoundVar(IVariable arg1, IVariable arg2) { + return arg1 == arg2 || + (arg1.Name == arg2.Name && + arg1.CompileName == arg2.CompileName && + arg1.DisplayName == arg2.DisplayName && + arg1.UniqueName == arg2.UniqueName && + arg1.IsGhost == arg2.IsGhost && + arg1.IsMutable == arg2.IsMutable); //FIXME compare types? + } + + /// + /// Compares two abstract syntax expressions, looking only at their direct members. Subexpressions and substatements are not compared. + /// + /// + internal static bool ShallowEq_Top(Expression expr1, Expression expr2) { + Contract.Requires(expr1 != null); + Contract.Requires(expr2 != null); + + // We never compare concrete expressions + Contract.Requires(!(expr1 is ConcreteSyntaxExpression)); + Contract.Requires(!(expr2 is ConcreteSyntaxExpression)); + + // CPC: Hey future editor: the following block of code is auto-generated. Just add your own cases at the end. + // This could be a visitor pattern, except I need to visit a pair of nodes. + // It could also be implemented in each individual class. I'd have a slight preference for that. + // This really just wants to use double dispatch. + if (expr1 is UnboxingCastExpr && expr2 is UnboxingCastExpr) { + return ShallowEq((UnboxingCastExpr)expr1, (UnboxingCastExpr)expr2); + } else if (expr1 is BoxingCastExpr && expr2 is BoxingCastExpr) { + return ShallowEq((BoxingCastExpr)expr1, (BoxingCastExpr)expr2); + } else if (expr1 is MatchExpr && expr2 is MatchExpr) { + return ShallowEq((MatchExpr)expr1, (MatchExpr)expr2); + } else if (expr1 is ITEExpr && expr2 is ITEExpr) { + return ShallowEq((ITEExpr)expr1, (ITEExpr)expr2); + } else if (expr1 is StmtExpr && expr2 is StmtExpr) { + return ShallowEq((StmtExpr)expr1, (StmtExpr)expr2); + } else if (expr1 is WildcardExpr && expr2 is WildcardExpr) { + return ShallowEq((WildcardExpr)expr1, (WildcardExpr)expr2); + } else if (expr1 is ComprehensionExpr && expr2 is ComprehensionExpr) { + return ShallowEq((ComprehensionExpr)expr1, (ComprehensionExpr)expr2); + } else if (expr1 is NamedExpr && expr2 is NamedExpr) { + return ShallowEq((NamedExpr)expr1, (NamedExpr)expr2); + } else if (expr1 is LetExpr && expr2 is LetExpr) { + return ShallowEq((LetExpr)expr1, (LetExpr)expr2); + } else if (expr1 is TernaryExpr && expr2 is TernaryExpr) { + return ShallowEq((TernaryExpr)expr1, (TernaryExpr)expr2); + } else if (expr1 is BinaryExpr && expr2 is BinaryExpr) { + return ShallowEq((BinaryExpr)expr1, (BinaryExpr)expr2); + } else if (expr1 is UnaryExpr && expr2 is UnaryExpr) { + return ShallowEq((UnaryExpr)expr1, (UnaryExpr)expr2); + } else if (expr1 is MultiSetFormingExpr && expr2 is MultiSetFormingExpr) { + return ShallowEq((MultiSetFormingExpr)expr1, (MultiSetFormingExpr)expr2); + } else if (expr1 is OldExpr && expr2 is OldExpr) { + return ShallowEq((OldExpr)expr1, (OldExpr)expr2); + } else if (expr1 is FunctionCallExpr && expr2 is FunctionCallExpr) { + return ShallowEq((FunctionCallExpr)expr1, (FunctionCallExpr)expr2); + } else if (expr1 is ApplyExpr && expr2 is ApplyExpr) { + return ShallowEq((ApplyExpr)expr1, (ApplyExpr)expr2); + } else if (expr1 is SeqUpdateExpr && expr2 is SeqUpdateExpr) { + return ShallowEq((SeqUpdateExpr)expr1, (SeqUpdateExpr)expr2); + } else if (expr1 is MultiSelectExpr && expr2 is MultiSelectExpr) { + return ShallowEq((MultiSelectExpr)expr1, (MultiSelectExpr)expr2); + } else if (expr1 is SeqSelectExpr && expr2 is SeqSelectExpr) { + return ShallowEq((SeqSelectExpr)expr1, (SeqSelectExpr)expr2); + } else if (expr1 is MemberSelectExpr && expr2 is MemberSelectExpr) { + return ShallowEq((MemberSelectExpr)expr1, (MemberSelectExpr)expr2); + } else if (expr1 is MapDisplayExpr && expr2 is MapDisplayExpr) { //Note: MapDisplayExpr is not a DisplayExpression + return ShallowEq((MapDisplayExpr)expr1, (MapDisplayExpr)expr2); + } else if (expr1 is DisplayExpression && expr2 is DisplayExpression) { + return ShallowEq((DisplayExpression)expr1, (DisplayExpression)expr2); + } else if (expr1 is IdentifierExpr && expr2 is IdentifierExpr) { + return ShallowEq((IdentifierExpr)expr1, (IdentifierExpr)expr2); + } else if (expr1 is ThisExpr && expr2 is ThisExpr) { + return ShallowEq((ThisExpr)expr1, (ThisExpr)expr2); + } else if (expr1 is DatatypeValue && expr2 is DatatypeValue) { + return ShallowEq((DatatypeValue)expr1, (DatatypeValue)expr2); + } else if (expr1 is LiteralExpr && expr2 is LiteralExpr) { + return ShallowEq((LiteralExpr)expr1, (LiteralExpr)expr2); + } else { + // If this assertion fail, then a new abstract AST node was probably introduced but not registered here. + Contract.Assert(expr1.GetType() != expr2.GetType()); + return false; + } + } + + private static bool ShallowEq(UnboxingCastExpr expr1, UnboxingCastExpr expr2) { + Contract.Requires(false); + throw new InvalidOperationException(); + } + + private static bool ShallowEq(BoxingCastExpr expr1, BoxingCastExpr expr2) { + return expr1.FromType == expr2.FromType && + expr1.ToType == expr2.ToType; + } + + private static bool ShallowEq(MatchExpr expr1, MatchExpr expr2) { + return true; + } + + private static bool ShallowEq(ITEExpr expr1, ITEExpr expr2) { + return true; + } + + private static bool ShallowEq(StmtExpr expr1, StmtExpr expr2) { +#if THROW_UNSUPPORTED_COMPARISONS + Contract.Assume(false); // This kind of expression never appears in a trigger + throw new NotImplementedException(); +#else + return expr1.S == expr2.S; +#endif + } + + private static bool ShallowEq(WildcardExpr expr1, WildcardExpr expr2) { + return true; + } + + private static bool ShallowEq(LambdaExpr expr1, LambdaExpr expr2) { +#if THROW_UNSUPPORTED_COMPARISONS + Contract.Assume(false); // This kind of expression never appears in a trigger + throw new NotImplementedException(); +#else + return false; +#endif + } + + private static bool ShallowEq(MapComprehension expr1, MapComprehension expr2) { + return expr1.Finite == expr2.Finite; + } + + private static bool ShallowEq(SetComprehension expr1, SetComprehension expr2) { + return expr1.TermIsImplicit == expr2.TermIsImplicit && //TODO + expr1.Finite == expr2.Finite; + } + + private static bool ShallowEq(ExistsExpr expr1, ExistsExpr expr2) { + return true; + } + + private static bool ShallowEq(ForallExpr expr1, ForallExpr expr2) { + return true; + } + + private static bool ShallowEq(QuantifierExpr expr1, QuantifierExpr expr2) { //FIXME are these TypeArgs still useful? + if (expr1.TypeArgs.Count != expr2.TypeArgs.Count || + !TriggerUtils.SameNullity(expr1.Range, expr2.Range)) { + return false; + } + + if (expr1 is ExistsExpr && expr2 is ExistsExpr) { + return ShallowEq((ExistsExpr)expr1, (ExistsExpr)expr2); + } else if (expr1 is ForallExpr && expr2 is ForallExpr) { + return ShallowEq((ForallExpr)expr1, (ForallExpr)expr2); + } else { + return false; + } + } + + private static bool ShallowEq(ComprehensionExpr expr1, ComprehensionExpr expr2) { + if (!TriggerUtils.SameLists(expr1.BoundVars, expr2.BoundVars, SameBoundVar) || + !ShallowSameAttributes(expr1.Attributes, expr2.Attributes) || + // Filled in during resolution: !SameLists(expr1.Bounds, expr2.Bounds, ReferenceCompare) || + // !SameLists(expr1.MissingBounds, expr2.MissingBounds, SameBoundVar) || + !TriggerUtils.SameNullity(expr1.Range, expr2.Range)) { //TODO Check + return false; + } + + if (expr1 is LambdaExpr && expr2 is LambdaExpr) { + return ShallowEq((LambdaExpr)expr1, (LambdaExpr)expr2); + } else if (expr1 is MapComprehension && expr2 is MapComprehension) { + return ShallowEq((MapComprehension)expr1, (MapComprehension)expr2); + } else if (expr1 is SetComprehension && expr2 is SetComprehension) { + return ShallowEq((SetComprehension)expr1, (SetComprehension)expr2); + } else if (expr1 is QuantifierExpr && expr2 is QuantifierExpr) { + return ShallowEq((QuantifierExpr)expr1, (QuantifierExpr)expr2); + } else { + return false; // ComprehensionExpr is abstract + } + } + + private static bool ShallowEq(NamedExpr expr1, NamedExpr expr2) { + return expr1.Name == expr2.Name && + TriggerUtils.SameNullity(expr1.Contract, expr2.Contract); + } + + private static bool ShallowEq(LetExpr expr1, LetExpr expr2) { + return expr1.Exact == expr2.Exact && + ShallowSameAttributes(expr1.Attributes, expr2.Attributes); + } + + private static bool ShallowEq(TernaryExpr expr1, TernaryExpr expr2) { + return expr1.Op == expr2.Op; + } + + private static bool ShallowEq(BinaryExpr expr1, BinaryExpr expr2) { + Contract.Requires(expr1.ResolvedOp != BinaryExpr.ResolvedOpcode.YetUndetermined); + Contract.Requires(expr2.ResolvedOp != BinaryExpr.ResolvedOpcode.YetUndetermined); + return expr1.ResolvedOp == expr2.ResolvedOp; + } + + private static bool ShallowEq(ConversionExpr expr1, ConversionExpr expr2) { + return expr1.Type == expr2.Type; //TODO equality on types? + } + + private static bool ShallowEq(UnaryOpExpr expr1, UnaryOpExpr expr2) { + return expr1.Op == expr2.Op; + } + + private static bool ShallowEq(UnaryExpr expr1, UnaryExpr expr2) { + if (expr1 is ConversionExpr && expr2 is ConversionExpr) { + return ShallowEq((ConversionExpr)expr1, (ConversionExpr)expr2); + } else if (expr1 is UnaryOpExpr && expr2 is UnaryOpExpr) { + return ShallowEq((UnaryOpExpr)expr1, (UnaryOpExpr)expr2); + } else { + return false; // UnaryExpr is abstract + } + } + + private static bool ShallowEq(MultiSetFormingExpr expr1, MultiSetFormingExpr expr2) { + return true; + } + + private static bool ShallowEq(OldExpr expr1, OldExpr expr2) { + return true; + } + + private static bool ShallowEq(FunctionCallExpr expr1, FunctionCallExpr expr2) { + return expr1.Name == expr2.Name && + expr1.CoCall == expr2.CoCall && //TODO + expr1.Function == expr2.Function; // TODO TypeArgumentSubstitutions? + } + + private static bool ShallowEq(ApplyExpr expr1, ApplyExpr expr2) { + return true; + } + + private static bool ShallowEq(SeqUpdateExpr expr1, SeqUpdateExpr expr2) { + Contract.Requires(expr1.ResolvedUpdateExpr != null && expr2.ResolvedUpdateExpr != null); + return true; + } + + private static bool ShallowEq(MultiSelectExpr expr1, MultiSelectExpr expr2) { + return true; + } + + private static bool ShallowEq(SeqSelectExpr expr1, SeqSelectExpr expr2) { + return expr1.SelectOne == expr2.SelectOne && + TriggerUtils.SameNullity(expr1.Seq, expr2.Seq) && + TriggerUtils.SameNullity(expr1.E0, expr2.E0) && + TriggerUtils.SameNullity(expr1.E1, expr2.E1); + } + + private static bool ShallowEq(MemberSelectExpr expr1, MemberSelectExpr expr2) { + return expr1.MemberName == expr2.MemberName && + expr1.Member == expr2.Member && + TriggerUtils.SameLists(expr1.TypeApplication, expr2.TypeApplication, Microsoft.Dafny.Type.Equals); + } + + private static bool ShallowEq(SeqDisplayExpr expr1, SeqDisplayExpr expr2) { + return true; + } + + private static bool ShallowEq(MapDisplayExpr expr1, MapDisplayExpr expr2) { + return expr1.Finite == expr2.Finite; + } + + private static bool ShallowEq(MultiSetDisplayExpr expr1, MultiSetDisplayExpr expr2) { + return true; + } + + private static bool ShallowEq(SetDisplayExpr expr1, SetDisplayExpr expr2) { + return expr1.Finite == expr2.Finite; + } + + private static bool ShallowEq(DisplayExpression expr1, DisplayExpression expr2) { + if (expr1 is SeqDisplayExpr && expr2 is SeqDisplayExpr) { + return ShallowEq((SeqDisplayExpr)expr1, (SeqDisplayExpr)expr2); + } else if (expr1 is MultiSetDisplayExpr && expr2 is MultiSetDisplayExpr) { + return ShallowEq((MultiSetDisplayExpr)expr1, (MultiSetDisplayExpr)expr2); + } else if (expr1 is SetDisplayExpr && expr2 is SetDisplayExpr) { + return ShallowEq((SetDisplayExpr)expr1, (SetDisplayExpr)expr2); + } else { + return false; + } + } + + private static bool ShallowEq(AutoGhostIdentifierExpr expr1, AutoGhostIdentifierExpr expr2) { + return true; + } + + private static bool ShallowEq(IdentifierExpr expr1, IdentifierExpr expr2) { + if (expr1.Name != expr2.Name || + expr1.Var != expr2.Var) { + return false; + } + + if (expr1 is AutoGhostIdentifierExpr && expr2 is AutoGhostIdentifierExpr) { + return ShallowEq((AutoGhostIdentifierExpr)expr1, (AutoGhostIdentifierExpr)expr2); + } else { + return true; + } + } + + private static bool ShallowEq(ImplicitThisExpr expr1, ImplicitThisExpr expr2) { + return true; + } + + private static bool ShallowEq(ThisExpr expr1, ThisExpr expr2) { + if (expr1 is ImplicitThisExpr && expr2 is ImplicitThisExpr) { + return ShallowEq((ImplicitThisExpr)expr1, (ImplicitThisExpr)expr2); + } else { + return (expr1.GetType() == expr2.GetType()); // LiteralExpr is not abstract + } + } + + private static bool ShallowEq(DatatypeValue expr1, DatatypeValue expr2) { + return // Implied by Ctor equality: expr1.DatatypeName == expr2.DatatypeName && + // Implied by Ctor equality: expr1.MemberName == expr2.MemberName && + expr1.Ctor == expr2.Ctor && + // Contextual information: expr1.IsCoCall == expr2.IsCoCall && + TriggerUtils.SameLists(expr1.InferredTypeArgs, expr2.InferredTypeArgs, Microsoft.Dafny.Type.Equals); + } + + private static bool ShallowEq(StringLiteralExpr expr1, StringLiteralExpr expr2) { + return true; + } + + private static bool ShallowEq(CharLiteralExpr expr1, CharLiteralExpr expr2) { + return true; + } + + private static bool ShallowEq(StaticReceiverExpr expr1, StaticReceiverExpr expr2) { + return true; + } + + private static bool ShallowEq(LiteralExpr expr1, LiteralExpr expr2) { + if (expr1.Value != expr2.Value) { + return false; + } + + if (expr1 is StringLiteralExpr && expr2 is StringLiteralExpr) { + return ShallowEq((StringLiteralExpr)expr1, (StringLiteralExpr)expr2); + } else if (expr1 is CharLiteralExpr && expr2 is CharLiteralExpr) { + return ShallowEq((CharLiteralExpr)expr1, (CharLiteralExpr)expr2); + } else if (expr1 is StaticReceiverExpr && expr2 is StaticReceiverExpr) { + return ShallowEq((StaticReceiverExpr)expr1, (StaticReceiverExpr)expr2); + } else { + return (expr1.GetType() == expr2.GetType()); // LiteralExpr is not abstract + } + } + } +} diff --git a/Source/Dafny/Triggers/TriggerGenerator.cs b/Source/Dafny/Triggers/TriggerGenerator.cs new file mode 100644 index 00000000..de4b212b --- /dev/null +++ b/Source/Dafny/Triggers/TriggerGenerator.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Boogie; +using System.Collections.ObjectModel; +using System.Diagnostics.Contracts; + +namespace Microsoft.Dafny.Triggers { //FIXME rename this file + internal class QuantifierCollectionsFinder : TopDownVisitor { + readonly ErrorReporter reporter; + internal List quantifierCollections = new List(); + + public QuantifierCollectionsFinder(ErrorReporter reporter) { + Contract.Requires(reporter != null); + this.reporter = reporter; + } + + 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 + + return true; + } + } +} diff --git a/Source/Dafny/Triggers/TriggerUtils.cs b/Source/Dafny/Triggers/TriggerUtils.cs new file mode 100644 index 00000000..5a118a2f --- /dev/null +++ b/Source/Dafny/Triggers/TriggerUtils.cs @@ -0,0 +1,94 @@ +#define DEBUG_AUTO_TRIGGERS + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Linq; +using System.Text; + +namespace Microsoft.Dafny.Triggers { + class TriggerUtils { + internal static List CopyAndAdd(List seq, T elem) { + var copy = new List(seq); + copy.Add(elem); + return copy; + } + + internal static IEnumerable> AllSubsets(IList source, int offset) { + if (offset >= source.Count) { + yield return new List(); + yield break; + } + + foreach (var subset in AllSubsets(source, offset + 1)) { + yield return CopyAndAdd(subset, source[offset]); + yield return new List(subset); + } + } + + internal static IEnumerable> AllNonEmptySubsets(IEnumerable source) { + List all = new List(source); + foreach (var subset in AllSubsets(all, 0)) { + if (subset.Count > 0) { + yield return subset; + } + } + } + + internal static List MergeAlterFirst(List a, List b) { + Contract.Requires(a != null); + Contract.Requires(b != null); + a.AddRange(b); + return a; + } + + internal static ISet MergeAlterFirst(ISet a, ISet b) { + Contract.Requires(a != null); + Contract.Requires(b != null); + a.UnionWith(b); + return a; + } + + internal static bool SameLists(IEnumerable list1, IEnumerable list2, Func comparer) { + if (ReferenceEquals(list1, list2)) { + return true; + } else if ((list1 == null) != (list2 == null)) { + return false; + } + + var it1 = list1.GetEnumerator(); + var it2 = list2.GetEnumerator(); + bool it1_has, it2_has; + bool acc = true; + + do { + it1_has = it1.MoveNext(); + it2_has = it2.MoveNext(); + + if (it1_has == true && it2_has == true) { + acc = acc && comparer(it1.Current, it2.Current); + } + } while (it1_has && it2_has); + + return it1_has == it2_has && acc; + } + + internal static bool SameNullity(T x1, T x2) where T : class { + return (x1 == null) == (x2 == null); + } + + internal string JoinStringsWithHeader(string header, IEnumerable lines) { + return header + String.Join(Environment.NewLine + new String(' ', header.Length), lines); + } + + [Conditional("DEBUG_AUTO_TRIGGERS")] + internal static void DebugTriggers(string format, params object[] more) { + Console.Error.WriteLine(format, more); + } + + internal static bool NeedsAutoTriggers(QuantifierExpr 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 new file mode 100644 index 00000000..912a661c --- /dev/null +++ b/Source/Dafny/Triggers/TriggersCollector.cs @@ -0,0 +1,256 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Boogie; +using System.Diagnostics.Contracts; +using System.Diagnostics; + +namespace Microsoft.Dafny.Triggers { + struct TriggerCandidate { + internal Expression Expr { get; set; } + internal ISet Variables { get; set; } + + public override string ToString() { + return Printer.ExprToString(Expr); + } + } + + class MultiTriggerCandidate { + internal List terms { get; set; } + + internal MultiTriggerCandidate(List candidates) { + this.terms = candidates; + } + + internal bool MentionsAll(List vars) { + return vars.All(x => terms.Any(candidate => candidate.Variables.Contains(x))); + } + + public override string ToString() { + return String.Join(", ", terms); + } + + public String AsDafnyAttributeString(bool wrap = true, bool includeTags = false) { + var repr = terms.MapConcat(t => Printer.ExprToString(t.Expr), ", "); + if (wrap) { + repr = "{:trigger " + repr + "}"; + } + return repr; + } + } + + class TriggerAnnotation { + internal bool IsTriggerKiller; + internal ISet Variables; + internal readonly List PrivateTerms; + internal readonly List ExportedTerms; + + internal TriggerAnnotation(bool IsTriggerKiller, IEnumerable Variables, + IEnumerable AllCandidates, IEnumerable PrivateCandidates = null) { + this.IsTriggerKiller = IsTriggerKiller; + this.Variables = new HashSet(Variables); + + this.PrivateTerms = new List(PrivateCandidates == null ? Enumerable.Empty() : PrivateCandidates); + this.ExportedTerms = new List(AllCandidates == null ? Enumerable.Empty() : AllCandidates.Except(this.PrivateTerms)); + } + + public override string ToString() { + StringBuilder sb = new StringBuilder(); + string indent = " {0}", nindent = "\n - {0}", subindent = "\n * {0}"; + + sb.AppendFormat(indent, IsTriggerKiller); + + sb.AppendFormat(nindent, "Variables:"); + foreach (var bv in Variables) { + sb.AppendFormat(subindent, bv.Name); + } + + sb.AppendFormat(nindent, "Exported candidates:"); + foreach (var candidate in ExportedTerms) { + sb.AppendFormat(subindent, candidate); + } + + if (PrivateTerms.Any()) { + sb.AppendFormat(nindent, "Private candidates:"); + foreach (var candidate in PrivateTerms) { + sb.AppendFormat(subindent, candidate); + } + } + + return sb.ToString(); + } + } + + public class TriggersCollector { + Dictionary cache; + + private static TriggersCollector instance = new TriggersCollector(); + + private TriggersCollector() { + this.cache = new Dictionary(); + } + + private T ReduceAnnotatedSubExpressions(Expression expr, T seed, Func map, Func reduce) { + return expr.SubExpressions.Select(e => map(Annotate(e))) + .Aggregate(seed, (acc, e) => reduce(acc, e)); + } + + private List CollectExportedCandidates(Expression expr) { + return ReduceAnnotatedSubExpressions>(expr, new List(), a => a.ExportedTerms, TriggerUtils.MergeAlterFirst); + } + + private ISet CollectVariables(Expression expr) { + return ReduceAnnotatedSubExpressions(expr, new HashSet(), a => a.Variables, TriggerUtils.MergeAlterFirst); + } + + private bool CollectIsKiller(Expression expr) { + return ReduceAnnotatedSubExpressions(expr, false, a => a.IsTriggerKiller, (a, b) => a || b); + } + + private IEnumerable OnlyPrivateCandidates(List candidates, IEnumerable privateVars) { + return candidates.Where(c => privateVars.Intersect(c.Variables).Any()); //TODO Check perf + } + + private TriggerAnnotation Annotate(Expression expr) { + TriggerAnnotation cached; + if (cache.TryGetValue(expr, out cached)) { + return cached; + } + + expr.SubExpressions.Iter(e => Annotate(e)); + + TriggerAnnotation annotation; // TODO: Using ApplySuffix fixes the unresolved members problem in GenericSort + if (expr is FunctionCallExpr || expr is SeqSelectExpr || expr is MemberSelectExpr || expr is OldExpr || + (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) { + annotation = AnnotateQuantifier((QuantifierExpr)expr); + } else if (expr is LetExpr) { + annotation = AnnotateLetExpr((LetExpr)expr); + } else if (expr is IdentifierExpr) { + annotation = AnnotateIdentifier((IdentifierExpr)expr); + } else if (expr is ApplySuffix) { + annotation = AnnotateApplySuffix((ApplySuffix)expr); + } else if (expr is ConcreteSyntaxExpression || + expr is LiteralExpr || + expr is OldExpr || + expr is ThisExpr || + expr is BoxingCastExpr || + expr is DatatypeValue || + expr is SeqDisplayExpr) { //FIXME what abvout other display expressions? + annotation = AnnotateOther(expr, false); + } else { + annotation = AnnotateOther(expr, true); + } + + TriggerUtils.DebugTriggers("{0} ({1})\n{2}", Printer.ExprToString(expr), expr.GetType(), annotation); + cache[expr] = annotation; + return annotation; + } + + private BinaryExpr.ResolvedOpcode RemoveNotInBinaryExprIn(BinaryExpr.ResolvedOpcode opcode) { + switch (opcode) { + case BinaryExpr.ResolvedOpcode.NotInMap: + return BinaryExpr.ResolvedOpcode.InMap; + case BinaryExpr.ResolvedOpcode.NotInSet: + return BinaryExpr.ResolvedOpcode.InSet; + case BinaryExpr.ResolvedOpcode.NotInSeq: + return BinaryExpr.ResolvedOpcode.InSeq; + case BinaryExpr.ResolvedOpcode.NotInMultiSet: + return BinaryExpr.ResolvedOpcode.InMultiSet; + } + + Contract.Assert(false); + throw new ArgumentException(); + } + + private Expression CleanupExpr(Expression expr, out bool isKiller) { + isKiller = false; + + if (!(expr is BinaryExpr)) { + return expr; + } + + var bexpr = expr as BinaryExpr; + + BinaryExpr new_expr = bexpr; + if (bexpr.Op == BinaryExpr.Opcode.NotIn) { + new_expr = new BinaryExpr(bexpr.tok, BinaryExpr.Opcode.In, bexpr.E0, bexpr.E1); + new_expr.ResolvedOp = RemoveNotInBinaryExprIn(bexpr.ResolvedOp); + new_expr.Type = bexpr.Type; + } + + Expression returned_expr = new_expr; + if (new_expr.ResolvedOp == BinaryExpr.ResolvedOpcode.InMultiSet) { + returned_expr = new SeqSelectExpr(new_expr.tok, true, new_expr.E1, new_expr.E0, null); + returned_expr.Type = bexpr.Type; + isKiller = true; // [a in s] becomes [s[a] > 0], which is a trigger killer + } + + return returned_expr; + } + + private TriggerAnnotation AnnotatePotentialCandidate(Expression expr) { + bool expr_is_killer = false; + var new_expr = CleanupExpr(expr, out expr_is_killer); + var new_candidate = new TriggerCandidate { Expr = new_expr, Variables = CollectVariables(expr) }; + + List collected_candidates = CollectExportedCandidates(expr); + var children_contain_killers = CollectIsKiller(expr); + + if (!children_contain_killers) { + // Add only if the children are not killers; the head has been cleaned up into non-killer form + collected_candidates.Add(new_candidate); + } + + // This new node is a killer if its children were killers, or if it's non-cleaned-up head is a killer + return new TriggerAnnotation(children_contain_killers || expr_is_killer, new_candidate.Variables, collected_candidates); + } + + private TriggerAnnotation AnnotateApplySuffix(ApplySuffix expr) { + // This is a bit tricky. A funcall node is generally meaningful as a trigger candidate, + // but when it's part of an ApplySuffix the function call itself may not resolve properly + // when the second round of resolving is done after modules are duplicated. + // Thus first we annotate expr and create a trigger candidate, and then we remove the + // candidate matching its direct subexpression if needed. Note that function calls are not the + // only possible child here; there can be DatatypeValue nodes, for example (see vstte2012/Combinators.dfy). + var annotation = AnnotatePotentialCandidate(expr); + // Comparing by reference is fine here. Using sets could yield a small speedup + annotation.ExportedTerms.RemoveAll(candidate => expr.SubExpressions.Contains(candidate.Expr)); + return annotation; + } + + private TriggerAnnotation AnnotateQuantifierOrLetExpr(Expression expr, IEnumerable boundVars) { + var candidates = CollectExportedCandidates(expr); + return new TriggerAnnotation(true, CollectVariables(expr), candidates, OnlyPrivateCandidates(candidates, boundVars)); + } + + private TriggerAnnotation AnnotateQuantifier(QuantifierExpr expr) { + return AnnotateQuantifierOrLetExpr(expr, expr.BoundVars); + } + + private TriggerAnnotation AnnotateLetExpr(LetExpr expr) { + return AnnotateQuantifierOrLetExpr(expr, expr.BoundVars); + } + + private TriggerAnnotation AnnotateIdentifier(IdentifierExpr expr) { + return new TriggerAnnotation(false, Enumerable.Repeat(expr.Var, 1), null); + } + + private TriggerAnnotation AnnotateOther(Expression expr, bool isTriggerKiller) { + return new TriggerAnnotation(isTriggerKiller || CollectIsKiller(expr), CollectVariables(expr), CollectExportedCandidates(expr)); + } + + // FIXME document that this will contain duplicates + internal static List CollectTriggers(QuantifierExpr quantifier) { + // TODO could check for existing triggers and return that instead, but that require a bit of work to extract the expressions + return instance.Annotate(quantifier).PrivateTerms; + } + + internal static bool IsTriggerKiller(Expression expr) { + return instance.Annotate(expr).IsTriggerKiller; + } + } +} diff --git a/Test/dafny0/SeqFromArray.dfy.expect b/Test/dafny0/SeqFromArray.dfy.expect index af845d3e..5395e298 100644 --- a/Test/dafny0/SeqFromArray.dfy.expect +++ b/Test/dafny0/SeqFromArray.dfy.expect @@ -1,3 +1,6 @@ +SeqFromArray.dfy(56,13): Warning: (!) No terms found to trigger on. +SeqFromArray.dfy(76,17): Warning: (!) No terms found to trigger on. +SeqFromArray.dfy(82,17): Warning: (!) No terms found to trigger on. Dafny program verifier finished with 10 verified, 0 errors Program compiled successfully -- cgit v1.2.3 From 5d78c0a9a7165e1a646c92c602b5f7d145c4c399 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 14 Aug 2015 10:04:18 -0700 Subject: Run the trigger rewriter after the quantifier splitter --- Source/Dafny/Resolver.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index db087109..7bf085f9 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -237,6 +237,7 @@ namespace Microsoft.Dafny rewriters.Add(new TimeLimitRewriter(reporter)); if (DafnyOptions.O.AutoTriggers) { + rewriters.Add(new QuantifierSplittingRewriter(this.reporter)); rewriters.Add(new TriggerGeneratingRewriter(this.reporter)); } -- cgit v1.2.3 From 47a2b369096bb316914983c08e473cb3fddc0c25 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 14 Aug 2015 10:04:41 -0700 Subject: Start committing generated triggers when /autoTriggers is 1 --- Source/Dafny/Rewriter.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Source/Dafny/Rewriter.cs b/Source/Dafny/Rewriter.cs index 4e274189..0798510a 100644 --- a/Source/Dafny/Rewriter.cs +++ b/Source/Dafny/Rewriter.cs @@ -39,15 +39,14 @@ namespace Microsoft.Dafny } public class TriggerGeneratingRewriter : IRewriter { - Triggers.QuantifierCollectionsFinder finder; - internal TriggerGeneratingRewriter(ErrorReporter reporter) : base(reporter) { Contract.Requires(reporter != null); - this.finder = new Triggers.QuantifierCollectionsFinder(reporter); } internal override void PostResolve(ModuleDefinition m) { - foreach (var decl in ModuleDefinition.AllCallables(m.TopLevelDecls)) { //CLEMENT + var finder = new Triggers.QuantifierCollectionsFinder(reporter); + + foreach (var decl in ModuleDefinition.AllCallables(m.TopLevelDecls)) { if (decl is Function) { var function = (Function)decl; finder.Visit(function.Ens, null); @@ -64,6 +63,12 @@ namespace Microsoft.Dafny } } } + + var triggersCollector = new Triggers.TriggersCollector(); + foreach (var quantifierCollection in finder.quantifierCollections) { + quantifierCollection.ComputeTriggers(triggersCollector); + quantifierCollection.CommitTriggers(); + } } } -- cgit v1.2.3 From 22a997192baf246bd86031f319aac154c2ec05cb Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 14 Aug 2015 10:05:37 -0700 Subject: Implement self-loop detection Implementing loop detection between multiple triggers is much harder, as it seems to require walking an exponentially sized graph. Self-loop detection is still a net gain compared to the current situation: we used to not detect loops in large quantifiers; not we break these apart, suppress the loops where we can in the smaller parts, and report the ones that we can't suppress. It could be that the broken-up quantifiers are mutually recursive, but these cases would have already led to a loop in the past. --- Source/Dafny/Translator.cs | 4 +- Source/Dafny/Triggers/QuantifierSplitter.cs | 97 +++++++++------ Source/Dafny/Triggers/QuantifiersCollection.cs | 165 +++++++++++++++++++------ Source/Dafny/Triggers/TriggerExtensions.cs | 37 +++--- Source/Dafny/Triggers/TriggerUtils.cs | 23 +++- Source/Dafny/Triggers/TriggersCollector.cs | 102 ++++++++------- 6 files changed, 275 insertions(+), 153 deletions(-) diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index b5d89abd..14fca463 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -13107,8 +13107,10 @@ namespace Microsoft.Dafny { return f.Formals.Zip(fexp.Args).All(formal_concrete => CanSafelySubstitute(visitor.TriggerVariables, formal_concrete.Item1, formal_concrete.Item2)); } + Triggers.TriggersCollector triggersCollector = new Triggers.TriggersCollector(); + private bool CanSafelySubstitute(ISet protectedVariables, IVariable variable, Expression substitution) { - return !(protectedVariables.Contains(variable) && Dafny.Triggers.TriggersCollector.IsTriggerKiller(substitution)); + return !(protectedVariables.Contains(variable) && triggersCollector.IsTriggerKiller(substitution)); } private class VariablesCollector: BottomUpVisitor { diff --git a/Source/Dafny/Triggers/QuantifierSplitter.cs b/Source/Dafny/Triggers/QuantifierSplitter.cs index e83feebb..33be0da2 100644 --- a/Source/Dafny/Triggers/QuantifierSplitter.cs +++ b/Source/Dafny/Triggers/QuantifierSplitter.cs @@ -6,59 +6,76 @@ using System.Text; namespace Microsoft.Dafny.Triggers { class QuantifierSplitter : BottomUpVisitor { - internal enum Quantifier { Forall, Exists } + private static BinaryExpr.Opcode FlipOpcode(BinaryExpr.Opcode opCode) { + if (opCode == BinaryExpr.Opcode.And) { + return BinaryExpr.Opcode.Or; + } else if (opCode == BinaryExpr.Opcode.Or) { + return BinaryExpr.Opcode.And; + } else { + throw new ArgumentException(); + } + } + + // NOTE: If we wanted to split quantifiers as far as possible, it would be + // enough to put the formulas in DNF (for foralls) or CNF (for exists). + // Unfortunately, this would cause ill-behaved quantifiers to produce + // exponentially many smaller quantifiers. - internal static IEnumerable BreakQuantifier(Expression expr, Quantifier quantifier) { + internal static IEnumerable SplitExpr(Expression expr, BinaryExpr.Opcode separator) { expr = expr.Resolved; + var unary = expr as UnaryOpExpr; var binary = expr as BinaryExpr; - if (binary == null) { - yield return expr; - yield break; + 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); } + } 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(binary.E1, separator)) { yield return e; } } + } - var e0 = binary.E0; - var e1 = binary.E1; - - if ((quantifier == Quantifier.Forall && binary.Op == BinaryExpr.Opcode.And) || - (quantifier == Quantifier.Exists && binary.Op == BinaryExpr.Opcode.Or)) { - foreach (var e in BreakQuantifier(e0, quantifier)) { yield return e; } - foreach (var e in BreakQuantifier(e1, quantifier)) { yield return e; } - } else if (binary.Op == BinaryExpr.Opcode.Imp) { - if (quantifier == Quantifier.Forall) { - foreach (var e in BreakImplication(e0, e1, quantifier, expr.tok)) { yield return e; } - } else { - yield return new UnaryOpExpr(e1.tok, UnaryOpExpr.Opcode.Not, e1); // FIXME should be broken further - foreach (var e in BreakQuantifier(e1, quantifier)) { 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); } } - internal static IEnumerable BreakImplication(Expression ante, Expression post, Quantifier quantifier, IToken tok) { // FIXME: should work for exists and && - foreach (var small_post in BreakQuantifier(post, quantifier)) { - var bin_post = small_post as BinaryExpr; - if (bin_post == null || bin_post.Op != BinaryExpr.Opcode.Imp) { - yield return new BinaryExpr(tok, BinaryExpr.Opcode.Imp, ante, small_post); - } else { // bin_post is an implication - var large_ante = new BinaryExpr(ante.tok, BinaryExpr.Opcode.And, ante, bin_post.E0); - foreach (var imp in BreakImplication(large_ante, bin_post.E1, quantifier, tok)) { - yield return imp; - } + internal static IEnumerable SplitQuantifier(QuantifierExpr quantifier) { + var body = quantifier.LogicalBody(); + var binary = body as BinaryExpr; + + if (quantifier is ForallExpr) { + IEnumerable stream; + if (binary != null && (binary.Op == BinaryExpr.Opcode.Imp || binary.Op == BinaryExpr.Opcode.Or)) { + stream = SplitAndStich(binary, BinaryExpr.Opcode.And); + } else { + 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); + } + } else if (quantifier is ExistsExpr) { + IEnumerable stream; + if (binary != null && binary.Op == BinaryExpr.Opcode.And) { + stream = SplitAndStich(binary, BinaryExpr.Opcode.Or); + } else { + 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); + } + } else { + yield return quantifier; } } - + protected override void VisitOneExpr(Expression expr) { //FIXME: This doesn't save the rewritten quantifier - var forall = expr as ForallExpr; - var exists = expr as ExistsExpr; - - if (forall != null && TriggerUtils.NeedsAutoTriggers(forall)) { - var rew = BreakQuantifier(forall.LogicalBody(), Quantifier.Forall); - //Console.WriteLine("!!! {0} => {1}", Printer.ExprToString(expr), rew.MapConcat(Printer.ExprToString, " ||| ")); - } else if (exists != null && TriggerUtils.NeedsAutoTriggers(exists)) { - var rew = BreakQuantifier(exists.LogicalBody(), Quantifier.Exists); + var quantifier = expr as QuantifierExpr; + if (quantifier != null) { + var rew = SplitQuantifier(quantifier); //Console.WriteLine("!!! {0} => {1}", Printer.ExprToString(expr), rew.MapConcat(Printer.ExprToString, " ||| ")); } } diff --git a/Source/Dafny/Triggers/QuantifiersCollection.cs b/Source/Dafny/Triggers/QuantifiersCollection.cs index 3cbe3bd9..828e0e18 100644 --- a/Source/Dafny/Triggers/QuantifiersCollection.cs +++ b/Source/Dafny/Triggers/QuantifiersCollection.cs @@ -1,7 +1,11 @@ -using System; +#define QUANTIFIER_WARNINGS + +using System; using System.Collections.Generic; using System.Linq; using System.Text; +using Microsoft.Boogie; +using System.Diagnostics.Contracts; //FIXME Generated triggers should be _triggers //FIXME: When scoring, do not consider old(x) to be higher than x. @@ -9,18 +13,23 @@ using System.Text; namespace Microsoft.Dafny.Triggers { class QuantifierWithTriggers { internal QuantifierExpr quantifier; - internal List triggers; + internal List CandidateTerms; + internal List Candidates; + internal List RejectedCandidates; + + internal bool AllowsLoops { get { return quantifier.Attributes.AsEnumerable().Any(a => a.Name == "loop"); } } + internal bool CouldSuppressLoops { get; set; } internal QuantifierWithTriggers(QuantifierExpr quantifier) { this.quantifier = quantifier; - this.triggers = null; + this.RejectedCandidates = new List(); } internal void TrimInvalidTriggers() { - triggers = triggers.Where(tr => tr.MentionsAll(quantifier.BoundVars)).ToList(); + Contract.Requires(CandidateTerms != null); + Contract.Requires(Candidates != null); + Candidates = TriggerUtils.Filter(Candidates, tr => tr.MentionsAll(quantifier.BoundVars), tr => { }).ToList(); } - - public bool QuantifierAlreadyHadTriggers { get { return !TriggerUtils.NeedsAutoTriggers(quantifier); } } } class QuantifiersCollection { @@ -32,26 +41,32 @@ namespace Microsoft.Dafny.Triggers { this.quantifiers = quantifiers.Select(q => new QuantifierWithTriggers(q)).ToList(); } - void ComputeTriggers() { - CollectAndShareTriggers(); + internal void ComputeTriggers(TriggersCollector triggersCollector) { + CollectAndShareTriggers(triggersCollector); TrimInvalidTriggers(); BuildDependenciesGraph(); SuppressMatchingLoops(); SelectTriggers(); } + + private bool SubsetGenerationPredicate(List terms, TriggerTerm additionalTerm) { + // Simple formulas like [P0(i) && P1(i) && P2(i) && P3(i) && P4(i)] yield very + // large numbers of multi-triggers, most of which are useless. This filter + // restricts subsets of terms so that we only generate sets of terms where each + // element contributes at least one variable. In the example above, we'll only + // get 5 triggers. + return additionalTerm.Variables.Where(v => !terms.Any(t => t.Variables.Contains(v))).Any(); + } - void CollectAndShareTriggers() { - var pool = quantifiers.SelectMany(q => TriggersCollector.CollectTriggers(q.quantifier)); - var distinctPool = pool.Deduplicate((x, y) => ExprExtensions.ExpressionEq(x.Expr, y.Expr)); - var multiPool = TriggerUtils.AllNonEmptySubsets(distinctPool).Select(candidates => new MultiTriggerCandidate(candidates)).ToList(); + //FIXME document that this assumes that the quantifiers live in the same context and share the same variables + void CollectAndShareTriggers(TriggersCollector triggersCollector) { + var pool = quantifiers.SelectMany(q => triggersCollector.CollectTriggers(q.quantifier)); + var distinctPool = pool.Deduplicate(TriggerTerm.Eq); + var multiPool = TriggerUtils.AllNonEmptySubsets(distinctPool, SubsetGenerationPredicate).Select(candidates => new TriggerCandidate(candidates)).ToList(); foreach (var q in quantifiers) { - if (q.QuantifierAlreadyHadTriggers) { - reporter.Info(MessageSource.Resolver, q.quantifier.tok, "Not generating triggers for this quantifier."); //FIXME: no_trigger is passed down to Boogie - return; - } else { - q.triggers = multiPool; - } + q.CandidateTerms = distinctPool; + q.Candidates = multiPool; } } @@ -62,43 +77,111 @@ namespace Microsoft.Dafny.Triggers { } void BuildDependenciesGraph() { - //FIXME + // FIXME: Think more about multi-quantifier dependencies + //class QuantifierDependency { + // QuantifierWithTriggers Cause; + // QuantifierWithTriggers Consequence; + // List Triggers; + // List MatchingTerm; + //} } void SuppressMatchingLoops() { - //FIXME + // NOTE: This only looks for self-loops; that is, loops involving a single + // quantifier. + + // For a given quantifier q, we introduce a triggering relation between trigger + // candidates by writing t1 → t2 if instantiating q from t1 introduces a ground + // term that matches t2. Then, we notice that this relation is transitive, since + // all triggers yield the same set of terms. This means that any matching loop + // t1 → ... → t1 can be reduced to a self-loop t1 → t1. Detecting such + // self-loops is then only a matter of finding terms in the body of the + // quantifier that match a given trigger. + + // Of course, each trigger that actually appears in the body of the quantifier + // yields a trivial self-loop (e.g. P(i) in [∀ i {P(i)} ⋅ P(i)]), so we + // ignore this type of loops. In fact, we ignore any term in the body of the + // quantifier that matches one of the terms of the trigger (this ensures that + // [∀ x {f(x), f(f(x))} ⋅ f(x) = f(f(x))] is not a loop). And we even + // ignore terms that almost match a trigger term, modulo a single variable + // (this ensures that [∀ x y {a(x, y)} ⋅ a(x, y) == a(y, x)] is not a loop). + // This ignoring logic is implemented by the CouldCauseLoops method. + + foreach (var q in quantifiers) { + var looping = new List(); + var loopingSubterms = q.Candidates.ToDictionary(candidate => candidate, candidate => candidate.LoopingSubterms(q.quantifier).ToList()); + + var safe = TriggerUtils.Filter( + q.Candidates, + c => !loopingSubterms[c].Any(), + c => { + looping.Add(c); + c.Annotation = "loop with " + loopingSubterms[c].MapConcat(t => Printer.ExprToString(t.Expr), ", "); + }).ToList(); + + q.CouldSuppressLoops = safe.Count > 0; + + if (!q.AllowsLoops && q.CouldSuppressLoops) { + q.Candidates = safe; + q.RejectedCandidates.AddRange(looping); + } + } } void SelectTriggers() { //FIXME } - private void CommitOne(QuantifierWithTriggers q) { - foreach (var multiCandidate in q.triggers) { //TODO: error message for when no triggers found - q.quantifier.Attributes = new Attributes("trigger", multiCandidate.terms.Select(t => t.Expr).ToList(), q.quantifier.Attributes); - } - - - //TriggerUtils.DebugTriggers(" Final results:\n{0}", PickMultiTriggers(quantifier)); + private void CommitOne(QuantifierWithTriggers q, object conjunctId) { + var errorLevel = ErrorLevel.Info; + var msg = new StringBuilder(); + var indent = conjunctId != null ? " " : " "; + var header = conjunctId != null ? String.Format(" For conjunct {0}:{1}", conjunctId, Environment.NewLine) : ""; + + if (!TriggerUtils.NeedsAutoTriggers(q.quantifier)) { //FIXME: no_trigger is passed down to Boogie + msg.Append(indent).AppendLine("Not generating triggers for this quantifier."); + } else { + foreach (var candidate in q.Candidates) { + q.quantifier.Attributes = new Attributes("trigger", candidate.Terms.Select(t => t.Expr).ToList(), q.quantifier.Attributes); + } - //var multi_candidates = PickMultiTriggers(quantifier); + if (q.Candidates.Any()) { + msg.Append(indent).AppendLine("Selected triggers:"); + foreach (var mc in q.Candidates) { + msg.Append(indent).Append(" ").AppendLine(mc.ToString()); + } + } - //if (multi_candidates.RejectedMultiCandidates.Any()) { - // var tooltip = TriggerUtils.JoinStringsWithHeader("Rejected: ", multi_candidates.RejectedMultiCandidates.Where(candidate => candidate.Tags != null) - // .Select(candidate => candidate.AsDafnyAttributeString(true, true))); - // reporter.Info(ErrorSource.Resolver, quantifier.tok, tooltip, quantifier.tok.val.Length); - //} + if (q.RejectedCandidates.Any()) { + msg.Append(indent).AppendLine("Rejected triggers:"); + foreach (var mc in q.RejectedCandidates) { + msg.Append(indent).Append(" ").AppendLine(mc.ToString()); + } + } - //if (multi_candidates.FinalMultiCandidates.Any()) { - // var tooltip = JoinStringsWithHeader("Triggers: ", multi_candidates.FinalMultiCandidates.Select(multi_candidate => multi_candidate.AsDafnyAttributeString())); - // reporter.Info(ErrorSource.Resolver, quantifier.tok, tooltip, quantifier.tok.val.Length); - //} +#if QUANTIFIER_WARNINGS + if (!q.CandidateTerms.Any()) { + errorLevel = ErrorLevel.Warning; + msg.Append(indent).AppendLine("No terms found to trigger on."); + } else if (!q.Candidates.Any()) { + errorLevel = ErrorLevel.Warning; + msg.Append(indent).AppendLine("No trigger covering all quantified variables found."); + } else if (!q.CouldSuppressLoops) { + errorLevel = ErrorLevel.Warning; + msg.Append(indent).AppendLine("Suppressing loops would leave this quantifier without triggers."); + } +#endif + } - //string warning = multi_candidates.Warning(); - //if (warning != null) { - // // FIXME reenable Resolver.Warning(quantifier.tok, warning); - //} + if (msg.Length > 0) { + reporter.Message(MessageSource.Rewriter, errorLevel, q.quantifier.tok, header + msg.ToString()); + } } + internal void CommitTriggers() { + foreach (var q in quantifiers) { + CommitOne(q, quantifiers.Count > 1 ? q.quantifier : null); + } + } } } diff --git a/Source/Dafny/Triggers/TriggerExtensions.cs b/Source/Dafny/Triggers/TriggerExtensions.cs index 7bebe1ac..da43abcc 100644 --- a/Source/Dafny/Triggers/TriggerExtensions.cs +++ b/Source/Dafny/Triggers/TriggerExtensions.cs @@ -22,6 +22,20 @@ namespace Microsoft.Dafny.Triggers { } } + internal struct TriggerMatch { + internal Expression Expr; + internal Dictionary Bindings; + + internal static bool Eq(TriggerMatch t1, TriggerMatch t2) { + return ExprExtensions.ExpressionEq(t1.Expr, t2.Expr); + } + + internal bool CouldCauseLoops(List terms) { + var expr = Expr; + return !terms.Any(term => term.Expr.ExpressionEqModuloVariableNames(expr)); + } + } + internal static class ExprExtensions { internal static IEnumerable AllSubExpressions(this Expression expr, bool strict = false) { foreach (var subexpr in expr.SubExpressions) { @@ -75,7 +89,7 @@ namespace Microsoft.Dafny.Triggers { return ShallowEq_Top(expr1, expr2) && TriggerUtils.SameLists(expr1.SubExpressions, expr2.SubExpressions, (e1, e2) => ExpressionEqModuloVariableNames(e1, e2)); } - private static bool MatchesTrigger(this Expression expr, Expression trigger, ISet holes, Dictionary bindings) { + internal static bool MatchesTrigger(this Expression expr, Expression trigger, ISet holes, Dictionary bindings) { expr = expr.Resolved; trigger = trigger.Resolved; @@ -96,27 +110,6 @@ namespace Microsoft.Dafny.Triggers { return ShallowEq_Top(expr, trigger) && TriggerUtils.SameLists(expr.SubExpressions, trigger.SubExpressions, (e1, e2) => MatchesTrigger(e1, e2, holes, bindings)); } - internal struct TriggerMatch { - internal Expression Expr; - internal Dictionary Bindings; - - internal bool CouldCauseLoops(IEnumerable candidates) { - // A match for a trigger in the body of a quantifier can be a problem if - // it yields to a matching loop: for example, f(x) is a bad trigger in - // forall x, y :: f(x) = f(f(x)) - // In general, any such match can lead to a loop, but two special cases - // will only lead to a finite number of instantiations: - // 1. The match equals one of the triggers in the set of triggers under - // consideration. For example, { f(x) } a bad trigger above, but the - // pair { f(x), f(f(x)) } is fine (instantiating won't yield new - // matches) - // 2. The match only differs from one of these triggers by variable - // names. This is a superset of the previous case. - var expr = Expr; - return !candidates.Any(c => c.Expr.ExpressionEqModuloVariableNames(expr)); - } - } - private static TriggerMatch? MatchAgainst(this Expression expr, Expression trigger, ISet holes) { var bindings = new Dictionary(); return expr.MatchesTrigger(trigger, holes, bindings) ? new TriggerMatch { Expr = expr, Bindings = bindings } : (TriggerMatch?)null; diff --git a/Source/Dafny/Triggers/TriggerUtils.cs b/Source/Dafny/Triggers/TriggerUtils.cs index 5a118a2f..dedbcbb1 100644 --- a/Source/Dafny/Triggers/TriggerUtils.cs +++ b/Source/Dafny/Triggers/TriggerUtils.cs @@ -15,21 +15,23 @@ namespace Microsoft.Dafny.Triggers { return copy; } - internal static IEnumerable> AllSubsets(IList source, int offset) { + internal static IEnumerable> AllSubsets(IList source, Func, T, bool> predicate, int offset) { if (offset >= source.Count) { yield return new List(); yield break; } - foreach (var subset in AllSubsets(source, offset + 1)) { - yield return CopyAndAdd(subset, source[offset]); + foreach (var subset in AllSubsets(source, predicate, offset + 1)) { + if (predicate(subset, source[offset])) { + yield return CopyAndAdd(subset, source[offset]); + } yield return new List(subset); } } - internal static IEnumerable> AllNonEmptySubsets(IEnumerable source) { + internal static IEnumerable> AllNonEmptySubsets(IEnumerable source, Func, T, bool> predicate) { List all = new List(source); - foreach (var subset in AllSubsets(all, 0)) { + foreach (var subset in AllSubsets(all, predicate, 0)) { if (subset.Count > 0) { yield return subset; } @@ -74,6 +76,17 @@ namespace Microsoft.Dafny.Triggers { return it1_has == it2_has && acc; } + internal static IEnumerable Filter(IEnumerable elements, Func predicate, Action reject) { + var positive = new List(); + foreach (var c in elements) { + if (predicate(c)) { + yield return c; + } else { + reject(c); + } + } + } + internal static bool SameNullity(T x1, T x2) where T : class { return (x1 == null) == (x2 == null); } diff --git a/Source/Dafny/Triggers/TriggersCollector.cs b/Source/Dafny/Triggers/TriggersCollector.cs index 912a661c..e1f8a013 100644 --- a/Source/Dafny/Triggers/TriggersCollector.cs +++ b/Source/Dafny/Triggers/TriggersCollector.cs @@ -7,52 +7,68 @@ using System.Diagnostics.Contracts; using System.Diagnostics; namespace Microsoft.Dafny.Triggers { - struct TriggerCandidate { + class TriggerTerm { internal Expression Expr { get; set; } internal ISet Variables { get; set; } public override string ToString() { return Printer.ExprToString(Expr); } + + internal static bool Eq(TriggerTerm t1, TriggerTerm t2) { + return ExprExtensions.ExpressionEq(t1.Expr, t2.Expr); + } } - class MultiTriggerCandidate { - internal List terms { get; set; } + class TriggerCandidate { + internal List Terms { get; set; } + internal string Annotation { get; set; } + + internal TriggerCandidate(List terms) { + this.Terms = terms; + } - internal MultiTriggerCandidate(List candidates) { - this.terms = candidates; + public TriggerCandidate(TriggerCandidate mc, string annotation) { + this.Terms = mc.Terms; + this.Annotation = annotation; } internal bool MentionsAll(List vars) { - return vars.All(x => terms.Any(candidate => candidate.Variables.Contains(x))); + return vars.All(x => Terms.Any(term => term.Variables.Contains(x))); } + private string Repr { get { return Terms.MapConcat(t => Printer.ExprToString(t.Expr), ", "); } } + public override string ToString() { - return String.Join(", ", terms); + return "{" + Repr + "}" + (String.IsNullOrWhiteSpace(Annotation) ? "" : " (" + Annotation + ")"); } - public String AsDafnyAttributeString(bool wrap = true, bool includeTags = false) { - var repr = terms.MapConcat(t => Printer.ExprToString(t.Expr), ", "); - if (wrap) { - repr = "{:trigger " + repr + "}"; - } - return repr; + internal IEnumerable LoopingSubterms(QuantifierExpr quantifier) { + var matchingSubterms = MatchingSubterms(quantifier); + return matchingSubterms.Where(tm => tm.CouldCauseLoops(Terms)); + } + + internal List MatchingSubterms(QuantifierExpr 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); + } + + public String AsDafnyAttributeString() { + return "{:trigger " + Repr + "}"; } } class TriggerAnnotation { internal bool IsTriggerKiller; internal ISet Variables; - internal readonly List PrivateTerms; - internal readonly List ExportedTerms; + internal readonly List PrivateTerms; + internal readonly List ExportedTerms; - internal TriggerAnnotation(bool IsTriggerKiller, IEnumerable Variables, - IEnumerable AllCandidates, IEnumerable PrivateCandidates = null) { + internal TriggerAnnotation(bool IsTriggerKiller, IEnumerable Variables, IEnumerable AllTerms, IEnumerable PrivateTerms = null) { this.IsTriggerKiller = IsTriggerKiller; this.Variables = new HashSet(Variables); - - this.PrivateTerms = new List(PrivateCandidates == null ? Enumerable.Empty() : PrivateCandidates); - this.ExportedTerms = new List(AllCandidates == null ? Enumerable.Empty() : AllCandidates.Except(this.PrivateTerms)); + this.PrivateTerms = new List(PrivateTerms == null ? Enumerable.Empty() : PrivateTerms); + this.ExportedTerms = new List(AllTerms == null ? Enumerable.Empty() : AllTerms.Except(this.PrivateTerms)); } public override string ToString() { @@ -66,15 +82,15 @@ namespace Microsoft.Dafny.Triggers { sb.AppendFormat(subindent, bv.Name); } - sb.AppendFormat(nindent, "Exported candidates:"); - foreach (var candidate in ExportedTerms) { - sb.AppendFormat(subindent, candidate); + sb.AppendFormat(nindent, "Exported terms:"); + foreach (var term in ExportedTerms) { + sb.AppendFormat(subindent, term); } if (PrivateTerms.Any()) { - sb.AppendFormat(nindent, "Private candidates:"); - foreach (var candidate in PrivateTerms) { - sb.AppendFormat(subindent, candidate); + sb.AppendFormat(nindent, "Private terms:"); + foreach (var term in PrivateTerms) { + sb.AppendFormat(subindent, term); } } @@ -85,9 +101,7 @@ namespace Microsoft.Dafny.Triggers { public class TriggersCollector { Dictionary cache; - private static TriggersCollector instance = new TriggersCollector(); - - private TriggersCollector() { + internal TriggersCollector() { this.cache = new Dictionary(); } @@ -96,8 +110,8 @@ namespace Microsoft.Dafny.Triggers { .Aggregate(seed, (acc, e) => reduce(acc, e)); } - private List CollectExportedCandidates(Expression expr) { - return ReduceAnnotatedSubExpressions>(expr, new List(), a => a.ExportedTerms, TriggerUtils.MergeAlterFirst); + private List CollectExportedCandidates(Expression expr) { + return ReduceAnnotatedSubExpressions>(expr, new List(), a => a.ExportedTerms, TriggerUtils.MergeAlterFirst); } private ISet CollectVariables(Expression expr) { @@ -108,8 +122,8 @@ namespace Microsoft.Dafny.Triggers { return ReduceAnnotatedSubExpressions(expr, false, a => a.IsTriggerKiller, (a, b) => a || b); } - private IEnumerable OnlyPrivateCandidates(List candidates, IEnumerable privateVars) { - return candidates.Where(c => privateVars.Intersect(c.Variables).Any()); //TODO Check perf + private IEnumerable OnlyPrivateCandidates(List terms, IEnumerable privateVars) { + return terms.Where(c => privateVars.Intersect(c.Variables).Any()); //TODO Check perf } private TriggerAnnotation Annotate(Expression expr) { @@ -195,18 +209,18 @@ namespace Microsoft.Dafny.Triggers { private TriggerAnnotation AnnotatePotentialCandidate(Expression expr) { bool expr_is_killer = false; var new_expr = CleanupExpr(expr, out expr_is_killer); - var new_candidate = new TriggerCandidate { Expr = new_expr, Variables = CollectVariables(expr) }; + var new_term = new TriggerTerm { Expr = new_expr, Variables = CollectVariables(expr) }; - List collected_candidates = CollectExportedCandidates(expr); + List collected_terms = CollectExportedCandidates(expr); var children_contain_killers = CollectIsKiller(expr); if (!children_contain_killers) { // Add only if the children are not killers; the head has been cleaned up into non-killer form - collected_candidates.Add(new_candidate); + collected_terms.Add(new_term); } // This new node is a killer if its children were killers, or if it's non-cleaned-up head is a killer - return new TriggerAnnotation(children_contain_killers || expr_is_killer, new_candidate.Variables, collected_candidates); + return new TriggerAnnotation(children_contain_killers || expr_is_killer, new_term.Variables, collected_terms); } private TriggerAnnotation AnnotateApplySuffix(ApplySuffix expr) { @@ -218,13 +232,13 @@ namespace Microsoft.Dafny.Triggers { // only possible child here; there can be DatatypeValue nodes, for example (see vstte2012/Combinators.dfy). var annotation = AnnotatePotentialCandidate(expr); // Comparing by reference is fine here. Using sets could yield a small speedup - annotation.ExportedTerms.RemoveAll(candidate => expr.SubExpressions.Contains(candidate.Expr)); + annotation.ExportedTerms.RemoveAll(term => expr.SubExpressions.Contains(term.Expr)); return annotation; } private TriggerAnnotation AnnotateQuantifierOrLetExpr(Expression expr, IEnumerable boundVars) { - var candidates = CollectExportedCandidates(expr); - return new TriggerAnnotation(true, CollectVariables(expr), candidates, OnlyPrivateCandidates(candidates, boundVars)); + var terms = CollectExportedCandidates(expr); + return new TriggerAnnotation(true, CollectVariables(expr), terms, OnlyPrivateCandidates(terms, boundVars)); } private TriggerAnnotation AnnotateQuantifier(QuantifierExpr expr) { @@ -244,13 +258,13 @@ namespace Microsoft.Dafny.Triggers { } // FIXME document that this will contain duplicates - internal static List CollectTriggers(QuantifierExpr quantifier) { + internal List CollectTriggers(QuantifierExpr quantifier) { // TODO could check for existing triggers and return that instead, but that require a bit of work to extract the expressions - return instance.Annotate(quantifier).PrivateTerms; + return Annotate(quantifier).PrivateTerms; } - internal static bool IsTriggerKiller(Expression expr) { - return instance.Annotate(expr).IsTriggerKiller; + internal bool IsTriggerKiller(Expression expr) { + return Annotate(expr).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(-) 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(-) 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 5b155d0270984f7e92565e5b329bfed05991d01b Mon Sep 17 00:00:00 2001 From: Bryan Parno Date: Mon, 17 Aug 2015 10:45:55 -0700 Subject: Update the way bounds are discovered to try to choose "better" bounds. --- Source/Dafny/DafnyAst.cs | 48 +++++++++ Source/Dafny/Resolver.cs | 261 ++++++++++++++++++++++++----------------------- 2 files changed, 184 insertions(+), 125 deletions(-) diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index fe6ebfc1..1b2af648 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -3937,6 +3937,9 @@ namespace Microsoft.Dafny { public override bool IsFinite { get { return false; } } + public override int Preference() { + return 0; + } } /// @@ -6683,9 +6686,33 @@ namespace Microsoft.Dafny { public virtual bool IsFinite { get { return true; } // most bounds are finite } + public abstract int Preference(); // higher is better + + public static BoundedPool GetBest(List bounds) { + Contract.Requires(bounds != null && bounds.Count > 0); + var ret = bounds[0]; + 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); + } + } + } + } + return ret; + } } public class BoolBoundedPool : BoundedPool { + public override int Preference() { + return 5; + } } public class IntBoundedPool : BoundedPool { @@ -6700,36 +6727,57 @@ namespace Microsoft.Dafny { return LowerBound != null && UpperBound != null; } } + public override int Preference() { + return 1; + } } public class SetBoundedPool : BoundedPool { public readonly Expression Set; public SetBoundedPool(Expression set) { Set = set; } + public override int Preference() { + return 10; + } } public class SubSetBoundedPool : BoundedPool { public readonly Expression UpperBound; public SubSetBoundedPool(Expression set) { UpperBound = set; } + public override int Preference() { + return 1; + } } public class SuperSetBoundedPool : BoundedPool { public readonly Expression LowerBound; public SuperSetBoundedPool(Expression set) { LowerBound = set; } + public override int Preference() { + return 0; + } } public class MapBoundedPool : BoundedPool { public readonly Expression Map; public MapBoundedPool(Expression map) { Map = map; } + public override int Preference() { + return 10; + } } public class SeqBoundedPool : BoundedPool { public readonly Expression Seq; public SeqBoundedPool(Expression seq) { Seq = seq; } + public override int Preference() { + return 10; + } } public class DatatypeBoundedPool : BoundedPool { public readonly DatatypeDecl Decl; public DatatypeBoundedPool(DatatypeDecl d) { Decl = d; } + public override int Preference() { + return 5; + } } public List Bounds; // initialized and filled in by resolver diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 2da365a9..1bf96d7d 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -6091,9 +6091,8 @@ namespace Microsoft.Dafny Contract.Assert(allBounds != null); s.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 - s.Bounds.Add(pair.Item2[0]); + Contract.Assert(1 <= pair.Item2.Count); + s.Bounds.Add(ComprehensionExpr.BoundedPool.GetBest(pair.Item2)); } } } @@ -9463,136 +9462,148 @@ namespace Microsoft.Dafny expr = polarity ? Expression.CreateAnd(c, expr) : Expression.CreateImplies(c, expr); } for (int j = 0; j < bvars.Count; j++) { - var bv = bvars[j]; - var bounds = new List(); - 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; + 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 }; + } + + allBounds.Add(new Tuple>(bv, bounds)); + } + return foundError ? null : allBounds; + } + + 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(); + 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 + } + } + 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; + default: + break; } - // Go through the conjuncts of the range expression to look for bounds. - Expression lowerBound = null; - Expression upperBound = null; - if (returnAllBounds && lowerBound != null) { + 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; } - 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 - } - } - 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; - default: - break; - } - if ((lowerBound != null && upperBound != null) || - (returnAllBounds && (lowerBound != null || upperBound != null))) { - // we have found two halves (or, in the case of returnAllBounds, we have found some bound) - bounds.Add(new ComprehensionExpr.IntBoundedPool(lowerBound, upperBound)); - lowerBound = null; - upperBound = null; - foundBoundsForBv = true; - if (!returnAllBounds) goto CHECK_NEXT_BOUND_VARIABLE; - } - CHECK_NEXT_CONJUNCT: ; - } - if (!foundBoundsForBv) { - // we have checked every conjunct in the range expression and still have not discovered good bounds - if (allowAnyIntegers && bv.Type is IntType) { - bounds.Add(new AssignSuchThatStmt.WiggleWaggleBound()); - } else { - missingBounds.Add(bv); // record failing bound variable - foundError = true; - } + 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; } } - 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) - allBounds.Add(new Tuple>(bv, bounds)); } - return foundError ? null : allBounds; + 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; } static Expression TypeConstraint(IVariable bv, Type ty) { -- cgit v1.2.3 From 747e2d218f49683605d52f70dbb372f37d9f304b Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 17 Aug 2015 16:04:31 -0700 Subject: Changed hover-text location for recursive ind/co-lemma calls --- Source/Dafny/Cloner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Dafny/Cloner.cs b/Source/Dafny/Cloner.cs index 0a5ca245..e85eeaca 100644 --- a/Source/Dafny/Cloner.cs +++ b/Source/Dafny/Cloner.cs @@ -837,7 +837,7 @@ namespace Microsoft.Dafny apply.Args.ForEach(arg => args.Add(CloneExpr(arg))); var applyClone = new ApplySuffix(Tok(apply.tok), lhsClone, args); var c = new ExprRhs(applyClone); - ReportAdditionalInformation(apply.tok, mse.Member.Name); + ReportAdditionalInformation(apply.Lhs.tok, mse.Member.Name); return c; } } -- cgit v1.2.3 From 4b3fc0e7413424e27131dd8dd919423711f097ad Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Tue, 18 Aug 2015 08:46:35 -0700 Subject: Use a nice warning symbol in some warning messages This is useful because trigger-related messages can contain quite a bit of information, especially if they include info about multiple split quantifiers. --- Source/Dafny/DafnyOptions.cs | 1 + Source/Dafny/Triggers/QuantifiersCollection.cs | 58 +++++++++++++++----------- Source/DafnyServer/Server.cs | 4 +- Source/DafnyServer/Utilities.cs | 5 ++- 4 files changed, 40 insertions(+), 28 deletions(-) diff --git a/Source/Dafny/DafnyOptions.cs b/Source/Dafny/DafnyOptions.cs index 66cf639f..245632ad 100644 --- a/Source/Dafny/DafnyOptions.cs +++ b/Source/Dafny/DafnyOptions.cs @@ -40,6 +40,7 @@ namespace Microsoft.Dafny Bpl.CommandLineOptions.Install(options); } + public bool UnicodeOutput = false; public bool DisallowSoundnessCheating = false; public bool Dafnycc = false; public int Induction = 3; diff --git a/Source/Dafny/Triggers/QuantifiersCollection.cs b/Source/Dafny/Triggers/QuantifiersCollection.cs index 828e0e18..cbc212d2 100644 --- a/Source/Dafny/Triggers/QuantifiersCollection.cs +++ b/Source/Dafny/Triggers/QuantifiersCollection.cs @@ -132,55 +132,63 @@ namespace Microsoft.Dafny.Triggers { //FIXME } - private void CommitOne(QuantifierWithTriggers q, object conjunctId) { + private void CommitOne(QuantifierWithTriggers q, bool addHeader) { var errorLevel = ErrorLevel.Info; var msg = new StringBuilder(); - var indent = conjunctId != null ? " " : " "; - var header = conjunctId != null ? String.Format(" For conjunct {0}:{1}", conjunctId, Environment.NewLine) : ""; + var indent = addHeader ? " " : " "; //FIXME if multiline messages were properly supported, this indentation wouldn't be needed - if (!TriggerUtils.NeedsAutoTriggers(q.quantifier)) { //FIXME: no_trigger is passed down to Boogie - msg.Append(indent).AppendLine("Not generating triggers for this quantifier."); + if (!TriggerUtils.NeedsAutoTriggers(q.quantifier)) { //FIXME: matchingloop and autotriggers attributes are passed down to Boogie + msg.AppendFormat(" Not generating triggers for {{{0}}}.", Printer.ExprToString(q.quantifier.Term)).AppendLine(); + // FIXME This shouldn't be printed for autoReqs. (see autoReq.dfy) + // FIXME typeQuantifier? } else { - foreach (var candidate in q.Candidates) { - q.quantifier.Attributes = new Attributes("trigger", candidate.Terms.Select(t => t.Expr).ToList(), q.quantifier.Attributes); + if (addHeader) { + msg.AppendFormat(" For expression {{{0}}}:", Printer.ExprToString(q.quantifier.Term)).AppendLine(); } - if (q.Candidates.Any()) { - msg.Append(indent).AppendLine("Selected triggers:"); - foreach (var mc in q.Candidates) { - msg.Append(indent).Append(" ").AppendLine(mc.ToString()); - } + foreach (var candidate in q.Candidates) { //FIXME make this _trigger instead of trigger + q.quantifier.Attributes = new Attributes("trigger", candidate.Terms.Select(t => t.Expr).ToList(), q.quantifier.Attributes); } - if (q.RejectedCandidates.Any()) { - msg.Append(indent).AppendLine("Rejected triggers:"); - foreach (var mc in q.RejectedCandidates) { - msg.Append(indent).Append(" ").AppendLine(mc.ToString()); - } - } + AddTriggersToMessage("Selected triggers:", q.Candidates, msg, indent); + AddTriggersToMessage("Rejected triggers:", q.RejectedCandidates, msg, indent, true); #if QUANTIFIER_WARNINGS + string WARN = DafnyOptions.O.UnicodeOutput ? "⚠ " : "(!) "; //FIXME set unicodeoutput in extension if (!q.CandidateTerms.Any()) { errorLevel = ErrorLevel.Warning; - msg.Append(indent).AppendLine("No terms found to trigger on."); + msg.Append(indent).Append(WARN).AppendLine("No terms found to trigger on."); } else if (!q.Candidates.Any()) { errorLevel = ErrorLevel.Warning; - msg.Append(indent).AppendLine("No trigger covering all quantified variables found."); - } else if (!q.CouldSuppressLoops) { + msg.Append(indent).Append(WARN).AppendLine("No trigger covering all quantified variables found."); + } else if (!q.CouldSuppressLoops && !q.AllowsLoops) { errorLevel = ErrorLevel.Warning; - msg.Append(indent).AppendLine("Suppressing loops would leave this quantifier without triggers."); + msg.Append(indent).Append(WARN).AppendLine("Suppressing loops would leave this expression without triggers."); } #endif } - + if (msg.Length > 0) { - reporter.Message(MessageSource.Rewriter, errorLevel, q.quantifier.tok, header + msg.ToString()); + reporter.Message(MessageSource.Rewriter, errorLevel, q.quantifier.tok, msg.ToString().TrimEnd("\r\n".ToCharArray())); + } + } + + private static void AddTriggersToMessage(string header, List triggers, StringBuilder msg, string indent, bool newlines = false) { + if (triggers.Any()) { + msg.Append(indent).Append(header); + if (triggers.Count == 1) { + msg.Append(" "); + } else if (triggers.Count > 1) { + msg.AppendLine().Append(indent).Append(" "); + } + var separator = newlines && triggers.Count > 1 ? Environment.NewLine + indent + " " : ", "; + msg.AppendLine(String.Join(separator, triggers)); } } internal void CommitTriggers() { foreach (var q in quantifiers) { - CommitOne(q, quantifiers.Count > 1 ? q.quantifier : null); + CommitOne(q, quantifiers.Count > 1); } } } diff --git a/Source/DafnyServer/Server.cs b/Source/DafnyServer/Server.cs index 74cdd8c2..bdfd66b4 100644 --- a/Source/DafnyServer/Server.cs +++ b/Source/DafnyServer/Server.cs @@ -33,11 +33,13 @@ namespace Microsoft.Dafny { public Server() { this.running = true; Console.CancelKeyPress += this.CancelKeyPress; + Console.InputEncoding = System.Text.Encoding.UTF8; + Console.OutputEncoding = System.Text.Encoding.UTF8; ExecutionEngine.printer = new DafnyConsolePrinter(); } void CancelKeyPress(object sender, ConsoleCancelEventArgs e) { - e.Cancel = true; + // e.Cancel = true; // FIXME TerminateProver and RunningProverFromCommandLine // Cancel the current verification? TerminateProver() ? Or kill entirely? } diff --git a/Source/DafnyServer/Utilities.cs b/Source/DafnyServer/Utilities.cs index 8fb03b05..4b06e9f5 100644 --- a/Source/DafnyServer/Utilities.cs +++ b/Source/DafnyServer/Utilities.cs @@ -45,13 +45,14 @@ namespace Microsoft.Dafny { internal static void ApplyArgs(string[] args) { Dafny.DafnyOptions.Install(new Dafny.DafnyOptions()); + Dafny.DafnyOptions.O.ProverKillTime = 10; if (CommandLineOptions.Clo.Parse(args)) { // Dafny.DafnyOptions.O.ErrorTrace = 0; //FIXME // Dafny.DafnyOptions.O.ModelViewFile = "-"; - Dafny.DafnyOptions.O.ProverKillTime = 10; - Dafny.DafnyOptions.O.VcsCores = Math.Max(1, System.Environment.ProcessorCount - 1); + Dafny.DafnyOptions.O.UnicodeOutput = true; Dafny.DafnyOptions.O.VerifySnapshots = 2; + Dafny.DafnyOptions.O.VcsCores = Math.Max(1, System.Environment.ProcessorCount - 1); } else { throw new ServerException("Invalid command line options"); } -- cgit v1.2.3 From 108e634af783601c60555c2e8e75775c3b4041ed Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Tue, 18 Aug 2015 08:47:40 -0700 Subject: Small cleanups, fixes, and refactorings In particular, start detecting loops between terms that don't look like each other at the Dafny level, such as {a[x]} and {x in a} (when a is a multiset) --- Source/Dafny/DafnyPipeline.csproj | 2 +- Source/Dafny/Rewriter.cs | 2 +- Source/Dafny/Triggers/QuantifiersCollection.cs | 8 ++-- Source/Dafny/Triggers/QuantifiersCollector.cs | 33 +++++++++++++++ Source/Dafny/Triggers/TriggerExtensions.cs | 15 ++++--- Source/Dafny/Triggers/TriggerGenerator.cs | 32 --------------- Source/Dafny/Triggers/TriggerUtils.cs | 56 ++++++++++++++++++++++++- Source/Dafny/Triggers/TriggersCollector.cs | 57 ++++---------------------- 8 files changed, 111 insertions(+), 94 deletions(-) create mode 100644 Source/Dafny/Triggers/QuantifiersCollector.cs delete mode 100644 Source/Dafny/Triggers/TriggerGenerator.cs diff --git a/Source/Dafny/DafnyPipeline.csproj b/Source/Dafny/DafnyPipeline.csproj index 5a824c36..13a1e53e 100644 --- a/Source/Dafny/DafnyPipeline.csproj +++ b/Source/Dafny/DafnyPipeline.csproj @@ -147,7 +147,7 @@ - + diff --git a/Source/Dafny/Rewriter.cs b/Source/Dafny/Rewriter.cs index 2c00e203..b6409b96 100644 --- a/Source/Dafny/Rewriter.cs +++ b/Source/Dafny/Rewriter.cs @@ -44,7 +44,7 @@ namespace Microsoft.Dafny } internal override void PostCyclicityResolve(ModuleDefinition m) { - var finder = new Triggers.QuantifierCollectionsFinder(reporter); + var finder = new Triggers.QuantifierCollector(reporter); foreach (var decl in ModuleDefinition.AllCallables(m.TopLevelDecls)) { if (decl is Function) { diff --git a/Source/Dafny/Triggers/QuantifiersCollection.cs b/Source/Dafny/Triggers/QuantifiersCollection.cs index cbc212d2..01bceeb7 100644 --- a/Source/Dafny/Triggers/QuantifiersCollection.cs +++ b/Source/Dafny/Triggers/QuantifiersCollection.cs @@ -17,7 +17,7 @@ namespace Microsoft.Dafny.Triggers { internal List Candidates; internal List RejectedCandidates; - internal bool AllowsLoops { get { return quantifier.Attributes.AsEnumerable().Any(a => a.Name == "loop"); } } + internal bool AllowsLoops { get { return TriggerUtils.AllowsMatchingLoops(quantifier); } } internal bool CouldSuppressLoops { get; set; } internal QuantifierWithTriggers(QuantifierExpr quantifier) { @@ -65,8 +65,8 @@ namespace Microsoft.Dafny.Triggers { var multiPool = TriggerUtils.AllNonEmptySubsets(distinctPool, SubsetGenerationPredicate).Select(candidates => new TriggerCandidate(candidates)).ToList(); foreach (var q in quantifiers) { - q.CandidateTerms = distinctPool; - q.Candidates = multiPool; + q.CandidateTerms = distinctPool; //Candidate terms are immutable: no copy needed + q.Candidates = multiPool.Select(candidate => new TriggerCandidate(candidate)).ToList(); } } @@ -116,7 +116,7 @@ namespace Microsoft.Dafny.Triggers { c => !loopingSubterms[c].Any(), c => { looping.Add(c); - c.Annotation = "loop with " + loopingSubterms[c].MapConcat(t => Printer.ExprToString(t.Expr), ", "); + c.Annotation = "loops with " + loopingSubterms[c].MapConcat(t => Printer.ExprToString(t.Expr), ", "); }).ToList(); q.CouldSuppressLoops = safe.Count > 0; diff --git a/Source/Dafny/Triggers/QuantifiersCollector.cs b/Source/Dafny/Triggers/QuantifiersCollector.cs new file mode 100644 index 00000000..a43aae7a --- /dev/null +++ b/Source/Dafny/Triggers/QuantifiersCollector.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Boogie; +using System.Collections.ObjectModel; +using System.Diagnostics.Contracts; + +namespace Microsoft.Dafny.Triggers { //FIXME rename this file + internal class QuantifierCollector : TopDownVisitor { + readonly ErrorReporter reporter; + internal List quantifierCollections = new List(); + + public QuantifierCollector(ErrorReporter reporter) { + Contract.Requires(reporter != null); + this.reporter = reporter; + } + + protected override bool VisitOneExpr(Expression expr, ref object st) { + var quantifier = expr as QuantifierExpr; + if (quantifier != null) { + if (quantifier.SplitQuantifier != null) { + var collection = quantifier.SplitQuantifier.Select(q => q as QuantifierExpr).Where(q => q != null); + quantifierCollections.Add(new QuantifiersCollection(collection, reporter)); + return false; + } else { + quantifierCollections.Add(new QuantifiersCollection(Enumerable.Repeat(quantifier, 1), reporter)); + } + } + return true; + } + } +} diff --git a/Source/Dafny/Triggers/TriggerExtensions.cs b/Source/Dafny/Triggers/TriggerExtensions.cs index 9fbc8a8a..0a0ad547 100644 --- a/Source/Dafny/Triggers/TriggerExtensions.cs +++ b/Source/Dafny/Triggers/TriggerExtensions.cs @@ -110,14 +110,18 @@ namespace Microsoft.Dafny.Triggers { return ShallowEq_Top(expr, trigger) && TriggerUtils.SameLists(expr.SubExpressions, trigger.SubExpressions, (e1, e2) => MatchesTrigger(e1, e2, holes, bindings)); } - private static TriggerMatch? MatchAgainst(this Expression expr, Expression trigger, ISet holes) { + private static TriggerMatch? MatchAgainst(this Expression expr, Expression trigger, IEnumerable holes) { var bindings = new Dictionary(); - return expr.MatchesTrigger(trigger, holes, bindings) ? new TriggerMatch { Expr = expr, Bindings = bindings } : (TriggerMatch?)null; + if (expr.MatchesTrigger(trigger, new HashSet(holes), bindings)) { + return new TriggerMatch { Expr = expr, Bindings = bindings }; + } else { + return null; + } } internal static IEnumerable SubexpressionsMatchingTrigger(this QuantifierExpr quantifier, Expression trigger) { return quantifier.Term.AllSubExpressions() - .Select(e => e.MatchAgainst(trigger, new HashSet(quantifier.BoundVars))) + .Select(e => TriggerUtils.CleanupExprForInclusionInTrigger(e).MatchAgainst(trigger, quantifier.BoundVars)) .Where(e => e.HasValue).Select(e => e.Value); } @@ -136,7 +140,8 @@ namespace Microsoft.Dafny.Triggers { arg1.DisplayName == arg2.DisplayName && arg1.UniqueName == arg2.UniqueName && arg1.IsGhost == arg2.IsGhost && - arg1.IsMutable == arg2.IsMutable); //FIXME compare types? + arg1.IsMutable == arg2.IsMutable && + ((arg1.Type == null && arg2.Type == null) || arg1.Type.Equals(arg2.Type))); } /// @@ -271,7 +276,7 @@ namespace Microsoft.Dafny.Triggers { return true; } - private static bool ShallowEq(QuantifierExpr expr1, QuantifierExpr expr2) { //FIXME are these TypeArgs still useful? + private static bool ShallowEq(QuantifierExpr expr1, QuantifierExpr expr2) { if (!TriggerUtils.SameNullity(expr1.SplitQuantifier, expr2.SplitQuantifier)) { return false; } diff --git a/Source/Dafny/Triggers/TriggerGenerator.cs b/Source/Dafny/Triggers/TriggerGenerator.cs deleted file mode 100644 index e218ad7b..00000000 --- a/Source/Dafny/Triggers/TriggerGenerator.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Microsoft.Boogie; -using System.Collections.ObjectModel; -using System.Diagnostics.Contracts; - -namespace Microsoft.Dafny.Triggers { //FIXME rename this file - internal class QuantifierCollectionsFinder : TopDownVisitor { - readonly ErrorReporter reporter; - internal List quantifierCollections = new List(); - - public QuantifierCollectionsFinder(ErrorReporter reporter) { - Contract.Requires(reporter != null); - this.reporter = reporter; - } - - protected override bool VisitOneExpr(Expression expr, ref object st) { - var quantifier = expr as QuantifierExpr; - if (quantifier != null) { - 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 6c6eede2..9ebcf846 100644 --- a/Source/Dafny/Triggers/TriggerUtils.cs +++ b/Source/Dafny/Triggers/TriggerUtils.cs @@ -100,9 +100,63 @@ namespace Microsoft.Dafny.Triggers { Console.Error.WriteLine(format, more); } + internal static bool AllowsMatchingLoops(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 Attributes.Contains(quantifier.Attributes, "matchingloop"); + } + 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"); + bool wantsAutoTriggers = true; + return !Attributes.Contains(quantifier.Attributes, "trigger") && + (!Attributes.ContainsBool(quantifier.Attributes, "autotriggers", ref wantsAutoTriggers) || wantsAutoTriggers); + } + + internal static BinaryExpr.ResolvedOpcode RemoveNotInBinaryExprIn(BinaryExpr.ResolvedOpcode opcode) { + switch (opcode) { + case BinaryExpr.ResolvedOpcode.NotInMap: + return BinaryExpr.ResolvedOpcode.InMap; + case BinaryExpr.ResolvedOpcode.NotInSet: + return BinaryExpr.ResolvedOpcode.InSet; + case BinaryExpr.ResolvedOpcode.NotInSeq: + return BinaryExpr.ResolvedOpcode.InSeq; + case BinaryExpr.ResolvedOpcode.NotInMultiSet: + return BinaryExpr.ResolvedOpcode.InMultiSet; + } + + Contract.Assert(false); + throw new ArgumentException(); + } + + internal static Expression CleanupExprForInclusionInTrigger(Expression expr, out bool isKiller) { + isKiller = false; + + if (!(expr is BinaryExpr)) { + return expr; + } + + var bexpr = expr as BinaryExpr; + + BinaryExpr new_expr = bexpr; + if (bexpr.Op == BinaryExpr.Opcode.NotIn) { + new_expr = new BinaryExpr(bexpr.tok, BinaryExpr.Opcode.In, bexpr.E0, bexpr.E1); + new_expr.ResolvedOp = RemoveNotInBinaryExprIn(bexpr.ResolvedOp); + new_expr.Type = bexpr.Type; + } + + Expression returned_expr = new_expr; + if (new_expr.ResolvedOp == BinaryExpr.ResolvedOpcode.InMultiSet) { + returned_expr = new SeqSelectExpr(new_expr.tok, true, new_expr.E1, new_expr.E0, null); + returned_expr.Type = bexpr.Type; + isKiller = true; // [a in s] becomes [s[a] > 0], which is a trigger killer + } + + return returned_expr; + } + + internal static Expression CleanupExprForInclusionInTrigger(Expression expr) { + bool _; + return CleanupExprForInclusionInTrigger(expr, out _); } } } diff --git a/Source/Dafny/Triggers/TriggersCollector.cs b/Source/Dafny/Triggers/TriggersCollector.cs index 9f721d9a..221a4255 100644 --- a/Source/Dafny/Triggers/TriggersCollector.cs +++ b/Source/Dafny/Triggers/TriggersCollector.cs @@ -9,10 +9,11 @@ using System.Diagnostics; namespace Microsoft.Dafny.Triggers { class TriggerTerm { internal Expression Expr { get; set; } + internal Expression OriginalExpr { get; set; } internal ISet Variables { get; set; } public override string ToString() { - return Printer.ExprToString(Expr); + return Printer.ExprToString(OriginalExpr); } internal static bool Eq(TriggerTerm t1, TriggerTerm t2) { @@ -28,16 +29,15 @@ namespace Microsoft.Dafny.Triggers { this.Terms = terms; } - public TriggerCandidate(TriggerCandidate mc, string annotation) { - this.Terms = mc.Terms; - this.Annotation = annotation; + public TriggerCandidate(TriggerCandidate candidate) { + this.Terms = candidate.Terms; } internal bool MentionsAll(List vars) { return vars.All(x => Terms.Any(term => term.Variables.Contains(x))); } - private string Repr { get { return Terms.MapConcat(t => Printer.ExprToString(t.Expr), ", "); } } + private string Repr { get { return String.Join(", ", Terms); } } public override string ToString() { return "{" + Repr + "}" + (String.IsNullOrWhiteSpace(Annotation) ? "" : " (" + Annotation + ")"); @@ -51,7 +51,6 @@ namespace Microsoft.Dafny.Triggers { 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); } @@ -166,52 +165,10 @@ namespace Microsoft.Dafny.Triggers { return annotation; } - private BinaryExpr.ResolvedOpcode RemoveNotInBinaryExprIn(BinaryExpr.ResolvedOpcode opcode) { - switch (opcode) { - case BinaryExpr.ResolvedOpcode.NotInMap: - return BinaryExpr.ResolvedOpcode.InMap; - case BinaryExpr.ResolvedOpcode.NotInSet: - return BinaryExpr.ResolvedOpcode.InSet; - case BinaryExpr.ResolvedOpcode.NotInSeq: - return BinaryExpr.ResolvedOpcode.InSeq; - case BinaryExpr.ResolvedOpcode.NotInMultiSet: - return BinaryExpr.ResolvedOpcode.InMultiSet; - } - - Contract.Assert(false); - throw new ArgumentException(); - } - - private Expression CleanupExpr(Expression expr, out bool isKiller) { - isKiller = false; - - if (!(expr is BinaryExpr)) { - return expr; - } - - var bexpr = expr as BinaryExpr; - - BinaryExpr new_expr = bexpr; - if (bexpr.Op == BinaryExpr.Opcode.NotIn) { - new_expr = new BinaryExpr(bexpr.tok, BinaryExpr.Opcode.In, bexpr.E0, bexpr.E1); - new_expr.ResolvedOp = RemoveNotInBinaryExprIn(bexpr.ResolvedOp); - new_expr.Type = bexpr.Type; - } - - Expression returned_expr = new_expr; - if (new_expr.ResolvedOp == BinaryExpr.ResolvedOpcode.InMultiSet) { - returned_expr = new SeqSelectExpr(new_expr.tok, true, new_expr.E1, new_expr.E0, null); - returned_expr.Type = bexpr.Type; - isKiller = true; // [a in s] becomes [s[a] > 0], which is a trigger killer - } - - return returned_expr; - } - private TriggerAnnotation AnnotatePotentialCandidate(Expression expr) { bool expr_is_killer = false; - var new_expr = CleanupExpr(expr, out expr_is_killer); - var new_term = new TriggerTerm { Expr = new_expr, Variables = CollectVariables(expr) }; + var new_expr = TriggerUtils.CleanupExprForInclusionInTrigger(expr, out expr_is_killer); + var new_term = new TriggerTerm { Expr = new_expr, OriginalExpr = expr, Variables = CollectVariables(expr) }; List collected_terms = CollectExportedCandidates(expr); var children_contain_killers = CollectIsKiller(expr); -- cgit v1.2.3 From b4f926f38536d30b7d12c35cbf8f45fa7ef71c27 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Wed, 19 Aug 2015 10:37:49 -0700 Subject: Cleanup indentation of trigger warnings --- Source/Dafny/Triggers/QuantifiersCollection.cs | 12 ++++++------ Test/dafny0/TriggerInPredicate.dfy | 2 +- Test/dafny0/TriggerInPredicate.dfy.expect | 2 ++ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Source/Dafny/Triggers/QuantifiersCollection.cs b/Source/Dafny/Triggers/QuantifiersCollection.cs index 01bceeb7..aa008e36 100644 --- a/Source/Dafny/Triggers/QuantifiersCollection.cs +++ b/Source/Dafny/Triggers/QuantifiersCollection.cs @@ -138,12 +138,12 @@ namespace Microsoft.Dafny.Triggers { var indent = addHeader ? " " : " "; //FIXME if multiline messages were properly supported, this indentation wouldn't be needed if (!TriggerUtils.NeedsAutoTriggers(q.quantifier)) { //FIXME: matchingloop and autotriggers attributes are passed down to Boogie - msg.AppendFormat(" Not generating triggers for {{{0}}}.", Printer.ExprToString(q.quantifier.Term)).AppendLine(); + msg.AppendFormat("Not generating triggers for {{{0}}}.", Printer.ExprToString(q.quantifier.Term)).AppendLine(); // FIXME This shouldn't be printed for autoReqs. (see autoReq.dfy) // FIXME typeQuantifier? } else { if (addHeader) { - msg.AppendFormat(" For expression {{{0}}}:", Printer.ExprToString(q.quantifier.Term)).AppendLine(); + msg.AppendFormat("For expression {{{0}}}:", Printer.ExprToString(q.quantifier.Term)).AppendLine(); } foreach (var candidate in q.Candidates) { //FIXME make this _trigger instead of trigger @@ -154,16 +154,16 @@ namespace Microsoft.Dafny.Triggers { AddTriggersToMessage("Rejected triggers:", q.RejectedCandidates, msg, indent, true); #if QUANTIFIER_WARNINGS - string WARN = DafnyOptions.O.UnicodeOutput ? "⚠ " : "(!) "; //FIXME set unicodeoutput in extension + string WARN = (msg.Length > 0 ? indent : "") + (DafnyOptions.O.UnicodeOutput ? "⚠ " : "(!) "); //FIXME set unicodeoutput in extension if (!q.CandidateTerms.Any()) { errorLevel = ErrorLevel.Warning; - msg.Append(indent).Append(WARN).AppendLine("No terms found to trigger on."); + msg.Append(WARN).AppendLine("No terms found to trigger on."); } else if (!q.Candidates.Any()) { errorLevel = ErrorLevel.Warning; - msg.Append(indent).Append(WARN).AppendLine("No trigger covering all quantified variables found."); + msg.Append(WARN).AppendLine("No trigger covering all quantified variables found."); } else if (!q.CouldSuppressLoops && !q.AllowsLoops) { errorLevel = ErrorLevel.Warning; - msg.Append(indent).Append(WARN).AppendLine("Suppressing loops would leave this expression without triggers."); + msg.Append(WARN).AppendLine("Suppressing loops would leave this expression without triggers."); } #endif } diff --git a/Test/dafny0/TriggerInPredicate.dfy b/Test/dafny0/TriggerInPredicate.dfy index 3f2eac2c..b9c372dc 100644 --- a/Test/dafny0/TriggerInPredicate.dfy +++ b/Test/dafny0/TriggerInPredicate.dfy @@ -3,7 +3,7 @@ predicate A(x: bool, y: bool) { x } -predicate B(x: bool, z: bool) { forall y {:trigger A(x, y) } :: A(x, y) && z } +predicate B(x: bool, z: bool) { forall y {:trigger A(x, y)} :: A(x, y) && z } // Inlining is disabled here to prevent pollution of the trigger in B method C() requires B(true || false, true) {} diff --git a/Test/dafny0/TriggerInPredicate.dfy.expect b/Test/dafny0/TriggerInPredicate.dfy.expect index 7f6e0268..1cbd4034 100644 --- a/Test/dafny0/TriggerInPredicate.dfy.expect +++ b/Test/dafny0/TriggerInPredicate.dfy.expect @@ -1,3 +1,5 @@ +TriggerInPredicate.dfy(6,32): Info: Not generating triggers for {A(x, y)}. +TriggerInPredicate.dfy(6,32): Info: Not generating triggers for {z}. TriggerInPredicate.dfy(9,20): Info: Some instances of this call cannot safely be inlined. TriggerInPredicate.dfy(9,20): Info: Some instances of this call cannot safely be inlined. -- cgit v1.2.3 From 6891a07f8213448bb483e434f66f552370fe9d66 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Tue, 18 Aug 2015 08:49:19 -0700 Subject: Use nested tokens in the quantifier splitter This allows it to report the source of the error, giving better feedback to the user about which precondition to a function failed to hold, for example. --- Source/Dafny/Triggers/QuantifierSplitter.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Source/Dafny/Triggers/QuantifierSplitter.cs b/Source/Dafny/Triggers/QuantifierSplitter.cs index 80381f0a..4b223856 100644 --- a/Source/Dafny/Triggers/QuantifierSplitter.cs +++ b/Source/Dafny/Triggers/QuantifierSplitter.cs @@ -19,7 +19,9 @@ namespace Microsoft.Dafny.Triggers { // NOTE: If we wanted to split quantifiers as far as possible, it would be // enough to put the formulas in DNF (for foralls) or CNF (for exists). // Unfortunately, this would cause ill-behaved quantifiers to produce - // exponentially many smaller quantifiers. + // exponentially many smaller quantifiers. Thus we only do one step of + // distributing, which takes care of the usual precondition case: + // forall x :: P(x) ==> (Q(x) && R(x)) private static UnaryOpExpr Not(Expression expr) { var not = new UnaryOpExpr(expr.tok, UnaryOpExpr.Opcode.Not, expr) { Type = expr.Type }; @@ -62,7 +64,8 @@ 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, e, quantifier.Attributes) { Type = quantifier.Type }; + var tok = new NestedToken(quantifier.tok, e.tok); + yield return new ForallExpr(tok, quantifier.TypeArgs, quantifier.BoundVars, quantifier.Range, e, quantifier.Attributes) { Type = quantifier.Type }; } } else if (quantifier is ExistsExpr) { IEnumerable stream; @@ -72,13 +75,14 @@ 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, e, quantifier.Attributes) { Type = quantifier.Type }; + var tok = new NestedToken(quantifier.tok, e.tok); + yield return new ExistsExpr(tok, quantifier.TypeArgs, quantifier.BoundVars, quantifier.Range, e, quantifier.Attributes) { Type = quantifier.Type }; } } else { yield return quantifier; } } - + protected override void VisitOneExpr(Expression expr) { var quantifier = expr as QuantifierExpr; if (quantifier != null) { -- cgit v1.2.3 From 6ed47f5d08cc266afab92795599290d029a39c86 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Tue, 18 Aug 2015 09:09:45 -0700 Subject: Refactor calls to 'new CallCmd' and clear a few FIXMEs --- Source/Dafny/Printer.cs | 2 +- Source/Dafny/Resolver.cs | 6 +++--- Source/Dafny/Translator.cs | 47 +++++++++++++++++++++++----------------------- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/Source/Dafny/Printer.cs b/Source/Dafny/Printer.cs index 7c684fde..971fe867 100644 --- a/Source/Dafny/Printer.cs +++ b/Source/Dafny/Printer.cs @@ -1373,7 +1373,7 @@ namespace Microsoft.Dafny { PrintTypeInstantiation(e.OptTypeArguments); } else if (expr is ExprDotName) { - var e = (ExprDotName)expr; //CLEMENT: Check the newly added Implicit parameter to make sure that we don't print "_default." DONE in FunctionCall. Where else? + var e = (ExprDotName)expr; // determine if parens are needed int opBindingStrength = 0x70; bool parensNeeded = !e.Lhs.IsImplicit && // KRML: I think that this never holds diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index e1c3c63b..34d4e5c7 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 reporter for the clone + reporter.ErrorsOnly = true; // turn off warning reporting for the clone var nw = new Cloner().CloneModuleDefinition(m, m.CompileName + "_Compile"); var compileSig = RegisterTopLevelDecls(nw, true); compileSig.Refines = refinementTransformer.RefinedSig; @@ -2026,7 +2026,7 @@ namespace Microsoft.Dafny foreach (var p in e.TypeArgumentSubstitutions) { if (!IsDetermined(p.Value.Normalize())) { resolver.reporter.Error(MessageSource.Resolver, e.tok, "type variable '{0}' in the function call to '{1}' could not be determined{2}", p.Key.Name, e.Name, - (e.Name.Contains("reveal_") || e.Name.Contains("_FULL")) //CLEMENT should this be StartsWith and EndsWith? + (e.Name.StartsWith("reveal_") || e.Name.EndsWith("_FULL")) ? ". If you are making an opaque function, make sure that the function can be called." : "" ); @@ -6963,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 reporter + expr.Type = GetThisType(expr.tok, currentClass); // do this regardless of scope.AllowInstance, for better error reporting } } else if (expr is IdentifierExpr) { diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index df816b6d..55dd637c 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -6453,7 +6453,7 @@ namespace Microsoft.Dafny { } // Make the call - builder.Add(new Bpl.CallCmd(method.tok, method.FullSanitizedName, ins, outs)); + builder.Add(Call(method.tok, method.FullSanitizedName, ins, outs)); for (int i = 0; i < m.Outs.Count; i++) { var bLhs = m.Outs[i]; @@ -6484,6 +6484,18 @@ namespace Microsoft.Dafny { Reset(); } + private static CallCmd Call(IToken tok, string methodName, List ins, List outs) { + Contract.Requires(tok != null); + Contract.Requires(methodName != null); + Contract.Requires(ins != null); + Contract.Requires(outs != null); + + CallCmd call; + call = new CallCmd(tok, methodName, ins, outs); + // CLEMENT enable this: call.ErrorData = "possible violation of function precondition"; + return call; + } + private static QKeyValue ErrorMessageAttribute(IToken t, string error) { var l = new List(1); l.Add(error); @@ -7977,13 +7989,6 @@ namespace Microsoft.Dafny { } } - //CLEMENT: Remove - //public static Expr PrintAndDie(Expr expr, [System.Runtime.CompilerServices.CallerMemberName] string caller = "", [System.Runtime.CompilerServices.CallerLineNumber] int linum = 0) { - // Console.Error.WriteLine("In {0} at line {1}: {2}", caller, linum, expr.ToString()); - // Environment.Exit(1); - // return expr; - //} - /// /// Generate: /// assume (forall x,y :: Range(x,y)[$Heap:=oldHeap] ==> @@ -8854,7 +8859,7 @@ namespace Microsoft.Dafny { builder.Add(new CommentCmd("ProcessCallStmt: Make the call")); // Make the call - Bpl.CallCmd call = new Bpl.CallCmd(tok, MethodName(callee, kind), ins, outs); + Bpl.CallCmd call = Call(tok, MethodName(callee, kind), ins, outs); if (module != currentModule && RefinementToken.IsInherited(tok, currentModule) && (codeContext == null || !codeContext.MustReverify)) { // The call statement is inherited, so the refined module already checked that the precondition holds. Note, // preconditions are not allowed to be strengthened, except if they use a predicate whose body has been strengthened. @@ -10524,7 +10529,6 @@ namespace Microsoft.Dafny { public readonly FuelSetting layerInterCluster; public readonly FuelSetting layerIntraCluster = null; // a value of null says to do the same as for inter-cluster calls public int Statistics_CustomLayerFunctionCount = 0; - public readonly bool ProducingCoCertificates = false; // CLEMENT Where is this used? public readonly bool stripLits = false; [ContractInvariantMethod] void ObjectInvariant() @@ -11212,7 +11216,7 @@ namespace Microsoft.Dafny { bool liftLit = lit0 != null && lit1 != null; // NOTE(namin): We usually avoid keeping literals, because their presence might mess up triggers that do not expect them. // Still for equality-related operations, it's useful to keep them instead of lifting them, so that they can be propagated. - bool keepLits = false; // CLEMENT: I don't really understand this + bool keepLits = false; if (lit0 != null) { e0 = lit0; } @@ -11583,14 +11587,12 @@ namespace Microsoft.Dafny { 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); + foreach (var aa in e.Attributes.AsEnumerable().Where(aa => 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); } if (e.Range != null) { antecedent = BplAnd(antecedent, bodyEtran.TrExpr(e.Range)); @@ -12035,7 +12037,7 @@ namespace Microsoft.Dafny { parms.Add(s); } else { var e = TrExpr(arg); - e = translator.RemoveLit(e); // CLEMENT: Why? + e = translator.RemoveLit(e); parms.Add(e); } } @@ -12981,7 +12983,7 @@ namespace Microsoft.Dafny { return true; } else { // Skip inlining, as it would cause arbitrary expressions to pop up in the trigger - // CLEMENT this should appear at the outmost call site, not at the innermost. See SnapshotableTrees.dfy + // TODO this should appear at the outmost call site, not at the innermost. See SnapshotableTrees.dfy reporter.Info(MessageSource.Translator, fexp.tok, "Some instances of this call cannot safely be inlined."); } } @@ -13124,7 +13126,6 @@ namespace Microsoft.Dafny { visitor.Visit(f.Body); foreach (var expr in f.Ens) { visitor.Visit(expr); } foreach (var expr in f.Req) { visitor.Visit(expr); } - // CLEMENT: Anything else? return f.Formals.Zip(fexp.Args).All(formal_concrete => CanSafelySubstitute(visitor.TriggerVariables, formal_concrete.Item1, formal_concrete.Item2)); } @@ -14452,12 +14453,12 @@ namespace Microsoft.Dafny { // Bpl-making-utilities - static Bpl.Expr BplForall(IEnumerable args_in, Bpl.Expr body) { // NO_TRIGGER + static Bpl.Expr BplForall(IEnumerable args_in, Bpl.Expr body) { Contract.Requires(args_in != null); Contract.Requires(body != null); Contract.Ensures(Contract.Result() != null); var args = new List(args_in); - if (args.Count == 0) { // CLEMENT don't add quantifiers if the body is trivial + if (args.Count == 0) { return body; } else { return new Bpl.ForallExpr(body.tok, args, body); // NO_TRIGGER -- cgit v1.2.3 From 045ee27da6435a6262edbada4f7911b2f3ff45b8 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Tue, 18 Aug 2015 09:12:55 -0700 Subject: Exclude folders named 'sandbox' from lit tests --- Test/lit.site.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/lit.site.cfg b/Test/lit.site.cfg index c5718f86..2d57d389 100644 --- a/Test/lit.site.cfg +++ b/Test/lit.site.cfg @@ -22,7 +22,7 @@ config.suffixes = ['.dfy'] # excludes: A list of directories to exclude from the testsuite. The 'Inputs' # subdirectories contain auxiliary inputs for various tests in their parent # directories. -config.excludes = ['Inputs'] +config.excludes = ['Inputs', 'sandbox'] # test_source_root: The root path where tests are located. config.test_source_root = os.path.dirname(os.path.abspath(__file__)) -- cgit v1.2.3 From 8a1ae35f1f4282573fa8b67e1151b0cbbabeb136 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Tue, 18 Aug 2015 09:35:23 -0700 Subject: Check Reads and Decreases clauses for expressions that could prevent inlining --- Source/Dafny/DafnyAst.cs | 6 ++++++ Source/Dafny/Translator.cs | 2 ++ 2 files changed, 8 insertions(+) diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index a902a18c..e5d8250b 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -7674,6 +7674,9 @@ namespace Microsoft.Dafny { public void Visit(MaybeFreeExpression expr) { Visit(expr.E); } + public void Visit(FrameExpression expr) { + Visit(expr.E); + } public void Visit(IEnumerable exprs) { exprs.Iter(Visit); } @@ -7717,6 +7720,9 @@ namespace Microsoft.Dafny { public void Visit(MaybeFreeExpression expr, State st) { Visit(expr.E, st); } + public void Visit(FrameExpression expr, State st) { + Visit(expr.E, st); + } public void Visit(IEnumerable exprs, State st) { exprs.Iter(e => Visit(e, st)); } diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 55dd637c..50324002 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -13126,6 +13126,8 @@ namespace Microsoft.Dafny { visitor.Visit(f.Body); foreach (var expr in f.Ens) { visitor.Visit(expr); } foreach (var expr in f.Req) { visitor.Visit(expr); } + foreach (var expr in f.Reads) { visitor.Visit(expr); } + foreach (var expr in f.Decreases.Expressions) { visitor.Visit(expr); } return f.Formals.Zip(fexp.Args).All(formal_concrete => CanSafelySubstitute(visitor.TriggerVariables, formal_concrete.Item1, formal_concrete.Item2)); } -- cgit v1.2.3 From ac137091eac412d2ea8a79ac1c05d161db3365f2 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Tue, 18 Aug 2015 09:36:17 -0700 Subject: Slightly improve the condition used to filter out trigger sets --- Source/Dafny/Triggers/QuantifiersCollection.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Dafny/Triggers/QuantifiersCollection.cs b/Source/Dafny/Triggers/QuantifiersCollection.cs index aa008e36..ecc193fc 100644 --- a/Source/Dafny/Triggers/QuantifiersCollection.cs +++ b/Source/Dafny/Triggers/QuantifiersCollection.cs @@ -55,7 +55,9 @@ namespace Microsoft.Dafny.Triggers { // restricts subsets of terms so that we only generate sets of terms where each // element contributes at least one variable. In the example above, we'll only // get 5 triggers. - return additionalTerm.Variables.Where(v => !terms.Any(t => t.Variables.Contains(v))).Any(); + // Note that this may still be an over-approximation, as in the following example: + // forall a, b :: forall x :: a[x] || b[x] > 0. + return additionalTerm.Variables.Where(v => v is BoundVar && !terms.Any(t => t.Variables.Contains(v))).Any(); } //FIXME document that this assumes that the quantifiers live in the same context and share the same variables -- cgit v1.2.3 From a019d797bd42866242e48ef00850f74e3bdc9241 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Tue, 18 Aug 2015 16:41:10 -0700 Subject: Server: disable tracing when running tests, and fix an encoding issue. z3 doesn't support byte-order marks; thus using the default UTF8Encoding object (to allow printing nice warning signs) causes issues, as it sends a BOM before anything else. --- Source/DafnyServer/Server.cs | 24 ++++++++++++++++-------- Source/DafnyServer/Utilities.cs | 3 ++- Source/DafnyServer/VerificationTask.cs | 6 +++--- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/Source/DafnyServer/Server.cs b/Source/DafnyServer/Server.cs index bdfd66b4..0a9ce599 100644 --- a/Source/DafnyServer/Server.cs +++ b/Source/DafnyServer/Server.cs @@ -11,6 +11,7 @@ using Microsoft.Boogie; namespace Microsoft.Dafny { class Server { + private bool trace; private bool running; static void Main(string[] args) { @@ -23,33 +24,40 @@ namespace Microsoft.Dafny { VerificationTask.SelfTest(); } else if (hasArg && File.Exists(arg)) { Console.WriteLine("# Reading from {0}", Path.GetFileName(arg)); - Console.SetIn(new StreamReader(arg)); + Console.SetIn(new StreamReader(arg, Encoding.UTF8)); + server.trace = false; server.Loop(); } else { server.Loop(); } } + private void SetupConsole() { + var utf8 = new UTF8Encoding(false, true); + Console.OutputEncoding = utf8; + Console.OutputEncoding = utf8; + Console.CancelKeyPress += CancelKeyPress; + } + public Server() { + this.trace = true; this.running = true; - Console.CancelKeyPress += this.CancelKeyPress; - Console.InputEncoding = System.Text.Encoding.UTF8; - Console.OutputEncoding = System.Text.Encoding.UTF8; ExecutionEngine.printer = new DafnyConsolePrinter(); + SetupConsole(); } void CancelKeyPress(object sender, ConsoleCancelEventArgs e) { // e.Cancel = true; // FIXME TerminateProver and RunningProverFromCommandLine - // Cancel the current verification? TerminateProver() ? Or kill entirely? + // Cancel the current verification? TerminateProver()? Or kill entirely? } - static bool EndOfPayload(out string line) { + bool EndOfPayload(out string line) { line = Console.ReadLine(); return line == null || line == Interaction.CLIENT_EOM_TAG; } - static string ReadPayload() { + string ReadPayload() { StringBuilder buffer = new StringBuilder(); string line = null; while (!EndOfPayload(out line)) { @@ -78,7 +86,7 @@ namespace Microsoft.Dafny { if (verb == "verify") { ServerUtils.checkArgs(command, 0); var payload = ReadPayload(); - VerificationTask.ReadTask(payload).Run(); + VerificationTask.ReadTask(payload).Run(trace); } else if (verb == "quit") { ServerUtils.checkArgs(command, 0); Exit(); diff --git a/Source/DafnyServer/Utilities.cs b/Source/DafnyServer/Utilities.cs index 4b06e9f5..96254a3d 100644 --- a/Source/DafnyServer/Utilities.cs +++ b/Source/DafnyServer/Utilities.cs @@ -43,7 +43,7 @@ namespace Microsoft.Dafny { } } - internal static void ApplyArgs(string[] args) { + internal static void ApplyArgs(string[] args, bool trace) { Dafny.DafnyOptions.Install(new Dafny.DafnyOptions()); Dafny.DafnyOptions.O.ProverKillTime = 10; @@ -53,6 +53,7 @@ namespace Microsoft.Dafny { Dafny.DafnyOptions.O.UnicodeOutput = true; Dafny.DafnyOptions.O.VerifySnapshots = 2; Dafny.DafnyOptions.O.VcsCores = Math.Max(1, System.Environment.ProcessorCount - 1); + Dafny.DafnyOptions.Clo.Trace = trace; } else { throw new ServerException("Invalid command line options"); } diff --git a/Source/DafnyServer/VerificationTask.cs b/Source/DafnyServer/VerificationTask.cs index a00688b1..dbafc781 100644 --- a/Source/DafnyServer/VerificationTask.cs +++ b/Source/DafnyServer/VerificationTask.cs @@ -44,15 +44,15 @@ namespace Microsoft.Dafny { source = "method selftest() { assert true; }" }; try { - task.Run(); + task.Run(false); Interaction.EOM(Interaction.SUCCESS, null); } catch (Exception ex) { Interaction.EOM(Interaction.FAILURE, ex); } } - internal void Run() { - ServerUtils.ApplyArgs(args); + internal void Run(bool trace = true) { + ServerUtils.ApplyArgs(args, trace); new DafnyHelper(filename, ProgramSource).Verify(); } } -- cgit v1.2.3 From 6a24e8c90ac467678dbf9aeb0d16c3d36c2dcf44 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Tue, 18 Aug 2015 18:14:36 -0700 Subject: runTests: Report mean completion time of passed tests, excluding outliers --- Test/runTests.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Test/runTests.py b/Test/runTests.py index 0232f81b..9f4fa5a5 100644 --- a/Test/runTests.py +++ b/Test/runTests.py @@ -6,6 +6,7 @@ import shutil import argparse import operator import platform +from math import floor, ceil from enum import Enum from time import time, strftime from collections import defaultdict @@ -168,6 +169,25 @@ class Test: results.append(Test.deserialize(row)) return results + @staticmethod + def mean_duration(results, margin): + durations = sorted(result.duration for result in results + if result.status == TestStatus.PASSED) + if len(durations) >= 15: + lq = durations[floor(0.25 * len(durations))] + hq = durations[ceil(0.85 * len(durations))] + iqr = hq - lq + filtered = [d for d in durations if (lq - margin * iqr) <= d <= (hq + margin * iqr)] + if filtered: + avg = sum(durations) / len(durations) + trimmed_avg = sum(filtered) / len(filtered) + outliers_count = len(durations) - len(filtered) + msg = "mean completion time: {:.2f}s".format(avg) + if outliers_count > 0: + msg += "; ignoring {} outliers: {:.2f}s".format(outliers_count, trimmed_avg) + return " ({})".format(msg) + return "" + @staticmethod def summarize(results): debug(Debug.INFO, "\nTesting complete ({} test(s))".format(len(results))) @@ -193,7 +213,8 @@ class Test: writer.write("{}\n".format(t.name)) debug(Debug.REPORT, "Some tests failed: use [runTests.py failing.lst] to rerun the failing tests") - debug(Debug.REPORT, "Testing took {:.2f}s on {} thread(s)".format(results[0].suite_time, results[0].njobs)) + debug(Debug.REPORT, "Testing took {:.2f}s on {} thread(s){}".format( + results[0].suite_time, results[0].njobs, Test.mean_duration(results, 1.5))) def run(self): -- cgit v1.2.3 From fcf9093f269b924555780e60fe05e4eff9de1cf4 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Wed, 19 Aug 2015 10:11:08 -0700 Subject: runTests: Include failed tests in mean completion time --- Test/runTests.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Test/runTests.py b/Test/runTests.py index 9f4fa5a5..eae29012 100644 --- a/Test/runTests.py +++ b/Test/runTests.py @@ -148,12 +148,12 @@ class Test: @staticmethod def build_report(tests, name): - time = strftime("%Y-%m-%d-%H-%M-%S") + now = strftime("%Y-%m-%d-%H-%M-%S") if name: directory, fname = os.path.split(name) - name = os.path.join(directory, time + "--" + fname) + name = os.path.join(directory, now + "--" + fname) else: - name = time + name = now with open(name + ".csv", mode='w', newline='') as writer: csv_writer = csv.DictWriter(writer, Test.COLUMNS, dialect='excel') @@ -172,7 +172,7 @@ class Test: @staticmethod def mean_duration(results, margin): durations = sorted(result.duration for result in results - if result.status == TestStatus.PASSED) + if result.status in (TestStatus.PASSED, TestStatus.FAILED)) if len(durations) >= 15: lq = durations[floor(0.25 * len(durations))] hq = durations[ceil(0.85 * len(durations))] -- cgit v1.2.3 From 165aa64949e964a24ac07b32fbe114e96975c5f6 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Wed, 19 Aug 2015 15:47:57 -0700 Subject: Generate triggers for nested quantifiers as well The new 'quantifiers' list keeps track of the quantifiers that have already been seen, so that they are not added both as a member of a collection and as a single quantifier. --- Source/Dafny/Triggers/QuantifiersCollector.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Source/Dafny/Triggers/QuantifiersCollector.cs b/Source/Dafny/Triggers/QuantifiersCollector.cs index a43aae7a..b30cb6b1 100644 --- a/Source/Dafny/Triggers/QuantifiersCollector.cs +++ b/Source/Dafny/Triggers/QuantifiersCollector.cs @@ -9,6 +9,7 @@ using System.Diagnostics.Contracts; namespace Microsoft.Dafny.Triggers { //FIXME rename this file internal class QuantifierCollector : TopDownVisitor { readonly ErrorReporter reporter; + private HashSet quantifiers = new HashSet(); internal List quantifierCollections = new List(); public QuantifierCollector(ErrorReporter reporter) { @@ -16,17 +17,20 @@ namespace Microsoft.Dafny.Triggers { //FIXME rename this file this.reporter = reporter; } - protected override bool VisitOneExpr(Expression expr, ref object st) { + protected override bool VisitOneExpr(Expression expr, ref object _) { var quantifier = expr as QuantifierExpr; - if (quantifier != null) { + + if (quantifier != null && !quantifiers.Contains(quantifier)) { + quantifiers.Add(quantifier); if (quantifier.SplitQuantifier != null) { var collection = quantifier.SplitQuantifier.Select(q => q as QuantifierExpr).Where(q => q != null); quantifierCollections.Add(new QuantifiersCollection(collection, reporter)); - return false; + foreach (var q in quantifier.SplitQuantifier) { quantifiers.Add(q); } } else { quantifierCollections.Add(new QuantifiersCollection(Enumerable.Repeat(quantifier, 1), reporter)); } } + return true; } } -- cgit v1.2.3 From c311617de4641a4eef54a0ce1bc51574effa2756 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Wed, 19 Aug 2015 15:58:36 -0700 Subject: Fix the equality test for literal expressions (compare by value, not by reference to boxed value) --- Source/Dafny/Triggers/TriggerExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Dafny/Triggers/TriggerExtensions.cs b/Source/Dafny/Triggers/TriggerExtensions.cs index 0a0ad547..a49ed13a 100644 --- a/Source/Dafny/Triggers/TriggerExtensions.cs +++ b/Source/Dafny/Triggers/TriggerExtensions.cs @@ -477,7 +477,7 @@ namespace Microsoft.Dafny.Triggers { } private static bool ShallowEq(LiteralExpr expr1, LiteralExpr expr2) { - if (expr1.Value != expr2.Value) { + if (!TriggerUtils.SameNullity(expr1.Value, expr2.Value) || (expr1.Value != null && !expr1.Value.Equals(expr2.Value))) { return false; } -- cgit v1.2.3 From 8b40a61b3e0dd5c8154cd2095a2fa813e0d7c9c5 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Wed, 19 Aug 2015 15:48:32 -0700 Subject: Collect ApplyExpr nodes when looking for trigger candidates --- Source/Dafny/Triggers/TriggersCollector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Dafny/Triggers/TriggersCollector.cs b/Source/Dafny/Triggers/TriggersCollector.cs index 221a4255..08d33af6 100644 --- a/Source/Dafny/Triggers/TriggersCollector.cs +++ b/Source/Dafny/Triggers/TriggersCollector.cs @@ -136,7 +136,7 @@ namespace Microsoft.Dafny.Triggers { expr.SubExpressions.Iter(e => Annotate(e)); TriggerAnnotation annotation; // TODO: Using ApplySuffix fixes the unresolved members problem in GenericSort - if (expr is FunctionCallExpr || expr is SeqSelectExpr || expr is MemberSelectExpr || expr is OldExpr || + if (expr is FunctionCallExpr || expr is SeqSelectExpr || expr is MemberSelectExpr || expr is OldExpr || expr is ApplyExpr || (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); -- cgit v1.2.3 From 8823b0e75fbf0460ddea5e10f9ee61f5fa171b44 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Wed, 19 Aug 2015 15:58:20 -0700 Subject: server: always print tooltips --- Source/DafnyServer/Utilities.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Source/DafnyServer/Utilities.cs b/Source/DafnyServer/Utilities.cs index 5bd6e7d5..3bc334b3 100644 --- a/Source/DafnyServer/Utilities.cs +++ b/Source/DafnyServer/Utilities.cs @@ -50,10 +50,11 @@ namespace Microsoft.Dafny { if (CommandLineOptions.Clo.Parse(args)) { // Dafny.DafnyOptions.O.ErrorTrace = 0; //FIXME // Dafny.DafnyOptions.O.ModelViewFile = "-"; - Dafny.DafnyOptions.O.UnicodeOutput = true; - Dafny.DafnyOptions.O.VerifySnapshots = 2; - Dafny.DafnyOptions.O.VcsCores = Math.Max(1, System.Environment.ProcessorCount - 1); - Dafny.DafnyOptions.O.Trace = trace; + DafnyOptions.O.PrintTooltips = true; + DafnyOptions.O.UnicodeOutput = true; + DafnyOptions.O.VerifySnapshots = 2; + DafnyOptions.O.VcsCores = Math.Max(1, System.Environment.ProcessorCount - 1); + DafnyOptions.O.Trace = trace; } else { throw new ServerException("Invalid command line options"); } -- cgit v1.2.3 From 2f5d59592c5930c32039855824cc49983f643641 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Wed, 19 Aug 2015 16:00:35 -0700 Subject: Enable unicode output in the VS extension --- Source/Dafny/Triggers/QuantifiersCollection.cs | 2 +- Source/DafnyExtension/DafnyDriver.cs | 1 + Source/DafnyExtension/ResolverTagger.cs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Dafny/Triggers/QuantifiersCollection.cs b/Source/Dafny/Triggers/QuantifiersCollection.cs index ecc193fc..a6340f10 100644 --- a/Source/Dafny/Triggers/QuantifiersCollection.cs +++ b/Source/Dafny/Triggers/QuantifiersCollection.cs @@ -156,7 +156,7 @@ namespace Microsoft.Dafny.Triggers { AddTriggersToMessage("Rejected triggers:", q.RejectedCandidates, msg, indent, true); #if QUANTIFIER_WARNINGS - string WARN = (msg.Length > 0 ? indent : "") + (DafnyOptions.O.UnicodeOutput ? "⚠ " : "(!) "); //FIXME set unicodeoutput in extension + string WARN = (msg.Length > 0 ? indent : "") + (DafnyOptions.O.UnicodeOutput ? "⚠ " : "(!) "); if (!q.CandidateTerms.Any()) { errorLevel = ErrorLevel.Warning; msg.Append(WARN).AppendLine("No terms found to trigger on."); diff --git a/Source/DafnyExtension/DafnyDriver.cs b/Source/DafnyExtension/DafnyDriver.cs index 36664a9b..30b0bd52 100644 --- a/Source/DafnyExtension/DafnyDriver.cs +++ b/Source/DafnyExtension/DafnyDriver.cs @@ -40,6 +40,7 @@ namespace DafnyLanguage options.ErrorTrace = 0; options.VcsCores = Math.Max(1, System.Environment.ProcessorCount - 1); options.ModelViewFile = "-"; + options.UnicodeOutput = true; Dafny.DafnyOptions.Install(options); // Read additional options from DafnyOptions.txt diff --git a/Source/DafnyExtension/ResolverTagger.cs b/Source/DafnyExtension/ResolverTagger.cs index 0ce68809..22706338 100644 --- a/Source/DafnyExtension/ResolverTagger.cs +++ b/Source/DafnyExtension/ResolverTagger.cs @@ -403,7 +403,7 @@ namespace DafnyLanguage chng(this, new SnapshotSpanEventArgs(new SnapshotSpan(snapshot, 0, snapshot.Length))); } - static TaskErrorCategory CategoryConversion(ErrorCategory cat) //CLEMENT: We've lost that info + static TaskErrorCategory CategoryConversion(ErrorCategory cat) { switch (cat) { -- cgit v1.2.3 From 43cbd76e07262d05434e36dff99f8d10eb59a773 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Wed, 19 Aug 2015 16:15:26 -0700 Subject: Use /tracePO instead of /trace in the server This removes the need for special treatment of test input (/trace includes timings in the output, which are not suitable for tests. /tracePO does not) --- Source/DafnyServer/Server.cs | 5 +- Source/DafnyServer/Utilities.cs | 12 +- Source/DafnyServer/VerificationTask.cs | 6 +- Test/server/minimal.transcript | 8 + Test/server/minimal.transcript.expect | 14 + Test/server/simple-session.transcript.expect | 694 ++++++++++++++++++++++++++- 6 files changed, 705 insertions(+), 34 deletions(-) create mode 100644 Test/server/minimal.transcript create mode 100644 Test/server/minimal.transcript.expect diff --git a/Source/DafnyServer/Server.cs b/Source/DafnyServer/Server.cs index 0a9ce599..e524a9a3 100644 --- a/Source/DafnyServer/Server.cs +++ b/Source/DafnyServer/Server.cs @@ -11,7 +11,6 @@ using Microsoft.Boogie; namespace Microsoft.Dafny { class Server { - private bool trace; private bool running; static void Main(string[] args) { @@ -25,7 +24,6 @@ namespace Microsoft.Dafny { } else if (hasArg && File.Exists(arg)) { Console.WriteLine("# Reading from {0}", Path.GetFileName(arg)); Console.SetIn(new StreamReader(arg, Encoding.UTF8)); - server.trace = false; server.Loop(); } else { server.Loop(); @@ -40,7 +38,6 @@ namespace Microsoft.Dafny { } public Server() { - this.trace = true; this.running = true; ExecutionEngine.printer = new DafnyConsolePrinter(); SetupConsole(); @@ -86,7 +83,7 @@ namespace Microsoft.Dafny { if (verb == "verify") { ServerUtils.checkArgs(command, 0); var payload = ReadPayload(); - VerificationTask.ReadTask(payload).Run(trace); + VerificationTask.ReadTask(payload).Run(); } else if (verb == "quit") { ServerUtils.checkArgs(command, 0); Exit(); diff --git a/Source/DafnyServer/Utilities.cs b/Source/DafnyServer/Utilities.cs index 3bc334b3..d6654ac1 100644 --- a/Source/DafnyServer/Utilities.cs +++ b/Source/DafnyServer/Utilities.cs @@ -43,18 +43,18 @@ namespace Microsoft.Dafny { } } - internal static void ApplyArgs(string[] args, bool trace) { + internal static void ApplyArgs(string[] args) { Dafny.DafnyOptions.Install(new Dafny.DafnyOptions()); Dafny.DafnyOptions.O.ProverKillTime = 10; if (CommandLineOptions.Clo.Parse(args)) { // Dafny.DafnyOptions.O.ErrorTrace = 0; //FIXME // Dafny.DafnyOptions.O.ModelViewFile = "-"; - DafnyOptions.O.PrintTooltips = true; - DafnyOptions.O.UnicodeOutput = true; - DafnyOptions.O.VerifySnapshots = 2; - DafnyOptions.O.VcsCores = Math.Max(1, System.Environment.ProcessorCount - 1); - DafnyOptions.O.Trace = trace; + DafnyOptions.O.VerifySnapshots = 2; // Use caching + DafnyOptions.O.VcsCores = Math.Max(1, System.Environment.ProcessorCount - 1); //FIXME + DafnyOptions.O.PrintTooltips = true; // Dump tooptips (ErrorLevel.Info) to stdout + DafnyOptions.O.UnicodeOutput = true; // Use pretty warning signs + DafnyOptions.O.TraceProofObligations = true; // Show which method is being verified, but don't show duration of verification } else { throw new ServerException("Invalid command line options"); } diff --git a/Source/DafnyServer/VerificationTask.cs b/Source/DafnyServer/VerificationTask.cs index dbafc781..a00688b1 100644 --- a/Source/DafnyServer/VerificationTask.cs +++ b/Source/DafnyServer/VerificationTask.cs @@ -44,15 +44,15 @@ namespace Microsoft.Dafny { source = "method selftest() { assert true; }" }; try { - task.Run(false); + task.Run(); Interaction.EOM(Interaction.SUCCESS, null); } catch (Exception ex) { Interaction.EOM(Interaction.FAILURE, ex); } } - internal void Run(bool trace = true) { - ServerUtils.ApplyArgs(args, trace); + internal void Run() { + ServerUtils.ApplyArgs(args); new DafnyHelper(filename, ProgramSource).Verify(); } } diff --git a/Test/server/minimal.transcript b/Test/server/minimal.transcript new file mode 100644 index 00000000..9625fb00 --- /dev/null +++ b/Test/server/minimal.transcript @@ -0,0 +1,8 @@ +# RUN: %server "%s" > "%t" +# RUN: %diff "%s.expect" "%t" +verify +eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp +bWl0OjIwIl0sImZpbGVuYW1lIjoidHJhbnNjcmlwdCIsInNvdXJjZSI6Im1ldGhvZCBBKGE6aW50 +KSByZXR1cm5zIChiOiBpbnQpIHtcbiAgYiA6PSBhO1xuICBhc3NlcnQgZmFsc2U7XG59XG4iLCJz +b3VyY2VJc0ZpbGUiOmZhbHNlfQ== +[[DAFNY-CLIENT: EOM]] diff --git a/Test/server/minimal.transcript.expect b/Test/server/minimal.transcript.expect new file mode 100644 index 00000000..bf3f9dfb --- /dev/null +++ b/Test/server/minimal.transcript.expect @@ -0,0 +1,14 @@ +# Reading from minimal.transcript + +Verifying CheckWellformed$$_module.__default.A ... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... + [2 proof obligations] error +transcript(3,9): Error: assertion violation +Execution trace: + (0,0): anon0 +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] diff --git a/Test/server/simple-session.transcript.expect b/Test/server/simple-session.transcript.expect index 89c3351d..91429d8e 100644 --- a/Test/server/simple-session.transcript.expect +++ b/Test/server/simple-session.transcript.expect @@ -1,24 +1,60 @@ # Reading from simple-session.transcript + +Verifying CheckWellformed$$_module.__default.A ... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... + [2 proof obligations] error transcript(3,9): Error: assertion violation Execution trace: (0,0): anon0 Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [2 proof obligations] error transcript(3,9): Error: assertion violation Execution trace: (0,0): anon0 Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [2 proof obligations] error transcript(3,9): Error: assertion violation Execution trace: (0,0): anon0 Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [2 proof obligations] error transcript(3,9): Error: assertion violation Execution trace: (0,0): anon0 Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] + +Verifying CheckWellformed$$_module.__default.A ... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... + [0 proof obligations] verified Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] transcript(5,13): Error: rbrace expected @@ -27,22 +63,114 @@ Verification completed successfully! transcript(6,2): Error: rbrace expected Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] -Verification completed successfully! -[SUCCESS] [[DAFNY-SERVER: EOM]] -Verification completed successfully! -[SUCCESS] [[DAFNY-SERVER: EOM]] + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... + [0 proof obligations] verified +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... +Retrieving cached verification result for implementation Impl$$_module.__default.M_k... + [0 proof obligations] verified +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... +Retrieving cached verification result for implementation Impl$$_module.__default.M_k... + [0 proof obligations] verified Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] transcript(7,0): Error: invalid UpdateStmt Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... +Retrieving cached verification result for implementation Impl$$_module.__default.M_k... + [0 proof obligations] verified Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] transcript(7,0): Error: ident expected Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] -Verification completed successfully! -[SUCCESS] [[DAFNY-SERVER: EOM]] + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... + [1 proof obligation] verified +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... +Retrieving cached verification result for implementation Impl$$_module.__default.M_k... + [1 proof obligation] verified Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] transcript(7,11): Error: the number of left-hand sides (2) and right-hand sides (1) must match for a multi-assignment @@ -51,21 +179,113 @@ Verification completed successfully! transcript(7,11): Error: the number of left-hand sides (2) and right-hand sides (1) must match for a multi-assignment Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] -Verification completed successfully! -[SUCCESS] [[DAFNY-SERVER: EOM]] -transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here -Verification completed successfully! -[SUCCESS] [[DAFNY-SERVER: EOM]] -transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here -Verification completed successfully! -[SUCCESS] [[DAFNY-SERVER: EOM]] -transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here -Verification completed successfully! -[SUCCESS] [[DAFNY-SERVER: EOM]] -transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here -Verification completed successfully! -[SUCCESS] [[DAFNY-SERVER: EOM]] -transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... + [2 proof obligations] verified +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... +Retrieving cached verification result for implementation Impl$$_module.__default.M_k... + [2 proof obligations] verified +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... +Retrieving cached verification result for implementation Impl$$_module.__default.M_k... + [2 proof obligations] verified +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... + [3 proof obligations] verified +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... +Retrieving cached verification result for implementation Impl$$_module.__default.M_k... + [3 proof obligations] verified +Verification completed successfully! +[SUCCESS] [[DAFNY-SERVER: EOM]] +transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... +Retrieving cached verification result for implementation Impl$$_module.__default.M_k... + [3 proof obligations] verified Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here @@ -77,6 +297,20 @@ transcript(6,2): Error: EOF expected Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... + [3 proof obligations] verified Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here @@ -112,15 +346,59 @@ transcript(10,27): Error: invalid UnaryExpression Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... + [1 proof obligation] error transcript(10,9): Error: assertion violation Execution trace: (0,0): anon0 Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... + [1 proof obligation] verified Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... +Retrieving cached verification result for implementation Impl$$_module.__default.M_k... + [1 proof obligation] verified Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here @@ -128,9 +406,39 @@ transcript(12,0): Error: invalid UpdateStmt Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... + [1 proof obligation] verified Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... +Retrieving cached verification result for implementation Impl$$_module.__default.M_k... + [1 proof obligation] verified Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here @@ -190,18 +498,130 @@ transcript(5,0): Warning: module-level methods are always non-instance, so the ' transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... +Retrieving cached verification result for implementation Impl$$_module.__default.M_k... + [1 proof obligation] verified + +Verifying CheckWellformed$$_module.__default.M0 ... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M0 ... + [5 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M1 ... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M1 ... + [5 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M2 ... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M2 ... + [5 proof obligations] verified Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... +Retrieving cached verification result for implementation Impl$$_module.__default.M_k... + [1 proof obligation] verified + +Verifying CheckWellformed$$_module.__default.M0 ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M0... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M0 ... +Retrieving cached verification result for implementation Impl$$_module.__default.M0... + [5 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M1 ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M1... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M1 ... +Retrieving cached verification result for implementation Impl$$_module.__default.M1... + [5 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M2 ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M2... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M2 ... +Retrieving cached verification result for implementation Impl$$_module.__default.M2... + [5 proof obligations] verified Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... +Retrieving cached verification result for implementation Impl$$_module.__default.M_k... + [1 proof obligation] verified + +Verifying CheckWellformed$$_module.__default.M0 ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M0... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M0 ... +Retrieving cached verification result for implementation Impl$$_module.__default.M0... + [5 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M1 ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M1... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M1 ... +Retrieving cached verification result for implementation Impl$$_module.__default.M1... + [5 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M2 ... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M2 ... + [2 proof obligations] error transcript(38,9): Error: assertion violation Execution trace: (0,0): anon0 @@ -218,6 +638,44 @@ transcript(5,0): Warning: module-level methods are always non-instance, so the ' transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... +Retrieving cached verification result for implementation Impl$$_module.__default.M_k... + [1 proof obligation] verified + +Verifying CheckWellformed$$_module.__default.M0 ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M0... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M0 ... +Retrieving cached verification result for implementation Impl$$_module.__default.M0... + [5 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M1 ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M1... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M1 ... +Retrieving cached verification result for implementation Impl$$_module.__default.M1... + [5 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M2 ... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M2 ... + [2 proof obligations] error transcript(38,9): Error: assertion violation Execution trace: (0,0): anon0 @@ -227,12 +685,90 @@ transcript(5,0): Warning: module-level methods are always non-instance, so the ' transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... +Retrieving cached verification result for implementation Impl$$_module.__default.M_k... + [1 proof obligation] verified + +Verifying CheckWellformed$$_module.__default.M0 ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M0... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M0 ... +Retrieving cached verification result for implementation Impl$$_module.__default.M0... + [5 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M1 ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M1... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M1 ... +Retrieving cached verification result for implementation Impl$$_module.__default.M1... + [5 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M2 ... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M2 ... + [2 proof obligations] verified Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... +Retrieving cached verification result for implementation Impl$$_module.__default.M_k... + [1 proof obligation] verified + +Verifying CheckWellformed$$_module.__default.M0 ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M0... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M0 ... +Retrieving cached verification result for implementation Impl$$_module.__default.M0... + [5 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M1 ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M1... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M1 ... +Retrieving cached verification result for implementation Impl$$_module.__default.M1... + [5 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M2 ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M2... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M2 ... +Retrieving cached verification result for implementation Impl$$_module.__default.M2... + [2 proof obligations] verified Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here @@ -260,6 +796,44 @@ transcript(5,0): Warning: module-level methods are always non-instance, so the ' transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... +Retrieving cached verification result for implementation Impl$$_module.__default.M_k... + [1 proof obligation] verified + +Verifying CheckWellformed$$_module.__default.M0 ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M0... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M0 ... +Retrieving cached verification result for implementation Impl$$_module.__default.M0... + [5 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M1 ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M1... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M1 ... +Retrieving cached verification result for implementation Impl$$_module.__default.M1... + [5 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M2 ... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M2 ... + [1 proof obligation] verified Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here @@ -287,12 +861,90 @@ transcript(5,0): Warning: module-level methods are always non-instance, so the ' transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... +Retrieving cached verification result for implementation Impl$$_module.__default.M_k... + [1 proof obligation] verified + +Verifying CheckWellformed$$_module.__default.M0 ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M0... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M0 ... +Retrieving cached verification result for implementation Impl$$_module.__default.M0... + [5 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M1 ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M1... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M1 ... +Retrieving cached verification result for implementation Impl$$_module.__default.M1... + [5 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M2 ... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M2 ... + [1 proof obligation] verified Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] transcript(5,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(15,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(24,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here transcript(33,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here + +Verifying CheckWellformed$$_module.__default.A ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.A... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.A ... +Retrieving cached verification result for implementation Impl$$_module.__default.A... + [0 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M_k ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M_k... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M_k ... +Retrieving cached verification result for implementation Impl$$_module.__default.M_k... + [1 proof obligation] verified + +Verifying CheckWellformed$$_module.__default.M0 ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M0... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M0 ... +Retrieving cached verification result for implementation Impl$$_module.__default.M0... + [5 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M1 ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M1... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M1 ... +Retrieving cached verification result for implementation Impl$$_module.__default.M1... + [5 proof obligations] verified + +Verifying CheckWellformed$$_module.__default.M2 ... +Retrieving cached verification result for implementation CheckWellformed$$_module.__default.M2... + [0 proof obligations] verified + +Verifying Impl$$_module.__default.M2 ... +Retrieving cached verification result for implementation Impl$$_module.__default.M2... + [1 proof obligation] verified Verification completed successfully! [SUCCESS] [[DAFNY-SERVER: EOM]] Verification completed successfully! -- cgit v1.2.3 From e5f9a4cbf74f7794ad13b2a5bd831fd54c20629c Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Wed, 19 Aug 2015 16:15:47 -0700 Subject: runTests: Accept tests one by one, even if they are given as a .lst file --- Test/runTests.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Test/runTests.py b/Test/runTests.py index eae29012..7cd90454 100644 --- a/Test/runTests.py +++ b/Test/runTests.py @@ -491,12 +491,13 @@ def run_tests(args): debug(Debug.ERROR, "Testing interrupted") -def diff(paths, accept, difftool): +def diff(paths, force_accept, difftool): for path in expand_lsts(paths): if not os.path.exists(path): debug(Debug.ERROR, "Not found: {}".format(path)) else: test = Test(None, path, [], None) + accept = force_accept if not accept: call([difftool, test.expect_path, test.temp_output_path]) -- 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(-) 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 487c6e48d305d57994b801440d4b62f1a4bfdd06 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Wed, 19 Aug 2015 22:12:36 -0700 Subject: Factor out some AST visiting code --- Source/Dafny/DafnyAst.cs | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ Source/Dafny/Rewriter.cs | 32 ++-------------------------- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index ffd6df63..89bba760 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -7725,6 +7725,33 @@ namespace Microsoft.Dafny { public void Visit(IEnumerable exprs) { exprs.Iter(Visit); } + public void Visit(IEnumerable exprs) { + exprs.Iter(Visit); + } + public void Visit(ICallable decl) { + if (decl is Function) { + Visit((Function)decl); + } else if (decl is Method) { + Visit((Method)decl); + } + //FIXME More? + } + public void Visit(Method method) { + Visit(method.Ens); + Visit(method.Req); + Visit(method.Mod.Expressions); + Visit(method.Decreases.Expressions); + if (method.Body != null) { Visit(method.Body); } + //FIXME More? + } + public void Visit(Function function) { + Visit(function.Ens); + Visit(function.Req); + Visit(function.Reads); + Visit(function.Decreases.Expressions); + if (function.Body != null) { Visit(function.Body); } + //FIXME More? + } protected virtual void VisitOneExpr(Expression expr) { Contract.Requires(expr != null); // by default, do nothing @@ -7771,6 +7798,33 @@ namespace Microsoft.Dafny { public void Visit(IEnumerable exprs, State st) { exprs.Iter(e => Visit(e, st)); } + public void Visit(IEnumerable exprs, State st) { + exprs.Iter(e => Visit(e, st)); + } + public void Visit(ICallable decl, State st) { + if (decl is Function) { + Visit((Function)decl, st); + } else if (decl is Method) { + Visit((Method)decl, st); + } + //FIXME More? + } + public void Visit(Method method, State st) { + Visit(method.Ens, st); + Visit(method.Req, st); + Visit(method.Mod.Expressions, st); + Visit(method.Decreases.Expressions, st); + if (method.Body != null) { Visit(method.Body, st); } + //FIXME More? + } + public void Visit(Function function, State st) { + Visit(function.Ens, st); + Visit(function.Req, st); + Visit(function.Reads, st); + Visit(function.Decreases.Expressions, st); + if (function.Body != null) { Visit(function.Body, st); } + //FIXME More? + } /// /// Visit one expression proper. This method is invoked before it is invoked on the /// sub-parts (sub-expressions and sub-statements). A return value of "true" says to diff --git a/Source/Dafny/Rewriter.cs b/Source/Dafny/Rewriter.cs index 83f49a12..230d9349 100644 --- a/Source/Dafny/Rewriter.cs +++ b/Source/Dafny/Rewriter.cs @@ -47,21 +47,7 @@ namespace Microsoft.Dafny var finder = new Triggers.QuantifierCollector(reporter); foreach (var decl in ModuleDefinition.AllCallables(m.TopLevelDecls)) { - if (decl is Function) { - var function = (Function)decl; - finder.Visit(function.Ens, null); - finder.Visit(function.Req, null); - if (function.Body != null) { - finder.Visit(function.Body, null); - } - } else if (decl is Method) { - var method = (Method)decl; - finder.Visit(method.Ens, null); - finder.Visit(method.Req, null); - if (method.Body != null) { - finder.Visit(method.Body, null); - } - } + finder.Visit(decl, null); } var triggersCollector = new Triggers.TriggersCollector(); @@ -80,21 +66,7 @@ namespace Microsoft.Dafny internal override void PostResolve(ModuleDefinition m) { var splitter = new Triggers.QuantifierSplitter(); foreach (var decl in ModuleDefinition.AllCallables(m.TopLevelDecls)) { - if (decl is Function) { - var function = (Function)decl; - splitter.Visit(function.Ens); - splitter.Visit(function.Req); - if (function.Body != null) { - splitter.Visit(function.Body); - } - } else if (decl is Method) { - var method = (Method)decl; - splitter.Visit(method.Ens); - splitter.Visit(method.Req); - if (method.Body != null) { - splitter.Visit(method.Body); - } - } + splitter.Visit(decl); } } } -- cgit v1.2.3 From 79679075d54060ec78a369879f7ec460d30f92f4 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Wed, 19 Aug 2015 22:13:33 -0700 Subject: Add a check for SplitQuantifiers that had been incorrectly left out from the merge --- Source/Dafny/Translator.cs | 88 ++++++++++++++++++++++------------------------ 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 23c0c3f6..8146d2cf 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -11526,56 +11526,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; + if (e.SplitQuantifier != null) { + return TrExpr(e.SplitQuantifierExpression); + } else { + List tyvars = translator.MkTyParamBinders(e.TypeArgs); + List bvars = new List(); - Bpl.Expr antecedent = Bpl.Expr.True; + var initEtran = this; + var bodyEtran = this; + bool _scratch = 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 { + 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 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.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)); + } + } } 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))". @@ -13092,13 +13096,7 @@ namespace Microsoft.Dafny { private bool CanSafelyInline(FunctionCallExpr fexp, Function f) { var visitor = new TriggersExplorer(); - - visitor.Visit(f.Body); - foreach (var expr in f.Ens) { visitor.Visit(expr); } - foreach (var expr in f.Req) { visitor.Visit(expr); } - foreach (var expr in f.Reads) { visitor.Visit(expr); } - foreach (var expr in f.Decreases.Expressions) { visitor.Visit(expr); } - + visitor.Visit(f); return f.Formals.Zip(fexp.Args).All(formal_concrete => CanSafelySubstitute(visitor.TriggerVariables, formal_concrete.Item1, formal_concrete.Item2)); } -- cgit v1.2.3 From dd4f127f36ec24fbcedaaae0e61e0894b2bf5e83 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Wed, 19 Aug 2015 22:14:56 -0700 Subject: Print matches for triggers as they appear in the buffer Triggers themselves, however, are printed exactly as used. For example, a term (x !in s) yields a trigger (x in s). --- Source/Dafny/Triggers/QuantifiersCollection.cs | 2 +- Source/Dafny/Triggers/TriggerExtensions.cs | 7 ++++--- Source/Dafny/Triggers/TriggersCollector.cs | 8 ++++++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Source/Dafny/Triggers/QuantifiersCollection.cs b/Source/Dafny/Triggers/QuantifiersCollection.cs index a6340f10..49cd84df 100644 --- a/Source/Dafny/Triggers/QuantifiersCollection.cs +++ b/Source/Dafny/Triggers/QuantifiersCollection.cs @@ -118,7 +118,7 @@ namespace Microsoft.Dafny.Triggers { c => !loopingSubterms[c].Any(), c => { looping.Add(c); - c.Annotation = "loops with " + loopingSubterms[c].MapConcat(t => Printer.ExprToString(t.Expr), ", "); + c.Annotation = "loops with " + loopingSubterms[c].MapConcat(t => "{" + Printer.ExprToString(t.OriginalExpr) + "}", ", "); }).ToList(); q.CouldSuppressLoops = safe.Count > 0; diff --git a/Source/Dafny/Triggers/TriggerExtensions.cs b/Source/Dafny/Triggers/TriggerExtensions.cs index a49ed13a..6c3f4ee7 100644 --- a/Source/Dafny/Triggers/TriggerExtensions.cs +++ b/Source/Dafny/Triggers/TriggerExtensions.cs @@ -24,6 +24,7 @@ namespace Microsoft.Dafny.Triggers { internal struct TriggerMatch { internal Expression Expr; + internal Expression OriginalExpr; internal Dictionary Bindings; internal static bool Eq(TriggerMatch t1, TriggerMatch t2) { @@ -110,10 +111,10 @@ namespace Microsoft.Dafny.Triggers { return ShallowEq_Top(expr, trigger) && TriggerUtils.SameLists(expr.SubExpressions, trigger.SubExpressions, (e1, e2) => MatchesTrigger(e1, e2, holes, bindings)); } - private static TriggerMatch? MatchAgainst(this Expression expr, Expression trigger, IEnumerable holes) { + private static TriggerMatch? MatchAgainst(this Expression expr, Expression trigger, IEnumerable holes, Expression originalExpr) { var bindings = new Dictionary(); if (expr.MatchesTrigger(trigger, new HashSet(holes), bindings)) { - return new TriggerMatch { Expr = expr, Bindings = bindings }; + return new TriggerMatch { Expr = expr, OriginalExpr = originalExpr ?? expr, Bindings = bindings }; } else { return null; } @@ -121,7 +122,7 @@ namespace Microsoft.Dafny.Triggers { internal static IEnumerable SubexpressionsMatchingTrigger(this QuantifierExpr quantifier, Expression trigger) { return quantifier.Term.AllSubExpressions() - .Select(e => TriggerUtils.CleanupExprForInclusionInTrigger(e).MatchAgainst(trigger, quantifier.BoundVars)) + .Select(e => TriggerUtils.CleanupExprForInclusionInTrigger(e).MatchAgainst(trigger, quantifier.BoundVars, e)) .Where(e => e.HasValue).Select(e => e.Value); } diff --git a/Source/Dafny/Triggers/TriggersCollector.cs b/Source/Dafny/Triggers/TriggersCollector.cs index 08d33af6..735baa01 100644 --- a/Source/Dafny/Triggers/TriggersCollector.cs +++ b/Source/Dafny/Triggers/TriggersCollector.cs @@ -13,7 +13,11 @@ namespace Microsoft.Dafny.Triggers { internal ISet Variables { get; set; } public override string ToString() { - return Printer.ExprToString(OriginalExpr); + return Printer.ExprToString(Expr); + // NOTE: Using OriginalExpr here could cause some confusion: + // for example, {a !in b} is a binary expression, yielding + // trigger {a in b}. Saying the trigger is a !in b would be + // rather misleading. } internal static bool Eq(TriggerTerm t1, TriggerTerm t2) { @@ -45,7 +49,7 @@ 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); + var matchingSubterms = this.MatchingSubterms(quantifier); return matchingSubterms.Where(tm => tm.CouldCauseLoops(Terms)); } -- cgit v1.2.3 From 19c70cd0d7a65c46bcaafa66b13bde43316bc081 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Wed, 19 Aug 2015 22:16:02 -0700 Subject: Add tests for quantifier splitting and trigger generation --- .../function-applications-are-triggers.dfy | 15 ++++++ .../function-applications-are-triggers.dfy.expect | 2 + .../large-quantifiers-dont-break-dafny.dfy | 61 ++++++++++++++++++++++ .../large-quantifiers-dont-break-dafny.dfy.expect | 2 + Test/triggers/loop-detection-is-not-too-strict.dfy | 21 ++++++++ .../loop-detection-is-not-too-strict.dfy.expect | 4 ++ .../nested-quantifiers-all-get-triggers.dfy | 9 ++++ .../nested-quantifiers-all-get-triggers.dfy.expect | 2 + ...ms-do-not-look-like-the-triggers-they-match.dfy | 16 ++++++ ...ot-look-like-the-triggers-they-match.dfy.expect | 10 ++++ ...s-yields-better-precondition-related-errors.dfy | 21 ++++++++ ...s-better-precondition-related-errors.dfy.expect | 32 ++++++++++++ Test/triggers/triggers-prevent-some-inlining.dfy | 26 +++++++++ .../triggers-prevent-some-inlining.dfy.expect | 2 + 14 files changed, 223 insertions(+) create mode 100644 Test/triggers/function-applications-are-triggers.dfy create mode 100644 Test/triggers/function-applications-are-triggers.dfy.expect create mode 100644 Test/triggers/large-quantifiers-dont-break-dafny.dfy create mode 100644 Test/triggers/large-quantifiers-dont-break-dafny.dfy.expect create mode 100644 Test/triggers/loop-detection-is-not-too-strict.dfy create mode 100644 Test/triggers/loop-detection-is-not-too-strict.dfy.expect create mode 100644 Test/triggers/nested-quantifiers-all-get-triggers.dfy create mode 100644 Test/triggers/nested-quantifiers-all-get-triggers.dfy.expect create mode 100644 Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy create mode 100644 Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect create mode 100644 Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy create mode 100644 Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy.expect create mode 100644 Test/triggers/triggers-prevent-some-inlining.dfy create mode 100644 Test/triggers/triggers-prevent-some-inlining.dfy.expect diff --git a/Test/triggers/function-applications-are-triggers.dfy b/Test/triggers/function-applications-are-triggers.dfy new file mode 100644 index 00000000..0fae8c0e --- /dev/null +++ b/Test/triggers/function-applications-are-triggers.dfy @@ -0,0 +1,15 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This files checks that function applications yield trigger candidates + +method M(P: (int -> int) -> bool, g: int -> int) + requires P.requires(g) + requires P(g) { + assume forall f: int -> int :: P.requires(f); + assume forall f: int -> int :: P(f) ==> f.requires(10) && f(10) == 0; + assert forall f: int -> int :: + (forall x :: f.requires(x) && g.requires(x) ==> f(x) == g(x)) ==> + f.requires(10) ==> + f(10) == 0; +} diff --git a/Test/triggers/function-applications-are-triggers.dfy.expect b/Test/triggers/function-applications-are-triggers.dfy.expect new file mode 100644 index 00000000..069e7767 --- /dev/null +++ b/Test/triggers/function-applications-are-triggers.dfy.expect @@ -0,0 +1,2 @@ + +Dafny program verifier finished with 2 verified, 0 errors diff --git a/Test/triggers/large-quantifiers-dont-break-dafny.dfy b/Test/triggers/large-quantifiers-dont-break-dafny.dfy new file mode 100644 index 00000000..8becae97 --- /dev/null +++ b/Test/triggers/large-quantifiers-dont-break-dafny.dfy @@ -0,0 +1,61 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This test ensures that the trigger collector (the routine that picks trigger +// candidates) does not actually consider all subsets of terms; if it did, the +// following would take horribly long + +predicate P0(x: bool) +predicate P1(x: bool) +predicate P2(x: bool) +predicate P3(x: bool) +predicate P4(x: bool) +predicate P5(x: bool) +predicate P6(x: bool) +predicate P7(x: bool) +predicate P8(x: bool) +predicate P9(x: bool) +predicate P10(x: bool) +predicate P11(x: bool) +predicate P12(x: bool) +predicate P13(x: bool) +predicate P14(x: bool) +predicate P15(x: bool) +predicate P16(x: bool) +predicate P17(x: bool) +predicate P18(x: bool) +predicate P19(x: bool) +predicate P20(x: bool) +predicate P21(x: bool) +predicate P22(x: bool) +predicate P23(x: bool) +predicate P24(x: bool) +predicate P25(x: bool) +predicate P26(x: bool) +predicate P27(x: bool) +predicate P28(x: bool) +predicate P29(x: bool) +predicate P30(x: bool) +predicate P31(x: bool) +predicate P32(x: bool) +predicate P33(x: bool) +predicate P34(x: bool) +predicate P35(x: bool) +predicate P36(x: bool) +predicate P37(x: bool) +predicate P38(x: bool) +predicate P39(x: bool) +predicate P40(x: bool) +predicate P41(x: bool) +predicate P42(x: bool) +predicate P43(x: bool) +predicate P44(x: bool) +predicate P45(x: bool) +predicate P46(x: bool) +predicate P47(x: bool) +predicate P48(x: bool) +predicate P49(x: bool) + +method M() { + assert forall x :: true || P0(x) || P1(x) || P2(x) || P3(x) || P4(x) || P5(x) || P6(x) || P7(x) || P8(x) || P9(x) || P10(x) || P11(x) || P12(x) || P13(x) || P14(x) || P15(x) || P16(x) || P17(x) || P18(x) || P19(x) || P20(x) || P21(x) || P22(x) || P23(x) || P24(x) || P25(x) || P26(x) || P27(x) || P28(x) || P29(x) || P30(x) || P31(x) || P32(x) || P33(x) || P34(x) || P35(x) || P36(x) || P37(x) || P38(x) || P39(x) || P40(x) || P41(x) || P42(x) || P43(x) || P44(x) || P45(x) || P46(x) || P47(x) || P48(x) || P49(x); +} diff --git a/Test/triggers/large-quantifiers-dont-break-dafny.dfy.expect b/Test/triggers/large-quantifiers-dont-break-dafny.dfy.expect new file mode 100644 index 00000000..c90560b0 --- /dev/null +++ b/Test/triggers/large-quantifiers-dont-break-dafny.dfy.expect @@ -0,0 +1,2 @@ + +Dafny program verifier finished with 52 verified, 0 errors diff --git a/Test/triggers/loop-detection-is-not-too-strict.dfy b/Test/triggers/loop-detection-is-not-too-strict.dfy new file mode 100644 index 00000000..c6722399 --- /dev/null +++ b/Test/triggers/loop-detection-is-not-too-strict.dfy @@ -0,0 +1,21 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This test shows that the loop detection engine makes compromises when looking +// for subexpressions matching a trigger; in particular, it allows a +// subexpression to match a trigger without reporting a loop and without being +// equal to that trigger, as long as the only differences are variable + +predicate P(x: int, y: int) + +method Test() { + // P(x, y) and P(y, x) might look like they would cause a loop. Since they + // only differ by their variables, though, they won't raise flags. + assume forall x: int, y: int :: P(x, y) == P(y, x); + + // This works independent of extra parentheses: + assume forall x: int, y: int :: P(x, y) == (P(y, x)); + + // Contrast with the following: + assume forall x: int, y: int :: P(x, y) == P(x, y+1); +} diff --git a/Test/triggers/loop-detection-is-not-too-strict.dfy.expect b/Test/triggers/loop-detection-is-not-too-strict.dfy.expect new file mode 100644 index 00000000..c2e5ef3a --- /dev/null +++ b/Test/triggers/loop-detection-is-not-too-strict.dfy.expect @@ -0,0 +1,4 @@ +loop-detection-is-not-too-strict.dfy(20,9): Warning: Selected triggers: {P(x, y)} (loops with {P(x, y + 1)}) + (!) Suppressing loops would leave this expression without triggers. + +Dafny program verifier finished with 3 verified, 0 errors diff --git a/Test/triggers/nested-quantifiers-all-get-triggers.dfy b/Test/triggers/nested-quantifiers-all-get-triggers.dfy new file mode 100644 index 00000000..c75cfab9 --- /dev/null +++ b/Test/triggers/nested-quantifiers-all-get-triggers.dfy @@ -0,0 +1,9 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This checks that nested quantifiers do get triggers, and that the parent +// quantifier does not get annotated twice + +method M() { + ghost var x := forall s: set, x: int :: (x in s ==> forall y :: y == x ==> y in s); +} diff --git a/Test/triggers/nested-quantifiers-all-get-triggers.dfy.expect b/Test/triggers/nested-quantifiers-all-get-triggers.dfy.expect new file mode 100644 index 00000000..069e7767 --- /dev/null +++ b/Test/triggers/nested-quantifiers-all-get-triggers.dfy.expect @@ -0,0 +1,2 @@ + +Dafny program verifier finished with 2 verified, 0 errors diff --git a/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy b/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy new file mode 100644 index 00000000..7423e086 --- /dev/null +++ b/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy @@ -0,0 +1,16 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This file shows how Dafny detects loops even for terms that are not literal +// AST matches. This file also checks that triggers are reported exactly as +// picked (that is, `x in s` yields `s[x]` for a multiset s), but matches as +// they appear in the buffer text (that is, `x+1 in s` is not translated to +// s[x+1] when highlited as a cause for a potential matching loop. + +method M() { + // This is an obvious loop + ghost var b := forall s: multiset, x: int :: s[x] > 0 ==> s[x+1] > 0; + + // x in s loops with s[x+1] due to the way [x in s] is translated + ghost var a := forall s: multiset, x: int :: x in s ==> s[x+1] > 0 && x+2 !in s; +} diff --git a/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect b/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect new file mode 100644 index 00000000..ef48f568 --- /dev/null +++ b/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect @@ -0,0 +1,10 @@ +some-terms-do-not-look-like-the-triggers-they-match.dfy(12,17): Warning: Selected triggers: {s[x]} (loops with {s[x + 1]}) + (!) Suppressing loops would leave this expression without triggers. +some-terms-do-not-look-like-the-triggers-they-match.dfy(15,17): Warning: For expression {x in s ==> s[x + 1] > 0}: + Selected triggers: {s[x]} (loops with {s[x + 1]}) + (!) Suppressing loops would leave this expression without triggers. +some-terms-do-not-look-like-the-triggers-they-match.dfy(15,17): Warning: For expression {x in s ==> x + 2 !in s}: + Selected triggers: {s[x]} (loops with {x + 2 !in s}) + (!) Suppressing loops would leave this expression without triggers. + +Dafny program verifier finished with 2 verified, 0 errors diff --git a/Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy b/Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy new file mode 100644 index 00000000..f25a624e --- /dev/null +++ b/Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy @@ -0,0 +1,21 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This tests shows that, since quantifiers are split, it becomes possible to know more precisely what part of a precondition did not hold at the call site. + +method f() + requires forall y :: y > 0 && y < 0 { +} + +method g(x: int) { + f(); +} + +function gf(): int + requires forall y :: y > 0 && y < 0 { + 1 +} + +function gg(x: int): int { + gf() +} diff --git a/Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy.expect b/Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy.expect new file mode 100644 index 00000000..c8e1a5fa --- /dev/null +++ b/Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy.expect @@ -0,0 +1,32 @@ +splitting-triggers-yields-better-precondition-related-errors.dfy(7,11): Warning: For expression {y > 0}: + (!) No terms found to trigger on. +splitting-triggers-yields-better-precondition-related-errors.dfy(7,11): Warning: For expression {y < 0}: + (!) No terms found to trigger on. +splitting-triggers-yields-better-precondition-related-errors.dfy(15,11): Warning: For expression {y > 0}: + (!) No terms found to trigger on. +splitting-triggers-yields-better-precondition-related-errors.dfy(15,11): Warning: For expression {y < 0}: + (!) No terms found to trigger on. +splitting-triggers-yields-better-precondition-related-errors.dfy(11,3): Error BP5002: A precondition for this call might not hold. +splitting-triggers-yields-better-precondition-related-errors.dfy(7,11): Related location: This is the precondition that might not hold. +splitting-triggers-yields-better-precondition-related-errors.dfy(7,34): Related location +Execution trace: + (0,0): anon0 +splitting-triggers-yields-better-precondition-related-errors.dfy(11,3): Error BP5002: A precondition for this call might not hold. +splitting-triggers-yields-better-precondition-related-errors.dfy(7,11): Related location: This is the precondition that might not hold. +splitting-triggers-yields-better-precondition-related-errors.dfy(7,25): Related location +Execution trace: + (0,0): anon0 +splitting-triggers-yields-better-precondition-related-errors.dfy(20,2): Error: possible violation of function precondition +splitting-triggers-yields-better-precondition-related-errors.dfy(15,11): Related location +splitting-triggers-yields-better-precondition-related-errors.dfy(15,34): Related location +Execution trace: + (0,0): anon0 + (0,0): anon4_Else +splitting-triggers-yields-better-precondition-related-errors.dfy(20,2): Error: possible violation of function precondition +splitting-triggers-yields-better-precondition-related-errors.dfy(15,11): Related location +splitting-triggers-yields-better-precondition-related-errors.dfy(15,25): Related location +Execution trace: + (0,0): anon0 + (0,0): anon4_Else + +Dafny program verifier finished with 4 verified, 4 errors diff --git a/Test/triggers/triggers-prevent-some-inlining.dfy b/Test/triggers/triggers-prevent-some-inlining.dfy new file mode 100644 index 00000000..04b8051c --- /dev/null +++ b/Test/triggers/triggers-prevent-some-inlining.dfy @@ -0,0 +1,26 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This file looks at the interactions between inlining and triggers. The +// sum_is_sum predicate gets a {sum(a, b)} trigger, which explicitly depends on +// one of the variables being passed in. Since triggers are generated prior to +// inlining (inlining happens during translation), inlining the last two +// instances of that call below would cause b+1 (a trigger killer) to pop up in +// a trigger. This would create an invalid trigger, so Dafny doesn't let it +// happen. + +function sum(a: int, b: int): int { + a + b +} + +predicate sum_is_sum(b: int, c: int) { + forall a: int :: sum(a, b) + c == a + b + c +} + +method can_we_inline(b: int, c: int) + ensures sum_is_sum(0, 0) // OK to inline + ensures sum_is_sum(b, c) // OK to inline + ensures sum_is_sum(b, c+1) // OK to inline + ensures sum_is_sum(b+1, c) // NOK to inline + ensures sum_is_sum(b+1, c+1) // NOK to inline +{ } diff --git a/Test/triggers/triggers-prevent-some-inlining.dfy.expect b/Test/triggers/triggers-prevent-some-inlining.dfy.expect new file mode 100644 index 00000000..73ba063c --- /dev/null +++ b/Test/triggers/triggers-prevent-some-inlining.dfy.expect @@ -0,0 +1,2 @@ + +Dafny program verifier finished with 4 verified, 0 errors -- cgit v1.2.3 From 8afb07fb60f9521212066fbbff233c034ee8af40 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Wed, 19 Aug 2015 22:17:18 -0700 Subject: Add a test to check that there are as many errors as failed preconditions --- Test/dafny0/one-message-per-failed-precondition.dfy | 20 ++++++++++++++++++++ .../one-message-per-failed-precondition.dfy.expect | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 Test/dafny0/one-message-per-failed-precondition.dfy create mode 100644 Test/dafny0/one-message-per-failed-precondition.dfy.expect diff --git a/Test/dafny0/one-message-per-failed-precondition.dfy b/Test/dafny0/one-message-per-failed-precondition.dfy new file mode 100644 index 00000000..ef4f5bd6 --- /dev/null +++ b/Test/dafny0/one-message-per-failed-precondition.dfy @@ -0,0 +1,20 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// When a function call violates two preconditions at the same time, it causes +// two errors to be reported for the same token + +method A(x: int) + requires x > 0 + requires x < 0 +{} + +method B(x: int) { + A(x); +} + +function fA(x: int): int + requires x > 0 + requires x < 0 { 1 } + +function fB(x: int): int { fA(x) } diff --git a/Test/dafny0/one-message-per-failed-precondition.dfy.expect b/Test/dafny0/one-message-per-failed-precondition.dfy.expect new file mode 100644 index 00000000..0a76965e --- /dev/null +++ b/Test/dafny0/one-message-per-failed-precondition.dfy.expect @@ -0,0 +1,20 @@ +one-message-per-failed-precondition.dfy(13,3): Error BP5002: A precondition for this call might not hold. +one-message-per-failed-precondition.dfy(9,13): Related location: This is the precondition that might not hold. +Execution trace: + (0,0): anon0 +one-message-per-failed-precondition.dfy(13,3): Error BP5002: A precondition for this call might not hold. +one-message-per-failed-precondition.dfy(8,13): Related location: This is the precondition that might not hold. +Execution trace: + (0,0): anon0 +one-message-per-failed-precondition.dfy(20,27): Error: possible violation of function precondition +one-message-per-failed-precondition.dfy(18,13): Related location +Execution trace: + (0,0): anon0 + (0,0): anon4_Else +one-message-per-failed-precondition.dfy(20,27): Error: possible violation of function precondition +one-message-per-failed-precondition.dfy(17,13): Related location +Execution trace: + (0,0): anon0 + (0,0): anon4_Else + +Dafny program verifier finished with 4 verified, 4 errors -- cgit v1.2.3 From 6e935875b5cfbdee8a7f6573f9f01c48db746d56 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Wed, 19 Aug 2015 22:22:54 -0700 Subject: Add a wishlist folder to the test suite, with things that we do not support (yet!) The curent examples include semi-bugs regarding calc statements and strings, and stuff about sequences --- Test/wishlist/calc.dfy | 17 +++++++++ Test/wishlist/calc.dfy.expect | 11 ++++++ Test/wishlist/sequences-literals.dfy | 58 +++++++++++++++++++++++++++++ Test/wishlist/sequences-literals.dfy.expect | 20 ++++++++++ Test/wishlist/sequences-s0-in-s.dfy | 25 +++++++++++++ Test/wishlist/sequences-s0-in-s.dfy.expect | 6 +++ Test/wishlist/strings.dfy | 6 +++ Test/wishlist/strings.dfy.expect | 5 +++ 8 files changed, 148 insertions(+) create mode 100644 Test/wishlist/calc.dfy create mode 100644 Test/wishlist/calc.dfy.expect create mode 100644 Test/wishlist/sequences-literals.dfy create mode 100644 Test/wishlist/sequences-literals.dfy.expect create mode 100644 Test/wishlist/sequences-s0-in-s.dfy create mode 100644 Test/wishlist/sequences-s0-in-s.dfy.expect create mode 100644 Test/wishlist/strings.dfy create mode 100644 Test/wishlist/strings.dfy.expect diff --git a/Test/wishlist/calc.dfy b/Test/wishlist/calc.dfy new file mode 100644 index 00000000..308fbb9a --- /dev/null +++ b/Test/wishlist/calc.dfy @@ -0,0 +1,17 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// There is a bug in Dafny that causes the error from `L` to be reported at +// position 0 in this file, instead of on a curly brace. + +lemma L() + ensures false { + calc { true; } +} + +// Empty calc statements work fine, though: + +lemma L'() + ensures false { + calc { } +} diff --git a/Test/wishlist/calc.dfy.expect b/Test/wishlist/calc.dfy.expect new file mode 100644 index 00000000..1d5a55a6 --- /dev/null +++ b/Test/wishlist/calc.dfy.expect @@ -0,0 +1,11 @@ +(0,-1): Error BP5003: A postcondition might not hold on this return path. +calc.dfy(8,10): Related location: This is the postcondition that might not hold. +Execution trace: + (0,0): anon0 + calc.dfy(9,5): anon2_Else +calc.dfy(15,16): Error BP5003: A postcondition might not hold on this return path. +calc.dfy(15,10): Related location: This is the postcondition that might not hold. +Execution trace: + (0,0): anon0 + +Dafny program verifier finished with 2 verified, 2 errors diff --git a/Test/wishlist/sequences-literals.dfy b/Test/wishlist/sequences-literals.dfy new file mode 100644 index 00000000..382349a4 --- /dev/null +++ b/Test/wishlist/sequences-literals.dfy @@ -0,0 +1,58 @@ +// RUN: %dafny /compile:0 /autoTriggers:1 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// Note: in the tests below, it could be useful to experiment with the +// following triggers for some of the library axioms: +// +// axiom (forall s0: Seq T, s1: Seq T, x: T :: +// { Seq#Contains(s0, x), Seq#Append(s0, s1) } +// { Seq#Contains(s1, x), Seq#Append(s0, s1) } +// Seq#Contains(Seq#Append(s0, s1), x) +// <==> Seq#Contains(s0, x) || Seq#Contains(s1, x)); +// +// axiom (forall s: Seq T, v: T, x: T :: +// { Seq#Contains(s, x), Seq#Build(s, v) } +// Seq#Contains(Seq#Build(s, v), x) <==> v == x || Seq#Contains(s, x)); +// +// Another, not necessarily incompatible approach would be to explicitly add +// `assume k in s` for each element k of constant lists. + +method SmallList() { + var s := [0, 1, 5, 6]; + if * { + // This fails: Dafny needs a hint here, because the triggers on the library axioms are pretty strict: + assert exists n :: n in s; // WISH + } else if * { + // This works + assert 0 in s; + assert exists n :: n in s; + } else if * { + // This also works, thanks to the magic of triggering on `$Box`. + assert exists n {:autotriggers false} :: n in s; + } +} + +method LargeList() { + var s := [0, 1, 2, 3, 4, 5, 6, 7, 8, /* 9, 10, 11, */ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, /* 119, 120, 121, */ 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136]; + if * { + // The hint fails here. Maybe because z3 gets into a loop trying to unwrap + // this large list? This is also very slow. + assert 0 in s; // WISH + assert exists n :: n in s; + } else if * { + // Strangely, the hint works here. Why? + assert 122 in s; + assert exists n :: n in s; + } else if * { + // This also fails; since z3 only goes to a depth of 100, this probably + // wouldn't work with relaxed triggers eithers + assert exists n :: n in s && n >= 120; + } else if * { + // This works: this is certainly more `triggering-on-$Box` magic, but I'm + // not sure exactly how it works + assert exists n {:autotriggers false} :: n in s && n >= 120; + } else if * { + // `$Box` only offers limited solace, though + assert exists n {:autotriggers false} :: n in s && n < 3; + } +} diff --git a/Test/wishlist/sequences-literals.dfy.expect b/Test/wishlist/sequences-literals.dfy.expect new file mode 100644 index 00000000..18e3f98a --- /dev/null +++ b/Test/wishlist/sequences-literals.dfy.expect @@ -0,0 +1,20 @@ +sequences-literals.dfy(24,11): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon7_Then +sequences-literals.dfy(40,13): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon17_Then +sequences-literals.dfy(49,11): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon20_Then + (0,0): anon7 +sequences-literals.dfy(56,11): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon24_Then + (0,0): anon15 + +Dafny program verifier finished with 2 verified, 4 errors diff --git a/Test/wishlist/sequences-s0-in-s.dfy b/Test/wishlist/sequences-s0-in-s.dfy new file mode 100644 index 00000000..20127917 --- /dev/null +++ b/Test/wishlist/sequences-s0-in-s.dfy @@ -0,0 +1,25 @@ +// RUN: %dafny /compile:0 /autoTriggers:1 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// The following is also due to a weakness in the axiomatization: namely, it is +// not easy to learn, using Dafny's axioms, that s[0] in s. One can of course +// prove it, but it doesn't come for free. + +method InSeqTriggers(s: seq, i: nat) + requires forall x :: x in s ==> x > 0; + requires |s| > 0 { + if * { + // Fails + assert s[0] > 0; // WISH + } else if * { + // Works + assert s[0] in s; + assert s[0] > 0; + } +} + +method InSeqNoAutoTriggers(s: seq, i: nat) + requires forall x {:autotriggers false} :: x in s ==> x > 0; + requires |s| > 0 { + assert s[0] > 0; // Works +} diff --git a/Test/wishlist/sequences-s0-in-s.dfy.expect b/Test/wishlist/sequences-s0-in-s.dfy.expect new file mode 100644 index 00000000..4633e5f6 --- /dev/null +++ b/Test/wishlist/sequences-s0-in-s.dfy.expect @@ -0,0 +1,6 @@ +sequences-s0-in-s.dfy(13,18): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon5_Then + +Dafny program verifier finished with 3 verified, 1 error diff --git a/Test/wishlist/strings.dfy b/Test/wishlist/strings.dfy new file mode 100644 index 00000000..372711b0 --- /dev/null +++ b/Test/wishlist/strings.dfy @@ -0,0 +1,6 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +method EqualityOfStrings() { + assert "a" != "b"; // WISH +} diff --git a/Test/wishlist/strings.dfy.expect b/Test/wishlist/strings.dfy.expect new file mode 100644 index 00000000..2817a66e --- /dev/null +++ b/Test/wishlist/strings.dfy.expect @@ -0,0 +1,5 @@ +strings.dfy(5,13): Error: assertion violation +Execution trace: + (0,0): anon0 + +Dafny program verifier finished with 1 verified, 1 error -- cgit v1.2.3 From e3fff39c37ed68cf718eab84613e3bbb02858653 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 20 Aug 2015 10:01:50 -0700 Subject: Allow users to disable quantifier splitting by with a {:split false} attribute --- Source/Dafny/Triggers/QuantifierSplitter.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/Dafny/Triggers/QuantifierSplitter.cs b/Source/Dafny/Triggers/QuantifierSplitter.cs index 4b223856..c50bc9c6 100644 --- a/Source/Dafny/Triggers/QuantifierSplitter.cs +++ b/Source/Dafny/Triggers/QuantifierSplitter.cs @@ -85,10 +85,12 @@ namespace Microsoft.Dafny.Triggers { protected override void VisitOneExpr(Expression expr) { var quantifier = expr as QuantifierExpr; - if (quantifier != null) { - var split = SplitQuantifier(quantifier).ToList(); - quantifier.SplitQuantifier = split; - //Console.WriteLine("!!! {0} => {1}", Printer.ExprToString(expr), rew.MapConcat(Printer.ExprToString, " ||| ")); + if (quantifier != null && quantifier.SplitQuantifier == null) { + bool splitAttr = true; + if (!Attributes.ContainsBool(quantifier.Attributes, "split", ref splitAttr) || splitAttr) { + var split = SplitQuantifier(quantifier).ToList(); + quantifier.SplitQuantifier = split; + } } } } -- cgit v1.2.3 From a8e619e3e23d5c426ef583411c3ddf223bc26fbe Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 20 Aug 2015 10:02:41 -0700 Subject: Add a test to show how trigger splitting balances the downsides of loop detection --- .../splitting-triggers-recovers-expressivity.dfy | 61 ++++++++++++++++++++++ ...tting-triggers-recovers-expressivity.dfy.expect | 10 ++++ 2 files changed, 71 insertions(+) create mode 100644 Test/triggers/splitting-triggers-recovers-expressivity.dfy create mode 100644 Test/triggers/splitting-triggers-recovers-expressivity.dfy.expect diff --git a/Test/triggers/splitting-triggers-recovers-expressivity.dfy b/Test/triggers/splitting-triggers-recovers-expressivity.dfy new file mode 100644 index 00000000..79a0dc72 --- /dev/null +++ b/Test/triggers/splitting-triggers-recovers-expressivity.dfy @@ -0,0 +1,61 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +predicate P(i: int) +predicate Q(i: int) + +/* This file demonstrates a case where automatic trigger splitting is useful to + prevent loop detection from reducing expressivity too much. */ + +lemma exists_0() + requires P(0) + ensures exists i {:split false} :: P(i) || (Q(i) ==> P(i+1)) { + // Fails: P(i) is not a trigger +} + +lemma forall_0(i: int) + requires forall j {:split false} :: j >= 0 ==> (P(j) && (Q(j) ==> P(j+1))) + requires i >= 0 + ensures P(i) { + // Fails: P(i) is not a trigger +} + + +lemma exists_1() + requires P(0) + ensures exists i {:split false} :: P(i) || (Q(i) ==> P(i+1)) { + assert Q(0) || !Q(0); + // Works: the dummy assertion introduces a term that causes the quantifier + // to trigger, producing a witness. + } + +lemma forall_1(i: int) + requires forall j {:split false} :: j >= 0 ==> (P(j) && (Q(j) ==> P(j+1))) + requires i >= 0 + ensures P(i) { + assert Q(i) || !Q(i); + // Works: the dummy assertion introduces a term that causes the quantifier + // to trigger, producing a witness. +} + + +lemma exists_2() + requires P(0) + ensures exists i :: P(i) || (Q(i) ==> P(i+1)) { + // Works: automatic trigger splitting allows P(i) to get its own triggers +} + +lemma forall_2(i: int) + requires forall j :: j >= 0 ==> (P(j) && (Q(j) ==> P(j+1))) + requires i >= 0 + ensures P(i) { + // Works: automatic trigger splitting allows P(i) to get its own triggers +} + + +lemma loop() + requires P(0) + requires forall i {:matchingloop} :: i >= 0 ==> Q(i) && (P(i) ==> P(i+1)) + ensures P(100) { + // Works: the matching loop is explicitly allowed +} diff --git a/Test/triggers/splitting-triggers-recovers-expressivity.dfy.expect b/Test/triggers/splitting-triggers-recovers-expressivity.dfy.expect new file mode 100644 index 00000000..8d7ff4ef --- /dev/null +++ b/Test/triggers/splitting-triggers-recovers-expressivity.dfy.expect @@ -0,0 +1,10 @@ +splitting-triggers-recovers-expressivity.dfy(12,63): Error BP5003: A postcondition might not hold on this return path. +splitting-triggers-recovers-expressivity.dfy(12,10): Related location: This is the postcondition that might not hold. +Execution trace: + (0,0): anon0 +splitting-triggers-recovers-expressivity.dfy(19,15): Error BP5003: A postcondition might not hold on this return path. +splitting-triggers-recovers-expressivity.dfy(19,10): Related location: This is the postcondition that might not hold. +Execution trace: + (0,0): anon0 + +Dafny program verifier finished with 14 verified, 2 errors -- cgit v1.2.3 From cfdaf4ccbea24636f2a94ca9d2f75b8699921d60 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 20 Aug 2015 10:28:47 -0700 Subject: Mark a few reporting functions as static --- Source/Dafny/Reporting.cs | 6 +++--- Source/Dafny/Translator.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Dafny/Reporting.cs b/Source/Dafny/Reporting.cs index c3797574..77869d9f 100644 --- a/Source/Dafny/Reporting.cs +++ b/Source/Dafny/Reporting.cs @@ -122,15 +122,15 @@ namespace Microsoft.Dafny { Info(source, tok, String.Format(msg, args)); } - public string ErrorToString(ErrorLevel header, IToken tok, string msg) { + public static string ErrorToString(ErrorLevel header, IToken tok, string msg) { return ErrorToString_Internal(": " + header.ToString(), tok.filename, tok.line, tok.col, ": " + msg); } - public string ErrorToString(ErrorLevel header, string filename, int oneBasedLine, int oneBasedColumn, string msg) { + public static string ErrorToString(ErrorLevel header, string filename, int oneBasedLine, int oneBasedColumn, string msg) { return ErrorToString_Internal(": " + header.ToString(), filename, oneBasedLine, oneBasedColumn, ": " + msg); } - public string ErrorToString_Internal(string header, string filename, int oneBasedLine, int oneBasedColumn, string msg) { + public static string ErrorToString_Internal(string header, string filename, int oneBasedLine, int oneBasedColumn, string msg) { return String.Format("{0}({1},{2}){3}{4}", filename, oneBasedLine, oneBasedColumn - 1, header, msg ?? ""); } } diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 8146d2cf..e39b777d 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -3585,7 +3585,7 @@ namespace Microsoft.Dafny { Contract.Requires(tok != null); Contract.Ensures(Contract.Result() != null); var col = tok.col + (isEndToken ? tok.val.Length : 0); - string description = reporter.ErrorToString_Internal(additionalInfo == null ? "" : ": ", tok.filename, tok.line, tok.col, additionalInfo ?? ""); + string description = ErrorReporter.ErrorToString_Internal(additionalInfo == null ? "" : ": ", tok.filename, tok.line, tok.col, additionalInfo ?? ""); QKeyValue kv = new QKeyValue(tok, "captureState", new List() { description }, null); return new Bpl.AssumeCmd(tok, Bpl.Expr.True, kv); } -- cgit v1.2.3 From aeb38d63369ee5b584ef9f5574c7371aea423759 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 20 Aug 2015 10:37:11 -0700 Subject: Simplify error reporting in the trigger generator to get cleaner messages --- Source/Dafny/Triggers/QuantifiersCollection.cs | 10 ++++++---- Test/triggers/loop-detection-is-not-too-strict.dfy.expect | 2 +- ...e-terms-do-not-look-like-the-triggers-they-match.dfy.expect | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Source/Dafny/Triggers/QuantifiersCollection.cs b/Source/Dafny/Triggers/QuantifiersCollection.cs index 49cd84df..ec2f1777 100644 --- a/Source/Dafny/Triggers/QuantifiersCollection.cs +++ b/Source/Dafny/Triggers/QuantifiersCollection.cs @@ -137,9 +137,9 @@ namespace Microsoft.Dafny.Triggers { private void CommitOne(QuantifierWithTriggers q, bool addHeader) { var errorLevel = ErrorLevel.Info; var msg = new StringBuilder(); - var indent = addHeader ? " " : " "; //FIXME if multiline messages were properly supported, this indentation wouldn't be needed + var indent = addHeader ? " " : ""; - if (!TriggerUtils.NeedsAutoTriggers(q.quantifier)) { //FIXME: matchingloop and autotriggers attributes are passed down to Boogie + if (!TriggerUtils.NeedsAutoTriggers(q.quantifier)) { //FIXME: matchingloop, split and autotriggers attributes are passed down to Boogie msg.AppendFormat("Not generating triggers for {{{0}}}.", Printer.ExprToString(q.quantifier.Term)).AppendLine(); // FIXME This shouldn't be printed for autoReqs. (see autoReq.dfy) // FIXME typeQuantifier? @@ -156,7 +156,7 @@ namespace Microsoft.Dafny.Triggers { AddTriggersToMessage("Rejected triggers:", q.RejectedCandidates, msg, indent, true); #if QUANTIFIER_WARNINGS - string WARN = (msg.Length > 0 ? indent : "") + (DafnyOptions.O.UnicodeOutput ? "⚠ " : "(!) "); + string WARN = indent + (DafnyOptions.O.UnicodeOutput ? "⚠ " : "(!) "); if (!q.CandidateTerms.Any()) { errorLevel = ErrorLevel.Warning; msg.Append(WARN).AppendLine("No terms found to trigger on."); @@ -171,7 +171,9 @@ namespace Microsoft.Dafny.Triggers { } if (msg.Length > 0) { - reporter.Message(MessageSource.Rewriter, errorLevel, q.quantifier.tok, msg.ToString().TrimEnd("\r\n".ToCharArray())); + // Extra indent added to make it easier to distinguish multiline error messages + var msgStr = msg.ToString().Replace(Environment.NewLine, Environment.NewLine + " ").TrimEnd("\r\n ".ToCharArray()); + reporter.Message(MessageSource.Rewriter, errorLevel, q.quantifier.tok, msgStr); } } diff --git a/Test/triggers/loop-detection-is-not-too-strict.dfy.expect b/Test/triggers/loop-detection-is-not-too-strict.dfy.expect index c2e5ef3a..29882da7 100644 --- a/Test/triggers/loop-detection-is-not-too-strict.dfy.expect +++ b/Test/triggers/loop-detection-is-not-too-strict.dfy.expect @@ -1,4 +1,4 @@ -loop-detection-is-not-too-strict.dfy(20,9): Warning: Selected triggers: {P(x, y)} (loops with {P(x, y + 1)}) +loop-detection-is-not-too-strict.dfy(20,9): Warning: Selected triggers: {P(x, y)} (loops with {P(x, y + 1)}) (!) Suppressing loops would leave this expression without triggers. Dafny program verifier finished with 3 verified, 0 errors diff --git a/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect b/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect index ef48f568..83648626 100644 --- a/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect +++ b/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect @@ -1,4 +1,4 @@ -some-terms-do-not-look-like-the-triggers-they-match.dfy(12,17): Warning: Selected triggers: {s[x]} (loops with {s[x + 1]}) +some-terms-do-not-look-like-the-triggers-they-match.dfy(12,17): Warning: Selected triggers: {s[x]} (loops with {s[x + 1]}) (!) Suppressing loops would leave this expression without triggers. some-terms-do-not-look-like-the-triggers-they-match.dfy(15,17): Warning: For expression {x in s ==> s[x + 1] > 0}: Selected triggers: {s[x]} (loops with {s[x + 1]}) -- cgit v1.2.3 From 0478d0c42a9e824f288bdbdd74ca99f86b36a345 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 20 Aug 2015 10:37:27 -0700 Subject: Add unit tests for trigger-related error messages --- .../loop-detection-messages--unit-tests.dfy | 29 ++++++++++++++++++++++ .../loop-detection-messages--unit-tests.dfy.expect | 13 ++++++++++ 2 files changed, 42 insertions(+) create mode 100644 Test/triggers/loop-detection-messages--unit-tests.dfy create mode 100644 Test/triggers/loop-detection-messages--unit-tests.dfy.expect diff --git a/Test/triggers/loop-detection-messages--unit-tests.dfy b/Test/triggers/loop-detection-messages--unit-tests.dfy new file mode 100644 index 00000000..d5439e09 --- /dev/null +++ b/Test/triggers/loop-detection-messages--unit-tests.dfy @@ -0,0 +1,29 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This file is a series of basic tests for loop detection, focusing on the +// warnings and information messages + +function f(i: int): int +function g(i: int): int + +method M() { + assert forall i :: false ==> f(i) == f(f(i)); + assert forall i :: false ==> f(i) == f(i+1); + assert forall i {:matchingloop} :: false ==> f(i) == f(i+1); + + assert forall i :: false ==> f(i) == f(i+1) && f(i) == g(i); + assert forall i :: false ==> f(i) == f(i+1) && f(i) == f(i); + assert forall i {:matchingloop} :: false ==> f(i) == f(i+1) && f(i) == f(i); + + assert forall i :: false ==> f(i) == 0; + assert forall i :: false ==> f(i+1) == 0; + assert forall i {:autotriggers false} :: false ==> f(i+1) == 0; + + assert forall i, j: int :: false ==> f(i) == f(j); + assert forall i, j: int :: false ==> f(i) == f(i); + assert forall i, j: int :: false ==> f(i) == f(i) && g(j) == 0; + assert forall i, j: int :: false ==> f(i) == f(i) && g(j+1) == 0; + assert forall i, j: int {:autotriggers false} :: false ==> f(i) == f(i); + assert forall i, j: int {:trigger f(i), g(j)} :: false ==> f(i) == f(i); +} diff --git a/Test/triggers/loop-detection-messages--unit-tests.dfy.expect b/Test/triggers/loop-detection-messages--unit-tests.dfy.expect new file mode 100644 index 00000000..1ebc0bce --- /dev/null +++ b/Test/triggers/loop-detection-messages--unit-tests.dfy.expect @@ -0,0 +1,13 @@ +loop-detection-messages--unit-tests.dfy(12,9): Warning: Selected triggers: {f(i)} (loops with {f(i + 1)}) + (!) Suppressing loops would leave this expression without triggers. +loop-detection-messages--unit-tests.dfy(16,9): Warning: For expression {false ==> f(i) == f(i + 1)}: + Selected triggers: {f(i)} (loops with {f(i + 1)}) + (!) Suppressing loops would leave this expression without triggers. +loop-detection-messages--unit-tests.dfy(20,9): Warning: (!) No terms found to trigger on. +loop-detection-messages--unit-tests.dfy(24,9): Warning: (!) No trigger covering all quantified variables found. +loop-detection-messages--unit-tests.dfy(26,9): Warning: For expression {false ==> f(i) == f(i)}: + (!) No trigger covering all quantified variables found. +loop-detection-messages--unit-tests.dfy(26,9): Warning: For expression {false ==> g(j + 1) == 0}: + (!) No trigger covering all quantified variables found. + +Dafny program verifier finished with 4 verified, 0 errors -- cgit v1.2.3 From 270b34fc36dcfc781e8f49df5339cb2e10e69828 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 20 Aug 2015 11:49:24 -0700 Subject: Fixed bug in type unification --- Source/Dafny/Resolver.cs | 7 +++++-- Test/dafny4/Regression0.dfy | 13 +++++++++++++ Test/dafny4/Regression0.dfy.expect | 4 ++++ 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 Test/dafny4/Regression0.dfy create mode 100644 Test/dafny4/Regression0.dfy.expect diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 58984b9b..2f2b5a54 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -4574,8 +4574,11 @@ namespace Microsoft.Dafny } else if (a is CollectionTypeProxy) { if (b is CollectionTypeProxy) { - a.T = b; - return UnifyTypes(((CollectionTypeProxy)a).Arg, ((CollectionTypeProxy)b).Arg); + var argUnificationSuccess = UnifyTypes(((CollectionTypeProxy)a).Arg, ((CollectionTypeProxy)b).Arg); + if (argUnificationSuccess) { + a.T = b; + } + return argUnificationSuccess; } else if (b is OperationTypeProxy) { var proxy = (OperationTypeProxy)b; if (proxy.AllowSeq && proxy.AllowSetVarieties && proxy.AllowISet) { diff --git a/Test/dafny4/Regression0.dfy b/Test/dafny4/Regression0.dfy new file mode 100644 index 00000000..be092261 --- /dev/null +++ b/Test/dafny4/Regression0.dfy @@ -0,0 +1,13 @@ +// RUN: %dafny /compile:0 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This once crashed Dafny + +method M() { + var s := [1, "2"]; + if * { + assert exists n :: n in s && n != 1; + } else { + assert "2" in s; + } +} diff --git a/Test/dafny4/Regression0.dfy.expect b/Test/dafny4/Regression0.dfy.expect new file mode 100644 index 00000000..9d1e3019 --- /dev/null +++ b/Test/dafny4/Regression0.dfy.expect @@ -0,0 +1,4 @@ +Regression0.dfy(7,15): Error: All elements of display must be of the same type (got string, but type of previous elements is int) +Regression0.dfy(9,28): Error: the type of this variable is underspecified +Regression0.dfy(11,15): Error: second argument to "in" must be a set, multiset, or sequence with elements of type string, or a map with domain string (instead got ?) +3 resolution/type errors detected in Regression0.dfy -- cgit v1.2.3 From 75e436019003604ea3118a13ccd9b6ea079c643f Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 20 Aug 2015 11:52:32 -0700 Subject: Discard error messages that refer to a non-nested TokenWrapper. The VS extension already did that, but it also filtered out nested tokens. That prevented info about triggers from being reported. Other interfaces (the CLI and Emacs, in particular) should have the same ability. Surprinsingly, this doesn't cause any tests failures. --- Source/Dafny/Reporting.cs | 13 +++---------- Source/DafnyExtension/IdentifierTagger.cs | 21 ++++----------------- 2 files changed, 7 insertions(+), 27 deletions(-) diff --git a/Source/Dafny/Reporting.cs b/Source/Dafny/Reporting.cs index 77869d9f..63ec520a 100644 --- a/Source/Dafny/Reporting.cs +++ b/Source/Dafny/Reporting.cs @@ -34,21 +34,14 @@ namespace Microsoft.Dafny { AllMessages[ErrorLevel.Info] = new List(); } - protected bool ShouldDiscard(MessageSource source, ErrorLevel level) { - return ((ErrorsOnly && level != ErrorLevel.Error) || - (!DafnyOptions.O.PrintTooltips && level == ErrorLevel.Info)); - } - // This is the only thing that needs to be overriden public virtual bool Message(MessageSource source, ErrorLevel level, IToken tok, string msg) { - var discard = ShouldDiscard(source, level); - + bool discard = (ErrorsOnly && level != ErrorLevel.Error) || // Discard non-errors if ErrorsOnly is set + (tok is TokenWrapper && !(tok is NestedToken)); // Discard wrapped tokens, except for nested ones if (!discard) { AllMessages[level].Add(new ErrorMessage { token = tok, message = msg }); - return true; } - - return false; + return !discard; } public int Count(ErrorLevel level) { diff --git a/Source/DafnyExtension/IdentifierTagger.cs b/Source/DafnyExtension/IdentifierTagger.cs index 13991496..5b70329d 100644 --- a/Source/DafnyExtension/IdentifierTagger.cs +++ b/Source/DafnyExtension/IdentifierTagger.cs @@ -15,7 +15,6 @@ using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.Utilities; using Bpl = Microsoft.Boogie; - namespace DafnyLanguage { @@ -136,8 +135,7 @@ namespace DafnyLanguage List newRegions = new List(); - foreach (var info in program.reporter.AllMessages[ErrorLevel.Info]) - { + foreach (var info in program.reporter.AllMessages[ErrorLevel.Info]) { IdRegion.Add(newRegions, info.token, info.message, info.token.val.Length); } @@ -368,11 +366,6 @@ namespace DafnyLanguage public readonly OccurrenceKind Kind; public readonly IVariable Variable; - static bool SurfaceSyntaxToken(Bpl.IToken tok) { - Contract.Requires(tok != null); - return !(tok is TokenWrapper); - } - public static void Add(List regions, Bpl.IToken tok, IVariable v, bool isDefinition, ModuleDefinition context) { Contract.Requires(regions != null); Contract.Requires(tok != null); @@ -383,27 +376,21 @@ namespace DafnyLanguage Contract.Requires(regions != null); Contract.Requires(tok != null); Contract.Requires(v != null); - if (SurfaceSyntaxToken(tok)) { - regions.Add(new IdRegion(tok, v, isDefinition, kind, context)); - } + regions.Add(new IdRegion(tok, v, isDefinition, kind, context)); } public static void Add(List regions, Bpl.IToken tok, Field decl, Microsoft.Dafny.Type showType, string kind, bool isDefinition, ModuleDefinition context) { Contract.Requires(regions != null); Contract.Requires(tok != null); Contract.Requires(decl != null); Contract.Requires(kind != null); - if (SurfaceSyntaxToken(tok)) { - regions.Add(new IdRegion(tok, decl, showType, kind, isDefinition, context)); - } + regions.Add(new IdRegion(tok, decl, showType, kind, isDefinition, context)); } public static void Add(List regions, Bpl.IToken tok, string text, int length) { Contract.Requires(regions != null); Contract.Requires(tok != null); Contract.Requires(text != null); - if (SurfaceSyntaxToken(tok)) { - regions.Add(new IdRegion(tok, OccurrenceKind.AdditionalInformation, text, length)); - } + regions.Add(new IdRegion(tok, OccurrenceKind.AdditionalInformation, text, length)); } private IdRegion(Bpl.IToken tok, IVariable v, bool isDefinition, string kind, ModuleDefinition context) { -- cgit v1.2.3 From 4b7ef1b817fe00bc32294b75797e7e264e2edbdd Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 20 Aug 2015 11:53:00 -0700 Subject: Move indentation of error messages to the ConsoleErrorReporter This indentation is just needed by CLI-based clients --- Source/Dafny/Reporting.cs | 5 ++++- Source/Dafny/Triggers/QuantifiersCollection.cs | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Source/Dafny/Reporting.cs b/Source/Dafny/Reporting.cs index 63ec520a..4cfbf20e 100644 --- a/Source/Dafny/Reporting.cs +++ b/Source/Dafny/Reporting.cs @@ -143,7 +143,10 @@ namespace Microsoft.Dafny { } public override bool Message(MessageSource source, ErrorLevel level, IToken tok, string msg) { - if (base.Message(source, level, tok, msg)) { + if (base.Message(source, level, tok, msg) && (DafnyOptions.O.PrintTooltips || level != ErrorLevel.Info)) { + // Extra indent added to make it easier to distinguish multiline error messages for clients that rely on the CLI + msg = msg.Replace(Environment.NewLine, Environment.NewLine + " "); + ConsoleColor previousColor = Console.ForegroundColor; Console.ForegroundColor = ColorForLevel(level); Console.WriteLine(ErrorToString(level, tok, msg)); diff --git a/Source/Dafny/Triggers/QuantifiersCollection.cs b/Source/Dafny/Triggers/QuantifiersCollection.cs index ec2f1777..e2afa2ee 100644 --- a/Source/Dafny/Triggers/QuantifiersCollection.cs +++ b/Source/Dafny/Triggers/QuantifiersCollection.cs @@ -171,8 +171,7 @@ namespace Microsoft.Dafny.Triggers { } if (msg.Length > 0) { - // Extra indent added to make it easier to distinguish multiline error messages - var msgStr = msg.ToString().Replace(Environment.NewLine, Environment.NewLine + " ").TrimEnd("\r\n ".ToCharArray()); + var msgStr = msg.ToString().TrimEnd("\r\n ".ToCharArray()); reporter.Message(MessageSource.Rewriter, errorLevel, q.quantifier.tok, msgStr); } } -- cgit v1.2.3 From 5176621e39435ddda9293b8ad0968cf1d7639fb6 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 20 Aug 2015 16:36:21 -0700 Subject: Implement the SelectTrigger method, removing redundant triggers. The idea is that we define an partial order on triggers (which may contain multiple terms), and find all the maximal elements. The algorithm as implemented is rather inefficient; in particular, is does not look at the structure of the graph of the relation (thus is does many redundant comparisons). See triggers/useless-triggers-are-removed.dfy for an example where such a filter is useful. --- Source/Dafny/Triggers/QuantifiersCollection.cs | 42 +++++++++++++--------- Source/Dafny/Triggers/TriggerUtils.cs | 11 +++--- Source/Dafny/Triggers/TriggersCollector.cs | 35 ++++++++++++++++-- Test/triggers/useless-triggers-are-removed.dfy | 25 +++++++++++++ .../useless-triggers-are-removed.dfy.expect | 17 +++++++++ 5 files changed, 106 insertions(+), 24 deletions(-) create mode 100644 Test/triggers/useless-triggers-are-removed.dfy create mode 100644 Test/triggers/useless-triggers-are-removed.dfy.expect diff --git a/Source/Dafny/Triggers/QuantifiersCollection.cs b/Source/Dafny/Triggers/QuantifiersCollection.cs index e2afa2ee..bfa90d81 100644 --- a/Source/Dafny/Triggers/QuantifiersCollection.cs +++ b/Source/Dafny/Triggers/QuantifiersCollection.cs @@ -7,7 +7,6 @@ using System.Text; using Microsoft.Boogie; using System.Diagnostics.Contracts; -//FIXME Generated triggers should be _triggers //FIXME: When scoring, do not consider old(x) to be higher than x. namespace Microsoft.Dafny.Triggers { @@ -28,7 +27,7 @@ namespace Microsoft.Dafny.Triggers { internal void TrimInvalidTriggers() { Contract.Requires(CandidateTerms != null); Contract.Requires(Candidates != null); - Candidates = TriggerUtils.Filter(Candidates, tr => tr.MentionsAll(quantifier.BoundVars), tr => { }).ToList(); + Candidates = TriggerUtils.Filter(Candidates, tr => tr, (tr, _) => tr.MentionsAll(quantifier.BoundVars), (tr, _) => { }).ToList(); } } @@ -60,7 +59,13 @@ namespace Microsoft.Dafny.Triggers { return additionalTerm.Variables.Where(v => v is BoundVar && !terms.Any(t => t.Variables.Contains(v))).Any(); } - //FIXME document that this assumes that the quantifiers live in the same context and share the same variables + /// + /// Collect triggers from the body of each quantifier, and share them + /// between all quantifiers. This method assumes that all quantifiers + /// actually come from the same context, and were the result of a split that + /// gave them all the same variables. + /// + /// void CollectAndShareTriggers(TriggersCollector triggersCollector) { var pool = quantifiers.SelectMany(q => triggersCollector.CollectTriggers(q.quantifier)); var distinctPool = pool.Deduplicate(TriggerTerm.Eq); @@ -79,13 +84,10 @@ namespace Microsoft.Dafny.Triggers { } void BuildDependenciesGraph() { - // FIXME: Think more about multi-quantifier dependencies - //class QuantifierDependency { - // QuantifierWithTriggers Cause; - // QuantifierWithTriggers Consequence; - // List Triggers; - // List MatchingTerm; - //} + // The problem of finding matching loops between multiple-triggers is hard; it + // seems to require one to track exponentially-sized dependencies between parts + // of triggers and quantifiers. For now, we only do single-quantifier loop + // detection } void SuppressMatchingLoops() { @@ -111,14 +113,14 @@ namespace Microsoft.Dafny.Triggers { foreach (var q in quantifiers) { var looping = new List(); - var loopingSubterms = q.Candidates.ToDictionary(candidate => candidate, candidate => candidate.LoopingSubterms(q.quantifier).ToList()); var safe = TriggerUtils.Filter( q.Candidates, - c => !loopingSubterms[c].Any(), - c => { - looping.Add(c); - c.Annotation = "loops with " + loopingSubterms[c].MapConcat(t => "{" + Printer.ExprToString(t.OriginalExpr) + "}", ", "); + candidate => candidate.LoopingSubterms(q.quantifier).ToList(), + (candidate, loopingSubterms) => !loopingSubterms.Any(), + (candidate, loopingSubterms) => { + looping.Add(candidate); + candidate.Annotation = "loops with " + loopingSubterms.MapConcat(t => "{" + Printer.ExprToString(t.OriginalExpr) + "}", ", "); }).ToList(); q.CouldSuppressLoops = safe.Count > 0; @@ -131,7 +133,15 @@ namespace Microsoft.Dafny.Triggers { } void SelectTriggers() { - //FIXME + foreach (var q in quantifiers) { //FIXME Check whether this makes verification faster + q.Candidates = TriggerUtils.Filter(q.Candidates, + candidate => q.Candidates.Where(candidate.IsStrongerThan).ToList(), + (candidate, weakerCandidates) => !weakerCandidates.Any(), + (candidate, weakerCandidates) => { + q.RejectedCandidates.Add(candidate); + candidate.Annotation = "stronger than " + String.Join(", ", weakerCandidates); + }).ToList(); + } } private void CommitOne(QuantifierWithTriggers q, bool addHeader) { diff --git a/Source/Dafny/Triggers/TriggerUtils.cs b/Source/Dafny/Triggers/TriggerUtils.cs index 9ebcf846..6f76464b 100644 --- a/Source/Dafny/Triggers/TriggerUtils.cs +++ b/Source/Dafny/Triggers/TriggerUtils.cs @@ -76,13 +76,14 @@ namespace Microsoft.Dafny.Triggers { return it1_has == it2_has && acc; } - internal static IEnumerable Filter(IEnumerable elements, Func predicate, Action reject) { + internal static IEnumerable Filter(IEnumerable elements, Func Converter, Func predicate, Action reject) { var positive = new List(); - foreach (var c in elements) { - if (predicate(c)) { - yield return c; + foreach (var elem in elements) { + var conv = Converter(elem); + if (predicate(elem, conv)) { + yield return elem; } else { - reject(c); + reject(elem, conv); } } } diff --git a/Source/Dafny/Triggers/TriggersCollector.cs b/Source/Dafny/Triggers/TriggersCollector.cs index 735baa01..3b2853ed 100644 --- a/Source/Dafny/Triggers/TriggersCollector.cs +++ b/Source/Dafny/Triggers/TriggersCollector.cs @@ -20,6 +20,20 @@ namespace Microsoft.Dafny.Triggers { // rather misleading. } + internal enum TermComparison { + SameStrength = 0, Stronger = 1, NotStronger = -1 + } + + internal TermComparison CompareTo(TriggerTerm other) { + if (this == other) { + return TermComparison.SameStrength; + } else if (Expr.AllSubExpressions(true).Any(other.Expr.ExpressionEq)) { + return TermComparison.Stronger; + } else { + return TermComparison.NotStronger; + } + } + internal static bool Eq(TriggerTerm t1, TriggerTerm t2) { return ExprExtensions.ExpressionEq(t1.Expr, t2.Expr); } @@ -41,7 +55,7 @@ namespace Microsoft.Dafny.Triggers { return vars.All(x => Terms.Any(term => term.Variables.Contains(x))); } - private string Repr { get { return String.Join(", ", Terms); } } + internal string Repr { get { return String.Join(", ", Terms); } } public override string ToString() { return "{" + Repr + "}" + (String.IsNullOrWhiteSpace(Annotation) ? "" : " (" + Annotation + ")"); @@ -58,8 +72,23 @@ namespace Microsoft.Dafny.Triggers { return Terms.SelectMany(term => quantifier.SubexpressionsMatchingTrigger(term.Expr)).Deduplicate(TriggerMatch.Eq); } - public String AsDafnyAttributeString() { - return "{:trigger " + Repr + "}"; + internal bool IsStrongerThan(TriggerCandidate that) { + if (this == that) { + return false; + } + + var hasStrictlyStrongerTerm = false; + foreach (var t in Terms) { + var comparison = that.Terms.Select(t.CompareTo).Max(); + + // All terms of `this` must be at least as strong as a term of `that` + if (comparison == TriggerTerm.TermComparison.NotStronger) { return false; } + + // Did we find a strictly stronger term? + hasStrictlyStrongerTerm = hasStrictlyStrongerTerm || comparison == TriggerTerm.TermComparison.Stronger; + } + + return hasStrictlyStrongerTerm; } } diff --git a/Test/triggers/useless-triggers-are-removed.dfy b/Test/triggers/useless-triggers-are-removed.dfy new file mode 100644 index 00000000..16458e41 --- /dev/null +++ b/Test/triggers/useless-triggers-are-removed.dfy @@ -0,0 +1,25 @@ +// RUN: %dafny /printTooltips /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This file ensures that Dafny does get rid of redundant triggers before +// annotating a quantifier, and that ths process does not interfere with cycle +// detection. + +function f(x: int): int +function g(x: int): int +function h(x: int): int + +method M() + // In the following, only f(x) is kept. Note that the subset enumeration was + // already smart enough to not build any trigger with multiple terms (it only + // built 5 candidates) + requires forall x: int :: f(x) + g(f(x)) + h(f(x)) + g(h(f(x))) + h(g(f(x))) == 0 + + // Loop detection still works fine: in the following example, the trigger is + // f(f(x)) + requires forall x: int :: f(x) == f(f(x)) + + // This works for multi-triggers, too: + requires forall x, y :: f(x) + g(f(y)) + g(y) + g(f(x)) == 0 +{ +} diff --git a/Test/triggers/useless-triggers-are-removed.dfy.expect b/Test/triggers/useless-triggers-are-removed.dfy.expect new file mode 100644 index 00000000..6c2c0a2b --- /dev/null +++ b/Test/triggers/useless-triggers-are-removed.dfy.expect @@ -0,0 +1,17 @@ +useless-triggers-are-removed.dfy(16,11): Info: Selected triggers: {f(x)} + Rejected triggers: + {h(g(f(x)))} (stronger than {g(f(x))}, {f(x)}) + {g(h(f(x)))} (stronger than {h(f(x))}, {f(x)}) + {h(f(x))} (stronger than {f(x)}) + {g(f(x))} (stronger than {f(x)}) +useless-triggers-are-removed.dfy(20,11): Info: Selected triggers: {f(f(x))} + Rejected triggers: {f(x)} (loops with {f(f(x))}) +useless-triggers-are-removed.dfy(23,11): Info: Selected triggers: + {g(f(x)), g(y)}, {f(y), f(x)} + Rejected triggers: + {g(y), f(x)} (loops with {g(f(y))}, {g(f(x))}) + {g(f(x)), g(f(y))} (stronger than {g(f(x)), f(y)}, {g(f(y)), f(x)}, {f(y), f(x)}) + {g(f(x)), f(y)} (stronger than {f(y), f(x)}) + {g(f(y)), f(x)} (stronger than {f(y), f(x)}) + +Dafny program verifier finished with 5 verified, 0 errors -- cgit v1.2.3 From fcffef4bdca732d9fffee24a0621a05c81a9b7c1 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 20 Aug 2015 16:36:27 -0700 Subject: Fix a typo --- Test/triggers/function-applications-are-triggers.dfy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/triggers/function-applications-are-triggers.dfy b/Test/triggers/function-applications-are-triggers.dfy index 0fae8c0e..67ffe4e4 100644 --- a/Test/triggers/function-applications-are-triggers.dfy +++ b/Test/triggers/function-applications-are-triggers.dfy @@ -1,7 +1,7 @@ // RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" // RUN: %diff "%s.expect" "%t" -// This files checks that function applications yield trigger candidates +// This file checks that function applications yield trigger candidates method M(P: (int -> int) -> bool, g: int -> int) requires P.requires(g) -- cgit v1.2.3 From 24812516d64ed809d7446680a79eac492ea6a201 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 20 Aug 2015 16:44:21 -0700 Subject: Make the dependency that DafnyServer has on DafnyPipeline explicit --- Source/Dafny.sln | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Dafny.sln b/Source/Dafny.sln index db035b4a..3e71a2a4 100644 --- a/Source/Dafny.sln +++ b/Source/Dafny.sln @@ -6,6 +6,9 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DafnyPipeline", "Dafny\DafnyPipeline.csproj", "{FE44674A-1633-4917-99F4-57635E6FA740}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DafnyServer", "DafnyServer\DafnyServer.csproj", "{AC9B21AE-EBC1-4A27-AD11-ED031FC7B4A2}" + ProjectSection(ProjectDependencies) = postProject + {FE44674A-1633-4917-99F4-57635E6FA740} = {FE44674A-1633-4917-99F4-57635E6FA740} + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution -- 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 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(-) 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 efcd1e908cb7ea05e78faffd206fa5ce1e966b74 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 20 Aug 2015 17:29:15 -0700 Subject: Changed equality tests involving traits from using strings to using reference equality --- Source/Dafny/Resolver.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 2f2b5a54..a1e89805 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -4342,13 +4342,12 @@ namespace Microsoft.Dafny successSoFar = UnifyTypes(aa.TypeArgs[i], bb.TypeArgs[i]); } return successSoFar; - } - else if ((rcb is TraitDecl) && (rca is TraitDecl)) { - return ((TraitDecl)rcb).FullCompileName == ((TraitDecl)rca).FullCompileName; - } else if ((rcb is ClassDecl) && (rca is TraitDecl)) { - return ((ClassDecl)rcb).TraitsObj.Any(tr => tr.FullCompileName == ((TraitDecl)rca).FullCompileName); - } else if ((rca is ClassDecl) && (rcb is TraitDecl)) { - return ((ClassDecl)rca).TraitsObj.Any(tr => tr.FullCompileName == ((TraitDecl)rcb).FullCompileName); + } else if (rcb is TraitDecl && rca is TraitDecl) { + return rca == rcb; + } else if (rcb is ClassDecl && rca is TraitDecl) { + return ((ClassDecl)rcb).TraitsObj.Contains(rca); + } else if (rca is ClassDecl && rcb is TraitDecl) { + return ((ClassDecl)rca).TraitsObj.Contains(rcb); } else if (aa.ResolvedParam != null && aa.ResolvedParam == bb.ResolvedParam) { // type parameters if (aa.TypeArgs.Count != bb.TypeArgs.Count) { -- cgit v1.2.3 From e676ad0877d31cb73a1a6bb5aae677ac64593fd6 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 20 Aug 2015 19:58:46 -0700 Subject: Cleanup a number of FIXMEs that I had left in the code --- Source/Dafny/DafnyAst.cs | 12 ++++++------ Source/Dafny/Resolver.cs | 6 +++--- Source/Dafny/Triggers/QuantifiersCollection.cs | 8 ++------ Source/Dafny/Triggers/TriggersCollector.cs | 6 ++++-- Source/DafnyServer/DafnyHelper.cs | 2 +- Source/DafnyServer/Server.cs | 9 +-------- Source/DafnyServer/Utilities.cs | 6 ++---- 7 files changed, 19 insertions(+), 30 deletions(-) diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index 14a354c4..06f6d856 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -7769,7 +7769,7 @@ namespace Microsoft.Dafny { } else if (decl is Method) { Visit((Method)decl); } - //FIXME More? + //TODO More? } public void Visit(Method method) { Visit(method.Ens); @@ -7777,7 +7777,7 @@ namespace Microsoft.Dafny { Visit(method.Mod.Expressions); Visit(method.Decreases.Expressions); if (method.Body != null) { Visit(method.Body); } - //FIXME More? + //TODO More? } public void Visit(Function function) { Visit(function.Ens); @@ -7785,7 +7785,7 @@ namespace Microsoft.Dafny { Visit(function.Reads); Visit(function.Decreases.Expressions); if (function.Body != null) { Visit(function.Body); } - //FIXME More? + //TODO More? } protected virtual void VisitOneExpr(Expression expr) { Contract.Requires(expr != null); @@ -7842,7 +7842,7 @@ namespace Microsoft.Dafny { } else if (decl is Method) { Visit((Method)decl, st); } - //FIXME More? + //TODO More? } public void Visit(Method method, State st) { Visit(method.Ens, st); @@ -7850,7 +7850,7 @@ namespace Microsoft.Dafny { Visit(method.Mod.Expressions, st); Visit(method.Decreases.Expressions, st); if (method.Body != null) { Visit(method.Body, st); } - //FIXME More? + //TODO More? } public void Visit(Function function, State st) { Visit(function.Ens, st); @@ -7858,7 +7858,7 @@ namespace Microsoft.Dafny { Visit(function.Reads, st); Visit(function.Decreases.Expressions, st); if (function.Body != null) { Visit(function.Body, st); } - //FIXME More? + //TODO More? } /// /// Visit one expression proper. This method is invoked before it is invoked on the diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 2f2b5a54..e5fe7cf8 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -199,7 +199,7 @@ namespace Microsoft.Dafny public void ResolveProgram(Program prog) { Contract.Requires(prog != null); - var origErrorCount = reporter.Count(ErrorLevel.Error); //FIXME (Clement): This is used further below, but not in the >0 comparisons in the next few lines. Is that right? + var origErrorCount = reporter.Count(ErrorLevel.Error); //TODO: This is used further below, but not in the >0 comparisons in the next few lines. Is that right? var bindings = new ModuleBindings(null); var b = BindModuleNames(prog.DefaultModuleDef, bindings); bindings.BindName("_module", prog.DefaultModule, b); @@ -8301,7 +8301,7 @@ namespace Microsoft.Dafny receiver = new StaticReceiverExpr(expr.tok, (ClassDecl)member.EnclosingClass, true); } else { if (!scope.AllowInstance) { - reporter.Error(MessageSource.Resolver, expr.tok, "'this' is not allowed in a 'static' context"); //FIXME: Rephrase this + reporter.Error(MessageSource.Resolver, expr.tok, "'this' is not allowed in a 'static' context"); //TODO: Rephrase this // nevertheless, set "receiver" to a value so we can continue resolution } receiver = new ImplicitThisExpr(expr.tok); @@ -8604,7 +8604,7 @@ namespace Microsoft.Dafny Dictionary members; if (classMembers.TryGetValue(cd, out members) && members.TryGetValue(expr.SuffixName, out member)) { if (!member.IsStatic) { - reporter.Error(MessageSource.Resolver, expr.tok, "accessing member '{0}' requires an instance expression", expr.SuffixName); //FIXME Unify with similar error message + reporter.Error(MessageSource.Resolver, expr.tok, "accessing member '{0}' requires an instance expression", expr.SuffixName); //TODO Unify with similar error messages // nevertheless, continue creating an expression that approximates a correct one } var receiver = new StaticReceiverExpr(expr.tok, (UserDefinedType)ty.NormalizeExpand(), (ClassDecl)member.EnclosingClass, false); diff --git a/Source/Dafny/Triggers/QuantifiersCollection.cs b/Source/Dafny/Triggers/QuantifiersCollection.cs index bfa90d81..8e4c3261 100644 --- a/Source/Dafny/Triggers/QuantifiersCollection.cs +++ b/Source/Dafny/Triggers/QuantifiersCollection.cs @@ -7,8 +7,6 @@ using System.Text; using Microsoft.Boogie; using System.Diagnostics.Contracts; -//FIXME: When scoring, do not consider old(x) to be higher than x. - namespace Microsoft.Dafny.Triggers { class QuantifierWithTriggers { internal QuantifierExpr quantifier; @@ -149,16 +147,14 @@ namespace Microsoft.Dafny.Triggers { var msg = new StringBuilder(); var indent = addHeader ? " " : ""; - if (!TriggerUtils.NeedsAutoTriggers(q.quantifier)) { //FIXME: matchingloop, split and autotriggers attributes are passed down to Boogie + if (!TriggerUtils.NeedsAutoTriggers(q.quantifier)) { // NOTE: matchingloop, split and autotriggers attributes are passed down to Boogie msg.AppendFormat("Not generating triggers for {{{0}}}.", Printer.ExprToString(q.quantifier.Term)).AppendLine(); - // FIXME This shouldn't be printed for autoReqs. (see autoReq.dfy) - // FIXME typeQuantifier? } else { if (addHeader) { msg.AppendFormat("For expression {{{0}}}:", Printer.ExprToString(q.quantifier.Term)).AppendLine(); } - foreach (var candidate in q.Candidates) { //FIXME make this _trigger instead of trigger + foreach (var candidate in q.Candidates) { q.quantifier.Attributes = new Attributes("trigger", candidate.Terms.Select(t => t.Expr).ToList(), q.quantifier.Attributes); } diff --git a/Source/Dafny/Triggers/TriggersCollector.cs b/Source/Dafny/Triggers/TriggersCollector.cs index 3b2853ed..30f7b9e8 100644 --- a/Source/Dafny/Triggers/TriggersCollector.cs +++ b/Source/Dafny/Triggers/TriggersCollector.cs @@ -249,10 +249,12 @@ namespace Microsoft.Dafny.Triggers { return new TriggerAnnotation(isTriggerKiller || CollectIsKiller(expr), CollectVariables(expr), CollectExportedCandidates(expr)); } - // FIXME document that this will contain duplicates + /// + /// Collect terms in the body of the subexpressions of the argument that look like quantifiers. The results of this function can contain duplicate terms. + /// 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 + // NOTE: We could check for existing trigger attributes and return that instead return Annotate(quantifier).PrivateTerms; } diff --git a/Source/DafnyServer/DafnyHelper.cs b/Source/DafnyServer/DafnyHelper.cs index 952f71c5..3204fdb3 100644 --- a/Source/DafnyServer/DafnyHelper.cs +++ b/Source/DafnyServer/DafnyHelper.cs @@ -74,7 +74,7 @@ namespace Microsoft.Dafny { ExecutionEngine.CoalesceBlocks(boogieProgram); ExecutionEngine.Inline(boogieProgram); - //FIXME Could capture errors instead of printing them (pass a delegate instead of null) + //NOTE: We could capture errors instead of printing them (pass a delegate instead of null) switch (ExecutionEngine.InferAndVerify(boogieProgram, new PipelineStatistics(), "ServerProgram", null, DateTime.UtcNow.Ticks.ToString())) { case PipelineOutcome.Done: case PipelineOutcome.VerificationCompleted: diff --git a/Source/DafnyServer/Server.cs b/Source/DafnyServer/Server.cs index e524a9a3..77840007 100644 --- a/Source/DafnyServer/Server.cs +++ b/Source/DafnyServer/Server.cs @@ -32,9 +32,8 @@ namespace Microsoft.Dafny { private void SetupConsole() { var utf8 = new UTF8Encoding(false, true); + Console.InputEncoding = utf8; Console.OutputEncoding = utf8; - Console.OutputEncoding = utf8; - Console.CancelKeyPress += CancelKeyPress; } public Server() { @@ -43,12 +42,6 @@ namespace Microsoft.Dafny { SetupConsole(); } - void CancelKeyPress(object sender, ConsoleCancelEventArgs e) { - // e.Cancel = true; - // FIXME TerminateProver and RunningProverFromCommandLine - // Cancel the current verification? TerminateProver()? Or kill entirely? - } - bool EndOfPayload(out string line) { line = Console.ReadLine(); return line == null || line == Interaction.CLIENT_EOM_TAG; diff --git a/Source/DafnyServer/Utilities.cs b/Source/DafnyServer/Utilities.cs index d6654ac1..59b3abb9 100644 --- a/Source/DafnyServer/Utilities.cs +++ b/Source/DafnyServer/Utilities.cs @@ -45,13 +45,11 @@ namespace Microsoft.Dafny { internal static void ApplyArgs(string[] args) { Dafny.DafnyOptions.Install(new Dafny.DafnyOptions()); - Dafny.DafnyOptions.O.ProverKillTime = 10; + Dafny.DafnyOptions.O.ProverKillTime = 15; //This is just a default; it can be overriden if (CommandLineOptions.Clo.Parse(args)) { - // Dafny.DafnyOptions.O.ErrorTrace = 0; //FIXME - // Dafny.DafnyOptions.O.ModelViewFile = "-"; DafnyOptions.O.VerifySnapshots = 2; // Use caching - DafnyOptions.O.VcsCores = Math.Max(1, System.Environment.ProcessorCount - 1); //FIXME + DafnyOptions.O.VcsCores = Math.Max(1, System.Environment.ProcessorCount / 2); // Don't use too many cores DafnyOptions.O.PrintTooltips = true; // Dump tooptips (ErrorLevel.Info) to stdout DafnyOptions.O.UnicodeOutput = true; // Use pretty warning signs DafnyOptions.O.TraceProofObligations = true; // Show which method is being verified, but don't show duration of verification -- cgit v1.2.3 From af6f23ba1869c0450c44e917becc48263b565327 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 21 Aug 2015 09:39:49 -0700 Subject: Add /printTooltips to trigger tests --- .../function-applications-are-triggers.dfy | 2 +- .../function-applications-are-triggers.dfy.expect | 11 ++++++++ .../large-quantifiers-dont-break-dafny.dfy | 2 +- .../large-quantifiers-dont-break-dafny.dfy.expect | 2 ++ Test/triggers/loop-detection-is-not-too-strict.dfy | 2 +- .../loop-detection-is-not-too-strict.dfy.expect | 4 +++ .../loop-detection-messages--unit-tests.dfy | 2 +- .../loop-detection-messages--unit-tests.dfy.expect | 24 ++++++++++++++++++ .../nested-quantifiers-all-get-triggers.dfy | 2 +- .../nested-quantifiers-all-get-triggers.dfy.expect | 2 ++ ...ms-do-not-look-like-the-triggers-they-match.dfy | 2 +- .../splitting-triggers-recovers-expressivity.dfy | 2 +- ...tting-triggers-recovers-expressivity.dfy.expect | 29 ++++++++++++++++++++++ ...s-yields-better-precondition-related-errors.dfy | 2 +- Test/triggers/triggers-prevent-some-inlining.dfy | 2 +- .../triggers-prevent-some-inlining.dfy.expect | 5 ++++ Test/triggers/useless-triggers-are-removed.dfy | 2 +- 17 files changed, 87 insertions(+), 10 deletions(-) diff --git a/Test/triggers/function-applications-are-triggers.dfy b/Test/triggers/function-applications-are-triggers.dfy index 67ffe4e4..0aad9018 100644 --- a/Test/triggers/function-applications-are-triggers.dfy +++ b/Test/triggers/function-applications-are-triggers.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" // RUN: %diff "%s.expect" "%t" // This file checks that function applications yield trigger candidates diff --git a/Test/triggers/function-applications-are-triggers.dfy.expect b/Test/triggers/function-applications-are-triggers.dfy.expect index 069e7767..7922e88d 100644 --- a/Test/triggers/function-applications-are-triggers.dfy.expect +++ b/Test/triggers/function-applications-are-triggers.dfy.expect @@ -1,2 +1,13 @@ +function-applications-are-triggers.dfy(9,9): Info: Selected triggers: {P.requires(f)} +function-applications-are-triggers.dfy(10,9): Info: For expression {P(f) ==> f.requires(10)}: + Selected triggers: + {f(10)}, {f.requires(10)}, {P(f)} +function-applications-are-triggers.dfy(10,9): Info: For expression {P(f) ==> f(10) == 0}: + Selected triggers: + {f(10)}, {f.requires(10)}, {P(f)} +function-applications-are-triggers.dfy(11,9): Info: Selected triggers: + {f(10)}, {f.requires(10)} +function-applications-are-triggers.dfy(12,5): Info: Selected triggers: + {g(x), f(x)}, {g(x), f.requires(x)}, {g(x)}, {f(x)}, {g.requires(x), f.requires(x)}, {g.requires(x)}, {f.requires(x)} Dafny program verifier finished with 2 verified, 0 errors diff --git a/Test/triggers/large-quantifiers-dont-break-dafny.dfy b/Test/triggers/large-quantifiers-dont-break-dafny.dfy index 8becae97..58eb56e1 100644 --- a/Test/triggers/large-quantifiers-dont-break-dafny.dfy +++ b/Test/triggers/large-quantifiers-dont-break-dafny.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" // RUN: %diff "%s.expect" "%t" // This test ensures that the trigger collector (the routine that picks trigger diff --git a/Test/triggers/large-quantifiers-dont-break-dafny.dfy.expect b/Test/triggers/large-quantifiers-dont-break-dafny.dfy.expect index c90560b0..5e7c14b9 100644 --- a/Test/triggers/large-quantifiers-dont-break-dafny.dfy.expect +++ b/Test/triggers/large-quantifiers-dont-break-dafny.dfy.expect @@ -1,2 +1,4 @@ +large-quantifiers-dont-break-dafny.dfy(60,9): Info: Selected triggers: + {P49(x)}, {P48(x)}, {P47(x)}, {P46(x)}, {P45(x)}, {P44(x)}, {P43(x)}, {P42(x)}, {P41(x)}, {P40(x)}, {P39(x)}, {P38(x)}, {P37(x)}, {P36(x)}, {P35(x)}, {P34(x)}, {P33(x)}, {P32(x)}, {P31(x)}, {P30(x)}, {P29(x)}, {P28(x)}, {P27(x)}, {P26(x)}, {P25(x)}, {P24(x)}, {P23(x)}, {P22(x)}, {P21(x)}, {P20(x)}, {P19(x)}, {P18(x)}, {P17(x)}, {P16(x)}, {P15(x)}, {P14(x)}, {P13(x)}, {P12(x)}, {P11(x)}, {P10(x)}, {P9(x)}, {P8(x)}, {P7(x)}, {P6(x)}, {P5(x)}, {P4(x)}, {P3(x)}, {P2(x)}, {P1(x)}, {P0(x)} Dafny program verifier finished with 52 verified, 0 errors diff --git a/Test/triggers/loop-detection-is-not-too-strict.dfy b/Test/triggers/loop-detection-is-not-too-strict.dfy index c6722399..a5a30c81 100644 --- a/Test/triggers/loop-detection-is-not-too-strict.dfy +++ b/Test/triggers/loop-detection-is-not-too-strict.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" // RUN: %diff "%s.expect" "%t" // This test shows that the loop detection engine makes compromises when looking diff --git a/Test/triggers/loop-detection-is-not-too-strict.dfy.expect b/Test/triggers/loop-detection-is-not-too-strict.dfy.expect index 29882da7..270f7e57 100644 --- a/Test/triggers/loop-detection-is-not-too-strict.dfy.expect +++ b/Test/triggers/loop-detection-is-not-too-strict.dfy.expect @@ -1,3 +1,7 @@ +loop-detection-is-not-too-strict.dfy(14,9): Info: Selected triggers: + {P(y, x)}, {P(x, y)} +loop-detection-is-not-too-strict.dfy(17,9): Info: Selected triggers: + {P(y, x)}, {P(x, y)} loop-detection-is-not-too-strict.dfy(20,9): Warning: Selected triggers: {P(x, y)} (loops with {P(x, y + 1)}) (!) Suppressing loops would leave this expression without triggers. diff --git a/Test/triggers/loop-detection-messages--unit-tests.dfy b/Test/triggers/loop-detection-messages--unit-tests.dfy index d5439e09..c1560317 100644 --- a/Test/triggers/loop-detection-messages--unit-tests.dfy +++ b/Test/triggers/loop-detection-messages--unit-tests.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" // RUN: %diff "%s.expect" "%t" // This file is a series of basic tests for loop detection, focusing on the diff --git a/Test/triggers/loop-detection-messages--unit-tests.dfy.expect b/Test/triggers/loop-detection-messages--unit-tests.dfy.expect index 1ebc0bce..89d7265c 100644 --- a/Test/triggers/loop-detection-messages--unit-tests.dfy.expect +++ b/Test/triggers/loop-detection-messages--unit-tests.dfy.expect @@ -1,13 +1,37 @@ +loop-detection-messages--unit-tests.dfy(11,9): Info: Selected triggers: {f(f(i))} + Rejected triggers: {f(i)} (loops with {f(f(i))}) loop-detection-messages--unit-tests.dfy(12,9): Warning: Selected triggers: {f(i)} (loops with {f(i + 1)}) (!) Suppressing loops would leave this expression without triggers. +loop-detection-messages--unit-tests.dfy(13,9): Info: Selected triggers: {f(i)} (loops with {f(i + 1)}) +loop-detection-messages--unit-tests.dfy(15,9): Info: For expression {false ==> f(i) == f(i + 1)}: + Selected triggers: {g(i)} + Rejected triggers: {f(i)} (loops with {f(i + 1)}) +loop-detection-messages--unit-tests.dfy(15,9): Info: For expression {false ==> f(i) == g(i)}: + Selected triggers: + {g(i)}, {f(i)} loop-detection-messages--unit-tests.dfy(16,9): Warning: For expression {false ==> f(i) == f(i + 1)}: Selected triggers: {f(i)} (loops with {f(i + 1)}) (!) Suppressing loops would leave this expression without triggers. +loop-detection-messages--unit-tests.dfy(16,9): Info: For expression {false ==> f(i) == f(i)}: + Selected triggers: {f(i)} +loop-detection-messages--unit-tests.dfy(17,9): Info: For expression {false ==> f(i) == f(i + 1)}: + Selected triggers: {f(i)} (loops with {f(i + 1)}) +loop-detection-messages--unit-tests.dfy(17,9): Info: For expression {false ==> f(i) == f(i)}: + Selected triggers: {f(i)} +loop-detection-messages--unit-tests.dfy(19,9): Info: Selected triggers: {f(i)} loop-detection-messages--unit-tests.dfy(20,9): Warning: (!) No terms found to trigger on. +loop-detection-messages--unit-tests.dfy(21,9): Info: Not generating triggers for {false ==> f(i + 1) == 0}. +loop-detection-messages--unit-tests.dfy(23,9): Info: Selected triggers: {f(j), f(i)} loop-detection-messages--unit-tests.dfy(24,9): Warning: (!) No trigger covering all quantified variables found. +loop-detection-messages--unit-tests.dfy(25,9): Info: For expression {false ==> f(i) == f(i)}: + Selected triggers: {g(j), f(i)} +loop-detection-messages--unit-tests.dfy(25,9): Info: For expression {false ==> g(j) == 0}: + Selected triggers: {g(j), f(i)} loop-detection-messages--unit-tests.dfy(26,9): Warning: For expression {false ==> f(i) == f(i)}: (!) No trigger covering all quantified variables found. loop-detection-messages--unit-tests.dfy(26,9): Warning: For expression {false ==> g(j + 1) == 0}: (!) No trigger covering all quantified variables found. +loop-detection-messages--unit-tests.dfy(27,9): Info: Not generating triggers for {false ==> f(i) == f(i)}. +loop-detection-messages--unit-tests.dfy(28,9): Info: Not generating triggers for {false ==> f(i) == f(i)}. Dafny program verifier finished with 4 verified, 0 errors diff --git a/Test/triggers/nested-quantifiers-all-get-triggers.dfy b/Test/triggers/nested-quantifiers-all-get-triggers.dfy index c75cfab9..a55019db 100644 --- a/Test/triggers/nested-quantifiers-all-get-triggers.dfy +++ b/Test/triggers/nested-quantifiers-all-get-triggers.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" // RUN: %diff "%s.expect" "%t" // This checks that nested quantifiers do get triggers, and that the parent diff --git a/Test/triggers/nested-quantifiers-all-get-triggers.dfy.expect b/Test/triggers/nested-quantifiers-all-get-triggers.dfy.expect index 069e7767..172f5607 100644 --- a/Test/triggers/nested-quantifiers-all-get-triggers.dfy.expect +++ b/Test/triggers/nested-quantifiers-all-get-triggers.dfy.expect @@ -1,2 +1,4 @@ +nested-quantifiers-all-get-triggers.dfy(8,17): Info: Selected triggers: {x in s} +nested-quantifiers-all-get-triggers.dfy(8,59): Info: Selected triggers: {y in s} Dafny program verifier finished with 2 verified, 0 errors diff --git a/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy b/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy index 7423e086..d7636ea2 100644 --- a/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy +++ b/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" // RUN: %diff "%s.expect" "%t" // This file shows how Dafny detects loops even for terms that are not literal diff --git a/Test/triggers/splitting-triggers-recovers-expressivity.dfy b/Test/triggers/splitting-triggers-recovers-expressivity.dfy index 79a0dc72..dd1bd81d 100644 --- a/Test/triggers/splitting-triggers-recovers-expressivity.dfy +++ b/Test/triggers/splitting-triggers-recovers-expressivity.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" // RUN: %diff "%s.expect" "%t" predicate P(i: int) diff --git a/Test/triggers/splitting-triggers-recovers-expressivity.dfy.expect b/Test/triggers/splitting-triggers-recovers-expressivity.dfy.expect index 8d7ff4ef..63cbc476 100644 --- a/Test/triggers/splitting-triggers-recovers-expressivity.dfy.expect +++ b/Test/triggers/splitting-triggers-recovers-expressivity.dfy.expect @@ -1,3 +1,32 @@ +splitting-triggers-recovers-expressivity.dfy(12,10): Info: Selected triggers: {Q(i)} + Rejected triggers: {P(i)} (loops with {P(i + 1)}) +splitting-triggers-recovers-expressivity.dfy(17,11): Info: Selected triggers: {Q(j)} + Rejected triggers: {P(j)} (loops with {P(j + 1)}) +splitting-triggers-recovers-expressivity.dfy(26,10): Info: Selected triggers: {Q(i)} + Rejected triggers: {P(i)} (loops with {P(i + 1)}) +splitting-triggers-recovers-expressivity.dfy(33,11): Info: Selected triggers: {Q(j)} + Rejected triggers: {P(j)} (loops with {P(j + 1)}) +splitting-triggers-recovers-expressivity.dfy(44,10): Info: For expression {P(i)}: + Selected triggers: + {Q(i)}, {P(i)} +splitting-triggers-recovers-expressivity.dfy(44,10): Info: For expression {!Q(i)}: + Selected triggers: + {Q(i)}, {P(i)} +splitting-triggers-recovers-expressivity.dfy(44,10): Info: For expression {P(i + 1)}: + Selected triggers: {Q(i)} + Rejected triggers: {P(i)} (loops with {P(i + 1)}) +splitting-triggers-recovers-expressivity.dfy(49,11): Info: For expression {j >= 0 ==> P(j)}: + Selected triggers: + {Q(j)}, {P(j)} +splitting-triggers-recovers-expressivity.dfy(49,11): Info: For expression {j >= 0 ==> Q(j) ==> P(j + 1)}: + Selected triggers: {Q(j)} + Rejected triggers: {P(j)} (loops with {P(j + 1)}) +splitting-triggers-recovers-expressivity.dfy(58,11): Info: For expression {i >= 0 ==> Q(i)}: + Selected triggers: + {P(i)}, {Q(i)} +splitting-triggers-recovers-expressivity.dfy(58,11): Info: For expression {i >= 0 ==> P(i) ==> P(i + 1)}: + Selected triggers: + {P(i)} (loops with {P(i + 1)}), {Q(i)} splitting-triggers-recovers-expressivity.dfy(12,63): Error BP5003: A postcondition might not hold on this return path. splitting-triggers-recovers-expressivity.dfy(12,10): Related location: This is the postcondition that might not hold. Execution trace: diff --git a/Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy b/Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy index f25a624e..20e90843 100644 --- a/Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy +++ b/Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" // RUN: %diff "%s.expect" "%t" // This tests shows that, since quantifiers are split, it becomes possible to know more precisely what part of a precondition did not hold at the call site. diff --git a/Test/triggers/triggers-prevent-some-inlining.dfy b/Test/triggers/triggers-prevent-some-inlining.dfy index 04b8051c..90af62a3 100644 --- a/Test/triggers/triggers-prevent-some-inlining.dfy +++ b/Test/triggers/triggers-prevent-some-inlining.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" // RUN: %diff "%s.expect" "%t" // This file looks at the interactions between inlining and triggers. The diff --git a/Test/triggers/triggers-prevent-some-inlining.dfy.expect b/Test/triggers/triggers-prevent-some-inlining.dfy.expect index 73ba063c..4c24893e 100644 --- a/Test/triggers/triggers-prevent-some-inlining.dfy.expect +++ b/Test/triggers/triggers-prevent-some-inlining.dfy.expect @@ -1,2 +1,7 @@ +triggers-prevent-some-inlining.dfy(17,2): Info: Selected triggers: {sum(a, b)} +triggers-prevent-some-inlining.dfy(24,10): Info: Some instances of this call cannot safely be inlined. +triggers-prevent-some-inlining.dfy(25,10): Info: Some instances of this call cannot safely be inlined. +triggers-prevent-some-inlining.dfy(24,10): Info: Some instances of this call cannot safely be inlined. +triggers-prevent-some-inlining.dfy(25,10): Info: Some instances of this call cannot safely be inlined. Dafny program verifier finished with 4 verified, 0 errors diff --git a/Test/triggers/useless-triggers-are-removed.dfy b/Test/triggers/useless-triggers-are-removed.dfy index 16458e41..658890f2 100644 --- a/Test/triggers/useless-triggers-are-removed.dfy +++ b/Test/triggers/useless-triggers-are-removed.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /printTooltips /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" // RUN: %diff "%s.expect" "%t" // This file ensures that Dafny does get rid of redundant triggers before -- cgit v1.2.3 From b819c42e85eb8ac0968abeab9f3fa3420a5dd760 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 21 Aug 2015 09:40:17 -0700 Subject: Allow display expressions as trigger terms --- Source/Dafny/Triggers/TriggersCollector.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/Dafny/Triggers/TriggersCollector.cs b/Source/Dafny/Triggers/TriggersCollector.cs index 30f7b9e8..167a6a1b 100644 --- a/Source/Dafny/Triggers/TriggersCollector.cs +++ b/Source/Dafny/Triggers/TriggersCollector.cs @@ -169,7 +169,7 @@ namespace Microsoft.Dafny.Triggers { expr.SubExpressions.Iter(e => Annotate(e)); TriggerAnnotation annotation; // TODO: Using ApplySuffix fixes the unresolved members problem in GenericSort - if (expr is FunctionCallExpr || expr is SeqSelectExpr || expr is MemberSelectExpr || expr is OldExpr || expr is ApplyExpr || + if (expr is FunctionCallExpr || expr is SeqSelectExpr || expr is MemberSelectExpr || expr is OldExpr || expr is ApplyExpr || expr is DisplayExpression || (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); @@ -186,8 +186,7 @@ namespace Microsoft.Dafny.Triggers { expr is OldExpr || expr is ThisExpr || expr is BoxingCastExpr || - expr is DatatypeValue || - expr is SeqDisplayExpr) { //FIXME what abvout other display expressions? + expr is DatatypeValue) { annotation = AnnotateOther(expr, false); } else { annotation = AnnotateOther(expr, true); -- cgit v1.2.3 From 8695668ca1121dde6a91f7a0c00ef0b065104c40 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 21 Aug 2015 09:40:58 -0700 Subject: Give up on trying to set the console's input encoding to UTF8 Fortunately, the server spec already assumes an ASCII input channel. --- Source/DafnyServer/Server.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/DafnyServer/Server.cs b/Source/DafnyServer/Server.cs index 77840007..8c94ad5b 100644 --- a/Source/DafnyServer/Server.cs +++ b/Source/DafnyServer/Server.cs @@ -31,9 +31,8 @@ namespace Microsoft.Dafny { } private void SetupConsole() { - var utf8 = new UTF8Encoding(false, true); - Console.InputEncoding = utf8; - Console.OutputEncoding = utf8; + // Setting InputEncoding to UTF8 causes z3 to choke. + Console.OutputEncoding = new UTF8Encoding(false, true); } public Server() { -- cgit v1.2.3 From 2dc3028a9f82e5109f82e5d370ae579ae947eb25 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 21 Aug 2015 09:41:24 -0700 Subject: Add tests for display expressions used as triggers --- Test/triggers/set-construction-is-a-good-trigger.dfy | 12 ++++++++++++ Test/triggers/set-construction-is-a-good-trigger.dfy.expect | 5 +++++ 2 files changed, 17 insertions(+) create mode 100644 Test/triggers/set-construction-is-a-good-trigger.dfy create mode 100644 Test/triggers/set-construction-is-a-good-trigger.dfy.expect diff --git a/Test/triggers/set-construction-is-a-good-trigger.dfy b/Test/triggers/set-construction-is-a-good-trigger.dfy new file mode 100644 index 00000000..b3dee172 --- /dev/null +++ b/Test/triggers/set-construction-is-a-good-trigger.dfy @@ -0,0 +1,12 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This file ensures that display expressions can be picked as triggers. This is +// useful for code that checks if a set, sequence, or multiset is a singleton. + +method M(s: seq, st: set, mst: multiset) + requires exists y :: s == [y] // Seq#Build(Seq#Empty(): Seq Box, $Box(y#3)) + requires exists y :: st == {y} // Set#UnionOne(Set#Empty(): Set Box, $Box(y#4)) + requires exists y :: mst == multiset{y} // MultiSet#UnionOne(MultiSet#Empty(): MultiSet Box, $Box(y#5)) +{ +} diff --git a/Test/triggers/set-construction-is-a-good-trigger.dfy.expect b/Test/triggers/set-construction-is-a-good-trigger.dfy.expect new file mode 100644 index 00000000..822b8498 --- /dev/null +++ b/Test/triggers/set-construction-is-a-good-trigger.dfy.expect @@ -0,0 +1,5 @@ +set-construction-is-a-good-trigger.dfy(8,11): Info: Selected triggers: {[y]} +set-construction-is-a-good-trigger.dfy(9,11): Info: Selected triggers: {{y}} +set-construction-is-a-good-trigger.dfy(10,11): Info: Selected triggers: {multiset{y}} + +Dafny program verifier finished with 2 verified, 0 errors -- cgit v1.2.3 From aa73a99a6fef9ada930d3b5b35e328006d8b30ee Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 21 Aug 2015 09:41:39 -0700 Subject: Ignore flycheck_* files in runTests --- Test/runTests.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Test/runTests.py b/Test/runTests.py index 7cd90454..53733eb1 100644 --- a/Test/runTests.py +++ b/Test/runTests.py @@ -35,7 +35,8 @@ except ImportError: pass class Defaults: - ALWAYS_EXCLUDED = ["Inputs", "Output", "sandbox", "desktop"] + EXCLUDED_FILES = ["^flycheck_"] + EXCLUDED_FOLDERS = ["Inputs", "Output", "sandbox", "desktop"] DAFNY_BIN = os.path.realpath(os.path.join(os.path.dirname(__file__), "../Binaries/Dafny.exe")) COMPILER = [DAFNY_BIN] FLAGS = ["/useBaseNameForFileName", "/compile:1", "/nologo", "/timeLimit:300"] @@ -311,7 +312,7 @@ def setup_parser(): help='Number of test workers.') parser.add_argument('--exclude', action='append', type=str, default=[], - help='Excluded directories. {} are automatically added.'.format(Defaults.ALWAYS_EXCLUDED)) + help='Excluded directories. {} are automatically added.'.format(Defaults.EXCLUDED_FOLDERS)) parser.add_argument('--verbosity', action='store', type=int, default=1, help='Set verbosity level. 0: Minimal; 1: Some info; 2: More info; 3: Trace.') @@ -405,8 +406,9 @@ def read_one_test(fname, compiler_cmds, timeout): def find_one(fname, compiler_cmds, timeout): - _, ext = os.path.splitext(fname) - if ext in Defaults.EXTENSIONS: + _, name = os.path.split(fname) + _, ext = os.path.splitext(name) + if ext in Defaults.EXTENSIONS and not any(re.search(pattern, name, re.IGNORECASE) for pattern in Defaults.EXCLUDED_FILES): if os.path.exists(fname): debug(Debug.TRACE, "Found test file: {}".format(fname)) yield from read_one_test(fname, compiler_cmds, timeout) @@ -455,7 +457,7 @@ def run_tests(args): tests = list(find_tests(args.path, [compiler + ' ' + " ".join(args.base_flags + args.flags) for compiler in args.compiler], - args.exclude + Defaults.ALWAYS_EXCLUDED, args.timeout)) + args.exclude + Defaults.EXCLUDED_FOLDERS, args.timeout)) tests.sort(key=operator.attrgetter("name")) args.njobs = max(1, min(args.njobs or os.cpu_count() or 1, len(tests))) -- cgit v1.2.3 From 0434fa61124d979d254fa4d38fdc1db06a578d18 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 21 Aug 2015 09:58:38 -0700 Subject: Interleave stderr output with test results; this allows one to print stuff directly to stderr in Dafny without breaking tests --- Test/runTests.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Test/runTests.py b/Test/runTests.py index 53733eb1..8caa65d1 100644 --- a/Test/runTests.py +++ b/Test/runTests.py @@ -248,12 +248,12 @@ class Test: self.duration = self.end - self.start stdout, stderr = stdout.strip(), stderr.strip() - if stdout != b"" or stderr != b"": + if stdout != b"": debug(Debug.TRACE, "Writing the output of {} to {}".format(self.name, self.temp_output_path)) with open(self.temp_output_path, mode='ab') as writer: - writer.write(stdout + stderr) + writer.write(stdout) if stderr != b"": - debug(Debug.TRACE, stderr.decode("utf-8")) + debug(Debug.INFO, stderr.decode("utf-8")) self.update_status() except TimeoutExpired: -- cgit v1.2.3 From 2befeb58569d39056e3d0797c901293446a14d03 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 21 Aug 2015 10:41:44 -0700 Subject: Make use of new syntax in a test file --- Test/dafny4/Fstar-QuickSort.dfy | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/Test/dafny4/Fstar-QuickSort.dfy b/Test/dafny4/Fstar-QuickSort.dfy index 4c5bc09b..d895ccf4 100644 --- a/Test/dafny4/Fstar-QuickSort.dfy +++ b/Test/dafny4/Fstar-QuickSort.dfy @@ -6,8 +6,6 @@ // Dafny needs help with a couple of lemmas in places where F* does not need them. // Comments below show differences between the F* and Dafny versions. -datatype Pair = P(T, U) - datatype List = Nil | Cons(T, List) function length(list: List): nat // for termination proof @@ -26,7 +24,7 @@ function In(x: int, list: List): nat } predicate SortedRange(m: int, n: int, list: List) - decreases list; // for termination proof + decreases list // for termination proof { match list case Nil => m <= n @@ -34,45 +32,45 @@ predicate SortedRange(m: int, n: int, list: List) } function append(n0: int, n1: int, n2: int, n3: int, i: List, j: List): List - requires n0 <= n1 <= n2 <= n3; - requires SortedRange(n0, n1, i) && SortedRange(n2, n3, j); - ensures SortedRange(n0, n3, append(n0, n1, n2, n3, i, j)); - ensures forall x :: In(x, append(n0, n1, n2, n3, i, j)) == In(x, i) + In(x, j); - decreases i; // for termination proof + requires n0 <= n1 <= n2 <= n3 + requires SortedRange(n0, n1, i) && SortedRange(n2, n3, j) + ensures SortedRange(n0, n3, append(n0, n1, n2, n3, i, j)) + ensures forall x :: In(x, append(n0, n1, n2, n3, i, j)) == In(x, i) + In(x, j) + decreases i // for termination proof { match i case Nil => j case Cons(hd, tl) => Cons(hd, append(hd, n1, n2, n3, tl, j)) } -function partition(x: int, l: List): Pair, List> - ensures var P(lo, hi) := partition(x, l); +function partition(x: int, l: List): (List, List) + ensures var (lo, hi) := partition(x, l); (forall y :: In(y, lo) == if y <= x then In(y, l) else 0) && (forall y :: In(y, hi) == if x < y then In(y, l) else 0) && - length(l) == length(lo) + length(hi); // for termination proof + length(l) == length(lo) + length(hi) // for termination proof { match l - case Nil => P(Nil, Nil) + case Nil => (Nil, Nil) case Cons(hd, tl) => - var P(lo, hi) := partition(x, tl); + var (lo, hi) := partition(x, tl); if hd <= x then - P(Cons(hd, lo), hi) + (Cons(hd, lo), hi) else - P(lo, Cons(hd, hi)) + (lo, Cons(hd, hi)) } function sort(min: int, max: int, i: List): List - requires min <= max; - requires forall x :: In(x, i) != 0 ==> min <= x <= max; - ensures SortedRange(min, max, sort(min, max, i)); - ensures forall x :: In(x, i) == In(x, sort(min, max, i)); - decreases length(i); // for termination proof + requires min <= max + requires forall x :: In(x, i) != 0 ==> min <= x <= max + ensures SortedRange(min, max, sort(min, max, i)) + ensures forall x :: In(x, i) == In(x, sort(min, max, i)) + decreases length(i) // for termination proof { match i case Nil => Nil case Cons(hd, tl) => assert In(hd, i) != 0; // this proof line not needed in F* - var P(lo, hi) := partition(hd, tl); + var (lo, hi) := partition(hd, tl); assert forall y :: In(y, lo) <= In(y, i); // this proof line not needed in F* var i' := sort(min, hd, lo); var j' := sort(hd, max, hi); -- cgit v1.2.3 From b41056b45861f6ddb41e2a5e4d7c6d816684f095 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 21 Aug 2015 15:32:50 -0700 Subject: Add some diversity to Dafny's alimentation --- Source/Dafny/Translator.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 8200f254..6f1a5833 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -2261,9 +2261,9 @@ namespace Microsoft.Dafny { Bpl.Trigger tr = new Bpl.Trigger(f.tok, true, new List { funcAppl }); var typeParams = TrTypeParamDecls(f.TypeArgs); - Bpl.Expr meat; + Bpl.Expr tastyVegetarianOption; if (visibility == FunctionAxiomVisibility.ForeignModuleOnly && f.IsProtected) { - meat = Bpl.Expr.True; + tastyVegetarianOption = Bpl.Expr.True; } else { var bodyWithSubst = Substitute(body, null, substMap); if (f is PrefixPredicate) { @@ -2271,14 +2271,14 @@ namespace Microsoft.Dafny { bodyWithSubst = PrefixSubstitution(pp, bodyWithSubst); } var etranBody = layer == null ? etran : etran.LimitedFunctions(f, new Bpl.IdentifierExpr(f.tok, layer)); - meat = BplAnd(CanCallAssumption(bodyWithSubst, etranBody), + tastyVegetarianOption = BplAnd(CanCallAssumption(bodyWithSubst, etranBody), Bpl.Expr.Eq(funcAppl, etranBody.TrExpr(bodyWithSubst))); } QKeyValue kv = null; if (lits != null) { kv = new QKeyValue(f.tok, "weight", new List() { Bpl.Expr.Literal(3) }, null); } - Bpl.Expr ax = new Bpl.ForallExpr(f.tok, typeParams, formals, kv, tr, Bpl.Expr.Imp(ante, meat)); + Bpl.Expr ax = new Bpl.ForallExpr(f.tok, typeParams, formals, kv, tr, Bpl.Expr.Imp(ante, tastyVegetarianOption)); var activate = AxiomActivation(f, visibility == FunctionAxiomVisibility.ForeignModuleOnly, visibility == FunctionAxiomVisibility.IntraModuleOnly, etran); string comment; if (overridingClass == null) { -- cgit v1.2.3 From 84b52a48efda3e9658b772dbc02d34a6f6c9d16b Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 21 Aug 2015 19:09:37 -0700 Subject: Adjust WF checks to use unsplit quantifiers. The logic about split quantifiers is that Boogie (and z3) should never realize that there was an unsplit quantifier. The WF check code does not produce a quantifier, at least in it's checking part; thus, it should use original quantifier. This fixes a problem in VerifyThis2015/Problem2.dfy with a null check, and a problem spotted by Chris, made into a test case saved in triggers/wf-checks-use-the-original-quantifier.dfy. The issue boiled down to being able to verify (forall x :: x != null && x.a == 0). Of course, the assumption that WF checks produce for a quantifier is a quantifier, so it uses the split expression. --- Source/Dafny/Translator.cs | 32 ++++++++++++---------- .../wf-checks-use-the-original-quantifier.dfy | 28 +++++++++++++++++++ ...f-checks-use-the-original-quantifier.dfy.expect | 17 ++++++++++++ 3 files changed, 62 insertions(+), 15 deletions(-) create mode 100644 Test/triggers/wf-checks-use-the-original-quantifier.dfy create mode 100644 Test/triggers/wf-checks-use-the-original-quantifier.dfy.expect diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 6f1a5833..524466c9 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -4437,7 +4437,7 @@ 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); } @@ -4735,12 +4735,7 @@ 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))); @@ -4748,15 +4743,24 @@ namespace Microsoft.Dafny { // Create local variables corresponding to the in-parameters: 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(), null, substMap, typeMap); + var body = Substitute(e.LogicalBody(true), null, substMap, typeMap); CheckWellformedAndAssume(body, options, locals, builder, etran); + if (e is ForallExpr) { - builder.Add(new Bpl.AssumeCmd(expr.tok, etran.TrExpr(expr))); + // Although we do the WF check on the original quantifier, we assume the split one. + // This ensures that cases like forall x :: x != null && f(x.a) do not fail to verify. + builder.Add(new Bpl.AssumeCmd(expr.tok, etran.TrExpr(e.SplitQuantifierExpression ?? e))); } return; } + // resort to the behavior of simply checking well-formedness followed by assuming the translated expression CheckWellformed(expr, options, locals, builder, etran); + + // NOTE: If the CheckWellformed call above found a split quantifier, it ignored + // the splitting and proceeded to decompose the full quantifier as + // normal. This call to TrExpr, on the other hand, will indeed use the + // split quantifier. builder.Add(new Bpl.AssumeCmd(expr.tok, etran.TrExpr(expr))); } @@ -5276,10 +5280,8 @@ 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); - return; - } + // This is a WF check, so we look at the original quantifier, not the split one. + // This ensures that cases like forall x :: x != null && f(x.a) do not fail to verify. var typeMap = new Dictionary(); var copies = new List(); @@ -5298,7 +5300,6 @@ namespace Microsoft.Dafny { var newEtran = etran; builder.Add(new Bpl.CommentCmd("Begin Comprehension WF check")); BplIfIf(e.tok, lam != null, null, builder, newBuilder => { - if (lam != null) { // Havoc heap, unless oneShot if (!lam.OneShot) { @@ -13098,7 +13099,8 @@ namespace Microsoft.Dafny { return f.Formals.Zip(fexp.Args).All(formal_concrete => CanSafelySubstitute(visitor.TriggerVariables, formal_concrete.Item1, formal_concrete.Item2)); } - Triggers.TriggersCollector triggersCollector = new Triggers.TriggersCollector(); + // Using an empty set of old expressions is ok here; the only uses of the triggersCollector will be to check for trigger killers. + Triggers.TriggersCollector triggersCollector = new Triggers.TriggersCollector(new HashSet()); private bool CanSafelySubstitute(ISet protectedVariables, IVariable variable, Expression substitution) { return !(protectedVariables.Contains(variable) && triggersCollector.IsTriggerKiller(substitution)); diff --git a/Test/triggers/wf-checks-use-the-original-quantifier.dfy b/Test/triggers/wf-checks-use-the-original-quantifier.dfy new file mode 100644 index 00000000..a1a2bd90 --- /dev/null +++ b/Test/triggers/wf-checks-use-the-original-quantifier.dfy @@ -0,0 +1,28 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This test checks that typical expressions requiring WF checks do not suddenly +// loose expressivity due to quantifier splitting. Without special care, the +// expression (forall x :: x != null && x.a == 0) could fail to verify. + +// The logic about split quantifiers is that Boogie (and z3) should never realize +// that there was an unsplit quantifier. The WF check code does not produce a +// quantifier, at least in it's checking part; thus, it should use original +// quantifier. This fixes a problem in VerifyThis2015/Problem2.dfy with a null +// check, and a problem spotted by Chris, made into a test case saved in +// triggers/wf-checks-use-the-original-quantifier.dfy. + +// Of course, the assumption that WF checks produce for a quantifier is a +// quantifier, so the assumption part that comes after the WF check does use the +// split expression. + +// This test case is inspired by the example that Chris gave. + +predicate P(b: nat) +function f(a: int): int +class C { var x: int; } + +method M(s: set) + requires forall n: nat :: 0 <= f(n) && P(f(n)) + requires forall c, c' | c in s && c' in s :: c != null && c'!= null && c.x == c'.x { +} diff --git a/Test/triggers/wf-checks-use-the-original-quantifier.dfy.expect b/Test/triggers/wf-checks-use-the-original-quantifier.dfy.expect new file mode 100644 index 00000000..6b152262 --- /dev/null +++ b/Test/triggers/wf-checks-use-the-original-quantifier.dfy.expect @@ -0,0 +1,17 @@ +wf-checks-use-the-original-quantifier.dfy(26,11): Info: For expression {0 <= f(n)}: + Selected triggers: {f(n)} + Rejected triggers: {P(f(n))} (more specific than {f(n)}) +wf-checks-use-the-original-quantifier.dfy(26,11): Info: For expression {P(f(n))}: + Selected triggers: {f(n)} + Rejected triggers: {P(f(n))} (more specific than {f(n)}) +wf-checks-use-the-original-quantifier.dfy(27,11): Info: For expression {c != null}: + Selected triggers: + {c'.x, c.x}, {c'.x, c in s}, {c.x, c' in s}, {c' in s, c in s} +wf-checks-use-the-original-quantifier.dfy(27,11): Info: For expression {c' != null}: + Selected triggers: + {c'.x, c.x}, {c'.x, c in s}, {c.x, c' in s}, {c' in s, c in s} +wf-checks-use-the-original-quantifier.dfy(27,11): Info: For expression {c.x == c'.x}: + Selected triggers: + {c'.x, c.x}, {c'.x, c in s}, {c.x, c' in s}, {c' in s, c in s} + +Dafny program verifier finished with 4 verified, 0 errors -- cgit v1.2.3 From 0551c469ca003176417255f19a9574312a7e7a0f Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 21 Aug 2015 19:10:06 -0700 Subject: Add a few things to the wishlist --- Test/wishlist/tooltips-on-inductive-lemmas.dfy | 12 ++++++++++++ Test/wishlist/tooltips-on-inductive-lemmas.dfy.expect | 7 +++++++ Test/wishlist/we-should-always-print-tooltips.dfy | 4 ++++ Test/wishlist/we-should-always-print-tooltips.dfy.expect | 2 ++ 4 files changed, 25 insertions(+) create mode 100644 Test/wishlist/tooltips-on-inductive-lemmas.dfy create mode 100644 Test/wishlist/tooltips-on-inductive-lemmas.dfy.expect create mode 100644 Test/wishlist/we-should-always-print-tooltips.dfy create mode 100644 Test/wishlist/we-should-always-print-tooltips.dfy.expect diff --git a/Test/wishlist/tooltips-on-inductive-lemmas.dfy b/Test/wishlist/tooltips-on-inductive-lemmas.dfy new file mode 100644 index 00000000..1bd25437 --- /dev/null +++ b/Test/wishlist/tooltips-on-inductive-lemmas.dfy @@ -0,0 +1,12 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +inductive lemma P() + requires Q() { // WISH the tooltip for this was broken at some point between + // revisions 94ee216fe0cd (1179) and bd779dda3b3d (1785) + P(); +} + +inductive predicate Q() { + Q() +} diff --git a/Test/wishlist/tooltips-on-inductive-lemmas.dfy.expect b/Test/wishlist/tooltips-on-inductive-lemmas.dfy.expect new file mode 100644 index 00000000..4d036eef --- /dev/null +++ b/Test/wishlist/tooltips-on-inductive-lemmas.dfy.expect @@ -0,0 +1,7 @@ +tooltips-on-inductive-lemmas.dfy(5,11): Info: Q +tooltips-on-inductive-lemmas.dfy(7,2): Info: P +tooltips-on-inductive-lemmas.dfy(11,2): Info: Q#[_k - 1] +tooltips-on-inductive-lemmas.dfy(4,16): Info: P# {:induction _k} +tooltips-on-inductive-lemmas.dfy(4,16): Info: P# decreases _k + +Dafny program verifier finished with 3 verified, 0 errors diff --git a/Test/wishlist/we-should-always-print-tooltips.dfy b/Test/wishlist/we-should-always-print-tooltips.dfy new file mode 100644 index 00000000..d7a55845 --- /dev/null +++ b/Test/wishlist/we-should-always-print-tooltips.dfy @@ -0,0 +1,4 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// WISH it would be great to add /printTooltips to all tests diff --git a/Test/wishlist/we-should-always-print-tooltips.dfy.expect b/Test/wishlist/we-should-always-print-tooltips.dfy.expect new file mode 100644 index 00000000..a1c1f7b9 --- /dev/null +++ b/Test/wishlist/we-should-always-print-tooltips.dfy.expect @@ -0,0 +1,2 @@ + +Dafny program verifier finished with 0 verified, 0 errors -- cgit v1.2.3 From a7d63787addef715ba8b77d3adf9455c8c174c48 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 21 Aug 2015 19:10:54 -0700 Subject: Rephrase the message about triggers being rejected because they are too strong --- Source/Dafny/Triggers/QuantifiersCollection.cs | 2 +- Test/triggers/useless-triggers-are-removed.dfy.expect | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/Dafny/Triggers/QuantifiersCollection.cs b/Source/Dafny/Triggers/QuantifiersCollection.cs index 8e4c3261..1d5625cd 100644 --- a/Source/Dafny/Triggers/QuantifiersCollection.cs +++ b/Source/Dafny/Triggers/QuantifiersCollection.cs @@ -137,7 +137,7 @@ namespace Microsoft.Dafny.Triggers { (candidate, weakerCandidates) => !weakerCandidates.Any(), (candidate, weakerCandidates) => { q.RejectedCandidates.Add(candidate); - candidate.Annotation = "stronger than " + String.Join(", ", weakerCandidates); + candidate.Annotation = "more specific than " + String.Join(", ", weakerCandidates); }).ToList(); } } diff --git a/Test/triggers/useless-triggers-are-removed.dfy.expect b/Test/triggers/useless-triggers-are-removed.dfy.expect index 6c2c0a2b..a526e517 100644 --- a/Test/triggers/useless-triggers-are-removed.dfy.expect +++ b/Test/triggers/useless-triggers-are-removed.dfy.expect @@ -1,17 +1,17 @@ useless-triggers-are-removed.dfy(16,11): Info: Selected triggers: {f(x)} Rejected triggers: - {h(g(f(x)))} (stronger than {g(f(x))}, {f(x)}) - {g(h(f(x)))} (stronger than {h(f(x))}, {f(x)}) - {h(f(x))} (stronger than {f(x)}) - {g(f(x))} (stronger than {f(x)}) + {h(g(f(x)))} (more specific than {g(f(x))}, {f(x)}) + {g(h(f(x)))} (more specific than {h(f(x))}, {f(x)}) + {h(f(x))} (more specific than {f(x)}) + {g(f(x))} (more specific than {f(x)}) useless-triggers-are-removed.dfy(20,11): Info: Selected triggers: {f(f(x))} Rejected triggers: {f(x)} (loops with {f(f(x))}) useless-triggers-are-removed.dfy(23,11): Info: Selected triggers: {g(f(x)), g(y)}, {f(y), f(x)} Rejected triggers: {g(y), f(x)} (loops with {g(f(y))}, {g(f(x))}) - {g(f(x)), g(f(y))} (stronger than {g(f(x)), f(y)}, {g(f(y)), f(x)}, {f(y), f(x)}) - {g(f(x)), f(y)} (stronger than {f(y), f(x)}) - {g(f(y)), f(x)} (stronger than {f(y), f(x)}) + {g(f(x)), g(f(y))} (more specific than {g(f(x)), f(y)}, {g(f(y)), f(x)}, {f(y), f(x)}) + {g(f(x)), f(y)} (more specific than {f(y), f(x)}) + {g(f(y)), f(x)} (more specific than {f(y), f(x)}) Dafny program verifier finished with 5 verified, 0 errors -- cgit v1.2.3 From ff05bb6936d433e7be5ded41233214c0517dc2d2 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 21 Aug 2015 19:13:56 -0700 Subject: Make `old` a special case for trigger generation. Old is particular, because in old(g(f(x))), the triggers are old(g(x)) and old(f(x)). This has a number of implications; see the new tests files for more information. --- Source/Dafny/Rewriter.cs | 4 +- Source/Dafny/Triggers/QuantifiersCollector.cs | 17 +++++--- Source/Dafny/Triggers/TriggerExtensions.cs | 24 ++++++------ Source/Dafny/Triggers/TriggerUtils.cs | 45 +++++++++++++++------- Source/Dafny/Triggers/TriggersCollector.cs | 34 +++++++++++----- .../looping-is-hard-to-decide-modulo-equality.dfy | 32 +++++++++++++++ ...ng-is-hard-to-decide-modulo-equality.dfy.expect | 10 +++++ .../old-is-a-special-case-for-triggers.dfy | 32 +++++++++++++++ .../old-is-a-special-case-for-triggers.dfy.expect | 22 +++++++++++ 9 files changed, 178 insertions(+), 42 deletions(-) create mode 100644 Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy create mode 100644 Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy.expect create mode 100644 Test/triggers/old-is-a-special-case-for-triggers.dfy create mode 100644 Test/triggers/old-is-a-special-case-for-triggers.dfy.expect diff --git a/Source/Dafny/Rewriter.cs b/Source/Dafny/Rewriter.cs index 230d9349..557eee93 100644 --- a/Source/Dafny/Rewriter.cs +++ b/Source/Dafny/Rewriter.cs @@ -47,10 +47,10 @@ namespace Microsoft.Dafny var finder = new Triggers.QuantifierCollector(reporter); foreach (var decl in ModuleDefinition.AllCallables(m.TopLevelDecls)) { - finder.Visit(decl, null); + finder.Visit(decl, false); } - var triggersCollector = new Triggers.TriggersCollector(); + var triggersCollector = new Triggers.TriggersCollector(finder.exprsInOldContext); foreach (var quantifierCollection in finder.quantifierCollections) { quantifierCollection.ComputeTriggers(triggersCollector); quantifierCollection.CommitTriggers(); diff --git a/Source/Dafny/Triggers/QuantifiersCollector.cs b/Source/Dafny/Triggers/QuantifiersCollector.cs index b30cb6b1..3385f36e 100644 --- a/Source/Dafny/Triggers/QuantifiersCollector.cs +++ b/Source/Dafny/Triggers/QuantifiersCollector.cs @@ -6,18 +6,19 @@ using Microsoft.Boogie; using System.Collections.ObjectModel; using System.Diagnostics.Contracts; -namespace Microsoft.Dafny.Triggers { //FIXME rename this file - internal class QuantifierCollector : TopDownVisitor { +namespace Microsoft.Dafny.Triggers { + internal class QuantifierCollector : TopDownVisitor { readonly ErrorReporter reporter; - private HashSet quantifiers = new HashSet(); - internal List quantifierCollections = new List(); + private readonly HashSet quantifiers = new HashSet(); + internal readonly HashSet exprsInOldContext = new HashSet(); + internal readonly List quantifierCollections = new List(); public QuantifierCollector(ErrorReporter reporter) { Contract.Requires(reporter != null); this.reporter = reporter; } - protected override bool VisitOneExpr(Expression expr, ref object _) { + protected override bool VisitOneExpr(Expression expr, ref bool inOldContext) { var quantifier = expr as QuantifierExpr; if (quantifier != null && !quantifiers.Contains(quantifier)) { @@ -31,6 +32,12 @@ namespace Microsoft.Dafny.Triggers { //FIXME rename this file } } + if (expr is OldExpr) { + inOldContext = true; + } else if (inOldContext) { // FIXME be more restrctive on the type of stuff that we annotate + exprsInOldContext.Add(expr); + } + return true; } } diff --git a/Source/Dafny/Triggers/TriggerExtensions.cs b/Source/Dafny/Triggers/TriggerExtensions.cs index 6c3f4ee7..02deb92f 100644 --- a/Source/Dafny/Triggers/TriggerExtensions.cs +++ b/Source/Dafny/Triggers/TriggerExtensions.cs @@ -38,17 +38,18 @@ namespace Microsoft.Dafny.Triggers { } internal static class ExprExtensions { - internal static IEnumerable AllSubExpressions(this Expression expr, bool strict = false) { + internal static IEnumerable AllSubExpressions(this Expression expr, bool wrapOld, bool strict) { + bool isOld = expr is OldExpr; + foreach (var subexpr in expr.SubExpressions) { - foreach (var r_subexpr in AllSubExpressions(subexpr, false)) { - yield return r_subexpr; + foreach (var r_subexpr in AllSubExpressions(subexpr, wrapOld, false)) { + yield return TriggerUtils.MaybeWrapInOld(r_subexpr, isOld); } - yield return subexpr; } if (expr is StmtExpr) { - foreach (var r_subexpr in AllSubExpressions(((StmtExpr)expr).S, false)) { - yield return r_subexpr; + foreach (var r_subexpr in AllSubExpressions(((StmtExpr)expr).S, wrapOld, false)) { + yield return TriggerUtils.MaybeWrapInOld(r_subexpr, isOld); } } @@ -57,16 +58,15 @@ namespace Microsoft.Dafny.Triggers { } } - internal static IEnumerable AllSubExpressions(this Statement stmt, bool strict = false) { + internal static IEnumerable AllSubExpressions(this Statement stmt, bool wrapOld, bool strict) { foreach (var subexpr in stmt.SubExpressions) { - foreach (var r_subexpr in AllSubExpressions(subexpr, false)) { + foreach (var r_subexpr in AllSubExpressions(subexpr, wrapOld, false)) { yield return r_subexpr; } - yield return subexpr; } foreach (var substmt in stmt.SubStatements) { - foreach (var r_subexpr in AllSubExpressions(substmt, false)) { + foreach (var r_subexpr in AllSubExpressions(substmt, wrapOld, false)) { yield return r_subexpr; } } @@ -121,8 +121,8 @@ namespace Microsoft.Dafny.Triggers { } internal static IEnumerable SubexpressionsMatchingTrigger(this QuantifierExpr quantifier, Expression trigger) { - return quantifier.Term.AllSubExpressions() - .Select(e => TriggerUtils.CleanupExprForInclusionInTrigger(e).MatchAgainst(trigger, quantifier.BoundVars, e)) + return quantifier.Term.AllSubExpressions(true, false) //FIXME should loop detection actually pass true here? + .Select(e => TriggerUtils.PrepareExprForInclusionInTrigger(e).MatchAgainst(trigger, quantifier.BoundVars, e)) .Where(e => e.HasValue).Select(e => e.Value); } diff --git a/Source/Dafny/Triggers/TriggerUtils.cs b/Source/Dafny/Triggers/TriggerUtils.cs index 6f76464b..4fd139e2 100644 --- a/Source/Dafny/Triggers/TriggerUtils.cs +++ b/Source/Dafny/Triggers/TriggerUtils.cs @@ -129,35 +129,52 @@ namespace Microsoft.Dafny.Triggers { throw new ArgumentException(); } - internal static Expression CleanupExprForInclusionInTrigger(Expression expr, out bool isKiller) { + internal static Expression PrepareExprForInclusionInTrigger(Expression expr, out bool isKiller) { isKiller = false; - if (!(expr is BinaryExpr)) { - return expr; + var ret = expr; + if (ret is BinaryExpr) { + ret = PrepareInMultisetForInclusionInTrigger(PrepareNotInForInclusionInTrigger((BinaryExpr)ret), ref isKiller); } - var bexpr = expr as BinaryExpr; + return ret; + } - BinaryExpr new_expr = bexpr; + private static BinaryExpr PrepareNotInForInclusionInTrigger(BinaryExpr bexpr) { if (bexpr.Op == BinaryExpr.Opcode.NotIn) { - new_expr = new BinaryExpr(bexpr.tok, BinaryExpr.Opcode.In, bexpr.E0, bexpr.E1); + var new_expr = new BinaryExpr(bexpr.tok, BinaryExpr.Opcode.In, bexpr.E0, bexpr.E1); new_expr.ResolvedOp = RemoveNotInBinaryExprIn(bexpr.ResolvedOp); new_expr.Type = bexpr.Type; + return new_expr; + } else { + return bexpr; } + } - Expression returned_expr = new_expr; - if (new_expr.ResolvedOp == BinaryExpr.ResolvedOpcode.InMultiSet) { - returned_expr = new SeqSelectExpr(new_expr.tok, true, new_expr.E1, new_expr.E0, null); - returned_expr.Type = bexpr.Type; + private static Expression PrepareInMultisetForInclusionInTrigger(BinaryExpr bexpr, ref bool isKiller) { + if (bexpr.ResolvedOp == BinaryExpr.ResolvedOpcode.InMultiSet) { + var new_expr = new SeqSelectExpr(bexpr.tok, true, bexpr.E1, bexpr.E0, null); + new_expr.Type = bexpr.Type; isKiller = true; // [a in s] becomes [s[a] > 0], which is a trigger killer + return new_expr; + } else { + return bexpr; } - - return returned_expr; } - internal static Expression CleanupExprForInclusionInTrigger(Expression expr) { + internal static Expression PrepareExprForInclusionInTrigger(Expression expr) { bool _; - return CleanupExprForInclusionInTrigger(expr, out _); + return PrepareExprForInclusionInTrigger(expr, out _); + } + + internal static Expression MaybeWrapInOld(Expression expr, bool wrap) { + if (wrap && !(expr is NameSegment) && !(expr is IdentifierExpr)) { + var newExpr = new OldExpr(expr.tok, expr); + newExpr.Type = expr.Type; + return newExpr; + } else { + return expr; + } } } } diff --git a/Source/Dafny/Triggers/TriggersCollector.cs b/Source/Dafny/Triggers/TriggersCollector.cs index 167a6a1b..11860404 100644 --- a/Source/Dafny/Triggers/TriggersCollector.cs +++ b/Source/Dafny/Triggers/TriggersCollector.cs @@ -27,7 +27,7 @@ namespace Microsoft.Dafny.Triggers { internal TermComparison CompareTo(TriggerTerm other) { if (this == other) { return TermComparison.SameStrength; - } else if (Expr.AllSubExpressions(true).Any(other.Expr.ExpressionEq)) { + } else if (Expr.AllSubExpressions(true, true).Any(other.Expr.ExpressionEq)) { return TermComparison.Stronger; } else { return TermComparison.NotStronger; @@ -92,7 +92,7 @@ namespace Microsoft.Dafny.Triggers { } } - class TriggerAnnotation { + internal class TriggerAnnotation { internal bool IsTriggerKiller; internal ISet Variables; internal readonly List PrivateTerms; @@ -132,11 +132,27 @@ namespace Microsoft.Dafny.Triggers { } } - public class TriggersCollector { - Dictionary cache; + internal class TriggerAnnotationsCache { + public readonly HashSet exprsInOldContext; + public readonly Dictionary annotations; - internal TriggersCollector() { - this.cache = new Dictionary(); + /// + /// For certain operations, the TriggersCollector class needs to know whether + /// an particular expression is under an old(...) wrapper. This is in particular + /// true for generating trigger terms (but it is not for checking wehter something + /// is a trigger killer, so passing an empty set here for that case would be fine. + /// + public TriggerAnnotationsCache(HashSet exprsInOldContext) { + this.exprsInOldContext = exprsInOldContext; + annotations = new Dictionary(); + } + } + + internal class TriggersCollector { + TriggerAnnotationsCache cache; + + internal TriggersCollector(HashSet exprsInOldContext) { + this.cache = new TriggerAnnotationsCache(exprsInOldContext); } private T ReduceAnnotatedSubExpressions(Expression expr, T seed, Func map, Func reduce) { @@ -162,7 +178,7 @@ namespace Microsoft.Dafny.Triggers { private TriggerAnnotation Annotate(Expression expr) { TriggerAnnotation cached; - if (cache.TryGetValue(expr, out cached)) { + if (cache.annotations.TryGetValue(expr, out cached)) { return cached; } @@ -193,13 +209,13 @@ namespace Microsoft.Dafny.Triggers { } TriggerUtils.DebugTriggers("{0} ({1})\n{2}", Printer.ExprToString(expr), expr.GetType(), annotation); - cache[expr] = annotation; + cache.annotations[expr] = annotation; return annotation; } private TriggerAnnotation AnnotatePotentialCandidate(Expression expr) { bool expr_is_killer = false; - var new_expr = TriggerUtils.CleanupExprForInclusionInTrigger(expr, out expr_is_killer); + var new_expr = TriggerUtils.MaybeWrapInOld(TriggerUtils.PrepareExprForInclusionInTrigger(expr, out expr_is_killer), cache.exprsInOldContext.Contains(expr)); var new_term = new TriggerTerm { Expr = new_expr, OriginalExpr = expr, Variables = CollectVariables(expr) }; List collected_terms = CollectExportedCandidates(expr); diff --git a/Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy b/Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy new file mode 100644 index 00000000..c54089f2 --- /dev/null +++ b/Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy @@ -0,0 +1,32 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This file shows cases where loops could hide behind equalities. In all three +// cases we behave the same; that is, we don't warn for loops that would only +// exist in the presence of an equality. The easiest way to understand the +// issue, I (CPC) feel, is to look at the old case: f(x) could very well loop +// with old(f(f(x))) if f(f(x)) did not change the heap at all. + +// This equality issue is generally undecidable. It could make sense to special +// case `old`, but KRML and CPC decided against it on 2015-08-21. Future +// experiences could cause a change of mind. + +class C { } +function f(c: C): C +function g(c: C): C +function h(c: C, i: int): C + +// With explicit arguments +method M0(i: int, j: int, sc: set) { + assert forall c | c in sc :: true || h(c, i) == h(h(c, j), j); +} + +// With implicit arguments (f and g respectively, to Apply) +method M1(f: int -> int, g: int -> int) { + assert forall x :: true || f(x) == g(f(x)); +} + +// With implicit arguments (the heap, to old) +method M2(sc: set) { + assert forall c | c in sc :: true || f(c) == old(f(f(c))); +} diff --git a/Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy.expect b/Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy.expect new file mode 100644 index 00000000..c0eebdba --- /dev/null +++ b/Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy.expect @@ -0,0 +1,10 @@ +looping-is-hard-to-decide-modulo-equality.dfy(21,9): Info: Selected triggers: + {h(h(c, j), j)}, {h(c, i)}, {c in sc} + Rejected triggers: {h(c, j)} (loops with {h(h(c, j), j)}) +looping-is-hard-to-decide-modulo-equality.dfy(26,9): Info: Selected triggers: {f(x)} + Rejected triggers: {g(f(x))} (more specific than {f(x)}) +looping-is-hard-to-decide-modulo-equality.dfy(31,9): Info: Selected triggers: + {old(f(f(c)))}, {f(c)}, {c in sc} + Rejected triggers: {old(f(c))} (loops with {old(f(f(c)))}) + +Dafny program verifier finished with 9 verified, 0 errors diff --git a/Test/triggers/old-is-a-special-case-for-triggers.dfy b/Test/triggers/old-is-a-special-case-for-triggers.dfy new file mode 100644 index 00000000..4424e8d3 --- /dev/null +++ b/Test/triggers/old-is-a-special-case-for-triggers.dfy @@ -0,0 +1,32 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This file ensures that `old()` receives the special treatment that it +// requires; that is, `old(f(x))` is not less liberal than `f(x)`, and +// old(f(f(x))) does not loop with f(x) (doesn't it?) + +class C { } +function f(c: C): C +function g(c: C): C +function h(c: C, i: int): C + +method M(sc: set) + // Ensure that old(c) does not get picked as a trigger + ensures forall c | c in sc :: true || c == old(f(c)) + + // This checks whether loop detection handles `old` expressions properly. + // In the first one f(c)/old(f(f(c))) is not reported as a loop. See + // looping-is-hard-to-decide-modulo-equalities.dfy for an explanation. + ensures forall c | c in sc :: true || f(c) == old(f(f(c))) + ensures forall c | c in sc :: true || old(f(f(c))) == old(g(f(c))) || old(f(g(c))) == g(f(c)) || f(g(c)) == g(f(c)) + + // These check that the final trigger filtering step doesn't get confused + // between old expressions and regular expressions. + ensures forall c | c in sc :: true || f(c) == old(g(f(c))) + ensures forall c | c in sc :: true || f(c) == old(f(c)) || old(g(f(c))) == g(f(c)) + + // WISH: A Dafny rewriter could cleanup expressions so that adding the + // expression forall c :: c == old(c) in a quantifier would cause a warning, + // instead of a trigger generation error as it does now. +{ +} diff --git a/Test/triggers/old-is-a-special-case-for-triggers.dfy.expect b/Test/triggers/old-is-a-special-case-for-triggers.dfy.expect new file mode 100644 index 00000000..23ec089d --- /dev/null +++ b/Test/triggers/old-is-a-special-case-for-triggers.dfy.expect @@ -0,0 +1,22 @@ +old-is-a-special-case-for-triggers.dfy(15,10): Info: Selected triggers: + {old(f(c))}, {c in sc} +old-is-a-special-case-for-triggers.dfy(20,10): Info: Selected triggers: + {old(f(f(c)))}, {f(c)}, {c in sc} + Rejected triggers: {old(f(c))} (loops with {old(f(f(c)))}) +old-is-a-special-case-for-triggers.dfy(21,10): Info: Selected triggers: + {f(g(c))}, {g(f(c))}, {old(f(g(c)))}, {old(g(f(c)))}, {old(f(f(c)))}, {c in sc} + Rejected triggers: + {g(c)} (loops with {g(f(c))}) + {f(c)} (loops with {f(g(c))}) + {old(g(c))} (loops with {old(g(f(c)))}) + {old(f(c))} (loops with {old(f(f(c)))}, {old(f(g(c)))}) +old-is-a-special-case-for-triggers.dfy(25,10): Info: Selected triggers: + {old(f(c))}, {f(c)}, {c in sc} + Rejected triggers: {old(g(f(c)))} (more specific than {old(f(c))}) +old-is-a-special-case-for-triggers.dfy(26,10): Info: Selected triggers: + {old(f(c))}, {f(c)}, {c in sc} + Rejected triggers: + {g(f(c))} (more specific than {f(c)}) + {old(g(f(c)))} (more specific than {old(f(c))}) + +Dafny program verifier finished with 5 verified, 0 errors -- cgit v1.2.3 From 5623306213e01e8d667a7893cb0c3275ecfbf065 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sat, 22 Aug 2015 13:44:42 -0700 Subject: Add a 'tutorial' folder to the distribution, with an initial example. It would be nice to gather neat Dafny examples there; each new feature could have its own small file that demoes it, and we could also have examples that showcase stuff that we think is impressive. I'm adding this as a test folder, because it's important to check that these cool examples don't break, but the focus probably shouldn't be on exhaustively testing the features being demoed. --- Test/tutorial/maximum.dfy | 32 ++++++++++++++++++++++++++++++++ Test/tutorial/maximum.dfy.expect | 7 +++++++ 2 files changed, 39 insertions(+) create mode 100644 Test/tutorial/maximum.dfy create mode 100644 Test/tutorial/maximum.dfy.expect diff --git a/Test/tutorial/maximum.dfy b/Test/tutorial/maximum.dfy new file mode 100644 index 00000000..81faa219 --- /dev/null +++ b/Test/tutorial/maximum.dfy @@ -0,0 +1,32 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This file shows how to specify and implement a function to compute the +// largest element of a list. The function is fully specified by two +// preconditions, as proved by the MaximumIsUnique lemma below. + +method Maximum(values: seq) returns (max: int) + requires values != [] + ensures max in values + ensures forall i | 0 <= i < |values| :: values[i] <= max +{ + max := values[0]; + var idx := 0; + while (idx < |values|) + invariant max in values + invariant idx <= |values| + invariant forall j | 0 <= j < idx :: values[j] <= max + { + if (values[idx] > max) { + max := values[idx]; + } + idx := idx + 1; + } +} + +lemma MaximumIsUnique(values: seq, m1: int, m2: int) + requires m1 in values && forall i | 0 <= i < |values| :: values[i] <= m1 + requires m2 in values && forall i | 0 <= i < |values| :: values[i] <= m2 + ensures m1 == m2 { + // This lemma does not need a body: Dafny is able to prove it correct entirely automatically. +} diff --git a/Test/tutorial/maximum.dfy.expect b/Test/tutorial/maximum.dfy.expect new file mode 100644 index 00000000..70768304 --- /dev/null +++ b/Test/tutorial/maximum.dfy.expect @@ -0,0 +1,7 @@ +maximum.dfy(11,10): Info: Selected triggers: {values[i]} +maximum.dfy(18,14): Info: Selected triggers: {values[j]} +maximum.dfy(28,27): Info: Selected triggers: {values[i]} +maximum.dfy(29,27): Info: Selected triggers: {values[i]} +maximum.dfy(15,2): Info: decreases int(|values|) - int(idx) + +Dafny program verifier finished with 4 verified, 0 errors -- cgit v1.2.3 From 109a5ba2678d911eaa4446674988101677096896 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sat, 22 Aug 2015 13:48:17 -0700 Subject: Set exec flag on Binaries/z3 --- Binaries/z3 | Bin 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 Binaries/z3 diff --git a/Binaries/z3 b/Binaries/z3 old mode 100644 new mode 100755 -- cgit v1.2.3 From 5b62a70205871f5f4d62db5247f11ac6c53fc57e Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sat, 22 Aug 2015 15:39:50 -0700 Subject: Look at the full quantifier to find loops, not just the term. --- Source/Dafny/Triggers/TriggerExtensions.cs | 2 +- Test/triggers/loop-detection-looks-at-ranges-too.dfy | 14 ++++++++++++++ .../triggers/loop-detection-looks-at-ranges-too.dfy.expect | 6 ++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 Test/triggers/loop-detection-looks-at-ranges-too.dfy create mode 100644 Test/triggers/loop-detection-looks-at-ranges-too.dfy.expect diff --git a/Source/Dafny/Triggers/TriggerExtensions.cs b/Source/Dafny/Triggers/TriggerExtensions.cs index 02deb92f..71414eee 100644 --- a/Source/Dafny/Triggers/TriggerExtensions.cs +++ b/Source/Dafny/Triggers/TriggerExtensions.cs @@ -121,7 +121,7 @@ namespace Microsoft.Dafny.Triggers { } internal static IEnumerable SubexpressionsMatchingTrigger(this QuantifierExpr quantifier, Expression trigger) { - return quantifier.Term.AllSubExpressions(true, false) //FIXME should loop detection actually pass true here? + return quantifier.AllSubExpressions(true, true) .Select(e => TriggerUtils.PrepareExprForInclusionInTrigger(e).MatchAgainst(trigger, quantifier.BoundVars, e)) .Where(e => e.HasValue).Select(e => e.Value); } diff --git a/Test/triggers/loop-detection-looks-at-ranges-too.dfy b/Test/triggers/loop-detection-looks-at-ranges-too.dfy new file mode 100644 index 00000000..7a99ea2d --- /dev/null +++ b/Test/triggers/loop-detection-looks-at-ranges-too.dfy @@ -0,0 +1,14 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This file checks that loops between the range and the term of a quantifier +// are properly detected. + +predicate P(x: int) + +method M(x: int) { + // This will be flagged as a loop even without looking at the range + assert true || forall x: int | P(x) :: P(x+1); + // This requires checking the range for looping terms + assert true || forall x: int | P(x+1) :: P(x); +} diff --git a/Test/triggers/loop-detection-looks-at-ranges-too.dfy.expect b/Test/triggers/loop-detection-looks-at-ranges-too.dfy.expect new file mode 100644 index 00000000..72482de5 --- /dev/null +++ b/Test/triggers/loop-detection-looks-at-ranges-too.dfy.expect @@ -0,0 +1,6 @@ +loop-detection-looks-at-ranges-too.dfy(11,17): Warning: Selected triggers: {P(x)} (loops with {P(x + 1)}) + (!) Suppressing loops would leave this expression without triggers. +loop-detection-looks-at-ranges-too.dfy(13,17): Warning: Selected triggers: {P(x)} (loops with {P(x + 1)}) + (!) Suppressing loops would leave this expression without triggers. + +Dafny program verifier finished with 3 verified, 0 errors -- cgit v1.2.3 From ea5592cb3fade85b31a28a3119f5e637da47e72a Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sat, 22 Aug 2015 15:40:48 -0700 Subject: Ignore flycheck_ and .orig files --- .hgignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.hgignore b/.hgignore index de39514d..667378b0 100644 --- a/.hgignore +++ b/.hgignore @@ -8,6 +8,8 @@ Test/desktop/.* Test/([^/]*)/([^/]*)\.sx ^Test/sandbox/.* ^Test/.*\.csv +Test/.*/flycheck_.* +.*\.orig syntax: glob *.exe *.pdb -- cgit v1.2.3 From d0a06250f82efe318fcc0f7ce80d4286552fe727 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sat, 22 Aug 2015 20:05:57 -0700 Subject: Add more wishes to the wishlist --- Test/wishlist/naked-function-in-recursive-setting.dfy | 13 +++++++++++++ .../wishlist/naked-function-in-recursive-setting.dfy.expect | 8 ++++++++ Test/wishlist/useless-casts-in-decreases-clauses.dfy | 9 +++++++++ Test/wishlist/useless-casts-in-decreases-clauses.dfy.expect | 3 +++ 4 files changed, 33 insertions(+) create mode 100644 Test/wishlist/naked-function-in-recursive-setting.dfy create mode 100644 Test/wishlist/naked-function-in-recursive-setting.dfy.expect create mode 100644 Test/wishlist/useless-casts-in-decreases-clauses.dfy create mode 100644 Test/wishlist/useless-casts-in-decreases-clauses.dfy.expect diff --git a/Test/wishlist/naked-function-in-recursive-setting.dfy b/Test/wishlist/naked-function-in-recursive-setting.dfy new file mode 100644 index 00000000..650fc4c3 --- /dev/null +++ b/Test/wishlist/naked-function-in-recursive-setting.dfy @@ -0,0 +1,13 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /printTooltips "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +function fact(n: int): int + requires n >= 0 +{ + if n == 0 then + 1 + else ( + assert fact.requires(n-1); //WISH + n * fact(n-1) + ) +} diff --git a/Test/wishlist/naked-function-in-recursive-setting.dfy.expect b/Test/wishlist/naked-function-in-recursive-setting.dfy.expect new file mode 100644 index 00000000..4b1691b4 --- /dev/null +++ b/Test/wishlist/naked-function-in-recursive-setting.dfy.expect @@ -0,0 +1,8 @@ +naked-function-in-recursive-setting.dfy(4,9): Info: decreases n +naked-function-in-recursive-setting.dfy(10,11): Error: cannot use naked function in recursive setting. Possible solution: eta expansion. +Execution trace: + (0,0): anon0 + (0,0): anon7_Else + (0,0): anon8_Else + +Dafny program verifier finished with 0 verified, 1 error diff --git a/Test/wishlist/useless-casts-in-decreases-clauses.dfy b/Test/wishlist/useless-casts-in-decreases-clauses.dfy new file mode 100644 index 00000000..9b002d47 --- /dev/null +++ b/Test/wishlist/useless-casts-in-decreases-clauses.dfy @@ -0,0 +1,9 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /printTooltips "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +method M() { + var pos := 10; + while (pos > 0) { // This shouldn't print int(pos) - int(0); pos - 0 would be better + pos := pos - 1; + } +} diff --git a/Test/wishlist/useless-casts-in-decreases-clauses.dfy.expect b/Test/wishlist/useless-casts-in-decreases-clauses.dfy.expect new file mode 100644 index 00000000..e37b8b9b --- /dev/null +++ b/Test/wishlist/useless-casts-in-decreases-clauses.dfy.expect @@ -0,0 +1,3 @@ +useless-casts-in-decreases-clauses.dfy(6,2): Info: decreases int(pos) - int(0) + +Dafny program verifier finished with 2 verified, 0 errors -- cgit v1.2.3 From 2142e51d713394d384b0f33c1189f633dcbe301a Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sat, 22 Aug 2015 20:15:34 -0700 Subject: Grant "wishlist/useless-casts-in-decreases-clauses.dfy" --- Source/Dafny/DafnyAst.cs | 17 +++++++++++++---- Test/tutorial/maximum.dfy.expect | 14 +++++++------- .../granted/useless-casts-in-decreases-clauses.dfy | 9 +++++++++ .../useless-casts-in-decreases-clauses.dfy.expect | 3 +++ Test/wishlist/useless-casts-in-decreases-clauses.dfy | 9 --------- .../useless-casts-in-decreases-clauses.dfy.expect | 3 --- 6 files changed, 32 insertions(+), 23 deletions(-) create mode 100644 Test/wishlist/granted/useless-casts-in-decreases-clauses.dfy create mode 100644 Test/wishlist/granted/useless-casts-in-decreases-clauses.dfy.expect delete mode 100644 Test/wishlist/useless-casts-in-decreases-clauses.dfy delete mode 100644 Test/wishlist/useless-casts-in-decreases-clauses.dfy.expect diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index 35c6cec4..c4ea0a5a 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -5182,6 +5182,7 @@ namespace Microsoft.Dafny { return s; } + /// /// Create a resolved expression of the form "CVT(e0) - CVT(e1)", where "CVT" is either "int" (if /// e0.Type is an integer-based numeric type) or "real" (if e0.Type is a real-based numeric type). @@ -5195,13 +5196,21 @@ namespace Microsoft.Dafny { Contract.Ensures(Contract.Result() != null); Type toType = e0.Type.IsNumericBased(Type.NumericPersuation.Int) ? (Type)Type.Int : Type.Real; - e0 = new ConversionExpr(e0.tok, e0, toType); - e0.Type = toType; - e1 = new ConversionExpr(e1.tok, e1, toType); - e1.Type = toType; + e0 = CastIfNeeded(e0, toType); + e1 = CastIfNeeded(e1, toType); return CreateSubtract(e0, e1); } + private static Expression CastIfNeeded(Expression expr, Type toType) { + if (!expr.Type.Equals(toType)) { + var cast = new ConversionExpr(expr.tok, expr, toType); + cast.Type = toType; + return cast; + } else { + return expr; + } + } + /// /// Create a resolved expression of the form "e0 - e1" /// diff --git a/Test/tutorial/maximum.dfy.expect b/Test/tutorial/maximum.dfy.expect index 70768304..16a67088 100644 --- a/Test/tutorial/maximum.dfy.expect +++ b/Test/tutorial/maximum.dfy.expect @@ -1,7 +1,7 @@ -maximum.dfy(11,10): Info: Selected triggers: {values[i]} -maximum.dfy(18,14): Info: Selected triggers: {values[j]} -maximum.dfy(28,27): Info: Selected triggers: {values[i]} -maximum.dfy(29,27): Info: Selected triggers: {values[i]} -maximum.dfy(15,2): Info: decreases int(|values|) - int(idx) - -Dafny program verifier finished with 4 verified, 0 errors +maximum.dfy(11,10): Info: Selected triggers: {values[i]} +maximum.dfy(18,14): Info: Selected triggers: {values[j]} +maximum.dfy(28,27): Info: Selected triggers: {values[i]} +maximum.dfy(29,27): Info: Selected triggers: {values[i]} +maximum.dfy(15,2): Info: decreases |values| - idx + +Dafny program verifier finished with 4 verified, 0 errors diff --git a/Test/wishlist/granted/useless-casts-in-decreases-clauses.dfy b/Test/wishlist/granted/useless-casts-in-decreases-clauses.dfy new file mode 100644 index 00000000..9b002d47 --- /dev/null +++ b/Test/wishlist/granted/useless-casts-in-decreases-clauses.dfy @@ -0,0 +1,9 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /printTooltips "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +method M() { + var pos := 10; + while (pos > 0) { // This shouldn't print int(pos) - int(0); pos - 0 would be better + pos := pos - 1; + } +} diff --git a/Test/wishlist/granted/useless-casts-in-decreases-clauses.dfy.expect b/Test/wishlist/granted/useless-casts-in-decreases-clauses.dfy.expect new file mode 100644 index 00000000..36d7e6b8 --- /dev/null +++ b/Test/wishlist/granted/useless-casts-in-decreases-clauses.dfy.expect @@ -0,0 +1,3 @@ +useless-casts-in-decreases-clauses.dfy(6,2): Info: decreases pos - 0 + +Dafny program verifier finished with 2 verified, 0 errors diff --git a/Test/wishlist/useless-casts-in-decreases-clauses.dfy b/Test/wishlist/useless-casts-in-decreases-clauses.dfy deleted file mode 100644 index 9b002d47..00000000 --- a/Test/wishlist/useless-casts-in-decreases-clauses.dfy +++ /dev/null @@ -1,9 +0,0 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /printTooltips "%s" > "%t" -// RUN: %diff "%s.expect" "%t" - -method M() { - var pos := 10; - while (pos > 0) { // This shouldn't print int(pos) - int(0); pos - 0 would be better - pos := pos - 1; - } -} diff --git a/Test/wishlist/useless-casts-in-decreases-clauses.dfy.expect b/Test/wishlist/useless-casts-in-decreases-clauses.dfy.expect deleted file mode 100644 index e37b8b9b..00000000 --- a/Test/wishlist/useless-casts-in-decreases-clauses.dfy.expect +++ /dev/null @@ -1,3 +0,0 @@ -useless-casts-in-decreases-clauses.dfy(6,2): Info: decreases int(pos) - int(0) - -Dafny program verifier finished with 2 verified, 0 errors -- cgit v1.2.3 From 7a86aab6b034dafe36635178b277393cb5a2abb8 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sat, 22 Aug 2015 23:57:35 -0700 Subject: Add one more wish: it would be nice to be able to prove exists b: bool :: b This is an issue because splitting `exists b: bool :: b || !b` produces two quantifiers that we don't know how to prove. --- Test/wishlist/exists-b-exists-not-b.dfy | 10 ++++++++++ Test/wishlist/exists-b-exists-not-b.dfy.expect | 8 ++++++++ 2 files changed, 18 insertions(+) create mode 100644 Test/wishlist/exists-b-exists-not-b.dfy create mode 100644 Test/wishlist/exists-b-exists-not-b.dfy.expect diff --git a/Test/wishlist/exists-b-exists-not-b.dfy b/Test/wishlist/exists-b-exists-not-b.dfy new file mode 100644 index 00000000..711c5611 --- /dev/null +++ b/Test/wishlist/exists-b-exists-not-b.dfy @@ -0,0 +1,10 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// It would be great if Dafny was able to verify the following statements; +// otherwise, trigger splitting prevents `exists b :: b || not b` from verifying + +method M() { + assert exists b: bool :: b; // WISH + assert exists b: bool :: !b; // WISH +} diff --git a/Test/wishlist/exists-b-exists-not-b.dfy.expect b/Test/wishlist/exists-b-exists-not-b.dfy.expect new file mode 100644 index 00000000..c785ee97 --- /dev/null +++ b/Test/wishlist/exists-b-exists-not-b.dfy.expect @@ -0,0 +1,8 @@ +exists-b-exists-not-b.dfy(8,9): Error: assertion violation +Execution trace: + (0,0): anon0 +exists-b-exists-not-b.dfy(9,9): Error: assertion violation +Execution trace: + (0,0): anon0 + +Dafny program verifier finished with 1 verified, 2 errors -- cgit v1.2.3 From 994a151bc3646b76283b1d5fafdb91a5a26c821c Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sat, 22 Aug 2015 23:59:00 -0700 Subject: Ignore .bpl files in the Test directory --- .hgignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgignore b/.hgignore index 667378b0..f2275660 100644 --- a/.hgignore +++ b/.hgignore @@ -10,6 +10,7 @@ Test/([^/]*)/([^/]*)\.sx ^Test/.*\.csv Test/.*/flycheck_.* .*\.orig +Test/.*\.bpl syntax: glob *.exe *.pdb -- cgit v1.2.3 From 69f54c327bcc5a41a143bce88b5ba2327d7246a7 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sun, 23 Aug 2015 00:06:17 -0700 Subject: Fix: multi-dimensional OOB errors were sometimes reported on incorrect locations. --- Source/Dafny/Translator.cs | 17 +++++++++-------- Test/dafny0/Matrix-OOB.dfy | 13 +++++++++++++ Test/dafny0/Matrix-OOB.dfy.expect | 12 ++++++++++++ 3 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 Test/dafny0/Matrix-OOB.dfy create mode 100644 Test/dafny0/Matrix-OOB.dfy.expect diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 524466c9..09ca32e0 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -4885,16 +4885,17 @@ namespace Microsoft.Dafny { MultiSelectExpr e = (MultiSelectExpr)expr; CheckWellformed(e.Array, options, locals, builder, etran); Bpl.Expr array = etran.TrExpr(e.Array); - int i = 0; - foreach (Expression idx in e.Indices) { + for (int idxId = 0; idxId < e.Indices.Count; idxId++) { + var idx = e.Indices[idxId]; CheckWellformed(idx, options, locals, builder, etran); - Bpl.Expr index = etran.TrExpr(idx); - Bpl.Expr lower = Bpl.Expr.Le(Bpl.Expr.Literal(0), index); - Bpl.Expr length = ArrayLength(idx.tok, array, e.Indices.Count, i); - Bpl.Expr upper = Bpl.Expr.Lt(index, length); - builder.Add(Assert(idx.tok, Bpl.Expr.And(lower, upper), "index " + i + " out of range", options.AssertKv)); - i++; + var index = etran.TrExpr(idx); + var lower = Bpl.Expr.Le(Bpl.Expr.Literal(0), index); + var length = ArrayLength(idx.tok, array, e.Indices.Count, idxId); + var upper = Bpl.Expr.Lt(index, length); + var tok = idx is IdentifierExpr ? e.tok : idx.tok; // TODO: Reusing the token of an identifier expression would underline its definition. but this is still not perfect. + + builder.Add(Assert(tok, Bpl.Expr.And(lower, upper), String.Format("index {0} out of range", idxId), options.AssertKv)); } } else if (expr is SeqUpdateExpr) { SeqUpdateExpr e = (SeqUpdateExpr)expr; diff --git a/Test/dafny0/Matrix-OOB.dfy b/Test/dafny0/Matrix-OOB.dfy new file mode 100644 index 00000000..2e5c0366 --- /dev/null +++ b/Test/dafny0/Matrix-OOB.dfy @@ -0,0 +1,13 @@ +// RUN: %dafny /compile:0 /dprint:"%t.dprint" /printTooltips "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This is a regression test: OOB errors for matrices used to be reported on the +// quantifier that introduced the variables that constituted the invalid indices. + +// WISH: It would be even better to report the error on the variables inside the +// array instead of the array itself. + +method M(m: array2) + requires m != null + ensures forall i, j :: m[i, j] == 0 +{ } diff --git a/Test/dafny0/Matrix-OOB.dfy.expect b/Test/dafny0/Matrix-OOB.dfy.expect new file mode 100644 index 00000000..94e77aa4 --- /dev/null +++ b/Test/dafny0/Matrix-OOB.dfy.expect @@ -0,0 +1,12 @@ +Matrix-OOB.dfy(12,26): Error: index 0 out of range +Execution trace: + (0,0): anon0 +Matrix-OOB.dfy(12,26): Error: index 1 out of range +Execution trace: + (0,0): anon0 +Matrix-OOB.dfy(13,0): Error BP5003: A postcondition might not hold on this return path. +Matrix-OOB.dfy(12,10): Related location: This is the postcondition that might not hold. +Execution trace: + (0,0): anon0 + +Dafny program verifier finished with 0 verified, 3 errors -- cgit v1.2.3 From 4119a14a906c3331921c4a44d84bdd30b3628af5 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sun, 23 Aug 2015 00:07:43 -0700 Subject: Exclude axiom-profiler.html and z3.log files in the Test directory --- .hgignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.hgignore b/.hgignore index f2275660..f082897f 100644 --- a/.hgignore +++ b/.hgignore @@ -11,6 +11,8 @@ Test/([^/]*)/([^/]*)\.sx Test/.*/flycheck_.* .*\.orig Test/.*\.bpl +Test/.*/axiom-profiler.html +Test/.*/z3.log syntax: glob *.exe *.pdb -- cgit v1.2.3 From 489605d67b72247866a9797d8c70c06e117e5596 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sun, 23 Aug 2015 00:07:59 -0700 Subject: Trivial code cleanup --- Source/Dafny/Triggers/QuantifiersCollector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Dafny/Triggers/QuantifiersCollector.cs b/Source/Dafny/Triggers/QuantifiersCollector.cs index 3385f36e..1f37dcfa 100644 --- a/Source/Dafny/Triggers/QuantifiersCollector.cs +++ b/Source/Dafny/Triggers/QuantifiersCollector.cs @@ -26,7 +26,7 @@ namespace Microsoft.Dafny.Triggers { if (quantifier.SplitQuantifier != null) { var collection = quantifier.SplitQuantifier.Select(q => q as QuantifierExpr).Where(q => q != null); quantifierCollections.Add(new QuantifiersCollection(collection, reporter)); - foreach (var q in quantifier.SplitQuantifier) { quantifiers.Add(q); } + quantifiers.UnionWith(quantifier.SplitQuantifier); } else { quantifierCollections.Add(new QuantifiersCollection(Enumerable.Repeat(quantifier, 1), reporter)); } -- cgit v1.2.3 From 1a0ef1ae83a88966a2d68e5eba5a70a215f28f3e Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sun, 23 Aug 2015 00:08:25 -0700 Subject: Allow MultiSelectExpr as quantifier heads --- Source/Dafny/Triggers/TriggersCollector.cs | 3 ++- Test/triggers/matrix-accesses-are-triggers.dfy | 9 +++++++++ Test/triggers/matrix-accesses-are-triggers.dfy.expect | 12 ++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 Test/triggers/matrix-accesses-are-triggers.dfy create mode 100644 Test/triggers/matrix-accesses-are-triggers.dfy.expect diff --git a/Source/Dafny/Triggers/TriggersCollector.cs b/Source/Dafny/Triggers/TriggersCollector.cs index 11860404..69c47d90 100644 --- a/Source/Dafny/Triggers/TriggersCollector.cs +++ b/Source/Dafny/Triggers/TriggersCollector.cs @@ -185,7 +185,8 @@ namespace Microsoft.Dafny.Triggers { expr.SubExpressions.Iter(e => Annotate(e)); TriggerAnnotation annotation; // TODO: Using ApplySuffix fixes the unresolved members problem in GenericSort - if (expr is FunctionCallExpr || expr is SeqSelectExpr || expr is MemberSelectExpr || expr is OldExpr || expr is ApplyExpr || expr is DisplayExpression || + if (expr is FunctionCallExpr || expr is SeqSelectExpr || expr is MultiSelectExpr || expr is MemberSelectExpr || + expr is OldExpr || expr is ApplyExpr || expr is DisplayExpression || (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); diff --git a/Test/triggers/matrix-accesses-are-triggers.dfy b/Test/triggers/matrix-accesses-are-triggers.dfy new file mode 100644 index 00000000..630fab9d --- /dev/null +++ b/Test/triggers/matrix-accesses-are-triggers.dfy @@ -0,0 +1,9 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This file checks that multi-dimensional array accesses yield trigger candidates + +method M(m: array2) + requires m != null + requires forall i, j | 0 <= i < m.Length0 && 0 <= j < m.Length1 :: m[i, j] == m[j, i+1] { +} diff --git a/Test/triggers/matrix-accesses-are-triggers.dfy.expect b/Test/triggers/matrix-accesses-are-triggers.dfy.expect new file mode 100644 index 00000000..b9dd1d88 --- /dev/null +++ b/Test/triggers/matrix-accesses-are-triggers.dfy.expect @@ -0,0 +1,12 @@ +matrix-accesses-are-triggers.dfy(8,11): Warning: Selected triggers: {m[i, j]} (loops with {m[j, i + 1]}) + (!) Suppressing loops would leave this expression without triggers. +matrix-accesses-are-triggers.dfy(8,81): Error: index 0 out of range +Execution trace: + (0,0): anon0 + (0,0): anon4_Then +matrix-accesses-are-triggers.dfy(8,86): Error: index 1 out of range +Execution trace: + (0,0): anon0 + (0,0): anon4_Then + +Dafny program verifier finished with 1 verified, 2 errors -- cgit v1.2.3 From e0dbb8af7290beb81350acd0088866c0ca54acb8 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sun, 23 Aug 2015 00:09:36 -0700 Subject: Improve error reporting for split quantifiers --- Source/Dafny/Triggers/QuantifierSplitter.cs | 3 +- Test/triggers/splitting-picks-the-right-tokens.dfy | 24 ++++++++++++++ .../splitting-picks-the-right-tokens.dfy.expect | 38 ++++++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 Test/triggers/splitting-picks-the-right-tokens.dfy create mode 100644 Test/triggers/splitting-picks-the-right-tokens.dfy.expect diff --git a/Source/Dafny/Triggers/QuantifierSplitter.cs b/Source/Dafny/Triggers/QuantifierSplitter.cs index c50bc9c6..ae76b780 100644 --- a/Source/Dafny/Triggers/QuantifierSplitter.cs +++ b/Source/Dafny/Triggers/QuantifierSplitter.cs @@ -48,7 +48,8 @@ namespace Microsoft.Dafny.Triggers { 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) { ResolvedOp = pair.ResolvedOp, Type = pair.Type }; + // Notice the token. This makes triggers/splitting-picks-the-right-tokens.dfy possible + yield return new BinaryExpr(e1.tok, pair.Op, pair.E0, e1) { ResolvedOp = pair.ResolvedOp, Type = pair.Type }; } } diff --git a/Test/triggers/splitting-picks-the-right-tokens.dfy b/Test/triggers/splitting-picks-the-right-tokens.dfy new file mode 100644 index 00000000..76065eca --- /dev/null +++ b/Test/triggers/splitting-picks-the-right-tokens.dfy @@ -0,0 +1,24 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This file ensures that trigger splitting picks the right tokens + +function Id(i: int): int { i } + +method MSuchThat() + requires forall x | x > 0 :: Id(x) > 1 && x > 2 && x > -1 { } + +method MImplies() + // The bodies of the two terms that are produced here are both + // BinaryExpressions(==>); the token they use, however, is that of the RHS + // terms of these implications; otherwise, error messages would get stacked on + // the ==> sign + requires forall x :: x > 0 ==> Id(x) > 1 && x > 2 && x > -1 { } + +method M() { + if * { + MImplies(); + } else { + MSuchThat(); + } +} diff --git a/Test/triggers/splitting-picks-the-right-tokens.dfy.expect b/Test/triggers/splitting-picks-the-right-tokens.dfy.expect new file mode 100644 index 00000000..af274e75 --- /dev/null +++ b/Test/triggers/splitting-picks-the-right-tokens.dfy.expect @@ -0,0 +1,38 @@ +splitting-picks-the-right-tokens.dfy(9,11): Info: For expression {Id(x) > 1}: + Selected triggers: {Id(x)} +splitting-picks-the-right-tokens.dfy(9,11): Info: For expression {x > 2}: + Selected triggers: {Id(x)} +splitting-picks-the-right-tokens.dfy(9,11): Info: For expression {x > -1}: + Selected triggers: {Id(x)} +splitting-picks-the-right-tokens.dfy(16,11): Info: For expression {x > 0 ==> Id(x) > 1}: + Selected triggers: {Id(x)} +splitting-picks-the-right-tokens.dfy(16,11): Info: For expression {x > 0 ==> x > 2}: + Selected triggers: {Id(x)} +splitting-picks-the-right-tokens.dfy(16,11): Info: For expression {x > 0 ==> x > -1}: + Selected triggers: {Id(x)} +splitting-picks-the-right-tokens.dfy(20,12): Error BP5002: A precondition for this call might not hold. +splitting-picks-the-right-tokens.dfy(16,11): Related location: This is the precondition that might not hold. +splitting-picks-the-right-tokens.dfy(16,48): Related location +Execution trace: + (0,0): anon0 + (0,0): anon3_Then +splitting-picks-the-right-tokens.dfy(20,12): Error BP5002: A precondition for this call might not hold. +splitting-picks-the-right-tokens.dfy(16,11): Related location: This is the precondition that might not hold. +splitting-picks-the-right-tokens.dfy(16,39): Related location +Execution trace: + (0,0): anon0 + (0,0): anon3_Then +splitting-picks-the-right-tokens.dfy(22,13): Error BP5002: A precondition for this call might not hold. +splitting-picks-the-right-tokens.dfy(9,11): Related location: This is the precondition that might not hold. +splitting-picks-the-right-tokens.dfy(9,46): Related location +Execution trace: + (0,0): anon0 + (0,0): anon3_Else +splitting-picks-the-right-tokens.dfy(22,13): Error BP5002: A precondition for this call might not hold. +splitting-picks-the-right-tokens.dfy(9,11): Related location: This is the precondition that might not hold. +splitting-picks-the-right-tokens.dfy(9,37): Related location +Execution trace: + (0,0): anon0 + (0,0): anon3_Else + +Dafny program verifier finished with 6 verified, 4 errors -- cgit v1.2.3 From 084f7c82033315d605dce606ce64a26e1dcc0b3d Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sun, 23 Aug 2015 00:10:05 -0700 Subject: Add a sanity check in QuantifiersCollection --- Source/Dafny/Triggers/QuantifiersCollection.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Dafny/Triggers/QuantifiersCollection.cs b/Source/Dafny/Triggers/QuantifiersCollection.cs index 1d5625cd..0ef59bad 100644 --- a/Source/Dafny/Triggers/QuantifiersCollection.cs +++ b/Source/Dafny/Triggers/QuantifiersCollection.cs @@ -34,6 +34,7 @@ namespace Microsoft.Dafny.Triggers { readonly List quantifiers; internal QuantifiersCollection(IEnumerable quantifiers, ErrorReporter reporter) { + Contract.Requires(quantifiers.All(q => q.SplitQuantifier == null)); this.reporter = reporter; this.quantifiers = quantifiers.Select(q => new QuantifierWithTriggers(q)).ToList(); } -- cgit v1.2.3 From be66434c97446d311aada9aec42fdbc8eba26ea8 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sun, 23 Aug 2015 00:11:00 -0700 Subject: Shallow-copy quantifier attributes when splitting --- Source/Dafny/Triggers/QuantifierSplitter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Dafny/Triggers/QuantifierSplitter.cs b/Source/Dafny/Triggers/QuantifierSplitter.cs index ae76b780..83059f8d 100644 --- a/Source/Dafny/Triggers/QuantifierSplitter.cs +++ b/Source/Dafny/Triggers/QuantifierSplitter.cs @@ -66,7 +66,7 @@ namespace Microsoft.Dafny.Triggers { } foreach (var e in stream) { var tok = new NestedToken(quantifier.tok, e.tok); - yield return new ForallExpr(tok, quantifier.TypeArgs, quantifier.BoundVars, quantifier.Range, e, quantifier.Attributes) { Type = quantifier.Type }; + yield return new ForallExpr(tok, quantifier.TypeArgs, quantifier.BoundVars, quantifier.Range, e, CopyAttributes(quantifier.Attributes)) { Type = quantifier.Type }; } } else if (quantifier is ExistsExpr) { IEnumerable stream; @@ -77,7 +77,7 @@ namespace Microsoft.Dafny.Triggers { } foreach (var e in stream) { var tok = new NestedToken(quantifier.tok, e.tok); - yield return new ExistsExpr(tok, quantifier.TypeArgs, quantifier.BoundVars, quantifier.Range, e, quantifier.Attributes) { Type = quantifier.Type }; + yield return new ExistsExpr(tok, quantifier.TypeArgs, quantifier.BoundVars, quantifier.Range, e, CopyAttributes(quantifier.Attributes)) { Type = quantifier.Type }; } } else { yield return quantifier; -- cgit v1.2.3 From dc08d67a27ffeb26889a56daa573ccc56daa1c0d Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sun, 23 Aug 2015 00:12:04 -0700 Subject: Make quantifier splitting a two step process This fixes a bug that affected Monad.dfy --- Source/Dafny/DafnyAst.cs | 2 +- Source/Dafny/Rewriter.cs | 1 + Source/Dafny/Triggers/QuantifierSplitter.cs | 42 ++++++++++++++++++++++++----- Test/triggers/regression-tests.dfy | 20 ++++++++++++++ Test/triggers/regression-tests.dfy.expect | 3 +++ 5 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 Test/triggers/regression-tests.dfy create mode 100644 Test/triggers/regression-tests.dfy.expect diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index c4ea0a5a..c7aae173 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -4796,7 +4796,7 @@ namespace Microsoft.Dafny { } public override IEnumerable SubExpressions { - get { + get { // FIXME: This can return duplicates; this could confuse BottomUpVisitors that modify the AST in place foreach (var e in base.SubExpressions) { yield return e; } foreach (var e in Attributes.SubExpressions(Attributes)) { yield return e; } diff --git a/Source/Dafny/Rewriter.cs b/Source/Dafny/Rewriter.cs index 557eee93..9bc24c30 100644 --- a/Source/Dafny/Rewriter.cs +++ b/Source/Dafny/Rewriter.cs @@ -68,6 +68,7 @@ namespace Microsoft.Dafny foreach (var decl in ModuleDefinition.AllCallables(m.TopLevelDecls)) { splitter.Visit(decl); } + splitter.Commit(); } } diff --git a/Source/Dafny/Triggers/QuantifierSplitter.cs b/Source/Dafny/Triggers/QuantifierSplitter.cs index 83059f8d..865aa33e 100644 --- a/Source/Dafny/Triggers/QuantifierSplitter.cs +++ b/Source/Dafny/Triggers/QuantifierSplitter.cs @@ -1,11 +1,19 @@ using Microsoft.Boogie; using System; using System.Collections.Generic; +using System.Diagnostics.Contracts; using System.Linq; using System.Text; namespace Microsoft.Dafny.Triggers { class QuantifierSplitter : BottomUpVisitor { + /// This cache was introduced because some statements (notably calc) return the same SubExpression multiple times. + /// This ended up causing an inconsistent situation when the calc statement's subexpressions contained the same quantifier + /// twice: on the first pass that quantifier got its SplitQuantifiers generated, and on the the second pass these + /// split quantifiers got re-split, creating a situation where the direct children of a split quantifier were + /// also split quantifiers. + private Dictionary> splits = new Dictionary>(); + private static BinaryExpr.Opcode FlipOpcode(BinaryExpr.Opcode opCode) { if (opCode == BinaryExpr.Opcode.And) { return BinaryExpr.Opcode.Or; @@ -24,8 +32,15 @@ namespace Microsoft.Dafny.Triggers { // forall x :: P(x) ==> (Q(x) && R(x)) private static UnaryOpExpr Not(Expression expr) { - var not = new UnaryOpExpr(expr.tok, UnaryOpExpr.Opcode.Not, expr) { Type = expr.Type }; - return not; + return new UnaryOpExpr(expr.tok, UnaryOpExpr.Opcode.Not, expr) { Type = expr.Type }; + } + + private static Attributes CopyAttributes(Attributes source) { + if (source == null) { + return null; + } else { + return new Attributes(source.Name, source.Args, CopyAttributes(source.Prev)); + } } internal static IEnumerable SplitExpr(Expression expr, BinaryExpr.Opcode separator) { @@ -83,16 +98,29 @@ namespace Microsoft.Dafny.Triggers { yield return quantifier; } } + + private static bool AllowsSplitting(QuantifierExpr quantifier) { + bool splitAttr = true; + return !Attributes.ContainsBool(quantifier.Attributes, "split", ref splitAttr) || splitAttr; + } protected override void VisitOneExpr(Expression expr) { var quantifier = expr as QuantifierExpr; - if (quantifier != null && quantifier.SplitQuantifier == null) { - bool splitAttr = true; - if (!Attributes.ContainsBool(quantifier.Attributes, "split", ref splitAttr) || splitAttr) { - var split = SplitQuantifier(quantifier).ToList(); - quantifier.SplitQuantifier = split; + if (quantifier != null) { + Contract.Assert(quantifier.SplitQuantifier == null); + if (!splits.ContainsKey(quantifier) && AllowsSplitting(quantifier)) { + splits[quantifier] = SplitQuantifier(quantifier).ToList(); } } } + + /// + /// See comments above definition of splits for reason why this method exists + /// + internal void Commit() { + foreach (var quantifier in splits.Keys) { + quantifier.SplitQuantifier = splits[quantifier]; + } + } } } diff --git a/Test/triggers/regression-tests.dfy b/Test/triggers/regression-tests.dfy new file mode 100644 index 00000000..263e424a --- /dev/null +++ b/Test/triggers/regression-tests.dfy @@ -0,0 +1,20 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This tests checks that quantifier splitting is resilient to the fact that +// certain statements (like calc) can return duplicate subexpressions. This was +// once a problem, because a quantifier that got returned twice would get split +// on the first pass over it, and would have its nely created children re-split +// on the second pass. This created a split quantifier whose children were split +// quantifiers, which violated an invariant of spliit quantifiers. + +abstract module Base { } + +module Blah refines Base { + lemma A() { + calc { + forall b :: b; + } + } +} + diff --git a/Test/triggers/regression-tests.dfy.expect b/Test/triggers/regression-tests.dfy.expect new file mode 100644 index 00000000..a03810fb --- /dev/null +++ b/Test/triggers/regression-tests.dfy.expect @@ -0,0 +1,3 @@ +regression-tests.dfy(16,5): Warning: (!) No terms found to trigger on. + +Dafny program verifier finished with 2 verified, 0 errors -- cgit v1.2.3 From 0858e1e3e86a41d809ce17cf3a25e2289ccc3b8d Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sun, 23 Aug 2015 00:13:21 -0700 Subject: Replace b || !b by true in Snapshots5.v1.dfy We can't prove `exists b: bool :: b || !b` when splitting is enabled, at least for now, and there's already a separate test for that particular issue in wishlist/ --- Test/dafny0/snapshots/Inputs/Snapshots5.v1.dfy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Test/dafny0/snapshots/Inputs/Snapshots5.v1.dfy b/Test/dafny0/snapshots/Inputs/Snapshots5.v1.dfy index 05dbced0..7b207d74 100644 --- a/Test/dafny0/snapshots/Inputs/Snapshots5.v1.dfy +++ b/Test/dafny0/snapshots/Inputs/Snapshots5.v1.dfy @@ -17,9 +17,9 @@ method M() } else { - assert (exists b: bool :: b || !b) || 4 != 4; + assert (exists b: bool :: true) || 4 != 4; } - assert (exists b: bool :: b || !b) || 5 != 5; + assert (exists b: bool :: true) || 5 != 5; } -- cgit v1.2.3 From c6d160193cba277905946f27c0dca2bdadb4a919 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sun, 23 Aug 2015 00:20:16 -0700 Subject: Added /autoTriggers to two tests where it only makes a cosmetic difference --- Test/dafny0/AutoReq.dfy | 7 ++++++- Test/dafny0/AutoReq.dfy.expect | 9 ++++++++- Test/dafny0/Inverses.dfy | 5 ++++- Test/dafny0/Inverses.dfy.expect | 2 ++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Test/dafny0/AutoReq.dfy b/Test/dafny0/AutoReq.dfy index acfe6b8d..d7c87e6d 100644 --- a/Test/dafny0/AutoReq.dfy +++ b/Test/dafny0/AutoReq.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" // RUN: %diff "%s.expect" "%t" function f(x:int) : bool @@ -313,3 +313,8 @@ module OpaqueTest { } } + +// autoTriggers added because it causes an extra error message related to +// violated preconditions to appear. That extra message is due to the extra +// precondition involving a split quantifier: the user now gets two traces, one +// for each conjunct. diff --git a/Test/dafny0/AutoReq.dfy.expect b/Test/dafny0/AutoReq.dfy.expect index b4b34e14..25f00797 100644 --- a/Test/dafny0/AutoReq.dfy.expect +++ b/Test/dafny0/AutoReq.dfy.expect @@ -1,5 +1,12 @@ AutoReq.dfy(247,4): Error: possible violation of function precondition AutoReq.dfy(239,13): Related location +AutoReq.dfy(239,59): Related location +Execution trace: + (0,0): anon0 + (0,0): anon4_Else +AutoReq.dfy(247,4): Error: possible violation of function precondition +AutoReq.dfy(239,13): Related location +AutoReq.dfy(239,35): Related location Execution trace: (0,0): anon0 (0,0): anon4_Else @@ -42,4 +49,4 @@ Execution trace: (0,0): anon0 (0,0): anon11_Then -Dafny program verifier finished with 52 verified, 8 errors +Dafny program verifier finished with 52 verified, 9 errors diff --git a/Test/dafny0/Inverses.dfy b/Test/dafny0/Inverses.dfy index 7995255a..b424cfd9 100644 --- a/Test/dafny0/Inverses.dfy +++ b/Test/dafny0/Inverses.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" // RUN: %diff "%s.expect" "%t" // This identity function is used to so that if the occurrence of i below @@ -110,3 +110,6 @@ method RotateD(a: array) returns (r: array) r[if a.Length - 1 == i then 0 else i + 1] := a[Id(i)]; // yes, Dafny can invert this one } } + +// autoTriggers added because it causes a slight rephrasing of an error +// message. diff --git a/Test/dafny0/Inverses.dfy.expect b/Test/dafny0/Inverses.dfy.expect index 29c67e5d..b9530e3f 100644 --- a/Test/dafny0/Inverses.dfy.expect +++ b/Test/dafny0/Inverses.dfy.expect @@ -1,10 +1,12 @@ Inverses.dfy(70,0): Error BP5003: A postcondition might not hold on this return path. Inverses.dfy(69,10): Related location: This is the postcondition that might not hold. +Inverses.dfy(69,66): Related location Execution trace: (0,0): anon0 (0,0): anon6_Else Inverses.dfy(83,0): Error BP5003: A postcondition might not hold on this return path. Inverses.dfy(82,10): Related location: This is the postcondition that might not hold. +Inverses.dfy(82,66): Related location Execution trace: (0,0): anon0 (0,0): anon9_Else -- cgit v1.2.3 From f09b14f4b3f3915343b1279da286351ab5348eac Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sun, 23 Aug 2015 04:23:51 -0700 Subject: Replace the Emacs mode files by a pointer to the boogie-friends repo. Does mercurial support submodules? That could be a good option too. --- Util/Emacs/README | 2 + Util/Emacs/dafny-mode.el | 121 -------------------------------------------- Util/Emacs/jennisys-mode.el | 113 ----------------------------------------- 3 files changed, 2 insertions(+), 234 deletions(-) create mode 100644 Util/Emacs/README delete mode 100644 Util/Emacs/dafny-mode.el delete mode 100644 Util/Emacs/jennisys-mode.el diff --git a/Util/Emacs/README b/Util/Emacs/README new file mode 100644 index 00000000..9140f20a --- /dev/null +++ b/Util/Emacs/README @@ -0,0 +1,2 @@ +Emacs support for dafny is provided by the boogie-friends package, available from MELPA. +See https://github.com/boogie-org/boogie-friends for setup instructions and tips. \ No newline at end of file diff --git a/Util/Emacs/dafny-mode.el b/Util/Emacs/dafny-mode.el deleted file mode 100644 index fd1bf369..00000000 --- a/Util/Emacs/dafny-mode.el +++ /dev/null @@ -1,121 +0,0 @@ -;; dafny-mode.el - GNU Emacs mode for Dafny -;; Adapted by Rustan Leino from Jean-Christophe FILLIATRE's GNU Emancs mode for Why - -(defvar dafny-mode-hook nil) - -(defvar dafny-mode-map nil - "Keymap for Dafny major mode") - -(if dafny-mode-map nil - (setq dafny-mode-map (make-keymap)) - (define-key dafny-mode-map "\C-c\C-c" 'dafny-run-verifier) - (define-key dafny-mode-map [(control return)] 'font-lock-fontify-buffer)) - -(setq auto-mode-alist - (append - '(("\\.dfy" . dafny-mode)) - auto-mode-alist)) - -;; font-lock - -(defun dafny-regexp-opt (l) - (concat "\\_<" (concat (regexp-opt l t) "\\_>"))) - -(defconst dafny-font-lock-keywords-1 - (list - ; comments have the form /* ... */ - '("/\\*\\([^*]\\|\\*[^/]\\)*\\*/" . font-lock-comment-face) - ; or // ... - '("//\\([^ -]\\)*" . font-lock-comment-face) - - `(,(dafny-regexp-opt '( - "class" "trait" "datatype" "codatatype" "newtype" "type" "iterator" - "function" "predicate" "copredicate" "inductive" - "var" "method" "constructor" "lemma" "colemma" - "ghost" "static" "protected" "abstract" - "module" "import" "default" "as" "opened" - "include" - "extends" "refines" "returns" "yields" - "requires" "ensures" "modifies" "reads" "free" "invariant" "decreases" - )) . font-lock-builtin-face) - `(,(dafny-regexp-opt '( - "assert" "assume" "break" "then" "else" "if" "label" "return" "yield" - "while" "print" "where" - "old" "forall" "exists" "new" "calc" "modify" "in" "this" "fresh" - "match" "case" "false" "true" "null")) . font-lock-keyword-face) - `(,(dafny-regexp-opt '( - "bool" "char" "int" "nat" "real" - "set" "iset" "multiset" "seq" "string" "map" "imap" - "object" "array" "array2" "array3")) . font-lock-type-face) - ) - "Minimal highlighting for Dafny mode") - -(defvar dafny-font-lock-keywords dafny-font-lock-keywords-1 - "Default highlighting for Dafny mode") - -;; syntax - -(defvar dafny-mode-syntax-table nil - "Syntax table for dafny-mode") - -(defun dafny-create-syntax-table () - (if dafny-mode-syntax-table - () - (setq dafny-mode-syntax-table (make-syntax-table)) - (set-syntax-table dafny-mode-syntax-table) - (modify-syntax-entry ?' "w" dafny-mode-syntax-table) - (modify-syntax-entry ?_ "w" dafny-mode-syntax-table))) - -;; menu - -(require 'easymenu) - -(defun dafny-menu () - (easy-menu-define - dafny-mode-menu (list dafny-mode-map) - "Dafny Mode Menu." - '("Dafny" - ["Run Dafny" dafny-run-verifier t] - "---" - ["Recolor buffer" font-lock-fontify-buffer t] - "---" - )) - (easy-menu-add dafny-mode-menu)) - -;; commands - -(defun dafny-command-line (file) - (concat "dafny " file)) - -(defun dafny-run-verifier () - "run Dafny verifier" - (interactive) - (let ((f (buffer-name))) - (compile (dafny-command-line f)))) - -;; setting the mode - -(defun dafny-mode () - "Major mode for editing Dafny programs. - -\\{dafny-mode-map}" - (interactive) - (kill-all-local-variables) - (dafny-create-syntax-table) - ; hilight - (make-local-variable 'font-lock-defaults) - (setq font-lock-defaults '(dafny-font-lock-keywords)) - ; indentation - ; (make-local-variable 'indent-line-function) - ; (setq indent-line-function 'dafny-indent-line) - ; menu - ; providing the mode - (setq major-mode 'dafny-mode) - (setq mode-name "Dafny") - (use-local-map dafny-mode-map) - (font-lock-mode 1) - (dafny-menu) - (run-hooks 'dafny-mode-hook)) - -(provide 'dafny-mode) diff --git a/Util/Emacs/jennisys-mode.el b/Util/Emacs/jennisys-mode.el deleted file mode 100644 index d8f20a31..00000000 --- a/Util/Emacs/jennisys-mode.el +++ /dev/null @@ -1,113 +0,0 @@ -;; jennisys-mode.el - GNU Emacs mode for Jennisys -;; Adapted by Rustan Leino from Jean-Christophe FILLIATRE's GNU Emancs mode for Why - -(defvar jennisys-mode-hook nil) - -(defvar jennisys-mode-map nil - "Keymap for Jennisys major mode") - -(if jennisys-mode-map nil - (setq jennisys-mode-map (make-keymap)) - (define-key jennisys-mode-map "\C-c\C-c" 'jennisys-run-boogie) - (define-key jennisys-mode-map [(control return)] 'font-lock-fontify-buffer)) - -(setq auto-mode-alist - (append - '(("\\.jen" . jennisys-mode)) - auto-mode-alist)) - -;; font-lock - -(defun jennisys-regexp-opt (l) - (concat "\\<" (concat (regexp-opt l t) "\\>"))) - -(defconst jennisys-font-lock-keywords-1 - (list - ; comments have the form /* ... */ - '("/\\*\\([^*]\\|\\*[^/]\\)*\\*/" . font-lock-comment-face) - ; or // ... - '("//\\([^ -]\\)*" . font-lock-comment-face) - - `(,(jennisys-regexp-opt '( - "interface" "datamodel" "code" - "var" "constructor" "method" - "frame" "invariant" "returns" "requires" "ensures" - )) . font-lock-builtin-face) - `(,(jennisys-regexp-opt '( - "if" "then" "else" - "forall" "exists" - "this" "in" - "false" "true" "null")) . font-lock-keyword-face) - `(,(jennisys-regexp-opt '("array" "bool" "int" "set" "seq")) . font-lock-type-face) - ) - "Minimal highlighting for Jennisys mode") - -(defvar jennisys-font-lock-keywords jennisys-font-lock-keywords-1 - "Default highlighting for Jennisys mode") - -;; syntax - -(defvar jennisys-mode-syntax-table nil - "Syntax table for jennisys-mode") - -(defun jennisys-create-syntax-table () - (if jennisys-mode-syntax-table - () - (setq jennisys-mode-syntax-table (make-syntax-table)) - (set-syntax-table jennisys-mode-syntax-table) - (modify-syntax-entry ?' "w" jennisys-mode-syntax-table) - (modify-syntax-entry ?_ "w" jennisys-mode-syntax-table))) - -;; menu - -(require 'easymenu) - -(defun jennisys-menu () - (easy-menu-define - jennisys-mode-menu (list jennisys-mode-map) - "Jennisys Mode Menu." - '("Jennisys" - ["Run Boogie" jennisys-run-boogie t] - "---" - ["Recolor buffer" font-lock-fontify-buffer t] - "---" - )) - (easy-menu-add jennisys-mode-menu)) - -;; commands - -(defun jennisys-command-line (file) - (concat "boogie " file)) - -(defun jennisys-run-boogie () - "run Boogie to check the Jennisys program" - (interactive) - (let ((f (buffer-name))) - (compile (jennisys-command-line f)))) - -;; setting the mode - -(defun jennisys-mode () - "Major mode for editing Jennisys programs. - -\\{jennisys-mode-map}" - (interactive) - (kill-all-local-variables) - (jennisys-create-syntax-table) - ; hilight - (make-local-variable 'font-lock-defaults) - (setq font-lock-defaults '(jennisys-font-lock-keywords)) - ; indentation - ; (make-local-variable 'indent-line-function) - ; (setq indent-line-function 'jennisys-indent-line) - ; menu - ; providing the mode - (setq major-mode 'jennisys-mode) - (setq mode-name "Jennisys") - (use-local-map jennisys-mode-map) - (font-lock-mode 1) - (jennisys-menu) - (run-hooks 'jennisys-mode-hook)) - -(provide 'jennisys-mode) -- cgit v1.2.3 From 94e1770b5aa240a807955e2855253c0f3ef895a7 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sun, 23 Aug 2015 04:29:59 -0700 Subject: Write a new packaging script for Dafny This is intended to superseed the PrepareDafnyZip.bat script. I tried doing this as an msbuild config, then as a makefile, but the first one was too cumbersome and the second one required too many dependencies to work properly (and parsing JSON from a makefile is not fun). The new script calls the Github API to retrieve the description of the latest z3 release, downloads the zip corresponding to each supported platfom, and packages one copy of Dafny for each copy of z3 that the latest release contains. The next step will be to make Dafny load its binary from the z3 folder. --- package.py | 198 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 package.py diff --git a/package.py b/package.py new file mode 100644 index 00000000..22ed232c --- /dev/null +++ b/package.py @@ -0,0 +1,198 @@ +import argparse +import json +import os +from os import path +import re +import urllib.request +import sys +import zipfile +import subprocess +import shutil + +# Configuration + +RELEASES_URL = "https://api.github.com/repos/Z3Prover/z3/releases/latest" +RELEASE_REGEXP = r"^(?Pz3-[0-9\.]+-(?Px86|x64)-(?P[a-z0-9\.\-]+)).zip$" + +SOURCE_DIRECTORY = "Source" +BINARIES_DIRECTORY = "Binaries" +DESTINATION_DIRECTORY = "Package" + +Z3_ARCHIVE_PREFIX = path.join("z3", "bin") + +DLLs = ["AbsInt", + "Basetypes", + "CodeContractsExtender", + "Concurrency", + "Core", + "DafnyPipeline", + "Doomed", + "ExecutionEngine", + "Graph", + "Houdini", + "Model", + "ModelViewer", + "ParserHelper", + "Provers.SMTLib", + "VCExpr", + "VCGeneration"] +EXEs = ["Dafny", "DafnyServer"] +ETCs = ["dafny", "DafnyPrelude.bpl", "DafnyRuntime.cs"] + +# Constants + +THIS_FILE = path.realpath(__file__) +ROOT_DIRECTORY = path.dirname(THIS_FILE) +SOURCE_DIRECTORY = path.join(ROOT_DIRECTORY, SOURCE_DIRECTORY) +BINARIES_DIRECTORY = path.join(ROOT_DIRECTORY, BINARIES_DIRECTORY) +DESTINATION_DIRECTORY = path.join(ROOT_DIRECTORY, DESTINATION_DIRECTORY) +CACHE_DIRECTORY = path.join(DESTINATION_DIRECTORY, "cache") + +RELEASE_REGEXP = re.compile(RELEASE_REGEXP, re.IGNORECASE) + +MONO = sys.platform not in ("win32", "cygwin") +DLL_PDB_EXT = ".dll.mdb" if MONO else ".pdb" +EXE_PDB_EXT = ".exe.mdb" if MONO else ".pdb" +ARCHIVE_FNAMES = ([dll + ".dll" for dll in DLLs] + [dll + DLL_PDB_EXT for dll in DLLs] + + [exe + ".exe" for exe in EXEs] + [exe + EXE_PDB_EXT for exe in EXEs] + + ETCs) + +# Code + +def flush(*args, **kwargs): + print(*args, **kwargs) + sys.stdout.flush() + +class Release: + @staticmethod + def parse_zip_name(name): + m = RELEASE_REGEXP.match(name) + if not m: + raise Exception("{} does not match RELEASE_REGEXP".format(name)) + return m.group('platform'), m.group('os'), m.group("directory") + + def __init__(self, js, version): + self.z3_name = js["name"] + self.size = js["size"] + self.url = js["browser_download_url"] + self.platform, self.os, self.directory = Release.parse_zip_name(js["name"]) + self.z3_zip = path.join(CACHE_DIRECTORY, self.z3_name) + self.z3_directory = path.join(CACHE_DIRECTORY, self.directory) + self.z3_bin_directory = path.join(self.z3_directory, "bin") + self.dafny_name = "dafny-{}-{}-{}.zip".format(version, self.platform, self.os) + self.dafny_zip = path.join(DESTINATION_DIRECTORY, self.dafny_name) + + @property + def cached(self): + return path.exists(self.z3_zip) and path.getsize(self.z3_zip) == self.size + + @property + def MB(self): + return self.size / 1e6 + + def download(self): + if self.cached: + print("cached!") + else: + flush("downloading {:.2f}MB...".format(self.MB), end=' ') + with urllib.request.urlopen(self.url) as reader: + with open(self.z3_zip, mode="wb") as writer: + writer.write(reader.read()) + flush("done!") + + def unpack(self): + shutil.rmtree(self.z3_directory) + with zipfile.ZipFile(self.z3_zip) as archive: + archive.extractall(CACHE_DIRECTORY) + flush("done!") + + def pack(self): + try: + os.remove(self.dafny_zip) + except FileNotFoundError: + pass + missing = [] + with zipfile.ZipFile(self.dafny_zip, 'w', zipfile.ZIP_DEFLATED) as archive: + for root, _, files in os.walk(self.z3_bin_directory): + for f in files: + fpath = path.join(root, f) + arcpath = path.join(Z3_ARCHIVE_PREFIX, path.relpath(fpath, self.z3_bin_directory)) + archive.write(fpath, arcpath) + for fname in ARCHIVE_FNAMES: + fpath = path.join(BINARIES_DIRECTORY, fname) + if path.exists(fpath): + archive.write(fpath, fname) + else: + missing.append(fname) + flush("done!") + if missing: + flush(" WARNING: Not all files were found: {} were missing".format(", ".join(missing))) + +def discover(version): + flush(" - Getting information about latest release") + with urllib.request.urlopen("https://api.github.com/repos/Z3Prover/z3/releases/latest") as reader: + js = json.loads(reader.read().decode("utf-8")) + + for release_js in js["assets"]: + release = Release(release_js, version) + if release.platform == "x64": + flush(" + Selecting {} ({:.2f}MB, {})".format(release.z3_name, release.MB, release.size)) + yield release + else: + flush(" + Rejecting {}".format(release.z3_name)) + +def download(releases): + flush(" - Downloading {} z3 archives".format(len(releases))) + for release in releases: + flush(" + {}:".format(release.z3_name), end=' ') + release.download() + +def unpack(releases): + flush(" - Unpacking {} z3 archives".format(len(releases))) + for release in releases: + flush(" + {}:".format(release.z3_name), end=' ') + release.unpack() + +def run(cmd): + flush(" + {}...".format(" ".join(cmd)), end=' ') + retv = subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + if retv != 0: + flush("failed!") + sys.exit(1) + else: + flush("done!") + +def build(): + os.chdir(ROOT_DIRECTORY) + flush(" - Building") + builder = "xbuild" if MONO else "xbuild" + run([builder, "Source/Dafny.sln", "/t:Clean"]) + run([builder, "Source/Dafny.sln", "/p:Configuration=Checked", "/t:Rebuild"]) + +def pack(releases): + flush(" - Packaging {} Dafny archives".format(len(releases))) + for release in releases: + flush(" + {}:".format(release.dafny_name), end=' ') + release.pack() + +def parse_arguments(): + parser = argparse.ArgumentParser(description="Prepare a Dafny release. Configuration is hardcoded; edit the `# Configuration' section of this script to change it.") + parser.add_argument("version", help="Version number for this release") + return parser.parse_args() + +def main(): + args = parse_arguments() + os.makedirs(CACHE_DIRECTORY, exist_ok=True) + + # Z3 + flush("* Finding, downloading, and unpacking Z3 releases") + releases = list(discover(args.version)) + download(releases) + unpack(releases) + + flush("* Building and packaging Dafny") + build() + pack(releases) + +if __name__ == '__main__': + main() -- cgit v1.2.3 From 7cd998145a00c54a25cc46a82f034769b281c4c1 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sun, 23 Aug 2015 14:09:00 -0700 Subject: s/loops with/may loop with/ --- Source/Dafny/Triggers/QuantifiersCollection.cs | 2 +- Test/triggers/loop-detection-is-not-too-strict.dfy.expect | 2 +- .../triggers/loop-detection-looks-at-ranges-too.dfy.expect | 4 ++-- .../loop-detection-messages--unit-tests.dfy.expect | 12 ++++++------ .../looping-is-hard-to-decide-modulo-equality.dfy.expect | 4 ++-- Test/triggers/matrix-accesses-are-triggers.dfy.expect | 2 +- .../triggers/old-is-a-special-case-for-triggers.dfy.expect | 10 +++++----- ...rms-do-not-look-like-the-triggers-they-match.dfy.expect | 6 +++--- .../splitting-triggers-recovers-expressivity.dfy.expect | 14 +++++++------- Test/triggers/useless-triggers-are-removed.dfy.expect | 4 ++-- 10 files changed, 30 insertions(+), 30 deletions(-) diff --git a/Source/Dafny/Triggers/QuantifiersCollection.cs b/Source/Dafny/Triggers/QuantifiersCollection.cs index 0ef59bad..6555b52a 100644 --- a/Source/Dafny/Triggers/QuantifiersCollection.cs +++ b/Source/Dafny/Triggers/QuantifiersCollection.cs @@ -119,7 +119,7 @@ namespace Microsoft.Dafny.Triggers { (candidate, loopingSubterms) => !loopingSubterms.Any(), (candidate, loopingSubterms) => { looping.Add(candidate); - candidate.Annotation = "loops with " + loopingSubterms.MapConcat(t => "{" + Printer.ExprToString(t.OriginalExpr) + "}", ", "); + candidate.Annotation = "may loop with " + loopingSubterms.MapConcat(t => "{" + Printer.ExprToString(t.OriginalExpr) + "}", ", "); }).ToList(); q.CouldSuppressLoops = safe.Count > 0; diff --git a/Test/triggers/loop-detection-is-not-too-strict.dfy.expect b/Test/triggers/loop-detection-is-not-too-strict.dfy.expect index 270f7e57..6f5ed3d7 100644 --- a/Test/triggers/loop-detection-is-not-too-strict.dfy.expect +++ b/Test/triggers/loop-detection-is-not-too-strict.dfy.expect @@ -2,7 +2,7 @@ loop-detection-is-not-too-strict.dfy(14,9): Info: Selected triggers: {P(y, x)}, {P(x, y)} loop-detection-is-not-too-strict.dfy(17,9): Info: Selected triggers: {P(y, x)}, {P(x, y)} -loop-detection-is-not-too-strict.dfy(20,9): Warning: Selected triggers: {P(x, y)} (loops with {P(x, y + 1)}) +loop-detection-is-not-too-strict.dfy(20,9): Warning: Selected triggers: {P(x, y)} (may loop with {P(x, y + 1)}) (!) Suppressing loops would leave this expression without triggers. Dafny program verifier finished with 3 verified, 0 errors diff --git a/Test/triggers/loop-detection-looks-at-ranges-too.dfy.expect b/Test/triggers/loop-detection-looks-at-ranges-too.dfy.expect index 72482de5..ce62d868 100644 --- a/Test/triggers/loop-detection-looks-at-ranges-too.dfy.expect +++ b/Test/triggers/loop-detection-looks-at-ranges-too.dfy.expect @@ -1,6 +1,6 @@ -loop-detection-looks-at-ranges-too.dfy(11,17): Warning: Selected triggers: {P(x)} (loops with {P(x + 1)}) +loop-detection-looks-at-ranges-too.dfy(11,17): Warning: Selected triggers: {P(x)} (may loop with {P(x + 1)}) (!) Suppressing loops would leave this expression without triggers. -loop-detection-looks-at-ranges-too.dfy(13,17): Warning: Selected triggers: {P(x)} (loops with {P(x + 1)}) +loop-detection-looks-at-ranges-too.dfy(13,17): Warning: Selected triggers: {P(x)} (may loop with {P(x + 1)}) (!) Suppressing loops would leave this expression without triggers. Dafny program verifier finished with 3 verified, 0 errors diff --git a/Test/triggers/loop-detection-messages--unit-tests.dfy.expect b/Test/triggers/loop-detection-messages--unit-tests.dfy.expect index 89d7265c..804a0116 100644 --- a/Test/triggers/loop-detection-messages--unit-tests.dfy.expect +++ b/Test/triggers/loop-detection-messages--unit-tests.dfy.expect @@ -1,21 +1,21 @@ loop-detection-messages--unit-tests.dfy(11,9): Info: Selected triggers: {f(f(i))} - Rejected triggers: {f(i)} (loops with {f(f(i))}) -loop-detection-messages--unit-tests.dfy(12,9): Warning: Selected triggers: {f(i)} (loops with {f(i + 1)}) + Rejected triggers: {f(i)} (may loop with {f(f(i))}) +loop-detection-messages--unit-tests.dfy(12,9): Warning: Selected triggers: {f(i)} (may loop with {f(i + 1)}) (!) Suppressing loops would leave this expression without triggers. -loop-detection-messages--unit-tests.dfy(13,9): Info: Selected triggers: {f(i)} (loops with {f(i + 1)}) +loop-detection-messages--unit-tests.dfy(13,9): Info: Selected triggers: {f(i)} (may loop with {f(i + 1)}) loop-detection-messages--unit-tests.dfy(15,9): Info: For expression {false ==> f(i) == f(i + 1)}: Selected triggers: {g(i)} - Rejected triggers: {f(i)} (loops with {f(i + 1)}) + Rejected triggers: {f(i)} (may loop with {f(i + 1)}) loop-detection-messages--unit-tests.dfy(15,9): Info: For expression {false ==> f(i) == g(i)}: Selected triggers: {g(i)}, {f(i)} loop-detection-messages--unit-tests.dfy(16,9): Warning: For expression {false ==> f(i) == f(i + 1)}: - Selected triggers: {f(i)} (loops with {f(i + 1)}) + Selected triggers: {f(i)} (may loop with {f(i + 1)}) (!) Suppressing loops would leave this expression without triggers. loop-detection-messages--unit-tests.dfy(16,9): Info: For expression {false ==> f(i) == f(i)}: Selected triggers: {f(i)} loop-detection-messages--unit-tests.dfy(17,9): Info: For expression {false ==> f(i) == f(i + 1)}: - Selected triggers: {f(i)} (loops with {f(i + 1)}) + Selected triggers: {f(i)} (may loop with {f(i + 1)}) loop-detection-messages--unit-tests.dfy(17,9): Info: For expression {false ==> f(i) == f(i)}: Selected triggers: {f(i)} loop-detection-messages--unit-tests.dfy(19,9): Info: Selected triggers: {f(i)} diff --git a/Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy.expect b/Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy.expect index c0eebdba..58094110 100644 --- a/Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy.expect +++ b/Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy.expect @@ -1,10 +1,10 @@ looping-is-hard-to-decide-modulo-equality.dfy(21,9): Info: Selected triggers: {h(h(c, j), j)}, {h(c, i)}, {c in sc} - Rejected triggers: {h(c, j)} (loops with {h(h(c, j), j)}) + Rejected triggers: {h(c, j)} (may loop with {h(h(c, j), j)}) looping-is-hard-to-decide-modulo-equality.dfy(26,9): Info: Selected triggers: {f(x)} Rejected triggers: {g(f(x))} (more specific than {f(x)}) looping-is-hard-to-decide-modulo-equality.dfy(31,9): Info: Selected triggers: {old(f(f(c)))}, {f(c)}, {c in sc} - Rejected triggers: {old(f(c))} (loops with {old(f(f(c)))}) + Rejected triggers: {old(f(c))} (may loop with {old(f(f(c)))}) Dafny program verifier finished with 9 verified, 0 errors diff --git a/Test/triggers/matrix-accesses-are-triggers.dfy.expect b/Test/triggers/matrix-accesses-are-triggers.dfy.expect index b9dd1d88..35df02f5 100644 --- a/Test/triggers/matrix-accesses-are-triggers.dfy.expect +++ b/Test/triggers/matrix-accesses-are-triggers.dfy.expect @@ -1,4 +1,4 @@ -matrix-accesses-are-triggers.dfy(8,11): Warning: Selected triggers: {m[i, j]} (loops with {m[j, i + 1]}) +matrix-accesses-are-triggers.dfy(8,11): Warning: Selected triggers: {m[i, j]} (may loop with {m[j, i + 1]}) (!) Suppressing loops would leave this expression without triggers. matrix-accesses-are-triggers.dfy(8,81): Error: index 0 out of range Execution trace: diff --git a/Test/triggers/old-is-a-special-case-for-triggers.dfy.expect b/Test/triggers/old-is-a-special-case-for-triggers.dfy.expect index 23ec089d..313d7c16 100644 --- a/Test/triggers/old-is-a-special-case-for-triggers.dfy.expect +++ b/Test/triggers/old-is-a-special-case-for-triggers.dfy.expect @@ -2,14 +2,14 @@ old-is-a-special-case-for-triggers.dfy(15,10): Info: Selected triggers: {old(f(c))}, {c in sc} old-is-a-special-case-for-triggers.dfy(20,10): Info: Selected triggers: {old(f(f(c)))}, {f(c)}, {c in sc} - Rejected triggers: {old(f(c))} (loops with {old(f(f(c)))}) + Rejected triggers: {old(f(c))} (may loop with {old(f(f(c)))}) old-is-a-special-case-for-triggers.dfy(21,10): Info: Selected triggers: {f(g(c))}, {g(f(c))}, {old(f(g(c)))}, {old(g(f(c)))}, {old(f(f(c)))}, {c in sc} Rejected triggers: - {g(c)} (loops with {g(f(c))}) - {f(c)} (loops with {f(g(c))}) - {old(g(c))} (loops with {old(g(f(c)))}) - {old(f(c))} (loops with {old(f(f(c)))}, {old(f(g(c)))}) + {g(c)} (may loop with {g(f(c))}) + {f(c)} (may loop with {f(g(c))}) + {old(g(c))} (may loop with {old(g(f(c)))}) + {old(f(c))} (may loop with {old(f(f(c)))}, {old(f(g(c)))}) old-is-a-special-case-for-triggers.dfy(25,10): Info: Selected triggers: {old(f(c))}, {f(c)}, {c in sc} Rejected triggers: {old(g(f(c)))} (more specific than {old(f(c))}) diff --git a/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect b/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect index 83648626..f5e47454 100644 --- a/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect +++ b/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect @@ -1,10 +1,10 @@ -some-terms-do-not-look-like-the-triggers-they-match.dfy(12,17): Warning: Selected triggers: {s[x]} (loops with {s[x + 1]}) +some-terms-do-not-look-like-the-triggers-they-match.dfy(12,17): Warning: Selected triggers: {s[x]} (may loop with {s[x + 1]}) (!) Suppressing loops would leave this expression without triggers. some-terms-do-not-look-like-the-triggers-they-match.dfy(15,17): Warning: For expression {x in s ==> s[x + 1] > 0}: - Selected triggers: {s[x]} (loops with {s[x + 1]}) + Selected triggers: {s[x]} (may loop with {s[x + 1]}) (!) Suppressing loops would leave this expression without triggers. some-terms-do-not-look-like-the-triggers-they-match.dfy(15,17): Warning: For expression {x in s ==> x + 2 !in s}: - Selected triggers: {s[x]} (loops with {x + 2 !in s}) + Selected triggers: {s[x]} (may loop with {x + 2 !in s}) (!) Suppressing loops would leave this expression without triggers. Dafny program verifier finished with 2 verified, 0 errors diff --git a/Test/triggers/splitting-triggers-recovers-expressivity.dfy.expect b/Test/triggers/splitting-triggers-recovers-expressivity.dfy.expect index 63cbc476..58c8dd2b 100644 --- a/Test/triggers/splitting-triggers-recovers-expressivity.dfy.expect +++ b/Test/triggers/splitting-triggers-recovers-expressivity.dfy.expect @@ -1,11 +1,11 @@ splitting-triggers-recovers-expressivity.dfy(12,10): Info: Selected triggers: {Q(i)} - Rejected triggers: {P(i)} (loops with {P(i + 1)}) + Rejected triggers: {P(i)} (may loop with {P(i + 1)}) splitting-triggers-recovers-expressivity.dfy(17,11): Info: Selected triggers: {Q(j)} - Rejected triggers: {P(j)} (loops with {P(j + 1)}) + Rejected triggers: {P(j)} (may loop with {P(j + 1)}) splitting-triggers-recovers-expressivity.dfy(26,10): Info: Selected triggers: {Q(i)} - Rejected triggers: {P(i)} (loops with {P(i + 1)}) + Rejected triggers: {P(i)} (may loop with {P(i + 1)}) splitting-triggers-recovers-expressivity.dfy(33,11): Info: Selected triggers: {Q(j)} - Rejected triggers: {P(j)} (loops with {P(j + 1)}) + Rejected triggers: {P(j)} (may loop with {P(j + 1)}) splitting-triggers-recovers-expressivity.dfy(44,10): Info: For expression {P(i)}: Selected triggers: {Q(i)}, {P(i)} @@ -14,19 +14,19 @@ splitting-triggers-recovers-expressivity.dfy(44,10): Info: For expression {!Q(i) {Q(i)}, {P(i)} splitting-triggers-recovers-expressivity.dfy(44,10): Info: For expression {P(i + 1)}: Selected triggers: {Q(i)} - Rejected triggers: {P(i)} (loops with {P(i + 1)}) + Rejected triggers: {P(i)} (may loop with {P(i + 1)}) splitting-triggers-recovers-expressivity.dfy(49,11): Info: For expression {j >= 0 ==> P(j)}: Selected triggers: {Q(j)}, {P(j)} splitting-triggers-recovers-expressivity.dfy(49,11): Info: For expression {j >= 0 ==> Q(j) ==> P(j + 1)}: Selected triggers: {Q(j)} - Rejected triggers: {P(j)} (loops with {P(j + 1)}) + Rejected triggers: {P(j)} (may loop with {P(j + 1)}) splitting-triggers-recovers-expressivity.dfy(58,11): Info: For expression {i >= 0 ==> Q(i)}: Selected triggers: {P(i)}, {Q(i)} splitting-triggers-recovers-expressivity.dfy(58,11): Info: For expression {i >= 0 ==> P(i) ==> P(i + 1)}: Selected triggers: - {P(i)} (loops with {P(i + 1)}), {Q(i)} + {P(i)} (may loop with {P(i + 1)}), {Q(i)} splitting-triggers-recovers-expressivity.dfy(12,63): Error BP5003: A postcondition might not hold on this return path. splitting-triggers-recovers-expressivity.dfy(12,10): Related location: This is the postcondition that might not hold. Execution trace: diff --git a/Test/triggers/useless-triggers-are-removed.dfy.expect b/Test/triggers/useless-triggers-are-removed.dfy.expect index a526e517..2e9bbedf 100644 --- a/Test/triggers/useless-triggers-are-removed.dfy.expect +++ b/Test/triggers/useless-triggers-are-removed.dfy.expect @@ -5,11 +5,11 @@ useless-triggers-are-removed.dfy(16,11): Info: Selected triggers: {f(x)} {h(f(x))} (more specific than {f(x)}) {g(f(x))} (more specific than {f(x)}) useless-triggers-are-removed.dfy(20,11): Info: Selected triggers: {f(f(x))} - Rejected triggers: {f(x)} (loops with {f(f(x))}) + Rejected triggers: {f(x)} (may loop with {f(f(x))}) useless-triggers-are-removed.dfy(23,11): Info: Selected triggers: {g(f(x)), g(y)}, {f(y), f(x)} Rejected triggers: - {g(y), f(x)} (loops with {g(f(y))}, {g(f(x))}) + {g(y), f(x)} (may loop with {g(f(y))}, {g(f(x))}) {g(f(x)), g(f(y))} (more specific than {g(f(x)), f(y)}, {g(f(y)), f(x)}, {f(y), f(x)}) {g(f(x)), f(y)} (more specific than {f(y), f(x)}) {g(f(y)), f(x)} (more specific than {f(y), f(x)}) -- cgit v1.2.3 From aa13b513cd70fd39ae9eb9ddc2621fb8747f89ff Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Sun, 23 Aug 2015 14:09:29 -0700 Subject: Add change missing from bd47e3cdb79c --- Test/dafny0/snapshots/Snapshots5.run.dfy.expect | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Test/dafny0/snapshots/Snapshots5.run.dfy.expect b/Test/dafny0/snapshots/Snapshots5.run.dfy.expect index 2ad73416..8148a8cf 100644 --- a/Test/dafny0/snapshots/Snapshots5.run.dfy.expect +++ b/Test/dafny0/snapshots/Snapshots5.run.dfy.expect @@ -18,9 +18,9 @@ Processing command (at Snapshots5.v1.dfy(12,4)) assert (forall $o: ref, $ >>> MarkAsFullyVerified Processing command (at Snapshots5.v1.dfy(13,38)) assert (forall b#3: bool :: true ==> b#3 || !b#3) || 3 != 3; >>> MarkAsFullyVerified -Processing command (at Snapshots5.v1.dfy(20,40)) assert (exists b#5: bool :: b#5 || !b#5) || 4 != 4; +Processing command (at Snapshots5.v1.dfy(20,37)) assert (exists b#5: bool :: Lit(true)) || 4 != 4; >>> DoNothingToAssert -Processing command (at Snapshots5.v1.dfy(22,38)) assert (exists b#7: bool :: b#7 || !b#7) || 5 != 5; +Processing command (at Snapshots5.v1.dfy(22,35)) assert (exists b#7: bool :: Lit(true)) || 5 != 5; >>> DoNothingToAssert Dafny program verifier finished with 3 verified, 0 errors -- cgit v1.2.3 From 9af54a6c618dd71bac8b00822a434db830efdc4d Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Mon, 24 Aug 2015 03:14:08 -0700 Subject: Add the Package folder to .hgignore --- .hgignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgignore b/.hgignore index de39514d..0c0fdbfd 100644 --- a/.hgignore +++ b/.hgignore @@ -8,6 +8,7 @@ Test/desktop/.* Test/([^/]*)/([^/]*)\.sx ^Test/sandbox/.* ^Test/.*\.csv +Package/.* syntax: glob *.exe *.pdb -- cgit v1.2.3 From c55cb226f4de90b080aa809d883d52c3386db063 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Wed, 26 Aug 2015 18:36:34 -0700 Subject: Improve the redundancy detection algorithm used while constructing sets of terms Based on an issue noted by Chris with redundancy removal resuls being dependent on the order of the terms. Interestingly, one of our tests already had an instance of that problem. Also fix the issue with nested quantifiers getting redundant triggers. --- Source/Dafny/DafnyAst.cs | 1 + Source/Dafny/Triggers/QuantifiersCollection.cs | 17 ++-- Source/Dafny/Triggers/TriggerUtils.cs | 100 ++++++++++++++++++--- Source/Dafny/Triggers/TriggersCollector.cs | 5 ++ .../function-applications-are-triggers.dfy.expect | 2 +- .../redundancy-detection-is-bidirectional.dfy | 29 ++++++ ...edundancy-detection-is-bidirectional.dfy.expect | 12 +++ 7 files changed, 143 insertions(+), 23 deletions(-) create mode 100644 Test/triggers/redundancy-detection-is-bidirectional.dfy create mode 100644 Test/triggers/redundancy-detection-is-bidirectional.dfy.expect diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index c7aae173..9bff2038 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -2965,6 +2965,7 @@ namespace Microsoft.Dafny { } } + [DebuggerDisplay("Bound<{name}>")] public class BoundVar : NonglobalVariable { public override bool IsMutable { get { diff --git a/Source/Dafny/Triggers/QuantifiersCollection.cs b/Source/Dafny/Triggers/QuantifiersCollection.cs index 6555b52a..114d5f5d 100644 --- a/Source/Dafny/Triggers/QuantifiersCollection.cs +++ b/Source/Dafny/Triggers/QuantifiersCollection.cs @@ -47,15 +47,9 @@ namespace Microsoft.Dafny.Triggers { SelectTriggers(); } - private bool SubsetGenerationPredicate(List terms, TriggerTerm additionalTerm) { - // Simple formulas like [P0(i) && P1(i) && P2(i) && P3(i) && P4(i)] yield very - // large numbers of multi-triggers, most of which are useless. This filter - // restricts subsets of terms so that we only generate sets of terms where each - // element contributes at least one variable. In the example above, we'll only - // get 5 triggers. - // Note that this may still be an over-approximation, as in the following example: - // forall a, b :: forall x :: a[x] || b[x] > 0. - return additionalTerm.Variables.Where(v => v is BoundVar && !terms.Any(t => t.Variables.Contains(v))).Any(); + private bool SubsetGenerationPredicate(TriggerUtils.SetOfTerms terms, TriggerTerm additionalTerm) { + return true; // FIXME Remove this + //return additionalTerm.Variables.Where(v => v is BoundVar && !terms.Any(t => t.Variables.Contains(v))).Any(); } /// @@ -68,11 +62,10 @@ namespace Microsoft.Dafny.Triggers { void CollectAndShareTriggers(TriggersCollector triggersCollector) { var pool = quantifiers.SelectMany(q => triggersCollector.CollectTriggers(q.quantifier)); var distinctPool = pool.Deduplicate(TriggerTerm.Eq); - var multiPool = TriggerUtils.AllNonEmptySubsets(distinctPool, SubsetGenerationPredicate).Select(candidates => new TriggerCandidate(candidates)).ToList(); foreach (var q in quantifiers) { - q.CandidateTerms = distinctPool; //Candidate terms are immutable: no copy needed - q.Candidates = multiPool.Select(candidate => new TriggerCandidate(candidate)).ToList(); + q.CandidateTerms = distinctPool; // The list of candidate terms is immutable + q.Candidates = TriggerUtils.AllNonEmptySubsets(distinctPool, SubsetGenerationPredicate, q.quantifier.BoundVars).Select(set => set.ToTriggerCandidate()).ToList(); } } diff --git a/Source/Dafny/Triggers/TriggerUtils.cs b/Source/Dafny/Triggers/TriggerUtils.cs index 4fd139e2..efa1f167 100644 --- a/Source/Dafny/Triggers/TriggerUtils.cs +++ b/Source/Dafny/Triggers/TriggerUtils.cs @@ -15,29 +15,109 @@ namespace Microsoft.Dafny.Triggers { return copy; } - internal static IEnumerable> AllSubsets(IList source, Func, T, bool> predicate, int offset) { + internal class SetOfTerms { + internal bool IsRedundant { get; private set; } + internal List Terms { get; set; } + + private ISet variables; + private Dictionary termOwningAUniqueVar; + private Dictionary> uniqueVarsOwnedByATerm; + + public int Count { get { return Terms.Count; } } + + private SetOfTerms() { } + + internal TriggerCandidate ToTriggerCandidate() { + return new TriggerCandidate(Terms); + } + + internal static SetOfTerms Empty() { + var newSet = new SetOfTerms(); + newSet.IsRedundant = false; + newSet.Terms = new List(); + newSet.variables = new HashSet(); + newSet.termOwningAUniqueVar = new Dictionary(); + newSet.uniqueVarsOwnedByATerm = new Dictionary>(); + return newSet; + } + + /// + /// Simple formulas like [P0(i) && P1(i) && P2(i) && P3(i) && P4(i)] yield very + /// large numbers of multi-triggers, most of which are useless. As it copies its + /// argument, this method updates various datastructures that allow it to + /// efficiently track ownership relations between formulae and bound variables, + /// and sets the IsRedundant flag of the returned set, allowing the caller to + /// discard that set of terms, and the ones that would have been built on top of + /// it, thus ensuring that the only sets of terms produced in the end are sets + /// of terms in which each element contributes (owns) at least one variable. + /// + /// Note that this is trickier than just checking every term for new variables; + /// indeed, a new term that does bring new variables in can make an existing + /// term redundant (see redundancy-detection-does-its-job-properly.dfy). + /// + internal SetOfTerms CopyWithAdd(TriggerTerm term, ISet relevantVariables) { + var copy = new SetOfTerms(); + copy.Terms = new List(Terms); + copy.variables = new HashSet(variables); + copy.termOwningAUniqueVar = new Dictionary(termOwningAUniqueVar); + copy.uniqueVarsOwnedByATerm = new Dictionary>(uniqueVarsOwnedByATerm); + + copy.Terms.Add(term); + + var varsInNewTerm = new HashSet(term.BoundVars); + varsInNewTerm.IntersectWith(relevantVariables); + + var ownedByNewTerm = new HashSet(); + + // Check #0: does this term bring anything new? + copy.IsRedundant = IsRedundant || varsInNewTerm.All(bv => copy.variables.Contains(bv)); + copy.variables.UnionWith(varsInNewTerm); + + // Check #1: does this term claiming ownership of all its variables cause another term to become useless? + foreach (var v in varsInNewTerm) { + TriggerTerm originalOwner; + if (copy.termOwningAUniqueVar.TryGetValue(v, out originalOwner)) { + var alsoOwnedByOriginalOwner = copy.uniqueVarsOwnedByATerm[originalOwner]; + alsoOwnedByOriginalOwner.Remove(v); + if (!alsoOwnedByOriginalOwner.Any()) { + copy.IsRedundant = true; + } + } else { + ownedByNewTerm.Add(v); + copy.termOwningAUniqueVar[v] = term; + } + } + + // Actually claim ownership + copy.uniqueVarsOwnedByATerm[term] = ownedByNewTerm; + + return copy; + } + } + + internal static IEnumerable AllSubsets(IList source, Func predicate, int offset, ISet relevantVariables) { if (offset >= source.Count) { - yield return new List(); + yield return SetOfTerms.Empty(); yield break; } - foreach (var subset in AllSubsets(source, predicate, offset + 1)) { - if (predicate(subset, source[offset])) { - yield return CopyAndAdd(subset, source[offset]); + foreach (var subset in AllSubsets(source, predicate, offset + 1, relevantVariables)) { + var newSet = subset.CopyWithAdd(source[offset], relevantVariables); + if (!newSet.IsRedundant && predicate(subset, source[offset])) { // Fixme remove the predicate? + yield return newSet; } - yield return new List(subset); + yield return subset; } } - internal static IEnumerable> AllNonEmptySubsets(IEnumerable source, Func, T, bool> predicate) { - List all = new List(source); - foreach (var subset in AllSubsets(all, predicate, 0)) { + internal static IEnumerable AllNonEmptySubsets(IList source, Func predicate, IEnumerable relevantVariables) { + foreach (var subset in AllSubsets(source, predicate, 0, new HashSet(relevantVariables))) { if (subset.Count > 0) { yield return subset; } } } - + internal static List MergeAlterFirst(List a, List b) { Contract.Requires(a != null); Contract.Requires(b != null); diff --git a/Source/Dafny/Triggers/TriggersCollector.cs b/Source/Dafny/Triggers/TriggersCollector.cs index 69c47d90..d78bcb9e 100644 --- a/Source/Dafny/Triggers/TriggersCollector.cs +++ b/Source/Dafny/Triggers/TriggersCollector.cs @@ -11,6 +11,11 @@ namespace Microsoft.Dafny.Triggers { internal Expression Expr { get; set; } internal Expression OriginalExpr { get; set; } internal ISet Variables { get; set; } + internal IEnumerable BoundVars { + get { + return Variables.Select(v => v as BoundVar).Where(v => v != null); + } + } public override string ToString() { return Printer.ExprToString(Expr); diff --git a/Test/triggers/function-applications-are-triggers.dfy.expect b/Test/triggers/function-applications-are-triggers.dfy.expect index 7922e88d..501cfa51 100644 --- a/Test/triggers/function-applications-are-triggers.dfy.expect +++ b/Test/triggers/function-applications-are-triggers.dfy.expect @@ -8,6 +8,6 @@ function-applications-are-triggers.dfy(10,9): Info: For expression {P(f) ==> f(1 function-applications-are-triggers.dfy(11,9): Info: Selected triggers: {f(10)}, {f.requires(10)} function-applications-are-triggers.dfy(12,5): Info: Selected triggers: - {g(x), f(x)}, {g(x), f.requires(x)}, {g(x)}, {f(x)}, {g.requires(x), f.requires(x)}, {g.requires(x)}, {f.requires(x)} + {g(x)}, {f(x)}, {g.requires(x)}, {f.requires(x)} Dafny program verifier finished with 2 verified, 0 errors diff --git a/Test/triggers/redundancy-detection-is-bidirectional.dfy b/Test/triggers/redundancy-detection-is-bidirectional.dfy new file mode 100644 index 00000000..df1d78c3 --- /dev/null +++ b/Test/triggers/redundancy-detection-is-bidirectional.dfy @@ -0,0 +1,29 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This tests checks for tricky cases of redundancy suppression when building +// triggers. + +predicate P(x: int, y: int) +predicate Q(x: int) +predicate R(x: int) + +method M() { + // For this term, it is enough to order the terms by number of variables + assert forall x, y :: true || P(x, y) || Q(y) || R(x); + assert forall x, y :: true || Q(y) || P(x, y) || R(x); + assert forall x, y :: true || Q(y) || R(x) || P(x, y); +} + +predicate PP(x: int, y: int, z: int) +predicate QQ(x: int, y: int) +predicate RR(x: int, y: int) +predicate SS(x: int, y: int) + +method MM() { + // Not for this one, though + assert forall x, y, z, u, v, w :: true || PP(x, y, z) || QQ(x, u) || RR(y, v) || SS(z, w); + assert forall x, y, z, u, v, w :: true || QQ(x, u) || PP(x, y, z) || RR(y, v) || SS(z, w); + assert forall x, y, z, u, v, w :: true || QQ(x, u) || RR(y, v) || PP(x, y, z) || SS(z, w); + assert forall x, y, z, u, v, w :: true || QQ(x, u) || RR(y, v) || SS(z, w) || PP(x, y, z); +} diff --git a/Test/triggers/redundancy-detection-is-bidirectional.dfy.expect b/Test/triggers/redundancy-detection-is-bidirectional.dfy.expect new file mode 100644 index 00000000..78c9e7ca --- /dev/null +++ b/Test/triggers/redundancy-detection-is-bidirectional.dfy.expect @@ -0,0 +1,12 @@ +redundancy-detection-is-bidirectional.dfy(13,9): Info: Selected triggers: + {R(x), Q(y)}, {P(x, y)} +redundancy-detection-is-bidirectional.dfy(14,9): Info: Selected triggers: + {R(x), Q(y)}, {P(x, y)} +redundancy-detection-is-bidirectional.dfy(15,9): Info: Selected triggers: + {P(x, y)}, {R(x), Q(y)} +redundancy-detection-is-bidirectional.dfy(25,9): Info: Selected triggers: {SS(z, w), RR(y, v), QQ(x, u)} +redundancy-detection-is-bidirectional.dfy(26,9): Info: Selected triggers: {SS(z, w), RR(y, v), QQ(x, u)} +redundancy-detection-is-bidirectional.dfy(27,9): Info: Selected triggers: {SS(z, w), RR(y, v), QQ(x, u)} +redundancy-detection-is-bidirectional.dfy(28,9): Info: Selected triggers: {SS(z, w), RR(y, v), QQ(x, u)} + +Dafny program verifier finished with 11 verified, 0 errors -- cgit v1.2.3 From 7a993f6c87eaa82f383b1c5e7411f1878d4edf30 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 27 Aug 2015 16:10:25 -0700 Subject: Small fix: there is no loop in (forall x :: Q(x) && Q(0)) Again, spotted by Chris :) --- Source/Dafny/Triggers/QuantifiersCollection.cs | 4 +++- Source/Dafny/Triggers/TriggerExtensions.cs | 8 ++++---- Test/triggers/redundancy-detection-is-bidirectional.dfy | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Source/Dafny/Triggers/QuantifiersCollection.cs b/Source/Dafny/Triggers/QuantifiersCollection.cs index 114d5f5d..b77afccb 100644 --- a/Source/Dafny/Triggers/QuantifiersCollection.cs +++ b/Source/Dafny/Triggers/QuantifiersCollection.cs @@ -100,7 +100,9 @@ namespace Microsoft.Dafny.Triggers { // quantifier that matches one of the terms of the trigger (this ensures that // [∀ x {f(x), f(f(x))} ⋅ f(x) = f(f(x))] is not a loop). And we even // ignore terms that almost match a trigger term, modulo a single variable - // (this ensures that [∀ x y {a(x, y)} ⋅ a(x, y) == a(y, x)] is not a loop). + // (this ensures that [∀ x y {a(x, y)} ⋅ a(x, y) == a(y, x)] is not a loop). + // The last thing that we ignore is matching variables against constants, + // to ensure that P(x) is not flagged as looping with P(0). // This ignoring logic is implemented by the CouldCauseLoops method. foreach (var q in quantifiers) { diff --git a/Source/Dafny/Triggers/TriggerExtensions.cs b/Source/Dafny/Triggers/TriggerExtensions.cs index 71414eee..dccd996d 100644 --- a/Source/Dafny/Triggers/TriggerExtensions.cs +++ b/Source/Dafny/Triggers/TriggerExtensions.cs @@ -33,7 +33,7 @@ namespace Microsoft.Dafny.Triggers { internal bool CouldCauseLoops(List terms) { var expr = Expr; - return !terms.Any(term => term.Expr.ExpressionEqModuloVariableNames(expr)); + return !terms.Any(term => term.Expr.ExpressionEqModuloVariableNamesAndConstants(expr)); } } @@ -79,15 +79,15 @@ namespace Microsoft.Dafny.Triggers { return ShallowEq_Top(expr1, expr2) && TriggerUtils.SameLists(expr1.SubExpressions, expr2.SubExpressions, (e1, e2) => ExpressionEq(e1, e2)); } - internal static bool ExpressionEqModuloVariableNames(this Expression expr1, Expression expr2) { + internal static bool ExpressionEqModuloVariableNamesAndConstants(this Expression expr1, Expression expr2) { expr1 = expr1.Resolved; expr2 = expr2.Resolved; if (expr1 is IdentifierExpr) { - return expr2 is IdentifierExpr; + return expr2 is IdentifierExpr || expr2 is LiteralExpr; } - return ShallowEq_Top(expr1, expr2) && TriggerUtils.SameLists(expr1.SubExpressions, expr2.SubExpressions, (e1, e2) => ExpressionEqModuloVariableNames(e1, e2)); + return ShallowEq_Top(expr1, expr2) && TriggerUtils.SameLists(expr1.SubExpressions, expr2.SubExpressions, (e1, e2) => ExpressionEqModuloVariableNamesAndConstants(e1, e2)); } internal static bool MatchesTrigger(this Expression expr, Expression trigger, ISet holes, Dictionary bindings) { diff --git a/Test/triggers/redundancy-detection-is-bidirectional.dfy b/Test/triggers/redundancy-detection-is-bidirectional.dfy index df1d78c3..06541b70 100644 --- a/Test/triggers/redundancy-detection-is-bidirectional.dfy +++ b/Test/triggers/redundancy-detection-is-bidirectional.dfy @@ -1,7 +1,7 @@ // RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" // RUN: %diff "%s.expect" "%t" -// This tests checks for tricky cases of redundancy suppression when building +// This test checks for tricky cases of redundancy suppression when building // triggers. predicate P(x: int, y: int) -- cgit v1.2.3 From 1e725f0c9382a3dd8be109d160581868c9567f61 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 27 Aug 2015 21:25:06 -0700 Subject: Further relax the loop detection conditions Mismatches are now allowed up to expressions not involving any of the bound variables of the quantifier under inspection. --- Source/Dafny/Triggers/QuantifiersCollection.cs | 7 +++++-- Source/Dafny/Triggers/TriggerExtensions.cs | 23 +++++++++++++++++----- Source/Dafny/Triggers/TriggersCollector.cs | 3 ++- Test/triggers/loop-detection-is-not-too-strict.dfy | 21 +++++++++++++++++++- .../loop-detection-is-not-too-strict.dfy.expect | 17 ++++++++++++---- 5 files changed, 58 insertions(+), 13 deletions(-) diff --git a/Source/Dafny/Triggers/QuantifiersCollection.cs b/Source/Dafny/Triggers/QuantifiersCollection.cs index b77afccb..50458ab7 100644 --- a/Source/Dafny/Triggers/QuantifiersCollection.cs +++ b/Source/Dafny/Triggers/QuantifiersCollection.cs @@ -101,8 +101,11 @@ namespace Microsoft.Dafny.Triggers { // [∀ x {f(x), f(f(x))} ⋅ f(x) = f(f(x))] is not a loop). And we even // ignore terms that almost match a trigger term, modulo a single variable // (this ensures that [∀ x y {a(x, y)} ⋅ a(x, y) == a(y, x)] is not a loop). - // The last thing that we ignore is matching variables against constants, - // to ensure that P(x) is not flagged as looping with P(0). + // In addition, we ignore cases where the only differences between a trigger + // and a trigger match are places where a variable is replaced with an + // expression whose free variables do not intersect that of the quantifier + // in which that expression is found. For examples of this behavious, see + // triggers/literals-do-not-cause-loops. // This ignoring logic is implemented by the CouldCauseLoops method. foreach (var q in quantifiers) { diff --git a/Source/Dafny/Triggers/TriggerExtensions.cs b/Source/Dafny/Triggers/TriggerExtensions.cs index dccd996d..d4a822a8 100644 --- a/Source/Dafny/Triggers/TriggerExtensions.cs +++ b/Source/Dafny/Triggers/TriggerExtensions.cs @@ -31,9 +31,15 @@ namespace Microsoft.Dafny.Triggers { return ExprExtensions.ExpressionEq(t1.Expr, t2.Expr); } - internal bool CouldCauseLoops(List terms) { + /// + /// This method checks whether this match could actually cause a loop, given a set of terms participating in a trigger; + /// to compute an answer, we match the Expr of this match against the Exprs of each of these term, allowing for harmless + /// variations. If any of these tests does match, this term likely won't cause a loop. + /// The boundVars list is useful to determine that forall x :: P(x) == P(y+z) does not loop. + /// + internal bool CouldCauseLoops(List terms, ISet boundVars) { var expr = Expr; - return !terms.Any(term => term.Expr.ExpressionEqModuloVariableNamesAndConstants(expr)); + return !terms.Any(term => term.Expr.ExpressionEqModuloExpressionsNotInvolvingBoundVariables(expr, boundVars)); } } @@ -79,15 +85,22 @@ namespace Microsoft.Dafny.Triggers { return ShallowEq_Top(expr1, expr2) && TriggerUtils.SameLists(expr1.SubExpressions, expr2.SubExpressions, (e1, e2) => ExpressionEq(e1, e2)); } - internal static bool ExpressionEqModuloVariableNamesAndConstants(this Expression expr1, Expression expr2) { + internal static bool ExpressionEqModuloExpressionsNotInvolvingBoundVariables(this Expression expr1, Expression expr2, ISet boundVars) { expr1 = expr1.Resolved; expr2 = expr2.Resolved; if (expr1 is IdentifierExpr) { - return expr2 is IdentifierExpr || expr2 is LiteralExpr; + if (expr2 is IdentifierExpr) { + return true; + } else { + var freeInE2 = Translator.ComputeFreeVariables(expr2); + freeInE2.IntersectWith(boundVars); + return !freeInE2.Any(); + } } - return ShallowEq_Top(expr1, expr2) && TriggerUtils.SameLists(expr1.SubExpressions, expr2.SubExpressions, (e1, e2) => ExpressionEqModuloVariableNamesAndConstants(e1, e2)); + return ShallowEq_Top(expr1, expr2) && TriggerUtils.SameLists(expr1.SubExpressions, + expr2.SubExpressions, (e1, e2) => ExpressionEqModuloExpressionsNotInvolvingBoundVariables(e1, e2, boundVars)); } internal static bool MatchesTrigger(this Expression expr, Expression trigger, ISet holes, Dictionary bindings) { diff --git a/Source/Dafny/Triggers/TriggersCollector.cs b/Source/Dafny/Triggers/TriggersCollector.cs index d78bcb9e..cc3b1978 100644 --- a/Source/Dafny/Triggers/TriggersCollector.cs +++ b/Source/Dafny/Triggers/TriggersCollector.cs @@ -69,7 +69,8 @@ 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 = this.MatchingSubterms(quantifier); - return matchingSubterms.Where(tm => tm.CouldCauseLoops(Terms)); + var boundVars = new HashSet(quantifier.BoundVars); + return matchingSubterms.Where(tm => tm.CouldCauseLoops(Terms, boundVars)); } internal List MatchingSubterms(QuantifierExpr quantifier) { diff --git a/Test/triggers/loop-detection-is-not-too-strict.dfy b/Test/triggers/loop-detection-is-not-too-strict.dfy index a5a30c81..81f764ad 100644 --- a/Test/triggers/loop-detection-is-not-too-strict.dfy +++ b/Test/triggers/loop-detection-is-not-too-strict.dfy @@ -7,8 +7,9 @@ // equal to that trigger, as long as the only differences are variable predicate P(x: int, y: int) +predicate Q(x: int) -method Test() { +method Test(z: int) { // P(x, y) and P(y, x) might look like they would cause a loop. Since they // only differ by their variables, though, they won't raise flags. assume forall x: int, y: int :: P(x, y) == P(y, x); @@ -18,4 +19,22 @@ method Test() { // Contrast with the following: assume forall x: int, y: int :: P(x, y) == P(x, y+1); + + // The following examples were made legal after an exchange where Chris + // pointed examples in the IronClad sources where things like this were + // incorrectly flagged. + assert forall x :: true || Q(x) || Q(0); + assert forall x :: true || Q(x) || Q(z); + assert forall x :: true || P(x, 1) || P(x, z); + + // Support for the following was added following a discussion with Rustan; in + // the second one the expression `if z > 1 then z else 3 * z + 1` is not + // directly a constant expression, but it does not involve x, so it's ok: + assert forall x :: true || Q(x) || Q(0+1); + assert forall x :: true || Q(x) || Q(if z > 1 then z else 3 * z + 1); + // Sanity check: + assert forall x :: true || Q(x) || Q(if z > 1 then x else 3 * z + 1); + + // WISH: It might also be good to zeta-reduce before loop detection. + assert forall x :: true || Q(x) || (var xx := x+1; Q(xx)); } diff --git a/Test/triggers/loop-detection-is-not-too-strict.dfy.expect b/Test/triggers/loop-detection-is-not-too-strict.dfy.expect index 6f5ed3d7..705c2a8c 100644 --- a/Test/triggers/loop-detection-is-not-too-strict.dfy.expect +++ b/Test/triggers/loop-detection-is-not-too-strict.dfy.expect @@ -1,8 +1,17 @@ -loop-detection-is-not-too-strict.dfy(14,9): Info: Selected triggers: +loop-detection-is-not-too-strict.dfy(15,9): Info: Selected triggers: {P(y, x)}, {P(x, y)} -loop-detection-is-not-too-strict.dfy(17,9): Info: Selected triggers: +loop-detection-is-not-too-strict.dfy(18,9): Info: Selected triggers: {P(y, x)}, {P(x, y)} -loop-detection-is-not-too-strict.dfy(20,9): Warning: Selected triggers: {P(x, y)} (may loop with {P(x, y + 1)}) +loop-detection-is-not-too-strict.dfy(21,9): Warning: Selected triggers: {P(x, y)} (may loop with {P(x, y + 1)}) (!) Suppressing loops would leave this expression without triggers. +loop-detection-is-not-too-strict.dfy(26,9): Info: Selected triggers: {Q(x)} +loop-detection-is-not-too-strict.dfy(27,9): Info: Selected triggers: {Q(x)} +loop-detection-is-not-too-strict.dfy(28,9): Info: Selected triggers: + {P(x, z)}, {P(x, 1)} +loop-detection-is-not-too-strict.dfy(33,9): Info: Selected triggers: {Q(x)} +loop-detection-is-not-too-strict.dfy(34,9): Info: Selected triggers: {Q(x)} +loop-detection-is-not-too-strict.dfy(36,9): Warning: Selected triggers: {Q(x)} (may loop with {Q(if z > 1 then x else 3 * z + 1)}) + (!) Suppressing loops would leave this expression without triggers. +loop-detection-is-not-too-strict.dfy(39,9): Info: Selected triggers: {Q(x)} -Dafny program verifier finished with 3 verified, 0 errors +Dafny program verifier finished with 4 verified, 0 errors -- cgit v1.2.3 From 8ede9fda35767f083899940886b69f53640891c9 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 27 Aug 2015 18:59:10 -0700 Subject: Look for z3 in Binaries/z3/bin (but keep the vendored version for convenience) Dafny now first looks for a z3 binary in z3/bin, and if it can't find one it looks for one in Binaries/z3.exe (mostly for backwards-compatibility with already set-up source installations). --- Binaries/z3 | Bin 16438468 -> 0 bytes Source/Dafny/DafnyOptions.cs | 37 ++++++++++++++++++++++++++++----- Source/Dafny/Reporting.cs | 2 +- Source/DafnyDriver/DafnyDriver.cs | 14 ++++++------- Source/DafnyServer/DafnyHelper.cs | 5 ++++- Source/DafnyServer/Utilities.cs | 4 ++-- Source/DafnyServer/VerificationTask.cs | 3 +-- 7 files changed, 47 insertions(+), 18 deletions(-) delete mode 100755 Binaries/z3 diff --git a/Binaries/z3 b/Binaries/z3 deleted file mode 100755 index 7c60feb4..00000000 Binary files a/Binaries/z3 and /dev/null differ diff --git a/Source/Dafny/DafnyOptions.cs b/Source/Dafny/DafnyOptions.cs index 1986ea6d..08e53d5c 100644 --- a/Source/Dafny/DafnyOptions.cs +++ b/Source/Dafny/DafnyOptions.cs @@ -9,8 +9,11 @@ namespace Microsoft.Dafny { public class DafnyOptions : Bpl.CommandLineOptions { - public DafnyOptions() + private ErrorReporter errorReporter; + + public DafnyOptions(ErrorReporter errorReporter = null) : base("Dafny", "Dafny program verifier") { + this.errorReporter = errorReporter; SetZ3ExecutableName(); } @@ -257,13 +260,37 @@ namespace Microsoft.Dafny // TODO: provide attribute help here } + + /// + /// Dafny comes with it's own copy of z3, to save new users the trouble of having to install extra dependency. + /// For this to work, Dafny makes the Z3ExecutablePath point to the path were Z3 is put by our release script. + /// For developers though (and people getting this from source), it's convenient to be able to run right away, + /// so we vendor a Windows version. + /// private void SetZ3ExecutableName() { var platform = (int)System.Environment.OSVersion.Platform; - var isLinux = platform == 4 || platform == 128; // http://www.mono-project.com/docs/faq/technical/ - //TODO should we also vendor an OSX build? - var binDir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); - Z3ExecutablePath = System.IO.Path.Combine(binDir, isLinux ? "z3" : "z3.exe"); + // http://www.mono-project.com/docs/faq/technical/ + var isUnix = platform == 4 || platform == 128; + + var z3binName = isUnix ? "z3" : "z3.exe"; + var dafnyBinDir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); + var z3BinDir = System.IO.Path.Combine(dafnyBinDir, "z3", "bin"); + var z3BinPath = System.IO.Path.Combine(z3BinDir, z3binName); + + if (!System.IO.File.Exists(z3BinPath) && !isUnix) { + // This is most likely a Windows user running from source without downloading z3 + // separately; this is ok, since we vendor z3.exe. + z3BinPath = System.IO.Path.Combine(dafnyBinDir, z3binName); + } + + if (!System.IO.File.Exists(z3BinPath) && errorReporter != null) { + var tok = new Bpl.Token(1, 1) { filename = "*** " }; + errorReporter.Warning(MessageSource.Other, tok, "Could not find '{0}' in '{1}'.{2}Downloading and extracting a Z3 distribution to Dafny's 'Binaries' folder would solve this issue; for now, we'll rely on Boogie to find Z3.", + z3binName, z3BinDir, System.Environment.NewLine); + } else { + Z3ExecutablePath = z3BinPath; + } } public override void Usage() { diff --git a/Source/Dafny/Reporting.cs b/Source/Dafny/Reporting.cs index 4cfbf20e..760392ca 100644 --- a/Source/Dafny/Reporting.cs +++ b/Source/Dafny/Reporting.cs @@ -143,7 +143,7 @@ namespace Microsoft.Dafny { } public override bool Message(MessageSource source, ErrorLevel level, IToken tok, string msg) { - if (base.Message(source, level, tok, msg) && (DafnyOptions.O.PrintTooltips || level != ErrorLevel.Info)) { + if (base.Message(source, level, tok, msg) && ((DafnyOptions.O != null && DafnyOptions.O.PrintTooltips) || level != ErrorLevel.Info)) { // Extra indent added to make it easier to distinguish multiline error messages for clients that rely on the CLI msg = msg.Replace(Environment.NewLine, Environment.NewLine + " "); diff --git a/Source/DafnyDriver/DafnyDriver.cs b/Source/DafnyDriver/DafnyDriver.cs index 4b5ae8d8..c45d66fc 100644 --- a/Source/DafnyDriver/DafnyDriver.cs +++ b/Source/DafnyDriver/DafnyDriver.cs @@ -40,10 +40,11 @@ namespace Microsoft.Dafny public static int ThreadMain(string[] args) { Contract.Requires(cce.NonNullElements(args)); - + + ErrorReporter reporter = new ConsoleErrorReporter(); ExecutionEngine.printer = new DafnyConsolePrinter(); // For boogie errors - DafnyOptions.Install(new DafnyOptions()); + DafnyOptions.Install(new DafnyOptions(reporter)); ExitValue exitValue = ExitValue.VERIFIED; CommandLineOptions.Clo.RunningBoogieFromCommandLine = true; @@ -92,7 +93,7 @@ namespace Microsoft.Dafny goto END; } } - exitValue = ProcessFiles(CommandLineOptions.Clo.Files); + exitValue = ProcessFiles(CommandLineOptions.Clo.Files, reporter); END: if (CommandLineOptions.Clo.XmlSink != null) { @@ -112,7 +113,7 @@ namespace Microsoft.Dafny } - static ExitValue ProcessFiles(IList/*!*/ fileNames, bool lookForSnapshots = true, string programId = null) + static ExitValue ProcessFiles(IList/*!*/ fileNames, ErrorReporter reporter, bool lookForSnapshots = true, string programId = null) { Contract.Requires(cce.NonNullElements(fileNames)); @@ -128,7 +129,7 @@ namespace Microsoft.Dafny { Console.WriteLine(); Console.WriteLine("-------------------- {0} --------------------", f); - var ev = ProcessFiles(new List { f }, lookForSnapshots, f); + var ev = ProcessFiles(new List { f }, reporter, lookForSnapshots, f); if (exitValue != ev && ev != ExitValue.VERIFIED) { exitValue = ev; @@ -142,7 +143,7 @@ namespace Microsoft.Dafny var snapshotsByVersion = ExecutionEngine.LookForSnapshots(fileNames); foreach (var s in snapshotsByVersion) { - var ev = ProcessFiles(new List(s), false, programId); + var ev = ProcessFiles(new List(s), reporter, false, programId); if (exitValue != ev && ev != ExitValue.VERIFIED) { exitValue = ev; @@ -153,7 +154,6 @@ namespace Microsoft.Dafny using (XmlFileScope xf = new XmlFileScope(CommandLineOptions.Clo.XmlSink, fileNames[fileNames.Count-1])) { Dafny.Program dafnyProgram; - ErrorReporter reporter = new ConsoleErrorReporter(); string programName = fileNames.Count == 1 ? fileNames[0] : "the program"; string err = Dafny.Main.ParseCheck(fileNames, programName, reporter, out dafnyProgram); if (err != null) { diff --git a/Source/DafnyServer/DafnyHelper.cs b/Source/DafnyServer/DafnyHelper.cs index 3204fdb3..e54e2b48 100644 --- a/Source/DafnyServer/DafnyHelper.cs +++ b/Source/DafnyServer/DafnyHelper.cs @@ -29,18 +29,21 @@ namespace Microsoft.Dafny { class DafnyHelper { private string fname; private string source; + private string[] args; private readonly Dafny.ErrorReporter reporter; private Dafny.Program dafnyProgram; private Bpl.Program boogieProgram; - public DafnyHelper(string fname, string source) { + public DafnyHelper(string[] args, string fname, string source) { + this.args = args; this.fname = fname; this.source = source; this.reporter = new Dafny.ConsoleErrorReporter(); } public bool Verify() { + ServerUtils.ApplyArgs(args, reporter); return Parse() && Resolve() && Translate() && Boogie(); } diff --git a/Source/DafnyServer/Utilities.cs b/Source/DafnyServer/Utilities.cs index 59b3abb9..5e2c6f96 100644 --- a/Source/DafnyServer/Utilities.cs +++ b/Source/DafnyServer/Utilities.cs @@ -43,8 +43,8 @@ namespace Microsoft.Dafny { } } - internal static void ApplyArgs(string[] args) { - Dafny.DafnyOptions.Install(new Dafny.DafnyOptions()); + internal static void ApplyArgs(string[] args, ErrorReporter reporter) { + Dafny.DafnyOptions.Install(new Dafny.DafnyOptions(reporter)); Dafny.DafnyOptions.O.ProverKillTime = 15; //This is just a default; it can be overriden if (CommandLineOptions.Clo.Parse(args)) { diff --git a/Source/DafnyServer/VerificationTask.cs b/Source/DafnyServer/VerificationTask.cs index a00688b1..eb740e70 100644 --- a/Source/DafnyServer/VerificationTask.cs +++ b/Source/DafnyServer/VerificationTask.cs @@ -52,8 +52,7 @@ namespace Microsoft.Dafny { } internal void Run() { - ServerUtils.ApplyArgs(args); - new DafnyHelper(filename, ProgramSource).Verify(); + new DafnyHelper(args, filename, ProgramSource).Verify(); } } } -- cgit v1.2.3 From a8f980afe85d26774806ba9c987423185f1c11ac Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 27 Aug 2015 17:55:08 -0700 Subject: Make the packaging script Windows-compatible --- package.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/package.py b/package.py index 22ed232c..7fc624cf 100644 --- a/package.py +++ b/package.py @@ -101,7 +101,10 @@ class Release: flush("done!") def unpack(self): - shutil.rmtree(self.z3_directory) + try: + shutil.rmtree(self.z3_directory) + except FileNotFoundError: + pass with zipfile.ZipFile(self.z3_zip) as archive: archive.extractall(CACHE_DIRECTORY) flush("done!") @@ -165,9 +168,13 @@ def run(cmd): def build(): os.chdir(ROOT_DIRECTORY) flush(" - Building") - builder = "xbuild" if MONO else "xbuild" - run([builder, "Source/Dafny.sln", "/t:Clean"]) - run([builder, "Source/Dafny.sln", "/p:Configuration=Checked", "/t:Rebuild"]) + builder = "xbuild" if MONO else "msbuild" + try: + run([builder, "Source/Dafny.sln", "/p:Configuration=Checked", "/p:Platform=Any CPU", "/t:Clean"]) + run([builder, "Source/Dafny.sln", "/p:Configuration=Checked", "/p:Platform=Any CPU", "/t:Rebuild"]) + except FileNotFoundError: + flush("Could not find '{}'! On Windows, you need to run this from the VS native tools command prompt.".format(builder)) + sys.exit(1) def pack(releases): flush(" - Packaging {} Dafny archives".format(len(releases))) -- cgit v1.2.3 From 8b49355bcbe76170504b8f23b4f54a7793b1ae5c Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 27 Aug 2015 18:41:12 -0700 Subject: More fixes to the packaging script --- package.py | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/package.py b/package.py index 7fc624cf..94c7f70e 100644 --- a/package.py +++ b/package.py @@ -1,25 +1,36 @@ +from fnmatch import fnmatch +from os import path import argparse import json import os -from os import path import re -import urllib.request +import shutil +import subprocess import sys +import urllib.request import zipfile -import subprocess -import shutil # Configuration +## Where do we fetch the list of releases from? RELEASES_URL = "https://api.github.com/repos/Z3Prover/z3/releases/latest" -RELEASE_REGEXP = r"^(?Pz3-[0-9\.]+-(?Px86|x64)-(?P[a-z0-9\.\-]+)).zip$" +## How do we extract info from the name of a release file? +RELEASE_REGEXP = re.compile(r"^(?Pz3-[0-9\.]+-(?Px86|x64)-(?P[a-z0-9\.\-]+)).zip$", re.IGNORECASE) +## Where are the sources? SOURCE_DIRECTORY = "Source" +## Where do the binaries get put? BINARIES_DIRECTORY = "Binaries" +## Where do we store the built packages and cache files? DESTINATION_DIRECTORY = "Package" -Z3_ARCHIVE_PREFIX = path.join("z3", "bin") +## What sub-folder of the packages does z3 go into? +Z3_PACKAGE_PREFIX = path.join("z3") + +## What do we take from the z3 archive? (Glob syntax) +Z3_INTERESTING_FILES = ["LICENSE.txt", "bin/*"] +## What do we take from Dafny's Binaries folder? DLLs = ["AbsInt", "Basetypes", "CodeContractsExtender", @@ -48,8 +59,6 @@ BINARIES_DIRECTORY = path.join(ROOT_DIRECTORY, BINARIES_DIRECTORY) DESTINATION_DIRECTORY = path.join(ROOT_DIRECTORY, DESTINATION_DIRECTORY) CACHE_DIRECTORY = path.join(DESTINATION_DIRECTORY, "cache") -RELEASE_REGEXP = re.compile(RELEASE_REGEXP, re.IGNORECASE) - MONO = sys.platform not in ("win32", "cygwin") DLL_PDB_EXT = ".dll.mdb" if MONO else ".pdb" EXE_PDB_EXT = ".exe.mdb" if MONO else ".pdb" @@ -78,7 +87,6 @@ class Release: self.platform, self.os, self.directory = Release.parse_zip_name(js["name"]) self.z3_zip = path.join(CACHE_DIRECTORY, self.z3_name) self.z3_directory = path.join(CACHE_DIRECTORY, self.directory) - self.z3_bin_directory = path.join(self.z3_directory, "bin") self.dafny_name = "dafny-{}-{}-{}.zip".format(version, self.platform, self.os) self.dafny_zip = path.join(DESTINATION_DIRECTORY, self.dafny_name) @@ -116,24 +124,28 @@ class Release: pass missing = [] with zipfile.ZipFile(self.dafny_zip, 'w', zipfile.ZIP_DEFLATED) as archive: - for root, _, files in os.walk(self.z3_bin_directory): + z3_files_count = 0 + for root, _, files in os.walk(self.z3_directory): for f in files: fpath = path.join(root, f) - arcpath = path.join(Z3_ARCHIVE_PREFIX, path.relpath(fpath, self.z3_bin_directory)) - archive.write(fpath, arcpath) + relpath = path.relpath(fpath, self.z3_directory) + if any(fnmatch(relpath, pattern) for pattern in Z3_INTERESTING_FILES): + arcpath = path.join(Z3_PACKAGE_PREFIX, relpath) + archive.write(fpath, arcpath) + z3_files_count += 1 for fname in ARCHIVE_FNAMES: fpath = path.join(BINARIES_DIRECTORY, fname) if path.exists(fpath): archive.write(fpath, fname) else: missing.append(fname) - flush("done!") + flush("done! (imported {} files from z3's sources)".format(z3_files_count)) if missing: flush(" WARNING: Not all files were found: {} were missing".format(", ".join(missing))) def discover(version): flush(" - Getting information about latest release") - with urllib.request.urlopen("https://api.github.com/repos/Z3Prover/z3/releases/latest") as reader: + with urllib.request.urlopen(RELEASES_URL) as reader: js = json.loads(reader.read().decode("utf-8")) for release_js in js["assets"]: -- cgit v1.2.3 From 4c71672823dfac1cd352f0da6a61b83680d7eb31 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 27 Aug 2015 19:38:12 -0700 Subject: package.py: Keep z3's exec bits, and set the exec bit on the dafny/ script --- package.py | 42 ++++++++++++++++-------------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/package.py b/package.py index 94c7f70e..62f8dbdf 100644 --- a/package.py +++ b/package.py @@ -30,6 +30,9 @@ Z3_PACKAGE_PREFIX = path.join("z3") ## What do we take from the z3 archive? (Glob syntax) Z3_INTERESTING_FILES = ["LICENSE.txt", "bin/*"] +## On unix system, which Dafny files should be marked as executable? (Glob syntax; Z3's permissions are preserved) +UNIX_EXECUTABLES = ["dafny"] + ## What do we take from Dafny's Binaries folder? DLLs = ["AbsInt", "Basetypes", @@ -86,7 +89,6 @@ class Release: self.url = js["browser_download_url"] self.platform, self.os, self.directory = Release.parse_zip_name(js["name"]) self.z3_zip = path.join(CACHE_DIRECTORY, self.z3_name) - self.z3_directory = path.join(CACHE_DIRECTORY, self.directory) self.dafny_name = "dafny-{}-{}-{}.zip".format(version, self.platform, self.os) self.dafny_zip = path.join(DESTINATION_DIRECTORY, self.dafny_name) @@ -108,15 +110,6 @@ class Release: writer.write(reader.read()) flush("done!") - def unpack(self): - try: - shutil.rmtree(self.z3_directory) - except FileNotFoundError: - pass - with zipfile.ZipFile(self.z3_zip) as archive: - archive.extractall(CACHE_DIRECTORY) - flush("done!") - def pack(self): try: os.remove(self.dafny_zip) @@ -124,18 +117,22 @@ class Release: pass missing = [] with zipfile.ZipFile(self.dafny_zip, 'w', zipfile.ZIP_DEFLATED) as archive: - z3_files_count = 0 - for root, _, files in os.walk(self.z3_directory): - for f in files: - fpath = path.join(root, f) - relpath = path.relpath(fpath, self.z3_directory) - if any(fnmatch(relpath, pattern) for pattern in Z3_INTERESTING_FILES): - arcpath = path.join(Z3_PACKAGE_PREFIX, relpath) - archive.write(fpath, arcpath) + with zipfile.ZipFile(self.z3_zip) as Z3_archive: + z3_files_count = 0 + for fileinfo in Z3_archive.infolist(): + fname = path.relpath(fileinfo.filename, self.directory) + if any(fnmatch(fname, pattern) for pattern in Z3_INTERESTING_FILES): z3_files_count += 1 + contents = Z3_archive.read(fileinfo) + fileinfo.filename = path.join(Z3_PACKAGE_PREFIX, fname) + archive.writestr(fileinfo, contents) for fname in ARCHIVE_FNAMES: fpath = path.join(BINARIES_DIRECTORY, fname) if path.exists(fpath): + fileinfo = zipfile.ZipInfo(fname) + if any(fnmatch(fname, pattern) for pattern in UNIX_EXECUTABLES): + # http://stackoverflow.com/questions/434641/ + fileinfo.external_attr = 0o777 << 16 archive.write(fpath, fname) else: missing.append(fname) @@ -162,12 +159,6 @@ def download(releases): flush(" + {}:".format(release.z3_name), end=' ') release.download() -def unpack(releases): - flush(" - Unpacking {} z3 archives".format(len(releases))) - for release in releases: - flush(" + {}:".format(release.z3_name), end=' ') - release.unpack() - def run(cmd): flush(" + {}...".format(" ".join(cmd)), end=' ') retv = subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) @@ -204,10 +195,9 @@ def main(): os.makedirs(CACHE_DIRECTORY, exist_ok=True) # Z3 - flush("* Finding, downloading, and unpacking Z3 releases") + flush("* Finding and downloading Z3 releases") releases = list(discover(args.version)) download(releases) - unpack(releases) flush("* Building and packaging Dafny") build() -- cgit v1.2.3 From 1239f6ac97178dba507d89982b3ab0caf7b3ed4d Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 27 Aug 2015 19:46:36 -0700 Subject: Update INSTALL to reflect the latest packaging changes --- INSTALL | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/INSTALL b/INSTALL index dbc63410..488060f6 100644 --- a/INSTALL +++ b/INSTALL @@ -1,7 +1,9 @@ Building on Linux ================= -(Tested on a fresh Linux Mint 17.2) +Tested on a fresh Linux Mint 17.2. Note that we now have official releases for +Linux, so these instructions mostly apply to people interested in looking at +Dafny's sources. 0. Dependencies @@ -28,25 +30,20 @@ Building on Linux cd dafny/Sources/ xbuild Dafny.sln -4. Download and build Z3 +4. Download and unpack z3 (Dafny looks for `z3` in Binaries/z3/bin/) - git clone https://github.com/Z3Prover/z3.git - cd z3 - git checkout z3-4.4.0 - ./configure - cd build - make -j4 - cd ../.. + cd BASE-DIRECTORY + wget https://github.com/Z3Prover/z3/releases/download/z3-4.4.0/z3-4.4.0-x64-ubuntu-14.04.zip + unzip z3-4.4.0-x64-ubuntu-14.04.zip + mv z3-4.4.0-x64-ubuntu-14.04 dafny/Binaries/z3 -5. Copy (or symlink) the z3 binary so that Dafny can find it: +5. (Optional) If you plan to use Boogie directly, copy (or symlink) the z3 binary so that Boogie, too, can find it: cd BASE-DIRECTORY - rm -f dafny/Binaries/z3 rm -f boogie/Binaries/z3.exe - ln -s /usr/bin/z3 dafny/Binaries/z3 - ln -s /usr/bin/z3 boogie/Binaries/z3.exe + cp dafny/Binaries/z3/bin/z3 boogie/Binaries/z3.exe -6. Run Dafny using the `dafny` shell script in the Binaries directory. +6. Run Dafny using the `dafny` shell script in the Binaries directory (it calls mono as appropriate) Editing in Emacs ================ @@ -55,6 +52,6 @@ The README at https://github.com/boogie-org/boogie-friends has plenty of information on how to set-up Emacs to work with Dafny. In short, it boils down to running [M-x package-install RET boogie-friends RET] and adding the following to your .emacs: - (setq flycheck-dafny-executable "BASE-DIRECTORy/dafny/") + (setq flycheck-dafny-executable "BASE-DIRECTORY/dafny/Binaries/dafny") Do look at the README, though! It's full of useful tips. -- cgit v1.2.3 From 354c53de79366c09f8550b8cd23ebddb0b7d2c2f Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 27 Aug 2015 20:25:37 -0700 Subject: Add a tip to the packaging script --- package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.py b/package.py index 62f8dbdf..fa111ec5 100644 --- a/package.py +++ b/package.py @@ -163,7 +163,7 @@ def run(cmd): flush(" + {}...".format(" ".join(cmd)), end=' ') retv = subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) if retv != 0: - flush("failed!") + flush("failed! (Is Dafny or the Dafny server running?)") sys.exit(1) else: flush("done!") -- cgit v1.2.3 From 6f2504ad3b3046dbf58a3fd2c52325d3b2009428 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 27 Aug 2015 20:51:43 -0700 Subject: Tiny cleanup in TriggersCollector --- Source/Dafny/Triggers/TriggersCollector.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Source/Dafny/Triggers/TriggersCollector.cs b/Source/Dafny/Triggers/TriggersCollector.cs index cc3b1978..c25f65b9 100644 --- a/Source/Dafny/Triggers/TriggersCollector.cs +++ b/Source/Dafny/Triggers/TriggersCollector.cs @@ -191,8 +191,13 @@ namespace Microsoft.Dafny.Triggers { expr.SubExpressions.Iter(e => Annotate(e)); TriggerAnnotation annotation; // TODO: Using ApplySuffix fixes the unresolved members problem in GenericSort - if (expr is FunctionCallExpr || expr is SeqSelectExpr || expr is MultiSelectExpr || expr is MemberSelectExpr || - expr is OldExpr || expr is ApplyExpr || expr is DisplayExpression || + if (expr is FunctionCallExpr || + expr is SeqSelectExpr || + expr is MultiSelectExpr || + expr is MemberSelectExpr || + expr is OldExpr || + expr is ApplyExpr || + expr is DisplayExpression || (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); -- cgit v1.2.3 From 71bcbeb4ce808f463d86b7a877e3e550e839fb17 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Thu, 27 Aug 2015 22:52:14 -0700 Subject: Add a small test from the IronClad notebook --- ...ix-an-issue-listed-in-the-ironclad-notebook.dfy | 23 ++++++++++++++++++++++ ...ssue-listed-in-the-ironclad-notebook.dfy.expect | 4 ++++ 2 files changed, 27 insertions(+) create mode 100644 Test/triggers/auto-triggers-fix-an-issue-listed-in-the-ironclad-notebook.dfy create mode 100644 Test/triggers/auto-triggers-fix-an-issue-listed-in-the-ironclad-notebook.dfy.expect diff --git a/Test/triggers/auto-triggers-fix-an-issue-listed-in-the-ironclad-notebook.dfy b/Test/triggers/auto-triggers-fix-an-issue-listed-in-the-ironclad-notebook.dfy new file mode 100644 index 00000000..09032453 --- /dev/null +++ b/Test/triggers/auto-triggers-fix-an-issue-listed-in-the-ironclad-notebook.dfy @@ -0,0 +1,23 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This example was listed in IronClad's notebook as one place were z3 picked +// much too liberal triggers. THe Boogie code for this is shown below: +// +// forall k#2: Seq Box :: $Is(k#2, TSeq(TInt)) && $IsAlloc(k#2, TSeq(TInt), $Heap) +// ==> Seq#Equal(_module.__default.HashtableLookup($Heap, h1#0, k#2), +// _module.__default.HashtableLookup($Heap, h2#0, k#2)) +// +// and z3 would pick $Is(k#2, TSeq(TInt)) or $IsAlloc(k#2, TSeq(TInt), $Heap) as +// triggers. + +type Key = seq +type Value = seq + +type Hashtable = map +function HashtableLookup(h: Hashtable, k: Key): Value + +lemma HashtableAgreement(h1:Hashtable, h2:Hashtable, k:Key) + requires forall k :: HashtableLookup(h1,k) == HashtableLookup(h2,k) { + assert true || (k in h1) == (k in h2); +} diff --git a/Test/triggers/auto-triggers-fix-an-issue-listed-in-the-ironclad-notebook.dfy.expect b/Test/triggers/auto-triggers-fix-an-issue-listed-in-the-ironclad-notebook.dfy.expect new file mode 100644 index 00000000..46ec143e --- /dev/null +++ b/Test/triggers/auto-triggers-fix-an-issue-listed-in-the-ironclad-notebook.dfy.expect @@ -0,0 +1,4 @@ +auto-triggers-fix-an-issue-listed-in-the-ironclad-notebook.dfy(21,11): Info: Selected triggers: + {HashtableLookup(h2, k)}, {HashtableLookup(h1, k)} + +Dafny program verifier finished with 3 verified, 0 errors -- cgit v1.2.3 From d0fb19ff74b0397749362beedf39ffb8d1c623a9 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 28 Aug 2015 13:42:59 -0700 Subject: Fixed spelling mistake in test file --- Test/dafny3/Dijkstra.dfy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/dafny3/Dijkstra.dfy b/Test/dafny3/Dijkstra.dfy index 42aa3b9e..56719180 100644 --- a/Test/dafny3/Dijkstra.dfy +++ b/Test/dafny3/Dijkstra.dfy @@ -56,7 +56,7 @@ lemma lemma_ping(j: nat, n: nat) } } -// The other directorion: f(n) <= n +// The other direction: f(n) <= n lemma lemma_pong(n: nat) requires P() ensures f(n) <= n -- cgit v1.2.3 From cd441778a003d0c1af5b3b9e59efa69283f47e01 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 28 Aug 2015 15:52:03 -0700 Subject: Added tests for Boogie's new /verifySnapshots:3, which will be used by the Dafny emacs mode --- Test/dafny0/snapshots/Inputs/Snapshots8.v0.dfy | 29 +++++++++++++ Test/dafny0/snapshots/Inputs/Snapshots8.v1.dfy | 33 +++++++++++++++ Test/dafny0/snapshots/Snapshots8.run.dfy | 2 + Test/dafny0/snapshots/Snapshots8.run.dfy.expect | 55 +++++++++++++++++++++++++ 4 files changed, 119 insertions(+) create mode 100644 Test/dafny0/snapshots/Inputs/Snapshots8.v0.dfy create mode 100644 Test/dafny0/snapshots/Inputs/Snapshots8.v1.dfy create mode 100644 Test/dafny0/snapshots/Snapshots8.run.dfy create mode 100644 Test/dafny0/snapshots/Snapshots8.run.dfy.expect diff --git a/Test/dafny0/snapshots/Inputs/Snapshots8.v0.dfy b/Test/dafny0/snapshots/Inputs/Snapshots8.v0.dfy new file mode 100644 index 00000000..97fcfccb --- /dev/null +++ b/Test/dafny0/snapshots/Inputs/Snapshots8.v0.dfy @@ -0,0 +1,29 @@ +method M(x: int) +{ assert x < 20 || 10 <= x; // always true + assert x < 10; // error + Other(x); // error: precondition violation +} + +method Other(y: int) + requires 0 <= y +{ +} + +method Posty() returns (z: int) + ensures 2 <= z // error: postcondition violation +{ + var t := 20; + if t < z { + } else { // the postcondition violation occurs on this 'else' branch + } +} + +method NoChangeWhazzoeva(u: int) +{ + assert u != 53; // error +} + +method NoChangeAndCorrect() +{ + assert true; +} diff --git a/Test/dafny0/snapshots/Inputs/Snapshots8.v1.dfy b/Test/dafny0/snapshots/Inputs/Snapshots8.v1.dfy new file mode 100644 index 00000000..8d8b215b --- /dev/null +++ b/Test/dafny0/snapshots/Inputs/Snapshots8.v1.dfy @@ -0,0 +1,33 @@ +method M(x: int) +{ +assert x < 20 || 10 <= x; // always true + + assert x < 10; // error + Other(x); // error: precondition violation + assert x == 7; // error: this is a new error in v1 +} + + + method Other(y: int) + requires 0 <= y + { + } + + + +method Posty() returns (z: int) + ensures 2 <= z // error: postcondition violation +{ + var t := 20; + if t < z { + assert true; // this is a new assert + } else { // the postcondition violation occurs on this 'else' branch + } +} + + method NoChangeWhazzoeva(u: int) + { + assert u != 53; // error + } + +method NoChangeAndCorrect() { assert true; } diff --git a/Test/dafny0/snapshots/Snapshots8.run.dfy b/Test/dafny0/snapshots/Snapshots8.run.dfy new file mode 100644 index 00000000..00d20f91 --- /dev/null +++ b/Test/dafny0/snapshots/Snapshots8.run.dfy @@ -0,0 +1,2 @@ +// RUN: %dafny /compile:0 /verifySnapshots:3 /traceCaching:1 /errorTrace:0 "%S/Inputs/Snapshots8.dfy" > "%t" +// RUN: %diff "%s.expect" "%t" diff --git a/Test/dafny0/snapshots/Snapshots8.run.dfy.expect b/Test/dafny0/snapshots/Snapshots8.run.dfy.expect new file mode 100644 index 00000000..625b71b4 --- /dev/null +++ b/Test/dafny0/snapshots/Snapshots8.run.dfy.expect @@ -0,0 +1,55 @@ +Processing command (at Snapshots8.v0.dfy(2,37)) assert x#0 < 20 || LitInt(10) <= x#0; + >>> DoNothingToAssert +Processing command (at Snapshots8.v0.dfy(3,12)) assert x#0 < 10; + >>> DoNothingToAssert +Processing command (at Snapshots8.v0.dfy(4,9)) assert true; + >>> DoNothingToAssert +Processing command (at Snapshots8.v0.dfy(4,8)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); + >>> DoNothingToAssert +Processing command (at Snapshots8.v0.dfy(4,8)) assert LitInt(0) <= call0formal#AT#y#0; + >>> DoNothingToAssert +Snapshots8.v0.dfy(3,11): Error: assertion violation +Snapshots8.v0.dfy(4,7): Error BP5002: A precondition for this call might not hold. +Snapshots8.v0.dfy(8,13): Related location: This is the precondition that might not hold. +Processing command (at Snapshots8.v0.dfy(15,12)) assert true; + >>> DoNothingToAssert +Processing command (at Snapshots8.v0.dfy(13,13)) assert LitInt(2) <= z#0; + >>> DoNothingToAssert +Snapshots8.v0.dfy(17,9): Error BP5003: A postcondition might not hold on this return path. +Snapshots8.v0.dfy(13,12): Related location: This is the postcondition that might not hold. +Processing command (at Snapshots8.v0.dfy(23,12)) assert u#0 != 53; + >>> DoNothingToAssert +Snapshots8.v0.dfy(23,11): Error: assertion violation +Processing command (at Snapshots8.v0.dfy(28,10)) assert Lit(true); + >>> DoNothingToAssert + +Dafny program verifier finished with 7 verified, 4 errors +Processing command (at Snapshots8.v1.dfy(30,17)) assert u#0 != 53; + >>> RecycleError +Snapshots8.v1.dfy(30,16): Error: assertion violation +Processing command (at Snapshots8.v1.dfy(3,15)) assert x#0 < 20 || LitInt(10) <= x#0; + >>> MarkAsFullyVerified +Processing command (at Snapshots8.v1.dfy(5,17)) assert x#0 < 10; + >>> RecycleError +Processing command (at Snapshots8.v1.dfy(6,9)) assert true; + >>> MarkAsFullyVerified +Processing command (at Snapshots8.v1.dfy(6,8)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); + >>> MarkAsFullyVerified +Processing command (at Snapshots8.v1.dfy(6,8)) assert LitInt(0) <= call0formal#AT#y#0; + >>> RecycleError +Processing command (at Snapshots8.v1.dfy(7,12)) assert x#0 == LitInt(7); + >>> DoNothingToAssert +Snapshots8.v1.dfy(5,16): Error: assertion violation +Snapshots8.v1.dfy(6,7): Error BP5002: A precondition for this call might not hold. +Snapshots8.v1.dfy(12,20): Related location: This is the precondition that might not hold. +Snapshots8.v1.dfy(7,11): Error: assertion violation +Processing command (at Snapshots8.v1.dfy(21,12)) assert true; + >>> MarkAsFullyVerified +Processing command (at Snapshots8.v1.dfy(23,12)) assert Lit(true); + >>> DoNothingToAssert +Processing command (at Snapshots8.v1.dfy(19,13)) assert LitInt(2) <= z#0; + >>> DoNothingToAssert +Snapshots8.v1.dfy(24,9): Error BP5003: A postcondition might not hold on this return path. +Snapshots8.v1.dfy(19,12): Related location: This is the postcondition that might not hold. + +Dafny program verifier finished with 7 verified, 5 errors -- cgit v1.2.3 From 3b1c3923a403efbd28b8f5ae6fc4429ccee8c2e8 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 28 Aug 2015 20:08:14 -0700 Subject: Put contents od release packages into a dafny/ directory --- package.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/package.py b/package.py index fa111ec5..33e83d73 100644 --- a/package.py +++ b/package.py @@ -24,6 +24,8 @@ BINARIES_DIRECTORY = "Binaries" ## Where do we store the built packages and cache files? DESTINATION_DIRECTORY = "Package" +## What's the root folder of the archive? +DAFNY_PACKAGE_PREFIX = path.join("dafny") ## What sub-folder of the packages does z3 go into? Z3_PACKAGE_PREFIX = path.join("z3") @@ -124,7 +126,7 @@ class Release: if any(fnmatch(fname, pattern) for pattern in Z3_INTERESTING_FILES): z3_files_count += 1 contents = Z3_archive.read(fileinfo) - fileinfo.filename = path.join(Z3_PACKAGE_PREFIX, fname) + fileinfo.filename = path.join(DAFNY_PACKAGE_PREFIX, Z3_PACKAGE_PREFIX, fname) archive.writestr(fileinfo, contents) for fname in ARCHIVE_FNAMES: fpath = path.join(BINARIES_DIRECTORY, fname) @@ -133,7 +135,7 @@ class Release: if any(fnmatch(fname, pattern) for pattern in UNIX_EXECUTABLES): # http://stackoverflow.com/questions/434641/ fileinfo.external_attr = 0o777 << 16 - archive.write(fpath, fname) + archive.write(fpath, path.join(DAFNY_PACKAGE_PREFIX, fname)) else: missing.append(fname) flush("done! (imported {} files from z3's sources)".format(z3_files_count)) -- cgit v1.2.3 From 2db858bf6e72e521b49aa0ae3a993dc943b8c875 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 28 Aug 2015 20:39:02 -0700 Subject: Implement {:nowarn}, clarify some messages, and add a few tests --- Source/Dafny/Triggers/QuantifiersCollection.cs | 26 +++++++---- Source/Dafny/Triggers/TriggerUtils.cs | 11 +++-- .../triggers/constructors-cause-matching-loops.dfy | 11 +++++ .../constructors-cause-matching-loops.dfy.expect | 6 +++ .../function-applications-are-triggers.dfy.expect | 4 +- .../loop-detection-is-not-too-strict.dfy.expect | 8 ++-- .../loop-detection-looks-at-ranges-too.dfy.expect | 8 ++-- .../loop-detection-messages--unit-tests.dfy.expect | 50 +++++++++++----------- ...ng-is-hard-to-decide-modulo-equality.dfy.expect | 4 +- .../matrix-accesses-are-triggers.dfy.expect | 4 +- .../old-is-a-special-case-for-triggers.dfy.expect | 10 ++--- Test/triggers/regression-tests.dfy.expect | 2 +- .../some-proofs-only-work-without-autoTriggers.dfy | 48 +++++++++++++++++++++ ...roofs-only-work-without-autoTriggers.dfy.expect | 31 ++++++++++++++ ...ot-look-like-the-triggers-they-match.dfy.expect | 16 +++---- .../splitting-picks-the-right-tokens.dfy.expect | 12 +++--- ...tting-triggers-recovers-expressivity.dfy.expect | 28 ++++++------ ...s-better-precondition-related-errors.dfy.expect | 16 +++---- .../suppressing-warnings-behaves-properly.dfy | 21 +++++++++ ...uppressing-warnings-behaves-properly.dfy.expect | 14 ++++++ .../useless-triggers-are-removed.dfy.expect | 4 +- ...f-checks-use-the-original-quantifier.dfy.expect | 10 ++--- 22 files changed, 244 insertions(+), 100 deletions(-) create mode 100644 Test/triggers/constructors-cause-matching-loops.dfy create mode 100644 Test/triggers/constructors-cause-matching-loops.dfy.expect create mode 100644 Test/triggers/some-proofs-only-work-without-autoTriggers.dfy create mode 100644 Test/triggers/some-proofs-only-work-without-autoTriggers.dfy.expect create mode 100644 Test/triggers/suppressing-warnings-behaves-properly.dfy create mode 100644 Test/triggers/suppressing-warnings-behaves-properly.dfy.expect diff --git a/Source/Dafny/Triggers/QuantifiersCollection.cs b/Source/Dafny/Triggers/QuantifiersCollection.cs index 50458ab7..f72dab7f 100644 --- a/Source/Dafny/Triggers/QuantifiersCollection.cs +++ b/Source/Dafny/Triggers/QuantifiersCollection.cs @@ -117,7 +117,7 @@ namespace Microsoft.Dafny.Triggers { (candidate, loopingSubterms) => !loopingSubterms.Any(), (candidate, loopingSubterms) => { looping.Add(candidate); - candidate.Annotation = "may loop with " + loopingSubterms.MapConcat(t => "{" + Printer.ExprToString(t.OriginalExpr) + "}", ", "); + candidate.Annotation = "may loop with " + loopingSubterms.MapConcat(t => "\"" + Printer.ExprToString(t.OriginalExpr) + "\"", ", "); }).ToList(); q.CouldSuppressLoops = safe.Count > 0; @@ -145,12 +145,14 @@ namespace Microsoft.Dafny.Triggers { var errorLevel = ErrorLevel.Info; var msg = new StringBuilder(); var indent = addHeader ? " " : ""; + bool suppressWarnings = Attributes.Contains(q.quantifier.Attributes, "nowarn"); - if (!TriggerUtils.NeedsAutoTriggers(q.quantifier)) { // NOTE: matchingloop, split and autotriggers attributes are passed down to Boogie - msg.AppendFormat("Not generating triggers for {{{0}}}.", Printer.ExprToString(q.quantifier.Term)).AppendLine(); + if (!TriggerUtils.NeedsAutoTriggers(q.quantifier)) { // NOTE: split and autotriggers attributes are passed down to Boogie + var extraMsg = TriggerUtils.WantsAutoTriggers(q.quantifier) ? "" : " Note that {:autotriggers false} can cause instabilities. Consider using {:nowarn}, {:matchingloop} (not great either), or a manual trigger instead."; + msg.AppendFormat("Not generating triggers for \"{0}\".{1}", Printer.ExprToString(q.quantifier.Term), extraMsg).AppendLine(); } else { if (addHeader) { - msg.AppendFormat("For expression {{{0}}}:", Printer.ExprToString(q.quantifier.Term)).AppendLine(); + msg.AppendFormat("For expression \"{0}\":", Printer.ExprToString(q.quantifier.Term)).AppendLine(); } foreach (var candidate in q.Candidates) { @@ -161,20 +163,26 @@ namespace Microsoft.Dafny.Triggers { AddTriggersToMessage("Rejected triggers:", q.RejectedCandidates, msg, indent, true); #if QUANTIFIER_WARNINGS - string WARN = indent + (DafnyOptions.O.UnicodeOutput ? "⚠ " : "(!) "); + var WARN_TAG = DafnyOptions.O.UnicodeOutput ? "⚠ " : "/!\\ "; + var WARN_TAG_OVERRIDE = suppressWarnings ? "(Suppressed warning) " : WARN_TAG; + var WARN_LEVEL = suppressWarnings ? ErrorLevel.Info : ErrorLevel.Warning; + var WARN = indent + WARN_TAG_OVERRIDE; if (!q.CandidateTerms.Any()) { - errorLevel = ErrorLevel.Warning; + errorLevel = WARN_LEVEL; msg.Append(WARN).AppendLine("No terms found to trigger on."); } else if (!q.Candidates.Any()) { - errorLevel = ErrorLevel.Warning; + errorLevel = WARN_LEVEL; msg.Append(WARN).AppendLine("No trigger covering all quantified variables found."); } else if (!q.CouldSuppressLoops && !q.AllowsLoops) { - errorLevel = ErrorLevel.Warning; + errorLevel = WARN_LEVEL; msg.Append(WARN).AppendLine("Suppressing loops would leave this expression without triggers."); + } else if (suppressWarnings) { + errorLevel = ErrorLevel.Warning; + msg.Append(indent).Append(WARN_TAG).AppendLine("There is no warning here to suppress."); } #endif } - + if (msg.Length > 0) { var msgStr = msg.ToString().TrimEnd("\r\n ".ToCharArray()); reporter.Message(MessageSource.Rewriter, errorLevel, q.quantifier.tok, msgStr); diff --git a/Source/Dafny/Triggers/TriggerUtils.cs b/Source/Dafny/Triggers/TriggerUtils.cs index efa1f167..c16a3e44 100644 --- a/Source/Dafny/Triggers/TriggerUtils.cs +++ b/Source/Dafny/Triggers/TriggerUtils.cs @@ -183,14 +183,19 @@ namespace Microsoft.Dafny.Triggers { internal static bool AllowsMatchingLoops(QuantifierExpr quantifier) { Contract.Requires(quantifier.SplitQuantifier == null); // Don't call this on a quantifier with a Split clause: it's not a real quantifier + // This is different from nowarn: it won't remove loops at all, even if another trigger is available. return Attributes.Contains(quantifier.Attributes, "matchingloop"); } - internal static bool NeedsAutoTriggers(QuantifierExpr quantifier) { + internal static bool WantsAutoTriggers(QuantifierExpr quantifier) { Contract.Requires(quantifier.SplitQuantifier == null); // Don't call this on a quantifier with a Split clause: it's not a real quantifier bool wantsAutoTriggers = true; - return !Attributes.Contains(quantifier.Attributes, "trigger") && - (!Attributes.ContainsBool(quantifier.Attributes, "autotriggers", ref wantsAutoTriggers) || wantsAutoTriggers); + return !Attributes.ContainsBool(quantifier.Attributes, "autotriggers", ref wantsAutoTriggers) || wantsAutoTriggers; + } + + 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 !Attributes.Contains(quantifier.Attributes, "trigger") && WantsAutoTriggers(quantifier); } internal static BinaryExpr.ResolvedOpcode RemoveNotInBinaryExprIn(BinaryExpr.ResolvedOpcode opcode) { diff --git a/Test/triggers/constructors-cause-matching-loops.dfy b/Test/triggers/constructors-cause-matching-loops.dfy new file mode 100644 index 00000000..61e6a66b --- /dev/null +++ b/Test/triggers/constructors-cause-matching-loops.dfy @@ -0,0 +1,11 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This file is just a small test to check that constructors do cause loops + +datatype Nat = Zero | Succ(x: Nat) +function f(n: Nat): Nat + +method M() { + assert forall s :: true || f(Succ(s)) == f(s); +} diff --git a/Test/triggers/constructors-cause-matching-loops.dfy.expect b/Test/triggers/constructors-cause-matching-loops.dfy.expect new file mode 100644 index 00000000..e7a671ab --- /dev/null +++ b/Test/triggers/constructors-cause-matching-loops.dfy.expect @@ -0,0 +1,6 @@ +constructors-cause-matching-loops.dfy(10,9): Info: Selected triggers: {Succ(s)} + Rejected triggers: + {f(s)} (may loop with "f(Succ(s))") + {f(Succ(s))} (more specific than {Succ(s)}) + +Dafny program verifier finished with 3 verified, 0 errors diff --git a/Test/triggers/function-applications-are-triggers.dfy.expect b/Test/triggers/function-applications-are-triggers.dfy.expect index 501cfa51..1214536d 100644 --- a/Test/triggers/function-applications-are-triggers.dfy.expect +++ b/Test/triggers/function-applications-are-triggers.dfy.expect @@ -1,8 +1,8 @@ function-applications-are-triggers.dfy(9,9): Info: Selected triggers: {P.requires(f)} -function-applications-are-triggers.dfy(10,9): Info: For expression {P(f) ==> f.requires(10)}: +function-applications-are-triggers.dfy(10,9): Info: For expression "P(f) ==> f.requires(10)": Selected triggers: {f(10)}, {f.requires(10)}, {P(f)} -function-applications-are-triggers.dfy(10,9): Info: For expression {P(f) ==> f(10) == 0}: +function-applications-are-triggers.dfy(10,9): Info: For expression "P(f) ==> f(10) == 0": Selected triggers: {f(10)}, {f.requires(10)}, {P(f)} function-applications-are-triggers.dfy(11,9): Info: Selected triggers: diff --git a/Test/triggers/loop-detection-is-not-too-strict.dfy.expect b/Test/triggers/loop-detection-is-not-too-strict.dfy.expect index 705c2a8c..65ea0d79 100644 --- a/Test/triggers/loop-detection-is-not-too-strict.dfy.expect +++ b/Test/triggers/loop-detection-is-not-too-strict.dfy.expect @@ -2,16 +2,16 @@ loop-detection-is-not-too-strict.dfy(15,9): Info: Selected triggers: {P(y, x)}, {P(x, y)} loop-detection-is-not-too-strict.dfy(18,9): Info: Selected triggers: {P(y, x)}, {P(x, y)} -loop-detection-is-not-too-strict.dfy(21,9): Warning: Selected triggers: {P(x, y)} (may loop with {P(x, y + 1)}) - (!) Suppressing loops would leave this expression without triggers. +loop-detection-is-not-too-strict.dfy(21,9): Warning: Selected triggers: {P(x, y)} (may loop with "P(x, y + 1)") + /!\ Suppressing loops would leave this expression without triggers. loop-detection-is-not-too-strict.dfy(26,9): Info: Selected triggers: {Q(x)} loop-detection-is-not-too-strict.dfy(27,9): Info: Selected triggers: {Q(x)} loop-detection-is-not-too-strict.dfy(28,9): Info: Selected triggers: {P(x, z)}, {P(x, 1)} loop-detection-is-not-too-strict.dfy(33,9): Info: Selected triggers: {Q(x)} loop-detection-is-not-too-strict.dfy(34,9): Info: Selected triggers: {Q(x)} -loop-detection-is-not-too-strict.dfy(36,9): Warning: Selected triggers: {Q(x)} (may loop with {Q(if z > 1 then x else 3 * z + 1)}) - (!) Suppressing loops would leave this expression without triggers. +loop-detection-is-not-too-strict.dfy(36,9): Warning: Selected triggers: {Q(x)} (may loop with "Q(if z > 1 then x else 3 * z + 1)") + /!\ Suppressing loops would leave this expression without triggers. loop-detection-is-not-too-strict.dfy(39,9): Info: Selected triggers: {Q(x)} Dafny program verifier finished with 4 verified, 0 errors diff --git a/Test/triggers/loop-detection-looks-at-ranges-too.dfy.expect b/Test/triggers/loop-detection-looks-at-ranges-too.dfy.expect index ce62d868..a32e4a60 100644 --- a/Test/triggers/loop-detection-looks-at-ranges-too.dfy.expect +++ b/Test/triggers/loop-detection-looks-at-ranges-too.dfy.expect @@ -1,6 +1,6 @@ -loop-detection-looks-at-ranges-too.dfy(11,17): Warning: Selected triggers: {P(x)} (may loop with {P(x + 1)}) - (!) Suppressing loops would leave this expression without triggers. -loop-detection-looks-at-ranges-too.dfy(13,17): Warning: Selected triggers: {P(x)} (may loop with {P(x + 1)}) - (!) Suppressing loops would leave this expression without triggers. +loop-detection-looks-at-ranges-too.dfy(11,17): Warning: Selected triggers: {P(x)} (may loop with "P(x + 1)") + /!\ Suppressing loops would leave this expression without triggers. +loop-detection-looks-at-ranges-too.dfy(13,17): Warning: Selected triggers: {P(x)} (may loop with "P(x + 1)") + /!\ Suppressing loops would leave this expression without triggers. Dafny program verifier finished with 3 verified, 0 errors diff --git a/Test/triggers/loop-detection-messages--unit-tests.dfy.expect b/Test/triggers/loop-detection-messages--unit-tests.dfy.expect index 804a0116..eba8c179 100644 --- a/Test/triggers/loop-detection-messages--unit-tests.dfy.expect +++ b/Test/triggers/loop-detection-messages--unit-tests.dfy.expect @@ -1,37 +1,37 @@ loop-detection-messages--unit-tests.dfy(11,9): Info: Selected triggers: {f(f(i))} - Rejected triggers: {f(i)} (may loop with {f(f(i))}) -loop-detection-messages--unit-tests.dfy(12,9): Warning: Selected triggers: {f(i)} (may loop with {f(i + 1)}) - (!) Suppressing loops would leave this expression without triggers. -loop-detection-messages--unit-tests.dfy(13,9): Info: Selected triggers: {f(i)} (may loop with {f(i + 1)}) -loop-detection-messages--unit-tests.dfy(15,9): Info: For expression {false ==> f(i) == f(i + 1)}: + Rejected triggers: {f(i)} (may loop with "f(f(i))") +loop-detection-messages--unit-tests.dfy(12,9): Warning: Selected triggers: {f(i)} (may loop with "f(i + 1)") + /!\ Suppressing loops would leave this expression without triggers. +loop-detection-messages--unit-tests.dfy(13,9): Info: Selected triggers: {f(i)} (may loop with "f(i + 1)") +loop-detection-messages--unit-tests.dfy(15,9): Info: For expression "false ==> f(i) == f(i + 1)": Selected triggers: {g(i)} - Rejected triggers: {f(i)} (may loop with {f(i + 1)}) -loop-detection-messages--unit-tests.dfy(15,9): Info: For expression {false ==> f(i) == g(i)}: + Rejected triggers: {f(i)} (may loop with "f(i + 1)") +loop-detection-messages--unit-tests.dfy(15,9): Info: For expression "false ==> f(i) == g(i)": Selected triggers: {g(i)}, {f(i)} -loop-detection-messages--unit-tests.dfy(16,9): Warning: For expression {false ==> f(i) == f(i + 1)}: - Selected triggers: {f(i)} (may loop with {f(i + 1)}) - (!) Suppressing loops would leave this expression without triggers. -loop-detection-messages--unit-tests.dfy(16,9): Info: For expression {false ==> f(i) == f(i)}: +loop-detection-messages--unit-tests.dfy(16,9): Warning: For expression "false ==> f(i) == f(i + 1)": + Selected triggers: {f(i)} (may loop with "f(i + 1)") + /!\ Suppressing loops would leave this expression without triggers. +loop-detection-messages--unit-tests.dfy(16,9): Info: For expression "false ==> f(i) == f(i)": Selected triggers: {f(i)} -loop-detection-messages--unit-tests.dfy(17,9): Info: For expression {false ==> f(i) == f(i + 1)}: - Selected triggers: {f(i)} (may loop with {f(i + 1)}) -loop-detection-messages--unit-tests.dfy(17,9): Info: For expression {false ==> f(i) == f(i)}: +loop-detection-messages--unit-tests.dfy(17,9): Info: For expression "false ==> f(i) == f(i + 1)": + Selected triggers: {f(i)} (may loop with "f(i + 1)") +loop-detection-messages--unit-tests.dfy(17,9): Info: For expression "false ==> f(i) == f(i)": Selected triggers: {f(i)} loop-detection-messages--unit-tests.dfy(19,9): Info: Selected triggers: {f(i)} -loop-detection-messages--unit-tests.dfy(20,9): Warning: (!) No terms found to trigger on. -loop-detection-messages--unit-tests.dfy(21,9): Info: Not generating triggers for {false ==> f(i + 1) == 0}. +loop-detection-messages--unit-tests.dfy(20,9): Warning: /!\ No terms found to trigger on. +loop-detection-messages--unit-tests.dfy(21,9): Info: Not generating triggers for "false ==> f(i + 1) == 0". Note that {:autotriggers false} can cause instabilities. Consider using {:nowarn}, {:matchingloop} (not great either), or a manual trigger instead. loop-detection-messages--unit-tests.dfy(23,9): Info: Selected triggers: {f(j), f(i)} -loop-detection-messages--unit-tests.dfy(24,9): Warning: (!) No trigger covering all quantified variables found. -loop-detection-messages--unit-tests.dfy(25,9): Info: For expression {false ==> f(i) == f(i)}: +loop-detection-messages--unit-tests.dfy(24,9): Warning: /!\ No trigger covering all quantified variables found. +loop-detection-messages--unit-tests.dfy(25,9): Info: For expression "false ==> f(i) == f(i)": Selected triggers: {g(j), f(i)} -loop-detection-messages--unit-tests.dfy(25,9): Info: For expression {false ==> g(j) == 0}: +loop-detection-messages--unit-tests.dfy(25,9): Info: For expression "false ==> g(j) == 0": Selected triggers: {g(j), f(i)} -loop-detection-messages--unit-tests.dfy(26,9): Warning: For expression {false ==> f(i) == f(i)}: - (!) No trigger covering all quantified variables found. -loop-detection-messages--unit-tests.dfy(26,9): Warning: For expression {false ==> g(j + 1) == 0}: - (!) No trigger covering all quantified variables found. -loop-detection-messages--unit-tests.dfy(27,9): Info: Not generating triggers for {false ==> f(i) == f(i)}. -loop-detection-messages--unit-tests.dfy(28,9): Info: Not generating triggers for {false ==> f(i) == f(i)}. +loop-detection-messages--unit-tests.dfy(26,9): Warning: For expression "false ==> f(i) == f(i)": + /!\ No trigger covering all quantified variables found. +loop-detection-messages--unit-tests.dfy(26,9): Warning: For expression "false ==> g(j + 1) == 0": + /!\ No trigger covering all quantified variables found. +loop-detection-messages--unit-tests.dfy(27,9): Info: Not generating triggers for "false ==> f(i) == f(i)". Note that {:autotriggers false} can cause instabilities. Consider using {:nowarn}, {:matchingloop} (not great either), or a manual trigger instead. +loop-detection-messages--unit-tests.dfy(28,9): Info: Not generating triggers for "false ==> f(i) == f(i)". Dafny program verifier finished with 4 verified, 0 errors diff --git a/Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy.expect b/Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy.expect index 58094110..e900c1f9 100644 --- a/Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy.expect +++ b/Test/triggers/looping-is-hard-to-decide-modulo-equality.dfy.expect @@ -1,10 +1,10 @@ looping-is-hard-to-decide-modulo-equality.dfy(21,9): Info: Selected triggers: {h(h(c, j), j)}, {h(c, i)}, {c in sc} - Rejected triggers: {h(c, j)} (may loop with {h(h(c, j), j)}) + Rejected triggers: {h(c, j)} (may loop with "h(h(c, j), j)") looping-is-hard-to-decide-modulo-equality.dfy(26,9): Info: Selected triggers: {f(x)} Rejected triggers: {g(f(x))} (more specific than {f(x)}) looping-is-hard-to-decide-modulo-equality.dfy(31,9): Info: Selected triggers: {old(f(f(c)))}, {f(c)}, {c in sc} - Rejected triggers: {old(f(c))} (may loop with {old(f(f(c)))}) + Rejected triggers: {old(f(c))} (may loop with "old(f(f(c)))") Dafny program verifier finished with 9 verified, 0 errors diff --git a/Test/triggers/matrix-accesses-are-triggers.dfy.expect b/Test/triggers/matrix-accesses-are-triggers.dfy.expect index 35df02f5..572fc41f 100644 --- a/Test/triggers/matrix-accesses-are-triggers.dfy.expect +++ b/Test/triggers/matrix-accesses-are-triggers.dfy.expect @@ -1,5 +1,5 @@ -matrix-accesses-are-triggers.dfy(8,11): Warning: Selected triggers: {m[i, j]} (may loop with {m[j, i + 1]}) - (!) Suppressing loops would leave this expression without triggers. +matrix-accesses-are-triggers.dfy(8,11): Warning: Selected triggers: {m[i, j]} (may loop with "m[j, i + 1]") + /!\ Suppressing loops would leave this expression without triggers. matrix-accesses-are-triggers.dfy(8,81): Error: index 0 out of range Execution trace: (0,0): anon0 diff --git a/Test/triggers/old-is-a-special-case-for-triggers.dfy.expect b/Test/triggers/old-is-a-special-case-for-triggers.dfy.expect index 313d7c16..7388a911 100644 --- a/Test/triggers/old-is-a-special-case-for-triggers.dfy.expect +++ b/Test/triggers/old-is-a-special-case-for-triggers.dfy.expect @@ -2,14 +2,14 @@ old-is-a-special-case-for-triggers.dfy(15,10): Info: Selected triggers: {old(f(c))}, {c in sc} old-is-a-special-case-for-triggers.dfy(20,10): Info: Selected triggers: {old(f(f(c)))}, {f(c)}, {c in sc} - Rejected triggers: {old(f(c))} (may loop with {old(f(f(c)))}) + Rejected triggers: {old(f(c))} (may loop with "old(f(f(c)))") old-is-a-special-case-for-triggers.dfy(21,10): Info: Selected triggers: {f(g(c))}, {g(f(c))}, {old(f(g(c)))}, {old(g(f(c)))}, {old(f(f(c)))}, {c in sc} Rejected triggers: - {g(c)} (may loop with {g(f(c))}) - {f(c)} (may loop with {f(g(c))}) - {old(g(c))} (may loop with {old(g(f(c)))}) - {old(f(c))} (may loop with {old(f(f(c)))}, {old(f(g(c)))}) + {g(c)} (may loop with "g(f(c))") + {f(c)} (may loop with "f(g(c))") + {old(g(c))} (may loop with "old(g(f(c)))") + {old(f(c))} (may loop with "old(f(f(c)))", "old(f(g(c)))") old-is-a-special-case-for-triggers.dfy(25,10): Info: Selected triggers: {old(f(c))}, {f(c)}, {c in sc} Rejected triggers: {old(g(f(c)))} (more specific than {old(f(c))}) diff --git a/Test/triggers/regression-tests.dfy.expect b/Test/triggers/regression-tests.dfy.expect index a03810fb..e780e966 100644 --- a/Test/triggers/regression-tests.dfy.expect +++ b/Test/triggers/regression-tests.dfy.expect @@ -1,3 +1,3 @@ -regression-tests.dfy(16,5): Warning: (!) No terms found to trigger on. +regression-tests.dfy(16,5): Warning: /!\ No terms found to trigger on. Dafny program verifier finished with 2 verified, 0 errors diff --git a/Test/triggers/some-proofs-only-work-without-autoTriggers.dfy b/Test/triggers/some-proofs-only-work-without-autoTriggers.dfy new file mode 100644 index 00000000..98cea392 --- /dev/null +++ b/Test/triggers/some-proofs-only-work-without-autoTriggers.dfy @@ -0,0 +1,48 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// The examples below work nicely with /autoTriggers:0, but break when we ass +// /autoTriggers. + +// The issue is that the axioms for sequences are missing a number of facts, +// which was not a problem before /autoTriggers and /stricterTriggers, but has +// become one. Here are examples of things that Dafny won’t prove with +// /autoTriggers (I would expect it wouldn’t with stricterTriggers either, +// though the second example is trickier than the first): + +method M(a: seq) { + if * { + // This fails; it needs the following axiom: + // axiom (forall s: Seq T :: + // { Seq#Take(s, Seq#Length(s)) } + // Seq#Take(s, Seq#Length(s)) == s); + assume forall x :: x in a ==> x > 0; + assert forall x :: x in a[..|a|] ==> x > 0; + } else if * { + // This fails; it needs the following axiom: + // axiom (forall s: Seq T, i: int :: + // { Seq#Index(s, i) } + // 0 <= i && i < Seq#Length(s) ==> + // Seq#Contains(s, Seq#Index(s, i))); + assume forall x :: x in a ==> x > 0; + assert forall i | 0 <= i < |a| :: a[i] > 0; + } else if * { + assume |a| > 3; + assume forall x | x in a[..3] :: x > 1; + // This fails, but here it's a lot harder to know what a good axiom would be. + assert forall x | x in a[..2] :: x > 1; + } +} + + +// In the first case, the Boogie version is +// +// Seq#Contains(Seq#Take(a#0, Seq#Length(a#0)), $Box(x#0_1)) ⟹ x#0_1 > 0 +// +// And of course Z3 picks $Box(x#0_1). The third case is similar. +// +// The problem is of course that knowing that x ∈ a[..2] doesn’t magically give +// you a term that matches x ∈ a[..3]. One could imagine introducing an extra +// symbol in the translation to put x and a together for triggering purposes, +// but that would have the same sort of issues as adding symbols for arithmetic +// operators. diff --git a/Test/triggers/some-proofs-only-work-without-autoTriggers.dfy.expect b/Test/triggers/some-proofs-only-work-without-autoTriggers.dfy.expect new file mode 100644 index 00000000..d48840b8 --- /dev/null +++ b/Test/triggers/some-proofs-only-work-without-autoTriggers.dfy.expect @@ -0,0 +1,31 @@ +some-proofs-only-work-without-autoTriggers.dfy(19,11): Info: Selected triggers: {x in a} +some-proofs-only-work-without-autoTriggers.dfy(20,11): Info: Selected triggers: {x in a[..|a|]} +some-proofs-only-work-without-autoTriggers.dfy(27,11): Info: Selected triggers: {x in a} +some-proofs-only-work-without-autoTriggers.dfy(28,11): Info: Selected triggers: {a[i]} +some-proofs-only-work-without-autoTriggers.dfy(31,11): Info: Selected triggers: {x in a[..3]} +some-proofs-only-work-without-autoTriggers.dfy(33,11): Info: Selected triggers: {x in a[..2]} +some-proofs-only-work-without-autoTriggers.dfy(20,11): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon22_Then + (0,0): anon3 + (0,0): anon23_Then + (0,0): anon5 +some-proofs-only-work-without-autoTriggers.dfy(28,11): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon25_Then + (0,0): anon9 + (0,0): anon26_Then + (0,0): anon27_Then + (0,0): anon13 +some-proofs-only-work-without-autoTriggers.dfy(33,11): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon28_Then + (0,0): anon29_Then + (0,0): anon17 + (0,0): anon30_Then + (0,0): anon19 + +Dafny program verifier finished with 1 verified, 3 errors diff --git a/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect b/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect index f5e47454..1a143edb 100644 --- a/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect +++ b/Test/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect @@ -1,10 +1,10 @@ -some-terms-do-not-look-like-the-triggers-they-match.dfy(12,17): Warning: Selected triggers: {s[x]} (may loop with {s[x + 1]}) - (!) Suppressing loops would leave this expression without triggers. -some-terms-do-not-look-like-the-triggers-they-match.dfy(15,17): Warning: For expression {x in s ==> s[x + 1] > 0}: - Selected triggers: {s[x]} (may loop with {s[x + 1]}) - (!) Suppressing loops would leave this expression without triggers. -some-terms-do-not-look-like-the-triggers-they-match.dfy(15,17): Warning: For expression {x in s ==> x + 2 !in s}: - Selected triggers: {s[x]} (may loop with {x + 2 !in s}) - (!) Suppressing loops would leave this expression without triggers. +some-terms-do-not-look-like-the-triggers-they-match.dfy(12,17): Warning: Selected triggers: {s[x]} (may loop with "s[x + 1]") + /!\ Suppressing loops would leave this expression without triggers. +some-terms-do-not-look-like-the-triggers-they-match.dfy(15,17): Warning: For expression "x in s ==> s[x + 1] > 0": + Selected triggers: {s[x]} (may loop with "s[x + 1]") + /!\ Suppressing loops would leave this expression without triggers. +some-terms-do-not-look-like-the-triggers-they-match.dfy(15,17): Warning: For expression "x in s ==> x + 2 !in s": + Selected triggers: {s[x]} (may loop with "x + 2 !in s") + /!\ Suppressing loops would leave this expression without triggers. Dafny program verifier finished with 2 verified, 0 errors diff --git a/Test/triggers/splitting-picks-the-right-tokens.dfy.expect b/Test/triggers/splitting-picks-the-right-tokens.dfy.expect index af274e75..f01ed1a0 100644 --- a/Test/triggers/splitting-picks-the-right-tokens.dfy.expect +++ b/Test/triggers/splitting-picks-the-right-tokens.dfy.expect @@ -1,14 +1,14 @@ -splitting-picks-the-right-tokens.dfy(9,11): Info: For expression {Id(x) > 1}: +splitting-picks-the-right-tokens.dfy(9,11): Info: For expression "Id(x) > 1": Selected triggers: {Id(x)} -splitting-picks-the-right-tokens.dfy(9,11): Info: For expression {x > 2}: +splitting-picks-the-right-tokens.dfy(9,11): Info: For expression "x > 2": Selected triggers: {Id(x)} -splitting-picks-the-right-tokens.dfy(9,11): Info: For expression {x > -1}: +splitting-picks-the-right-tokens.dfy(9,11): Info: For expression "x > -1": Selected triggers: {Id(x)} -splitting-picks-the-right-tokens.dfy(16,11): Info: For expression {x > 0 ==> Id(x) > 1}: +splitting-picks-the-right-tokens.dfy(16,11): Info: For expression "x > 0 ==> Id(x) > 1": Selected triggers: {Id(x)} -splitting-picks-the-right-tokens.dfy(16,11): Info: For expression {x > 0 ==> x > 2}: +splitting-picks-the-right-tokens.dfy(16,11): Info: For expression "x > 0 ==> x > 2": Selected triggers: {Id(x)} -splitting-picks-the-right-tokens.dfy(16,11): Info: For expression {x > 0 ==> x > -1}: +splitting-picks-the-right-tokens.dfy(16,11): Info: For expression "x > 0 ==> x > -1": Selected triggers: {Id(x)} splitting-picks-the-right-tokens.dfy(20,12): Error BP5002: A precondition for this call might not hold. splitting-picks-the-right-tokens.dfy(16,11): Related location: This is the precondition that might not hold. diff --git a/Test/triggers/splitting-triggers-recovers-expressivity.dfy.expect b/Test/triggers/splitting-triggers-recovers-expressivity.dfy.expect index 58c8dd2b..a8bb2345 100644 --- a/Test/triggers/splitting-triggers-recovers-expressivity.dfy.expect +++ b/Test/triggers/splitting-triggers-recovers-expressivity.dfy.expect @@ -1,32 +1,32 @@ splitting-triggers-recovers-expressivity.dfy(12,10): Info: Selected triggers: {Q(i)} - Rejected triggers: {P(i)} (may loop with {P(i + 1)}) + Rejected triggers: {P(i)} (may loop with "P(i + 1)") splitting-triggers-recovers-expressivity.dfy(17,11): Info: Selected triggers: {Q(j)} - Rejected triggers: {P(j)} (may loop with {P(j + 1)}) + Rejected triggers: {P(j)} (may loop with "P(j + 1)") splitting-triggers-recovers-expressivity.dfy(26,10): Info: Selected triggers: {Q(i)} - Rejected triggers: {P(i)} (may loop with {P(i + 1)}) + Rejected triggers: {P(i)} (may loop with "P(i + 1)") splitting-triggers-recovers-expressivity.dfy(33,11): Info: Selected triggers: {Q(j)} - Rejected triggers: {P(j)} (may loop with {P(j + 1)}) -splitting-triggers-recovers-expressivity.dfy(44,10): Info: For expression {P(i)}: + Rejected triggers: {P(j)} (may loop with "P(j + 1)") +splitting-triggers-recovers-expressivity.dfy(44,10): Info: For expression "P(i)": Selected triggers: {Q(i)}, {P(i)} -splitting-triggers-recovers-expressivity.dfy(44,10): Info: For expression {!Q(i)}: +splitting-triggers-recovers-expressivity.dfy(44,10): Info: For expression "!Q(i)": Selected triggers: {Q(i)}, {P(i)} -splitting-triggers-recovers-expressivity.dfy(44,10): Info: For expression {P(i + 1)}: +splitting-triggers-recovers-expressivity.dfy(44,10): Info: For expression "P(i + 1)": Selected triggers: {Q(i)} - Rejected triggers: {P(i)} (may loop with {P(i + 1)}) -splitting-triggers-recovers-expressivity.dfy(49,11): Info: For expression {j >= 0 ==> P(j)}: + Rejected triggers: {P(i)} (may loop with "P(i + 1)") +splitting-triggers-recovers-expressivity.dfy(49,11): Info: For expression "j >= 0 ==> P(j)": Selected triggers: {Q(j)}, {P(j)} -splitting-triggers-recovers-expressivity.dfy(49,11): Info: For expression {j >= 0 ==> Q(j) ==> P(j + 1)}: +splitting-triggers-recovers-expressivity.dfy(49,11): Info: For expression "j >= 0 ==> Q(j) ==> P(j + 1)": Selected triggers: {Q(j)} - Rejected triggers: {P(j)} (may loop with {P(j + 1)}) -splitting-triggers-recovers-expressivity.dfy(58,11): Info: For expression {i >= 0 ==> Q(i)}: + Rejected triggers: {P(j)} (may loop with "P(j + 1)") +splitting-triggers-recovers-expressivity.dfy(58,11): Info: For expression "i >= 0 ==> Q(i)": Selected triggers: {P(i)}, {Q(i)} -splitting-triggers-recovers-expressivity.dfy(58,11): Info: For expression {i >= 0 ==> P(i) ==> P(i + 1)}: +splitting-triggers-recovers-expressivity.dfy(58,11): Info: For expression "i >= 0 ==> P(i) ==> P(i + 1)": Selected triggers: - {P(i)} (may loop with {P(i + 1)}), {Q(i)} + {P(i)} (may loop with "P(i + 1)"), {Q(i)} splitting-triggers-recovers-expressivity.dfy(12,63): Error BP5003: A postcondition might not hold on this return path. splitting-triggers-recovers-expressivity.dfy(12,10): Related location: This is the postcondition that might not hold. Execution trace: diff --git a/Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy.expect b/Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy.expect index c8e1a5fa..27548ac9 100644 --- a/Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy.expect +++ b/Test/triggers/splitting-triggers-yields-better-precondition-related-errors.dfy.expect @@ -1,11 +1,11 @@ -splitting-triggers-yields-better-precondition-related-errors.dfy(7,11): Warning: For expression {y > 0}: - (!) No terms found to trigger on. -splitting-triggers-yields-better-precondition-related-errors.dfy(7,11): Warning: For expression {y < 0}: - (!) No terms found to trigger on. -splitting-triggers-yields-better-precondition-related-errors.dfy(15,11): Warning: For expression {y > 0}: - (!) No terms found to trigger on. -splitting-triggers-yields-better-precondition-related-errors.dfy(15,11): Warning: For expression {y < 0}: - (!) No terms found to trigger on. +splitting-triggers-yields-better-precondition-related-errors.dfy(7,11): Warning: For expression "y > 0": + /!\ No terms found to trigger on. +splitting-triggers-yields-better-precondition-related-errors.dfy(7,11): Warning: For expression "y < 0": + /!\ No terms found to trigger on. +splitting-triggers-yields-better-precondition-related-errors.dfy(15,11): Warning: For expression "y > 0": + /!\ No terms found to trigger on. +splitting-triggers-yields-better-precondition-related-errors.dfy(15,11): Warning: For expression "y < 0": + /!\ No terms found to trigger on. splitting-triggers-yields-better-precondition-related-errors.dfy(11,3): Error BP5002: A precondition for this call might not hold. splitting-triggers-yields-better-precondition-related-errors.dfy(7,11): Related location: This is the precondition that might not hold. splitting-triggers-yields-better-precondition-related-errors.dfy(7,34): Related location diff --git a/Test/triggers/suppressing-warnings-behaves-properly.dfy b/Test/triggers/suppressing-warnings-behaves-properly.dfy new file mode 100644 index 00000000..237269e5 --- /dev/null +++ b/Test/triggers/suppressing-warnings-behaves-properly.dfy @@ -0,0 +1,21 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This file checks that suppressing warnings works properly + +predicate f(x: int) +predicate g(x: int) + +method M() { + assert forall n :: n >= 0 || n < 0; + assert forall n {:nowarn} :: n >= 0 || n < 0; + assert forall n {:autotriggers false} :: n >= 0 || n < 0; + + assert forall n: nat :: (n != 0) == f(n) || true; + assert forall n: nat {:nowarn} :: (n != 0) == f(n) || true; + assert forall n: nat {:autotriggers false} :: (n != 0) == f(n) || true; + + assert forall n: nat :: f(n) == f(n+1) || g(n) || true; + assert forall n: nat {:nowarn} :: (n != 0) == f(n) || true; + assert forall n: nat {:autotriggers false} :: (n != 0) == f(n) || true; +} diff --git a/Test/triggers/suppressing-warnings-behaves-properly.dfy.expect b/Test/triggers/suppressing-warnings-behaves-properly.dfy.expect new file mode 100644 index 00000000..124984b1 --- /dev/null +++ b/Test/triggers/suppressing-warnings-behaves-properly.dfy.expect @@ -0,0 +1,14 @@ +suppressing-warnings-behaves-properly.dfy(10,9): Warning: /!\ No terms found to trigger on. +suppressing-warnings-behaves-properly.dfy(11,9): Info: (Suppressed warning) No terms found to trigger on. +suppressing-warnings-behaves-properly.dfy(12,9): Info: Not generating triggers for "n >= 0 || n < 0". Note that {:autotriggers false} can cause instabilities. Consider using {:nowarn}, {:matchingloop} (not great either), or a manual trigger instead. +suppressing-warnings-behaves-properly.dfy(14,9): Info: Selected triggers: {f(n)} +suppressing-warnings-behaves-properly.dfy(15,9): Warning: Selected triggers: {f(n)} + /!\ There is no warning here to suppress. +suppressing-warnings-behaves-properly.dfy(16,9): Info: Not generating triggers for "(n != 0) == f(n) || true". Note that {:autotriggers false} can cause instabilities. Consider using {:nowarn}, {:matchingloop} (not great either), or a manual trigger instead. +suppressing-warnings-behaves-properly.dfy(18,9): Info: Selected triggers: {g(n)} + Rejected triggers: {f(n)} (may loop with "f(n + 1)") +suppressing-warnings-behaves-properly.dfy(19,9): Warning: Selected triggers: {f(n)} + /!\ There is no warning here to suppress. +suppressing-warnings-behaves-properly.dfy(20,9): Info: Not generating triggers for "(n != 0) == f(n) || true". Note that {:autotriggers false} can cause instabilities. Consider using {:nowarn}, {:matchingloop} (not great either), or a manual trigger instead. + +Dafny program verifier finished with 4 verified, 0 errors diff --git a/Test/triggers/useless-triggers-are-removed.dfy.expect b/Test/triggers/useless-triggers-are-removed.dfy.expect index 2e9bbedf..d6b49a9e 100644 --- a/Test/triggers/useless-triggers-are-removed.dfy.expect +++ b/Test/triggers/useless-triggers-are-removed.dfy.expect @@ -5,11 +5,11 @@ useless-triggers-are-removed.dfy(16,11): Info: Selected triggers: {f(x)} {h(f(x))} (more specific than {f(x)}) {g(f(x))} (more specific than {f(x)}) useless-triggers-are-removed.dfy(20,11): Info: Selected triggers: {f(f(x))} - Rejected triggers: {f(x)} (may loop with {f(f(x))}) + Rejected triggers: {f(x)} (may loop with "f(f(x))") useless-triggers-are-removed.dfy(23,11): Info: Selected triggers: {g(f(x)), g(y)}, {f(y), f(x)} Rejected triggers: - {g(y), f(x)} (may loop with {g(f(y))}, {g(f(x))}) + {g(y), f(x)} (may loop with "g(f(y))", "g(f(x))") {g(f(x)), g(f(y))} (more specific than {g(f(x)), f(y)}, {g(f(y)), f(x)}, {f(y), f(x)}) {g(f(x)), f(y)} (more specific than {f(y), f(x)}) {g(f(y)), f(x)} (more specific than {f(y), f(x)}) diff --git a/Test/triggers/wf-checks-use-the-original-quantifier.dfy.expect b/Test/triggers/wf-checks-use-the-original-quantifier.dfy.expect index 6b152262..6c3e4853 100644 --- a/Test/triggers/wf-checks-use-the-original-quantifier.dfy.expect +++ b/Test/triggers/wf-checks-use-the-original-quantifier.dfy.expect @@ -1,16 +1,16 @@ -wf-checks-use-the-original-quantifier.dfy(26,11): Info: For expression {0 <= f(n)}: +wf-checks-use-the-original-quantifier.dfy(26,11): Info: For expression "0 <= f(n)": Selected triggers: {f(n)} Rejected triggers: {P(f(n))} (more specific than {f(n)}) -wf-checks-use-the-original-quantifier.dfy(26,11): Info: For expression {P(f(n))}: +wf-checks-use-the-original-quantifier.dfy(26,11): Info: For expression "P(f(n))": Selected triggers: {f(n)} Rejected triggers: {P(f(n))} (more specific than {f(n)}) -wf-checks-use-the-original-quantifier.dfy(27,11): Info: For expression {c != null}: +wf-checks-use-the-original-quantifier.dfy(27,11): Info: For expression "c != null": Selected triggers: {c'.x, c.x}, {c'.x, c in s}, {c.x, c' in s}, {c' in s, c in s} -wf-checks-use-the-original-quantifier.dfy(27,11): Info: For expression {c' != null}: +wf-checks-use-the-original-quantifier.dfy(27,11): Info: For expression "c' != null": Selected triggers: {c'.x, c.x}, {c'.x, c in s}, {c.x, c' in s}, {c' in s, c in s} -wf-checks-use-the-original-quantifier.dfy(27,11): Info: For expression {c.x == c'.x}: +wf-checks-use-the-original-quantifier.dfy(27,11): Info: For expression "c.x == c'.x": Selected triggers: {c'.x, c.x}, {c'.x, c in s}, {c.x, c' in s}, {c' in s, c in s} -- cgit v1.2.3 From 3d45aa05a023c092167d938a72adf78cf1f76fdf Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 28 Aug 2015 20:47:42 -0700 Subject: Clarify a comment --- Test/wishlist/sequences-s0-in-s.dfy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/wishlist/sequences-s0-in-s.dfy b/Test/wishlist/sequences-s0-in-s.dfy index 20127917..c221dbb2 100644 --- a/Test/wishlist/sequences-s0-in-s.dfy +++ b/Test/wishlist/sequences-s0-in-s.dfy @@ -21,5 +21,5 @@ method InSeqTriggers(s: seq, i: nat) method InSeqNoAutoTriggers(s: seq, i: nat) requires forall x {:autotriggers false} :: x in s ==> x > 0; requires |s| > 0 { - assert s[0] > 0; // Works + assert s[0] > 0; // Works (Z3 matches on $Box above) } -- cgit v1.2.3 From 95a42a224dff8eae383d93beb37a3da6a28bb0f3 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 28 Aug 2015 20:50:50 -0700 Subject: Suppress many warnings in the test suite. We already have separate tests for those, and we want the output to be the same with and without /autoTriggers. --- Test/dafny0/Comprehensions.dfy | 10 +-- Test/dafny0/DTypes.dfy | 36 ++++----- Test/dafny0/Matrix-OOB.dfy | 2 +- Test/dafny0/Matrix-OOB.dfy.expect | 2 + Test/dafny0/Modules1.dfy | 2 +- Test/dafny0/Parallel.dfy | 10 +-- Test/dafny0/Predicates.dfy | 2 +- Test/dafny0/Predicates.dfy.expect | 2 + Test/dafny0/SeqFromArray.dfy | 6 +- Test/dafny0/SeqFromArray.dfy.expect | 3 - Test/dafny0/SmallTests.dfy | 90 +++++++++++----------- Test/dafny0/SmallTests.dfy.expect | 5 +- Test/dafny0/TriggerInPredicate.dfy.expect | 4 +- Test/dafny0/columns.dfy | 4 +- Test/dafny0/columns.dfy.expect | 16 ++-- Test/dafny0/snapshots/Snapshots5.run.dfy | 2 +- Test/dafny0/snapshots/Snapshots5.run.dfy.expect | 9 +++ Test/dafny1/FindZero.dfy | 8 +- Test/dafny1/MoreInduction.dfy | 6 +- Test/dafny1/MoreInduction.dfy.expect | 2 + Test/dafny1/PriorityQueue.dfy | 32 ++++---- .../COST-verif-comp-2011-4-FloydCycleDetect.dfy | 4 +- Test/dafny3/GenericSort.dfy | 2 +- Test/dafny4/Bug60.dfy | 4 +- Test/dafny4/Bug63.dfy | 4 +- Test/dafny4/Primes.dfy | 8 +- Test/vacid0/Composite.dfy | 2 +- Test/wishlist/exists-b-exists-not-b.dfy | 4 +- 28 files changed, 145 insertions(+), 136 deletions(-) diff --git a/Test/dafny0/Comprehensions.dfy b/Test/dafny0/Comprehensions.dfy index d0436815..dd83e46c 100644 --- a/Test/dafny0/Comprehensions.dfy +++ b/Test/dafny0/Comprehensions.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" // RUN: %diff "%s.expect" "%t" method M() @@ -19,18 +19,18 @@ datatype D = A | B // have to run the resulting program to check that the compiler is doing the right thing. method Main() { - var q := set i,j | 0 <= i && i < 10 && 0 <= j && j < 3 :: i+j; + var q := set i,j | 0 <= i < 10 && 0 <= j < 3 :: i+j; PrintSet(q); q := set b: bool | true :: if b then 3 else 7; var d := set b:D | true; - var test := forall d:D :: d == A || d == B; + var test := forall d:D {:nowarn} :: d == A || d == B; // Ignoring the warning as we're only compiling here PrintSet(q); var m := set k | k in q :: 2*k; PrintSet(m); PrintSet(set k | k in q && k % 2 == 0); var sq := [30, 40, 20]; - PrintSet(set k, i | k in sq && 0 <= i && i < k && i % 7 == 0 :: k + i); - var bb := forall k, i | k in sq && 0 <= i && i < k && i % 7 == 0 :: k + i == 17; + PrintSet(set k, i | k in sq && 0 <= i < k && i % 7 == 0 :: k + i); + var bb := forall k, i {:nowarn} | k in sq && 0 <= i < k && i % 7 == 0 :: k + i == 17; // Ignoring the warning as we're only compiling here } method PrintSet(s: set) { diff --git a/Test/dafny0/DTypes.dfy b/Test/dafny0/DTypes.dfy index c8c893a0..9e36e64c 100644 --- a/Test/dafny0/DTypes.dfy +++ b/Test/dafny0/DTypes.dfy @@ -5,7 +5,7 @@ class C { var n: set; method M(v: Stack) - requires v != null; + requires v != null { var o: object := v; assert o !in n; // should be known from the types involved @@ -28,12 +28,12 @@ class C { method A1(a: CP) { var x: object := a; - assert (forall b: CP :: x == b ==> b == null); // follows from type antecedents + assert (forall b: CP {:nowarn} :: x == b ==> b == null); // follows from type antecedents } var a2x: set>; method A2(b: set>) - requires null !in b; + requires null !in b { var x: set := a2x; var y: set := b; @@ -81,7 +81,7 @@ class CP { datatype Data = Lemon | Kiwi(int) function G(d: Data): int - requires d != Data.Lemon; + requires d != Data.Lemon { match d case Lemon => G(d) @@ -101,28 +101,28 @@ class DatatypeInduction { } method Theorem0(tree: Tree) - ensures 1 <= LeafCount(tree); + ensures 1 <= LeafCount(tree) { assert (forall t: Tree :: 1 <= LeafCount(t)); } // also make sure it works for an instantiated generic datatype method Theorem1(bt: Tree, it: Tree) - ensures 1 <= LeafCount(bt); - ensures 1 <= LeafCount(it); + ensures 1 <= LeafCount(bt) + ensures 1 <= LeafCount(it) { assert (forall t: Tree :: 1 <= LeafCount(t)); assert (forall t: Tree :: 1 <= LeafCount(t)); } method NotATheorem0(tree: Tree) - ensures LeafCount(tree) % 2 == 1; + ensures LeafCount(tree) % 2 == 1 { assert (forall t: Tree :: LeafCount(t) % 2 == 1); // error: fails for Branch case } method NotATheorem1(tree: Tree) - ensures 2 <= LeafCount(tree); + ensures 2 <= LeafCount(tree) { assert (forall t: Tree :: 2 <= LeafCount(t)); // error: fails for Leaf case } @@ -140,22 +140,22 @@ class DatatypeInduction { // ----- here is a test for induction over integers method IntegerInduction_Succeeds(a: array) - requires a != null; - requires a.Length == 0 || a[0] == 0; - requires forall j :: 1 <= j && j < a.Length ==> a[j] == a[j-1]+2*j-1; + requires a != null + requires a.Length == 0 || a[0] == 0 + requires forall j {:nowarn} :: 1 <= j < a.Length ==> a[j] == a[j-1]+2*j-1 // WISH: If induction was more powerful, we wouldn't need to rely on the quantifier to produce the j-1 term. { // The following assertion can be proved by induction: - assert forall n {:induction} :: 0 <= n && n < a.Length ==> a[n] == n*n; + assert forall n {:induction} :: 0 <= n < a.Length ==> a[n] == n*n; } method IntegerInduction_Fails(a: array) - requires a != null; - requires a.Length == 0 || a[0] == 0; - requires forall j :: 1 <= j && j < a.Length ==> a[j] == a[j-1]+2*j-1; + requires a != null + requires a.Length == 0 || a[0] == 0 + requires forall j {:nowarn} :: 1 <= j < a.Length ==> a[j] == a[j-1]+2*j-1 // WISH: Same as above { // ...but the induction heuristics don't recognize the situation as one where // applying induction would be profitable: - assert forall n :: 0 <= n && n < a.Length ==> a[n] == n*n; // error reported + assert forall n :: 0 <= n < a.Length ==> a[n] == n*n; // error reported } } @@ -171,7 +171,7 @@ abstract module OpaqueTypesWithParameters { } method DifferentTypes(a: array>, b: array>) - requires a != null && b != null; + requires a != null && b != null // If P were a known type, then it would also be known that P and P // would be different types, and then the types of 'a' and 'b' would be different, // which would imply that the following postcondition would hold. diff --git a/Test/dafny0/Matrix-OOB.dfy b/Test/dafny0/Matrix-OOB.dfy index 2e5c0366..d7aacd79 100644 --- a/Test/dafny0/Matrix-OOB.dfy +++ b/Test/dafny0/Matrix-OOB.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /dprint:"%t.dprint" /printTooltips "%s" > "%t" +// RUN: %dafny /compile:0 /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" // RUN: %diff "%s.expect" "%t" // This is a regression test: OOB errors for matrices used to be reported on the diff --git a/Test/dafny0/Matrix-OOB.dfy.expect b/Test/dafny0/Matrix-OOB.dfy.expect index 94e77aa4..e2920445 100644 --- a/Test/dafny0/Matrix-OOB.dfy.expect +++ b/Test/dafny0/Matrix-OOB.dfy.expect @@ -1,3 +1,4 @@ +Matrix-OOB.dfy(12,10): Info: Selected triggers: {m[i, j]} Matrix-OOB.dfy(12,26): Error: index 0 out of range Execution trace: (0,0): anon0 @@ -6,6 +7,7 @@ Execution trace: (0,0): anon0 Matrix-OOB.dfy(13,0): Error BP5003: A postcondition might not hold on this return path. Matrix-OOB.dfy(12,10): Related location: This is the postcondition that might not hold. +Matrix-OOB.dfy(12,33): Related location Execution trace: (0,0): anon0 diff --git a/Test/dafny0/Modules1.dfy b/Test/dafny0/Modules1.dfy index 505d9b74..3ffa5a23 100644 --- a/Test/dafny0/Modules1.dfy +++ b/Test/dafny0/Modules1.dfy @@ -125,7 +125,7 @@ abstract module Regression { predicate p(m: map) lemma m(m: map) - ensures exists m :: p(var m : map := m; m); + ensures exists m {:nowarn} :: p(var m : map := m; m) // WISH: Zeta-expanding the let binding would provide a good trigger } abstract module B diff --git a/Test/dafny0/Parallel.dfy b/Test/dafny0/Parallel.dfy index e0d6491b..93a16475 100644 --- a/Test/dafny0/Parallel.dfy +++ b/Test/dafny0/Parallel.dfy @@ -210,7 +210,7 @@ class TwoState_C { ghost var data: int } // contexts are not allowed to allocate state. Callers of this ghost method will know // that the postcondition is tantamount to 'false'. ghost method TwoState0(y: int) - ensures exists o: TwoState_C :: o != null && fresh(o) + ensures exists o: TwoState_C {:nowarn} :: o != null && fresh(o) method TwoState_Main0() { forall x { TwoState0(x); } @@ -236,7 +236,7 @@ method X_Legit(c: TwoState_C) method TwoState_Main2() { forall x: int - ensures exists o: TwoState_C :: o != null && fresh(o) + ensures exists o: TwoState_C {:nowarn} :: o != null && fresh(o) { TwoState0(x); } @@ -252,7 +252,7 @@ method TwoState_Main2() method TwoState_Main3() { forall x: int - ensures exists o: TwoState_C :: o != null && fresh(o) + ensures exists o: TwoState_C {:nowarn} :: o != null && fresh(o) { assume false; // (there's no other way to achieve this forall-statement postcondition) } @@ -309,12 +309,12 @@ predicate ThProperty(step: nat, t: Nat, r: nat) { match t case Zero => true - case Succ(o) => step>0 && exists ro:nat :: ThProperty(step-1, o, ro) + case Succ(o) => step>0 && exists ro:nat, ss | ss == step-1 :: ThProperty(ss, o, ro) //WISH: ss should be autogrnerated. Note that step is not a bound variable. } lemma Th(step: nat, t: Nat, r: nat) requires t.Succ? && ThProperty(step, t, r) // the next line follows from the precondition and the definition of ThProperty - ensures exists ro:nat :: ThProperty(step-1, t.tail, ro) + ensures exists ro:nat, ss | ss == step-1 :: ThProperty(ss, t.tail, ro) //WISH same as above { } diff --git a/Test/dafny0/Predicates.dfy b/Test/dafny0/Predicates.dfy index 737dacd2..f8b3355d 100644 --- a/Test/dafny0/Predicates.dfy +++ b/Test/dafny0/Predicates.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 "%s" > "%t" // RUN: %diff "%s.expect" "%t" module A { diff --git a/Test/dafny0/Predicates.dfy.expect b/Test/dafny0/Predicates.dfy.expect index 2d7ea6f1..36c9dfdd 100644 --- a/Test/dafny0/Predicates.dfy.expect +++ b/Test/dafny0/Predicates.dfy.expect @@ -17,10 +17,12 @@ Execution trace: (0,0): anon0 Predicates.dfy(164,4): Error BP5003: A postcondition might not hold on this return path. Predicates.dfy(163,14): Related location: This is the postcondition that might not hold. +Predicates.dfy(163,42): Related location Execution trace: (0,0): anon0 Predicates.dfy[Q1](154,4): Error BP5003: A postcondition might not hold on this return path. Predicates.dfy[Q1](153,14): Related location: This is the postcondition that might not hold. +Predicates.dfy[Q1](153,45): Related location Execution trace: (0,0): anon0 diff --git a/Test/dafny0/SeqFromArray.dfy b/Test/dafny0/SeqFromArray.dfy index 629c5045..cf889804 100644 --- a/Test/dafny0/SeqFromArray.dfy +++ b/Test/dafny0/SeqFromArray.dfy @@ -53,7 +53,7 @@ method L(a: array, c: array, n: nat) case A == C => assert forall i :: 0 <= i < h ==> A[i] == C[i]; case A == C => - assert forall i :: 0 <= i < h ==> a[n+i] == c[n+i]; + assert forall i :: n <= i < n + h ==> a[i] == c[i]; case true => } } @@ -73,13 +73,13 @@ method M(a: array, c: array, m: nat, n: nat, k: nat, l: nat) } else if * { assert forall i :: 0 <= i < n ==> A[i] == C[i]; } else if * { - assert forall i :: k <= i < k+n ==> A[i-k] == C[i-k]; + assert forall i {:nowarn} :: k <= i < k+n ==> A[i-k] == C[i-k]; } else if * { assert forall i :: 0 <= i < n ==> A[i] == a[k+i]; } else if * { assert forall i :: 0 <= i < n ==> C[i] == c[l+i]; } else if * { - assert forall i :: 0 <= i < n ==> a[k+i] == c[l+i]; + assert forall i {:nowarn} :: 0 <= i < n ==> a[k+i] == c[l+i]; } } case l+m <= c.Length && forall i :: 0 <= i < m ==> a[i] == c[l+i] => diff --git a/Test/dafny0/SeqFromArray.dfy.expect b/Test/dafny0/SeqFromArray.dfy.expect index 5395e298..af845d3e 100644 --- a/Test/dafny0/SeqFromArray.dfy.expect +++ b/Test/dafny0/SeqFromArray.dfy.expect @@ -1,6 +1,3 @@ -SeqFromArray.dfy(56,13): Warning: (!) No terms found to trigger on. -SeqFromArray.dfy(76,17): Warning: (!) No terms found to trigger on. -SeqFromArray.dfy(82,17): Warning: (!) No terms found to trigger on. Dafny program verifier finished with 10 verified, 0 errors Program compiled successfully diff --git a/Test/dafny0/SmallTests.dfy b/Test/dafny0/SmallTests.dfy index e9c2beb4..ba009b83 100644 --- a/Test/dafny0/SmallTests.dfy +++ b/Test/dafny0/SmallTests.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint.dfy" "%s" > "%t" +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint.dfy" /autoTriggers:1 "%s" > "%t" // RUN: %dafny /noVerify /compile:0 "%t.dprint.dfy" >> "%t" // RUN: %diff "%s.expect" "%t" @@ -35,11 +35,11 @@ class Node { } method Sequence(s: seq, j: int, b: bool, c: bool) returns (t: seq) - requires 10 <= |s|; - requires 8 <= j && j < |s|; - ensures |t| == |s|; - ensures t[8] == s[8] || t[9] == s[9]; - ensures t[j] == b; + requires 10 <= |s| + requires 8 <= j < |s| + ensures |t| == |s| + ensures t[8] == s[8] || t[9] == s[9] + ensures t[j] == b { if (c) { t := s[j := b]; @@ -49,14 +49,14 @@ class Node { } method Max0(x: int, y: int) returns (r: int) - ensures r == (if x < y then y else x); + ensures r == (if x < y then y else x) { if (x < y) { r := y; } else { r := x; } } method Max1(x: int, y: int) returns (r: int) - ensures r == x || r == y; - ensures x <= r && y <= r; + ensures r == x || r == y + ensures x <= r && y <= r { r := if x < y then y else x; } @@ -122,12 +122,12 @@ class Modifies { method C(b: bool) modifies this; - ensures !b ==> x == old(x) && next == old(next); + ensures !b ==> x == old(x) && next == old(next) { } method D(p: Modifies, y: int) - requires p != null; + requires p != null { if (y == 3) { p.C(true); // error: may violate modifies clause @@ -230,15 +230,15 @@ class InitCalls { method Init(y: int) modifies this; - ensures z == y; + ensures z == y { z := y; } method InitFromReference(q: InitCalls) - requires q != null && 15 <= q.z; + requires q != null && 15 <= q.z modifies this; - ensures p == q; + ensures p == q { p := q; } @@ -265,35 +265,35 @@ class InitCalls { // --------------- some tests with quantifiers and ranges ---------------------- method QuantifierRange0(a: seq, x: T, y: T, N: int) - requires 0 <= N && N <= |a|; - requires forall k | 0 <= k && k < N :: a[k] != x; - requires exists k | 0 <= k && k < N :: a[k] == y; - ensures forall k :: 0 <= k && k < N ==> a[k] != x; // same as the precondition, but using ==> instead of | - ensures exists k :: 0 <= k && k < N && a[k] == y; // same as the precondition, but using && instead of | + requires 0 <= N <= |a| + requires forall k | 0 <= k < N :: a[k] != x + requires exists k | 0 <= k < N :: a[k] == y + ensures forall k :: 0 <= k < N ==> a[k] != x; // same as the precondition, but using ==> instead of | + ensures exists k :: 0 <= k < N && a[k] == y; // same as the precondition, but using && instead of | { assert x != y; } method QuantifierRange1(a: seq, x: T, y: T, N: int) - requires 0 <= N && N <= |a|; - requires forall k :: 0 <= k && k < N ==> a[k] != x; - requires exists k :: 0 <= k && k < N && a[k] == y; - ensures forall k | 0 <= k && k < N :: a[k] != x; // same as the precondition, but using | instead of ==> - ensures exists k | 0 <= k && k < N :: a[k] == y; // same as the precondition, but using | instead of && + requires 0 <= N <= |a| + requires forall k :: 0 <= k < N ==> a[k] != x + requires exists k :: 0 <= k < N && a[k] == y + ensures forall k | 0 <= k < N :: a[k] != x; // same as the precondition, but using | instead of ==> + ensures exists k | 0 <= k < N :: a[k] == y; // same as the precondition, but using | instead of && { assert x != y; } method QuantifierRange2(a: seq, x: T, y: T, N: int) - requires 0 <= N && N <= |a|; - requires exists k | 0 <= k && k < N :: a[k] == y; - ensures forall k | 0 <= k && k < N :: a[k] == y; // error + requires 0 <= N <= |a| + requires exists k | 0 <= k < N :: a[k] == y + ensures forall k | 0 <= k < N :: a[k] == y; // error { assert N != 0; if (N == 1) { - assert forall k | a[if 0 <= k && k < N then k else 0] != y :: k < 0 || N <= k; // in this case, the precondition holds trivially + assert forall k {:nowarn} | a[if 0 <= k < N then k else 0] != y :: k < 0 || N <= k; // in this case, the precondition holds trivially } - if (forall k | 0 <= k && k < N :: a[k] == x) { + if (forall k | 0 <= k < N :: a[k] == x) { assert x == y; } } @@ -301,8 +301,8 @@ method QuantifierRange2(a: seq, x: T, y: T, N: int) // ----------------------- tests that involve sequences of boxes -------- ghost method M(zeros: seq, Z: bool) - requires 1 <= |zeros| && Z == false; - requires forall k :: 0 <= k && k < |zeros| ==> zeros[k] == Z; + requires 1 <= |zeros| && Z == false + requires forall k :: 0 <= k < |zeros| ==> zeros[k] == Z { var x := [Z]; assert zeros[0..1] == [Z]; @@ -312,7 +312,7 @@ class SomeType { var x: int; method DoIt(stack: seq) - requires null !in stack; + requires null !in stack modifies stack; { forall n | n in stack { @@ -333,7 +333,7 @@ method TestSequences0() } else { assert 2 in s; assert 0 in s; - assert exists n :: n in s && -3 <= n && n < 2; + assert exists n :: n in s && -3 <= n < 2; } assert 7 in s; // error } @@ -399,7 +399,7 @@ class Test { function F(b: bool): int // The if-then-else in the following line was once translated incorrectly, // incorrectly causing the postcondition to verify - ensures if b then F(b) == 5 else F(b) == 6; + ensures if b then F(b) == 5 else F(b) == 6 { 5 } @@ -430,10 +430,10 @@ class AttributeTests { } method testAttributes0() returns (r: AttributeTests) - ensures {:boolAttr true} true; - ensures {:boolAttr false} true; - ensures {:intAttr 0} true; - ensures {:intAttr 1} true; + ensures {:boolAttr true} true + ensures {:boolAttr false} true + ensures {:intAttr 0} true + ensures {:intAttr 1} true modifies {:boolAttr true} this`f; modifies {:boolAttr false} this`f; modifies {:intAttr 0} this`f; @@ -541,7 +541,7 @@ method TestNotNot() // ----------------------- Assign-such-that statements ------- method AssignSuchThat0(a: int, b: int) returns (x: int, y: int) - ensures x == a && y == b; + ensures x == a && y == b { if (*) { x, y :| a <= x < a + 1 && b + a <= y + a && y <= b; @@ -635,7 +635,7 @@ method AssignSuchThat9() returns (q: QuiteFinite) function method LetSuchThat_P(x: int): bool method LetSuchThat0(ghost g: int) - requires LetSuchThat_P(g); + requires LetSuchThat_P(g) { var t :| LetSuchThat_P(t); // assign-such-that statement ghost var u := var q :| LetSuchThat_P(q); q + 1; // let-such-that expression @@ -710,10 +710,10 @@ class GT { { if (*) { P0(); - assert forall x: GT :: x != null ==> !fresh(x); // error: method P2 may have allocated stuff + assert forall x: GT {:nowarn} :: x != null ==> !fresh(x); // error: method P2 may have allocated stuff } else { P1(); - assert forall x: GT :: x != null ==> !fresh(x); // fine, because the ghost method does not allocate anything + assert forall x: GT {:nowarn} :: x != null ==> !fresh(x); // fine, because the ghost method does not allocate anything } } } @@ -777,20 +777,20 @@ module GenericPick { var x :| x in s; x } function SeqPick3(s: seq): U - requires exists i :: 0 <= i < |s| + requires exists i {:nowarn} :: 0 <= i < |s| { EquivalentWaysOfSayingSequenceIsNonempty(s); // I wish this wasn't needed; see comment near Seq#Length axioms in DafnyPrelude.bpl var x :| x in s; x } function SeqPick4(s: seq): U - requires exists i :: 0 <= i < |s| + requires exists i {:nowarn} :: 0 <= i < |s| { var i :| 0 <= i < |s|; s[i] } lemma EquivalentWaysOfSayingSequenceIsNonempty(s: seq) requires s != [] || |s| != 0 - || exists i :: 0 <= i < |s| + || exists i {:nowarn} :: 0 <= i < |s| ensures exists x :: x in s { assert s[0] in s; diff --git a/Test/dafny0/SmallTests.dfy.expect b/Test/dafny0/SmallTests.dfy.expect index eee0d4f1..4bd12096 100644 --- a/Test/dafny0/SmallTests.dfy.expect +++ b/Test/dafny0/SmallTests.dfy.expect @@ -107,6 +107,7 @@ Execution trace: (0,0): anon3 SmallTests.dfy(296,2): Error BP5003: A postcondition might not hold on this return path. SmallTests.dfy(290,10): Related location: This is the postcondition that might not hold. +SmallTests.dfy(290,40): Related location Execution trace: (0,0): anon0 (0,0): anon18_Else @@ -117,8 +118,8 @@ Execution trace: SmallTests.dfy(338,11): Error: assertion violation Execution trace: (0,0): anon0 - (0,0): anon8_Then - (0,0): anon7 + (0,0): anon7_Then + (0,0): anon6 SmallTests.dfy(345,9): Error: assertion violation Execution trace: (0,0): anon0 diff --git a/Test/dafny0/TriggerInPredicate.dfy.expect b/Test/dafny0/TriggerInPredicate.dfy.expect index 1cbd4034..b3fb9cc0 100644 --- a/Test/dafny0/TriggerInPredicate.dfy.expect +++ b/Test/dafny0/TriggerInPredicate.dfy.expect @@ -1,5 +1,5 @@ -TriggerInPredicate.dfy(6,32): Info: Not generating triggers for {A(x, y)}. -TriggerInPredicate.dfy(6,32): Info: Not generating triggers for {z}. +TriggerInPredicate.dfy(6,32): Info: Not generating triggers for "A(x, y)". +TriggerInPredicate.dfy(6,32): Info: Not generating triggers for "z". TriggerInPredicate.dfy(9,20): Info: Some instances of this call cannot safely be inlined. TriggerInPredicate.dfy(9,20): Info: Some instances of this call cannot safely be inlined. diff --git a/Test/dafny0/columns.dfy b/Test/dafny0/columns.dfy index e36142be..72c9ab81 100644 --- a/Test/dafny0/columns.dfy +++ b/Test/dafny0/columns.dfy @@ -3,8 +3,10 @@ // Dafny counts columns from 0, but Boogie from one, so for a while there were small bugs with that. +predicate P(x: int) + static method A(x:int) requires x > 0 { // error os 's' - assert (forall y :: y > x ==> y > 100); // error on '(' + assert (forall y: int :: P(y)); // error on '(' assert x != 1; // error on '!' assert x in {}; // error on 'i' } diff --git a/Test/dafny0/columns.dfy.expect b/Test/dafny0/columns.dfy.expect index 295ca351..0a99be69 100644 --- a/Test/dafny0/columns.dfy.expect +++ b/Test/dafny0/columns.dfy.expect @@ -1,18 +1,12 @@ -columns.dfy(6,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here -columns.dfy(7,9): Error: assertion violation +columns.dfy(8,0): Warning: module-level methods are always non-instance, so the 'static' keyword is not allowed here +columns.dfy(9,9): Error: assertion violation Execution trace: (0,0): anon0 - (0,0): anon3_Then - (0,0): anon2 -columns.dfy(8,11): Error: assertion violation +columns.dfy(10,11): Error: assertion violation Execution trace: (0,0): anon0 - (0,0): anon3_Then - (0,0): anon2 -columns.dfy(9,11): Error: assertion violation +columns.dfy(11,11): Error: assertion violation Execution trace: (0,0): anon0 - (0,0): anon3_Then - (0,0): anon2 -Dafny program verifier finished with 1 verified, 3 errors +Dafny program verifier finished with 2 verified, 3 errors diff --git a/Test/dafny0/snapshots/Snapshots5.run.dfy b/Test/dafny0/snapshots/Snapshots5.run.dfy index e0f3b16b..096df53c 100644 --- a/Test/dafny0/snapshots/Snapshots5.run.dfy +++ b/Test/dafny0/snapshots/Snapshots5.run.dfy @@ -1,2 +1,2 @@ -// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 "%S/Inputs/Snapshots5.dfy" > "%t" +// RUN: %dafny /compile:0 /verifySnapshots:2 /traceCaching:1 "%S/Inputs/Snapshots5.dfy" /autoTriggers:1 > "%t" // RUN: %diff "%s.expect" "%t" diff --git a/Test/dafny0/snapshots/Snapshots5.run.dfy.expect b/Test/dafny0/snapshots/Snapshots5.run.dfy.expect index 8148a8cf..8cc44882 100644 --- a/Test/dafny0/snapshots/Snapshots5.run.dfy.expect +++ b/Test/dafny0/snapshots/Snapshots5.run.dfy.expect @@ -1,3 +1,7 @@ +Snapshots5.v0.dfy(10,12): Warning: /!\ No terms found to trigger on. +Snapshots5.v0.dfy(13,10): Warning: /!\ No terms found to trigger on. +Snapshots5.v0.dfy(20,12): Warning: /!\ No terms found to trigger on. +Snapshots5.v0.dfy(26,11): Warning: /!\ No terms found to trigger on. Processing command (at Snapshots5.v0.dfy(3,4)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); >>> DoNothingToAssert Processing command (at Snapshots5.v0.dfy(10,40)) assert (forall b#1: bool :: true ==> b#1 || !b#1) || 0 != 0; @@ -10,6 +14,11 @@ Processing command (at Snapshots5.v0.dfy(20,40)) assert (forall b#5: bool :: tru >>> DoNothingToAssert Dafny program verifier finished with 3 verified, 0 errors +Snapshots5.v1.dfy(10,12): Warning: /!\ No terms found to trigger on. +Snapshots5.v1.dfy(13,10): Warning: /!\ No terms found to trigger on. +Snapshots5.v1.dfy(20,12): Warning: /!\ No terms found to trigger on. +Snapshots5.v1.dfy(22,10): Warning: /!\ No terms found to trigger on. +Snapshots5.v1.dfy(27,11): Warning: /!\ No terms found to trigger on. Processing command (at Snapshots5.v1.dfy(3,4)) assert (forall $o: ref, $f: Field alpha :: false ==> $_Frame[$o, $f]); >>> MarkAsFullyVerified Processing command (at Snapshots5.v1.dfy(10,40)) assert (forall b#1: bool :: true ==> b#1 || !b#1) || 0 != 0; diff --git a/Test/dafny1/FindZero.dfy b/Test/dafny1/FindZero.dfy index 0940d9e7..374555b0 100644 --- a/Test/dafny1/FindZero.dfy +++ b/Test/dafny1/FindZero.dfy @@ -3,7 +3,7 @@ method FindZero(a: array) returns (r: int) requires a != null && forall i :: 0 <= i < a.Length ==> 0 <= a[i]; - requires forall i :: 0 <= i && i+1 < a.Length ==> a[i]-1 <= a[i+1]; + requires forall i {:nowarn} :: 0 <= i && i+1 < a.Length ==> a[i]-1 <= a[i+1]; ensures 0 <= r ==> r < a.Length && a[r] == 0; ensures r < 0 ==> forall i :: 0 <= i < a.Length ==> a[i] != 0; { @@ -20,7 +20,7 @@ method FindZero(a: array) returns (r: int) lemma Lemma(a: array, k: int, m: int) requires a != null && forall i :: 0 <= i < a.Length ==> 0 <= a[i]; - requires forall i :: 0 <= i && i+1 < a.Length ==> a[i]-1 <= a[i+1]; + requires forall i {:nowarn} :: 0 <= i && i+1 < a.Length ==> a[i]-1 <= a[i+1]; requires 0 <= k; requires k < a.Length ==> m <= a[k]; ensures forall i :: k <= i < k+m && i < a.Length ==> a[i] != 0; @@ -36,7 +36,7 @@ lemma Lemma(a: array, k: int, m: int) method FindZero_GhostLoop(a: array) returns (r: int) requires a != null && forall i :: 0 <= i < a.Length ==> 0 <= a[i]; - requires forall i :: 0 <= i && i+1 < a.Length ==> a[i]-1 <= a[i+1]; + requires forall i {:nowarn} :: 0 <= i && i+1 < a.Length ==> a[i]-1 <= a[i+1]; ensures 0 <= r ==> r < a.Length && a[r] == 0; ensures r < 0 ==> forall i :: 0 <= i < a.Length ==> a[i] != 0; { @@ -63,7 +63,7 @@ method FindZero_GhostLoop(a: array) returns (r: int) method FindZero_Assert(a: array) returns (r: int) requires a != null && forall i :: 0 <= i < a.Length ==> 0 <= a[i]; - requires forall i :: 0 <= i-1 && i < a.Length ==> a[i-1]-1 <= a[i]; + requires forall i {:nowarn} :: 0 <= i-1 && i < a.Length ==> a[i-1]-1 <= a[i]; ensures 0 <= r ==> r < a.Length && a[r] == 0; ensures r < 0 ==> forall i :: 0 <= i < a.Length ==> a[i] != 0; { diff --git a/Test/dafny1/MoreInduction.dfy b/Test/dafny1/MoreInduction.dfy index 319bb8d0..bd654db5 100644 --- a/Test/dafny1/MoreInduction.dfy +++ b/Test/dafny1/MoreInduction.dfy @@ -83,12 +83,12 @@ lemma LemmaOne(n: int) { } -lemma LemmaAll_Neg() - ensures forall n :: NegFac(-n) <= -1; // error: fails to verify because of the minus in the trigger +lemma LemmaAll_Neg() //FIXME I don't understand the comment below; what trigger? + ensures forall n {:nowarn} :: NegFac(-n) <= -1; // error: fails to verify because of the minus in the trigger { } -lemma LemmaOne_Neg(n: int) +lemma LemmaOne_Neg(n: int) //FIXME What trigger? ensures NegFac(-n) <= -1; // error: fails to verify because of the minus in the trigger { } diff --git a/Test/dafny1/MoreInduction.dfy.expect b/Test/dafny1/MoreInduction.dfy.expect index 5de0ace6..7da5e2ec 100644 --- a/Test/dafny1/MoreInduction.dfy.expect +++ b/Test/dafny1/MoreInduction.dfy.expect @@ -1,5 +1,6 @@ MoreInduction.dfy(78,0): Error BP5003: A postcondition might not hold on this return path. MoreInduction.dfy(77,10): Related location: This is the postcondition that might not hold. +MoreInduction.dfy(77,32): Related location Execution trace: (0,0): anon0 MoreInduction.dfy(83,0): Error BP5003: A postcondition might not hold on this return path. @@ -8,6 +9,7 @@ Execution trace: (0,0): anon0 MoreInduction.dfy(88,0): Error BP5003: A postcondition might not hold on this return path. MoreInduction.dfy(87,10): Related location: This is the postcondition that might not hold. +MoreInduction.dfy(87,43): Related location Execution trace: (0,0): anon0 MoreInduction.dfy(93,0): Error BP5003: A postcondition might not hold on this return path. diff --git a/Test/dafny1/PriorityQueue.dfy b/Test/dafny1/PriorityQueue.dfy index 94223cba..3d2a5d78 100644 --- a/Test/dafny1/PriorityQueue.dfy +++ b/Test/dafny1/PriorityQueue.dfy @@ -12,7 +12,7 @@ class PriorityQueue { reads this, Repr; { MostlyValid() && - (forall j :: 2 <= j && j <= n ==> a[j/2] <= a[j]) + (forall j {:nowarn} :: 2 <= j && j <= n ==> a[j/2] <= a[j]) } predicate MostlyValid() @@ -50,8 +50,8 @@ class PriorityQueue { method SiftUp(k: int) requires 1 <= k && k <= n; requires MostlyValid(); - requires (forall j :: 2 <= j && j <= n && j != k ==> a[j/2] <= a[j]); - requires (forall j :: 1 <= j && j <= n ==> j/2 != k); // k is a leaf + requires (forall j {:nowarn} :: 2 <= j && j <= n && j != k ==> a[j/2] <= a[j]); + requires (forall j {:nowarn} :: 1 <= j && j <= n ==> j/2 != k); // k is a leaf modifies a; ensures Valid(); { @@ -59,8 +59,8 @@ class PriorityQueue { assert MostlyValid(); while (1 < i) invariant i <= k && MostlyValid(); - invariant (forall j :: 2 <= j && j <= n && j != i ==> a[j/2] <= a[j]); - invariant (forall j :: 1 <= j/2/2 && j/2 == i && j <= n ==> a[j/2/2] <= a[j]); + invariant (forall j {:nowarn} :: 2 <= j && j <= n && j != i ==> a[j/2] <= a[j]); + invariant (forall j {:nowarn} :: 1 <= j/2/2 && j/2 == i && j <= n ==> a[j/2/2] <= a[j]); { if (a[i/2] <= a[i]) { return; @@ -85,8 +85,8 @@ class PriorityQueue { method SiftDown(k: int) requires 1 <= k; requires MostlyValid(); - requires (forall j :: 2 <= j && j <= n && j/2 != k ==> a[j/2] <= a[j]); - requires (forall j :: 2 <= j && j <= n && 1 <= j/2/2 && j/2/2 != k ==> a[j/2/2] <= a[j]); + requires (forall j {:nowarn} :: 2 <= j && j <= n && j/2 != k ==> a[j/2] <= a[j]); + requires (forall j {:nowarn} :: 2 <= j && j <= n && 1 <= j/2/2 && j/2/2 != k ==> a[j/2/2] <= a[j]); // Alternatively, the line above can be expressed as: // requires (forall j :: 1 <= k/2 && j/2 == k && j <= n ==> a[j/2/2] <= a[j]); modifies a; @@ -95,8 +95,8 @@ class PriorityQueue { var i := k; while (2*i <= n) // while i is not a leaf invariant 1 <= i && MostlyValid(); - invariant (forall j :: 2 <= j && j <= n && j/2 != i ==> a[j/2] <= a[j]); - invariant (forall j :: 2 <= j && j <= n && 1 <= j/2/2 && j/2/2 != i ==> a[j/2/2] <= a[j]); + invariant (forall j {:nowarn} :: 2 <= j && j <= n && j/2 != i ==> a[j/2] <= a[j]); + invariant (forall j {:nowarn} :: 2 <= j && j <= n && 1 <= j/2/2 && j/2/2 != i ==> a[j/2/2] <= a[j]); { var smallestChild; if (2*i + 1 <= n && a[2*i + 1] < a[2*i]) { @@ -127,7 +127,7 @@ class PriorityQueue_Alternative { reads this, Repr; { MostlyValid() && - (forall j :: 2 <= j && j <= n ==> a[j/2] <= a[j]) + (forall j {:nowarn} :: 2 <= j && j <= n ==> a[j/2] <= a[j]) } predicate MostlyValid() @@ -164,7 +164,7 @@ class PriorityQueue_Alternative { method SiftUp() requires MostlyValid(); - requires (forall j :: 2 <= j && j <= n && j != n ==> a[j/2] <= a[j]); + requires (forall j {:nowarn} :: 2 <= j && j <= n && j != n ==> a[j/2] <= a[j]); modifies a; ensures Valid(); { @@ -172,8 +172,8 @@ class PriorityQueue_Alternative { assert MostlyValid(); while (1 < i) invariant i <= n && MostlyValid(); - invariant (forall j :: 2 <= j && j <= n && j != i ==> a[j/2] <= a[j]); - invariant (forall j :: 1 <= j/2/2 && j/2 == i && j <= n ==> a[j/2/2] <= a[j]); + invariant (forall j {:nowarn} :: 2 <= j && j <= n && j != i ==> a[j/2] <= a[j]); + invariant (forall j {:nowarn} :: 1 <= j/2/2 && j/2 == i && j <= n ==> a[j/2/2] <= a[j]); { if (a[i/2] <= a[i]) { return; @@ -197,15 +197,15 @@ class PriorityQueue_Alternative { method SiftDown() requires MostlyValid(); - requires (forall j :: 4 <= j && j <= n ==> a[j/2] <= a[j]); + requires (forall j {:nowarn} :: 4 <= j && j <= n ==> a[j/2] <= a[j]); modifies a; ensures Valid(); { var i := 1; while (2*i <= n) // while i is not a leaf invariant 1 <= i && MostlyValid(); - invariant (forall j :: 2 <= j && j <= n && j/2 != i ==> a[j/2] <= a[j]); - invariant (forall j :: 1 <= j/2/2 && j/2 == i && j <= n ==> a[j/2/2] <= a[j]); + invariant (forall j {:nowarn} :: 2 <= j && j <= n && j/2 != i ==> a[j/2] <= a[j]); + invariant (forall j {:nowarn} :: 1 <= j/2/2 && j/2 == i && j <= n ==> a[j/2/2] <= a[j]); { var smallestChild; if (2*i + 1 <= n && a[2*i + 1] < a[2*i]) { diff --git a/Test/dafny2/COST-verif-comp-2011-4-FloydCycleDetect.dfy b/Test/dafny2/COST-verif-comp-2011-4-FloydCycleDetect.dfy index 2aa14db7..72250f99 100644 --- a/Test/dafny2/COST-verif-comp-2011-4-FloydCycleDetect.dfy +++ b/Test/dafny2/COST-verif-comp-2011-4-FloydCycleDetect.dfy @@ -164,7 +164,7 @@ class Node { invariant 0 <= t < h && Nexxxt(t, S) == tortoise && Nexxxt(h, S) == hare; // What follows of the invariant is for proving termination: invariant h == 1 + 2*t && t <= A + B; - invariant forall k :: 0 <= k < t ==> Nexxxt(k, S) != Nexxxt(1+2*k, S); + invariant forall k {:nowarn} :: 0 <= k < t ==> Nexxxt(k, S) != Nexxxt(1+2*k, S); decreases A + B - t; { if hare == null || hare.next == null { @@ -225,7 +225,7 @@ class Node { requires 0 <= a && 1 <= b; requires forall k,l :: 0 <= k < l < a ==> Nexxxt(k, S) != Nexxxt(l, S); requires Nexxxt(a, S) == null || Nexxxt(a, S).Nexxxt(b, S) == Nexxxt(a, S); - ensures exists T :: 0 <= T < a+b && Nexxxt(T, S) == Nexxxt(1+2*T, S); + ensures exists T {:nowarn} :: 0 <= T < a+b && Nexxxt(T, S) == Nexxxt(1+2*T, S); { if Nexxxt(a, S) == null { Lemma_NullIsTerminal(1+2*a, S); diff --git a/Test/dafny3/GenericSort.dfy b/Test/dafny3/GenericSort.dfy index 6bd06965..ea75c196 100644 --- a/Test/dafny3/GenericSort.dfy +++ b/Test/dafny3/GenericSort.dfy @@ -36,7 +36,7 @@ abstract module Sort { requires a != null && 0 <= low <= high <= a.Length reads a { - forall i :: low < i < high ==> O.Leq(a[i-1], a[i]) + forall i {:nowarn} :: low < i < high ==> O.Leq(a[i-1], a[i]) } // ...but we show that property to imply all pairs to be sorted. The proof of this // lemma uses the transitivity property. diff --git a/Test/dafny4/Bug60.dfy b/Test/dafny4/Bug60.dfy index 5340ad6b..c433451c 100644 --- a/Test/dafny4/Bug60.dfy +++ b/Test/dafny4/Bug60.dfy @@ -9,5 +9,5 @@ method Main() print (s, m), "\n"; print (|s|, |m|), "\n"; print(set s | s in m), "\n"; - print (forall x :: x in (map [1:=10, 2:=20]) ==> x > 0), "\n"; -} \ No newline at end of file + print (forall x {:nowarn} :: x in (map [1:=10, 2:=20]) ==> x > 0), "\n"; +} diff --git a/Test/dafny4/Bug63.dfy b/Test/dafny4/Bug63.dfy index 86aad232..39cbae1b 100644 --- a/Test/dafny4/Bug63.dfy +++ b/Test/dafny4/Bug63.dfy @@ -8,6 +8,6 @@ method M() method Client() { - assume forall o: object :: o != null ==> false; + assume forall o: object {:nowarn} :: o != null ==> false; M(); -} \ No newline at end of file +} diff --git a/Test/dafny4/Primes.dfy b/Test/dafny4/Primes.dfy index b0bb7527..fd64b45e 100644 --- a/Test/dafny4/Primes.dfy +++ b/Test/dafny4/Primes.dfy @@ -3,7 +3,7 @@ predicate IsPrime(n: int) { - 2 <= n && forall m :: 2 <= m < n ==> n % m != 0 + 2 <= n && forall m {:nowarn} :: 2 <= m < n ==> n % m != 0 // WISH It would be great to think about the status of modulo as a trigger } // The following theorem shows that there is an infinite number of primes @@ -167,8 +167,8 @@ lemma Composite(c: int) returns (a: int, b: int) calc { true; !IsPrime(c); - !(2 <= c && forall m :: 2 <= m < c ==> c % m != 0); - exists m :: 2 <= m < c && c % m == 0; + !(2 <= c && forall m {:nowarn} :: 2 <= m < c ==> c % m != 0); + exists m {:nowarn} :: 2 <= m < c && c % m == 0; } a :| 2 <= a < c && c % a == 0; b := c / a; @@ -194,7 +194,7 @@ lemma LargestElementExists(s: set) var s' := s; while true invariant s' != {} && s' <= s; - invariant forall x,y :: x in s' && y in s - s' ==> y <= x; + invariant forall x,y {:nowarn} :: x in s' && y in s - s' ==> y <= x; decreases s'; { var x :| x in s'; // pick something diff --git a/Test/vacid0/Composite.dfy b/Test/vacid0/Composite.dfy index d5551d82..bc3b5baf 100644 --- a/Test/vacid0/Composite.dfy +++ b/Test/vacid0/Composite.dfy @@ -68,7 +68,7 @@ class Composite { // sets child.parent to this: ensures child.parent == this; // leaves everything in S+U valid: - ensures (forall c :: c in S+U ==> c.Valid(S+U)); + ensures (forall c {:autotriggers false} :: c in S+U ==> c.Valid(S+U)); // We can't generate a trigger for this at the moment; if we did, we would still need to prevent TrSplitExpr from translating c in S+U to S[c] || U[c]. { if (left == null) { left := child; diff --git a/Test/wishlist/exists-b-exists-not-b.dfy b/Test/wishlist/exists-b-exists-not-b.dfy index 711c5611..2573b2f2 100644 --- a/Test/wishlist/exists-b-exists-not-b.dfy +++ b/Test/wishlist/exists-b-exists-not-b.dfy @@ -5,6 +5,6 @@ // otherwise, trigger splitting prevents `exists b :: b || not b` from verifying method M() { - assert exists b: bool :: b; // WISH - assert exists b: bool :: !b; // WISH + assert exists b : bool {:nowarn} :: b; // WISH + assert exists b : bool {:nowarn} :: !b; // WISH } -- cgit v1.2.3 From f3cfd7a9994af3518655bc4d1d77eeb3619b0999 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 28 Aug 2015 21:05:19 -0700 Subject: Implement workarounds for some tests that fail with /autoTriggers. The issues here are mostly with induction (wrt. to trigger selection and quantifier splitting) and with expressions like P(i, j-1) where no good choices are available. --- Test/VerifyThis2015/Problem2.dfy | 2 +- Test/dafny0/Array.dfy | 4 +++- Test/dafny0/ComputationsNeg.dfy | 2 +- Test/dafny0/MultiSets.dfy | 5 ++++- Test/dafny1/Induction.dfy | 8 ++++---- Test/dafny2/COST-verif-comp-2011-3-TwoDuplicates.dfy | 4 ++-- Test/dafny3/InductionVsCoinduction.dfy | 2 +- 7 files changed, 16 insertions(+), 11 deletions(-) diff --git a/Test/VerifyThis2015/Problem2.dfy b/Test/VerifyThis2015/Problem2.dfy index 1c7deffd..86b4a019 100644 --- a/Test/VerifyThis2015/Problem2.dfy +++ b/Test/VerifyThis2015/Problem2.dfy @@ -315,7 +315,7 @@ lemma GcdDecrease(a: int, b: int) ensures Gcd(a, b) == Gcd(a - b, b) { var k := Gcd(a - b, b); - assert DividesBoth(k, a-b, b) && forall m :: DividesBoth(m, a-b, b) ==> m <= k; + assert DividesBoth(k, a-b, b) && forall m, mm :: mm == a - b ==> DividesBoth(m, mm, b) ==> m <= k; // WISH: auto-generate 'mm' var n := DividesProperty(k, a-b); assert n*k == a-b; var p := DividesProperty(k, b); diff --git a/Test/dafny0/Array.dfy b/Test/dafny0/Array.dfy index 391ca5f7..309e9248 100644 --- a/Test/dafny0/Array.dfy +++ b/Test/dafny0/Array.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t" // RUN: %diff "%s.expect" "%t" class A { @@ -327,3 +327,5 @@ module DtypeRegression { } } } + +// WISH: autoTriggers disabled because of induction diff --git a/Test/dafny0/ComputationsNeg.dfy b/Test/dafny0/ComputationsNeg.dfy index 0c539117..b9425d64 100644 --- a/Test/dafny0/ComputationsNeg.dfy +++ b/Test/dafny0/ComputationsNeg.dfy @@ -16,7 +16,7 @@ predicate ThProperty(step: nat, t: Nat, r: nat) { match t case Zero => true - case Succ(o) => step>0 && exists ro:nat :: ThProperty(step-1, o, ro) + case Succ(o) => step>0 && exists ro:nat, ss :: ss == step-1 ==> ThProperty(ss, o, ro) // WISH: auto-generate ss } ghost method test_ThProperty() ensures ThProperty(10, Succ(Zero), 0); diff --git a/Test/dafny0/MultiSets.dfy b/Test/dafny0/MultiSets.dfy index 3535f857..ba075fc3 100644 --- a/Test/dafny0/MultiSets.dfy +++ b/Test/dafny0/MultiSets.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:0 "%s" > "%t" // RUN: %diff "%s.expect" "%t" method test1() @@ -295,3 +295,6 @@ lemma Set_and_Multiset_Cardinalities(x: int, y: int) assert |multiset{x,y}| == 2; } } + +// AutoTriggers explicitly removed, as simplifications of set expressions such +// as x in {1,2} cause invalid terms to appear in the triggers diff --git a/Test/dafny1/Induction.dfy b/Test/dafny1/Induction.dfy index 3445dab9..e2cd4ade 100644 --- a/Test/dafny1/Induction.dfy +++ b/Test/dafny1/Induction.dfy @@ -53,7 +53,7 @@ class IntegerInduction { } lemma DoItAllInOneGo() - ensures (forall n :: 0 <= n ==> + ensures (forall n {:split false} :: 0 <= n ==> // WISH reenable quantifier splitting here. This will only work once we generate induction hypotheses at the Dafny level. SumOfCubes(n) == Gauss(n) * Gauss(n) && 2 * Gauss(n) == n*(n+1)); { @@ -148,11 +148,11 @@ class IntegerInduction { // Proving the "<==" case is simple; it's the "==>" case that requires induction. // The example uses an attribute that requests induction on just "j". However, the proof also // goes through by applying induction on both bound variables. - function method IsSorted(s: seq): bool - ensures IsSorted(s) ==> (forall i,j {:induction j} :: 0 <= i && i < j && j < |s| ==> s[i] <= s[j]); + function method IsSorted(s: seq): bool //WISH remove autotriggers false + ensures IsSorted(s) ==> (forall i,j {:induction j} {:autotriggers false} :: 0 <= i < j < |s| ==> s[i] <= s[j]); ensures (forall i,j :: 0 <= i && i < j && j < |s| ==> s[i] <= s[j]) ==> IsSorted(s); { - (forall i :: 1 <= i && i < |s| ==> s[i-1] <= s[i]) + (forall i {:nowarn} :: 1 <= i && i < |s| ==> s[i-1] <= s[i]) } } diff --git a/Test/dafny2/COST-verif-comp-2011-3-TwoDuplicates.dfy b/Test/dafny2/COST-verif-comp-2011-3-TwoDuplicates.dfy index 72a22cfd..4c702674 100644 --- a/Test/dafny2/COST-verif-comp-2011-3-TwoDuplicates.dfy +++ b/Test/dafny2/COST-verif-comp-2011-3-TwoDuplicates.dfy @@ -93,8 +93,8 @@ method Search(a: array) returns (p: int, q: int) invariant forall j :: 0 <= j < d.Length ==> (d[j] == -1 && forall k :: 0 <= k < i ==> a[k] != j) || (0 <= d[j] < i && a[d[j]] == j); - invariant p == q ==> IsDuplicate(a, p); - invariant forall k :: 0 <= k < i && IsPrefixDuplicate(a, i, a[k]) ==> p == q == a[k]; + invariant p == q ==> IsDuplicate(a, p); //WISH remove the trigger on the next line + invariant forall k {:trigger old(a[k])} :: 0 <= k < i && IsPrefixDuplicate(a, i, a[k]) ==> p == q == a[k]; decreases a.Length - i; { var k := d[a[i]]; diff --git a/Test/dafny3/InductionVsCoinduction.dfy b/Test/dafny3/InductionVsCoinduction.dfy index 89fa6cc8..0074b742 100644 --- a/Test/dafny3/InductionVsCoinduction.dfy +++ b/Test/dafny3/InductionVsCoinduction.dfy @@ -80,7 +80,7 @@ lemma SAppendIsAssociative(a:Stream, b:Stream, c:Stream) { forall k:nat { SAppendIsAssociativeK(k, a, b, c); } // assert for clarity only, postcondition follows directly from it - assert (forall k:nat :: SAppend(SAppend(a, b), c) ==#[k] SAppend(a, SAppend(b, c))); + assert (forall k:nat {:autotriggers false} :: SAppend(SAppend(a, b), c) ==#[k] SAppend(a, SAppend(b, c))); //FIXME: Should Dafny generate a trigger here? If so then which one? } // Equivalent proof using the colemma syntax. -- cgit v1.2.3 From 677619075af8ef30793a2a2e078a764b21be5913 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 28 Aug 2015 22:50:37 -0700 Subject: Add a small test from a discussion with Bryan --- Test/dafny0/fun-with-slices.dfy | 19 +++++++++++++++++++ Test/dafny0/fun-with-slices.dfy.expect | 2 ++ 2 files changed, 21 insertions(+) create mode 100644 Test/dafny0/fun-with-slices.dfy create mode 100644 Test/dafny0/fun-with-slices.dfy.expect diff --git a/Test/dafny0/fun-with-slices.dfy b/Test/dafny0/fun-with-slices.dfy new file mode 100644 index 00000000..3d8da242 --- /dev/null +++ b/Test/dafny0/fun-with-slices.dfy @@ -0,0 +1,19 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// This test was contributed by Bryan. It has shown some instabilities in the past. + +method seqIntoArray(s: seq, a: array, index: nat) + requires a != null + requires index + |s| <= a.Length + modifies a + ensures a[..] == old(a[0..index]) + s + old(a[index + |s|..]) { + var i := index; + + while i < index + |s| + invariant index <= i <= index + |s| <= a.Length + invariant a[..] == old(a[0..index]) + s[0..(i-index)] + old(a[i..]) { + a[i] := s[i - index]; + i := i + 1; + } +} diff --git a/Test/dafny0/fun-with-slices.dfy.expect b/Test/dafny0/fun-with-slices.dfy.expect new file mode 100644 index 00000000..069e7767 --- /dev/null +++ b/Test/dafny0/fun-with-slices.dfy.expect @@ -0,0 +1,2 @@ + +Dafny program verifier finished with 2 verified, 0 errors -- cgit v1.2.3 From 6b1085d784e3773ad9ccbae5bd6b158c97095edc Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 28 Aug 2015 22:51:32 -0700 Subject: Align the server's default kill time with the one of the VS extension --- Source/DafnyServer/Utilities.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/DafnyServer/Utilities.cs b/Source/DafnyServer/Utilities.cs index 5e2c6f96..30d779e7 100644 --- a/Source/DafnyServer/Utilities.cs +++ b/Source/DafnyServer/Utilities.cs @@ -45,7 +45,7 @@ namespace Microsoft.Dafny { internal static void ApplyArgs(string[] args, ErrorReporter reporter) { Dafny.DafnyOptions.Install(new Dafny.DafnyOptions(reporter)); - Dafny.DafnyOptions.O.ProverKillTime = 15; //This is just a default; it can be overriden + Dafny.DafnyOptions.O.ProverKillTime = 10; //This is just a default; it can be overriden if (CommandLineOptions.Clo.Parse(args)) { DafnyOptions.O.VerifySnapshots = 2; // Use caching -- cgit v1.2.3 From 4fe2619c267b0330dc3ceaca761256794094d3cc Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Fri, 28 Aug 2015 23:13:38 -0700 Subject: Fix some tests by locally disabling auto triggers --- Test/VSComp2010/Problem2-Invert.dfy | 2 +- Test/dafny1/Rippling.dfy | 6 +++--- Test/dafny1/SchorrWaite.dfy | 6 +++--- Test/dafny4/NipkowKlein-chapter7.dfy | 4 +++- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Test/VSComp2010/Problem2-Invert.dfy b/Test/VSComp2010/Problem2-Invert.dfy index 274d86de..0cf93061 100644 --- a/Test/VSComp2010/Problem2-Invert.dfy +++ b/Test/VSComp2010/Problem2-Invert.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 "%s" > "%t" +// RUN: %dafny /compile:0 /autoTriggers:0 "%s" > "%t" // RUN: %diff "%s.expect" "%t" // VSComp 2010, problem 2, compute the inverse 'B' of a permutation 'A' and prove that 'B' is diff --git a/Test/dafny1/Rippling.dfy b/Test/dafny1/Rippling.dfy index 4d1761b1..d888a5cc 100644 --- a/Test/dafny1/Rippling.dfy +++ b/Test/dafny1/Rippling.dfy @@ -550,7 +550,7 @@ lemma P54() ensures forall m, n :: minus(add(m, n), n) == m; { // the proof of this theorem follows from two lemmas: - assert forall m, n :: minus(add(n, m), n) == m; + assert forall m, n {:autotriggers false} :: minus(add(n, m), n) == m; // FIXME: Why does Autotriggers false make things verify? assert forall m, n :: add(m, n) == add(n, m); } @@ -559,7 +559,7 @@ lemma P65() { if (*) { // the proof of this theorem follows from two lemmas: - assert forall i, m :: less(i, Suc(add(i, m))) == True; + assert forall i, m {:autotriggers false} :: less(i, Suc(add(i, m))) == True; // FIXME: Why does Autotriggers false make things verify? assert forall m, n :: add(m, n) == add(n, m); } else { // a different way to prove it uses the following lemma: @@ -572,7 +572,7 @@ lemma P67() { if (*) { // the proof of this theorem follows from two lemmas: - assert forall m, n :: leq(n, add(n, m)) == True; + assert forall m, n {:autotriggers false} :: leq(n, add(n, m)) == True; // FIXME: Why does Autotriggers false make things verify? assert forall m, n :: add(m, n) == add(n, m); } else { // a different way to prove it uses the following lemma: diff --git a/Test/dafny1/SchorrWaite.dfy b/Test/dafny1/SchorrWaite.dfy index b29a6829..50210eb1 100644 --- a/Test/dafny1/SchorrWaite.dfy +++ b/Test/dafny1/SchorrWaite.dfy @@ -180,7 +180,7 @@ class Main { ensures forall n :: n in S && n.marked ==> forall ch :: ch in n.children && ch != null ==> ch.marked // every marked node was reachable from 'root' in the pre-state: - ensures forall n :: n in S && n.marked ==> old(Reachable(root, n, S)) + ensures forall n {:autotriggers false} :: n in S && n.marked ==> old(Reachable(root, n, S)) // the structure of the graph has not changed: ensures forall n :: n in S ==> n.childrenVisited == old(n.childrenVisited) && @@ -207,7 +207,7 @@ class Main { forall j :: 0 <= j < n.childrenVisited ==> n.children[j] == null || n.children[j].marked invariant forall n :: n in stackNodes ==> n.childrenVisited < |n.children| - invariant forall n :: n in S && n.marked && n !in stackNodes && n != t ==> + invariant forall n {:autotriggers false} :: n in S && n.marked && n !in stackNodes && n != t ==> forall ch :: ch in n.children && ch != null ==> ch.marked invariant forall n :: n in S && n !in stackNodes && n != t ==> n.childrenVisited == old(n.childrenVisited) @@ -219,7 +219,7 @@ class Main { // every marked node is reachable: invariant !fresh(path); // needed to show 'path' worthy as argument to old(Reachable(...)) invariant old(ReachableVia(root, path, t, S)); - invariant forall n, pth :: n in S && n.marked && pth == n.pathFromRoot ==> !fresh(pth) + invariant forall n, pth {:nowarn} :: n in S && n.marked && pth == n.pathFromRoot ==> !fresh(pth) invariant forall n, pth :: n in S && n.marked && pth == n.pathFromRoot ==> old(ReachableVia(root, pth, n, S)) invariant forall n :: n in S && n.marked ==> old(Reachable(root, n, S)) diff --git a/Test/dafny4/NipkowKlein-chapter7.dfy b/Test/dafny4/NipkowKlein-chapter7.dfy index e694fc4b..aae94550 100644 --- a/Test/dafny4/NipkowKlein-chapter7.dfy +++ b/Test/dafny4/NipkowKlein-chapter7.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /rprint:"%t.rprint" "%s" > "%t" +// RUN: %dafny /compile:0 /rprint:"%t.rprint" /autoTriggers:0 "%s" > "%t" // RUN: %diff "%s.expect" "%t" // This file is a Dafny encoding of chapter 7 from "Concrete Semantics: With Isabelle/HOL" by @@ -360,3 +360,5 @@ lemma lemma_7_18(c: com, s: state) BigStep_SmallStepStar_Same(c, s, s'); } } + +// Autotriggers:0 added as this file relies on proving a property of the form body(f) == f -- cgit v1.2.3 From 2a442cedb2d920cb45382af4add7f05270e31207 Mon Sep 17 00:00:00 2001 From: Michael Lowell Roberts Date: Mon, 31 Aug 2015 10:54:41 -0700 Subject: fix for comparison error in prelude when using /optimize. --- Binaries/DafnyRuntime.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Binaries/DafnyRuntime.cs b/Binaries/DafnyRuntime.cs index dfb8cf38..d1a3c092 100644 --- a/Binaries/DafnyRuntime.cs +++ b/Binaries/DafnyRuntime.cs @@ -72,11 +72,11 @@ namespace Dafny } } public bool Equals(Set other) { - return this.setImpl.Equals(other.setImpl); + return this.setImpl.SetEquals(other.setImpl); } public override bool Equals(object other) { var otherSet = other as Set; - return otherSet != null && Equals(otherSet); + return otherSet != null && this.Equals(otherSet); } public override int GetHashCode() { var hashCode = 1; -- cgit v1.2.3 From 66281ddb041604d1c02d0356b48e38b9ac2c79dc Mon Sep 17 00:00:00 2001 From: leino Date: Mon, 31 Aug 2015 18:20:33 -0700 Subject: Refactored most of UnifyTypes calls into a ConstrainTypes method, preparing for way to build up and later solve type constraints. --- Source/Dafny/Resolver.cs | 517 +++++++++++++++++++++++------------------------ 1 file changed, 249 insertions(+), 268 deletions(-) diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 899d0a3d..66f8f449 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -667,13 +667,17 @@ namespace Microsoft.Dafny } private void ResolveModuleDefinition(ModuleDefinition m, ModuleSignature sig) { + Contract.Requires(AllTypeConstraints.Count == 0); + Contract.Ensures(AllTypeConstraints.Count == 0); moduleInfo = MergeSignature(sig, systemNameInfo); // resolve var datatypeDependencies = new Graph(); var codatatypeDependencies = new Graph(); int prevErrorCount = reporter.Count(ErrorLevel.Error); ResolveTopLevelDecls_Signatures(m, m.TopLevelDecls, datatypeDependencies, codatatypeDependencies); + Contract.Assert(AllTypeConstraints.Count == 0); // signature resolution does not add any type constraints ResolveAttributes(m.Attributes, new ResolveOpts(new NoContext(m.Module), false)); // Must follow ResolveTopLevelDecls_Signatures, in case attributes refer to members + SolveAllTypeConstraints(); // solve any type constraints entailed by the attributes if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { ResolveTopLevelDecls_Core(m.TopLevelDecls, datatypeDependencies, codatatypeDependencies); } @@ -1334,7 +1338,9 @@ namespace Microsoft.Dafny 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); @@ -1363,9 +1369,8 @@ namespace Microsoft.Dafny ResolveType(dd.Var.tok, dd.Var.Type, dd, ResolveTypeOptionEnum.DontInfer, null); ResolveExpression(dd.Constraint, new ResolveOpts(dd, false, true)); Contract.Assert(dd.Constraint.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(dd.Constraint.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, dd.Constraint, "newtype constraint must be of type bool (instead got {0})", dd.Constraint.Type); - } + ConstrainTypes(dd.Constraint.Type, Type.Bool, dd.Constraint, "newtype constraint must be of type bool (instead got {0})", dd.Constraint.Type); + SolveAllTypeConstraints(); if (!CheckTypeInference_Visitor.IsDetermined(dd.BaseType.NormalizeExpand())) { reporter.Error(MessageSource.Resolver, dd.tok, "newtype's base type is not fully determined; add an explicit type for '{0}'", dd.Var.Name); } @@ -1376,6 +1381,7 @@ namespace Microsoft.Dafny } // Now, we're ready for the other declarations. foreach (TopLevelDecl d in declarations) { + Contract.Assert(AllTypeConstraints.Count == 0); if (d is TraitDecl && d.TypeArgs.Count > 0) { reporter.Error(MessageSource.Resolver, d, "sorry, traits with type parameters are not supported"); } @@ -1394,6 +1400,7 @@ namespace Microsoft.Dafny ResolveClassMemberBodies(cl); } allTypeParameters.PopMarker(); + SolveAllTypeConstraints(); } if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { @@ -1863,6 +1870,135 @@ namespace Microsoft.Dafny } } + /// + /// 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. + /// + private bool ConstrainTypes(Type ty, Type expected, TopLevelDecl declForToken, string msg, params object[] args) { + Contract.Requires(ty != null); + Contract.Requires(expected != null); + Contract.Requires(declForToken != null); + Contract.Requires(msg != null); + Contract.Requires(args != null); + return ConstrainTypes(ty, expected, declForToken.tok, msg, args); + } + /// + /// 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. + /// + private bool ConstrainTypes(Type ty, Type expected, MemberDecl declForToken, string msg, params object[] args) { + Contract.Requires(ty != null); + Contract.Requires(expected != null); + Contract.Requires(declForToken != null); + Contract.Requires(msg != null); + Contract.Requires(args != null); + return ConstrainTypes(ty, expected, declForToken.tok, msg, args); + } + /// + /// 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. + /// + private bool ConstrainTypes(Type ty, Type expected, Statement stmtForToken, string msg, params object[] args) { + Contract.Requires(ty != null); + Contract.Requires(expected != null); + Contract.Requires(stmtForToken != null); + Contract.Requires(msg != null); + Contract.Requires(args != null); + return ConstrainTypes(ty, expected, stmtForToken.Tok, msg, args); + } + /// + /// 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. + /// + private bool ConstrainTypes(Type ty, Type expected, Expression exprForToken, string msg, params object[] args) { + Contract.Requires(ty != null); + Contract.Requires(expected != null); + Contract.Requires(exprForToken != null); + Contract.Requires(msg != null); + Contract.Requires(args != null); + return ConstrainTypes(ty, expected, exprForToken.tok, msg, args); + } + /// + /// 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. + /// + private bool ConstrainTypes(Type ty, Type expected, IToken tok, string msg, params object[] args) { + Contract.Requires(ty != null); + Contract.Requires(expected != null); + Contract.Requires(tok != null); + Contract.Requires(msg != null); + Contract.Requires(args != null); +#if LAZY_TYPE_CHECKING + var c = new TypeConstraint(ty, expected, tok, msg, args); + AllTypeConstraints.Add(c); + return true; +#else + if (!UnifyTypes(ty, expected)) { + reporter.Error(MessageSource.Resolver, tok, msg, args); + return false; + } + return true; +#endif + } + + public List AllTypeConstraints = new List(); + + /// + /// Solves or simplifies as many type constraints as possible + /// + void PartiallySolveTypeConstraints() { + var remainingConstraints = new List(); + foreach (var constraint in AllTypeConstraints) { + if (!constraint.CheckTenable(this)) { + remainingConstraints.Add(constraint); + } + } + AllTypeConstraints = remainingConstraints; + } + + /// + /// Attempts to fully solve all type constraints. Upon success, returns "true". + /// Upon failure, reports errors and returns "false". + /// Clears all constraints. + /// + bool SolveAllTypeConstraints() { + PartiallySolveTypeConstraints(); + foreach (var constraint in AllTypeConstraints) { + constraint.ReportAsError(reporter); + } + var success = AllTypeConstraints.Count == 0; + AllTypeConstraints.Clear(); + return success; + } + + public class TypeConstraint + { + readonly Type Ty; + readonly Type Expected; + readonly IToken Tok; + readonly string Msg; + readonly object[] MsgArgs; + public TypeConstraint(Type ty, Type expected, IToken tok, string msg, object[] msgArgs) { + Ty = ty; + Expected = expected; + Tok = tok; + Msg = msg; + MsgArgs = msgArgs; + } + /// + /// Returns "true" if constraint is tenable, "false" otherwise. + /// + /// + public bool CheckTenable(Resolver resolver) { + Contract.Requires(resolver != null); + return resolver.UnifyTypes(Ty, Expected); + } + public void ReportAsError(ErrorReporter reporter) { + Contract.Requires(reporter != null); + reporter.Error(MessageSource.Resolver, Tok, Msg, MsgArgs); + } + } + // ------------------------------------------------------------------------------------------------------ // ----- Visitors --------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------------------ @@ -1965,6 +2101,7 @@ namespace Microsoft.Dafny } void CheckTypeInference(Expression expr) { Contract.Requires(expr != null); + PartiallySolveTypeConstraints(); var c = new CheckTypeInference_Visitor(this); c.Visit(expr); } @@ -3566,9 +3703,7 @@ namespace Microsoft.Dafny foreach (Expression r in f.Req) { ResolveExpression(r, new ResolveOpts(f, false, true)); Contract.Assert(r.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(r.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, r, "Precondition must be a boolean (got {0})", r.Type); - } + ConstrainTypes(r.Type, Type.Bool, r, "Precondition must be a boolean (got {0})", r.Type); } foreach (FrameExpression fr in f.Reads) { ResolveFrameExpression(fr, true, f.IsGhost, f); @@ -3576,9 +3711,7 @@ namespace Microsoft.Dafny foreach (Expression r in f.Ens) { ResolveExpression(r, new ResolveOpts(f, false, true)); // since this is a function, the postcondition is still a one-state predicate Contract.Assert(r.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(r.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, r, "Postcondition must be a boolean (got {0})", r.Type); - } + ConstrainTypes(r.Type, Type.Bool, r, "Postcondition must be a boolean (got {0})", r.Type); } ResolveAttributes(f.Decreases.Attributes, new ResolveOpts(f, false, true)); foreach (Expression r in f.Decreases.Expressions) { @@ -3592,9 +3725,7 @@ namespace Microsoft.Dafny CheckIsNonGhost(f.Body); } Contract.Assert(f.Body.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(f.Body.Type, f.ResultType)) { - reporter.Error(MessageSource.Resolver, f, "Function body type mismatch (expected {0}, got {1})", f.ResultType, f.Body.Type); - } + ConstrainTypes(f.Body.Type, f.ResultType, f, "Function body type mismatch (expected {0}, got {1})", f.ResultType, f.Body.Type); } scope.PopMarker(); } @@ -3620,8 +3751,7 @@ namespace Microsoft.Dafny if (collType != null) { t = collType.Arg; } - if (!UnifyTypes(t, new ObjectType())) { - reporter.Error(MessageSource.Resolver, fe.E, "a {0}-clause expression must denote an object or a collection of objects (instead got {1})", readsFrame ? "reads" : "modifies", fe.E.Type); + if (!ConstrainTypes(t, new ObjectType(), fe.E, "a {0}-clause expression must denote an object or a collection of objects (instead got {1})", readsFrame ? "reads" : "modifies", fe.E.Type)) { } else if (fe.FieldName != null) { NonProxyType nptype; MemberDecl member = ResolveMember(fe.E.tok, t, fe.FieldName, out nptype); @@ -3686,9 +3816,7 @@ namespace Microsoft.Dafny ResolveAttributes(e.Attributes, new ResolveOpts(m, false, true)); ResolveExpression(e.E, new ResolveOpts(m, false, true)); Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(e.E.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, e.E, "Precondition must be a boolean (got {0})", e.E.Type); - } + ConstrainTypes(e.E.Type, Type.Bool, e.E, "Precondition must be a boolean (got {0})", e.E.Type); } ResolveAttributes(m.Mod.Attributes, new ResolveOpts(m, false, true)); foreach (FrameExpression fe in m.Mod.Expressions) { @@ -3722,9 +3850,7 @@ namespace Microsoft.Dafny ResolveAttributes(e.Attributes, new ResolveOpts(m, true, true)); ResolveExpression(e.E, new ResolveOpts(m, true, true)); Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(e.E.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, e.E, "Postcondition must be a boolean (got {0})", e.E.Type); - } + ConstrainTypes(e.E.Type, Type.Bool, e.E, "Postcondition must be a boolean (got {0})", e.E.Type); } // Resolve body @@ -3810,10 +3936,8 @@ namespace Microsoft.Dafny ResolveExpression(e, new ResolveOpts(iter, false, true)); // any type is fine, but associate this type with the corresponding _decreases field var d = iter.DecreasesFields[i]; - if (!UnifyTypes(d.Type, e.Type)) { - // bummer, there was a use--and a bad use--of the field before, so this won't be the best of error messages - reporter.Error(MessageSource.Resolver, e, "type of field {0} is {1}, but has been constrained elsewhere to be of type {2}", d.Name, e.Type, d.Type); - } + // If the following type constraint does not hold, then: Bummer, there was a use--and a bad use--of the field before, so this won't be the best of error messages + ConstrainTypes(d.Type, e.Type, e, "type of field {0} is {1}, but has been constrained elsewhere to be of type {2}", d.Name, e.Type, d.Type); } foreach (FrameExpression fe in iter.Reads.Expressions) { ResolveFrameExpression(fe, true, false, iter); @@ -3824,9 +3948,7 @@ namespace Microsoft.Dafny foreach (MaybeFreeExpression e in iter.Requires) { ResolveExpression(e.E, new ResolveOpts(iter, false, true)); Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(e.E.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, e.E, "Precondition must be a boolean (got {0})", e.E.Type); - } + ConstrainTypes(e.E.Type, Type.Bool, e.E, "Precondition must be a boolean (got {0})", e.E.Type); } scope.PopMarker(); // for the in-parameters @@ -3840,23 +3962,17 @@ namespace Microsoft.Dafny foreach (MaybeFreeExpression e in iter.YieldRequires) { ResolveExpression(e.E, new ResolveOpts(iter, false, true)); Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(e.E.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, e.E, "Yield precondition must be a boolean (got {0})", e.E.Type); - } + ConstrainTypes(e.E.Type, Type.Bool, e.E, "Yield precondition must be a boolean (got {0})", e.E.Type); } foreach (MaybeFreeExpression e in iter.YieldEnsures) { ResolveExpression(e.E, new ResolveOpts(iter, true, true)); Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(e.E.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, e.E, "Yield postcondition must be a boolean (got {0})", e.E.Type); - } + ConstrainTypes(e.E.Type, Type.Bool, e.E, "Yield postcondition must be a boolean (got {0})", e.E.Type); } foreach (MaybeFreeExpression e in iter.Ensures) { ResolveExpression(e.E, new ResolveOpts(iter, true, true)); Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(e.E.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, e.E, "Postcondition must be a boolean (got {0})", e.E.Type); - } + ConstrainTypes(e.E.Type, Type.Bool, e.E, "Postcondition must be a boolean (got {0})", e.E.Type); } ResolveAttributes(iter.Attributes, new ResolveOpts(iter, false, true)); @@ -4715,9 +4831,7 @@ namespace Microsoft.Dafny s.IsGhost = true; ResolveExpression(s.Expr, new ResolveOpts(codeContext, true, true)); Contract.Assert(s.Expr.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(s.Expr.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, s.Expr, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Expr.Type); - } + ConstrainTypes(s.Expr.Type, Type.Bool, s.Expr, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Expr.Type); } else if (stmt is PrintStmt) { PrintStmt s = (PrintStmt)stmt; @@ -4963,9 +5077,7 @@ namespace Microsoft.Dafny CheckIsNonGhost(rr.Expr); } Contract.Assert(rr.Expr.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(lhsType, rr.Expr.Type)) { - reporter.Error(MessageSource.Resolver, stmt, "RHS (of type {0}) not assignable to LHS (of type {1})", rr.Expr.Type, lhsType); - } + ConstrainTypes(lhsType, rr.Expr.Type, stmt, "RHS (of type {0}) not assignable to LHS (of type {1})", rr.Expr.Type, lhsType); } else if (s.Rhs is TypeRhs) { TypeRhs rr = (TypeRhs)s.Rhs; Type t = ResolveTypeRhs(rr, stmt, lvalueIsGhost, codeContext); @@ -4981,9 +5093,7 @@ namespace Microsoft.Dafny } } } - if (!UnifyTypes(lhsType, t)) { - reporter.Error(MessageSource.Resolver, stmt, "type {0} is not assignable to LHS (of type {1})", t, lhsType); - } + ConstrainTypes(lhsType, t, stmt, "type {0} is not assignable to LHS (of type {1})", t, lhsType); } else if (s.Rhs is HavocRhs) { // nothing else to do } else { @@ -5011,9 +5121,7 @@ namespace Microsoft.Dafny ResolveExpression(s.Guard, new ResolveOpts(codeContext, true, specContextOnly)); Contract.Assert(s.Guard.Type != null); // follows from postcondition of ResolveExpression bool successfullyResolved = reporter.Count(ErrorLevel.Error) == prevErrorCount; - if (!UnifyTypes(s.Guard.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, s.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Guard.Type); - } + ConstrainTypes(s.Guard.Type, Type.Bool, s.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Guard.Type); if (!specContextOnly && successfullyResolved) { branchesAreSpecOnly = UsesSpecFeatures(s.Guard); } @@ -5045,9 +5153,7 @@ namespace Microsoft.Dafny Contract.Assert(s.Guard.Type != null); // follows from postcondition of ResolveExpression bool successfullyResolved = reporter.Count(ErrorLevel.Error) == prevErrorCount; Translator.ComputeFreeVariables(s.Guard, fvs); - if (!UnifyTypes(s.Guard.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, s.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Guard.Type); - } + ConstrainTypes(s.Guard.Type, Type.Bool, s.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Guard.Type); if (!specContextOnly && successfullyResolved) { bodyMustBeSpecOnly = UsesSpecFeatures(s.Guard); } @@ -5058,9 +5164,7 @@ namespace Microsoft.Dafny ResolveExpression(inv.E, new ResolveOpts(codeContext, true, true)); Contract.Assert(inv.E.Type != null); // follows from postcondition of ResolveExpression Translator.ComputeFreeVariables(inv.E, fvs); - if (!UnifyTypes(inv.E.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, inv.E, "invariant is expected to be of type {0}, but is {1}", Type.Bool, inv.E.Type); - } + ConstrainTypes(inv.E.Type, Type.Bool, inv.E, "invariant is expected to be of type {0}, but is {1}", Type.Bool, inv.E.Type); } ResolveAttributes(s.Decreases.Attributes, new ResolveOpts(codeContext, true, true)); @@ -5103,9 +5207,7 @@ namespace Microsoft.Dafny foreach (MaybeFreeExpression inv in s.Invariants) { ResolveExpression(inv.E, new ResolveOpts(codeContext, true, true)); Contract.Assert(inv.E.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(inv.E.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, inv.E, "invariant is expected to be of type {0}, but is {1}", Type.Bool, inv.E.Type); - } + ConstrainTypes(inv.E.Type, Type.Bool, inv.E, "invariant is expected to be of type {0}, but is {1}", Type.Bool, inv.E.Type); } foreach (Expression e in s.Decreases.Expressions) { @@ -5131,15 +5233,11 @@ namespace Microsoft.Dafny } ResolveExpression(s.Range, new ResolveOpts(codeContext, true, specContextOnly)); Contract.Assert(s.Range.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(s.Range.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, stmt, "range restriction in forall statement must be of type bool (instead got {0})", s.Range.Type); - } + ConstrainTypes(s.Range.Type, Type.Bool, stmt, "range restriction in forall statement must be of type bool (instead got {0})", s.Range.Type); foreach (var ens in s.Ens) { ResolveExpression(ens.E, new ResolveOpts(codeContext, true, true)); Contract.Assert(ens.E.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(ens.E.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, ens.E, "ensures condition is expected to be of type {0}, but is {1}", Type.Bool, ens.E.Type); - } + ConstrainTypes(ens.E.Type, Type.Bool, ens.E, "ensures condition is expected to be of type {0}, but is {1}", Type.Bool, ens.E.Type); } // Since the range and postconditions are more likely to infer the types of the bound variables, resolve them // first (above) and only then resolve the attributes (below). @@ -5232,8 +5330,7 @@ namespace Microsoft.Dafny var e1 = s.Lines[i]; ResolveExpression(e1, new ResolveOpts(codeContext, true, true)); Contract.Assert(e1.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(e0.Type, e1.Type)) { - reporter.Error(MessageSource.Resolver, e1, "all lines in a calculation must have the same type (got {0} after {1})", e1.Type, e0.Type); + if (!ConstrainTypes(e0.Type, e1.Type, e1, "all lines in a calculation must have the same type (got {0} after {1})", e1.Type, e0.Type)) { } else { var step = s.StepOps[i - 1].StepExpr(e0, e1); // Use custom line operator ResolveExpression(step, new ResolveOpts(codeContext, true, true)); @@ -5354,16 +5451,14 @@ namespace Microsoft.Dafny if (ctor != null && i < ctor.Formals.Count) { Formal formal = ctor.Formals[i]; Type st = SubstType(formal.Type, subst); - if (!UnifyTypes(v.Type, st)) { - reporter.Error(MessageSource.Resolver, stmt, "the declared type of the formal ({0}) does not agree with the corresponding type in the constructor's signature ({1})", v.Type, st); - } + ConstrainTypes(v.Type, st, stmt, "the declared type of the formal ({0}) does not agree with the corresponding type in the constructor's signature ({1})", v.Type, st); v.IsGhost = formal.IsGhost; // update the type of the boundvars in the MatchCaseToken if (v.tok is MatchCaseToken) { MatchCaseToken mt = (MatchCaseToken)v.tok; foreach (Tuple entry in mt.varList) { - UnifyTypes(entry.Item2.Type, v.Type); + UnifyTypes(entry.Item2.Type, v.Type); // TODO: What if this unification fails? Can it? --KRML } } } @@ -5961,9 +6056,7 @@ namespace Microsoft.Dafny s.IsGhost = s.Lhss.TrueForAll(AssignStmt.LhsIsToGhost); var ec = reporter.Count(ErrorLevel.Error); ResolveExpression(s.Expr, new ResolveOpts(codeContext, true, specContextOnly)); - if (!UnifyTypes(s.Expr.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, s.Expr, "type of RHS of assign-such-that statement must be boolean (got {0})", s.Expr.Type); - } + ConstrainTypes(s.Expr.Type, Type.Bool, s.Expr, "type of RHS of assign-such-that statement must be boolean (got {0})", s.Expr.Type); if (ec == reporter.Count(ErrorLevel.Error) && !s.IsGhost && s.AssumeToken == null && !specContextOnly) { CheckIsNonGhost(s.Expr); @@ -5990,9 +6083,7 @@ namespace Microsoft.Dafny ResolveExpression(alternative.Guard, new ResolveOpts(codeContext, true, specContextOnly)); Contract.Assert(alternative.Guard.Type != null); // follows from postcondition of ResolveExpression bool successfullyResolved = reporter.Count(ErrorLevel.Error) == prevErrorCount; - if (!UnifyTypes(alternative.Guard.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, alternative.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, alternative.Guard.Type); - } + ConstrainTypes(alternative.Guard.Type, Type.Bool, alternative.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, alternative.Guard.Type); if (!specContextOnly && successfullyResolved) { isGhost = isGhost || UsesSpecFeatures(alternative.Guard); } @@ -6082,15 +6173,12 @@ namespace Microsoft.Dafny // type check the arguments for (int i = 0; i < callee.Ins.Count; i++) { Type st = SubstType(callee.Ins[i].Type, s.MethodSelect.TypeArgumentSubstitutions()); - if (!UnifyTypes(cce.NonNull(s.Args[i].Type), st)) { - reporter.Error(MessageSource.Resolver, s, "incorrect type of method in-parameter {0} (expected {1}, got {2})", i, st, s.Args[i].Type); - } + ConstrainTypes(s.Args[i].Type, st, s, "incorrect type of method in-parameter {0} (expected {1}, got {2})", i, st, s.Args[i].Type); } for (int i = 0; i < callee.Outs.Count; i++) { Type st = SubstType(callee.Outs[i].Type, s.MethodSelect.TypeArgumentSubstitutions()); var lhs = s.Lhs[i]; - if (!UnifyTypes(cce.NonNull(lhs.Type), st)) { - reporter.Error(MessageSource.Resolver, s, "incorrect type of method out-parameter {0} (expected {1}, got {2})", i, st, lhs.Type); + if (!ConstrainTypes(lhs.Type, st, s, "incorrect type of method out-parameter {0} (expected {1}, got {2})", i, st, lhs.Type)) { } else { var resolvedLhs = lhs.Resolved; if (!specContextOnly && (s.IsGhost || callee.Outs[i].IsGhost)) { @@ -6164,9 +6252,7 @@ namespace Microsoft.Dafny } } else if (lhs is SeqSelectExpr) { var ll = (SeqSelectExpr)lhs; - if (!UnifyTypes(ll.Seq.Type, ResolvedArrayType(ll.Seq.tok, 1, new InferredTypeProxy(), codeContext))) { - reporter.Error(MessageSource.Resolver, ll.Seq, "LHS of array assignment must denote an array element (found {0})", ll.Seq.Type); - } + ConstrainTypes(ll.Seq.Type, ResolvedArrayType(ll.Seq.tok, 1, new InferredTypeProxy(), codeContext), ll.Seq, "LHS of array assignment must denote an array element (found {0})", ll.Seq.Type); if (!ll.SelectOne) { reporter.Error(MessageSource.Resolver, ll.Seq, "cannot assign to a range of array elements (try the 'forall' statement)"); } @@ -6490,9 +6576,7 @@ namespace Microsoft.Dafny foreach (Expression dim in rr.ArrayDimensions) { Contract.Assert(dim != null); ResolveExpression(dim, new ResolveOpts(codeContext, true)); - if (!UnifyTypes(dim.Type, new OperationTypeProxy(true, false, false, false, false, false))) { - reporter.Error(MessageSource.Resolver, stmt, "new must use an integer-based expression for the array size (got {0} for index {1})", dim.Type, i); - } + ConstrainTypes(dim.Type, new OperationTypeProxy(true, false, false, false, false, false), stmt, "new must use an integer-based expression for the array size (got {0} for index {1})", dim.Type, i); i++; } rr.Type = ResolvedArrayType(stmt.Tok, rr.ArrayDimensions.Count, rr.EType, codeContext); @@ -6574,6 +6658,8 @@ namespace Microsoft.Dafny Contract.Requires(memberName != null); Contract.Ensures(Contract.Result() == null || Contract.ValueAtReturn(out nptype) != null); + PartiallySolveTypeConstraints(); // so that we can try to pick up the type of the receiver + nptype = null; // prepare for the worst receiverType = receiverType.NormalizeExpand(); var opProxy = receiverType as OperationTypeProxy; @@ -6965,9 +7051,7 @@ namespace Microsoft.Dafny foreach (Expression ee in e.Elements) { ResolveExpression(ee, opts); Contract.Assert(ee.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(elementType, ee.Type)) { - reporter.Error(MessageSource.Resolver, ee, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", ee.Type, elementType); - } + ConstrainTypes(elementType, ee.Type, ee, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", ee.Type, elementType); } if (expr is SetDisplayExpr) { var se = (SetDisplayExpr)expr; @@ -6984,14 +7068,10 @@ namespace Microsoft.Dafny foreach (ExpressionPair p in e.Elements) { ResolveExpression(p.A, opts); Contract.Assert(p.A.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(domainType, p.A.Type)) { - reporter.Error(MessageSource.Resolver, p.A, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", p.A.Type, domainType); - } + ConstrainTypes(domainType, p.A.Type, p.A, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", p.A.Type, domainType); ResolveExpression(p.B, opts); Contract.Assert(p.B.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(rangeType, p.B.Type)) { - reporter.Error(MessageSource.Resolver, p.B, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", p.B.Type, rangeType); - } + ConstrainTypes(rangeType, p.B.Type, p.B, "All elements of display must be of the same type (got {0}, but type of previous elements is {1})", p.B.Type, rangeType); } expr.Type = new MapType(e.Finite, domainType, rangeType); } else if (expr is NameSegment) { @@ -7074,17 +7154,13 @@ namespace Microsoft.Dafny ResolveExpression(e.Array, opts); Contract.Assert(e.Array.Type != null); // follows from postcondition of ResolveExpression Type elementType = new InferredTypeProxy(); - if (!UnifyTypes(e.Array.Type, ResolvedArrayType(e.Array.tok, e.Indices.Count, elementType, opts.codeContext))) { - reporter.Error(MessageSource.Resolver, e.Array, "array selection requires an array{0} (got {1})", e.Indices.Count, e.Array.Type); - } + ConstrainTypes(e.Array.Type, ResolvedArrayType(e.Array.tok, e.Indices.Count, elementType, opts.codeContext), e.Array, "array selection requires an array{0} (got {1})", e.Indices.Count, e.Array.Type); int i = 0; foreach (Expression idx in e.Indices) { Contract.Assert(idx != null); ResolveExpression(idx, opts); Contract.Assert(idx.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(idx.Type, new OperationTypeProxy(true, false, false, false, false, false))) { - reporter.Error(MessageSource.Resolver, idx, "array selection requires integer-based numeric indices (got {0} for index {1})", idx.Type, i); - } + ConstrainTypes(idx.Type, new OperationTypeProxy(true, false, false, false, false, false), idx, "array selection requires integer-based numeric indices (got {0} for index {1})", idx.Type, i); i++; } e.Type = elementType; @@ -7099,44 +7175,28 @@ namespace Microsoft.Dafny if (UnifyTypes(e.Seq.Type, new SeqType(elementType))) { ResolveExpression(e.Index, opts); Contract.Assert(e.Index.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(e.Index.Type, Type.Int)) { - reporter.Error(MessageSource.Resolver, e.Index, "sequence update requires integer index (got {0})", e.Index.Type); - } + ConstrainTypes(e.Index.Type, Type.Int, e.Index, "sequence update requires integer index (got {0})", e.Index.Type); ResolveExpression(e.Value, opts); Contract.Assert(e.Value.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(e.Value.Type, elementType)) { - reporter.Error(MessageSource.Resolver, e.Value, "sequence update requires the value to have the element type of the sequence (got {0})", e.Value.Type); - } + ConstrainTypes(e.Value.Type, elementType, e.Value, "sequence update requires the value to have the element type of the sequence (got {0})", e.Value.Type); expr.Type = e.Seq.Type; } else if (UnifyTypes(e.Seq.Type, new MapType(true, domainType, rangeType))) { ResolveExpression(e.Index, opts); - if (!UnifyTypes(e.Index.Type, domainType)) { - reporter.Error(MessageSource.Resolver, e.Index, "map update requires domain element to be of type {0} (got {1})", domainType, e.Index.Type); - } + ConstrainTypes(e.Index.Type, domainType, e.Index, "map update requires domain element to be of type {0} (got {1})", domainType, e.Index.Type); ResolveExpression(e.Value, opts); - if (!UnifyTypes(e.Value.Type, rangeType)) { - reporter.Error(MessageSource.Resolver, e.Value, "map update requires the value to have the range type {0} (got {1})", rangeType, e.Value.Type); - } + ConstrainTypes(e.Value.Type, rangeType, e.Value, "map update requires the value to have the range type {0} (got {1})", rangeType, e.Value.Type); expr.Type = e.Seq.Type; } else if (UnifyTypes(e.Seq.Type, new MapType(false, domainType, rangeType))) { ResolveExpression(e.Index, opts); - if (!UnifyTypes(e.Index.Type, domainType)) { - reporter.Error(MessageSource.Resolver, e.Index, "imap update requires domain element to be of type {0} (got {1})", domainType, e.Index.Type); - } + ConstrainTypes(e.Index.Type, domainType, e.Index, "imap update requires domain element to be of type {0} (got {1})", domainType, e.Index.Type); ResolveExpression(e.Value, opts); - if (!UnifyTypes(e.Value.Type, rangeType)) { - reporter.Error(MessageSource.Resolver, e.Value, "imap update requires the value to have the range type {0} (got {1})", rangeType, e.Value.Type); - } + ConstrainTypes(e.Value.Type, rangeType, e.Value, "imap update requires the value to have the range type {0} (got {1})", rangeType, e.Value.Type); expr.Type = e.Seq.Type; } else if (UnifyTypes(e.Seq.Type, new MultiSetType(elementType))) { ResolveExpression(e.Index, opts); - if (!UnifyTypes(e.Index.Type, elementType)) { - reporter.Error(MessageSource.Resolver, e.Index, "multiset update requires domain element to be of type {0} (got {1})", elementType, e.Index.Type); - } + ConstrainTypes(e.Index.Type, elementType, e.Index, "multiset update requires domain element to be of type {0} (got {1})", elementType, e.Index.Type); ResolveExpression(e.Value, opts); - if (!UnifyTypes(e.Value.Type, new OperationTypeProxy(true, false, false, false, false, false))) { - reporter.Error(MessageSource.Resolver, e.Value, "multiset update requires integer-based numeric value (got {0})", e.Value.Type); - } + ConstrainTypes(e.Value.Type, new OperationTypeProxy(true, false, false, false, false, false), e.Value, "multiset update requires integer-based numeric value (got {0})", e.Value.Type); expr.Type = e.Seq.Type; } else if (e.Seq.Type.IsDatatype) { @@ -7243,9 +7303,7 @@ namespace Microsoft.Dafny reporter.Error(MessageSource.Resolver, e.tok, "wrong number of arguments to function application (function type '{0}' expects {1}, got {2})", fnType, fnType.Arity, e.Args.Count); } else { for (var i = 0; i < fnType.Arity; i++) { - if (!UnifyTypes(fnType.Args[i], e.Args[i].Type)) { - reporter.Error(MessageSource.Resolver, e.Args[i].tok, "type mismatch for argument {0} (function expects {1}, got {2})", i, fnType.Args[i], e.Args[i].Type); - } + ConstrainTypes(fnType.Args[i], e.Args[i].Type, e.Args[i].tok, "type mismatch for argument {0} (function expects {1}, got {2})", i, fnType.Args[i], e.Args[i].Type); } } expr.Type = fnType == null ? new InferredTypeProxy() : fnType.Result; @@ -7272,15 +7330,11 @@ namespace Microsoft.Dafny Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression switch (e.Op) { case UnaryOpExpr.Opcode.Not: - if (!UnifyTypes(e.E.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, expr, "logical negation expects a boolean argument (instead got {0})", e.E.Type); - } + ConstrainTypes(e.E.Type, Type.Bool, expr, "logical negation expects a boolean argument (instead got {0})", e.E.Type); expr.Type = Type.Bool; break; case UnaryOpExpr.Opcode.Cardinality: - if (!UnifyTypes(e.E.Type, new CollectionTypeProxy(new InferredTypeProxy(), false, false))) { - reporter.Error(MessageSource.Resolver, expr, "size operator expects a collection argument (instead got {0})", e.E.Type); - } + ConstrainTypes(e.E.Type, new CollectionTypeProxy(new InferredTypeProxy(), false, false), expr, "size operator expects a collection argument (instead got {0})", e.E.Type); expr.Type = Type.Int; break; case UnaryOpExpr.Opcode.Fresh: @@ -7313,13 +7367,9 @@ namespace Microsoft.Dafny ResolveType(e.tok, e.ToType, opts.codeContext, new ResolveTypeOption(ResolveTypeOptionEnum.DontInfer), null); ResolveExpression(e.E, opts); if (e.ToType.IsNumericBased(Type.NumericPersuation.Int)) { - if (!UnifyTypes(e.E.Type, new OperationTypeProxy(true, true, false, false, false, false))) { - reporter.Error(MessageSource.Resolver, expr, "type conversion to an int-based type is allowed only from numeric types (got {0})", e.E.Type); - } + ConstrainTypes(e.E.Type, new OperationTypeProxy(true, true, false, false, false, false), expr, "type conversion to an int-based type is allowed only from numeric types (got {0})", e.E.Type); } else if (e.ToType.IsNumericBased(Type.NumericPersuation.Real)) { - if (!UnifyTypes(e.E.Type, new OperationTypeProxy(true, true, false, false, false, false))) { - reporter.Error(MessageSource.Resolver, expr, "type conversion to a real-based type is allowed only from numeric types (got {0})", e.E.Type); - } + ConstrainTypes(e.E.Type, new OperationTypeProxy(true, true, false, false, false, false), expr, "type conversion to a real-based type is allowed only from numeric types (got {0})", e.E.Type); } else { reporter.Error(MessageSource.Resolver, expr, "type conversions are not supported to this type (got {0})", e.ToType); } @@ -7337,12 +7387,8 @@ namespace Microsoft.Dafny case BinaryExpr.Opcode.Exp: case BinaryExpr.Opcode.And: case BinaryExpr.Opcode.Or: - if (!UnifyTypes(e.E0.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, expr, "first argument to {0} must be of type bool (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type); - } - if (!UnifyTypes(e.E1.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, expr, "second argument to {0} must be of type bool (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E1.Type); - } + ConstrainTypes(e.E0.Type, Type.Bool, expr, "first argument to {0} must be of type bool (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type); + ConstrainTypes(e.E1.Type, Type.Bool, expr, "second argument to {0} must be of type bool (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E1.Type); expr.Type = Type.Bool; break; @@ -7369,9 +7415,7 @@ namespace Microsoft.Dafny case BinaryExpr.Opcode.Disjoint: // TODO: the error messages are backwards from what (ideally) they should be. this is necessary because UnifyTypes can't backtrack. - if (!UnifyTypes(e.E0.Type, e.E1.Type)) { - reporter.Error(MessageSource.Resolver, expr, "arguments must have the same type (got {0} and {1})", e.E0.Type, e.E1.Type); - } + ConstrainTypes(e.E0.Type, e.E1.Type, expr, "arguments must have the same type (got {0} and {1})", e.E0.Type, e.E1.Type); if (!UnifyTypes(e.E0.Type, new SetType(true, new InferredTypeProxy())) && !UnifyTypes(e.E0.Type, new MultiSetType(new InferredTypeProxy())) && !UnifyTypes(e.E0.Type, new MapType(true, new InferredTypeProxy(), new InferredTypeProxy()))) { @@ -7384,34 +7428,25 @@ namespace Microsoft.Dafny case BinaryExpr.Opcode.Le: case BinaryExpr.Opcode.Add: { if (e.Op == BinaryExpr.Opcode.Lt && (e.E0.Type.NormalizeExpand().IsIndDatatype || e.E0.Type.IsTypeParameter)) { - if (UnifyTypes(e.E1.Type, new DatatypeProxy(false, false))) { + if (ConstrainTypes(e.E1.Type, new DatatypeProxy(false, false), expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E1.Type)) { e.ResolvedOp = BinaryExpr.ResolvedOpcode.RankLt; - } else { - reporter.Error(MessageSource.Resolver, expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E1.Type); } expr.Type = Type.Bool; } else if (e.Op == BinaryExpr.Opcode.Lt && e.E1.Type.NormalizeExpand().IsIndDatatype) { - if (UnifyTypes(e.E0.Type, new DatatypeProxy(false, true))) { + if (ConstrainTypes(e.E0.Type, new DatatypeProxy(false, true), expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E0.Type)) { e.ResolvedOp = BinaryExpr.ResolvedOpcode.RankLt; - } else { - reporter.Error(MessageSource.Resolver, expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E0.Type); } expr.Type = Type.Bool; } else { - bool err = false; bool isComparison = e.Op != BinaryExpr.Opcode.Add; - if (!UnifyTypes(e.E0.Type, new OperationTypeProxy(true, true, isComparison, true, true, true))) { - reporter.Error(MessageSource.Resolver, expr, "arguments to {0} must be of a numeric type{2} or a collection type (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, - isComparison ? ", char," : ""); - err = true; - } - if (!UnifyTypes(e.E1.Type, e.E0.Type)) { - reporter.Error(MessageSource.Resolver, expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type); - err = true; - } + var good0 = ConstrainTypes(e.E0.Type, new OperationTypeProxy(true, true, isComparison, true, true, true), + expr, "arguments to {0} must be of a numeric type{2} or a collection type (instead got {1})", + BinaryExpr.OpcodeString(e.Op), e.E0.Type, isComparison ? ", char," : ""); + var good1 = ConstrainTypes(e.E1.Type, e.E0.Type, + expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type); if (isComparison) { expr.Type = Type.Bool; - } else if (!err) { + } else if (good0 && good1) { expr.Type = e.E0.Type; } } @@ -7423,34 +7458,24 @@ namespace Microsoft.Dafny case BinaryExpr.Opcode.Gt: case BinaryExpr.Opcode.Ge: { if (e.Op == BinaryExpr.Opcode.Gt && e.E0.Type.NormalizeExpand().IsIndDatatype) { - if (UnifyTypes(e.E1.Type, new DatatypeProxy(false, true))) { + if (ConstrainTypes(e.E1.Type, new DatatypeProxy(false, true), expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E1.Type)) { e.ResolvedOp = BinaryExpr.ResolvedOpcode.RankGt; - } else { - reporter.Error(MessageSource.Resolver, expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E1.Type); } expr.Type = Type.Bool; } else if (e.Op == BinaryExpr.Opcode.Gt && (e.E1.Type.NormalizeExpand().IsIndDatatype || e.E1.Type.IsTypeParameter)) { - if (UnifyTypes(e.E0.Type, new DatatypeProxy(false, false))) { + if (ConstrainTypes(e.E0.Type, new DatatypeProxy(false, false), expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E0.Type)) { e.ResolvedOp = BinaryExpr.ResolvedOpcode.RankGt; - } else { - reporter.Error(MessageSource.Resolver, expr, "arguments to rank comparison must be datatypes (instead of {0})", e.E0.Type); } expr.Type = Type.Bool; } else { - bool err = false; bool isComparison = e.Op == BinaryExpr.Opcode.Gt || e.Op == BinaryExpr.Opcode.Ge; - if (!UnifyTypes(e.E0.Type, new OperationTypeProxy(true, true, isComparison, false, true, true))) { - reporter.Error(MessageSource.Resolver, expr, "arguments to {0} must be of a numeric type{2} or a set type (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, - isComparison ? ", char, " : ""); - err = true; - } - if (!UnifyTypes(e.E1.Type, e.E0.Type)) { - reporter.Error(MessageSource.Resolver, expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type); - err = true; - } + var good0 = ConstrainTypes(e.E0.Type, new OperationTypeProxy(true, true, isComparison, false, true, true), + expr, "arguments to {0} must be of a numeric type{2} or a set type (instead got {1})", + BinaryExpr.OpcodeString(e.Op), e.E0.Type, isComparison ? ", char, " : ""); + var good1 = ConstrainTypes(e.E1.Type, e.E0.Type, expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type); if (isComparison) { expr.Type = Type.Bool; - } else if (!err) { + } else if (good0 && good1) { expr.Type = e.E0.Type; } } @@ -7459,29 +7484,23 @@ namespace Microsoft.Dafny case BinaryExpr.Opcode.In: case BinaryExpr.Opcode.NotIn: - if (!UnifyTypes(e.E1.Type, new CollectionTypeProxy(e.E0.Type, true, true))) { - reporter.Error(MessageSource.Resolver, expr, "second argument to \"{0}\" must be a set, multiset, or sequence with elements of type {1}, or a map with domain {1} (instead got {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type); - } + ConstrainTypes(e.E1.Type, new CollectionTypeProxy(e.E0.Type, true, true), expr, "second argument to \"{0}\" must be a set, multiset, or sequence with elements of type {1}, or a map with domain {1} (instead got {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type); expr.Type = Type.Bool; break; case BinaryExpr.Opcode.Div: - if (!UnifyTypes(e.E0.Type, new OperationTypeProxy(true, true, false, false, false, false))) { - reporter.Error(MessageSource.Resolver, expr, "first argument to {0} must be of numeric type (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type); - } - if (!UnifyTypes(e.E1.Type, e.E0.Type)) { - reporter.Error(MessageSource.Resolver, expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type); - } + ConstrainTypes(e.E0.Type, new OperationTypeProxy(true, true, false, false, false, false), + expr, "first argument to {0} must be of numeric type (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type); + ConstrainTypes(e.E1.Type, e.E0.Type, + expr, "arguments to {0} must have the same type (got {1} and {2})", BinaryExpr.OpcodeString(e.Op), e.E0.Type, e.E1.Type); expr.Type = e.E0.Type; break; case BinaryExpr.Opcode.Mod: - if (!UnifyTypes(e.E0.Type, new OperationTypeProxy(true, false, false, false, false, false))) { - reporter.Error(MessageSource.Resolver, expr, "first argument to {0} must be of type int (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type); - } - if (!UnifyTypes(e.E1.Type, e.E0.Type)) { - reporter.Error(MessageSource.Resolver, expr, "second argument to {0} must be of type int (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E1.Type); - } + ConstrainTypes(e.E0.Type, new OperationTypeProxy(true, false, false, false, false, false), + expr, "first argument to {0} must be of type int (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E0.Type); + ConstrainTypes(e.E1.Type, e.E0.Type, + expr, "second argument to {0} must be of type int (instead got {1})", BinaryExpr.OpcodeString(e.Op), e.E1.Type); expr.Type = e.E0.Type; break; @@ -7499,15 +7518,12 @@ namespace Microsoft.Dafny switch (e.Op) { case TernaryExpr.Opcode.PrefixEqOp: case TernaryExpr.Opcode.PrefixNeqOp: - if (!UnifyTypes(e.E0.Type, Type.Int)) { - reporter.Error(MessageSource.Resolver, e.E0, "prefix-equality limit argument must be an integer expression (got {0})", e.E0.Type); - } - if (!UnifyTypes(e.E1.Type, new DatatypeProxy(true))) { - reporter.Error(MessageSource.Resolver, expr, "arguments to prefix equality must be codatatypes (instead of {0})", e.E1.Type); - } - if (!UnifyTypes(e.E1.Type, e.E2.Type)) { - reporter.Error(MessageSource.Resolver, expr, "arguments must have the same type (got {0} and {1})", e.E1.Type, e.E2.Type); - } + ConstrainTypes(e.E0.Type, Type.Int, + e.E0, "prefix-equality limit argument must be an integer expression (got {0})", e.E0.Type); + ConstrainTypes(e.E1.Type, new DatatypeProxy(true), + expr, "arguments to prefix equality must be codatatypes (instead of {0})", e.E1.Type); + ConstrainTypes(e.E1.Type, e.E2.Type, + expr, "arguments must have the same type (got {0} and {1})", e.E1.Type, e.E2.Type); expr.Type = Type.Bool; break; default: @@ -7556,9 +7572,7 @@ namespace Microsoft.Dafny } foreach (var rhs in e.RHSs) { ResolveExpression(rhs, opts); - if (!UnifyTypes(rhs.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, rhs.tok, "type of RHS of let-such-that expression must be boolean (got {0})", rhs.Type); - } + 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); @@ -7594,15 +7608,11 @@ namespace Microsoft.Dafny if (e.Range != null) { ResolveExpression(e.Range, new ResolveOpts(opts, true)); Contract.Assert(e.Range.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(e.Range.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, expr, "range of quantifier must be of type bool (instead got {0})", e.Range.Type); - } + ConstrainTypes(e.Range.Type, Type.Bool, expr, "range of quantifier must be of type bool (instead got {0})", e.Range.Type); } ResolveExpression(e.Term, new ResolveOpts(opts, true)); Contract.Assert(e.Term.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(e.Term.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, expr, "body of quantifier must be of type bool (instead got {0})", e.Term.Type); - } + ConstrainTypes(e.Term.Type, Type.Bool, expr, "body of quantifier must be of type bool (instead got {0})", e.Term.Type); // Since the body is more likely to infer the types of the bound variables, resolve it // first (above) and only then resolve the attributes (below). ResolveAttributes(e.Attributes, new ResolveOpts(opts, true)); @@ -7643,9 +7653,7 @@ namespace Microsoft.Dafny } ResolveExpression(e.Range, opts); Contract.Assert(e.Range.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(e.Range.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, expr, "range of comprehension must be of type bool (instead got {0})", e.Range.Type); - } + ConstrainTypes(e.Range.Type, Type.Bool, expr, "range of comprehension must be of type bool (instead got {0})", e.Range.Type); ResolveExpression(e.Term, opts); Contract.Assert(e.Term.Type != null); // follows from postcondition of ResolveExpression @@ -7672,9 +7680,7 @@ namespace Microsoft.Dafny } ResolveExpression(e.Range, opts); Contract.Assert(e.Range.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(e.Range.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, expr, "range of comprehension must be of type bool (instead got {0})", e.Range.Type); - } + ConstrainTypes(e.Range.Type, Type.Bool, expr, "range of comprehension must be of type bool (instead got {0})", e.Range.Type); ResolveExpression(e.Term, opts); Contract.Assert(e.Term.Type != null); // follows from postcondition of ResolveExpression @@ -7707,9 +7713,7 @@ namespace Microsoft.Dafny if (e.Range != null) { ResolveExpression(e.Range, opts); Contract.Assert(e.Range.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(e.Range.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, expr, "Precondition must be boolean (got {0})", e.Range.Type); - } + ConstrainTypes(e.Range.Type, Type.Bool, expr, "Precondition must be boolean (got {0})", e.Range.Type); } foreach (var read in e.Reads) { @@ -7747,13 +7751,9 @@ namespace Microsoft.Dafny Contract.Assert(e.Thn.Type != null); // follows from postcondition of ResolveExpression ResolveExpression(e.Els, opts); Contract.Assert(e.Els.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(e.Test.Type, Type.Bool)) { - reporter.Error(MessageSource.Resolver, expr, "guard condition in if-then-else expression must be a boolean (instead got {0})", e.Test.Type); - } - if (UnifyTypes(e.Thn.Type, e.Els.Type)) { + ConstrainTypes(e.Test.Type, Type.Bool, expr, "guard condition in if-then-else expression must be a boolean (instead got {0})", e.Test.Type); + if (ConstrainTypes(e.Thn.Type, e.Els.Type, expr, "the two branches of an if-then-else expression must have the same type (got {0} and {1})", e.Thn.Type, e.Els.Type)) { expr.Type = e.Thn.Type; - } else { - reporter.Error(MessageSource.Resolver, expr, "the two branches of an if-then-else expression must have the same type (got {0} and {1})", e.Thn.Type, e.Els.Type); } } else if (expr is MatchExpr) { @@ -7835,16 +7835,15 @@ namespace Microsoft.Dafny if (ctor != null && i < ctor.Formals.Count) { Formal formal = ctor.Formals[i]; Type st = SubstType(formal.Type, subst); - if (!UnifyTypes(v.Type, st)) { - reporter.Error(MessageSource.Resolver, expr, "the declared type of the formal ({0}) does not agree with the corresponding type in the constructor's signature ({1})", v.Type, st); - } + ConstrainTypes(v.Type, st, + expr, "the declared type of the formal ({0}) does not agree with the corresponding type in the constructor's signature ({1})", v.Type, st); v.IsGhost = formal.IsGhost; // update the type of the boundvars in the MatchCaseToken if (v.tok is MatchCaseToken) { MatchCaseToken mt = (MatchCaseToken)v.tok; foreach (Tuple entry in mt.varList) { - UnifyTypes(entry.Item2.Type, v.Type); + UnifyTypes(entry.Item2.Type, v.Type); // TODO: What to do if this unification fails? Or can it? --KRML } } } @@ -7862,9 +7861,7 @@ namespace Microsoft.Dafny } Contract.Assert(mc.Body.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(expr.Type, mc.Body.Type)) { - reporter.Error(MessageSource.Resolver, mc.Body.tok, "type of case bodies do not agree (found {0}, previous types {1})", mc.Body.Type, expr.Type); - } + ConstrainTypes(expr.Type, mc.Body.Type, mc.Body.tok, "type of case bodies do not agree (found {0}, previous types {1})", mc.Body.Type, expr.Type); scope.PopMarker(); } if (dtd != null && memberNamesUsed.Count != dtd.Ctors.Count) { @@ -8198,9 +8195,7 @@ namespace Microsoft.Dafny // this is a simple resolution var v = pat.Var; ResolveType(v.tok, v.Type, context, ResolveTypeOptionEnum.InferTypeProxies, null); - if (!UnifyTypes(v.Type, sourceType)) { - reporter.Error(MessageSource.Resolver, v.tok, "type of corresponding source/RHS ({0}) does not match type of bound variable ({1})", sourceType, v.Type); - } + ConstrainTypes(v.Type, sourceType, v.tok, "type of corresponding source/RHS ({0}) does not match type of bound variable ({1})", sourceType, v.Type); pat.AssembleExpr(null); } else if (dtd == null) { reporter.Error(MessageSource.Resolver, pat.tok, "to use a pattern, the type of the source/RHS expression must be a datatype (instead found {0})", sourceType); @@ -8918,9 +8913,8 @@ namespace Microsoft.Dafny reporter.Error(MessageSource.Resolver, e.tok, "wrong number of arguments to function application ({0} expects {1}, got {2})", what, fnType.Arity, e.Args.Count); } else { for (var i = 0; i < fnType.Arity; i++) { - if (!UnifyTypes(fnType.Args[i], e.Args[i].Type)) { - reporter.Error(MessageSource.Resolver, e.Args[i].tok, "type mismatch for argument {0} (function expects {1}, got {2})", i, fnType.Args[i], e.Args[i].Type); - } + ConstrainTypes(fnType.Args[i], e.Args[i].Type, + e.Args[i].tok, "type mismatch for argument {0} (function expects {1}, got {2})", i, fnType.Args[i], e.Args[i].Type); } if (errorCount != reporter.Count(ErrorLevel.Error)) { // do nothing else; error has been reported @@ -8946,9 +8940,7 @@ namespace Microsoft.Dafny ResolveExpression(farg, opts); Contract.Assert(farg.Type != null); // follows from postcondition of ResolveExpression Type s = SubstType(callee.Formals[i].Type, rr.TypeArgumentSubstitutions); - if (!UnifyTypes(farg.Type, s)) { - reporter.Error(MessageSource.Resolver, rr, "incorrect type of function argument {0} (expected {1}, got {2})", i, s, farg.Type); - } + ConstrainTypes(farg.Type, s, rr, "incorrect type of function argument {0} (expected {1}, got {2})", i, s, farg.Type); } rr.Type = SubstType(callee.ResultType, rr.TypeArgumentSubstitutions); // further bookkeeping @@ -9004,9 +8996,7 @@ namespace Microsoft.Dafny Contract.Assert(arg.Type != null); // follows from postcondition of ResolveExpression if (formal != null) { Type st = SubstType(formal.Type, subst); - if (!UnifyTypes(arg.Type, st)) { - reporter.Error(MessageSource.Resolver, arg.tok, "incorrect type of datatype constructor argument (found {0}, expected {1})", arg.Type, st); - } + ConstrainTypes(arg.Type, st, arg.tok, "incorrect type of datatype constructor argument (found {0}, expected {1})", arg.Type, st); } j++; } @@ -9241,9 +9231,7 @@ namespace Microsoft.Dafny ResolveExpression(farg, opts); Contract.Assert(farg.Type != null); // follows from postcondition of ResolveExpression Type s = SubstType(function.Formals[i].Type, e.TypeArgumentSubstitutions); - if (!UnifyTypes(farg.Type, s)) { - reporter.Error(MessageSource.Resolver, e, "incorrect type of function argument {0} (expected {1}, got {2})", i, s, farg.Type); - } + ConstrainTypes(farg.Type, s, e, "incorrect type of function argument {0} (expected {1}, got {2})", i, s, farg.Type); } e.Type = SubstType(function.ResultType, e.TypeArgumentSubstitutions); } @@ -9296,7 +9284,7 @@ namespace Microsoft.Dafny /// 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, null); + var c = GetImpliedTypeConstraint(bv, bv.Type, null); expr = polarity ? Expression.CreateAnd(c, expr) : Expression.CreateImplies(c, expr); } var all = DiscoverAllBounds_Aux_MultipleVars(bvars, expr, polarity); @@ -9311,7 +9299,7 @@ namespace Microsoft.Dafny } public static List DiscoverAllBounds_SingleVar(VT v, Expression expr) where VT : IVariable { - expr = Expression.CreateAnd(TypeConstraint(v, v.Type, null), expr); + expr = Expression.CreateAnd(GetImpliedTypeConstraint(v, v.Type, null), expr); return DiscoverAllBounds_Aux_SingleVar(new List { v }, 0, expr, true); } @@ -9425,13 +9413,13 @@ namespace Microsoft.Dafny return bounds; } - static Expression TypeConstraint(IVariable bv, Type ty, ErrorReporter reporter) { + static Expression GetImpliedTypeConstraint(IVariable bv, Type ty, ErrorReporter reporter) { Contract.Requires(bv != null); Contract.Requires(ty != null); ty = ty.NormalizeExpand(); var dd = ty.AsNewtype; if (dd != null) { - var c = TypeConstraint(bv, dd.BaseType, reporter); + var c = GetImpliedTypeConstraint(bv, dd.BaseType, reporter); if (dd.Var != null) { c = Expression.CreateAnd(c, new Translator(reporter).Substitute(dd.Constraint, dd.Var, Expression.CreateIdentExpr(bv))); } @@ -9778,17 +9766,14 @@ namespace Microsoft.Dafny Type argType = new InferredTypeProxy(); IndexableTypeProxy expectedType = new IndexableTypeProxy(domainType, elementType, argType, true, true, true); - if (!UnifyTypes(e.Seq.Type, expectedType)) { - reporter.Error(MessageSource.Resolver, e, "sequence/array/multiset/map selection requires a sequence, array, multiset, or map (got {0})", e.Seq.Type); + if (!ConstrainTypes(e.Seq.Type, expectedType, e, "sequence/array/multiset/map selection requires a sequence, array, multiset, or map (got {0})", e.Seq.Type)) { seqErr = true; } if (!e.SelectOne) // require sequence or array { if (!allowNonUnitArraySelection) { // require seq - if (!UnifyTypes(expectedType, new SeqType(new InferredTypeProxy()))) { - reporter.Error(MessageSource.Resolver, e, "selection requires a sequence (got {0})", e.Seq.Type); - } + ConstrainTypes(expectedType, new SeqType(new InferredTypeProxy()), e, "selection requires a sequence (got {0})", e.Seq.Type); } else { if (UnifyTypes(expectedType, new MapType(true, new InferredTypeProxy(), new InferredTypeProxy()))) { reporter.Error(MessageSource.Resolver, e, "cannot multiselect a map (got {0} as map type)", e.Seq.Type); @@ -9800,17 +9785,13 @@ namespace Microsoft.Dafny if (e.E0 != null) { ResolveExpression(e.E0, opts); Contract.Assert(e.E0.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(e.E0.Type, domainType)) { - reporter.Error(MessageSource.Resolver, e.E0, "sequence/array/multiset/map selection requires {1} indices (got {0})", e.E0.Type, domainType); - } + ConstrainTypes(e.E0.Type, domainType, e.E0, "sequence/array/multiset/map selection requires {1} indices (got {0})", e.E0.Type, domainType); } if (e.E1 != null) { ResolveExpression(e.E1, opts); Contract.Assert(e.E1.Type != null); // follows from postcondition of ResolveExpression var domType = e.E0 == null ? domainType : new OperationTypeProxy(true, false, false, false, false, false); // reuse 'domainType' if .E0 did not use it; otherwise, create a new proxy to allow .E1 to be any integer-based numeric type, independent of the integer-based numeric type used by .E0 - if (!UnifyTypes(e.E1.Type, domType)) { - reporter.Error(MessageSource.Resolver, e.E1, "sequence/array/multiset/map selection requires {1} indices (got {0})", e.E1.Type, domType); - } + ConstrainTypes(e.E1.Type, domType, e.E1, "sequence/array/multiset/map selection requires {1} indices (got {0})", e.E1.Type, domType); } if (!seqErr) { if (e.SelectOne) { -- cgit v1.2.3 From 510623d57984f81105c621c898c60c79d3a32c0f Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 8 Sep 2015 17:08:06 -0700 Subject: Proof that Ackermann can be curried and that it is monotonic in both arguments. --- Test/dafny4/Ackermann.dfy | 235 +++++++++++++++++++++++++++++++++++++++ Test/dafny4/Ackermann.dfy.expect | 6 + 2 files changed, 241 insertions(+) create mode 100644 Test/dafny4/Ackermann.dfy create mode 100644 Test/dafny4/Ackermann.dfy.expect diff --git a/Test/dafny4/Ackermann.dfy b/Test/dafny4/Ackermann.dfy new file mode 100644 index 00000000..7ab686e0 --- /dev/null +++ b/Test/dafny4/Ackermann.dfy @@ -0,0 +1,235 @@ +// RUN: %dafny /compile:3 /rprint:"%t.rprint" /autoTriggers:1 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +// Rustan Leino, 8 Sep 2015. +// This file proves that the Ackermann function is monotonic in each argument + +method Main() { + // Note, don't try much larger numbers unless you're prepared to wait! + print "Ack(3, 4) = ", Ack(3, 4), "\n"; +} + +// Here is the definition of the Ackermann function: +function method Ack(m: nat, n: nat): nat +{ + if m == 0 then + n + 1 + else if n == 0 then + Ack(m - 1, 1) + else + Ack(m - 1, Ack(m, n - 1)) +} + +// And here is the theorem that proves the desired result: +lemma AckermannIsMonotonic(m: nat, n: nat, m': nat, n': nat) + requires m <= m' && n <= n' + ensures Ack(m, n) <= Ack(m', n') +{ + // This is the proof. It calls two lemmas, stated and proved below. + MonotonicN(m, n, n'); + MonotonicM(m, n, m'); +} + +// -------------------------------------------------------- +// What follows are the supporting lemmas and their proofs. +// -------------------------------------------------------- + +// Proving that Ackermann is monotonic in its second argument is easy. +lemma MonotonicN(m: nat, n: nat, n': nat) + requires n <= n' + ensures Ack(m, n) <= Ack(m, n') +{ + // In fact, Dafny completes this proof automatically. +} + +// To prove the other direction, we consider an alternative formulation +// of the Ackermann function, namely a curried form: +function CurriedAckermann(m: int, n: int): int +{ + A(m)(n) +} + +function A(m: int): int -> int +{ + if m <= 0 then + n => n + 1 + else + n => Iter(A(m-1), n) +} + +function Iter(f: int -> int, n: int): int + requires IsTotal(f) + decreases n +{ + if n <= 0 then + f(1) + else + f(Iter(f, n-1)) +} + +// In the 3-part definition above, function Iter needs to know that it can +// apply its function parameter "f". Therefore, Iter's precondition says that +// "f" must be total. We define totality of "f" by saying that its precondition +// holds for any n and that it never reads the heap: +predicate IsTotal(f: int -> int) + reads f.reads +{ + forall n :: f.requires(n) && f.reads(n) == {} +} + +// Now, we can prove certain things about CurriedAckermann. The first thing we +// prove is that it gives the same result as the Ack function above. + +lemma CurriedIsTheSame(m: nat, n: nat) + ensures CurriedAckermann(m, n) == Ack(m, n) +{ + // The proof considers 3 cases, following the 3 cases in the definition of Ack + if m == 0 { + // trivial + } else if n == 0 { + // trivial + } else { + // we help Dafny out with the following lemma: + assert A(m)(n) == A(m-1)(Iter(A(m-1), n-1)); + } +} + +// Monotonicity in the first argument of Ackermann now follows from the fact that, +// for m <= m', A(m) is a function that is always below A(m') +lemma MonotonicM(m: nat, n: nat, m': nat) + requires m <= m' + ensures Ack(m, n) <= Ack(m', n) +{ + CurriedIsTheSame(m, n); + CurriedIsTheSame(m', n); + ABelow(m, m'); +} + +// We must now prove ABelow. To do that, we will prove some properties of A and of +// Iter. Let us define the three properties we will reason about. + +// The first property is a relation on functions. It is the property that above was referred +// to as "f is a function that is always below g". The lemma ABelow(m, m') used above establishes +// FunctionBelow(A(m), A(m')). +predicate FunctionBelow(f: int -> int, g: int -> int) + requires IsTotal(f) && IsTotal(g) +{ + forall n :: f(n) <= g(n) +} + +// The next property says that a function is monotonic. +predicate FunctionMonotonic(f: int -> int) + requires IsTotal(f) +{ + forall n,n' :: n <= n' ==> f(n) <= f(n') +} + +// The third property says that a function's return value is strictly greater than its argument. +predicate Expanding(f: int -> int) + requires IsTotal(f) +{ + forall n :: n < f(n) +} + +// We will prove that A(_) satisfies the properties FunctionBelow, FunctionMonotonic, and +// FunctionExpanding. But first we prove three properties of Iter. + +// Provided that "f" is monotonic and expanding, Iter(f, _) is monotonic. +lemma IterIsMonotonicN(f: int -> int, n: int, n': int) + requires IsTotal(f) && Expanding(f) && FunctionMonotonic(f) && n <= n' + ensures Iter(f, n) <= Iter(f, n') +{ + // This proof is a simple induction over n' and Dafny completes the proof automatically. +} + +// Next, we prove that Iter(_, n) is monotonic. That is, given functions "f" and "g" +// such that FunctionBelow(f, g), we have Iter(f, n) <= Iter(g, n). The lemma requires +// "f" to be monotonic, but we don't have to state the same property for g. +lemma IterIsMonotonicF(f: int -> int, g: int -> int, n: int) + requires IsTotal(f) && IsTotal(g) && FunctionMonotonic(f) && FunctionBelow(f, g) + ensures Iter(f, n) <= Iter(g, n) +{ + // This proof is a simple induction over n and Dafny completes the proof automatically. +} + +// Finally, we shows that for any expanding function "f", Iter(f, _) is also expanding. +lemma IterExpanding(f: int -> int, n: int) + requires IsTotal(f) && Expanding(f) + ensures n < Iter(f, n) +{ + // Here, too, the proof is a simple induction of n and Dafny completes it automatically. +} + +// We complete the proof by showing that A(_) has the three properties we need. + +// Of the three properties we prove about A(_), we start by showing that A(m) returns a +// function that is expanding. +lemma AExp(m: int) + ensures Expanding(A(m)) +{ + if m <= 0 { + // trivial + } else { + // This branch of the proof follows from the fact that Iter(A(m-1), _) is expanding. + forall n { + // The following lemma requires A(m-1) to be expanding, which is something that + // following from our induction hypothesis. + IterExpanding(A(m-1), n); + } + } +} + +// Next, we show that A(m) returns a monotonic function. +lemma Am(m: int) + ensures FunctionMonotonic(A(m)) +{ + if m <= 0 { + // trivial + } else { + // We make use of the fact that A(m-1) is expanding: + AExp(m-1); + // and the fact that Iter(A(m-1), _) is monotonic: + forall n,n' | n <= n' { + // The following lemma requires A(m-1) to be expanding and monotonic. + // The former comes from the invocation of AExp(m-1) above, and the + // latter comes from our induction hypothesis. + IterIsMonotonicN(A(m-1), n, n'); + } + } +} + +// Our final property about A, which is the lemma we had used above to prove +// that Ackermann is monotonic in its first argument, is stated and proved here: +lemma ABelow(m: int, m': int) + requires m <= m' + ensures FunctionBelow(A(m), A(m')) +{ + if m' <= 0 { + // trivial + } else if m <= 0 { + forall n { + calc { + A(m)(n); + == + n + 1; + <= { AExp(m'-1); IterExpanding(A(m'-1), n); } + Iter(A(m'-1), n); + == + A(m')(n); + } + } + } else { + forall n { + calc { + A(m)(n); + == + Iter(A(m-1), n); + <= { Am(m-1); ABelow(m-1, m'-1); + IterIsMonotonicF(A(m-1), A(m'-1), n); } + Iter(A(m'-1), n); + == + A(m')(n); + } + } + } +} diff --git a/Test/dafny4/Ackermann.dfy.expect b/Test/dafny4/Ackermann.dfy.expect new file mode 100644 index 00000000..158f2a48 --- /dev/null +++ b/Test/dafny4/Ackermann.dfy.expect @@ -0,0 +1,6 @@ + +Dafny program verifier finished with 30 verified, 0 errors +Program compiled successfully +Running... + +Ack(3, 4) = 125 -- cgit v1.2.3 From ca96e3974019ca956f46c91eb07b2c2dfede1d29 Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Wed, 9 Sep 2015 07:02:54 -0400 Subject: Fix #90 The mono wrapper for Dafny didn't forward command line arguments. --- Binaries/dafny | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Binaries/dafny b/Binaries/dafny index b8571bec..889ebc81 100755 --- a/Binaries/dafny +++ b/Binaries/dafny @@ -13,4 +13,4 @@ if [[ ! -x "$DAFNY" ]]; then exit 1 fi -"$MONO" "$DAFNY" +"$MONO" "$DAFNY" "$@" -- cgit v1.2.3 From 2196e226c6a1c1cca5cb70b8dbdd89088751c525 Mon Sep 17 00:00:00 2001 From: Michael Lowell Roberts Date: Thu, 17 Sep 2015 15:00:02 -0700 Subject: Only print extraneous comments if asked. --- Source/Dafny/DafnyOptions.cs | 785 +++++---- Source/Dafny/Printer.cs | 4020 +++++++++++++++++++++--------------------- 2 files changed, 2403 insertions(+), 2402 deletions(-) diff --git a/Source/Dafny/DafnyOptions.cs b/Source/Dafny/DafnyOptions.cs index 08e53d5c..dd25e3c4 100644 --- a/Source/Dafny/DafnyOptions.cs +++ b/Source/Dafny/DafnyOptions.cs @@ -1,392 +1,393 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Diagnostics.Contracts; -using Bpl = Microsoft.Boogie; - -namespace Microsoft.Dafny -{ - public class DafnyOptions : Bpl.CommandLineOptions - { - private ErrorReporter errorReporter; - - public DafnyOptions(ErrorReporter errorReporter = null) - : base("Dafny", "Dafny program verifier") { - this.errorReporter = errorReporter; - SetZ3ExecutableName(); - } - - public override string VersionNumber { - get { - return System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileVersion -#if ENABLE_IRONDAFNY - + "[IronDafny]" -#endif - ; - } - } - public override string VersionSuffix { - get { - return " version " + VersionNumber + ", Copyright (c) 2003-2015, Microsoft."; - } - } - - private static DafnyOptions clo; - public static DafnyOptions O { - get { return clo; } - } - - public static void Install(DafnyOptions options) { - Contract.Requires(options != null); - clo = options; - Bpl.CommandLineOptions.Install(options); - } - - public bool UnicodeOutput = false; - public bool DisallowSoundnessCheating = false; - public bool Dafnycc = false; - public int Induction = 3; - public int InductionHeuristic = 6; - public string DafnyPrelude = null; - public string DafnyPrintFile = null; - public enum PrintModes { Everything, NoIncludes, NoGhost }; - public PrintModes PrintMode; - public bool DafnyVerify = true; - public string DafnyPrintResolvedFile = null; - public bool Compile = true; - public bool ForceCompile = false; - public bool RunAfterCompile = false; - public bool SpillTargetCode = false; - public bool DisallowIncludes = false; - public bool DisableNLarith = false; - public string AutoReqPrintFile = null; - public bool ignoreAutoReq = false; - public bool AllowGlobals = false; - public bool CountVerificationErrors = true; - public bool Optimize = false; - public bool AutoTriggers = false; - public bool PrintTooltips = false; - public bool PrintStats = false; - public bool PrintFunctionCallGraph = false; - public bool WarnShadowing = false; - public bool IronDafny = -#if ENABLE_IRONDAFNY - true -#else - false -#endif - ; - - protected override bool ParseOption(string name, Bpl.CommandLineOptionEngine.CommandLineParseState ps) { - var args = ps.args; // convenient synonym - - switch (name) { - case "dprelude": - if (ps.ConfirmArgumentCount(1)) { - DafnyPrelude = args[ps.i]; - } - return true; - - case "dprint": - if (ps.ConfirmArgumentCount(1)) { - DafnyPrintFile = args[ps.i]; - } - return true; - - case "printMode": - if (ps.ConfirmArgumentCount(1)) { - if (args[ps.i].Equals("Everything")) { - PrintMode = PrintModes.Everything; - } - else if (args[ps.i].Equals("NoIncludes")) - { - PrintMode = PrintModes.NoIncludes; - } - else if (args[ps.i].Equals("NoGhost")) - { - PrintMode = PrintModes.NoGhost; - } - else - { - throw new Exception("Invalid value for printMode"); - } - } - return true; - - case "rprint": - if (ps.ConfirmArgumentCount(1)) { - DafnyPrintResolvedFile = args[ps.i]; - } - return true; - - case "compile": { - int compile = 0; - if (ps.GetNumericArgument(ref compile, 4)) { - // convert option to two booleans - Compile = compile != 0; - ForceCompile = compile == 2; - RunAfterCompile = compile == 3; - } - return true; - } - - case "dafnyVerify": - { - int verify = 0; - if (ps.GetNumericArgument(ref verify, 2)) { - DafnyVerify = verify != 0; // convert to boolean - } - return true; - } - - case "spillTargetCode": { - int spill = 0; - if (ps.GetNumericArgument(ref spill, 2)) { - SpillTargetCode = spill != 0; // convert to a boolean - } - return true; - } - - case "dafnycc": - Dafnycc = true; - Induction = 0; - Compile = false; - UseAbstractInterpretation = false; // /noinfer - return true; - - case "noCheating": { - int cheat = 0; // 0 is default, allows cheating - if (ps.GetNumericArgument(ref cheat, 2)) { - DisallowSoundnessCheating = cheat == 1; - } - return true; - } - - case "induction": - ps.GetNumericArgument(ref Induction, 4); - return true; - - case "inductionHeuristic": - ps.GetNumericArgument(ref InductionHeuristic, 7); - return true; - - case "noIncludes": - DisallowIncludes = true; - return true; - - case "noNLarith": - DisableNLarith = true; - this.AddZ3Option("NL_ARITH=false"); - return true; - - case "autoReqPrint": - if (ps.ConfirmArgumentCount(1)) { - AutoReqPrintFile = args[ps.i]; - } - return true; - - case "noAutoReq": - ignoreAutoReq = true; - return true; - - case "allowGlobals": - AllowGlobals = true; - return true; - - case "stats": - PrintStats = true; - return true; - - case "funcCallGraph": - PrintFunctionCallGraph = true; - return true; - - case "warnShadowing": - WarnShadowing = true; - return true; - - case "countVerificationErrors": { - int countErrors = 1; // defaults to reporting verification errors - if (ps.GetNumericArgument(ref countErrors, 2)) { - CountVerificationErrors = countErrors == 1; - } - return true; - } - - case "printTooltips": - PrintTooltips = true; - return true; - - case "autoTriggers": { - int autoTriggers = 0; - if (ps.GetNumericArgument(ref autoTriggers, 2)) { - AutoTriggers = autoTriggers == 1; - } - return true; - } - - case "optimize": { - Optimize = true; - return true; - } - - case "noIronDafny": { - IronDafny = false; - return true; - } - - case "ironDafny": { - IronDafny = true; - return true; - } - - default: - break; - } - // not a Dafny-specific option, so defer to superclass - return base.ParseOption(name, ps); - } - - public override void ApplyDefaultOptions() { - base.ApplyDefaultOptions(); - - // expand macros in filenames, now that LogPrefix is fully determined - ExpandFilename(ref DafnyPrelude, LogPrefix, FileTimestamp); - ExpandFilename(ref DafnyPrintFile, LogPrefix, FileTimestamp); - } - - public override void AttributeUsage() { - // TODO: provide attribute help here - } - - - /// - /// Dafny comes with it's own copy of z3, to save new users the trouble of having to install extra dependency. - /// For this to work, Dafny makes the Z3ExecutablePath point to the path were Z3 is put by our release script. - /// For developers though (and people getting this from source), it's convenient to be able to run right away, - /// so we vendor a Windows version. - /// - private void SetZ3ExecutableName() { - var platform = (int)System.Environment.OSVersion.Platform; - - // http://www.mono-project.com/docs/faq/technical/ - var isUnix = platform == 4 || platform == 128; - - var z3binName = isUnix ? "z3" : "z3.exe"; - var dafnyBinDir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); - var z3BinDir = System.IO.Path.Combine(dafnyBinDir, "z3", "bin"); - var z3BinPath = System.IO.Path.Combine(z3BinDir, z3binName); - - if (!System.IO.File.Exists(z3BinPath) && !isUnix) { - // This is most likely a Windows user running from source without downloading z3 - // separately; this is ok, since we vendor z3.exe. - z3BinPath = System.IO.Path.Combine(dafnyBinDir, z3binName); - } - - if (!System.IO.File.Exists(z3BinPath) && errorReporter != null) { - var tok = new Bpl.Token(1, 1) { filename = "*** " }; - errorReporter.Warning(MessageSource.Other, tok, "Could not find '{0}' in '{1}'.{2}Downloading and extracting a Z3 distribution to Dafny's 'Binaries' folder would solve this issue; for now, we'll rely on Boogie to find Z3.", - z3binName, z3BinDir, System.Environment.NewLine); - } else { - Z3ExecutablePath = z3BinPath; - } - } - - public override void Usage() { - Console.WriteLine(@" ---- Dafny options --------------------------------------------------------- - - Multiple .dfy files supplied on the command line are concatenated into one - Dafny program. - - /dprelude: - choose Dafny prelude file - /dprint: - print Dafny program after parsing it - (use - as to print to console) - /printMode: - NoIncludes disables printing of {:verify false} methods incorporated via the - include mechanism, as well as datatypes and fields included from other files. - NoGhost disables printing of functions, ghost methods, and proof statements in - implementation methods. It also disables anything NoIncludes disables. - /rprint: - print Dafny program after resolving it - (use - as to print to console) - /dafnyVerify: - 0 - stop after typechecking - 1 - continue on to translation, verification, and compilation - /compile: 0 - do not compile Dafny program - 1 (default) - upon successful verification of the Dafny - program, compile Dafny program to .NET assembly - Program.exe (if the program has a Main method) or - Program.dll (othewise), where Program.dfy is the name - of the last .dfy file on the command line - 2 - always attempt to compile Dafny program to C# program - out.cs, regardless of verification outcome - 3 - if there is a Main method and there are no verification - errors, compiles program in memory (i.e., does not write - an output file) and runs it - /spillTargetCode: - 0 (default) - don't write the compiled Dafny program (but - still compile it, if /compile indicates to do so) - 1 - write the compiled Dafny program as a .cs file - /dafnycc Disable features not supported by DafnyCC - /noCheating: - 0 (default) - allow assume statements and free invariants - 1 - treat all assumptions as asserts, and drop free. - /induction: - 0 - never do induction, not even when attributes request it - 1 - only apply induction when attributes request it - 2 - apply induction as requested (by attributes) and also - for heuristically chosen quantifiers - 3 (default) - apply induction as requested, and for - heuristically chosen quantifiers and lemmas - /inductionHeuristic: - 0 - least discriminating induction heuristic (that is, lean - toward applying induction more often) - 1,2,3,4,5 - levels in between, ordered as follows as far as - how discriminating they are: 0 < 1 < 2 < (3,4) < 5 < 6 - 6 (default) - most discriminating - /noIncludes Ignore include directives - /noNLarith Reduce Z3's knowledge of non-linear arithmetic (*,/,%). - Results in more manual work, but also produces more predictable behavior. - /autoReqPrint: - Print out requirements that were automatically generated by autoReq. - /noAutoReq Ignore autoReq attributes - /allowGlobals Allow the implicit class '_default' to contain fields, instance functions, - and instance methods. These class members are declared at the module scope, - outside of explicit classes. This command-line option is provided to simplify - a transition from the behavior in the language prior to version 1.9.3, from - which point onward all functions and methods declared at the module scope are - implicitly static and fields declarations are not allowed at the module scope. - /countVerificationErrors: - 0 - If preprocessing succeeds, set exit code to 0 regardless of the number - of verification errors. - 1 (default) - If preprocessing succeeds, set exit code to the number of - verification errors. - /autoTriggers: - 0 (default) - Do not generate {:trigger} annotations for user-level quantifiers. - 1 - Add a {:trigger} to each user-level quantifier. Existing - annotations are preserved. - /optimize Produce optimized C# code, meaning: - - selects optimized C# prelude by passing - /define:DAFNY_USE_SYSTEM_COLLECTIONS_IMMUTABLE to csc.exe (requires - System.Collections.Immutable.dll in the source directory to successfully - compile). - - passes /optimize flag to csc.exe. - /stats Print interesting statistics about the Dafny files supplied. - /funcCallGraph Print out the function call graph. Format is: func,mod=callee* - /warnShadowing Emits a warning if the name of a declared variable caused another variable - to be shadowed - /ironDafny Enable experimental features needed to support Ironclad/Ironfleet. Use of - these features may cause your code to become incompatible with future - releases of Dafny. - /noIronDafny Disable Ironclad/Ironfleet features, if enabled by default. - /printTooltips - Dump additional positional information (displayed as mouse-over tooltips by - the VS plugin) to stdout as 'Info' messages. -"); - base.Usage(); // also print the Boogie options - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Diagnostics.Contracts; +using Bpl = Microsoft.Boogie; + +namespace Microsoft.Dafny +{ + public class DafnyOptions : Bpl.CommandLineOptions + { + private ErrorReporter errorReporter; + + public DafnyOptions(ErrorReporter errorReporter = null) + : base("Dafny", "Dafny program verifier") { + this.errorReporter = errorReporter; + SetZ3ExecutableName(); + } + + public override string VersionNumber { + get { + return System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileVersion +#if ENABLE_IRONDAFNY + + "[IronDafny]" +#endif + ; + } + } + public override string VersionSuffix { + get { + return " version " + VersionNumber + ", Copyright (c) 2003-2015, Microsoft."; + } + } + + private static DafnyOptions clo; + public static DafnyOptions O { + get { return clo; } + } + + public static void Install(DafnyOptions options) { + Contract.Requires(options != null); + clo = options; + Bpl.CommandLineOptions.Install(options); + } + + public bool UnicodeOutput = false; + public bool DisallowSoundnessCheating = false; + public bool Dafnycc = false; + public int Induction = 3; + public int InductionHeuristic = 6; + public string DafnyPrelude = null; + public string DafnyPrintFile = null; + public enum PrintModes { Everything, NoIncludes, NoGhost }; + public PrintModes PrintMode = PrintModes.Everything; // Default to printing everything + public bool DafnyVerify = true; + public string DafnyPrintResolvedFile = null; + public bool Compile = true; + public bool ForceCompile = false; + public bool RunAfterCompile = false; + public bool SpillTargetCode = false; + public bool DisallowIncludes = false; + public bool DisableNLarith = false; + public string AutoReqPrintFile = null; + public bool ignoreAutoReq = false; + public bool AllowGlobals = false; + public bool CountVerificationErrors = true; + public bool Optimize = false; + public bool AutoTriggers = false; + public bool PrintTooltips = false; + public bool PrintStats = false; + public bool PrintFunctionCallGraph = false; + public bool WarnShadowing = false; + public bool IronDafny = +#if ENABLE_IRONDAFNY + true +#else + false +#endif + ; + + protected override bool ParseOption(string name, Bpl.CommandLineOptionEngine.CommandLineParseState ps) { + var args = ps.args; // convenient synonym + + switch (name) { + case "dprelude": + if (ps.ConfirmArgumentCount(1)) { + DafnyPrelude = args[ps.i]; + } + return true; + + case "dprint": + if (ps.ConfirmArgumentCount(1)) { + DafnyPrintFile = args[ps.i]; + } + return true; + + case "printMode": + if (ps.ConfirmArgumentCount(1)) { + if (args[ps.i].Equals("Everything")) { + PrintMode = PrintModes.Everything; + } + else if (args[ps.i].Equals("NoIncludes")) + { + PrintMode = PrintModes.NoIncludes; + } + else if (args[ps.i].Equals("NoGhost")) + { + PrintMode = PrintModes.NoGhost; + } + else + { + throw new Exception("Invalid value for printMode"); + } + } + return true; + + case "rprint": + if (ps.ConfirmArgumentCount(1)) { + DafnyPrintResolvedFile = args[ps.i]; + } + return true; + + case "compile": { + int compile = 0; + if (ps.GetNumericArgument(ref compile, 4)) { + // convert option to two booleans + Compile = compile != 0; + ForceCompile = compile == 2; + RunAfterCompile = compile == 3; + } + return true; + } + + case "dafnyVerify": + { + int verify = 0; + if (ps.GetNumericArgument(ref verify, 2)) { + DafnyVerify = verify != 0; // convert to boolean + } + return true; + } + + case "spillTargetCode": { + int spill = 0; + if (ps.GetNumericArgument(ref spill, 2)) { + SpillTargetCode = spill != 0; // convert to a boolean + } + return true; + } + + case "dafnycc": + Dafnycc = true; + Induction = 0; + Compile = false; + UseAbstractInterpretation = false; // /noinfer + return true; + + case "noCheating": { + int cheat = 0; // 0 is default, allows cheating + if (ps.GetNumericArgument(ref cheat, 2)) { + DisallowSoundnessCheating = cheat == 1; + } + return true; + } + + case "induction": + ps.GetNumericArgument(ref Induction, 4); + return true; + + case "inductionHeuristic": + ps.GetNumericArgument(ref InductionHeuristic, 7); + return true; + + case "noIncludes": + DisallowIncludes = true; + return true; + + case "noNLarith": + DisableNLarith = true; + this.AddZ3Option("NL_ARITH=false"); + return true; + + case "autoReqPrint": + if (ps.ConfirmArgumentCount(1)) { + AutoReqPrintFile = args[ps.i]; + } + return true; + + case "noAutoReq": + ignoreAutoReq = true; + return true; + + case "allowGlobals": + AllowGlobals = true; + return true; + + case "stats": + PrintStats = true; + return true; + + case "funcCallGraph": + PrintFunctionCallGraph = true; + return true; + + case "warnShadowing": + WarnShadowing = true; + return true; + + case "countVerificationErrors": { + int countErrors = 1; // defaults to reporting verification errors + if (ps.GetNumericArgument(ref countErrors, 2)) { + CountVerificationErrors = countErrors == 1; + } + return true; + } + + case "printTooltips": + PrintTooltips = true; + return true; + + case "autoTriggers": { + int autoTriggers = 0; + if (ps.GetNumericArgument(ref autoTriggers, 2)) { + AutoTriggers = autoTriggers == 1; + } + return true; + } + + case "optimize": { + Optimize = true; + return true; + } + + case "noIronDafny": { + IronDafny = false; + return true; + } + + case "ironDafny": { + IronDafny = true; + return true; + } + + default: + break; + } + // not a Dafny-specific option, so defer to superclass + return base.ParseOption(name, ps); + } + + public override void ApplyDefaultOptions() { + base.ApplyDefaultOptions(); + + // expand macros in filenames, now that LogPrefix is fully determined + ExpandFilename(ref DafnyPrelude, LogPrefix, FileTimestamp); + ExpandFilename(ref DafnyPrintFile, LogPrefix, FileTimestamp); + } + + public override void AttributeUsage() { + // TODO: provide attribute help here + } + + + /// + /// Dafny comes with it's own copy of z3, to save new users the trouble of having to install extra dependency. + /// For this to work, Dafny makes the Z3ExecutablePath point to the path were Z3 is put by our release script. + /// For developers though (and people getting this from source), it's convenient to be able to run right away, + /// so we vendor a Windows version. + /// + private void SetZ3ExecutableName() { + var platform = (int)System.Environment.OSVersion.Platform; + + // http://www.mono-project.com/docs/faq/technical/ + var isUnix = platform == 4 || platform == 128; + + var z3binName = isUnix ? "z3" : "z3.exe"; + var dafnyBinDir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); + var z3BinDir = System.IO.Path.Combine(dafnyBinDir, "z3", "bin"); + var z3BinPath = System.IO.Path.Combine(z3BinDir, z3binName); + + if (!System.IO.File.Exists(z3BinPath) && !isUnix) { + // This is most likely a Windows user running from source without downloading z3 + // separately; this is ok, since we vendor z3.exe. + z3BinPath = System.IO.Path.Combine(dafnyBinDir, z3binName); + } + + if (!System.IO.File.Exists(z3BinPath) && errorReporter != null) { + var tok = new Bpl.Token(1, 1) { filename = "*** " }; + errorReporter.Warning(MessageSource.Other, tok, "Could not find '{0}' in '{1}'.{2}Downloading and extracting a Z3 distribution to Dafny's 'Binaries' folder would solve this issue; for now, we'll rely on Boogie to find Z3.", + z3binName, z3BinDir, System.Environment.NewLine); + } else { + Z3ExecutablePath = z3BinPath; + } + } + + public override void Usage() { + Console.WriteLine(@" ---- Dafny options --------------------------------------------------------- + + Multiple .dfy files supplied on the command line are concatenated into one + Dafny program. + + /dprelude: + choose Dafny prelude file + /dprint: + print Dafny program after parsing it + (use - as to print to console) + /printMode: + Everything is the default. + NoIncludes disables printing of {:verify false} methods incorporated via the + include mechanism, as well as datatypes and fields included from other files. + NoGhost disables printing of functions, ghost methods, and proof statements in + implementation methods. It also disables anything NoIncludes disables. + /rprint: + print Dafny program after resolving it + (use - as to print to console) + /dafnyVerify: + 0 - stop after typechecking + 1 - continue on to translation, verification, and compilation + /compile: 0 - do not compile Dafny program + 1 (default) - upon successful verification of the Dafny + program, compile Dafny program to .NET assembly + Program.exe (if the program has a Main method) or + Program.dll (othewise), where Program.dfy is the name + of the last .dfy file on the command line + 2 - always attempt to compile Dafny program to C# program + out.cs, regardless of verification outcome + 3 - if there is a Main method and there are no verification + errors, compiles program in memory (i.e., does not write + an output file) and runs it + /spillTargetCode: + 0 (default) - don't write the compiled Dafny program (but + still compile it, if /compile indicates to do so) + 1 - write the compiled Dafny program as a .cs file + /dafnycc Disable features not supported by DafnyCC + /noCheating: + 0 (default) - allow assume statements and free invariants + 1 - treat all assumptions as asserts, and drop free. + /induction: + 0 - never do induction, not even when attributes request it + 1 - only apply induction when attributes request it + 2 - apply induction as requested (by attributes) and also + for heuristically chosen quantifiers + 3 (default) - apply induction as requested, and for + heuristically chosen quantifiers and lemmas + /inductionHeuristic: + 0 - least discriminating induction heuristic (that is, lean + toward applying induction more often) + 1,2,3,4,5 - levels in between, ordered as follows as far as + how discriminating they are: 0 < 1 < 2 < (3,4) < 5 < 6 + 6 (default) - most discriminating + /noIncludes Ignore include directives + /noNLarith Reduce Z3's knowledge of non-linear arithmetic (*,/,%). + Results in more manual work, but also produces more predictable behavior. + /autoReqPrint: + Print out requirements that were automatically generated by autoReq. + /noAutoReq Ignore autoReq attributes + /allowGlobals Allow the implicit class '_default' to contain fields, instance functions, + and instance methods. These class members are declared at the module scope, + outside of explicit classes. This command-line option is provided to simplify + a transition from the behavior in the language prior to version 1.9.3, from + which point onward all functions and methods declared at the module scope are + implicitly static and fields declarations are not allowed at the module scope. + /countVerificationErrors: + 0 - If preprocessing succeeds, set exit code to 0 regardless of the number + of verification errors. + 1 (default) - If preprocessing succeeds, set exit code to the number of + verification errors. + /autoTriggers: + 0 (default) - Do not generate {:trigger} annotations for user-level quantifiers. + 1 - Add a {:trigger} to each user-level quantifier. Existing + annotations are preserved. + /optimize Produce optimized C# code, meaning: + - selects optimized C# prelude by passing + /define:DAFNY_USE_SYSTEM_COLLECTIONS_IMMUTABLE to csc.exe (requires + System.Collections.Immutable.dll in the source directory to successfully + compile). + - passes /optimize flag to csc.exe. + /stats Print interesting statistics about the Dafny files supplied. + /funcCallGraph Print out the function call graph. Format is: func,mod=callee* + /warnShadowing Emits a warning if the name of a declared variable caused another variable + to be shadowed + /ironDafny Enable experimental features needed to support Ironclad/Ironfleet. Use of + these features may cause your code to become incompatible with future + releases of Dafny. + /noIronDafny Disable Ironclad/Ironfleet features, if enabled by default. + /printTooltips + Dump additional positional information (displayed as mouse-over tooltips by + the VS plugin) to stdout as 'Info' messages. +"); + base.Usage(); // also print the Boogie options + } + } +} diff --git a/Source/Dafny/Printer.cs b/Source/Dafny/Printer.cs index ce8b54bb..e242c8bb 100644 --- a/Source/Dafny/Printer.cs +++ b/Source/Dafny/Printer.cs @@ -1,2010 +1,2010 @@ -//----------------------------------------------------------------------------- -// -// Copyright (C) Microsoft Corporation. All Rights Reserved. -// -//----------------------------------------------------------------------------- -using System; -using System.IO; -using System.Collections.Generic; -using System.Diagnostics.Contracts; -using System.Numerics; -using System.Linq; -using Bpl = Microsoft.Boogie; - -namespace Microsoft.Dafny { - public class Printer { - TextWriter wr; - DafnyOptions.PrintModes printMode; - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(wr!=null); - } - - public Printer(TextWriter wr, DafnyOptions.PrintModes printMode = DafnyOptions.PrintModes.Everything) { - Contract.Requires(wr != null); - this.wr = wr; - this.printMode = printMode; - } - - public static string ExprToString(Expression expr) - { - Contract.Requires(expr != null); - using (var wr = new System.IO.StringWriter()) { - var pr = new Printer(wr); - pr.PrintExpression(expr, false); - return wr.ToString(); - } - } - - public static string GuardToString(Expression expr) { - using (var wr = new System.IO.StringWriter()) { - var pr = new Printer(wr); - pr.PrintGuard(expr); - return wr.ToString(); - } - } - - public static string ExtendedExprToString(Expression expr) { - Contract.Requires(expr != null); - using (var wr = new System.IO.StringWriter()) { - var pr = new Printer(wr); - pr.PrintExtendedExpr(expr, 0, true, false); - return wr.ToString(); - } - } - - public static string FrameExprListToString(List fexprs) { - Contract.Requires(fexprs != null); - using (var wr = new System.IO.StringWriter()) { - var pr = new Printer(wr); - pr.PrintFrameExpressionList(fexprs); - return wr.ToString(); - } - } - - public static string StatementToString(Statement stmt) { - Contract.Requires(stmt != null); - using (var wr = new System.IO.StringWriter()) { - var pr = new Printer(wr); - pr.PrintStatement(stmt, 0); - return ToStringWithoutNewline(wr); - } - } - - public static string IteratorClassToString(IteratorDecl iter) { - Contract.Requires(iter != null); - using (var wr = new System.IO.StringWriter()) { - var pr = new Printer(wr); - pr.PrintIteratorClass(iter, 0, null); - return ToStringWithoutNewline(wr); - } - } - - public static string IteratorSignatureToString(IteratorDecl iter) { - Contract.Requires(iter != null); - using (var wr = new System.IO.StringWriter()) { - var pr = new Printer(wr); - pr.PrintIteratorSignature(iter, 0); - return ToStringWithoutNewline(wr); - } - } - - public static string FunctionSignatureToString(Function f) { - Contract.Requires(f != null); - using (var wr = new System.IO.StringWriter()) { - var pr = new Printer(wr); - pr.PrintFunction(f, 0, true); - return ToStringWithoutNewline(wr); - } - } - - public static string MethodSignatureToString(Method m) { - Contract.Requires(m != null); - using (var wr = new System.IO.StringWriter()) { - var pr = new Printer(wr); - pr.PrintMethod(m, 0, true); - return ToStringWithoutNewline(wr); - } - } - - public static string OneAttributeToString(Attributes a, string nameSubstitution = null) { - Contract.Requires(a != null); - using (var wr = new System.IO.StringWriter()) { - var pr = new Printer(wr); - pr.PrintOneAttribute(a, nameSubstitution); - return ToStringWithoutNewline(wr); - } - } - - public static string ToStringWithoutNewline(System.IO.StringWriter wr) { - Contract.Requires(wr != null); - var sb = wr.GetStringBuilder(); - var len = sb.Length; - while (len > 0 && (sb[len - 1] == '\n' || sb[len - 1] == '\r')) { - len--; - } - return sb.ToString(0, len); - } - - public void PrintProgram(Program prog) { - Contract.Requires(prog != null); - if (Bpl.CommandLineOptions.Clo.ShowEnv != Bpl.CommandLineOptions.ShowEnvironment.Never) { - wr.WriteLine("// " + Bpl.CommandLineOptions.Clo.Version); - wr.WriteLine("// " + Bpl.CommandLineOptions.Clo.Environment); - } - wr.WriteLine("// {0}", prog.Name); - if (DafnyOptions.O.DafnyPrintResolvedFile != null) { - wr.WriteLine(); - wr.WriteLine("/*"); - PrintModuleDefinition(prog.BuiltIns.SystemModule, 0, Path.GetFullPath(DafnyOptions.O.DafnyPrintResolvedFile)); - wr.WriteLine("*/"); - } - wr.WriteLine(); - PrintCallGraph(prog.DefaultModuleDef, 0); - PrintTopLevelDecls(prog.DefaultModuleDef.TopLevelDecls, 0, Path.GetFullPath(prog.FullName)); - wr.Flush(); - } - - public void PrintCallGraph(ModuleDefinition module, int indent) { - Contract.Requires(module != null); - Contract.Requires(0 <= indent); - if (DafnyOptions.O.DafnyPrintResolvedFile != null) { - // print call graph - Indent(indent); wr.WriteLine("/* CALL GRAPH for module {0}:", module.Name); - var SCCs = module.CallGraph.TopologicallySortedComponents(); - SCCs.Reverse(); - foreach (var clbl in SCCs) { - Indent(indent); wr.WriteLine(" * SCC at height {0}:", module.CallGraph.GetSCCRepresentativeId(clbl)); - var r = module.CallGraph.GetSCC(clbl); - foreach (var m in r) { - Indent(indent); wr.WriteLine(" * {0}", m.NameRelativeToModule); - } - } - Indent(indent); wr.WriteLine(" */"); - } - } - - public void PrintTopLevelDecls(List decls, int indent, string fileBeingPrinted) { - Contract.Requires(decls!= null); - int i = 0; - foreach (TopLevelDecl d in decls) { - Contract.Assert(d != null); - if (PrintModeSkipGeneral(d.tok, fileBeingPrinted)) { continue; } - if (d is OpaqueTypeDecl) { - var at = (OpaqueTypeDecl)d; - if (i++ != 0) { wr.WriteLine(); } - Indent(indent); - PrintClassMethodHelper("type", at.Attributes, at.Name, new List()); - wr.Write(EqualitySupportSuffix(at.EqualitySupport)); - wr.WriteLine(); - } else if (d is NewtypeDecl) { - var dd = (NewtypeDecl)d; - if (i++ != 0) { wr.WriteLine(); } - Indent(indent); - PrintClassMethodHelper("newtype", dd.Attributes, dd.Name, new List()); - wr.Write(" = "); - if (dd.Var == null) { - PrintType(dd.BaseType); - } else { - wr.Write(dd.Var.DisplayName); - if (!(dd.Var.Type is TypeProxy) || DafnyOptions.O.DafnyPrintResolvedFile != null) { - wr.Write(": "); - PrintType(dd.BaseType); - } - wr.Write(" | "); - PrintExpression(dd.Constraint, true); - } - wr.WriteLine(); - } else if (d is TypeSynonymDecl) { - var syn = (TypeSynonymDecl)d; - if (i++ != 0) { wr.WriteLine(); } - Indent(indent); - PrintClassMethodHelper("type", syn.Attributes, syn.Name, syn.TypeArgs); - wr.Write(" = "); - PrintType(syn.Rhs); - wr.WriteLine(); - } else if (d is DatatypeDecl) { - if (i++ != 0) { wr.WriteLine(); } - PrintDatatype((DatatypeDecl)d, indent); - } else if (d is IteratorDecl) { - var iter = (IteratorDecl)d; - PrintIteratorSignature(iter, indent); - - if (iter.Body != null) { - Indent(indent); - PrintStatement(iter.Body, indent); - wr.WriteLine(); - } - - if (DafnyOptions.O.DafnyPrintResolvedFile != null) { - // also print the members that were created as part of the interpretation of the iterator - Contract.Assert(iter.Members.Count != 0); // filled in during resolution - wr.WriteLine("/*---------- iterator members ----------"); - PrintIteratorClass(iter, indent, fileBeingPrinted); - wr.WriteLine("---------- iterator members ----------*/"); - } - - } else if (d is ClassDecl) { - ClassDecl cl = (ClassDecl)d; - if (!cl.IsDefaultClass) { - if (i++ != 0) { wr.WriteLine(); } - PrintClass(cl, indent, fileBeingPrinted); - } else if (cl.Members.Count == 0) { - // print nothing - } else { - if (i++ != 0) { wr.WriteLine(); } - PrintMembers(cl.Members, indent, fileBeingPrinted); - } - - } else if (d is ModuleDecl) { - wr.WriteLine(); - Indent(indent); - if (d is LiteralModuleDecl) { - ModuleDefinition module = ((LiteralModuleDecl)d).ModuleDef; - PrintModuleDefinition(module, indent, fileBeingPrinted); - } else if (d is AliasModuleDecl) { - wr.Write("import"); if (((AliasModuleDecl)d).Opened) wr.Write(" opened"); - wr.Write(" {0} ", ((AliasModuleDecl)d).Name); - wr.WriteLine("= {0}", Util.Comma(".", ((AliasModuleDecl)d).Path, id => id.val)); - } else if (d is ModuleFacadeDecl) { - wr.Write("import"); if (((ModuleFacadeDecl)d).Opened) wr.Write(" opened"); - wr.Write(" {0} ", ((ModuleFacadeDecl)d).Name); - wr.WriteLine("as {0}", Util.Comma(".", ((ModuleFacadeDecl)d).Path, id => id.val)); - } - - } else { - Contract.Assert(false); // unexpected TopLevelDecl - } - } - } - - void PrintModuleDefinition(ModuleDefinition module, int indent, string fileBeingPrinted) { - Contract.Requires(module != null); - Contract.Requires(0 <= indent); - if (module.IsAbstract) { - wr.Write("abstract "); - } - wr.Write("module"); - PrintAttributes(module.Attributes); - wr.Write(" {0} ", module.Name); - if (module.RefinementBaseName != null) { - wr.Write("refines {0} ", Util.Comma(".", module.RefinementBaseName, id => id.val)); - } - if (module.TopLevelDecls.Count == 0) { - wr.WriteLine("{ }"); - } else { - wr.WriteLine("{"); - PrintCallGraph(module, indent + IndentAmount); - PrintTopLevelDecls(module.TopLevelDecls, indent + IndentAmount, fileBeingPrinted); - Indent(indent); - wr.WriteLine("}"); - } - } - - void PrintIteratorSignature(IteratorDecl iter, int indent) { - Indent(indent); - PrintClassMethodHelper("iterator", iter.Attributes, iter.Name, iter.TypeArgs); - if (iter.SignatureIsOmitted) { - wr.WriteLine(" ..."); - } else { - PrintFormals(iter.Ins); - if (iter.Outs.Count != 0) { - if (iter.Ins.Count + iter.Outs.Count <= 3) { - wr.Write(" yields "); - } else { - wr.WriteLine(); - Indent(indent + 2 * IndentAmount); - wr.Write("yields "); - } - PrintFormals(iter.Outs); - } - wr.WriteLine(); - } - - int ind = indent + IndentAmount; - PrintSpec("requires", iter.Requires, ind); - if (iter.Reads.Expressions != null) { - PrintFrameSpecLine("reads", iter.Reads.Expressions, ind, iter.Reads.HasAttributes() ? iter.Reads.Attributes : null); - } - if (iter.Modifies.Expressions != null) { - PrintFrameSpecLine("modifies", iter.Modifies.Expressions, ind, iter.Modifies.HasAttributes() ? iter.Modifies.Attributes : null); - } - PrintSpec("yield requires", iter.YieldRequires, ind); - PrintSpec("yield ensures", iter.YieldEnsures, ind); - PrintSpec("ensures", iter.Ensures, ind); - PrintDecreasesSpec(iter.Decreases, ind); - } - - private void PrintIteratorClass(IteratorDecl iter, int indent, string fileBeingPrinted) { - PrintClassMethodHelper("class", null, iter.Name, iter.TypeArgs); - wr.WriteLine(" {"); - PrintMembers(iter.Members, indent + IndentAmount, fileBeingPrinted); - Indent(indent); wr.WriteLine("}"); - } - - public void PrintClass(ClassDecl c, int indent, string fileBeingPrinted) { - Contract.Requires(c != null); - Indent(indent); - PrintClassMethodHelper((c is TraitDecl) ? "trait" : "class", c.Attributes, c.Name, c.TypeArgs); - string sep = " extends "; - foreach (var trait in c.TraitsTyp) { - wr.Write(sep); - PrintType(trait); - sep = ", "; - } - if (c.Members.Count == 0) { - wr.WriteLine(" { }"); - } else { - wr.WriteLine(" {"); - PrintMembers(c.Members, indent + IndentAmount, fileBeingPrinted); - Indent(indent); - wr.WriteLine("}"); - } - } - - public void PrintMembers(List members, int indent, string fileBeingPrinted) - { - Contract.Requires(members != null); - - int state = 0; // 0 - no members yet; 1 - previous member was a field; 2 - previous member was non-field - foreach (MemberDecl m in members) { - if (PrintModeSkipGeneral(m.tok, fileBeingPrinted)) { continue; } - if (m is Method) { - if (state != 0) { wr.WriteLine(); } - PrintMethod((Method)m, indent, false); - var com = m as FixpointLemma; - if (com != null && com.PrefixLemma != null) { - Indent(indent); wr.WriteLine("/***"); - PrintMethod(com.PrefixLemma, indent, false); - Indent(indent); wr.WriteLine("***/"); - } - state = 2; - } else if (m is Field) { - if (state == 2) { wr.WriteLine(); } - PrintField((Field)m, indent); - state = 1; - } else if (m is Function) { - if (state != 0) { wr.WriteLine(); } - PrintFunction((Function)m, indent, false); - var fixp = m as FixpointPredicate; - if (fixp != null && fixp.PrefixPredicate != null) { - Indent(indent); wr.WriteLine("/***"); - PrintFunction(fixp.PrefixPredicate, indent, false); - Indent(indent); wr.WriteLine("***/"); - } - state = 2; - } else { - Contract.Assert(false); throw new cce.UnreachableException(); // unexpected member - } - } - } - - /// - /// Prints no space before "kind", but does print a space before "attrs" and "name". - /// - void PrintClassMethodHelper(string kind, Attributes attrs, string name, List typeArgs) { - Contract.Requires(kind != null); - Contract.Requires(name != null); - Contract.Requires(typeArgs != null); - if (kind.Length != 0) { - wr.Write(kind); - } - - PrintAttributes(attrs); - - wr.Write(" {0}", name); - PrintTypeParams(typeArgs); - } - - private void PrintTypeParams(List typeArgs) { - Contract.Requires(typeArgs != null); - if (typeArgs.Count != 0) { - wr.Write("<" + - Util.Comma(", ", typeArgs, - tp => tp.Name + EqualitySupportSuffix(tp.EqualitySupport)) - + ">"); - } - } - - private void PrintTypeInstantiation(List typeArgs) { - Contract.Requires(typeArgs == null || typeArgs.Count != 0); - if (typeArgs != null) { - wr.Write("<{0}>", Util.Comma(",", typeArgs, ty => ty.ToString())); - } - } - - public void PrintDatatype(DatatypeDecl dt, int indent) { - Contract.Requires(dt != null); - Indent(indent); - PrintClassMethodHelper(dt is IndDatatypeDecl ? "datatype" : "codatatype", dt.Attributes, dt.Name, dt.TypeArgs); - wr.Write(" ="); - string sep = ""; - foreach (DatatypeCtor ctor in dt.Ctors) { - wr.Write(sep); - PrintClassMethodHelper("", ctor.Attributes, ctor.Name, new List()); - if (ctor.Formals.Count != 0) { - PrintFormals(ctor.Formals); - } - sep = " |"; - } - wr.WriteLine(); - } - - /// - /// Prints a space before each attribute. - /// - public void PrintAttributes(Attributes a) { - if (a != null) { - PrintAttributes(a.Prev); - wr.Write(" "); - PrintOneAttribute(a); - } - } - public void PrintOneAttribute(Attributes a, string nameSubstitution = null) { - Contract.Requires(a != null); - var name = nameSubstitution ?? a.Name; - var usAttribute = name.StartsWith("_"); - wr.Write("{1}{{:{0}", name, usAttribute ? "/*" : ""); - if (a.Args != null) { - PrintAttributeArgs(a.Args, false); - } - wr.Write("}}{0}", usAttribute ? "*/" : ""); - } - - public void PrintAttributeArgs(List args, bool isFollowedBySemicolon) { - Contract.Requires(args != null); - string prefix = " "; - foreach (var arg in args) { - Contract.Assert(arg != null); - wr.Write(prefix); - prefix = ", "; - PrintExpression(arg, isFollowedBySemicolon); - } - } - - public void PrintField(Field field, int indent) { - Contract.Requires(field != null); - Indent(indent); - if (field.IsGhost) { - wr.Write("ghost "); - } - wr.Write("var"); - PrintAttributes(field.Attributes); - wr.Write(" {0}: ", field.Name); - PrintType(field.Type); - if (field.IsUserMutable) { - // nothing more to say - } else if (field.IsMutable) { - wr.Write(" // non-assignable"); - } else { - wr.Write(" // immutable"); - } - wr.WriteLine(); - } - - public void PrintFunction(Function f, int indent, bool printSignatureOnly) { - Contract.Requires(f != null); - - if (PrintModeSkipFunctionOrMethod(f.IsGhost, f.Attributes, f.Name)) { return; } - var isPredicate = f is Predicate || f is PrefixPredicate; - Indent(indent); - string k = isPredicate ? "predicate" : f is InductivePredicate ? "inductive predicate" : f is CoPredicate ? "copredicate" : "function"; - if (f.IsProtected) { k = "protected " + k; } - if (f.HasStaticKeyword) { k = "static " + k; } - if (!f.IsGhost) { k += " method"; } - PrintClassMethodHelper(k, f.Attributes, f.Name, f.TypeArgs); - if (f.SignatureIsOmitted) { - wr.WriteLine(" ..."); - } else { - PrintFormals(f.Formals, f.Name); - if (!isPredicate) { - wr.Write(": "); - PrintType(f.ResultType); - } - wr.WriteLine(); - } - - int ind = indent + IndentAmount; - PrintSpec("requires", f.Req, ind); - PrintFrameSpecLine("reads", f.Reads, ind, null); - PrintSpec("ensures", f.Ens, ind); - PrintDecreasesSpec(f.Decreases, ind); - if (f.Body != null && !printSignatureOnly) { - Indent(indent); - wr.WriteLine("{"); - PrintExtendedExpr(f.Body, ind, true, false); - Indent(indent); - wr.WriteLine("}"); - } - } - - // ----------------------------- PrintMethod ----------------------------- - - const int IndentAmount = 2; // The amount of indent for each new scope - const string BunchaSpaces = " "; - void Indent(int amount) - { - Contract.Requires(0 <= amount); - - while (0 < amount) { - wr.Write(BunchaSpaces.Substring(0, amount)); - amount -= BunchaSpaces.Length; - } - } - - private bool PrintModeSkipFunctionOrMethod(bool IsGhost, Attributes attributes, string name) - { - if (printMode == DafnyOptions.PrintModes.NoGhost && IsGhost) - { return true; } - if (printMode == DafnyOptions.PrintModes.NoIncludes || printMode == DafnyOptions.PrintModes.NoGhost) - { - bool verify = true; - if (Attributes.ContainsBool(attributes, "verify", ref verify) && !verify) - { return true; } - if (name.Contains("INTERNAL") || name.StartsWith("reveal_")) - { return true; } - } - return false; - } - - private bool PrintModeSkipGeneral(Bpl.IToken tok, string fileBeingPrinted) - { - return (printMode == DafnyOptions.PrintModes.NoIncludes || printMode == DafnyOptions.PrintModes.NoGhost) - && (tok.filename != null && fileBeingPrinted != null && Path.GetFullPath(tok.filename) != fileBeingPrinted); - } - - public void PrintMethod(Method method, int indent, bool printSignatureOnly) { - Contract.Requires(method != null); - - if (PrintModeSkipFunctionOrMethod(method.IsGhost, method.Attributes, method.Name)) { return; } - Indent(indent); - string k = method is Constructor ? "constructor" : - method is InductiveLemma ? "inductive lemma" : - method is CoLemma ? "colemma" : - method is Lemma ? "lemma" : - "method"; - if (method.HasStaticKeyword) { k = "static " + k; } - if (method.IsGhost && !(method is Lemma) && !(method is FixpointLemma)) { k = "ghost " + k; } - string nm = method is Constructor && !((Constructor)method).HasName ? "" : method.Name; - PrintClassMethodHelper(k, method.Attributes, nm, method.TypeArgs); - if (method.SignatureIsOmitted) { - wr.WriteLine(" ..."); - } else { - PrintFormals(method.Ins, method.Name); - if (method.Outs.Count != 0) { - if (method.Ins.Count + method.Outs.Count <= 3) { - wr.Write(" returns "); - } else { - wr.WriteLine(); - Indent(indent + 2 * IndentAmount); - wr.Write("returns "); - } - PrintFormals(method.Outs); - } - wr.WriteLine(); - } - - int ind = indent + IndentAmount; - PrintSpec("requires", method.Req, ind); - if (method.Mod.Expressions != null) - { - PrintFrameSpecLine("modifies", method.Mod.Expressions, ind, method.Mod.HasAttributes() ? method.Mod.Attributes : null); - } - PrintSpec("ensures", method.Ens, ind); - PrintDecreasesSpec(method.Decreases, ind); - - if (method.Body != null && !printSignatureOnly) { - Indent(indent); - PrintStatement(method.Body, indent); - wr.WriteLine(); - } - } - - internal void PrintFormals(List ff, string name = null) { - Contract.Requires(ff != null); - if (name != null && name.EndsWith("#")) { - wr.Write("["); - PrintFormal(ff[0]); - wr.Write("]"); - ff = new List(ff.Skip(1)); - } - wr.Write("("); - string sep = ""; - foreach (Formal f in ff) { - Contract.Assert(f != null); - wr.Write(sep); - sep = ", "; - PrintFormal(f); - } - wr.Write(")"); - } - - void PrintFormal(Formal f) { - Contract.Requires(f != null); - if (f.IsGhost) { - wr.Write("ghost "); - } - if (f.HasName) { - wr.Write("{0}: ", f.DisplayName); - } - PrintType(f.Type); - } - - internal void PrintSpec(string kind, List ee, int indent) { - Contract.Requires(kind != null); - Contract.Requires(ee != null); - foreach (Expression e in ee) { - Contract.Assert(e != null); - Indent(indent); - wr.Write("{0} ", kind); - PrintExpression(e, true); - wr.WriteLine(); - } - } - - internal void PrintDecreasesSpec(Specification decs, int indent, bool newLine = true) { - Contract.Requires(decs != null); - if (printMode == DafnyOptions.PrintModes.NoGhost) { return; } - if (decs.Expressions != null && decs.Expressions.Count != 0) { - Indent(indent); - wr.Write("decreases"); - if (decs.HasAttributes()) - { - PrintAttributes(decs.Attributes); - } - wr.Write(" "); - PrintExpressionList(decs.Expressions, true); - if (newLine) { - wr.WriteLine(); - } else { - wr.Write(" "); - } - } - } - - internal void PrintFrameSpecLine(string kind, List ee, int indent, Attributes attrs, bool newLine = true) { - Contract.Requires(kind != null); - Contract.Requires(cce.NonNullElements(ee)); - if (ee != null && ee.Count != 0) { - Indent(indent); - wr.Write("{0}", kind); - if (attrs != null) { - PrintAttributes(attrs); - } - wr.Write(" "); - PrintFrameExpressionList(ee); - if (newLine) { - wr.WriteLine(); - } else { - wr.Write(" "); - } - } - } - - internal void PrintSpec(string kind, List ee, int indent, bool newLine = true) { - Contract.Requires(kind != null); - Contract.Requires(ee != null); - if (printMode == DafnyOptions.PrintModes.NoGhost) { return; } - foreach (MaybeFreeExpression e in ee) - { - Contract.Assert(e != null); - Indent(indent); - wr.Write("{0}{1}", e.IsFree ? "free " : "", kind); - - if (e.HasAttributes()) - { - PrintAttributes(e.Attributes); - } - - wr.Write(" "); - PrintExpression(e.E, true); - if (newLine) { - wr.WriteLine(); - } else { - wr.Write(" "); - } - } - } - - // ----------------------------- PrintType ----------------------------- - - public void PrintType(Type ty) { - Contract.Requires(ty != null); - wr.Write(ty.ToString()); - } - - public void PrintType(string prefix, Type ty) { - Contract.Requires(prefix != null); - Contract.Requires(ty != null); - string s = ty.ToString(); - if (s != "?") { - wr.Write("{0}{1}", prefix, s); - } - } - - string EqualitySupportSuffix(TypeParameter.EqualitySupportValue es) { - if (es == TypeParameter.EqualitySupportValue.Required || - (es == TypeParameter.EqualitySupportValue.InferredRequired && DafnyOptions.O.DafnyPrintResolvedFile != null)) { - return "(==)"; - } else { - return ""; - } - } - - // ----------------------------- PrintStatement ----------------------------- - - /// - /// Prints from the current position of the current line. - /// If the statement requires several lines, subsequent lines are indented at "indent". - /// No newline is printed after the statement. - /// - public void PrintStatement(Statement stmt, int indent) { - Contract.Requires(stmt != null); - - if (stmt.IsGhost && printMode == DafnyOptions.PrintModes.NoGhost) { return; } - for (LList(A a) { - return a; - } - } - - public struct BigRational - { - public static readonly BigRational ZERO = new BigRational(0); - - BigInteger num, den; // invariant 1 <= den - public override string ToString() { - return string.Format("({0}.0 / {1}.0)", num, den); - } - public BigRational(int n) { - num = new BigInteger(n); - den = BigInteger.One; - } - public BigRational(BigInteger n, BigInteger d) { - // requires 1 <= d - num = n; - den = d; - } - public BigInteger ToBigInteger() { - if (0 <= num) { - return num / den; - } else { - return (num - den + 1) / den; - } - } - /// - /// Returns values such that aa/dd == a and bb/dd == b. - /// - private static void Normalize(BigRational a, BigRational b, out BigInteger aa, out BigInteger bb, out BigInteger dd) { - var gcd = BigInteger.GreatestCommonDivisor(a.den, b.den); - var xx = a.den / gcd; - var yy = b.den / gcd; - // We now have a == a.num / (xx * gcd) and b == b.num / (yy * gcd). - aa = a.num * yy; - bb = b.num * xx; - dd = a.den * yy; - } - public int CompareTo(BigRational that) { - // simple things first - int asign = this.num.Sign; - int bsign = that.num.Sign; - if (asign < 0 && 0 <= bsign) { - return 1; - } else if (asign <= 0 && 0 < bsign) { - return 1; - } else if (bsign < 0 && 0 <= asign) { - return -1; - } else if (bsign <= 0 && 0 < asign) { - return -1; - } - BigInteger aa, bb, dd; - Normalize(this, that, out aa, out bb, out dd); - return aa.CompareTo(bb); - } - public override int GetHashCode() { - return num.GetHashCode() + 29 * den.GetHashCode(); - } - public override bool Equals(object obj) { - if (obj is BigRational) { - return this == (BigRational)obj; - } else { - return false; - } - } - public static bool operator ==(BigRational a, BigRational b) { - return a.CompareTo(b) == 0; - } - public static bool operator !=(BigRational a, BigRational b) { - return a.CompareTo(b) != 0; - } - public static bool operator >(BigRational a, BigRational b) { - return 0 < a.CompareTo(b); - } - public static bool operator >=(BigRational a, BigRational b) { - return 0 <= a.CompareTo(b); - } - public static bool operator <(BigRational a, BigRational b) { - return a.CompareTo(b) < 0; - } - public static bool operator <=(BigRational a, BigRational b) { - return a.CompareTo(b) <= 0; - } - public static BigRational operator +(BigRational a, BigRational b) { - BigInteger aa, bb, dd; - Normalize(a, b, out aa, out bb, out dd); - return new BigRational(aa + bb, dd); - } - public static BigRational operator -(BigRational a, BigRational b) { - BigInteger aa, bb, dd; - Normalize(a, b, out aa, out bb, out dd); - return new BigRational(aa - bb, dd); - } - public static BigRational operator -(BigRational a) { - return new BigRational(-a.num, a.den); - } - public static BigRational operator *(BigRational a, BigRational b) { - return new BigRational(a.num * b.num, a.den * b.den); - } - public static BigRational operator /(BigRational a, BigRational b) { - // Compute the reciprocal of b - BigRational bReciprocal; - if (0 < b.num) { - bReciprocal = new BigRational(b.den, b.num); - } else { - // this is the case b.num < 0 - bReciprocal = new BigRational(-b.den, -b.num); - } - return a * bReciprocal; - } - } -} +using System; // for Func +using System.Numerics; + +namespace Dafny +{ + using System.Collections.Generic; +// set this option if you want to use System.Collections.Immutable and if you know what you're doing. +#if DAFNY_USE_SYSTEM_COLLECTIONS_IMMUTABLE + using System.Collections.Immutable; + using System.Linq; + + public class Set + { + readonly ImmutableHashSet setImpl; + Set(ImmutableHashSet d) { + this.setImpl = d; + } + public static readonly Set Empty = new Set(ImmutableHashSet.Empty); + public static Set FromElements(params T[] values) { + return FromElements((IEnumerable)values); + } + public static Set FromElements(IEnumerable values) { + var d = ImmutableHashSet.Empty.ToBuilder(); + foreach (T t in values) + d.Add(t); + return new Set(d.ToImmutable()); + } + public static Set FromCollection(ICollection values) { + var d = ImmutableHashSet.Empty.ToBuilder(); + foreach (T t in values) + d.Add(t); + return new Set(d.ToImmutable()); + } + public int Length { + get { return this.setImpl.Count; } + } + public long LongLength { + get { return this.setImpl.Count; } + } + public IEnumerable Elements { + get { + return this.setImpl; + } + } + /// + /// This is an inefficient iterator for producing all subsets of "this". Each set returned is the same + /// Set object (but this Set object is fresh; in particular, it is not "this"). + /// + public IEnumerable> AllSubsets { + get { + // Start by putting all set elements into a list + var elmts = new List(); + elmts.AddRange(this.setImpl); + var n = elmts.Count; + var which = new bool[n]; + var s = ImmutableHashSet.Empty.ToBuilder(); + while (true) { + yield return new Set(s.ToImmutable()); + // "add 1" to "which", as if doing a carry chain. For every digit changed, change the membership of the corresponding element in "s". + int i = 0; + for (; i < n && which[i]; i++) { + which[i] = false; + s.Remove(elmts[i]); + } + if (i == n) { + // we have cycled through all the subsets + break; + } + which[i] = true; + s.Add(elmts[i]); + } + } + } + public bool Equals(Set other) { + return this.setImpl.SetEquals(other.setImpl); + } + public override bool Equals(object other) { + var otherSet = other as Set; + return otherSet != null && this.Equals(otherSet); + } + public override int GetHashCode() { + var hashCode = 1; + foreach (var t in this.setImpl) { + hashCode = hashCode * (t.GetHashCode()+3); + } + return hashCode; + } + public override string ToString() { + var s = "{"; + var sep = ""; + foreach (var t in this.setImpl) { + s += sep + t.ToString(); + sep = ", "; + } + return s + "}"; + } + public bool IsProperSubsetOf(Set other) { + return IsProperSubsetOf(other); + } + public bool IsSubsetOf(Set other) { + return IsSubsetOf(other); + } + public bool IsSupersetOf(Set other) { + return other.IsSupersetOf(this); + } + public bool IsProperSupersetOf(Set other) { + return other.IsProperSupersetOf(this); + } + public bool IsDisjointFrom(Set other) { + ImmutableHashSet a, b; + if (this.setImpl.Count < other.setImpl.Count) { + a = this.setImpl; b = other.setImpl; + } else { + a = other.setImpl; b = this.setImpl; + } + foreach (T t in a) { + if (b.Contains(t)) + return false; + } + return true; + } + public bool Contains(T t) { + return this.setImpl.Contains(t); + } + public Set Union(Set other) { + return new Set(this.setImpl.Union(other.setImpl)); + } + public Set Intersect(Set other) { + return new Set(this.setImpl.Intersect(other.setImpl)); + } + public Set Difference(Set other) { + return new Set(this.setImpl.Except(other.setImpl)); + } + } + public partial class MultiSet + { + + readonly ImmutableDictionary dict; + MultiSet(ImmutableDictionary d) { + dict = d; + } + public static readonly MultiSet Empty = new MultiSet(ImmutableDictionary.Empty); + public static MultiSet FromElements(params T[] values) { + var d = ImmutableDictionary.Empty.ToBuilder(); + foreach (T t in values) { + var i = 0; + if (!d.TryGetValue(t, out i)) { + i = 0; + } + d[t] = i + 1; + } + return new MultiSet(d.ToImmutable()); + } + public static MultiSet FromCollection(ICollection values) { + var d = ImmutableDictionary.Empty.ToBuilder(); + foreach (T t in values) { + var i = 0; + if (!d.TryGetValue(t, out i)) { + i = 0; + } + d[t] = i + 1; + } + return new MultiSet(d.ToImmutable()); + } + public static MultiSet FromSeq(Sequence values) { + var d = ImmutableDictionary.Empty.ToBuilder(); + foreach (T t in values.Elements) { + var i = 0; + if (!d.TryGetValue(t, out i)) { + i = 0; + } + d[t] = i + 1; + } + return new MultiSet(d.ToImmutable()); + } + public static MultiSet FromSet(Set values) { + var d = ImmutableDictionary.Empty.ToBuilder(); + foreach (T t in values.Elements) { + d[t] = 1; + } + return new MultiSet(d.ToImmutable()); + } + + public bool Equals(MultiSet other) { + return other.IsSubsetOf(this) && this.IsSubsetOf(other); + } + public override bool Equals(object other) { + return other is MultiSet && Equals((MultiSet)other); + } + public override int GetHashCode() { + var hashCode = 1; + foreach (var kv in dict) { + var key = kv.Key.GetHashCode(); + key = (key << 3) | (key >> 29) ^ kv.Value.GetHashCode(); + hashCode = hashCode * (key + 3); + } + return hashCode; + } + public override string ToString() { + var s = "multiset{"; + var sep = ""; + foreach (var kv in dict) { + var t = kv.Key.ToString(); + for (int i = 0; i < kv.Value; i++) { + s += sep + t.ToString(); + sep = ", "; + } + } + return s + "}"; + } + public bool IsProperSubsetOf(MultiSet other) { + return !Equals(other) && IsSubsetOf(other); + } + public bool IsSubsetOf(MultiSet other) { + foreach (T t in dict.Keys) { + if (!other.dict.ContainsKey(t) || other.dict[t] < dict[t]) + return false; + } + return true; + } + public bool IsSupersetOf(MultiSet other) { + return other.IsSubsetOf(this); + } + public bool IsProperSupersetOf(MultiSet other) { + return other.IsProperSubsetOf(this); + } + public bool IsDisjointFrom(MultiSet other) { + foreach (T t in dict.Keys) { + if (other.dict.ContainsKey(t)) + return false; + } + foreach (T t in other.dict.Keys) { + if (dict.ContainsKey(t)) + return false; + } + return true; + } + public bool Contains(T t) { + return dict.ContainsKey(t); + } + public MultiSet Union(MultiSet other) { + if (dict.Count == 0) + return other; + else if (other.dict.Count == 0) + return this; + var r = ImmutableDictionary.Empty.ToBuilder(); + foreach (T t in dict.Keys) { + var i = 0; + if (!r.TryGetValue(t, out i)) { + i = 0; + } + r[t] = i + dict[t]; + } + foreach (T t in other.dict.Keys) { + var i = 0; + if (!r.TryGetValue(t, out i)) { + i = 0; + } + r[t] = i + other.dict[t]; + } + return new MultiSet(r.ToImmutable()); + } + public MultiSet Intersect(MultiSet other) { + if (dict.Count == 0) + return this; + else if (other.dict.Count == 0) + return other; + var r = ImmutableDictionary.Empty.ToBuilder(); + foreach (T t in dict.Keys) { + if (other.dict.ContainsKey(t)) { + r[t] = other.dict[t] < dict[t] ? other.dict[t] : dict[t]; + } + } + return new MultiSet(r.ToImmutable()); + } + public MultiSet Difference(MultiSet other) { // \result == this - other + if (dict.Count == 0) + return this; + else if (other.dict.Count == 0) + return this; + var r = ImmutableDictionary.Empty.ToBuilder(); + foreach (T t in dict.Keys) { + if (!other.dict.ContainsKey(t)) { + r[t] = dict[t]; + } else if (other.dict[t] < dict[t]) { + r[t] = dict[t] - other.dict[t]; + } + } + return new MultiSet(r.ToImmutable()); + } + public IEnumerable Elements { + get { + foreach (T t in dict.Keys) { + int n; + dict.TryGetValue(t, out n); + for (int i = 0; i < n; i ++) { + yield return t; + } + } + } + } + } + + public partial class Map + { + readonly ImmutableDictionary dict; + Map(ImmutableDictionary d) { + dict = d; + } + public static readonly Map Empty = new Map(ImmutableDictionary.Empty); + public static Map FromElements(params Pair[] values) { + var d = ImmutableDictionary.Empty.ToBuilder(); + foreach (Pair p in values) { + d[p.Car] = p.Cdr; + } + return new Map(d.ToImmutable()); + } + public static Map FromCollection(List> values) { + var d = ImmutableDictionary.Empty.ToBuilder(); + foreach (Pair p in values) { + d[p.Car] = p.Cdr; + } + return new Map(d.ToImmutable()); + } + public int Length { + get { return dict.Count; } + } + public long LongLength { + get { return dict.Count; } + } + public bool Equals(Map other) { + foreach (U u in dict.Keys) { + V v1, v2; + if (!dict.TryGetValue(u, out v1)) { + return false; // this shouldn't happen + } + if (!other.dict.TryGetValue(u, out v2)) { + return false; // other dictionary does not contain this element + } + if (!v1.Equals(v2)) { + return false; + } + } + foreach (U u in other.dict.Keys) { + if (!dict.ContainsKey(u)) { + return false; // this shouldn't happen + } + } + return true; + } + public override bool Equals(object other) { + return other is Map && Equals((Map)other); + } + public override int GetHashCode() { + var hashCode = 1; + foreach (var kv in dict) { + var key = kv.Key.GetHashCode(); + key = (key << 3) | (key >> 29) ^ kv.Value.GetHashCode(); + hashCode = hashCode * (key + 3); + } + return hashCode; + } + public override string ToString() { + var s = "map["; + var sep = ""; + foreach (var kv in dict) { + s += sep + kv.Key.ToString() + " := " + kv.Value.ToString(); + sep = ", "; + } + return s + "]"; + } + public bool IsDisjointFrom(Map other) { + foreach (U u in dict.Keys) { + if (other.dict.ContainsKey(u)) + return false; + } + foreach (U u in other.dict.Keys) { + if (dict.ContainsKey(u)) + return false; + } + return true; + } + public bool Contains(U u) { + return dict.ContainsKey(u); + } + public V Select(U index) { + return dict[index]; + } + public Map Update(U index, V val) { + return new Map(dict.SetItem(index, val)); + } + public IEnumerable Domain { + get { + return dict.Keys; + } + } + } +#else // !def DAFNY_USE_SYSTEM_COLLECTIONS_IMMUTABLE + public class Set + { + HashSet set; + Set(HashSet s) { + this.set = s; + } + public static Set Empty { + get { + return new Set(new HashSet()); + } + } + public static Set FromElements(params T[] values) { + var s = new HashSet(); + foreach (T t in values) + s.Add(t); + return new Set(s); + } + public static Set FromCollection(ICollection values) { + HashSet s = new HashSet(); + foreach (T t in values) + s.Add(t); + return new Set(s); + } + public int Length { + get { return this.set.Count; } + } + public long LongLength { + get { return this.set.Count; } + } + public IEnumerable Elements { + get { + return this.set; + } + } + /// + /// This is an inefficient iterator for producing all subsets of "this". Each set returned is the same + /// Set object (but this Set object is fresh; in particular, it is not "this"). + /// + public IEnumerable> AllSubsets { + get { + // Start by putting all set elements into a list + var elmts = new List(); + elmts.AddRange(this.set); + var n = elmts.Count; + var which = new bool[n]; + var s = new Set(new HashSet()); + while (true) { + yield return s; + // "add 1" to "which", as if doing a carry chain. For every digit changed, change the membership of the corresponding element in "s". + int i = 0; + for (; i < n && which[i]; i++) { + which[i] = false; + s.set.Remove(elmts[i]); + } + if (i == n) { + // we have cycled through all the subsets + break; + } + which[i] = true; + s.set.Add(elmts[i]); + } + } + } + public bool Equals(Set other) { + return this.set.Count == other.set.Count && IsSubsetOf(other); + } + public override bool Equals(object other) { + return other is Set && Equals((Set)other); + } + public override int GetHashCode() { + var hashCode = 1; + foreach (var t in this.set) { + hashCode = hashCode * (t.GetHashCode()+3); + } + return hashCode; + } + public override string ToString() { + var s = "{"; + var sep = ""; + foreach (var t in this.set) { + s += sep + t.ToString(); + sep = ", "; + } + return s + "}"; + } + public bool IsProperSubsetOf(Set other) { + return this.set.Count < other.set.Count && IsSubsetOf(other); + } + public bool IsSubsetOf(Set other) { + if (other.set.Count < this.set.Count) + return false; + foreach (T t in this.set) { + if (!other.set.Contains(t)) + return false; + } + return true; + } + public bool IsSupersetOf(Set other) { + return other.IsSubsetOf(this); + } + public bool IsProperSupersetOf(Set other) { + return other.IsProperSubsetOf(this); + } + public bool IsDisjointFrom(Set other) { + HashSet a, b; + if (this.set.Count < other.set.Count) { + a = this.set; b = other.set; + } else { + a = other.set; b = this.set; + } + foreach (T t in a) { + if (b.Contains(t)) + return false; + } + return true; + } + public bool Contains(T t) { + return this.set.Contains(t); + } + public Set Union(Set other) { + if (this.set.Count == 0) + return other; + else if (other.set.Count == 0) + return this; + HashSet a, b; + if (this.set.Count < other.set.Count) { + a = this.set; b = other.set; + } else { + a = other.set; b = this.set; + } + var r = new HashSet(); + foreach (T t in b) + r.Add(t); + foreach (T t in a) + r.Add(t); + return new Set(r); + } + public Set Intersect(Set other) { + if (this.set.Count == 0) + return this; + else if (other.set.Count == 0) + return other; + HashSet a, b; + if (this.set.Count < other.set.Count) { + a = this.set; b = other.set; + } else { + a = other.set; b = this.set; + } + var r = new HashSet(); + foreach (T t in a) { + if (b.Contains(t)) + r.Add(t); + } + return new Set(r); + } + public Set Difference(Set other) { + if (this.set.Count == 0) + return this; + else if (other.set.Count == 0) + return this; + var r = new HashSet(); + foreach (T t in this.set) { + if (!other.set.Contains(t)) + r.Add(t); + } + return new Set(r); + } + } + public class MultiSet + { + Dictionary dict; + MultiSet(Dictionary d) { + dict = d; + } + public static MultiSet Empty { + get { + return new MultiSet(new Dictionary(0)); + } + } + public static MultiSet FromElements(params T[] values) { + Dictionary d = new Dictionary(values.Length); + foreach (T t in values) { + var i = 0; + if (!d.TryGetValue(t, out i)) { + i = 0; + } + d[t] = i + 1; + } + return new MultiSet(d); + } + public static MultiSet FromCollection(ICollection values) { + Dictionary d = new Dictionary(); + foreach (T t in values) { + var i = 0; + if (!d.TryGetValue(t, out i)) { + i = 0; + } + d[t] = i + 1; + } + return new MultiSet(d); + } + public static MultiSet FromSeq(Sequence values) { + Dictionary d = new Dictionary(); + foreach (T t in values.Elements) { + var i = 0; + if (!d.TryGetValue(t, out i)) { + i = 0; + } + d[t] = i + 1; + } + return new MultiSet(d); + } + public static MultiSet FromSet(Set values) { + Dictionary d = new Dictionary(); + foreach (T t in values.Elements) { + d[t] = 1; + } + return new MultiSet(d); + } + + public bool Equals(MultiSet other) { + return other.IsSubsetOf(this) && this.IsSubsetOf(other); + } + public override bool Equals(object other) { + return other is MultiSet && Equals((MultiSet)other); + } + public override int GetHashCode() { + var hashCode = 1; + foreach (var kv in dict) { + var key = kv.Key.GetHashCode(); + key = (key << 3) | (key >> 29) ^ kv.Value.GetHashCode(); + hashCode = hashCode * (key + 3); + } + return hashCode; + } + public override string ToString() { + var s = "multiset{"; + var sep = ""; + foreach (var kv in dict) { + var t = kv.Key.ToString(); + for (int i = 0; i < kv.Value; i++) { + s += sep + t.ToString(); + sep = ", "; + } + } + return s + "}"; + } + public bool IsProperSubsetOf(MultiSet other) { + return !Equals(other) && IsSubsetOf(other); + } + public bool IsSubsetOf(MultiSet other) { + foreach (T t in dict.Keys) { + if (!other.dict.ContainsKey(t) || other.dict[t] < dict[t]) + return false; + } + return true; + } + public bool IsSupersetOf(MultiSet other) { + return other.IsSubsetOf(this); + } + public bool IsProperSupersetOf(MultiSet other) { + return other.IsProperSubsetOf(this); + } + public bool IsDisjointFrom(MultiSet other) { + foreach (T t in dict.Keys) { + if (other.dict.ContainsKey(t)) + return false; + } + foreach (T t in other.dict.Keys) { + if (dict.ContainsKey(t)) + return false; + } + return true; + } + public bool Contains(T t) { + return dict.ContainsKey(t); + } + public MultiSet Union(MultiSet other) { + if (dict.Count == 0) + return other; + else if (other.dict.Count == 0) + return this; + var r = new Dictionary(); + foreach (T t in dict.Keys) { + var i = 0; + if (!r.TryGetValue(t, out i)) { + i = 0; + } + r[t] = i + dict[t]; + } + foreach (T t in other.dict.Keys) { + var i = 0; + if (!r.TryGetValue(t, out i)) { + i = 0; + } + r[t] = i + other.dict[t]; + } + return new MultiSet(r); + } + public MultiSet Intersect(MultiSet other) { + if (dict.Count == 0) + return this; + else if (other.dict.Count == 0) + return other; + var r = new Dictionary(); + foreach (T t in dict.Keys) { + if (other.dict.ContainsKey(t)) { + r.Add(t, other.dict[t] < dict[t] ? other.dict[t] : dict[t]); + } + } + return new MultiSet(r); + } + public MultiSet Difference(MultiSet other) { // \result == this - other + if (dict.Count == 0) + return this; + else if (other.dict.Count == 0) + return this; + var r = new Dictionary(); + foreach (T t in dict.Keys) { + if (!other.dict.ContainsKey(t)) { + r.Add(t, dict[t]); + } else if (other.dict[t] < dict[t]) { + r.Add(t, dict[t] - other.dict[t]); + } + } + return new MultiSet(r); + } + public IEnumerable Elements { + get { + List l = new List(); + foreach (T t in dict.Keys) { + int n; + dict.TryGetValue(t, out n); + for (int i = 0; i < n; i ++) { + l.Add(t); + } + } + return l; + } + } + } + + public class Map + { + Dictionary dict; + Map(Dictionary d) { + dict = d; + } + public static Map Empty { + get { + return new Map(new Dictionary()); + } + } + public static Map FromElements(params Pair[] values) { + Dictionary d = new Dictionary(values.Length); + foreach (Pair p in values) { + d[p.Car] = p.Cdr; + } + return new Map(d); + } + public static Map FromCollection(List> values) { + Dictionary d = new Dictionary(values.Count); + foreach (Pair p in values) { + d[p.Car] = p.Cdr; + } + return new Map(d); + } + public int Length { + get { return dict.Count; } + } + public long LongLength { + get { return dict.Count; } + } + public bool Equals(Map other) { + foreach (U u in dict.Keys) { + V v1, v2; + if (!dict.TryGetValue(u, out v1)) { + return false; // this shouldn't happen + } + if (!other.dict.TryGetValue(u, out v2)) { + return false; // other dictionary does not contain this element + } + if (!v1.Equals(v2)) { + return false; + } + } + foreach (U u in other.dict.Keys) { + if (!dict.ContainsKey(u)) { + return false; // this shouldn't happen + } + } + return true; + } + public override bool Equals(object other) { + return other is Map && Equals((Map)other); + } + public override int GetHashCode() { + var hashCode = 1; + foreach (var kv in dict) { + var key = kv.Key.GetHashCode(); + key = (key << 3) | (key >> 29) ^ kv.Value.GetHashCode(); + hashCode = hashCode * (key + 3); + } + return hashCode; + } + public override string ToString() { + var s = "map["; + var sep = ""; + foreach (var kv in dict) { + s += sep + kv.Key.ToString() + " := " + kv.Value.ToString(); + sep = ", "; + } + return s + "]"; + } + public bool IsDisjointFrom(Map other) { + foreach (U u in dict.Keys) { + if (other.dict.ContainsKey(u)) + return false; + } + foreach (U u in other.dict.Keys) { + if (dict.ContainsKey(u)) + return false; + } + return true; + } + public bool Contains(U u) { + return dict.ContainsKey(u); + } + public V Select(U index) { + return dict[index]; + } + public Map Update(U index, V val) { + Dictionary d = new Dictionary(dict); + d[index] = val; + return new Map(d); + } + public IEnumerable Domain { + get { + return dict.Keys; + } + } + } +#endif + public class Sequence + { + T[] elmts; + public Sequence(T[] ee) { + elmts = ee; + } + public static Sequence Empty { + get { + return new Sequence(new T[0]); + } + } + public static Sequence FromElements(params T[] values) { + return new Sequence(values); + } + public static Sequence FromString(string s) { + return new Sequence(s.ToCharArray()); + } + public int Length { + get { return elmts.Length; } + } + public long LongLength { + get { return elmts.LongLength; } + } + public T[] Elements { + get { + return elmts; + } + } + public IEnumerable UniqueElements { + get { + var st = Set.FromElements(elmts); + return st.Elements; + } + } + public T Select(ulong index) { + return elmts[index]; + } + public T Select(long index) { + return elmts[index]; + } + public T Select(uint index) { + return elmts[index]; + } + public T Select(int index) { + return elmts[index]; + } + public T Select(BigInteger index) { + return elmts[(int)index]; + } + public Sequence Update(long index, T t) { + T[] a = (T[])elmts.Clone(); + a[index] = t; + return new Sequence(a); + } + public Sequence Update(ulong index, T t) { + return Update((long)index, t); + } + public Sequence Update(BigInteger index, T t) { + return Update((long)index, t); + } + public bool Equals(Sequence other) { + int n = elmts.Length; + return n == other.elmts.Length && EqualUntil(other, n); + } + public override bool Equals(object other) { + return other is Sequence && Equals((Sequence)other); + } + public override int GetHashCode() { + if (elmts == null || elmts.Length == 0) + return 0; + var hashCode = 0; + for (var i = 0; i < elmts.Length; i++) { + hashCode = (hashCode << 3) | (hashCode >> 29) ^ elmts[i].GetHashCode(); + } + return hashCode; + } + public override string ToString() { + if (elmts is char[]) { + var s = ""; + foreach (var t in elmts) { + s += t.ToString(); + } + return s; + } else { + var s = "["; + var sep = ""; + foreach (var t in elmts) { + s += sep + t.ToString(); + sep = ", "; + } + return s + "]"; + } + } + bool EqualUntil(Sequence other, int n) { + for (int i = 0; i < n; i++) { + if (!elmts[i].Equals(other.elmts[i])) + return false; + } + return true; + } + public bool IsProperPrefixOf(Sequence other) { + int n = elmts.Length; + return n < other.elmts.Length && EqualUntil(other, n); + } + public bool IsPrefixOf(Sequence other) { + int n = elmts.Length; + return n <= other.elmts.Length && EqualUntil(other, n); + } + public Sequence Concat(Sequence other) { + if (elmts.Length == 0) + return other; + else if (other.elmts.Length == 0) + return this; + T[] a = new T[elmts.Length + other.elmts.Length]; + System.Array.Copy(elmts, 0, a, 0, elmts.Length); + System.Array.Copy(other.elmts, 0, a, elmts.Length, other.elmts.Length); + return new Sequence(a); + } + public bool Contains(T t) { + int n = elmts.Length; + for (int i = 0; i < n; i++) { + if (t.Equals(elmts[i])) + return true; + } + return false; + } + public Sequence Take(long m) { + if (elmts.LongLength == m) + return this; + T[] a = new T[m]; + System.Array.Copy(elmts, a, m); + return new Sequence(a); + } + public Sequence Take(ulong n) { + return Take((long)n); + } + public Sequence Take(BigInteger n) { + return Take((long)n); + } + public Sequence Drop(long m) { + if (m == 0) + return this; + T[] a = new T[elmts.Length - m]; + System.Array.Copy(elmts, m, a, 0, elmts.Length - m); + return new Sequence(a); + } + public Sequence Drop(ulong n) { + return Drop((long)n); + } + public Sequence Drop(BigInteger n) { + if (n.IsZero) + return this; + return Drop((long)n); + } + } + public struct Pair + { + public readonly A Car; + public readonly B Cdr; + public Pair(A a, B b) { + this.Car = a; + this.Cdr = b; + } + } + public partial class Helpers { + // Computing forall/exists quantifiers + public static bool QuantBool(bool frall, System.Predicate pred) { + if (frall) { + return pred(false) && pred(true); + } else { + return pred(false) || pred(true); + } + } + 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; } + } + return frall; + } + public static bool QuantSet(Dafny.Set set, bool frall, System.Predicate pred) { + foreach (var u in set.Elements) { + if (pred(u) != frall) { return !frall; } + } + return frall; + } + public static bool QuantMap(Dafny.Map map, bool frall, System.Predicate pred) { + foreach (var u in map.Domain) { + if (pred(u) != frall) { return !frall; } + } + return frall; + } + public static bool QuantSeq(Dafny.Sequence seq, bool frall, System.Predicate pred) { + foreach (var u in seq.Elements) { + if (pred(u) != frall) { return !frall; } + } + return frall; + } + public static bool QuantDatatype(IEnumerable set, bool frall, System.Predicate pred) { + foreach (var u in set) { + if (pred(u) != frall) { return !frall; } + } + return frall; + } + // Enumerating other collections + public delegate Dafny.Set ComprehensionDelegate(); + public delegate Dafny.Map MapComprehensionDelegate(); + public static IEnumerable AllBooleans { + get { + yield return false; + yield return true; + } + } + public static IEnumerable AllIntegers { + get { + yield return new BigInteger(0); + for (var j = new BigInteger(1);; j++) { + yield return j; + yield return -j; + } + } + } + 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) { + return (sbyte)EuclideanDivision_int(a, b); + } + public static short EuclideanDivision_short(short a, short b) { + return (short)EuclideanDivision_int(a, b); + } + public static int EuclideanDivision_int(int a, int b) { + if (0 <= a) { + if (0 <= b) { + // +a +b: a/b + return (int)(((uint)(a)) / ((uint)(b))); + } else { + // +a -b: -(a/(-b)) + return -((int)(((uint)(a)) / ((uint)(unchecked(-b))))); + } + } else { + if (0 <= b) { + // -a +b: -((-a-1)/b) - 1 + return -((int)(((uint)(-(a + 1))) / ((uint)(b)))) - 1; + } else { + // -a -b: ((-a-1)/(-b)) + 1 + return ((int)(((uint)(-(a + 1))) / ((uint)(unchecked(-b))))) + 1; + } + } + } + public static long EuclideanDivision_long(long a, long b) { + if (0 <= a) { + if (0 <= b) { + // +a +b: a/b + return (long)(((ulong)(a)) / ((ulong)(b))); + } else { + // +a -b: -(a/(-b)) + return -((long)(((ulong)(a)) / ((ulong)(unchecked(-b))))); + } + } else { + if (0 <= b) { + // -a +b: -((-a-1)/b) - 1 + return -((long)(((ulong)(-(a + 1))) / ((ulong)(b)))) - 1; + } else { + // -a -b: ((-a-1)/(-b)) + 1 + return ((long)(((ulong)(-(a + 1))) / ((ulong)(unchecked(-b))))) + 1; + } + } + } + public static BigInteger EuclideanDivision(BigInteger a, BigInteger b) { + if (0 <= a.Sign) { + if (0 <= b.Sign) { + // +a +b: a/b + return BigInteger.Divide(a, b); + } else { + // +a -b: -(a/(-b)) + return BigInteger.Negate(BigInteger.Divide(a, BigInteger.Negate(b))); + } + } else { + if (0 <= b.Sign) { + // -a +b: -((-a-1)/b) - 1 + return BigInteger.Negate(BigInteger.Divide(BigInteger.Negate(a) - 1, b)) - 1; + } else { + // -a -b: ((-a-1)/(-b)) + 1 + return BigInteger.Divide(BigInteger.Negate(a) - 1, BigInteger.Negate(b)) + 1; + } + } + } + // pre: b != 0 + // post: result == a%b, as defined by Euclidean Division (http://en.wikipedia.org/wiki/Modulo_operation) + public static sbyte EuclideanModulus_sbyte(sbyte a, sbyte b) { + return (sbyte)EuclideanModulus_int(a, b); + } + public static short EuclideanModulus_short(short a, short b) { + return (short)EuclideanModulus_int(a, b); + } + public static int EuclideanModulus_int(int a, int b) { + uint bp = (0 <= b) ? (uint)b : (uint)(unchecked(-b)); + if (0 <= a) { + // +a: a % b' + return (int)(((uint)a) % bp); + } else { + // c = ((-a) % b') + // -a: b' - c if c > 0 + // -a: 0 if c == 0 + uint c = ((uint)(unchecked(-a))) % bp; + return (int)(c == 0 ? c : bp - c); + } + } + public static long EuclideanModulus_long(long a, long b) { + ulong bp = (0 <= b) ? (ulong)b : (ulong)(unchecked(-b)); + if (0 <= a) { + // +a: a % b' + return (long)(((ulong)a) % bp); + } else { + // c = ((-a) % b') + // -a: b' - c if c > 0 + // -a: 0 if c == 0 + ulong c = ((ulong)(unchecked(-a))) % bp; + return (long)(c == 0 ? c : bp - c); + } + } + public static BigInteger EuclideanModulus(BigInteger a, BigInteger b) { + var bp = BigInteger.Abs(b); + if (0 <= a.Sign) { + // +a: a % b' + return BigInteger.Remainder(a, bp); + } else { + // c = ((-a) % b') + // -a: b' - c if c > 0 + // -a: 0 if c == 0 + var c = BigInteger.Remainder(BigInteger.Negate(a), bp); + return c.IsZero ? c : BigInteger.Subtract(bp, c); + } + } + public static Sequence SeqFromArray(T[] array) { + return new Sequence((T[])array.Clone()); + } + // In .NET version 4.5, it it possible to mark a method with "AggressiveInlining", which says to inline the + // method if possible. Method "ExpressionSequence" would be a good candidate for it: + // [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static U ExpressionSequence(T t, U u) + { + return u; + } + + public static U Let(T t, Func f) { + return f(t); + } + + public delegate Result Function(Input input); + + public static A Id(A a) { + return a; + } + } + + public struct BigRational + { + public static readonly BigRational ZERO = new BigRational(0); + + BigInteger num, den; // invariant 1 <= den + public override string ToString() { + return string.Format("({0}.0 / {1}.0)", num, den); + } + public BigRational(int n) { + num = new BigInteger(n); + den = BigInteger.One; + } + public BigRational(BigInteger n, BigInteger d) { + // requires 1 <= d + num = n; + den = d; + } + public BigInteger ToBigInteger() { + if (0 <= num) { + return num / den; + } else { + return (num - den + 1) / den; + } + } + /// + /// Returns values such that aa/dd == a and bb/dd == b. + /// + private static void Normalize(BigRational a, BigRational b, out BigInteger aa, out BigInteger bb, out BigInteger dd) { + var gcd = BigInteger.GreatestCommonDivisor(a.den, b.den); + var xx = a.den / gcd; + var yy = b.den / gcd; + // We now have a == a.num / (xx * gcd) and b == b.num / (yy * gcd). + aa = a.num * yy; + bb = b.num * xx; + dd = a.den * yy; + } + public int CompareTo(BigRational that) { + // simple things first + int asign = this.num.Sign; + int bsign = that.num.Sign; + if (asign < 0 && 0 <= bsign) { + return 1; + } else if (asign <= 0 && 0 < bsign) { + return 1; + } else if (bsign < 0 && 0 <= asign) { + return -1; + } else if (bsign <= 0 && 0 < asign) { + return -1; + } + BigInteger aa, bb, dd; + Normalize(this, that, out aa, out bb, out dd); + return aa.CompareTo(bb); + } + public override int GetHashCode() { + return num.GetHashCode() + 29 * den.GetHashCode(); + } + public override bool Equals(object obj) { + if (obj is BigRational) { + return this == (BigRational)obj; + } else { + return false; + } + } + public static bool operator ==(BigRational a, BigRational b) { + return a.CompareTo(b) == 0; + } + public static bool operator !=(BigRational a, BigRational b) { + return a.CompareTo(b) != 0; + } + public static bool operator >(BigRational a, BigRational b) { + return 0 < a.CompareTo(b); + } + public static bool operator >=(BigRational a, BigRational b) { + return 0 <= a.CompareTo(b); + } + public static bool operator <(BigRational a, BigRational b) { + return a.CompareTo(b) < 0; + } + public static bool operator <=(BigRational a, BigRational b) { + return a.CompareTo(b) <= 0; + } + public static BigRational operator +(BigRational a, BigRational b) { + BigInteger aa, bb, dd; + Normalize(a, b, out aa, out bb, out dd); + return new BigRational(aa + bb, dd); + } + public static BigRational operator -(BigRational a, BigRational b) { + BigInteger aa, bb, dd; + Normalize(a, b, out aa, out bb, out dd); + return new BigRational(aa - bb, dd); + } + public static BigRational operator -(BigRational a) { + return new BigRational(-a.num, a.den); + } + public static BigRational operator *(BigRational a, BigRational b) { + return new BigRational(a.num * b.num, a.den * b.den); + } + public static BigRational operator /(BigRational a, BigRational b) { + // Compute the reciprocal of b + BigRational bReciprocal; + if (0 < b.num) { + bReciprocal = new BigRational(b.den, b.num); + } else { + // this is the case b.num < 0 + bReciprocal = new BigRational(-b.den, -b.num); + } + return a * bReciprocal; + } + } +} -- cgit v1.2.3 From 4685709c25b5f22c51b6419c4898a689e8ffb4f9 Mon Sep 17 00:00:00 2001 From: Bryan Parno Date: Thu, 17 Sep 2015 15:30:39 -0700 Subject: Fix a check that occasionally led to an out of bounds exception in the extension --- Binaries/dafny | Bin 315 -> 19456 bytes Source/DafnyExtension/IdentifierTagger.cs | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Binaries/dafny b/Binaries/dafny index 889ebc81..05fa3203 100755 Binary files a/Binaries/dafny and b/Binaries/dafny differ diff --git a/Source/DafnyExtension/IdentifierTagger.cs b/Source/DafnyExtension/IdentifierTagger.cs index 5b70329d..d638cb6c 100644 --- a/Source/DafnyExtension/IdentifierTagger.cs +++ b/Source/DafnyExtension/IdentifierTagger.cs @@ -77,7 +77,7 @@ namespace DafnyLanguage int start = entire.Start; int end = entire.End; foreach (var r in _regions) { - if (0 <= r.Length && r.Start <= end && start <= r.Start + r.Length) { + if (0 <= r.Length && r.Start >= start && r.Start + r.Length <= end) { DafnyTokenKind kind; switch (r.Kind) { case IdRegion.OccurrenceKind.Use: -- cgit v1.2.3 From 800885b4d7d0164803c0c2f117b78c65479283f9 Mon Sep 17 00:00:00 2001 From: leino Date: Sun, 20 Sep 2015 21:51:42 -0700 Subject: Preliminary refactoring of ghost-statement computations to after type checking --- Source/Dafny/DafnyAst.cs | 84 +++-- Source/Dafny/Resolver.cs | 478 +++++++++++++++++++++++---- Test/dafny0/AssumptionVariables0.dfy | 8 +- Test/dafny0/AssumptionVariables0.dfy.expect | 5 +- Test/dafny0/ParallelResolveErrors.dfy | 4 +- Test/dafny0/ParallelResolveErrors.dfy.expect | 5 +- Test/dafny0/ResolutionErrors.dfy | 16 +- Test/dafny0/ResolutionErrors.dfy.expect | 13 +- Test/dafny0/TypeTests.dfy.expect | 2 +- 9 files changed, 495 insertions(+), 120 deletions(-) diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index 9bff2038..83db732e 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -1655,7 +1655,7 @@ namespace Microsoft.Dafny { // it is abstract). Otherwise, it points to that definition. public ModuleSignature CompileSignature = null; // This is the version of the signature that should be used at compile time. public ModuleSignature Refines = null; - public bool IsGhost = false; + public bool IsAbstract = false; public ModuleSignature() {} public bool FindSubmodule(string name, out ModuleSignature pp) { @@ -3461,7 +3461,23 @@ namespace Microsoft.Dafny { Contract.Invariant(EndTok != null); } +#if OLD_GHOST_HANDLING public bool IsGhost; // filled in by resolution +#else + public static bool ReadyToDealWithGhostField = true; + private bool izzaGhost; + public bool IsGhost { + get { + Contract.Requires(ReadyToDealWithGhostField); + return izzaGhost; + } + set { + Contract.Requires(ReadyToDealWithGhostField); + izzaGhost = value; + } + } + public bool C_IsGhost; // new ghost computation +#endif public Statement(IToken tok, IToken endTok, Attributes attrs) { Contract.Requires(tok != null); @@ -4051,18 +4067,42 @@ namespace Microsoft.Dafny { /// This method assumes "lhs" has been successfully resolved. /// public static bool LhsIsToGhost(Expression lhs) { + Contract.Requires(lhs != null); + return LhsIsToGhost_Which(lhs) == NonGhostKind.IsGhost; + } + public enum NonGhostKind { IsGhost, Variable, Field, ArrayElement } + public static string NonGhostKind_To_String(NonGhostKind gk) { + Contract.Requires(gk != NonGhostKind.IsGhost); + switch (gk) { + case NonGhostKind.Variable: return "non-ghost variable"; + case NonGhostKind.Field: return "non-ghost field"; + case NonGhostKind.ArrayElement: return "array element"; + default: + Contract.Assume(false); // unexpected NonGhostKind + throw new cce.UnreachableException(); // please compiler + } + } + /// + /// This method assumes "lhs" has been successfully resolved. + /// + public static NonGhostKind LhsIsToGhost_Which(Expression lhs) { Contract.Requires(lhs != null); lhs = lhs.Resolved; if (lhs is IdentifierExpr) { var x = (IdentifierExpr)lhs; - return x.Var.IsGhost; + if (!x.Var.IsGhost) { + return NonGhostKind.Variable; + } } else if (lhs is MemberSelectExpr) { var x = (MemberSelectExpr)lhs; - return x.Member.IsGhost; + if (!x.Member.IsGhost) { + return NonGhostKind.Field; + } } else { // LHS denotes an array element, which is always non-ghost - return false; + return NonGhostKind.ArrayElement; } + return NonGhostKind.IsGhost; } } @@ -7741,24 +7781,6 @@ namespace Microsoft.Dafny { public class BottomUpVisitor { - public void Visit(Expression expr) { - Contract.Requires(expr != null); - // recursively visit all subexpressions and all substatements - expr.SubExpressions.Iter(Visit); - if (expr is StmtExpr) { - // a StmtExpr also has a sub-statement - var e = (StmtExpr)expr; - Visit(e.S); - } - VisitOneExpr(expr); - } - public void Visit(Statement stmt) { - Contract.Requires(stmt != null); - // recursively visit all subexpressions and all substatements - stmt.SubExpressions.Iter(Visit); - stmt.SubStatements.Iter(Visit); - VisitOneStmt(stmt); - } public void Visit(IEnumerable exprs) { exprs.Iter(Visit); } @@ -7801,6 +7823,24 @@ namespace Microsoft.Dafny { if (function.Body != null) { Visit(function.Body); } //TODO More? } + public void Visit(Expression expr) { + Contract.Requires(expr != null); + // recursively visit all subexpressions and all substatements + expr.SubExpressions.Iter(Visit); + if (expr is StmtExpr) { + // a StmtExpr also has a sub-statement + var e = (StmtExpr)expr; + Visit(e.S); + } + VisitOneExpr(expr); + } + public void Visit(Statement stmt) { + Contract.Requires(stmt != null); + // recursively visit all subexpressions and all substatements + stmt.SubExpressions.Iter(Visit); + stmt.SubStatements.Iter(Visit); + VisitOneStmt(stmt); + } protected virtual void VisitOneExpr(Expression expr) { Contract.Requires(expr != null); // by default, do nothing diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 66f8f449..57a42c73 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -345,7 +345,7 @@ namespace Microsoft.Dafny "module " + Util.Comma(".", abs.CompilePath, x => x.val) + " must be a refinement of " + Util.Comma(".", abs.Path, x => x.val)); } - abs.Signature.IsGhost = compileSig.IsGhost; + abs.Signature.IsAbstract = compileSig.IsAbstract; // always keep the ghost information, to supress a spurious error message when the compile module isn't actually a refinement } } @@ -835,14 +835,14 @@ namespace Microsoft.Dafny foreach (var kv in m.StaticMembers) { info.StaticMembers[kv.Key] = kv.Value; } - info.IsGhost = m.IsGhost; + info.IsAbstract = m.IsAbstract; return info; } ModuleSignature RegisterTopLevelDecls(ModuleDefinition moduleDef, bool useImports) { Contract.Requires(moduleDef != null); var sig = new ModuleSignature(); sig.ModuleDef = moduleDef; - sig.IsGhost = moduleDef.IsAbstract; + sig.IsAbstract = moduleDef.IsAbstract; List declarations = moduleDef.TopLevelDecls; // First go through and add anything from the opened imports @@ -1195,7 +1195,7 @@ namespace Microsoft.Dafny var sig = RegisterTopLevelDecls(mod, false); sig.Refines = p.Refines; sig.CompileSignature = p; - sig.IsGhost = p.IsGhost; + sig.IsAbstract = p.IsAbstract; sig.ExclusiveRefinement = p.ExclusiveRefinement; mods.Add(mod); ResolveModuleDefinition(mod, sig); @@ -1279,7 +1279,7 @@ namespace Microsoft.Dafny } else if (d is ModuleDecl) { var decl = (ModuleDecl)d; if (!def.IsAbstract) { - if (decl.Signature.IsGhost) + if (decl.Signature.IsAbstract) { if (!(def.IsDefaultModule)) // _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. @@ -1400,43 +1400,7 @@ namespace Microsoft.Dafny ResolveClassMemberBodies(cl); } allTypeParameters.PopMarker(); - SolveAllTypeConstraints(); - } - - if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { - 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 (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(); Dictionary nativeTypeMap = new Dictionary(); foreach (var nativeType in NativeTypes) { @@ -1546,6 +1510,42 @@ namespace Microsoft.Dafny } } + 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 (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(); + if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { // fill in the postconditions and bodies of prefix lemmas foreach (var com in ModuleDefinition.AllFixpointLemmas(declarations)) { @@ -2972,6 +2972,294 @@ namespace Microsoft.Dafny #endregion CheckEqualityTypes + // ------------------------------------------------------------------------------------------------------ + // ----- ComputeGhostInterest --------------------------------------------------------------------------- + // ------------------------------------------------------------------------------------------------------ + #region ComputeGhostInterest + public void ComputeGhostInterest(Statement stmt, ICodeContext codeContext) { + Contract.Requires(stmt != null); + Contract.Requires(codeContext != null); + var visitor = new GhostInterest_Visitor(codeContext, this); + visitor.Visit(stmt, codeContext.IsGhost); + } + class GhostInterest_Visitor + { + readonly ICodeContext codeContext; + readonly Resolver resolver; + public GhostInterest_Visitor(ICodeContext codeContext, Resolver resolver) { + Contract.Requires(codeContext != null); + Contract.Requires(resolver != null); + this.codeContext = codeContext; + this.resolver = resolver; + } + protected void Error(Statement stmt, string msg, params object[] msgArgs) { + Contract.Requires(stmt != null); + Contract.Requires(msg != null); + Contract.Requires(msgArgs != null); + resolver.reporter.Error(MessageSource.Resolver, stmt, msg, msgArgs); + } + protected void Error(Expression expr, string msg, params object[] msgArgs) { + Contract.Requires(expr != null); + Contract.Requires(msg != null); + 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 + /// 1. Reports an error if the statement assigns to a non-ghost field and the right-hand side + /// depends on a ghost. + /// 2. Sets to "true" the .IsGhost field of the statement if it should be erased during compilation. + /// In that case, substatements should be visited with "mustBeErasable". + /// Note that the method called by a StmtExpr must be ghost; however, this is checked elsewhere. For + /// this reason, it is not necessary to visit all subexpressions, unless the subexpression + /// matter for the ghost checking/recording of "stmt". + /// Note, it is important to set the statement's ghost status before descending into its sub-statements, + /// because break statements look at the ghost status of its enclosing statements. + /// + public void Visit(Statement stmt, bool mustBeErasable) { + Contract.Assume(!codeContext.IsGhost || mustBeErasable); // (this is really a precondition) codeContext.IsGhost ==> mustBeErasable + stmt.IsGhost = false; // DEBUG + + if (stmt is PredicateStmt) { + stmt.C_IsGhost = true; + + } else if (stmt is PrintStmt) { + var s = (PrintStmt)stmt; + if (mustBeErasable) { + Error(stmt, "print statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); + } else { + s.Args.Iter(resolver.CheckIsNonGhost); + } + + } else if (stmt is BreakStmt) { + var s = (BreakStmt)stmt; + if (mustBeErasable) { + if (!s.TargetStmt.C_IsGhost && !resolver.inSpecOnlyContext[s.TargetStmt]) { // TODO: inSpecOnlyContext should probably move from Resolver to GhostInterest_Visitor + var targetIsLoop = s.TargetStmt is WhileStmt || s.TargetStmt is AlternativeLoopStmt; + Error(stmt, "ghost-context break statement is not allowed to break out of non-ghost " + (targetIsLoop ? "loop" : "structure")); + } + } + + } else if (stmt is ProduceStmt) { + var s = (ProduceStmt)stmt; + var kind = stmt is YieldStmt ? "yield" : "return"; + if (mustBeErasable && !codeContext.IsGhost) { + Error(stmt, "{0} statement is not allowed in this context (because it is guarded by a specification-only expression)", kind); + } + if (s.hiddenUpdate != null) { + Visit(s.hiddenUpdate, mustBeErasable); + } + + } else if (stmt is AssignSuchThatStmt) { + var s = (AssignSuchThatStmt)stmt; + s.C_IsGhost = mustBeErasable || s.AssumeToken != null || s.Lhss.Any(AssignStmt.LhsIsToGhost); + if (mustBeErasable && !codeContext.IsGhost) { + foreach (var lhs in s.Lhss) { + var gk = AssignStmt.LhsIsToGhost_Which(lhs); + if (gk != AssignStmt.NonGhostKind.IsGhost) { + Error(lhs, "cannot assign to {0} in a ghost context", AssignStmt.NonGhostKind_To_String(gk)); + } + } + } else if (!mustBeErasable && s.AssumeToken == null && resolver.UsesSpecFeatures(s.Expr)) { + foreach (var lhs in s.Lhss) { + var gk = AssignStmt.LhsIsToGhost_Which(lhs); + if (gk != AssignStmt.NonGhostKind.IsGhost) { + Error(lhs, "{0} cannot be assigned a value that depends on a ghost", AssignStmt.NonGhostKind_To_String(gk)); + } + } + } + + } else if (stmt is UpdateStmt) { + var s = (UpdateStmt)stmt; + s.ResolvedStatements.Iter(ss => Visit(ss, mustBeErasable)); + s.C_IsGhost = s.ResolvedStatements.All(ss => ss.C_IsGhost); + + } else if (stmt is VarDeclStmt) { + var s = (VarDeclStmt)stmt; + if (mustBeErasable) { + foreach (var local in s.Locals) { + // a local variable in a specification-only context might as well be ghost + 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.C_IsGhost = (s.Update == null || s.Update.C_IsGhost) && s.Locals.All(v => v.IsGhost); + if (s.Update != null) { + Visit(s.Update, mustBeErasable); + } + + } else if (stmt is AssignStmt) { + var s = (AssignStmt)stmt; + var lhs = s.Lhs.Resolved; + var gk = AssignStmt.LhsIsToGhost_Which(lhs); + if (gk == AssignStmt.NonGhostKind.IsGhost) { + s.C_IsGhost = true; + } else if (gk == AssignStmt.NonGhostKind.Variable && codeContext.IsGhost) { + // cool + } else if (mustBeErasable) { + Error(stmt, "Assignment to {0} is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)", + AssignStmt.NonGhostKind_To_String(gk)); + } else if (s.Rhs is ExprRhs) { + var rhs = (ExprRhs)s.Rhs; + resolver.CheckIsNonGhost(rhs.Expr); + } else if (s.Rhs is HavocRhs) { + // cool + } else { + var rhs = (TypeRhs)s.Rhs; + if (rhs.ArrayDimensions != null) { + foreach (var dim in rhs.ArrayDimensions) { + resolver.CheckIsNonGhost(dim); + } + } + if (rhs.InitCall != null) { + foreach (var arg in rhs.InitCall.Args) { + resolver.CheckIsNonGhost(arg); + } + } + } + + } else if (stmt is CallStmt) { + var s = (CallStmt)stmt; + var callee = s.Method; + Contract.Assert(callee != null); // follows from the invariant of CallStmt + s.C_IsGhost = callee.IsGhost; + // check in-parameters + if (mustBeErasable) { + if (!s.C_IsGhost) { + Error(s, "only ghost methods can be called from this context"); + } + } else { + resolver.CheckIsNonGhost(s.Receiver); + int j; + if (!callee.IsGhost) { + j = 0; + foreach (var e in s.Args) { + Contract.Assume(j < callee.Ins.Count); // this should have already been checked by the resolver + if (!callee.Ins[j].IsGhost) { + resolver.CheckIsNonGhost(e); + } + j++; + } + } + j = 0; + foreach (var e in s.Lhs) { + var resolvedLhs = e.Resolved; + if (callee.IsGhost || callee.Outs[j].IsGhost) { + // LHS must denote a ghost + if (resolvedLhs is IdentifierExpr) { + var ll = (IdentifierExpr)resolvedLhs; + if (!ll.Var.IsGhost) { + if (ll is AutoGhostIdentifierExpr && ll.Var is LocalVariable) { + // the variable was actually declared in this statement, so auto-declare it as ghost + ((LocalVariable)ll.Var).MakeGhost(); + } else { + Error(s, "actual out-parameter {0} is required to be a ghost variable", j); + } + } + } else if (resolvedLhs is MemberSelectExpr) { + var ll = (MemberSelectExpr)resolvedLhs; + if (!ll.Member.IsGhost) { + Error(s, "actual out-parameter {0} is required to be a ghost field", j); + } + } else { + // this is an array update, and arrays are always non-ghost + Error(s, "actual out-parameter {0} is required to be a ghost variable", j); + } + } + j++; + } + } + + } else if (stmt is BlockStmt) { + var s = (BlockStmt)stmt; + s.Body.Iter(ss => Visit(ss, mustBeErasable)); + s.C_IsGhost = s.Body.All(ss => ss.C_IsGhost); // mark the block statement as ghost if all its substatements are ghost + + } else if (stmt is IfStmt) { + var s = (IfStmt)stmt; + s.C_IsGhost = mustBeErasable || (s.Guard != null && resolver.UsesSpecFeatures(s.Guard)); + Visit(s.Thn, s.C_IsGhost); + if (s.Els != null) { + Visit(s.Els, s.C_IsGhost); + } + // if both branches were all ghost, then we can mark the enclosing statement as ghost as well + s.C_IsGhost = s.C_IsGhost || (s.Thn.C_IsGhost && (s.Els == null || s.Els.C_IsGhost)); + + } else if (stmt is AlternativeStmt) { + var s = (AlternativeStmt)stmt; + s.C_IsGhost = mustBeErasable || s.Alternatives.Exists(alt => resolver.UsesSpecFeatures(alt.Guard)); + s.Alternatives.Iter(alt => alt.Body.Iter(ss => Visit(ss, s.C_IsGhost))); + s.C_IsGhost = s.C_IsGhost || s.Alternatives.All(alt => alt.Body.All(ss => ss.C_IsGhost)); + + } else if (stmt is WhileStmt) { + var s = (WhileStmt)stmt; + s.C_IsGhost = mustBeErasable || (s.Guard != null && resolver.UsesSpecFeatures(s.Guard)); + if (s.Body != null) { + Visit(s.Body, s.C_IsGhost); + } + s.C_IsGhost = s.C_IsGhost || s.Body == null || (!s.Decreases.Expressions.Exists(e => e is WildcardExpr) && s.Body.C_IsGhost); + + } else if (stmt is AlternativeLoopStmt) { + var s = (AlternativeLoopStmt)stmt; + s.C_IsGhost = mustBeErasable || s.Alternatives.Exists(alt => resolver.UsesSpecFeatures(alt.Guard)); + s.Alternatives.Iter(alt => alt.Body.Iter(ss => Visit(ss, s.C_IsGhost))); + s.C_IsGhost = s.C_IsGhost || (!s.Decreases.Expressions.Exists(e => e is WildcardExpr) && s.Alternatives.All(alt => alt.Body.All(ss => ss.C_IsGhost))); + + } else if (stmt is ForallStmt) { + var s = (ForallStmt)stmt; + s.C_IsGhost = mustBeErasable || s.Kind != ForallStmt.ParBodyKind.Assign || resolver.UsesSpecFeatures(s.Range); + if (s.Body != null) { + Visit(s.Body, s.C_IsGhost); + } + s.C_IsGhost = s.C_IsGhost || s.Body == null || s.Body.C_IsGhost; + + } else if (stmt is ModifyStmt) { + var s = (ModifyStmt)stmt; + if (s.Body != null) { + Visit(s.Body, mustBeErasable); + } + s.C_IsGhost = mustBeErasable; + + } else if (stmt is CalcStmt) { + var s = (CalcStmt)stmt; + s.C_IsGhost = true; + foreach (var h in s.Hints) { + Visit(h, true); + } + + } else if (stmt is MatchStmt) { + var s = (MatchStmt)stmt; + var mbe = mustBeErasable || resolver.UsesSpecFeatures(s.Source); + s.Cases.Iter(kase => kase.Body.Iter(ss => Visit(ss, mbe))); + s.C_IsGhost = mbe || s.Cases.All(kase => kase.Body.All(ss => ss.C_IsGhost)); + + } else if (stmt is SkeletonStatement) { + var s = (SkeletonStatement)stmt; + if (s.S != null) { + Visit(s.S, mustBeErasable); + } + + } else { + Contract.Assert(false); throw new cce.UnreachableException(); + } + stmt.IsGhost = stmt.C_IsGhost; // DEBUG + } + } + #endregion + // ------------------------------------------------------------------------------------------------------ // ----- FillInDefaultLoopDecreases --------------------------------------------------------------------- // ------------------------------------------------------------------------------------------------------ @@ -3601,22 +3889,14 @@ namespace Microsoft.Dafny // order does not matter much for resolution, so resolve them in reverse order foreach (var attr in attrs.AsEnumerable()) { if (attr.Args != null) { - ResolveAttributeArgs(attr.Args, opts, true); - } - } - } - - void ResolveAttributeArgs(List args, ResolveOpts opts, bool allowGhosts) { - Contract.Requires(args != null); - foreach (var arg in args) { - Contract.Assert(arg != null); - int prevErrors = reporter.Count(ErrorLevel.Error); - ResolveExpression(arg, opts); - if (!allowGhosts) { - CheckIsNonGhost(arg); - } - if (prevErrors == reporter.Count(ErrorLevel.Error)) { - CheckTypeInference(arg); + foreach (var arg in attr.Args) { + Contract.Assert(arg != null); + int prevErrors = reporter.Count(ErrorLevel.Error); + ResolveExpression(arg, opts); + if (prevErrors == reporter.Count(ErrorLevel.Error)) { + CheckTypeInference(arg); + } + } } } } @@ -3718,9 +3998,11 @@ namespace Microsoft.Dafny ResolveExpression(r, new ResolveOpts(f, false, true)); // any type is fine } + SolveAllTypeConstraints(); if (f.Body != null) { 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); } @@ -3852,6 +4134,7 @@ namespace Microsoft.Dafny Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression ConstrainTypes(e.E.Type, Type.Bool, e.E, "Postcondition must be a boolean (got {0})", e.E.Type); } + SolveAllTypeConstraints(); // Resolve body if (m.Body != null) { @@ -3862,8 +4145,12 @@ namespace Microsoft.Dafny var k = com.PrefixLemma.Ins[0]; scope.Push(k.Name, k); // we expect no name conflict for _k } - var codeContext = m; - ResolveBlockStatement(m.Body, m.IsGhost, codeContext); + var prevErrorCount = reporter.Count(ErrorLevel.Error); + ResolveBlockStatement(m.Body, m.IsGhost, m); + SolveAllTypeConstraints(); + if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { + ComputeGhostInterest(m.Body, m); + } } // attributes are allowed to mention both in- and out-parameters (including the implicit _k, for colemmas) @@ -3982,6 +4269,9 @@ namespace Microsoft.Dafny // Resolve body if (iter.Body != null) { ResolveBlockStatement(iter.Body, false, iter); + if (reporter.Count(ErrorLevel.Error) == postSpecErrorCount) { + ComputeGhostInterest(iter.Body, iter); + } } currentClass = null; @@ -4834,11 +5124,15 @@ namespace Microsoft.Dafny ConstrainTypes(s.Expr.Type, Type.Bool, s.Expr, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Expr.Type); } else if (stmt is PrintStmt) { - PrintStmt s = (PrintStmt)stmt; - ResolveAttributeArgs(s.Args, new ResolveOpts(codeContext, false, specContextOnly), false); + var s = (PrintStmt)stmt; + var opts = new ResolveOpts(codeContext, false, specContextOnly); + s.Args.Iter(e => ResolveExpression(e, opts)); +#if OLD_GHOST_HANDLING + ResolveAttributeArgs(s.Args, new ResolveOpts(codeContext, false, specContextOnly)); if (specContextOnly) { reporter.Error(MessageSource.Resolver, stmt, "print statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); } +#endif } else if (stmt is BreakStmt) { var s = (BreakStmt)stmt; @@ -4848,10 +5142,12 @@ namespace Microsoft.Dafny reporter.Error(MessageSource.Resolver, s, "break label is undefined or not in scope: {0}", s.TargetLabel); } else { s.TargetStmt = target; +#if OLD_GHOST_HANDLING bool targetIsLoop = target is WhileStmt || target is AlternativeLoopStmt; if (specContextOnly && !s.TargetStmt.IsGhost && !inSpecOnlyContext[s.TargetStmt]) { reporter.Error(MessageSource.Resolver, stmt, "ghost-context break statement is not allowed to break out of non-ghost " + (targetIsLoop ? "loop" : "structure")); } +#endif } } else { if (loopStack.Count < s.BreakCount) { @@ -4863,9 +5159,11 @@ namespace Microsoft.Dafny target.Labels = new LList) not assignable to LHS (of type List) -ResolutionErrors.dfy(507,7): Error: RHS (of type List) not assignable to LHS (of type List) -ResolutionErrors.dfy(521,23): Error: type of case bodies do not agree (found Tree<_T1,_T0>, previous types Tree<_T0,_T1>) -ResolutionErrors.dfy(533,24): Error: Wrong number of type arguments (0 instead of 2) passed to datatype: Tree -ResolutionErrors.dfy(561,25): Error: the type of this variable is underspecified -ResolutionErrors.dfy(561,23): Error: type variable 'T' in the function call to 'P' could not be determined -ResolutionErrors.dfy(568,25): Error: the type of this variable is underspecified -ResolutionErrors.dfy(568,23): Error: type variable 'T' in the function call to 'P' could not be determined -ResolutionErrors.dfy(581,13): Error: 'new' is not allowed in ghost contexts -ResolutionErrors.dfy(582,9): Error: 'new' is not allowed in ghost contexts -ResolutionErrors.dfy(589,14): Error: new allocation not supported in forall statements -ResolutionErrors.dfy(594,11): Error: the body of the enclosing forall statement is not allowed to update heap locations -ResolutionErrors.dfy(594,14): Error: new allocation not allowed in ghost context -ResolutionErrors.dfy(604,23): Error: 'new' is not allowed in ghost contexts -ResolutionErrors.dfy(611,15): Error: 'new' is not allowed in ghost contexts -ResolutionErrors.dfy(611,15): Error: only ghost methods can be called from this context -ResolutionErrors.dfy(620,17): Error: 'new' is not allowed in ghost contexts -ResolutionErrors.dfy(622,29): Error: only ghost methods can be called from this context -ResolutionErrors.dfy(624,17): Error: calls to methods with side-effects are not allowed inside a hint -ResolutionErrors.dfy(642,21): Error: the type of this variable is underspecified -ResolutionErrors.dfy(680,20): Error: calls to methods with side-effects are not allowed inside a hint -ResolutionErrors.dfy(690,24): Error: only ghost methods can be called from this context -ResolutionErrors.dfy(693,22): Error: 'decreases *' is not allowed on ghost loops -ResolutionErrors.dfy(704,18): Error: a hint is not allowed to update heap locations -ResolutionErrors.dfy(705,23): Error: a hint is not allowed to update heap locations -ResolutionErrors.dfy(706,20): Error: calls to methods with side-effects are not allowed inside a hint -ResolutionErrors.dfy(709,21): Error: a while statement used inside a hint is not allowed to have a modifies clause -ResolutionErrors.dfy(728,24): Error: only ghost methods can be called from this context -ResolutionErrors.dfy(731,22): Error: 'decreases *' is not allowed on ghost loops -ResolutionErrors.dfy(736,18): Error: a hint is not allowed to update heap locations -ResolutionErrors.dfy(737,23): Error: a hint is not allowed to update heap locations -ResolutionErrors.dfy(738,11): Error: calls to methods with side-effects are not allowed inside a hint -ResolutionErrors.dfy(741,21): Error: a while statement used inside a hint is not allowed to have a modifies clause -ResolutionErrors.dfy(766,19): Error: calls to methods with side-effects are not allowed inside a statement expression -ResolutionErrors.dfy(767,20): Error: only ghost methods can be called from this context -ResolutionErrors.dfy(768,20): Error: wrong number of method result arguments (got 0, expected 1) -ResolutionErrors.dfy(780,23): Error: function calls are allowed only in specification contexts (consider declaring the function a 'function method') -ResolutionErrors.dfy(790,4): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(801,36): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(810,17): Error: function calls are allowed only in specification contexts (consider declaring the function a 'function method') -ResolutionErrors.dfy(824,6): Error: RHS (of type B) not assignable to LHS (of type object) -ResolutionErrors.dfy(825,6): Error: RHS (of type int) not assignable to LHS (of type object) -ResolutionErrors.dfy(826,6): Error: RHS (of type B) not assignable to LHS (of type object) -ResolutionErrors.dfy(831,6): Error: RHS (of type G) not assignable to LHS (of type object) -ResolutionErrors.dfy(832,6): Error: RHS (of type Dt) not assignable to LHS (of type object) -ResolutionErrors.dfy(833,6): Error: RHS (of type CoDt) not assignable to LHS (of type object) -ResolutionErrors.dfy(895,4): Error: LHS of array assignment must denote an array element (found seq) -ResolutionErrors.dfy(896,4): Error: LHS of array assignment must denote an array element (found seq) -ResolutionErrors.dfy(901,10): Error: LHS of assignment must denote a mutable field -ResolutionErrors.dfy(902,10): Error: LHS of assignment must denote a mutable field -ResolutionErrors.dfy(903,9): Error: cannot assign to a range of array elements (try the 'forall' statement) -ResolutionErrors.dfy(904,9): Error: cannot assign to a range of array elements (try the 'forall' statement) -ResolutionErrors.dfy(905,5): Error: cannot assign to a range of array elements (try the 'forall' statement) -ResolutionErrors.dfy(906,5): Error: cannot assign to a range of array elements (try the 'forall' statement) -ResolutionErrors.dfy(987,11): Error: Wrong number of type arguments (2 instead of 1) passed to array type: array3 -ResolutionErrors.dfy(988,11): Error: Wrong number of type arguments (2 instead of 1) passed to class: C -ResolutionErrors.dfy(999,7): Error: Duplicate name of top-level declaration: BadSyn2 -ResolutionErrors.dfy(996,17): Error: Wrong number of type arguments (0 instead of 1) passed to datatype: List -ResolutionErrors.dfy(997,17): Error: Undeclared top-level type or type parameter: badName (did you forget to qualify a name or declare a module import 'opened?') -ResolutionErrors.dfy(998,22): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') -ResolutionErrors.dfy(1005,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> A -ResolutionErrors.dfy(1008,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A -ResolutionErrors.dfy(1012,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A -ResolutionErrors.dfy(1021,11): Error: because of cyclic dependencies among constructor argument types, no instances of datatype 'D' can be constructed -ResolutionErrors.dfy(1024,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A -ResolutionErrors.dfy(1029,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A -ResolutionErrors.dfy(1048,21): Error: unresolved identifier: x -ResolutionErrors.dfy(1055,35): Error: Wrong number of type arguments (2 instead of 1) passed to opaque type: P -ResolutionErrors.dfy(1067,13): Error: Undeclared top-level type or type parameter: BX (did you forget to qualify a name or declare a module import 'opened?') -ResolutionErrors.dfy(1077,6): Error: RHS (of type P) not assignable to LHS (of type P) -ResolutionErrors.dfy(1082,6): Error: RHS (of type P) not assignable to LHS (of type P) -ResolutionErrors.dfy(1087,6): Error: RHS (of type P) not assignable to LHS (of type P) -ResolutionErrors.dfy(1088,6): Error: RHS (of type P) not assignable to LHS (of type P) -ResolutionErrors.dfy(1093,13): Error: arguments must have the same type (got P and P) -ResolutionErrors.dfy(1094,13): Error: arguments must have the same type (got P and P) -ResolutionErrors.dfy(1095,13): Error: arguments must have the same type (got P and P) -ResolutionErrors.dfy(1118,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(1120,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(1225,26): Error: the type of this variable is underspecified -ResolutionErrors.dfy(1226,31): Error: the type of this variable is underspecified -ResolutionErrors.dfy(1227,29): Error: the type of this variable is underspecified -ResolutionErrors.dfy(1237,34): Error: the type of this variable is underspecified -ResolutionErrors.dfy(1253,21): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') -ResolutionErrors.dfy(1254,24): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') -ResolutionErrors.dfy(1291,16): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (y) -ResolutionErrors.dfy(1301,18): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) -ResolutionErrors.dfy(1329,15): Error: The name Inner ambiguously refers to a type in one of the modules A, B (try qualifying the type name with the module name) -ResolutionErrors.dfy(1339,29): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(1341,49): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(1341,54): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(1362,11): Error: name of type (X) is used as a variable -ResolutionErrors.dfy(1362,16): Error: name of type (X) is used as a variable -ResolutionErrors.dfy(1363,11): Error: name of module (Y) is used as a variable -ResolutionErrors.dfy(1363,16): Error: name of module (Y) is used as a variable -ResolutionErrors.dfy(1364,11): Error: name of type (X) is used as a variable -ResolutionErrors.dfy(1364,13): Error: second argument to "in" must be a set, multiset, or sequence with elements of type #type, or a map with domain #type (instead got map) -ResolutionErrors.dfy(1365,11): Error: name of module (Y) is used as a variable -ResolutionErrors.dfy(1365,13): Error: second argument to "in" must be a set, multiset, or sequence with elements of type #module, or a map with domain #module (instead got map) -ResolutionErrors.dfy(1370,16): Error: name of type (X) is used as a variable -ResolutionErrors.dfy(1370,13): Error: arguments must have the same type (got int and #type) -ResolutionErrors.dfy(1371,16): Error: name of module (Y) is used as a variable -ResolutionErrors.dfy(1371,13): Error: arguments must have the same type (got int and #module) -ResolutionErrors.dfy(1372,4): Error: name of type (X) is used as a variable -ResolutionErrors.dfy(1373,4): Error: name of module (Y) is used as a variable -ResolutionErrors.dfy(1382,11): Error: type of RHS of assign-such-that statement must be boolean (got int) -ResolutionErrors.dfy(1383,9): Error: type of RHS of assign-such-that statement must be boolean (got int) -ResolutionErrors.dfy(1384,13): Error: type of RHS of assign-such-that statement must be boolean (got int) -ResolutionErrors.dfy(1387,15): Error: type of RHS of let-such-that expression must be boolean (got int) -ResolutionErrors.dfy(432,2): Error: More than one anonymous constructor +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>) +ResolutionErrors.dfy(589,24): Error: Wrong number of type arguments (0 instead of 2) passed to datatype: Tree +ResolutionErrors.dfy(619,25): Error: the type of this variable is underspecified +ResolutionErrors.dfy(619,23): Error: type variable 'T' in the function call to 'P' could not be determined +ResolutionErrors.dfy(626,25): Error: the type of this variable is underspecified +ResolutionErrors.dfy(626,23): Error: type variable 'T' in the function call to 'P' could not be determined +ResolutionErrors.dfy(639,13): Error: 'new' is not allowed in ghost contexts +ResolutionErrors.dfy(640,9): Error: 'new' is not allowed in ghost contexts +ResolutionErrors.dfy(647,14): Error: new allocation not supported in forall statements +ResolutionErrors.dfy(652,11): Error: the body of the enclosing forall statement is not allowed to update heap locations +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 +ResolutionErrors.dfy(859,36): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(868,17): Error: function calls are allowed only in specification contexts (consider declaring the function a 'function method') +ResolutionErrors.dfy(882,6): Error: RHS (of type B) not assignable to LHS (of type object) +ResolutionErrors.dfy(883,6): Error: RHS (of type int) not assignable to LHS (of type object) +ResolutionErrors.dfy(884,6): Error: RHS (of type B) not assignable to LHS (of type object) +ResolutionErrors.dfy(889,6): Error: RHS (of type G) not assignable to LHS (of type object) +ResolutionErrors.dfy(890,6): Error: RHS (of type Dt) not assignable to LHS (of type object) +ResolutionErrors.dfy(891,6): Error: RHS (of type CoDt) not assignable to LHS (of type object) +ResolutionErrors.dfy(953,4): Error: LHS of array assignment must denote an array element (found seq) +ResolutionErrors.dfy(954,4): Error: LHS of array assignment must denote an array element (found seq) +ResolutionErrors.dfy(959,10): Error: LHS of assignment must denote a mutable field +ResolutionErrors.dfy(960,10): Error: LHS of assignment must denote a mutable field +ResolutionErrors.dfy(961,9): Error: cannot assign to a range of array elements (try the 'forall' statement) +ResolutionErrors.dfy(962,9): Error: cannot assign to a range of array elements (try the 'forall' statement) +ResolutionErrors.dfy(963,5): Error: cannot assign to a range of array elements (try the 'forall' statement) +ResolutionErrors.dfy(964,5): Error: cannot assign to a range of array elements (try the 'forall' statement) +ResolutionErrors.dfy(1045,11): Error: Wrong number of type arguments (2 instead of 1) passed to array type: array3 +ResolutionErrors.dfy(1046,11): Error: Wrong number of type arguments (2 instead of 1) passed to class: C +ResolutionErrors.dfy(1057,7): Error: Duplicate name of top-level declaration: BadSyn2 +ResolutionErrors.dfy(1054,17): Error: Wrong number of type arguments (0 instead of 1) passed to datatype: List +ResolutionErrors.dfy(1055,17): Error: Undeclared top-level type or type parameter: badName (did you forget to qualify a name or declare a module import 'opened?') +ResolutionErrors.dfy(1056,22): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') +ResolutionErrors.dfy(1063,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> A +ResolutionErrors.dfy(1066,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A +ResolutionErrors.dfy(1070,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A +ResolutionErrors.dfy(1079,11): Error: because of cyclic dependencies among constructor argument types, no instances of datatype 'D' can be constructed +ResolutionErrors.dfy(1082,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A +ResolutionErrors.dfy(1087,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A +ResolutionErrors.dfy(1106,21): Error: unresolved identifier: x +ResolutionErrors.dfy(1113,35): Error: Wrong number of type arguments (2 instead of 1) passed to opaque type: P +ResolutionErrors.dfy(1125,13): Error: Undeclared top-level type or type parameter: BX (did you forget to qualify a name or declare a module import 'opened?') +ResolutionErrors.dfy(1135,6): Error: RHS (of type P) not assignable to LHS (of type P) +ResolutionErrors.dfy(1140,6): Error: RHS (of type P) not assignable to LHS (of type P) +ResolutionErrors.dfy(1145,6): Error: RHS (of type P) not assignable to LHS (of type P) +ResolutionErrors.dfy(1146,6): Error: RHS (of type P) not assignable to LHS (of type P) +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(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 +ResolutionErrors.dfy(1295,34): Error: the type of this variable is underspecified +ResolutionErrors.dfy(1311,21): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') +ResolutionErrors.dfy(1312,24): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') +ResolutionErrors.dfy(1349,16): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (y) +ResolutionErrors.dfy(1359,18): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) +ResolutionErrors.dfy(1387,15): Error: The name Inner ambiguously refers to a type in one of the modules A, B (try qualifying the type name with the module name) +ResolutionErrors.dfy(1397,29): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(1399,49): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(1399,54): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(1420,11): Error: name of type (X) is used as a variable +ResolutionErrors.dfy(1420,16): Error: name of type (X) is used as a variable +ResolutionErrors.dfy(1421,11): Error: name of module (Y) is used as a variable +ResolutionErrors.dfy(1421,16): Error: name of module (Y) is used as a variable +ResolutionErrors.dfy(1422,11): Error: name of type (X) is used as a variable +ResolutionErrors.dfy(1422,13): Error: second argument to "in" must be a set, multiset, or sequence with elements of type #type, or a map with domain #type (instead got map) +ResolutionErrors.dfy(1423,11): Error: name of module (Y) is used as a variable +ResolutionErrors.dfy(1423,13): Error: second argument to "in" must be a set, multiset, or sequence with elements of type #module, or a map with domain #module (instead got map) +ResolutionErrors.dfy(1428,16): Error: name of type (X) is used as a variable +ResolutionErrors.dfy(1428,13): Error: arguments must have the same type (got int and #type) +ResolutionErrors.dfy(1429,16): Error: name of module (Y) is used as a variable +ResolutionErrors.dfy(1429,13): Error: arguments must have the same type (got int and #module) +ResolutionErrors.dfy(1430,4): Error: name of type (X) is used as a variable +ResolutionErrors.dfy(1431,4): Error: name of module (Y) is used as a variable +ResolutionErrors.dfy(1440,11): Error: type of RHS of assign-such-that statement must be boolean (got int) +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(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') ResolutionErrors.dfy(92,14): Error: the name 'David' denotes a datatype constructor, but does not do so uniquely; add an explicit qualification (for example, 'Abc.David') @@ -124,25 +124,25 @@ ResolutionErrors.dfy(141,21): Error: ghost variables are allowed only in specifi 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(233,12): Error: trying to break out of more loop levels than there are enclosing loops -ResolutionErrors.dfy(408,11): Error: calls to methods with side-effects are not allowed inside a hint -ResolutionErrors.dfy(410,14): Error: a hint is not allowed to update heap locations -ResolutionErrors.dfy(412,10): Error: a hint is not allowed to update a variable declared outside the hint -ResolutionErrors.dfy(438,14): Error: when allocating an object of type 'YHWH', one of its constructor methods must be called -ResolutionErrors.dfy(443,6): Error: when allocating an object of type 'Lucifer', one of its constructor methods must be called -ResolutionErrors.dfy(444,6): Error: when allocating an object of type 'Lucifer', one of its constructor methods must be called -ResolutionErrors.dfy(446,9): Error: class Lamb does not have an anonymous constructor -ResolutionErrors.dfy(844,11): Error: a modifies-clause expression must denote an object or a collection of objects (instead got int) -ResolutionErrors.dfy(848,14): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) -ResolutionErrors.dfy(851,12): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) -ResolutionErrors.dfy(859,14): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) -ResolutionErrors.dfy(869,18): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) -ResolutionErrors.dfy(880,16): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) -ResolutionErrors.dfy(1036,23): Error: unresolved identifier: x -ResolutionErrors.dfy(1039,20): Error: unresolved identifier: x -ResolutionErrors.dfy(1042,23): Error: unresolved identifier: x -ResolutionErrors.dfy(1044,19): Error: unresolved identifier: x -ResolutionErrors.dfy(1046,19): Error: unresolved identifier: x +ResolutionErrors.dfy(231,12): Error: trying to break out of more loop levels than there are enclosing loops +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 +ResolutionErrors.dfy(502,9): Error: class Lamb does not have an anonymous constructor +ResolutionErrors.dfy(902,11): Error: a modifies-clause expression must denote an object or a collection of objects (instead got int) +ResolutionErrors.dfy(906,14): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) +ResolutionErrors.dfy(909,12): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) +ResolutionErrors.dfy(917,14): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) +ResolutionErrors.dfy(927,18): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) +ResolutionErrors.dfy(938,16): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) +ResolutionErrors.dfy(1094,23): Error: unresolved identifier: x +ResolutionErrors.dfy(1097,20): Error: unresolved identifier: x +ResolutionErrors.dfy(1100,23): Error: unresolved identifier: x +ResolutionErrors.dfy(1102,19): Error: unresolved identifier: x +ResolutionErrors.dfy(1104,19): Error: unresolved identifier: x ResolutionErrors.dfy(12,16): Error: 'decreases *' is not allowed on ghost loops ResolutionErrors.dfy(24,11): Error: array selection requires an array2 (got array3) ResolutionErrors.dfy(25,12): Error: sequence/array/multiset/map selection requires a sequence, array, multiset, or map (got array3) @@ -156,39 +156,39 @@ ResolutionErrors.dfy(62,14): Error: accessing member 'M' requires an instance ex ResolutionErrors.dfy(63,7): Error: unresolved identifier: N ResolutionErrors.dfy(66,8): Error: non-function expression (of type int) is called with parameters ResolutionErrors.dfy(67,14): Error: member 'z' does not exist in type 'Global' -ResolutionErrors.dfy(260,4): Error: label shadows an enclosing label -ResolutionErrors.dfy(265,2): Error: duplicate label -ResolutionErrors.dfy(291,4): Error: when allocating an object of type 'ClassWithConstructor', one of its constructor methods must be called -ResolutionErrors.dfy(292,4): Error: when allocating an object of type 'ClassWithConstructor', one of its constructor methods must be called -ResolutionErrors.dfy(294,9): Error: a constructor is allowed to be called only when an object is being allocated -ResolutionErrors.dfy(308,16): Error: arguments must have the same type (got int and DTD_List) -ResolutionErrors.dfy(309,16): Error: arguments must have the same type (got DTD_List and int) -ResolutionErrors.dfy(310,25): Error: arguments must have the same type (got bool and int) -ResolutionErrors.dfy(322,15): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(347,5): Error: incorrect type of method in-parameter 1 (expected GenericClass, got GenericClass) -ResolutionErrors.dfy(359,18): Error: incorrect type of datatype constructor argument (found GList<_T0>, expected GList) -ResolutionErrors.dfy(367,6): Error: arguments to + must be of a numeric type or a collection type (instead got bool) -ResolutionErrors.dfy(372,6): Error: all lines in a calculation must have the same type (got int after bool) -ResolutionErrors.dfy(375,6): Error: first argument to ==> must be of type bool (instead got int) -ResolutionErrors.dfy(375,6): Error: second argument to ==> must be of type bool (instead got int) -ResolutionErrors.dfy(376,10): Error: first argument to ==> must be of type bool (instead got int) -ResolutionErrors.dfy(376,10): Error: second argument to ==> must be of type bool (instead got int) -ResolutionErrors.dfy(381,10): Error: first argument to ==> must be of type bool (instead got int) -ResolutionErrors.dfy(381,10): Error: second argument to ==> must be of type bool (instead got int) -ResolutionErrors.dfy(470,7): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(476,2): Error: non-ghost variable cannot be assigned a value that depends on a ghost -ResolutionErrors.dfy(549,18): Error: unresolved identifier: w -ResolutionErrors.dfy(656,11): Error: lemmas are not allowed to have modifies clauses -ResolutionErrors.dfy(918,9): Error: unresolved identifier: s -ResolutionErrors.dfy(929,32): Error: RHS (of type (int,int,real)) not assignable to LHS (of type (int,real,int)) -ResolutionErrors.dfy(930,37): Error: RHS (of type (int,real,int)) not assignable to LHS (of type (int,real,int,real)) -ResolutionErrors.dfy(936,16): Error: condition is expected to be of type bool, but is int -ResolutionErrors.dfy(937,16): Error: member 3 does not exist in datatype _tuple#3 -ResolutionErrors.dfy(937,26): Error: member x does not exist in datatype _tuple#2 -ResolutionErrors.dfy(960,15): Error: arguments to / must have the same type (got real and int) -ResolutionErrors.dfy(961,10): Error: second argument to % must be of type int (instead got real) -ResolutionErrors.dfy(1106,8): Error: new cannot be applied to a trait -ResolutionErrors.dfy(1127,13): Error: first argument to / must be of numeric type (instead got set) -ResolutionErrors.dfy(1134,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(1149,14): Error: a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating +ResolutionErrors.dfy(311,4): Error: label shadows an enclosing label +ResolutionErrors.dfy(316,2): Error: duplicate label +ResolutionErrors.dfy(342,4): Error: when allocating an object of type 'ClassWithConstructor', one of its constructor methods must be called +ResolutionErrors.dfy(343,4): Error: when allocating an object of type 'ClassWithConstructor', one of its constructor methods must be called +ResolutionErrors.dfy(345,9): Error: a constructor is allowed to be called only when an object is being allocated +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(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) +ResolutionErrors.dfy(425,6): Error: all lines in a calculation must have the same type (got int after bool) +ResolutionErrors.dfy(428,6): Error: first argument to ==> must be of type bool (instead got int) +ResolutionErrors.dfy(428,6): Error: second argument to ==> must be of type bool (instead got int) +ResolutionErrors.dfy(429,10): Error: first argument to ==> must be of type bool (instead got int) +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(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(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)) +ResolutionErrors.dfy(988,37): Error: RHS (of type (int,real,int)) not assignable to LHS (of type (int,real,int,real)) +ResolutionErrors.dfy(994,16): Error: condition is expected to be of type bool, but is int +ResolutionErrors.dfy(995,16): Error: member 3 does not exist in datatype _tuple#3 +ResolutionErrors.dfy(995,26): Error: member x does not exist in datatype _tuple#2 +ResolutionErrors.dfy(1018,15): Error: arguments to / must have the same type (got real and int) +ResolutionErrors.dfy(1019,10): Error: second argument to % must be of type int (instead got real) +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 193 resolution/type errors detected in ResolutionErrors.dfy -- cgit v1.2.3 From 029f016b365c844137399b2153bf59339d5612b7 Mon Sep 17 00:00:00 2001 From: leino Date: Sun, 20 Sep 2015 22:11:33 -0700 Subject: Added back in various ghost tests --- Test/dafny0/ResolutionErrors.dfy | 10 +++++----- Test/dafny0/ResolutionErrors.dfy.expect | 9 ++++++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Test/dafny0/ResolutionErrors.dfy b/Test/dafny0/ResolutionErrors.dfy index d38d2506..2fab6bf3 100644 --- a/Test/dafny0/ResolutionErrors.dfy +++ b/Test/dafny0/ResolutionErrors.dfy @@ -238,7 +238,7 @@ class GhostTests { p := p + 1; } } -/***KRML method BreakMayNotBeFineHere_Ghost(ghost t: int) +method BreakMayNotBeFineHere_Ghost(ghost t: int) { var n := 0; ghost var k := 0; @@ -295,7 +295,7 @@ class GhostTests { n := n + 1; p := p + 1; } - }****/ + } } method DuplicateLabels(n: int) { @@ -364,7 +364,7 @@ method DatatypeDestructors(d: DTD_List) { } } method DatatypeDestructors_Ghost(d: DTD_List) { -//KRML 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 } // ------------------- print statements --------------------------------------- @@ -439,7 +439,7 @@ method TestCalc_Ghost(m: int, n: int, a: bool, b: bool) { calc { n + m; -//KRML { print n + m; } // error: non-ghost statements are not allowed in hints + { print n + m; } // error: non-ghost statements are not allowed in hints m + n; } } @@ -605,7 +605,7 @@ method LetSuchThat(ghost z: int, n: nat) } method LetSuchThat_Ghost(ghost z: int, n: nat) { -//KRML 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) } // ------------ quantified variables whose types are not inferred ---------- diff --git a/Test/dafny0/ResolutionErrors.dfy.expect b/Test/dafny0/ResolutionErrors.dfy.expect index 55d2e79c..0286778b 100644 --- a/Test/dafny0/ResolutionErrors.dfy.expect +++ b/Test/dafny0/ResolutionErrors.dfy.expect @@ -125,6 +125,10 @@ ResolutionErrors.dfy(142,35): Error: ghost variables are allowed only in specifi 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 @@ -164,6 +168,7 @@ 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) @@ -175,9 +180,11 @@ 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)) @@ -191,4 +198,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 -193 resolution/type errors detected in ResolutionErrors.dfy +200 resolution/type errors detected in ResolutionErrors.dfy -- 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(-) 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 7134a276e9fb53a4c11e1ce9383b6676a7d50bc8 Mon Sep 17 00:00:00 2001 From: leino Date: Mon, 21 Sep 2015 20:01:10 -0700 Subject: Moved premature uses of .IsGhost for Statement's --- Source/Dafny/Resolver.cs | 166 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 128 insertions(+), 38 deletions(-) diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 57a42c73..87379478 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -1314,6 +1314,7 @@ 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 @@ -1419,6 +1420,12 @@ namespace Microsoft.Dafny } 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); + } + } } else if (d is NewtypeDecl) { var dd = (NewtypeDecl)d; bool? boolNativeType = null; @@ -1525,6 +1532,23 @@ namespace Microsoft.Dafny } } } + foreach (AssignSuchThatStmt s in needBoundsDiscovery_AssignSuchThatStmt) { + Contract.Assert(!s.C_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 @@ -2034,29 +2058,6 @@ namespace Microsoft.Dafny CheckTypeInference_Specification_Expr(m.Decreases); if (m.Body != null) { CheckTypeInference(m.Body); - bool tail = true; - bool hasTailRecursionPreference = Attributes.ContainsBool(m.Attributes, "tailrecursion", ref tail); - if (hasTailRecursionPreference && !tail) { - // the user specifically requested no tail recursion, so do nothing else - } else if (hasTailRecursionPreference && tail && m.IsGhost) { - reporter.Error(MessageSource.Resolver, m.tok, "tail recursion can be specified only for methods that will be compiled, not for ghost methods"); - } else { - var module = m.EnclosingClass.Module; - var sccSize = module.CallGraph.GetSCCSize(m); - if (hasTailRecursionPreference && 2 <= sccSize) { - reporter.Error(MessageSource.Resolver, m.tok, "sorry, tail-call optimizations are not supported for mutually recursive methods"); - } else if (hasTailRecursionPreference || sccSize == 1) { - CallStmt tailCall = null; - var status = CheckTailRecursive(m.Body.Body, m, ref tailCall, hasTailRecursionPreference); - if (status != TailRecursionStatus.NotTailRecursive) { - m.IsTailRecursive = true; - if (tailCall != null) { - // this means there was at least one recursive call - reporter.Info(MessageSource.Resolver, m.tok, "tail recursive"); - } - } - } - } } } else if (member is Function) { var f = (Function)member; @@ -2067,10 +2068,6 @@ namespace Microsoft.Dafny CheckTypeInference_Specification_Expr(f.Decreases); if (f.Body != null) { CheckTypeInference(f.Body); - bool tail = true; - if (Attributes.ContainsBool(f.Attributes, "tailrecursion", ref tail) && tail) { - reporter.Error(MessageSource.Resolver, f.tok, "sorry, tail-call functions are not supported"); - } } if (errorCount == reporter.Count(ErrorLevel.Error) && f is FixpointPredicate) { var cop = (FixpointPredicate)f; @@ -2078,6 +2075,7 @@ namespace Microsoft.Dafny } } } + private void CheckTypeInference_MaybeFreeExpression(MaybeFreeExpression mfe) { Contract.Requires(mfe != null); foreach (var e in Attributes.SubExpressions(mfe.Attributes)) { @@ -2300,6 +2298,41 @@ namespace Microsoft.Dafny return status; } + void DetermineTailRecursion(Function f) { + Contract.Requires(f != null); + bool tail = true; + if (Attributes.ContainsBool(f.Attributes, "tailrecursion", ref tail) && tail) { + reporter.Error(MessageSource.Resolver, f.tok, "sorry, tail-call functions are not supported"); + } + } + + void DetermineTailRecursion(Method m) { + Contract.Requires(m != null); + bool tail = true; + bool hasTailRecursionPreference = Attributes.ContainsBool(m.Attributes, "tailrecursion", ref tail); + if (hasTailRecursionPreference && !tail) { + // the user specifically requested no tail recursion, so do nothing else + } else if (hasTailRecursionPreference && tail && m.IsGhost) { + reporter.Error(MessageSource.Resolver, m.tok, "tail recursion can be specified only for methods that will be compiled, not for ghost methods"); + } else { + var module = m.EnclosingClass.Module; + var sccSize = module.CallGraph.GetSCCSize(m); + if (hasTailRecursionPreference && 2 <= sccSize) { + reporter.Error(MessageSource.Resolver, m.tok, "sorry, tail-call optimizations are not supported for mutually recursive methods"); + } else if (hasTailRecursionPreference || sccSize == 1) { + CallStmt tailCall = null; + var status = CheckTailRecursive(m.Body.Body, m, ref tailCall, hasTailRecursionPreference); + if (status != TailRecursionStatus.NotTailRecursive) { + m.IsTailRecursive = true; + if (tailCall != null) { + // this means there was at least one recursive call + reporter.Info(MessageSource.Resolver, m.tok, "tail recursive"); + } + } + } + } + } + /// /// See CheckTailRecursive(List Statement, ...), including its description of "tailCall". /// In the current implementation, "enclosingMethod" is not allowed to be a mutually recursive method. @@ -3025,7 +3058,6 @@ namespace Microsoft.Dafny /// public void Visit(Statement stmt, bool mustBeErasable) { Contract.Assume(!codeContext.IsGhost || mustBeErasable); // (this is really a precondition) codeContext.IsGhost ==> mustBeErasable - stmt.IsGhost = false; // DEBUG if (stmt is PredicateStmt) { stmt.C_IsGhost = true; @@ -3075,6 +3107,9 @@ namespace Microsoft.Dafny } } } + if (!s.C_IsGhost) { + resolver.needBoundsDiscovery_AssignSuchThatStmt.Add(s); + } } else if (stmt is UpdateStmt) { var s = (UpdateStmt)stmt; @@ -3207,6 +3242,9 @@ namespace Microsoft.Dafny } else if (stmt is WhileStmt) { var s = (WhileStmt)stmt; s.C_IsGhost = mustBeErasable || (s.Guard != null && resolver.UsesSpecFeatures(s.Guard)); + if (s.C_IsGhost && s.Decreases.Expressions.Exists(e => e is WildcardExpr)) { + Error(s, "'decreases *' is not allowed on ghost loops"); + } if (s.Body != null) { Visit(s.Body, s.C_IsGhost); } @@ -4010,6 +4048,7 @@ namespace Microsoft.Dafny ConstrainTypes(f.Body.Type, f.ResultType, f, "Function body type mismatch (expected {0}, got {1})", f.ResultType, f.Body.Type); } scope.PopMarker(); + DetermineTailRecursion(f); } /// @@ -4080,6 +4119,10 @@ namespace Microsoft.Dafny void ResolveMethod(Method m) { Contract.Requires(m != null); +#if !OLD_GH + var oldx = Statement.ReadyToDealWithGhostField; + Statement.ReadyToDealWithGhostField = false; +#endif try { currentMethod = m; @@ -4149,6 +4192,7 @@ namespace Microsoft.Dafny ResolveBlockStatement(m.Body, m.IsGhost, m); SolveAllTypeConstraints(); if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { + Statement.ReadyToDealWithGhostField = true; ComputeGhostInterest(m.Body, m); } } @@ -4162,6 +4206,9 @@ namespace Microsoft.Dafny finally { currentMethod = null; +#if !OLD_GH + Statement.ReadyToDealWithGhostField = oldx; +#endif } } @@ -5118,7 +5165,9 @@ namespace Microsoft.Dafny } if (stmt is PredicateStmt) { PredicateStmt s = (PredicateStmt)stmt; +#if OLD_GH s.IsGhost = true; +#endif ResolveExpression(s.Expr, new ResolveOpts(codeContext, true, true)); Contract.Assert(s.Expr.Type != null); // follows from postcondition of ResolveExpression ConstrainTypes(s.Expr.Type, Type.Bool, s.Expr, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Expr.Type); @@ -5270,9 +5319,11 @@ namespace Microsoft.Dafny ResolveConcreteUpdateStmt(s.Update, specContextOnly, codeContext); } // Update the VarDeclStmt's ghost status according to its components +#if OLD_GH if (!s.IsGhost) { s.IsGhost = (s.Update == null || s.Update.IsGhost) && s.Locals.All(v => v.IsGhost); } +#endif foreach (var local in s.Locals) { if (Attributes.Contains(local.Attributes, "assumption")) @@ -5380,7 +5431,9 @@ namespace Microsoft.Dafny CheckIsLvalue(lhs, codeContext); } +#if OLD_GH s.IsGhost = lvalueIsGhost; +#endif Type lhsType = s.Lhs.Type; if (s.Rhs is ExprRhs) { ExprRhs rr = (ExprRhs)s.Rhs; @@ -5424,9 +5477,11 @@ namespace Microsoft.Dafny var s = (BlockStmt)stmt; scope.PushMarker(); ResolveBlockStatement(s, specContextOnly, codeContext); +#if OLD_GH if (!s.IsGhost) { s.IsGhost = s.Body.All(ss => ss.IsGhost); // mark the block statement as ghost if all its substatements are ghost } +#endif scope.PopMarker(); } else if (stmt is IfStmt) { @@ -5442,22 +5497,29 @@ namespace Microsoft.Dafny branchesAreSpecOnly = UsesSpecFeatures(s.Guard); } } +#if OLD_GH s.IsGhost = branchesAreSpecOnly; +#endif ResolveStatement(s.Thn, branchesAreSpecOnly, codeContext); if (s.Els != null) { ResolveStatement(s.Els, branchesAreSpecOnly, codeContext); } +#if OLD_GH if (!s.IsGhost && s.Thn.IsGhost && (s.Els == null || s.Els.IsGhost)) { // mark the entire 'if' statement as ghost if its branches are ghost s.IsGhost = true; } +#endif } else if (stmt is AlternativeStmt) { var s = (AlternativeStmt)stmt; - s.IsGhost = ResolveAlternatives(s.Alternatives, specContextOnly, null, codeContext); + ResolveAlternatives(s.Alternatives, specContextOnly, null, codeContext); +#if OLD_GH + s.IsGhost = g; if (!s.IsGhost) { s.IsGhost = s.Alternatives.All(alt => alt.Body.All(ss => ss.IsGhost)); } +#endif } else if (stmt is WhileStmt) { WhileStmt s = (WhileStmt)stmt; @@ -5503,7 +5565,9 @@ namespace Microsoft.Dafny Translator.ComputeFreeVariables(fe.E, fvs); } } +#if OLD_GH s.IsGhost = s.Body == null || bodyMustBeSpecOnly; +#endif if (s.Body != null) { loopStack.Add(s); // push if (s.Labels == null) { // otherwise, "s" is already in "inSpecOnlyContext" map @@ -5519,7 +5583,7 @@ namespace Microsoft.Dafny } else if (stmt is AlternativeLoopStmt) { var s = (AlternativeLoopStmt)stmt; - s.IsGhost = ResolveAlternatives(s.Alternatives, specContextOnly, s, codeContext); + ResolveAlternatives(s.Alternatives, specContextOnly, s, codeContext); foreach (MaybeFreeExpression inv in s.Invariants) { ResolveExpression(inv.E, new ResolveOpts(codeContext, true, true)); Contract.Assert(inv.E.Type != null); // follows from postcondition of ResolveExpression @@ -5529,9 +5593,12 @@ namespace Microsoft.Dafny foreach (Expression e in s.Decreases.Expressions) { ResolveExpression(e, new ResolveOpts(codeContext, true, true)); if (e is WildcardExpr) { +#if OLD_GHOST_HANDLING if (s.IsGhost) { reporter.Error(MessageSource.Resolver, e, "'decreases *' is not allowed on ghost loops"); - } else if (!codeContext.AllowsNontermination && !DafnyOptions.O.Dafnycc) { + } else +#endif + if (!codeContext.AllowsNontermination && !DafnyOptions.O.Dafnycc) { reporter.Error(MessageSource.Resolver, e, "a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating"); } } @@ -5568,7 +5635,9 @@ namespace Microsoft.Dafny bodyMustBeSpecOnly = true; } } +#if OLD_GH s.IsGhost = s.Body == null || bodyMustBeSpecOnly; +#endif if (s.Body != null) { // clear the labels for the duration of checking the body, because break statements are not allowed to leave a forall statement @@ -5631,12 +5700,16 @@ namespace Microsoft.Dafny if (s.Body != null) { ResolveBlockStatement(s.Body, specContextOnly, codeContext); } +#if OLD_GH s.IsGhost = specContextOnly; +#endif } else if (stmt is CalcStmt) { var prevErrorCount = reporter.Count(ErrorLevel.Error); CalcStmt s = (CalcStmt)stmt; +#if OLD_GH s.IsGhost = true; +#endif if (s.Lines.Count > 0) { var e0 = s.Lines.First(); ResolveExpression(e0, new ResolveOpts(codeContext, true, true)); @@ -5726,7 +5799,9 @@ namespace Microsoft.Dafny subst.Add(dtd.TypeArgs[i], sourceType.TypeArgs[i]); } } +#if OLD_GH s.IsGhost = bodyIsSpecOnly; +#endif // convert CasePattern in MatchCaseExpr to BoundVar and flatten the MatchCaseExpr. Type type = new InferredTypeProxy(); @@ -5812,9 +5887,11 @@ namespace Microsoft.Dafny } Contract.Assert(memberNamesUsed.Count + s.MissingCases.Count == dtd.Ctors.Count); } +#if OLD_GH if (!s.IsGhost) { s.IsGhost = s.Cases.All(cs => cs.Body.All(ss => ss.IsGhost)); } +#endif } /* @@ -6351,7 +6428,9 @@ namespace Microsoft.Dafny foreach (var a in update.ResolvedStatements) { ResolveStatement(a, specContextOnly, codeContext); } +#if OLD_GH update.IsGhost = update.ResolvedStatements.TrueForAll(ss => ss.IsGhost); +#endif } private void ResolveAssignSuchThatStmt(AssignSuchThatStmt s, bool specContextOnly, ICodeContext codeContext) { @@ -6371,15 +6450,16 @@ namespace Microsoft.Dafny } } +#if OLD_GH s.IsGhost = s.Lhss.TrueForAll(AssignStmt.LhsIsToGhost); +#endif var ec = reporter.Count(ErrorLevel.Error); ResolveExpression(s.Expr, new ResolveOpts(codeContext, true, specContextOnly)); ConstrainTypes(s.Expr.Type, Type.Bool, s.Expr, "type of RHS of assign-such-that statement must be boolean (got {0})", s.Expr.Type); - if (ec == reporter.Count(ErrorLevel.Error) && !s.IsGhost && s.AssumeToken == null && !specContextOnly) { #if OLD_GHOST_HANDLING + if (ec == reporter.Count(ErrorLevel.Error) && !s.IsGhost && s.AssumeToken == null && !specContextOnly) { CheckIsNonGhost(s.Expr); -#endif CheckTypeInference(s.Expr); // we need to resolve operators before the call to DiscoverBoundsAux List missingBounds; var bestBounds = DiscoverBestBounds_MultipleVars(varLhss, s.Expr, true, false, out missingBounds); @@ -6390,13 +6470,13 @@ namespace Microsoft.Dafny s.Bounds = bestBounds; } } +#endif } - bool ResolveAlternatives(List alternatives, bool specContextOnly, AlternativeLoopStmt loopToCatchBreaks, ICodeContext codeContext) { + void ResolveAlternatives(List alternatives, bool specContextOnly, AlternativeLoopStmt loopToCatchBreaks, ICodeContext codeContext) { Contract.Requires(alternatives != null); Contract.Requires(codeContext != null); - bool isGhost = specContextOnly; // first, resolve the guards, which tells us whether or not the entire statement is a ghost statement foreach (var alternative in alternatives) { int prevErrorCount = reporter.Count(ErrorLevel.Error); @@ -6404,9 +6484,11 @@ namespace Microsoft.Dafny Contract.Assert(alternative.Guard.Type != null); // follows from postcondition of ResolveExpression bool successfullyResolved = reporter.Count(ErrorLevel.Error) == prevErrorCount; ConstrainTypes(alternative.Guard.Type, Type.Bool, alternative.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, alternative.Guard.Type); +#if OLD_GHOST_HANDLING if (!specContextOnly && successfullyResolved) { isGhost = isGhost || UsesSpecFeatures(alternative.Guard); } +#endif } if (loopToCatchBreaks != null) { @@ -6418,15 +6500,13 @@ namespace Microsoft.Dafny foreach (var alternative in alternatives) { scope.PushMarker(); foreach (Statement ss in alternative.Body) { - ResolveStatement(ss, isGhost, codeContext); + ResolveStatement(ss, specContextOnly, codeContext); } scope.PopMarker(); } if (loopToCatchBreaks != null) { loopStack.RemoveAt(loopStack.Count - 1); // pop } - - return isGhost; } /// @@ -6443,7 +6523,9 @@ 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 OLD_GH s.IsGhost = callee.IsGhost; +#endif if (specContextOnly && !callee.IsGhost) { reporter.Error(MessageSource.Resolver, s, "only ghost methods can be called from this context"); } @@ -6453,13 +6535,21 @@ namespace Microsoft.Dafny Contract.Assume(lhs.Type != null); // a sanity check that LHSs have already been resolved } // resolve arguments +#if OLD_GH if (!s.IsGhost && s.Receiver.WasResolved()) { CheckIsNonGhost(s.Receiver); } +#endif int j = 0; foreach (Expression e in s.Args) { +#if OLD_GHOST_HANDLING bool allowGhost = s.IsGhost || callee.Ins.Count <= j || callee.Ins[j].IsGhost; +#else + bool allowGhost = callee.Ins.Count <= j || callee.Ins[j].IsGhost; +#endif +#if OLD_GHOST_HANDLING var ec = reporter.Count(ErrorLevel.Error); +#endif ResolveExpression(e, new ResolveOpts(codeContext, true, allowGhost)); #if OLD_GHOST_HANDLING if (ec == reporter.Count(ErrorLevel.Error) && !allowGhost) { -- cgit v1.2.3 From bd3dedcc023edb51d2a03619061bb03463821534 Mon Sep 17 00:00:00 2001 From: leino Date: Mon, 21 Sep 2015 21:25:55 -0700 Subject: Removed unused code (old code from previous ghost-statement handling) --- Source/Dafny/DafnyAst.cs | 16 --- Source/Dafny/Resolver.cs | 282 ++++++----------------------------------------- 2 files changed, 31 insertions(+), 267 deletions(-) diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index 83db732e..2a98d5c2 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -3461,23 +3461,7 @@ namespace Microsoft.Dafny { Contract.Invariant(EndTok != null); } -#if OLD_GHOST_HANDLING public bool IsGhost; // filled in by resolution -#else - public static bool ReadyToDealWithGhostField = true; - private bool izzaGhost; - public bool IsGhost { - get { - Contract.Requires(ReadyToDealWithGhostField); - return izzaGhost; - } - set { - Contract.Requires(ReadyToDealWithGhostField); - izzaGhost = value; - } - } - public bool C_IsGhost; // new ghost computation -#endif public Statement(IToken tok, IToken endTok, Attributes attrs) { Contract.Requires(tok != null); diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 87379478..d82d7d1f 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -1533,7 +1533,7 @@ namespace Microsoft.Dafny } } foreach (AssignSuchThatStmt s in needBoundsDiscovery_AssignSuchThatStmt) { - Contract.Assert(!s.C_IsGhost); + 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 @@ -3060,7 +3060,7 @@ namespace Microsoft.Dafny Contract.Assume(!codeContext.IsGhost || mustBeErasable); // (this is really a precondition) codeContext.IsGhost ==> mustBeErasable if (stmt is PredicateStmt) { - stmt.C_IsGhost = true; + stmt.IsGhost = true; } else if (stmt is PrintStmt) { var s = (PrintStmt)stmt; @@ -3073,7 +3073,7 @@ namespace Microsoft.Dafny } else if (stmt is BreakStmt) { var s = (BreakStmt)stmt; if (mustBeErasable) { - if (!s.TargetStmt.C_IsGhost && !resolver.inSpecOnlyContext[s.TargetStmt]) { // TODO: inSpecOnlyContext should probably move from Resolver to GhostInterest_Visitor + if (!s.TargetStmt.IsGhost && !resolver.inSpecOnlyContext[s.TargetStmt]) { // TODO: inSpecOnlyContext should probably move from Resolver to GhostInterest_Visitor var targetIsLoop = s.TargetStmt is WhileStmt || s.TargetStmt is AlternativeLoopStmt; Error(stmt, "ghost-context break statement is not allowed to break out of non-ghost " + (targetIsLoop ? "loop" : "structure")); } @@ -3091,7 +3091,7 @@ namespace Microsoft.Dafny } else if (stmt is AssignSuchThatStmt) { var s = (AssignSuchThatStmt)stmt; - s.C_IsGhost = mustBeErasable || s.AssumeToken != null || s.Lhss.Any(AssignStmt.LhsIsToGhost); + s.IsGhost = mustBeErasable || s.AssumeToken != null || s.Lhss.Any(AssignStmt.LhsIsToGhost); if (mustBeErasable && !codeContext.IsGhost) { foreach (var lhs in s.Lhss) { var gk = AssignStmt.LhsIsToGhost_Which(lhs); @@ -3107,14 +3107,14 @@ namespace Microsoft.Dafny } } } - if (!s.C_IsGhost) { + if (!s.IsGhost) { resolver.needBoundsDiscovery_AssignSuchThatStmt.Add(s); } } else if (stmt is UpdateStmt) { var s = (UpdateStmt)stmt; s.ResolvedStatements.Iter(ss => Visit(ss, mustBeErasable)); - s.C_IsGhost = s.ResolvedStatements.All(ss => ss.C_IsGhost); + s.IsGhost = s.ResolvedStatements.All(ss => ss.IsGhost); } else if (stmt is VarDeclStmt) { var s = (VarDeclStmt)stmt; @@ -3131,7 +3131,7 @@ namespace Microsoft.Dafny } } } - s.C_IsGhost = (s.Update == null || s.Update.C_IsGhost) && s.Locals.All(v => v.IsGhost); + s.IsGhost = (s.Update == null || s.Update.IsGhost) && s.Locals.All(v => v.IsGhost); if (s.Update != null) { Visit(s.Update, mustBeErasable); } @@ -3141,7 +3141,7 @@ namespace Microsoft.Dafny var lhs = s.Lhs.Resolved; var gk = AssignStmt.LhsIsToGhost_Which(lhs); if (gk == AssignStmt.NonGhostKind.IsGhost) { - s.C_IsGhost = true; + s.IsGhost = true; } else if (gk == AssignStmt.NonGhostKind.Variable && codeContext.IsGhost) { // cool } else if (mustBeErasable) { @@ -3170,10 +3170,10 @@ namespace Microsoft.Dafny var s = (CallStmt)stmt; var callee = s.Method; Contract.Assert(callee != null); // follows from the invariant of CallStmt - s.C_IsGhost = callee.IsGhost; + s.IsGhost = callee.IsGhost; // check in-parameters if (mustBeErasable) { - if (!s.C_IsGhost) { + if (!s.IsGhost) { Error(s, "only ghost methods can be called from this context"); } } else { @@ -3221,59 +3221,59 @@ namespace Microsoft.Dafny } else if (stmt is BlockStmt) { var s = (BlockStmt)stmt; s.Body.Iter(ss => Visit(ss, mustBeErasable)); - s.C_IsGhost = s.Body.All(ss => ss.C_IsGhost); // mark the block statement as ghost if all its substatements are ghost + s.IsGhost = s.Body.All(ss => ss.IsGhost); // mark the block statement as ghost if all its substatements are ghost } else if (stmt is IfStmt) { var s = (IfStmt)stmt; - s.C_IsGhost = mustBeErasable || (s.Guard != null && resolver.UsesSpecFeatures(s.Guard)); - Visit(s.Thn, s.C_IsGhost); + s.IsGhost = mustBeErasable || (s.Guard != null && resolver.UsesSpecFeatures(s.Guard)); + Visit(s.Thn, s.IsGhost); if (s.Els != null) { - Visit(s.Els, s.C_IsGhost); + Visit(s.Els, s.IsGhost); } // if both branches were all ghost, then we can mark the enclosing statement as ghost as well - s.C_IsGhost = s.C_IsGhost || (s.Thn.C_IsGhost && (s.Els == null || s.Els.C_IsGhost)); + s.IsGhost = s.IsGhost || (s.Thn.IsGhost && (s.Els == null || s.Els.IsGhost)); } else if (stmt is AlternativeStmt) { var s = (AlternativeStmt)stmt; - s.C_IsGhost = mustBeErasable || s.Alternatives.Exists(alt => resolver.UsesSpecFeatures(alt.Guard)); - s.Alternatives.Iter(alt => alt.Body.Iter(ss => Visit(ss, s.C_IsGhost))); - s.C_IsGhost = s.C_IsGhost || s.Alternatives.All(alt => alt.Body.All(ss => ss.C_IsGhost)); + s.IsGhost = mustBeErasable || s.Alternatives.Exists(alt => resolver.UsesSpecFeatures(alt.Guard)); + s.Alternatives.Iter(alt => alt.Body.Iter(ss => Visit(ss, s.IsGhost))); + s.IsGhost = s.IsGhost || s.Alternatives.All(alt => alt.Body.All(ss => ss.IsGhost)); } else if (stmt is WhileStmt) { var s = (WhileStmt)stmt; - s.C_IsGhost = mustBeErasable || (s.Guard != null && resolver.UsesSpecFeatures(s.Guard)); - if (s.C_IsGhost && s.Decreases.Expressions.Exists(e => e is WildcardExpr)) { + s.IsGhost = mustBeErasable || (s.Guard != null && resolver.UsesSpecFeatures(s.Guard)); + if (s.IsGhost && s.Decreases.Expressions.Exists(e => e is WildcardExpr)) { Error(s, "'decreases *' is not allowed on ghost loops"); } if (s.Body != null) { - Visit(s.Body, s.C_IsGhost); + Visit(s.Body, s.IsGhost); } - s.C_IsGhost = s.C_IsGhost || s.Body == null || (!s.Decreases.Expressions.Exists(e => e is WildcardExpr) && s.Body.C_IsGhost); + s.IsGhost = s.IsGhost || s.Body == null || (!s.Decreases.Expressions.Exists(e => e is WildcardExpr) && s.Body.IsGhost); } else if (stmt is AlternativeLoopStmt) { var s = (AlternativeLoopStmt)stmt; - s.C_IsGhost = mustBeErasable || s.Alternatives.Exists(alt => resolver.UsesSpecFeatures(alt.Guard)); - s.Alternatives.Iter(alt => alt.Body.Iter(ss => Visit(ss, s.C_IsGhost))); - s.C_IsGhost = s.C_IsGhost || (!s.Decreases.Expressions.Exists(e => e is WildcardExpr) && s.Alternatives.All(alt => alt.Body.All(ss => ss.C_IsGhost))); + s.IsGhost = mustBeErasable || s.Alternatives.Exists(alt => resolver.UsesSpecFeatures(alt.Guard)); + s.Alternatives.Iter(alt => alt.Body.Iter(ss => Visit(ss, s.IsGhost))); + s.IsGhost = s.IsGhost || (!s.Decreases.Expressions.Exists(e => e is WildcardExpr) && s.Alternatives.All(alt => alt.Body.All(ss => ss.IsGhost))); } else if (stmt is ForallStmt) { var s = (ForallStmt)stmt; - s.C_IsGhost = mustBeErasable || s.Kind != ForallStmt.ParBodyKind.Assign || resolver.UsesSpecFeatures(s.Range); + s.IsGhost = mustBeErasable || s.Kind != ForallStmt.ParBodyKind.Assign || resolver.UsesSpecFeatures(s.Range); if (s.Body != null) { - Visit(s.Body, s.C_IsGhost); + Visit(s.Body, s.IsGhost); } - s.C_IsGhost = s.C_IsGhost || s.Body == null || s.Body.C_IsGhost; + s.IsGhost = s.IsGhost || s.Body == null || s.Body.IsGhost; } else if (stmt is ModifyStmt) { var s = (ModifyStmt)stmt; if (s.Body != null) { Visit(s.Body, mustBeErasable); } - s.C_IsGhost = mustBeErasable; + s.IsGhost = mustBeErasable; } else if (stmt is CalcStmt) { var s = (CalcStmt)stmt; - s.C_IsGhost = true; + s.IsGhost = true; foreach (var h in s.Hints) { Visit(h, true); } @@ -3282,7 +3282,7 @@ namespace Microsoft.Dafny var s = (MatchStmt)stmt; var mbe = mustBeErasable || resolver.UsesSpecFeatures(s.Source); s.Cases.Iter(kase => kase.Body.Iter(ss => Visit(ss, mbe))); - s.C_IsGhost = mbe || s.Cases.All(kase => kase.Body.All(ss => ss.C_IsGhost)); + s.IsGhost = mbe || s.Cases.All(kase => kase.Body.All(ss => ss.IsGhost)); } else if (stmt is SkeletonStatement) { var s = (SkeletonStatement)stmt; @@ -3293,7 +3293,6 @@ namespace Microsoft.Dafny } else { Contract.Assert(false); throw new cce.UnreachableException(); } - stmt.IsGhost = stmt.C_IsGhost; // DEBUG } } #endregion @@ -4119,10 +4118,6 @@ namespace Microsoft.Dafny void ResolveMethod(Method m) { Contract.Requires(m != null); -#if !OLD_GH - var oldx = Statement.ReadyToDealWithGhostField; - Statement.ReadyToDealWithGhostField = false; -#endif try { currentMethod = m; @@ -4192,7 +4187,6 @@ namespace Microsoft.Dafny ResolveBlockStatement(m.Body, m.IsGhost, m); SolveAllTypeConstraints(); if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { - Statement.ReadyToDealWithGhostField = true; ComputeGhostInterest(m.Body, m); } } @@ -4206,9 +4200,6 @@ namespace Microsoft.Dafny finally { currentMethod = null; -#if !OLD_GH - Statement.ReadyToDealWithGhostField = oldx; -#endif } } @@ -5165,9 +5156,6 @@ namespace Microsoft.Dafny } if (stmt is PredicateStmt) { PredicateStmt s = (PredicateStmt)stmt; -#if OLD_GH - s.IsGhost = true; -#endif ResolveExpression(s.Expr, new ResolveOpts(codeContext, true, true)); Contract.Assert(s.Expr.Type != null); // follows from postcondition of ResolveExpression ConstrainTypes(s.Expr.Type, Type.Bool, s.Expr, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Expr.Type); @@ -5176,12 +5164,6 @@ namespace Microsoft.Dafny var s = (PrintStmt)stmt; var opts = new ResolveOpts(codeContext, false, specContextOnly); s.Args.Iter(e => ResolveExpression(e, opts)); -#if OLD_GHOST_HANDLING - ResolveAttributeArgs(s.Args, new ResolveOpts(codeContext, false, specContextOnly)); - if (specContextOnly) { - reporter.Error(MessageSource.Resolver, stmt, "print statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); - } -#endif } else if (stmt is BreakStmt) { var s = (BreakStmt)stmt; @@ -5191,12 +5173,6 @@ namespace Microsoft.Dafny reporter.Error(MessageSource.Resolver, s, "break label is undefined or not in scope: {0}", s.TargetLabel); } else { s.TargetStmt = target; -#if OLD_GHOST_HANDLING - bool targetIsLoop = target is WhileStmt || target is AlternativeLoopStmt; - if (specContextOnly && !s.TargetStmt.IsGhost && !inSpecOnlyContext[s.TargetStmt]) { - reporter.Error(MessageSource.Resolver, stmt, "ghost-context break statement is not allowed to break out of non-ghost " + (targetIsLoop ? "loop" : "structure")); - } -#endif } } else { if (loopStack.Count < s.BreakCount) { @@ -5208,11 +5184,6 @@ namespace Microsoft.Dafny target.Labels = new LList public static Expression CreateSubtract(Expression e0, Expression e1) { Contract.Requires(e0 != null); + Contract.Requires(e0.Type != null); Contract.Requires(e1 != null); - Contract.Requires((e0.Type.IsIntegerType && e1.Type.IsIntegerType) || (e0.Type.IsRealType && e1.Type.IsRealType)); + Contract.Requires(e1.Type != null); + Contract.Requires( + (e0.Type.IsNumericBased(Type.NumericPersuation.Int) && e1.Type.IsNumericBased(Type.NumericPersuation.Int)) || + (e0.Type.IsNumericBased(Type.NumericPersuation.Real) && e1.Type.IsNumericBased(Type.NumericPersuation.Real))); Contract.Ensures(Contract.Result() != null); var s = new BinaryExpr(e0.tok, BinaryExpr.Opcode.Sub, e0, e1); s.ResolvedOp = BinaryExpr.ResolvedOpcode.Sub; // resolve here @@ -5279,7 +5285,8 @@ namespace Microsoft.Dafny { /// public static Expression CreateIncrement(Expression e, int n) { Contract.Requires(e != null); - Contract.Requires(e.Type.IsIntegerType); + Contract.Requires(e.Type != null); + Contract.Requires(e.Type.IsNumericBased(Type.NumericPersuation.Int)); Contract.Requires(0 <= n); Contract.Ensures(Contract.Result() != null); if (n == 0) { @@ -5294,7 +5301,7 @@ namespace Microsoft.Dafny { /// public static Expression CreateDecrement(Expression e, int n) { Contract.Requires(e != null); - Contract.Requires(e.Type.IsIntegerType); + Contract.Requires(e.Type.IsNumericBased(Type.NumericPersuation.Int)); Contract.Requires(0 <= n); Contract.Ensures(Contract.Result() != null); if (n == 0) { @@ -5353,7 +5360,7 @@ namespace Microsoft.Dafny { public static Expression CreateLess(Expression e0, Expression e1) { Contract.Requires(e0 != null); Contract.Requires(e1 != null); - Contract.Requires(e0.Type.IsIntegerType && e1.Type.IsIntegerType); + Contract.Requires(e0.Type.IsNumericBased(Type.NumericPersuation.Int) && e1.Type.IsNumericBased(Type.NumericPersuation.Int)); Contract.Ensures(Contract.Result() != null); var s = new BinaryExpr(e0.tok, BinaryExpr.Opcode.Lt, e0, e1); s.ResolvedOp = BinaryExpr.ResolvedOpcode.Lt; // resolve here @@ -5367,7 +5374,9 @@ namespace Microsoft.Dafny { public static Expression CreateAtMost(Expression e0, Expression e1) { Contract.Requires(e0 != null); Contract.Requires(e1 != null); - Contract.Requires((e0.Type.IsIntegerType && e1.Type.IsIntegerType) || (e0.Type.IsRealType && e1.Type.IsRealType)); + Contract.Requires( + (e0.Type.IsNumericBased(Type.NumericPersuation.Int) && e1.Type.IsNumericBased(Type.NumericPersuation.Int)) || + (e0.Type.IsNumericBased(Type.NumericPersuation.Real) && e1.Type.IsNumericBased(Type.NumericPersuation.Real))); Contract.Ensures(Contract.Result() != null); var s = new BinaryExpr(e0.tok, BinaryExpr.Opcode.Le, e0, e1); s.ResolvedOp = BinaryExpr.ResolvedOpcode.Le; // resolve here diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index a2ae401a..210cd4ac 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -7305,41 +7305,43 @@ namespace Microsoft.Dafny } eIter = ei.Seq; } - var e0 = eIter; - - // Rewrite an update of the form "dt[dtor := E]" to be "let d' := dt in dtCtr(E, d'.dtor2, d'.dtor3,...)" - // Wrapping it in a let expr avoids exponential growth in the size of the expression - // More generally, rewrite "E0[dtor1 := E1][dtor2 := E2]...[dtorn := En]" to - // "let d' := E0 in dtCtr(...mixtures of Ek and d'.dtorj...)" - - // Create a unique name for d', the variable we introduce in the let expression - string tmpName = FreshTempVarName("dt_update_tmp#", opts.codeContext); - IdentifierExpr tmpVarIdExpr = new IdentifierExpr(e0.tok, tmpName); - BoundVar tmpVarBv = new BoundVar(e0.tok, tmpName, e0.Type); - - // Build the arguments to the datatype constructor, using the updated value in the appropriate slot - List ctor_args = new List(); - foreach (Formal d in ctor.Formals) { - Expression v = null; - foreach (var dvPair in IndexToValue.Values) { - var destructor = dvPair.Item1; - if (d == destructor.CorrespondingFormal) { - Contract.Assert(v == null); - v = dvPair.Item2; + if (ctor != null) { + var e0 = eIter; + + // Rewrite an update of the form "dt[dtor := E]" to be "let d' := dt in dtCtr(E, d'.dtor2, d'.dtor3,...)" + // Wrapping it in a let expr avoids exponential growth in the size of the expression + // More generally, rewrite "E0[dtor1 := E1][dtor2 := E2]...[dtorn := En]" to + // "let d' := E0 in dtCtr(...mixtures of Ek and d'.dtorj...)" + + // Create a unique name for d', the variable we introduce in the let expression + string tmpName = FreshTempVarName("dt_update_tmp#", opts.codeContext); + IdentifierExpr tmpVarIdExpr = new IdentifierExpr(e0.tok, tmpName); + BoundVar tmpVarBv = new BoundVar(e0.tok, tmpName, e0.Type); + + // Build the arguments to the datatype constructor, using the updated value in the appropriate slot + List ctor_args = new List(); + foreach (Formal d in ctor.Formals) { + Expression v = null; + foreach (var dvPair in IndexToValue.Values) { + var destructor = dvPair.Item1; + if (d == destructor.CorrespondingFormal) { + Contract.Assert(v == null); + v = dvPair.Item2; + } } + ctor_args.Add(v ?? new ExprDotName(expr.tok, tmpVarIdExpr, d.Name, null)); } - ctor_args.Add(v ?? new ExprDotName(expr.tok, tmpVarIdExpr, d.Name, null)); - } - DatatypeValue ctor_call = new DatatypeValue(expr.tok, ctor.EnclosingDatatype.Name, ctor.Name, ctor_args); + DatatypeValue ctor_call = new DatatypeValue(expr.tok, ctor.EnclosingDatatype.Name, ctor.Name, ctor_args); - CasePattern tmpVarPat = new CasePattern(e0.tok, tmpVarBv); - LetExpr let = new LetExpr(e0.tok, new List() { tmpVarPat }, new List() { e0 }, ctor_call, true); + CasePattern tmpVarPat = new CasePattern(e0.tok, tmpVarBv); + LetExpr let = new LetExpr(e0.tok, new List() { tmpVarPat }, new List() { e0 }, ctor_call, true); - ResolveExpression(let, opts); - e.ResolvedUpdateExpr = let; + ResolveExpression(let, opts); + e.ResolvedUpdateExpr = let; - expr.Type = e0.Type; + expr.Type = e0.Type; + } } else { reporter.Error(MessageSource.Resolver, expr, "update requires a sequence, map, or datatype (got {0})", e.Seq.Type); } -- 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(-) 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 6c4b0f1362ecea4c0fdc3e87ca9bc2de48158b82 Mon Sep 17 00:00:00 2001 From: leino Date: Mon, 28 Sep 2015 13:19:31 -0700 Subject: Improvements in proofs --- Test/dafny4/Leq.dfy | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/Test/dafny4/Leq.dfy b/Test/dafny4/Leq.dfy index 046febcf..0491dd00 100644 --- a/Test/dafny4/Leq.dfy +++ b/Test/dafny4/Leq.dfy @@ -36,7 +36,6 @@ colemma NatCasesAux(a: Nat) ensures IsInfinity(a) { assert a != Num(0); - assert a != Z; if IsFinite(a.pred) { // going for a contradiction var m:nat :| a.pred == Num(m); @@ -158,25 +157,18 @@ lemma CoLeq1'(a: Nat, b: Nat) returns (m: nat, n: nat) if !IsInfinity(b) { NatCases(b); n :| b == Num(n); - CoLeq1Aux(a, n); - m :| a == Num(m) && m <= n; + m := CoLeq1Aux(a, n); } } -lemma CoLeq1Aux(a: Nat, n: nat) +lemma CoLeq1Aux(a: Nat, n: nat) returns (m: nat) requires CoLeq(a, Num(n)) - ensures exists m:nat :: a == Num(m) && m <= n + ensures a == Num(m) && m <= n { - var b := Num(n); - assert a == Z || - (a.S? && b.S? && CoLeq(a.pred, b.pred)); if a == Z { - assert a == Num(0); + m := 0; } else { - assert b.pred == Num(n-1); - assert a.S? && b.S? && CoLeq(a.pred, b.pred); - CoLeq1Aux(a.pred, n-1); - var m:nat :| a.pred == Num(m) && m <= n-1; - assert a == Num(m+1) && m+1 <= n-1+1; + m := CoLeq1Aux(a.pred, n-1); + m := m + 1; } } -- cgit v1.2.3 From 4c21d765625b35eab9f5dc4ca21f170d3f7a2f04 Mon Sep 17 00:00:00 2001 From: leino Date: Mon, 28 Sep 2015 13:27:22 -0700 Subject: Whitespace changes in test file --- Test/dafny0/ResolutionErrors.dfy | 150 +++++--------- Test/dafny0/ResolutionErrors.dfy.expect | 344 ++++++++++++++++---------------- 2 files changed, 221 insertions(+), 273 deletions(-) diff --git a/Test/dafny0/ResolutionErrors.dfy b/Test/dafny0/ResolutionErrors.dfy index d3514b2b..a4161a46 100644 --- a/Test/dafny0/ResolutionErrors.dfy +++ b/Test/dafny0/ResolutionErrors.dfy @@ -150,14 +150,6 @@ class GhostTests { r := r + g; // fine, for the same reason r := N(20, 20); // error: call to non-ghost method from ghost method is not okay } - ghost method NiceTry() - ensures false; - { - while (true) -//KRML-confirmed decreases *; // error: not allowed in ghost context - { - } - } ghost method BreaksAreFineHere(t: int) { var n := 0; @@ -227,8 +219,6 @@ class GhostTests { label IfNest: if (p == 67) { break break; // fine, since this is not a ghost context - } else if (*) { -//KRML-confirmed break break break; // error: tries to break out of more loop levels than there are } q := q + 1; } @@ -363,9 +353,6 @@ method DatatypeDestructors(d: DTD_List) { ghost var g0 := d.g; // fine } } -method DatatypeDestructors_Ghost(d: DTD_List) { - var g1 := d.g; // error: cannot use ghost member in non-ghost code//KRML-confirmed -} // ------------------- print statements --------------------------------------- module GhostPrintAttempts { @@ -435,47 +422,41 @@ method TestCalc(m: int, n: int, a: bool, b: bool) ==> n + m + 2; // error: ==> operator requires boolean lines } } -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//KRML-confirmed - m + n; - } -} + module MyOwnModule { -class SideEffectChecks { - ghost var ycalc: int; + class SideEffectChecks { + ghost var ycalc: int; - ghost method Mod(a: int) - modifies this; - ensures ycalc == a; - { - ycalc := a; - } + ghost method Mod(a: int) + modifies this; + ensures ycalc == a; + { + ycalc := a; + } - ghost method Bad() - modifies this; - ensures 0 == 1; - { - var x: int; - calc { - 0; - { Mod(0); } // error: methods with side-effects are not allowed - ycalc; - { ycalc := 1; } // error: heap updates are not allowed - 1; - { x := 1; } // error: updates to locals defined outside of the hint are not allowed - x; - { - var x: int; - x := 1; // this is OK + ghost method Bad() + modifies this; + ensures 0 == 1; + { + var x: int; + calc { + 0; + { Mod(0); } // error: methods with side-effects are not allowed + ycalc; + { ycalc := 1; } // error: heap updates are not allowed + 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; } - 1; } } } -} + // ------------------- nameless constructors ------------------------------ class YHWH { @@ -523,14 +504,10 @@ method AssignSuchThatFromGhost() var x: int; ghost var g: int; - 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//KRML-confirmed - x :| assume x == g; // this is cool, since it's an assume (but, of course, the // compiler will complain) @@ -603,10 +580,6 @@ method LetSuchThat(ghost z: int, n: nat) x := var w := 2*w; w; // error: the 'w' in the RHS of the assignment is not in scope ghost var xg := var w :| w == 2*w; w; } -method LetSuchThat_Ghost(ghost z: int, n: nat) -{ - var x := var y :| y < z; y; // error: contraint depend on ghost (z)//KRML-confirmed -} // ------------ quantified variables whose types are not inferred ---------- @@ -677,10 +650,6 @@ 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//KRML-confirmed - 1 + 4; - { GhostNew5(g); } // error: cannot call method with nonempty modifies//KRML-confirmed - -5 + 10; } } @@ -730,22 +699,11 @@ module StatementsInExpressions { { } - ghost method M() - modifies this; - { - calc { - 5; - { SideEffect(); } // error: cannot call method with side effects//KRML - 5; - } - } - function F(): int { calc { 6; { assert 6 < 8; } - { 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 +717,8 @@ module StatementsInExpressions { x := x - 1; } } - { 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//KRML-confirmed { x := x - 1; } @@ -783,7 +737,6 @@ module StatementsInExpressions { calc { 6; { assert 6 < 8; } - { 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 +744,8 @@ module StatementsInExpressions { x := x - 1; } } - { 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//KRML-confirmed { x := x - 1; } @@ -822,7 +771,6 @@ module StatementsInExpressions { { MyLemma(); MyGhostMethod(); // error: modifi2es state - OrdinaryMethod(); // error: not a ghost//KRML-confirmed OutParamMethod(); // error: has out-parameters 10 } @@ -1458,9 +1406,9 @@ module GhostTests { calc { 5; 2 + 3; - { if n != 0 { GhostNew4(n-1); } } // error: cannot call non-ghost method in a ghost context//ADD:680 + { if n != 0 { GhostNew4(n-1); } } // error: cannot call non-ghost method in a ghost context 1 + 4; - { GhostNew5(g); } // error: cannot call method with nonempty modifies//ADD:682 + { GhostNew5(g); } // error: cannot call method with nonempty modifies -5 + 10; } } @@ -1485,7 +1433,7 @@ module GhostTests { { calc { 5; - { SideEffect(); } // error: cannot call method with side effects//ADD:738 + { SideEffect(); } // error: cannot call method with side effects 5; } } @@ -1494,7 +1442,7 @@ module GhostTests { calc { 6; { assert 6 < 8; } - { NonGhostMethod(); } // error: cannot call non-ghost method//ADD:748 + { NonGhostMethod(); } // error: cannot call non-ghost method { var x := 8; while x != 0 { @@ -1507,12 +1455,12 @@ module GhostTests { 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 + { MyField := 12; } // error: cannot assign to a field, and especially not a non-ghost field + { MyGhostField := 12; } // error: cannot assign to any field + { SideEffect(); } // error: cannot call (ghost) method with a modifies clause { var x := 8; while x != 0 - modifies this; // error: cannot use a modifies clause on a loop//ADD:767 + modifies this; // error: cannot use a modifies clause on a loop { x := x - 1; } @@ -1529,19 +1477,19 @@ module GhostTests { calc { 6; { assert 6 < 8; } - { NonGhostMethod(); } // error: cannot call non-ghost method//ADD:786 + { NonGhostMethod(); } // error: cannot call non-ghost method { 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 + { MyField := 12; } // error: cannot assign to a field, and especially not a non-ghost field + { MyGhostField := 12; } // error: cannot assign to any field + { M(); } // error: cannot call (ghost) method with a modifies clause { var x := 8; while x != 0 - modifies this; // error: cannot use a modifies clause on a loop//ADD:799 + modifies this; // error: cannot use a modifies clause on a loop { x := x - 1; } @@ -1565,7 +1513,7 @@ module GhostTests { function UseLemma(): int { MyLemma(); - OrdinaryMethod(); // error: not a ghost//ADD:825 + OrdinaryMethod(); // error: not a ghost 10 } } @@ -1576,7 +1524,7 @@ module EvenMoreGhostTests { ensures false; { while (true) - decreases *; // error: not allowed in ghost context//ADD:157 + decreases *; // error: not allowed in ghost context { } } @@ -1594,7 +1542,7 @@ module EvenMoreGhostTests { 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 + break break break; // error: tries to break out of more loop levels than there are } } } @@ -1620,20 +1568,20 @@ 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 + var g1 := d.g; // error: cannot use ghost member in non-ghost code } method AssignSuchThatFromGhost() { var x: int; ghost var g: int; - x := g; // error: ghost cannot flow into non-ghost//ADD:526 + x := g; // error: ghost cannot flow into non-ghost 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 :| x == g; // error: left-side has non-ghost, so RHS must be non-ghost as well x :| assume x == g; // this is cool, since it's an assume (but, of course, the // compiler will complain) @@ -1650,7 +1598,7 @@ module MoreGhostPrintAttempts { { calc { n + m; - { print n + m; } // error: non-ghost statements are not allowed in hints//ADD:442 + { print n + m; } // error: non-ghost statements are not allowed in hints m + n; } } @@ -1659,6 +1607,6 @@ module MoreGhostPrintAttempts { 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 + var x := var y :| y < z; y; // error: contraint depend on ghost (z) } } diff --git a/Test/dafny0/ResolutionErrors.dfy.expect b/Test/dafny0/ResolutionErrors.dfy.expect index ee2dd5f7..e5506688 100644 --- a/Test/dafny0/ResolutionErrors.dfy.expect +++ b/Test/dafny0/ResolutionErrors.dfy.expect @@ -8,152 +8,152 @@ ResolutionErrors.dfy(137,4): Error: ghost variables are allowed only in specific 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>) -ResolutionErrors.dfy(589,24): Error: Wrong number of type arguments (0 instead of 2) passed to datatype: Tree -ResolutionErrors.dfy(619,25): Error: the type of this variable is underspecified -ResolutionErrors.dfy(619,23): Error: type variable 'T' in the function call to 'P' could not be determined -ResolutionErrors.dfy(626,25): Error: the type of this variable is underspecified -ResolutionErrors.dfy(626,23): Error: type variable 'T' in the function call to 'P' could not be determined -ResolutionErrors.dfy(639,13): Error: 'new' is not allowed in ghost contexts -ResolutionErrors.dfy(640,9): Error: 'new' is not allowed in ghost contexts -ResolutionErrors.dfy(647,14): Error: new allocation not supported in forall statements -ResolutionErrors.dfy(652,11): Error: the body of the enclosing forall statement is not allowed to update heap locations -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(678,17): Error: 'new' is not allowed in ghost contexts -ResolutionErrors.dfy(700,21): Error: the type of this variable is underspecified -ResolutionErrors.dfy(751,22): Error: 'decreases *' is not allowed on ghost loops -ResolutionErrors.dfy(789,22): Error: 'decreases *' is not allowed on ghost loops -ResolutionErrors.dfy(824,19): Error: calls to methods with side-effects are not allowed inside a statement expression -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 -ResolutionErrors.dfy(859,36): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(868,17): Error: function calls are allowed only in specification contexts (consider declaring the function a 'function method') -ResolutionErrors.dfy(882,6): Error: RHS (of type B) not assignable to LHS (of type object) -ResolutionErrors.dfy(883,6): Error: RHS (of type int) not assignable to LHS (of type object) -ResolutionErrors.dfy(884,6): Error: RHS (of type B) not assignable to LHS (of type object) -ResolutionErrors.dfy(889,6): Error: RHS (of type G) not assignable to LHS (of type object) -ResolutionErrors.dfy(890,6): Error: RHS (of type Dt) not assignable to LHS (of type object) -ResolutionErrors.dfy(891,6): Error: RHS (of type CoDt) not assignable to LHS (of type object) -ResolutionErrors.dfy(953,4): Error: LHS of array assignment must denote an array element (found seq) -ResolutionErrors.dfy(954,4): Error: LHS of array assignment must denote an array element (found seq) -ResolutionErrors.dfy(959,10): Error: LHS of assignment must denote a mutable field -ResolutionErrors.dfy(960,10): Error: LHS of assignment must denote a mutable field -ResolutionErrors.dfy(961,9): Error: cannot assign to a range of array elements (try the 'forall' statement) -ResolutionErrors.dfy(962,9): Error: cannot assign to a range of array elements (try the 'forall' statement) -ResolutionErrors.dfy(963,5): Error: cannot assign to a range of array elements (try the 'forall' statement) -ResolutionErrors.dfy(964,5): Error: cannot assign to a range of array elements (try the 'forall' statement) -ResolutionErrors.dfy(1045,11): Error: Wrong number of type arguments (2 instead of 1) passed to array type: array3 -ResolutionErrors.dfy(1046,11): Error: Wrong number of type arguments (2 instead of 1) passed to class: C -ResolutionErrors.dfy(1057,7): Error: Duplicate name of top-level declaration: BadSyn2 -ResolutionErrors.dfy(1054,17): Error: Wrong number of type arguments (0 instead of 1) passed to datatype: List -ResolutionErrors.dfy(1055,17): Error: Undeclared top-level type or type parameter: badName (did you forget to qualify a name or declare a module import 'opened?') -ResolutionErrors.dfy(1056,22): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') -ResolutionErrors.dfy(1063,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> A -ResolutionErrors.dfy(1066,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A -ResolutionErrors.dfy(1070,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A -ResolutionErrors.dfy(1079,11): Error: because of cyclic dependencies among constructor argument types, no instances of datatype 'D' can be constructed -ResolutionErrors.dfy(1082,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A -ResolutionErrors.dfy(1087,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A -ResolutionErrors.dfy(1106,21): Error: unresolved identifier: x -ResolutionErrors.dfy(1113,35): Error: Wrong number of type arguments (2 instead of 1) passed to opaque type: P -ResolutionErrors.dfy(1125,13): Error: Undeclared top-level type or type parameter: BX (did you forget to qualify a name or declare a module import 'opened?') -ResolutionErrors.dfy(1135,6): Error: RHS (of type P) not assignable to LHS (of type P) -ResolutionErrors.dfy(1140,6): Error: RHS (of type P) not assignable to LHS (of type P) -ResolutionErrors.dfy(1145,6): Error: RHS (of type P) not assignable to LHS (of type P) -ResolutionErrors.dfy(1146,6): Error: RHS (of type P) not assignable to LHS (of type P) -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: 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 -ResolutionErrors.dfy(1295,34): Error: the type of this variable is underspecified -ResolutionErrors.dfy(1311,21): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') -ResolutionErrors.dfy(1312,24): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') -ResolutionErrors.dfy(1349,16): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (y) -ResolutionErrors.dfy(1359,18): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) -ResolutionErrors.dfy(1387,15): Error: The name Inner ambiguously refers to a type in one of the modules A, B (try qualifying the type name with the module name) -ResolutionErrors.dfy(1397,29): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(1399,49): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(1399,54): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(1420,11): Error: name of type (X) is used as a variable -ResolutionErrors.dfy(1420,16): Error: name of type (X) is used as a variable -ResolutionErrors.dfy(1421,11): Error: name of module (Y) is used as a variable -ResolutionErrors.dfy(1421,16): Error: name of module (Y) is used as a variable -ResolutionErrors.dfy(1422,11): Error: name of type (X) is used as a variable -ResolutionErrors.dfy(1422,13): Error: second argument to "in" must be a set, multiset, or sequence with elements of type #type, or a map with domain #type (instead got map) -ResolutionErrors.dfy(1423,11): Error: name of module (Y) is used as a variable -ResolutionErrors.dfy(1423,13): Error: second argument to "in" must be a set, multiset, or sequence with elements of type #module, or a map with domain #module (instead got map) -ResolutionErrors.dfy(1428,16): Error: name of type (X) is used as a variable -ResolutionErrors.dfy(1428,13): Error: arguments must have the same type (got int and #type) -ResolutionErrors.dfy(1429,16): Error: name of module (Y) is used as a variable -ResolutionErrors.dfy(1429,13): Error: arguments must have the same type (got int and #module) -ResolutionErrors.dfy(1430,4): Error: name of type (X) is used as a variable -ResolutionErrors.dfy(1431,4): Error: name of module (Y) is used as a variable -ResolutionErrors.dfy(1440,11): Error: type of RHS of assign-such-that statement must be boolean (got int) -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(241,27): Error: ghost-context break statement is not allowed to break out of non-ghost structure +ResolutionErrors.dfy(264,12): Error: ghost-context break statement is not allowed to break out of non-ghost loop +ResolutionErrors.dfy(278,12): Error: ghost-context break statement is not allowed to break out of non-ghost loop +ResolutionErrors.dfy(283,8): Error: return statement is not allowed in this context (because it is guarded by a specification-only expression) +ResolutionErrors.dfy(362,15): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(444,13): Error: calls to methods with side-effects are not allowed inside a hint +ResolutionErrors.dfy(446,16): Error: a hint is not allowed to update heap locations +ResolutionErrors.dfy(448,12): Error: a hint is not allowed to update a variable declared outside the hint +ResolutionErrors.dfy(535,7): Error: RHS (of type List) not assignable to LHS (of type List) +ResolutionErrors.dfy(540,7): Error: RHS (of type List) not assignable to LHS (of type List) +ResolutionErrors.dfy(554,23): Error: type of case bodies do not agree (found Tree<_T1,_T0>, previous types Tree<_T0,_T1>) +ResolutionErrors.dfy(566,24): Error: Wrong number of type arguments (0 instead of 2) passed to datatype: Tree +ResolutionErrors.dfy(592,25): Error: the type of this variable is underspecified +ResolutionErrors.dfy(592,23): Error: type variable 'T' in the function call to 'P' could not be determined +ResolutionErrors.dfy(599,25): Error: the type of this variable is underspecified +ResolutionErrors.dfy(599,23): Error: type variable 'T' in the function call to 'P' could not be determined +ResolutionErrors.dfy(612,13): Error: 'new' is not allowed in ghost contexts +ResolutionErrors.dfy(613,9): Error: 'new' is not allowed in ghost contexts +ResolutionErrors.dfy(620,14): Error: new allocation not supported in forall statements +ResolutionErrors.dfy(625,11): Error: the body of the enclosing forall statement is not allowed to update heap locations +ResolutionErrors.dfy(625,14): Error: new allocation not allowed in ghost context +ResolutionErrors.dfy(635,23): Error: 'new' is not allowed in ghost contexts +ResolutionErrors.dfy(642,15): Error: 'new' is not allowed in ghost contexts +ResolutionErrors.dfy(651,17): Error: 'new' is not allowed in ghost contexts +ResolutionErrors.dfy(669,21): Error: the type of this variable is underspecified +ResolutionErrors.dfy(709,22): Error: 'decreases *' is not allowed on ghost loops +ResolutionErrors.dfy(742,22): Error: 'decreases *' is not allowed on ghost loops +ResolutionErrors.dfy(773,19): Error: calls to methods with side-effects are not allowed inside a statement expression +ResolutionErrors.dfy(774,20): Error: wrong number of method result arguments (got 0, expected 1) +ResolutionErrors.dfy(786,23): Error: function calls are allowed only in specification contexts (consider declaring the function a 'function method') +ResolutionErrors.dfy(796,4): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(807,36): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(816,17): Error: function calls are allowed only in specification contexts (consider declaring the function a 'function method') +ResolutionErrors.dfy(830,6): Error: RHS (of type B) not assignable to LHS (of type object) +ResolutionErrors.dfy(831,6): Error: RHS (of type int) not assignable to LHS (of type object) +ResolutionErrors.dfy(832,6): Error: RHS (of type B) not assignable to LHS (of type object) +ResolutionErrors.dfy(837,6): Error: RHS (of type G) not assignable to LHS (of type object) +ResolutionErrors.dfy(838,6): Error: RHS (of type Dt) not assignable to LHS (of type object) +ResolutionErrors.dfy(839,6): Error: RHS (of type CoDt) not assignable to LHS (of type object) +ResolutionErrors.dfy(901,4): Error: LHS of array assignment must denote an array element (found seq) +ResolutionErrors.dfy(902,4): Error: LHS of array assignment must denote an array element (found seq) +ResolutionErrors.dfy(907,10): Error: LHS of assignment must denote a mutable field +ResolutionErrors.dfy(908,10): Error: LHS of assignment must denote a mutable field +ResolutionErrors.dfy(909,9): Error: cannot assign to a range of array elements (try the 'forall' statement) +ResolutionErrors.dfy(910,9): Error: cannot assign to a range of array elements (try the 'forall' statement) +ResolutionErrors.dfy(911,5): Error: cannot assign to a range of array elements (try the 'forall' statement) +ResolutionErrors.dfy(912,5): Error: cannot assign to a range of array elements (try the 'forall' statement) +ResolutionErrors.dfy(993,11): Error: Wrong number of type arguments (2 instead of 1) passed to array type: array3 +ResolutionErrors.dfy(994,11): Error: Wrong number of type arguments (2 instead of 1) passed to class: C +ResolutionErrors.dfy(1005,7): Error: Duplicate name of top-level declaration: BadSyn2 +ResolutionErrors.dfy(1002,17): Error: Wrong number of type arguments (0 instead of 1) passed to datatype: List +ResolutionErrors.dfy(1003,17): Error: Undeclared top-level type or type parameter: badName (did you forget to qualify a name or declare a module import 'opened?') +ResolutionErrors.dfy(1004,22): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') +ResolutionErrors.dfy(1011,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> A +ResolutionErrors.dfy(1014,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A +ResolutionErrors.dfy(1018,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A +ResolutionErrors.dfy(1027,11): Error: because of cyclic dependencies among constructor argument types, no instances of datatype 'D' can be constructed +ResolutionErrors.dfy(1030,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A +ResolutionErrors.dfy(1035,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A +ResolutionErrors.dfy(1054,21): Error: unresolved identifier: x +ResolutionErrors.dfy(1061,35): Error: Wrong number of type arguments (2 instead of 1) passed to opaque type: P +ResolutionErrors.dfy(1073,13): Error: Undeclared top-level type or type parameter: BX (did you forget to qualify a name or declare a module import 'opened?') +ResolutionErrors.dfy(1083,6): Error: RHS (of type P) not assignable to LHS (of type P) +ResolutionErrors.dfy(1088,6): Error: RHS (of type P) not assignable to LHS (of type P) +ResolutionErrors.dfy(1093,6): Error: RHS (of type P) not assignable to LHS (of type P) +ResolutionErrors.dfy(1094,6): Error: RHS (of type P) not assignable to LHS (of type P) +ResolutionErrors.dfy(1099,13): Error: arguments must have the same type (got P and P) +ResolutionErrors.dfy(1100,13): Error: arguments must have the same type (got P and P) +ResolutionErrors.dfy(1101,13): Error: arguments must have the same type (got P and P) +ResolutionErrors.dfy(1124,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(1126,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(1231,26): Error: the type of this variable is underspecified +ResolutionErrors.dfy(1232,31): Error: the type of this variable is underspecified +ResolutionErrors.dfy(1233,29): Error: the type of this variable is underspecified +ResolutionErrors.dfy(1243,34): Error: the type of this variable is underspecified +ResolutionErrors.dfy(1259,21): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') +ResolutionErrors.dfy(1260,24): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') +ResolutionErrors.dfy(1297,16): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (y) +ResolutionErrors.dfy(1307,18): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) +ResolutionErrors.dfy(1335,15): Error: The name Inner ambiguously refers to a type in one of the modules A, B (try qualifying the type name with the module name) +ResolutionErrors.dfy(1345,29): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(1347,49): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(1347,54): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(1368,11): Error: name of type (X) is used as a variable +ResolutionErrors.dfy(1368,16): Error: name of type (X) is used as a variable +ResolutionErrors.dfy(1369,11): Error: name of module (Y) is used as a variable +ResolutionErrors.dfy(1369,16): Error: name of module (Y) is used as a variable +ResolutionErrors.dfy(1370,11): Error: name of type (X) is used as a variable +ResolutionErrors.dfy(1370,13): Error: second argument to "in" must be a set, multiset, or sequence with elements of type #type, or a map with domain #type (instead got map) +ResolutionErrors.dfy(1371,11): Error: name of module (Y) is used as a variable +ResolutionErrors.dfy(1371,13): Error: second argument to "in" must be a set, multiset, or sequence with elements of type #module, or a map with domain #module (instead got map) +ResolutionErrors.dfy(1376,16): Error: name of type (X) is used as a variable +ResolutionErrors.dfy(1376,13): Error: arguments must have the same type (got int and #type) +ResolutionErrors.dfy(1377,16): Error: name of module (Y) is used as a variable +ResolutionErrors.dfy(1377,13): Error: arguments must have the same type (got int and #module) +ResolutionErrors.dfy(1378,4): Error: name of type (X) is used as a variable +ResolutionErrors.dfy(1379,4): Error: name of module (Y) is used as a variable +ResolutionErrors.dfy(1388,11): Error: type of RHS of assign-such-that statement must be boolean (got int) +ResolutionErrors.dfy(1389,9): Error: type of RHS of assign-such-that statement must be boolean (got int) +ResolutionErrors.dfy(1390,13): Error: type of RHS of assign-such-that statement must be boolean (got int) +ResolutionErrors.dfy(1393,15): Error: type of RHS of let-such-that expression must be boolean (got int) +ResolutionErrors.dfy(1436,20): Error: calls to methods with side-effects are not allowed inside a hint +ResolutionErrors.dfy(1458,18): Error: a hint is not allowed to update heap locations +ResolutionErrors.dfy(1459,23): Error: a hint is not allowed to update heap locations +ResolutionErrors.dfy(1460,20): Error: calls to methods with side-effects are not allowed inside a hint +ResolutionErrors.dfy(1463,21): Error: a while statement used inside a hint is not allowed to have a modifies clause +ResolutionErrors.dfy(1445,24): Error: only ghost methods can be called from this context +ResolutionErrors.dfy(1458,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(1487,18): Error: a hint is not allowed to update heap locations +ResolutionErrors.dfy(1488,23): Error: a hint is not allowed to update heap locations +ResolutionErrors.dfy(1489,11): Error: calls to methods with side-effects are not allowed inside a hint +ResolutionErrors.dfy(1492,21): Error: a while statement used inside a hint is not allowed to have a modifies clause +ResolutionErrors.dfy(1480,24): Error: only ghost methods can be called from this context +ResolutionErrors.dfy(1487,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(1516,20): Error: only ghost methods can be called from this context +ResolutionErrors.dfy(1409,29): Error: only ghost methods can be called from this context +ResolutionErrors.dfy(1411,17): Error: calls to methods with side-effects are not allowed inside a hint +ResolutionErrors.dfy(1527,16): Error: 'decreases *' is not allowed on ghost loops +ResolutionErrors.dfy(1545,12): Error: trying to break out of more loop levels than there are enclosing loops +ResolutionErrors.dfy(1571,16): Error: ghost fields are allowed only in specification contexts +ResolutionErrors.dfy(1578,9): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(1584,4): Error: non-ghost variable cannot be assigned a value that depends on a ghost +ResolutionErrors.dfy(1601,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(1610,26): Error: ghost variables are allowed only in specification contexts +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') ResolutionErrors.dfy(92,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(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(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 -ResolutionErrors.dfy(502,9): Error: class Lamb does not have an anonymous constructor -ResolutionErrors.dfy(902,11): Error: a modifies-clause expression must denote an object or a collection of objects (instead got int) -ResolutionErrors.dfy(906,14): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) -ResolutionErrors.dfy(909,12): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) -ResolutionErrors.dfy(917,14): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) -ResolutionErrors.dfy(927,18): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) -ResolutionErrors.dfy(938,16): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) -ResolutionErrors.dfy(1094,23): Error: unresolved identifier: x -ResolutionErrors.dfy(1097,20): Error: unresolved identifier: x -ResolutionErrors.dfy(1100,23): Error: unresolved identifier: x -ResolutionErrors.dfy(1102,19): Error: unresolved identifier: x -ResolutionErrors.dfy(1104,19): Error: unresolved identifier: x +ResolutionErrors.dfy(475,14): Error: when allocating an object of type 'YHWH', one of its constructor methods must be called +ResolutionErrors.dfy(480,6): Error: when allocating an object of type 'Lucifer', one of its constructor methods must be called +ResolutionErrors.dfy(481,6): Error: when allocating an object of type 'Lucifer', one of its constructor methods must be called +ResolutionErrors.dfy(483,9): Error: class Lamb does not have an anonymous constructor +ResolutionErrors.dfy(850,11): Error: a modifies-clause expression must denote an object or a collection of objects (instead got int) +ResolutionErrors.dfy(854,14): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) +ResolutionErrors.dfy(857,12): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) +ResolutionErrors.dfy(865,14): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) +ResolutionErrors.dfy(875,18): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) +ResolutionErrors.dfy(886,16): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) +ResolutionErrors.dfy(1042,23): Error: unresolved identifier: x +ResolutionErrors.dfy(1045,20): Error: unresolved identifier: x +ResolutionErrors.dfy(1048,23): Error: unresolved identifier: x +ResolutionErrors.dfy(1050,19): Error: unresolved identifier: x +ResolutionErrors.dfy(1052,19): Error: unresolved identifier: x ResolutionErrors.dfy(12,16): Error: 'decreases *' is not allowed on ghost loops ResolutionErrors.dfy(24,11): Error: array selection requires an array2 (got array3) ResolutionErrors.dfy(25,12): Error: sequence/array/multiset/map selection requires a sequence, array, multiset, or map (got array3) @@ -167,36 +167,36 @@ ResolutionErrors.dfy(62,14): Error: accessing member 'M' requires an instance ex ResolutionErrors.dfy(63,7): Error: unresolved identifier: N ResolutionErrors.dfy(66,8): Error: non-function expression (of type int) is called with parameters ResolutionErrors.dfy(67,14): Error: member 'z' does not exist in type 'Global' -ResolutionErrors.dfy(311,4): Error: label shadows an enclosing label -ResolutionErrors.dfy(316,2): Error: duplicate label -ResolutionErrors.dfy(342,4): Error: when allocating an object of type 'ClassWithConstructor', one of its constructor methods must be called -ResolutionErrors.dfy(343,4): Error: when allocating an object of type 'ClassWithConstructor', one of its constructor methods must be called -ResolutionErrors.dfy(345,9): Error: a constructor is allowed to be called only when an object is being allocated -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(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) -ResolutionErrors.dfy(425,6): Error: all lines in a calculation must have the same type (got int after bool) -ResolutionErrors.dfy(428,6): Error: first argument to ==> must be of type bool (instead got int) -ResolutionErrors.dfy(428,6): Error: second argument to ==> must be of type bool (instead got int) -ResolutionErrors.dfy(429,10): Error: first argument to ==> must be of type bool (instead got int) -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(603,18): Error: unresolved identifier: w -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)) -ResolutionErrors.dfy(988,37): Error: RHS (of type (int,real,int)) not assignable to LHS (of type (int,real,int,real)) -ResolutionErrors.dfy(994,16): Error: condition is expected to be of type bool, but is int -ResolutionErrors.dfy(995,16): Error: member 3 does not exist in datatype _tuple#3 -ResolutionErrors.dfy(995,26): Error: member x does not exist in datatype _tuple#2 -ResolutionErrors.dfy(1018,15): Error: arguments to / must have the same type (got real and int) -ResolutionErrors.dfy(1019,10): Error: second argument to % must be of type int (instead got real) -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 +ResolutionErrors.dfy(301,4): Error: label shadows an enclosing label +ResolutionErrors.dfy(306,2): Error: duplicate label +ResolutionErrors.dfy(332,4): Error: when allocating an object of type 'ClassWithConstructor', one of its constructor methods must be called +ResolutionErrors.dfy(333,4): Error: when allocating an object of type 'ClassWithConstructor', one of its constructor methods must be called +ResolutionErrors.dfy(335,9): Error: a constructor is allowed to be called only when an object is being allocated +ResolutionErrors.dfy(349,16): Error: arguments must have the same type (got int and DTD_List) +ResolutionErrors.dfy(350,16): Error: arguments must have the same type (got DTD_List and int) +ResolutionErrors.dfy(351,25): Error: arguments must have the same type (got bool and int) +ResolutionErrors.dfy(387,5): Error: incorrect type of method in-parameter 1 (expected GenericClass, got GenericClass) +ResolutionErrors.dfy(399,18): Error: incorrect type of datatype constructor argument (found GList<_T0>, expected GList) +ResolutionErrors.dfy(407,6): Error: arguments to + must be of a numeric type or a collection type (instead got bool) +ResolutionErrors.dfy(412,6): Error: all lines in a calculation must have the same type (got int after bool) +ResolutionErrors.dfy(415,6): Error: first argument to ==> must be of type bool (instead got int) +ResolutionErrors.dfy(415,6): Error: second argument to ==> must be of type bool (instead got int) +ResolutionErrors.dfy(416,10): Error: first argument to ==> must be of type bool (instead got int) +ResolutionErrors.dfy(416,10): Error: second argument to ==> must be of type bool (instead got int) +ResolutionErrors.dfy(421,10): Error: first argument to ==> must be of type bool (instead got int) +ResolutionErrors.dfy(421,10): Error: second argument to ==> must be of type bool (instead got int) +ResolutionErrors.dfy(580,18): Error: unresolved identifier: w +ResolutionErrors.dfy(683,11): Error: lemmas are not allowed to have modifies clauses +ResolutionErrors.dfy(924,9): Error: unresolved identifier: s +ResolutionErrors.dfy(935,32): Error: RHS (of type (int,int,real)) not assignable to LHS (of type (int,real,int)) +ResolutionErrors.dfy(936,37): Error: RHS (of type (int,real,int)) not assignable to LHS (of type (int,real,int,real)) +ResolutionErrors.dfy(942,16): Error: condition is expected to be of type bool, but is int +ResolutionErrors.dfy(943,16): Error: member 3 does not exist in datatype _tuple#3 +ResolutionErrors.dfy(943,26): Error: member x does not exist in datatype _tuple#2 +ResolutionErrors.dfy(966,15): Error: arguments to / must have the same type (got real and int) +ResolutionErrors.dfy(967,10): Error: second argument to % must be of type int (instead got real) +ResolutionErrors.dfy(1112,8): Error: new cannot be applied to a trait +ResolutionErrors.dfy(1133,13): Error: first argument to / must be of numeric type (instead got set) +ResolutionErrors.dfy(1140,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(1155,14): Error: a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating 201 resolution/type errors detected in ResolutionErrors.dfy -- cgit v1.2.3 From aec9290fbd3ca9ada5a7fad4dabb4ed1ffad6a84 Mon Sep 17 00:00:00 2001 From: leino Date: Mon, 28 Sep 2015 13:31:26 -0700 Subject: Additional tests --- Test/dafny0/CoPrefix.dfy | 32 ++++++++++++++++++++++++++++++++ Test/dafny0/CoPrefix.dfy.expect | 17 ++++++++++++++++- Test/dafny0/ResolutionErrors.dfy | 9 +++++++++ Test/dafny0/ResolutionErrors.dfy.expect | 3 ++- 4 files changed, 59 insertions(+), 2 deletions(-) diff --git a/Test/dafny0/CoPrefix.dfy b/Test/dafny0/CoPrefix.dfy index 0becb24d..3b6bd670 100644 --- a/Test/dafny0/CoPrefix.dfy +++ b/Test/dafny0/CoPrefix.dfy @@ -192,3 +192,35 @@ module Recursion { } } } + +module PrefixEquality { + codatatype Stream = Cons(head: T, Stream) + + colemma Test0(s: Stream, t: Stream) + requires s.head == t.head + { + calc { + s; + ==#[_k-1] + t; // error: this step might not hold + ==#[if 2 <= _k then _k-2 else _k-1] + s; // error: this step might not hold + ==#[0] + t; + } + } + + colemma Test1(s: Stream, t: Stream) + requires s == t + { + calc { + s; + ==#[_k-1] + t; + ==#[_k-2] // error: prefix-equality limit must be at least 0 + s; + ==#[0] + t; + } + } +} diff --git a/Test/dafny0/CoPrefix.dfy.expect b/Test/dafny0/CoPrefix.dfy.expect index a7295367..b42f2593 100644 --- a/Test/dafny0/CoPrefix.dfy.expect +++ b/Test/dafny0/CoPrefix.dfy.expect @@ -12,6 +12,21 @@ CoPrefix.dfy(176,10): Error: cannot prove termination; try supplying a decreases Execution trace: (0,0): anon0 (0,0): anon3_Then +CoPrefix.dfy(205,6): Error: the calculation step between the previous line and this line might not hold +Execution trace: + (0,0): anon0 + (0,0): anon8_Then + (0,0): anon10_Then +CoPrefix.dfy(207,6): Error: the calculation step between the previous line and this line might not hold +Execution trace: + (0,0): anon0 + (0,0): anon8_Then + (0,0): anon11_Then +CoPrefix.dfy(220,12): Error: prefix-equality limit must be at least 0 +Execution trace: + (0,0): anon0 + (0,0): anon8_Then + (0,0): anon11_Then CoPrefix.dfy(63,56): Error: failure to decrease termination measure Execution trace: (0,0): anon0 @@ -47,4 +62,4 @@ Execution trace: (0,0): anon0 (0,0): anon3_Else -Dafny program verifier finished with 41 verified, 9 errors +Dafny program verifier finished with 43 verified, 12 errors diff --git a/Test/dafny0/ResolutionErrors.dfy b/Test/dafny0/ResolutionErrors.dfy index a4161a46..9d4d67f1 100644 --- a/Test/dafny0/ResolutionErrors.dfy +++ b/Test/dafny0/ResolutionErrors.dfy @@ -1610,3 +1610,12 @@ module MoreLetSuchThatExpr { var x := var y :| y < z; y; // error: contraint depend on ghost (z) } } + +module UnderspecifiedTypedShouldBeResolvedOnlyOnce { + method CalcTest0(s: seq) { + calc { + 2; + var t :| true; 2; // error: type of 't' is underspecified + } + } +} diff --git a/Test/dafny0/ResolutionErrors.dfy.expect b/Test/dafny0/ResolutionErrors.dfy.expect index e5506688..6f4b3519 100644 --- a/Test/dafny0/ResolutionErrors.dfy.expect +++ b/Test/dafny0/ResolutionErrors.dfy.expect @@ -132,6 +132,7 @@ ResolutionErrors.dfy(1578,9): Error: ghost variables are allowed only in specifi ResolutionErrors.dfy(1584,4): Error: non-ghost variable cannot be assigned a value that depends on a ghost ResolutionErrors.dfy(1601,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(1610,26): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(1618,6): Error: the type of the bound variable 't' 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') @@ -199,4 +200,4 @@ ResolutionErrors.dfy(1112,8): Error: new cannot be applied to a trait ResolutionErrors.dfy(1133,13): Error: first argument to / must be of numeric type (instead got set) ResolutionErrors.dfy(1140,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(1155,14): Error: a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating -201 resolution/type errors detected in ResolutionErrors.dfy +202 resolution/type errors detected in ResolutionErrors.dfy -- cgit v1.2.3 From 49706a5d1599f9167cb627a305d4abb32cc71edb Mon Sep 17 00:00:00 2001 From: leino Date: Mon, 28 Sep 2015 15:36:45 -0700 Subject: Removed more traces of the previous resolution checks that happened during pass 0. Fixed resolution of specification components of alternative loops. --- Source/Dafny/Resolver.cs | 294 ++++++++++++++++---------------- Test/dafny0/ResolutionErrors.dfy | 215 +++++++++++++++-------- Test/dafny0/ResolutionErrors.dfy.expect | 275 ++++++++++++++--------------- 3 files changed, 430 insertions(+), 354 deletions(-) diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index ec3a69c9..f4fac31b 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -1368,7 +1368,7 @@ namespace Microsoft.Dafny var added = scope.Push(dd.Var.Name, dd.Var); Contract.Assert(added == Scope.PushResult.Success); ResolveType(dd.Var.tok, dd.Var.Type, dd, ResolveTypeOptionEnum.DontInfer, null); - ResolveExpression(dd.Constraint, new ResolveOpts(dd, false, true)); + ResolveExpression(dd.Constraint, new ResolveOpts(dd, false)); Contract.Assert(dd.Constraint.Type != null); // follows from postcondition of ResolveExpression ConstrainTypes(dd.Constraint.Type, Type.Bool, dd.Constraint, "newtype constraint must be of type bool (instead got {0})", dd.Constraint.Type); SolveAllTypeConstraints(); @@ -3129,6 +3129,12 @@ 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 @@ -3219,6 +3225,9 @@ namespace Microsoft.Dafny var gk = AssignStmt.LhsIsToGhost_Which(lhs); if (gk == AssignStmt.NonGhostKind.IsGhost) { s.IsGhost = true; + if (s.Rhs is TypeRhs) { + Error(s.Rhs.Tok, "'new' is not allowed in ghost contexts"); + } } else if (gk == AssignStmt.NonGhostKind.Variable && codeContext.IsGhost) { // cool } else if (mustBeErasable) { @@ -3322,6 +3331,9 @@ namespace Microsoft.Dafny if (s.IsGhost && s.Decreases.Expressions.Exists(e => e is WildcardExpr)) { Error(s, "'decreases *' is not allowed on ghost loops"); } + if (s.IsGhost && s.Mod.Expressions != null) { + s.Mod.Expressions.Iter(resolver.DisallowNonGhostFieldSpecifiers); + } if (s.Body != null) { Visit(s.Body, s.IsGhost); } @@ -3330,6 +3342,12 @@ namespace Microsoft.Dafny } else if (stmt is AlternativeLoopStmt) { var s = (AlternativeLoopStmt)stmt; s.IsGhost = mustBeErasable || s.Alternatives.Exists(alt => resolver.UsesSpecFeatures(alt.Guard)); + if (s.IsGhost && s.Decreases.Expressions.Exists(e => e is WildcardExpr)) { + Error(s, "'decreases *' is not allowed on ghost loops"); + } + if (s.IsGhost && s.Mod.Expressions != null) { + s.Mod.Expressions.Iter(resolver.DisallowNonGhostFieldSpecifiers); + } s.Alternatives.Iter(alt => alt.Body.Iter(ss => Visit(ss, s.IsGhost))); s.IsGhost = s.IsGhost || (!s.Decreases.Expressions.Exists(e => e is WildcardExpr) && s.Alternatives.All(alt => alt.Body.All(ss => ss.IsGhost))); @@ -3343,10 +3361,13 @@ namespace Microsoft.Dafny } else if (stmt is ModifyStmt) { var s = (ModifyStmt)stmt; + s.IsGhost = mustBeErasable; + if (s.IsGhost) { + s.Mod.Expressions.Iter(resolver.DisallowNonGhostFieldSpecifiers); + } if (s.Body != null) { Visit(s.Body, mustBeErasable); } - s.IsGhost = mustBeErasable; } else if (stmt is CalcStmt) { var s = (CalcStmt)stmt; @@ -3999,7 +4020,6 @@ namespace Microsoft.Dafny void ResolveAttributes(Attributes attrs, ResolveOpts opts) { Contract.Requires(opts != null); - Contract.Requires(opts.DontCareAboutCompilation); // attributes are never compiled // order does not matter much for resolution, so resolve them in reverse order foreach (var attr in attrs.AsEnumerable()) { if (attr.Args != null) { @@ -4093,23 +4113,23 @@ namespace Microsoft.Dafny foreach (Formal p in f.Formals) { scope.Push(p.Name, p); } - ResolveAttributes(f.Attributes, new ResolveOpts(f, false, true)); + ResolveAttributes(f.Attributes, new ResolveOpts(f, false)); foreach (Expression r in f.Req) { - ResolveExpression(r, new ResolveOpts(f, false, true)); + ResolveExpression(r, new ResolveOpts(f, false)); Contract.Assert(r.Type != null); // follows from postcondition of ResolveExpression ConstrainTypes(r.Type, Type.Bool, r, "Precondition must be a boolean (got {0})", r.Type); } foreach (FrameExpression fr in f.Reads) { - ResolveFrameExpression(fr, true, f.IsGhost, f); + ResolveFrameExpression(fr, true, f); } foreach (Expression r in f.Ens) { - ResolveExpression(r, new ResolveOpts(f, false, true)); // since this is a function, the postcondition is still a one-state predicate + ResolveExpression(r, new ResolveOpts(f, false)); // since this is a function, the postcondition is still a one-state predicate Contract.Assert(r.Type != null); // follows from postcondition of ResolveExpression ConstrainTypes(r.Type, Type.Bool, r, "Postcondition must be a boolean (got {0})", r.Type); } - ResolveAttributes(f.Decreases.Attributes, new ResolveOpts(f, false, true)); + ResolveAttributes(f.Decreases.Attributes, new ResolveOpts(f, false)); foreach (Expression r in f.Decreases.Expressions) { - ResolveExpression(r, new ResolveOpts(f, false, true)); + ResolveExpression(r, new ResolveOpts(f, false)); // any type is fine } SolveAllTypeConstraints(); @@ -4126,14 +4146,11 @@ namespace Microsoft.Dafny /// /// /// - /// /// True indicates "reads", false indicates "modifies". - /// - /// - void ResolveFrameExpression(FrameExpression fe, bool readsFrame, bool isGhostContext, ICodeContext codeContext) { + void ResolveFrameExpression(FrameExpression fe, bool readsFrame, ICodeContext codeContext) { Contract.Requires(fe != null); Contract.Requires(codeContext != null); - ResolveExpression(fe.E, new ResolveOpts(codeContext, false, true /* yes, this is ghost */)); + ResolveExpression(fe.E, new ResolveOpts(codeContext, false)); Type t = fe.E.Type; Contract.Assert(t != null); // follows from postcondition of ResolveExpression var arrTy = t.AsArrowType; @@ -4153,8 +4170,6 @@ namespace Microsoft.Dafny // error has already been reported by ResolveMember } else if (!(member is Field)) { reporter.Error(MessageSource.Resolver, fe.E, "member {0} in type {1} does not refer to a field", fe.FieldName, ctype.Name); - } else if (!readsFrame && isGhostContext && !member.IsGhost) { - reporter.Error(MessageSource.Resolver, fe.E, "in a ghost context, only ghost fields can be mentioned as modifies frame targets ({0})", fe.FieldName); } else { Contract.Assert(ctype != null && ctype.ResolvedClass != null); // follows from postcondition of ResolveMember fe.Field = (Field)member; @@ -4162,6 +4177,17 @@ namespace Microsoft.Dafny } } + /// + /// This method can be called even if the resolution of "fe" failed; in that case, this method will + /// not issue any error message. + /// + void DisallowNonGhostFieldSpecifiers(FrameExpression fe) { + Contract.Requires(fe != null); + if (fe.Field != null && !fe.Field.IsGhost) { + reporter.Error(MessageSource.Resolver, fe.E, "in a ghost context, only ghost fields can be mentioned as modifies frame targets ({0})", fe.FieldName); + } + } + /// /// Assumes type parameters have already been pushed /// @@ -4206,25 +4232,24 @@ namespace Microsoft.Dafny // Start resolving specification... foreach (MaybeFreeExpression e in m.Req) { - ResolveAttributes(e.Attributes, new ResolveOpts(m, false, true)); - ResolveExpression(e.E, new ResolveOpts(m, false, true)); + ResolveAttributes(e.Attributes, new ResolveOpts(m, false)); + ResolveExpression(e.E, new ResolveOpts(m, false)); Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression ConstrainTypes(e.E.Type, Type.Bool, e.E, "Precondition must be a boolean (got {0})", e.E.Type); } - ResolveAttributes(m.Mod.Attributes, new ResolveOpts(m, false, true)); + ResolveAttributes(m.Mod.Attributes, new ResolveOpts(m, false)); foreach (FrameExpression fe in m.Mod.Expressions) { - ResolveFrameExpression(fe, false, m.IsGhost, m); + ResolveFrameExpression(fe, false, m); if (m is Lemma || m is FixpointLemma) { reporter.Error(MessageSource.Resolver, fe.tok, "{0}s are not allowed to have modifies clauses", m.WhatKind); + } else if (m.IsGhost) { + DisallowNonGhostFieldSpecifiers(fe); } } - ResolveAttributes(m.Decreases.Attributes, new ResolveOpts(m, false, true)); + ResolveAttributes(m.Decreases.Attributes, new ResolveOpts(m, false)); foreach (Expression e in m.Decreases.Expressions) { - ResolveExpression(e, new ResolveOpts(m, false, true)); + ResolveExpression(e, new ResolveOpts(m, false)); // any type is fine - if (m.IsGhost && e is WildcardExpr) { - reporter.Error(MessageSource.Resolver, e, "'decreases *' is not allowed on ghost methods"); - } } // Add out-parameters to a new scope that will also include the outermost-level locals of the body @@ -4240,8 +4265,8 @@ namespace Microsoft.Dafny // ... continue resolving specification foreach (MaybeFreeExpression e in m.Ens) { - ResolveAttributes(e.Attributes, new ResolveOpts(m, true, true)); - ResolveExpression(e.E, new ResolveOpts(m, true, true)); + ResolveAttributes(e.Attributes, new ResolveOpts(m, true)); + ResolveExpression(e.E, new ResolveOpts(m, true)); Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression ConstrainTypes(e.E.Type, Type.Bool, e.E, "Postcondition must be a boolean (got {0})", e.E.Type); } @@ -4265,7 +4290,7 @@ namespace Microsoft.Dafny } // attributes are allowed to mention both in- and out-parameters (including the implicit _k, for colemmas) - ResolveAttributes(m.Attributes, new ResolveOpts(m, false, true)); + ResolveAttributes(m.Attributes, new ResolveOpts(m, false)); scope.PopMarker(); // for the out-parameters and outermost-level locals scope.PopMarker(); // for the in-parameters @@ -4331,20 +4356,20 @@ namespace Microsoft.Dafny Contract.Assert(iter.Decreases.Expressions.Count == iter.DecreasesFields.Count); for (int i = 0; i < iter.Decreases.Expressions.Count; i++) { var e = iter.Decreases.Expressions[i]; - ResolveExpression(e, new ResolveOpts(iter, false, true)); + ResolveExpression(e, new ResolveOpts(iter, false)); // any type is fine, but associate this type with the corresponding _decreases field var d = iter.DecreasesFields[i]; // If the following type constraint does not hold, then: Bummer, there was a use--and a bad use--of the field before, so this won't be the best of error messages ConstrainTypes(d.Type, e.Type, e, "type of field {0} is {1}, but has been constrained elsewhere to be of type {2}", d.Name, e.Type, d.Type); } foreach (FrameExpression fe in iter.Reads.Expressions) { - ResolveFrameExpression(fe, true, false, iter); + ResolveFrameExpression(fe, true, iter); } foreach (FrameExpression fe in iter.Modifies.Expressions) { - ResolveFrameExpression(fe, false, false, iter); + ResolveFrameExpression(fe, false, iter); } foreach (MaybeFreeExpression e in iter.Requires) { - ResolveExpression(e.E, new ResolveOpts(iter, false, true)); + ResolveExpression(e.E, new ResolveOpts(iter, false)); Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression ConstrainTypes(e.E.Type, Type.Bool, e.E, "Precondition must be a boolean (got {0})", e.E.Type); } @@ -4358,22 +4383,22 @@ namespace Microsoft.Dafny Contract.Assert(scope.AllowInstance); foreach (MaybeFreeExpression e in iter.YieldRequires) { - ResolveExpression(e.E, new ResolveOpts(iter, false, true)); + ResolveExpression(e.E, new ResolveOpts(iter, false)); Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression ConstrainTypes(e.E.Type, Type.Bool, e.E, "Yield precondition must be a boolean (got {0})", e.E.Type); } foreach (MaybeFreeExpression e in iter.YieldEnsures) { - ResolveExpression(e.E, new ResolveOpts(iter, true, true)); + ResolveExpression(e.E, new ResolveOpts(iter, true)); Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression ConstrainTypes(e.E.Type, Type.Bool, e.E, "Yield postcondition must be a boolean (got {0})", e.E.Type); } foreach (MaybeFreeExpression e in iter.Ensures) { - ResolveExpression(e.E, new ResolveOpts(iter, true, true)); + ResolveExpression(e.E, new ResolveOpts(iter, true)); Contract.Assert(e.E.Type != null); // follows from postcondition of ResolveExpression ConstrainTypes(e.E.Type, Type.Bool, e.E, "Postcondition must be a boolean (got {0})", e.E.Type); } - ResolveAttributes(iter.Attributes, new ResolveOpts(iter, false, true)); + ResolveAttributes(iter.Attributes, new ResolveOpts(iter, false)); var postSpecErrorCount = reporter.Count(ErrorLevel.Error); @@ -4687,13 +4712,13 @@ namespace Microsoft.Dafny } var prevErrorCount = reporter.Count(ErrorLevel.Error); if (t.NamePath is ExprDotName) { - var ret = ResolveDotSuffix_Type((ExprDotName)t.NamePath, new ResolveOpts(context, true, true), allowDanglingDotName, option, defaultTypeArguments); + var ret = ResolveDotSuffix_Type((ExprDotName)t.NamePath, new ResolveOpts(context, true), allowDanglingDotName, option, defaultTypeArguments); if (ret != null) { return ret; } } else { var s = (NameSegment)t.NamePath; - ResolveNameSegment_Type(s, new ResolveOpts(context, true, true), option, defaultTypeArguments); + ResolveNameSegment_Type(s, new ResolveOpts(context, true), option, defaultTypeArguments); } if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { var r = t.NamePath.Resolved as Resolver_IdentifierExpr; @@ -5225,17 +5250,17 @@ namespace Microsoft.Dafny Contract.Requires(stmt != null); Contract.Requires(codeContext != null); if (!(stmt is ForallStmt)) { // forall statements do their own attribute resolution below - ResolveAttributes(stmt.Attributes, new ResolveOpts(codeContext, true, true)); + ResolveAttributes(stmt.Attributes, new ResolveOpts(codeContext, true)); } if (stmt is PredicateStmt) { PredicateStmt s = (PredicateStmt)stmt; - ResolveExpression(s.Expr, new ResolveOpts(codeContext, true, true)); + ResolveExpression(s.Expr, new ResolveOpts(codeContext, true)); Contract.Assert(s.Expr.Type != null); // follows from postcondition of ResolveExpression ConstrainTypes(s.Expr.Type, Type.Bool, s.Expr, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Expr.Type); } else if (stmt is PrintStmt) { var s = (PrintStmt)stmt; - var opts = new ResolveOpts(codeContext, false, specContextOnly); + var opts = new ResolveOpts(codeContext, false); s.Args.Iter(e => ResolveExpression(e, opts)); } else if (stmt is BreakStmt) { @@ -5346,7 +5371,7 @@ namespace Microsoft.Dafny } // With the new locals in scope, it's now time to resolve the attributes on all the locals foreach (var local in s.Locals) { - ResolveAttributes(local.Attributes, new ResolveOpts(codeContext, true, true)); + ResolveAttributes(local.Attributes, new ResolveOpts(codeContext, true)); } // Resolve the AssignSuchThatStmt, if any if (s.Update is AssignSuchThatStmt) { @@ -5374,18 +5399,16 @@ namespace Microsoft.Dafny } else if (stmt is AssignStmt) { AssignStmt s = (AssignStmt)stmt; int prevErrorCount = reporter.Count(ErrorLevel.Error); - ResolveExpression(s.Lhs, new ResolveOpts(codeContext, true, specContextOnly)); // allow ghosts for now, tighted up below + ResolveExpression(s.Lhs, new ResolveOpts(codeContext, true)); // allow ghosts for now, tighted up below bool lhsResolvedSuccessfully = reporter.Count(ErrorLevel.Error) == prevErrorCount; Contract.Assert(s.Lhs.Type != null); // follows from postcondition of ResolveExpression // check that LHS denotes a mutable variable or a field - bool lvalueIsGhost = false; var lhs = s.Lhs.Resolved; if (lhs is IdentifierExpr) { IVariable var = ((IdentifierExpr)lhs).Var; if (var == null) { // the LHS didn't resolve correctly; some error would already have been reported } else { - lvalueIsGhost = var.IsGhost || codeContext.IsGhost; CheckIsLvalue(lhs, codeContext); var localVar = var as LocalVariable; @@ -5411,7 +5434,6 @@ namespace Microsoft.Dafny } else if (lhs is MemberSelectExpr) { var fse = (MemberSelectExpr)lhs; if (fse.Member != null) { // otherwise, an error was reported above - lvalueIsGhost = fse.Member.IsGhost; CheckIsLvalue(fse, codeContext); } } else if (lhs is SeqSelectExpr) { @@ -5432,12 +5454,12 @@ namespace Microsoft.Dafny Type lhsType = s.Lhs.Type; if (s.Rhs is ExprRhs) { ExprRhs rr = (ExprRhs)s.Rhs; - ResolveExpression(rr.Expr, new ResolveOpts(codeContext, true, specContextOnly)); + ResolveExpression(rr.Expr, new ResolveOpts(codeContext, true)); Contract.Assert(rr.Expr.Type != null); // follows from postcondition of ResolveExpression ConstrainTypes(lhsType, rr.Expr.Type, stmt, "RHS (of type {0}) not assignable to LHS (of type {1})", rr.Expr.Type, lhsType); } else if (s.Rhs is TypeRhs) { TypeRhs rr = (TypeRhs)s.Rhs; - Type t = ResolveTypeRhs(rr, stmt, lvalueIsGhost, codeContext); + Type t = ResolveTypeRhs(rr, stmt, codeContext); ConstrainTypes(lhsType, t, stmt, "type {0} is not assignable to LHS (of type {1})", t, lhsType); } else if (s.Rhs is HavocRhs) { // nothing else to do @@ -5447,7 +5469,7 @@ namespace Microsoft.Dafny } else if (stmt is CallStmt) { CallStmt s = (CallStmt)stmt; - ResolveCallStmt(s, specContextOnly, codeContext, null); + ResolveCallStmt(s, codeContext, null); } else if (stmt is BlockStmt) { var s = (BlockStmt)stmt; @@ -5460,7 +5482,7 @@ namespace Microsoft.Dafny bool branchesAreSpecOnly = specContextOnly; if (s.Guard != null) { int prevErrorCount = reporter.Count(ErrorLevel.Error); - ResolveExpression(s.Guard, new ResolveOpts(codeContext, true, specContextOnly)); + ResolveExpression(s.Guard, new ResolveOpts(codeContext, true)); Contract.Assert(s.Guard.Type != null); // follows from postcondition of ResolveExpression bool successfullyResolved = reporter.Count(ErrorLevel.Error) == prevErrorCount; ConstrainTypes(s.Guard.Type, Type.Bool, s.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Guard.Type); @@ -5483,7 +5505,7 @@ namespace Microsoft.Dafny var fvs = new HashSet(); if (s.Guard != null) { int prevErrorCount = reporter.Count(ErrorLevel.Error); - ResolveExpression(s.Guard, new ResolveOpts(codeContext, true, specContextOnly)); + ResolveExpression(s.Guard, new ResolveOpts(codeContext, true)); Contract.Assert(s.Guard.Type != null); // follows from postcondition of ResolveExpression bool successfullyResolved = reporter.Count(ErrorLevel.Error) == prevErrorCount; Translator.ComputeFreeVariables(s.Guard, fvs); @@ -5493,34 +5515,8 @@ namespace Microsoft.Dafny } } - foreach (MaybeFreeExpression inv in s.Invariants) { - ResolveAttributes(inv.Attributes, new ResolveOpts(codeContext, true, true)); - ResolveExpression(inv.E, new ResolveOpts(codeContext, true, true)); - Contract.Assert(inv.E.Type != null); // follows from postcondition of ResolveExpression - Translator.ComputeFreeVariables(inv.E, fvs); - ConstrainTypes(inv.E.Type, Type.Bool, inv.E, "invariant is expected to be of type {0}, but is {1}", Type.Bool, inv.E.Type); - } + ResolveLoopSpecificationComponents(s.Invariants, s.Decreases, s.Mod, codeContext, fvs); - ResolveAttributes(s.Decreases.Attributes, new ResolveOpts(codeContext, true, true)); - foreach (Expression e in s.Decreases.Expressions) { - ResolveExpression(e, new ResolveOpts(codeContext, true, true)); - if (e is WildcardExpr) { - if (bodyMustBeSpecOnly) { - reporter.Error(MessageSource.Resolver, e, "'decreases *' is not allowed on ghost loops"); - } else if (!codeContext.AllowsNontermination && !DafnyOptions.O.Dafnycc) { - reporter.Error(MessageSource.Resolver, e, "a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating"); - } - } - // any type is fine - } - - if (s.Mod.Expressions != null) { - ResolveAttributes(s.Mod.Attributes, new ResolveOpts(codeContext, true, true)); - foreach (FrameExpression fe in s.Mod.Expressions) { - ResolveFrameExpression(fe, false, bodyMustBeSpecOnly, codeContext); - Translator.ComputeFreeVariables(fe.E, fvs); - } - } if (s.Body != null) { loopStack.Add(s); // push if (s.Labels == null) { // otherwise, "s" is already in "inSpecOnlyContext" map @@ -5537,21 +5533,7 @@ namespace Microsoft.Dafny } else if (stmt is AlternativeLoopStmt) { var s = (AlternativeLoopStmt)stmt; ResolveAlternatives(s.Alternatives, specContextOnly, s, codeContext); - foreach (MaybeFreeExpression inv in s.Invariants) { - ResolveExpression(inv.E, new ResolveOpts(codeContext, true, true)); - Contract.Assert(inv.E.Type != null); // follows from postcondition of ResolveExpression - ConstrainTypes(inv.E.Type, Type.Bool, inv.E, "invariant is expected to be of type {0}, but is {1}", Type.Bool, inv.E.Type); - } - - foreach (Expression e in s.Decreases.Expressions) { - ResolveExpression(e, new ResolveOpts(codeContext, true, true)); - if (e is WildcardExpr) { - if (!codeContext.AllowsNontermination && !DafnyOptions.O.Dafnycc) { - reporter.Error(MessageSource.Resolver, e, "a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating"); - } - } - // any type is fine - } + ResolveLoopSpecificationComponents(s.Invariants, s.Decreases, s.Mod, codeContext, null); } else if (stmt is ForallStmt) { var s = (ForallStmt)stmt; @@ -5562,17 +5544,17 @@ namespace Microsoft.Dafny ScopePushAndReport(scope, v, "local-variable"); ResolveType(v.tok, v.Type, codeContext, ResolveTypeOptionEnum.InferTypeProxies, null); } - ResolveExpression(s.Range, new ResolveOpts(codeContext, true, specContextOnly)); + ResolveExpression(s.Range, new ResolveOpts(codeContext, true)); Contract.Assert(s.Range.Type != null); // follows from postcondition of ResolveExpression ConstrainTypes(s.Range.Type, Type.Bool, stmt, "range restriction in forall statement must be of type bool (instead got {0})", s.Range.Type); foreach (var ens in s.Ens) { - ResolveExpression(ens.E, new ResolveOpts(codeContext, true, true)); + ResolveExpression(ens.E, new ResolveOpts(codeContext, true)); Contract.Assert(ens.E.Type != null); // follows from postcondition of ResolveExpression ConstrainTypes(ens.E.Type, Type.Bool, ens.E, "ensures condition is expected to be of type {0}, but is {1}", Type.Bool, ens.E.Type); } // Since the range and postconditions are more likely to infer the types of the bound variables, resolve them // first (above) and only then resolve the attributes (below). - ResolveAttributes(s.Attributes, new ResolveOpts(codeContext, true, true)); + ResolveAttributes(s.Attributes, new ResolveOpts(codeContext, true)); bool bodyMustBeSpecOnly = specContextOnly || (prevErrorCount == reporter.Count(ErrorLevel.Error) && UsesSpecFeatures(s.Range)); if (!bodyMustBeSpecOnly && prevErrorCount == reporter.Count(ErrorLevel.Error)) { @@ -5637,10 +5619,9 @@ namespace Microsoft.Dafny } else if (stmt is ModifyStmt) { var s = (ModifyStmt)stmt; - ResolveAttributes(s.Mod.Attributes, new ResolveOpts(codeContext, true, true)); + ResolveAttributes(s.Mod.Attributes, new ResolveOpts(codeContext, true)); foreach (FrameExpression fe in s.Mod.Expressions) { - // (yes, say "modifies", not "modify", in the next line -- it seems to give a more readable error message - ResolveFrameExpression(fe, false, specContextOnly, codeContext); + ResolveFrameExpression(fe, false, codeContext); } if (s.Body != null) { ResolveBlockStatement(s.Body, specContextOnly, codeContext); @@ -5651,17 +5632,17 @@ namespace Microsoft.Dafny CalcStmt s = (CalcStmt)stmt; if (s.Lines.Count > 0) { var e0 = s.Lines.First(); - ResolveExpression(e0, new ResolveOpts(codeContext, true, true)); + ResolveExpression(e0, new ResolveOpts(codeContext, true)); Contract.Assert(e0.Type != null); // follows from postcondition of ResolveExpression for (int i = 1; i < s.Lines.Count; i++) { if (i < s.Lines.Count - 1 || prevErrorCount == reporter.Count(ErrorLevel.Error)) { // do not resolve the dummy step if there were errors, it might generate more errors var e1 = s.Lines[i]; - ResolveExpression(e1, new ResolveOpts(codeContext, true, true)); + ResolveExpression(e1, new ResolveOpts(codeContext, true)); Contract.Assert(e1.Type != null); // follows from postcondition of ResolveExpression if (!ConstrainTypes(e0.Type, e1.Type, e1, "all lines in a calculation must have the same type (got {0} after {1})", e1.Type, e0.Type)) { } else { var step = s.StepOps[i - 1].StepExpr(e0, e1); // Use custom line operator - ResolveExpression(step, new ResolveOpts(codeContext, true, true)); + ResolveExpression(step, new ResolveOpts(codeContext, true)); s.Steps.Add(step); } e0 = e1; @@ -5686,7 +5667,7 @@ namespace Microsoft.Dafny } else { s.Result = CalcStmt.DefaultOp.StepExpr(Expression.CreateIntLiteral(s.Tok, 0), Expression.CreateIntLiteral(s.Tok, 0)); } - ResolveExpression(s.Result, new ResolveOpts(codeContext, true, true)); + ResolveExpression(s.Result, new ResolveOpts(codeContext, true)); Contract.Assert(s.Result != null); Contract.Assert(prevErrorCount != reporter.Count(ErrorLevel.Error) || s.Steps.Count == s.Hints.Count); @@ -5704,13 +5685,51 @@ namespace Microsoft.Dafny } } + private void ResolveLoopSpecificationComponents(List invariants, Specification decreases, Specification modifies, ICodeContext codeContext, HashSet fvs) { + Contract.Requires(invariants != null); + Contract.Requires(decreases != null); + Contract.Requires(modifies != null); + Contract.Requires(codeContext != null); + + foreach (MaybeFreeExpression inv in invariants) { + ResolveAttributes(inv.Attributes, new ResolveOpts(codeContext, true)); + ResolveExpression(inv.E, new ResolveOpts(codeContext, true)); + Contract.Assert(inv.E.Type != null); // follows from postcondition of ResolveExpression + if (fvs != null) { + Translator.ComputeFreeVariables(inv.E, fvs); + } + ConstrainTypes(inv.E.Type, Type.Bool, inv.E, "invariant is expected to be of type {0}, but is {1}", Type.Bool, inv.E.Type); + } + + ResolveAttributes(decreases.Attributes, new ResolveOpts(codeContext, true)); + foreach (Expression e in decreases.Expressions) { + ResolveExpression(e, new ResolveOpts(codeContext, true)); + if (e is WildcardExpr) { + if (!codeContext.AllowsNontermination && !DafnyOptions.O.Dafnycc) { + reporter.Error(MessageSource.Resolver, e, "a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating"); + } + } + // any type is fine + } + + ResolveAttributes(modifies.Attributes, new ResolveOpts(codeContext, true)); + if (modifies.Expressions != null) { + foreach (FrameExpression fe in modifies.Expressions) { + ResolveFrameExpression(fe, false, codeContext); + if (fvs != null) { + Translator.ComputeFreeVariables(fe.E, fvs); + } + } + } + } + void ResolveMatchStmt(Statement stmt, bool specContextOnly, ICodeContext codeContext) { MatchStmt s = (MatchStmt)stmt; DesugarMatchStmtWithTupleExpression(s); bool bodyIsSpecOnly = specContextOnly; int prevErrorCount = reporter.Count(ErrorLevel.Error); - ResolveExpression(s.Source, new ResolveOpts(codeContext, true, specContextOnly)); + ResolveExpression(s.Source, new ResolveOpts(codeContext, true)); Contract.Assert(s.Source.Type != null); // follows from postcondition of ResolveExpression bool successfullyResolved = reporter.Count(ErrorLevel.Error) == prevErrorCount; if (!specContextOnly && successfullyResolved) { @@ -6230,7 +6249,7 @@ namespace Microsoft.Dafny var lhsNameSet = new HashSet(); // used to check for duplicate identifiers on the left (full duplication checking for references and the like is done during verification) foreach (var lhs in s.Lhss) { var ec = reporter.Count(ErrorLevel.Error); - ResolveExpression(lhs, new ResolveOpts(codeContext, true, specContextOnly)); + ResolveExpression(lhs, new ResolveOpts(codeContext, true)); if (ec == reporter.Count(ErrorLevel.Error)) { if (lhs is SeqSelectExpr && !((SeqSelectExpr)lhs).SelectOne) { reporter.Error(MessageSource.Resolver, lhs, "cannot assign to a range of array elements (try the 'forall' statement)"); @@ -6241,11 +6260,11 @@ namespace Microsoft.Dafny // Resolve RHSs if (update == null) { var suchThat = (AssignSuchThatStmt)s; // this is the other possible subclass - ResolveAssignSuchThatStmt(suchThat, specContextOnly, codeContext); + ResolveAssignSuchThatStmt(suchThat, codeContext); } else { ResolveUpdateStmt(update, specContextOnly, codeContext, errorCountBeforeCheckingLhs); } - ResolveAttributes(s.Attributes, new ResolveOpts(codeContext, true, true)); + ResolveAttributes(s.Attributes, new ResolveOpts(codeContext, true)); } /// /// Resolve the RHSs and entire UpdateStmt (LHSs should already have been checked by the caller). @@ -6262,7 +6281,7 @@ namespace Microsoft.Dafny bool isEffectful; if (rhs is TypeRhs) { var tr = (TypeRhs)rhs; - ResolveTypeRhs(tr, update, specContextOnly, codeContext); + ResolveTypeRhs(tr, update, codeContext); isEffectful = tr.InitCall != null; } else if (rhs is HavocRhs) { isEffectful = false; @@ -6270,17 +6289,11 @@ namespace Microsoft.Dafny var er = (ExprRhs)rhs; if (er.Expr is ApplySuffix) { var a = (ApplySuffix)er.Expr; - // Note, in the following line, the dontCareAboutCompilation could be more precise. It could be computed as in the else - // branch if the ApplySuffix is really just the RHS of an assignment. However, if "update" is really a call statement, - // then we should not let the LHS influence the call to ResolveApplySuffix. Unfortunately, we don't know which case - // we're in until ResolveApplySuffix has returned (where a non-null cRhs indicates that "update" is a call statement). - // So, we'll be conservative and will simply pass in specContextOnly here. - var cRhs = ResolveApplySuffix(a, new ResolveOpts(codeContext, true, specContextOnly/*see note on previous line*/), true); + var cRhs = ResolveApplySuffix(a, new ResolveOpts(codeContext, true), true); isEffectful = cRhs != null; methodCallInfo = methodCallInfo ?? cRhs; } else { - var dontCareAboutCompilation = specContextOnly || (j < update.Lhss.Count && AssignStmt.LhsIsToGhost(update.Lhss[j])); - ResolveExpression(er.Expr, new ResolveOpts(codeContext, true, dontCareAboutCompilation)); + ResolveExpression(er.Expr, new ResolveOpts(codeContext, true)); isEffectful = false; } } @@ -6355,7 +6368,7 @@ namespace Microsoft.Dafny } } - private void ResolveAssignSuchThatStmt(AssignSuchThatStmt s, bool specContextOnly, ICodeContext codeContext) { + private void ResolveAssignSuchThatStmt(AssignSuchThatStmt s, ICodeContext codeContext) { Contract.Requires(s != null); Contract.Requires(codeContext != null); @@ -6373,7 +6386,7 @@ namespace Microsoft.Dafny } var ec = reporter.Count(ErrorLevel.Error); - ResolveExpression(s.Expr, new ResolveOpts(codeContext, true, specContextOnly)); + ResolveExpression(s.Expr, new ResolveOpts(codeContext, true)); ConstrainTypes(s.Expr.Type, Type.Bool, s.Expr, "type of RHS of assign-such-that statement must be boolean (got {0})", s.Expr.Type); } @@ -6384,7 +6397,7 @@ namespace Microsoft.Dafny // first, resolve the guards, which tells us whether or not the entire statement is a ghost statement foreach (var alternative in alternatives) { int prevErrorCount = reporter.Count(ErrorLevel.Error); - ResolveExpression(alternative.Guard, new ResolveOpts(codeContext, true, specContextOnly)); + ResolveExpression(alternative.Guard, new ResolveOpts(codeContext, true)); Contract.Assert(alternative.Guard.Type != null); // follows from postcondition of ResolveExpression bool successfullyResolved = reporter.Count(ErrorLevel.Error) == prevErrorCount; ConstrainTypes(alternative.Guard.Type, Type.Bool, alternative.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, alternative.Guard.Type); @@ -6412,7 +6425,7 @@ namespace Microsoft.Dafny /// Resolves the given call statement. /// Assumes all LHSs have already been resolved (and checked for mutability). /// - void ResolveCallStmt(CallStmt s, bool specContextOnly, ICodeContext codeContext, Type receiverType) { + void ResolveCallStmt(CallStmt s, ICodeContext codeContext, Type receiverType) { Contract.Requires(s != null); Contract.Requires(codeContext != null); bool isInitCall = receiverType != null; @@ -6430,8 +6443,7 @@ namespace Microsoft.Dafny // resolve arguments int j = 0; foreach (Expression e in s.Args) { - bool allowGhost = callee.Ins.Count <= j || callee.Ins[j].IsGhost; - ResolveExpression(e, new ResolveOpts(codeContext, true, allowGhost)); + ResolveExpression(e, new ResolveOpts(codeContext, true)); j++; } @@ -6827,16 +6839,12 @@ namespace Microsoft.Dafny } } - Type ResolveTypeRhs(TypeRhs rr, Statement stmt, bool specContextOnly, ICodeContext codeContext) { + Type ResolveTypeRhs(TypeRhs rr, Statement stmt, ICodeContext codeContext) { Contract.Requires(rr != null); Contract.Requires(stmt != null); Contract.Requires(codeContext != null); Contract.Ensures(Contract.Result() != null); - // "new" is not allowed in ghost contexts - if (specContextOnly) { - reporter.Error(MessageSource.Resolver, rr.Tok, "'new' is not allowed in ghost contexts"); - } if (rr.Type == null) { if (rr.ArrayDimensions != null) { // ---------- new T[EE] @@ -6888,13 +6896,13 @@ namespace Microsoft.Dafny // type, create an dot-suffix expression around this receiver, and then resolve it in the usual way for dot-suffix expressions. var lhs = new ImplicitThisExpr(initCallTok) { Type = rr.EType }; var callLhs = new ExprDotName(initCallTok, lhs, initCallName, ret == null ? null : ret.LastComponent.OptTypeArguments); - ResolveDotSuffix(callLhs, true, rr.Arguments, new ResolveOpts(codeContext, true, specContextOnly), true); + ResolveDotSuffix(callLhs, true, rr.Arguments, new ResolveOpts(codeContext, true), true); if (prevErrorCount == reporter.Count(ErrorLevel.Error)) { Contract.Assert(callLhs.ResolvedExpression is MemberSelectExpr); // since ResolveApplySuffix succeeded and call.Lhs denotes an expression (not a module or a type) var methodSel = (MemberSelectExpr)callLhs.ResolvedExpression; if (methodSel.Member is Method) { rr.InitCall = new CallStmt(initCallTok, stmt.EndTok, new List(), methodSel, rr.Arguments); - ResolveCallStmt(rr.InitCall, specContextOnly, codeContext, rr.EType); + ResolveCallStmt(rr.InitCall, codeContext, rr.EType); if (rr.InitCall.Method is Constructor) { callsConstructor = true; } @@ -7170,24 +7178,10 @@ namespace Microsoft.Dafny { public readonly ICodeContext codeContext; public readonly bool twoState; - public readonly bool DontCareAboutCompilation; public ResolveOpts(ICodeContext codeContext, bool twoState) { Contract.Requires(codeContext != null); this.codeContext = codeContext; this.twoState = twoState; - DontCareAboutCompilation = codeContext.IsGhost; - } - public ResolveOpts(ICodeContext codeContext, bool twoState, bool dontCareAboutCompilation) { - Contract.Requires(codeContext != null); - this.codeContext = codeContext; - this.twoState = twoState; - this.DontCareAboutCompilation = dontCareAboutCompilation; - } - public ResolveOpts(ResolveOpts r, bool dontCareAboutCompilation) { - Contract.Requires(r != null); - this.codeContext = r.codeContext; - this.twoState = r.twoState; - this.DontCareAboutCompilation = dontCareAboutCompilation; } } @@ -7846,7 +7840,7 @@ namespace Microsoft.Dafny } } ResolveExpression(e.Body, opts); - ResolveAttributes(e.Attributes, new ResolveOpts(opts, true)); + ResolveAttributes(e.Attributes, opts); scope.PopMarker(); expr.Type = e.Body.Type; @@ -7873,16 +7867,16 @@ namespace Microsoft.Dafny reporter.Error(MessageSource.Resolver, expr, "a quantifier cannot quantify over types. Possible fix: use the experimental attribute :typeQuantifier"); } if (e.Range != null) { - ResolveExpression(e.Range, new ResolveOpts(opts, true)); + ResolveExpression(e.Range, opts); Contract.Assert(e.Range.Type != null); // follows from postcondition of ResolveExpression ConstrainTypes(e.Range.Type, Type.Bool, expr, "range of quantifier must be of type bool (instead got {0})", e.Range.Type); } - ResolveExpression(e.Term, new ResolveOpts(opts, true)); + ResolveExpression(e.Term, opts); Contract.Assert(e.Term.Type != null); // follows from postcondition of ResolveExpression ConstrainTypes(e.Term.Type, Type.Bool, expr, "body of quantifier must be of type bool (instead got {0})", e.Term.Type); // Since the body is more likely to infer the types of the bound variables, resolve it // first (above) and only then resolve the attributes (below). - ResolveAttributes(e.Attributes, new ResolveOpts(opts, true)); + ResolveAttributes(e.Attributes, opts); scope.PopMarker(); allTypeParameters.PopMarker(); expr.Type = Type.Bool; @@ -7920,7 +7914,7 @@ namespace Microsoft.Dafny ResolveExpression(e.Term, opts); Contract.Assert(e.Term.Type != null); // follows from postcondition of ResolveExpression - ResolveAttributes(e.Attributes, new ResolveOpts(opts, true)); + ResolveAttributes(e.Attributes, opts); scope.PopMarker(); expr.Type = new SetType(e.Finite, e.Term.Type); @@ -7941,7 +7935,7 @@ namespace Microsoft.Dafny ResolveExpression(e.Term, opts); Contract.Assert(e.Term.Type != null); // follows from postcondition of ResolveExpression - ResolveAttributes(e.Attributes, new ResolveOpts(opts, true)); + ResolveAttributes(e.Attributes, opts); scope.PopMarker(); expr.Type = new MapType(e.Finite, e.BoundVars[0].Type, e.Term.Type); @@ -7974,7 +7968,7 @@ namespace Microsoft.Dafny } foreach (var read in e.Reads) { - ResolveFrameExpression(read, true, false, opts.codeContext); + ResolveFrameExpression(read, true, opts.codeContext); } ResolveExpression(e.Term, opts); diff --git a/Test/dafny0/ResolutionErrors.dfy b/Test/dafny0/ResolutionErrors.dfy index 9d4d67f1..49e6efa0 100644 --- a/Test/dafny0/ResolutionErrors.dfy +++ b/Test/dafny0/ResolutionErrors.dfy @@ -9,9 +9,9 @@ method GhostDivergentLoop() a[1] := -1; ghost var i := 0; while (i < 2) - decreases *; // error: not allowed on a ghost loop - invariant i <= 2; - invariant (forall j :: 0 <= j && j < i ==> a[j] > 0); + decreases * // error: not allowed on a ghost loop + invariant i <= 2 + invariant (forall j :: 0 <= j && j < i ==> a[j] > 0) { i := 0; } @@ -613,20 +613,7 @@ module GhostAllocationTests { p := new G; // error: ditto } - method GhostNew1(n: nat) - { - var a := new G[n]; - forall i | 0 <= i < n { - a[i] := new G; // error: 'new' is currently not supported in forall statements - } - forall i | 0 <= i < n - ensures true; // this makes the whole 'forall' statement into a ghost statement - { - a[i] := new G; // error: 'new' not allowed in ghost contexts, and proof-forall cannot update state - } - } - - method GhostNew2(n: nat, ghost g: int) returns (t: G, z: int) + method GhostNew1(n: nat, ghost g: int) returns (t: G, z: int) { if n < 0 { z, t := 5, new G; // fine @@ -636,14 +623,14 @@ module GhostAllocationTests { } } - method GhostNew3(ghost b: bool) + method GhostNew2(ghost b: bool) { if (b) { var y := new GIter(); // error: 'new' not allowed in ghost contexts (and a non-ghost method is not allowed to be called here either) } } - method GhostNew4(n: nat) + method GhostNew3(n: nat) { var g := new G; calc { @@ -653,12 +640,28 @@ module GhostAllocationTests { } } - ghost method GhostNew5(g: G) + ghost method GhostNew4(g: G) modifies g; { } } +module NewForall { + class G { } + method NewForallTest(n: nat) + { + var a := new G[n]; + forall i | 0 <= i < n { + a[i] := new G; // error: 'new' is currently not supported in forall statements + } + forall i | 0 <= i < n + ensures true; // this makes the whole 'forall' statement into a ghost statement + { + a[i] := new G; // error: 'new' not allowed in ghost contexts, and proof-forall cannot update state + } + } +} + // ------------------------- underspecified types ------------------------------ module UnderspecifiedTypes { @@ -706,7 +709,7 @@ module StatementsInExpressions { { assert 6 < 8; } { var x := 8; while x != 0 - decreases *; // error: cannot use 'decreases *' in a ghost context + decreases * // error: cannot use 'decreases *' here { x := x - 1; } @@ -739,7 +742,7 @@ module StatementsInExpressions { { assert 6 < 8; } { var x := 8; while x != 0 - decreases *; // error: cannot use 'decreases *' in a ghost context + decreases * // error: cannot use 'decreases *' here { x := x - 1; } @@ -852,40 +855,48 @@ class ModifyStatementClass { ghost method G0() modifies `g; modifies `x; // error: non-ghost field mentioned in ghost context - { - modify `g; - modify `x; // error: non-ghost field mentioned in ghost context - } - method G1() - modifies this; - { - modify `x; - if g < 100 { - // we are now in a ghost context +} +module ModifyStatementClass_More { + class C { + var x: int; + ghost var g: int; + ghost method G0() + modifies `g; + { + modify `g; modify `x; // error: non-ghost field mentioned in ghost context } - } - method G2(y: nat) - modifies this; - { - if g < 100 { - // we're now in a ghost context - var n := 0; - while n < y - modifies `x; // error: non-ghost field mentioned in ghost context - { - if * { - g := g + 1; // if we got as far as verification, this would be flagged as an error too - } - n := n + 1; + method G1() + modifies this; + { + modify `x; + if g < 100 { + // we are now in a ghost context + modify `x; // error: non-ghost field mentioned in ghost context } } - modify `x; // fine - ghost var i := 0; - while i < y - modifies `x; // error: non-ghost field mentioned in ghost context + method G2(y: nat) + modifies this; { - i := i + 1; + if g < 100 { + // we're now in a ghost context + var n := 0; + while n < y + modifies `x; // error: non-ghost field mentioned in ghost context + { + if * { + g := g + 1; // if we got as far as verification, this would be flagged as an error too + } + n := n + 1; + } + } + modify `x; // fine + ghost var i := 0; + while i < y + modifies `x; // error: non-ghost field mentioned in ghost context + { + i := i + 1; + } } } } @@ -1298,7 +1309,7 @@ module FrameTargetFields { modifies `z // cool { } - +} } module FrameTargetFields_More { class C { var x: int var y: int ghost var z: int method P() modifies this { @@ -1400,20 +1411,20 @@ module SuchThat { module GhostTests { class G { } - method GhostNew4(n: nat) + method GhostNew3(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 + { if n != 0 { GhostNew3(n-1); } } // error: cannot call non-ghost method in a ghost context 1 + 4; - { GhostNew5(g); } // error: cannot call method with nonempty modifies + { GhostNew4(g); } // error: cannot call method with nonempty modifies -5 + 10; } } - ghost method GhostNew5(g: G) + ghost method GhostNew4(g: G) modifies g; { } @@ -1524,7 +1535,7 @@ module EvenMoreGhostTests { ensures false; { while (true) - decreases *; // error: not allowed in ghost context + decreases * // error: not allowed here { } } @@ -1548,20 +1559,6 @@ module EvenMoreGhostTests { } } } - 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 { @@ -1619,3 +1616,79 @@ module UnderspecifiedTypedShouldBeResolvedOnlyOnce { } } } + +module LoopResolutionTests { + class C { + var x: int + ghost var y: int + } + + ghost method M(c: C) + requires c != null + modifies c + { + var n := 0; + while n < 100 + modifies c`y + modifies c`x // error: not allowed to mention non-ghost field in modifies clause of ghost loops + { + c.x := c.x + 1; // error: assignment to non-ghost field not allowed here + } + } + + method MM(c: C) + requires c != null + modifies c + { + var n := 0; + while + invariant n <= 100 + modifies c // regression test + { + case n < 100 => n := n + 1; + } + } + + method MMX(c: C, ghost g: int) + requires c != null + modifies c + { + var n := 0; + while + invariant n <= 100 + modifies c`y + modifies c`x // error: not allowed to mention non-ghost field in modifies clause of ghost loops + { + case n < 100 => n := n + 1; // error: cannot assign to non-ghost in a ghost loop + case g < 56 && n != 100 => n := n + 1; // error: cannot assign to non-ghost in a ghost loop + } + } + + method MD0(c: C, ghost g: nat) + requires c != null + modifies c + decreases * + { + var n := 0; + while n + g < 100 + invariant n <= 100 + decreases * // error: disallowed on ghost loops + { + n := n + 1; // error: cannot assign to non-ghost in a ghost loop + } + } + + method MD1(c: C, ghost g: nat) + requires c != null + modifies c + decreases * + { + var n := 0; + while + invariant n <= 100 + decreases * // error: disallowed on ghost loops + { + case n + g < 100 => n := n + 1; // error: cannot assign to non-ghost in a ghost loop + } + } +} diff --git a/Test/dafny0/ResolutionErrors.dfy.expect b/Test/dafny0/ResolutionErrors.dfy.expect index 6f4b3519..edf61b33 100644 --- a/Test/dafny0/ResolutionErrors.dfy.expect +++ b/Test/dafny0/ResolutionErrors.dfy.expect @@ -26,113 +26,126 @@ ResolutionErrors.dfy(599,25): Error: the type of this variable is underspecified ResolutionErrors.dfy(599,23): Error: type variable 'T' in the function call to 'P' could not be determined ResolutionErrors.dfy(612,13): Error: 'new' is not allowed in ghost contexts ResolutionErrors.dfy(613,9): Error: 'new' is not allowed in ghost contexts -ResolutionErrors.dfy(620,14): Error: new allocation not supported in forall statements -ResolutionErrors.dfy(625,11): Error: the body of the enclosing forall statement is not allowed to update heap locations -ResolutionErrors.dfy(625,14): Error: new allocation not allowed in ghost context -ResolutionErrors.dfy(635,23): Error: 'new' is not allowed in ghost contexts -ResolutionErrors.dfy(642,15): Error: 'new' is not allowed in ghost contexts -ResolutionErrors.dfy(651,17): Error: 'new' is not allowed in ghost contexts -ResolutionErrors.dfy(669,21): Error: the type of this variable is underspecified -ResolutionErrors.dfy(709,22): Error: 'decreases *' is not allowed on ghost loops -ResolutionErrors.dfy(742,22): Error: 'decreases *' is not allowed on ghost loops -ResolutionErrors.dfy(773,19): Error: calls to methods with side-effects are not allowed inside a statement expression -ResolutionErrors.dfy(774,20): Error: wrong number of method result arguments (got 0, expected 1) -ResolutionErrors.dfy(786,23): Error: function calls are allowed only in specification contexts (consider declaring the function a 'function method') -ResolutionErrors.dfy(796,4): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(807,36): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(816,17): Error: function calls are allowed only in specification contexts (consider declaring the function a 'function method') -ResolutionErrors.dfy(830,6): Error: RHS (of type B) not assignable to LHS (of type object) -ResolutionErrors.dfy(831,6): Error: RHS (of type int) not assignable to LHS (of type object) -ResolutionErrors.dfy(832,6): Error: RHS (of type B) not assignable to LHS (of type object) -ResolutionErrors.dfy(837,6): Error: RHS (of type G) not assignable to LHS (of type object) -ResolutionErrors.dfy(838,6): Error: RHS (of type Dt) not assignable to LHS (of type object) -ResolutionErrors.dfy(839,6): Error: RHS (of type CoDt) not assignable to LHS (of type object) -ResolutionErrors.dfy(901,4): Error: LHS of array assignment must denote an array element (found seq) -ResolutionErrors.dfy(902,4): Error: LHS of array assignment must denote an array element (found seq) -ResolutionErrors.dfy(907,10): Error: LHS of assignment must denote a mutable field -ResolutionErrors.dfy(908,10): Error: LHS of assignment must denote a mutable field -ResolutionErrors.dfy(909,9): Error: cannot assign to a range of array elements (try the 'forall' statement) -ResolutionErrors.dfy(910,9): Error: cannot assign to a range of array elements (try the 'forall' statement) -ResolutionErrors.dfy(911,5): Error: cannot assign to a range of array elements (try the 'forall' statement) -ResolutionErrors.dfy(912,5): Error: cannot assign to a range of array elements (try the 'forall' statement) -ResolutionErrors.dfy(993,11): Error: Wrong number of type arguments (2 instead of 1) passed to array type: array3 -ResolutionErrors.dfy(994,11): Error: Wrong number of type arguments (2 instead of 1) passed to class: C -ResolutionErrors.dfy(1005,7): Error: Duplicate name of top-level declaration: BadSyn2 -ResolutionErrors.dfy(1002,17): Error: Wrong number of type arguments (0 instead of 1) passed to datatype: List -ResolutionErrors.dfy(1003,17): Error: Undeclared top-level type or type parameter: badName (did you forget to qualify a name or declare a module import 'opened?') -ResolutionErrors.dfy(1004,22): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') -ResolutionErrors.dfy(1011,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> A -ResolutionErrors.dfy(1014,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A -ResolutionErrors.dfy(1018,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A -ResolutionErrors.dfy(1027,11): Error: because of cyclic dependencies among constructor argument types, no instances of datatype 'D' can be constructed -ResolutionErrors.dfy(1030,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A -ResolutionErrors.dfy(1035,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A -ResolutionErrors.dfy(1054,21): Error: unresolved identifier: x -ResolutionErrors.dfy(1061,35): Error: Wrong number of type arguments (2 instead of 1) passed to opaque type: P -ResolutionErrors.dfy(1073,13): Error: Undeclared top-level type or type parameter: BX (did you forget to qualify a name or declare a module import 'opened?') -ResolutionErrors.dfy(1083,6): Error: RHS (of type P) not assignable to LHS (of type P) -ResolutionErrors.dfy(1088,6): Error: RHS (of type P) not assignable to LHS (of type P) -ResolutionErrors.dfy(1093,6): Error: RHS (of type P) not assignable to LHS (of type P) -ResolutionErrors.dfy(1094,6): Error: RHS (of type P) not assignable to LHS (of type P) -ResolutionErrors.dfy(1099,13): Error: arguments must have the same type (got P and P) -ResolutionErrors.dfy(1100,13): Error: arguments must have the same type (got P and P) -ResolutionErrors.dfy(1101,13): Error: arguments must have the same type (got P and P) -ResolutionErrors.dfy(1124,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(1126,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(1231,26): Error: the type of this variable is underspecified -ResolutionErrors.dfy(1232,31): Error: the type of this variable is underspecified -ResolutionErrors.dfy(1233,29): Error: the type of this variable is underspecified -ResolutionErrors.dfy(1243,34): Error: the type of this variable is underspecified -ResolutionErrors.dfy(1259,21): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') -ResolutionErrors.dfy(1260,24): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') -ResolutionErrors.dfy(1297,16): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (y) -ResolutionErrors.dfy(1307,18): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) -ResolutionErrors.dfy(1335,15): Error: The name Inner ambiguously refers to a type in one of the modules A, B (try qualifying the type name with the module name) -ResolutionErrors.dfy(1345,29): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(1347,49): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(1347,54): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(1368,11): Error: name of type (X) is used as a variable -ResolutionErrors.dfy(1368,16): Error: name of type (X) is used as a variable -ResolutionErrors.dfy(1369,11): Error: name of module (Y) is used as a variable -ResolutionErrors.dfy(1369,16): Error: name of module (Y) is used as a variable -ResolutionErrors.dfy(1370,11): Error: name of type (X) is used as a variable -ResolutionErrors.dfy(1370,13): Error: second argument to "in" must be a set, multiset, or sequence with elements of type #type, or a map with domain #type (instead got map) -ResolutionErrors.dfy(1371,11): Error: name of module (Y) is used as a variable -ResolutionErrors.dfy(1371,13): Error: second argument to "in" must be a set, multiset, or sequence with elements of type #module, or a map with domain #module (instead got map) -ResolutionErrors.dfy(1376,16): Error: name of type (X) is used as a variable -ResolutionErrors.dfy(1376,13): Error: arguments must have the same type (got int and #type) -ResolutionErrors.dfy(1377,16): Error: name of module (Y) is used as a variable -ResolutionErrors.dfy(1377,13): Error: arguments must have the same type (got int and #module) -ResolutionErrors.dfy(1378,4): Error: name of type (X) is used as a variable -ResolutionErrors.dfy(1379,4): Error: name of module (Y) is used as a variable -ResolutionErrors.dfy(1388,11): Error: type of RHS of assign-such-that statement must be boolean (got int) -ResolutionErrors.dfy(1389,9): Error: type of RHS of assign-such-that statement must be boolean (got int) -ResolutionErrors.dfy(1390,13): Error: type of RHS of assign-such-that statement must be boolean (got int) -ResolutionErrors.dfy(1393,15): Error: type of RHS of let-such-that expression must be boolean (got int) -ResolutionErrors.dfy(1436,20): Error: calls to methods with side-effects are not allowed inside a hint -ResolutionErrors.dfy(1458,18): Error: a hint is not allowed to update heap locations -ResolutionErrors.dfy(1459,23): Error: a hint is not allowed to update heap locations -ResolutionErrors.dfy(1460,20): Error: calls to methods with side-effects are not allowed inside a hint -ResolutionErrors.dfy(1463,21): Error: a while statement used inside a hint is not allowed to have a modifies clause -ResolutionErrors.dfy(1445,24): Error: only ghost methods can be called from this context -ResolutionErrors.dfy(1458,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(1487,18): Error: a hint is not allowed to update heap locations -ResolutionErrors.dfy(1488,23): Error: a hint is not allowed to update heap locations -ResolutionErrors.dfy(1489,11): Error: calls to methods with side-effects are not allowed inside a hint -ResolutionErrors.dfy(1492,21): Error: a while statement used inside a hint is not allowed to have a modifies clause -ResolutionErrors.dfy(1480,24): Error: only ghost methods can be called from this context -ResolutionErrors.dfy(1487,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(1516,20): Error: only ghost methods can be called from this context -ResolutionErrors.dfy(1409,29): Error: only ghost methods can be called from this context -ResolutionErrors.dfy(1411,17): Error: calls to methods with side-effects are not allowed inside a hint -ResolutionErrors.dfy(1527,16): Error: 'decreases *' is not allowed on ghost loops -ResolutionErrors.dfy(1545,12): Error: trying to break out of more loop levels than there are enclosing loops -ResolutionErrors.dfy(1571,16): Error: ghost fields are allowed only in specification contexts -ResolutionErrors.dfy(1578,9): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(1584,4): Error: non-ghost variable cannot be assigned a value that depends on a ghost -ResolutionErrors.dfy(1601,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(1610,26): Error: ghost variables are allowed only in specification contexts -ResolutionErrors.dfy(1618,6): Error: the type of the bound variable 't' could not be determined +ResolutionErrors.dfy(622,23): Error: 'new' is not allowed in ghost contexts +ResolutionErrors.dfy(629,15): Error: 'new' is not allowed in ghost contexts +ResolutionErrors.dfy(638,17): Error: 'new' is not allowed in ghost contexts +ResolutionErrors.dfy(655,14): Error: new allocation not supported in forall statements +ResolutionErrors.dfy(660,11): Error: the body of the enclosing forall statement is not allowed to update heap locations +ResolutionErrors.dfy(660,14): Error: new allocation not allowed in ghost context +ResolutionErrors.dfy(672,21): Error: the type of this variable is underspecified +ResolutionErrors.dfy(712,22): Error: a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating +ResolutionErrors.dfy(745,22): Error: a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating +ResolutionErrors.dfy(776,19): Error: calls to methods with side-effects are not allowed inside a statement expression +ResolutionErrors.dfy(777,20): Error: wrong number of method result arguments (got 0, expected 1) +ResolutionErrors.dfy(789,23): Error: function calls are allowed only in specification contexts (consider declaring the function a 'function method') +ResolutionErrors.dfy(799,4): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(810,36): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(819,17): Error: function calls are allowed only in specification contexts (consider declaring the function a 'function method') +ResolutionErrors.dfy(833,6): Error: RHS (of type B) not assignable to LHS (of type object) +ResolutionErrors.dfy(834,6): Error: RHS (of type int) not assignable to LHS (of type object) +ResolutionErrors.dfy(835,6): Error: RHS (of type B) not assignable to LHS (of type object) +ResolutionErrors.dfy(840,6): Error: RHS (of type G) not assignable to LHS (of type object) +ResolutionErrors.dfy(841,6): Error: RHS (of type Dt) not assignable to LHS (of type object) +ResolutionErrors.dfy(842,6): Error: RHS (of type CoDt) not assignable to LHS (of type object) +ResolutionErrors.dfy(867,14): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) +ResolutionErrors.dfy(875,16): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) +ResolutionErrors.dfy(885,20): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) +ResolutionErrors.dfy(896,18): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) +ResolutionErrors.dfy(912,4): Error: LHS of array assignment must denote an array element (found seq) +ResolutionErrors.dfy(913,4): Error: LHS of array assignment must denote an array element (found seq) +ResolutionErrors.dfy(918,10): Error: LHS of assignment must denote a mutable field +ResolutionErrors.dfy(919,10): Error: LHS of assignment must denote a mutable field +ResolutionErrors.dfy(920,9): Error: cannot assign to a range of array elements (try the 'forall' statement) +ResolutionErrors.dfy(921,9): Error: cannot assign to a range of array elements (try the 'forall' statement) +ResolutionErrors.dfy(922,5): Error: cannot assign to a range of array elements (try the 'forall' statement) +ResolutionErrors.dfy(923,5): Error: cannot assign to a range of array elements (try the 'forall' statement) +ResolutionErrors.dfy(1004,11): Error: Wrong number of type arguments (2 instead of 1) passed to array type: array3 +ResolutionErrors.dfy(1005,11): Error: Wrong number of type arguments (2 instead of 1) passed to class: C +ResolutionErrors.dfy(1016,7): Error: Duplicate name of top-level declaration: BadSyn2 +ResolutionErrors.dfy(1013,17): Error: Wrong number of type arguments (0 instead of 1) passed to datatype: List +ResolutionErrors.dfy(1014,17): Error: Undeclared top-level type or type parameter: badName (did you forget to qualify a name or declare a module import 'opened?') +ResolutionErrors.dfy(1015,22): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') +ResolutionErrors.dfy(1022,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> A +ResolutionErrors.dfy(1025,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A +ResolutionErrors.dfy(1029,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A +ResolutionErrors.dfy(1038,11): Error: because of cyclic dependencies among constructor argument types, no instances of datatype 'D' can be constructed +ResolutionErrors.dfy(1041,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A +ResolutionErrors.dfy(1046,7): Error: Cycle among redirecting types (newtypes, type synonyms): A -> B -> A +ResolutionErrors.dfy(1065,21): Error: unresolved identifier: x +ResolutionErrors.dfy(1072,35): Error: Wrong number of type arguments (2 instead of 1) passed to opaque type: P +ResolutionErrors.dfy(1084,13): Error: Undeclared top-level type or type parameter: BX (did you forget to qualify a name or declare a module import 'opened?') +ResolutionErrors.dfy(1094,6): Error: RHS (of type P) not assignable to LHS (of type P) +ResolutionErrors.dfy(1099,6): Error: RHS (of type P) not assignable to LHS (of type P) +ResolutionErrors.dfy(1104,6): Error: RHS (of type P) not assignable to LHS (of type P) +ResolutionErrors.dfy(1105,6): Error: RHS (of type P) not assignable to LHS (of type P) +ResolutionErrors.dfy(1110,13): Error: arguments must have the same type (got P and P) +ResolutionErrors.dfy(1111,13): Error: arguments must have the same type (got P and P) +ResolutionErrors.dfy(1112,13): Error: arguments must have the same type (got P and P) +ResolutionErrors.dfy(1135,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(1137,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(1242,26): Error: the type of this variable is underspecified +ResolutionErrors.dfy(1243,31): Error: the type of this variable is underspecified +ResolutionErrors.dfy(1244,29): Error: the type of this variable is underspecified +ResolutionErrors.dfy(1254,34): Error: the type of this variable is underspecified +ResolutionErrors.dfy(1270,21): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') +ResolutionErrors.dfy(1271,24): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') +ResolutionErrors.dfy(1308,16): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (y) +ResolutionErrors.dfy(1318,18): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) +ResolutionErrors.dfy(1346,15): Error: The name Inner ambiguously refers to a type in one of the modules A, B (try qualifying the type name with the module name) +ResolutionErrors.dfy(1356,29): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(1358,49): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(1358,54): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(1379,11): Error: name of type (X) is used as a variable +ResolutionErrors.dfy(1379,16): Error: name of type (X) is used as a variable +ResolutionErrors.dfy(1380,11): Error: name of module (Y) is used as a variable +ResolutionErrors.dfy(1380,16): Error: name of module (Y) is used as a variable +ResolutionErrors.dfy(1381,11): Error: name of type (X) is used as a variable +ResolutionErrors.dfy(1381,13): Error: second argument to "in" must be a set, multiset, or sequence with elements of type #type, or a map with domain #type (instead got map) +ResolutionErrors.dfy(1382,11): Error: name of module (Y) is used as a variable +ResolutionErrors.dfy(1382,13): Error: second argument to "in" must be a set, multiset, or sequence with elements of type #module, or a map with domain #module (instead got map) +ResolutionErrors.dfy(1387,16): Error: name of type (X) is used as a variable +ResolutionErrors.dfy(1387,13): Error: arguments must have the same type (got int and #type) +ResolutionErrors.dfy(1388,16): Error: name of module (Y) is used as a variable +ResolutionErrors.dfy(1388,13): Error: arguments must have the same type (got int and #module) +ResolutionErrors.dfy(1389,4): Error: name of type (X) is used as a variable +ResolutionErrors.dfy(1390,4): Error: name of module (Y) is used as a variable +ResolutionErrors.dfy(1399,11): Error: type of RHS of assign-such-that statement must be boolean (got int) +ResolutionErrors.dfy(1400,9): Error: type of RHS of assign-such-that statement must be boolean (got int) +ResolutionErrors.dfy(1401,13): Error: type of RHS of assign-such-that statement must be boolean (got int) +ResolutionErrors.dfy(1404,15): Error: type of RHS of let-such-that expression must be boolean (got int) +ResolutionErrors.dfy(1447,20): Error: calls to methods with side-effects are not allowed inside a hint +ResolutionErrors.dfy(1469,18): Error: a hint is not allowed to update heap locations +ResolutionErrors.dfy(1470,23): Error: a hint is not allowed to update heap locations +ResolutionErrors.dfy(1471,20): Error: calls to methods with side-effects are not allowed inside a hint +ResolutionErrors.dfy(1474,21): Error: a while statement used inside a hint is not allowed to have a modifies clause +ResolutionErrors.dfy(1456,24): Error: only ghost methods can be called from this context +ResolutionErrors.dfy(1469,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(1498,18): Error: a hint is not allowed to update heap locations +ResolutionErrors.dfy(1499,23): Error: a hint is not allowed to update heap locations +ResolutionErrors.dfy(1500,11): Error: calls to methods with side-effects are not allowed inside a hint +ResolutionErrors.dfy(1503,21): Error: a while statement used inside a hint is not allowed to have a modifies clause +ResolutionErrors.dfy(1491,24): Error: only ghost methods can be called from this context +ResolutionErrors.dfy(1498,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(1527,20): Error: only ghost methods can be called from this context +ResolutionErrors.dfy(1420,29): Error: only ghost methods can be called from this context +ResolutionErrors.dfy(1422,17): Error: calls to methods with side-effects are not allowed inside a hint +ResolutionErrors.dfy(1538,16): Error: a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating +ResolutionErrors.dfy(1556,12): Error: trying to break out of more loop levels than there are enclosing loops +ResolutionErrors.dfy(1568,16): Error: ghost fields are allowed only in specification contexts +ResolutionErrors.dfy(1575,9): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(1581,4): Error: non-ghost variable cannot be assigned a value that depends on a ghost +ResolutionErrors.dfy(1598,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(1607,26): Error: ghost variables are allowed only in specification contexts +ResolutionErrors.dfy(1615,6): Error: the type of the bound variable 't' could not be determined +ResolutionErrors.dfy(1633,15): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) +ResolutionErrors.dfy(1635,10): 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(1660,15): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) +ResolutionErrors.dfy(1662,25): 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(1663,35): 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(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(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') @@ -144,18 +157,14 @@ ResolutionErrors.dfy(475,14): Error: when allocating an object of type 'YHWH', o ResolutionErrors.dfy(480,6): Error: when allocating an object of type 'Lucifer', one of its constructor methods must be called ResolutionErrors.dfy(481,6): Error: when allocating an object of type 'Lucifer', one of its constructor methods must be called ResolutionErrors.dfy(483,9): Error: class Lamb does not have an anonymous constructor -ResolutionErrors.dfy(850,11): Error: a modifies-clause expression must denote an object or a collection of objects (instead got int) -ResolutionErrors.dfy(854,14): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) -ResolutionErrors.dfy(857,12): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) -ResolutionErrors.dfy(865,14): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) -ResolutionErrors.dfy(875,18): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) -ResolutionErrors.dfy(886,16): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) -ResolutionErrors.dfy(1042,23): Error: unresolved identifier: x -ResolutionErrors.dfy(1045,20): Error: unresolved identifier: x -ResolutionErrors.dfy(1048,23): Error: unresolved identifier: x -ResolutionErrors.dfy(1050,19): Error: unresolved identifier: x -ResolutionErrors.dfy(1052,19): Error: unresolved identifier: x -ResolutionErrors.dfy(12,16): Error: 'decreases *' is not allowed on ghost loops +ResolutionErrors.dfy(853,11): Error: a modifies-clause expression must denote an object or a collection of objects (instead got int) +ResolutionErrors.dfy(857,14): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (x) +ResolutionErrors.dfy(1053,23): Error: unresolved identifier: x +ResolutionErrors.dfy(1056,20): Error: unresolved identifier: x +ResolutionErrors.dfy(1059,23): Error: unresolved identifier: x +ResolutionErrors.dfy(1061,19): Error: unresolved identifier: x +ResolutionErrors.dfy(1063,19): Error: unresolved identifier: x +ResolutionErrors.dfy(12,16): Error: a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating ResolutionErrors.dfy(24,11): Error: array selection requires an array2 (got array3) ResolutionErrors.dfy(25,12): Error: sequence/array/multiset/map selection requires a sequence, array, multiset, or map (got array3) ResolutionErrors.dfy(26,11): Error: array selection requires an array4 (got array) @@ -187,17 +196,17 @@ ResolutionErrors.dfy(416,10): Error: second argument to ==> must be of type bool ResolutionErrors.dfy(421,10): Error: first argument to ==> must be of type bool (instead got int) ResolutionErrors.dfy(421,10): Error: second argument to ==> must be of type bool (instead got int) ResolutionErrors.dfy(580,18): Error: unresolved identifier: w -ResolutionErrors.dfy(683,11): Error: lemmas are not allowed to have modifies clauses -ResolutionErrors.dfy(924,9): Error: unresolved identifier: s -ResolutionErrors.dfy(935,32): Error: RHS (of type (int,int,real)) not assignable to LHS (of type (int,real,int)) -ResolutionErrors.dfy(936,37): Error: RHS (of type (int,real,int)) not assignable to LHS (of type (int,real,int,real)) -ResolutionErrors.dfy(942,16): Error: condition is expected to be of type bool, but is int -ResolutionErrors.dfy(943,16): Error: member 3 does not exist in datatype _tuple#3 -ResolutionErrors.dfy(943,26): Error: member x does not exist in datatype _tuple#2 -ResolutionErrors.dfy(966,15): Error: arguments to / must have the same type (got real and int) -ResolutionErrors.dfy(967,10): Error: second argument to % must be of type int (instead got real) -ResolutionErrors.dfy(1112,8): Error: new cannot be applied to a trait -ResolutionErrors.dfy(1133,13): Error: first argument to / must be of numeric type (instead got set) -ResolutionErrors.dfy(1140,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(1155,14): Error: a possibly infinite loop is allowed only if the enclosing method is declared (with 'decreases *') to be possibly non-terminating -202 resolution/type errors detected in ResolutionErrors.dfy +ResolutionErrors.dfy(686,11): Error: lemmas are not allowed to have modifies clauses +ResolutionErrors.dfy(935,9): Error: unresolved identifier: s +ResolutionErrors.dfy(946,32): Error: RHS (of type (int,int,real)) not assignable to LHS (of type (int,real,int)) +ResolutionErrors.dfy(947,37): Error: RHS (of type (int,real,int)) not assignable to LHS (of type (int,real,int,real)) +ResolutionErrors.dfy(953,16): Error: condition is expected to be of type bool, but is int +ResolutionErrors.dfy(954,16): Error: member 3 does not exist in datatype _tuple#3 +ResolutionErrors.dfy(954,26): Error: member x does not exist in datatype _tuple#2 +ResolutionErrors.dfy(977,15): Error: arguments to / must have the same type (got real and int) +ResolutionErrors.dfy(978,10): Error: second argument to % must be of type int (instead got real) +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 +211 resolution/type errors detected in ResolutionErrors.dfy -- cgit v1.2.3 From fc76e0501cb3f1f7e9254b970315d4c63254a2d5 Mon Sep 17 00:00:00 2001 From: leino Date: Mon, 28 Sep 2015 15:38:41 -0700 Subject: Renamed CheckIsNonGhost to CheckIsCompilable. --- Source/Dafny/Resolver.cs | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index f4fac31b..a50d1a7d 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -1454,7 +1454,7 @@ namespace Microsoft.Dafny } else if (member is Function) { var f = (Function)member; if (!f.IsGhost && f.Body != null) { - CheckIsNonGhost(f.Body); + CheckIsCompilable(f.Body); } DetermineTailRecursion(f); } @@ -3160,7 +3160,7 @@ namespace Microsoft.Dafny if (mustBeErasable) { Error(stmt, "print statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); } else { - s.Args.Iter(resolver.CheckIsNonGhost); + s.Args.Iter(resolver.CheckIsCompilable); } } else if (stmt is BreakStmt) { @@ -3235,19 +3235,19 @@ namespace Microsoft.Dafny AssignStmt.NonGhostKind_To_String(gk)); } else if (s.Rhs is ExprRhs) { var rhs = (ExprRhs)s.Rhs; - resolver.CheckIsNonGhost(rhs.Expr); + resolver.CheckIsCompilable(rhs.Expr); } else if (s.Rhs is HavocRhs) { // cool } else { var rhs = (TypeRhs)s.Rhs; if (rhs.ArrayDimensions != null) { foreach (var dim in rhs.ArrayDimensions) { - resolver.CheckIsNonGhost(dim); + resolver.CheckIsCompilable(dim); } } if (rhs.InitCall != null) { foreach (var arg in rhs.InitCall.Args) { - resolver.CheckIsNonGhost(arg); + resolver.CheckIsCompilable(arg); } } } @@ -3263,14 +3263,14 @@ namespace Microsoft.Dafny Error(s, "only ghost methods can be called from this context"); } } else { - resolver.CheckIsNonGhost(s.Receiver); + resolver.CheckIsCompilable(s.Receiver); int j; if (!callee.IsGhost) { j = 0; foreach (var e in s.Args) { Contract.Assume(j < callee.Ins.Count); // this should have already been checked by the resolver if (!callee.Ins[j].IsGhost) { - resolver.CheckIsNonGhost(e); + resolver.CheckIsCompilable(e); } j++; } @@ -9283,7 +9283,7 @@ namespace Microsoft.Dafny /// Generate an error for every non-ghost feature used in "expr". /// Requires "expr" to have been successfully resolved. /// - void CheckIsNonGhost(Expression expr) { + void CheckIsCompilable(Expression expr) { Contract.Requires(expr != null); Contract.Requires(expr.WasResolved()); // this check approximates the requirement that "expr" be resolved @@ -9309,10 +9309,10 @@ namespace Microsoft.Dafny return; } // function is okay, so check all NON-ghost arguments - CheckIsNonGhost(e.Receiver); + CheckIsCompilable(e.Receiver); for (int i = 0; i < e.Function.Formals.Count; i++) { if (!e.Function.Formals[i].IsGhost) { - CheckIsNonGhost(e.Args[i]); + CheckIsCompilable(e.Args[i]); } } } @@ -9324,7 +9324,7 @@ namespace Microsoft.Dafny // note that if resolution is successful, then |e.Arguments| == |e.Ctor.Formals| for (int i = 0; i < e.Arguments.Count; i++) { if (!e.Ctor.Formals[i].IsGhost) { - CheckIsNonGhost(e.Arguments[i]); + CheckIsCompilable(e.Arguments[i]); } } return; @@ -9343,7 +9343,7 @@ namespace Microsoft.Dafny } else if (expr is StmtExpr) { var e = (StmtExpr)expr; // ignore the statement - CheckIsNonGhost(e.E); + CheckIsCompilable(e.E); return; } else if (expr is BinaryExpr) { @@ -9374,18 +9374,18 @@ namespace Microsoft.Dafny var i = 0; foreach (var ee in e.RHSs) { if (!e.LHSs[i].Vars.All(bv => bv.IsGhost)) { - CheckIsNonGhost(ee); + CheckIsCompilable(ee); } i++; } - CheckIsNonGhost(e.Body); + CheckIsCompilable(e.Body); } else { Contract.Assert(e.RHSs.Count == 1); var lhsVarsAreAllGhost = e.BoundVars.All(bv => bv.IsGhost); if (!lhsVarsAreAllGhost) { - CheckIsNonGhost(e.RHSs[0]); + CheckIsCompilable(e.RHSs[0]); } - CheckIsNonGhost(e.Body); + CheckIsCompilable(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 @@ -9405,7 +9405,7 @@ namespace Microsoft.Dafny return; } else if (expr is LambdaExpr) { var e = expr as LambdaExpr; - CheckIsNonGhost(e.Body); + CheckIsCompilable(e.Body); return; } else if (expr is ComprehensionExpr) { var e = (ComprehensionExpr)expr; @@ -9437,18 +9437,18 @@ namespace Microsoft.Dafny } } else if (expr is NamedExpr) { if (!moduleInfo.IsAbstract) - CheckIsNonGhost(((NamedExpr)expr).Body); + CheckIsCompilable(((NamedExpr)expr).Body); return; } else if (expr is ChainingExpression) { // We don't care about the different operators; we only want the operands, so let's get them directly from // the chaining expression var e = (ChainingExpression)expr; - e.Operands.ForEach(CheckIsNonGhost); + e.Operands.ForEach(CheckIsCompilable); return; } foreach (var ee in expr.SubExpressions) { - CheckIsNonGhost(ee); + CheckIsCompilable(ee); } } -- cgit v1.2.3 From 9dd401ba24bf795c73a8d66c0890c760de6c8ad5 Mon Sep 17 00:00:00 2001 From: leino Date: Mon, 28 Sep 2015 16:18:01 -0700 Subject: Removed the 'inSpecOnlyContext' map that had been part of the resolution of 'break' statements out of ghost structures. This is now done in pass 2 by looking at the .IsGhost field of the target statement. --- Source/Dafny/Resolver.cs | 59 +++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index a50d1a7d..9f2feb14 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -3136,17 +3136,23 @@ namespace Microsoft.Dafny 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 - /// 1. Reports an error if the statement assigns to a non-ghost field and the right-hand side - /// depends on a ghost. - /// 2. Sets to "true" the .IsGhost field of the statement if it should be erased during compilation. - /// In that case, substatements should be visited with "mustBeErasable". - /// Note that the method called by a StmtExpr must be ghost; however, this is checked elsewhere. For - /// this reason, it is not necessary to visit all subexpressions, unless the subexpression - /// matter for the ghost checking/recording of "stmt". - /// Note, it is important to set the statement's ghost status before descending into its sub-statements, - /// because break statements look at the ghost status of its enclosing statements. + /// This method does three things, in order: + /// 0. Sets .IsGhost to "true" if the statement is ghost. This often depends on some guard of the statement + /// (like the guard of an "if" statement) or the LHS of the statement (if it is an assignment). + /// Note, if "mustBeErasable", then the statement is already in a ghost context. + /// statement itself is ghost) or and the statement assigns to a non-ghost field + /// 1. Determines if the statement and all its subparts are legal under its computed .IsGhost setting. + /// 2. ``Upgrades'' .IsGhost to "true" if, after investigation of the substatements of the statement, it + /// turns out that the statement can be erased during compilation. + /// Notes: + /// * Both step (0) and step (2) sets the .IsGhost field. What step (0) does affects only the + /// rules of resolution, whereas step (2) makes a note for the later compilation phase. + /// * It is important to do step (0) before step (1)--that is, it is important to set the statement's ghost + /// status before descending into its sub-statements--because break statements look at the ghost status of + /// its enclosing statements. + /// * The method called by a StmtExpr must be ghost; however, this is checked elsewhere. For + /// this reason, it is not necessary to visit all subexpressions, unless the subexpression + /// matter for the ghost checking/recording of "stmt". /// public void Visit(Statement stmt, bool mustBeErasable) { Contract.Requires(stmt != null); @@ -3165,11 +3171,10 @@ namespace Microsoft.Dafny } else if (stmt is BreakStmt) { var s = (BreakStmt)stmt; - if (mustBeErasable) { - if (!s.TargetStmt.IsGhost && !resolver.inSpecOnlyContext[s.TargetStmt]) { // TODO: inSpecOnlyContext should probably move from Resolver to GhostInterest_Visitor - var targetIsLoop = s.TargetStmt is WhileStmt || s.TargetStmt is AlternativeLoopStmt; - Error(stmt, "ghost-context break statement is not allowed to break out of non-ghost " + (targetIsLoop ? "loop" : "structure")); - } + s.IsGhost = mustBeErasable; + if (s.IsGhost && !s.TargetStmt.IsGhost) { + var targetIsLoop = s.TargetStmt is WhileStmt || s.TargetStmt is AlternativeLoopStmt; + Error(stmt, "ghost-context break statement is not allowed to break out of non-ghost " + (targetIsLoop ? "loop" : "structure")); } } else if (stmt is ProduceStmt) { @@ -3306,8 +3311,9 @@ namespace Microsoft.Dafny } else if (stmt is BlockStmt) { var s = (BlockStmt)stmt; + s.IsGhost = mustBeErasable; // set .IsGhost before descending into substatements (since substatements may do a 'break' out of this block) s.Body.Iter(ss => Visit(ss, mustBeErasable)); - s.IsGhost = s.Body.All(ss => ss.IsGhost); // mark the block statement as ghost if all its substatements are ghost + s.IsGhost = s.IsGhost || s.Body.All(ss => ss.IsGhost); // mark the block statement as ghost if all its substatements are ghost } else if (stmt is IfStmt) { var s = (IfStmt)stmt; @@ -3378,14 +3384,16 @@ namespace Microsoft.Dafny } else if (stmt is MatchStmt) { var s = (MatchStmt)stmt; - var mbe = mustBeErasable || resolver.UsesSpecFeatures(s.Source); - s.Cases.Iter(kase => kase.Body.Iter(ss => Visit(ss, mbe))); - s.IsGhost = mbe || s.Cases.All(kase => kase.Body.All(ss => ss.IsGhost)); + s.IsGhost = mustBeErasable || resolver.UsesSpecFeatures(s.Source); + s.Cases.Iter(kase => kase.Body.Iter(ss => Visit(ss, s.IsGhost))); + s.IsGhost = s.IsGhost || s.Cases.All(kase => kase.Body.All(ss => ss.IsGhost)); } else if (stmt is SkeletonStatement) { var s = (SkeletonStatement)stmt; + s.IsGhost = mustBeErasable; if (s.S != null) { Visit(s.S, mustBeErasable); + s.IsGhost = s.IsGhost || s.S.IsGhost; } } else { @@ -3509,7 +3517,6 @@ namespace Microsoft.Dafny readonly Scope/*!*/ scope = new Scope(); Scope/*!*/ labeledStatements = new Scope(); List loopStack = new List(); // the enclosing loops (from which it is possible to break out) - readonly Dictionary inSpecOnlyContext = new Dictionary(); // invariant: domain contain union of the domains of "labeledStatements" and "loopStack" /// /// This method resolves the types that have been given after the 'extends' keyword. Then, it populates @@ -5519,10 +5526,6 @@ namespace Microsoft.Dafny if (s.Body != null) { loopStack.Add(s); // push - if (s.Labels == null) { // otherwise, "s" is already in "inSpecOnlyContext" map - inSpecOnlyContext.Add(s, specContextOnly); - } - ResolveStatement(s.Body, bodyMustBeSpecOnly, codeContext); loopStack.RemoveAt(loopStack.Count - 1); // pop } else { @@ -6405,9 +6408,6 @@ namespace Microsoft.Dafny if (loopToCatchBreaks != null) { loopStack.Add(loopToCatchBreaks); // push - if (loopToCatchBreaks.Labels == null) { // otherwise, "loopToCatchBreak" is already in "inSpecOnlyContext" map - inSpecOnlyContext.Add(loopToCatchBreaks, specContextOnly); - } } foreach (var alternative in alternatives) { scope.PushMarker(); @@ -6559,9 +6559,6 @@ namespace Microsoft.Dafny } else { var r = labeledStatements.Push(lnode.Name, ss); Contract.Assert(r == Scope.PushResult.Success); // since we just checked for duplicates, we expect the Push to succeed - if (l == ss.Labels) { // add it only once - inSpecOnlyContext.Add(ss, specContextOnly); - } } } ResolveStatement(ss, specContextOnly, codeContext); -- cgit v1.2.3 From 8a869bcfaeceb6b5a1d01e9b1c0c08b7000a094e Mon Sep 17 00:00:00 2001 From: leino Date: Mon, 28 Sep 2015 22:47:35 -0700 Subject: Removed specContextOnly parameter from ResolveStatement. Moved all bounds discovery to resolution pass 1. --- Source/Dafny/Resolver.cs | 277 ++++++++++++++------------------ Test/dafny0/ResolutionErrors.dfy | 26 +-- Test/dafny0/ResolutionErrors.dfy.expect | 27 +++- Test/dafny4/Regression0.dfy | 6 +- Test/dafny4/Regression0.dfy.expect | 3 +- Test/dafny4/set-compr.dfy | 54 +++++-- Test/dafny4/set-compr.dfy.expect | 15 +- Test/hofs/ReadsReads.dfy | 4 +- 8 files changed, 218 insertions(+), 194 deletions(-) diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 9f2feb14..1c376c49 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -1336,11 +1336,6 @@ namespace Microsoft.Dafny // ---------------------------------- 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. // ---------------------------------------------------------------------------- @@ -1375,7 +1370,7 @@ namespace Microsoft.Dafny if (!CheckTypeInference_Visitor.IsDetermined(dd.BaseType.NormalizeExpand())) { reporter.Error(MessageSource.Resolver, dd.tok, "newtype's base type is not fully determined; add an explicit type for '{0}'", dd.Var.Name); } - CheckTypeInference(dd.Constraint); + CheckTypeInference(dd.Constraint, dd); scope.PopMarker(); } } @@ -1408,7 +1403,10 @@ namespace Microsoft.Dafny // * checks that type inference was able to determine all types // * fills in the .ResolvedOp field of binary expressions // * discovers bounds for: + // - forall statements // - set comprehensions + // - map comprehensions + // - quantifier expressions // - assign-such-that statements // - compilable let-such-that expressions // - newtype constraints @@ -1432,7 +1430,7 @@ namespace Microsoft.Dafny iter.SubExpressions.Iter(e => CheckExpression(e, this, iter)); } if (iter.Body != null) { - CheckTypeInference(iter.Body); + CheckTypeInference(iter.Body, iter); if (prevErrCnt == reporter.Count(ErrorLevel.Error)) { ComputeGhostInterest(iter.Body, false, iter); CheckExpression(iter.Body, this, iter); @@ -2047,22 +2045,22 @@ namespace Microsoft.Dafny private void CheckTypeInference_Member(MemberDecl member) { if (member is Method) { var m = (Method)member; - m.Req.Iter(CheckTypeInference_MaybeFreeExpression); - m.Ens.Iter(CheckTypeInference_MaybeFreeExpression); - CheckTypeInference_Specification_FrameExpr(m.Mod); - CheckTypeInference_Specification_Expr(m.Decreases); + m.Req.Iter(mfe => CheckTypeInference_MaybeFreeExpression(mfe, m)); + m.Ens.Iter(mfe => CheckTypeInference_MaybeFreeExpression(mfe, m)); + CheckTypeInference_Specification_FrameExpr(m.Mod, m); + CheckTypeInference_Specification_Expr(m.Decreases, m); if (m.Body != null) { - CheckTypeInference(m.Body); + CheckTypeInference(m.Body, m); } } else if (member is Function) { var f = (Function)member; var errorCount = reporter.Count(ErrorLevel.Error); - f.Req.Iter(CheckTypeInference); - f.Ens.Iter(CheckTypeInference); - f.Reads.Iter(fe => CheckTypeInference(fe.E)); - CheckTypeInference_Specification_Expr(f.Decreases); + f.Req.Iter(e => CheckTypeInference(e, f)); + f.Ens.Iter(e => CheckTypeInference(e, f)); + f.Reads.Iter(fe => CheckTypeInference(fe.E, f)); + CheckTypeInference_Specification_Expr(f.Decreases, f); if (f.Body != null) { - CheckTypeInference(f.Body); + CheckTypeInference(f.Body, f); } if (errorCount == reporter.Count(ErrorLevel.Error) && f is FixpointPredicate) { var cop = (FixpointPredicate)f; @@ -2071,43 +2069,51 @@ namespace Microsoft.Dafny } } - private void CheckTypeInference_MaybeFreeExpression(MaybeFreeExpression mfe) { + private void CheckTypeInference_MaybeFreeExpression(MaybeFreeExpression mfe, ICodeContext codeContext) { Contract.Requires(mfe != null); + Contract.Requires(codeContext != null); foreach (var e in Attributes.SubExpressions(mfe.Attributes)) { - CheckTypeInference(e); + CheckTypeInference(e, codeContext); } - CheckTypeInference(mfe.E); + CheckTypeInference(mfe.E, codeContext); } - private void CheckTypeInference_Specification_Expr(Specification spec) { + private void CheckTypeInference_Specification_Expr(Specification spec, ICodeContext codeContext) { Contract.Requires(spec != null); + Contract.Requires(codeContext != null); foreach (var e in Attributes.SubExpressions(spec.Attributes)) { - CheckTypeInference(e); + CheckTypeInference(e, codeContext); } - spec.Expressions.Iter(CheckTypeInference); + spec.Expressions.Iter(e => CheckTypeInference(e, codeContext)); } - private void CheckTypeInference_Specification_FrameExpr(Specification spec) { + private void CheckTypeInference_Specification_FrameExpr(Specification spec, ICodeContext codeContext) { Contract.Requires(spec != null); + Contract.Requires(codeContext != null); foreach (var e in Attributes.SubExpressions(spec.Attributes)) { - CheckTypeInference(e); + CheckTypeInference(e, codeContext); } - spec.Expressions.Iter(fe => CheckTypeInference(fe.E)); + spec.Expressions.Iter(fe => CheckTypeInference(fe.E, codeContext)); } - void CheckTypeInference(Expression expr) { + void CheckTypeInference(Expression expr, ICodeContext codeContext) { Contract.Requires(expr != null); + Contract.Requires(codeContext != null); PartiallySolveTypeConstraints(); - var c = new CheckTypeInference_Visitor(this); + var c = new CheckTypeInference_Visitor(this, codeContext); c.Visit(expr); } - void CheckTypeInference(Statement stmt) { + void CheckTypeInference(Statement stmt, ICodeContext codeContext) { Contract.Requires(stmt != null); - var c = new CheckTypeInference_Visitor(this); + Contract.Requires(codeContext != null); + var c = new CheckTypeInference_Visitor(this, codeContext); c.Visit(stmt); } class CheckTypeInference_Visitor : ResolverBottomUpVisitor { - public CheckTypeInference_Visitor(Resolver resolver) + readonly ICodeContext codeContext; + public CheckTypeInference_Visitor(Resolver resolver, ICodeContext codeContext) : base(resolver) { Contract.Requires(resolver != null); + Contract.Requires(codeContext != null); + this.codeContext = codeContext; } protected override void VisitOneStmt(Statement stmt) { if (stmt is VarDeclStmt) { @@ -2118,6 +2124,9 @@ namespace Microsoft.Dafny } else if (stmt is ForallStmt) { var s = (ForallStmt)stmt; s.BoundVars.Iter(bv => CheckTypeIsDetermined(bv.tok, bv.Type, "bound variable")); + List missingBounds; + s.Bounds = DiscoverBestBounds_MultipleVars(s.BoundVars, s.Range, true, true, out missingBounds); + } else if (stmt is AssignSuchThatStmt) { var s = (AssignSuchThatStmt)stmt; if (s.AssumeToken == null) { @@ -2154,27 +2163,55 @@ namespace Microsoft.Dafny 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); - } + // apply bounds discovery to quantifiers, finite sets, and finite maps + string what = null; + Expression whereToLookForBounds = null; + bool polarity = true; + if (e is QuantifierExpr) { + what = "quantifier"; + whereToLookForBounds = ((QuantifierExpr)e).LogicalBody(); + polarity = e is ExistsExpr; + } else if (e is SetComprehension && ((SetComprehension)e).Finite) { + what = "set comprehension"; + whereToLookForBounds = e.Range; + } else if (e is MapComprehension && ((MapComprehension)e).Finite) { + what = "map comprehension"; + whereToLookForBounds = e.Range; + } + if (whereToLookForBounds != null) { + List missingBounds; + e.Bounds = DiscoverBestBounds_MultipleVars(e.BoundVars, whereToLookForBounds, polarity, true, out missingBounds); + if (missingBounds.Count != 0) { + 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 + } 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) + } 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 + // resolution), the resolver will generate an error about that later. + } else { + // we cannot be sure that the set/map really is finite + foreach (var bv in missingBounds) { + resolver.reporter.Error(MessageSource.Resolver, e, "a {0} must produce a finite set, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{1}'", what, bv.Name); + } + } + } + if (codeContext is Function && e.Bounds != null) { + // functions are not allowed to depend on the set of allocated objects + 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]; + resolver.reporter.Error(MessageSource.Resolver, expr, "a {0} 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 '{1}'", what, bv.Name); } } } } + } else if (expr is MemberSelectExpr) { var e = (MemberSelectExpr)expr; if (e.Member is Function || e.Member is Method) { @@ -4035,7 +4072,7 @@ namespace Microsoft.Dafny int prevErrors = reporter.Count(ErrorLevel.Error); ResolveExpression(arg, opts); if (prevErrors == reporter.Count(ErrorLevel.Error)) { - CheckTypeInference(arg); + CheckTypeInference(arg, opts.codeContext); } } } @@ -4289,7 +4326,7 @@ namespace Microsoft.Dafny scope.Push(k.Name, k); // we expect no name conflict for _k } var prevErrorCount = reporter.Count(ErrorLevel.Error); - ResolveBlockStatement(m.Body, m.IsGhost, m); + ResolveBlockStatement(m.Body, m); SolveAllTypeConstraints(); if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { //KRML ComputeGhostInterest(m.Body, m); @@ -4411,7 +4448,7 @@ namespace Microsoft.Dafny // Resolve body if (iter.Body != null) { - ResolveBlockStatement(iter.Body, false, iter); + ResolveBlockStatement(iter.Body, iter); if (reporter.Count(ErrorLevel.Error) == postSpecErrorCount) { //KRML ComputeGhostInterest(iter.Body, iter); } @@ -5249,11 +5286,7 @@ namespace Microsoft.Dafny return at; } - /// - /// "specContextOnly" means that the statement must be erasable, that is, it should be okay to omit it - /// at run time. That means it must not have any side effects on non-ghost variables, for example. - /// - public void ResolveStatement(Statement stmt, bool specContextOnly, ICodeContext codeContext) { + public void ResolveStatement(Statement stmt, ICodeContext codeContext) { Contract.Requires(stmt != null); Contract.Requires(codeContext != null); if (!(stmt is ForallStmt)) { // forall statements do their own attribute resolution below @@ -5328,13 +5361,13 @@ namespace Microsoft.Dafny } s.hiddenUpdate = new UpdateStmt(s.Tok, s.EndTok, formals, s.rhss, true); // resolving the update statement will check for return/yield statement specifics. - ResolveStatement(s.hiddenUpdate, specContextOnly, codeContext); + ResolveStatement(s.hiddenUpdate, codeContext); } } else {// this is a regular return/yield statement. s.hiddenUpdate = null; } } else if (stmt is ConcreteUpdateStatement) { - ResolveConcreteUpdateStmt((ConcreteUpdateStatement)stmt, specContextOnly, codeContext); + ResolveConcreteUpdateStmt((ConcreteUpdateStatement)stmt, codeContext); } else if (stmt is VarDeclStmt) { var s = (VarDeclStmt)stmt; // We have three cases. @@ -5370,7 +5403,7 @@ namespace Microsoft.Dafny lhs.Type = local.Type; } // resolve the whole thing - ResolveConcreteUpdateStmt(s.Update, specContextOnly, codeContext); + ResolveConcreteUpdateStmt(s.Update, codeContext); } // Add the locals to the scope foreach (var local in s.Locals) { @@ -5382,7 +5415,7 @@ namespace Microsoft.Dafny } // Resolve the AssignSuchThatStmt, if any if (s.Update is AssignSuchThatStmt) { - ResolveConcreteUpdateStmt(s.Update, specContextOnly, codeContext); + ResolveConcreteUpdateStmt(s.Update, codeContext); } // Update the VarDeclStmt's ghost status according to its components foreach (var local in s.Locals) @@ -5481,52 +5514,40 @@ namespace Microsoft.Dafny } else if (stmt is BlockStmt) { var s = (BlockStmt)stmt; scope.PushMarker(); - ResolveBlockStatement(s, specContextOnly, codeContext); + ResolveBlockStatement(s, codeContext); scope.PopMarker(); } else if (stmt is IfStmt) { IfStmt s = (IfStmt)stmt; - bool branchesAreSpecOnly = specContextOnly; if (s.Guard != null) { - int prevErrorCount = reporter.Count(ErrorLevel.Error); ResolveExpression(s.Guard, new ResolveOpts(codeContext, true)); Contract.Assert(s.Guard.Type != null); // follows from postcondition of ResolveExpression - bool successfullyResolved = reporter.Count(ErrorLevel.Error) == prevErrorCount; ConstrainTypes(s.Guard.Type, Type.Bool, s.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Guard.Type); - if (!specContextOnly && successfullyResolved) { - branchesAreSpecOnly = UsesSpecFeatures(s.Guard); - } } - ResolveStatement(s.Thn, branchesAreSpecOnly, codeContext); + ResolveStatement(s.Thn, codeContext); if (s.Els != null) { - ResolveStatement(s.Els, branchesAreSpecOnly, codeContext); + ResolveStatement(s.Els, codeContext); } } else if (stmt is AlternativeStmt) { var s = (AlternativeStmt)stmt; - ResolveAlternatives(s.Alternatives, specContextOnly, null, codeContext); + ResolveAlternatives(s.Alternatives, null, codeContext); } else if (stmt is WhileStmt) { WhileStmt s = (WhileStmt)stmt; - bool bodyMustBeSpecOnly = specContextOnly; var fvs = new HashSet(); if (s.Guard != null) { - int prevErrorCount = reporter.Count(ErrorLevel.Error); ResolveExpression(s.Guard, new ResolveOpts(codeContext, true)); Contract.Assert(s.Guard.Type != null); // follows from postcondition of ResolveExpression - bool successfullyResolved = reporter.Count(ErrorLevel.Error) == prevErrorCount; Translator.ComputeFreeVariables(s.Guard, fvs); ConstrainTypes(s.Guard.Type, Type.Bool, s.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Guard.Type); - if (!specContextOnly && successfullyResolved) { - bodyMustBeSpecOnly = UsesSpecFeatures(s.Guard); - } } ResolveLoopSpecificationComponents(s.Invariants, s.Decreases, s.Mod, codeContext, fvs); if (s.Body != null) { loopStack.Add(s); // push - ResolveStatement(s.Body, bodyMustBeSpecOnly, codeContext); + ResolveStatement(s.Body, codeContext); loopStack.RemoveAt(loopStack.Count - 1); // pop } else { string text = "havoc {" + Util.Comma(", ", fvs, fv => fv.Name) + "};"; // always terminate with a semi-colon @@ -5535,7 +5556,7 @@ namespace Microsoft.Dafny } else if (stmt is AlternativeLoopStmt) { var s = (AlternativeLoopStmt)stmt; - ResolveAlternatives(s.Alternatives, specContextOnly, s, codeContext); + ResolveAlternatives(s.Alternatives, s, codeContext); ResolveLoopSpecificationComponents(s.Invariants, s.Decreases, s.Mod, codeContext, null); } else if (stmt is ForallStmt) { @@ -5559,23 +5580,13 @@ namespace Microsoft.Dafny // first (above) and only then resolve the attributes (below). ResolveAttributes(s.Attributes, new ResolveOpts(codeContext, true)); - bool bodyMustBeSpecOnly = specContextOnly || (prevErrorCount == reporter.Count(ErrorLevel.Error) && UsesSpecFeatures(s.Range)); - if (!bodyMustBeSpecOnly && prevErrorCount == reporter.Count(ErrorLevel.Error)) { - CheckTypeInference(s.Range); // we need to resolve operators before the call to DiscoverBounds - List missingBounds; - s.Bounds = DiscoverBestBounds_MultipleVars(s.BoundVars, s.Range, true, true, out missingBounds); - if (missingBounds.Count != 0) { - bodyMustBeSpecOnly = true; - } - } - if (s.Body != null) { // clear the labels for the duration of checking the body, because break statements are not allowed to leave a forall statement var prevLblStmts = labeledStatements; var prevLoopStack = loopStack; labeledStatements = new Scope(); loopStack = new List(); - ResolveStatement(s.Body, bodyMustBeSpecOnly, codeContext); + ResolveStatement(s.Body, codeContext); labeledStatements = prevLblStmts; loopStack = prevLoopStack; } @@ -5627,7 +5638,7 @@ namespace Microsoft.Dafny ResolveFrameExpression(fe, false, codeContext); } if (s.Body != null) { - ResolveBlockStatement(s.Body, specContextOnly, codeContext); + ResolveBlockStatement(s.Body, codeContext); } } else if (stmt is CalcStmt) { @@ -5658,7 +5669,7 @@ namespace Microsoft.Dafny labeledStatements = new Scope(); loopStack = new List(); foreach (var h in s.Hints) { - ResolveStatement(h, true, codeContext); + ResolveStatement(h, codeContext); } labeledStatements = prevLblStmts; loopStack = prevLoopStack; @@ -5675,13 +5686,13 @@ namespace Microsoft.Dafny Contract.Assert(prevErrorCount != reporter.Count(ErrorLevel.Error) || s.Steps.Count == s.Hints.Count); } else if (stmt is MatchStmt) { - ResolveMatchStmt(stmt, specContextOnly, codeContext); + ResolveMatchStmt(stmt, codeContext); } else if (stmt is SkeletonStatement) { var s = (SkeletonStatement)stmt; reporter.Error(MessageSource.Resolver, s.Tok, "skeleton statements are allowed only in refining methods"); // nevertheless, resolve the underlying statement; hey, why not if (s.S != null) { - ResolveStatement(s.S, specContextOnly, codeContext); + ResolveStatement(s.S, codeContext); } } else { Contract.Assert(false); throw new cce.UnreachableException(); @@ -5726,18 +5737,12 @@ namespace Microsoft.Dafny } } - void ResolveMatchStmt(Statement stmt, bool specContextOnly, ICodeContext codeContext) { + void ResolveMatchStmt(Statement stmt, ICodeContext codeContext) { MatchStmt s = (MatchStmt)stmt; DesugarMatchStmtWithTupleExpression(s); - bool bodyIsSpecOnly = specContextOnly; - int prevErrorCount = reporter.Count(ErrorLevel.Error); ResolveExpression(s.Source, new ResolveOpts(codeContext, true)); Contract.Assert(s.Source.Type != null); // follows from postcondition of ResolveExpression - bool successfullyResolved = reporter.Count(ErrorLevel.Error) == prevErrorCount; - if (!specContextOnly && successfullyResolved) { - bodyIsSpecOnly = UsesSpecFeatures(s.Source); - } UserDefinedType sourceType = null; DatatypeDecl dtd = null; if (s.Source.Type.IsDatatype) { @@ -5814,7 +5819,7 @@ namespace Microsoft.Dafny } } foreach (Statement ss in mc.Body) { - ResolveStatement(ss, bodyIsSpecOnly, codeContext); + ResolveStatement(ss, codeContext); } // substitute body to replace the case pat with v. This needs to happen // after the body is resolved so we can scope the bv correctly. @@ -5824,7 +5829,7 @@ namespace Microsoft.Dafny foreach (Statement ss in mc.Body) { Statement clone = cloner.CloneStmt(ss); // resolve it again since we just cloned it. - ResolveStatement(clone, bodyIsSpecOnly, codeContext); + ResolveStatement(clone, codeContext); list.Add(clone); } mc.UpdateBody(list); @@ -6242,7 +6247,7 @@ namespace Microsoft.Dafny reporter.Info(MessageSource.Resolver, loopStmt.Tok, s); } } - private void ResolveConcreteUpdateStmt(ConcreteUpdateStatement s, bool specContextOnly, ICodeContext codeContext) { + private void ResolveConcreteUpdateStmt(ConcreteUpdateStatement s, ICodeContext codeContext) { Contract.Requires(codeContext != null); // First, resolve all LHS's and expression-looking RHS's. @@ -6265,7 +6270,7 @@ namespace Microsoft.Dafny var suchThat = (AssignSuchThatStmt)s; // this is the other possible subclass ResolveAssignSuchThatStmt(suchThat, codeContext); } else { - ResolveUpdateStmt(update, specContextOnly, codeContext, errorCountBeforeCheckingLhs); + ResolveUpdateStmt(update, codeContext, errorCountBeforeCheckingLhs); } ResolveAttributes(s.Attributes, new ResolveOpts(codeContext, true)); } @@ -6274,7 +6279,7 @@ namespace Microsoft.Dafny /// errorCountBeforeCheckingLhs is passed in so that this method can determined if any resolution errors were found during /// LHS or RHS checking, because only if no errors were found is update.ResolvedStmt changed. /// - private void ResolveUpdateStmt(UpdateStmt update, bool specContextOnly, ICodeContext codeContext, int errorCountBeforeCheckingLhs) { + private void ResolveUpdateStmt(UpdateStmt update, ICodeContext codeContext, int errorCountBeforeCheckingLhs) { Contract.Requires(update != null); Contract.Requires(codeContext != null); IToken firstEffectfulRhs = null; @@ -6367,7 +6372,7 @@ namespace Microsoft.Dafny } foreach (var a in update.ResolvedStatements) { - ResolveStatement(a, specContextOnly, codeContext); + ResolveStatement(a, codeContext); } } @@ -6393,7 +6398,7 @@ namespace Microsoft.Dafny ConstrainTypes(s.Expr.Type, Type.Bool, s.Expr, "type of RHS of assign-such-that statement must be boolean (got {0})", s.Expr.Type); } - void ResolveAlternatives(List alternatives, bool specContextOnly, AlternativeLoopStmt loopToCatchBreaks, ICodeContext codeContext) { + void ResolveAlternatives(List alternatives, AlternativeLoopStmt loopToCatchBreaks, ICodeContext codeContext) { Contract.Requires(alternatives != null); Contract.Requires(codeContext != null); @@ -6412,7 +6417,7 @@ namespace Microsoft.Dafny foreach (var alternative in alternatives) { scope.PushMarker(); foreach (Statement ss in alternative.Body) { - ResolveStatement(ss, specContextOnly, codeContext); + ResolveStatement(ss, codeContext); } scope.PopMarker(); } @@ -6541,7 +6546,7 @@ namespace Microsoft.Dafny } } - void ResolveBlockStatement(BlockStmt blockStmt, bool specContextOnly, ICodeContext codeContext) { + void ResolveBlockStatement(BlockStmt blockStmt, ICodeContext codeContext) { Contract.Requires(blockStmt != null); Contract.Requires(codeContext != null); @@ -6561,7 +6566,7 @@ namespace Microsoft.Dafny Contract.Assert(r == Scope.PushResult.Success); // since we just checked for duplicates, we expect the Push to succeed } } - ResolveStatement(ss, specContextOnly, codeContext); + ResolveStatement(ss, codeContext); labeledStatements.PopMarker(); } } @@ -7878,25 +7883,6 @@ namespace Microsoft.Dafny allTypeParameters.PopMarker(); expr.Type = Type.Bool; - if (prevErrorCount == reporter.Count(ErrorLevel.Error)) { - CheckTypeInference(e.LogicalBody()); // we need to resolve operators before the call to DiscoverBounds - List missingBounds; - e.Bounds = DiscoverBestBounds_MultipleVars(e.BoundVars, e.LogicalBody(), e is ExistsExpr, true, out missingBounds); - if (missingBounds.Count != 0) { - 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); - } - } - } - } - } else if (expr is SetComprehension) { var e = (SetComprehension)expr; int prevErrorCount = reporter.Count(ErrorLevel.Error); @@ -7936,19 +7922,6 @@ namespace Microsoft.Dafny scope.PopMarker(); expr.Type = new MapType(e.Finite, e.BoundVars[0].Type, e.Term.Type); - if (prevErrorCount == reporter.Count(ErrorLevel.Error)) { - 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, expr, "a map comprehension must produce a finite domain, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name); - } - } - } - } } else if (expr is LambdaExpr) { var e = (LambdaExpr)expr; int prevErrorCount = reporter.Count(ErrorLevel.Error); @@ -7977,7 +7950,7 @@ namespace Microsoft.Dafny } else if (expr is StmtExpr) { var e = (StmtExpr)expr; int prevErrorCount = reporter.Count(ErrorLevel.Error); - ResolveStatement(e.S, true, opts.codeContext); + ResolveStatement(e.S, opts.codeContext); if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { var r = e.S as UpdateStmt; if (r != null && r.ResolvedStatements.Count == 1) { @@ -10335,21 +10308,19 @@ namespace Microsoft.Dafny } } else if (expr is NamedExpr) { return moduleInfo.IsAbstract ? false : UsesSpecFeatures(((NamedExpr)expr).Body); - } else if (expr is ComprehensionExpr) { - var q = expr as QuantifierExpr; - Contract.Assert(q == null || q.SplitQuantifier == null); // No split quantifiers during resolution - if (q != null && q.Bounds.Contains(null)) { - return true; // the quantifier cannot be compiled if the resolver found no bounds - } - return Contract.Exists(expr.SubExpressions, se => UsesSpecFeatures(se)); + } else if (expr is QuantifierExpr) { + var e = (QuantifierExpr)expr; + Contract.Assert(e.SplitQuantifier == null); // No split quantifiers during resolution + return e.UncompilableBoundVars().Count != 0; } else if (expr is SetComprehension) { var e = (SetComprehension)expr; - return (e.Range != null && UsesSpecFeatures(e.Range)) || (e.Term != null && UsesSpecFeatures(e.Term)); + return !e.Finite || e.UncompilableBoundVars().Count != 0 || (e.Range != null && UsesSpecFeatures(e.Range)) || (e.Term != null && UsesSpecFeatures(e.Term)); } else if (expr is MapComprehension) { var e = (MapComprehension)expr; - return (UsesSpecFeatures(e.Range)) || (UsesSpecFeatures(e.Term)); + return !e.Finite || e.UncompilableBoundVars().Count != 0 || UsesSpecFeatures(e.Range) || UsesSpecFeatures(e.Term); } else if (expr is LambdaExpr) { - return Contract.Exists(expr.SubExpressions, UsesSpecFeatures); + var e = (LambdaExpr)expr; + return UsesSpecFeatures(e.Term); } else if (expr is WildcardExpr) { return false; } else if (expr is StmtExpr) { diff --git a/Test/dafny0/ResolutionErrors.dfy b/Test/dafny0/ResolutionErrors.dfy index 49e6efa0..e935c83d 100644 --- a/Test/dafny0/ResolutionErrors.dfy +++ b/Test/dafny0/ResolutionErrors.dfy @@ -586,16 +586,16 @@ method LetSuchThat(ghost z: int, n: nat) module NonInferredType { predicate P(x: T) - method NonInferredType0(x: int) + method InferredType(x: int) { var t; - assume forall z :: P(z) && z == t; // It would be nice to allow the following example, but the implementation calls DiscoverBounds before CheckInference for quantifiers. + assume forall z :: P(z) && z == t; assume t == x; // this statement determines the type of t and z } - method NonInferredType1(x: int) + method NonInferredType(x: int) { - var t; + var t; // error: the type of t is not determined assume forall z :: P(z) && z == t; // error: the type of z is not determined } } @@ -1126,15 +1126,15 @@ method TraitSynonym() // ----- set comprehensions where the term type is finite ----- module ObjectSetComprehensions { - // allowed in non-ghost context: - function A() : set { set o : object | true :: o } + // the following set comprehensions are known to be finite + function A() : set { set o : object | true :: o } // error: a function is not allowed to depend on the allocated state - lemma B() { var x := set o : object | true :: o; } + function method B() : set { set o : object | true :: o } // error: a function is not allowed to depend on the allocated state - // not allowed in non-ghost context: - function method C() : set { set o : object | true :: o } + // outside functions, the comprehension is permitted, but it cannot be compiled + lemma C() { var x := set o : object | true :: o; } - method D() { var x := set o : object | true :: o; } + method D() { var x := set o : object | true :: o; } // error: not (easily) compilable } // ------ regression test for type checking of integer division ----- @@ -1228,9 +1228,9 @@ module NonInferredTypeVariables { method BadClient(n: nat) { var p := P(n); // error: cannot infer the type argument for P - ghost var q := Q(n); // error: cannot infer the type argument for Q + ghost var q := Q(n); // error: cannot infer the type argument for Q (and thus q's type cannot be determined either) M(n); // error: cannot infer the type argument for M - var x := N(n); // error: cannot infer the type argument for N + var x := N(n); // error: cannot infer the type argument for N (and thus x's type cannot be determined either) var a := new array; // error: cannot infer the type argument for 'array' var c := new C; // error: cannot infer the type argument for 'C' var s: set; // type argument for 'set' @@ -1248,7 +1248,7 @@ module NonInferredTypeVariables { ghost var d0 := forall s :: s == {7} ==> s != {}; var d1 := forall s: set :: s in S ==> s == {}; var ggcc0: C; - var ggcc1: C; + var ggcc1: C; // error: full type cannot be determined ghost var d2 := forall c: C :: c != null ==> c.f == 10; ghost var d2' := forall c :: c == ggcc0 && c != null ==> c.f == 10; ghost var d2'' := forall c :: c == ggcc1 && c != null ==> c.f == c.f; // error: here, type of c is not determined diff --git a/Test/dafny0/ResolutionErrors.dfy.expect b/Test/dafny0/ResolutionErrors.dfy.expect index edf61b33..be19eeac 100644 --- a/Test/dafny0/ResolutionErrors.dfy.expect +++ b/Test/dafny0/ResolutionErrors.dfy.expect @@ -20,10 +20,9 @@ ResolutionErrors.dfy(535,7): Error: RHS (of type List) not assignable to LHS ResolutionErrors.dfy(540,7): Error: RHS (of type List) not assignable to LHS (of type List) ResolutionErrors.dfy(554,23): Error: type of case bodies do not agree (found Tree<_T1,_T0>, previous types Tree<_T0,_T1>) ResolutionErrors.dfy(566,24): Error: Wrong number of type arguments (0 instead of 2) passed to datatype: Tree -ResolutionErrors.dfy(592,25): Error: the type of this variable is underspecified -ResolutionErrors.dfy(592,23): Error: type variable 'T' in the function call to 'P' could not be determined -ResolutionErrors.dfy(599,25): Error: the type of this variable is underspecified +ResolutionErrors.dfy(598,8): Error: the type of this local variable is underspecified ResolutionErrors.dfy(599,23): Error: type variable 'T' in the function call to 'P' could not be determined +ResolutionErrors.dfy(599,18): Error: type of bound variable 'z' could not be determined; please specify the type explicitly ResolutionErrors.dfy(612,13): Error: 'new' is not allowed in ghost contexts ResolutionErrors.dfy(613,9): Error: 'new' is not allowed in ghost contexts ResolutionErrors.dfy(622,23): Error: 'new' is not allowed in ghost contexts @@ -81,12 +80,28 @@ ResolutionErrors.dfy(1105,6): Error: RHS (of type P) not assignable to LHS ResolutionErrors.dfy(1110,13): Error: arguments must have the same type (got P and P) ResolutionErrors.dfy(1111,13): Error: arguments must have the same type (got P and P) ResolutionErrors.dfy(1112,13): Error: arguments must have the same type (got P and P) -ResolutionErrors.dfy(1135,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(1130,31): Error: a set comprehension 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 'o' +ResolutionErrors.dfy(1132,38): Error: a set comprehension 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 'o' ResolutionErrors.dfy(1137,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(1230,13): Error: type variable 'PT' in the function call to 'P' could not be determined +ResolutionErrors.dfy(1231,14): Error: the type of this variable is underspecified +ResolutionErrors.dfy(1231,19): Error: type variable 'QT' in the function call to 'Q' could not be determined +ResolutionErrors.dfy(1232,4): Error: type '?' to the method 'M' is not determined +ResolutionErrors.dfy(1233,8): Error: the type of this variable is underspecified +ResolutionErrors.dfy(1233,13): Error: type '?' to the method 'N' is not determined +ResolutionErrors.dfy(1234,8): Error: the type of this variable is underspecified +ResolutionErrors.dfy(1235,8): Error: the type of this variable is underspecified +ResolutionErrors.dfy(1236,8): Error: the type of this local variable is underspecified +ResolutionErrors.dfy(1237,8): Error: the type of this variable is underspecified +ResolutionErrors.dfy(1238,8): Error: the type of this local variable is underspecified ResolutionErrors.dfy(1242,26): Error: the type of this variable is underspecified +ResolutionErrors.dfy(1242,21): Error: type of bound variable 's' could not be determined; please specify the type explicitly ResolutionErrors.dfy(1243,31): Error: the type of this variable is underspecified +ResolutionErrors.dfy(1243,21): Error: type of bound variable 's' could not be determined; please specify the type explicitly ResolutionErrors.dfy(1244,29): Error: the type of this variable is underspecified -ResolutionErrors.dfy(1254,34): Error: the type of this variable is underspecified +ResolutionErrors.dfy(1244,21): Error: type of bound variable 'c' could not be determined; please specify the type explicitly +ResolutionErrors.dfy(1251,8): Error: the type of this local variable is underspecified +ResolutionErrors.dfy(1254,29): Error: type of bound variable 'c' could not be determined; please specify the type explicitly ResolutionErrors.dfy(1270,21): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') ResolutionErrors.dfy(1271,24): Error: Undeclared top-level type or type parameter: X (did you forget to qualify a name or declare a module import 'opened?') ResolutionErrors.dfy(1308,16): Error: in a ghost context, only ghost fields can be mentioned as modifies frame targets (y) @@ -209,4 +224,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 -211 resolution/type errors detected in ResolutionErrors.dfy +226 resolution/type errors detected in ResolutionErrors.dfy diff --git a/Test/dafny4/Regression0.dfy b/Test/dafny4/Regression0.dfy index be092261..666d9575 100644 --- a/Test/dafny4/Regression0.dfy +++ b/Test/dafny4/Regression0.dfy @@ -4,10 +4,10 @@ // This once crashed Dafny method M() { - var s := [1, "2"]; + var s := [1, "2"]; // error: all elements must have the same type if * { - assert exists n :: n in s && n != 1; + assert exists n :: n in s && n != 1; // the type of n is inferred to be int } else { - assert "2" in s; + assert "2" in s; // error: since the type of s wasn't determined } } diff --git a/Test/dafny4/Regression0.dfy.expect b/Test/dafny4/Regression0.dfy.expect index 9d1e3019..566b3e3f 100644 --- a/Test/dafny4/Regression0.dfy.expect +++ b/Test/dafny4/Regression0.dfy.expect @@ -1,4 +1,3 @@ Regression0.dfy(7,15): Error: All elements of display must be of the same type (got string, but type of previous elements is int) -Regression0.dfy(9,28): Error: the type of this variable is underspecified Regression0.dfy(11,15): Error: second argument to "in" must be a set, multiset, or sequence with elements of type string, or a map with domain string (instead got ?) -3 resolution/type errors detected in Regression0.dfy +2 resolution/type errors detected in Regression0.dfy diff --git a/Test/dafny4/set-compr.dfy b/Test/dafny4/set-compr.dfy index 71a07f3d..d093a924 100644 --- a/Test/dafny4/set-compr.dfy +++ b/Test/dafny4/set-compr.dfy @@ -22,7 +22,7 @@ method O() returns (ghost p: set) method P() returns (p: set) { - p := set o: object | true; // not allowed -- not in a ghost context + p := set o: object | true; // error: not (easily) compilable } ghost method Q() returns (p: set) @@ -30,26 +30,54 @@ ghost method Q() returns (p: set) p := set o: object | true; // allowed, since the whole method is ghost } -function F(): int +function F(p: object): int + requires p in set o: object | true // error: function is not allowed to depend on allocation state + ensures p in set o: object | true // error: ditto (although one could argue that this would be okay) + reads set o: object | true // error: same as for 'requires' + decreases set o: object | true // error: same as for 'ensures' +{ + if p in set o: object | true then // error: function is not allowed to depend on allocation state + F(p) + else + 0 +} + +function method G(p: object): int + requires p in set o: object | true // error (see F) + ensures p in set o: object | true // error (see F) + reads set o: object | true // error (see F) + decreases set o: object | true // error (see F) +{ + if p in set o: object | true then // error (see F) + G(p) + else + 0 +} + +method M0() returns (ghost r: int, s: int) requires null in set o: object | true // allowed ensures null in set o: object | true // allowed - reads set o: object | true // allowed + modifies set o: object | true // allowed decreases set o: object | true // allowed { - if null in set o: object | true then // allowed -- in a ghost context - F() - else - 0 + if null in set o: object | true { // this makes the "if" a ghost + r := G(null); + s := G(null); // error: assignment of non-ghost not allowed inside ghost "if" + } else { + r := 0; + } } -function method G(): int +method M1() returns (ghost r: int, s: int) requires null in set o: object | true // (X) allowed ensures null in set o: object | true // (X) allowed - reads set o: object | true // allowed + modifies set o: object | true // allowed decreases set o: object | true // (X) allowed { - if null in set o: object | true then // not allowed, since this is not a ghost context - G() - else - 0 + if null in set o: object | true { // this makes the "if" a ghost + r := G(null); + s := G(null); // error: assignment of non-ghost not allowed inside ghost "if" + } else { + r := 0; + } } diff --git a/Test/dafny4/set-compr.dfy.expect b/Test/dafny4/set-compr.dfy.expect index 615ee2bc..b0490a11 100644 --- a/Test/dafny4/set-compr.dfy.expect +++ b/Test/dafny4/set-compr.dfy.expect @@ -1,3 +1,14 @@ 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 +set-compr.dfy(34,16): Error: a set comprehension 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 'o' +set-compr.dfy(35,15): Error: a set comprehension 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 'o' +set-compr.dfy(36,8): Error: a set comprehension 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 'o' +set-compr.dfy(37,12): Error: a set comprehension 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 'o' +set-compr.dfy(39,10): Error: a set comprehension 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 'o' +set-compr.dfy(46,16): Error: a set comprehension 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 'o' +set-compr.dfy(47,15): Error: a set comprehension 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 'o' +set-compr.dfy(48,8): Error: a set comprehension 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 'o' +set-compr.dfy(49,12): Error: a set comprehension 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 'o' +set-compr.dfy(51,10): Error: a set comprehension 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 'o' +set-compr.dfy(65,6): 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) +set-compr.dfy(79,6): 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) +13 resolution/type errors detected in set-compr.dfy diff --git a/Test/hofs/ReadsReads.dfy b/Test/hofs/ReadsReads.dfy index a6f8d922..60ac35f5 100644 --- a/Test/hofs/ReadsReads.dfy +++ b/Test/hofs/ReadsReads.dfy @@ -105,14 +105,14 @@ module WhatWeKnowAboutReads { module ReadsAll { function A(f: int -> int) : int - reads set o,x | o in f.reads(x) :: o + reads set x,o | o in f.reads(x) :: o // note, with "set o,x ..." instead, Dafny complains (this is perhaps less than ideal) requires forall x :: f.requires(x) { f(0) + f(1) + f(2) } function method B(f: int -> int) : int - reads set o,x | o in f.reads(x) :: o + reads set x,o | o in f.reads(x) :: o // note, with "set o,x ..." instead, Dafny complains (this is perhaps less than ideal) requires forall x :: f.requires(x) { f(0) + f(1) + f(2) -- cgit v1.2.3 From 69ed5bac5efbc0ac64f26b6dadf81bcfec9a0b5b Mon Sep 17 00:00:00 2001 From: wuestholz Date: Tue, 29 Sep 2015 17:45:10 +0200 Subject: Fix the build. --- Source/DafnyDriver/DafnyDriver.cs | 6 +++--- Source/DafnyServer/DafnyServer.csproj | 13 ++++++++----- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Source/DafnyDriver/DafnyDriver.cs b/Source/DafnyDriver/DafnyDriver.cs index c45d66fc..8282edc7 100644 --- a/Source/DafnyDriver/DafnyDriver.cs +++ b/Source/DafnyDriver/DafnyDriver.cs @@ -226,8 +226,8 @@ namespace Microsoft.Dafny stats = new PipelineStatistics(); LinearTypeChecker ltc; - MoverTypeChecker mtc; - PipelineOutcome oc = ExecutionEngine.ResolveAndTypecheck(program, bplFileName, out ltc, out mtc); + CivlTypeChecker ctc; + PipelineOutcome oc = ExecutionEngine.ResolveAndTypecheck(program, bplFileName, out ltc, out ctc); switch (oc) { case PipelineOutcome.Done: return oc; @@ -244,7 +244,7 @@ namespace Microsoft.Dafny fileNames.Add(bplFileName); Bpl.Program reparsedProgram = ExecutionEngine.ParseBoogieProgram(fileNames, true); if (reparsedProgram != null) { - ExecutionEngine.ResolveAndTypecheck(reparsedProgram, bplFileName, out ltc, out mtc); + ExecutionEngine.ResolveAndTypecheck(reparsedProgram, bplFileName, out ltc, out ctc); } } return oc; diff --git a/Source/DafnyServer/DafnyServer.csproj b/Source/DafnyServer/DafnyServer.csproj index 1a256f5f..af262fc3 100644 --- a/Source/DafnyServer/DafnyServer.csproj +++ b/Source/DafnyServer/DafnyServer.csproj @@ -1,4 +1,4 @@ - + @@ -84,9 +84,6 @@ - - ..\..\Binaries\DafnyPipeline.dll - @@ -99,6 +96,12 @@ + + + {fe44674a-1633-4917-99f4-57635e6fa740} + DafnyPipeline + + - + \ No newline at end of file -- cgit v1.2.3 From 13dcb53b7dde21f887d87c47608f81082e7efbb3 Mon Sep 17 00:00:00 2001 From: wuestholz Date: Wed, 30 Sep 2015 03:12:53 +0200 Subject: Fix two test cases that failed if the path to "DafnySever.exe" contained spaces. --- Test/server/minimal.transcript | 2 +- Test/server/simple-session.transcript | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Test/server/minimal.transcript b/Test/server/minimal.transcript index 9625fb00..394fd921 100644 --- a/Test/server/minimal.transcript +++ b/Test/server/minimal.transcript @@ -1,4 +1,4 @@ -# RUN: %server "%s" > "%t" +# RUN: "%server" "%s" > "%t" # RUN: %diff "%s.expect" "%t" verify eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp diff --git a/Test/server/simple-session.transcript b/Test/server/simple-session.transcript index 26539267..d095f6dd 100644 --- a/Test/server/simple-session.transcript +++ b/Test/server/simple-session.transcript @@ -1,4 +1,4 @@ -# RUN: %server "%s" > "%t" +# RUN: "%server" "%s" > "%t" # RUN: %diff "%s.expect" "%t" verify eyJhcmdzIjpbIi9jb21waWxlOjAiLCIvbm9sb2dvIiwiL3ByaW50VG9vbHRpcHMiLCIvdGltZUxp -- cgit v1.2.3 From d6d0062d4fd25d733d97e02ea65d9653f7d77175 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 30 Sep 2015 13:30:43 -0700 Subject: Removed some unused code. --- Source/Dafny/Resolver.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 0b0bbf26..36464925 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -4406,12 +4406,8 @@ namespace Microsoft.Dafny var k = com.PrefixLemma.Ins[0]; scope.Push(k.Name, k); // we expect no name conflict for _k } - var prevErrorCount = reporter.Count(ErrorLevel.Error); ResolveBlockStatement(m.Body, m); SolveAllTypeConstraints(); - if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { -//KRML ComputeGhostInterest(m.Body, m); - } } // attributes are allowed to mention both in- and out-parameters (including the implicit _k, for colemmas) @@ -4530,9 +4526,6 @@ namespace Microsoft.Dafny // Resolve body if (iter.Body != null) { ResolveBlockStatement(iter.Body, iter); - if (reporter.Count(ErrorLevel.Error) == postSpecErrorCount) { - //KRML ComputeGhostInterest(iter.Body, iter); - } } currentClass = null; -- cgit v1.2.3 From c5a1c58d3c89c55c31331cb419cd3c06e276b5dd Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 2 Oct 2015 14:46:50 -0700 Subject: Fixed latent crash of hovertext/outlining with include. (This also undoes two previous attempted fixes, which had accidentally disabled some outlining and hovertexts.) --- Source/DafnyExtension/IdentifierTagger.cs | 146 +++++++++++++++++------------- Source/DafnyExtension/OutliningTagger.cs | 30 +++--- 2 files changed, 103 insertions(+), 73 deletions(-) diff --git a/Source/DafnyExtension/IdentifierTagger.cs b/Source/DafnyExtension/IdentifierTagger.cs index d638cb6c..51f837f7 100644 --- a/Source/DafnyExtension/IdentifierTagger.cs +++ b/Source/DafnyExtension/IdentifierTagger.cs @@ -77,7 +77,7 @@ namespace DafnyLanguage int start = entire.Start; int end = entire.End; foreach (var r in _regions) { - if (0 <= r.Length && r.Start >= start && r.Start + r.Length <= end) { + if (0 <= r.Length && r.Start <= end && start <= r.Start + r.Length) { DafnyTokenKind kind; switch (r.Kind) { case IdRegion.OccurrenceKind.Use: @@ -136,7 +136,7 @@ namespace DafnyLanguage List newRegions = new List(); foreach (var info in program.reporter.AllMessages[ErrorLevel.Info]) { - IdRegion.Add(newRegions, info.token, info.message, info.token.val.Length); + IdRegion.Add(newRegions, program, info.token, info.message, info.token.val.Length); } foreach (var module in program.Modules) { @@ -149,29 +149,29 @@ namespace DafnyLanguage foreach (var ctor in dt.Ctors) { foreach (var dtor in ctor.Destructors) { if (dtor.CorrespondingFormal.HasName) { - IdRegion.Add(newRegions, dtor.tok, dtor, null, "destructor", true, module); + IdRegion.Add(newRegions, program, dtor.tok, dtor, null, "destructor", true, module); } } } } else if (d is IteratorDecl) { var iter = (IteratorDecl)d; foreach (var p in iter.Ins) { - IdRegion.Add(newRegions, p.tok, p, true, module); + IdRegion.Add(newRegions, program, p.tok, p, true, module); } foreach (var p in iter.Outs) { - IdRegion.Add(newRegions, p.tok, p, true, "yield-parameter", module); + IdRegion.Add(newRegions, program, p.tok, p, true, "yield-parameter", module); } - iter.Reads.Expressions.ForEach(fe => FrameExprRegions(fe, newRegions, true, module)); - iter.Modifies.Expressions.ForEach(fe => FrameExprRegions(fe, newRegions, true, module)); - iter.Requires.ForEach(e => ExprRegions(e.E, newRegions, module)); - iter.YieldRequires.ForEach(e => ExprRegions(e.E, newRegions, module)); - iter.YieldEnsures.ForEach(e => ExprRegions(e.E, newRegions, module)); - iter.Ensures.ForEach(e => ExprRegions(e.E, newRegions, module)); + iter.Reads.Expressions.ForEach(fe => FrameExprRegions(fe, newRegions, true, program, module)); + iter.Modifies.Expressions.ForEach(fe => FrameExprRegions(fe, newRegions, true, program, module)); + iter.Requires.ForEach(e => ExprRegions(e.E, newRegions, program, module)); + iter.YieldRequires.ForEach(e => ExprRegions(e.E, newRegions, program, module)); + iter.YieldEnsures.ForEach(e => ExprRegions(e.E, newRegions, program, module)); + iter.Ensures.ForEach(e => ExprRegions(e.E, newRegions, program, module)); if (!((ICallable)iter).InferredDecreases) { - iter.Decreases.Expressions.ForEach(e => ExprRegions(e, newRegions, module)); + iter.Decreases.Expressions.ForEach(e => ExprRegions(e, newRegions, program, module)); } if (iter.Body != null) { - StatementRegions(iter.Body, newRegions, module); + StatementRegions(iter.Body, newRegions, program, module); } } else if (d is ClassDecl) { @@ -182,42 +182,42 @@ namespace DafnyLanguage } else if (member is Function) { var f = (Function)member; foreach (var p in f.Formals) { - IdRegion.Add(newRegions, p.tok, p, true, module); + IdRegion.Add(newRegions, program, p.tok, p, true, module); } - f.Req.ForEach(e => ExprRegions(e, newRegions, module)); - f.Reads.ForEach(fe => FrameExprRegions(fe, newRegions, true, module)); - f.Ens.ForEach(e => ExprRegions(e, newRegions, module)); - f.Decreases.Expressions.ForEach(e => ExprRegions(e, newRegions, module)); + f.Req.ForEach(e => ExprRegions(e, newRegions, program, module)); + f.Reads.ForEach(fe => FrameExprRegions(fe, newRegions, true, program, module)); + f.Ens.ForEach(e => ExprRegions(e, newRegions, program, module)); + f.Decreases.Expressions.ForEach(e => ExprRegions(e, newRegions, program, module)); if (f.Body != null) { - ExprRegions(f.Body, newRegions, module); + ExprRegions(f.Body, newRegions, program, module); } } else if (member is Method) { var m = (Method)member; foreach (var p in m.Ins) { - IdRegion.Add(newRegions, p.tok, p, true, module); + IdRegion.Add(newRegions, program, p.tok, p, true, module); } foreach (var p in m.Outs) { - IdRegion.Add(newRegions, p.tok, p, true, module); + IdRegion.Add(newRegions, program, p.tok, p, true, module); } - m.Req.ForEach(e => ExprRegions(e.E, newRegions, module)); - m.Mod.Expressions.ForEach(fe => FrameExprRegions(fe, newRegions, true, module)); - m.Ens.ForEach(e => ExprRegions(e.E, newRegions, module)); - m.Decreases.Expressions.ForEach(e => ExprRegions(e, newRegions, module)); + m.Req.ForEach(e => ExprRegions(e.E, newRegions, program, module)); + m.Mod.Expressions.ForEach(fe => FrameExprRegions(fe, newRegions, true, program, module)); + m.Ens.ForEach(e => ExprRegions(e.E, newRegions, program, module)); + m.Decreases.Expressions.ForEach(e => ExprRegions(e, newRegions, program, module)); if (m.Body != null) { - StatementRegions(m.Body, newRegions, module); + StatementRegions(m.Body, newRegions, program, module); } } else if (member is SpecialField) { // do nothing } else if (member is Field) { var fld = (Field)member; - IdRegion.Add(newRegions, fld.tok, fld, null, "field", true, module); + IdRegion.Add(newRegions, program, fld.tok, fld, null, "field", true, module); } } } else if (d is NewtypeDecl) { var dd = (NewtypeDecl)d; if (dd.Var != null) { - IdRegion.Add(newRegions, dd.Var.tok, dd.Var, true, module); - ExprRegions(dd.Constraint, newRegions, module); + IdRegion.Add(newRegions, program, dd.Var.tok, dd.Var, true, module); + ExprRegions(dd.Constraint, newRegions, program, module); } } } @@ -228,54 +228,56 @@ namespace DafnyLanguage return true; } - static void FrameExprRegions(FrameExpression fe, List regions, bool descendIntoExpressions, ModuleDefinition module) { + static void FrameExprRegions(FrameExpression fe, List regions, bool descendIntoExpressions, Microsoft.Dafny.Program prog, ModuleDefinition module) { Contract.Requires(fe != null); Contract.Requires(regions != null); + Contract.Requires(prog != null); if (descendIntoExpressions) { - ExprRegions(fe.E, regions, module); + ExprRegions(fe.E, regions, prog, module); } if (fe.Field != null) { Microsoft.Dafny.Type showType = null; // TODO: if we had the instantiated type of this field, that would have been nice to use here (but the Resolver currently does not compute or store the instantiated type for a FrameExpression) - IdRegion.Add(regions, fe.tok, fe.Field, showType, "field", false, module); + IdRegion.Add(regions, prog, fe.tok, fe.Field, showType, "field", false, module); } } - static void ExprRegions(Microsoft.Dafny.Expression expr, List regions, ModuleDefinition module) { + static void ExprRegions(Microsoft.Dafny.Expression expr, List regions, Microsoft.Dafny.Program prog, ModuleDefinition module) { Contract.Requires(expr != null); Contract.Requires(regions != null); + Contract.Requires(prog != null); if (expr is AutoGeneratedExpression) { // do nothing return; } else if (expr is IdentifierExpr) { var e = (IdentifierExpr)expr; - IdRegion.Add(regions, e.tok, e.Var, false, module); + IdRegion.Add(regions, prog, e.tok, e.Var, false, module); } else if (expr is MemberSelectExpr) { var e = (MemberSelectExpr)expr; var field = e.Member as Field; if (field != null) { - IdRegion.Add(regions, e.tok, field, e.Type, "field", false, module); + IdRegion.Add(regions, prog, e.tok, field, e.Type, "field", false, module); } } else if (expr is LetExpr) { var e = (LetExpr)expr; foreach (var bv in e.BoundVars) { - IdRegion.Add(regions, bv.tok, bv, true, module); + IdRegion.Add(regions, prog, bv.tok, bv, true, module); } } else if (expr is ComprehensionExpr) { var e = (ComprehensionExpr)expr; foreach (var bv in e.BoundVars) { - IdRegion.Add(regions, bv.tok, bv, true, module); + IdRegion.Add(regions, prog, bv.tok, bv, true, module); } } else if (expr is MatchExpr) { var e = (MatchExpr)expr; foreach (var kase in e.Cases) { kase.Arguments.ForEach(bv => { - IdRegion.Add(regions, bv.tok, bv, true, module); + IdRegion.Add(regions, prog, bv.tok, bv, true, module); // if the arguments is an encapsulation of different boundvars from nested match cases, // add the boundvars so that they can show up in the IDE correctly if (bv.tok is MatchCaseToken) { MatchCaseToken mt = (MatchCaseToken)bv.tok; foreach(Tuple entry in mt.varList) { - IdRegion.Add(regions, entry.Item1, entry.Item2, entry.Item3, module); + IdRegion.Add(regions, prog, entry.Item1, entry.Item2, entry.Item3, module); } } }); @@ -283,22 +285,23 @@ namespace DafnyLanguage } else if (expr is ChainingExpression) { var e = (ChainingExpression)expr; // Do the subexpressions only once (that is, avoid the duplication that occurs in the desugared form of the ChainingExpression) - e.Operands.ForEach(ee => ExprRegions(ee, regions, module)); + e.Operands.ForEach(ee => ExprRegions(ee, regions, prog, module)); return; // return here, so as to avoid doing the subexpressions below } foreach (var ee in expr.SubExpressions) { - ExprRegions(ee, regions, module); + ExprRegions(ee, regions, prog, module); } } - static void StatementRegions(Statement stmt, List regions, ModuleDefinition module) { + static void StatementRegions(Statement stmt, List regions, Microsoft.Dafny.Program prog, ModuleDefinition module) { Contract.Requires(stmt != null); Contract.Requires(regions != null); + Contract.Requires(prog != null); if (stmt is VarDeclStmt) { var s = (VarDeclStmt)stmt; // Add the variables here, once, and then go directly to the RHS's (without letting the sub-statements re-do the LHS's) foreach (var local in s.Locals) { - IdRegion.Add(regions, local.Tok, local, true, module); + IdRegion.Add(regions, prog, local.Tok, local, true, module); } if (s.Update == null) { // the VarDeclStmt has no associated assignment @@ -306,29 +309,29 @@ namespace DafnyLanguage var upd = (UpdateStmt)s.Update; foreach (var rhs in upd.Rhss) { foreach (var ee in rhs.SubExpressions) { - ExprRegions(ee, regions, module); + ExprRegions(ee, regions, prog, module); } } } else { var upd = (AssignSuchThatStmt)s.Update; - ExprRegions(upd.Expr, regions, module); + ExprRegions(upd.Expr, regions, prog, module); } // we're done, so don't do the sub-statements/expressions again return; } else if (stmt is ForallStmt) { var s = (ForallStmt)stmt; - s.BoundVars.ForEach(bv => IdRegion.Add(regions, bv.tok, bv, true, module)); + s.BoundVars.ForEach(bv => IdRegion.Add(regions, prog, bv.tok, bv, true, module)); } else if (stmt is MatchStmt) { var s = (MatchStmt)stmt; foreach (var kase in s.Cases) { kase.Arguments.ForEach(bv => { - IdRegion.Add(regions, bv.tok, bv, true, module); + IdRegion.Add(regions, prog, bv.tok, bv, true, module); // if the arguments is an encapsulation of different boundvars from nested match cases, // add the boundvars so that they can show up in the IDE correctly if (bv.tok is MatchCaseToken) { MatchCaseToken mt = (MatchCaseToken)bv.tok; foreach (Tuple entry in mt.varList) { - IdRegion.Add(regions, entry.Item1, entry.Item2, entry.Item3, module); + IdRegion.Add(regions, prog, entry.Item1, entry.Item2, entry.Item3, module); } } }); @@ -336,28 +339,28 @@ namespace DafnyLanguage } else if (stmt is LoopStmt) { var s = (LoopStmt)stmt; if (s.Mod.Expressions != null) { - s.Mod.Expressions.ForEach(fe => FrameExprRegions(fe, regions, false, module)); + s.Mod.Expressions.ForEach(fe => FrameExprRegions(fe, regions, false, prog, module)); } } else if (stmt is CalcStmt) { var s = (CalcStmt)stmt; // skip the last line, which is just a duplicate anyway for (int i = 0; i < s.Lines.Count - 1; i++) { - ExprRegions(s.Lines[i], regions, module); + ExprRegions(s.Lines[i], regions, prog, module); } foreach (var ss in stmt.SubStatements) { - StatementRegions(ss, regions, module); + StatementRegions(ss, regions, prog, module); } return; } foreach (var ee in stmt.SubExpressions) { - ExprRegions(ee, regions, module); + ExprRegions(ee, regions, prog, module); } foreach (var ss in stmt.SubStatements) { - StatementRegions(ss, regions, module); + StatementRegions(ss, regions, prog, module); } } - class IdRegion + class IdRegion : DafnyRegion { public readonly int Start; public readonly int Length; @@ -366,31 +369,41 @@ namespace DafnyLanguage public readonly OccurrenceKind Kind; public readonly IVariable Variable; - public static void Add(List regions, Bpl.IToken tok, IVariable v, bool isDefinition, ModuleDefinition context) { + public static void Add(List regions, Microsoft.Dafny.Program prog, Bpl.IToken tok, IVariable v, bool isDefinition, ModuleDefinition context) { Contract.Requires(regions != null); + Contract.Requires(prog != null); Contract.Requires(tok != null); Contract.Requires(v != null); - Add(regions, tok, v, isDefinition, null, context); + Add(regions, prog, tok, v, isDefinition, null, context); } - public static void Add(List regions, Bpl.IToken tok, IVariable v, bool isDefinition, string kind, ModuleDefinition context) { + public static void Add(List regions, Microsoft.Dafny.Program prog, Bpl.IToken tok, IVariable v, bool isDefinition, string kind, ModuleDefinition context) { Contract.Requires(regions != null); + Contract.Requires(prog != null); Contract.Requires(tok != null); Contract.Requires(v != null); - regions.Add(new IdRegion(tok, v, isDefinition, kind, context)); + if (InMainFile(prog, tok)) { + regions.Add(new IdRegion(tok, v, isDefinition, kind, context)); + } } - public static void Add(List regions, Bpl.IToken tok, Field decl, Microsoft.Dafny.Type showType, string kind, bool isDefinition, ModuleDefinition context) { + public static void Add(List regions, Microsoft.Dafny.Program prog, Bpl.IToken tok, Field decl, Microsoft.Dafny.Type showType, string kind, bool isDefinition, ModuleDefinition context) { Contract.Requires(regions != null); + Contract.Requires(prog != null); Contract.Requires(tok != null); Contract.Requires(decl != null); Contract.Requires(kind != null); - regions.Add(new IdRegion(tok, decl, showType, kind, isDefinition, context)); + if (InMainFile(prog, tok)) { + regions.Add(new IdRegion(tok, decl, showType, kind, isDefinition, context)); + } } - public static void Add(List regions, Bpl.IToken tok, string text, int length) { + public static void Add(List regions, Microsoft.Dafny.Program prog, Bpl.IToken tok, string text, int length) { Contract.Requires(regions != null); + Contract.Requires(prog != null); Contract.Requires(tok != null); Contract.Requires(text != null); - regions.Add(new IdRegion(tok, OccurrenceKind.AdditionalInformation, text, length)); + if (InMainFile(prog, tok)) { + regions.Add(new IdRegion(tok, OccurrenceKind.AdditionalInformation, text, length)); + } } private IdRegion(Bpl.IToken tok, IVariable v, bool isDefinition, string kind, ModuleDefinition context) { @@ -442,6 +455,15 @@ namespace DafnyLanguage } } + public abstract class DafnyRegion + { + public static bool InMainFile(Microsoft.Dafny.Program prog, Bpl.IToken tok) { + Contract.Requires(prog != null); + Contract.Requires(tok != null); + return object.Equals(prog.FullName, tok.filename); + } + } + #endregion } diff --git a/Source/DafnyExtension/OutliningTagger.cs b/Source/DafnyExtension/OutliningTagger.cs index 85771e94..ea611594 100644 --- a/Source/DafnyExtension/OutliningTagger.cs +++ b/Source/DafnyExtension/OutliningTagger.cs @@ -80,7 +80,7 @@ namespace DafnyLanguage if (start == end) yield break; foreach (var r in _regions) { - if (0 <= r.Length && r.Start >= start && r.Start + r.Length <= end) { + if (0 <= r.Length && r.Start <= end && start <= r.Start + r.Length) { yield return new TagSpan( new SnapshotSpan(_snapshot, r.Start, r.Length), new OutliningRegionTag(false, false, "...", r.HoverText)); @@ -130,18 +130,18 @@ namespace DafnyLanguage if (module.IsAbstract) { nm = "abstract " + nm; } - newRegions.Add(new OutliningRegion(module, nm)); + OutliningRegion.Add(newRegions, program, module, nm); } foreach (Dafny.TopLevelDecl d in module.TopLevelDecls) { if (!HasBodyTokens(d) && !(d is Dafny.ClassDecl)) { continue; } if (d is Dafny.OpaqueTypeDecl) { - newRegions.Add(new OutliningRegion(d, "type")); + OutliningRegion.Add(newRegions, program, d, "type"); } else if (d is Dafny.CoDatatypeDecl) { - newRegions.Add(new OutliningRegion(d, "codatatype")); + OutliningRegion.Add(newRegions, program, d, "codatatype"); } else if (d is Dafny.DatatypeDecl) { - newRegions.Add(new OutliningRegion(d, "datatype")); + OutliningRegion.Add(newRegions, program, d, "datatype"); } else if (d is Dafny.ModuleDecl) { // do nothing here, since the outer loop handles modules } else { @@ -149,9 +149,9 @@ namespace DafnyLanguage if (cl.IsDefaultClass) { // do nothing } else if (cl is Dafny.IteratorDecl) { - newRegions.Add(new OutliningRegion(cl, "iterator")); + OutliningRegion.Add(newRegions, program, cl, "iterator"); } else { - newRegions.Add(new OutliningRegion(cl, "class")); + OutliningRegion.Add(newRegions, program, cl, "class"); } // do the class members (in particular, functions and methods) foreach (Dafny.MemberDecl m in cl.Members) { @@ -168,7 +168,7 @@ namespace DafnyLanguage if (!m.IsGhost) { nm += " method"; } - newRegions.Add(new OutliningRegion(m, nm)); + OutliningRegion.Add(newRegions, program, m, nm); } else if (m is Dafny.Method && ((Dafny.Method)m).Body != null) { var nm = m is Dafny.Constructor ? "constructor" : @@ -179,7 +179,7 @@ namespace DafnyLanguage if (m.IsGhost && !(m is Dafny.CoLemma)) { nm = "ghost " + nm; } - newRegions.Add(new OutliningRegion(m, nm)); + OutliningRegion.Add(newRegions, program, m, nm); } } } @@ -196,12 +196,20 @@ namespace DafnyLanguage return decl.BodyStartTok != Bpl.Token.NoToken && decl.BodyEndTok != Bpl.Token.NoToken; } - class OutliningRegion + class OutliningRegion : DafnyRegion { + public static void Add(List regions, Microsoft.Dafny.Program prog, Dafny.INamedRegion decl, string kind) { + Contract.Requires(regions != null); + Contract.Requires(prog != null); + if (InMainFile(prog, decl.BodyStartTok)) { + regions.Add(new OutliningRegion(decl, kind)); + } + } + public readonly int Start; public readonly int Length; public readonly string HoverText; - public OutliningRegion(Dafny.INamedRegion decl, string kind) { + private OutliningRegion(Dafny.INamedRegion decl, string kind) { int startPosition = decl.BodyStartTok.pos + 1; // skip the open-curly brace itself int length = decl.BodyEndTok.pos - startPosition; Start = startPosition; -- cgit v1.2.3 From 1885f7d7d1fb9bd6ceb8220450dbb5d890501337 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 2 Oct 2015 15:26:56 -0700 Subject: Hover text includes #[_k-1] suffix for terms rewritten in prefix predicates/lemmas (this fixes an item from the wishlist). Include in hover text the extreme predicates for which an extreme lemmas has been rewritten (but don't include ==# in this list--or should it perhaps be included?). Under a (temporary) switch /rewriteFocalPredicates, each use of a focal predicate P in a prefix lemma is rewritten into P#[_k-1]. --- Source/Dafny/Cloner.cs | 91 ++++++++++++++++++---- Source/Dafny/DafnyOptions.cs | 27 +++++-- Source/Dafny/Resolver.cs | 16 +++- Test/dafny0/CoinductiveProofs.dfy | 61 ++++++++++++++- Test/dafny0/CoinductiveProofs.dfy.expect | 65 +++++++++++----- Test/wishlist/tooltips-on-inductive-lemmas.dfy | 12 --- .../tooltips-on-inductive-lemmas.dfy.expect | 7 -- 7 files changed, 214 insertions(+), 65 deletions(-) delete mode 100644 Test/wishlist/tooltips-on-inductive-lemmas.dfy delete mode 100644 Test/wishlist/tooltips-on-inductive-lemmas.dfy.expect diff --git a/Source/Dafny/Cloner.cs b/Source/Dafny/Cloner.cs index c94c697d..45d8a2c9 100644 --- a/Source/Dafny/Cloner.cs +++ b/Source/Dafny/Cloner.cs @@ -715,7 +715,7 @@ namespace Microsoft.Dafny { protected readonly Expression k; protected readonly ErrorReporter reporter; - readonly string suffix; + protected readonly string suffix; protected FixpointCloner(Expression k, ErrorReporter reporter) { Contract.Requires(k != null); @@ -724,6 +724,18 @@ namespace Microsoft.Dafny this.reporter = reporter; this.suffix = string.Format("#[{0}]", Printer.ExprToString(k)); } + protected Expression CloneCallAndAddK(FunctionCallExpr e) { + Contract.Requires(e != null); + var receiver = CloneExpr(e.Receiver); + var args = new List(); + args.Add(k); + foreach (var arg in e.Args) { + args.Add(CloneExpr(arg)); + } + var fexp = new FunctionCallExpr(Tok(e.tok), e.Name + "#", receiver, e.OpenParen, args); + reporter.Info(MessageSource.Cloner, e.tok, e.Name + suffix); + return fexp; + } } /// @@ -733,6 +745,7 @@ namespace Microsoft.Dafny /// precondition (resp. postcondition) of the inductive lemma's (resp. colemma's) corresponding prefix lemma. /// It is assumed that the source expression has been resolved. Note, the "k" given to the constructor /// is not cloned with each use; it is simply used as is. + /// The resulting expression needs to be resolved by the caller. /// class FixpointLemmaSpecificationSubstituter : FixpointCloner { @@ -756,15 +769,7 @@ namespace Microsoft.Dafny } else if (expr is FunctionCallExpr) { var e = (FunctionCallExpr)expr; if (friendlyCalls.Contains(e)) { - var receiver = CloneExpr(e.Receiver); - var args = new List(); - args.Add(k); - foreach (var arg in e.Args) { - args.Add(CloneExpr(arg)); - } - var fexp = new FunctionCallExpr(Tok(e.tok), e.Name + "#", receiver, e.OpenParen, args); - reporter.Info(MessageSource.Cloner, e.tok, e.Name); - return fexp; + return CloneCallAndAddK(e); } } else if (expr is BinaryExpr && isCoContext) { var e = (BinaryExpr)expr; @@ -774,7 +779,7 @@ namespace Microsoft.Dafny var B = CloneExpr(e.E1); var teq = new TernaryExpr(Tok(e.tok), op, k, A, B); var opString = op == TernaryExpr.Opcode.PrefixEqOp ? "==" : "!="; - reporter.Info(MessageSource.Cloner, e.tok, opString); + reporter.Info(MessageSource.Cloner, e.tok, opString + suffix); return teq; } } @@ -803,19 +808,77 @@ namespace Microsoft.Dafny } /// - /// The task of the FixpointLemmaBodyCloner is to fill in the implicit _k-1 arguments in recursive inductive/co-lemma calls. + /// The task of the FixpointLemmaBodyCloner is to fill in the implicit _k-1 arguments in recursive inductive/co-lemma calls + /// and in calls to the focal predicates. /// The source statement and the given "k" are assumed to have been resolved. /// class FixpointLemmaBodyCloner : FixpointCloner { readonly FixpointLemma context; - public FixpointLemmaBodyCloner(FixpointLemma context, Expression k, ErrorReporter reporter) + readonly ISet focalPredicates; + public FixpointLemmaBodyCloner(FixpointLemma context, Expression k, ISet focalPredicates, ErrorReporter reporter) : base(k, reporter) { Contract.Requires(context != null); Contract.Requires(k != null); Contract.Requires(reporter != null); this.context = context; + this.focalPredicates = focalPredicates; + } + public override Expression CloneExpr(Expression expr) { + if (DafnyOptions.O.RewriteFocalPredicates) { + if (expr is FunctionCallExpr) { + var e = (FunctionCallExpr)expr; +#if DEBUG_PRINT + if (e.Function.Name.EndsWith("#") && Contract.Exists(focalPredicates, p => e.Function.Name == p.Name + "#")) { + Console.WriteLine("{0}({1},{2}): DEBUG: Possible opportunity to rely on new rewrite: {3}", e.tok.filename, e.tok.line, e.tok.col, Printer.ExprToString(e)); + } +#endif + // Note, we don't actually ever get here, because all calls will have been parsed as ApplySuffix. + // However, if something changes in the future (for example, some rewrite that changing an ApplySuffix + // to its resolved FunctionCallExpr), then we do want this code, so with the hope of preventing + // some error in the future, this case is included. (Of course, it is currently completely untested!) + var f = e.Function as FixpointPredicate; + if (f != null && focalPredicates.Contains(f)) { +#if DEBUG_PRINT + var r = CloneCallAndAddK(e); + Console.WriteLine("{0}({1},{2}): DEBUG: Rewrote extreme predicate into prefix predicate: {3}", e.tok.filename, e.tok.line, e.tok.col, Printer.ExprToString(r)); + return r; +#else + return CloneCallAndAddK(e); +#endif + } + } else if (expr is ApplySuffix) { + var apply = (ApplySuffix)expr; + if (!apply.WasResolved()) { + // Since we're assuming the enclosing statement to have been resolved, this ApplySuffix must + // be part of an ExprRhs that actually designates a method call. Such an ApplySuffix does + // not get listed as being resolved, but its components (like its .Lhs) are resolved. + var mse = (MemberSelectExpr)apply.Lhs.Resolved; + Contract.Assume(mse.Member is Method); + } else { + var fce = apply.Resolved as FunctionCallExpr; + if (fce != null) { +#if DEBUG_PRINT + if (fce.Function.Name.EndsWith("#") && Contract.Exists(focalPredicates, p => fce.Function.Name == p.Name + "#")) { + Console.WriteLine("{0}({1},{2}): DEBUG: Possible opportunity to rely on new rewrite: {3}", fce.tok.filename, fce.tok.line, fce.tok.col, Printer.ExprToString(fce)); + } +#endif + var f = fce.Function as FixpointPredicate; + if (f != null && focalPredicates.Contains(f)) { +#if DEBUG_PRINT + var r = CloneCallAndAddK(fce); + Console.WriteLine("{0}({1},{2}): DEBUG: Rewrote extreme predicate into prefix predicate: {3}", fce.tok.filename, fce.tok.line, fce.tok.col, Printer.ExprToString(r)); + return r; +#else + return CloneCallAndAddK(fce); +#endif + } + } + } + } + } + return base.CloneExpr(expr); } public override AssignmentRhs CloneRHS(AssignmentRhs rhs) { var r = rhs as ExprRhs; @@ -839,7 +902,7 @@ namespace Microsoft.Dafny apply.Args.ForEach(arg => args.Add(CloneExpr(arg))); var applyClone = new ApplySuffix(Tok(apply.tok), lhsClone, args); var c = new ExprRhs(applyClone); - reporter.Info(MessageSource.Cloner, apply.Lhs.tok, mse.Member.Name); + reporter.Info(MessageSource.Cloner, apply.Lhs.tok, mse.Member.Name + suffix); return c; } } diff --git a/Source/Dafny/DafnyOptions.cs b/Source/Dafny/DafnyOptions.cs index 59d0eb2c..2d8756d2 100644 --- a/Source/Dafny/DafnyOptions.cs +++ b/Source/Dafny/DafnyOptions.cs @@ -65,11 +65,12 @@ namespace Microsoft.Dafny public bool AllowGlobals = false; public bool CountVerificationErrors = true; public bool Optimize = false; - public bool AutoTriggers = false; + public bool AutoTriggers = false; + public bool RewriteFocalPredicates = false; public bool PrintTooltips = false; public bool PrintStats = false; public bool PrintFunctionCallGraph = false; - public bool WarnShadowing = false; + public bool WarnShadowing = false; public bool IronDafny = #if ENABLE_IRONDAFNY true @@ -216,12 +217,20 @@ namespace Microsoft.Dafny case "printTooltips": PrintTooltips = true; - return true; + return true; + + case "autoTriggers": { + int autoTriggers = 0; + if (ps.GetNumericArgument(ref autoTriggers, 2)) { + AutoTriggers = autoTriggers == 1; + } + return true; + } - case "autoTriggers": { - int autoTriggers = 0; - if (ps.GetNumericArgument(ref autoTriggers, 2)) { - AutoTriggers = autoTriggers == 1; + case "rewriteFocalPredicates": { + int rewriteFocalPredicates = 0; + if (ps.GetNumericArgument(ref rewriteFocalPredicates, 2)) { + RewriteFocalPredicates = rewriteFocalPredicates == 1; } return true; } @@ -369,6 +378,10 @@ namespace Microsoft.Dafny 0 (default) - Do not generate {:trigger} annotations for user-level quantifiers. 1 - Add a {:trigger} to each user-level quantifier. Existing annotations are preserved. + /rewriteFocalPredicates: + 0 (default) - Don't rewrite predicates in the body of prefix lemmas. + 1 - In the body of prefix lemmas, rewrite any use of a focal predicate + P to P#[_k-1]. /optimize Produce optimized C# code, meaning: - selects optimized C# prelude by passing /define:DAFNY_USE_SYSTEM_COLLECTIONS_IMMUTABLE to csc.exe (requires diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 36464925..7a540722 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -1565,6 +1565,7 @@ namespace Microsoft.Dafny continue; // something went wrong during registration of the prefix lemma (probably a duplicated fixpoint-lemma name) } var k = prefixLemma.Ins[0]; + var focalPredicates = new HashSet(); if (com is CoLemma) { // compute the postconditions of the prefix lemma Contract.Assume(prefixLemma.Ens.Count == 0); // these are not supposed to have been filled in before @@ -1574,6 +1575,12 @@ namespace Microsoft.Dafny var subst = new FixpointLemmaSpecificationSubstituter(coConclusions, new IdentifierExpr(k.tok, k.Name), this.reporter, true); var post = subst.CloneExpr(p.E); prefixLemma.Ens.Add(new MaybeFreeExpression(post, p.IsFree)); + foreach (var e in coConclusions) { + var fce = e as FunctionCallExpr; + if (fce != null) { // the other possibility is that "e" is a BinaryExpr + focalPredicates.Add((CoPredicate)fce.Function); + } + } } } else { // compute the preconditions of the prefix lemma @@ -1584,13 +1591,18 @@ namespace Microsoft.Dafny var subst = new FixpointLemmaSpecificationSubstituter(antecedents, new IdentifierExpr(k.tok, k.Name), this.reporter, false); var pre = subst.CloneExpr(p.E); prefixLemma.Req.Add(new MaybeFreeExpression(pre, p.IsFree)); + foreach (var e in antecedents) { + var fce = (FunctionCallExpr)e; // we expect "antecedents" to contain only FunctionCallExpr's + focalPredicates.Add((InductivePredicate)fce.Function); + } } } + reporter.Info(MessageSource.Resolver, com.tok, string.Format("{0} specialized for {1}", com.PrefixLemma.Name, Util.Comma(focalPredicates, p => p.Name))); // Compute the statement body of the prefix lemma Contract.Assume(prefixLemma.Body == null); // this is not supposed to have been filled in before if (com.Body != null) { var kMinusOne = new BinaryExpr(com.tok, BinaryExpr.Opcode.Sub, new IdentifierExpr(k.tok, k.Name), new LiteralExpr(com.tok, 1)); - var subst = new FixpointLemmaBodyCloner(com, kMinusOne, this.reporter); + var subst = new FixpointLemmaBodyCloner(com, kMinusOne, focalPredicates, this.reporter); var mainBody = subst.CloneBlockStmt(com.Body); var kPositive = new BinaryExpr(com.tok, BinaryExpr.Opcode.Lt, new LiteralExpr(com.tok, 0), new IdentifierExpr(k.tok, k.Name)); var condBody = new IfStmt(com.BodyStartTok, mainBody.EndTok, kPositive, mainBody, null); @@ -3571,7 +3583,7 @@ namespace Microsoft.Dafny var substituter = new Translator.AlphaConverting_Substituter(cs.Receiver, argsSubstMap, new Dictionary(), new Translator(resolver.reporter)); foreach (var ens in cs.Method.Ens) { var p = substituter.Substitute(ens.E); // substitute the call's actuals for the method's formals - resolver.reporter.Info(MessageSource.Resolver, s.Tok, "ensures " + Printer.ExprToString(p) + ";"); + resolver.reporter.Info(MessageSource.Resolver, s.Tok, "ensures " + Printer.ExprToString(p)); } } } diff --git a/Test/dafny0/CoinductiveProofs.dfy b/Test/dafny0/CoinductiveProofs.dfy index d990ae51..0dce8af9 100644 --- a/Test/dafny0/CoinductiveProofs.dfy +++ b/Test/dafny0/CoinductiveProofs.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /rewriteFocalPredicates:1 "%s" > "%t" // RUN: %diff "%s.expect" "%t" codatatype Stream = Cons(head: T, tail: Stream) @@ -12,6 +12,7 @@ copredicate Pos(s: Stream) { 0 < s.head && Pos(s.tail) } +predicate FullPos(s: Stream) { Pos(s) } // a way in the test file to sidestep focal-predicate rewrites colemma {:induction false} PosLemma0(n: int) requires 1 <= n; @@ -26,7 +27,25 @@ colemma {:induction false} PosLemma1(n: int) { PosLemma1(n + 1); if (*) { - assert Pos(Upward(n + 1)); // error: cannot conclude this here, because we only have prefix predicates + assert FullPos(Upward(n + 1)); // error: cannot conclude this here, because we only have prefix predicates + } +} + +colemma {:induction false} PosLemma2(n: int) + requires 1 <= n; + ensures Pos(Upward(n)); +{ + PosLemma2(n + 1); + if (*) { + assert Pos(Upward(n + 1)); // Pos gets rewritten to Pos#[_k-1], which does hold + } else if (*) { + assert Pos#[_k-1](Upward(n + 1)); // explicitly saying Pos#[_k-1] also holds + } else if (*) { + assert Pos#[_k](Upward(n + 1)); // error: this is not known to hold for _k and n+1 + } else if (*) { + assert Pos#[_k](Upward(n)); // but it does hold with Pos#[_k] and n (which is the postcondition of the prefix lemma) + } else if (*) { + assert Pos#[_k+1](Upward(n)); // error: this is too much to ask for } } @@ -65,13 +84,29 @@ colemma {:induction false} AlwaysLemma_X1(s: Stream) { AlwaysLemma_X1(s); // this is the right proof } +predicate FullX(s: Stream) { X(s) } // a way in the test file to sidestep focal-predicate rewrites colemma {:induction false} AlwaysLemma_X2(s: Stream) ensures X(s); { AlwaysLemma_X2(s); if (*) { - assert X(s); // error: cannot conclude the full predicate here + assert FullX(s); // error: cannot conclude the full predicate here + } +} + +colemma {:induction false} AlwaysLemma_X3(s: Stream) + ensures X(s); +{ + AlwaysLemma_X3(s); + if (*) { + assert X(s); // holds, because it gets rewritten to X#[_k-1] + } else if (*) { + assert X#[_k-1](s); // explicitly saying X#[_k-1] also holds + } else if (*) { + assert X#[_k](s); // in fact, X#[_k] holds, too (which is the postcondition of the prefix lemma) + } else if (*) { + assert X#[_k+1](s); // as it turns out, this holds too, since the definition of X makes X#[_k+1] equal X#[_k] } } @@ -79,6 +114,7 @@ copredicate Y(s: Stream) // this is equivalent to always returning 'true' { Y(s.tail) } +predicate FullY(s: Stream) { Y(s) } // a way in the test file to sidestep focal-predicate rewrites colemma {:induction false} AlwaysLemma_Y0(s: Stream) ensures Y(s); // prove that Y(s) really is always 'true' @@ -97,7 +133,24 @@ colemma {:induction false} AlwaysLemma_Y2(s: Stream) { AlwaysLemma_Y2(s.tail); if (*) { - assert Y(s.tail); // error: not provable here + assert FullY(s.tail); // error: not provable here + } +} + +colemma {:induction false} AlwaysLemma_Y3(s: Stream) + ensures Y(s); +{ + AlwaysLemma_Y3(s.tail); + if (*) { + assert Y(s.tail); // this holds, because it's rewritten to Y#[_k-1] + } else if (*) { + assert Y#[_k-1](s.tail); + } else if (*) { + assert Y#[_k](s.tail); // error: not known to hold for _k and s.tail + } else if (*) { + assert Y#[_k](s); // this is also the postcondition of the prefix lemma + } else if (*) { + assert Y#[_k+1](s); // error: this is too much to ask for } } diff --git a/Test/dafny0/CoinductiveProofs.dfy.expect b/Test/dafny0/CoinductiveProofs.dfy.expect index 2a5a2b0b..c4f4c405 100644 --- a/Test/dafny0/CoinductiveProofs.dfy.expect +++ b/Test/dafny0/CoinductiveProofs.dfy.expect @@ -1,50 +1,77 @@ -CoinductiveProofs.dfy(29,11): Error: assertion violation +CoinductiveProofs.dfy(30,11): Error: assertion violation +CoinductiveProofs.dfy(15,36): Related location CoinductiveProofs.dfy(13,16): Related location Execution trace: (0,0): anon0 (0,0): anon5_Then (0,0): anon6_Then -CoinductiveProofs.dfy(59,0): Error BP5003: A postcondition might not hold on this return path. -CoinductiveProofs.dfy(58,10): Related location: This is the postcondition that might not hold. -CoinductiveProofs.dfy(54,2): Related location +CoinductiveProofs.dfy(44,11): Error: assertion violation +CoinductiveProofs.dfy(13,16): Related location +Execution trace: + (0,0): anon0 + (0,0): anon13_Then + (0,0): anon16_Then +CoinductiveProofs.dfy(48,11): Error: assertion violation +CoinductiveProofs.dfy(13,16): Related location +Execution trace: + (0,0): anon0 + (0,0): anon13_Then + (0,0): anon18_Then +CoinductiveProofs.dfy(78,0): Error BP5003: A postcondition might not hold on this return path. +CoinductiveProofs.dfy(77,10): Related location: This is the postcondition that might not hold. +CoinductiveProofs.dfy(73,2): Related location Execution trace: (0,0): anon0 (0,0): anon3_Then -CoinductiveProofs.dfy(74,11): Error: assertion violation -CoinductiveProofs.dfy(54,2): Related location +CoinductiveProofs.dfy(94,11): Error: assertion violation +CoinductiveProofs.dfy(87,29): Related location +CoinductiveProofs.dfy(73,2): Related location Execution trace: (0,0): anon0 (0,0): anon5_Then (0,0): anon6_Then -CoinductiveProofs.dfy(91,0): Error BP5003: A postcondition might not hold on this return path. -CoinductiveProofs.dfy(90,10): Related location: This is the postcondition that might not hold. -CoinductiveProofs.dfy(80,2): Related location +CoinductiveProofs.dfy(127,0): Error BP5003: A postcondition might not hold on this return path. +CoinductiveProofs.dfy(126,10): Related location: This is the postcondition that might not hold. +CoinductiveProofs.dfy(115,2): Related location Execution trace: (0,0): anon0 (0,0): anon3_Then -CoinductiveProofs.dfy(100,11): Error: assertion violation -CoinductiveProofs.dfy(80,2): Related location +CoinductiveProofs.dfy(136,11): Error: assertion violation +CoinductiveProofs.dfy(117,29): Related location +CoinductiveProofs.dfy(115,2): Related location Execution trace: (0,0): anon0 (0,0): anon5_Then (0,0): anon6_Then -CoinductiveProofs.dfy(111,0): Error BP5003: A postcondition might not hold on this return path. -CoinductiveProofs.dfy(110,10): Related location: This is the postcondition that might not hold. -CoinductiveProofs.dfy(106,2): Related location +CoinductiveProofs.dfy(149,11): Error: assertion violation +CoinductiveProofs.dfy(115,2): Related location +Execution trace: + (0,0): anon0 + (0,0): anon13_Then + (0,0): anon16_Then +CoinductiveProofs.dfy(153,11): Error: assertion violation +CoinductiveProofs.dfy(115,2): Related location +Execution trace: + (0,0): anon0 + (0,0): anon13_Then + (0,0): anon18_Then +CoinductiveProofs.dfy(164,0): Error BP5003: A postcondition might not hold on this return path. +CoinductiveProofs.dfy(163,10): Related location: This is the postcondition that might not hold. +CoinductiveProofs.dfy(159,2): Related location Execution trace: (0,0): anon0 (0,0): anon3_Then -CoinductiveProofs.dfy(150,0): Error BP5003: A postcondition might not hold on this return path. -CoinductiveProofs.dfy(149,21): Related location: This is the postcondition that might not hold. +CoinductiveProofs.dfy(203,0): Error BP5003: A postcondition might not hold on this return path. +CoinductiveProofs.dfy(202,21): Related location: This is the postcondition that might not hold. CoinductiveProofs.dfy(4,23): Related location Execution trace: (0,0): anon0 (0,0): anon3_Then -CoinductiveProofs.dfy(156,0): Error BP5003: A postcondition might not hold on this return path. -CoinductiveProofs.dfy(155,21): Related location: This is the postcondition that might not hold. +CoinductiveProofs.dfy(209,0): Error BP5003: A postcondition might not hold on this return path. +CoinductiveProofs.dfy(208,21): Related location: This is the postcondition that might not hold. CoinductiveProofs.dfy(4,23): Related location Execution trace: (0,0): anon0 (0,0): anon3_Then -Dafny program verifier finished with 35 verified, 8 errors +Dafny program verifier finished with 42 verified, 12 errors diff --git a/Test/wishlist/tooltips-on-inductive-lemmas.dfy b/Test/wishlist/tooltips-on-inductive-lemmas.dfy deleted file mode 100644 index 1bd25437..00000000 --- a/Test/wishlist/tooltips-on-inductive-lemmas.dfy +++ /dev/null @@ -1,12 +0,0 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /autoTriggers:1 /printTooltips "%s" > "%t" -// RUN: %diff "%s.expect" "%t" - -inductive lemma P() - requires Q() { // WISH the tooltip for this was broken at some point between - // revisions 94ee216fe0cd (1179) and bd779dda3b3d (1785) - P(); -} - -inductive predicate Q() { - Q() -} diff --git a/Test/wishlist/tooltips-on-inductive-lemmas.dfy.expect b/Test/wishlist/tooltips-on-inductive-lemmas.dfy.expect deleted file mode 100644 index 4d036eef..00000000 --- a/Test/wishlist/tooltips-on-inductive-lemmas.dfy.expect +++ /dev/null @@ -1,7 +0,0 @@ -tooltips-on-inductive-lemmas.dfy(5,11): Info: Q -tooltips-on-inductive-lemmas.dfy(7,2): Info: P -tooltips-on-inductive-lemmas.dfy(11,2): Info: Q#[_k - 1] -tooltips-on-inductive-lemmas.dfy(4,16): Info: P# {:induction _k} -tooltips-on-inductive-lemmas.dfy(4,16): Info: P# decreases _k - -Dafny program verifier finished with 3 verified, 0 errors -- cgit v1.2.3 From cfe05df94a5ccb6025c94bd21b09bfc1240de756 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 2 Oct 2015 18:56:13 -0700 Subject: Made /rewriteFocalPredicates:1 the default --- Source/Dafny/DafnyOptions.cs | 8 ++++---- Test/dafny0/CoinductiveProofs.dfy | 2 +- Test/dafny0/InductivePredicates.dfy | 6 +++--- Test/dafny3/Filter.dfy | 6 +++--- Test/dafny4/NipkowKlein-chapter7.dfy | 12 ++++++------ 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Source/Dafny/DafnyOptions.cs b/Source/Dafny/DafnyOptions.cs index 2d8756d2..b61ba555 100644 --- a/Source/Dafny/DafnyOptions.cs +++ b/Source/Dafny/DafnyOptions.cs @@ -66,7 +66,7 @@ namespace Microsoft.Dafny public bool CountVerificationErrors = true; public bool Optimize = false; public bool AutoTriggers = false; - public bool RewriteFocalPredicates = false; + public bool RewriteFocalPredicates = true; public bool PrintTooltips = false; public bool PrintStats = false; public bool PrintFunctionCallGraph = false; @@ -379,9 +379,9 @@ namespace Microsoft.Dafny 1 - Add a {:trigger} to each user-level quantifier. Existing annotations are preserved. /rewriteFocalPredicates: - 0 (default) - Don't rewrite predicates in the body of prefix lemmas. - 1 - In the body of prefix lemmas, rewrite any use of a focal predicate - P to P#[_k-1]. + 0 - Don't rewrite predicates in the body of prefix lemmas. + 1 (default) - In the body of prefix lemmas, rewrite any use of a focal predicate + P to P#[_k-1]. /optimize Produce optimized C# code, meaning: - selects optimized C# prelude by passing /define:DAFNY_USE_SYSTEM_COLLECTIONS_IMMUTABLE to csc.exe (requires diff --git a/Test/dafny0/CoinductiveProofs.dfy b/Test/dafny0/CoinductiveProofs.dfy index 0dce8af9..c8bb45c7 100644 --- a/Test/dafny0/CoinductiveProofs.dfy +++ b/Test/dafny0/CoinductiveProofs.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" /rewriteFocalPredicates:1 "%s" > "%t" +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" // RUN: %diff "%s.expect" "%t" codatatype Stream = Cons(head: T, tail: Stream) diff --git a/Test/dafny0/InductivePredicates.dfy b/Test/dafny0/InductivePredicates.dfy index 8d05af11..e9aa7604 100644 --- a/Test/dafny0/InductivePredicates.dfy +++ b/Test/dafny0/InductivePredicates.dfy @@ -58,7 +58,7 @@ inductive lemma {:induction false} IL_EvenBetter(x: natinf) if { case x.N? && x.n == 0 => // trivial - case x.N? && 2 <= x.n && Even(N(x.n - 2)) => + case x.N? && 2 <= x.n && Even(N(x.n - 2)) => // syntactic rewrite makes this like in IL IL_EvenBetter(N(x.n - 2)); } } @@ -142,7 +142,7 @@ module Alt { } } - inductive lemma {:induction false} MyLemma_NiceButNotFast(x: natinf) + inductive lemma {:induction false} MyLemma_Nicer(x: natinf) // same as MyLemma_NotSoNice but relying on syntactic rewrites requires Even(x) ensures x.N? && x.n % 2 == 0 { @@ -151,7 +151,7 @@ module Alt { // trivial case exists y :: x == S(S(y)) && Even(y) => var y :| x == S(S(y)) && Even(y); - MyLemma_NiceButNotFast(y); + MyLemma_Nicer(y); assert x.n == y.n + 2; } } diff --git a/Test/dafny3/Filter.dfy b/Test/dafny3/Filter.dfy index 6f541396..4f8b35ec 100644 --- a/Test/dafny3/Filter.dfy +++ b/Test/dafny3/Filter.dfy @@ -157,11 +157,11 @@ colemma Filter_IsSubStream(s: Stream, P: Predicate) calc { true; == { Filter_IsSubStream(s.tail, P); } // induction hypothesis - IsSubStream#[_k-1](Filter(s.tail, P), s.tail); + IsSubStream(Filter(s.tail, P), s.tail); == // { assert Filter(s.tail, h) == Filter(s, h).tail; } - IsSubStream#[_k-1](Filter(s, P).tail, s.tail); + IsSubStream(Filter(s, P).tail, s.tail); ==> { Lemma_TailSubStreamK(Filter(s, P).tail, s, _k-1); } - IsSubStream#[_k-1](Filter(s, P).tail, s); + IsSubStream(Filter(s, P).tail, s); } calc { In(Filter(s, P).head, s); diff --git a/Test/dafny4/NipkowKlein-chapter7.dfy b/Test/dafny4/NipkowKlein-chapter7.dfy index aae94550..0c089895 100644 --- a/Test/dafny4/NipkowKlein-chapter7.dfy +++ b/Test/dafny4/NipkowKlein-chapter7.dfy @@ -162,8 +162,8 @@ inductive lemma SmallStep_is_deterministic(cs: (com, state), cs': (com, state), case Seq(c0, c1) => if c0 == SKIP { } else { - var c0' :| cs'.0 == Seq(c0', c1) && small_step#[_k-1](c0, cs.1, c0', cs'.1); - var c0'' :| cs''.0 == Seq(c0'', c1) && small_step#[_k-1](c0, cs.1, c0'', cs''.1); + var c0' :| cs'.0 == Seq(c0', c1) && small_step(c0, cs.1, c0', cs'.1); + var c0'' :| cs''.0 == Seq(c0'', c1) && small_step(c0, cs.1, c0'', cs''.1); SmallStep_is_deterministic((c0, cs.1), (c0', cs'.1), (c0'', cs''.1)); } case If(b, thn, els) => @@ -200,7 +200,7 @@ inductive lemma BigStep_implies_SmallStepStar(c: com, s: state, t: state) case Assign(x, a) => assert small_step_star(SKIP, t, SKIP, t); case Seq(c0, c1) => - var s' :| big_step#[_k-1](c0, s, s') && big_step#[_k-1](c1, s', t); + var s' :| big_step(c0, s, s') && big_step(c1, s', t); calc <== { small_step_star(c, s, SKIP, t); { star_transitive(Seq(c0, c1), s, Seq(SKIP, c1), s', SKIP, t); } @@ -226,7 +226,7 @@ inductive lemma BigStep_implies_SmallStepStar(c: com, s: state, t: state) true; } } else { - var s' :| big_step#[_k-1](body, s, s') && big_step#[_k-1](While(b, body), s', t); + var s' :| big_step(body, s, s') && big_step(While(b, body), s', t); calc <== { small_step_star(c, s, SKIP, t); { assert small_step(c, s, If(b, Seq(body, While(b, body)), SKIP), s); } @@ -253,7 +253,7 @@ inductive lemma lemma_7_13(c0: com, s0: state, c: com, t: state, c1: com) { if c0 == c && s0 == t { } else { - var c', s' :| small_step(c0, s0, c', s') && small_step_star#[_k-1](c', s', c, t); + var c', s' :| small_step(c0, s0, c', s') && small_step_star(c', s', c, t); lemma_7_13(c', s', c, t, c1); } } @@ -264,7 +264,7 @@ inductive lemma SmallStepStar_implies_BigStep(c: com, s: state, t: state) { if c == SKIP && s == t { } else { - var c', s' :| small_step(c, s, c', s') && small_step_star#[_k-1](c', s', SKIP, t); + var c', s' :| small_step(c, s, c', s') && small_step_star(c', s', SKIP, t); SmallStep_plus_BigStep(c, s, c', s', t); } } -- cgit v1.2.3 From e07d86d6cc4423703dbfb479f09b44c80f877ef9 Mon Sep 17 00:00:00 2001 From: leino Date: Sat, 3 Oct 2015 02:40:41 -0700 Subject: Parsing and pretty printing of the new "existential guards" of the two kinds of "if" statements. --- Source/Dafny/Cloner.cs | 4 +- Source/Dafny/Dafny.atg | 64 +- Source/Dafny/DafnyAst.cs | 12 +- Source/Dafny/Parser.cs | 1268 ++++++++++++++++-------------- Source/Dafny/Printer.cs | 60 +- Source/Dafny/RefinementTransformer.cs | 8 +- Source/Dafny/Resolver.cs | 2 +- Source/Dafny/Rewriter.cs | 2 +- Source/Dafny/Scanner.cs | 264 +++---- Source/Dafny/Translator.cs | 4 +- Test/dafny0/ExistentialGuards.dfy | 89 +++ Test/dafny0/ExistentialGuards.dfy.expect | 94 +++ Test/dafny0/Simple.dfy | 27 + Test/dafny0/Simple.dfy.expect | 29 + 14 files changed, 1137 insertions(+), 790 deletions(-) create mode 100644 Test/dafny0/ExistentialGuards.dfy create mode 100644 Test/dafny0/ExistentialGuards.dfy.expect diff --git a/Source/Dafny/Cloner.cs b/Source/Dafny/Cloner.cs index 45d8a2c9..9a93a340 100644 --- a/Source/Dafny/Cloner.cs +++ b/Source/Dafny/Cloner.cs @@ -531,7 +531,7 @@ namespace Microsoft.Dafny } else if (stmt is IfStmt) { var s = (IfStmt)stmt; - r = new IfStmt(Tok(s.Tok), Tok(s.EndTok), CloneExpr(s.Guard), CloneBlockStmt(s.Thn), CloneStmt(s.Els)); + r = new IfStmt(Tok(s.Tok), Tok(s.EndTok), s.IsExistentialGuard, CloneExpr(s.Guard), CloneBlockStmt(s.Thn), CloneStmt(s.Els)); } else if (stmt is AlternativeStmt) { var s = (AlternativeStmt)stmt; @@ -631,7 +631,7 @@ namespace Microsoft.Dafny } public GuardedAlternative CloneGuardedAlternative(GuardedAlternative alt) { - return new GuardedAlternative(Tok(alt.Tok), CloneExpr(alt.Guard), alt.Body.ConvertAll(CloneStmt)); + return new GuardedAlternative(Tok(alt.Tok), alt.IsExistentialGuard, CloneExpr(alt.Guard), alt.Body.ConvertAll(CloneStmt)); } public Function CloneFunction(Function f, string newName = null) { diff --git a/Source/Dafny/Dafny.atg b/Source/Dafny/Dafny.atg index 954448af..66dff8a2 100644 --- a/Source/Dafny/Dafny.atg +++ b/Source/Dafny/Dafny.atg @@ -104,6 +104,25 @@ bool IsAlternative() { return la.kind == _lbrace && x.kind == _case; } +// an existential guard starts with an identifier and is then followed by +// * a colon (if the first identifier is given an explicit type), +// * a comma (if there's a list a bound variables and the first one is not given an explicit type), +// * a start-attribute (if there's one bound variable and it is not given an explicit type and there are attributes), or +// * a bored smiley (if there's one bound variable and it is not given an explicit type). +bool IsExistentialGuard() { + scanner.ResetPeek(); + if (la.kind == _ident) { + Token x = scanner.Peek(); + if (x.kind == _colon || x.kind == _comma || x.kind == _boredSmiley) { + return true; + } else if (x.kind == _lbrace) { + x = scanner.Peek(); + return x.kind == _colon; + } + } + return false; +} + bool IsLoopSpec() { return la.kind == _invariant | la.kind == _decreases | la.kind == _modifies; } @@ -456,6 +475,7 @@ TOKENS comma = ','. verticalbar = '|'. doublecolon = "::". + boredSmiley = ":|". bullet = '\u2022'. dot = '.'. semi = ';'. @@ -1651,7 +1671,7 @@ VarDeclStatement<.out Statement/*!*/ s.> . IfStmt = (. Contract.Ensures(Contract.ValueAtReturn(out ifStmt) != null); IToken/*!*/ x; - Expression guard = null; IToken guardEllipsis = null; + Expression guard = null; IToken guardEllipsis = null; bool isExistentialGuard = false; BlockStmt/*!*/ thn; BlockStmt/*!*/ bs; Statement/*!*/ s; @@ -1663,11 +1683,13 @@ IfStmt "if" (. x = t; .) ( IF(IsAlternative()) - AlternativeBlock + AlternativeBlock (. ifStmt = new AlternativeStmt(x, endTok, alternatives); .) | - ( Guard - | "..." (. guardEllipsis = t; .) + ( IF(IsExistentialGuard()) + ExistentialGuard (. isExistentialGuard = true; .) + | Guard + | "..." (. guardEllipsis = t; .) ) BlockStmt (. endTok = thn.EndTok; .) [ "else" @@ -1676,26 +1698,29 @@ IfStmt ) ] (. if (guardEllipsis != null) { - ifStmt = new SkeletonStatement(new IfStmt(x, endTok, guard, thn, els), guardEllipsis, null); + ifStmt = new SkeletonStatement(new IfStmt(x, endTok, isExistentialGuard, guard, thn, els), guardEllipsis, null); } else { - ifStmt = new IfStmt(x, endTok, guard, thn, els); + ifStmt = new IfStmt(x, endTok, isExistentialGuard, guard, thn, els); } .) ) . -AlternativeBlock<.out List alternatives, out IToken endTok.> +AlternativeBlock<.bool allowExistentialGuards, out List alternatives, out IToken endTok.> = (. alternatives = new List(); IToken x; - Expression e; + Expression e; bool isExistentialGuard; List body; .) "{" - { "case" (. x = t; .) - Expression // NB: don't allow lambda here + { "case" (. x = t; isExistentialGuard = false; e = dummyExpr; .) + ( IF(allowExistentialGuards && IsExistentialGuard()) + ExistentialGuard (. isExistentialGuard = true; .) // NB: don't allow lambda here + | Expression // NB: don't allow lambda here + ) "=>" (. body = new List(); .) { Stmt } - (. alternatives.Add(new GuardedAlternative(x, e, body)); .) + (. alternatives.Add(new GuardedAlternative(x, isExistentialGuard, e, body)); .) } "}" (. endTok = t; .) . @@ -1719,7 +1744,7 @@ WhileStmt ( IF(IsLoopSpec() || IsAlternative()) { LoopSpec } - AlternativeBlock + AlternativeBlock (. stmt = new AlternativeLoopStmt(x, endTok, invariants, new Specification(decreases, decAttrs), new Specification(mod, modAttrs), alternatives); .) | ( Guard (. Contract.Assume(guard == null || cce.Owner.None(guard)); .) @@ -1799,6 +1824,21 @@ Guard /* null represents demonic-choice */ | Expression (. e = ee; .) ) . +ExistentialGuard += (. var bvars = new List(); + BoundVar bv; IToken x; + Attributes attrs = null; + Expression body; + .) + IdentTypeOptional (. bvars.Add(bv); x = bv.tok; .) + { "," + IdentTypeOptional (. bvars.Add(bv); .) + } + { Attribute } + ":|" + Expression + (. e = new ExistsExpr(x, bvars, null, body, attrs); .) + . MatchStmt = (. Contract.Ensures(Contract.ValueAtReturn(out s) != null); Token x; Expression/*!*/ e; MatchCaseStmt/*!*/ c; diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index 4fc48f2f..64af1425 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -4411,20 +4411,24 @@ namespace Microsoft.Dafny { } public class IfStmt : Statement { + public readonly bool IsExistentialGuard; public readonly Expression Guard; public readonly BlockStmt Thn; public readonly Statement Els; [ContractInvariantMethod] void ObjectInvariant() { + Contract.Invariant(!IsExistentialGuard || (Guard is ExistsExpr && ((ExistsExpr)Guard).Range == null)); Contract.Invariant(Thn != null); Contract.Invariant(Els == null || Els is BlockStmt || Els is IfStmt || Els is SkeletonStatement); } - public IfStmt(IToken tok, IToken endTok, Expression guard, BlockStmt thn, Statement els) + public IfStmt(IToken tok, IToken endTok, bool isExistentialGuard, Expression guard, BlockStmt thn, Statement els) : base(tok, endTok) { Contract.Requires(tok != null); Contract.Requires(endTok != null); + Contract.Requires(!isExistentialGuard || (guard is ExistsExpr && ((ExistsExpr)guard).Range == null)); Contract.Requires(thn != null); Contract.Requires(els == null || els is BlockStmt || els is IfStmt || els is SkeletonStatement); + this.IsExistentialGuard = isExistentialGuard; this.Guard = guard; this.Thn = thn; this.Els = els; @@ -4450,20 +4454,24 @@ namespace Microsoft.Dafny { public class GuardedAlternative { public readonly IToken Tok; + public readonly bool IsExistentialGuard; public readonly Expression Guard; public readonly List Body; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(Tok != null); Contract.Invariant(Guard != null); + Contract.Invariant(!IsExistentialGuard || (Guard is ExistsExpr && ((ExistsExpr)Guard).Range == null)); Contract.Invariant(Body != null); } - public GuardedAlternative(IToken tok, Expression guard, List body) + public GuardedAlternative(IToken tok, bool isExistentialGuard, Expression guard, List body) { Contract.Requires(tok != null); Contract.Requires(guard != null); + Contract.Requires(!isExistentialGuard || (guard is ExistsExpr && ((ExistsExpr)guard).Range == null)); Contract.Requires(body != null); this.Tok = tok; + this.IsExistentialGuard = isExistentialGuard; this.Guard = guard; this.Body = body; } diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs index 7dafb572..c79c1051 100644 --- a/Source/Dafny/Parser.cs +++ b/Source/Dafny/Parser.cs @@ -38,40 +38,41 @@ public class Parser { public const int _comma = 22; public const int _verticalbar = 23; public const int _doublecolon = 24; - public const int _bullet = 25; - public const int _dot = 26; - public const int _semi = 27; - public const int _darrow = 28; - public const int _arrow = 29; - public const int _assume = 30; - public const int _calc = 31; - public const int _case = 32; - public const int _then = 33; - public const int _else = 34; - public const int _decreases = 35; - public const int _invariant = 36; - public const int _function = 37; - public const int _predicate = 38; - public const int _inductive = 39; - public const int _lemma = 40; - public const int _copredicate = 41; - public const int _modifies = 42; - public const int _reads = 43; - public const int _requires = 44; - public const int _lbrace = 45; - public const int _rbrace = 46; - public const int _lbracket = 47; - public const int _rbracket = 48; - public const int _openparen = 49; - public const int _closeparen = 50; - public const int _openAngleBracket = 51; - public const int _closeAngleBracket = 52; - public const int _eq = 53; - public const int _neq = 54; - public const int _neqAlt = 55; - public const int _star = 56; - public const int _notIn = 57; - public const int _ellipsis = 58; + public const int _boredSmiley = 25; + public const int _bullet = 26; + public const int _dot = 27; + public const int _semi = 28; + public const int _darrow = 29; + public const int _arrow = 30; + public const int _assume = 31; + public const int _calc = 32; + public const int _case = 33; + public const int _then = 34; + public const int _else = 35; + public const int _decreases = 36; + public const int _invariant = 37; + public const int _function = 38; + public const int _predicate = 39; + public const int _inductive = 40; + public const int _lemma = 41; + public const int _copredicate = 42; + public const int _modifies = 43; + public const int _reads = 44; + public const int _requires = 45; + public const int _lbrace = 46; + public const int _rbrace = 47; + public const int _lbracket = 48; + public const int _rbracket = 49; + public const int _openparen = 50; + public const int _closeparen = 51; + public const int _openAngleBracket = 52; + public const int _closeAngleBracket = 53; + public const int _eq = 54; + public const int _neq = 55; + public const int _neqAlt = 56; + public const int _star = 57; + public const int _notIn = 58; + public const int _ellipsis = 59; public const int maxT = 138; const bool _T = true; @@ -175,6 +176,25 @@ bool IsAlternative() { return la.kind == _lbrace && x.kind == _case; } +// an existential guard starts with an identifier and is then followed by +// * a colon (if the first identifier is given an explicit type), +// * a comma (if there's a list a bound variables and the first one is not given an explicit type), +// * a start-attribute (if there's one bound variable and it is not given an explicit type and there are attributes), or +// * a bored smiley (if there's one bound variable and it is not given an explicit type). +bool IsExistentialGuard() { + scanner.ResetPeek(); + if (la.kind == _ident) { + Token x = scanner.Peek(); + if (x.kind == _colon || x.kind == _comma || x.kind == _boredSmiley) { + return true; + } else if (x.kind == _lbrace) { + x = scanner.Peek(); + return x.kind == _colon; + } + } + return false; +} + bool IsLoopSpec() { return la.kind == _invariant | la.kind == _decreases | la.kind == _modifies; } @@ -532,7 +552,7 @@ bool IsType(ref IToken pt) { TraitDecl/*!*/ trait; Contract.Assert(defaultModule != null); - while (la.kind == 59) { + while (la.kind == 60) { Get(); Expect(20); { @@ -552,42 +572,42 @@ bool IsType(ref IToken pt) { } while (StartOf(1)) { switch (la.kind) { - case 60: case 61: case 64: { + case 61: case 62: case 65: { SubModuleDecl(defaultModule, out submodule); defaultModule.TopLevelDecls.Add(submodule); break; } - case 69: { + case 70: { ClassDecl(defaultModule, out c); defaultModule.TopLevelDecls.Add(c); break; } - case 75: case 76: { + case 76: case 77: { DatatypeDecl(defaultModule, out dt); defaultModule.TopLevelDecls.Add(dt); break; } - case 78: { + case 79: { NewtypeDecl(defaultModule, out td); defaultModule.TopLevelDecls.Add(td); break; } - case 79: { + case 80: { OtherTypeDecl(defaultModule, out td); defaultModule.TopLevelDecls.Add(td); break; } - case 80: { + case 81: { IteratorDecl(defaultModule, out iter); defaultModule.TopLevelDecls.Add(iter); break; } - case 71: { + case 72: { TraitDecl(defaultModule, out trait); defaultModule.TopLevelDecls.Add(trait); break; } - case 37: case 38: case 39: case 40: case 41: case 72: case 73: case 74: case 77: case 83: case 84: case 85: case 86: { + 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; } @@ -621,20 +641,20 @@ bool IsType(ref IToken pt) { bool isExclusively = false; bool opened = false; - if (la.kind == 60 || la.kind == 61) { - if (la.kind == 60) { + if (la.kind == 61 || la.kind == 62) { + if (la.kind == 61) { Get(); isAbstract = true; } - Expect(61); - while (la.kind == 45) { + Expect(62); + while (la.kind == 46) { Attribute(ref attrs); } NoUSIdent(out id); - if (la.kind == 62 || la.kind == 63) { - if (la.kind == 62) { + if (la.kind == 63 || la.kind == 64) { + if (la.kind == 63) { Get(); - Expect(63); + Expect(64); QualifiedModuleName(out idRefined); isExclusively = true; } else { @@ -644,79 +664,79 @@ bool IsType(ref IToken pt) { } } module = new ModuleDefinition(id, id.val, isAbstract, false, isExclusively, idRefined == null ? null : idRefined, parent, attrs, false, this); - Expect(45); + Expect(46); module.BodyStartTok = t; while (StartOf(1)) { switch (la.kind) { - case 60: case 61: case 64: { + case 61: case 62: case 65: { SubModuleDecl(module, out sm); module.TopLevelDecls.Add(sm); break; } - case 69: { + case 70: { ClassDecl(module, out c); module.TopLevelDecls.Add(c); break; } - case 71: { + case 72: { TraitDecl(module, out trait); module.TopLevelDecls.Add(trait); break; } - case 75: case 76: { + case 76: case 77: { DatatypeDecl(module, out dt); module.TopLevelDecls.Add(dt); break; } - case 78: { + case 79: { NewtypeDecl(module, out td); module.TopLevelDecls.Add(td); break; } - case 79: { + case 80: { OtherTypeDecl(module, out td); module.TopLevelDecls.Add(td); break; } - case 80: { + case 81: { IteratorDecl(module, out iter); module.TopLevelDecls.Add(iter); break; } - case 37: case 38: case 39: case 40: case 41: case 72: case 73: case 74: case 77: case 83: case 84: case 85: case 86: { + 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; } } } - Expect(46); + Expect(47); module.BodyEndTok = t; module.TopLevelDecls.Add(new DefaultClassDecl(module, namedModuleDefaultClassMembers)); submodule = new LiteralModuleDecl(module, parent); - } else if (la.kind == 64) { + } else if (la.kind == 65) { Get(); - if (la.kind == 65) { + if (la.kind == 66) { Get(); opened = true; } NoUSIdent(out id); - if (la.kind == 66 || la.kind == 67) { - if (la.kind == 66) { + if (la.kind == 67 || la.kind == 68) { + if (la.kind == 67) { Get(); QualifiedModuleName(out idPath); submodule = new AliasModuleDecl(idPath, id, parent, opened); } else { Get(); QualifiedModuleName(out idPath); - if (la.kind == 68) { + if (la.kind == 69) { Get(); QualifiedModuleName(out idAssignment); } submodule = new ModuleFacadeDecl(idPath, id, parent, idAssignment, opened); } } - if (la.kind == 27) { - while (!(la.kind == 0 || la.kind == 27)) {SynErr(139); Get();} + if (la.kind == 28) { + while (!(la.kind == 0 || la.kind == 28)) {SynErr(139); 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"); } @@ -740,16 +760,16 @@ bool IsType(ref IToken pt) { List members = new List(); IToken bodyStart; - while (!(la.kind == 0 || la.kind == 69)) {SynErr(141); Get();} - Expect(69); - while (la.kind == 45) { + while (!(la.kind == 0 || la.kind == 70)) {SynErr(141); Get();} + Expect(70); + while (la.kind == 46) { Attribute(ref attrs); } NoUSIdent(out id); - if (la.kind == 51) { + if (la.kind == 52) { GenericParameters(typeArgs); } - if (la.kind == 70) { + if (la.kind == 71) { Get(); Type(out trait); traits.Add(trait); @@ -759,12 +779,12 @@ bool IsType(ref IToken pt) { traits.Add(trait); } } - Expect(45); + Expect(46); bodyStart = t; while (StartOf(2)) { ClassMemberDecl(members, true, false, false); } - Expect(46); + Expect(47); c = new ClassDecl(id, id.val, module, typeArgs, members, attrs, traits); c.BodyStartTok = bodyStart; c.BodyEndTok = t; @@ -781,29 +801,29 @@ bool IsType(ref IToken pt) { IToken bodyStart = Token.NoToken; // dummy assignment bool co = false; - while (!(la.kind == 0 || la.kind == 75 || la.kind == 76)) {SynErr(142); Get();} - if (la.kind == 75) { + while (!(la.kind == 0 || la.kind == 76 || la.kind == 77)) {SynErr(142); Get();} + if (la.kind == 76) { Get(); - } else if (la.kind == 76) { + } else if (la.kind == 77) { Get(); co = true; } else SynErr(143); - while (la.kind == 45) { + while (la.kind == 46) { Attribute(ref attrs); } NoUSIdent(out id); - if (la.kind == 51) { + if (la.kind == 52) { GenericParameters(typeArgs); } - Expect(66); + Expect(67); bodyStart = t; DatatypeMemberDecl(ctors); while (la.kind == 23) { Get(); DatatypeMemberDecl(ctors); } - if (la.kind == 27) { - while (!(la.kind == 0 || la.kind == 27)) {SynErr(144); Get();} + if (la.kind == 28) { + while (!(la.kind == 0 || la.kind == 28)) {SynErr(144); 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"); } @@ -824,12 +844,12 @@ bool IsType(ref IToken pt) { Type baseType = null; Expression wh; - Expect(78); - while (la.kind == 45) { + Expect(79); + while (la.kind == 46) { Attribute(ref attrs); } NoUSIdent(out id); - Expect(66); + Expect(67); if (IsIdentColonOrBar()) { NoUSIdent(out bvId); if (la.kind == 21) { @@ -854,24 +874,24 @@ bool IsType(ref IToken pt) { td = null; Type ty; - Expect(79); - while (la.kind == 45) { + Expect(80); + while (la.kind == 46) { Attribute(ref attrs); } NoUSIdent(out id); - if (la.kind == 49) { + if (la.kind == 50) { Get(); - Expect(53); - Expect(50); + Expect(54); + Expect(51); eqSupport = TypeParameter.EqualitySupportValue.Required; - if (la.kind == 51) { + if (la.kind == 52) { GenericParameters(typeArgs); } } else if (StartOf(4)) { - if (la.kind == 51) { + if (la.kind == 52) { GenericParameters(typeArgs); } - if (la.kind == 66) { + if (la.kind == 67) { Get(); Type(out ty); td = new TypeSynonymDecl(id, id.val, typeArgs, module, ty, attrs); @@ -881,8 +901,8 @@ bool IsType(ref IToken pt) { td = new OpaqueTypeDecl(id, id.val, module, eqSupport, typeArgs, attrs); } - if (la.kind == 27) { - while (!(la.kind == 0 || la.kind == 27)) {SynErr(147); Get();} + if (la.kind == 28) { + while (!(la.kind == 0 || la.kind == 28)) {SynErr(147); 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"); } @@ -911,19 +931,19 @@ bool IsType(ref IToken pt) { IToken bodyStart = Token.NoToken; IToken bodyEnd = Token.NoToken; - while (!(la.kind == 0 || la.kind == 80)) {SynErr(148); Get();} - Expect(80); - while (la.kind == 45) { + while (!(la.kind == 0 || la.kind == 81)) {SynErr(148); Get();} + Expect(81); + while (la.kind == 46) { Attribute(ref attrs); } NoUSIdent(out id); - if (la.kind == 49 || la.kind == 51) { - if (la.kind == 51) { + if (la.kind == 50 || la.kind == 52) { + if (la.kind == 52) { GenericParameters(typeArgs); } Formals(true, true, ins); - if (la.kind == 81 || la.kind == 82) { - if (la.kind == 81) { + if (la.kind == 82 || la.kind == 83) { + if (la.kind == 82) { Get(); } else { Get(); @@ -931,14 +951,14 @@ bool IsType(ref IToken pt) { } Formals(false, true, outs); } - } else if (la.kind == 58) { + } else if (la.kind == 59) { Get(); signatureEllipsis = t; } else SynErr(149); while (StartOf(5)) { IteratorSpec(reads, mod, decreases, req, ens, yieldReq, yieldEns, ref readsAttrs, ref modAttrs, ref decrAttrs); } - if (la.kind == 45) { + if (la.kind == 46) { BlockStmt(out body, out bodyStart, out bodyEnd); } iter = new IteratorDecl(id, id.val, module, typeArgs, ins, outs, @@ -961,21 +981,21 @@ bool IsType(ref IToken pt) { List members = new List(); IToken bodyStart; - while (!(la.kind == 0 || la.kind == 71)) {SynErr(150); Get();} - Expect(71); - while (la.kind == 45) { + while (!(la.kind == 0 || la.kind == 72)) {SynErr(150); Get();} + Expect(72); + while (la.kind == 46) { Attribute(ref attrs); } NoUSIdent(out id); - if (la.kind == 51) { + if (la.kind == 52) { GenericParameters(typeArgs); } - Expect(45); + Expect(46); bodyStart = t; while (StartOf(2)) { ClassMemberDecl(members, true, false, false); } - Expect(46); + Expect(47); trait = new TraitDecl(id, id.val, module, typeArgs, members, attrs); trait.BodyStartTok = bodyStart; trait.BodyEndTok = t; @@ -989,11 +1009,11 @@ bool IsType(ref IToken pt) { MemberModifiers mmod = new MemberModifiers(); IToken staticToken = null, protectedToken = null; - while (la.kind == 72 || la.kind == 73 || la.kind == 74) { - if (la.kind == 72) { + while (la.kind == 73 || la.kind == 74 || la.kind == 75) { + if (la.kind == 73) { Get(); mmod.IsGhost = true; - } else if (la.kind == 73) { + } else if (la.kind == 74) { Get(); mmod.IsStatic = true; staticToken = t; } else { @@ -1001,7 +1021,7 @@ bool IsType(ref IToken pt) { mmod.IsProtected = true; protectedToken = t; } } - if (la.kind == 77) { + if (la.kind == 78) { 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; @@ -1036,14 +1056,14 @@ bool IsType(ref IToken pt) { IToken x; string name; var args = new List(); - Expect(45); + Expect(46); Expect(21); NoUSIdent(out x); name = x.val; if (StartOf(7)) { Expressions(args); } - Expect(46); + Expect(47); attrs = new Attributes(name, args, attrs); } @@ -1061,7 +1081,7 @@ bool IsType(ref IToken pt) { IToken id; ids = new List(); Ident(out id); ids.Add(id); - while (la.kind == 26) { + while (la.kind == 27) { Get(); Ident(out id); ids.Add(id); @@ -1079,13 +1099,13 @@ bool IsType(ref IToken pt) { IToken/*!*/ id; TypeParameter.EqualitySupportValue eqSupport; - Expect(51); + Expect(52); NoUSIdent(out id); eqSupport = TypeParameter.EqualitySupportValue.Unspecified; - if (la.kind == 49) { + if (la.kind == 50) { Get(); - Expect(53); - Expect(50); + Expect(54); + Expect(51); eqSupport = TypeParameter.EqualitySupportValue.Required; } typeArgs.Add(new TypeParameter(id, id.val, eqSupport)); @@ -1093,15 +1113,15 @@ bool IsType(ref IToken pt) { Get(); NoUSIdent(out id); eqSupport = TypeParameter.EqualitySupportValue.Unspecified; - if (la.kind == 49) { + if (la.kind == 50) { Get(); - Expect(53); - Expect(50); + Expect(54); + Expect(51); eqSupport = TypeParameter.EqualitySupportValue.Required; } typeArgs.Add(new TypeParameter(id, id.val, eqSupport)); } - Expect(52); + Expect(53); } void Type(out Type ty) { @@ -1114,11 +1134,11 @@ bool IsType(ref IToken pt) { Attributes attrs = null; IToken/*!*/ id; Type/*!*/ ty; - while (!(la.kind == 0 || la.kind == 77)) {SynErr(152); Get();} - Expect(77); + 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 == 45) { + while (la.kind == 46) { Attribute(ref attrs); } FIdentType(out id, out ty); @@ -1150,48 +1170,48 @@ bool IsType(ref IToken pt) { IToken signatureEllipsis = null; bool missingOpenParen; - if (la.kind == 37) { + if (la.kind == 38) { Get(); - if (la.kind == 83) { + if (la.kind == 84) { Get(); isFunctionMethod = true; } if (mmod.IsGhost) { SemErr(t, "functions cannot be declared 'ghost' (they are ghost by default)"); } - while (la.kind == 45) { + while (la.kind == 46) { Attribute(ref attrs); } NoUSIdent(out id); - if (la.kind == 49 || la.kind == 51) { - if (la.kind == 51) { + if (la.kind == 50 || la.kind == 52) { + if (la.kind == 52) { GenericParameters(typeArgs); } Formals(true, isFunctionMethod, formals); Expect(21); Type(out returnType); - } else if (la.kind == 58) { + } else if (la.kind == 59) { Get(); signatureEllipsis = t; } else SynErr(153); - } else if (la.kind == 38) { + } else if (la.kind == 39) { Get(); isPredicate = true; - if (la.kind == 83) { + if (la.kind == 84) { Get(); isFunctionMethod = true; } if (mmod.IsGhost) { SemErr(t, "predicates cannot be declared 'ghost' (they are ghost by default)"); } - while (la.kind == 45) { + while (la.kind == 46) { Attribute(ref attrs); } NoUSIdent(out id); if (StartOf(8)) { - if (la.kind == 51) { + if (la.kind == 52) { GenericParameters(typeArgs); } missingOpenParen = true; - if (la.kind == 49) { + if (la.kind == 50) { Formals(true, isFunctionMethod, formals); missingOpenParen = false; } @@ -1200,22 +1220,22 @@ bool IsType(ref IToken pt) { Get(); SemErr(t, "predicates do not have an explicitly declared return type; it is always bool"); } - } else if (la.kind == 58) { + } else if (la.kind == 59) { Get(); signatureEllipsis = t; } else SynErr(154); - } else if (la.kind == 39) { + } else if (la.kind == 40) { Get(); - Expect(38); + Expect(39); isIndPredicate = true; if (mmod.IsGhost) { SemErr(t, "inductive predicates cannot be declared 'ghost' (they are ghost by default)"); } - while (la.kind == 45) { + while (la.kind == 46) { Attribute(ref attrs); } NoUSIdent(out id); - if (la.kind == 49 || la.kind == 51) { - if (la.kind == 51) { + if (la.kind == 50 || la.kind == 52) { + if (la.kind == 52) { GenericParameters(typeArgs); } Formals(true, isFunctionMethod, formals); @@ -1223,21 +1243,21 @@ bool IsType(ref IToken pt) { Get(); SemErr(t, "inductive predicates do not have an explicitly declared return type; it is always bool"); } - } else if (la.kind == 58) { + } else if (la.kind == 59) { Get(); signatureEllipsis = t; } else SynErr(155); - } else if (la.kind == 41) { + } else if (la.kind == 42) { Get(); isCoPredicate = true; if (mmod.IsGhost) { SemErr(t, "copredicates cannot be declared 'ghost' (they are ghost by default)"); } - while (la.kind == 45) { + while (la.kind == 46) { Attribute(ref attrs); } NoUSIdent(out id); - if (la.kind == 49 || la.kind == 51) { - if (la.kind == 51) { + if (la.kind == 50 || la.kind == 52) { + if (la.kind == 52) { GenericParameters(typeArgs); } Formals(true, isFunctionMethod, formals); @@ -1245,7 +1265,7 @@ bool IsType(ref IToken pt) { Get(); SemErr(t, "copredicates do not have an explicitly declared return type; it is always bool"); } - } else if (la.kind == 58) { + } else if (la.kind == 59) { Get(); signatureEllipsis = t; } else SynErr(156); @@ -1254,7 +1274,7 @@ bool IsType(ref IToken pt) { while (StartOf(9)) { FunctionSpec(reqs, reads, ens, decreases); } - if (la.kind == 45) { + 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")) { @@ -1310,34 +1330,34 @@ bool IsType(ref IToken pt) { while (!(StartOf(10))) {SynErr(158); Get();} switch (la.kind) { - case 83: { + case 84: { Get(); break; } - case 40: { + case 41: { Get(); isLemma = true; break; } - case 84: { + case 85: { Get(); isCoLemma = true; break; } - case 85: { + case 86: { Get(); isCoLemma = true; errors.Warning(t, "the 'comethod' keyword has been deprecated; it has been renamed to 'colemma'"); break; } - case 39: { + case 40: { Get(); - Expect(40); + Expect(41); isIndLemma = true; break; } - case 86: { + case 87: { Get(); if (allowConstructor) { isConstructor = true; @@ -1371,7 +1391,7 @@ bool IsType(ref IToken pt) { } } - while (la.kind == 45) { + while (la.kind == 46) { Attribute(ref attrs); } if (la.kind == 1) { @@ -1385,24 +1405,24 @@ bool IsType(ref IToken pt) { } } - if (la.kind == 49 || la.kind == 51) { - if (la.kind == 51) { + if (la.kind == 50 || la.kind == 52) { + if (la.kind == 52) { GenericParameters(typeArgs); } Formals(true, !mmod.IsGhost, ins); - if (la.kind == 82) { + if (la.kind == 83) { Get(); if (isConstructor) { SemErr(t, "constructors cannot have out-parameters"); } Formals(false, !mmod.IsGhost, outs); } - } else if (la.kind == 58) { + } else if (la.kind == 59) { Get(); signatureEllipsis = t; } else SynErr(160); while (StartOf(11)) { MethodSpec(req, mod, ens, dec, ref decAttrs, ref modAttrs); } - if (la.kind == 45) { + if (la.kind == 46) { BlockStmt(out body, out bodyStart, out bodyEnd); } if (!isWithinAbstractModule && DafnyOptions.O.DisallowSoundnessCheating && body == null && ens.Count > 0 && !Attributes.Contains(attrs, "axiom") && !Attributes.Contains(attrs, "imported") && !Attributes.Contains(attrs, "decl") && theVerifyThisFile) { @@ -1437,11 +1457,11 @@ bool IsType(ref IToken pt) { IToken/*!*/ id; List formals = new List(); - while (la.kind == 45) { + while (la.kind == 46) { Attribute(ref attrs); } NoUSIdent(out id); - if (la.kind == 49) { + if (la.kind == 50) { FormalsOptionalIds(formals); } ctors.Add(new DatatypeCtor(id, id.val, formals, attrs)); @@ -1449,7 +1469,7 @@ bool IsType(ref IToken pt) { void FormalsOptionalIds(List/*!*/ formals) { Contract.Requires(cce.NonNullElements(formals)); IToken/*!*/ id; Type/*!*/ ty; string/*!*/ name; bool isGhost; - Expect(49); + Expect(50); if (StartOf(12)) { TypeIdentOptional(out id, out name, out ty, out isGhost); formals.Add(new Formal(id, name, ty, true, isGhost)); @@ -1459,7 +1479,7 @@ bool IsType(ref IToken pt) { formals.Add(new Formal(id, name, ty, true, isGhost)); } } - Expect(50); + Expect(51); } void FIdentType(out IToken/*!*/ id, out Type/*!*/ ty) { @@ -1477,8 +1497,8 @@ bool IsType(ref IToken pt) { } void OldSemi() { - if (la.kind == 27) { - while (!(la.kind == 0 || la.kind == 27)) {SynErr(162); Get();} + if (la.kind == 28) { + while (!(la.kind == 0 || la.kind == 28)) {SynErr(162); Get();} Get(); } } @@ -1487,7 +1507,7 @@ bool IsType(ref IToken pt) { Expression e0; IToken endTok; EquivExpression(out e, allowSemi, allowLambda); if (SemiFollowsCall(allowSemi, e)) { - Expect(27); + Expect(28); endTok = t; Expression(out e0, allowSemi, allowLambda); e = new StmtExpr(e.tok, @@ -1501,7 +1521,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 == 72) { + if (la.kind == 73) { Get(); if (allowGhostKeyword) { isGhost = true; } else { SemErr(t, "formal cannot be declared 'ghost' in this context"); } } @@ -1553,7 +1573,7 @@ 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 == 72) { + if (la.kind == 73) { Get(); isGhost = true; } @@ -1623,7 +1643,7 @@ bool IsType(ref IToken pt) { case 13: { Get(); tok = t; gt = new List(); - if (la.kind == 51) { + if (la.kind == 52) { GenericInstantiation(gt); } if (gt.Count > 1) { @@ -1636,7 +1656,7 @@ bool IsType(ref IToken pt) { case 14: { Get(); tok = t; gt = new List(); - if (la.kind == 51) { + if (la.kind == 52) { GenericInstantiation(gt); } if (gt.Count > 1) { @@ -1649,7 +1669,7 @@ bool IsType(ref IToken pt) { case 15: { Get(); tok = t; gt = new List(); - if (la.kind == 51) { + if (la.kind == 52) { GenericInstantiation(gt); } if (gt.Count > 1) { @@ -1662,7 +1682,7 @@ bool IsType(ref IToken pt) { case 16: { Get(); tok = t; gt = new List(); - if (la.kind == 51) { + if (la.kind == 52) { GenericInstantiation(gt); } if (gt.Count > 1) { @@ -1680,7 +1700,7 @@ bool IsType(ref IToken pt) { case 17: { Get(); tok = t; gt = new List(); - if (la.kind == 51) { + if (la.kind == 52) { GenericInstantiation(gt); } if (gt.Count == 0) { @@ -1697,7 +1717,7 @@ bool IsType(ref IToken pt) { case 18: { Get(); tok = t; gt = new List(); - if (la.kind == 51) { + if (la.kind == 52) { GenericInstantiation(gt); } if (gt.Count == 0) { @@ -1714,7 +1734,7 @@ bool IsType(ref IToken pt) { case 5: { Get(); tok = t; gt = null; - if (la.kind == 51) { + if (la.kind == 52) { gt = new List(); GenericInstantiation(gt); } @@ -1723,7 +1743,7 @@ bool IsType(ref IToken pt) { break; } - case 49: { + case 50: { Get(); tok = t; tupleArgTypes = new List(); if (StartOf(3)) { @@ -1735,7 +1755,7 @@ bool IsType(ref IToken pt) { tupleArgTypes.Add(ty); } } - Expect(50); + Expect(51); if (tupleArgTypes.Count == 1) { // just return the type 'ty' } else { @@ -1750,11 +1770,11 @@ bool IsType(ref IToken pt) { Expression e; tok = t; NameSegmentForTypeName(out e); tok = t; - while (la.kind == 26) { + while (la.kind == 27) { Get(); Expect(1); tok = t; List typeArgs = null; - if (la.kind == 51) { + if (la.kind == 52) { typeArgs = new List(); GenericInstantiation(typeArgs); } @@ -1765,7 +1785,7 @@ bool IsType(ref IToken pt) { } default: SynErr(164); break; } - if (la.kind == 29) { + if (la.kind == 30) { Type t2; Get(); tok = t; @@ -1783,8 +1803,8 @@ 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(49); - if (la.kind == 1 || la.kind == 72) { + Expect(50); + if (la.kind == 1 || la.kind == 73) { GIdentType(allowGhostKeyword, out id, out ty, out isGhost); formals.Add(new Formal(id, id.val, ty, incoming, isGhost)); while (la.kind == 22) { @@ -1793,7 +1813,7 @@ bool IsType(ref IToken pt) { formals.Add(new Formal(id, id.val, ty, incoming, isGhost)); } } - Expect(50); + Expect(51); } void IteratorSpec(List/*!*/ reads, List/*!*/ mod, List decreases, @@ -1803,7 +1823,7 @@ 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();} - if (la.kind == 43) { + if (la.kind == 44) { Get(); while (IsAttribute()) { Attribute(ref readsAttrs); @@ -1816,7 +1836,7 @@ ref Attributes readsAttrs, ref Attributes modAttrs, ref Attributes decrAttrs) { reads.Add(fe); } OldSemi(); - } else if (la.kind == 42) { + } else if (la.kind == 43) { Get(); while (IsAttribute()) { Attribute(ref modAttrs); @@ -1830,17 +1850,17 @@ ref Attributes readsAttrs, ref Attributes modAttrs, ref Attributes decrAttrs) { } OldSemi(); } else if (StartOf(14)) { - if (la.kind == 87) { + if (la.kind == 88) { Get(); isFree = true; errors.Warning(t, "the 'free' keyword is soon to be deprecated"); } - if (la.kind == 89) { + if (la.kind == 90) { Get(); isYield = true; } - if (la.kind == 44) { + if (la.kind == 45) { Get(); Expression(out e, false, false); OldSemi(); @@ -1850,7 +1870,7 @@ ref Attributes readsAttrs, ref Attributes modAttrs, ref Attributes decrAttrs) { req.Add(new MaybeFreeExpression(e, isFree)); } - } else if (la.kind == 88) { + } else if (la.kind == 89) { Get(); while (IsAttribute()) { Attribute(ref ensAttrs); @@ -1864,7 +1884,7 @@ ref Attributes readsAttrs, ref Attributes modAttrs, ref Attributes decrAttrs) { } } else SynErr(166); - } else if (la.kind == 35) { + } else if (la.kind == 36) { Get(); while (IsAttribute()) { Attribute(ref decrAttrs); @@ -1878,12 +1898,12 @@ ref Attributes readsAttrs, ref Attributes modAttrs, ref Attributes decrAttrs) { Contract.Ensures(Contract.ValueAtReturn(out block) != null); List body = new List(); - Expect(45); + Expect(46); bodyStart = t; while (StartOf(15)) { Stmt(body); } - Expect(46); + Expect(47); bodyEnd = t; block = new BlockStmt(bodyStart, bodyEnd, body); } @@ -1894,7 +1914,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Expression/*!*/ e; FrameExpression/*!*/ fe; bool isFree = false; Attributes ensAttrs = null; while (!(StartOf(16))) {SynErr(168); Get();} - if (la.kind == 42) { + if (la.kind == 43) { Get(); while (IsAttribute()) { Attribute(ref modAttrs); @@ -1907,19 +1927,19 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo mod.Add(fe); } OldSemi(); - } else if (la.kind == 44 || la.kind == 87 || la.kind == 88) { - if (la.kind == 87) { + } else if (la.kind == 45 || la.kind == 88 || la.kind == 89) { + if (la.kind == 88) { Get(); isFree = true; errors.Warning(t, "the 'free' keyword is soon to be deprecated"); } - if (la.kind == 44) { + if (la.kind == 45) { Get(); Expression(out e, false, false); OldSemi(); req.Add(new MaybeFreeExpression(e, isFree)); - } else if (la.kind == 88) { + } else if (la.kind == 89) { Get(); while (IsAttribute()) { Attribute(ref ensAttrs); @@ -1928,7 +1948,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo OldSemi(); ens.Add(new MaybeFreeExpression(e, isFree, ensAttrs)); } else SynErr(169); - } else if (la.kind == 35) { + } else if (la.kind == 36) { Get(); while (IsAttribute()) { Attribute(ref decAttrs); @@ -1948,13 +1968,13 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo if (StartOf(7)) { Expression(out e, allowSemi, allowLambda); feTok = e.tok; - if (la.kind == 90) { + if (la.kind == 91) { Get(); Ident(out id); fieldName = id.val; feTok = id; } fe = new FrameExpression(feTok, e, fieldName); - } else if (la.kind == 90) { + } else if (la.kind == 91) { Get(); Ident(out id); fieldName = id.val; @@ -1985,7 +2005,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo void GenericInstantiation(List/*!*/ gt) { Contract.Requires(cce.NonNullElements(gt)); Type/*!*/ ty; - Expect(51); + Expect(52); Type(out ty); gt.Add(ty); while (la.kind == 22) { @@ -1993,7 +2013,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Type(out ty); gt.Add(ty); } - Expect(52); + Expect(53); } void NameSegmentForTypeName(out Expression e) { @@ -2001,7 +2021,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo List typeArgs = null; Ident(out id); - if (la.kind == 51) { + if (la.kind == 52) { typeArgs = new List(); GenericInstantiation(typeArgs); } @@ -2015,12 +2035,12 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Contract.Requires(decreases == null || cce.NonNullElements(decreases)); Expression/*!*/ e; FrameExpression/*!*/ fe; while (!(StartOf(17))) {SynErr(172); Get();} - if (la.kind == 44) { + if (la.kind == 45) { Get(); Expression(out e, false, false); OldSemi(); reqs.Add(e); - } else if (la.kind == 43) { + } else if (la.kind == 44) { Get(); PossiblyWildFrameExpression(out fe, false); reads.Add(fe); @@ -2030,12 +2050,12 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo reads.Add(fe); } OldSemi(); - } else if (la.kind == 88) { + } else if (la.kind == 89) { Get(); Expression(out e, false, false); OldSemi(); ens.Add(e); - } else if (la.kind == 35) { + } else if (la.kind == 36) { Get(); if (decreases == null) { SemErr(t, "'decreases' clauses are meaningless for copredicates, so they are not allowed"); @@ -2049,16 +2069,16 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo void FunctionBody(out Expression/*!*/ e, out IToken bodyStart, out IToken bodyEnd) { Contract.Ensures(Contract.ValueAtReturn(out e) != null); e = dummyExpr; - Expect(45); + Expect(46); bodyStart = t; Expression(out e, true, true); - Expect(46); + Expect(47); bodyEnd = t; } void PossiblyWildFrameExpression(out FrameExpression fe, bool allowSemi) { Contract.Ensures(Contract.ValueAtReturn(out fe) != null); fe = dummyFrameExpr; - if (la.kind == 56) { + if (la.kind == 57) { Get(); fe = new FrameExpression(t, new WildcardExpr(t), null); } else if (StartOf(18)) { @@ -2069,7 +2089,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo void PossiblyWildExpression(out Expression e, bool allowLambda) { Contract.Ensures(Contract.ValueAtReturn(out e)!=null); e = dummyExpr; - if (la.kind == 56) { + if (la.kind == 57) { Get(); e = new WildcardExpr(t); } else if (StartOf(7)) { @@ -2093,7 +2113,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo while (!(StartOf(19))) {SynErr(176); Get();} switch (la.kind) { - case 45: { + case 46: { BlockStmt(out bs, out bodyStart, out bodyEnd); s = bs; break; @@ -2102,7 +2122,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo AssertStmt(out s); break; } - case 30: { + case 31: { AssumeStmt(out s); break; } @@ -2110,11 +2130,11 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo PrintStmt(out s); break; } - case 1: case 2: case 3: case 4: case 8: case 10: case 19: case 20: case 23: case 49: 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 131: case 132: case 133: case 134: case 135: case 136: { UpdateStmt(out s); break; } - case 72: case 77: { + case 73: case 78: { VarDeclStatement(out s); break; } @@ -2134,7 +2154,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo ForallStmt(out s); break; } - case 31: { + case 32: { CalcStmt(out s); break; } @@ -2142,7 +2162,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo ModifyStmt(out s); break; } - case 91: { + case 92: { Get(); x = t; NoUSIdent(out id); @@ -2151,28 +2171,28 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo s.Labels = new LList