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(-) (limited to 'Source') 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 (limited to 'Source') 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 (limited to 'Source') 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 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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 (limited to 'Source') 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 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(-) (limited to 'Source') 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 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 (limited to 'Source') 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(-) (limited to 'Source') 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 (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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 (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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 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 (limited to 'Source') 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 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(-) (limited to 'Source') 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(+) (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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 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(-) (limited to 'Source') 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 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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(+) (limited to 'Source') 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 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(-) (limited to 'Source') 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 (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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 (limited to 'Source') 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 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(-) (limited to 'Source') 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 (limited to 'Source') 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 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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 (limited to 'Source') 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 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(-) (limited to 'Source') 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 (limited to 'Source') 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 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 (limited to 'Source') 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(-) (limited to 'Source') 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 (limited to 'Source') 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 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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 (limited to 'Source') 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 (limited to 'Source') 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 (limited to 'Source') 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(+) (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') diff --git a/Source/Dafny/Cloner.cs b/Source/Dafny/Cloner.cs index 323abc70..961d4d14 100644 --- a/Source/Dafny/Cloner.cs +++ b/Source/Dafny/Cloner.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Numerics; using System.Diagnostics.Contracts; using IToken = Microsoft.Boogie.IToken; +using System.Linq; namespace Microsoft.Dafny { @@ -373,13 +374,18 @@ namespace Microsoft.Dafny if (e is QuantifierExpr) { var q = (QuantifierExpr)e; var tvs = q.TypeArgs.ConvertAll(CloneTypeParam); + QuantifierExpr cloned; if (e is ForallExpr) { - return new ForallExpr(tk, tvs, bvs, range, term, CloneAttributes(e.Attributes)); + cloned = new ForallExpr(tk, tvs, bvs, range, term, CloneAttributes(e.Attributes)); } else if (e is ExistsExpr) { - return new ExistsExpr(tk, tvs, bvs, range, term, CloneAttributes(e.Attributes)); + cloned = new ExistsExpr(tk, tvs, bvs, range, term, CloneAttributes(e.Attributes)); } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected quantifier expression } + if (q.SplitQuantifier != null) { //TODO CLEMENT TRIGGERS: Should we clone the quantifier as a quantifier in this case? Or should we just replace entirely? + cloned.SplitQuantifier = q.SplitQuantifier.Select(CloneExpr).ToList(); + } + return cloned; } else if (e is MapComprehension) { return new MapComprehension(tk, ((MapComprehension)e).Finite, bvs, range, term, CloneAttributes(e.Attributes)); } else if (e is LambdaExpr) { diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index 4fdd34f6..d19d2bed 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -2816,6 +2816,12 @@ namespace Microsoft.Dafny { } else if (expr is QuantifierExpr) { var e = (QuantifierExpr)expr; + + if (e.SplitQuantifier != null) { //TODO CLEMENT TRIGGERS: Do we compile a split quantifier in its original form, or in its split form? + TrExpr(e.SplitQuantifierExpression); + return; + } + Contract.Assert(e.Bounds != null); // for non-ghost quantifiers, the resolver would have insisted on finding bounds var n = e.BoundVars.Count; Contract.Assert(e.Bounds.Count == n); diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index efe94c66..05548f38 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -1555,7 +1555,7 @@ namespace Microsoft.Dafny { // BUGBUG: The following line is a workaround to tell the verifier that 'value' is not of an Immutable type. // A proper solution would be to be able to express that in the program (in a specification or attribute) or // to be able to declare 'parent' as [PeerOrImmutable]. - Contract.Requires(value is TopLevelDecl || value is Function || value is Method || value is DatatypeCtor || value is QuantifierExpr); + Contract.Requires(value is TopLevelDecl || value is Function || value is Method || value is DatatypeCtor || (value is QuantifierExpr && ((QuantifierExpr)value).SplitQuantifier == null)); //modifies parent; parent = value; } @@ -6552,6 +6552,15 @@ namespace Microsoft.Dafny { } } + /// + /// Returns a resolved binary expression + /// + public BinaryExpr(Boogie.IToken tok, BinaryExpr.ResolvedOpcode rop, Expression e0, Expression e1) + : this(tok, BinaryExpr.ResolvedOp2SyntacticOp(rop), e0, e1) { + ResolvedOp = rop; + Type = Type.Bool; + } + public override IEnumerable SubExpressions { get { yield return E0; @@ -6770,6 +6779,8 @@ namespace Microsoft.Dafny { public List TypeArgs; private static int currentQuantId = -1; + protected abstract BinaryExpr.ResolvedOpcode SplitResolvedOp { get; } + static int FreshQuantId() { return System.Threading.Interlocked.Increment(ref currentQuantId); } @@ -6800,10 +6811,47 @@ namespace Microsoft.Dafny { this.TypeArgs = tvars; this.UniqueId = FreshQuantId(); } + + private Expression SplitQuantifierToExpression() { + Contract.Requires(SplitQuantifier != null && SplitQuantifier.Any()); + Expression accumulator = SplitQuantifier[0]; + for (int tid = 1; tid < SplitQuantifier.Count; tid++) { + var newAcc = new BinaryExpr(Term.tok, SplitResolvedOp, accumulator, SplitQuantifier[tid]); + accumulator = newAcc; + } + return accumulator; + } + + private List _SplitQuantifier; + public List SplitQuantifier { + get { + return _SplitQuantifier; + } + set { + _SplitQuantifier = value; + SplitQuantifierExpression = SplitQuantifierToExpression(); + } + } + + internal Expression SplitQuantifierExpression { get; private set; } + public abstract Expression LogicalBody(); + + public override IEnumerable SubExpressions { + get { + if (SplitQuantifier == null) { + return base.SubExpressions; + } else { + return SplitQuantifier; + } + } + } } public class ForallExpr : QuantifierExpr { + protected override BinaryExpr.Opcode SplitOp { get { return BinaryExpr.Opcode.And; } } + protected override BinaryExpr.ResolvedOpcode SplitResolvedOp { get { return BinaryExpr.ResolvedOpcode.And; } } + public ForallExpr(IToken tok, List bvars, Expression range, Expression term, Attributes attrs) : this(tok, new List(), bvars, range, term, attrs) { Contract.Requires(cce.NonNullElements(bvars)); @@ -6817,6 +6865,7 @@ namespace Microsoft.Dafny { Contract.Requires(term != null); } public override Expression LogicalBody() { + Contract.Assert(SplitQuantifier == null); // Don't call this on a quantifier with a Split clause: it's not a real quantifier if (Range == null) { return Term; } @@ -6828,6 +6877,9 @@ namespace Microsoft.Dafny { } public class ExistsExpr : QuantifierExpr { + protected override BinaryExpr.Opcode SplitOp { get { return BinaryExpr.Opcode.Or; } } + protected override BinaryExpr.ResolvedOpcode SplitResolvedOp { get { return BinaryExpr.ResolvedOpcode.Or; } } + public ExistsExpr(IToken tok, List bvars, Expression range, Expression term, Attributes attrs) : this(tok, new List(), bvars, range, term, attrs) { Contract.Requires(cce.NonNullElements(bvars)); diff --git a/Source/Dafny/Printer.cs b/Source/Dafny/Printer.cs index 1c7508eb..43b69bbb 100644 --- a/Source/Dafny/Printer.cs +++ b/Source/Dafny/Printer.cs @@ -1729,6 +1729,13 @@ namespace Microsoft.Dafny { } else if (expr is QuantifierExpr) { QuantifierExpr e = (QuantifierExpr)expr; + + if (e.SplitQuantifier != null) { + // CLEMENT TODO TRIGGERS: Should (do) we have a setting to print the original forms instead of rewritten forms? + PrintExpr(e.SplitQuantifierExpression, contextBindingStrength, fragileContext, isRightmost, isFollowedBySemicolon, indent, resolv_count); + return; + } + bool parensNeeded = !isRightmost; if (parensNeeded) { wr.Write("("); } wr.Write(e is ForallExpr ? "forall" : "exists"); diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 7bf085f9..e1c3c63b 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -305,7 +305,7 @@ namespace Microsoft.Dafny Contract.Assert(!useCompileSignatures); useCompileSignatures = true; // set Resolver-global flag to indicate that Signatures should be followed to their CompiledSignature var oldErrorsOnly = reporter.ErrorsOnly; - reporter.ErrorsOnly = true; // turn off warning reporting for the clone + reporter.ErrorsOnly = true; // turn off warning reporter for the clone var nw = new Cloner().CloneModuleDefinition(m, m.CompileName + "_Compile"); var compileSig = RegisterTopLevelDecls(nw, true); compileSig.Refines = refinementTransformer.RefinedSig; @@ -338,7 +338,7 @@ namespace Microsoft.Dafny if (refinementTransformer.CheckIsRefinement(compileSig, p)) { abs.Signature.CompileSignature = compileSig; } else { - reporter.Error(MessageSource.Resolver, + reporter.Error(MessageSource.Resolver, abs.CompilePath[0], "module " + Util.Comma(".", abs.CompilePath, x => x.val) + " must be a refinement of " + Util.Comma(".", abs.Path, x => x.val)); @@ -2438,6 +2438,7 @@ namespace Microsoft.Dafny return false; } else if (expr is QuantifierExpr) { var e = (QuantifierExpr)expr; + Contract.Assert(e.SplitQuantifier == null); // No split quantifiers during resolution var cpos = IsCoContext ? cp : Invert(cp); if ((cpos == CallingPosition.Positive && e is ExistsExpr) || (cpos == CallingPosition.Negative && e is ForallExpr)) { if (e.MissingBounds != null && e.MissingBounds.Count != 0) { @@ -6962,7 +6963,7 @@ namespace Microsoft.Dafny reporter.Error(MessageSource.Resolver, expr, "'this' is not allowed in a 'static' context"); } if (currentClass != null) { - expr.Type = GetThisType(expr.tok, currentClass); // do this regardless of scope.AllowInstance, for better error reporting + expr.Type = GetThisType(expr.tok, currentClass); // do this regardless of scope.AllowInstance, for better error reporter } } else if (expr is IdentifierExpr) { @@ -7605,6 +7606,7 @@ namespace Microsoft.Dafny e.Type = e.Body.Type; } else if (expr is QuantifierExpr) { QuantifierExpr e = (QuantifierExpr)expr; + Contract.Assert(e.SplitQuantifier == null); // No split quantifiers during resolution int prevErrorCount = reporter.Count(ErrorLevel.Error); bool _val = true; bool typeQuantifier = Attributes.ContainsBool(e.Attributes, "typeQuantifier", ref _val) && _val; @@ -9177,6 +9179,7 @@ namespace Microsoft.Dafny return; } else if (expr is QuantifierExpr) { var e = (QuantifierExpr)expr; + Contract.Assert(e.SplitQuantifier == null); // No split quantifiers during resolution if (e.MissingBounds != null) { foreach (var bv in e.MissingBounds) { reporter.Error(MessageSource.Resolver, expr, "quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name); @@ -9774,10 +9777,13 @@ namespace Microsoft.Dafny if (expr is IdentifierExpr) { var e = (IdentifierExpr)expr; + return new HashSet() { e.Var }; } else if (expr is QuantifierExpr) { var e = (QuantifierExpr)expr; + Contract.Assert(e.SplitQuantifier == null); // No split quantifiers during resolution + var s = FreeVariables(e.LogicalBody()); foreach (var bv in e.BoundVars) { s.Remove(bv); @@ -10139,7 +10145,9 @@ namespace Microsoft.Dafny } else if (expr is NamedExpr) { return moduleInfo.IsGhost ? false : UsesSpecFeatures(((NamedExpr)expr).Body); } else if (expr is ComprehensionExpr) { - if (expr is QuantifierExpr && ((QuantifierExpr)expr).Bounds == null) { + var q = expr as QuantifierExpr; + Contract.Assert(q == null || q.SplitQuantifier == null); // No split quantifiers during resolution + if (q != null && q.Bounds == null) { return true; // the quantifier cannot be compiled if the resolver found no bounds } return Contract.Exists(expr.SubExpressions, se => UsesSpecFeatures(se)); diff --git a/Source/Dafny/Rewriter.cs b/Source/Dafny/Rewriter.cs index 0798510a..2c00e203 100644 --- a/Source/Dafny/Rewriter.cs +++ b/Source/Dafny/Rewriter.cs @@ -43,9 +43,9 @@ namespace Microsoft.Dafny Contract.Requires(reporter != null); } - internal override void PostResolve(ModuleDefinition m) { + internal override void PostCyclicityResolve(ModuleDefinition m) { var finder = new Triggers.QuantifierCollectionsFinder(reporter); - + foreach (var decl in ModuleDefinition.AllCallables(m.TopLevelDecls)) { if (decl is Function) { var function = (Function)decl; diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 14fca463..f0b7f276 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -4465,6 +4465,11 @@ namespace Microsoft.Dafny { var e = (ComprehensionExpr)expr; var canCall = CanCallAssumption(e.Term, etran); var q = e as QuantifierExpr; + + if (q != null && q.SplitQuantifier != null) { + return CanCallAssumption(q.SplitQuantifierExpression, etran); + } + var tyvars = MkTyParamBinders(q != null ? q.TypeArgs : new List()); if (e.Range != null) { canCall = BplAnd(CanCallAssumption(e.Range, etran), BplImp(etran.TrExpr(e.Range), canCall)); @@ -4758,6 +4763,12 @@ namespace Microsoft.Dafny { // If the quantifier is universal, then continue as: // assume (\forall x :: body(x)); // Create local variables corresponding to the type arguments: + + if (e.SplitQuantifier != null) { + CheckWellformedAndAssume(e.SplitQuantifierExpression, options, locals, builder, etran); + return; + } + var typeArgumentCopies = Map(e.TypeArgs, tp => e.Refresh(tp, CurrentIdGenerator)); var typeMap = Util.Dict(e.TypeArgs, Map(typeArgumentCopies, tp => (Type)new UserDefinedType(tp))); var newLocals = Map(typeArgumentCopies, tp => new Bpl.LocalVariable(tp.tok, new TypedIdent(tp.tok, nameTypeParam(tp), predef.Ty))); @@ -5293,6 +5304,10 @@ namespace Microsoft.Dafny { var q = e as QuantifierExpr; var lam = e as LambdaExpr; + if (q != null && q.SplitQuantifier != null) { + CheckWellformedWithResult(q.SplitQuantifierExpression, options, result, resultType, locals, builder, etran); + } + var typeMap = new Dictionary(); var copies = new List(); if (q != null) { @@ -11534,56 +11549,60 @@ namespace Microsoft.Dafny { return TrExpr(((NamedExpr)expr).Body); } else if (expr is QuantifierExpr) { QuantifierExpr e = (QuantifierExpr)expr; - List tyvars = translator.MkTyParamBinders(e.TypeArgs); - List bvars = new List(); - - var initEtran = this; - var bodyEtran = this; - bool _scratch = true; - Bpl.Expr antecedent = Bpl.Expr.True; - - if (Attributes.ContainsBool(e.Attributes, "layerQuantifier", ref _scratch)) { - // If this is a layer quantifier, quantify over layers here, and use $LS(ly) layers in the translation of the body - var ly = BplBoundVar(e.Refresh("q$ly#", translator.CurrentIdGenerator), predef.LayerType, bvars); - bodyEtran = new ExpressionTranslator(translator, predef, HeapExpr, This, applyLimited_CurrentFunction, new FuelSetting(translator, 1, ly), new FuelSetting(translator, 1, ly), modifiesFrame, stripLits); - } - if (Attributes.ContainsBool(e.Attributes, "heapQuantifier", ref _scratch)) { - var h = BplBoundVar(e.Refresh("q$heap#", translator.CurrentIdGenerator), predef.HeapType, bvars); - bodyEtran = new ExpressionTranslator(bodyEtran, h); - antecedent = BplAnd(new List { - antecedent, - translator.FunctionCall(e.tok, BuiltinFunction.IsGoodHeap, null, h), - translator.HeapSameOrSucc(initEtran.HeapExpr, h) - }); - } + if (e.SplitQuantifier != null) { + return TrExpr(e.SplitQuantifierExpression); + } else { + List tyvars = translator.MkTyParamBinders(e.TypeArgs); + List bvars = new List(); + + var initEtran = this; + var bodyEtran = this; + bool _scratch = true; + + Bpl.Expr antecedent = Bpl.Expr.True; + + if (Attributes.ContainsBool(e.Attributes, "layerQuantifier", ref _scratch)) { + // If this is a layer quantifier, quantify over layers here, and use $LS(ly) layers in the translation of the body + var ly = BplBoundVar(e.Refresh("q$ly#", translator.CurrentIdGenerator), predef.LayerType, bvars); + bodyEtran = new ExpressionTranslator(translator, predef, HeapExpr, This, applyLimited_CurrentFunction, new FuelSetting(translator, 1, ly), new FuelSetting(translator, 1, ly), modifiesFrame, stripLits); + } + if (Attributes.ContainsBool(e.Attributes, "heapQuantifier", ref _scratch)) { + var h = BplBoundVar(e.Refresh("q$heap#", translator.CurrentIdGenerator), predef.HeapType, bvars); + bodyEtran = new ExpressionTranslator(bodyEtran, h); + antecedent = BplAnd(new List { + antecedent, + translator.FunctionCall(e.tok, BuiltinFunction.IsGoodHeap, null, h), + translator.HeapSameOrSucc(initEtran.HeapExpr, h) + }); + } - antecedent = BplAnd(antecedent, bodyEtran.TrBoundVariables(e.BoundVars, bvars)); + antecedent = BplAnd(antecedent, bodyEtran.TrBoundVariables(e.BoundVars, bvars)); - Bpl.QKeyValue kv = TrAttributes(e.Attributes, "trigger"); - Bpl.Trigger tr = null; - var argsEtran = bodyEtran.WithNoLits(); - foreach (var aa in e.Attributes.AsEnumerable()) { - if (aa.Name == "trigger") { - List tt = new List(); - foreach (var arg in aa.Args) { - tt.Add(argsEtran.TrExpr(arg)); + Bpl.QKeyValue kv = TrAttributes(e.Attributes, "trigger"); + Bpl.Trigger tr = null; + var argsEtran = bodyEtran.WithNoLits(); + foreach (var aa in e.Attributes.AsEnumerable()) { + if (aa.Name == "trigger") { + List tt = new List(); + foreach (var arg in aa.Args) { + tt.Add(argsEtran.TrExpr(arg)); + } + tr = new Bpl.Trigger(expr.tok, true, tt, tr); } - tr = new Bpl.Trigger(expr.tok, true, tt, tr); } - } - if (e.Range != null) { - antecedent = BplAnd(antecedent, bodyEtran.TrExpr(e.Range)); - } - Bpl.Expr body = bodyEtran.TrExpr(e.Term); + if (e.Range != null) { + antecedent = BplAnd(antecedent, bodyEtran.TrExpr(e.Range)); + } + Bpl.Expr body = bodyEtran.TrExpr(e.Term); - if (e is ForallExpr) { - return new Bpl.ForallExpr(expr.tok, new List(), Concat(tyvars, bvars), kv, tr, Bpl.Expr.Imp(antecedent, body)); - } else { - Contract.Assert(e is ExistsExpr); - return new Bpl.ExistsExpr(expr.tok, new List(), Concat(tyvars, bvars), kv, tr, Bpl.Expr.And(antecedent, body)); + if (e is ForallExpr) { + return new Bpl.ForallExpr(expr.tok, new List(), Concat(tyvars, bvars), kv, tr, Bpl.Expr.Imp(antecedent, body)); + } else { + Contract.Assert(e is ExistsExpr); + return new Bpl.ExistsExpr(expr.tok, new List(), Concat(tyvars, bvars), kv, tr, Bpl.Expr.And(antecedent, body)); + } } - } else if (expr is SetComprehension) { var e = (SetComprehension)expr; // Translate "set xs | R :: T" into "lambda y: BoxType :: (exists xs :: CorrectType(xs) && R && y==Box(T))". @@ -12966,9 +12985,11 @@ namespace Microsoft.Dafny { } } + } else if (expr is QuantifierExpr && ((QuantifierExpr)expr).SplitQuantifier != null) { + return TrSplitExpr(((QuantifierExpr)expr).SplitQuantifierExpression, splits, position, heightLimit, apply_induction, etran); } else if (((position && expr is ForallExpr) || (!position && expr is ExistsExpr)) - /* NB: only for type arg less quantifiers for now: */ - && ((QuantifierExpr)expr).TypeArgs.Count == 0) { + /* NB: only for type arg less quantifiers for now: */ + && ((QuantifierExpr)expr).TypeArgs.Count == 0) { var e = (QuantifierExpr)expr; var inductionVariables = ApplyInduction(e); if (apply_induction && 2 <= DafnyOptions.O.Induction && inductionVariables.Count != 0) { @@ -13138,8 +13159,11 @@ namespace Microsoft.Dafny { protected override void VisitOneExpr(Expression expr) { if (expr is QuantifierExpr) { - foreach (var trigger in (expr as QuantifierExpr).Attributes.AsEnumerable().Where(a => a.Name == "trigger").SelectMany(a => a.Args)) { - collector.Visit(trigger); + var e = (QuantifierExpr)expr; + if (e.SplitQuantifier == null) { + foreach (var trigger in (expr as QuantifierExpr).Attributes.AsEnumerable().Where(a => a.Name == "trigger").SelectMany(a => a.Args)) { + collector.Visit(trigger); + } } } } @@ -13173,6 +13197,7 @@ namespace Microsoft.Dafny { } List ApplyInduction(QuantifierExpr e) { + Contract.Requires(e.SplitQuantifier == null); // Don't call this on a quantifier with a Split clause: it's not a real quantifier Contract.Requires(e.TypeArgs.Count == 0); return ApplyInduction(e.BoundVars, e.Attributes, new List() { e.LogicalBody() }, delegate(System.IO.TextWriter wr) { new Printer(wr).PrintExpression(e, true); }); @@ -13946,6 +13971,12 @@ namespace Microsoft.Dafny { var e = (ComprehensionExpr)expr; // For quantifiers and setComprehesion we want to make sure that we don't introduce name clashes with // the enclosing scopes. + + var q = e as QuantifierExpr; + if (q != null && q.SplitQuantifier != null) { + return Substitute(q.SplitQuantifierExpression); + } + var newBoundVars = CreateBoundVarSubstitutions(e.BoundVars, expr is ForallExpr || expr is ExistsExpr || expr is SetComprehension); Expression newRange = e.Range == null ? null : Substitute(e.Range); Expression newTerm = Substitute(e.Term); diff --git a/Source/Dafny/Triggers/QuantifierSplitter.cs b/Source/Dafny/Triggers/QuantifierSplitter.cs index 33be0da2..80381f0a 100644 --- a/Source/Dafny/Triggers/QuantifierSplitter.cs +++ b/Source/Dafny/Triggers/QuantifierSplitter.cs @@ -21,30 +21,37 @@ namespace Microsoft.Dafny.Triggers { // Unfortunately, this would cause ill-behaved quantifiers to produce // exponentially many smaller quantifiers. + private static UnaryOpExpr Not(Expression expr) { + var not = new UnaryOpExpr(expr.tok, UnaryOpExpr.Opcode.Not, expr) { Type = expr.Type }; + return not; + } + internal static IEnumerable SplitExpr(Expression expr, BinaryExpr.Opcode separator) { expr = expr.Resolved; var unary = expr as UnaryOpExpr; var binary = expr as BinaryExpr; if (unary != null && unary.Op == UnaryOpExpr.Opcode.Not) { - foreach (var e in SplitExpr(unary.E, FlipOpcode(separator))) { yield return new UnaryOpExpr(unary.tok, UnaryOpExpr.Opcode.Not, e); } + foreach (var e in SplitExpr(unary.E, FlipOpcode(separator))) { yield return Not(e); } } else if (binary != null && binary.Op == separator) { foreach (var e in SplitExpr(binary.E0, separator)) { yield return e; } foreach (var e in SplitExpr(binary.E1, separator)) { yield return e; } } else if (binary != null && binary.Op == BinaryExpr.Opcode.Imp && separator == BinaryExpr.Opcode.Or) { - foreach (var e in SplitExpr(new UnaryOpExpr(unary.tok, UnaryOpExpr.Opcode.Not, binary.E0), separator)) { yield return e; } + foreach (var e in SplitExpr(Not(binary.E0), separator)) { yield return e; } foreach (var e in SplitExpr(binary.E1, separator)) { yield return e; } + } else { + yield return expr; } } internal static IEnumerable SplitAndStich(BinaryExpr pair, BinaryExpr.Opcode separator) { - foreach (var e1 in SplitExpr(pair.E1, separator)) { - yield return new BinaryExpr(pair.tok, pair.Op, pair.E0, e1); + foreach (var e1 in SplitExpr(pair.E1, separator)) { + yield return new BinaryExpr(pair.tok, pair.Op, pair.E0, e1) { ResolvedOp = pair.ResolvedOp, Type = pair.Type }; } } internal static IEnumerable SplitQuantifier(QuantifierExpr quantifier) { - var body = quantifier.LogicalBody(); + var body = quantifier.Term; var binary = body as BinaryExpr; if (quantifier is ForallExpr) { @@ -55,7 +62,7 @@ namespace Microsoft.Dafny.Triggers { stream = SplitExpr(body, BinaryExpr.Opcode.And); } foreach (var e in stream) { - yield return new ForallExpr(quantifier.tok, quantifier.BoundVars, quantifier.Range, quantifier.Term, quantifier.Attributes); + yield return new ForallExpr(quantifier.tok, quantifier.BoundVars, quantifier.Range, e, quantifier.Attributes) { Type = quantifier.Type }; } } else if (quantifier is ExistsExpr) { IEnumerable stream; @@ -65,17 +72,18 @@ namespace Microsoft.Dafny.Triggers { stream = SplitExpr(body, BinaryExpr.Opcode.Or); } foreach (var e in stream) { - yield return new ExistsExpr(quantifier.tok, quantifier.BoundVars, quantifier.Range, quantifier.Term, quantifier.Attributes); + yield return new ExistsExpr(quantifier.tok, quantifier.BoundVars, quantifier.Range, e, quantifier.Attributes) { Type = quantifier.Type }; } } else { yield return quantifier; } } - protected override void VisitOneExpr(Expression expr) { //FIXME: This doesn't save the rewritten quantifier + protected override void VisitOneExpr(Expression expr) { var quantifier = expr as QuantifierExpr; if (quantifier != null) { - var rew = SplitQuantifier(quantifier); + var split = SplitQuantifier(quantifier).ToList(); + quantifier.SplitQuantifier = split; //Console.WriteLine("!!! {0} => {1}", Printer.ExprToString(expr), rew.MapConcat(Printer.ExprToString, " ||| ")); } } diff --git a/Source/Dafny/Triggers/TriggerExtensions.cs b/Source/Dafny/Triggers/TriggerExtensions.cs index da43abcc..9fbc8a8a 100644 --- a/Source/Dafny/Triggers/TriggerExtensions.cs +++ b/Source/Dafny/Triggers/TriggerExtensions.cs @@ -272,6 +272,14 @@ namespace Microsoft.Dafny.Triggers { } private static bool ShallowEq(QuantifierExpr expr1, QuantifierExpr expr2) { //FIXME are these TypeArgs still useful? + if (!TriggerUtils.SameNullity(expr1.SplitQuantifier, expr2.SplitQuantifier)) { + return false; + } + + if (expr1.SplitQuantifier != null && expr2.SplitQuantifier != null) { + return ShallowEq_Top(expr1.SplitQuantifierExpression, expr2.SplitQuantifierExpression); + } + if (expr1.TypeArgs.Count != expr2.TypeArgs.Count || !TriggerUtils.SameNullity(expr1.Range, expr2.Range)) { return false; diff --git a/Source/Dafny/Triggers/TriggerGenerator.cs b/Source/Dafny/Triggers/TriggerGenerator.cs index de4b212b..e218ad7b 100644 --- a/Source/Dafny/Triggers/TriggerGenerator.cs +++ b/Source/Dafny/Triggers/TriggerGenerator.cs @@ -19,9 +19,13 @@ namespace Microsoft.Dafny.Triggers { //FIXME rename this file protected override bool VisitOneExpr(Expression expr, ref object st) { var quantifier = expr as QuantifierExpr; if (quantifier != null) { - quantifierCollections.Add(new QuantifiersCollection(Enumerable.Repeat(quantifier, 1), reporter)); - } //FIXME handle the case of groups of quantifiers resulting from a split - + if (quantifier.SplitQuantifier != null) { + var collection = quantifier.SplitQuantifier.Select(q => q as QuantifierExpr).Where(q => q != null); + quantifierCollections.Add(new QuantifiersCollection(collection, reporter)); + } else { + quantifierCollections.Add(new QuantifiersCollection(Enumerable.Repeat(quantifier, 1), reporter)); + } + } return true; } } diff --git a/Source/Dafny/Triggers/TriggerUtils.cs b/Source/Dafny/Triggers/TriggerUtils.cs index dedbcbb1..6c6eede2 100644 --- a/Source/Dafny/Triggers/TriggerUtils.cs +++ b/Source/Dafny/Triggers/TriggerUtils.cs @@ -101,6 +101,7 @@ namespace Microsoft.Dafny.Triggers { } internal static bool NeedsAutoTriggers(QuantifierExpr quantifier) { + Contract.Requires(quantifier.SplitQuantifier == null); // Don't call this on a quantifier with a Split clause: it's not a real quantifier return quantifier.Attributes.AsEnumerable().All(aa => aa.Name != "trigger" && aa.Name != "no_trigger"); } } diff --git a/Source/Dafny/Triggers/TriggersCollector.cs b/Source/Dafny/Triggers/TriggersCollector.cs index e1f8a013..9f721d9a 100644 --- a/Source/Dafny/Triggers/TriggersCollector.cs +++ b/Source/Dafny/Triggers/TriggersCollector.cs @@ -44,11 +44,13 @@ namespace Microsoft.Dafny.Triggers { } internal IEnumerable LoopingSubterms(QuantifierExpr quantifier) { + Contract.Requires(quantifier.SplitQuantifier == null); // Don't call this on a quantifier with a Split clause: it's not a real quantifier var matchingSubterms = MatchingSubterms(quantifier); return matchingSubterms.Where(tm => tm.CouldCauseLoops(Terms)); } internal List MatchingSubterms(QuantifierExpr quantifier) { + Contract.Requires(quantifier.SplitQuantifier == null); // Don't call this on a quantifier with a Split clause: it's not a real quantifier //FIXME this will miss rewritten expressions (CleanupExpr). Should introduce an OriginalExpr to compare against. return Terms.SelectMany(term => quantifier.SubexpressionsMatchingTrigger(term.Expr)).Deduplicate(TriggerMatch.Eq); } @@ -139,7 +141,7 @@ namespace Microsoft.Dafny.Triggers { (expr is UnaryOpExpr && (((UnaryOpExpr)expr).Op == UnaryOpExpr.Opcode.Cardinality)) || // FIXME || ((UnaryOpExpr)expr).Op == UnaryOpExpr.Opcode.Fresh doesn't work, as fresh is a pretty tricky predicate when it's not about datatypes. See translator.cs:10944 (expr is BinaryExpr && (((BinaryExpr)expr).Op == BinaryExpr.Opcode.NotIn || ((BinaryExpr)expr).Op == BinaryExpr.Opcode.In))) { annotation = AnnotatePotentialCandidate(expr); - } else if (expr is QuantifierExpr) { + } else if (expr is QuantifierExpr && ((QuantifierExpr)expr).SplitQuantifier == null) { annotation = AnnotateQuantifier((QuantifierExpr)expr); } else if (expr is LetExpr) { annotation = AnnotateLetExpr((LetExpr)expr); @@ -259,6 +261,7 @@ namespace Microsoft.Dafny.Triggers { // FIXME document that this will contain duplicates internal List CollectTriggers(QuantifierExpr quantifier) { + Contract.Requires(quantifier.SplitQuantifier == null); // Don't call this on a quantifier with a Split clause: it's not a real quantifier // TODO could check for existing triggers and return that instead, but that require a bit of work to extract the expressions return Annotate(quantifier).PrivateTerms; } -- cgit v1.2.3 From db9821ac440cdfa817049ab83c2e94f861ff429d Mon Sep 17 00:00:00 2001 From: Clément Pit--Claudel Date: Mon, 17 Aug 2015 09:37:47 -0700 Subject: Review preceding commit with Rustan --- Source/Dafny/Cloner.cs | 9 ++------- Source/Dafny/Compiler.cs | 7 ++----- Source/Dafny/DafnyAst.cs | 23 +++++++++-------------- Source/Dafny/Printer.cs | 3 +-- Source/Dafny/Translator.cs | 1 + 5 files changed, 15 insertions(+), 28 deletions(-) (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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 (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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 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(+) (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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 (limited to 'Source') 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 69c320b225825eb2adf2ae899f88588a10fd27fe Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 19 Aug 2015 20:05:47 -0700 Subject: Refactored and improved bounds discovery --- Source/Dafny/Compiler.cs | 1 + Source/Dafny/DafnyAst.cs | 65 ++++++-- Source/Dafny/Resolver.cs | 386 ++++++++++++++++++--------------------------- Source/Dafny/Translator.cs | 64 ++++---- Test/dafny0/ISets.dfy | 5 +- 5 files changed, 242 insertions(+), 279 deletions(-) (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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 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(-) (limited to 'Source') 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 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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 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 (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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 (limited to 'Source') 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 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(+) (limited to 'Source') 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 (limited to 'Source') diff --git a/Binaries/DafnyRuntime.cs b/Binaries/DafnyRuntime.cs index 7d3799d8..dfb8cf38 100644 --- a/Binaries/DafnyRuntime.cs +++ b/Binaries/DafnyRuntime.cs @@ -1060,6 +1060,11 @@ namespace Dafny } } } + public static IEnumerable IntegerRange(BigInteger lo, BigInteger hi) { + for (var j = lo; j < hi; j++) { + yield return j; + } + } // pre: b != 0 // post: result == a/b, as defined by Euclidean Division (http://en.wikipedia.org/wiki/Modulo_operation) public static sbyte EuclideanDivision_sbyte(sbyte a, sbyte b) { diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index f2cc5d23..8bfa5fa9 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -117,7 +117,19 @@ namespace Microsoft.Dafny { } else if (d is TypeSynonymDecl) { // do nothing, just bypass type synonyms in the compiler } else if (d is NewtypeDecl) { - // do nothing, just bypass newtypes in the compiler + var nt = (NewtypeDecl)d; + Indent(indent); + wr.WriteLine("public class @{0} {{", nt.CompileName); + if (nt.NativeType != null) { + Indent(indent + IndentAmount); + wr.WriteLine("public static System.Collections.Generic.IEnumerable<{0}> IntegerRange(BigInteger lo, BigInteger hi) {{", nt.NativeType.Name); + Indent(indent + 2 * IndentAmount); + wr.WriteLine("for (var j = lo; j < hi; j++) {{ yield return ({0})j; }}", nt.NativeType.Name); + Indent(indent + IndentAmount); + wr.WriteLine("}"); + } + Indent(indent); + wr.WriteLine("}"); } else if (d is DatatypeDecl) { var dt = (DatatypeDecl)d; Indent(indent); @@ -676,7 +688,7 @@ namespace Microsoft.Dafny { } string DtName(DatatypeDecl decl) { - return decl.Module.IsDefaultModule ? decl.CompileName : decl.FullCompileName; + return decl.FullCompileName; } string DtCtorName(DatatypeCtor ctor) { Contract.Requires(ctor != null); @@ -1560,11 +1572,15 @@ namespace Microsoft.Dafny { } else if (bound is ComprehensionExpr.IntBoundedPool) { var b = (ComprehensionExpr.IntBoundedPool)bound; Indent(ind); - wr.Write("for (var @{0} = ", bv.CompileName); + if (AsNativeType(bv.Type) != null) { + wr.Write("foreach (var @{0} in @{1}.IntegerRange(", bv.CompileName, bv.Type.AsNewtype.FullCompileName); + } else { + wr.Write("foreach (var @{0} in Dafny.Helpers.IntegerRange(", bv.CompileName); + } TrExpr(b.LowerBound); - wr.Write("; @{0} < ", bv.CompileName); + wr.Write(", "); TrExpr(b.UpperBound); - wr.Write("; @{0}++) {{ ", bv.CompileName); + wr.Write(")) { "); } else if (bound is ComprehensionExpr.SetBoundedPool) { var b = (ComprehensionExpr.SetBoundedPool)bound; Indent(ind); @@ -1766,27 +1782,23 @@ namespace Microsoft.Dafny { wr.WriteLine("foreach (var {0} in Dafny.Helpers.AllBooleans) {{ @{1} = {0};", tmpVar, bv.CompileName); } else if (bound is ComprehensionExpr.IntBoundedPool) { var b = (ComprehensionExpr.IntBoundedPool)bound; - // (tmpVar is not used in this case) - if (b.LowerBound != null) { - wr.Write("@{0} = ", bv.CompileName); + if (AsNativeType(bv.Type) != null) { + wr.Write("foreach (var @{0} in @{1}.IntegerRange(", tmpVar, bv.Type.AsNewtype.FullCompileName); + } else { + wr.Write("foreach (var @{0} in Dafny.Helpers.IntegerRange(", tmpVar); + } + if (b.LowerBound == null) { + wr.Write("null"); + } else { TrExpr(b.LowerBound); - wr.WriteLine(";"); - Indent(ind); - if (b.UpperBound != null) { - wr.Write("for (; @{0} < ", bv.CompileName); - TrExpr(b.UpperBound); - wr.WriteLine("; @{0}++) {{ ", bv.CompileName); - } else { - wr.WriteLine("for (;; @{0}++) {{ ", bv.CompileName); - } + } + wr.Write(", "); + if (b.UpperBound == null) { + wr.Write("null"); } else { - Contract.Assert(b.UpperBound != null); - wr.Write("@{0} = ", bv.CompileName); TrExpr(b.UpperBound); - wr.WriteLine(";"); - Indent(ind); - wr.WriteLine("for (;; @{0}--) {{ ", bv.CompileName); } + wr.WriteLine(")) {{ @{1} = {0};", tmpVar, bv.CompileName); } else if (bound is AssignSuchThatStmt.WiggleWaggleBound) { wr.WriteLine("foreach (var {0} in Dafny.Helpers.AllIntegers) {{ @{1} = {0};", tmpVar, bv.CompileName); } else if (bound is ComprehensionExpr.SetBoundedPool) { @@ -2891,11 +2903,15 @@ namespace Microsoft.Dafny { wr.Write("foreach (var @{0} in Dafny.Helpers.AllBooleans) {{ ", bv.CompileName); } else if (bound is ComprehensionExpr.IntBoundedPool) { var b = (ComprehensionExpr.IntBoundedPool)bound; - wr.Write("for (var @{0} = ", bv.CompileName); + if (AsNativeType(bv.Type) != null) { + wr.Write("foreach (var @{0} in @{1}.IntegerRange(", bv.CompileName, bv.Type.AsNewtype.FullCompileName); + } else { + wr.Write("foreach (var @{0} in Dafny.Helpers.IntegerRange(", bv.CompileName); + } TrExpr(b.LowerBound); - wr.Write("; @{0} < ", bv.CompileName); + wr.Write(", "); TrExpr(b.UpperBound); - wr.Write("; @{0}++) {{ ", bv.CompileName); + wr.Write(")) { "); } else if (bound is ComprehensionExpr.SetBoundedPool) { var b = (ComprehensionExpr.SetBoundedPool)bound; wr.Write("foreach (var @{0} in (", bv.CompileName); @@ -2960,11 +2976,15 @@ namespace Microsoft.Dafny { wr.Write("foreach (var @{0} in Dafny.Helpers.AllBooleans) {{ ", bv.CompileName); } else if (bound is ComprehensionExpr.IntBoundedPool) { var b = (ComprehensionExpr.IntBoundedPool)bound; - wr.Write("for (var @{0} = ", bv.CompileName); + if (AsNativeType(bv.Type) != null) { + wr.Write("foreach (var @{0} in @{1}.IntegerRange(", bv.CompileName, bv.Type.AsNewtype.FullCompileName); + } else { + wr.Write("foreach (var @{0} in Dafny.Helpers.IntegerRange(", bv.CompileName); + } TrExpr(b.LowerBound); - wr.Write("; @{0} < ", bv.CompileName); + wr.Write(", "); TrExpr(b.UpperBound); - wr.Write("; @{0}++) {{ ", bv.CompileName); + wr.Write(")) { "); } else if (bound is ComprehensionExpr.SetBoundedPool) { var b = (ComprehensionExpr.SetBoundedPool)bound; wr.Write("foreach (var @{0} in (", bv.CompileName); diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index 7328d8dd..d78ae170 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -1967,7 +1967,11 @@ namespace Microsoft.Dafny { } public string FullCompileName { get { - return Module.CompileName + ".@" + CompileName; + if (!Module.IsDefaultModule) { + return Module.CompileName + ".@" + CompileName; + } else { + return CompileName; + } } } } diff --git a/Test/dafny0/RangeCompilation.dfy b/Test/dafny0/RangeCompilation.dfy new file mode 100644 index 00000000..de8ca68e --- /dev/null +++ b/Test/dafny0/RangeCompilation.dfy @@ -0,0 +1,25 @@ +// RUN: %dafny /compile:3 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +newtype Byte = x | 0 <= x < 256 +predicate method GoodByte(b: Byte) { + b % 3 == 2 +} +predicate method GoodInteger(i: int) { + i % 5 == 4 +} + +method Main() { + assert GoodByte(11) && GoodInteger(24); + var b: Byte :| GoodByte(b); + var i: int :| 0 <= i < 256 && GoodInteger(i); + print "b=", b, " i=", i, "\n"; + var m0 := new MyClass; + var m17 := new M17.AnotherClass; +} + +class MyClass { } + +module M17 { + class AnotherClass { } +} diff --git a/Test/dafny0/RangeCompilation.dfy.expect b/Test/dafny0/RangeCompilation.dfy.expect new file mode 100644 index 00000000..c3275d12 --- /dev/null +++ b/Test/dafny0/RangeCompilation.dfy.expect @@ -0,0 +1,6 @@ + +Dafny program verifier finished with 5 verified, 0 errors +Program compiled successfully +Running... + +b=2 i=4 -- cgit v1.2.3 From b8d7d6d785a29fd948e9a9740fb96ed270ac19d8 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 20 Aug 2015 17:24:58 -0700 Subject: Minor refactoring --- Source/Dafny/Compiler.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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 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(-) (limited to 'Source') 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 (limited to 'Source') 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 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(-) (limited to 'Source') 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 (limited to 'Source') 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 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 (limited to 'Source') 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 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 (limited to 'Source') 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 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 (limited to 'Source') 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 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(-) (limited to 'Source') 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 (limited to 'Source') 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 (limited to 'Source') 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(+) (limited to 'Source') 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(-) (limited to 'Source') 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 (limited to 'Source') 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 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(-) (limited to 'Source') 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 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 (limited to 'Source') 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(-) (limited to 'Source') 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(-) (limited to 'Source') 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 (limited to 'Source') 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 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(-) (limited to 'Source') 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 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 (limited to 'Source') 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 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(-) (limited to 'Source') 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 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(-) (limited to 'Source') 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 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(-) (limited to 'Source') 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 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