From e7430a9b1d17ea92e986470e898d6b74fae3cea6 Mon Sep 17 00:00:00 2001 From: leino Date: Tue, 30 Jun 2015 17:27:21 -0700 Subject: Additional test case for instance functions --- Test/hofs/Classes.dfy | 17 +++++++++++++++++ Test/hofs/Classes.dfy.expect | 5 ++++- 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'Test') diff --git a/Test/hofs/Classes.dfy b/Test/hofs/Classes.dfy index 0ceb46f1..9d8044db 100644 --- a/Test/hofs/Classes.dfy +++ b/Test/hofs/Classes.dfy @@ -47,3 +47,20 @@ method U(t : T) t.h := x => x; assert J(t) == 0; // ok } + +class MyClass { + var data: int + function method F(): int + reads this + { + data + } + method M(that: MyClass) + requires that != null + { + var fn := that.F; // "that" is captured into the closure + var d := fn(); + assert d == that.data; // yes + assert d == this.data; // error: no reason to believe that this would hold + } +} diff --git a/Test/hofs/Classes.dfy.expect b/Test/hofs/Classes.dfy.expect index e490dbe0..1c9e31f0 100644 --- a/Test/hofs/Classes.dfy.expect +++ b/Test/hofs/Classes.dfy.expect @@ -1,7 +1,10 @@ +Classes.dfy(64,12): Error: assertion violation +Execution trace: + (0,0): anon0 Classes.dfy(40,6): Error: possible violation of function precondition Execution trace: (0,0): anon0 (0,0): anon7_Else (0,0): anon8_Else -Dafny program verifier finished with 6 verified, 1 error +Dafny program verifier finished with 8 verified, 2 errors -- cgit v1.2.3 From 1697a133cababe66fef1fbf7a1ed9036255d8e68 Mon Sep 17 00:00:00 2001 From: leino Date: Tue, 30 Jun 2015 23:42:57 -0700 Subject: Fixed bugs in encoding of preconditions of function values, Issue #84. --- Source/Dafny/Translator.cs | 43 ++++++++--------------- Test/hofs/Naked.dfy | 10 +++--- Test/hofs/Naked.dfy.expect | 8 ++--- Test/hofs/Requires.dfy | 82 +++++++++++++++++++++++++++++++++++++++++++ Test/hofs/Requires.dfy.expect | 5 +++ Test/hofs/Simple.dfy.expect | 5 +-- 6 files changed, 109 insertions(+), 44 deletions(-) create mode 100644 Test/hofs/Requires.dfy create mode 100644 Test/hofs/Requires.dfy.expect (limited to 'Test') diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 4324b2b8..c98bd203 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -2176,13 +2176,6 @@ namespace Microsoft.Dafny { foreach (Expression req in f.Req) { pre = BplAnd(pre, etran.TrExpr(Substitute(req, null, substMap))); } - // useViaContext: (mh != ModuleContextHeight || fh != FunctionContextHeight) - ModuleDefinition mod = f.EnclosingClass.Module; - Bpl.Expr useViaContext = visibility == FunctionAxiomVisibility.ForeignModuleOnly ? (Bpl.Expr)Bpl.Expr.True : - Bpl.Expr.Neq(Bpl.Expr.Literal(mod.CallGraph.GetSCCRepresentativeId(f)), etran.FunctionContextHeight()); - - // ante := (useViaContext && typeAnte && pre) - ante = BplAnd(useViaContext, BplAnd(ante, pre)); // Add the precondition function and its axiom (which is equivalent to the ante) if (body == null || (visibility == FunctionAxiomVisibility.IntraModuleOnly && lits == null)) { @@ -2195,14 +2188,21 @@ namespace Microsoft.Dafny { } var appl = FunctionCall(f.tok, RequiresName(f), Bpl.Type.Bool, formals.ConvertAll(x => (Bpl.Expr)(new Bpl.IdentifierExpr(f.tok, x)))); - sink.AddTopLevelDeclaration(new Axiom(f.tok, BplForall(formals, BplTrigger(appl), Bpl.Expr.Eq(appl, ante)))); - // you could use it to check that it always works, but it makes VSI-Benchmarks/b3.dfy time out: - // ante = appl; + // axiom (forall params :: { f#requires(params) } ante ==> f#requires(params) == pre); + sink.AddTopLevelDeclaration(new Axiom(f.tok, BplForall(formals, BplTrigger(appl), + BplImp(ante, Bpl.Expr.Eq(appl, pre))))); if (body == null) { return null; } } + // useViaContext: (mh != ModuleContextHeight || fh != FunctionContextHeight) + ModuleDefinition mod = f.EnclosingClass.Module; + Bpl.Expr useViaContext = visibility == FunctionAxiomVisibility.ForeignModuleOnly ? (Bpl.Expr)Bpl.Expr.True : + Bpl.Expr.Neq(Bpl.Expr.Literal(mod.CallGraph.GetSCCRepresentativeId(f)), etran.FunctionContextHeight()); + // ante := (useViaContext && typeAnte && pre) + ante = BplAnd(useViaContext, BplAnd(ante, pre)); + // useViaCanCall: f#canCall(args) Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool); Bpl.Expr useViaCanCall = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(canCallFuncID), Concat(tyargs,args)); @@ -5632,7 +5632,6 @@ namespace Microsoft.Dafny { { // Requires(Ty.., F#Handle( Ty1, ..., TyN, Layer, self), Heap, arg1, ..., argN) // = F#Requires(Ty1, .., TyN, Layer, Heap, self, [Unbox] arg1, .., [Unbox] argN) - // || Scramble(...) var fhandle = FunctionCall(f.tok, name, predef.HandleType, SnocSelf(args)); var lhs = FunctionCall(f.tok, Requires(arity), Bpl.Type.Bool, Concat(tyargs, Cons(fhandle, Cons(h, lhs_args)))); @@ -5641,9 +5640,7 @@ namespace Microsoft.Dafny { // In case this is the /requires/ or /reads/ function, then there is no precondition rhs = Bpl.Expr.True; } else { - rhs = BplOr( - FunctionCall(f.tok, RequiresName(f), Bpl.Type.Bool, Concat(SnocSelf(Snoc(args, h)), rhs_args)), - MakeScrambler(f.tok, f.FullSanitizedName + "#lessReq", Concat(vars, bvars))); + rhs = FunctionCall(f.tok, RequiresName(f), Bpl.Type.Bool, Concat(SnocSelf(Snoc(args, h)), rhs_args)); } sink.AddTopLevelDeclaration(new Axiom(f.tok, @@ -5671,15 +5668,6 @@ namespace Microsoft.Dafny { return name; } - public Bpl.Expr MakeScrambler(IToken tk, string name, List bvars) { - var f = new Bpl.Function(tk, name, - bvars.ConvertAll(bv => (Bpl.Variable)BplFormalVar(null, bv.TypedIdent.Type, true)), - BplFormalVar(null, Bpl.Type.Bool, false)); - - sink.AddTopLevelDeclaration(f); - return FunctionCall(tk, name, Bpl.Type.Bool, bvars.ConvertAll(bv => (Bpl.Expr)new Bpl.IdentifierExpr(tk, bv))); - } - private void AddArrowTypeAxioms(ArrowTypeDecl ad) { Contract.Requires(ad != null); var arity = ad.Arity; @@ -11476,11 +11464,8 @@ namespace Microsoft.Dafny { var rdvars = new List(); var o = translator.UnboxIfBoxed(BplBoundVar(varNameGen.FreshId("#o#"), predef.BoxType, rdvars), new ObjectType()); - Bpl.Expr ante = Bpl.Expr.And(Bpl.Expr.Neq(o, predef.Null), et.IsAlloced(e.tok, o)); - Bpl.Expr consequent = translator.InRWClause(e.tok, o, null, e.Reads.ConvertAll(su.SubstFrameExpr), et, null, null); - Bpl.Expr rdbody = - new Bpl.LambdaExpr(e.tok, new List(), rdvars, null, - BplImp(ante, consequent)); + Bpl.Expr rdbody = new Bpl.LambdaExpr(e.tok, new List(), rdvars, null, + translator.InRWClause(e.tok, o, null, e.Reads.ConvertAll(su.SubstFrameExpr), et, null, null)); return translator.Lit( translator.FunctionCall(e.tok, BuiltinFunction.AtLayer, predef.HandleType, @@ -14267,7 +14252,7 @@ namespace Microsoft.Dafny { /// Makes a simple trigger static Bpl.Trigger BplTrigger(Bpl.Expr e) { - return new Trigger(e.tok, true, new List { e }); + return new Bpl.Trigger(e.tok, true, new List { e }); } static Bpl.Axiom BplAxiom(Bpl.Expr e) { diff --git a/Test/hofs/Naked.dfy b/Test/hofs/Naked.dfy index fa99377f..d23eb507 100644 --- a/Test/hofs/Naked.dfy +++ b/Test/hofs/Naked.dfy @@ -19,17 +19,17 @@ module Functions { module Requires { function t(x: nat): nat - requires !t.requires(x); + requires !t.requires(x); // error: use of naked function in its own SCC { x } function g(x: nat): nat - requires !(g).requires(x); + requires !(g).requires(x); // error: use of naked function in its own SCC { x } - function g2(x: int): int { h(x) } - + function D(x: int): int // used so termination errors don't mask other errors + function g2(x: int): int decreases D(x) { h(x) } // error: precondition violation function h(x: int): int - requires !g2.requires(x); + requires !g2.requires(x); // error: use of naked function in its own SCC { x } } diff --git a/Test/hofs/Naked.dfy.expect b/Test/hofs/Naked.dfy.expect index b4dfc561..514952a1 100644 --- a/Test/hofs/Naked.dfy.expect +++ b/Test/hofs/Naked.dfy.expect @@ -21,11 +21,7 @@ Execution trace: Naked.dfy(26,14): Error: cannot use naked function in recursive setting. Possible solution: eta expansion. Execution trace: (0,0): anon0 -Naked.dfy(29,30): Error: cannot prove termination; try supplying a decreases clause -Execution trace: - (0,0): anon0 - (0,0): anon4_Else -Naked.dfy(29,30): Error: possible violation of function precondition +Naked.dfy(30,45): Error: possible violation of function precondition Naked.dfy(32,14): Related location Execution trace: (0,0): anon0 @@ -47,4 +43,4 @@ Naked.dfy(48,11): Error: cannot use naked function in recursive setting. Possibl Execution trace: (0,0): anon0 -Dafny program verifier finished with 1 verified, 12 errors +Dafny program verifier finished with 2 verified, 11 errors diff --git a/Test/hofs/Requires.dfy b/Test/hofs/Requires.dfy new file mode 100644 index 00000000..68677b3e --- /dev/null +++ b/Test/hofs/Requires.dfy @@ -0,0 +1,82 @@ +// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +method Main() +{ + test0(10); + test5(11); + test6(12); + test1(); + test2(); +} + +predicate valid(x:int) +{ + x > 0 +} + +function ref1(y:int) : int + requires valid(y); +{ + y - 1 +} + +lemma assumption1() + ensures forall a, b :: valid(a) && valid(b) && ref1(a) == ref1(b) ==> a == b; +{ +} + +method test0(a: int) +{ + if ref1.requires(a) { + // the precondition should suffice to let us call the method + ghost var b := ref1(a); + } +} +method test5(a: int) +{ + if valid(a) { + // valid(a) is the precondition of ref1 + assert ref1.requires(a); + } +} +method test6(a: int) +{ + if ref1.requires(a) { + // the precondition of ref1 is valid(a) + assert valid(a); + } +} + +method test1() +{ + if * { + assert forall a, b :: valid(a) && valid(b) && ref1(a) == ref1(b) ==> a == b; + } else { + assert forall a, b :: ref1.requires(a) && ref1.requires(b) && ref1(a) == ref1(b) + ==> a == b; + } +} + +function {:opaque} ref2(y:int) : int // Now with an opaque attribute + requires valid(y); +{ + y - 1 +} + +lemma assumption2() + ensures forall a, b :: valid(a) && valid(b) && ref2(a) == ref2(b) ==> a == b; +{ + reveal_ref2(); +} + +method test2() +{ + assumption2(); + if * { + assert forall a, b :: valid(a) && valid(b) && ref2(a) == ref2(b) ==> a == b; + } else { + assert forall a, b :: ref2.requires(a) && ref2.requires(b) && ref2(a) == ref2(b) + ==> a == b; + } +} diff --git a/Test/hofs/Requires.dfy.expect b/Test/hofs/Requires.dfy.expect new file mode 100644 index 00000000..b9a40d66 --- /dev/null +++ b/Test/hofs/Requires.dfy.expect @@ -0,0 +1,5 @@ + +Dafny program verifier finished with 20 verified, 0 errors +Program compiled successfully +Running... + diff --git a/Test/hofs/Simple.dfy.expect b/Test/hofs/Simple.dfy.expect index 1a1027ae..e2f16ef3 100644 --- a/Test/hofs/Simple.dfy.expect +++ b/Test/hofs/Simple.dfy.expect @@ -18,9 +18,6 @@ Execution trace: (0,0): anon3_Then (0,0): anon2 Simple.dfy(61,10): Error: possible violation of function precondition -Execution trace: - (0,0): anon0 -Simple.dfy(61,18): Error: assertion violation Execution trace: (0,0): anon0 Simple.dfy(73,10): Error: assertion violation @@ -29,4 +26,4 @@ Execution trace: Simple.dfy(72,38): anon5_Else Simple.dfy(73,38): anon6_Else -Dafny program verifier finished with 14 verified, 7 errors +Dafny program verifier finished with 14 verified, 6 errors -- cgit v1.2.3 From 5b3dcd0c09a7ea8a34e7f5e4b8800015b3b07e96 Mon Sep 17 00:00:00 2001 From: Bryan Parno Date: Wed, 1 Jul 2015 12:37:54 -0700 Subject: Add the ability to specify how much "fuel" a function should have, i.e., how many times Z3 is permitted to unfold it's definition. The new {:fuel} annotation can be added to the function itself, it which case it will apply to all uses of that function, or it can overridden within the scope of a module, function, method, iterator, calc, forall, while, assert, or assume. The general format is: {:fuel functionName,lowFuel,highFuel} When applied as an annotation to the function itself, omit functionName. If highFuel is omitted, it defaults to lowFuel + 1. The default fuel setting for recursive functions is 1,2. Setting the fuel higher, say, to 3,4, will give more unfoldings, which may make some proofs go through with less programmer assistance (e.g., with fewer assert statements), but it may also increase verification time, so use it with care. Setting the fuel to 0,0 is similar to making the definition opaque, except when used with all literal arguments. --- Source/Dafny/Cloner.cs | 2 +- Source/Dafny/Dafny.atg | 4 +- Source/Dafny/DafnyAst.cs | 25 ++- Source/Dafny/Parser.cs | 78 ++++---- Source/Dafny/Resolver.cs | 74 +++++++- Source/Dafny/Scanner.cs | 31 +--- Source/Dafny/Translator.cs | 388 ++++++++++++++++++++++++++++++---------- Test/dafny0/Fuel.dfy | 423 ++++++++++++++++++++++++++++++++++++++++++++ Test/dafny0/Fuel.dfy.expect | 95 ++++++++++ Test/dafny4/Circ.dfy | 2 + 10 files changed, 967 insertions(+), 155 deletions(-) create mode 100644 Test/dafny0/Fuel.dfy create mode 100644 Test/dafny0/Fuel.dfy.expect (limited to 'Test') diff --git a/Source/Dafny/Cloner.cs b/Source/Dafny/Cloner.cs index f13aa2f4..e313ffac 100644 --- a/Source/Dafny/Cloner.cs +++ b/Source/Dafny/Cloner.cs @@ -545,7 +545,7 @@ namespace Microsoft.Dafny } else if (stmt is CalcStmt) { var s = (CalcStmt)stmt; - r = new CalcStmt(Tok(s.Tok), Tok(s.EndTok), CloneCalcOp(s.Op), s.Lines.ConvertAll(CloneExpr), s.Hints.ConvertAll(CloneBlockStmt), s.StepOps.ConvertAll(CloneCalcOp), CloneCalcOp(s.ResultOp)); + r = new CalcStmt(Tok(s.Tok), Tok(s.EndTok), CloneCalcOp(s.Op), s.Lines.ConvertAll(CloneExpr), s.Hints.ConvertAll(CloneBlockStmt), s.StepOps.ConvertAll(CloneCalcOp), CloneCalcOp(s.ResultOp), CloneAttributes(s.Attributes)); } else if (stmt is MatchStmt) { var s = (MatchStmt)stmt; diff --git a/Source/Dafny/Dafny.atg b/Source/Dafny/Dafny.atg index 6cc0af5a..5d4a4321 100644 --- a/Source/Dafny/Dafny.atg +++ b/Source/Dafny/Dafny.atg @@ -1965,6 +1965,7 @@ ModifyStmt CalcStmt = (. Contract.Ensures(Contract.ValueAtReturn(out s) != null); Token x; + Attributes attrs = null; CalcStmt.CalcOp op, calcOp = Microsoft.Dafny.CalcStmt.DefaultOp, resOp = Microsoft.Dafny.CalcStmt.DefaultOp; var lines = new List(); var hints = new List(); @@ -1976,6 +1977,7 @@ CalcStmt IToken danglingOperator = null; .) "calc" (. x = t; .) + { IF(IsAttribute()) Attribute } [ CalcOp (. maybeOp = calcOp.ResultOp(calcOp); // guard against non-transitive calcOp (like !=) if (maybeOp == null) { SemErr(opTok, "the main operator of a calculation must be transitive"); @@ -2024,7 +2026,7 @@ CalcStmt // Repeat the last line to create a dummy line for the dangling hint lines.Add(lines[lines.Count - 1]); } - s = new CalcStmt(x, t, calcOp, lines, hints, stepOps, resOp); + s = new CalcStmt(x, t, calcOp, lines, hints, stepOps, resOp, attrs); .) . CalcOp diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index b8660f98..c9c9ccc8 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -314,6 +314,22 @@ namespace Microsoft.Dafny { return null; } + + /// + /// Same as FindExpressions, but returns all matches + /// + public static List> FindAllExpressions(Attributes attrs, string nm) { + Contract.Requires(nm != null); + List> ret = null; + for (; attrs != null; attrs = attrs.Prev) { + if (attrs.Name == nm) { + ret = ret ?? new List>(); // Avoid allocating the list in the common case where we don't find nm + ret.Add(attrs.Args); + } + } + return ret; + } + /// /// Returns true if "nm" is a specified attribute whose arguments match the "allowed" parameter. /// - if "nm" is not found in attrs, return false and leave value unmodified. Otherwise, @@ -2874,6 +2890,7 @@ namespace Microsoft.Dafny { public override string WhatKind { get { return "function"; } } public readonly bool IsProtected; public bool IsRecursive; // filled in during resolution + public bool IsFueled; // filled in during resolution if anyone tries to adjust this function's fuel public readonly List TypeArgs; public readonly List Formals; public readonly Type ResultType; @@ -2899,7 +2916,7 @@ namespace Microsoft.Dafny { return Contract.Exists(Decreases.Expressions, e => e is WildcardExpr); } } - + /// /// The "AllCalls" field is used for non-FixpointPredicate, non-PrefixPredicate functions only (so its value should not be relied upon for FixpointPredicate and PrefixPredicate functions). /// It records all function calls made by the Function, including calls made in the body as well as in the specification. @@ -2944,6 +2961,7 @@ namespace Microsoft.Dafny { Contract.Requires(cce.NonNullElements(ens)); Contract.Requires(decreases != null); this.IsProtected = isProtected; + this.IsFueled = false; // Defaults to false. Only set to true if someone mentions this function in a fuel annotation this.TypeArgs = typeArgs; this.Formals = formals; this.ResultType = resultType; @@ -2968,6 +2986,8 @@ namespace Microsoft.Dafny { } ModuleDefinition ICodeContext.EnclosingModule { get { return this.EnclosingClass.Module; } } bool ICodeContext.MustReverify { get { return false; } } + + public bool IsFuelAware() { return IsRecursive || IsFueled; } } public class Predicate : Function @@ -4640,7 +4660,7 @@ namespace Microsoft.Dafny { Contract.Invariant(StepOps.Count == Hints.Count); } - public CalcStmt(IToken tok, IToken endTok, CalcOp op, List lines, List hints, List stepOps, CalcOp resultOp) + public CalcStmt(IToken tok, IToken endTok, CalcOp op, List lines, List hints, List stepOps, CalcOp resultOp, Attributes attrs) : base(tok, endTok) { Contract.Requires(tok != null); @@ -4665,6 +4685,7 @@ namespace Microsoft.Dafny { } this.Steps = new List(); this.Result = null; + this.Attributes = attrs; } public override IEnumerable SubStatements diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs index b183fff1..c4047819 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 = 137; - const bool _T = true; - const bool _x = false; + const bool T = true; + const bool x = false; const int minErrDist = 2; public Scanner/*!*/ scanner; @@ -2565,6 +2565,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo void CalcStmt(out Statement s) { Contract.Ensures(Contract.ValueAtReturn(out s) != null); Token x; + Attributes attrs = null; CalcStmt.CalcOp op, calcOp = Microsoft.Dafny.CalcStmt.DefaultOp, resOp = Microsoft.Dafny.CalcStmt.DefaultOp; var lines = new List(); var hints = new List(); @@ -2577,6 +2578,9 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Expect(31); x = t; + while (IsAttribute()) { + Attribute(ref attrs); + } if (StartOf(25)) { CalcOp(out opTok, out calcOp); maybeOp = calcOp.ResultOp(calcOp); // guard against non-transitive calcOp (like !=) @@ -2631,7 +2635,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo // Repeat the last line to create a dummy line for the dangling hint lines.Add(lines[lines.Count - 1]); } - s = new CalcStmt(x, t, calcOp, lines, hints, stepOps, resOp); + s = new CalcStmt(x, t, calcOp, lines, hints, stepOps, resOp, attrs); } @@ -3988,7 +3992,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo Get(); S = Util.RemoveUnderscores(t.val); try { - n = BigIntegerParser.Parse(S); + n = BigIntegerParser.Parse(S); } catch (System.FormatException) { SemErr("incorrectly formatted number"); n = BigInteger.Zero; @@ -3999,7 +4003,7 @@ List/*!*/ decreases, ref Attributes decAttrs, ref Attributes mo S = Util.RemoveUnderscores(t.val.Substring(2)); try { // note: leading 0 required when parsing positive hex numbers - n = BigIntegerParser.Parse("0" + S, System.Globalization.NumberStyles.HexNumber); + n = BigIntegerParser.Parse("0" + S, System.Globalization.NumberStyles.HexNumber); } catch (System.FormatException) { SemErr("incorrectly formatted number"); n = BigInteger.Zero; @@ -4364,38 +4368,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, _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,_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,_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}, - {_T,_x,_x,_x, _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,_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,_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,_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,_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,_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,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _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,_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,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_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,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _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,_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,_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,_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,_T,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _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,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_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,_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,_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,_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,_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,_T,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_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,_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,_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,_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,_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,_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,_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,_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,_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,_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, 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,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,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}, + {T,x,x,x, 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,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,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,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,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,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,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, 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,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,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,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,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, 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,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,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,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,T,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, 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,T, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,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,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,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,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,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,T,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,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,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,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,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,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,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,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,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,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,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 835871d9..e48a7932 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -455,6 +455,25 @@ 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); + 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); + } else if (clbl is IteratorDecl) { + body = ((IteratorDecl)clbl).Body; + CheckForFuelAdjustments(clbl.Tok, ((IteratorDecl)clbl).Attributes, module, this); + } + if (body != null) { + var c = new FuelAdjustment_Visitor(this); + c.Visit(body, new FuelAdjustment_Context(module, this)); + } + } + } } void FillInDefaultDecreasesClauses(Program prog) @@ -683,8 +702,8 @@ namespace Microsoft.Dafny var datatypeDependencies = new Graph(); var codatatypeDependencies = new Graph(); int prevErrorCount = ErrorCount; - ResolveAttributes(m.Attributes, new ResolveOpts(new NoContext(m.Module), false)); 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); } @@ -2335,6 +2354,59 @@ namespace Microsoft.Dafny } #endregion CheckTailRecursive + // ------------------------------------------------------------------------------------------------------ + // ----- FuelAdjustmentChecks --------------------------------------------------------------------------- + // ------------------------------------------------------------------------------------------------------ + #region FuelAdjustmentChecks + + protected static void CheckForFuelAdjustments(IToken tok, Attributes attrs, ModuleDefinition currentModule, ResolutionErrorReporter reporter) { + List> results = Attributes.FindAllExpressions(attrs, "fuel"); + + if (results != null) { + foreach (List args in results) { + if (args != null && args.Count >= 2) { + // Try to extract the function from the first argument + MemberSelectExpr selectExpr = args[0].Resolved as MemberSelectExpr; + if (selectExpr != null) { + Function f = selectExpr.Member as Function; + 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); + } + } + } + } + } + } + } + + public class FuelAdjustment_Context + { + public ModuleDefinition currentModule; + public ResolutionErrorReporter reporter; + public FuelAdjustment_Context(ModuleDefinition currentModule, ResolutionErrorReporter reporter) { + this.currentModule = currentModule; + this.reporter = reporter; + } + } + + class FuelAdjustment_Visitor : ResolverTopDownVisitor + { + public FuelAdjustment_Visitor(Resolver resolver) + : base(resolver) { + Contract.Requires(resolver != null); + } + + protected override bool VisitOneStmt(Statement stmt, ref FuelAdjustment_Context st) { + Contract.Requires(stmt != null); + Resolver.CheckForFuelAdjustments(stmt.Tok, stmt.Attributes, st.currentModule, st.reporter); + return true; + } + } + + #endregion FuelAdjustmentChecks + // ------------------------------------------------------------------------------------------------------ // ----- FixpointPredicateChecks ------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------------ diff --git a/Source/Dafny/Scanner.cs b/Source/Dafny/Scanner.cs index 4c5eedb4..d0d2e4dd 100644 --- a/Source/Dafny/Scanner.cs +++ b/Source/Dafny/Scanner.cs @@ -217,7 +217,7 @@ public class Scanner { [ContractInvariantMethod] void objectInvariant(){ - Contract.Invariant(this._buffer != null); + Contract.Invariant(buffer!=null); Contract.Invariant(t != null); Contract.Invariant(start != null); Contract.Invariant(tokens != null); @@ -227,18 +227,7 @@ public class Scanner { Contract.Invariant(errorHandler != null); } - private Buffer/*!*/ _buffer; // scanner buffer - - public Buffer/*!*/ buffer { - get { - Contract.Ensures(Contract.Result() != null); - return this._buffer; - } - set { - Contract.Requires(value != null); - this._buffer = value; - } - } + public Buffer/*!*/ buffer; // scanner buffer Token/*!*/ t; // current token int ch; // current input character @@ -310,7 +299,7 @@ public class Scanner { } // [NotDelayed] - public Scanner (string/*!*/ fileName, Errors/*!*/ errorHandler, bool useBaseName = false) : base() { + public Scanner (string/*!*/ fileName, Errors/*!*/ errorHandler) : base() { Contract.Requires(fileName != null); Contract.Requires(errorHandler != null); this.errorHandler = errorHandler; @@ -318,8 +307,8 @@ public class Scanner { t = new Token(); // dummy because t is a non-null field try { Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); - this._buffer = new Buffer(stream, false); - Filename = useBaseName? GetBaseName(fileName): fileName; + buffer = new Buffer(stream, false); + Filename = fileName; Init(); } catch (IOException) { throw new FatalError("Cannot open file " + fileName); @@ -327,22 +316,18 @@ public class Scanner { } // [NotDelayed] - public Scanner (Stream/*!*/ s, Errors/*!*/ errorHandler, string/*!*/ fileName, bool useBaseName = false) : base() { + public Scanner (Stream/*!*/ s, Errors/*!*/ errorHandler, string/*!*/ fileName) : base() { 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); + buffer = new Buffer(s, true); this.errorHandler = errorHandler; - this.Filename = useBaseName? GetBaseName(fileName) : fileName; + this.Filename = fileName; Init(); } - string GetBaseName(string fileName) { - return System.IO.Path.GetFileName(fileName); // Return basename - } - void Init() { pos = -1; line = 1; col = 0; oldEols = 0; diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 4324b2b8..9ba11e2f 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -114,6 +114,7 @@ namespace Microsoft.Dafny { readonly Dictionary fieldConstants = new Dictionary(); readonly ISet abstractTypes = new HashSet(); readonly ISet opaqueTypes = new HashSet(); + FuelContext fuelContext = null; Program program; [ContractInvariantMethod] @@ -625,6 +626,11 @@ namespace Microsoft.Dafny { } void AddTypeDecl(NewtypeDecl dd) { Contract.Requires(dd != null); + Contract.Ensures(fuelContext == Contract.OldValue(fuelContext)); + + FuelContext oldFuelContext = this.fuelContext; + this.fuelContext = FuelSetting.NewFuelContext(dd); + AddTypeDecl_Aux(dd.tok, dd.FullName, new List()); AddWellformednessCheck(dd); // Add $Is and $IsAlloc axioms for the newtype @@ -667,6 +673,7 @@ namespace Microsoft.Dafny { sink.AddTopLevelDeclaration(new Bpl.Axiom(dd.tok, BplForall(vars, BplTrigger(is_o), body), name)); }); + this.fuelContext = oldFuelContext; } void AddTypeDecl_Aux(IToken tok, string nm, List typeArgs) { Contract.Requires(tok != null); @@ -1181,7 +1188,7 @@ namespace Microsoft.Dafny { } } - Bpl.Expr LayerSucc(Bpl.Expr e, int amt = 1) { + public Bpl.Expr LayerSucc(Bpl.Expr e, int amt = 1) { if (amt == 0) { return e; } else if (amt > 0) { @@ -1301,6 +1308,7 @@ namespace Microsoft.Dafny { { Contract.Requires(sink != null && predef != null); Contract.Requires(c != null); + Contract.Ensures(fuelContext == Contract.OldValue(fuelContext)); sink.AddTopLevelDeclaration(GetClass(c)); if (c is ArrayClassDecl) { @@ -1387,6 +1395,9 @@ namespace Microsoft.Dafny { } else if (member is Function) { var f = (Function)member; + FuelContext oldFuelContext = this.fuelContext; + this.fuelContext = FuelSetting.NewFuelContext(f); + AddClassMember_Function(f); if (!IsOpaqueFunction(f) && !f.IsBuiltin && !(f.tok is IncludeToken)) { // Opaque function's well-formedness is checked on the full version AddWellformednessCheck(f); @@ -1399,9 +1410,11 @@ namespace Microsoft.Dafny { AddClassMember_Function(cop.PrefixPredicate); // skip the well-formedness check, because it has already been done for the fixpoint-predicate } - + this.fuelContext = oldFuelContext; } else if (member is Method) { Method m = (Method)member; + FuelContext oldFuelContext = this.fuelContext; + this.fuelContext = FuelSetting.NewFuelContext(m); // wellformedness check for method specification if (m.EnclosingClass is IteratorDecl && m == ((IteratorDecl)m.EnclosingClass).Member_MoveNext) { @@ -1434,7 +1447,7 @@ namespace Microsoft.Dafny { sink.AddTopLevelDeclaration(proc); AddMethodImpl(m, proc, false); } - + this.fuelContext = oldFuelContext; } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected member } @@ -1467,7 +1480,7 @@ namespace Microsoft.Dafny { // declare function AddFunction(f); // add synonym axiom - if (f.IsRecursive) { + if (f.IsFuelAware()) { AddSynonymAxiom(f); } // add frame axiom @@ -1494,6 +1507,10 @@ namespace Microsoft.Dafny { void AddIteratorSpecAndBody(IteratorDecl iter) { Contract.Requires(iter != null); + Contract.Ensures(fuelContext == Contract.OldValue(fuelContext)); + + FuelContext oldFuelContext = this.fuelContext; + this.fuelContext = FuelSetting.NewFuelContext(iter); // wellformedness check for method specification Bpl.Procedure proc = AddIteratorProc(iter, MethodTranslationKind.SpecWellformedness); @@ -1506,6 +1523,7 @@ namespace Microsoft.Dafny { // ...and its implementation AddIteratorImpl(iter, proc); } + this.fuelContext = oldFuelContext; } Bpl.Procedure AddIteratorProc(IteratorDecl iter, MethodTranslationKind kind) { @@ -1950,7 +1968,7 @@ namespace Microsoft.Dafny { var formals = MkTyParamBinders(GetTypeParams(f), out tyargs); var args = new List(); Bpl.BoundVariable layer; - if (f.IsRecursive) { + if (f.IsFuelAware()) { layer = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$ly", predef.LayerType)); formals.Add(layer); // Note, "layer" is not added to "args" here; rather, that's done below, as needed @@ -2121,7 +2139,7 @@ namespace Microsoft.Dafny { var formals = MkTyParamBinders(GetTypeParams(f), out tyargs); Bpl.BoundVariable layer; - if (f.IsRecursive) { + if (f.IsFuelAware()) { layer = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$ly", predef.LayerType)); formals.Add(layer); // Note, "layer" is not added to "args" here; rather, that's done below, as needed @@ -2317,7 +2335,7 @@ namespace Microsoft.Dafny { void AddSynonymAxiom(Function f) { Contract.Requires(f != null); - Contract.Requires(f.IsRecursive); + Contract.Requires(f.IsFuelAware()); Contract.Requires(sink != null && predef != null); // axiom // layer synonym axiom // (forall s, $Heap, formals :: @@ -2400,7 +2418,7 @@ namespace Microsoft.Dafny { var coArgs = new List(tyexprs); var prefixArgs = new List(tyexprs); var prefixArgsLimited = new List(tyexprs); - if (pp.IsRecursive) { + if (pp.IsFuelAware()) { var sV = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$ly", predef.LayerType)); var s = new Bpl.IdentifierExpr(tok, sV); var succS = FunctionCall(tok, BuiltinFunction.LayerSucc, null, s); @@ -3073,13 +3091,13 @@ namespace Microsoft.Dafny { Bpl.FunctionCall funcIdT = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.OverriddenFunction.tok, f.OverriddenFunction.FullSanitizedName, TrType(f.OverriddenFunction.ResultType))); List argsC = new List(); List argsT = new List(); - if (f.IsRecursive) + if (f.IsFuelAware()) { - argsC.Add(etran.LayerN(1)); + argsC.Add(etran.layerInterCluster.GetFunctionFuel(f)); } - if (f.OverriddenFunction.IsRecursive) + if (f.OverriddenFunction.IsFuelAware()) { - argsT.Add(etran.LayerN(1)); + argsT.Add(etran.layerInterCluster.GetFunctionFuel(f)); } argsC.Add(etran.HeapExpr); argsT.Add(etran.HeapExpr); @@ -3726,7 +3744,7 @@ namespace Microsoft.Dafny { var typeParams = TrTypeParamDecls(f.TypeArgs); { var formals = new List(); - if (f.IsRecursive) { + if (f.IsFuelAware()) { formals.Add(new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "$ly", predef.LayerType), true)); } if (!f.IsStatic) { @@ -3750,7 +3768,7 @@ namespace Microsoft.Dafny { List argsF = new List(); List argsFFrame = new List(); List argsCanCall = new List(); - if (f.IsRecursive) { + if (f.IsFuelAware()) { var sV = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, "$ly", predef.LayerType)); var s = new Bpl.IdentifierExpr(f.tok, sV); bvars.Add(sV); @@ -3795,7 +3813,7 @@ namespace Microsoft.Dafny { sink.AddTopLevelDeclaration(new Bpl.Axiom(f.tok, ax)); } else { #else - // This is the general case + // This is the general case Bpl.Expr h0; var h0Var = BplBoundVar("$h0", predef.HeapType, out h0); Bpl.Expr h1; var h1Var = BplBoundVar("$h1", predef.HeapType, out h1); @@ -3825,7 +3843,7 @@ namespace Microsoft.Dafny { var f1args = new List(tyexprs); var f0argsCanCall = new List(tyexprs); var f1argsCanCall = new List(tyexprs); - if (f.IsRecursive) { + if (f.IsFuelAware()) { Bpl.Expr s; var sV = BplBoundVar("$ly", predef.LayerType, out s); bvars.Add(sV); f0args.Add(s); f1args.Add(s); // but don't add to f0argsCanCall or f1argsCanCall @@ -4066,8 +4084,8 @@ namespace Microsoft.Dafny { foreach (var p in GetTypeParams(f)) { args.Add(trTypeParam(p, null)); } - if (f.IsRecursive) { - args.Add(etran.LayerN(1)); + if (f.IsFuelAware()) { + args.Add(etran.layerInterCluster.GetFunctionFuel(f)); } args.Add(etran.HeapExpr); if (!f.IsStatic) { @@ -4100,8 +4118,8 @@ namespace Microsoft.Dafny { foreach (var p in GetTypeParams(f)) { args.Add(trTypeParam(p, null)); } - if (f.IsRecursive) { - args.Add(etran.LayerN(1)); + if (f.IsFuelAware()) { + args.Add(etran.layerInterCluster.GetFunctionFuel(f)); } args.Add(etran.HeapExpr); foreach (Variable p in implInParams) { @@ -5572,7 +5590,7 @@ namespace Microsoft.Dafny { tyargs.Add(TypeToTy(fm.Type)); } tyargs.Add(TypeToTy(f.ResultType)); - if (f.IsRecursive) { + if (f.IsFuelAware()) { Bpl.Expr ly; vars.Add(BplBoundVar("$ly", predef.LayerType, out ly)); args.Add(ly); formals.Add(BplFormalVar(null, predef.LayerType, true)); } @@ -6135,7 +6153,7 @@ namespace Microsoft.Dafny { var typeParams = TrTypeParamDecls(f.TypeArgs); var formals = new List(); formals.AddRange(MkTyParamFormals(GetTypeParams(f))); - if (f.IsRecursive) { + if (f.IsFuelAware()) { formals.Add(new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "$ly", predef.LayerType), true)); } formals.Add(new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "$heap", predef.HeapType), true)); @@ -6212,7 +6230,7 @@ namespace Microsoft.Dafny { Contract.Requires(predef != null); Contract.Requires(currentModule == null && codeContext == null); Contract.Ensures(currentModule == null && codeContext == null); - Contract.Ensures(Contract.Result() != null); + Contract.Ensures(Contract.Result() != null); currentModule = m.EnclosingClass.Module; codeContext = m; @@ -6495,7 +6513,7 @@ namespace Microsoft.Dafny { // parameters of the procedure List inParams = new List(); Bpl.Formal layer; - if (f.IsRecursive) { + if (f.IsFuelAware()) { layer = new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "$ly", predef.LayerType), true); inParams.Add(layer); } else { @@ -7018,9 +7036,11 @@ namespace Microsoft.Dafny { Contract.Requires(locals != null); Contract.Requires(etran != null); Contract.Requires(codeContext != null && predef != null); + Contract.Ensures(fuelContext == Contract.OldValue(fuelContext)); if (stmt is PredicateStmt) { + this.fuelContext = FuelSetting.ExpandFuelContext(stmt.Attributes, this.fuelContext); if (stmt is AssertStmt || DafnyOptions.O.DisallowSoundnessCheating) { - AddComment(builder, stmt, "assert statement"); + AddComment(builder, stmt, "assert statement"); PredicateStmt s = (PredicateStmt)stmt; TrStmt_CheckWellformed(s.Expr, builder, locals, etran, false); IToken enclosingToken = null; @@ -7040,13 +7060,14 @@ namespace Microsoft.Dafny { } } builder.Add(new Bpl.AssumeCmd(stmt.Tok, etran.TrExpr(s.Expr))); - } + } } else if (stmt is AssumeStmt) { AddComment(builder, stmt, "assume statement"); AssumeStmt s = (AssumeStmt)stmt; TrStmt_CheckWellformed(s.Expr, builder, locals, etran, false); builder.Add(new Bpl.AssumeCmd(stmt.Tok, etran.TrExpr(s.Expr), etran.TrAttributes(stmt.Attributes, null))); } + this.fuelContext = FuelSetting.PopFuelContext(); } else if (stmt is PrintStmt) { AddComment(builder, stmt, "print statement"); PrintStmt s = (PrintStmt)stmt; @@ -7279,6 +7300,7 @@ namespace Microsoft.Dafny { } else if (stmt is WhileStmt) { AddComment(builder, stmt, "while statement"); + this.fuelContext = FuelSetting.ExpandFuelContext(stmt.Attributes, this.fuelContext); var s = (WhileStmt)stmt; BodyTranslator bodyTr = null; if (s.Body != null) { @@ -7289,7 +7311,7 @@ namespace Microsoft.Dafny { }; } TrLoop(s, s.Guard, bodyTr, builder, locals, etran); - + this.fuelContext = FuelSetting.PopFuelContext(); } else if (stmt is AlternativeLoopStmt) { AddComment(builder, stmt, "alternative loop statement"); var s = (AlternativeLoopStmt)stmt; @@ -7331,6 +7353,7 @@ namespace Microsoft.Dafny { } else if (stmt is ForallStmt) { var s = (ForallStmt)stmt; + this.fuelContext = FuelSetting.ExpandFuelContext(stmt.Attributes, this.fuelContext); if (s.Kind == ForallStmt.ParBodyKind.Assign) { AddComment(builder, stmt, "forall statement (assign)"); Contract.Assert(s.Ens.Count == 0); @@ -7374,7 +7397,7 @@ namespace Microsoft.Dafny { } else { Contract.Assert(false); // unexpected kind } - + this.fuelContext = FuelSetting.PopFuelContext(); } else if (stmt is CalcStmt) { /* Translate into: if (*) { @@ -7397,9 +7420,10 @@ namespace Microsoft.Dafny { } assume line<0> op line; */ - var s = (CalcStmt)stmt; + var s = (CalcStmt)stmt; Contract.Assert(s.Steps.Count == s.Hints.Count); // established by the resolver AddComment(builder, stmt, "calc statement"); + this.fuelContext = FuelSetting.ExpandFuelContext(stmt.Attributes, this.fuelContext); CurrentIdGenerator.Push(); // put the entire calc statement within its own sub-branch if (s.Lines.Count > 0) { Bpl.IfCmd ifCmd = null; @@ -7464,7 +7488,7 @@ namespace Microsoft.Dafny { } } CurrentIdGenerator.Pop(); - + this.fuelContext = FuelSetting.PopFuelContext(); } else if (stmt is MatchStmt) { var s = (MatchStmt)stmt; TrStmt_CheckWellformed(s.Source, builder, locals, etran, true); @@ -10262,6 +10286,227 @@ namespace Microsoft.Dafny { } } + internal class FuelSettingPair + { + public int low; + public int high; + + public FuelSettingPair(int low = (int)FuelSetting.FuelAmount.LOW, int high = (int)FuelSetting.FuelAmount.HIGH) { + this.low = low; + this.high = high; + } + } + + // C#'s version of a type alias + internal class FuelContext : Dictionary { } + + internal class FuelSetting + { + public enum FuelAmount { NONE, LOW, HIGH }; + public static Stack SavedContexts = new Stack(); + + private static FuelSettingPair FuelAttrib(Function f) { + Contract.Requires(f != null); + Contract.Ensures(Contract.Result() != null); + FuelSettingPair setting = new FuelSettingPair(); + + if (f.Attributes != null) { + List args = Attributes.FindExpressions(f.Attributes, "fuel"); + if (args != null && args.Count >= 2) { + LiteralExpr literalLow = args[0] as LiteralExpr; + LiteralExpr literalHigh = args[1] as LiteralExpr; + + if (literalLow != null && literalLow.Value is BigInteger && literalHigh != null && literalHigh.Value is BigInteger) { + setting.low = (int)((BigInteger)literalLow.Value); + setting.high = (int)((BigInteger)literalHigh.Value); + } + } else if (args != null && args.Count >= 1) { + LiteralExpr literal = args[0] as LiteralExpr; + if (literal != null && literal.Value is BigInteger) { + setting.low = (int)((BigInteger)literal.Value); + setting.high = setting.low + 1; + } + } + } + + return setting; + } + + public int amount; // Amount of fuel above that represented by start + private Bpl.Expr start; // Starting fuel argument (null indicates LZ) + private Translator translator; + + public FuelSetting(Translator translator, int amount, Bpl.Expr start = null) { + this.translator = translator; + this.amount = amount; + this.start = start; + } + + public FuelSetting Offset(int offset) { + return new FuelSetting(translator, this.amount + offset, start); + } + + public Bpl.Expr LayerZero() { + Contract.Ensures(Contract.Result() != null); + return new Bpl.IdentifierExpr(Token.NoToken, "$LZ", translator.predef.LayerType); + } + + public Bpl.Expr LayerN(int n) { + Contract.Requires(0 <= n); + Contract.Ensures(Contract.Result() != null); + return translator.LayerSucc(LayerZero(), n); + } + + private Bpl.Expr ToExpr(int amount) { + if (start == null) { + return LayerN(amount); + } else { + return translator.LayerSucc(start, amount); + } + } + + public Bpl.Expr ToExpr() { + return this.ToExpr(this.amount); + } + + /// + /// Get the fuel value for this function, given the ambient environment (represented by the fuel setting) + /// the function itself, and the function call's context (if any) + /// + public Bpl.Expr GetFunctionFuel(Function f) { + Contract.Requires(f != null); + if (this.amount == (int)FuelAmount.NONE) { + return this.ToExpr(); + } else { + FuelSettingPair setting = null; + var found = translator.fuelContext.TryGetValue(f, out setting); + + if (!found) { // If the context doesn't define fuel for this function, check for a fuel attribute (which supplies a default value if none is found) + setting = FuelAttrib(f); + } + + if (this.amount == (int)FuelAmount.LOW) { + return ToExpr(setting.low); + } else if (this.amount == (int)FuelAmount.HIGH) { + return ToExpr(setting.high); + } else { + Contract.Assert(false); // Should not reach here + return null; + } + } + } + + + /// + /// Finds all fuel related attributes of the form {:fuel function low [high]} + /// Adds the setting to the context _if_ the context does not already have a setting for that function. + /// In other words, it should be called in order from most to least specific contenxt scope. + /// + private static void FindFuelAttributes(Attributes attribs, FuelContext fuelContext) { + Function f = null; + FuelSettingPair setting = null; + + if (attribs != null) { + List> results = Attributes.FindAllExpressions(attribs, "fuel"); + + if (results != null) { + foreach (List args in results) { + if (args != null && args.Count >= 2) { + // Try to extract the function from the first argument + MemberSelectExpr selectExpr = args[0].Resolved as MemberSelectExpr; + if (selectExpr != null) { + f = selectExpr.Member as Function; + } + + // Try to extract the lower fuel setting + LiteralExpr literalLow = args[1] as LiteralExpr; + if (literalLow != null && literalLow.Value is BigInteger) { + setting = new FuelSettingPair(); + setting.low = (int)((BigInteger)literalLow.Value); + } + + // The user may supply an additional high argument; if not, it defaults to low + 1 + if (f != null && args.Count >= 3) { + LiteralExpr literalHigh = args[2] as LiteralExpr; + if (setting != null && literalHigh != null && literalHigh.Value is BigInteger) { + setting.high = (int)((BigInteger)literalHigh.Value); + if (!fuelContext.ContainsKey(f)) { + fuelContext.Add(f, setting); + } + } + } else if (f != null && setting != null) { + setting.high = setting.low + 1; + if (!fuelContext.ContainsKey(f)) { + fuelContext.Add(f, setting); + } + } + } + } + } + } + } + + /// + /// Extend the given context with fuel information from the declaration itself, and enclosing modules + /// + private static void AddFuelContext(FuelContext context, TopLevelDecl decl) { + FindFuelAttributes(decl.Attributes, context); + + var module = decl.Module; + while (module != null) { + FindFuelAttributes(module.Attributes, context); + module = module.Module; + } + } + + /// + /// Creates a summary of all fuel settings in scope, starting from the given class declaration + /// + public static FuelContext NewFuelContext(TopLevelDecl decl) { + FuelContext context = new FuelContext(); + AddFuelContext(context, decl); + return context; + } + + /// + /// Creates a summary of all fuel settings in scope, starting from the given member declaration + /// + public static FuelContext NewFuelContext(MemberDecl decl) { + FuelContext context = new FuelContext(); + + FindFuelAttributes(decl.Attributes, context); + AddFuelContext(context, decl.EnclosingClass); + + return context; + } + + /// + /// Extends the given fuel context with any new fuel settings found in attribs + /// + public static FuelContext ExpandFuelContext(Attributes attribs, FuelContext oldFuelContext) { + Contract.Ensures(SavedContexts.Count == Contract.OldValue(SavedContexts.Count) + 1); + FuelContext newContext = new FuelContext(); + FindFuelAttributes(attribs, newContext); + if (newContext.Count > 0) { + foreach (var pair in oldFuelContext) { + if (!newContext.ContainsKey(pair.Key)) { // Local setting takes precedence over old context + newContext.Add(pair.Key, pair.Value); + } + } + } else { + newContext = oldFuelContext; + } + SavedContexts.Push(oldFuelContext); + return newContext; + } + + public static FuelContext PopFuelContext() { + Contract.Requires(SavedContexts.Count > 0); + return SavedContexts.Pop(); + } + + } + internal class ExpressionTranslator { public readonly Bpl.Expr HeapExpr; @@ -10270,8 +10515,8 @@ namespace Microsoft.Dafny { public readonly string This; public readonly string modifiesFrame; // the name of the context's frame variable. readonly Function applyLimited_CurrentFunction; - 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 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; [ContractInvariantMethod] @@ -10287,41 +10532,17 @@ namespace Microsoft.Dafny { Contract.Invariant(0 <= Statistics_CustomLayerFunctionCount); } - /// - /// 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) { - - Contract.Requires(translator != null); - Contract.Requires(predef != null); - Contract.Requires(heap != null); - Contract.Requires(thisVar != null); - Contract.Requires(0 <= layerInterCluster); - Contract.Requires(modifiesFrame != null); - - this.translator = translator; - this.predef = predef; - this.HeapExpr = heap; - this.This = thisVar; - this.applyLimited_CurrentFunction = applyLimited_CurrentFunction; - this.layerInterCluster = LayerN(layerInterCluster); - this.layerIntraCluster = layerIntraCluster; - this.modifiesFrame = modifiesFrame; - } - /// /// This is the most general constructor. It is private and takes all the parameters. Whenever /// 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, FuelSetting layerInterCluster, FuelSetting layerIntraCluster, string modifiesFrame) { Contract.Requires(translator != null); Contract.Requires(predef != null); Contract.Requires(heap != null); - Contract.Requires(thisVar != null); - Contract.Requires(layerInterCluster != null); + Contract.Requires(thisVar != null); Contract.Requires(modifiesFrame != null); this.translator = translator; @@ -10329,7 +10550,7 @@ namespace Microsoft.Dafny { this.HeapExpr = heap; this.This = thisVar; this.applyLimited_CurrentFunction = applyLimited_CurrentFunction; - this.layerInterCluster = layerInterCluster; + this.layerInterCluster = layerInterCluster; if (layerIntraCluster == null) { this.layerIntraCluster = layerInterCluster; } else { @@ -10365,7 +10586,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, new FuelSetting(translator, 1), null, "$_Frame") { Contract.Requires(translator != null); Contract.Requires(predef != null); Contract.Requires(heap != null); @@ -10409,7 +10630,7 @@ 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, new FuelSetting(translator, 0, layerArgument), new FuelSetting(translator, 0, layerArgument), modifiesFrame); } public ExpressionTranslator LimitedFunctions(Function applyLimited_CurrentFunction, Bpl.Expr layerArgument) { @@ -10417,16 +10638,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, new FuelSetting(translator, 0, layerArgument), modifiesFrame); } 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, layerInterCluster.Offset(offset), layerIntraCluster, modifiesFrame); 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, layerInterCluster.Offset(offset), layerIntraCluster, modifiesFrame); etOld.oldEtran = etOld; et.oldEtran = etOld; } @@ -10465,17 +10686,6 @@ namespace Microsoft.Dafny { } } - public Bpl.Expr LayerZero() { - Contract.Ensures(Contract.Result() != null); - return new Bpl.IdentifierExpr(Token.NoToken, "$LZ", predef.LayerType); - } - - public Bpl.Expr LayerN(int n) { - Contract.Requires(0 <= n); - Contract.Ensures(Contract.Result() != null); - return translator.LayerSucc(LayerZero(), n); - } - public Bpl.IdentifierExpr ModuleContextHeight() { Contract.Ensures(Contract.Result().Type != null); return new Bpl.IdentifierExpr(Token.NoToken, "$ModuleContextHeight", Bpl.Type.Int); @@ -10649,8 +10859,8 @@ namespace Microsoft.Dafny { }, fn => { var args = e.TypeApplication.ConvertAll(translator.TypeToTy); - if (fn.IsRecursive) { - args.Add(layerInterCluster); + if (fn.IsFuelAware()) { + args.Add(this.layerInterCluster.GetFunctionFuel(fn)); } if (!fn.IsStatic) { args.Add(/* translator.BoxIfUnboxed */(TrExpr(e.Obj)/*, e.Type */)); @@ -10806,15 +11016,15 @@ namespace Microsoft.Dafny { } else if (expr is FunctionCallExpr) { FunctionCallExpr e = (FunctionCallExpr)expr; Bpl.Expr layerArgument; - if (e.Function.IsRecursive) { + if (e.Function.IsFuelAware()) { Statistics_CustomLayerFunctionCount++; ModuleDefinition module = e.Function.EnclosingClass.Module; if (this.applyLimited_CurrentFunction != null && this.layerIntraCluster != null && ModuleDefinition.InSameSCC(e.Function, applyLimited_CurrentFunction)) { - layerArgument = this.layerIntraCluster; + layerArgument = this.layerIntraCluster.GetFunctionFuel(e.Function); } else { - layerArgument = this.layerInterCluster; + layerArgument = this.layerInterCluster.GetFunctionFuel(e.Function); } } else { layerArgument = null; @@ -10992,7 +11202,7 @@ namespace Microsoft.Dafny { if (cot != null) { var e0args = e.E0.Type.NormalizeExpand().TypeArgs; var e1args = e.E1.Type.NormalizeExpand().TypeArgs; - return translator.CoEqualCall(cot, e0args, e1args, null, LayerN(2), e0, e1, expr.tok); + return translator.CoEqualCall(cot, e0args, e1args, null, this.layerInterCluster.LayerN((int)FuelSetting.FuelAmount.HIGH), e0, e1, expr.tok); } typ = Bpl.Type.Bool; bOpcode = BinaryOperator.Opcode.Eq; break; @@ -11001,7 +11211,7 @@ namespace Microsoft.Dafny { if (cotx != null) { var e0args = e.E0.Type.NormalizeExpand().TypeArgs; var e1args = e.E1.Type.NormalizeExpand().TypeArgs; - var x = translator.CoEqualCall(cotx, e0args, e1args, null, LayerN(2), e0, e1, expr.tok); + var x = translator.CoEqualCall(cotx, e0args, e1args, null, this.layerInterCluster.LayerN((int)FuelSetting.FuelAmount.HIGH), e0, e1, expr.tok); return Bpl.Expr.Unary(expr.tok, UnaryOperator.Opcode.Not, x); } typ = Bpl.Type.Bool; @@ -11282,7 +11492,7 @@ namespace Microsoft.Dafny { var e2type = e.E2.Type.NormalizeExpand(); var cot = e1type.AsCoDatatype; Contract.Assert(cot != null); // the argument types of prefix equality (and prefix disequality) are codatatypes - var r = translator.CoEqualCall(cot, e1type.TypeArgs, e2type.TypeArgs, e0, LayerN(2), e1, e2); + var r = translator.CoEqualCall(cot, e1type.TypeArgs, e2type.TypeArgs, e0, this.layerInterCluster.LayerN((int)FuelSetting.FuelAmount.HIGH), e1, e2); if (e.Op == TernaryExpr.Opcode.PrefixEqOp) { return r; } else { @@ -11315,8 +11525,7 @@ namespace Microsoft.Dafny { 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); - 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, new FuelSetting(translator, 1, ly), new FuelSetting(translator, 1, ly), modifiesFrame); } if (Attributes.ContainsBool(e.Attributes, "heapQuantifier", ref _scratch)) { var h = BplBoundVar(e.Refresh("q$heap#", translator.CurrentIdGenerator), predef.HeapType, bvars); @@ -11489,7 +11698,7 @@ namespace Microsoft.Dafny { new Bpl.LambdaExpr(e.tok, new List(), bvars, null, ebody), new Bpl.LambdaExpr(e.tok, new List(), bvars, null, reqbody), new Bpl.LambdaExpr(e.tok, new List(), bvars, null, rdbody))), - layerIntraCluster ?? layerInterCluster), + layerIntraCluster != null ? layerIntraCluster.ToExpr() : layerInterCluster.ToExpr()), predef.HandleType); } @@ -12582,15 +12791,14 @@ namespace Microsoft.Dafny { // checked $PrefixEqual#Dt(k, A, B) || (0 < k ==> A.Cons? ==> B.Cons? && A.head == B.head && $PrefixEqual#2#Dt(k - 1, A.tail, B.tail)) // note the #2 in the recursive call, just like for user-defined predicates that are inlined by TrSplitExpr // free $PrefixEqual#Dt(k, A, B); var kPos = Bpl.Expr.Lt(Bpl.Expr.Literal(0), k); - var prefixEqK = CoEqualCall(codecl, e1type.TypeArgs, e2type.TypeArgs, k, etran.LayerN(2), A, B); // FunctionCall(expr.tok, CoPrefixName(codecl, 1), Bpl.Type.Bool, k, A, B); + var prefixEqK = CoEqualCall(codecl, e1type.TypeArgs, e2type.TypeArgs, k, etran.layerInterCluster.LayerN((int)FuelSetting.FuelAmount.HIGH), A, B); // FunctionCall(expr.tok, CoPrefixName(codecl, 1), Bpl.Type.Bool, k, A, B); var kMinusOne = Bpl.Expr.Sub(k, Bpl.Expr.Literal(1)); // for the inlining of the definition of prefix equality, translate the two main equality operands arguments with a higher offset (to obtain #2 functions) var etran2 = etran.LayerOffset(1); var A2 = etran2.TrExpr(e.E1); var B2 = etran2.TrExpr(e.E2); var needsTokenAdjust = TrSplitNeedsTokenAdjustment(expr); - // Dan: dafny4/Circ.dfy needs this one to be 2+, rather than 1+ - Bpl.Expr layer = LayerSucc(etran.layerInterCluster, 2); + Bpl.Expr layer = etran.layerInterCluster.LayerN((int)FuelSetting.FuelAmount.HIGH); foreach (var c in CoPrefixEquality(needsTokenAdjust ? new ForceCheckToken(expr.tok) : expr.tok, codecl, e1type.TypeArgs, e2type.TypeArgs, kMinusOne, layer, A2, B2, true)) { var p = Bpl.Expr.Binary(c.tok, BinaryOperator.Opcode.Or, prefixEqK, Bpl.Expr.Imp(kPos, c)); splits.Add(new SplitExprInfo(SplitExprInfo.K.Checked, p)); @@ -13944,7 +14152,7 @@ namespace Microsoft.Dafny { r = rr; } else if (stmt is CalcStmt) { var s = (CalcStmt)stmt; - var rr = new CalcStmt(s.Tok, s.EndTok, SubstCalcOp(s.Op), s.Lines.ConvertAll(Substitute), s.Hints.ConvertAll(SubstBlockStmt), s.StepOps.ConvertAll(SubstCalcOp), SubstCalcOp(s.ResultOp)); + var rr = new CalcStmt(s.Tok, s.EndTok, SubstCalcOp(s.Op), s.Lines.ConvertAll(Substitute), s.Hints.ConvertAll(SubstBlockStmt), s.StepOps.ConvertAll(SubstCalcOp), SubstCalcOp(s.ResultOp), SubstAttributes(s.Attributes)); rr.Steps.AddRange(s.Steps.ConvertAll(Substitute)); rr.Result = Substitute(s.Result); r = rr; diff --git a/Test/dafny0/Fuel.dfy b/Test/dafny0/Fuel.dfy new file mode 100644 index 00000000..c8a1fc2f --- /dev/null +++ b/Test/dafny0/Fuel.dfy @@ -0,0 +1,423 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +module TestModule1 { + function pos(x:int) : int + { + if x < 0 then 0 + else 1 + pos(x - 1) + } + + method test(y:int, z:int) + requires y > 5; + requires z < 0; + { + assert pos(z) == 0; + assert pos(-1) == 0; + assert pos(y) == 3 + pos(y - 3); // error: Should fail, due to lack of fuel + assert pos(y) == 4 + pos(y - 4); // Succeeds, thanks to the assume from the preceding assert + } +} + +// Test with function-level fuel boost +module TestModule2 { + function {:fuel 3} pos1(x:int) : int + { + if x < 0 then 0 + else 1 + pos1(x - 1) + } + + function {:fuel 3,5} pos2(x:int) : int + { + if x < 0 then 0 + else 1 + pos2(x - 1) + } + + function {:fuel 3,5} pos3(x:int) : int + { + if x < 0 then 0 + else 1 + pos3(x - 1) + } + + function {:opaque} {:fuel 3,5} pos4(x:int) : int + { + if x < 0 then 0 + else 1 + pos3(x - 1) + } + + method test(y:int, z:int) + requires y > 5; + requires z < 0; + { + assert pos1(z) == 0; + assert pos1(-1) == 0; + assert pos1(y) == 3 + pos1(y - 3); + assert pos1(y) == 4 + pos1(y - 4); + + assert pos2(z) == 0; + assert pos2(-1) == 0; + assert pos2(y) == 3 + pos2(y - 3); + assert pos2(y) == 4 + pos2(y - 4); + + if (*) { + assert pos3(y) == 5 + pos3(y - 5); // Just enough fuel to get here + } else { + assert pos3(y) == 6 + pos3(y - 6); // error: Should fail even with a boost, since boost is too small + } + + if (*) { + assert pos4(z) == 0; // error: Fuel shouldn't overcome opaque + } else { + reveal_pos4(); + assert pos4(y) == 5 + pos4(y - 5); // With reveal, everything should work as above + } + + + } +} + + +module TestModule3 { + // This fuel setting is equivalent to opaque, except for literals + function {:fuel 0,0} pos(x:int) : int + { + if x < 0 then 0 + else 1 + pos(x - 1) + } + + method test(y:int, z:int) + requires y > 5; + requires z < 0; + { + assert pos(z) == 0; // error: Opaque setting hides body + assert pos(-1) == 0; // Passes, since Dafny's computation mode for lits ignore fuel + assert pos(y) == 3 + pos(y - 3);// error: Opaque setting hides body + } +} + +// Test fuel settings via different contexts +module TestModule4 { + function pos(x:int) : int + { + if x < 0 then 0 + else 1 + pos(x - 1) + } + + // Should pass + method {:fuel pos,3,5} test1(y:int, z:int) + requires y > 5; + requires z < 0; + { + assert pos(z) == 0; + assert pos(-1) == 0; + assert pos(y) == 3 + pos(y - 3); + } + + method {:fuel pos,0,0} test2(y:int, z:int) + requires y > 5; + requires z < 0; + { + assert pos(z) == 0; // error: Should fail due to "opaque" fuel setting + assert pos(-1) == 0; + assert pos(y) == 3 + pos(y - 3); // error: Should fail due to "opaque" fuel setting + } + + method test3(y:int, z:int) + requires y > 5; + requires z < 0; + { + assert {:fuel pos,0,0} pos(z) == 0; // error: Should fail due to "opaque" fuel setting + assert pos(-1) == 0; + if (*) { + assert pos(y) == 3 + pos(y - 3); // error: Should fail without extra fuel setting + assert pos(y) == 6 + pos(y - 6); // error: Should fail even with previous assert turned into assume + } else { + assert {:fuel pos,3,5} pos(y) == 3 + pos(y - 3); // Should succeed with extra fuel setting + assert pos(y) == 6 + pos(y - 6); // Should succeed thanks to previous assert turned into assume + } + } + + method test4(y:int, z:int) + requires y > 5; + requires z < 0; + { + forall t:int {:fuel pos,3} | t > 0 + ensures true; + { + assert pos(y) == 3 + pos(y - 3); // Expected to pass, due to local fuel boost + } + + if (*) { + calc {:fuel pos,3} { + pos(y); + 3 + pos(y - 3); + } + } + + assert pos(y) == 3 + pos(y - 3); // error: Should fail, due to lack of fuel outside the forall + } +} + +// Test fuel settings via different module contexts +module TestModule5 { + // Test module level fuel settings, with nested modules + + module TestModule5a { + module {:fuel TestModule5aiA.pos,3} TestModule5ai { + module TestModule5aiA { + function pos(x:int) : int + { + if x < 0 then 0 + else 1 + pos(x - 1) + } + + method test(y:int, z:int) + requires y > 5; + requires z < 0; + { + assert pos(z) == 0; + assert pos(-1) == 0; + assert pos(y) == 3 + pos(y - 3); // Should pass due to intermediate module's fuel setting + } + } + + method test(y:int, z:int) + requires y > 5; + requires z < 0; + { + assert TestModule5aiA.pos(z) == 0; + assert TestModule5aiA.pos(-1) == 0; + assert TestModule5aiA.pos(y) == 3 + TestModule5aiA.pos(y - 3); // Should pass due to module level fuel + } + } + + method test(y:int, z:int) + requires y > 5; + requires z < 0; + { + assert TestModule5ai.TestModule5aiA.pos(z) == 0; + assert TestModule5ai.TestModule5aiA.pos(-1) == 0; + assert TestModule5ai.TestModule5aiA.pos(y) == 3 + TestModule5ai.TestModule5aiA.pos(y - 3); // error: Should fail, due to lack of fuel + } + } + + module {:fuel TestModule5bi.TestModule5biA.pos,3} TestModule5b { + module TestModule5bi { + module TestModule5biA { + function pos(x:int) : int + { + if x < 0 then 0 + else 1 + pos(x - 1) + } + + method test(y:int, z:int) + requires y > 5; + requires z < 0; + { + assert pos(z) == 0; + assert pos(-1) == 0; + assert pos(y) == 3 + pos(y - 3); // Should succceed due to outer module fuel setting + } + } + } + } +} + +// Test fuel setting for multiple functions +module TestModule6 { + function pos(x:int) : int + { + if x < 0 then 0 + else 1 + pos(x - 1) + } + + function neg(x:int) : int + decreases 1 - x; + { + if x > 0 then 0 + else 1 + neg(x + 1) + } + + method test1(y:int, z:int) + requires y > 5; + requires z < 5; + { + assert pos(y) == 3 + pos(y - 3); // error: Should fail, due to lack of fuel + + assert neg(z) == 3 + neg(z + 3); // error: Should fail, due to lack of fuel + } + + method {:fuel pos,3} {:fuel neg,4} test2(y:int, z:int) + requires y > 5; + requires z < -5; + { + assert pos(y) == 3 + pos(y - 3); + + assert neg(z) == 3 + neg(z + 3); + } +} + +// Test fuel settings with multiple overlapping contexts +module TestModule7 { + function {:fuel 3} pos(x:int) : int + { + if x < 0 then 0 + else 1 + pos(x - 1) + } + + function {:fuel 0,0} neg(x:int) : int + decreases 1 - x; + { + if x > 0 then 0 + else 1 + neg(x + 1) + } + + method {:fuel neg,4} {:fuel pos,0,0} test1(y:int, z:int) + requires y > 5; + requires z < -5; + { + if (*) { + assert pos(y) == 3 + pos(y - 3); // error: Method fuel should override function fuel, so this should fail + assert neg(z) == 3 + neg(z + 3); // Method fuel should override function fuel, so this succeeds + } + + forall t:int {:fuel pos,3} | t > 0 + ensures true; + { + assert pos(y) == 3 + pos(y - 3); // Statement fuel should override method fuel, so this should succeed + } + } +} + +// Test fuel in a slightly more complicated setting +module TestModule8 { + + newtype byte = i:int | 0 <= i < 0x100 + newtype uint64 = i:int | 0 <= i < 0x10000000000000000 + + datatype G = GUint64 + | GArray(elt:G) + | GTuple(t:seq) + | GByteArray + | GTaggedUnion(cases:seq) + + datatype V = VUint64(u:uint64) + | VTuple(t:seq) + | VCase(c:uint64, val:V) + + predicate {:fuel 2} ValInGrammar(val:V, grammar:G) + { + match val + case VUint64(_) => grammar.GUint64? + case VTuple(t) => grammar.GTuple? && |t| == |grammar.t| + && forall i :: 0 <= i < |t| ==> ValInGrammar(t[i], grammar.t[i]) + case VCase(c, val) => grammar.GTaggedUnion? && int(c) < |grammar.cases| && ValInGrammar(val, grammar.cases[c]) + } + + datatype CRequest = CRequest(client:EndPoint, seqno:uint64, request:CAppMessage) | CRequestNoOp() + + type EndPoint + function method EndPoint_grammar() : G { GUint64 } + function method CRequest_grammar() : G { GTaggedUnion([ GTuple([EndPoint_grammar(), GUint64, CAppMessage_grammar()]), GUint64]) } + + function method parse_EndPoint(val:V) : EndPoint + requires ValInGrammar(val, EndPoint_grammar()); + + type CAppMessage + function method CAppMessage_grammar() : G { GTaggedUnion([GUint64, GUint64, GUint64]) } + function method parse_AppMessage(val:V) : CAppMessage + requires ValInGrammar(val, CAppMessage_grammar()); + + function method {:fuel ValInGrammar,1,2} parse_Request1(val:V) : CRequest + requires ValInGrammar(val, CRequest_grammar()); + { + if val.c == 0 then + var ep := parse_EndPoint(val.val.t[0]); // With default fuel, error: function precondition, destructor, index + CRequest(ep, val.val.t[1].u, parse_AppMessage(val.val.t[2])) // error: index out of range, destructor + else + CRequestNoOp() + } + + function method parse_Request2(val:V) : CRequest + requires ValInGrammar(val, CRequest_grammar()); + { + if val.c == 0 then + var ep := parse_EndPoint(val.val.t[0]); // With fuel boosted to 2 this succeeds + CRequest(ep, val.val.t[1].u, parse_AppMessage(val.val.t[2])) // error: destructor + else + CRequestNoOp() + } + + function method {:fuel ValInGrammar,3} parse_Request3(val:V) : CRequest + requires ValInGrammar(val, CRequest_grammar()); + { + if val.c == 0 then + var ep := parse_EndPoint(val.val.t[0]); + CRequest(ep, val.val.t[1].u, parse_AppMessage(val.val.t[2])) // With one more boost, everything succeeds + else + CRequestNoOp() + } + + // With the method, everything succeeds with one less fuel boost (i.e., 2, rather than 3, as in parse_Request3) + method parse_Request4(val:V) returns (req:CRequest) + requires ValInGrammar(val, CRequest_grammar()); + { + if val.c == 0 { + var ep := parse_EndPoint(val.val.t[0]); + req := CRequest(ep, val.val.t[1].u, parse_AppMessage(val.val.t[2])); + } else { + req := CRequestNoOp(); + } + } +} + + +// Test fuel when it's applied to a non-recursive function +module TestModule9 { + function abs(x:int) : int + { + if x < 0 then -1 * x else x + } + + // All should pass. + method test1(y:int, z:int) + requires y > 5; + requires z < 0; + { + assert abs(z) == -1*z; + assert abs(y) == y; + assert abs(-1) == 1; + } + + // Method-level fuel override + method {:fuel abs,0,0} test2(y:int, z:int) + requires y > 5; + requires z < 0; + { + assert abs(z) == -1*z; // error: Cannot see the body of abs + assert abs(y) == y; // error: Cannot see the body of abs + assert abs(-1) == 1; // lit bypasses fuel, so this should succeed + } + + // Statement-level fuel override + method test3(y:int, z:int) + requires y > 5; + requires z < 0; + { + assert {:fuel abs,0,0} abs(z) == -1*z; // error: Cannot see the body of abs + assert abs(y) == y; // Normal success + assert abs(-1) == 1; // lit bypasses fuel, so this should succeed + } + + // Giving more fuel to a non-recursive function won't help, + // but it shouldn't hurt either. + method {:fuel abs,5,7} test4(y:int, z:int) + requires y > 5; + requires z < 0; + { + assert abs(z) == -1*z; + assert abs(y) == y; + assert abs(-1) == 1; + } +} + diff --git a/Test/dafny0/Fuel.dfy.expect b/Test/dafny0/Fuel.dfy.expect new file mode 100644 index 00000000..4c180a9c --- /dev/null +++ b/Test/dafny0/Fuel.dfy.expect @@ -0,0 +1,95 @@ +Fuel.dfy(17,23): Error: assertion violation +Execution trace: + (0,0): anon0 +Fuel.dfy(65,28): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon6_Else +Fuel.dfy(69,28): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon6_Then + (0,0): anon7_Then +Fuel.dfy(92,23): Error: assertion violation +Execution trace: + (0,0): anon0 +Fuel.dfy(94,23): Error: assertion violation +Execution trace: + (0,0): anon0 +Fuel.dfy(120,23): Error: assertion violation +Execution trace: + (0,0): anon0 +Fuel.dfy(122,23): Error: assertion violation +Execution trace: + (0,0): anon0 +Fuel.dfy(129,39): Error: assertion violation +Execution trace: + (0,0): anon0 +Fuel.dfy(132,27): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon3_Then +Fuel.dfy(133,27): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon3_Then +Fuel.dfy(157,23): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon10_Else + (0,0): anon9 +Fuel.dfy(200,56): Error: assertion violation +Execution trace: + (0,0): anon0 +Fuel.dfy(245,23): Error: assertion violation +Execution trace: + (0,0): anon0 +Fuel.dfy(247,23): Error: assertion violation +Execution trace: + (0,0): anon0 +Fuel.dfy(280,27): 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 +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' +Execution trace: + (0,0): anon0 + (0,0): anon7_Else + (0,0): anon8_Then +Fuel.dfy(335,51): 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 +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' +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' +Execution trace: + (0,0): anon0 + (0,0): anon7_Else + (0,0): anon8_Then +Fuel.dfy(397,23): Error: assertion violation +Execution trace: + (0,0): anon0 +Fuel.dfy(398,23): Error: assertion violation +Execution trace: + (0,0): anon0 +Fuel.dfy(407,39): Error: assertion violation +Execution trace: + (0,0): anon0 + +Dafny program verifier finished with 51 verified, 24 errors diff --git a/Test/dafny4/Circ.dfy b/Test/dafny4/Circ.dfy index e7609195..d110c05c 100644 --- a/Test/dafny4/Circ.dfy +++ b/Test/dafny4/Circ.dfy @@ -16,6 +16,7 @@ function zip(a: Stream, b: Stream): Stream { Cons(a.head, zip(b, a.tail)) } colemma BlinkZipProperty() ensures zip(zeros(), ones()) == blink(); { + BlinkZipProperty(); } // ----- Thue-Morse sequence ----- @@ -75,6 +76,7 @@ colemma FProperty(s: Stream) // def. zip Cons(s.head, Cons(not(s).head, zip(s.tail, not(s).tail))); } + FProperty(s.tail); } // The fix-point theorem now follows easily. -- cgit v1.2.3 From 550caf7bc7e6427f26bfb3d24f224e12c6c1c318 Mon Sep 17 00:00:00 2001 From: leino Date: Wed, 1 Jul 2015 13:29:11 -0700 Subject: Fixed bug in BplImp! Improvements in encoding of reads of function values. --- Binaries/DafnyPrelude.bpl | 8 ++++++++ Source/Dafny/Translator.cs | 16 +++++++--------- Test/VerifyThis2015/Problem3.dfy | 2 +- Test/dafny4/NumberRepresentations.dfy | 2 +- 4 files changed, 17 insertions(+), 11 deletions(-) (limited to 'Test') diff --git a/Binaries/DafnyPrelude.bpl b/Binaries/DafnyPrelude.bpl index dbf9b76c..2ca10f73 100644 --- a/Binaries/DafnyPrelude.bpl +++ b/Binaries/DafnyPrelude.bpl @@ -273,6 +273,8 @@ const unique class._System.set: ClassName; const unique class._System.seq: ClassName; const unique class._System.multiset: ClassName; +function Tclass._System.object(): Ty; + function /*{:never_pattern true}*/ dtype(ref): Ty; // changed from ClassName to Ty function TypeTuple(a: ClassName, b: ClassName): ClassName; @@ -287,6 +289,12 @@ axiom (forall a: ClassName, b: ClassName :: { TypeTuple(a,b) } type HandleType; +function SetRef_to_SetBox(s: [ref]bool): Set Box; +axiom (forall s: [ref]bool, bx: Box :: { SetRef_to_SetBox(s)[bx] } + SetRef_to_SetBox(s)[bx] == s[$Unbox(bx): ref]); +axiom (forall s: [ref]bool :: { SetRef_to_SetBox(s) } + $Is(SetRef_to_SetBox(s), TSet(Tclass._System.object()))); + // --------------------------------------------------------------- // -- Datatypes -------------------------------------------------- // --------------------------------------------------------------- diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index c98bd203..78f5c89d 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -5713,11 +5713,9 @@ namespace Microsoft.Dafny { { // forall t1, .., tN+1 : Ty, p: [Heap, Box, ..., Box] Box, heap : Heap, b1, ..., bN : Box - // :: RequriesN(...) ==> ApplyN(t1, .. tN+1, HandleN(h, r, rd), heap, b1, ..., bN) = h[heap, b1, ..., bN] - // - // no precondition for these, but: - // for requires, we add: RequiresN(...) <== r[heap, b1, ..., bN] - // for reads, we could: ReadsN(...)[bx] ==> rd[heap, b1, ..., bN][bx] , but we don't + // :: ApplyN(t1, .. tN+1, HandleN(h, r, rd), heap, b1, ..., bN) == h[heap, b1, ..., bN] + // :: RequiresN(t1, .. tN+1, HandleN(h, r, rd), heap, b1, ..., bN) <== r[heap, b1, ..., bN] + // :: ReadsN(t1, .. tN+1, HandleN(h, r, rd), heap, b1, ..., bN) == rd[heap, b1, ..., bN] Action SelectorSemantics = (selector, selectorTy, selectorVar, selectorVarTy, precond, precondTy) => { Contract.Assert((precond == null) == (precondTy == null)); var bvars = new List(); @@ -5919,7 +5917,7 @@ namespace Microsoft.Dafny { var inner_name = GetClass(td).TypedIdent.Name; string name = "T" + inner_name; // Create the type constructor - { + if (td.Name != "object") { // the type constructor for "object" is in DafnyPrelude.bpl Bpl.Variable tyVarOut = BplFormalVar(null, predef.Ty, false); List args = new List( Enumerable.Range(0, arity).Select(i => @@ -11462,10 +11460,10 @@ namespace Microsoft.Dafny { } var rdvars = new List(); - var o = translator.UnboxIfBoxed(BplBoundVar(varNameGen.FreshId("#o#"), predef.BoxType, rdvars), new ObjectType()); - + var o = BplBoundVar(varNameGen.FreshId("#o#"), predef.RefType, rdvars); Bpl.Expr rdbody = new Bpl.LambdaExpr(e.tok, new List(), rdvars, null, translator.InRWClause(e.tok, o, null, e.Reads.ConvertAll(su.SubstFrameExpr), et, null, null)); + rdbody = translator.FunctionCall(e.tok, "SetRef_to_SetBox", predef.SetType(e.tok, true, predef.BoxType), rdbody); return translator.Lit( translator.FunctionCall(e.tok, BuiltinFunction.AtLayer, predef.HandleType, @@ -14220,7 +14218,7 @@ namespace Microsoft.Dafny { Contract.Requires(b != null); Contract.Ensures(Contract.Result() != null); - if (a == Bpl.Expr.True || b == Bpl.Expr.True || b == Bpl.Expr.False) { + if (a == Bpl.Expr.True || b == Bpl.Expr.True) { return b; } else if (a == Bpl.Expr.False) { return Bpl.Expr.True; diff --git a/Test/VerifyThis2015/Problem3.dfy b/Test/VerifyThis2015/Problem3.dfy index 10ad2d3a..4205035d 100644 --- a/Test/VerifyThis2015/Problem3.dfy +++ b/Test/VerifyThis2015/Problem3.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" /vcsMaxKeepGoingSplits:5 "%s" > "%t" // RUN: %diff "%s.expect" "%t" // Rustan Leino diff --git a/Test/dafny4/NumberRepresentations.dfy b/Test/dafny4/NumberRepresentations.dfy index 8a94505c..1ebdf64c 100644 --- a/Test/dafny4/NumberRepresentations.dfy +++ b/Test/dafny4/NumberRepresentations.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /vcsMaxKeepGoingSplits:5 /dprint:"%t.dprint" "%s" > "%t" +// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t" // RUN: %diff "%s.expect" "%t" // We consider a number representation that consists of a sequence of digits. The least -- cgit v1.2.3 From c7f6887e452cbb91a8297bb64db39a8066750351 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 2 Jul 2015 11:46:48 -0700 Subject: Added another lemma to a test file --- Test/dafny4/NipkowKlein-chapter3.dfy | 6 ++++++ Test/dafny4/NipkowKlein-chapter3.dfy.expect | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'Test') diff --git a/Test/dafny4/NipkowKlein-chapter3.dfy b/Test/dafny4/NipkowKlein-chapter3.dfy index ab45f536..725d68f6 100644 --- a/Test/dafny4/NipkowKlein-chapter3.dfy +++ b/Test/dafny4/NipkowKlein-chapter3.dfy @@ -131,6 +131,12 @@ lemma AsimpCorrect(a: aexp, s: state) forall a' | a' < a { AsimpCorrect(a', s); } } +// The following lemma is not in the Nipkow and Klein book, but it's a fun one to prove. +lemma ASimplInvolutive(a: aexp) + ensures asimp(asimp(a)) == asimp(a) +{ +} + // ----- boolean expressions ----- datatype bexp = Bc(v: bool) | Not(bexp) | And(bexp, bexp) | Less(aexp, aexp) diff --git a/Test/dafny4/NipkowKlein-chapter3.dfy.expect b/Test/dafny4/NipkowKlein-chapter3.dfy.expect index ab18d98e..bb45fee9 100644 --- a/Test/dafny4/NipkowKlein-chapter3.dfy.expect +++ b/Test/dafny4/NipkowKlein-chapter3.dfy.expect @@ -1,2 +1,2 @@ -Dafny program verifier finished with 28 verified, 0 errors +Dafny program verifier finished with 30 verified, 0 errors -- cgit v1.2.3 From e10098cde7bac9a7a1576000fa29d15f1fcd8970 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 2 Jul 2015 16:06:02 -0700 Subject: Type parameters in method/function signatures are no longer auto-declared. Although convenient and concise, the auto-declare behavior has on many occasions caused confusion when a type name has accidentally been mistyped (and Dafny had then accepted and auto-declared the name). Note, the behavior of filling in missing type parameters is still supported. This mode, although unusual (even original?) in languages, is different from the auto- declare behavior. For auto-declare, identifiers could be used in the program without having a declaration. For fill-in parameters, the implicitly declared type parameters remain anonymous. --- Source/Dafny/Resolver.cs | 6 ---- Test/dafny0/Basics.dfy | 2 +- Test/dafny0/Modules0.dfy | 14 ++++----- Test/dafny0/Modules0.dfy.expect | 19 +++--------- Test/dafny0/NestedMatch.dfy | 2 +- Test/dafny0/NestedPatterns.dfy | 8 ++--- Test/dafny0/ResolutionErrors.dfy | 10 +++--- Test/hofs/Examples.dfy | 14 ++++----- Test/hofs/Fold.dfy | 2 +- Test/hofs/Monads.dfy | 34 ++++++++++---------- Test/hofs/ReadsReads.dfy | 52 +++++++++++++++---------------- Test/hofs/ResolveError.dfy | 34 ++++++++++---------- Test/hofs/ResolveError.dfy.expect | 6 ++-- Test/hofs/Simple.dfy | 20 ++++++------ Test/hofs/TreeMapSimple.dfy | 24 +++++++-------- Test/hofs/Twice.dfy | 4 +-- Test/hofs/VectorUpdate.dfy | 65 +++++++++++++++++++++++++++++---------- Test/hofs/VectorUpdate.dfy.expect | 2 +- 18 files changed, 166 insertions(+), 152 deletions(-) (limited to 'Test') diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 29e36ccd..7c78c1e2 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -8469,12 +8469,6 @@ namespace Microsoft.Dafny r = ResolveExprDotCall(expr.tok, receiver, member, expr.OptTypeArguments, opts.codeContext, allowMethodCall); } #endif - } else if (option.Opt == ResolveTypeOptionEnum.AllowPrefixExtend && expr.OptTypeArguments == null) { - // it woulc plausibly be a type parameter, but isn't; we will declare it automatically - tp = new TypeParameter(expr.tok, expr.Name, defaultTypeArguments.Count, option.Parent); - defaultTypeArguments.Add(tp); - r = new Resolver_IdentifierExpr(expr.tok, tp); - allTypeParameters.Push(expr.Name, tp); } 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); diff --git a/Test/dafny0/Basics.dfy b/Test/dafny0/Basics.dfy index c8fa76c8..89b0f02a 100644 --- a/Test/dafny0/Basics.dfy +++ b/Test/dafny0/Basics.dfy @@ -100,7 +100,7 @@ method ExpliesAssociativityM(A: bool, B: bool, C: bool) { } } -method ExpliesShortCircuiting(a: array) +method ExpliesShortCircuiting(a: array) { assert a == null || 0 <= a.Length; // (W) assert a != null ==> 0 <= a.Length; // (X) -- same as (W) diff --git a/Test/dafny0/Modules0.dfy b/Test/dafny0/Modules0.dfy index 34aba3de..dbbffd87 100644 --- a/Test/dafny0/Modules0.dfy +++ b/Test/dafny0/Modules0.dfy @@ -71,16 +71,17 @@ module X1 { } module X2 { + import opened X1 class MyClass2 { - method Down(x1: MyClass1, x0: MyClass0) { + method Down(x1: MyClass1, x0: X0'.MyClass0) { x1.Down(x0); } - method WayDown(x0: MyClass0) { + method WayDown(x0: X0'.MyClass0) { x0.Down(); } method Up() { } - method Somewhere(y: MyClassY) { + method Somewhere(y: MyClassY) { // error: no such type in scope y.M(); } } @@ -97,8 +98,7 @@ module YY { class ClassG { method T() { } function method TFunc(): int { 10 } - method V(y: MyClassY) { // Note, MyClassY is in scope, since we are in the _default - // module, which imports everything + method V(y: MyClassY) { y.M(); } } @@ -141,10 +141,10 @@ class AClassWithSomeField { SomeField := SomeField + 4; var a := old(SomeField); // error: old can only be used in ghost contexts var b := fresh(this); // error: fresh can only be used in ghost contexts - var c := allocated(this); // error: allocated can only be used in ghost contexts +// var c := allocated(this); // error: allocated can only be used in ghost contexts if (fresh(this)) { // this guard makes the if statement a ghost statement ghost var x := old(SomeField); // this is a ghost context, so it's okay - ghost var y := allocated(this); // this is a ghost context, so it's okay +// ghost var y := allocated(this); // this is a ghost context, so it's okay } } } diff --git a/Test/dafny0/Modules0.dfy.expect b/Test/dafny0/Modules0.dfy.expect index 5d11f9c9..d2f0bcc8 100644 --- a/Test/dafny0/Modules0.dfy.expect +++ b/Test/dafny0/Modules0.dfy.expect @@ -9,13 +9,8 @@ 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(76,9): Error: type MyClass1 does not have a member Down -Modules0.dfy(76,13): Error: expected method call, found expression -Modules0.dfy(79,9): Error: type MyClass0 does not have a member Down -Modules0.dfy(79,13): Error: expected method call, found expression -Modules0.dfy(84,8): Error: type MyClassY does not have a member M -Modules0.dfy(84,9): Error: expected method call, found expression -Modules0.dfy(92,19): Error: Undeclared top-level type or type parameter: ClassG (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(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' @@ -35,11 +30,5 @@ Modules0.dfy(320,11): Error: Undeclared top-level type or type parameter: Wazzup 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(102,6): Error: type MyClassY does not have a member M -Modules0.dfy(102,7): Error: expected method call, found expression -Modules0.dfy(127,11): Error: ghost variables are allowed only in specification contexts -Modules0.dfy(142,13): Error: old expressions are allowed only in specification and ghost contexts -Modules0.dfy(143,13): Error: fresh expressions are allowed only in specification and ghost contexts -Modules0.dfy(144,13): Error: unresolved identifier: allocated -Modules0.dfy(147,21): Error: unresolved identifier: allocated -42 resolution/type errors detected in Modules0.dfy +Modules0.dfy(101,14): Error: Undeclared top-level type or type parameter: MyClassY (did you forget to qualify a name?) +31 resolution/type errors detected in Modules0.dfy diff --git a/Test/dafny0/NestedMatch.dfy b/Test/dafny0/NestedMatch.dfy index e6e7c489..81319b4a 100644 --- a/Test/dafny0/NestedMatch.dfy +++ b/Test/dafny0/NestedMatch.dfy @@ -28,7 +28,7 @@ function last(xs: List): T case Cons(y, Cons(z, zs)) => last(Cons(z, zs)) } -method checkLast(y: T) { +method checkLast(y: T) { assert last(Cons(y, Nil)) == y; assert last(Cons(y, Cons(y, Nil))) == last(Cons(y, Nil)); } diff --git a/Test/dafny0/NestedPatterns.dfy b/Test/dafny0/NestedPatterns.dfy index ef597936..d1d88b2a 100644 --- a/Test/dafny0/NestedPatterns.dfy +++ b/Test/dafny0/NestedPatterns.dfy @@ -69,7 +69,7 @@ method MethodG(xs: List) returns (xxs: List>) case Cons(h, Cons(ht, tt)) => } -method AssertionFailure(xs: List) +method AssertionFailure(xs: List) { match xs case (Nil) => // BUG: this line causes an assertion in the Dafny implementation (what should happen is that "(Nil)" should not be allowed here) @@ -100,7 +100,7 @@ method DuplicateIdentifierInPattern2(xs: List) case Cons(h, Cons(e, e)) => // BUG: here, the duplicate identifier is detected, but the error message is shown 3 times, which is less than ideal } -method Tuples0(xs: List, ys: List) +method Tuples0(xs: List, ys: List) { match (xs, ys) case (Nil, Nil) => @@ -110,14 +110,14 @@ method Tuples0(xs: List, ys: List) // only the identifiers in the last constructors are } -method Tuples1(xs: List, ys: List) +method Tuples1(xs: List, ys: List) { match (xs, ys, 4) case (Nil, Nil) => // BUG: the mismatch of 3 versus 2 arguments in the previous line and this line causes Dafny to crash with an // assertion failure "mc.CasePatterns.Count == e.Arguments.Count" } -method Tuples2(xs: List, ys: List) +method Tuples2(xs: List, ys: List) { match (xs, ys, ()) case (Nil, Nil, ()) => // BUG: Dafny crashes with an assertion failure "e.Arguments.Count >= 1" diff --git a/Test/dafny0/ResolutionErrors.dfy b/Test/dafny0/ResolutionErrors.dfy index 761cffa0..8c910959 100644 --- a/Test/dafny0/ResolutionErrors.dfy +++ b/Test/dafny0/ResolutionErrors.dfy @@ -1253,14 +1253,14 @@ module SignatureCompletion { datatype Dt = Ctor(X -> Dt) // error: X is not a declared type datatype Et = Ctor(X -> Et, Y) // error: X is not a declared type - // For methods and functions, signatures can auto-declare type parameters - method My0(s: set, x: A -> B) - method My1(x: A -> B, s: set) + + method My0(s: set, x: A -> B) + method My1(x: A -> B, s: set) method My2(s: set, x: A -> B) method My3(x: A -> B, s: set) - function F0(s: set, x: A -> B): int - function F1(x: A -> B, s: set): int + function F0(s: set, x: A -> B): int + function F1(x: A -> B, s: set): int function F2(s: set, x: A -> B): int function F3(x: A -> B, s: set): int } diff --git a/Test/hofs/Examples.dfy b/Test/hofs/Examples.dfy index be2672f5..306d278d 100644 --- a/Test/hofs/Examples.dfy +++ b/Test/hofs/Examples.dfy @@ -1,14 +1,14 @@ // RUN: %dafny /print:"%t.print" "%s" > "%t" // RUN: %diff "%s.expect" "%t" -function Apply(f: A -> B, x: A): B +function Apply(f: A -> B, x: A): B reads f.reads(x); requires f.requires(x); { f(x) } -function Apply'(f: A -> B) : A -> B +function Apply'(f: A -> B) : A -> B { x reads f.reads(x) requires f.requires(x) @@ -16,7 +16,7 @@ function Apply'(f: A -> B) : A -> B } -function Compose(f: B -> C, g:A -> B): A -> C +function Compose(f: B -> C, g:A -> B): A -> C { x reads g.reads(x) reads if g.requires(x) then f.reads(g(x)) else {} @@ -25,21 +25,21 @@ function Compose(f: B -> C, g:A -> B): A -> C => f(g(x)) } -function W(f : (A,A) -> A): A -> A +function W(f : (A,A) -> A): A -> A { x requires f.requires(x,x) reads f.reads(x,x) => f(x,x) } -function Curry(f : (A,B) -> C) : A -> B -> C +function Curry(f : (A,B) -> C) : A -> B -> C { x => y requires f.requires(x,y) reads f.reads(x,y) => f(x,y) } -function Uncurry(f : A -> B -> C) : (A,B) -> C +function Uncurry(f : A -> B -> C) : (A,B) -> C { (x,y) requires f.requires(x) requires f(x).requires(y) @@ -48,7 +48,7 @@ function Uncurry(f : A -> B -> C) : (A,B) -> C => f(x)(y) } -function S(f : (A,B) -> C, g : A -> B): A -> C +function S(f : (A,B) -> C, g : A -> B): A -> C { x requires g.requires(x) requires f.requires(x,g(x)) diff --git a/Test/hofs/Fold.dfy b/Test/hofs/Fold.dfy index 6ca2d3b1..9bcd9e02 100644 --- a/Test/hofs/Fold.dfy +++ b/Test/hofs/Fold.dfy @@ -13,7 +13,7 @@ function method Eval(e : Expr): int case Lit(i) => i } -function method Fold(xs : List, unit : B, f : (A,B) -> B): B +function method Fold(xs : List, unit : B, f : (A,B) -> B): B reads f.reads; requires forall x, y :: x < xs ==> f.requires(x,y); { diff --git a/Test/hofs/Monads.dfy b/Test/hofs/Monads.dfy index 3598d2b3..633dd339 100644 --- a/Test/hofs/Monads.dfy +++ b/Test/hofs/Monads.dfy @@ -4,29 +4,29 @@ abstract module Monad { type M - function method Return(x: A): M - function method Bind(m: M, f:A -> M):M - reads f.reads; - requires forall a :: f.requires(a); + function method Return(x: A): M + function method Bind(m: M, f:A -> M):M + reads f.reads + requires forall a :: f.requires(a) // return x >>= f = f x - lemma LeftIdentity(x : A, f : A -> M) - requires forall a :: f.requires(a); - ensures Bind(Return(x),f) == f(x); + lemma LeftIdentity(x : A, f : A -> M) + requires forall a :: f.requires(a) + ensures Bind(Return(x),f) == f(x) // m >>= return = m - lemma RightIdentity(m : M) - ensures Bind(m,Return) == m; + lemma RightIdentity(m : M) + ensures Bind(m,Return) == m // (m >>= f) >>= g = m >>= (x => f(x) >>= g) - lemma Associativity(m : M, f:A -> M, g: B -> M) - requires forall a :: f.requires(a); - requires forall b :: g.requires(b); + lemma Associativity(m : M, f:A -> M, g: B -> M) + requires forall a :: f.requires(a) + requires forall b :: g.requires(b) ensures Bind(Bind(m,f),g) == Bind(m,x reads f.reads(x) reads g.reads requires f.requires(x) - requires forall b :: g.requires(b) => Bind(f(x),g)); + requires forall b :: g.requires(b) => Bind(f(x),g)) } module Identity refines Monad { @@ -101,21 +101,21 @@ module List refines Monad { function method Return(x: A): M { Cons(x,Nil) } - function method Concat(xs: M, ys: M): M + function method Concat(xs: M, ys: M): M { match xs case Nil => ys case Cons(x,xs) => Cons(x,Concat(xs,ys)) } - function method Join(xss: M>) : M + function method Join(xss: M>) : M { match xss case Nil => Nil case Cons(xs,xss) => Concat(xs,Join(xss)) } - function method Map(xs: M, f: A -> B):M + function method Map(xs: M, f: A -> B):M reads f.reads; requires forall a :: f.requires(a); { @@ -170,7 +170,7 @@ module List refines Monad { ensures Concat(Concat(xs,ys),zs) == Concat(xs,Concat(ys,zs)); {} - lemma BindMorphism(xs : M, ys: M, f : A -> M) + lemma BindMorphism(xs : M, ys: M, f : A -> M) requires forall a :: f.requires(a); ensures Bind(Concat(xs,ys),f) == Concat(Bind(xs,f),Bind(ys,f)); { diff --git a/Test/hofs/ReadsReads.dfy b/Test/hofs/ReadsReads.dfy index e11473bd..a6f8d922 100644 --- a/Test/hofs/ReadsReads.dfy +++ b/Test/hofs/ReadsReads.dfy @@ -2,58 +2,58 @@ // RUN: %diff "%s.expect" "%t" module ReadsRequiresReads { - function MyReadsOk(f : A -> B, a : A) : set - reads f.reads(a); + function MyReadsOk(f : A -> B, a : A) : set + reads f.reads(a) { f.reads(a) } - function MyReadsOk2(f : A -> B, a : A) : set - reads f.reads(a); + function MyReadsOk2(f : A -> B, a : A) : set + reads f.reads(a) { (f.reads)(a) } - function MyReadsOk3(f : A -> B, a : A) : set - reads (f.reads)(a); + function MyReadsOk3(f : A -> B, a : A) : set + reads (f.reads)(a) { f.reads(a) } - function MyReadsOk4(f : A -> B, a : A) : set - reads (f.reads)(a); + function MyReadsOk4(f : A -> B, a : A) : set + reads (f.reads)(a) { (f.reads)(a) } - function MyReadsBad(f : A -> B, a : A) : set + function MyReadsBad(f : A -> B, a : A) : set { f.reads(a) // error: MyReadsBad does not have permission to read what f.reads(a) reads } - function MyReadsBad2(f : A -> B, a : A) : set + function MyReadsBad2(f : A -> B, a : A) : set { (f.reads)(a) // error: MyReadsBad2 does not have permission to read what f.reads(a) reads } - function MyReadsOk'(f : A -> B, a : A, o : object) : bool - reads f.reads(a); + function MyReadsOk'(f : A -> B, a : A, o : object) : bool + reads f.reads(a) { o in f.reads(a) } - function MyReadsBad'(f : A -> B, a : A, o : object) : bool + function MyReadsBad'(f : A -> B, a : A, o : object) : bool { o in f.reads(a) // error: MyReadsBad' does not have permission to read what f.reads(a) reads } - function MyRequiresOk(f : A -> B, a : A) : bool - reads f.reads(a); + function MyRequiresOk(f : A -> B, a : A) : bool + reads f.reads(a) { f.requires(a) } - function MyRequiresBad(f : A -> B, a : A) : bool + function MyRequiresBad(f : A -> B, a : A) : bool { f.requires(a) // error: MyRequiresBad does not have permission to read what f.requires(a) reads } @@ -72,11 +72,11 @@ module WhatWeKnowAboutReads { } class S { - var s : S; + var s : S } function ReadsSomething(s : S):() - reads s; + reads s {()} method MaybeSomething() { @@ -105,29 +105,29 @@ module WhatWeKnowAboutReads { module ReadsAll { function A(f: int -> int) : int - reads set o,x | o in f.reads(x) :: o; - requires forall x :: f.requires(x); + reads set o,x | o in f.reads(x) :: o + requires forall x :: f.requires(x) { f(0) + f(1) + f(2) } function method B(f: int -> int) : int - reads set o,x | o in f.reads(x) :: o; - requires forall x :: f.requires(x); + reads set o,x | o in f.reads(x) :: o + requires forall x :: f.requires(x) { f(0) + f(1) + f(2) } function C(f: int -> int) : int - reads f.reads; - requires forall x :: f.requires(x); + reads f.reads + requires forall x :: f.requires(x) { f(0) + f(1) + f(2) } function method D(f: int -> int) : int - reads f.reads; - requires forall x :: f.requires(x); + reads f.reads + requires forall x :: f.requires(x) { f(0) + f(1) + f(2) } diff --git a/Test/hofs/ResolveError.dfy b/Test/hofs/ResolveError.dfy index 3c0d7cd9..ae838eb3 100644 --- a/Test/hofs/ResolveError.dfy +++ b/Test/hofs/ResolveError.dfy @@ -3,9 +3,9 @@ method ResolutionErrors() { - var x; - var g5 := x, y => (y, x); // fail at resolution - var g6 := x, (y => (y, x)); // fail at resolution + var x; + var g5 := x, y => (y, x); // fail at resolution + var g6 := x, (y => (y, x)); // fail at resolution } // cannot assign functions @@ -23,20 +23,20 @@ method Nope3() { method RequiresFail(f : int -> int) // ok - requires f(0) == 0; - requires f.requires(0); - requires f.reads(0) == {}; + requires f(0) == 0 + requires f.requires(0) + requires f.reads(0) == {} // fail - requires f(0) == true; - requires f(1,2) == 0; - requires f(true) == 0; - requires f.requires(true); - requires f.requires(1) == 0; - requires f.requires(1,2); - requires f.reads(true) == {}; - requires f.reads(1) == 0; - requires f.reads(1,2) == {}; + requires f(0) == true + requires f(1,2) == 0 + requires f(true) == 0 + requires f.requires(true) + requires f.requires(1) == 0 + requires f.requires(1,2) + requires f.reads(true) == {} + requires f.reads(1) == 0 + requires f.reads(1,2) == {} { } @@ -56,7 +56,7 @@ method Bla() { assert Bool; } -method Pli(f : A -> B) requires f != null; +method Pli(f : A -> B) requires f != null { var o : object; assert f != o; @@ -102,7 +102,7 @@ module AritySituations { w := V; // error } - method P(r: T -> U, x: T) returns (u: U) + method P(r: T -> U, x: T) returns (u: U) requires r.requires(x); { u := r(x); diff --git a/Test/hofs/ResolveError.dfy.expect b/Test/hofs/ResolveError.dfy.expect index c3e0c242..11471ffd 100644 --- a/Test/hofs/ResolveError.dfy.expect +++ b/Test/hofs/ResolveError.dfy.expect @@ -2,8 +2,8 @@ ResolveError.dfy(86,6): Error: RHS (of type ((int,bool)) -> real) not assignable ResolveError.dfy(91,15): Error: incorrect type of method in-parameter 0 (expected ? -> ?, got (int,bool) -> real) ResolveError.dfy(101,6): Error: RHS (of type (()) -> real) not assignable to LHS (of type () -> real) ResolveError.dfy(102,6): Error: RHS (of type () -> real) not assignable to LHS (of type (()) -> real) -ResolveError.dfy(7,11): Error: the number of left-hand sides (1) and right-hand sides (2) must match for a multi-assignment -ResolveError.dfy(8,11): Error: the number of left-hand sides (1) and right-hand sides (2) must match for a multi-assignment +ResolveError.dfy(7,9): Error: the number of left-hand sides (1) and right-hand sides (2) must match for a multi-assignment +ResolveError.dfy(8,9): Error: the number of left-hand sides (1) and right-hand sides (2) must match for a multi-assignment ResolveError.dfy(21,6): Error: LHS of assignment must denote a mutable field ResolveError.dfy(31,16): Error: arguments must have the same type (got int and bool) ResolveError.dfy(32,12): Error: wrong number of arguments to function application (function type 'int -> int' expects 1, got 2) @@ -17,7 +17,7 @@ ResolveError.dfy(39,18): Error: wrong number of arguments to function applicatio ResolveError.dfy(46,15): Error: a reads-clause expression must denote an object or a collection of objects (instead got int) ResolveError.dfy(47,7): Error: Precondition must be boolean (got int) ResolveError.dfy(56,9): Error: condition is expected to be of type bool, but is () -> bool -ResolveError.dfy(59,34): Error: arguments must have the same type (got A -> B and ?) +ResolveError.dfy(59,39): Error: arguments must have the same type (got A -> B and ?) ResolveError.dfy(62,11): Error: arguments must have the same type (got A -> B and object) ResolveError.dfy(68,24): Error: unresolved identifier: _ 22 resolution/type errors detected in ResolveError.dfy diff --git a/Test/hofs/Simple.dfy b/Test/hofs/Simple.dfy index c27fa82c..6d98531e 100644 --- a/Test/hofs/Simple.dfy +++ b/Test/hofs/Simple.dfy @@ -50,7 +50,7 @@ method Main() { } function method succ(x : int) : int - requires x > 0; + requires x > 0 { x + 1 } @@ -74,24 +74,24 @@ method Main3() { } -function P(f: A -> B, x : A): B - reads (f.reads)(x); - requires (f.requires)(x); +function P(f: A -> B, x : A): B + reads (f.reads)(x) + requires (f.requires)(x) { f(x) } -function Q(f: U -> V, x : U): V - reads P.reads(f,x); - requires f.requires(x); // would be nice to be able to write P.requires(f,x) +function Q(f: U -> V, x : U): V + reads P.reads(f,x) + requires f.requires(x) // would be nice to be able to write P.requires(f,x) { P(f,x) } -function QQ(f: U -> V, x : U): V - reads ((() => ((()=>f)()).reads)())((()=>x)()); - requires ((() => ((()=>f)()).requires)())((()=>x)()); +function QQ(f: U -> V, x : U): V + reads ((() => ((()=>f)()).reads)())((()=>x)()) + requires ((() => ((()=>f)()).requires)())((()=>x)()) { ((() => P)())((()=>f)(),(()=>x)()) } diff --git a/Test/hofs/TreeMapSimple.dfy b/Test/hofs/TreeMapSimple.dfy index a853b82c..6b8f1377 100644 --- a/Test/hofs/TreeMapSimple.dfy +++ b/Test/hofs/TreeMapSimple.dfy @@ -6,7 +6,7 @@ datatype List = Nil | Cons(head: A,tail: List) datatype Tree = Branch(val: A,trees: List>) function ListData(xs : List) : set - ensures forall x :: x in ListData(xs) ==> x < xs; + ensures forall x :: x in ListData(xs) ==> x < xs { match xs case Nil => {} @@ -14,32 +14,32 @@ function ListData(xs : List) : set } function TreeData(t0 : Tree) : set - ensures forall t :: t in TreeData(t0) ==> t < t0; + ensures forall t :: t in TreeData(t0) ==> t < t0 { var Branch(x,ts) := t0; {x} + set t, y | t in ListData(ts) && y in TreeData(t) :: y } -function Pre(f : A -> B, s : set) : bool - reads (set x, y | x in s && y in f.reads(x) :: y); +function Pre(f : A -> B, s : set) : bool + reads (set x, y | x in s && y in f.reads(x) :: y) { forall x :: x in s ==> f.reads(x) == {} && f.requires(x) } -function method Map(xs : List, f : A -> B): List - reads Pre.reads(f, ListData(xs)); - requires Pre(f, ListData(xs)); - decreases xs; +function method Map(xs : List, f : A -> B): List + reads Pre.reads(f, ListData(xs)) + requires Pre(f, ListData(xs)) + decreases xs { match xs case Nil => Nil case Cons(x,xs) => Cons(f(x),Map(xs,f)) } -function method TMap(t0 : Tree, f : A -> B) : Tree - reads Pre.reads(f, TreeData(t0)); - requires Pre(f, TreeData(t0)); - decreases t0; +function method TMap(t0 : Tree, f : A -> B) : Tree + reads Pre.reads(f, TreeData(t0)) + requires Pre(f, TreeData(t0)) + decreases t0 { var Branch(x,ts) := t0; Branch(f(x),Map(ts, t requires t in ListData(ts) diff --git a/Test/hofs/Twice.dfy b/Test/hofs/Twice.dfy index add7e83c..5d948a58 100644 --- a/Test/hofs/Twice.dfy +++ b/Test/hofs/Twice.dfy @@ -1,7 +1,7 @@ // RUN: %dafny /print:"%t.print" "%s" > "%t" // RUN: %diff "%s.expect" "%t" -function method Twice(f : A -> A): A -> A +function method Twice(f : A -> A): A -> A { x requires f.requires(x) && f.requires(f(x)) reads f.reads(x) reads if f.requires(x) then f.reads(f(x)) else {} @@ -29,7 +29,7 @@ method WithReads() { } -function method Twice_bad(f : A -> A): A -> A +function method Twice_bad(f : A -> A): A -> A { x requires f.requires(x) && f.requires(f(x)) reads f.reads(x) + f.reads(f(x)) diff --git a/Test/hofs/VectorUpdate.dfy b/Test/hofs/VectorUpdate.dfy index 96edbe77..ca6b20b3 100644 --- a/Test/hofs/VectorUpdate.dfy +++ b/Test/hofs/VectorUpdate.dfy @@ -1,28 +1,59 @@ // RUN: %dafny /compile:3 "%s" > "%t" // RUN: %diff "%s.expect" "%t" -method VectorUpdate(N: int, a : array, f : (int,A) -> A) - requires a != null; - requires N == a.Length; - requires forall j :: 0 <= j < N ==> f.requires(j,a[j]); - requires forall j :: 0 <= j < N ==> a !in f.reads(j,a[j]); - modifies a; - ensures forall j :: 0 <= j < N ==> a[j] == f(j,old(a[j])); +// this is a rather verbose version of the VectorUpdate method +method VectorUpdate(N: int, a : array, f : (int,A) -> A) + requires a != null + requires N == a.Length + requires forall j :: 0 <= j < N ==> f.requires(j,a[j]) + requires forall j :: 0 <= j < N ==> a !in f.reads(j,a[j]) + modifies a + ensures forall j :: 0 <= j < N ==> a[j] == f(j,old(a[j])) { var i := 0; - while (i < N) - invariant 0 <= i <= N; - invariant forall j :: i <= j < N ==> f.requires(j,a[j]); - invariant forall j :: 0 <= j < N ==> f.requires(j,old(a[j])); - invariant forall j :: i <= j < N ==> a !in f.reads(j,a[j]); - invariant forall j :: i <= j < N ==> a[j] == old(a[j]); - invariant forall j :: 0 <= j < i ==> a[j] == f(j,old(a[j])); + while i < N + invariant 0 <= i <= N + invariant forall j :: i <= j < N ==> f.requires(j,a[j]) + invariant forall j :: 0 <= j < N ==> f.requires(j,old(a[j])) + invariant forall j :: i <= j < N ==> a !in f.reads(j,a[j]) + invariant forall j :: i <= j < N ==> a[j] == old(a[j]) + invariant forall j :: 0 <= j < i ==> a[j] == f(j,old(a[j])) { a[i] := f(i,a[i]); i := i + 1; } } +// here's a shorter version of the method above +method VectorUpdate'(a : array, f : (int,A) -> A) + requires a != null + requires forall j :: 0 <= j < a.Length ==> a !in f.reads(j,a[j]) && f.requires(j,a[j]) + modifies a + ensures forall j :: 0 <= j < a.Length ==> a[j] == f(j,old(a[j])) +{ + var i := 0; + while i < a.Length + invariant 0 <= i <= a.Length + invariant forall j :: i <= j < a.Length ==> a[j] == old(a[j]) + invariant forall j :: 0 <= j < i ==> a[j] == f(j,old(a[j])) + { + a[i] := f(i,a[i]); + i := i + 1; + } +} + +// here's yet another version +method VectorUpdate''(a : array, f : (int,A) -> A) + requires a != null + requires forall j :: 0 <= j < a.Length ==> a !in f.reads(j,a[j]) && f.requires(j,a[j]) + modifies a + ensures forall j :: 0 <= j < a.Length ==> a[j] == f(j,old(a[j])) +{ + forall i | 0 <= i < a.Length { + a[i] := f(i,a[i]); + } +} + method Main() { var v := new int[10]; @@ -46,11 +77,11 @@ method Main() } method PrintArray(a : array) - requires a != null; + requires a != null { var i := 0; - while (i < a.Length) { - if (i != 0) { + while i < a.Length { + if i != 0 { print ", "; } print a[i]; diff --git a/Test/hofs/VectorUpdate.dfy.expect b/Test/hofs/VectorUpdate.dfy.expect index b01ace00..18a7b110 100644 --- a/Test/hofs/VectorUpdate.dfy.expect +++ b/Test/hofs/VectorUpdate.dfy.expect @@ -1,5 +1,5 @@ -Dafny program verifier finished with 6 verified, 0 errors +Dafny program verifier finished with 10 verified, 0 errors Program compiled successfully Running... -- cgit v1.2.3 From 4d11b8d19bab3c4b37914f12226e19ac6d68ffb1 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 2 Jul 2015 20:00:11 -0700 Subject: Added command-line option /warnShadowing, which emits warnings if variables shadow other variables (Issue #86) --- Source/Dafny/DafnyOptions.cs | 7 +++ Source/Dafny/Resolver.cs | 109 ++++++++++++++++++++++++----------------- Test/dafny0/Shadows.dfy | 42 ++++++++++++++++ Test/dafny0/Shadows.dfy.expect | 12 +++++ 4 files changed, 124 insertions(+), 46 deletions(-) create mode 100644 Test/dafny0/Shadows.dfy create mode 100644 Test/dafny0/Shadows.dfy.expect (limited to 'Test') diff --git a/Source/Dafny/DafnyOptions.cs b/Source/Dafny/DafnyOptions.cs index af940439..a3b26f2a 100644 --- a/Source/Dafny/DafnyOptions.cs +++ b/Source/Dafny/DafnyOptions.cs @@ -58,6 +58,7 @@ namespace Microsoft.Dafny public bool Optimize = false; public bool PrintStats = false; public bool PrintFunctionCallGraph = false; + public bool WarnShadowing = false; protected override bool ParseOption(string name, Bpl.CommandLineOptionEngine.CommandLineParseState ps) { var args = ps.args; // convenient synonym @@ -183,6 +184,10 @@ namespace Microsoft.Dafny 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)) { @@ -294,6 +299,8 @@ namespace Microsoft.Dafny - 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 "); base.Usage(); // also print the Boogie options } diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 7c78c1e2..91570a1e 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -1374,7 +1374,7 @@ namespace Microsoft.Dafny Contract.Assert(dd.Constraint != null); // follows from NewtypeDecl invariant scope.PushMarker(); var added = scope.Push(dd.Var.Name, dd.Var); - Contract.Assert(added); + Contract.Assert(added == Scope.PushResult.Success); ResolveType(dd.Var.tok, dd.Var.Type, dd, ResolveTypeOptionEnum.DontInfer, null); ResolveExpression(dd.Constraint, new ResolveOpts(dd, false, true)); Contract.Assert(dd.Constraint.Type != null); // follows from postcondition of ResolveExpression @@ -3533,12 +3533,43 @@ namespace Microsoft.Dafny tp.Parent = parent; tp.PositionalIndex = index; } - if (!allTypeParameters.Push(tp.Name, tp) && emitErrors) { - Error(tp, "Duplicate type-parameter name: {0}", tp.Name); + var r = allTypeParameters.Push(tp.Name, tp); + if (emitErrors) { + if (r == Scope.PushResult.Duplicate) { + Error(tp, "Duplicate type-parameter name: {0}", tp.Name); + } else if (r == Scope.PushResult.Shadow) { + Warning(tp.tok, "Shadowed type-parameter name: {0}", tp.Name); + } } } } + void ScopePushAndReport(Scope scope, IVariable v, string kind) { + Contract.Requires(scope != null); + Contract.Requires(v != null); + Contract.Requires(kind != null); + ScopePushAndReport(scope, v.Name, v, v.Tok, kind); + } + + void ScopePushAndReport(Scope scope, string name, Thing thing, IToken tok, string kind) where Thing : class { + Contract.Requires(scope != null); + Contract.Requires(name != null); + Contract.Requires(thing != null); + Contract.Requires(tok != null); + Contract.Requires(kind != null); + var r = scope.Push(name, thing); + switch (r) { + case Scope.PushResult.Success: + break; + case Scope.PushResult.Duplicate: + Error(tok, "Duplicate {0} name: {1}", kind, name); + break; + case Scope.PushResult.Shadow: + Warning(tok, "Shadowed {0} name: {1}", kind, name); + break; + } + } + /// /// Assumes type parameters have already been pushed /// @@ -3550,9 +3581,7 @@ namespace Microsoft.Dafny } var option = f.TypeArgs.Count == 0 ? new ResolveTypeOption(f) : new ResolveTypeOption(ResolveTypeOptionEnum.AllowPrefix); foreach (Formal p in f.Formals) { - if (!scope.Push(p.Name, p)) { - Error(p, "Duplicate parameter name: {0}", p.Name); - } + ScopePushAndReport(scope, p, "parameter"); ResolveType(p.tok, p.Type, f, option, f.TypeArgs); } ResolveType(f.tok, f.ResultType, f, option, f.TypeArgs); @@ -3660,16 +3689,12 @@ namespace Microsoft.Dafny var option = m.TypeArgs.Count == 0 ? new ResolveTypeOption(m) : new ResolveTypeOption(ResolveTypeOptionEnum.AllowPrefix); // resolve in-parameters foreach (Formal p in m.Ins) { - if (!scope.Push(p.Name, p)) { - Error(p, "Duplicate parameter name: {0}", p.Name); - } + ScopePushAndReport(scope, p, "parameter"); ResolveType(p.tok, p.Type, m, option, m.TypeArgs); } // resolve out-parameters foreach (Formal p in m.Outs) { - if (!scope.Push(p.Name, p)) { - Error(p, "Duplicate parameter name: {0}", p.Name); - } + ScopePushAndReport(scope, p, "parameter"); ResolveType(p.tok, p.Type, m, option, m.TypeArgs); } scope.PopMarker(); @@ -4828,9 +4853,7 @@ namespace Microsoft.Dafny } // Add the locals to the scope foreach (var local in s.Locals) { - if (!scope.Push(local.Name, local)) { - Error(local.Tok, "Duplicate local-variable name: {0}", local.Name); - } + ScopePushAndReport(scope, local, "local-variable"); } // With the new locals in scope, it's now time to resolve the attributes on all the locals foreach (var local in s.Locals) { @@ -5121,9 +5144,7 @@ namespace Microsoft.Dafny int prevErrorCount = ErrorCount; scope.PushMarker(); foreach (BoundVar v in s.BoundVars) { - if (!scope.Push(v.Name, v)) { - Error(v, "Duplicate bound-variable name: {0}", v.Name); - } + ScopePushAndReport(scope, v, "local-variable"); ResolveType(v.tok, v.Type, codeContext, ResolveTypeOptionEnum.InferTypeProxies, null); } ResolveExpression(s.Range, new ResolveOpts(codeContext, true, specContextOnly)); @@ -5586,9 +5607,7 @@ namespace Microsoft.Dafny if (pat.Var != null) { BoundVar v = pat.Var; if (topLevel) { - if (!scope.Push(v.Name, v)) { - Error(v, "Duplicate parameter name: {0}", v.Name); - } + ScopePushAndReport(scope, v, "parameter"); } else { if (scope.Find(v.Name) != null) { Error(v, "Duplicate parameter name: {0}", v.Name); @@ -6202,8 +6221,8 @@ namespace Microsoft.Dafny } else if (prev != null) { Error(lnode.Tok, "label shadows an enclosing label"); } else { - bool b = labeledStatements.Push(lnode.Name, ss); - Contract.Assert(b); // since we just checked for duplicates, we expect the Push to succeed + var r = labeledStatements.Push(lnode.Name, ss); + Contract.Assert(r == Scope.PushResult.Success); // since we just checked for duplicates, we expect the Push to succeed if (l == ss.Labels) { // add it only once inSpecOnlyContext.Add(ss, specContextOnly); } @@ -7541,9 +7560,7 @@ namespace Microsoft.Dafny // Check for duplicate names now, because not until after resolving the case pattern do we know if identifiers inside it refer to bound variables or nullary constructors var c = 0; foreach (var v in lhs.Vars) { - if (!scope.Push(v.Name, v)) { - Error(v, "Duplicate let-variable name: {0}", v.Name); - } + ScopePushAndReport(scope, v, "let-variable"); c++; } if (c == 0) { @@ -7562,9 +7579,7 @@ namespace Microsoft.Dafny foreach (var lhs in e.LHSs) { Contract.Assert(lhs.Var != null); // the parser already checked that every LHS is a BoundVar, not a general pattern var v = lhs.Var; - if (!scope.Push(v.Name, v)) { - Error(v, "Duplicate let-variable name: {0}", v.Name); - } + ScopePushAndReport(scope, v, "let-variable"); ResolveType(v.tok, v.Type, opts.codeContext, ResolveTypeOptionEnum.InferTypeProxies, null); } foreach (var rhs in e.RHSs) { @@ -7596,9 +7611,7 @@ namespace Microsoft.Dafny ResolveTypeParameters(e.TypeArgs, true, e); scope.PushMarker(); foreach (BoundVar v in e.BoundVars) { - if (!scope.Push(v.Name, v)) { - Error(v, "Duplicate bound-variable name: {0}", v.Name); - } + ScopePushAndReport(scope, v, "bound-variable"); var option = typeQuantifier ? new ResolveTypeOption(e) : new ResolveTypeOption(ResolveTypeOptionEnum.InferTypeProxies); ResolveType(v.tok, v.Type, opts.codeContext, option, typeQuantifier ? e.TypeArgs : null); } @@ -7652,9 +7665,7 @@ namespace Microsoft.Dafny int prevErrorCount = ErrorCount; scope.PushMarker(); foreach (BoundVar v in e.BoundVars) { - if (!scope.Push(v.Name, v)) { - Error(v, "Duplicate bound-variable name: {0}", v.Name); - } + ScopePushAndReport(scope, v, "bound-variable"); ResolveType(v.tok, v.Type, opts.codeContext, ResolveTypeOptionEnum.InferTypeProxies, null); } ResolveExpression(e.Range, opts); @@ -7683,9 +7694,7 @@ namespace Microsoft.Dafny Error(e.tok, "a map comprehension must have exactly one bound variable."); } foreach (BoundVar v in e.BoundVars) { - if (!scope.Push(v.Name, v)) { - Error(v, "Duplicate bound-variable name: {0}", v.Name); - } + ScopePushAndReport(scope, v, "bound-variable"); ResolveType(v.tok, v.Type, opts.codeContext, ResolveTypeOptionEnum.InferTypeProxies, null); } ResolveExpression(e.Range, opts); @@ -7718,9 +7727,7 @@ namespace Microsoft.Dafny int prevErrorCount = ErrorCount; scope.PushMarker(); foreach (BoundVar v in e.BoundVars) { - if (!scope.Push(v.Name, v)) { - Error(v, "Duplicate bound-variable name: {0}", v.Name); - } + ScopePushAndReport(scope, v, "bound-variable"); ResolveType(v.tok, v.Type, opts.codeContext, ResolveTypeOptionEnum.InferTypeProxies, null); } @@ -10439,17 +10446,27 @@ namespace Microsoft.Dafny } } - // Pushes name-->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 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 'Test') 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 'Test') diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index 134fb4c1..d14d82a3 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -2223,6 +2223,7 @@ namespace Microsoft.Dafny { public interface ICallable : ICodeContext { IToken Tok { get; } + string WhatKind { get; } string NameRelativeToModule { get; } Specification Decreases { get; } /// @@ -2234,6 +2235,7 @@ namespace Microsoft.Dafny { } public class DontUseICallable : ICallable { + public string WhatKind { get { throw new cce.UnreachableException(); } } public bool IsGhost { get { throw new cce.UnreachableException(); } } public List TypeArgs { get { throw new cce.UnreachableException(); } } public List Ins { get { throw new cce.UnreachableException(); } } diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index cefce6b6..a4cf3700 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -3018,7 +3018,7 @@ namespace Microsoft.Dafny { AddFunctionOverrideReqsChk(f, builder, etran, substMap); //adding assert R <= Rank’; - AddFunctionOverrideTerminationChk(f, builder, etran, substMap); + AddOverrideTerminationChk(f, f.OverriddenFunction, builder, etran, substMap); //adding assert W <= Frame’ AddFunctionOverrideSubsetChk(f, builder, etran, localVariables, substMap); @@ -3181,35 +3181,6 @@ namespace Microsoft.Dafny { builder.Add(Assert(tok, q, "expression may read an object not in the parent trait context's reads clause", kv)); } - private void AddFunctionOverrideTerminationChk(Function f, StmtListBuilder builder, ExpressionTranslator etran, Dictionary substMap) - { - var decrToks = new List(); - var decrTypes1 = new List(); - var decrTypes2 = new List(); - var decrClass = new List(); - var decrTrait = new List(); - if (f.Decreases != null) - { - foreach (var decC in f.Decreases.Expressions) - { - decrToks.Add(decC.tok); - decrTypes1.Add(decC.Type); - decrClass.Add(etran.TrExpr(decC)); - } - } - if (f.OverriddenFunction.Decreases != null) - { - foreach (var decT in f.OverriddenFunction.Decreases.Expressions) - { - var decCNew = Substitute(decT, null, substMap); - decrTypes2.Add(decCNew.Type); - decrTrait.Add(etran.TrExpr(decCNew)); - } - } - var decrChk = DecreasesCheck(decrToks, decrTypes1, decrTypes2, decrClass, decrTrait, null, null, true, false); - builder.Add(new Bpl.AssertCmd(f.tok, decrChk)); - } - private void AddFunctionOverrideReqsChk(Function f, StmtListBuilder builder, ExpressionTranslator etran, Dictionary substMap) { //generating trait pre-conditions with class variables @@ -3277,7 +3248,7 @@ namespace Microsoft.Dafny { AddMethodOverrideReqsChk(m, builder, etran, substMap); //adding assert R <= Rank’; - AddMethodOverrideTerminationChk(m, builder, etran, substMap); + AddOverrideTerminationChk(m, m.OverriddenMethod, builder, etran, substMap); //adding assert W <= Frame’ AddMethodOverrideSubsetChk(m, builder, etran, localVariables, substMap); @@ -3364,14 +3335,15 @@ namespace Microsoft.Dafny { } } - private void AddMethodOverrideTerminationChk(Method m, Bpl.StmtListBuilder builder, ExpressionTranslator etran, Dictionary substMap) { - Contract.Requires(m != null); + private void AddOverrideTerminationChk(ICallable original, ICallable overryd, Bpl.StmtListBuilder builder, ExpressionTranslator etran, Dictionary substMap) { + Contract.Requires(original != null); + Contract.Requires(overryd != null); Contract.Requires(builder != null); Contract.Requires(etran != null); Contract.Requires(substMap != null); // Note, it is as if the trait's method is calling the class's method. - var contextDecreases = m.OverriddenMethod.Decreases.Expressions; - var calleeDecreases = m.Decreases.Expressions; + var contextDecreases = overryd.Decreases.Expressions; + var calleeDecreases = original.Decreases.Expressions; // We want to check: calleeDecreases <= contextDecreases (note, we can allow equality, since there is a bounded, namely 1, number of dynamic dispatches) if (Contract.Exists(contextDecreases, e => e is WildcardExpr)) { // no check needed @@ -3392,7 +3364,7 @@ namespace Microsoft.Dafny { N = i; break; } - toks.Add(new NestedToken(m.tok, e1.tok)); + toks.Add(new NestedToken(original.Tok, e1.tok)); types0.Add(e0.Type.NormalizeExpand()); types1.Add(e1.Type.NormalizeExpand()); callee.Add(etran.TrExpr(e0)); @@ -3425,7 +3397,7 @@ namespace Microsoft.Dafny { // as "false". bool allowNoChange = N == decrCountT && decrCountT <= decrCountC; var decrChk = DecreasesCheck(toks, types0, types1, callee, caller, null, null, allowNoChange, false); - builder.Add(Assert(m.tok, decrChk, "method's decreases clause must be below or equal to that in the trait")); + builder.Add(Assert(original.Tok, decrChk, string.Format("{0}'s decreases clause must be below or equal to that in the trait", original.WhatKind))); } private void AddMethodOverrideSubsetChk(Method m, Bpl.StmtListBuilder builder, ExpressionTranslator etran, List localVariables, Dictionary substMap) diff --git a/Test/dafny0/Trait/TraitsDecreases.dfy b/Test/dafny0/Trait/TraitsDecreases.dfy index 53ce28be..8ab3672a 100644 --- a/Test/dafny0/Trait/TraitsDecreases.dfy +++ b/Test/dafny0/Trait/TraitsDecreases.dfy @@ -106,3 +106,49 @@ class CC extends TT { decreases * { } } + + +// The following module contains various regression tests +module More { + trait A0 { + predicate P() decreases 5 + } + class B0 extends A0 { + predicate P() // error: rank is not lower + } + + trait A1 { + predicate P() decreases 5 + } + class B1 extends A1 { + predicate P() reads this // error: rank is not lower + } + + trait A2 { + predicate P(x: int) + } + class B2 extends A2 { + predicate P(x: int) reads this // error: rank is not lower + } + + trait A3 { + predicate P() reads this + } + class B3 extends A3 { + predicate P() // error: rank is not lower + } + + trait A4 { + predicate P(x: int) decreases 5 + } + class B4 extends A4 { + predicate P(x: int) // error: rank is not lower + } + + trait A5 { + method M(x: int) decreases 5 + } + class B5 extends A5 { + method M(x: int) // error: rank is not lower + } +} diff --git a/Test/dafny0/Trait/TraitsDecreases.dfy.expect b/Test/dafny0/Trait/TraitsDecreases.dfy.expect index 6c76f9a8..2607a0c6 100644 --- a/Test/dafny0/Trait/TraitsDecreases.dfy.expect +++ b/Test/dafny0/Trait/TraitsDecreases.dfy.expect @@ -1,3 +1,21 @@ +TraitsDecreases.dfy(117,15): Error: predicate's decreases clause must be below or equal to that in the trait +Execution trace: + (0,0): anon0 +TraitsDecreases.dfy(124,15): Error: predicate's decreases clause must be below or equal to that in the trait +Execution trace: + (0,0): anon0 +TraitsDecreases.dfy(131,15): Error: predicate's decreases clause must be below or equal to that in the trait +Execution trace: + (0,0): anon0 +TraitsDecreases.dfy(138,15): Error: predicate's decreases clause must be below or equal to that in the trait +Execution trace: + (0,0): anon0 +TraitsDecreases.dfy(145,15): Error: predicate's decreases clause must be below or equal to that in the trait +Execution trace: + (0,0): anon0 +TraitsDecreases.dfy(152,12): Error: method's decreases clause must be below or equal to that in the trait +Execution trace: + (0,0): anon0 TraitsDecreases.dfy(57,10): Error: method's decreases clause must be below or equal to that in the trait Execution trace: (0,0): anon0 @@ -14,4 +32,4 @@ TraitsDecreases.dfy(88,10): Error: method's decreases clause must be below or eq Execution trace: (0,0): anon0 -Dafny program verifier finished with 63 verified, 5 errors +Dafny program verifier finished with 75 verified, 11 errors -- cgit v1.2.3 From fad74b96e5d9367960358b1c4cc9c2cce79e961a Mon Sep 17 00:00:00 2001 From: Michael Lowell Roberts Date: Wed, 8 Jul 2015 11:01:11 -0700 Subject: added unit tests for exclusive refinement. --- Test/irondafny0/FIFO.dfy | 43 ++++++++++++++++++ Test/irondafny0/FIFO.dfy.expect | 8 ++++ Test/irondafny0/LIFO.dfy | 43 ++++++++++++++++++ Test/irondafny0/LIFO.dfy.expect | 8 ++++ Test/irondafny0/Queue.dfyi | 22 ++++++++++ Test/irondafny0/inheritreqs0.dfy | 22 ++++++++++ Test/irondafny0/inheritreqs0.dfy.expect | 6 +++ Test/irondafny0/inheritreqs1.dfy | 22 ++++++++++ Test/irondafny0/inheritreqs1.dfy.expect | 6 +++ Test/irondafny0/xrefine0.dfy | 6 +++ Test/irondafny0/xrefine0.dfy.expect | 2 + Test/irondafny0/xrefine1.dfy | 77 +++++++++++++++++++++++++++++++++ Test/irondafny0/xrefine1.dfy.expect | 6 +++ Test/irondafny0/xrefine2.dfy | 77 +++++++++++++++++++++++++++++++++ Test/irondafny0/xrefine2.dfy.expect | 9 ++++ Test/irondafny0/xrefine3.dfy | 72 ++++++++++++++++++++++++++++++ Test/irondafny0/xrefine3.dfy.expect | 6 +++ 17 files changed, 435 insertions(+) create mode 100644 Test/irondafny0/FIFO.dfy create mode 100644 Test/irondafny0/FIFO.dfy.expect create mode 100644 Test/irondafny0/LIFO.dfy create mode 100644 Test/irondafny0/LIFO.dfy.expect create mode 100644 Test/irondafny0/Queue.dfyi create mode 100644 Test/irondafny0/inheritreqs0.dfy create mode 100644 Test/irondafny0/inheritreqs0.dfy.expect create mode 100644 Test/irondafny0/inheritreqs1.dfy create mode 100644 Test/irondafny0/inheritreqs1.dfy.expect create mode 100644 Test/irondafny0/xrefine0.dfy create mode 100644 Test/irondafny0/xrefine0.dfy.expect create mode 100644 Test/irondafny0/xrefine1.dfy create mode 100644 Test/irondafny0/xrefine1.dfy.expect create mode 100644 Test/irondafny0/xrefine2.dfy create mode 100644 Test/irondafny0/xrefine2.dfy.expect create mode 100644 Test/irondafny0/xrefine3.dfy create mode 100644 Test/irondafny0/xrefine3.dfy.expect (limited to 'Test') diff --git a/Test/irondafny0/FIFO.dfy b/Test/irondafny0/FIFO.dfy new file mode 100644 index 00000000..1256b55d --- /dev/null +++ b/Test/irondafny0/FIFO.dfy @@ -0,0 +1,43 @@ +// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +include "Queue.dfyi" + +module FIFO exclusively refines Queue { + type Item = int + + method Init() returns (q: Queue) { + q := []; + } + + method Push(item: Item, q: Queue) returns (q': Queue) { + return q + [item]; + } + + method Pop(q: Queue) returns (item: Item, q': Queue) + ensures item == q[0] + { + item := q[0]; + q' := q[1..]; + } +} + +module MainImpl refines MainSpec { + import Q = FIFO + + method Main() + { + var q := Q.Init(); + q := Q.Push(0, q); + q := Q.Push(1, q); + q := Q.Push(2, q); + + var n: int; + n, q := Q.Pop(q); + print n, "\n"; + n, q := Q.Pop(q); + print n, "\n"; + n, q := Q.Pop(q); + print n, "\n"; + } +} diff --git a/Test/irondafny0/FIFO.dfy.expect b/Test/irondafny0/FIFO.dfy.expect new file mode 100644 index 00000000..25021947 --- /dev/null +++ b/Test/irondafny0/FIFO.dfy.expect @@ -0,0 +1,8 @@ + +Dafny program verifier finished with 8 verified, 0 errors +Program compiled successfully +Running... + +0 +1 +2 diff --git a/Test/irondafny0/LIFO.dfy b/Test/irondafny0/LIFO.dfy new file mode 100644 index 00000000..bac08fba --- /dev/null +++ b/Test/irondafny0/LIFO.dfy @@ -0,0 +1,43 @@ +// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +include "Queue.dfyi" + +module LIFO exclusively refines Queue { + type Item = int + + method Init() returns (q: Queue) { + q := []; + } + + method Push(item: Item, q: Queue) returns (q': Queue) { + return [item] + q; + } + + method Pop(q: Queue) returns (item: Item, q': Queue) + ensures item == q[0] + { + item := q[0]; + q' := q[1..]; + } +} + +module MainImpl refines MainSpec { + import Q = LIFO + + method Main() + { + var q := Q.Init(); + q := Q.Push(0, q); + q := Q.Push(1, q); + q := Q.Push(2, q); + + var n: int; + n, q := Q.Pop(q); + print n, "\n"; + n, q := Q.Pop(q); + print n, "\n"; + n, q := Q.Pop(q); + print n, "\n"; + } +} diff --git a/Test/irondafny0/LIFO.dfy.expect b/Test/irondafny0/LIFO.dfy.expect new file mode 100644 index 00000000..83f90a5b --- /dev/null +++ b/Test/irondafny0/LIFO.dfy.expect @@ -0,0 +1,8 @@ + +Dafny program verifier finished with 8 verified, 0 errors +Program compiled successfully +Running... + +2 +1 +0 diff --git a/Test/irondafny0/Queue.dfyi b/Test/irondafny0/Queue.dfyi new file mode 100644 index 00000000..9f7eb534 --- /dev/null +++ b/Test/irondafny0/Queue.dfyi @@ -0,0 +1,22 @@ +// Queue.dfyi + +abstract module Queue { + type Item + type Queue = seq + + method Init() returns (q: Queue) + ensures |q| == 0; + + method Push(item: Item, q: Queue) returns (q': Queue) + ensures |q'| == |q| + 1; + + method Pop(q: Queue) returns (item: Item, q': Queue) + requires |q| > 0; + ensures item in q; + ensures |q'| == |q| - 1; +} + +abstract module MainSpec { + import Q as Queue +} + diff --git a/Test/irondafny0/inheritreqs0.dfy b/Test/irondafny0/inheritreqs0.dfy new file mode 100644 index 00000000..a0117da0 --- /dev/null +++ b/Test/irondafny0/inheritreqs0.dfy @@ -0,0 +1,22 @@ +// RUN: %dafny /compile:3 /optimize /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +abstract module Spec { + method Greet(b: bool) + requires b; +} + +module Impl refines Spec { + method Greet(b: bool) { + print "o hai!\n"; + } + + method Xyzzy(b: bool) + requires b; + {} + + method Main() { + Greet(false); + Xyzzy(false); + } +} diff --git a/Test/irondafny0/inheritreqs0.dfy.expect b/Test/irondafny0/inheritreqs0.dfy.expect new file mode 100644 index 00000000..eaadc85a --- /dev/null +++ b/Test/irondafny0/inheritreqs0.dfy.expect @@ -0,0 +1,6 @@ +inheritreqs0.dfy(19,14): Error BP5002: A precondition for this call might not hold. +inheritreqs0.dfy[Impl](6,18): Related location: This is the precondition that might not hold. +Execution trace: + (0,0): anon0 + +Dafny program verifier finished with 6 verified, 1 error diff --git a/Test/irondafny0/inheritreqs1.dfy b/Test/irondafny0/inheritreqs1.dfy new file mode 100644 index 00000000..c83d04ac --- /dev/null +++ b/Test/irondafny0/inheritreqs1.dfy @@ -0,0 +1,22 @@ +// RUN: %dafny /compile:3 /optimize /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +abstract module Spec { + method Greet(b: bool) + requires b; +} + +module Impl refines Spec { + method Greet(b: bool) { + print "o hai!\n"; + } + + method Xyzzy(b: bool) + requires b; + {} + + method Main() { + Greet(true); + Xyzzy(false); + } +} diff --git a/Test/irondafny0/inheritreqs1.dfy.expect b/Test/irondafny0/inheritreqs1.dfy.expect new file mode 100644 index 00000000..27c76fee --- /dev/null +++ b/Test/irondafny0/inheritreqs1.dfy.expect @@ -0,0 +1,6 @@ +inheritreqs1.dfy(20,14): Error BP5002: A precondition for this call might not hold. +inheritreqs1.dfy(15,18): Related location: This is the precondition that might not hold. +Execution trace: + (0,0): anon0 + +Dafny program verifier finished with 6 verified, 1 error diff --git a/Test/irondafny0/xrefine0.dfy b/Test/irondafny0/xrefine0.dfy new file mode 100644 index 00000000..35993ce8 --- /dev/null +++ b/Test/irondafny0/xrefine0.dfy @@ -0,0 +1,6 @@ +// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +abstract module Delicious {} +module Chocolate exclusively refines Delicious {} +module Strawberry exclusively refines Delicious {} diff --git a/Test/irondafny0/xrefine0.dfy.expect b/Test/irondafny0/xrefine0.dfy.expect new file mode 100644 index 00000000..136e06db --- /dev/null +++ b/Test/irondafny0/xrefine0.dfy.expect @@ -0,0 +1,2 @@ +xrefine0.dfy(6,7): Error: no more than one exclusive refinement may exist for a given module. +1 resolution/type errors detected in xrefine0.dfy diff --git a/Test/irondafny0/xrefine1.dfy b/Test/irondafny0/xrefine1.dfy new file mode 100644 index 00000000..10d25f53 --- /dev/null +++ b/Test/irondafny0/xrefine1.dfy @@ -0,0 +1,77 @@ +// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +abstract module ProtocolSpec { + type ProtoT + + predicate Init(p:ProtoT) +} + +abstract module HostSpec { + type HostT + import P as ProtocolSpec + + function method foo(h:HostT) : P.ProtoT +} + +module ProtocolImpl exclusively refines ProtocolSpec { + type ProtoT = bool + + predicate Init(p:ProtoT) { !p } + + method orange(i:nat) returns (j:nat) + { + j := i + 1; + } +} + +module HostImpl exclusively refines HostSpec { + import P = ProtocolImpl + + type HostT = int + + function method foo(h:HostT) : P.ProtoT + { + h > 0 + } + + method apple(i:nat) returns (j:nat) + { + j := i + 1; + } +} + +abstract module MainSpec { + import HI as HostSpec + import PI as ProtocolSpec + + method Test(h1:HI.HostT, h2:HI.HostT) + requires HI.foo(h1) == HI.foo(h2); + requires PI.Init(HI.foo(h1)) +} + +module MainImpl exclusively refines MainSpec { + import HI = HostImpl + import PI = ProtocolImpl + + method Test(h1:HI.HostT, h2:HI.HostT) + { + var a := HI.foo(h1); + print "HI.foo(h1) => ", a, "\n"; + var b := HI.foo(h2); + print "HI.foo(h2) => ", b, "\n"; + var i := PI.orange(1); + print "PI.orange(1) => ", i, "\n"; + var j := HI.apple(2); + print "PI.apple(2) => ", j, "\n"; + } + + method Main() + { + Test(-1, 1); + } +} + + + + diff --git a/Test/irondafny0/xrefine1.dfy.expect b/Test/irondafny0/xrefine1.dfy.expect new file mode 100644 index 00000000..ae844fc8 --- /dev/null +++ b/Test/irondafny0/xrefine1.dfy.expect @@ -0,0 +1,6 @@ +xrefine1.dfy(71,13): Error BP5002: A precondition for this call might not hold. +xrefine1.dfy[MainImpl](49,29): Related location: This is the precondition that might not hold. +Execution trace: + (0,0): anon0 + +Dafny program verifier finished with 12 verified, 1 error diff --git a/Test/irondafny0/xrefine2.dfy b/Test/irondafny0/xrefine2.dfy new file mode 100644 index 00000000..7578b424 --- /dev/null +++ b/Test/irondafny0/xrefine2.dfy @@ -0,0 +1,77 @@ +// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +abstract module ProtocolSpec { + type ProtoT + + predicate Init(p:ProtoT) +} + +abstract module HostSpec { + type HostT + import P as ProtocolSpec + + function method foo(h:HostT) : P.ProtoT +} + +module ProtocolImpl exclusively refines ProtocolSpec { + type ProtoT = bool + + predicate Init(p:ProtoT) { p } + + method orange(i:nat) returns (j:nat) + { + j := i + 1; + } +} + +module HostImpl exclusively refines HostSpec { + import P = ProtocolImpl + + type HostT = int + + function method foo(h:HostT) : P.ProtoT + { + h != 0 + } + + method apple(i:nat) returns (j:nat) + { + j := i + 1; + } +} + +abstract module MainSpec { + import HI as HostSpec + import PI as ProtocolSpec + + method Test(h1:HI.HostT, h2:HI.HostT) + requires HI.foo(h1) == HI.foo(h2); + requires PI.Init(HI.foo(h1)) +} + +module MainImpl exclusively refines MainSpec { + import HI = HostImpl + import PI = ProtocolImpl + + method Test(h1:HI.HostT, h2:HI.HostT) + { + var a := HI.foo(h1); + print "HI.foo(h1) => ", a, "\n"; + var b := HI.foo(h2); + print "HI.foo(h2) => ", b, "\n"; + var i := PI.orange(1); + print "PI.orange(1) => ", i, "\n"; + var j := HI.apple(2); + print "PI.apple(2) => ", j, "\n"; + } + + method Main() + { + Test(-1, 1); + } +} + + + + diff --git a/Test/irondafny0/xrefine2.dfy.expect b/Test/irondafny0/xrefine2.dfy.expect new file mode 100644 index 00000000..6d3fecd4 --- /dev/null +++ b/Test/irondafny0/xrefine2.dfy.expect @@ -0,0 +1,9 @@ + +Dafny program verifier finished with 13 verified, 0 errors +Program compiled successfully +Running... + +HI.foo(h1) => True +HI.foo(h2) => True +PI.orange(1) => 2 +PI.apple(2) => 3 diff --git a/Test/irondafny0/xrefine3.dfy b/Test/irondafny0/xrefine3.dfy new file mode 100644 index 00000000..69c5bc27 --- /dev/null +++ b/Test/irondafny0/xrefine3.dfy @@ -0,0 +1,72 @@ +// RUN: %dafny /compile:3 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +abstract module AlphaSpec { + type Alpha + + predicate IsValid(a:Alpha) + + method Init() returns (a:Alpha) + ensures IsValid(a); +} + +abstract module BetaSpec { + type Beta + import A as AlphaSpec + + predicate IsValid(b:Beta) + + method Init(ays:seq) returns (b:Beta) + requires forall i :: 0 <= i < |ays| ==> A.IsValid(ays[i]); + ensures IsValid(b); +} + +module AlphaImpl exclusively refines AlphaSpec { + type Alpha = bool + + predicate IsValid(a:Alpha) { + a + } + + method Init() returns (a:Alpha) + ensures IsValid(a); + { + a := true; + } +} + +module BetaImpl exclusively refines BetaSpec { + import A = AlphaImpl + type Beta = seq + + predicate IsValid(b:Beta) { + forall i :: 0 <= i < |b| ==> A.IsValid(b[i]) + } + + method Init(ays:seq) returns (b:Beta) { + b := ays; + } +} + +abstract module MainSpec { + import A as AlphaSpec + import B as BetaSpec + + method Main() + { + var a := A.Init(); + var ays := [a, a]; + assert forall i :: 0 <= i < |ays| ==> A.IsValid(ays[i]); + var b := B.Init(ays); + print "o hai!\n"; + } +} + +module MainImpl exclusively refines MainSpec { + import B = BetaImpl + import A = AlphaImpl +} + + + + diff --git a/Test/irondafny0/xrefine3.dfy.expect b/Test/irondafny0/xrefine3.dfy.expect new file mode 100644 index 00000000..1e5a5b4e --- /dev/null +++ b/Test/irondafny0/xrefine3.dfy.expect @@ -0,0 +1,6 @@ + +Dafny program verifier finished with 14 verified, 0 errors +Program compiled successfully +Running... + +o hai! -- cgit v1.2.3 From fe501d243c0413db8ae85bda174d0761da00d330 Mon Sep 17 00:00:00 2001 From: Michael Lowell Roberts Date: Mon, 13 Jul 2015 10:40:35 -0700 Subject: [IronDafny] implemented workaround for "import opened" bug(s). --- Source/Dafny/Cloner.cs | 8 ++-- Source/Dafny/Dafny.atg | 16 ++++---- Source/Dafny/DafnyAst.cs | 52 +++++++++++++++++++----- Source/Dafny/DafnyOptions.cs | 27 +++++++++++- Source/Dafny/Parser.cs | 16 ++++---- Source/Dafny/Resolver.cs | 61 ++++++++++++++++++++-------- Test/irondafny0/FIFO.dfy | 2 +- Test/irondafny0/LIFO.dfy | 2 +- Test/irondafny0/opened_workaround.dfy | 21 ++++++++++ Test/irondafny0/opened_workaround.dfy.expect | 3 ++ Test/irondafny0/xrefine0.dfy | 2 +- Test/irondafny0/xrefine1.dfy | 2 +- Test/irondafny0/xrefine2.dfy | 2 +- Test/irondafny0/xrefine3.dfy | 2 +- 14 files changed, 161 insertions(+), 55 deletions(-) create mode 100644 Test/irondafny0/opened_workaround.dfy create mode 100644 Test/irondafny0/opened_workaround.dfy.expect (limited to 'Test') 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 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 'Test') 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