From dcaef29ffbe2570632e7368d27377fb9ac282d7b Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 22 Apr 2011 22:57:58 -0700 Subject: Dafny: include source location for array types supplied in input --- Source/Dafny/Dafny.atg | 4 ++-- Source/Dafny/DafnyAst.cs | 9 +++++---- Source/Dafny/Parser.cs | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Source/Dafny/Dafny.atg b/Source/Dafny/Dafny.atg index 23582495..c630d67e 100644 --- a/Source/Dafny/Dafny.atg +++ b/Source/Dafny/Dafny.atg @@ -555,7 +555,7 @@ ReferenceType if (tok.val.Length != 5) { dims = int.Parse(tok.val.Substring(5)); } - ty = theBuiltIns.ArrayType(dims, gt[0], true); + ty = theBuiltIns.ArrayType(tok, dims, gt[0], true); .) | Ident (. gt = new List(); .) [ GenericInstantiation ] (. ty = new UserDefinedType(tok, tok.val, gt); .) @@ -808,7 +808,7 @@ AssignRhs<.out List ee, out Type ty, out CallStmt initCall, Expressi [ "[" (. ee = new List(); .) Expressions "]" (. // make sure an array class with this dimensionality exists - UserDefinedType tmp = theBuiltIns.ArrayType(ee.Count, new IntType(), true); + UserDefinedType tmp = theBuiltIns.ArrayType(x, ee.Count, new IntType(), true); .) | "." Ident "(" (. args = new List(); .) diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index d923e5dc..f76f24de 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -40,20 +40,21 @@ namespace Microsoft.Dafny { ClassDecl obj = new ClassDecl(Token.NoToken, "object", SystemModule, new List(), new List(), null); SystemModule.TopLevelDecls.Add(obj); // add one-dimensional arrays, since they may arise during type checking - UserDefinedType tmp = ArrayType(1, Type.Int, true); + UserDefinedType tmp = ArrayType(Token.NoToken, 1, Type.Int, true); } public UserDefinedType ArrayType(int dims, Type arg) { - return ArrayType(dims, arg, false); + return ArrayType(Token.NoToken, dims, arg, false); } - public UserDefinedType ArrayType(int dims, Type arg, bool allowCreationOfNewClass) { + public UserDefinedType ArrayType(IToken tok, int dims, Type arg, bool allowCreationOfNewClass) { + Contract.Requires(tok != null); Contract.Requires(1 <= dims); Contract.Requires(arg != null); Contract.Ensures(Contract.Result() != null); List typeArgs = new List(); typeArgs.Add(arg); - UserDefinedType udt = new UserDefinedType(Token.NoToken, ArrayClassName(dims), typeArgs); + UserDefinedType udt = new UserDefinedType(tok, ArrayClassName(dims), typeArgs); if (allowCreationOfNewClass && !arrayTypeDecls.ContainsKey(dims)) { ArrayClassDecl arrayClass = new ArrayClassDecl(dims, SystemModule); for (int d = 0; d < dims; d++) { diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs index 180f15ab..e2a45ceb 100644 --- a/Source/Dafny/Parser.cs +++ b/Source/Dafny/Parser.cs @@ -877,7 +877,7 @@ List/*!*/ decreases) { if (tok.val.Length != 5) { dims = int.Parse(tok.val.Substring(5)); } - ty = theBuiltIns.ArrayType(dims, gt[0], true); + ty = theBuiltIns.ArrayType(tok, dims, gt[0], true); } else if (la.kind == 1) { Ident(out tok); @@ -1419,7 +1419,7 @@ List/*!*/ decreases) { ee = new List(); Expressions(ee); Expect(53); - UserDefinedType tmp = theBuiltIns.ArrayType(ee.Count, new IntType(), true); + UserDefinedType tmp = theBuiltIns.ArrayType(x, ee.Count, new IntType(), true); } else { Get(); -- cgit v1.2.3 From 27241e69516368f116baea922938d1cb10570d85 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 11 May 2011 17:25:34 -0700 Subject: Dafny: * added missing error checking for ghost-vs-physical contexts (e.g., use of the "old" keyword) * check that arrays are not null when accessed * added dafny1/FindZero.dfy test case --- Source/Dafny/Resolver.cs | 13 ++++++++++++- Source/Dafny/Translator.cs | 3 +++ Test/dafny0/Answer | 9 ++++++--- Test/dafny0/Modules0.dfy | 15 +++++++++++++++ Test/dafny0/NatTypes.dfy | 2 +- Test/dafny1/Answer | 4 ++++ Test/dafny1/FindZero.dfy | 30 ++++++++++++++++++++++++++++++ Test/dafny1/runtest.bat | 2 +- 8 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 Test/dafny1/FindZero.dfy diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 22a58a39..ba28d531 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -837,7 +837,11 @@ namespace Microsoft.Dafny { #if !NO_CHEAP_OBJECT_WORKAROUND if (a is ObjectType || b is ObjectType) { // TODO: remove this temporary hack - // allow anything with object; this is BOGUS + var other = a is ObjectType ? b : a; + if (other is BoolType || other is IntType || other is SetType || other is SeqType || other.IsDatatype) { + return false; + } + // allow anything else with object; this is BOGUS return true; } #endif @@ -2019,6 +2023,8 @@ namespace Microsoft.Dafny { OldExpr e = (OldExpr)expr; if (!twoState) { Error(expr, "old expressions are not allowed in this context"); + } else if (!specContext) { + Error(expr, "old expressions are allowed only in specification and ghost contexts"); } ResolveExpression(e.E, twoState, specContext); expr.Type = e.E.Type; @@ -2027,6 +2033,8 @@ namespace Microsoft.Dafny { FreshExpr e = (FreshExpr)expr; if (!twoState) { Error(expr, "fresh expressions are not allowed in this context"); + } else if (!specContext) { + Error(expr, "fresh expressions are allowed only in specification and ghost contexts"); } ResolveExpression(e.E, twoState, specContext); // the type of e.E must be either an object or a collection of objects @@ -2047,6 +2055,9 @@ namespace Microsoft.Dafny { } else if (expr is AllocatedExpr) { AllocatedExpr e = (AllocatedExpr)expr; ResolveExpression(e.E, twoState, specContext); + if (!specContext) { + Error(expr, "allocated expressions are allowed only in specification and ghost contexts"); + } // e.E can be of any type expr.Type = Type.Bool; diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 2bb5a51e..557fcaf0 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -1785,6 +1785,9 @@ namespace Microsoft.Dafny { bool isSequence = e.Seq.Type is SeqType; CheckWellformed(e.Seq, options, locals, builder, etran); Bpl.Expr seq = etran.TrExpr(e.Seq); + if (e.Seq.Type.IsArrayType) { + builder.Add(Assert(e.Seq.tok, Bpl.Expr.Neq(seq, predef.Null), "array may be null")); + } Bpl.Expr e0 = null; if (e.E0 != null) { e0 = etran.TrExpr(e.E0); diff --git a/Test/dafny0/Answer b/Test/dafny0/Answer index 1ac8fc1c..afa88a47 100644 --- a/Test/dafny0/Answer +++ b/Test/dafny0/Answer @@ -483,9 +483,12 @@ Modules0.dfy(62,6): Error: inter-module calls must follow the module import rela Modules0.dfy(72,6): Error: inter-module calls must follow the module import relation (so module X0 must transitively import X1) Modules0.dfy(91,4): Error: inter-module calls must follow the module import relation (so module _default must transitively import YY) Modules0.dfy(116,16): Error: ghost variables are allowed only in specification contexts -Modules0.dfy(133,10): Error: match source expression 'tree' has already been used as a match source expression in this context -Modules0.dfy(172,12): Error: match source expression 'l' has already been used as a match source expression in this context -10 resolution/type errors detected in Modules0.dfy +Modules0.dfy(130,11): Error: old expressions are allowed only in specification and ghost contexts +Modules0.dfy(131,11): Error: fresh expressions are allowed only in specification and ghost contexts +Modules0.dfy(132,11): Error: allocated expressions are allowed only in specification and ghost contexts +Modules0.dfy(148,10): Error: match source expression 'tree' has already been used as a match source expression in this context +Modules0.dfy(187,12): Error: match source expression 'l' has already been used as a match source expression in this context +13 resolution/type errors detected in Modules0.dfy -------------------- Modules1.dfy -------------------- Modules1.dfy(55,3): Error: decreases expression must be bounded below by 0 diff --git a/Test/dafny0/Modules0.dfy b/Test/dafny0/Modules0.dfy index adce71a8..59052ac2 100644 --- a/Test/dafny0/Modules0.dfy +++ b/Test/dafny0/Modules0.dfy @@ -121,6 +121,21 @@ class Ghosty { ghost method Theorem(a: int) { } } +var SomeField: int; + +method SpecialFunctions() + modifies this; +{ + 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 + 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 + } +} + // ---------------------- illegal match expressions --------------- datatype Tree { Nil; Cons(int, Tree, Tree); } diff --git a/Test/dafny0/NatTypes.dfy b/Test/dafny0/NatTypes.dfy index 93bd4b65..6b7ce9b9 100644 --- a/Test/dafny0/NatTypes.dfy +++ b/Test/dafny0/NatTypes.dfy @@ -60,7 +60,7 @@ datatype List { Cons(nat, T, List); } -method MatchIt(list: List) returns (k: nat) +method MatchIt(list: List) returns (k: nat) { match (list) { case Nil => diff --git a/Test/dafny1/Answer b/Test/dafny1/Answer index d96e12a5..d0eba7cb 100644 --- a/Test/dafny1/Answer +++ b/Test/dafny1/Answer @@ -59,6 +59,10 @@ Dafny program verifier finished with 2 verified, 0 errors Dafny program verifier finished with 17 verified, 0 errors +-------------------- FindZero.dfy -------------------- + +Dafny program verifier finished with 4 verified, 0 errors + -------------------- TerminationDemos.dfy -------------------- Dafny program verifier finished with 11 verified, 0 errors diff --git a/Test/dafny1/FindZero.dfy b/Test/dafny1/FindZero.dfy new file mode 100644 index 00000000..cff8b934 --- /dev/null +++ b/Test/dafny1/FindZero.dfy @@ -0,0 +1,30 @@ +method FindZero(a: array) returns (r: int) + requires a != null && forall i :: 0 <= i && i < a.Length ==> 0 <= a[i]; + requires forall i :: 0 <= i && i+1 < a.Length ==> a[i]-1 <= a[i+1]; + ensures 0 <= r ==> r < a.Length && a[r] == 0; + ensures r < 0 ==> forall i :: 0 <= i && i < a.Length ==> a[i] != 0; +{ + var n := 0; + while (n < a.Length) + invariant forall i :: 0 <= i && i < n && i < a.Length ==> a[i] != 0; + { + if (a[n] == 0) { r := n; return; } + call Lemma(a, n, a[n]); + n := n + a[n]; + } + r := -1; +} + +ghost method Lemma(a: array, k: int, m: int) + requires a != null && forall i :: 0 <= i && i < a.Length ==> 0 <= a[i]; + requires forall i :: 0 <= i && i+1 < a.Length ==> a[i]-1 <= a[i+1]; + requires 0 <= k; + requires k < a.Length ==> m <= a[k]; + ensures forall i :: k <= i && i < k+m && i < a.Length ==> a[i] != 0; + decreases m; +{ + if (0 < m && k < a.Length) { + assert a[k] != 0; + call Lemma(a, k+1, m-1); + } +} diff --git a/Test/dafny1/runtest.bat b/Test/dafny1/runtest.bat index b6fc7f62..cab18ddb 100644 --- a/Test/dafny1/runtest.bat +++ b/Test/dafny1/runtest.bat @@ -18,7 +18,7 @@ for %%f in (Queue.dfy PriorityQueue.dfy ExtensibleArray.dfy ListCopy.dfy ListReverse.dfy ListContents.dfy MatrixFun.dfy pow2.dfy SchorrWaite.dfy - Cubes.dfy SumOfCubes.dfy + Cubes.dfy SumOfCubes.dfy FindZero.dfy TerminationDemos.dfy Substitution.dfy TreeDatatype.dfy KatzManna.dfy Induction.dfy Rippling.dfy Celebrity.dfy -- cgit v1.2.3 From d608e9c2390fbd5e3e49685d5dd9fdeaeccf8f5a Mon Sep 17 00:00:00 2001 From: Mike Barnett Date: Thu, 12 May 2011 10:09:55 -0700 Subject: Trying to fix the bound expression simplifier. --- BCT/BytecodeTranslator/ExpressionTraverser.cs | 131 ++++++++++++++------- BCT/BytecodeTranslator/Heap.cs | 2 + .../TranslationTest/GeneralHeapInput.txt | 24 +++- .../TranslationTest/SplitFieldsHeapInput.txt | 24 +++- 4 files changed, 129 insertions(+), 52 deletions(-) diff --git a/BCT/BytecodeTranslator/ExpressionTraverser.cs b/BCT/BytecodeTranslator/ExpressionTraverser.cs index 7dbf8d0f..e4f8d1ff 100644 --- a/BCT/BytecodeTranslator/ExpressionTraverser.cs +++ b/BCT/BytecodeTranslator/ExpressionTraverser.cs @@ -644,17 +644,23 @@ namespace BytecodeTranslator Contract.Assert(TranslatedExpressions.Count == 0); #region Transform Right Hand Side ... - this.Visit(assignment.Source); - Bpl.Expr sourceexp = this.TranslatedExpressions.Pop(); + // Simplify the RHS so that all nested dereferences and method calls are broken + // up into separate assignments to locals. + var sourceExpression = ExpressionSimplifier.Simplify(this.sink, assignment.Source); + this.Visit(sourceExpression); + var sourceexp = this.TranslatedExpressions.Pop(); #endregion // Simplify the LHS so that all nested dereferences and method calls are broken // up into separate assignments to locals. - var blockExpression = AssignmentSimplifier.Simplify(this.sink, assignment.Target); - foreach (var s in blockExpression.BlockStatement.Statements) { - this.StmtTraverser.Visit(s); - } - var target = blockExpression.Expression as ITargetExpression; + var targetExpression = ExpressionSimplifier.Simplify(this.sink, assignment.Target); + //var targetBlockExpression = targetExpression as IBlockExpression; + //if (targetBlockExpression != null){ + //foreach (var s in targetBlockExpression.BlockStatement.Statements) { + // this.StmtTraverser.Visit(s); + //} + + var target = targetExpression as ITargetExpression; List args = null; Bpl.Expr arrayExpr = null; @@ -1198,71 +1204,108 @@ namespace BytecodeTranslator /// /// This is a rewriter so it must be used on a mutable Code Model!!! /// - private class AssignmentSimplifier : CodeRewriter { + private class ExpressionSimplifier : CodeRewriter { Sink sink; - private List localDeclarations = new List(); - private AssignmentSimplifier(Sink sink) + private ExpressionSimplifier(Sink sink) : base(sink.host) { this.sink = sink; } - public static IBlockExpression Simplify(Sink sink, ITargetExpression targetExpression) { - var a = new AssignmentSimplifier(sink); - var leftOverExpression = a.Rewrite(targetExpression); - return new BlockExpression() { - BlockStatement = new BlockStatement() { Statements = a.localDeclarations, }, - Expression = leftOverExpression, - Type = targetExpression.Type, - }; + public static IExpression Simplify(Sink sink, IExpression expression) { + var a = new ExpressionSimplifier(sink); + return a.Rewrite(expression); } public override IExpression Rewrite(IBoundExpression boundExpression) { if (boundExpression.Instance == null) return base.Rewrite(boundExpression); // REVIEW: Maybe just stop the rewriting and return boundExpression? + var thisInst = boundExpression.Instance as IThisReference; + if (thisInst != null) return boundExpression; + var p = boundExpression.Instance as IParameterDefinition; + if (p != null) return boundExpression; + var existingLocal = boundExpression.Instance as ILocalDefinition; + if (existingLocal != null) return boundExpression; + var e = base.Rewrite(boundExpression); boundExpression = e as IBoundExpression; if (boundExpression == null) return e; + var loc = new LocalDefinition() { - Name = this.host.NameTable.GetNameFor("_loc" + this.sink.LocalCounter.ToString()), // TODO: should make the name unique within the method containing the assignment + Name = this.host.NameTable.GetNameFor("_loc" + this.sink.LocalCounter.ToString()), Type = boundExpression.Type, }; - this.localDeclarations.Add( - new LocalDeclarationStatement() { - InitialValue = boundExpression, - LocalVariable = loc, - } - ); - return new BoundExpression() { - Definition = loc, - Instance = null, + var locDecl = new LocalDeclarationStatement() { + InitialValue = boundExpression, + LocalVariable = loc, + }; + return new BlockExpression() { + BlockStatement = new BlockStatement() { + Statements = new List{ locDecl }, + }, + Expression = new BoundExpression() { + Definition = loc, + Instance = null, + Type = boundExpression.Type, + }, Type = boundExpression.Type, }; + } - public override IExpression Rewrite(IMethodCall methodCall) { + public override ITargetExpression Rewrite(ITargetExpression targetExpression) { + var be = targetExpression.Instance as IBoundExpression; + if (be == null) return targetExpression; - var e = base.Rewrite(methodCall); // simplify anything deeper in the tree - methodCall = e as IMethodCall; - if (methodCall == null) return e; + var e = this.Rewrite(be); var loc = new LocalDefinition() { - Name = this.host.NameTable.GetNameFor("_loc"), // TODO: should make the name unique within the method containing the assignment - Type = methodCall.Type, + Name = this.host.NameTable.GetNameFor("_loc" + this.sink.LocalCounter.ToString()), + Type = be.Type, }; - this.localDeclarations.Add( - new LocalDeclarationStatement() { - InitialValue = methodCall, - LocalVariable = loc, - } - ); - return new BoundExpression() { - Definition = loc, - Instance = null, - Type = methodCall.Type, + var locDecl = new LocalDeclarationStatement() { + InitialValue = e, + LocalVariable = loc, + }; + return new TargetExpression() { + Definition = targetExpression.Definition, + Instance = new BlockExpression() { + BlockStatement = new BlockStatement() { + Statements = new List { locDecl, }, + }, + Expression = new BoundExpression() { + Definition = loc, + Instance = null, + }, + Type = loc.Type, + }, + Type = targetExpression.Type, }; } + + //public override IExpression Rewrite(IMethodCall methodCall) { + + // var e = base.Rewrite(methodCall); // simplify anything deeper in the tree + // methodCall = e as IMethodCall; + // if (methodCall == null) return e; + + // var loc = new LocalDefinition() { + // Name = this.host.NameTable.GetNameFor("_loc"), // TODO: should make the name unique within the method containing the assignment + // Type = methodCall.Type, + // }; + // this.localDeclarations.Add( + // new LocalDeclarationStatement() { + // InitialValue = methodCall, + // LocalVariable = loc, + // } + // ); + // return new BoundExpression() { + // Definition = loc, + // Instance = null, + // Type = methodCall.Type, + // }; + //} } } diff --git a/BCT/BytecodeTranslator/Heap.cs b/BCT/BytecodeTranslator/Heap.cs index 283a59a3..f38e0093 100644 --- a/BCT/BytecodeTranslator/Heap.cs +++ b/BCT/BytecodeTranslator/Heap.cs @@ -39,6 +39,7 @@ var $Heap: HeapType; var $Alloc: [Ref] bool; procedure {:inline 1} Alloc() returns (x: Ref) + free ensures x != null; modifies $Alloc; { assume $Alloc[x] == false && x != null; @@ -175,6 +176,7 @@ type HeapType = [Ref,Field]Box; var $Alloc: [Ref] bool; procedure {:inline 1} Alloc() returns (x: Ref) + free ensures x != null; modifies $Alloc; { assume $Alloc[x] == false && x != null; diff --git a/BCT/RegressionTests/TranslationTest/GeneralHeapInput.txt b/BCT/RegressionTests/TranslationTest/GeneralHeapInput.txt index 98d25303..430474d2 100644 --- a/BCT/RegressionTests/TranslationTest/GeneralHeapInput.txt +++ b/BCT/RegressionTests/TranslationTest/GeneralHeapInput.txt @@ -5,8 +5,6 @@ type Struct = [Field]Box; type HeapType = [Ref,Field]Box; -function IsGoodHeap(HeapType) : bool; - var $Alloc: [Ref]bool; procedure {:inline 1} Alloc() returns (x: Ref); @@ -17,7 +15,7 @@ procedure {:inline 1} Alloc() returns (x: Ref); implementation Alloc() returns (x: Ref) { - assume $Alloc[x] == false; + assume $Alloc[x] == false && x != null; $Alloc[x] := true; } @@ -29,10 +27,18 @@ axiom Box2Int($DefaultBox) == 0; axiom Box2Bool($DefaultBox) == false; +axiom Box2Ref($DefaultBox) == null; + +axiom Box2Struct($DefaultBox) == $DefaultStruct; + axiom (forall x: int :: { Int2Box(x) } Box2Int(Int2Box(x)) == x); axiom (forall x: bool :: { Bool2Box(x) } Box2Bool(Bool2Box(x)) == x); +axiom (forall x: Ref :: { Ref2Box(x) } Box2Ref(Ref2Box(x)) == x); + +axiom (forall x: Struct :: { Struct2Box(x) } Box2Struct(Struct2Box(x)) == x); + procedure DelegateAdd(a: Ref, b: Ref) returns (c: Ref); @@ -190,7 +196,7 @@ implementation DelegateRemoveHelper(oldi: Ref, m: int, o: Ref) returns (i: Ref) -var $Heap: HeapType where IsGoodHeap($Heap); +var $Heap: HeapType; function {:inline true} Read(H: HeapType, o: Ref, f: Field) : Box { @@ -220,6 +226,8 @@ type Type; const unique $DefaultStruct: Struct; +type Real; + function Box2Int(Box) : int; function Box2Bool(Box) : bool; @@ -228,6 +236,8 @@ function Box2Struct(Box) : Struct; function Box2Ref(Box) : Ref; +function Box2Real(Box) : Real; + function Int2Box(int) : Box; function Bool2Box(bool) : Box; @@ -236,12 +246,18 @@ function Struct2Box(Struct) : Box; function Ref2Box(Ref) : Box; +function Real2Box(Real) : Box; + function Struct2Ref(Struct) : Ref; function Int2Ref(int) : Ref; function Bool2Ref(bool) : Ref; +function Int2Real(int, Type, Type) : Real; + +function Real2Int(Real, Type, Type) : Real; + function $DynamicType(Ref) : Type; function $TypeOf(Type) : Ref; diff --git a/BCT/RegressionTests/TranslationTest/SplitFieldsHeapInput.txt b/BCT/RegressionTests/TranslationTest/SplitFieldsHeapInput.txt index 6e450f38..629a241c 100644 --- a/BCT/RegressionTests/TranslationTest/SplitFieldsHeapInput.txt +++ b/BCT/RegressionTests/TranslationTest/SplitFieldsHeapInput.txt @@ -5,9 +5,7 @@ type Struct = [Field]Box; type HeapType = [Ref,Field]Box; -var $Heap: HeapType where IsGoodHeap($Heap); - -function IsGoodHeap(HeapType) : bool; +var $Heap: HeapType; var $Alloc: [Ref]bool; @@ -19,7 +17,7 @@ procedure {:inline 1} Alloc() returns (x: Ref); implementation Alloc() returns (x: Ref) { - assume $Alloc[x] == false; + assume $Alloc[x] == false && x != null; $Alloc[x] := true; } @@ -31,10 +29,18 @@ axiom Box2Int($DefaultBox) == 0; axiom Box2Bool($DefaultBox) == false; +axiom Box2Ref($DefaultBox) == null; + +axiom Box2Struct($DefaultBox) == $DefaultStruct; + axiom (forall x: int :: { Int2Box(x) } Box2Int(Int2Box(x)) == x); axiom (forall x: bool :: { Bool2Box(x) } Box2Bool(Bool2Box(x)) == x); +axiom (forall x: Ref :: { Ref2Box(x) } Box2Ref(Ref2Box(x)) == x); + +axiom (forall x: Struct :: { Struct2Box(x) } Box2Struct(Struct2Box(x)) == x); + procedure DelegateAdd(a: Ref, b: Ref) returns (c: Ref); @@ -210,6 +216,8 @@ type Type; const unique $DefaultStruct: Struct; +type Real; + function Box2Int(Box) : int; function Box2Bool(Box) : bool; @@ -218,6 +226,8 @@ function Box2Struct(Box) : Struct; function Box2Ref(Box) : Ref; +function Box2Real(Box) : Real; + function Int2Box(int) : Box; function Bool2Box(bool) : Box; @@ -226,12 +236,18 @@ function Struct2Box(Struct) : Box; function Ref2Box(Ref) : Box; +function Real2Box(Real) : Box; + function Struct2Ref(Struct) : Ref; function Int2Ref(int) : Ref; function Bool2Ref(bool) : Ref; +function Int2Real(int, Type, Type) : Real; + +function Real2Int(Real, Type, Type) : Real; + function $DynamicType(Ref) : Type; function $TypeOf(Type) : Ref; -- cgit v1.2.3 From 81acbf9b3d4cc0662df9e42cf0a4eaeb99c760d3 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 12 May 2011 17:28:19 -0700 Subject: Dafny: forbid "decreases *" on ghost loops --- Source/Dafny/Resolver.cs | 11 +++++++---- Test/dafny0/Answer | 4 ++++ Test/dafny0/ResolutionErrors.dfy | 18 ++++++++++++++++++ Test/dafny0/runtest.bat | 4 ++-- 4 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 Test/dafny0/ResolutionErrors.dfy diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 22a58a39..972c82ab 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -1316,7 +1316,7 @@ namespace Microsoft.Dafny { } else if (stmt is WhileStmt) { WhileStmt s = (WhileStmt)stmt; - bool bodyIsSpecOnly = specContextOnly; + bool bodyMustBeSpecOnly = specContextOnly; if (s.Guard != null) { int prevErrorCount = ErrorCount; ResolveExpression(s.Guard, true, true); @@ -1326,7 +1326,7 @@ namespace Microsoft.Dafny { Error(s.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, s.Guard.Type); } if (!specContextOnly && successfullyResolved) { - bodyIsSpecOnly = UsesSpecFeatures(s.Guard); + bodyMustBeSpecOnly = UsesSpecFeatures(s.Guard); } } foreach (MaybeFreeExpression inv in s.Invariants) { @@ -1338,10 +1338,13 @@ namespace Microsoft.Dafny { } foreach (Expression e in s.Decreases) { ResolveExpression(e, true, true); + if (bodyMustBeSpecOnly && e is WildcardExpr) { + Error(e, "'decreases *' is not allowed on ghost loops"); + } // any type is fine } - s.IsGhost = bodyIsSpecOnly; - ResolveStatement(s.Body, bodyIsSpecOnly, method); + s.IsGhost = bodyMustBeSpecOnly; + ResolveStatement(s.Body, bodyMustBeSpecOnly, method); } else if (stmt is ForeachStmt) { ForeachStmt s = (ForeachStmt)stmt; diff --git a/Test/dafny0/Answer b/Test/dafny0/Answer index 1ac8fc1c..cd531472 100644 --- a/Test/dafny0/Answer +++ b/Test/dafny0/Answer @@ -391,6 +391,10 @@ Execution trace: Dafny program verifier finished with 3 verified, 3 errors +-------------------- ResolutionErrors.dfy -------------------- +ResolutionErrors.dfy(10,16): Error: 'decreases *' is not allowed on ghost loops +1 resolution/type errors detected in ResolutionErrors.dfy + -------------------- Array.dfy -------------------- Array.dfy(10,12): Error: assignment may update an array element not in the enclosing method's modifies clause Execution trace: diff --git a/Test/dafny0/ResolutionErrors.dfy b/Test/dafny0/ResolutionErrors.dfy new file mode 100644 index 00000000..b105a5c0 --- /dev/null +++ b/Test/dafny0/ResolutionErrors.dfy @@ -0,0 +1,18 @@ +method UmmThisIsntGoingToWork() +{ + var a := new int [2]; + var b := new int [1]; + a[0] := 1; + a[1] := -1; + + ghost var i := 0; + while (i < 2) + decreases *; // error: not allowed on a ghost loop + invariant i <= 2; + invariant forall j :: 0 <= j && j < i ==> a[j] > 0; + { + i := 0; + } + assert a[1] == -1; // ...for then this would incorrectly verify + b[a[1]] := 0; +} diff --git a/Test/dafny0/runtest.bat b/Test/dafny0/runtest.bat index 491b7b75..12d9bcf4 100644 --- a/Test/dafny0/runtest.bat +++ b/Test/dafny0/runtest.bat @@ -11,8 +11,8 @@ for %%f in (Simple.dfy) do ( %DAFNY_EXE% %* /dprint:- /env:0 /noVerify %%f ) -for %%f in (TypeTests.dfy NatTypes.dfy SmallTests.dfy Definedness.dfy - FunctionSpecifications.dfy +for %%f in (TypeTests.dfy NatTypes.dfy SmallTests.dfy Definedness.dfy + FunctionSpecifications.dfy ResolutionErrors.dfy Array.dfy MultiDimArray.dfy NonGhostQuantifiers.dfy AdvancedLHS.dfy Modules0.dfy Modules1.dfy BadFunction.dfy Termination.dfy Use.dfy DTypes.dfy -- cgit v1.2.3 From c4eb22c363b65823fbb38ab618df3c28ffa59bbd Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 12 May 2011 18:01:59 -0700 Subject: Dafny: fixed bugs in resolution of multi-dimensional arrays --- Source/Dafny/Dafny.atg | 2 + Source/Dafny/Parser.cs | 51 ++++++++++---------- Source/Dafny/Resolver.cs | 4 +- Source/Dafny/Scanner.cs | 102 +++++++++++++++++++-------------------- Test/dafny0/Answer | 5 +- Test/dafny0/ResolutionErrors.dfy | 8 +++ 6 files changed, 90 insertions(+), 82 deletions(-) diff --git a/Source/Dafny/Dafny.atg b/Source/Dafny/Dafny.atg index 23582495..8e999553 100644 --- a/Source/Dafny/Dafny.atg +++ b/Source/Dafny/Dafny.atg @@ -1312,6 +1312,8 @@ SelectOrCallSuffix ) (. if (multipleIndices != null) { e = new MultiSelectExpr(x, e, multipleIndices); + // make sure an array class with this dimensionality exists + UserDefinedType tmp = theBuiltIns.ArrayType(multipleIndices.Count, new IntType(), true); } else { if (!anyDots && e0 == null) { /* a parsing error occurred */ diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs index 180f15ab..db33283b 100644 --- a/Source/Dafny/Parser.cs +++ b/Source/Dafny/Parser.cs @@ -25,7 +25,7 @@ public class Parser { const bool T = true; const bool x = false; const int minErrDist = 2; - + public Scanner/*!*/ scanner; public Errors/*!*/ errors; @@ -161,10 +161,10 @@ public static int Parse (string/*!*/ s, string/*!*/ filename, List= minErrDist) errors.SemErr(t, msg); errDist = 0; } - - public void SemErr(IToken/*!*/ tok, string/*!*/ msg) { - Contract.Requires(tok != null); - Contract.Requires(msg != null); + + public void SemErr(IToken/*!*/ tok, string/*!*/ msg) { + Contract.Requires(tok != null); + Contract.Requires(msg != null); errors.SemErr(tok, msg); } @@ -177,15 +177,15 @@ public static int Parse (string/*!*/ s, string/*!*/ filename, List theImports; @@ -2047,6 +2047,8 @@ List/*!*/ decreases) { } else SynErr(144); if (multipleIndices != null) { e = new MultiSelectExpr(x, e, multipleIndices); + // make sure an array class with this dimensionality exists + UserDefinedType tmp = theBuiltIns.ArrayType(multipleIndices.Count, new IntType(), true); } else { if (!anyDots && e0 == null) { /* a parsing error occurred */ @@ -2131,13 +2133,13 @@ List/*!*/ decreases) { public void Parse() { la = new Token(); - la.val = ""; + la.val = ""; Get(); Dafny(); - Expect(0); + Expect(0); } - + static readonly bool[,]/*!*/ set = { {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,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,x, x,x,T,x, T,x,x,x, x,T,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}, @@ -2164,20 +2166,18 @@ List/*!*/ decreases) { public class Errors { public int count = 0; // number of errors detected public System.IO.TextWriter/*!*/ errorStream = Console.Out; // error messages go to this stream - public string errMsgFormat = "{0}({1},{2}): error: {3}"; // 0=filename, 1=line, 2=column, 3=text - public string warningMsgFormat = "{0}({1},{2}): warning: {3}"; // 0=filename, 1=line, 2=column, 3=text - + public string errMsgFormat = "{0}({1},{2}): error: {3}"; // 0=filename, 1=line, 2=column, 3=text + public string warningMsgFormat = "{0}({1},{2}): warning: {3}"; // 0=filename, 1=line, 2=column, 3=text + public void SynErr(string filename, int line, int col, int n) { - SynErr(filename, line, col, GetSyntaxErrorString(n)); - } - - public virtual void SynErr(string filename, int line, int col, string/*!*/ msg) { - Contract.Requires(msg != null); + SynErr(filename, line, col, GetSyntaxErrorString(n)); + } + public virtual void SynErr(string filename, int line, int col, string msg) { + Contract.Requires(msg != null); errorStream.WriteLine(errMsgFormat, filename, line, col, msg); count++; - } - - string GetSyntaxErrorString(int n) { + } + string GetSyntaxErrorString(int n) { string s; switch (n) { case 0: s = "EOF expected"; break; @@ -2333,7 +2333,7 @@ public class Errors { default: s = "error " + n; break; } - return s; + return s; } public void SemErr(IToken/*!*/ tok, string/*!*/ msg) { // semantic errors @@ -2341,9 +2341,8 @@ public class Errors { Contract.Requires(msg != null); SemErr(tok.filename, tok.line, tok.col, msg); } - public virtual void SemErr(string filename, int line, int col, string/*!*/ msg) { - Contract.Requires(msg != null); + Contract.Requires(msg != null); errorStream.WriteLine(errMsgFormat, filename, line, col, msg); count++; } diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 972c82ab..00cabd1d 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -960,7 +960,7 @@ namespace Microsoft.Dafny { if (!UnifyTypes(iProxy.Arg, ((SeqType)t).Arg)) { return false; } - } else if (t.IsArrayType) { + } else if (t.IsArrayType && (t.AsArrayType).Dims == 1) { Type elType = UserDefinedType.ArrayElementType(t); if (!UnifyTypes(iProxy.Arg, elType)) { return false; @@ -1909,7 +1909,7 @@ namespace Microsoft.Dafny { Contract.Assert(e.Array.Type != null); // follows from postcondition of ResolveExpression Type elementType = new InferredTypeProxy(); if (!UnifyTypes(e.Array.Type, builtIns.ArrayType(e.Indices.Count, elementType))) { - Error(e.Array, "array selection requires an array (got {0})", e.Array.Type); + Error(e.Array, "array selection requires an array{0} (got {1})", e.Indices.Count, e.Array.Type); } int i = 0; foreach (Expression idx in e.Indices) { diff --git a/Source/Dafny/Scanner.cs b/Source/Dafny/Scanner.cs index 817df6cd..0af254f3 100644 --- a/Source/Dafny/Scanner.cs +++ b/Source/Dafny/Scanner.cs @@ -19,7 +19,7 @@ public class Buffer { // a) whole stream in buffer // b) part of stream in buffer // 2) non seekable stream (network, console) - + public const int EOF = 65535 + 1; // char.MaxValue + 1; const int MIN_BUFFER_LENGTH = 1024; // 1KB const int MAX_BUFFER_LENGTH = MIN_BUFFER_LENGTH * 64; // 64KB @@ -31,17 +31,15 @@ public class Buffer { Stream/*!*/ stream; // input stream (seekable) bool isUserStream; // was the stream opened by the user? - [ContractInvariantMethod] - void ObjectInvariant(){ - Contract.Invariant(buf != null); - Contract.Invariant(stream != null); - } - -// [NotDelayed] - public Buffer (Stream/*!*/ s, bool isUserStream) : base() { +[ContractInvariantMethod] +void ObjectInvariant(){ + Contract.Invariant(buf != null); + Contract.Invariant(stream != null);} + [NotDelayed] + public Buffer (Stream/*!*/ s, bool isUserStream) :base() { Contract.Requires(s != null); stream = s; this.isUserStream = isUserStream; - + int fl, bl; if (s.CanSeek) { fl = (int) s.Length; @@ -53,12 +51,12 @@ public class Buffer { buf = new byte[(bl>0) ? bl : MIN_BUFFER_LENGTH]; fileLen = fl; bufLen = bl; - + if (fileLen > 0) Pos = 0; // setup buffer to position 0 (start) else bufPos = 0; // index 0 is already after the file, thus Pos = 0 is invalid if (bufLen == fileLen && s.CanSeek) Close(); } - + protected Buffer(Buffer/*!*/ b) { // called in UTF8Buffer constructor Contract.Requires(b != null); buf = b.buf; @@ -75,14 +73,14 @@ public class Buffer { } ~Buffer() { Close(); } - + protected void Close() { if (!isUserStream && stream != null) { stream.Close(); //stream = null; } } - + public virtual int Read () { if (bufPos < bufLen) { return buf[bufPos++]; @@ -102,7 +100,7 @@ public class Buffer { Pos = curPos; return ch; } - + public string/*!*/ GetString (int beg, int end) { Contract.Ensures(Contract.Result() != null); int len = 0; @@ -141,7 +139,7 @@ public class Buffer { } } } - + // Read the next chunk of bytes from the stream, increases the buffer // if needed and updates the fields fileLen and bufLen. // Returns the number of bytes read. @@ -215,20 +213,19 @@ public class Scanner { const int noSym = 107; - [ContractInvariantMethod] - void objectInvariant(){ - Contract.Invariant(buffer!=null); - Contract.Invariant(t != null); - Contract.Invariant(start != null); - Contract.Invariant(tokens != null); - Contract.Invariant(pt != null); - Contract.Invariant(tval != null); - Contract.Invariant(Filename != null); - Contract.Invariant(errorHandler != null); - } - +[ContractInvariantMethod] +void objectInvariant(){ + Contract.Invariant(buffer!=null); + Contract.Invariant(t != null); + Contract.Invariant(start != null); + Contract.Invariant(tokens != null); + Contract.Invariant(pt != null); + Contract.Invariant(tval != null); + Contract.Invariant(Filename != null); + Contract.Invariant(errorHandler != null); +} public Buffer/*!*/ buffer; // scanner buffer - + Token/*!*/ t; // current token int ch; // current input character int pos; // byte position of current character @@ -239,13 +236,13 @@ public class Scanner { Token/*!*/ tokens; // list of tokens already peeked (first token is a dummy) Token/*!*/ pt; // current peek token - + char[]/*!*/ tval = new char[128]; // text of current token int tlen; // length of current token - + private string/*!*/ Filename; private Errors/*!*/ errorHandler; - + static Scanner() { start = new Hashtable(128); for (int i = 39; i <= 39; ++i) start[i] = 1; @@ -294,9 +291,9 @@ public class Scanner { start[Buffer.EOF] = -1; } - -// [NotDelayed] - public Scanner (string/*!*/ fileName, Errors/*!*/ errorHandler) : base() { + + [NotDelayed] + public Scanner (string/*!*/ fileName, Errors/*!*/ errorHandler) :base(){ Contract.Requires(fileName != null); Contract.Requires(errorHandler != null); this.errorHandler = errorHandler; @@ -306,14 +303,15 @@ public class Scanner { Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); buffer = new Buffer(stream, false); Filename = fileName; + Init(); } catch (IOException) { throw new FatalError("Cannot open file " + fileName); } } - -// [NotDelayed] - public Scanner (Stream/*!*/ s, Errors/*!*/ errorHandler, string/*!*/ fileName) : base() { + + [NotDelayed] + public Scanner (Stream/*!*/ s, Errors/*!*/ errorHandler, string/*!*/ fileName) :base(){ Contract.Requires(s != null); Contract.Requires(errorHandler != null); Contract.Requires(fileName != null); @@ -322,9 +320,10 @@ public class Scanner { buffer = new Buffer(s, true); this.errorHandler = errorHandler; this.Filename = fileName; + Init(); } - + void Init() { pos = -1; line = 1; col = 0; oldEols = 0; @@ -345,11 +344,11 @@ public class Scanner { Contract.Ensures(Contract.Result() != null); int p = buffer.Pos; int ch = buffer.Read(); - // replace isolated '\r' by '\n' in order to make + // replace isolated '\r' by '\n' in order to make // eol handling uniform across Windows, Unix and Mac if (ch == '\r' && buffer.Peek() != '\n') ch = EOL; while (ch != EOL && ch != Buffer.EOF){ - ch = buffer.Read(); + ch = buffer.Read(); // replace isolated '\r' by '\n' in order to make // eol handling uniform across Windows, Unix and Mac if (ch == '\r' && buffer.Peek() != '\n') ch = EOL; @@ -360,7 +359,7 @@ public class Scanner { } void NextCh() { - if (oldEols > 0) { ch = EOL; oldEols--; } + if (oldEols > 0) { ch = EOL; oldEols--; } else { // pos = buffer.Pos; // ch = buffer.Read(); col++; @@ -368,9 +367,9 @@ public class Scanner { // // eol handling uniform across Windows, Unix and Mac // if (ch == '\r' && buffer.Peek() != '\n') ch = EOL; // if (ch == EOL) { line++; col = 0; } - + while (true) { - pos = buffer.Pos; + pos = buffer.Pos; ch = buffer.Read(); col++; // replace isolated '\r' by '\n' in order to make // eol handling uniform across Windows, Unix and Mac @@ -420,7 +419,7 @@ public class Scanner { return; } - + } } @@ -559,13 +558,10 @@ public class Scanner { t.pos = pos; t.col = col; t.line = line; t.filename = this.Filename; int state; - if (start.ContainsKey(ch)) { - Contract.Assert(start[ch] != null); - state = (int) start[ch]; - } + if (start.ContainsKey(ch)) { state = (int) cce.NonNull( start[ch]); } else { state = 0; } tlen = 0; AddCh(); - + switch (state) { case -1: { t.kind = eofSym; break; } // NextCh already done case 0: { @@ -773,14 +769,14 @@ public class Scanner { t.val = new String(tval, 0, tlen); return t; } - + private void SetScannerBehindT() { buffer.Pos = t.pos; NextCh(); line = t.line; col = t.col; for (int i = 0; i < tlen; i++) NextCh(); } - + // get the next token (possibly a token already seen during peeking) public Token/*!*/ Scan () { Contract.Ensures(Contract.Result() != null); @@ -801,7 +797,7 @@ public class Scanner { } pt = pt.next; } while (pt.kind > maxT); // skip pragmas - + return pt; } diff --git a/Test/dafny0/Answer b/Test/dafny0/Answer index cd531472..8382a525 100644 --- a/Test/dafny0/Answer +++ b/Test/dafny0/Answer @@ -393,7 +393,10 @@ Dafny program verifier finished with 3 verified, 3 errors -------------------- ResolutionErrors.dfy -------------------- ResolutionErrors.dfy(10,16): Error: 'decreases *' is not allowed on ghost loops -1 resolution/type errors detected in ResolutionErrors.dfy +ResolutionErrors.dfy(23,11): Error: array selection requires an array2 (got array3) +ResolutionErrors.dfy(24,12): Error: sequence/array selection requires a sequence or array (got array3) +ResolutionErrors.dfy(25,11): Error: array selection requires an array4 (got array) +4 resolution/type errors detected in ResolutionErrors.dfy -------------------- Array.dfy -------------------- Array.dfy(10,12): Error: assignment may update an array element not in the enclosing method's modifies clause diff --git a/Test/dafny0/ResolutionErrors.dfy b/Test/dafny0/ResolutionErrors.dfy index b105a5c0..77a30102 100644 --- a/Test/dafny0/ResolutionErrors.dfy +++ b/Test/dafny0/ResolutionErrors.dfy @@ -16,3 +16,11 @@ method UmmThisIsntGoingToWork() assert a[1] == -1; // ...for then this would incorrectly verify b[a[1]] := 0; } + +method M(a: array3, b: array, m: int, n: int) +{ + // the following invalid expressions were once incorrectly resolved: + var x := a[m, n]; // error + var y := a[m]; // error + var z := b[m, n, m, n]; // error +} -- cgit v1.2.3 From 01ef4de837c37c98edc7ba10241867873190b008 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Thu, 12 May 2011 23:29:40 -0700 Subject: Add source file (Expression Design) for the BVD icon in case anyone ever needs it --- Source/ModelViewer/bvdicon.design | Bin 0 -> 7272 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Source/ModelViewer/bvdicon.design diff --git a/Source/ModelViewer/bvdicon.design b/Source/ModelViewer/bvdicon.design new file mode 100644 index 00000000..c71ca9a6 Binary files /dev/null and b/Source/ModelViewer/bvdicon.design differ -- cgit v1.2.3 From 2178536246cab25a4564a15c05a6d2fcb4ac54ca Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 13 May 2011 10:43:58 -0700 Subject: Cleaner version of ghost loop termination example. --- Test/dafny0/Answer | 6 +++--- Test/dafny0/ResolutionErrors.dfy | 17 ++++++++--------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Test/dafny0/Answer b/Test/dafny0/Answer index 8382a525..0ee81392 100644 --- a/Test/dafny0/Answer +++ b/Test/dafny0/Answer @@ -393,9 +393,9 @@ Dafny program verifier finished with 3 verified, 3 errors -------------------- ResolutionErrors.dfy -------------------- ResolutionErrors.dfy(10,16): Error: 'decreases *' is not allowed on ghost loops -ResolutionErrors.dfy(23,11): Error: array selection requires an array2 (got array3) -ResolutionErrors.dfy(24,12): Error: sequence/array selection requires a sequence or array (got array3) -ResolutionErrors.dfy(25,11): Error: array selection requires an array4 (got array) +ResolutionErrors.dfy(22,11): Error: array selection requires an array2 (got array3) +ResolutionErrors.dfy(23,12): Error: sequence/array selection requires a sequence or array (got array3) +ResolutionErrors.dfy(24,11): Error: array selection requires an array4 (got array) 4 resolution/type errors detected in ResolutionErrors.dfy -------------------- Array.dfy -------------------- diff --git a/Test/dafny0/ResolutionErrors.dfy b/Test/dafny0/ResolutionErrors.dfy index 77a30102..5ec0cc4c 100644 --- a/Test/dafny0/ResolutionErrors.dfy +++ b/Test/dafny0/ResolutionErrors.dfy @@ -1,20 +1,19 @@ -method UmmThisIsntGoingToWork() + +//Should not verify, as ghost loops should not be allowed to diverge. +method GhostDivergentLoop() { var a := new int [2]; - var b := new int [1]; a[0] := 1; a[1] := -1; - ghost var i := 0; while (i < 2) - decreases *; // error: not allowed on a ghost loop - invariant i <= 2; - invariant forall j :: 0 <= j && j < i ==> a[j] > 0; + decreases *; // error: not allowed on a ghost loop + invariant i <= 2; + invariant (forall j :: 0 <= j && j < i ==> a[j] > 0); { - i := 0; + i := 0; } - assert a[1] == -1; // ...for then this would incorrectly verify - b[a[1]] := 0; + assert a[1] != a[1]; // ...for then this would incorrectly verify } method M(a: array3, b: array, m: int, n: int) -- cgit v1.2.3 From a3d7bd9b4a7b62cb0d8e4b0762d8db51c1548f1e Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 13 May 2011 14:38:28 -0700 Subject: Boogie: added features to help with modular verification. In particular, define FILE_n when parsing file n on the command line, and support :extern and :ignore attributes on top-level declarations. --- Source/BoogieDriver/BoogieDriver.cs | 6 +- Source/Core/Absy.cs | 26 +++---- Source/Core/CommandLineOptions.cs | 15 ++++ Source/Core/ResolutionContext.cs | 142 +++++++++++++++-------------------- Test/test0/Answer | 13 ++++ Test/test0/SeparateVerification0.bpl | 21 ++++++ Test/test0/SeparateVerification1.bpl | 19 +++++ Test/test0/runtest.bat | 9 +++ 8 files changed, 151 insertions(+), 100 deletions(-) create mode 100644 Test/test0/SeparateVerification0.bpl create mode 100644 Test/test0/SeparateVerification1.bpl diff --git a/Source/BoogieDriver/BoogieDriver.cs b/Source/BoogieDriver/BoogieDriver.cs index 3929e237..dcc007fe 100644 --- a/Source/BoogieDriver/BoogieDriver.cs +++ b/Source/BoogieDriver/BoogieDriver.cs @@ -261,7 +261,8 @@ namespace Microsoft.Boogie { //BoogiePL.Errors.count = 0; Program program = null; bool okay = true; - foreach (string bplFileName in fileNames) { + for (int fileId = 0; fileId < fileNames.Count; fileId++) { + string bplFileName = fileNames[fileId]; if (!suppressTraceOutput) { if (CommandLineOptions.Clo.XmlSink != null) { CommandLineOptions.Clo.XmlSink.WriteFileFragment(bplFileName); @@ -274,7 +275,8 @@ namespace Microsoft.Boogie { Program programSnippet; int errorCount; try { - errorCount = BoogiePL.Parser.Parse(bplFileName, null, out programSnippet); + var defines = new List() { "FILE_" + fileId }; + errorCount = BoogiePL.Parser.Parse(bplFileName, defines, out programSnippet); if (programSnippet == null || errorCount != 0) { Console.WriteLine("{0} parse errors detected in {1}", errorCount, bplFileName); okay = false; diff --git a/Source/Core/Absy.cs b/Source/Core/Absy.cs index cde8fc3f..67500c88 100644 --- a/Source/Core/Absy.cs +++ b/Source/Core/Absy.cs @@ -235,30 +235,26 @@ namespace Microsoft.Boogie { ResolveTypes(rc); - List prunedTopLevelDecls = CommandLineOptions.Clo.OverlookBoogieTypeErrors ? new List() : null; - + var prunedTopLevelDecls = new List(); foreach (Declaration d in TopLevelDeclarations) { + if (QKeyValue.FindBoolAttribute(d.Attributes, "ignore")) { + continue; + } // resolve all the non-type-declarations if (d is TypeCtorDecl || d is TypeSynonymDecl) { - if (prunedTopLevelDecls != null) - prunedTopLevelDecls.Add(d); } else { int e = rc.ErrorCount; d.Resolve(rc); - if (prunedTopLevelDecls != null) { - if (rc.ErrorCount != e && d is Implementation) { - // ignore this implementation - System.Console.WriteLine("Warning: Ignoring implementation {0} because of translation resolution errors", ((Implementation)d).Name); - rc.ErrorCount = e; - } else { - prunedTopLevelDecls.Add(d); - } + if (CommandLineOptions.Clo.OverlookBoogieTypeErrors && rc.ErrorCount != e && d is Implementation) { + // ignore this implementation + System.Console.WriteLine("Warning: Ignoring implementation {0} because of translation resolution errors", ((Implementation)d).Name); + rc.ErrorCount = e; + continue; } } + prunedTopLevelDecls.Add(d); } - if (prunedTopLevelDecls != null) { - TopLevelDeclarations = prunedTopLevelDecls; - } + TopLevelDeclarations = prunedTopLevelDecls; foreach (Declaration d in TopLevelDeclarations) { Variable v = d as Variable; diff --git a/Source/Core/CommandLineOptions.cs b/Source/Core/CommandLineOptions.cs index 5094fb9e..84ecce6d 100644 --- a/Source/Core/CommandLineOptions.cs +++ b/Source/Core/CommandLineOptions.cs @@ -1875,6 +1875,21 @@ namespace Microsoft.Boogie { Console.WriteLine( @"Boogie: The following attributes are supported by this implementation. + ---- On top-level declarations --------------------------------------------- + + {:ignore} + Ignore the declaration (after checking for duplicate names). + See also below for more options when :ignore is used with axioms. + + {:extern} + If two top-level declarations introduce the same name (for example, two + constants with the same name or two procedures with the same name), then + Boogie usually produces an error message. However, if at least one of + the declarations is declared with :extern, one of the declarations is + ignored. If both declarations are :extern, Boogie arbitrarily chooses + one of them to keep; otherwise, Boogie ignore the :extern declaration + and keeps the other. + ---- On axioms ------------------------------------------------------------- {:inline true} diff --git a/Source/Core/ResolutionContext.cs b/Source/Core/ResolutionContext.cs index d4b353ef..803b1f8b 100644 --- a/Source/Core/ResolutionContext.cs +++ b/Source/Core/ResolutionContext.cs @@ -170,15 +170,22 @@ namespace Microsoft.Boogie { public void AddType(NamedDeclaration td) { Contract.Requires(td != null); Contract.Assert((td is TypeCtorDecl) || (td is TypeSynonymDecl)); + Contract.Requires(td.Name != null); - string name = cce.NonNull(td.Name); + string name = td.Name; if (CheckBvNameClashes(td, name)) return; // error has already been reported - if (types[name] != null) { - Error(td, "more than one declaration of type name: {0}", name); - } else { + var previous = (NamedDeclaration)types[name]; + if (previous == null) { types.Add(name, td); + } else { + var r = (NamedDeclaration)SelectNonExtern(td, previous); + if (r == null) { + Error(td, "more than one declaration of type name: {0}", name); + } else { + types[name] = r; + } } } @@ -247,79 +254,6 @@ namespace Microsoft.Boogie { return null; // not present } - // ------------------------------ Types ------------------------------ - - // user-defined types - // Hashtable /*string->TypeDecl*/! types = new Hashtable /*string->TypeDecl*/ (); - /* - public void AddType(TypeDecl td){ - Contract.Requires(td != null); - string! name = (!)td.Name; - - if (name.StartsWith("bv") && name.Length > 2) { - bool isBv = true; - for (int i = 2; i < name.Length; ++i) - if (!char.IsDigit(name[i])) isBv = false; - if (isBv) - Error(td, "type name: {0} is registered for bitvectors", name); - } - - if (types[name] != null) - { - Error(td, "more than one declaration of type name: {0}", name); - } - else - { - types.Add(name, td); - } - } - */ - /// - /// Returns the declaration of the named type, or null if - /// no such type is declared. - /// - /// - /// - /* public TypeDecl LookUpType(string name){ -Contract.Requires(name != null); - return (TypeDecl)types[name]; - } - */ - // ------------------------------ Type Binders ------------------------------ - /* - List! typeBinders = new List(5); - - public void AddTypeBinder(TypeBinderDecl td){ - Contract.Requires(td != null); - for (int i = 0; i < typeBinders.Count; i++) { - if (typeBinders[i].Name == td.Name) { - Error(td, "more than one declaration of type binder name: {0}", td.Name); - return; - } - } - typeBinders.Add(td); - } - - public int TypeBinderState { - get { return typeBinders.Count; } - set { typeBinders.RemoveRange(value, typeBinders.Count - value); } - } - - /// - /// Returns the declaration of the named type binder, or null if - /// no such binder is declared. - /// - public TypeDecl LookUpTypeBinder(string name){ - Contract.Requires(name != null); - for (int i = typeBinders.Count; 0 <= --i; ) { - TypeBinderDecl td = typeBinders[i]; - if (td.Name == name) { - return td; - } - } - return null; // not present - } - */ // ------------------------------ Variables ------------------------------ class VarContextNode { @@ -365,10 +299,16 @@ Contract.Requires(name != null); public void AddVariable(Variable var, bool global) { Contract.Requires(var != null); - if (FindVariable(cce.NonNull(var.Name), !global) != null) { - Error(var, "more than one declaration of variable name: {0}", var.Name); - } else { + var previous = FindVariable(cce.NonNull(var.Name), !global); + if (previous == null) { varContext.VarSymbols.Add(var.Name, var); + } else { + var r = (Variable)SelectNonExtern(var, previous); + if (r == null) { + Error(var, "more than one declaration of variable name: {0}", var.Name); + } else { + varContext.VarSymbols[var.Name] = r; + } } } @@ -417,11 +357,47 @@ Contract.Requires(name != null); public void AddProcedure(DeclWithFormals proc) { Contract.Requires(proc != null); - if (funcdures[cce.NonNull(proc.Name)] != null) { - Error(proc, "more than one declaration of function/procedure name: {0}", proc.Name); + Contract.Requires(proc.Name != null); + + string name = proc.Name; + var previous = (DeclWithFormals)funcdures[name]; + if (previous == null) { + funcdures.Add(name, proc); + } else { + var r = (DeclWithFormals)SelectNonExtern(proc, previous); + if (r == null) { + Error(proc, "more than one declaration of function/procedure name: {0}", name); + } else { + funcdures[name] = r; + } + } + } + + /// + /// If both "a" and "b" have an ":extern" attribute, returns either one. + /// If one of "a" and "b" has an ":extern" attribute, returns that one. + /// If neither of "a" and "b" has an ":extern" attribute, returns null. + /// If a non-value value is returned, this method also adds the ":ignore" + /// attribute to the declaration NOT returned. + /// + Declaration SelectNonExtern(Declaration a, Declaration b) { + Contract.Requires(a != null); + Contract.Requires(b != null); + Contract.Ensures(Contract.Result() == null || Contract.Result() == a || Contract.Result() == b); + + Declaration ignore, keep; + if (QKeyValue.FindBoolAttribute(a.Attributes, "extern")) { + ignore = a; + keep = b; + } else if (QKeyValue.FindBoolAttribute(b.Attributes, "extern")) { + ignore = b; + keep = a; } else { - funcdures.Add(proc.Name, proc); + return null; } + // prepend :ignore attribute + ignore.Attributes = new QKeyValue(ignore.tok, "ignore", new List(), ignore.Attributes); + return keep; } /// diff --git a/Test/test0/Answer b/Test/test0/Answer index 0441b69b..d434a8ca 100644 --- a/Test/test0/Answer +++ b/Test/test0/Answer @@ -247,3 +247,16 @@ BadQuantifier.bpl(3,15): error: invalid QuantifierBody EmptyCallArgs.bpl(31,2): Error: type variable must occur in types of given arguments: a EmptyCallArgs.bpl(32,2): Error: type variable must occur in types of given arguments: a 2 name resolution errors detected in EmptyCallArgs.bpl +----- SeparateVerification0.bpl +SeparateVerification0.bpl(13,6): Error: undeclared identifier: y +1 name resolution errors detected in SeparateVerification0.bpl +----- SeparateVerification1.bpl SeparateVerification0.bpl +SeparateVerification1.bpl(4,6): Error: undeclared identifier: x +SeparateVerification0.bpl(10,6): Error: undeclared identifier: x +2 name resolution errors detected in SeparateVerification0.bpl +----- SeparateVerification0.bpl SeparateVerification0.bpl + +Boogie program verifier finished with 0 verified, 0 errors +----- SeparateVerification0.bpl SeparateVerification0.bpl SeparateVerification1.bpl + +Boogie program verifier finished with 0 verified, 0 errors diff --git a/Test/test0/SeparateVerification0.bpl b/Test/test0/SeparateVerification0.bpl new file mode 100644 index 00000000..5a8ef283 --- /dev/null +++ b/Test/test0/SeparateVerification0.bpl @@ -0,0 +1,21 @@ +// need to include this file twice for it to include all necessary declarations + +#if FILE_0 +const x: int; +#else +const y: int; +#endif + +#if FILE_1 +axiom x == 12; +procedure Q(); +#else +axiom y == 7; +#endif + +// duplicates of :extern's are fine (Boogie keeps the non-:extern or chooses arbitrarily among the :extern's) +type {:extern} T; +const {:extern} C: int; +function {:extern} F(): int; +var {:extern} n: int; +procedure {:extern} P(inconsistentParameterButThatIsOkayBecauseTheExternIsIgnored: int); diff --git a/Test/test0/SeparateVerification1.bpl b/Test/test0/SeparateVerification1.bpl new file mode 100644 index 00000000..0e3e7926 --- /dev/null +++ b/Test/test0/SeparateVerification1.bpl @@ -0,0 +1,19 @@ +// to be used with SeparateVerification0.bpl + +// x and y are declared in SeparateVerification0.bpl +axiom x + y <= 100; + +// these are declared as :extern as SeparateVerification0.bpl +type T; +const C: int; +function F(): int; +var n: int; +procedure P(); +procedure {:extern} Q(x: int); + +procedure Main() { + call P(); // note, calling the parameter-less non-extern P (an extern and a non-extern + // declaration of the same name are usually mostly identical declarations, + // but Boogie allows them to be different, because it ignores the extern ones) + call Q(); // ditto +} diff --git a/Test/test0/runtest.bat b/Test/test0/runtest.bat index c7c6ee3d..5ecc22dc 100644 --- a/Test/test0/runtest.bat +++ b/Test/test0/runtest.bat @@ -31,3 +31,12 @@ rem set BGEXE=mono ..\..\Binaries\Boogie.exe %BGEXE% %* /noVerify Orderings.bpl %BGEXE% %* /noVerify BadQuantifier.bpl %BGEXE% %* /noVerify EmptyCallArgs.bpl + +echo ----- SeparateVerification0.bpl +%BGEXE% %* /noVerify SeparateVerification0.bpl +echo ----- SeparateVerification1.bpl SeparateVerification0.bpl +%BGEXE% %* /noVerify SeparateVerification1.bpl SeparateVerification0.bpl +echo ----- SeparateVerification0.bpl SeparateVerification0.bpl +%BGEXE% %* /noVerify SeparateVerification0.bpl SeparateVerification0.bpl +echo ----- SeparateVerification0.bpl SeparateVerification0.bpl SeparateVerification1.bpl +%BGEXE% %* /noVerify SeparateVerification0.bpl SeparateVerification0.bpl SeparateVerification1.bpl -- cgit v1.2.3 From f2c2a579f9db4f012d79f25469563204f8fb285e Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 13 May 2011 15:20:11 -0700 Subject: Dafny: fixed typo in parser code --- Source/Dafny/Dafny.atg | 2 +- Source/Dafny/Parser.cs | 51 ++++++++++++------------ Source/Dafny/Scanner.cs | 102 +++++++++++++++++++++++++----------------------- 3 files changed, 81 insertions(+), 74 deletions(-) diff --git a/Source/Dafny/Dafny.atg b/Source/Dafny/Dafny.atg index 780d35ea..39c4c29d 100644 --- a/Source/Dafny/Dafny.atg +++ b/Source/Dafny/Dafny.atg @@ -1313,7 +1313,7 @@ SelectOrCallSuffix (. if (multipleIndices != null) { e = new MultiSelectExpr(x, e, multipleIndices); // make sure an array class with this dimensionality exists - UserDefinedType tmp = theBuiltIns.ArrayType(multipleIndices.Count, new IntType(), true); + UserDefinedType tmp = theBuiltIns.ArrayType(x, multipleIndices.Count, new IntType(), true); } else { if (!anyDots && e0 == null) { /* a parsing error occurred */ diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs index 26cb7fbb..70259973 100644 --- a/Source/Dafny/Parser.cs +++ b/Source/Dafny/Parser.cs @@ -25,7 +25,7 @@ public class Parser { const bool T = true; const bool x = false; const int minErrDist = 2; - + public Scanner/*!*/ scanner; public Errors/*!*/ errors; @@ -161,10 +161,10 @@ public static int Parse (string/*!*/ s, string/*!*/ filename, List= minErrDist) errors.SemErr(t, msg); errDist = 0; } - - public void SemErr(IToken/*!*/ tok, string/*!*/ msg) { - Contract.Requires(tok != null); - Contract.Requires(msg != null); + + public void SemErr(IToken/*!*/ tok, string/*!*/ msg) { + Contract.Requires(tok != null); + Contract.Requires(msg != null); errors.SemErr(tok, msg); } @@ -177,15 +177,15 @@ public static int Parse (string/*!*/ s, string/*!*/ filename, List theImports; @@ -2048,7 +2048,7 @@ List/*!*/ decreases) { if (multipleIndices != null) { e = new MultiSelectExpr(x, e, multipleIndices); // make sure an array class with this dimensionality exists - UserDefinedType tmp = theBuiltIns.ArrayType(multipleIndices.Count, new IntType(), true); + UserDefinedType tmp = theBuiltIns.ArrayType(x, multipleIndices.Count, new IntType(), true); } else { if (!anyDots && e0 == null) { /* a parsing error occurred */ @@ -2133,13 +2133,13 @@ List/*!*/ decreases) { public void Parse() { la = new Token(); - la.val = ""; + la.val = ""; Get(); Dafny(); - Expect(0); + Expect(0); } - + static readonly bool[,]/*!*/ set = { {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,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,x, x,x,T,x, T,x,x,x, x,T,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}, @@ -2166,18 +2166,20 @@ List/*!*/ decreases) { public class Errors { public int count = 0; // number of errors detected public System.IO.TextWriter/*!*/ errorStream = Console.Out; // error messages go to this stream - public string errMsgFormat = "{0}({1},{2}): error: {3}"; // 0=filename, 1=line, 2=column, 3=text - public string warningMsgFormat = "{0}({1},{2}): warning: {3}"; // 0=filename, 1=line, 2=column, 3=text - + public string errMsgFormat = "{0}({1},{2}): error: {3}"; // 0=filename, 1=line, 2=column, 3=text + public string warningMsgFormat = "{0}({1},{2}): warning: {3}"; // 0=filename, 1=line, 2=column, 3=text + public void SynErr(string filename, int line, int col, int n) { - SynErr(filename, line, col, GetSyntaxErrorString(n)); - } - public virtual void SynErr(string filename, int line, int col, string msg) { - Contract.Requires(msg != null); + SynErr(filename, line, col, GetSyntaxErrorString(n)); + } + + public virtual void SynErr(string filename, int line, int col, string/*!*/ msg) { + Contract.Requires(msg != null); errorStream.WriteLine(errMsgFormat, filename, line, col, msg); count++; - } - string GetSyntaxErrorString(int n) { + } + + string GetSyntaxErrorString(int n) { string s; switch (n) { case 0: s = "EOF expected"; break; @@ -2333,7 +2335,7 @@ public class Errors { default: s = "error " + n; break; } - return s; + return s; } public void SemErr(IToken/*!*/ tok, string/*!*/ msg) { // semantic errors @@ -2341,8 +2343,9 @@ public class Errors { Contract.Requires(msg != null); SemErr(tok.filename, tok.line, tok.col, msg); } + public virtual void SemErr(string filename, int line, int col, string/*!*/ msg) { - Contract.Requires(msg != null); + Contract.Requires(msg != null); errorStream.WriteLine(errMsgFormat, filename, line, col, msg); count++; } diff --git a/Source/Dafny/Scanner.cs b/Source/Dafny/Scanner.cs index 0af254f3..817df6cd 100644 --- a/Source/Dafny/Scanner.cs +++ b/Source/Dafny/Scanner.cs @@ -19,7 +19,7 @@ public class Buffer { // a) whole stream in buffer // b) part of stream in buffer // 2) non seekable stream (network, console) - + public const int EOF = 65535 + 1; // char.MaxValue + 1; const int MIN_BUFFER_LENGTH = 1024; // 1KB const int MAX_BUFFER_LENGTH = MIN_BUFFER_LENGTH * 64; // 64KB @@ -31,15 +31,17 @@ public class Buffer { Stream/*!*/ stream; // input stream (seekable) bool isUserStream; // was the stream opened by the user? -[ContractInvariantMethod] -void ObjectInvariant(){ - Contract.Invariant(buf != null); - Contract.Invariant(stream != null);} - [NotDelayed] - public Buffer (Stream/*!*/ s, bool isUserStream) :base() { + [ContractInvariantMethod] + void ObjectInvariant(){ + Contract.Invariant(buf != null); + Contract.Invariant(stream != null); + } + +// [NotDelayed] + public Buffer (Stream/*!*/ s, bool isUserStream) : base() { Contract.Requires(s != null); stream = s; this.isUserStream = isUserStream; - + int fl, bl; if (s.CanSeek) { fl = (int) s.Length; @@ -51,12 +53,12 @@ void ObjectInvariant(){ buf = new byte[(bl>0) ? bl : MIN_BUFFER_LENGTH]; fileLen = fl; bufLen = bl; - + if (fileLen > 0) Pos = 0; // setup buffer to position 0 (start) else bufPos = 0; // index 0 is already after the file, thus Pos = 0 is invalid if (bufLen == fileLen && s.CanSeek) Close(); } - + protected Buffer(Buffer/*!*/ b) { // called in UTF8Buffer constructor Contract.Requires(b != null); buf = b.buf; @@ -73,14 +75,14 @@ void ObjectInvariant(){ } ~Buffer() { Close(); } - + protected void Close() { if (!isUserStream && stream != null) { stream.Close(); //stream = null; } } - + public virtual int Read () { if (bufPos < bufLen) { return buf[bufPos++]; @@ -100,7 +102,7 @@ void ObjectInvariant(){ Pos = curPos; return ch; } - + public string/*!*/ GetString (int beg, int end) { Contract.Ensures(Contract.Result() != null); int len = 0; @@ -139,7 +141,7 @@ void ObjectInvariant(){ } } } - + // Read the next chunk of bytes from the stream, increases the buffer // if needed and updates the fields fileLen and bufLen. // Returns the number of bytes read. @@ -213,19 +215,20 @@ public class Scanner { const int noSym = 107; -[ContractInvariantMethod] -void objectInvariant(){ - Contract.Invariant(buffer!=null); - Contract.Invariant(t != null); - Contract.Invariant(start != null); - Contract.Invariant(tokens != null); - Contract.Invariant(pt != null); - Contract.Invariant(tval != null); - Contract.Invariant(Filename != null); - Contract.Invariant(errorHandler != null); -} + [ContractInvariantMethod] + void objectInvariant(){ + Contract.Invariant(buffer!=null); + Contract.Invariant(t != null); + Contract.Invariant(start != null); + Contract.Invariant(tokens != null); + Contract.Invariant(pt != null); + Contract.Invariant(tval != null); + Contract.Invariant(Filename != null); + Contract.Invariant(errorHandler != null); + } + public Buffer/*!*/ buffer; // scanner buffer - + Token/*!*/ t; // current token int ch; // current input character int pos; // byte position of current character @@ -236,13 +239,13 @@ void objectInvariant(){ Token/*!*/ tokens; // list of tokens already peeked (first token is a dummy) Token/*!*/ pt; // current peek token - + char[]/*!*/ tval = new char[128]; // text of current token int tlen; // length of current token - + private string/*!*/ Filename; private Errors/*!*/ errorHandler; - + static Scanner() { start = new Hashtable(128); for (int i = 39; i <= 39; ++i) start[i] = 1; @@ -291,9 +294,9 @@ void objectInvariant(){ start[Buffer.EOF] = -1; } - - [NotDelayed] - public Scanner (string/*!*/ fileName, Errors/*!*/ errorHandler) :base(){ + +// [NotDelayed] + public Scanner (string/*!*/ fileName, Errors/*!*/ errorHandler) : base() { Contract.Requires(fileName != null); Contract.Requires(errorHandler != null); this.errorHandler = errorHandler; @@ -303,15 +306,14 @@ void objectInvariant(){ Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); buffer = new Buffer(stream, false); Filename = fileName; - Init(); } catch (IOException) { throw new FatalError("Cannot open file " + fileName); } } - - [NotDelayed] - public Scanner (Stream/*!*/ s, Errors/*!*/ errorHandler, string/*!*/ fileName) :base(){ + +// [NotDelayed] + public Scanner (Stream/*!*/ s, Errors/*!*/ errorHandler, string/*!*/ fileName) : base() { Contract.Requires(s != null); Contract.Requires(errorHandler != null); Contract.Requires(fileName != null); @@ -320,10 +322,9 @@ void objectInvariant(){ buffer = new Buffer(s, true); this.errorHandler = errorHandler; this.Filename = fileName; - Init(); } - + void Init() { pos = -1; line = 1; col = 0; oldEols = 0; @@ -344,11 +345,11 @@ void objectInvariant(){ Contract.Ensures(Contract.Result() != null); int p = buffer.Pos; int ch = buffer.Read(); - // replace isolated '\r' by '\n' in order to make + // replace isolated '\r' by '\n' in order to make // eol handling uniform across Windows, Unix and Mac if (ch == '\r' && buffer.Peek() != '\n') ch = EOL; while (ch != EOL && ch != Buffer.EOF){ - ch = buffer.Read(); + ch = buffer.Read(); // replace isolated '\r' by '\n' in order to make // eol handling uniform across Windows, Unix and Mac if (ch == '\r' && buffer.Peek() != '\n') ch = EOL; @@ -359,7 +360,7 @@ void objectInvariant(){ } void NextCh() { - if (oldEols > 0) { ch = EOL; oldEols--; } + if (oldEols > 0) { ch = EOL; oldEols--; } else { // pos = buffer.Pos; // ch = buffer.Read(); col++; @@ -367,9 +368,9 @@ void objectInvariant(){ // // eol handling uniform across Windows, Unix and Mac // if (ch == '\r' && buffer.Peek() != '\n') ch = EOL; // if (ch == EOL) { line++; col = 0; } - + while (true) { - pos = buffer.Pos; + pos = buffer.Pos; ch = buffer.Read(); col++; // replace isolated '\r' by '\n' in order to make // eol handling uniform across Windows, Unix and Mac @@ -419,7 +420,7 @@ void objectInvariant(){ return; } - + } } @@ -558,10 +559,13 @@ void objectInvariant(){ t.pos = pos; t.col = col; t.line = line; t.filename = this.Filename; int state; - if (start.ContainsKey(ch)) { state = (int) cce.NonNull( start[ch]); } + if (start.ContainsKey(ch)) { + Contract.Assert(start[ch] != null); + state = (int) start[ch]; + } else { state = 0; } tlen = 0; AddCh(); - + switch (state) { case -1: { t.kind = eofSym; break; } // NextCh already done case 0: { @@ -769,14 +773,14 @@ void objectInvariant(){ t.val = new String(tval, 0, tlen); return t; } - + private void SetScannerBehindT() { buffer.Pos = t.pos; NextCh(); line = t.line; col = t.col; for (int i = 0; i < tlen; i++) NextCh(); } - + // get the next token (possibly a token already seen during peeking) public Token/*!*/ Scan () { Contract.Ensures(Contract.Result() != null); @@ -797,7 +801,7 @@ void objectInvariant(){ } pt = pt.next; } while (pt.kind > maxT); // skip pragmas - + return pt; } -- cgit v1.2.3 From 1003fe081685669ad7d3fe088c99fc072980d562 Mon Sep 17 00:00:00 2001 From: CodeplexBot Date: Sat, 14 May 2011 07:06:08 +0200 Subject: Boogie build failed --- _admin/Boogie/aste/summary.log | 69 ++++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/_admin/Boogie/aste/summary.log b/_admin/Boogie/aste/summary.log index d5fb6585..b837237b 100644 --- a/_admin/Boogie/aste/summary.log +++ b/_admin/Boogie/aste/summary.log @@ -1,23 +1,50 @@ -# Aste started: 2011-05-04 07:00:19 +# Aste started: 2011-05-14 07:00:16 # Host id: Boogiebox -# [2011-05-04 07:02:49] SpecSharp revision: 6e9f7e851bd3 -# [2011-05-04 07:02:49] SscBoogie revision: 6e9f7e851bd3 -# [2011-05-04 07:04:02] Boogie revision: e7280be476fa -[2011-05-04 07:06:38] C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.com Boogie.sln /Rebuild Checked +# [2011-05-14 07:02:49] SpecSharp revision: 5fb7af71cc82 +# [2011-05-14 07:02:49] SscBoogie revision: 5fb7af71cc82 +# [2011-05-14 07:04:08] Boogie revision: 76b4d17abc74 +[2011-05-14 07:06:08] [Error] C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.com Boogie.sln /Rebuild Checked D:\Temp\aste\Boogie\Source\Core\AbsyType.cs(823,16): warning CS0659: 'Microsoft.Boogie.BasicType' overrides Object.Equals(object o) but does not override Object.GetHashCode() D:\Temp\aste\Boogie\Source\Core\AbsyType.cs(2802,16): warning CS0659: 'Microsoft.Boogie.CtorType' overrides Object.Equals(object o) but does not override Object.GetHashCode() D:\Temp\aste\Boogie\Source\Core\OOLongUtil.cs(109,7): warning CS0162: Unreachable code detected - D:\Temp\aste\Boogie\Source\Core\Absy.cs(686,7): warning CC1036: Detected call to method 'Graphing.Graph`1.TopologicalSort' without [Pure] in contracts of method 'Microsoft.Boogie.Program.GraphFromImpl(Microsoft.Boogie.Implementation)'. + D:\Temp\aste\Boogie\Source\Core\ResolutionContext.cs(173,7): error CC1016: Contract.Assert/Contract.Assume cannot be used in contract section. Use only Requires and Ensures. + D:\Temp\aste\Boogie\Source\Core\Absy.cs(682,7): warning CC1036: Detected call to method 'Graphing.Graph`1.TopologicalSort' without [Pure] in contracts of method 'Microsoft.Boogie.Program.GraphFromImpl(Microsoft.Boogie.Implementation)'. D:\Temp\aste\Boogie\Source\Core\Parser.cs(109,3): warning CC1032: Method 'Microsoft.Boogie.Parser+BvBounds.Resolve(Microsoft.Boogie.ResolutionContext)' overrides 'Microsoft.Boogie.Absy.Resolve(Microsoft.Boogie.ResolutionContext)', thus cannot add Requires. D:\Temp\aste\Boogie\Source\Core\Parser.cs(114,5): warning CC1032: Method 'Microsoft.Boogie.Parser+BvBounds.Emit(Microsoft.Boogie.TokenTextWriter,System.Int32,System.Boolean)' overrides 'Microsoft.Boogie.Expr.Emit(Microsoft.Boogie.TokenTextWriter,System.Int32,System.Boolean)', thus cannot add Requires. D:\Temp\aste\Boogie\Source\Core\Parser.cs(117,65): warning CC1032: Method 'Microsoft.Boogie.Parser+BvBounds.ComputeFreeVariables(Microsoft.Boogie.Set)' overrides 'Microsoft.Boogie.Expr.ComputeFreeVariables(Microsoft.Boogie.Set)', thus cannot add Requires. - D:\Temp\aste\Boogie\Source\AbsInt\ExprFactories.cs(111,7): warning CS0162: Unreachable code detected - D:\Temp\aste\Boogie\Source\AbsInt\ExprFactories.cs(290,7): warning CS0162: Unreachable code detected - D:\Temp\aste\Boogie\Source\AbsInt\ExprFactories.cs(309,7): warning CS0162: Unreachable code detected - D:\Temp\aste\Boogie\Source\VCGeneration\VC.cs(1662,11): warning CS0162: Unreachable code detected - D:\Temp\aste\Boogie\Source\VCGeneration\VC.cs(1822,11): warning CS0162: Unreachable code detected - D:\Temp\aste\Boogie\Source\VCGeneration\StratifiedVC.cs(662,17): warning CC1032: Method 'VC.StratifiedVCGen+NormalChecker.CheckVC' overrides 'VC.StratifiedVCGen+StratifiedCheckerInterface.CheckVC', thus cannot add Requires. + C:\Program Files (x86)\Microsoft\Contracts\MsBuild\v4.0\Microsoft.CodeContracts.targets(215,5): error MSB3073: The command ""C:\Program Files (x86)\Microsoft\Contracts\Bin\ccrewrite" /level:4 /rewrite "/assemblyMode=standard" /throwonfailure /libpaths:"D:\Temp\aste\Boogie\Source\AIFramework\bin\Checked\;D:\Temp\aste\Boogie\Source\Basetypes\bin\Checked\;D:\Temp\aste\Boogie\Source\CodeContractsExtender\bin\Checked\;D:\Temp\aste\Boogie\Source\Graph\bin\Checked\;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\Profile\Client\;D:\Temp\aste\Boogie\Source\ParserHelper\bin\Checked\;D:\Temp\aste\Boogie\Source\AIFramework\bin\Checked\CodeContracts;D:\Temp\aste\Boogie\Source\Basetypes\bin\Checked\CodeContracts;D:\Temp\aste\Boogie\Source\CodeContractsExtender\bin\Checked\CodeContracts;D:\Temp\aste\Boogie\Source\Graph\bin\Checked\CodeContracts;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\Profile\Client\CodeContracts;D:\Temp\aste\Boogie\Source\ParserHelper\bin\Checked\CodeContracts;C:\Program Files (x86)\Microsoft\Contracts\Contracts\.NETFramework\v4.0 " "Core.dll"" exited with code 1. + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Core\bin\Checked\Core.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Core\bin\Checked\Core.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCExpr\bin\Checked\VCExpr.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Core\bin\Checked\Core.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCGeneration\bin\Checked\VCGeneration.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCExpr\bin\Checked\VCExpr.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Core\bin\Checked\Core.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCGeneration\bin\Checked\VCGeneration.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCExpr\bin\Checked\VCExpr.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Core\bin\Checked\Core.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCGeneration\bin\Checked\VCGeneration.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCExpr\bin\Checked\VCExpr.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Provers\Simplify\bin\Checked\Provers.Simplify.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Core\bin\Checked\Core.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCGeneration\bin\Checked\VCGeneration.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCExpr\bin\Checked\VCExpr.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Provers\Simplify\bin\Checked\Provers.Simplify.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Core\bin\Checked\Core.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCGeneration\bin\Checked\VCGeneration.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCExpr\bin\Checked\VCExpr.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Provers\Simplify\bin\Checked\Provers.Simplify.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\AbsInt\bin\Checked\AbsInt.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Core\bin\Checked\Core.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCGeneration\bin\Checked\VCGeneration.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Provers\Z3\bin\Checked\Provers.Z3.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Provers\TPTP\bin\Checked\Provers.TPTP.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Provers\Isabelle\bin\Checked\Provers.Isabelle.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Provers\Simplify\bin\Checked\Provers.Simplify.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Provers\SMTLib\bin\Checked\Provers.SMTLib.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\AbsInt\bin\Checked\AbsInt.dll' could not be found + error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Core\bin\Checked\Core.dll' could not be found warning CS0659: 'Microsoft.Boogie.BasicType' overrides Object.Equals(object o) but does not override Object.GetHashCode() warning CS0659: 'Microsoft.Boogie.CtorType' overrides Object.Equals(object o) but does not override Object.GetHashCode() warning CS0162: Unreachable code detected @@ -25,11 +52,13 @@ warning CC1032: Method 'Microsoft.Boogie.Parser+BvBounds.Resolve(Microsoft.Boogie.ResolutionContext)' overrides 'Microsoft.Boogie.Absy.Resolve(Microsoft.Boogie.ResolutionContext)', thus cannot add Requires. warning CC1032: Method 'Microsoft.Boogie.Parser+BvBounds.Emit(Microsoft.Boogie.TokenTextWriter,System.Int32,System.Boolean)' overrides 'Microsoft.Boogie.Expr.Emit(Microsoft.Boogie.TokenTextWriter,System.Int32,System.Boolean)', thus cannot add Requires. warning CC1032: Method 'Microsoft.Boogie.Parser+BvBounds.ComputeFreeVariables(Microsoft.Boogie.Set)' overrides 'Microsoft.Boogie.Expr.ComputeFreeVariables(Microsoft.Boogie.Set)', thus cannot add Requires. - warning CS0162: Unreachable code detected - warning CS0162: Unreachable code detected - warning CS0162: Unreachable code detected - warning CS0162: Unreachable code detected - warning CS0162: Unreachable code detected - warning CC1032: Method 'VC.StratifiedVCGen+NormalChecker.CheckVC' overrides 'VC.StratifiedVCGen+StratifiedCheckerInterface.CheckVC', thus cannot add Requires. -[2011-05-04 07:48:22] 0 out of 30 test(s) in D:\Temp\aste\Boogie\Test\alltests.txt failed -# [2011-05-04 07:49:23] Released nightly of Boogie + 1 error + 1 error + 2 error + 3 error + 3 error + 4 error + 4 error + 5 error + 8 error + 10 failed -- cgit v1.2.3 From 5d983730fba0b12bd5520be075e1a46d48b644a3 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Sat, 14 May 2011 01:35:57 -0700 Subject: Dafny: fixed bug in quantifier bounds discovery --- Source/Dafny/Resolver.cs | 106 ++++++++++++++++++++++++----------------------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 37b80894..6c2f6c38 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -2398,12 +2398,11 @@ namespace Microsoft.Dafny { } var e0 = c.E0; var e1 = c.E1; - var op = c.ResolvedOp; - int whereIsBv = SanitizeForBoundDiscovery(e.BoundVars, j, ref op, ref e0, ref e1); + int whereIsBv = SanitizeForBoundDiscovery(e.BoundVars, j, c.ResolvedOp, ref e0, ref e1); if (whereIsBv < 0) { goto CHECK_NEXT_CONJUNCT; } - switch (op) { + switch (c.ResolvedOp) { case BinaryExpr.ResolvedOpcode.InSet: if (whereIsBv == 0) { bounds.Add(new QuantifierExpr.SetBoundedPool(e1)); @@ -2412,7 +2411,7 @@ namespace Microsoft.Dafny { break; case BinaryExpr.ResolvedOpcode.InSeq: if (whereIsBv == 0) { - bounds.Add(new QuantifierExpr.SetBoundedPool(e1)); + bounds.Add(new QuantifierExpr.SeqBoundedPool(e1)); goto CHECK_NEXT_BOUND_VARIABLE; } break; @@ -2425,7 +2424,7 @@ namespace Microsoft.Dafny { break; case BinaryExpr.ResolvedOpcode.Gt: case BinaryExpr.ResolvedOpcode.Ge: - Contract.Assert(false); throw new cce.UnreachableException(); // promised by postconditions of NormalizedConjunct and SanitizeForBoundDiscovery + Contract.Assert(false); throw new cce.UnreachableException(); // promised by postconditions of NormalizedConjunct case BinaryExpr.ResolvedOpcode.Lt: if (whereIsBv == 0 && upperBound == null) { upperBound = e1; // bv < E @@ -2462,14 +2461,13 @@ namespace Microsoft.Dafny { } /// - /// If the return value is negative, the resulting "op", "e0", and "e1" should not be used. + /// If the return value is negative, the resulting "e0" and "e1" should not be used. /// Otherwise, the following is true on return: /// The new "e0 op e1" is equivalent to the old "e0 op e1". /// One of "e0" and "e1" is the identifier "boundVars[bvi]"; the return value is either 0 or 1, and indicates which. /// The other of "e0" and "e1" is an expression whose free variables are not among "boundVars[bvi..]". - /// Requires the initial value of "op" not to be Gt or Ge, and ensures the same about the final value of "op". /// - int SanitizeForBoundDiscovery(List boundVars, int bvi, ref BinaryExpr.ResolvedOpcode op, ref Expression e0, ref Expression e1) + int SanitizeForBoundDiscovery(List boundVars, int bvi, BinaryExpr.ResolvedOpcode op, ref Expression e0, ref Expression e1) { Contract.Requires(e0 != null); Contract.Requires(e1 != null); @@ -2499,54 +2497,57 @@ namespace Microsoft.Dafny { } // Next, clean up the side where bv is by adjusting both sides of the expression - while (true) { - switch (op) { - case BinaryExpr.ResolvedOpcode.EqCommon: - case BinaryExpr.ResolvedOpcode.NeqCommon: - case BinaryExpr.ResolvedOpcode.Gt: - case BinaryExpr.ResolvedOpcode.Ge: - case BinaryExpr.ResolvedOpcode.Le: - case BinaryExpr.ResolvedOpcode.Lt: + switch (op) { + case BinaryExpr.ResolvedOpcode.EqCommon: + case BinaryExpr.ResolvedOpcode.NeqCommon: + case BinaryExpr.ResolvedOpcode.Gt: + case BinaryExpr.ResolvedOpcode.Ge: + case BinaryExpr.ResolvedOpcode.Le: + case BinaryExpr.ResolvedOpcode.Lt: + // Repeatedly move additive or subtractive terms from thisSide to thatSide + while (true) { var bin = thisSide as BinaryExpr; - if (bin != null) { - if (bin.ResolvedOp == BinaryExpr.ResolvedOpcode.Add) { - // Change "A+B op C" into either "A op C-B" or "B op C-A", depending on where we find bv among A and B. - if (!FreeVariables(bin.E1).Contains(bv)) { - thisSide = bin.E0; - thatSide = new BinaryExpr(bin.tok, BinaryExpr.Opcode.Sub, thatSide, bin.E1); - } else { - thisSide = bin.E1; - thatSide = new BinaryExpr(bin.tok, BinaryExpr.Opcode.Sub, thatSide, bin.E0); - } + if (bin == null) { + break; // done simplifying + + } else if (bin.ResolvedOp == BinaryExpr.ResolvedOpcode.Add) { + // Change "A+B op C" into either "A op C-B" or "B op C-A", depending on where we find bv among A and B. + if (!FreeVariables(bin.E1).Contains(bv)) { + thisSide = bin.E0; + thatSide = new BinaryExpr(bin.tok, BinaryExpr.Opcode.Sub, thatSide, bin.E1); + } else { + thisSide = bin.E1; + thatSide = new BinaryExpr(bin.tok, BinaryExpr.Opcode.Sub, thatSide, bin.E0); + } + ((BinaryExpr)thatSide).ResolvedOp = BinaryExpr.ResolvedOpcode.Sub; + thatSide.Type = bin.Type; + + } else if (bin.ResolvedOp == BinaryExpr.ResolvedOpcode.Sub) { + // Change "A-B op C" in a similar way. + if (!FreeVariables(bin.E1).Contains(bv)) { + // change to "A op C+B" + thisSide = bin.E0; + thatSide = new BinaryExpr(bin.tok, BinaryExpr.Opcode.Add, thatSide, bin.E1); + ((BinaryExpr)thatSide).ResolvedOp = BinaryExpr.ResolvedOpcode.Add; + } else { + // In principle, change to "-B op C-A" and then to "B dualOp A-C". But since we don't want + // to change "op", we instead end with "A-C op B" and switch the mapping of thisSide/thatSide + // to e0/e1 (by inverting "whereIsBv"). + thisSide = bin.E1; + thatSide = new BinaryExpr(bin.tok, BinaryExpr.Opcode.Sub, bin.E0, thatSide); ((BinaryExpr)thatSide).ResolvedOp = BinaryExpr.ResolvedOpcode.Sub; - thatSide.Type = bin.Type; - continue; // continue simplifying - - } else if (bin.ResolvedOp == BinaryExpr.ResolvedOpcode.Sub) { - // Change "A-B op C" in a similar way. - if (!FreeVariables(bin.E1).Contains(bv)) { - // change to "A op C+B" - thisSide = bin.E0; - thatSide = new BinaryExpr(bin.tok, BinaryExpr.Opcode.Add, thatSide, bin.E1); - ((BinaryExpr)thatSide).ResolvedOp = BinaryExpr.ResolvedOpcode.Add; - } else { - // In principle, change to "-B op C-A" and then to "B dualOp A-C". But since we don't want - // to return with the operator being > or >=, we instead end with "A-C op B" and switch the - // mapping of thisSide/thatSide to e0/e1 (by inverting "whereIsBv"). - thisSide = bin.E1; - thatSide = new BinaryExpr(bin.tok, BinaryExpr.Opcode.Sub, bin.E0, thatSide); - ((BinaryExpr)thatSide).ResolvedOp = BinaryExpr.ResolvedOpcode.Sub; - whereIsBv = 1 - whereIsBv; - } - thatSide.Type = bin.Type; - continue; // continue simplifying + whereIsBv = 1 - whereIsBv; } + thatSide.Type = bin.Type; + + } else { + break; // done simplifying } - break; // done simplifying - default: - break; // done simplifying - } - break; // done simplifying + } + break; + + default: + break; } // Now, see if the interesting side is simply bv itself @@ -2675,6 +2676,7 @@ namespace Microsoft.Dafny { b.ResolvedOp = newROp; b.Type = Type.Bool; yield return b; + yield break; } } JUST_RETURN_IT: ; -- cgit v1.2.3 From 904fcded9ae0a3219ccccf7c62f116b332deb9ac Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Sun, 15 May 2011 22:02:08 -0700 Subject: Dafny: added optional range expressions to logical quantifiers, preparing for addition other other comprehensions (like set comprehension) --- Source/Dafny/Compiler.cs | 2 +- Source/Dafny/Dafny.atg | 8 +++- Source/Dafny/DafnyAst.cs | 73 +++++++++++++++++++++++++++++-------- Source/Dafny/Parser.cs | 9 ++++- Source/Dafny/Printer.cs | 9 ++++- Source/Dafny/Resolver.cs | 19 +++++++--- Source/Dafny/Translator.cs | 33 ++++++++++------- Test/dafny0/Answer | 31 +++++++++++----- Test/dafny0/NonGhostQuantifiers.dfy | 17 +++++++++ Test/dafny0/SmallTests.dfy | 36 ++++++++++++++++++ 10 files changed, 185 insertions(+), 52 deletions(-) diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index 9495d64e..4d352f65 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -1335,7 +1335,7 @@ namespace Microsoft.Dafny { wr.Write("{0}, ", expr is ForallExpr ? "true" : "false"); wr.Write("@{0} => ", bv.Name); } - TrExpr(e.Body); + TrExpr(e.LogicalBody()); for (int i = 0; i < n; i++) { wr.Write(")"); } diff --git a/Source/Dafny/Dafny.atg b/Source/Dafny/Dafny.atg index 39c4c29d..0ce1835f 100644 --- a/Source/Dafny/Dafny.atg +++ b/Source/Dafny/Dafny.atg @@ -1361,6 +1361,7 @@ QuantifierGuts List bvars = new List(); Attributes attrs = null; Triggers trigs = null; + Expression range = null; Expression/*!*/ body; .) ( Forall (. x = t; univ = true; .) @@ -1372,12 +1373,15 @@ QuantifierGuts IdentTypeOptional (. bvars.Add(bv); parseVarScope.Push(bv.Name, bv.Name); .) } { AttributeOrTrigger } + [ "|" + Expression + ] QSep Expression (. if (univ) { - q = new ForallExpr(x, bvars, body, trigs, attrs); + q = new ForallExpr(x, bvars, range, body, trigs, attrs); } else { - q = new ExistsExpr(x, bvars, body, trigs, attrs); + q = new ExistsExpr(x, bvars, range, body, trigs, attrs); } parseVarScope.PopMarker(); .) diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index f76f24de..ad7f5fb6 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -2300,17 +2300,23 @@ namespace Microsoft.Dafny { } } - public abstract class QuantifierExpr : Expression { + /// + /// A ComprehensionExpr has the form: + /// BINDER x Attributes | Range(x) :: Term(x) + /// where "Attributes" is optional, and "| Range(x)" is optional and defaults to "true". + /// Currently, BINDER is one of the logical quantifiers "exists" or "forall". + /// + public abstract class ComprehensionExpr : Expression { public readonly List/*!*/ BoundVars; - public readonly Expression/*!*/ Body; + public readonly Expression Range; + public readonly Expression/*!*/ Term; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(BoundVars != null); - Contract.Invariant(Body != null); + Contract.Invariant(Term != null); } - public readonly Triggers Trigs; public readonly Attributes Attributes; public abstract class BoundedPool { } @@ -2338,23 +2344,40 @@ namespace Microsoft.Dafny { } public List Bounds; // initialized and filled in by resolver - // invariant bounds == null || bounds.Count == BoundVars.Count; + // invariant Bounds == null || Bounds.Count == BoundVars.Count; - public QuantifierExpr(IToken/*!*/ tok, List/*!*/ bvars, Expression/*!*/ body, Triggers trigs, Attributes attrs) + public ComprehensionExpr(IToken/*!*/ tok, List/*!*/ bvars, Expression/*!*/ range, Expression/*!*/ term, Attributes attrs) : base(tok) { Contract.Requires(tok != null); Contract.Requires(cce.NonNullElements(bvars)); - Contract.Requires(body != null); + Contract.Requires(term != null); this.BoundVars = bvars; - this.Body = body; - this.Trigs = trigs; + this.Range = range; + this.Term = term; this.Attributes = attrs; } public override IEnumerable SubExpressions { - get { yield return Body; } + get { + if (Range != null) { yield return Range; } + yield return Term; + } + } + } + + public abstract class QuantifierExpr : ComprehensionExpr { + public readonly Triggers Trigs; + + public QuantifierExpr(IToken/*!*/ tok, List/*!*/ bvars, Expression/*!*/ range, Expression/*!*/ term, Triggers trigs, Attributes attrs) + : base(tok, bvars, range, term, attrs) { + Contract.Requires(tok != null); + Contract.Requires(cce.NonNullElements(bvars)); + Contract.Requires(term != null); + + this.Trigs = trigs; } + public abstract Expression/*!*/ LogicalBody(); } public class Triggers { @@ -2373,20 +2396,38 @@ namespace Microsoft.Dafny { } public class ForallExpr : QuantifierExpr { - public ForallExpr(IToken tok, List/*!*/ bvars, Expression body, Triggers trig, Attributes attrs) - : base(tok, bvars, body, trig, attrs) { + public ForallExpr(IToken tok, List/*!*/ bvars, Expression range, Expression term, Triggers trig, Attributes attrs) + : base(tok, bvars, range, term, trig, attrs) { Contract.Requires(cce.NonNullElements(bvars)); Contract.Requires(tok != null); - Contract.Requires(body != null); + Contract.Requires(term != null); + } + public override Expression/*!*/ LogicalBody() { + if (Range == null) { + return Term; + } + var body = new BinaryExpr(Term.tok, BinaryExpr.Opcode.Imp, Range, Term); + body.ResolvedOp = BinaryExpr.ResolvedOpcode.Imp; + body.Type = Term.Type; + return body; } } public class ExistsExpr : QuantifierExpr { - public ExistsExpr(IToken tok, List/*!*/ bvars, Expression body, Triggers trig, Attributes attrs) - : base(tok, bvars, body, trig, attrs) { + public ExistsExpr(IToken tok, List/*!*/ bvars, Expression range, Expression term, Triggers trig, Attributes attrs) + : base(tok, bvars, range, term, trig, attrs) { Contract.Requires(cce.NonNullElements(bvars)); Contract.Requires(tok != null); - Contract.Requires(body != null); + Contract.Requires(term != null); + } + public override Expression/*!*/ LogicalBody() { + if (Range == null) { + return Term; + } + var body = new BinaryExpr(Term.tok, BinaryExpr.Opcode.And, Range, Term); + body.ResolvedOp = BinaryExpr.ResolvedOpcode.And; + body.Type = Term.Type; + return body; } } diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs index 70259973..983996d1 100644 --- a/Source/Dafny/Parser.cs +++ b/Source/Dafny/Parser.cs @@ -1920,6 +1920,7 @@ List/*!*/ decreases) { List bvars = new List(); Attributes attrs = null; Triggers trigs = null; + Expression range = null; Expression/*!*/ body; if (la.kind == 101 || la.kind == 102) { @@ -1940,12 +1941,16 @@ List/*!*/ decreases) { while (la.kind == 7) { AttributeOrTrigger(ref attrs, ref trigs); } + if (la.kind == 17) { + Get(); + Expression(out range); + } QSep(); Expression(out body); if (univ) { - q = new ForallExpr(x, bvars, body, trigs, attrs); + q = new ForallExpr(x, bvars, range, body, trigs, attrs); } else { - q = new ExistsExpr(x, bvars, body, trigs, attrs); + q = new ExistsExpr(x, bvars, range, body, trigs, attrs); } parseVarScope.PopMarker(); diff --git a/Source/Dafny/Printer.cs b/Source/Dafny/Printer.cs index 003a977c..6dcf0020 100644 --- a/Source/Dafny/Printer.cs +++ b/Source/Dafny/Printer.cs @@ -931,14 +931,19 @@ namespace Microsoft.Dafny { wr.Write(" "); PrintAttributes(e.Attributes); PrintTriggers(e.Trigs); + if (e.Range != null) { + wr.Write("| "); + PrintExpression(e.Range); + wr.Write(" "); + } wr.Write(":: "); if (0 <= indent) { int ind = indent + IndentAmount; wr.WriteLine(); Indent(ind); - PrintExpression(e.Body, ind); + PrintExpression(e.Term, ind); } else { - PrintExpression(e.Body); + PrintExpression(e.Term); } if (parensNeeded) { wr.Write(")"); } diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index 6c2f6c38..c7deb42f 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -2227,10 +2227,17 @@ namespace Microsoft.Dafny { } ResolveType(v.tok, v.Type); } - ResolveExpression(e.Body, twoState, specContext); - Contract.Assert(e.Body.Type != null); // follows from postcondition of ResolveExpression - if (!UnifyTypes(e.Body.Type, Type.Bool)) { - Error(expr, "body of quantifier must be of type bool (instead got {0})", e.Body.Type); + if (e.Range != null) { + ResolveExpression(e.Range, twoState, specContext); + Contract.Assert(e.Range.Type != null); // follows from postcondition of ResolveExpression + if (!UnifyTypes(e.Range.Type, Type.Bool)) { + Error(expr, "range of quantifier must be of type bool (instead got {0})", e.Range.Type); + } + } + ResolveExpression(e.Term, twoState, specContext); + Contract.Assert(e.Term.Type != null); // follows from postcondition of ResolveExpression + if (!UnifyTypes(e.Term.Type, Type.Bool)) { + Error(expr, "body of quantifier must be of type bool (instead got {0})", e.Term.Type); } // Since the body is more likely to infer the types of the bound variables, resolve it // first (above) and only then resolve the attributes and triggers (below). @@ -2391,7 +2398,7 @@ namespace Microsoft.Dafny { // Go through the conjuncts of the range expression look for bounds. Expression lowerBound = bv.Type is NatType ? new LiteralExpr(bv.tok, new BigInteger(0)) : null; Expression upperBound = null; - foreach (var conjunct in NormalizedConjuncts(e.Body, e is ExistsExpr)) { + foreach (var conjunct in NormalizedConjuncts(e.LogicalBody(), e is ExistsExpr)) { var c = conjunct as BinaryExpr; if (c == null) { goto CHECK_NEXT_CONJUNCT; @@ -2715,7 +2722,7 @@ namespace Microsoft.Dafny { } else if (expr is QuantifierExpr) { var e = (QuantifierExpr)expr; - var s = FreeVariables(e.Body); + var s = FreeVariables(e.LogicalBody()); foreach (var bv in e.BoundVars) { s.Remove(bv); } diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 557fcaf0..253a1f9b 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -1511,7 +1511,7 @@ namespace Microsoft.Dafny { return z == null ? r : BplAnd(r, z); } else if (expr is QuantifierExpr) { QuantifierExpr e = (QuantifierExpr)expr; - Bpl.Expr total = IsTotal(e.Body, etran); + Bpl.Expr total = IsTotal(e.LogicalBody(), etran); if (total != Bpl.Expr.True) { Bpl.VariableSeq bvars = new Bpl.VariableSeq(); Bpl.Expr typeAntecedent = etran.TrBoundVariables(e.BoundVars, bvars); @@ -1638,7 +1638,7 @@ namespace Microsoft.Dafny { return BplAnd(t0, t1); } else if (expr is QuantifierExpr) { QuantifierExpr e = (QuantifierExpr)expr; - Bpl.Expr total = CanCallAssumption(e.Body, etran); + Bpl.Expr total = CanCallAssumption(e.LogicalBody(), etran); if (total != Bpl.Expr.True) { Bpl.VariableSeq bvars = new Bpl.VariableSeq(); Bpl.Expr typeAntecedent = etran.TrBoundVariables(e.BoundVars, bvars); @@ -1986,7 +1986,7 @@ namespace Microsoft.Dafny { builder.Add(new Bpl.AssumeCmd(bv.tok, wh)); } } - Expression body = Substitute(e.Body, null, substMap); + Expression body = Substitute(e.LogicalBody(), null, substMap); CheckWellformed(body, options, locals, builder, etran); } else if (expr is ITEExpr) { @@ -4415,13 +4415,17 @@ namespace Microsoft.Dafny { } tr = new Bpl.Trigger(expr.tok, true, tt, tr); } - Bpl.Expr body = TrExpr(e.Body); + var antecedent = typeAntecedent; + if (e.Range != null) { + antecedent = Bpl.Expr.And(antecedent, TrExpr(e.Range)); + } + Bpl.Expr body = TrExpr(e.Term); if (e is ForallExpr) { - return new Bpl.ForallExpr(expr.tok, new Bpl.TypeVariableSeq(), bvars, kv, tr, Bpl.Expr.Imp(typeAntecedent, body)); + return new Bpl.ForallExpr(expr.tok, new Bpl.TypeVariableSeq(), bvars, kv, tr, Bpl.Expr.Imp(antecedent, body)); } else { Contract.Assert(e is ExistsExpr); - return new Bpl.ExistsExpr(expr.tok, new Bpl.TypeVariableSeq(), bvars, kv, tr, Bpl.Expr.And(typeAntecedent, body)); + return new Bpl.ExistsExpr(expr.tok, new Bpl.TypeVariableSeq(), bvars, kv, tr, Bpl.Expr.And(antecedent, body)); } } else if (expr is ITEExpr) { @@ -5220,7 +5224,7 @@ namespace Microsoft.Dafny { substMap.Add(n, ieK); } - Expression bodyK = Substitute(e.Body, null, substMap); + Expression bodyK = Substitute(e.LogicalBody(), null, substMap); Bpl.Expr less = DecreasesCheck(toks, types, kk, nn, etran, null, null, false, true); @@ -5252,7 +5256,7 @@ namespace Microsoft.Dafny { typeAntecedent = etran.TrBoundVariables(e.BoundVars, bvars); foreach (var kase in caseProduct) { var ante = BplAnd(BplAnd(typeAntecedent, ih), kase); - var bdy = etran.LayerOffset(1).TrExpr(e.Body); + var bdy = etran.LayerOffset(1).TrExpr(e.LogicalBody()); Bpl.Expr q = new Bpl.ForallExpr(kase.tok, bvars, Bpl.Expr.Imp(ante, bdy)); splits.Add(new SplitExprInfo(false, q)); } @@ -5344,7 +5348,7 @@ namespace Microsoft.Dafny { // consider automatically applying induction var inductionVariables = new List(); foreach (var n in e.BoundVars) { - if (VarOccursInArgumentToRecursiveFunction(e.Body, n, null)) { + if (VarOccursInArgumentToRecursiveFunction(e.LogicalBody(), n, null)) { if (CommandLineOptions.Clo.Trace) { Console.Write("Applying automatic induction on variable '{0}' of: ", n.Name); new Printer(Console.Out).PrintExpression(e); @@ -5447,7 +5451,7 @@ namespace Microsoft.Dafny { VarOccursInArgumentToRecursiveFunction(e.E1, n, p); } else if (expr is QuantifierExpr) { var e = (QuantifierExpr)expr; - return VarOccursInArgumentToRecursiveFunction(e.Body, n, null); + return VarOccursInArgumentToRecursiveFunction(e.LogicalBody(), n, null); } else if (expr is ITEExpr) { var e = (ITEExpr)expr; return VarOccursInArgumentToRecursiveFunction(e.Test, n, null) || // test is not "elevated" @@ -5617,14 +5621,15 @@ namespace Microsoft.Dafny { } else if (expr is QuantifierExpr) { QuantifierExpr e = (QuantifierExpr)expr; - Expression newBody = Substitute(e.Body, receiverReplacement, substMap); + Expression newRange = e.Range == null ? null : Substitute(e.Range, receiverReplacement, substMap); + Expression newTerm = Substitute(e.Term, receiverReplacement, substMap); Triggers newTrigs = SubstTriggers(e.Trigs, receiverReplacement, substMap); Attributes newAttrs = SubstAttributes(e.Attributes, receiverReplacement, substMap); - if (newBody != e.Body || newTrigs != e.Trigs || newAttrs != e.Attributes) { + if (newRange != e.Range || newTerm != e.Term || newTrigs != e.Trigs || newAttrs != e.Attributes) { if (expr is ForallExpr) { - newExpr = new ForallExpr(expr.tok, e.BoundVars, newBody, newTrigs, newAttrs); + newExpr = new ForallExpr(expr.tok, e.BoundVars, newRange, newTerm, newTrigs, newAttrs); } else { - newExpr = new ExistsExpr(expr.tok, e.BoundVars, newBody, newTrigs, newAttrs); + newExpr = new ExistsExpr(expr.tok, e.BoundVars, newRange, newTerm, newTrigs, newAttrs); } } diff --git a/Test/dafny0/Answer b/Test/dafny0/Answer index 4bdea760..070aa6be 100644 --- a/Test/dafny0/Answer +++ b/Test/dafny0/Answer @@ -198,8 +198,18 @@ Execution trace: (0,0): anon0 SmallTests.dfy(267,19): anon3_Else (0,0): anon2 +SmallTests.dfy(307,3): Error BP5003: A postcondition might not hold on this return path. +SmallTests.dfy(301,11): Related location: This is the postcondition that might not hold. +Execution trace: + (0,0): anon0 + (0,0): anon18_Else + (0,0): anon11 + (0,0): anon23_Then + (0,0): anon24_Then + (0,0): anon15 + (0,0): anon25_Else -Dafny program verifier finished with 34 verified, 13 errors +Dafny program verifier finished with 39 verified, 14 errors -------------------- Definedness.dfy -------------------- Definedness.dfy(8,7): Error: possible division by zero @@ -464,14 +474,17 @@ Dafny program verifier finished with 8 verified, 2 errors -------------------- NonGhostQuantifiers.dfy -------------------- NonGhostQuantifiers.dfy(13,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'n' -NonGhostQuantifiers.dfy(57,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'i' -NonGhostQuantifiers.dfy(61,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'j' -NonGhostQuantifiers.dfy(71,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'j' -NonGhostQuantifiers.dfy(86,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'j' -NonGhostQuantifiers.dfy(94,10): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'y' -NonGhostQuantifiers.dfy(103,8): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'x' -NonGhostQuantifiers.dfy(120,8): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression) -8 resolution/type errors detected in NonGhostQuantifiers.dfy +NonGhostQuantifiers.dfy(42,4): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'n' +NonGhostQuantifiers.dfy(46,4): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'd' +NonGhostQuantifiers.dfy(50,4): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'n' +NonGhostQuantifiers.dfy(74,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'i' +NonGhostQuantifiers.dfy(78,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'j' +NonGhostQuantifiers.dfy(88,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'j' +NonGhostQuantifiers.dfy(103,5): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'j' +NonGhostQuantifiers.dfy(111,10): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'y' +NonGhostQuantifiers.dfy(120,8): Error: quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for 'x' +NonGhostQuantifiers.dfy(137,8): Error: Assignment to non-ghost variable is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression) +11 resolution/type errors detected in NonGhostQuantifiers.dfy -------------------- AdvancedLHS.dfy -------------------- AdvancedLHS.dfy(32,23): Error: target object may be null diff --git a/Test/dafny0/NonGhostQuantifiers.dfy b/Test/dafny0/NonGhostQuantifiers.dfy index ea256a58..58e64827 100644 --- a/Test/dafny0/NonGhostQuantifiers.dfy +++ b/Test/dafny0/NonGhostQuantifiers.dfy @@ -33,6 +33,23 @@ class MyClass { (forall n :: 2 <= n ==> 1000 <= n || (exists d :: n < d && d < 2*n)) } + function method GoodRange(): bool + { + (forall n | 2 <= n :: 1000 <= n || (exists d | n < d :: d < 2*n)) && (exists K: nat | K < 100 :: true) + } + function method BadRangeForall(): bool + { + forall n | n <= 2 :: 1000 <= n || n % 2 == 0 // error: cannot bound range for n + } + function method BadRangeExists(): bool + { + exists d | d < 1000 :: d < 2000 // error: cannot bound range for d + } + function method WhatAboutThis(): bool + { + forall n :: 2 <= n && (forall M | M == 1000 :: n < M) ==> n % 2 == 0 // error: heuristics don't get this one for n + } + // Here are more tests function method F(a: array): bool diff --git a/Test/dafny0/SmallTests.dfy b/Test/dafny0/SmallTests.dfy index a839d5a9..ef2049a3 100644 --- a/Test/dafny0/SmallTests.dfy +++ b/Test/dafny0/SmallTests.dfy @@ -272,3 +272,39 @@ class InitCalls { r := new InitCalls.InitFromReference(r); // error, since r.z is unknown } } + +// --------------- some tests with quantifiers and ranges ---------------------- + +method QuantifierRange0(a: seq, x: T, y: T, N: int) + requires 0 <= N && N <= |a|; + requires forall k | 0 <= k && k < N :: a[k] != x; + requires exists k | 0 <= k && k < N :: a[k] == y; + ensures forall k :: 0 <= k && k < N ==> a[k] != x; // same as the precondition, but using ==> instead of | + ensures exists k :: 0 <= k && k < N && a[k] == y; // same as the precondition, but using && instead of | +{ + assert x != y; +} + +method QuantifierRange1(a: seq, x: T, y: T, N: int) + requires 0 <= N && N <= |a|; + requires forall k :: 0 <= k && k < N ==> a[k] != x; + requires exists k :: 0 <= k && k < N && a[k] == y; + ensures forall k | 0 <= k && k < N :: a[k] != x; // same as the precondition, but using | instead of ==> + ensures exists k | 0 <= k && k < N :: a[k] == y; // same as the precondition, but using | instead of && +{ + assert x != y; +} + +method QuantifierRange2(a: seq, x: T, y: T, N: int) + requires 0 <= N && N <= |a|; + requires exists k | 0 <= k && k < N :: a[k] == y; + ensures forall k | 0 <= k && k < N :: a[k] == y; // error +{ + assert N != 0; + if (N == 1) { + assert forall k | a[if 0 <= k && k < N then k else 0] != y :: k < 0 || N <= k; // in this case, the precondition holds trivially + } + if (forall k | 0 <= k && k < N :: a[k] == x) { + assert x == y; + } +} -- cgit v1.2.3 From f448d52dce21c0ed55369af51522d130cb1737b8 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 16 May 2011 11:49:01 -0700 Subject: Dafny: added some test cases that use nat --- Test/dafny1/Answer | 2 +- Test/dafny1/TerminationDemos.dfy | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Test/dafny1/Answer b/Test/dafny1/Answer index d0eba7cb..0111f9af 100644 --- a/Test/dafny1/Answer +++ b/Test/dafny1/Answer @@ -65,7 +65,7 @@ Dafny program verifier finished with 4 verified, 0 errors -------------------- TerminationDemos.dfy -------------------- -Dafny program verifier finished with 11 verified, 0 errors +Dafny program verifier finished with 14 verified, 0 errors -------------------- Substitution.dfy -------------------- diff --git a/Test/dafny1/TerminationDemos.dfy b/Test/dafny1/TerminationDemos.dfy index 01a7c7bd..49f5a075 100644 --- a/Test/dafny1/TerminationDemos.dfy +++ b/Test/dafny1/TerminationDemos.dfy @@ -42,6 +42,28 @@ class Ackermann { else G(m - 1, G(m, n - 1)) } + + function H(m: nat, n: nat): nat + { + if m == 0 then + n + 1 + else if n == 0 then + H(m - 1, 1) + else + H(m - 1, H(m, n - 1)) + } + + method ComputeAck(m: nat, n: nat) returns (r: nat) + { + if (m == 0) { + r := n + 1; + } else if (n == 0) { + call r := ComputeAck(m - 1, 1); + } else { + call s := ComputeAck(m, n - 1); + call r := ComputeAck(m - 1, s); + } + } } // ----------------------------------- -- cgit v1.2.3 From ddebd84b961b6ac942e4e97380ac1ff874e97386 Mon Sep 17 00:00:00 2001 From: qadeer Date: Mon, 16 May 2011 13:45:03 -0700 Subject: convert assert to requires --- Source/Core/ResolutionContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/ResolutionContext.cs b/Source/Core/ResolutionContext.cs index 803b1f8b..251ca1db 100644 --- a/Source/Core/ResolutionContext.cs +++ b/Source/Core/ResolutionContext.cs @@ -169,7 +169,7 @@ namespace Microsoft.Boogie { public void AddType(NamedDeclaration td) { Contract.Requires(td != null); - Contract.Assert((td is TypeCtorDecl) || (td is TypeSynonymDecl)); + Contract.Requires((td is TypeCtorDecl) || (td is TypeSynonymDecl)); Contract.Requires(td.Name != null); string name = td.Name; -- cgit v1.2.3 From 2ad98cac004873f9f1eb48dd5a1e34332bda5f5b Mon Sep 17 00:00:00 2001 From: Mike Barnett Date: Mon, 16 May 2011 14:05:08 -0700 Subject: Mark procedures/functions as "extern" if they are defined in a different assembly than the one under translation. Normalize method bodies so anonymous delegates do not appear, but instead show up as the nested classes/methods that implement them. Beginning of some simplfication for nested expressions. --- BCT/BytecodeTranslator/BytecodeTranslator.csproj | 12 +- BCT/BytecodeTranslator/ExpressionTraverser.cs | 242 ++++++++++++++++++----- BCT/BytecodeTranslator/MetadataTraverser.cs | 21 +- BCT/BytecodeTranslator/Program.cs | 45 ++++- BCT/BytecodeTranslator/Sink.cs | 31 ++- BCT/BytecodeTranslator/StatementTraverser.cs | 32 ++- 6 files changed, 317 insertions(+), 66 deletions(-) diff --git a/BCT/BytecodeTranslator/BytecodeTranslator.csproj b/BCT/BytecodeTranslator/BytecodeTranslator.csproj index 86fc4a5d..e4575480 100644 --- a/BCT/BytecodeTranslator/BytecodeTranslator.csproj +++ b/BCT/BytecodeTranslator/BytecodeTranslator.csproj @@ -32,6 +32,7 @@ false true + 0 true @@ -41,7 +42,7 @@ DEBUG;TRACE prompt 4 - False + True @@ -67,6 +68,15 @@ True False AllRules.ruleset + False + False + False + False + False + + False + %28none%29 + 0 pdbonly diff --git a/BCT/BytecodeTranslator/ExpressionTraverser.cs b/BCT/BytecodeTranslator/ExpressionTraverser.cs index 2ec38e8b..a8d06194 100644 --- a/BCT/BytecodeTranslator/ExpressionTraverser.cs +++ b/BCT/BytecodeTranslator/ExpressionTraverser.cs @@ -138,6 +138,16 @@ namespace BytecodeTranslator } public override void Visit(IArrayIndexer arrayIndexer) { + +#if EXPERIMENTAL + if (!IsAtomicInstance(arrayIndexer.IndexedObject)) { + // Simplify the BE so that all nested dereferences and method calls are broken up into separate assignments to locals. + var se = ExpressionSimplifier.Simplify(this.sink, arrayIndexer); + this.Visit(se); + return; + } +#endif + this.Visit(arrayIndexer.IndexedObject); Bpl.Expr arrayExpr = TranslatedExpressions.Pop(); this.Visit(arrayIndexer.Indices); @@ -155,6 +165,9 @@ namespace BytecodeTranslator indexExpr = new Bpl.NAryExpr(arrayIndexer.Token(), new Bpl.FunctionCall(f), new Bpl.ExprSeq(indexExprs)); } +#if EXPERIMENTAL + this.TranslatedExpressions.Push(arrayExpr); +#else Bpl.IdentifierExpr temp = Bpl.Expr.Ident(this.sink.CreateFreshLocal(arrayIndexer.Type)); Bpl.Expr selectExpr = sink.Heap.ReadHeap(arrayExpr, indexExpr, AccessType.Array, temp.Type); this.StmtTraverser.StmtBuilder.Add(TranslationHelper.BuildAssignCmd(temp, selectExpr)); @@ -162,10 +175,22 @@ namespace BytecodeTranslator this.arrayExpr = arrayExpr; this.indexExpr = indexExpr; +#endif } public override void Visit(ITargetExpression targetExpression) { +#if EXPERIMENTAL + Contract.Assume(false, "The expression containing this as a subexpression should never allow a call to this routine."); + + if (targetExpression.Instance != null && !IsAtomicInstance(targetExpression.Instance)) { + //// Simplify the BE so that all nested dereferences and method calls are broken up into separate assignments to locals. + var se = ExpressionSimplifier.Simplify(this.sink, targetExpression); + this.Visit(se); + return; + } +#endif + #region ArrayIndexer IArrayIndexer/*?*/ indexer = targetExpression.Definition as IArrayIndexer; if (indexer != null) @@ -252,11 +277,14 @@ namespace BytecodeTranslator public override void Visit(IBoundExpression boundExpression) { - //if (boundExpression.Instance != null) - //{ - // this.Visit(boundExpression.Instance); - // // TODO: (mschaef) look where it's bound and do something - //} + + if (boundExpression.Instance != null && !IsAtomicInstance(boundExpression.Instance)) { + // Simplify the BE so that all nested dereferences and method calls are broken up into separate assignments to locals. + var se = ExpressionSimplifier.Simplify(this.sink, boundExpression); + this.Visit(se); + return; + } + #region Local ILocalDefinition local = boundExpression.Definition as ILocalDefinition; if (local != null) @@ -290,9 +318,9 @@ namespace BytecodeTranslator } else { Bpl.Expr instanceExpr = TranslatedExpressions.Pop(); - Bpl.IdentifierExpr temp = Bpl.Expr.Ident(this.sink.CreateFreshLocal(field.ResolvedField.Type)); - this.StmtTraverser.StmtBuilder.Add(TranslationHelper.BuildAssignCmd(temp, this.sink.Heap.ReadHeap(instanceExpr, f, field.ContainingType.ResolvedType.IsStruct ? AccessType.Struct : AccessType.Heap, temp.Type))); - TranslatedExpressions.Push(temp); + var bplType = this.sink.CciTypeToBoogie(field.Type); + var e = this.sink.Heap.ReadHeap(instanceExpr, f, field.ContainingType.ResolvedType.IsStruct ? AccessType.Struct : AccessType.Heap, bplType); + this.TranslatedExpressions.Push(e); } } return; @@ -339,6 +367,14 @@ namespace BytecodeTranslator #endregion } + internal static bool IsAtomicInstance(IExpression expression) { + var thisInst = expression as IThisReference; + if (thisInst != null) return true; + var be = expression as IBoundExpression; + if (be == null) return false; + return be.Instance == null; + } + public override void Visit(IPopValue popValue) { var locExpr = this.StmtTraverser.operandStack.Pop(); this.TranslatedExpressions.Push(locExpr); @@ -604,32 +640,131 @@ namespace BytecodeTranslator private Bpl.Expr arrayExpr = null; private Bpl.Expr indexExpr = null; +#if EXPERIMENTAL /// /// /// /// (mschaef) Works, but still a stub /// + public override void Visit(IAssignment assignment) { + Contract.Assert(TranslatedExpressions.Count == 0); + + var tok = assignment.Token(); + + object container = assignment.Target.Definition; + + var/*?*/ local = container as ILocalDefinition; + if (local != null) { + Contract.Assume(assignment.Target.Instance == null); + this.Visit(assignment.Source); + var e = this.TranslatedExpressions.Pop(); + var bplLocal = Bpl.Expr.Ident(this.sink.FindOrCreateLocalVariable(local)); + StmtTraverser.StmtBuilder.Add(Bpl.Cmd.SimpleAssign(tok, bplLocal, e)); + return; + } + + var/*?*/ parameter = container as IParameterDefinition; + if (parameter != null) { + Contract.Assume(assignment.Target.Instance == null); + this.Visit(assignment.Source); + var e = this.TranslatedExpressions.Pop(); + var bplParam = Bpl.Expr.Ident(this.sink.FindParameterVariable(parameter, this.contractContext)); + StmtTraverser.StmtBuilder.Add(Bpl.Cmd.SimpleAssign(tok, bplParam, e)); + return; + } + + var/*?*/ field = container as IFieldReference; + if (field != null) { + this.Visit(assignment.Source); + var e = this.TranslatedExpressions.Pop(); + var f = Bpl.Expr.Ident(this.sink.FindOrCreateFieldVariable(field)); + if (assignment.Target.Instance == null) { + // static fields are not kept in the heap + StmtTraverser.StmtBuilder.Add(Bpl.Cmd.SimpleAssign(tok, f, e)); + } else { + if (field.ContainingType.ResolvedType.IsStruct) { + //var s_prime = this.sink.CreateFreshLocal(this.sink.Heap.StructType); + //var s_prime_expr = Bpl.Expr.Ident(s_prime); + //var boogieType = sink.CciTypeToBoogie(field.Type); + //StmtTraverser.StmtBuilder.Add(this.sink.Heap.WriteHeap(tok, s_prime_expr, f, e, + // field.ResolvedField.ContainingType.ResolvedType.IsStruct ? AccessType.Struct : AccessType.Heap, + // boogieType)); + UpdateStruct(tok, assignment.Target.Instance, field, e); + } else { + this.Visit(assignment.Target.Instance); + var x = this.TranslatedExpressions.Pop(); + var boogieType = sink.CciTypeToBoogie(field.Type); + StmtTraverser.StmtBuilder.Add(this.sink.Heap.WriteHeap(tok, x, f, e, + field.ResolvedField.ContainingType.ResolvedType.IsStruct ? AccessType.Struct : AccessType.Heap, + boogieType)); + } + } + return; + } + + var/*?*/ arrayIndexer = container as IArrayIndexer; + if (arrayIndexer != null) { + this.Visit(assignment.Target.Instance); + var x = this.TranslatedExpressions.Pop(); + this.Visit(arrayIndexer.Indices); + var indices_prime = this.TranslatedExpressions.Pop(); + this.Visit(assignment.Source); + var e = this.TranslatedExpressions.Pop(); + StmtTraverser.StmtBuilder.Add(sink.Heap.WriteHeap(Bpl.Token.NoToken, x, indices_prime, e, AccessType.Array, sink.CciTypeToBoogie(arrayIndexer.Type))); + return; + } + + Contract.Assume(false); + } + + private void UpdateStruct(Bpl.IToken tok, IExpression iExpression, IFieldReference field, Bpl.Expr e) { + var addrOf = iExpression as IAddressOf; + if (addrOf == null) return; + var addressableExpression = addrOf.Expression as IAddressableExpression; + if (addressableExpression == null) return; + + var f = Bpl.Expr.Ident(this.sink.FindOrCreateFieldVariable(field)); + + if (addressableExpression.Instance == null) { + var boogieType = sink.CciTypeToBoogie(field.Type); + this.Visit(addressableExpression); + var def = this.TranslatedExpressions.Pop(); + StmtTraverser.StmtBuilder.Add(this.sink.Heap.WriteHeap(tok, def, f, e, + AccessType.Struct, + boogieType)); + } else { + var s_prime = this.sink.CreateFreshLocal(this.sink.Heap.StructType); + var s_prime_expr = Bpl.Expr.Ident(s_prime); + var boogieType = sink.CciTypeToBoogie(field.Type); + StmtTraverser.StmtBuilder.Add(this.sink.Heap.WriteHeap(tok, s_prime_expr, f, e, + AccessType.Struct, + boogieType)); + UpdateStruct(tok, addressableExpression.Instance, addressableExpression.Definition as IFieldReference, s_prime_expr); + } + } + +#else + public override void Visit(IAssignment assignment) { Contract.Assert(TranslatedExpressions.Count == 0); #region Transform Right Hand Side ... - // Simplify the RHS so that all nested dereferences and method calls are broken - // up into separate assignments to locals. - var sourceExpression = ExpressionSimplifier.Simplify(this.sink, assignment.Source); - this.Visit(sourceExpression); + this.Visit(assignment.Source); var sourceexp = this.TranslatedExpressions.Pop(); #endregion // Simplify the LHS so that all nested dereferences and method calls are broken // up into separate assignments to locals. - var targetExpression = ExpressionSimplifier.Simplify(this.sink, assignment.Target); + //var targetExpression = ExpressionSimplifier.Simplify(this.sink, assignment.Target); + //this.Visit(targetExpression); //var targetBlockExpression = targetExpression as IBlockExpression; //if (targetBlockExpression != null){ //foreach (var s in targetBlockExpression.BlockStatement.Statements) { // this.StmtTraverser.Visit(s); //} - var target = targetExpression as ITargetExpression; + //var target = targetExpression as ITargetExpression; + var target = assignment.Target; List args = null; Bpl.Expr arrayExpr = null; @@ -649,8 +784,7 @@ namespace BytecodeTranslator Bpl.IdentifierExpr f = Bpl.Expr.Ident(this.sink.FindOrCreateFieldVariable(fieldReference.ResolvedField)); if (target.Instance == null) { StmtTraverser.StmtBuilder.Add(Bpl.Cmd.SimpleAssign(assignment.Token(), f, sourceexp)); - } - else { + } else { Debug.Assert(args != null); List locals = new List(); Bpl.IdentifierExpr instanceExpr = TranslatedExpressions.Pop() as Bpl.IdentifierExpr; @@ -721,6 +855,8 @@ namespace BytecodeTranslator return; } +#endif + #endregion #region Translate Object Creation @@ -1167,6 +1303,11 @@ namespace BytecodeTranslator } #endregion + public override void Visit(IBlockExpression blockExpression) { + this.StmtTraverser.Visit(blockExpression.BlockStatement); + this.Visit(blockExpression.Expression); + } + /// /// This is a rewriter so it must be used on a mutable Code Model!!! /// @@ -1185,71 +1326,78 @@ namespace BytecodeTranslator } public override IExpression Rewrite(IBoundExpression boundExpression) { - if (boundExpression.Instance == null) - return base.Rewrite(boundExpression); // REVIEW: Maybe just stop the rewriting and return boundExpression? - var thisInst = boundExpression.Instance as IThisReference; - if (thisInst != null) return boundExpression; - var p = boundExpression.Instance as IParameterDefinition; - if (p != null) return boundExpression; - var existingLocal = boundExpression.Instance as ILocalDefinition; - if (existingLocal != null) return boundExpression; - - var e = base.Rewrite(boundExpression); - boundExpression = e as IBoundExpression; - if (boundExpression == null) return e; + + if (ExpressionTraverser.IsAtomicInstance(boundExpression.Instance)) return boundExpression; + + // boundExpression == BE(inst, def), i.e., inst.def + // return { loc := e; [assert loc != null;] | BE(BE(null,loc), def) }, i.e., "loc := e; loc.def" + // where e is the rewritten inst + + var e = base.Rewrite(boundExpression.Instance); var loc = new LocalDefinition() { Name = this.host.NameTable.GetNameFor("_loc" + this.sink.LocalCounter.ToString()), Type = boundExpression.Type, }; var locDecl = new LocalDeclarationStatement() { - InitialValue = boundExpression, + InitialValue = e, LocalVariable = loc, }; return new BlockExpression() { BlockStatement = new BlockStatement() { - Statements = new List{ locDecl }, + Statements = new List { locDecl }, }, Expression = new BoundExpression() { - Definition = loc, - Instance = null, + Definition = boundExpression.Definition, + Instance = new BoundExpression() { + Definition = loc, + Instance = null, + Type = loc.Type, + }, Type = boundExpression.Type, }, - Type = boundExpression.Type, }; } - public override ITargetExpression Rewrite(ITargetExpression targetExpression) { - var be = targetExpression.Instance as IBoundExpression; - if (be == null) return targetExpression; + public override IExpression Rewrite(IArrayIndexer arrayIndexer) { + if (ExpressionTraverser.IsAtomicInstance(arrayIndexer.IndexedObject)) return arrayIndexer; + + // arrayIndexer == AI(inst, [index]), i.e., inst[index0, index1,...] + // return { loc := e; [assert loc != null;] | AI(BE(null,loc), [index]) } + // where e is the rewritten array instance - var e = this.Rewrite(be); + var e = base.Rewrite(arrayIndexer.IndexedObject); var loc = new LocalDefinition() { Name = this.host.NameTable.GetNameFor("_loc" + this.sink.LocalCounter.ToString()), - Type = be.Type, + Type = arrayIndexer.Type, }; var locDecl = new LocalDeclarationStatement() { InitialValue = e, LocalVariable = loc, }; - return new TargetExpression() { - Definition = targetExpression.Definition, - Instance = new BlockExpression() { - BlockStatement = new BlockStatement() { - Statements = new List { locDecl, }, - }, - Expression = new BoundExpression() { + return new BlockExpression() { + BlockStatement = new BlockStatement() { + Statements = new List { locDecl }, + }, + Expression = new ArrayIndexer() { + IndexedObject = new BoundExpression() { Definition = loc, Instance = null, + Type = loc.Type, }, - Type = loc.Type, + Indices = new List(arrayIndexer.Indices), + Type = arrayIndexer.Type, }, - Type = targetExpression.Type, }; } + public override ITargetExpression Rewrite(ITargetExpression targetExpression) { + Contract.Assume(false, "The expression containing this as a subexpression should never allow a call to this routine."); + return null; + } + //public override IExpression Rewrite(IMethodCall methodCall) { // var e = base.Rewrite(methodCall); // simplify anything deeper in the tree diff --git a/BCT/BytecodeTranslator/MetadataTraverser.cs b/BCT/BytecodeTranslator/MetadataTraverser.cs index 58bec27f..b1be6294 100644 --- a/BCT/BytecodeTranslator/MetadataTraverser.cs +++ b/BCT/BytecodeTranslator/MetadataTraverser.cs @@ -46,7 +46,12 @@ namespace BytecodeTranslator { } public override void Visit(IAssembly assembly) { - base.Visit(assembly); + this.sink.BeginAssembly(assembly); + try { + base.Visit(assembly); + } finally { + this.sink.EndAssembly(assembly); + } } /// @@ -55,6 +60,9 @@ namespace BytecodeTranslator { /// public override void Visit(ITypeDefinition typeDefinition) { + var savedPrivateTypes = this.privateTypes; + this.privateTypes = new List(); + if (typeDefinition.IsClass) { bool savedSawCctor = this.sawCctor; this.sawCctor = false; @@ -80,7 +88,12 @@ namespace BytecodeTranslator { TypeHelper.GetTypeName(typeDefinition)); throw new NotImplementedException(String.Format("Unknown kind of type definition '{0}'.", TypeHelper.GetTypeName(typeDefinition))); } + this.Visit(typeDefinition.PrivateHelperMembers); + foreach (var t in this.privateTypes) { + this.Visit(t); + } } + List privateTypes = new List(); private void CreateDefaultStructConstructor(ITypeDefinition typeDefinition) { Contract.Requires(typeDefinition.IsStruct); @@ -256,7 +269,11 @@ namespace BytecodeTranslator { #endregion #region Translate body - method.Body.Dispatch(stmtTraverser); + var helperTypes = stmtTraverser.TranslateMethod(method); + if (helperTypes != null) { + this.privateTypes.AddRange(helperTypes); + } + //method.Body.Dispatch(stmtTraverser); #endregion #region Create Local Vars For Implementation diff --git a/BCT/BytecodeTranslator/Program.cs b/BCT/BytecodeTranslator/Program.cs index f42c34d9..471b3431 100644 --- a/BCT/BytecodeTranslator/Program.cs +++ b/BCT/BytecodeTranslator/Program.cs @@ -132,12 +132,19 @@ namespace BytecodeTranslator { var copier = new CodeDeepCopier(host); var mutableModule = copier.Copy(module); - var mutator = new ReparentModule(host, - TypeHelper.GetDefiningUnit(host.PlatformType.SystemObject.ResolvedType), - mutableModule); - module = mutator.Rewrite(mutableModule); + var mscorlib = TypeHelper.GetDefiningUnit(host.PlatformType.SystemObject.ResolvedType); + + //var mutator = new ReparentModule(host, mscorlib, mutableModule); + //module = mutator.Rewrite(mutableModule); + //modules.Add(Tuple.Create(module, pdbReader)); + + RewriteUnitReferences renamer = new RewriteUnitReferences(host, mutableModule); + var mscorlibAssembly = (IAssembly)mscorlib; + renamer.targetAssembly = mscorlibAssembly; + renamer.originalAssemblyIdentity = mscorlibAssembly.AssemblyIdentity; + renamer.RewriteChildren(mutableModule); + modules.Add(Tuple.Create((IModule)mutableModule, pdbReader)); - modules.Add(Tuple.Create(module, pdbReader)); } } if (modules.Count == 0) { @@ -340,6 +347,34 @@ namespace BytecodeTranslator { // Maybe this is a good place to add the procedure to the toplevel declarations } } + + private class RewriteUnitReferences : MetadataRewriter { + private UnitIdentity sourceUnitIdentity = null; + internal IAssembly/*?*/ targetAssembly = null; + internal AssemblyIdentity/*?*/ originalAssemblyIdentity = null; + + Dictionary internedKeys = new Dictionary(); + + public RewriteUnitReferences(IMetadataHost host, Module sourceUnit) + : base(host) { + this.sourceUnitIdentity = sourceUnit.UnitIdentity; + } + + public override IModuleReference Rewrite(IModuleReference moduleReference) { + if (this.sourceUnitIdentity.Equals(moduleReference.UnitIdentity)) { + return this.targetAssembly; + } + return base.Rewrite(moduleReference); + } + public override IAssemblyReference Rewrite(IAssemblyReference assemblyReference) { + if (this.sourceUnitIdentity.Equals(assemblyReference.UnitIdentity)) { + return this.targetAssembly; + } + return base.Rewrite(assemblyReference); + } + + } + } } diff --git a/BCT/BytecodeTranslator/Sink.cs b/BCT/BytecodeTranslator/Sink.cs index b8d8334d..852c3986 100644 --- a/BCT/BytecodeTranslator/Sink.cs +++ b/BCT/BytecodeTranslator/Sink.cs @@ -118,7 +118,7 @@ namespace BytecodeTranslator { return heap.StructType; else if (type.IsEnum) return Bpl.Type.Int; // The underlying type of an enum is always some kind of integer - else if (type is IGenericTypeParameter) + else if (type is IGenericTypeParameter) return heap.BoxType; else return heap.RefType; @@ -167,7 +167,9 @@ namespace BytecodeTranslator { Bpl.IToken tok = local.Token(); Bpl.Type t = CciTypeToBoogie(local.Type.ResolvedType); if (!localVarMap.TryGetValue(local, out v)) { - v = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, local.Name.Value, t)); + var name = local.Name.Value; + name = TranslationHelper.TurnStringIntoValidIdentifier(name); + v = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, name, t)); localVarMap.Add(local, v); } return v; @@ -334,6 +336,7 @@ namespace BytecodeTranslator { ProcedureInfo procAndFormalMap; var key = method.InternedKey; + if (!this.declaredMethods.TryGetValue(key, out procAndFormalMap)) { string MethodName = TranslationHelper.CreateUniqueMethodName(method); @@ -375,7 +378,7 @@ namespace BytecodeTranslator { Bpl.Formal selfOut = null; #region Create 'this' parameter if (!method.IsStatic) { - Bpl.Type selfType = CciTypeToBoogie(method.ContainingType); + var selfType = CciTypeToBoogie(method.ContainingType); if (method.ContainingType.ResolvedType.IsStruct) { //selfType = Heap.StructType; in_count++; @@ -470,15 +473,16 @@ namespace BytecodeTranslator { #endregion + var tok = method.Token(); Bpl.DeclWithFormals decl; if (IsPure(method)) { - var func = new Bpl.Function(method.Token(), + var func = new Bpl.Function(tok, MethodName, new Bpl.VariableSeq(invars), this.RetVariable); decl = func; } else { - var proc = new Bpl.Procedure(method.Token(), + var proc = new Bpl.Procedure(tok, MethodName, new Bpl.TypeVariableSeq(), new Bpl.VariableSeq(invars), @@ -488,7 +492,10 @@ namespace BytecodeTranslator { boogiePostcondition); decl = proc; } - + if (!TypeHelper.GetDefiningUnitReference(method.ContainingType).UnitIdentity.Equals(this.assemblyBeingTranslated.UnitIdentity)) { + var attrib = new Bpl.QKeyValue(tok, "extern", new List(1), null); + decl.Attributes = attrib; + } string newName = null; if (IsStubMethod(method, out newName)) { @@ -573,7 +580,8 @@ namespace BytecodeTranslator { public ProcedureInfo FindOrCreateProcedureAndReturnProcAndFormalMap(IMethodDefinition method) { this.FindOrCreateProcedure(method); - return this.declaredMethods[method.InternedKey]; + var key = method.InternedKey; + return this.declaredMethods[key]; } public static IMethodReference Unspecialize(IMethodReference method) { IMethodReference result = method; @@ -634,6 +642,15 @@ namespace BytecodeTranslator { this.localCounter = 0; } + public void BeginAssembly(IAssembly assembly) { + this.assemblyBeingTranslated = assembly; + } + + public void EndAssembly(IAssembly assembly) { + this.assemblyBeingTranslated = null; + } + private IAssembly assemblyBeingTranslated; + public Dictionary> delegateTypeToDelegates = new Dictionary>(); public void AddDelegate(ITypeDefinition type, IMethodDefinition defn) diff --git a/BCT/BytecodeTranslator/StatementTraverser.cs b/BCT/BytecodeTranslator/StatementTraverser.cs index 24b4b2ca..0e13c510 100644 --- a/BCT/BytecodeTranslator/StatementTraverser.cs +++ b/BCT/BytecodeTranslator/StatementTraverser.cs @@ -50,13 +50,37 @@ namespace BytecodeTranslator return etrav.TranslatedExpressions.Pop(); } + public ICollection/*?*/ TranslateMethod(IMethodDefinition method) { + var methodBody = method.Body as ISourceMethodBody; + if (methodBody == null) return null; + var block = methodBody.Block as BlockStatement; + // TODO: Error if cast fails? + + ICollection newTypes = null; + if (block != null) { + var remover = new AnonymousDelegateRemover(this.sink.host, this.PdbReader); + newTypes = remover.RemoveAnonymousDelegates(methodBody.MethodDefinition, block); + } + this.Visit(methodBody); + return newTypes; + } + #endregion - public override void Visit(IBlockStatement block) { - Bpl.StmtListBuilder slb = new Bpl.StmtListBuilder(); + //public override void Visit(ISourceMethodBody methodBody) { + // var block = methodBody.Block as BlockStatement; + // // TODO: Error if cast fails? + + // if (block != null) { + // var remover = new AnonymousDelegateRemover(this.sink.host, this.PdbReader); + // var newTypes = remover.RemoveAnonymousDelegates(methodBody.MethodDefinition, block); + // } + // base.Visit(methodBody); + //} - foreach (IStatement st in block.Statements) { - this.Visit(st); + public override void Visit(IBlockStatement block) { + foreach (var s in block.Statements) { + this.Visit(s); } } -- cgit v1.2.3 From 8c6c8235015b2c5482fb74d2e8489c1bec13ec6c Mon Sep 17 00:00:00 2001 From: Mike Barnett Date: Mon, 16 May 2011 14:05:46 -0700 Subject: Add extern declarations to procedures. Normalize method bodies so anonymous delegates are gone. Some attempts at simplifying. New regression output. --- .../TranslationTest/GeneralHeapInput.txt | 105 +++++++++------------ .../TranslationTest/SplitFieldsHeapInput.txt | 105 +++++++++------------ 2 files changed, 92 insertions(+), 118 deletions(-) diff --git a/BCT/RegressionTests/TranslationTest/GeneralHeapInput.txt b/BCT/RegressionTests/TranslationTest/GeneralHeapInput.txt index 430474d2..5b24fa32 100644 --- a/BCT/RegressionTests/TranslationTest/GeneralHeapInput.txt +++ b/BCT/RegressionTests/TranslationTest/GeneralHeapInput.txt @@ -248,6 +248,11 @@ function Ref2Box(Ref) : Box; function Real2Box(Real) : Box; +function {:inline true} Box2Box(b: Box) : Box +{ + b +} + function Struct2Ref(Struct) : Ref; function Int2Ref(int) : Ref; @@ -370,34 +375,22 @@ procedure RegressionTestInput.ClassWithArrayTypes.Main3$System.Int32(this: Ref, implementation RegressionTestInput.ClassWithArrayTypes.Main3$System.Int32(this: Ref, x$in: int) { var x: int; - var _loc0: Ref; - var $tmp14: Ref; + var $tmp14: int; var $tmp15: int; - var _loc1: Ref; - var $tmp16: Ref; + var $tmp16: int; var $tmp17: int; - var $tmp18: Ref; - var $tmp19: int; - var $tmp20: Ref; - var $tmp21: int; x := x$in; assert {:sourceFile "Class1.cs"} {:sourceLine 114} true; - $tmp14 := Box2Ref(Read($Heap, this, RegressionTestInput.ClassWithArrayTypes.a)); - _loc0 := $tmp14; - $tmp15 := Box2Int($ArrayContents[_loc0][x]); - $ArrayContents := $ArrayContents[_loc0 := $ArrayContents[_loc0][x := Int2Box(42)]]; + $tmp14 := Box2Int($ArrayContents[this][x]); + $ArrayContents := $ArrayContents[this := $ArrayContents[this][x := Int2Box(42)]]; assert {:sourceFile "Class1.cs"} {:sourceLine 115} true; - $tmp16 := Box2Ref(Read($Heap, this, RegressionTestInput.ClassWithArrayTypes.a)); - _loc1 := $tmp16; - $tmp17 := Box2Int($ArrayContents[_loc1][x + 1]); - $ArrayContents := $ArrayContents[_loc1 := $ArrayContents[_loc1][x + 1 := Int2Box(43)]]; + $tmp15 := Box2Int($ArrayContents[this][x + 1]); + $ArrayContents := $ArrayContents[this := $ArrayContents[this][x + 1 := Int2Box(43)]]; assert {:sourceFile "Class1.cs"} {:sourceLine 116} true; - $tmp18 := Box2Ref(Read($Heap, this, RegressionTestInput.ClassWithArrayTypes.a)); - $tmp19 := Box2Int($ArrayContents[$tmp18][x + 1]); - $tmp20 := Box2Ref(Read($Heap, this, RegressionTestInput.ClassWithArrayTypes.a)); - $tmp21 := Box2Int($ArrayContents[$tmp20][x]); - assert $tmp19 == $tmp21 + 1; + $tmp16 := Box2Int($ArrayContents[Box2Ref(Read($Heap, this, RegressionTestInput.ClassWithArrayTypes.a))][x + 1]); + $tmp17 := Box2Int($ArrayContents[Box2Ref(Read($Heap, this, RegressionTestInput.ClassWithArrayTypes.a))][x]); + assert $tmp16 == $tmp17 + 1; assert {:sourceFile "Class1.cs"} {:sourceLine 117} true; return; } @@ -411,20 +404,16 @@ procedure RegressionTestInput.ClassWithArrayTypes.Main4$System.Int32array(this: implementation RegressionTestInput.ClassWithArrayTypes.Main4$System.Int32array(this: Ref, xs$in: Ref) { var xs: Ref; - var $tmp22: int; - var _loc0: Ref; - var $tmp23: Ref; - var $tmp24: int; + var $tmp18: int; + var $tmp19: int; xs := xs$in; if (!(if xs != null then $ArrayLength[xs] <= 0 else true)) { assert {:sourceFile "Class1.cs"} {:sourceLine 121} true; - $tmp22 := Box2Int($ArrayContents[xs][0]); - $tmp23 := Box2Ref(Read($Heap, this, RegressionTestInput.ClassWithArrayTypes.a)); - _loc0 := $tmp23; - $tmp24 := Box2Int($ArrayContents[_loc0][0]); - $ArrayContents := $ArrayContents[_loc0 := $ArrayContents[_loc0][0 := Int2Box($tmp22)]]; + $tmp18 := Box2Int($ArrayContents[xs][0]); + $tmp19 := Box2Int($ArrayContents[this][0]); + $ArrayContents := $ArrayContents[this := $ArrayContents[this][0 := Int2Box($tmp18)]]; } else { @@ -440,7 +429,7 @@ procedure RegressionTestInput.ClassWithArrayTypes.#ctor(this: Ref); -procedure System.Object.#ctor(this: Ref); +procedure {:extern} System.Object.#ctor(this: Ref); @@ -531,13 +520,13 @@ implementation RegressionTestInput.Class0.M$System.Int32(this: Ref, x$in: int) { var x: int; var __temp_1: int; - var $tmp25: int; + var $tmp20: int; var local_0: int; x := x$in; - $tmp25 := x; - assert $tmp25 != 0; - __temp_1 := 5 / $tmp25; + $tmp20 := x; + assert $tmp20 != 0; + __temp_1 := 5 / $tmp20; x := 3; local_0 := __temp_1 + 3; assert {:sourceFile "Class1.cs"} {:sourceLine 22} true; @@ -605,22 +594,23 @@ procedure RegressionTestInput.Class0.NonVoid(this: Ref) returns ($result: int); implementation RegressionTestInput.Class0.NonVoid(this: Ref) returns ($result: int) { - var $tmp26: int; + var $tmp21: int; assert {:sourceFile "Class1.cs"} {:sourceLine 34} true; - call $tmp26 := RegressionTestInput.Class0.StaticMethod$System.Int32(3); - $result := 3 + RegressionTestInput.Class0.StaticInt + $tmp26; + call $tmp21 := RegressionTestInput.Class0.StaticMethod$System.Int32(3); + $result := 3 + RegressionTestInput.Class0.StaticInt + $tmp21; return; } -procedure RegressionTestInput.Class0.OutParam$System.Int32$(this: Ref) returns (x$out: int, $result: int); +procedure RegressionTestInput.Class0.OutParam$System.Int32$(this: Ref, x$in: int) returns (x$out: int, $result: int); -implementation RegressionTestInput.Class0.OutParam$System.Int32$(this: Ref) returns (x$out: int, $result: int) +implementation RegressionTestInput.Class0.OutParam$System.Int32$(this: Ref, x$in: int) returns (x$out: int, $result: int) { + x$out := x$in; assert {:sourceFile "Class1.cs"} {:sourceLine 37} true; x$out := 3 + RegressionTestInput.Class0.StaticInt; assert {:sourceFile "Class1.cs"} {:sourceLine 39} true; @@ -691,12 +681,12 @@ procedure RegressionTestInput.Class0.CallAsyncMethod$System.Int32(this: Ref, y$i implementation RegressionTestInput.Class0.CallAsyncMethod$System.Int32(this: Ref, y$in: int) returns ($result: int) { var y: int; - var $tmp27: int; + var $tmp22: int; y := y$in; assert {:sourceFile "Class1.cs"} {:sourceLine 60} true; - call {:async} $tmp27 := RegressionTestInput.Class0.MethodThatRepresentsAnAynchronousMethod$System.Int32(this, y); - $result := $tmp27; + call {:async} $tmp22 := RegressionTestInput.Class0.MethodThatRepresentsAnAynchronousMethod$System.Int32(this, y); + $result := $tmp22; return; } @@ -737,11 +727,8 @@ procedure RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.M(this: Ref); implementation RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.M(this: Ref) { - var $tmp28: int; - assert {:sourceFile "Class1.cs"} {:sourceLine 130} true; - $tmp28 := Box2Int(Read($Heap, this, RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.x)); - $Heap := Write($Heap, this, RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.y, Int2Box($tmp28)); + $Heap := Write($Heap, this, RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.y, Int2Box(Box2Int(Read($Heap, this, RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.x)))); assert {:sourceFile "Class1.cs"} {:sourceLine 131} true; return; } @@ -831,10 +818,10 @@ procedure RegressionTestInput.ClassWithBoolTypes.Main(); implementation RegressionTestInput.ClassWithBoolTypes.Main() { - var $tmp29: bool; + var $tmp23: bool; assert {:sourceFile "Class1.cs"} {:sourceLine 78} true; - call $tmp29 := RegressionTestInput.ClassWithBoolTypes.M$System.Int32$System.Int32(3, 4); + call $tmp23 := RegressionTestInput.ClassWithBoolTypes.M$System.Int32$System.Int32(3, 4); assert {:sourceFile "Class1.cs"} {:sourceLine 79} true; return; } @@ -864,7 +851,7 @@ procedure RegressionTestInput.AsyncAttribute.#ctor(this: Ref); -procedure System.Attribute.#ctor(this: Ref); +procedure {:extern} System.Attribute.#ctor(this: Ref); @@ -895,17 +882,17 @@ procedure RegressionTestInput.CreateStruct.Create(this: Ref) returns ($result: [ implementation RegressionTestInput.CreateStruct.Create(this: Ref) returns ($result: [Field]Box) { var local_0: [Field]Box; - var $tmp30: int; - var $tmp31: bool; + var _loc0: int; + var _loc1: bool; assert {:sourceFile "Class1.cs"} {:sourceLine 141} true; local_0 := $DefaultStruct; assert {:sourceFile "Class1.cs"} {:sourceLine 142} true; - $tmp30 := Box2Int(local_0[RegressionTestInput.S.x]); - assert $tmp30 == 0; + _loc0 := local_0; + assert Box2Int(_loc0[RegressionTestInput.S.x]) == 0; assert {:sourceFile "Class1.cs"} {:sourceLine 143} true; - $tmp31 := Box2Bool(local_0[RegressionTestInput.S.b]); - assert !$tmp31; + _loc1 := local_0; + assert !Box2Bool(_loc1[RegressionTestInput.S.b]); assert {:sourceFile "Class1.cs"} {:sourceLine 145} true; $result := local_0; return; @@ -920,14 +907,14 @@ procedure RegressionTestInput.CreateStruct.AssignThreeToSDotX$RegressionTestInpu implementation RegressionTestInput.CreateStruct.AssignThreeToSDotX$RegressionTestInput.S(this: Ref, s$in: [Field]Box) returns ($result: [Field]Box) { var s: [Field]Box; - var $tmp32: int; + var _loc0: int; s := s$in; assert {:sourceFile "Class1.cs"} {:sourceLine 147} true; s := s[RegressionTestInput.S.x := Int2Box(3)]; assert {:sourceFile "Class1.cs"} {:sourceLine 148} true; - $tmp32 := Box2Int(s[RegressionTestInput.S.x]); - assert $tmp32 == 3; + _loc0 := s; + assert Box2Int(_loc0[RegressionTestInput.S.x]) == 3; assert {:sourceFile "Class1.cs"} {:sourceLine 150} true; $result := s; return; diff --git a/BCT/RegressionTests/TranslationTest/SplitFieldsHeapInput.txt b/BCT/RegressionTests/TranslationTest/SplitFieldsHeapInput.txt index 629a241c..163b9153 100644 --- a/BCT/RegressionTests/TranslationTest/SplitFieldsHeapInput.txt +++ b/BCT/RegressionTests/TranslationTest/SplitFieldsHeapInput.txt @@ -238,6 +238,11 @@ function Ref2Box(Ref) : Box; function Real2Box(Real) : Box; +function {:inline true} Box2Box(b: Box) : Box +{ + b +} + function Struct2Ref(Struct) : Ref; function Int2Ref(int) : Ref; @@ -360,34 +365,22 @@ procedure RegressionTestInput.ClassWithArrayTypes.Main3$System.Int32(this: Ref, implementation RegressionTestInput.ClassWithArrayTypes.Main3$System.Int32(this: Ref, x$in: int) { var x: int; - var _loc0: Ref; - var $tmp14: Ref; + var $tmp14: int; var $tmp15: int; - var _loc1: Ref; - var $tmp16: Ref; + var $tmp16: int; var $tmp17: int; - var $tmp18: Ref; - var $tmp19: int; - var $tmp20: Ref; - var $tmp21: int; x := x$in; assert {:sourceFile "Class1.cs"} {:sourceLine 114} true; - $tmp14 := RegressionTestInput.ClassWithArrayTypes.a[this]; - _loc0 := $tmp14; - $tmp15 := $ArrayContents[_loc0][x]; - $ArrayContents := $ArrayContents[_loc0 := $ArrayContents[_loc0][x := 42]]; + $tmp14 := $ArrayContents[this][x]; + $ArrayContents := $ArrayContents[this := $ArrayContents[this][x := 42]]; assert {:sourceFile "Class1.cs"} {:sourceLine 115} true; - $tmp16 := RegressionTestInput.ClassWithArrayTypes.a[this]; - _loc1 := $tmp16; - $tmp17 := $ArrayContents[_loc1][x + 1]; - $ArrayContents := $ArrayContents[_loc1 := $ArrayContents[_loc1][x + 1 := 43]]; + $tmp15 := $ArrayContents[this][x + 1]; + $ArrayContents := $ArrayContents[this := $ArrayContents[this][x + 1 := 43]]; assert {:sourceFile "Class1.cs"} {:sourceLine 116} true; - $tmp18 := RegressionTestInput.ClassWithArrayTypes.a[this]; - $tmp19 := $ArrayContents[$tmp18][x + 1]; - $tmp20 := RegressionTestInput.ClassWithArrayTypes.a[this]; - $tmp21 := $ArrayContents[$tmp20][x]; - assert $tmp19 == $tmp21 + 1; + $tmp16 := $ArrayContents[RegressionTestInput.ClassWithArrayTypes.a[this]][x + 1]; + $tmp17 := $ArrayContents[RegressionTestInput.ClassWithArrayTypes.a[this]][x]; + assert $tmp16 == $tmp17 + 1; assert {:sourceFile "Class1.cs"} {:sourceLine 117} true; return; } @@ -401,20 +394,16 @@ procedure RegressionTestInput.ClassWithArrayTypes.Main4$System.Int32array(this: implementation RegressionTestInput.ClassWithArrayTypes.Main4$System.Int32array(this: Ref, xs$in: Ref) { var xs: Ref; - var $tmp22: int; - var _loc0: Ref; - var $tmp23: Ref; - var $tmp24: int; + var $tmp18: int; + var $tmp19: int; xs := xs$in; if (!(if xs != null then $ArrayLength[xs] <= 0 else true)) { assert {:sourceFile "Class1.cs"} {:sourceLine 121} true; - $tmp22 := $ArrayContents[xs][0]; - $tmp23 := RegressionTestInput.ClassWithArrayTypes.a[this]; - _loc0 := $tmp23; - $tmp24 := $ArrayContents[_loc0][0]; - $ArrayContents := $ArrayContents[_loc0 := $ArrayContents[_loc0][0 := $tmp22]]; + $tmp18 := $ArrayContents[xs][0]; + $tmp19 := $ArrayContents[this][0]; + $ArrayContents := $ArrayContents[this := $ArrayContents[this][0 := $tmp18]]; } else { @@ -430,7 +419,7 @@ procedure RegressionTestInput.ClassWithArrayTypes.#ctor(this: Ref); -procedure System.Object.#ctor(this: Ref); +procedure {:extern} System.Object.#ctor(this: Ref); @@ -521,13 +510,13 @@ implementation RegressionTestInput.Class0.M$System.Int32(this: Ref, x$in: int) { var x: int; var __temp_1: int; - var $tmp25: int; + var $tmp20: int; var local_0: int; x := x$in; - $tmp25 := x; - assert $tmp25 != 0; - __temp_1 := 5 / $tmp25; + $tmp20 := x; + assert $tmp20 != 0; + __temp_1 := 5 / $tmp20; x := 3; local_0 := __temp_1 + 3; assert {:sourceFile "Class1.cs"} {:sourceLine 22} true; @@ -595,22 +584,23 @@ procedure RegressionTestInput.Class0.NonVoid(this: Ref) returns ($result: int); implementation RegressionTestInput.Class0.NonVoid(this: Ref) returns ($result: int) { - var $tmp26: int; + var $tmp21: int; assert {:sourceFile "Class1.cs"} {:sourceLine 34} true; - call $tmp26 := RegressionTestInput.Class0.StaticMethod$System.Int32(3); - $result := 3 + RegressionTestInput.Class0.StaticInt + $tmp26; + call $tmp21 := RegressionTestInput.Class0.StaticMethod$System.Int32(3); + $result := 3 + RegressionTestInput.Class0.StaticInt + $tmp21; return; } -procedure RegressionTestInput.Class0.OutParam$System.Int32$(this: Ref) returns (x$out: int, $result: int); +procedure RegressionTestInput.Class0.OutParam$System.Int32$(this: Ref, x$in: int) returns (x$out: int, $result: int); -implementation RegressionTestInput.Class0.OutParam$System.Int32$(this: Ref) returns (x$out: int, $result: int) +implementation RegressionTestInput.Class0.OutParam$System.Int32$(this: Ref, x$in: int) returns (x$out: int, $result: int) { + x$out := x$in; assert {:sourceFile "Class1.cs"} {:sourceLine 37} true; x$out := 3 + RegressionTestInput.Class0.StaticInt; assert {:sourceFile "Class1.cs"} {:sourceLine 39} true; @@ -681,12 +671,12 @@ procedure RegressionTestInput.Class0.CallAsyncMethod$System.Int32(this: Ref, y$i implementation RegressionTestInput.Class0.CallAsyncMethod$System.Int32(this: Ref, y$in: int) returns ($result: int) { var y: int; - var $tmp27: int; + var $tmp22: int; y := y$in; assert {:sourceFile "Class1.cs"} {:sourceLine 60} true; - call {:async} $tmp27 := RegressionTestInput.Class0.MethodThatRepresentsAnAynchronousMethod$System.Int32(this, y); - $result := $tmp27; + call {:async} $tmp22 := RegressionTestInput.Class0.MethodThatRepresentsAnAynchronousMethod$System.Int32(this, y); + $result := $tmp22; return; } @@ -727,11 +717,8 @@ procedure RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.M(this: Ref); implementation RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.M(this: Ref) { - var $tmp28: int; - assert {:sourceFile "Class1.cs"} {:sourceLine 130} true; - $tmp28 := RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.x[this]; - RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.y[this] := $tmp28; + RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.y[this] := RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.x[this]; assert {:sourceFile "Class1.cs"} {:sourceLine 131} true; return; } @@ -821,10 +808,10 @@ procedure RegressionTestInput.ClassWithBoolTypes.Main(); implementation RegressionTestInput.ClassWithBoolTypes.Main() { - var $tmp29: bool; + var $tmp23: bool; assert {:sourceFile "Class1.cs"} {:sourceLine 78} true; - call $tmp29 := RegressionTestInput.ClassWithBoolTypes.M$System.Int32$System.Int32(3, 4); + call $tmp23 := RegressionTestInput.ClassWithBoolTypes.M$System.Int32$System.Int32(3, 4); assert {:sourceFile "Class1.cs"} {:sourceLine 79} true; return; } @@ -854,7 +841,7 @@ procedure RegressionTestInput.AsyncAttribute.#ctor(this: Ref); -procedure System.Attribute.#ctor(this: Ref); +procedure {:extern} System.Attribute.#ctor(this: Ref); @@ -885,17 +872,17 @@ procedure RegressionTestInput.CreateStruct.Create(this: Ref) returns ($result: [ implementation RegressionTestInput.CreateStruct.Create(this: Ref) returns ($result: [Field]Box) { var local_0: [Field]Box; - var $tmp30: int; - var $tmp31: bool; + var _loc0: int; + var _loc1: bool; assert {:sourceFile "Class1.cs"} {:sourceLine 141} true; local_0 := $DefaultStruct; assert {:sourceFile "Class1.cs"} {:sourceLine 142} true; - $tmp30 := local_0[RegressionTestInput.S.x]; - assert $tmp30 == 0; + _loc0 := local_0; + assert _loc0[RegressionTestInput.S.x] == 0; assert {:sourceFile "Class1.cs"} {:sourceLine 143} true; - $tmp31 := local_0[RegressionTestInput.S.b]; - assert !$tmp31; + _loc1 := local_0; + assert !_loc1[RegressionTestInput.S.b]; assert {:sourceFile "Class1.cs"} {:sourceLine 145} true; $result := local_0; return; @@ -910,14 +897,14 @@ procedure RegressionTestInput.CreateStruct.AssignThreeToSDotX$RegressionTestInpu implementation RegressionTestInput.CreateStruct.AssignThreeToSDotX$RegressionTestInput.S(this: Ref, s$in: [Field]Box) returns ($result: [Field]Box) { var s: [Field]Box; - var $tmp32: int; + var _loc0: int; s := s$in; assert {:sourceFile "Class1.cs"} {:sourceLine 147} true; s[RegressionTestInput.S.x] := 3; assert {:sourceFile "Class1.cs"} {:sourceLine 148} true; - $tmp32 := s[RegressionTestInput.S.x]; - assert $tmp32 == 3; + _loc0 := s; + assert _loc0[RegressionTestInput.S.x] == 3; assert {:sourceFile "Class1.cs"} {:sourceLine 150} true; $result := s; return; -- cgit v1.2.3 From 9026ac103f2e5ca9be7617de99c7d4a1fc1d9e0e Mon Sep 17 00:00:00 2001 From: Mike Barnett Date: Mon, 16 May 2011 14:21:35 -0700 Subject: Merge --- BCT/BytecodeTranslator/BytecodeTranslator.csproj | 12 +- BCT/BytecodeTranslator/ExpressionTraverser.cs | 294 ++++++++++++++++++----- BCT/BytecodeTranslator/MetadataTraverser.cs | 21 +- BCT/BytecodeTranslator/Program.cs | 45 +++- BCT/BytecodeTranslator/Sink.cs | 31 ++- BCT/BytecodeTranslator/StatementTraverser.cs | 32 ++- 6 files changed, 362 insertions(+), 73 deletions(-) diff --git a/BCT/BytecodeTranslator/BytecodeTranslator.csproj b/BCT/BytecodeTranslator/BytecodeTranslator.csproj index 86fc4a5d..e4575480 100644 --- a/BCT/BytecodeTranslator/BytecodeTranslator.csproj +++ b/BCT/BytecodeTranslator/BytecodeTranslator.csproj @@ -32,6 +32,7 @@ false true + 0 true @@ -41,7 +42,7 @@ DEBUG;TRACE prompt 4 - False + True @@ -67,6 +68,15 @@ True False AllRules.ruleset + False + False + False + False + False + + False + %28none%29 + 0 pdbonly diff --git a/BCT/BytecodeTranslator/ExpressionTraverser.cs b/BCT/BytecodeTranslator/ExpressionTraverser.cs index 29c0033f..77050c46 100644 --- a/BCT/BytecodeTranslator/ExpressionTraverser.cs +++ b/BCT/BytecodeTranslator/ExpressionTraverser.cs @@ -138,6 +138,16 @@ namespace BytecodeTranslator } public override void Visit(IArrayIndexer arrayIndexer) { + +#if EXPERIMENTAL + if (!IsAtomicInstance(arrayIndexer.IndexedObject)) { + // Simplify the BE so that all nested dereferences and method calls are broken up into separate assignments to locals. + var se = ExpressionSimplifier.Simplify(this.sink, arrayIndexer); + this.Visit(se); + return; + } +#endif + this.Visit(arrayIndexer.IndexedObject); Bpl.Expr arrayExpr = TranslatedExpressions.Pop(); this.Visit(arrayIndexer.Indices); @@ -155,6 +165,9 @@ namespace BytecodeTranslator indexExpr = new Bpl.NAryExpr(arrayIndexer.Token(), new Bpl.FunctionCall(f), new Bpl.ExprSeq(indexExprs)); } +#if EXPERIMENTAL + this.TranslatedExpressions.Push(arrayExpr); +#else Bpl.IdentifierExpr temp = Bpl.Expr.Ident(this.sink.CreateFreshLocal(arrayIndexer.Type)); Bpl.Expr selectExpr = sink.Heap.ReadHeap(arrayExpr, indexExpr, AccessType.Array, temp.Type); this.StmtTraverser.StmtBuilder.Add(TranslationHelper.BuildAssignCmd(temp, selectExpr)); @@ -162,10 +175,22 @@ namespace BytecodeTranslator this.arrayExpr = arrayExpr; this.indexExpr = indexExpr; +#endif } public override void Visit(ITargetExpression targetExpression) { +#if EXPERIMENTAL + Contract.Assume(false, "The expression containing this as a subexpression should never allow a call to this routine."); + + if (targetExpression.Instance != null && !IsAtomicInstance(targetExpression.Instance)) { + //// Simplify the BE so that all nested dereferences and method calls are broken up into separate assignments to locals. + var se = ExpressionSimplifier.Simplify(this.sink, targetExpression); + this.Visit(se); + return; + } +#endif + #region ArrayIndexer IArrayIndexer/*?*/ indexer = targetExpression.Definition as IArrayIndexer; if (indexer != null) @@ -252,11 +277,14 @@ namespace BytecodeTranslator public override void Visit(IBoundExpression boundExpression) { - //if (boundExpression.Instance != null) - //{ - // this.Visit(boundExpression.Instance); - // // TODO: (mschaef) look where it's bound and do something - //} + + if (boundExpression.Instance != null && !IsAtomicInstance(boundExpression.Instance)) { + // Simplify the BE so that all nested dereferences and method calls are broken up into separate assignments to locals. + var se = ExpressionSimplifier.Simplify(this.sink, boundExpression); + this.Visit(se); + return; + } + #region Local ILocalDefinition local = boundExpression.Definition as ILocalDefinition; if (local != null) @@ -290,9 +318,9 @@ namespace BytecodeTranslator } else { Bpl.Expr instanceExpr = TranslatedExpressions.Pop(); - Bpl.IdentifierExpr temp = Bpl.Expr.Ident(this.sink.CreateFreshLocal(field.ResolvedField.Type)); - this.StmtTraverser.StmtBuilder.Add(TranslationHelper.BuildAssignCmd(temp, this.sink.Heap.ReadHeap(instanceExpr, f, field.ContainingType.ResolvedType.IsStruct ? AccessType.Struct : AccessType.Heap, temp.Type))); - TranslatedExpressions.Push(temp); + var bplType = this.sink.CciTypeToBoogie(field.Type); + var e = this.sink.Heap.ReadHeap(instanceExpr, f, field.ContainingType.ResolvedType.IsStruct ? AccessType.Struct : AccessType.Heap, bplType); + this.TranslatedExpressions.Push(e); } } return; @@ -339,6 +367,14 @@ namespace BytecodeTranslator #endregion } + internal static bool IsAtomicInstance(IExpression expression) { + var thisInst = expression as IThisReference; + if (thisInst != null) return true; + var be = expression as IBoundExpression; + if (be == null) return false; + return be.Instance == null; + } + public override void Visit(IPopValue popValue) { var locExpr = this.StmtTraverser.operandStack.Pop(); this.TranslatedExpressions.Push(locExpr); @@ -604,17 +640,117 @@ namespace BytecodeTranslator private Bpl.Expr arrayExpr = null; private Bpl.Expr indexExpr = null; +#if EXPERIMENTAL /// /// /// /// (mschaef) Works, but still a stub /// + public override void Visit(IAssignment assignment) { + Contract.Assert(TranslatedExpressions.Count == 0); + + var tok = assignment.Token(); + + object container = assignment.Target.Definition; + + var/*?*/ local = container as ILocalDefinition; + if (local != null) { + Contract.Assume(assignment.Target.Instance == null); + this.Visit(assignment.Source); + var e = this.TranslatedExpressions.Pop(); + var bplLocal = Bpl.Expr.Ident(this.sink.FindOrCreateLocalVariable(local)); + StmtTraverser.StmtBuilder.Add(Bpl.Cmd.SimpleAssign(tok, bplLocal, e)); + return; + } + + var/*?*/ parameter = container as IParameterDefinition; + if (parameter != null) { + Contract.Assume(assignment.Target.Instance == null); + this.Visit(assignment.Source); + var e = this.TranslatedExpressions.Pop(); + var bplParam = Bpl.Expr.Ident(this.sink.FindParameterVariable(parameter, this.contractContext)); + StmtTraverser.StmtBuilder.Add(Bpl.Cmd.SimpleAssign(tok, bplParam, e)); + return; + } + + var/*?*/ field = container as IFieldReference; + if (field != null) { + this.Visit(assignment.Source); + var e = this.TranslatedExpressions.Pop(); + var f = Bpl.Expr.Ident(this.sink.FindOrCreateFieldVariable(field)); + if (assignment.Target.Instance == null) { + // static fields are not kept in the heap + StmtTraverser.StmtBuilder.Add(Bpl.Cmd.SimpleAssign(tok, f, e)); + } else { + if (field.ContainingType.ResolvedType.IsStruct) { + //var s_prime = this.sink.CreateFreshLocal(this.sink.Heap.StructType); + //var s_prime_expr = Bpl.Expr.Ident(s_prime); + //var boogieType = sink.CciTypeToBoogie(field.Type); + //StmtTraverser.StmtBuilder.Add(this.sink.Heap.WriteHeap(tok, s_prime_expr, f, e, + // field.ResolvedField.ContainingType.ResolvedType.IsStruct ? AccessType.Struct : AccessType.Heap, + // boogieType)); + UpdateStruct(tok, assignment.Target.Instance, field, e); + } else { + this.Visit(assignment.Target.Instance); + var x = this.TranslatedExpressions.Pop(); + var boogieType = sink.CciTypeToBoogie(field.Type); + StmtTraverser.StmtBuilder.Add(this.sink.Heap.WriteHeap(tok, x, f, e, + field.ResolvedField.ContainingType.ResolvedType.IsStruct ? AccessType.Struct : AccessType.Heap, + boogieType)); + } + } + return; + } + + var/*?*/ arrayIndexer = container as IArrayIndexer; + if (arrayIndexer != null) { + this.Visit(assignment.Target.Instance); + var x = this.TranslatedExpressions.Pop(); + this.Visit(arrayIndexer.Indices); + var indices_prime = this.TranslatedExpressions.Pop(); + this.Visit(assignment.Source); + var e = this.TranslatedExpressions.Pop(); + StmtTraverser.StmtBuilder.Add(sink.Heap.WriteHeap(Bpl.Token.NoToken, x, indices_prime, e, AccessType.Array, sink.CciTypeToBoogie(arrayIndexer.Type))); + return; + } + + Contract.Assume(false); + } + + private void UpdateStruct(Bpl.IToken tok, IExpression iExpression, IFieldReference field, Bpl.Expr e) { + var addrOf = iExpression as IAddressOf; + if (addrOf == null) return; + var addressableExpression = addrOf.Expression as IAddressableExpression; + if (addressableExpression == null) return; + + var f = Bpl.Expr.Ident(this.sink.FindOrCreateFieldVariable(field)); + + if (addressableExpression.Instance == null) { + var boogieType = sink.CciTypeToBoogie(field.Type); + this.Visit(addressableExpression); + var def = this.TranslatedExpressions.Pop(); + StmtTraverser.StmtBuilder.Add(this.sink.Heap.WriteHeap(tok, def, f, e, + AccessType.Struct, + boogieType)); + } else { + var s_prime = this.sink.CreateFreshLocal(this.sink.Heap.StructType); + var s_prime_expr = Bpl.Expr.Ident(s_prime); + var boogieType = sink.CciTypeToBoogie(field.Type); + StmtTraverser.StmtBuilder.Add(this.sink.Heap.WriteHeap(tok, s_prime_expr, f, e, + AccessType.Struct, + boogieType)); + UpdateStruct(tok, addressableExpression.Instance, addressableExpression.Definition as IFieldReference, s_prime_expr); + } + } + +#else + public override void Visit(IAssignment assignment) { Contract.Assert(TranslatedExpressions.Count == 0); #region Transform Right Hand Side ... this.Visit(assignment.Source); - Bpl.Expr sourceexp = this.TranslatedExpressions.Pop(); + var sourceexp = this.TranslatedExpressions.Pop(); #endregion // Simplify the LHS so that all nested dereferences and method calls are broken @@ -643,8 +779,7 @@ namespace BytecodeTranslator Bpl.IdentifierExpr f = Bpl.Expr.Ident(this.sink.FindOrCreateFieldVariable(fieldReference.ResolvedField)); if (target.Instance == null) { StmtTraverser.StmtBuilder.Add(Bpl.Cmd.SimpleAssign(assignment.Token(), f, sourceexp)); - } - else { + } else { Debug.Assert(args != null); List locals = new List(); Bpl.IdentifierExpr instanceExpr = TranslatedExpressions.Pop() as Bpl.IdentifierExpr; @@ -715,6 +850,8 @@ namespace BytecodeTranslator return; } +#endif + #endregion #region Translate Object Creation @@ -1161,74 +1298,123 @@ namespace BytecodeTranslator } #endregion + public override void Visit(IBlockExpression blockExpression) { + this.StmtTraverser.Visit(blockExpression.BlockStatement); + this.Visit(blockExpression.Expression); + } + /// /// This is a rewriter so it must be used on a mutable Code Model!!! /// - private class AssignmentSimplifier : CodeRewriter { + private class ExpressionSimplifier : CodeRewriter { Sink sink; - private List localDeclarations = new List(); - private AssignmentSimplifier(Sink sink) + private ExpressionSimplifier(Sink sink) : base(sink.host) { this.sink = sink; } - public static IBlockExpression Simplify(Sink sink, ITargetExpression targetExpression) { - var a = new AssignmentSimplifier(sink); - var leftOverExpression = a.Rewrite(targetExpression); - return new BlockExpression() { - BlockStatement = new BlockStatement() { Statements = a.localDeclarations, }, - Expression = leftOverExpression, - Type = targetExpression.Type, - }; + public static IExpression Simplify(Sink sink, IExpression expression) { + var a = new ExpressionSimplifier(sink); + return a.Rewrite(expression); } public override IExpression Rewrite(IBoundExpression boundExpression) { - if (boundExpression.Instance == null) - return base.Rewrite(boundExpression); // REVIEW: Maybe just stop the rewriting and return boundExpression? - var e = base.Rewrite(boundExpression); - boundExpression = e as IBoundExpression; - if (boundExpression == null) return e; + + if (ExpressionTraverser.IsAtomicInstance(boundExpression.Instance)) return boundExpression; + + // boundExpression == BE(inst, def), i.e., inst.def + // return { loc := e; [assert loc != null;] | BE(BE(null,loc), def) }, i.e., "loc := e; loc.def" + // where e is the rewritten inst + + var e = base.Rewrite(boundExpression.Instance); + var loc = new LocalDefinition() { - Name = this.host.NameTable.GetNameFor("_loc" + this.sink.LocalCounter.ToString()), // TODO: should make the name unique within the method containing the assignment + Name = this.host.NameTable.GetNameFor("_loc" + this.sink.LocalCounter.ToString()), Type = boundExpression.Type, }; - this.localDeclarations.Add( - new LocalDeclarationStatement() { - InitialValue = boundExpression, - LocalVariable = loc, - } - ); - return new BoundExpression() { - Definition = loc, - Instance = null, - Type = boundExpression.Type, + var locDecl = new LocalDeclarationStatement() { + InitialValue = e, + LocalVariable = loc, }; + return new BlockExpression() { + BlockStatement = new BlockStatement() { + Statements = new List { locDecl }, + }, + Expression = new BoundExpression() { + Definition = boundExpression.Definition, + Instance = new BoundExpression() { + Definition = loc, + Instance = null, + Type = loc.Type, + }, + Type = boundExpression.Type, + }, + }; + } - public override IExpression Rewrite(IMethodCall methodCall) { + public override IExpression Rewrite(IArrayIndexer arrayIndexer) { + if (ExpressionTraverser.IsAtomicInstance(arrayIndexer.IndexedObject)) return arrayIndexer; - var e = base.Rewrite(methodCall); // simplify anything deeper in the tree - methodCall = e as IMethodCall; - if (methodCall == null) return e; + // arrayIndexer == AI(inst, [index]), i.e., inst[index0, index1,...] + // return { loc := e; [assert loc != null;] | AI(BE(null,loc), [index]) } + // where e is the rewritten array instance + + var e = base.Rewrite(arrayIndexer.IndexedObject); var loc = new LocalDefinition() { - Name = this.host.NameTable.GetNameFor("_loc"), // TODO: should make the name unique within the method containing the assignment - Type = methodCall.Type, + Name = this.host.NameTable.GetNameFor("_loc" + this.sink.LocalCounter.ToString()), + Type = arrayIndexer.Type, }; - this.localDeclarations.Add( - new LocalDeclarationStatement() { - InitialValue = methodCall, - LocalVariable = loc, - } - ); - return new BoundExpression() { - Definition = loc, - Instance = null, - Type = methodCall.Type, + var locDecl = new LocalDeclarationStatement() { + InitialValue = e, + LocalVariable = loc, + }; + return new BlockExpression() { + BlockStatement = new BlockStatement() { + Statements = new List { locDecl }, + }, + Expression = new ArrayIndexer() { + IndexedObject = new BoundExpression() { + Definition = loc, + Instance = null, + Type = loc.Type, + }, + Indices = new List(arrayIndexer.Indices), + Type = arrayIndexer.Type, + }, }; } + + public override ITargetExpression Rewrite(ITargetExpression targetExpression) { + Contract.Assume(false, "The expression containing this as a subexpression should never allow a call to this routine."); + return null; + } + + //public override IExpression Rewrite(IMethodCall methodCall) { + + // var e = base.Rewrite(methodCall); // simplify anything deeper in the tree + // methodCall = e as IMethodCall; + // if (methodCall == null) return e; + + // var loc = new LocalDefinition() { + // Name = this.host.NameTable.GetNameFor("_loc"), // TODO: should make the name unique within the method containing the assignment + // Type = methodCall.Type, + // }; + // this.localDeclarations.Add( + // new LocalDeclarationStatement() { + // InitialValue = methodCall, + // LocalVariable = loc, + // } + // ); + // return new BoundExpression() { + // Definition = loc, + // Instance = null, + // Type = methodCall.Type, + // }; + //} } } diff --git a/BCT/BytecodeTranslator/MetadataTraverser.cs b/BCT/BytecodeTranslator/MetadataTraverser.cs index 58bec27f..b1be6294 100644 --- a/BCT/BytecodeTranslator/MetadataTraverser.cs +++ b/BCT/BytecodeTranslator/MetadataTraverser.cs @@ -46,7 +46,12 @@ namespace BytecodeTranslator { } public override void Visit(IAssembly assembly) { - base.Visit(assembly); + this.sink.BeginAssembly(assembly); + try { + base.Visit(assembly); + } finally { + this.sink.EndAssembly(assembly); + } } /// @@ -55,6 +60,9 @@ namespace BytecodeTranslator { /// public override void Visit(ITypeDefinition typeDefinition) { + var savedPrivateTypes = this.privateTypes; + this.privateTypes = new List(); + if (typeDefinition.IsClass) { bool savedSawCctor = this.sawCctor; this.sawCctor = false; @@ -80,7 +88,12 @@ namespace BytecodeTranslator { TypeHelper.GetTypeName(typeDefinition)); throw new NotImplementedException(String.Format("Unknown kind of type definition '{0}'.", TypeHelper.GetTypeName(typeDefinition))); } + this.Visit(typeDefinition.PrivateHelperMembers); + foreach (var t in this.privateTypes) { + this.Visit(t); + } } + List privateTypes = new List(); private void CreateDefaultStructConstructor(ITypeDefinition typeDefinition) { Contract.Requires(typeDefinition.IsStruct); @@ -256,7 +269,11 @@ namespace BytecodeTranslator { #endregion #region Translate body - method.Body.Dispatch(stmtTraverser); + var helperTypes = stmtTraverser.TranslateMethod(method); + if (helperTypes != null) { + this.privateTypes.AddRange(helperTypes); + } + //method.Body.Dispatch(stmtTraverser); #endregion #region Create Local Vars For Implementation diff --git a/BCT/BytecodeTranslator/Program.cs b/BCT/BytecodeTranslator/Program.cs index f42c34d9..471b3431 100644 --- a/BCT/BytecodeTranslator/Program.cs +++ b/BCT/BytecodeTranslator/Program.cs @@ -132,12 +132,19 @@ namespace BytecodeTranslator { var copier = new CodeDeepCopier(host); var mutableModule = copier.Copy(module); - var mutator = new ReparentModule(host, - TypeHelper.GetDefiningUnit(host.PlatformType.SystemObject.ResolvedType), - mutableModule); - module = mutator.Rewrite(mutableModule); + var mscorlib = TypeHelper.GetDefiningUnit(host.PlatformType.SystemObject.ResolvedType); + + //var mutator = new ReparentModule(host, mscorlib, mutableModule); + //module = mutator.Rewrite(mutableModule); + //modules.Add(Tuple.Create(module, pdbReader)); + + RewriteUnitReferences renamer = new RewriteUnitReferences(host, mutableModule); + var mscorlibAssembly = (IAssembly)mscorlib; + renamer.targetAssembly = mscorlibAssembly; + renamer.originalAssemblyIdentity = mscorlibAssembly.AssemblyIdentity; + renamer.RewriteChildren(mutableModule); + modules.Add(Tuple.Create((IModule)mutableModule, pdbReader)); - modules.Add(Tuple.Create(module, pdbReader)); } } if (modules.Count == 0) { @@ -340,6 +347,34 @@ namespace BytecodeTranslator { // Maybe this is a good place to add the procedure to the toplevel declarations } } + + private class RewriteUnitReferences : MetadataRewriter { + private UnitIdentity sourceUnitIdentity = null; + internal IAssembly/*?*/ targetAssembly = null; + internal AssemblyIdentity/*?*/ originalAssemblyIdentity = null; + + Dictionary internedKeys = new Dictionary(); + + public RewriteUnitReferences(IMetadataHost host, Module sourceUnit) + : base(host) { + this.sourceUnitIdentity = sourceUnit.UnitIdentity; + } + + public override IModuleReference Rewrite(IModuleReference moduleReference) { + if (this.sourceUnitIdentity.Equals(moduleReference.UnitIdentity)) { + return this.targetAssembly; + } + return base.Rewrite(moduleReference); + } + public override IAssemblyReference Rewrite(IAssemblyReference assemblyReference) { + if (this.sourceUnitIdentity.Equals(assemblyReference.UnitIdentity)) { + return this.targetAssembly; + } + return base.Rewrite(assemblyReference); + } + + } + } } diff --git a/BCT/BytecodeTranslator/Sink.cs b/BCT/BytecodeTranslator/Sink.cs index b8d8334d..852c3986 100644 --- a/BCT/BytecodeTranslator/Sink.cs +++ b/BCT/BytecodeTranslator/Sink.cs @@ -118,7 +118,7 @@ namespace BytecodeTranslator { return heap.StructType; else if (type.IsEnum) return Bpl.Type.Int; // The underlying type of an enum is always some kind of integer - else if (type is IGenericTypeParameter) + else if (type is IGenericTypeParameter) return heap.BoxType; else return heap.RefType; @@ -167,7 +167,9 @@ namespace BytecodeTranslator { Bpl.IToken tok = local.Token(); Bpl.Type t = CciTypeToBoogie(local.Type.ResolvedType); if (!localVarMap.TryGetValue(local, out v)) { - v = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, local.Name.Value, t)); + var name = local.Name.Value; + name = TranslationHelper.TurnStringIntoValidIdentifier(name); + v = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, name, t)); localVarMap.Add(local, v); } return v; @@ -334,6 +336,7 @@ namespace BytecodeTranslator { ProcedureInfo procAndFormalMap; var key = method.InternedKey; + if (!this.declaredMethods.TryGetValue(key, out procAndFormalMap)) { string MethodName = TranslationHelper.CreateUniqueMethodName(method); @@ -375,7 +378,7 @@ namespace BytecodeTranslator { Bpl.Formal selfOut = null; #region Create 'this' parameter if (!method.IsStatic) { - Bpl.Type selfType = CciTypeToBoogie(method.ContainingType); + var selfType = CciTypeToBoogie(method.ContainingType); if (method.ContainingType.ResolvedType.IsStruct) { //selfType = Heap.StructType; in_count++; @@ -470,15 +473,16 @@ namespace BytecodeTranslator { #endregion + var tok = method.Token(); Bpl.DeclWithFormals decl; if (IsPure(method)) { - var func = new Bpl.Function(method.Token(), + var func = new Bpl.Function(tok, MethodName, new Bpl.VariableSeq(invars), this.RetVariable); decl = func; } else { - var proc = new Bpl.Procedure(method.Token(), + var proc = new Bpl.Procedure(tok, MethodName, new Bpl.TypeVariableSeq(), new Bpl.VariableSeq(invars), @@ -488,7 +492,10 @@ namespace BytecodeTranslator { boogiePostcondition); decl = proc; } - + if (!TypeHelper.GetDefiningUnitReference(method.ContainingType).UnitIdentity.Equals(this.assemblyBeingTranslated.UnitIdentity)) { + var attrib = new Bpl.QKeyValue(tok, "extern", new List(1), null); + decl.Attributes = attrib; + } string newName = null; if (IsStubMethod(method, out newName)) { @@ -573,7 +580,8 @@ namespace BytecodeTranslator { public ProcedureInfo FindOrCreateProcedureAndReturnProcAndFormalMap(IMethodDefinition method) { this.FindOrCreateProcedure(method); - return this.declaredMethods[method.InternedKey]; + var key = method.InternedKey; + return this.declaredMethods[key]; } public static IMethodReference Unspecialize(IMethodReference method) { IMethodReference result = method; @@ -634,6 +642,15 @@ namespace BytecodeTranslator { this.localCounter = 0; } + public void BeginAssembly(IAssembly assembly) { + this.assemblyBeingTranslated = assembly; + } + + public void EndAssembly(IAssembly assembly) { + this.assemblyBeingTranslated = null; + } + private IAssembly assemblyBeingTranslated; + public Dictionary> delegateTypeToDelegates = new Dictionary>(); public void AddDelegate(ITypeDefinition type, IMethodDefinition defn) diff --git a/BCT/BytecodeTranslator/StatementTraverser.cs b/BCT/BytecodeTranslator/StatementTraverser.cs index 24b4b2ca..0e13c510 100644 --- a/BCT/BytecodeTranslator/StatementTraverser.cs +++ b/BCT/BytecodeTranslator/StatementTraverser.cs @@ -50,13 +50,37 @@ namespace BytecodeTranslator return etrav.TranslatedExpressions.Pop(); } + public ICollection/*?*/ TranslateMethod(IMethodDefinition method) { + var methodBody = method.Body as ISourceMethodBody; + if (methodBody == null) return null; + var block = methodBody.Block as BlockStatement; + // TODO: Error if cast fails? + + ICollection newTypes = null; + if (block != null) { + var remover = new AnonymousDelegateRemover(this.sink.host, this.PdbReader); + newTypes = remover.RemoveAnonymousDelegates(methodBody.MethodDefinition, block); + } + this.Visit(methodBody); + return newTypes; + } + #endregion - public override void Visit(IBlockStatement block) { - Bpl.StmtListBuilder slb = new Bpl.StmtListBuilder(); + //public override void Visit(ISourceMethodBody methodBody) { + // var block = methodBody.Block as BlockStatement; + // // TODO: Error if cast fails? + + // if (block != null) { + // var remover = new AnonymousDelegateRemover(this.sink.host, this.PdbReader); + // var newTypes = remover.RemoveAnonymousDelegates(methodBody.MethodDefinition, block); + // } + // base.Visit(methodBody); + //} - foreach (IStatement st in block.Statements) { - this.Visit(st); + public override void Visit(IBlockStatement block) { + foreach (var s in block.Statements) { + this.Visit(s); } } -- cgit v1.2.3 From 86f25cd3795a6653334cd58ccf9eab1eb41c263f Mon Sep 17 00:00:00 2001 From: Mike Barnett Date: Mon, 16 May 2011 14:28:01 -0700 Subject: merge --- BCT/BytecodeTranslator/ExpressionTraverser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BCT/BytecodeTranslator/ExpressionTraverser.cs b/BCT/BytecodeTranslator/ExpressionTraverser.cs index 77050c46..d9ba488e 100644 --- a/BCT/BytecodeTranslator/ExpressionTraverser.cs +++ b/BCT/BytecodeTranslator/ExpressionTraverser.cs @@ -755,7 +755,7 @@ namespace BytecodeTranslator // Simplify the LHS so that all nested dereferences and method calls are broken // up into separate assignments to locals. - var blockExpression = AssignmentSimplifier.Simplify(this.sink, assignment.Target); + var blockExpression = ExpressionSimplifier.Simplify(this.sink, assignment.Target) as IBlockExpression; foreach (var s in blockExpression.BlockStatement.Statements) { this.StmtTraverser.Visit(s); } -- cgit v1.2.3 From 9f52ea312a1d57859ef7ba0e131bca74817b637f Mon Sep 17 00:00:00 2001 From: Mike Barnett Date: Mon, 16 May 2011 14:53:42 -0700 Subject: Fix break. --- BCT/BytecodeTranslator/ExpressionTraverser.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/BCT/BytecodeTranslator/ExpressionTraverser.cs b/BCT/BytecodeTranslator/ExpressionTraverser.cs index d9ba488e..73a5951a 100644 --- a/BCT/BytecodeTranslator/ExpressionTraverser.cs +++ b/BCT/BytecodeTranslator/ExpressionTraverser.cs @@ -755,11 +755,13 @@ namespace BytecodeTranslator // Simplify the LHS so that all nested dereferences and method calls are broken // up into separate assignments to locals. - var blockExpression = ExpressionSimplifier.Simplify(this.sink, assignment.Target) as IBlockExpression; - foreach (var s in blockExpression.BlockStatement.Statements) { - this.StmtTraverser.Visit(s); - } - var target = blockExpression.Expression as ITargetExpression; + //var blockExpression = ExpressionSimplifier.Simplify(this.sink, assignment.Target) as IBlockExpression; + //foreach (var s in blockExpression.BlockStatement.Statements) { + // this.StmtTraverser.Visit(s); + //} + //var target = blockExpression.Expression as ITargetExpression; + + var target = assignment.Target; List args = null; Bpl.Expr arrayExpr = null; @@ -1388,10 +1390,12 @@ namespace BytecodeTranslator }; } +#if EXPERIMENTAL public override ITargetExpression Rewrite(ITargetExpression targetExpression) { Contract.Assume(false, "The expression containing this as a subexpression should never allow a call to this routine."); return null; } +#endif //public override IExpression Rewrite(IMethodCall methodCall) { -- cgit v1.2.3 From 0d5d859e057952fbf38a1a706552b9663a7e8370 Mon Sep 17 00:00:00 2001 From: qadeer Date: Mon, 16 May 2011 16:10:40 -0700 Subject: bug fixes --- BCT/BytecodeTranslator/Sink.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/BCT/BytecodeTranslator/Sink.cs b/BCT/BytecodeTranslator/Sink.cs index 852c3986..97fd4441 100644 --- a/BCT/BytecodeTranslator/Sink.cs +++ b/BCT/BytecodeTranslator/Sink.cs @@ -492,7 +492,7 @@ namespace BytecodeTranslator { boogiePostcondition); decl = proc; } - if (!TypeHelper.GetDefiningUnitReference(method.ContainingType).UnitIdentity.Equals(this.assemblyBeingTranslated.UnitIdentity)) { + if (this.assemblyBeingTranslated != null && !TypeHelper.GetDefiningUnitReference(method.ContainingType).UnitIdentity.Equals(this.assemblyBeingTranslated.UnitIdentity)) { var attrib = new Bpl.QKeyValue(tok, "extern", new List(1), null); decl.Attributes = attrib; } @@ -617,6 +617,10 @@ namespace BytecodeTranslator { t = this.Heap.CreateTypeVariable(type); this.declaredTypes.Add(key, t); this.TranslatedProgram.TopLevelDeclarations.Add(t); + if (this.assemblyBeingTranslated != null && !TypeHelper.GetDefiningUnitReference(type).UnitIdentity.Equals(this.assemblyBeingTranslated.UnitIdentity)) { + var attrib = new Bpl.QKeyValue(Bpl.Token.NoToken, "extern", new List(1), null); + t.Attributes = attrib; + } } return t; } @@ -649,7 +653,7 @@ namespace BytecodeTranslator { public void EndAssembly(IAssembly assembly) { this.assemblyBeingTranslated = null; } - private IAssembly assemblyBeingTranslated; + private IAssembly/*?*/ assemblyBeingTranslated; public Dictionary> delegateTypeToDelegates = new Dictionary>(); -- cgit v1.2.3 From 64e7e8a1194e5bb4667253e301720d4aa55a7dd9 Mon Sep 17 00:00:00 2001 From: Mike Barnett Date: Mon, 16 May 2011 16:41:23 -0700 Subject: Fix command-line option processing. --- BCT/BytecodeTranslator/Program.cs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/BCT/BytecodeTranslator/Program.cs b/BCT/BytecodeTranslator/Program.cs index 471b3431..d04a8783 100644 --- a/BCT/BytecodeTranslator/Program.cs +++ b/BCT/BytecodeTranslator/Program.cs @@ -25,6 +25,9 @@ namespace BytecodeTranslator { [OptionDescription("The names of the assemblies to use as input", ShortForm = "a")] public List assemblies = null; + [OptionDescription("Break into debugger", ShortForm = "break")] + public bool breakIntoDebugger = false; + [OptionDescription("Search paths for assembly dependencies.", ShortForm = "lib")] public List libpaths = new List(); @@ -36,7 +39,7 @@ namespace BytecodeTranslator { public bool wholeProgram = false; [OptionDescription("Stub assembly", ShortForm = "s")] - public List/*?*/ stubAssemblies = null; + public List/*?*/ stub = null; } @@ -47,14 +50,21 @@ namespace BytecodeTranslator { static int Main(string[] args) { int result = 0; + int errorReturnValue = -1; #region Parse options var options = new Options(); options.Parse(args); + if (options.HelpRequested) { + options.PrintOptions(""); + return errorReturnValue; + } if (options.HasErrors) { - if (options.HelpRequested) - options.PrintOptions(""); - return 1; + options.PrintErrorsAndExit(Console.Out); + } + + if (options.breakIntoDebugger) { + System.Diagnostics.Debugger.Break(); } #endregion @@ -81,7 +91,7 @@ namespace BytecodeTranslator { return 1; } - result = TranslateAssembly(assemblyNames, heap, options.libpaths, options.wholeProgram, options.stubAssemblies); + result = TranslateAssembly(assemblyNames, heap, options.libpaths, options.wholeProgram, options.stub); } catch (Exception e) { // swallow everything and just return an error code Console.WriteLine("The byte-code translator failed: {0}", e.Message); -- cgit v1.2.3 From 5dcda744e4ace3b8f627fe98ac837ec10624272d Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 16 May 2011 18:20:08 -0700 Subject: Dafny: To help verifications involving sequences of (boxed) booleans along, added function $IsCanonicalBoolBox --- Binaries/DafnyPrelude.bpl | 79 ++++++++++++++++++++++++---------------------- Source/Dafny/Translator.cs | 23 +++++++++++++- 2 files changed, 63 insertions(+), 39 deletions(-) diff --git a/Binaries/DafnyPrelude.bpl b/Binaries/DafnyPrelude.bpl index 646b16f1..a358e3c0 100644 --- a/Binaries/DafnyPrelude.bpl +++ b/Binaries/DafnyPrelude.bpl @@ -20,14 +20,14 @@ const null: ref; type Set T = [T]bool; -function Set#Empty() returns (Set T); +function Set#Empty(): Set T; axiom (forall o: T :: { Set#Empty()[o] } !Set#Empty()[o]); -function Set#Singleton(T) returns (Set T); +function Set#Singleton(T): Set T; axiom (forall r: T :: { Set#Singleton(r) } Set#Singleton(r)[r]); axiom (forall r: T, o: T :: { Set#Singleton(r)[o] } Set#Singleton(r)[o] <==> r == o); -function Set#UnionOne(Set T, T) returns (Set T); +function Set#UnionOne(Set T, T): Set T; axiom (forall a: Set T, x: T, o: T :: { Set#UnionOne(a,x)[o] } Set#UnionOne(a,x)[o] <==> o == x || a[o]); axiom (forall a: Set T, x: T :: { Set#UnionOne(a, x) } @@ -35,7 +35,7 @@ axiom (forall a: Set T, x: T :: { Set#UnionOne(a, x) } axiom (forall a: Set T, x: T, y: T :: { Set#UnionOne(a, x), a[y] } a[y] ==> Set#UnionOne(a, x)[y]); -function Set#Union(Set T, Set T) returns (Set T); +function Set#Union(Set T, Set T): Set T; axiom (forall a: Set T, b: Set T, o: T :: { Set#Union(a,b)[o] } Set#Union(a,b)[o] <==> a[o] || b[o]); axiom (forall a, b: Set T, y: T :: { Set#Union(a, b), a[y] } @@ -47,7 +47,7 @@ axiom (forall a, b: Set T :: { Set#Union(a, b) } Set#Difference(Set#Union(a, b), a) == b && Set#Difference(Set#Union(a, b), b) == a); -function Set#Intersection(Set T, Set T) returns (Set T); +function Set#Intersection(Set T, Set T): Set T; axiom (forall a: Set T, b: Set T, o: T :: { Set#Intersection(a,b)[o] } Set#Intersection(a,b)[o] <==> a[o] && b[o]); @@ -60,27 +60,27 @@ axiom (forall a, b: Set T :: { Set#Intersection(Set#Intersection(a, b), b) } axiom (forall a, b: Set T :: { Set#Intersection(a, Set#Intersection(a, b)) } Set#Intersection(a, Set#Intersection(a, b)) == Set#Intersection(a, b)); -function Set#Difference(Set T, Set T) returns (Set T); +function Set#Difference(Set T, Set T): Set T; axiom (forall a: Set T, b: Set T, o: T :: { Set#Difference(a,b)[o] } Set#Difference(a,b)[o] <==> a[o] && !b[o]); axiom (forall a, b: Set T, y: T :: { Set#Difference(a, b), b[y] } b[y] ==> !Set#Difference(a, b)[y] ); -function Set#Subset(Set T, Set T) returns (bool); +function Set#Subset(Set T, Set T): bool; axiom(forall a: Set T, b: Set T :: { Set#Subset(a,b) } Set#Subset(a,b) <==> (forall o: T :: {a[o]} {b[o]} a[o] ==> b[o])); -function Set#Equal(Set T, Set T) returns (bool); +function Set#Equal(Set T, Set T): bool; axiom(forall a: Set T, b: Set T :: { Set#Equal(a,b) } Set#Equal(a,b) <==> (forall o: T :: {a[o]} {b[o]} a[o] <==> b[o])); axiom(forall a: Set T, b: Set T :: { Set#Equal(a,b) } // extensionality axiom for sets Set#Equal(a,b) ==> a == b); -function Set#Disjoint(Set T, Set T) returns (bool); +function Set#Disjoint(Set T, Set T): bool; axiom (forall a: Set T, b: Set T :: { Set#Disjoint(a,b) } Set#Disjoint(a,b) <==> (forall o: T :: {a[o]} {b[o]} !a[o] || !b[o])); -function Set#Choose(Set T, TickType) returns (T); +function Set#Choose(Set T, TickType): T; axiom (forall a: Set T, tick: TickType :: { Set#Choose(a, tick) } a != Set#Empty() ==> a[Set#Choose(a, tick)]); @@ -90,25 +90,25 @@ axiom (forall a: Set T, tick: TickType :: { Set#Choose(a, tick) } type Seq T; -function Seq#Length(Seq T) returns (int); +function Seq#Length(Seq T): int; axiom (forall s: Seq T :: { Seq#Length(s) } 0 <= Seq#Length(s)); -function Seq#Empty() returns (Seq T); +function Seq#Empty(): Seq T; axiom (forall :: Seq#Length(Seq#Empty(): Seq T) == 0); axiom (forall s: Seq T :: { Seq#Length(s) } Seq#Length(s) == 0 ==> s == Seq#Empty()); -function Seq#Singleton(T) returns (Seq T); +function Seq#Singleton(T): Seq T; axiom (forall t: T :: { Seq#Length(Seq#Singleton(t)) } Seq#Length(Seq#Singleton(t)) == 1); -function Seq#Build(s: Seq T, index: int, val: T, newLength: int) returns (Seq T); +function Seq#Build(s: Seq T, index: int, val: T, newLength: int): Seq T; axiom (forall s: Seq T, i: int, v: T, len: int :: { Seq#Length(Seq#Build(s,i,v,len)) } 0 <= len ==> Seq#Length(Seq#Build(s,i,v,len)) == len); -function Seq#Append(Seq T, Seq T) returns (Seq T); +function Seq#Append(Seq T, Seq T): Seq T; axiom (forall s0: Seq T, s1: Seq T :: { Seq#Length(Seq#Append(s0,s1)) } Seq#Length(Seq#Append(s0,s1)) == Seq#Length(s0) + Seq#Length(s1)); -function Seq#Index(Seq T, int) returns (T); +function Seq#Index(Seq T, int): T; axiom (forall t: T :: { Seq#Index(Seq#Singleton(t), 0) } Seq#Index(Seq#Singleton(t), 0) == t); axiom (forall s0: Seq T, s1: Seq T, n: int :: { Seq#Index(Seq#Append(s0,s1), n) } (n < Seq#Length(s0) ==> Seq#Index(Seq#Append(s0,s1), n) == Seq#Index(s0, n)) && @@ -118,7 +118,7 @@ axiom (forall s: Seq T, i: int, v: T, len: int, n: int :: { Seq#Index(Seq#Bui (i == n ==> Seq#Index(Seq#Build(s,i,v,len),n) == v) && (i != n ==> Seq#Index(Seq#Build(s,i,v,len),n) == Seq#Index(s,n))); -function Seq#Update(Seq T, int, T) returns (Seq T); +function Seq#Update(Seq T, int, T): Seq T; axiom (forall s: Seq T, i: int, v: T :: { Seq#Length(Seq#Update(s,i,v)) } 0 <= i && i < Seq#Length(s) ==> Seq#Length(Seq#Update(s,i,v)) == Seq#Length(s)); axiom (forall s: Seq T, i: int, v: T, n: int :: { Seq#Index(Seq#Update(s,i,v),n) } @@ -126,7 +126,7 @@ axiom (forall s: Seq T, i: int, v: T, n: int :: { Seq#Index(Seq#Update(s,i,v) (i == n ==> Seq#Index(Seq#Update(s,i,v),n) == v) && (i != n ==> Seq#Index(Seq#Update(s,i,v),n) == Seq#Index(s,n))); -function Seq#Contains(Seq T, T) returns (bool); +function Seq#Contains(Seq T, T): bool; axiom (forall s: Seq T, x: T :: { Seq#Contains(s,x) } Seq#Contains(s,x) <==> (exists i: int :: { Seq#Index(s,i) } 0 <= i && i < Seq#Length(s) && Seq#Index(s,i) == x)); @@ -153,22 +153,22 @@ axiom (forall s: Seq T, n: int, x: T :: (exists i: int :: { Seq#Index(s, i) } 0 <= n && n <= i && i < Seq#Length(s) && Seq#Index(s, i) == x)); -function Seq#Equal(Seq T, Seq T) returns (bool); +function Seq#Equal(Seq T, Seq T): bool; axiom (forall s0: Seq T, s1: Seq T :: { Seq#Equal(s0,s1) } Seq#Equal(s0,s1) <==> Seq#Length(s0) == Seq#Length(s1) && (forall j: int :: { Seq#Index(s0,j) } { Seq#Index(s1,j) } 0 <= j && j < Seq#Length(s0) ==> Seq#Index(s0,j) == Seq#Index(s1,j))); -axiom(forall a: Seq T, b: Seq T :: { Seq#Equal(a,b) } // extensionality axiom for sequences +axiom (forall a: Seq T, b: Seq T :: { Seq#Equal(a,b) } // extensionality axiom for sequences Seq#Equal(a,b) ==> a == b); -function Seq#SameUntil(Seq T, Seq T, int) returns (bool); +function Seq#SameUntil(Seq T, Seq T, int): bool; axiom (forall s0: Seq T, s1: Seq T, n: int :: { Seq#SameUntil(s0,s1,n) } Seq#SameUntil(s0,s1,n) <==> (forall j: int :: { Seq#Index(s0,j) } { Seq#Index(s1,j) } 0 <= j && j < n ==> Seq#Index(s0,j) == Seq#Index(s1,j))); -function Seq#Take(s: Seq T, howMany: int) returns (Seq T); +function Seq#Take(s: Seq T, howMany: int): Seq T; axiom (forall s: Seq T, n: int :: { Seq#Length(Seq#Take(s,n)) } 0 <= n ==> (n <= Seq#Length(s) ==> Seq#Length(Seq#Take(s,n)) == n) && @@ -177,7 +177,7 @@ axiom (forall s: Seq T, n: int, j: int :: { Seq#Index(Seq#Take(s,n), j) } {:w 0 <= j && j < n && j < Seq#Length(s) ==> Seq#Index(Seq#Take(s,n), j) == Seq#Index(s, j)); -function Seq#Drop(s: Seq T, howMany: int) returns (Seq T); +function Seq#Drop(s: Seq T, howMany: int): Seq T; axiom (forall s: Seq T, n: int :: { Seq#Length(Seq#Drop(s,n)) } 0 <= n ==> (n <= Seq#Length(s) ==> Seq#Length(Seq#Drop(s,n)) == Seq#Length(s) - n) && @@ -197,8 +197,8 @@ axiom (forall s, t: Seq T :: type BoxType; -function $Box(T) returns (BoxType); -function $Unbox(BoxType) returns (T); +function $Box(T): BoxType; +function $Unbox(BoxType): T; axiom (forall x: T :: { $Box(x) } $Unbox($Box(x)) == x); axiom (forall b: BoxType :: { $Unbox(b): int } $Box($Unbox(b): int) == b); @@ -206,7 +206,10 @@ axiom (forall b: BoxType :: { $Unbox(b): ref } $Box($Unbox(b): ref) == b); axiom (forall b: BoxType :: { $Unbox(b): Set BoxType } $Box($Unbox(b): Set BoxType) == b); axiom (forall b: BoxType :: { $Unbox(b): Seq BoxType } $Box($Unbox(b): Seq BoxType) == b); axiom (forall b: BoxType :: { $Unbox(b): DatatypeType } $Box($Unbox(b): DatatypeType) == b); -// note: an axiom like this for bool would not be sound +// Note: an axiom like this for bool would not be sound; instead, we do: +function $IsCanonicalBoolBox(BoxType): bool; +axiom $IsCanonicalBoolBox($Box(false)) && $IsCanonicalBoolBox($Box(true)); +axiom (forall b: BoxType :: { $Unbox(b): bool } $IsCanonicalBoolBox(b) ==> $Box($Unbox(b): bool) == b); // --------------------------------------------------------------- // -- Encoding of type names ------------------------------------- @@ -218,12 +221,12 @@ const unique class.bool: ClassName; const unique class.set: ClassName; const unique class.seq: ClassName; -function /*{:never_pattern true}*/ dtype(ref) returns (ClassName); -function /*{:never_pattern true}*/ TypeParams(ref, int) returns (ClassName); +function /*{:never_pattern true}*/ dtype(ref): ClassName; +function /*{:never_pattern true}*/ TypeParams(ref, int): ClassName; -function TypeTuple(a: ClassName, b: ClassName) returns (ClassName); -function TypeTupleCar(ClassName) returns (ClassName); -function TypeTupleCdr(ClassName) returns (ClassName); +function TypeTuple(a: ClassName, b: ClassName): ClassName; +function TypeTupleCar(ClassName): ClassName; +function TypeTupleCdr(ClassName): ClassName; // TypeTuple is injective in both arguments: axiom (forall a: ClassName, b: ClassName :: { TypeTuple(a,b) } TypeTupleCar(TypeTuple(a,b)) == a && @@ -235,13 +238,13 @@ axiom (forall a: ClassName, b: ClassName :: { TypeTuple(a,b) } type DatatypeType; -function /*{:never_pattern true}*/ DtType(DatatypeType) returns (ClassName); // the analog of dtype for datatype values -function /*{:never_pattern true}*/ DtTypeParams(DatatypeType, int) returns (ClassName); // the analog of TypeParams +function /*{:never_pattern true}*/ DtType(DatatypeType): ClassName; // the analog of dtype for datatype values +function /*{:never_pattern true}*/ DtTypeParams(DatatypeType, int): ClassName; // the analog of TypeParams type DtCtorId; -function DatatypeCtorId(DatatypeType) returns (DtCtorId); +function DatatypeCtorId(DatatypeType): DtCtorId; -function DtRank(DatatypeType) returns (int); +function DtRank(DatatypeType): int; // --------------------------------------------------------------- // -- Axiom contexts --------------------------------------------- @@ -274,7 +277,7 @@ axiom (forall f: Field BoxType, i: int :: { MultiIndexField(f,i) } MultiIndexField_Inverse1(MultiIndexField(f,i)) == i); -function DeclType(Field T) returns (ClassName); +function DeclType(Field T): ClassName; // --------------------------------------------------------------- // -- Allocatedness ---------------------------------------------- @@ -339,10 +342,10 @@ type HeapType = [ref,Field alpha]alpha; function {:inline true} read(H:HeapType, r:ref, f:Field alpha): alpha { H[r, f] } function {:inline true} update(H:HeapType, r:ref, f:Field alpha, v:alpha): HeapType { H[r,f := v] } -function $IsGoodHeap(HeapType) returns (bool); +function $IsGoodHeap(HeapType): bool; var $Heap: HeapType where $IsGoodHeap($Heap); -function $HeapSucc(HeapType, HeapType) returns (bool); +function $HeapSucc(HeapType, HeapType): bool; axiom (forall h: HeapType, r: ref, f: Field alpha, x: alpha :: { update(h, r, f, x) } $HeapSucc(h, update(h, r, f, x))); axiom (forall a,b,c: HeapType :: { $HeapSucc(a,b), $HeapSucc(b,c) } diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 253a1f9b..67559aee 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -3676,11 +3676,15 @@ namespace Microsoft.Dafny { Bpl.Expr xSubI = FunctionCall(tok, BuiltinFunction.SeqIndex, predef.BoxType, x, i); Bpl.Expr unbox = ExpressionTranslator.ModeledAsBoxType(st.Arg) ? xSubI : FunctionCall(tok, BuiltinFunction.Unbox, TrType(st.Arg), xSubI); + Bpl.Expr c = GetBoolBoxCondition(xSubI, st.Arg); Bpl.Expr wh = GetWhereClause(tok, unbox, st.Arg, etran); if (wh != null) { + c = BplAnd(c, wh); + } + if (c != Bpl.Expr.True) { Bpl.Expr range = InSeqRange(tok, i, x, true, null, false); Bpl.Trigger tr = new Bpl.Trigger(tok, true, new Bpl.ExprSeq(xSubI)); - return new Bpl.ForallExpr(tok, new Bpl.VariableSeq(iVar), tr, Bpl.Expr.Imp(range, wh)); + return new Bpl.ForallExpr(tok, new Bpl.VariableSeq(iVar), tr, Bpl.Expr.Imp(range, c)); } } else if (type.IsRefType) { @@ -3706,6 +3710,18 @@ namespace Microsoft.Dafny { return null; } + Bpl.Expr GetBoolBoxCondition(Expr box, Type type) { + Contract.Requires(box != null); + Contract.Requires(type != null); + Contract.Ensures(Contract.Result() != null); + + if (type is BoolType) { + return FunctionCall(box.tok, BuiltinFunction.IsCanonicalBoolBox, null, box); + } else { + return Bpl.Expr.True; + } + } + void TrAssignment(IToken tok, Expression lhs, AssignmentRhs rhs, Bpl.StmtListBuilder builder, Bpl.VariableSeq locals, ExpressionTranslator etran) { @@ -4826,6 +4842,7 @@ namespace Microsoft.Dafny { Box, Unbox, + IsCanonicalBoolBox, IsGoodHeap, HeapSucc, @@ -4957,6 +4974,10 @@ namespace Microsoft.Dafny { Contract.Assert(args.Length == 1); Contract.Assert(typeInstantiation != null); return Bpl.Expr.CoerceType(tok, FunctionCall(tok, "$Unbox", typeInstantiation, args), typeInstantiation); + case BuiltinFunction.IsCanonicalBoolBox: + Contract.Assert(args.Length == 1); + Contract.Assert(typeInstantiation == null); + return FunctionCall(tok, "$IsCanonicalBoolBox", Bpl.Type.Bool, args); case BuiltinFunction.IsGoodHeap: Contract.Assert(args.Length == 1); -- cgit v1.2.3 From c76d588dd2768de8505d3d08f43f3de0b5f84dae Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 16 May 2011 18:24:57 -0700 Subject: Dafny: Test case for sequence of boxed booleans --- Test/dafny0/Answer | 2 +- Test/dafny0/SmallTests.dfy | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Test/dafny0/Answer b/Test/dafny0/Answer index 070aa6be..2463b0e5 100644 --- a/Test/dafny0/Answer +++ b/Test/dafny0/Answer @@ -209,7 +209,7 @@ Execution trace: (0,0): anon15 (0,0): anon25_Else -Dafny program verifier finished with 39 verified, 14 errors +Dafny program verifier finished with 41 verified, 14 errors -------------------- Definedness.dfy -------------------- Definedness.dfy(8,7): Error: possible division by zero diff --git a/Test/dafny0/SmallTests.dfy b/Test/dafny0/SmallTests.dfy index ef2049a3..a5f02dc6 100644 --- a/Test/dafny0/SmallTests.dfy +++ b/Test/dafny0/SmallTests.dfy @@ -308,3 +308,13 @@ method QuantifierRange2(a: seq, x: T, y: T, N: int) assert x == y; } } + +// ----------------------- tests that involve sequences of boxed booleans -------- + +ghost method M(zeros: seq, Z: bool) + requires 1 <= |zeros| && Z == false; + requires forall k :: 0 <= k && k < |zeros| ==> zeros[k] == Z; +{ + var x := [Z]; + assert zeros[0..1] == [Z]; +} -- cgit v1.2.3 From eb1d4129ff71a4b1d7752fff7ae7b7fc418490e8 Mon Sep 17 00:00:00 2001 From: CodeplexBot Date: Tue, 17 May 2011 07:55:08 +0200 Subject: Boogie build succeeded --- _admin/Boogie/aste/summary.log | 67 ++++++++++++------------------------------ 1 file changed, 19 insertions(+), 48 deletions(-) diff --git a/_admin/Boogie/aste/summary.log b/_admin/Boogie/aste/summary.log index b837237b..c0185e9c 100644 --- a/_admin/Boogie/aste/summary.log +++ b/_admin/Boogie/aste/summary.log @@ -1,50 +1,23 @@ -# Aste started: 2011-05-14 07:00:16 +# Aste started: 2011-05-17 07:00:09 # Host id: Boogiebox -# [2011-05-14 07:02:49] SpecSharp revision: 5fb7af71cc82 -# [2011-05-14 07:02:49] SscBoogie revision: 5fb7af71cc82 -# [2011-05-14 07:04:08] Boogie revision: 76b4d17abc74 -[2011-05-14 07:06:08] [Error] C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.com Boogie.sln /Rebuild Checked +# [2011-05-17 07:02:41] SpecSharp revision: 5fb7af71cc82 +# [2011-05-17 07:02:41] SscBoogie revision: 5fb7af71cc82 +# [2011-05-17 07:03:56] Boogie revision: e6ac2aed917f +[2011-05-17 07:06:14] C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.com Boogie.sln /Rebuild Checked D:\Temp\aste\Boogie\Source\Core\AbsyType.cs(823,16): warning CS0659: 'Microsoft.Boogie.BasicType' overrides Object.Equals(object o) but does not override Object.GetHashCode() D:\Temp\aste\Boogie\Source\Core\AbsyType.cs(2802,16): warning CS0659: 'Microsoft.Boogie.CtorType' overrides Object.Equals(object o) but does not override Object.GetHashCode() D:\Temp\aste\Boogie\Source\Core\OOLongUtil.cs(109,7): warning CS0162: Unreachable code detected - D:\Temp\aste\Boogie\Source\Core\ResolutionContext.cs(173,7): error CC1016: Contract.Assert/Contract.Assume cannot be used in contract section. Use only Requires and Ensures. D:\Temp\aste\Boogie\Source\Core\Absy.cs(682,7): warning CC1036: Detected call to method 'Graphing.Graph`1.TopologicalSort' without [Pure] in contracts of method 'Microsoft.Boogie.Program.GraphFromImpl(Microsoft.Boogie.Implementation)'. D:\Temp\aste\Boogie\Source\Core\Parser.cs(109,3): warning CC1032: Method 'Microsoft.Boogie.Parser+BvBounds.Resolve(Microsoft.Boogie.ResolutionContext)' overrides 'Microsoft.Boogie.Absy.Resolve(Microsoft.Boogie.ResolutionContext)', thus cannot add Requires. D:\Temp\aste\Boogie\Source\Core\Parser.cs(114,5): warning CC1032: Method 'Microsoft.Boogie.Parser+BvBounds.Emit(Microsoft.Boogie.TokenTextWriter,System.Int32,System.Boolean)' overrides 'Microsoft.Boogie.Expr.Emit(Microsoft.Boogie.TokenTextWriter,System.Int32,System.Boolean)', thus cannot add Requires. D:\Temp\aste\Boogie\Source\Core\Parser.cs(117,65): warning CC1032: Method 'Microsoft.Boogie.Parser+BvBounds.ComputeFreeVariables(Microsoft.Boogie.Set)' overrides 'Microsoft.Boogie.Expr.ComputeFreeVariables(Microsoft.Boogie.Set)', thus cannot add Requires. - C:\Program Files (x86)\Microsoft\Contracts\MsBuild\v4.0\Microsoft.CodeContracts.targets(215,5): error MSB3073: The command ""C:\Program Files (x86)\Microsoft\Contracts\Bin\ccrewrite" /level:4 /rewrite "/assemblyMode=standard" /throwonfailure /libpaths:"D:\Temp\aste\Boogie\Source\AIFramework\bin\Checked\;D:\Temp\aste\Boogie\Source\Basetypes\bin\Checked\;D:\Temp\aste\Boogie\Source\CodeContractsExtender\bin\Checked\;D:\Temp\aste\Boogie\Source\Graph\bin\Checked\;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\Profile\Client\;D:\Temp\aste\Boogie\Source\ParserHelper\bin\Checked\;D:\Temp\aste\Boogie\Source\AIFramework\bin\Checked\CodeContracts;D:\Temp\aste\Boogie\Source\Basetypes\bin\Checked\CodeContracts;D:\Temp\aste\Boogie\Source\CodeContractsExtender\bin\Checked\CodeContracts;D:\Temp\aste\Boogie\Source\Graph\bin\Checked\CodeContracts;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\Profile\Client\CodeContracts;D:\Temp\aste\Boogie\Source\ParserHelper\bin\Checked\CodeContracts;C:\Program Files (x86)\Microsoft\Contracts\Contracts\.NETFramework\v4.0 " "Core.dll"" exited with code 1. - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Core\bin\Checked\Core.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Core\bin\Checked\Core.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCExpr\bin\Checked\VCExpr.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Core\bin\Checked\Core.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCGeneration\bin\Checked\VCGeneration.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCExpr\bin\Checked\VCExpr.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Core\bin\Checked\Core.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCGeneration\bin\Checked\VCGeneration.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCExpr\bin\Checked\VCExpr.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Core\bin\Checked\Core.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCGeneration\bin\Checked\VCGeneration.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCExpr\bin\Checked\VCExpr.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Provers\Simplify\bin\Checked\Provers.Simplify.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Core\bin\Checked\Core.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCGeneration\bin\Checked\VCGeneration.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCExpr\bin\Checked\VCExpr.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Provers\Simplify\bin\Checked\Provers.Simplify.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Core\bin\Checked\Core.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCGeneration\bin\Checked\VCGeneration.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCExpr\bin\Checked\VCExpr.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Provers\Simplify\bin\Checked\Provers.Simplify.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\AbsInt\bin\Checked\AbsInt.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Core\bin\Checked\Core.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\VCGeneration\bin\Checked\VCGeneration.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Provers\Z3\bin\Checked\Provers.Z3.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Provers\TPTP\bin\Checked\Provers.TPTP.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Provers\Isabelle\bin\Checked\Provers.Isabelle.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Provers\Simplify\bin\Checked\Provers.Simplify.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Provers\SMTLib\bin\Checked\Provers.SMTLib.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\AbsInt\bin\Checked\AbsInt.dll' could not be found - error CS0006: Metadata file 'D:\Temp\aste\Boogie\Source\Core\bin\Checked\Core.dll' could not be found + D:\Temp\aste\Boogie\Source\AbsInt\ExprFactories.cs(111,7): warning CS0162: Unreachable code detected + D:\Temp\aste\Boogie\Source\AbsInt\ExprFactories.cs(290,7): warning CS0162: Unreachable code detected + D:\Temp\aste\Boogie\Source\AbsInt\ExprFactories.cs(309,7): warning CS0162: Unreachable code detected + D:\Temp\aste\Boogie\Source\VCGeneration\VC.cs(1662,11): warning CS0162: Unreachable code detected + D:\Temp\aste\Boogie\Source\VCGeneration\VC.cs(1822,11): warning CS0162: Unreachable code detected + D:\Temp\aste\Boogie\Source\VCGeneration\StratifiedVC.cs(662,17): warning CC1032: Method 'VC.StratifiedVCGen+NormalChecker.CheckVC' overrides 'VC.StratifiedVCGen+StratifiedCheckerInterface.CheckVC', thus cannot add Requires. warning CS0659: 'Microsoft.Boogie.BasicType' overrides Object.Equals(object o) but does not override Object.GetHashCode() warning CS0659: 'Microsoft.Boogie.CtorType' overrides Object.Equals(object o) but does not override Object.GetHashCode() warning CS0162: Unreachable code detected @@ -52,13 +25,11 @@ warning CC1032: Method 'Microsoft.Boogie.Parser+BvBounds.Resolve(Microsoft.Boogie.ResolutionContext)' overrides 'Microsoft.Boogie.Absy.Resolve(Microsoft.Boogie.ResolutionContext)', thus cannot add Requires. warning CC1032: Method 'Microsoft.Boogie.Parser+BvBounds.Emit(Microsoft.Boogie.TokenTextWriter,System.Int32,System.Boolean)' overrides 'Microsoft.Boogie.Expr.Emit(Microsoft.Boogie.TokenTextWriter,System.Int32,System.Boolean)', thus cannot add Requires. warning CC1032: Method 'Microsoft.Boogie.Parser+BvBounds.ComputeFreeVariables(Microsoft.Boogie.Set)' overrides 'Microsoft.Boogie.Expr.ComputeFreeVariables(Microsoft.Boogie.Set)', thus cannot add Requires. - 1 error - 1 error - 2 error - 3 error - 3 error - 4 error - 4 error - 5 error - 8 error - 10 failed + warning CS0162: Unreachable code detected + warning CS0162: Unreachable code detected + warning CS0162: Unreachable code detected + warning CS0162: Unreachable code detected + warning CS0162: Unreachable code detected + warning CC1032: Method 'VC.StratifiedVCGen+NormalChecker.CheckVC' overrides 'VC.StratifiedVCGen+StratifiedCheckerInterface.CheckVC', thus cannot add Requires. +[2011-05-17 07:54:06] 0 out of 30 test(s) in D:\Temp\aste\Boogie\Test\alltests.txt failed +# [2011-05-17 07:55:08] Released nightly of Boogie -- cgit v1.2.3 From a80851ee6dd439c8f5894b80481837ce41d6fb55 Mon Sep 17 00:00:00 2001 From: qadeer Date: Tue, 17 May 2011 12:45:58 -0700 Subject: added spec for GetType --- BCT/BytecodeTranslator/Heap.cs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/BCT/BytecodeTranslator/Heap.cs b/BCT/BytecodeTranslator/Heap.cs index f38e0093..78774876 100644 --- a/BCT/BytecodeTranslator/Heap.cs +++ b/BCT/BytecodeTranslator/Heap.cs @@ -39,7 +39,6 @@ var $Heap: HeapType; var $Alloc: [Ref] bool; procedure {:inline 1} Alloc() returns (x: Ref) - free ensures x != null; modifies $Alloc; { assume $Alloc[x] == false && x != null; @@ -57,6 +56,11 @@ axiom (forall x: bool :: { Bool2Box(x) } Box2Bool(Bool2Box(x)) == x ); axiom (forall x: Ref :: { Ref2Box(x) } Box2Ref(Ref2Box(x)) == x ); axiom (forall x: Struct :: { Struct2Box(x) } Box2Struct(Struct2Box(x)) == x ); +procedure {:inline 1} System.Object.GetType(this: Ref) returns ($result: Ref) +{ + $result := $TypeOf($DynamicType(this)); +} + "; private Sink sink; @@ -83,6 +87,7 @@ axiom (forall x: Struct :: { Struct2Box(x) } Box2Struct(Struct2Box(x)) == x ); public override Bpl.Variable CreateFieldVariable(IFieldReference field) { Bpl.Variable v; string fieldname = TypeHelper.GetTypeName(field.ContainingType) + "." + field.Name.Value; + fieldname = TranslationHelper.TurnStringIntoValidIdentifier(fieldname); Bpl.IToken tok = field.Token(); Bpl.Type t = this.sink.CciTypeToBoogie(field.Type.ResolvedType); @@ -101,6 +106,7 @@ axiom (forall x: Struct :: { Struct2Box(x) } Box2Struct(Struct2Box(x)) == x ); public override Bpl.Variable CreateEventVariable(IEventDefinition e) { Bpl.Variable v; string eventName = TypeHelper.GetTypeName(e.ContainingType) + "." + e.Name.Value; + eventName = TranslationHelper.TurnStringIntoValidIdentifier(eventName); Bpl.IToken tok = e.Token(); Bpl.Type t = this.sink.CciTypeToBoogie(e.Type.ResolvedType); @@ -109,7 +115,7 @@ axiom (forall x: Struct :: { Struct2Box(x) } Box2Struct(Struct2Box(x)) == x ); v = new Bpl.GlobalVariable(tok, tident); } else { - Bpl.Type mt = new Bpl.MapType(tok, new Bpl.TypeVariableSeq(), new Bpl.TypeSeq(Bpl.Type.Int), t); + Bpl.Type mt = new Bpl.MapType(tok, new Bpl.TypeVariableSeq(), new Bpl.TypeSeq(this.RefType), t); Bpl.TypedIdent tident = new Bpl.TypedIdent(tok, eventName, mt); v = new Bpl.GlobalVariable(tok, tident); } @@ -126,11 +132,11 @@ axiom (forall x: Struct :: { Struct2Box(x) } Box2Struct(Struct2Box(x)) == x ); /// public override Bpl.Expr ReadHeap(Bpl.Expr/*?*/ o, Bpl.Expr f, AccessType accessType, Bpl.Type unboxType) { if (accessType == AccessType.Struct) - return Bpl.Expr.Select(o, f); + return Unbox(f.tok, unboxType, Bpl.Expr.Select(o, f)); else if (accessType == AccessType.Heap) return Bpl.Expr.Select(f, o); else - return Bpl.Expr.Select(Bpl.Expr.Select(Bpl.Expr.Ident(ArrayContentsVariable), o), f); + return Unbox(f.tok, unboxType, Bpl.Expr.Select(Bpl.Expr.Select(Bpl.Expr.Ident(ArrayContentsVariable), o), f)); } /// @@ -140,11 +146,11 @@ axiom (forall x: Struct :: { Struct2Box(x) } Box2Struct(Struct2Box(x)) == x ); public override Bpl.Cmd WriteHeap(Bpl.IToken tok, Bpl.Expr/*?*/ o, Bpl.Expr f, Bpl.Expr value, AccessType accessType, Bpl.Type boxType) { Debug.Assert(o != null); if (accessType == AccessType.Struct) - return Bpl.Cmd.MapAssign(tok, (Bpl.IdentifierExpr)o, f, value); + return Bpl.Cmd.MapAssign(tok, (Bpl.IdentifierExpr)o, f, Box(f.tok, boxType, value)); else if (accessType == AccessType.Heap) return Bpl.Cmd.MapAssign(tok, (Bpl.IdentifierExpr)f, o, value); else - return TranslationHelper.BuildAssignCmd(Bpl.Expr.Ident(ArrayContentsVariable), Bpl.Expr.Store(Bpl.Expr.Ident(ArrayContentsVariable), o, Bpl.Expr.Store(Bpl.Expr.Select(Bpl.Expr.Ident(ArrayContentsVariable), o), f, value))); + return TranslationHelper.BuildAssignCmd(Bpl.Expr.Ident(ArrayContentsVariable), Bpl.Expr.Store(Bpl.Expr.Ident(ArrayContentsVariable), o, Bpl.Expr.Store(Bpl.Expr.Select(Bpl.Expr.Ident(ArrayContentsVariable), o), f, Box(f.tok, boxType, value)))); } } @@ -176,7 +182,6 @@ type HeapType = [Ref,Field]Box; var $Alloc: [Ref] bool; procedure {:inline 1} Alloc() returns (x: Ref) - free ensures x != null; modifies $Alloc; { assume $Alloc[x] == false && x != null; @@ -194,6 +199,11 @@ axiom (forall x: bool :: { Bool2Box(x) } Box2Bool(Bool2Box(x)) == x ); axiom (forall x: Ref :: { Ref2Box(x) } Box2Ref(Ref2Box(x)) == x ); axiom (forall x: Struct :: { Struct2Box(x) } Box2Struct(Struct2Box(x)) == x ); +procedure {:inline 1} System.Object.GetType(this: Ref) returns ($result: Ref) +{ + $result := $TypeOf($DynamicType(this)); +} + "; private Sink sink; @@ -241,6 +251,7 @@ axiom (forall x: Struct :: { Struct2Box(x) } Box2Struct(Struct2Box(x)) == x ); public override Bpl.Variable CreateEventVariable(IEventDefinition e) { Bpl.Variable v; string fieldname = TypeHelper.GetTypeName(e.ContainingType) + "." + e.Name.Value; + fieldname = TranslationHelper.TurnStringIntoValidIdentifier(fieldname); Bpl.IToken tok = e.Token(); if (e.Adder.ResolvedMethod.IsStatic) { -- cgit v1.2.3 From 4406a3565886e88c7c573463d815b488afaa4505 Mon Sep 17 00:00:00 2001 From: Mike Barnett Date: Tue, 17 May 2011 15:20:14 -0700 Subject: If a method has been translated as a function, generate a function call and not a procedure call for calls to the method. Use the Location (full path) instead of just the file name for source locations. --- BCT/BytecodeTranslator/ExpressionTraverser.cs | 64 ++++++++++++++++----------- BCT/BytecodeTranslator/MetadataTraverser.cs | 9 ++-- BCT/BytecodeTranslator/StatementTraverser.cs | 2 +- 3 files changed, 46 insertions(+), 29 deletions(-) diff --git a/BCT/BytecodeTranslator/ExpressionTraverser.cs b/BCT/BytecodeTranslator/ExpressionTraverser.cs index 73a5951a..d1708608 100644 --- a/BCT/BytecodeTranslator/ExpressionTraverser.cs +++ b/BCT/BytecodeTranslator/ExpressionTraverser.cs @@ -544,27 +544,30 @@ namespace BytecodeTranslator } #endregion - Bpl.IToken cloc = methodCall.Token(); - if (resolvedMethod.Type.ResolvedType.TypeCode != PrimitiveTypeCode.Void) { - Bpl.Variable v = this.sink.CreateFreshLocal(methodCall.Type.ResolvedType); - Bpl.IdentifierExpr unboxed = new Bpl.IdentifierExpr(cloc, v); - if (resolvedMethod.Type is IGenericTypeParameter) { - Bpl.IdentifierExpr boxed = Bpl.Expr.Ident(this.sink.CreateFreshLocal(this.sink.Heap.BoxType)); - toBoxed[unboxed] = boxed; - outvars.Add(boxed); - } - else { - outvars.Add(unboxed); - } - TranslatedExpressions.Push(unboxed); - } var proc = this.sink.FindOrCreateProcedure(resolvedMethod); string methodname = proc.Name; - + var translateAsFunctionCall = proc is Bpl.Function; + Bpl.IToken cloc = methodCall.Token(); Bpl.QKeyValue attrib = null; - foreach (var a in resolvedMethod.Attributes) { - if (TypeHelper.GetTypeName(a.Type).EndsWith("AsyncAttribute")) { - attrib = new Bpl.QKeyValue(cloc, "async", new List(), null); + + if (!translateAsFunctionCall) { + if (resolvedMethod.Type.ResolvedType.TypeCode != PrimitiveTypeCode.Void) { + Bpl.Variable v = this.sink.CreateFreshLocal(methodCall.Type.ResolvedType); + Bpl.IdentifierExpr unboxed = new Bpl.IdentifierExpr(cloc, v); + if (resolvedMethod.Type is IGenericTypeParameter) { + Bpl.IdentifierExpr boxed = Bpl.Expr.Ident(this.sink.CreateFreshLocal(this.sink.Heap.BoxType)); + toBoxed[unboxed] = boxed; + outvars.Add(boxed); + } else { + outvars.Add(unboxed); + } + TranslatedExpressions.Push(unboxed); + } + + foreach (var a in resolvedMethod.Attributes) { + if (TypeHelper.GetTypeName(a.Type).EndsWith("AsyncAttribute")) { + attrib = new Bpl.QKeyValue(cloc, "async", new List(), null); + } } } @@ -599,13 +602,24 @@ namespace BytecodeTranslator else { this.StmtTraverser.StmtBuilder.Add(this.sink.Heap.WriteHeap(methodCall.Token(), thisExpr, Bpl.Expr.Ident(eventVar), Bpl.Expr.Ident(local), resolvedMethod.ContainingType.ResolvedType.IsStruct ? AccessType.Struct : AccessType.Heap, local.TypedIdent.Type)); } - } - else { - if (attrib != null) - call = new Bpl.CallCmd(cloc, methodname, inexpr, outvars, attrib); - else - call = new Bpl.CallCmd(cloc, methodname, inexpr, outvars); - this.StmtTraverser.StmtBuilder.Add(call); + } else { + if (translateAsFunctionCall) { + var func = proc as Bpl.Function; + var exprSeq = new Bpl.ExprSeq(); + foreach (var e in inexpr) { + exprSeq.Add(e); + } + var callFunction = new Bpl.NAryExpr(cloc, new Bpl.FunctionCall(func), exprSeq); + this.TranslatedExpressions.Push(callFunction); + return; + + } else { + if (attrib != null) + call = new Bpl.CallCmd(cloc, methodname, inexpr, outvars, attrib); + else + call = new Bpl.CallCmd(cloc, methodname, inexpr, outvars); + this.StmtTraverser.StmtBuilder.Add(call); + } } foreach (KeyValuePair kv in toBoxed) { diff --git a/BCT/BytecodeTranslator/MetadataTraverser.cs b/BCT/BytecodeTranslator/MetadataTraverser.cs index b1be6294..e300d3b5 100644 --- a/BCT/BytecodeTranslator/MetadataTraverser.cs +++ b/BCT/BytecodeTranslator/MetadataTraverser.cs @@ -289,6 +289,8 @@ namespace BytecodeTranslator { Bpl.VariableSeq vseq = new Bpl.VariableSeq(vars.ToArray()); #endregion + var translatedBody = stmtTraverser.StmtBuilder.Collect(Bpl.Token.NoToken); + #region Add implementation to Boogie program if (proc != null) { Bpl.Implementation impl = @@ -298,13 +300,14 @@ namespace BytecodeTranslator { decl.InParams, decl.OutParams, vseq, - stmtTraverser.StmtBuilder.Collect(Bpl.Token.NoToken)); + translatedBody); impl.Proc = proc; this.sink.TranslatedProgram.TopLevelDeclarations.Add(impl); } else { // method is translated as a function - //Bpl.Function func = decl as Bpl.Function; - //func.Body = new Bpl.CodeExpr(new Bpl.VariableSeq(), new List{ new Bpl.Block( + //var func = decl as Bpl.Function; + //Contract.Assume(func != null); + //func.Body = new Bpl.CodeExpr(new Bpl.VariableSeq(), translatedBody.BigBlocks); } #endregion diff --git a/BCT/BytecodeTranslator/StatementTraverser.cs b/BCT/BytecodeTranslator/StatementTraverser.cs index 0e13c510..d26f540d 100644 --- a/BCT/BytecodeTranslator/StatementTraverser.cs +++ b/BCT/BytecodeTranslator/StatementTraverser.cs @@ -97,7 +97,7 @@ namespace BytecodeTranslator if (this.PdbReader != null) { var slocs = this.PdbReader.GetClosestPrimarySourceLocationsFor(statement.Locations); foreach (var sloc in slocs) { - fileName = sloc.Document.Name.Value; + fileName = sloc.Document.Location; lineNumber = sloc.StartLine; break; } -- cgit v1.2.3 From 23b62f2245437788238f93c63226d29f5526e12e Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 17 May 2011 16:57:37 -0700 Subject: added another axiom --- BCT/BytecodeTranslator/Heap.cs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/BCT/BytecodeTranslator/Heap.cs b/BCT/BytecodeTranslator/Heap.cs index 78774876..7ab72e82 100644 --- a/BCT/BytecodeTranslator/Heap.cs +++ b/BCT/BytecodeTranslator/Heap.cs @@ -60,6 +60,17 @@ procedure {:inline 1} System.Object.GetType(this: Ref) returns ($result: Ref) { $result := $TypeOf($DynamicType(this)); } +axiom (forall t: Type :: $DynamicType($TypeOf(t)) == t); + +function $ThreadDelegate(Ref) : Ref; +procedure {:inline 1} System.Threading.Thread.#ctor$System.Threading.ParameterizedThreadStart(this: Ref, start$in: Ref) +{ + assume $ThreadDelegate(this) == start$in; +} +procedure {:inline 1} System.Threading.Thread.Start$System.Object(this: Ref, parameter$in: Ref) +{ + call {:async} System.Threading.ParameterizedThreadStart.Invoke$System.Object($ThreadDelegate(this), parameter$in); +} "; private Sink sink; @@ -203,6 +214,17 @@ procedure {:inline 1} System.Object.GetType(this: Ref) returns ($result: Ref) { $result := $TypeOf($DynamicType(this)); } +axiom (forall t: Type :: $DynamicType($TypeOf(t)) == t); + +function $ThreadDelegate(Ref) : Ref; +procedure {:inline 1} System.Threading.Thread.#ctor$System.Threading.ParameterizedThreadStart(this: Ref, start$in: Ref) +{ + assume $ThreadDelegate(this) == start$in; +} +procedure {:inline 1} System.Threading.Thread.Start$System.Object(this: Ref, parameter$in: Ref) +{ + call {:async} System.Threading.ParameterizedThreadStart.Invoke$System.Object($ThreadDelegate(this), parameter$in); +} "; private Sink sink; -- cgit v1.2.3 From f90a2fd212c8e4893b37aa9bfa5e6ed70d882702 Mon Sep 17 00:00:00 2001 From: qadeer Date: Tue, 17 May 2011 23:01:04 -0700 Subject: Fixed array construction Added a couple more stubs --- BCT/BytecodeTranslator/ExpressionTraverser.cs | 32 +++++++++++++-------------- BCT/BytecodeTranslator/Heap.cs | 24 +++++++++++++++++++- BCT/BytecodeTranslator/HeapFactory.cs | 4 ++-- 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/BCT/BytecodeTranslator/ExpressionTraverser.cs b/BCT/BytecodeTranslator/ExpressionTraverser.cs index d1708608..052d8bf4 100644 --- a/BCT/BytecodeTranslator/ExpressionTraverser.cs +++ b/BCT/BytecodeTranslator/ExpressionTraverser.cs @@ -883,7 +883,21 @@ namespace BytecodeTranslator public override void Visit(ICreateArray createArrayInstance) { - TranslateArrayCreation(createArrayInstance); + Bpl.IToken cloc = createArrayInstance.Token(); + var a = this.sink.CreateFreshLocal(createArrayInstance.Type); + + Debug.Assert(createArrayInstance.Rank > 0); + Bpl.Expr lengthExpr = Bpl.Expr.Literal(1); + foreach (IExpression expr in createArrayInstance.Sizes) { + this.Visit(expr); + lengthExpr = Bpl.Expr.Mul(lengthExpr, TranslatedExpressions.Pop()); + } + + // First generate an Alloc() call + this.StmtTraverser.StmtBuilder.Add(new Bpl.CallCmd(cloc, this.sink.AllocationMethodName, new Bpl.ExprSeq(), new Bpl.IdentifierExprSeq(Bpl.Expr.Ident(a)))); + Bpl.Expr assumeExpr = Bpl.Expr.Eq(new Bpl.NAryExpr(cloc, new Bpl.FunctionCall(this.sink.Heap.ArrayLengthFunction), new Bpl.ExprSeq(Bpl.Expr.Ident(a))), lengthExpr); + this.StmtTraverser.StmtBuilder.Add(new Bpl.AssumeCmd(cloc, assumeExpr)); + TranslatedExpressions.Push(Bpl.Expr.Ident(a)); } public override void Visit(ICreateDelegateInstance createDelegateInstance) @@ -898,18 +912,6 @@ namespace BytecodeTranslator TranslateDelegateCreation(createDelegateInstance.MethodToCallViaDelegate, createDelegateInstance.Type, createDelegateInstance); } - private void TranslateArrayCreation(IExpression creationAST) - { - Bpl.IToken cloc = creationAST.Token(); - - var a = this.sink.CreateFreshLocal(creationAST.Type); - - // First generate an Alloc() call - this.StmtTraverser.StmtBuilder.Add(new Bpl.CallCmd(cloc, this.sink.AllocationMethodName, new Bpl.ExprSeq(), new Bpl.IdentifierExprSeq(Bpl.Expr.Ident(a)))); - - TranslatedExpressions.Push(Bpl.Expr.Ident(a)); - } - private void TranslateObjectCreation(IMethodReference ctor, IEnumerable arguments, IExpression creationAST) { var resolvedMethod = Sink.Unspecialize(ctor).ResolvedMethod; @@ -1287,9 +1289,7 @@ namespace BytecodeTranslator public override void Visit(IVectorLength vectorLength) { base.Visit(vectorLength.Vector); var e = TranslatedExpressions.Pop(); - TranslatedExpressions.Push( - Bpl.Expr.Select(new Bpl.IdentifierExpr(vectorLength.Token(), this.sink.Heap.ArrayLengthVariable), new Bpl.Expr[] { e }) - ); + TranslatedExpressions.Push(new Bpl.NAryExpr(vectorLength.Token(), new Bpl.FunctionCall(this.sink.Heap.ArrayLengthFunction), new Bpl.ExprSeq(e))); } #endregion diff --git a/BCT/BytecodeTranslator/Heap.cs b/BCT/BytecodeTranslator/Heap.cs index 7ab72e82..28964c15 100644 --- a/BCT/BytecodeTranslator/Heap.cs +++ b/BCT/BytecodeTranslator/Heap.cs @@ -63,6 +63,7 @@ procedure {:inline 1} System.Object.GetType(this: Ref) returns ($result: Ref) axiom (forall t: Type :: $DynamicType($TypeOf(t)) == t); function $ThreadDelegate(Ref) : Ref; + procedure {:inline 1} System.Threading.Thread.#ctor$System.Threading.ParameterizedThreadStart(this: Ref, start$in: Ref) { assume $ThreadDelegate(this) == start$in; @@ -71,6 +72,17 @@ procedure {:inline 1} System.Threading.Thread.Start$System.Object(this: Ref, par { call {:async} System.Threading.ParameterizedThreadStart.Invoke$System.Object($ThreadDelegate(this), parameter$in); } +procedure {:extern} System.Threading.ParameterizedThreadStart.Invoke$System.Object(this: Ref, obj$in: Ref); + +procedure {:inline 1} System.Threading.Thread.#ctor$System.Threading.ThreadStart(this: Ref, start$in: Ref) +{ + assume $ThreadDelegate(this) == start$in; +} +procedure {:inline 1} System.Threading.Thread.Start(this: Ref) +{ + call {:async} System.Threading.ThreadStart.Invoke($ThreadDelegate(this)); +} +procedure {:extern} System.Threading.ThreadStart.Invoke(this: Ref); "; private Sink sink; @@ -87,7 +99,7 @@ procedure {:inline 1} System.Threading.Thread.Start$System.Object(this: Ref, par this.TypeType = new Bpl.CtorType(this.TypeTypeDecl.tok, this.TypeTypeDecl, new Bpl.TypeSeq()); this.RefType = new Bpl.CtorType(this.RefTypeDecl.tok, this.RefTypeDecl, new Bpl.TypeSeq()); this.RealType = new Bpl.CtorType(this.RealTypeDecl.tok, this.RealTypeDecl, new Bpl.TypeSeq()); - } + } return b; } @@ -225,7 +237,17 @@ procedure {:inline 1} System.Threading.Thread.Start$System.Object(this: Ref, par { call {:async} System.Threading.ParameterizedThreadStart.Invoke$System.Object($ThreadDelegate(this), parameter$in); } +procedure {:extern} System.Threading.ParameterizedThreadStart.Invoke$System.Object(this: Ref, obj$in: Ref); +procedure {:inline 1} System.Threading.Thread.#ctor$System.Threading.ThreadStart(this: Ref, start$in: Ref) +{ + assume $ThreadDelegate(this) == start$in; +} +procedure {:inline 1} System.Threading.Thread.Start(this: Ref) +{ + call {:async} System.Threading.ThreadStart.Invoke($ThreadDelegate(this)); +} +procedure {:extern} System.Threading.ThreadStart.Invoke(this: Ref); "; private Sink sink; diff --git a/BCT/BytecodeTranslator/HeapFactory.cs b/BCT/BytecodeTranslator/HeapFactory.cs index 6d03ef9f..004d4224 100644 --- a/BCT/BytecodeTranslator/HeapFactory.cs +++ b/BCT/BytecodeTranslator/HeapFactory.cs @@ -68,8 +68,8 @@ namespace BytecodeTranslator { { [RepresentationFor("$ArrayContents", "var $ArrayContents: [Ref][int]Box;")] public Bpl.Variable ArrayContentsVariable = null; - [RepresentationFor("$ArrayLength", "var $ArrayLength: [Ref]int;")] - public Bpl.Variable ArrayLengthVariable = null; + [RepresentationFor("$ArrayLength", "function $ArrayLength(Ref): int;")] + public Bpl.Function ArrayLengthFunction = null; public abstract Bpl.Variable CreateFieldVariable(IFieldReference field); -- cgit v1.2.3 From 4baa0fad00861977f7ab9b11161adb1cb0d691cf Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 18 May 2011 17:19:18 -0700 Subject: Dafny: added set comprehension expressions --- Binaries/DafnyRuntime.cs | 34 ++++++++++---- Source/Dafny/Compiler.cs | 65 +++++++++++++++++++++++++- Source/Dafny/Dafny.atg | 26 +++++++++++ Source/Dafny/DafnyAst.cs | 24 ++++++++-- Source/Dafny/Parser.cs | 90 ++++++++++++++++++++++++------------ Source/Dafny/Printer.cs | 23 +++++++++- Source/Dafny/Resolver.cs | 60 +++++++++++++++++------- Source/Dafny/Scanner.cs | 102 ++++++++++++++++++++--------------------- Source/Dafny/Translator.cs | 99 ++++++++++++++++++++++++++++++--------- Test/dafny0/Answer | 13 ++++++ Test/dafny0/Comprehensions.dfy | 40 ++++++++++++++++ Test/dafny0/runtest.bat | 1 + 12 files changed, 441 insertions(+), 136 deletions(-) create mode 100644 Test/dafny0/Comprehensions.dfy diff --git a/Binaries/DafnyRuntime.cs b/Binaries/DafnyRuntime.cs index b10aeac9..46a3f3df 100644 --- a/Binaries/DafnyRuntime.cs +++ b/Binaries/DafnyRuntime.cs @@ -18,9 +18,16 @@ namespace Dafny public static Set FromElements(params T[] values) { Dictionary d = new Dictionary(values.Length); foreach (T t in values) - d.Add(t, true); + d[t] = true; return new Set(d); } + public static Set FromCollection(ICollection values) { + Dictionary d = new Dictionary(); + foreach (T t in values) + d[t] = true; + return new Set(d); + } + public IEnumerable Elements { get { return dict.Keys; @@ -98,7 +105,7 @@ namespace Dafny } else { a = other.dict; b = dict; } - Dictionary r = new Dictionary(); + var r = new Dictionary(); foreach (T t in a.Keys) { if (b.ContainsKey(t)) r.Add(t, true); @@ -110,7 +117,7 @@ namespace Dafny return this; else if (other.dict.Count == 0) return this; - Dictionary r = new Dictionary(); + var r = new Dictionary(); foreach (T t in dict.Keys) { if (!other.dict.ContainsKey(t)) r.Add(t, true); @@ -226,6 +233,7 @@ namespace Dafny } } public partial class Helpers { + // Computing forall/exists quantifiers public static bool QuantBool(bool frall, System.Predicate pred) { if (frall) { return pred(false) && pred(true); @@ -239,17 +247,25 @@ namespace Dafny } return frall; } - public static bool QuantSet(Dafny.Set set, bool frall, System.Predicate pred) { - foreach (var t in set.Elements) { - if (pred(t) != frall) { return !frall; } + public static bool QuantSet(Dafny.Set set, bool frall, System.Predicate pred) { + foreach (var u in set.Elements) { + if (pred(u) != frall) { return !frall; } } return frall; } - public static bool QuantSeq(Dafny.Sequence seq, bool frall, System.Predicate pred) { - foreach (var t in seq.Elements) { - if (pred(t) != frall) { return !frall; } + public static bool QuantSeq(Dafny.Sequence seq, bool frall, System.Predicate pred) { + foreach (var u in seq.Elements) { + if (pred(u) != frall) { return !frall; } } return frall; } + // Enumerating other collections + public delegate Dafny.Set ComprehensionDelegate(); + public static IEnumerable AllBooleans { + get { + yield return false; + yield return true; + } + } } } diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index 4d352f65..f1a649ee 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -1306,7 +1306,7 @@ namespace Microsoft.Dafny { Contract.Assert(e.Bounds != null); // for non-ghost quantifiers, the resolver would have insisted on finding bounds var n = e.BoundVars.Count; Contract.Assert(e.Bounds.Count == n); - for (int i = n; 0 <= --i; ) { + for (int i = 0; i < n; i++) { var bound = e.Bounds[i]; var bv = e.BoundVars[i]; // emit: Dafny.Helpers.QuantX(boundsInformation, isForall, bv => body) @@ -1339,7 +1339,68 @@ namespace Microsoft.Dafny { for (int i = 0; i < n; i++) { wr.Write(")"); } - + + } else if (expr is SetComprehension) { + var e = (SetComprehension)expr; + // For "set i,j,k,l | R(i,j,k,l) :: Term(i,j,k,l)" where the term has type "G", emit something like: + // ((ComprehensionDelegate)delegate() { + // var _coll = new List(); + // foreach (L l in sq.Elements) { + // foreach (K k in st.Elements) { + // for (BigInteger j = Lo; j < Hi; j++) { + // for (bool i in Helper.AllBooleans) { + // if (R(i,j,k,l)) { + // _coll.Add(Term(i,j,k,l)); + // } + // } + // } + // } + // } + // return Dafny.Set.FromCollection(_coll); + // })() + Contract.Assert(e.Bounds != null); // the resolver would have insisted on finding bounds + var typeName = TypeName(((SetType)e.Type).Arg); + wr.Write("((Dafny.Helpers.ComprehensionDelegate<{0}>)delegate() {{ ", typeName); + wr.Write("var _coll = new System.Collections.Generic.List<{0}>(); ", typeName); + var n = e.BoundVars.Count; + Contract.Assert(e.Bounds.Count == n); + for (int i = 0; i < n; i++) { + var bound = e.Bounds[i]; + var bv = e.BoundVars[i]; + if (bound is QuantifierExpr.BoolBoundedPool) { + wr.Write("foreach (var @{0} in Dafny.Helpers.AllBooleans) {{ ", bv.Name); + } else if (bound is QuantifierExpr.IntBoundedPool) { + var b = (QuantifierExpr.IntBoundedPool)bound; + wr.Write("for (var @{0} = ", bv.Name); + TrExpr(b.LowerBound); + wr.Write("; @{0} < ", bv.Name); + TrExpr(b.UpperBound); + wr.Write("; @{0}++) {{ ", bv.Name); + } else if (bound is QuantifierExpr.SetBoundedPool) { + var b = (QuantifierExpr.SetBoundedPool)bound; + wr.Write("foreach (var @{0} in (", bv.Name); + TrExpr(b.Set); + wr.Write(").Elements) { "); + } else if (bound is QuantifierExpr.SeqBoundedPool) { + var b = (QuantifierExpr.SeqBoundedPool)bound; + wr.Write("foreach (var @{0} in (", bv.Name); + TrExpr(b.Seq); + wr.Write(").Elements) { "); + } else { + Contract.Assert(false); throw new cce.UnreachableException(); // unexpected BoundedPool type + } + } + wr.Write("if ("); + TrExpr(e.Range); + wr.Write(") { _coll.Add("); + TrExpr(e.Term); + wr.Write("); }"); + for (int i = 0; i < n; i++) { + wr.Write("}"); + } + wr.Write("return Dafny.Set<{0}>.FromCollection(_coll); ", typeName); + wr.Write("})()"); + } else if (expr is ITEExpr) { ITEExpr e = (ITEExpr)expr; wr.Write("("); diff --git a/Source/Dafny/Dafny.atg b/Source/Dafny/Dafny.atg index 0ce1835f..f2a0a762 100644 --- a/Source/Dafny/Dafny.atg +++ b/Source/Dafny/Dafny.atg @@ -1237,6 +1237,7 @@ ConstAtomExpression "then" Expression "else" Expression (. e = new ITEExpr(x, e, e0, e1); .) | QuantifierGuts + | ComprehensionExpr ) . @@ -1391,6 +1392,31 @@ Forall = "forall" | '\u2200'. Exists = "exists" | '\u2203'. QSep = "::" | '\u2022'. +ComprehensionExpr += (. Contract.Ensures(Contract.ValueAtReturn(out q) != null); + IToken/*!*/ x = Token.NoToken; + BoundVar/*!*/ bv; + List bvars = new List(); + Expression/*!*/ range; + Expression body = null; + .) + "set" (. x = t; .) + (. parseVarScope.PushMarker(); .) + IdentTypeOptional (. bvars.Add(bv); parseVarScope.Push(bv.Name, bv.Name); .) + { "," + IdentTypeOptional (. bvars.Add(bv); parseVarScope.Push(bv.Name, bv.Name); .) + } + "|" Expression + [ + QSep + Expression + ] + (. if (body == null && bvars.Count != 1) { SemErr(t, "a set comprehension with more than one bound variable must have a term expression"); } + q = new SetComprehension(x, bvars, range, body); + parseVarScope.PopMarker(); + .) + . + Expressions<.List/*!*/ args.> = (. Contract.Requires(cce.NonNullElements(args)); Expression/*!*/ e; .) Expression (. args.Add(e); .) diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index ad7f5fb6..d83560e0 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -2303,6 +2303,8 @@ namespace Microsoft.Dafny { /// /// A ComprehensionExpr has the form: /// BINDER x Attributes | Range(x) :: Term(x) + /// When BINDER is "forall" or "exists", the range may be "null" (which stands for the logical value "true"). + /// For other BINDERs (currently, "set"), the range is non-null. /// where "Attributes" is optional, and "| Range(x)" is optional and defaults to "true". /// Currently, BINDER is one of the logical quantifiers "exists" or "forall". /// @@ -2346,7 +2348,7 @@ namespace Microsoft.Dafny { public List Bounds; // initialized and filled in by resolver // invariant Bounds == null || Bounds.Count == BoundVars.Count; - public ComprehensionExpr(IToken/*!*/ tok, List/*!*/ bvars, Expression/*!*/ range, Expression/*!*/ term, Attributes attrs) + public ComprehensionExpr(IToken/*!*/ tok, List/*!*/ bvars, Expression range, Expression/*!*/ term, Attributes attrs) : base(tok) { Contract.Requires(tok != null); Contract.Requires(cce.NonNullElements(bvars)); @@ -2369,7 +2371,7 @@ namespace Microsoft.Dafny { public abstract class QuantifierExpr : ComprehensionExpr { public readonly Triggers Trigs; - public QuantifierExpr(IToken/*!*/ tok, List/*!*/ bvars, Expression/*!*/ range, Expression/*!*/ term, Triggers trigs, Attributes attrs) + public QuantifierExpr(IToken/*!*/ tok, List/*!*/ bvars, Expression range, Expression/*!*/ term, Triggers trigs, Attributes attrs) : base(tok, bvars, range, term, attrs) { Contract.Requires(tok != null); Contract.Requires(cce.NonNullElements(bvars)); @@ -2431,7 +2433,23 @@ namespace Microsoft.Dafny { } } - public class WildcardExpr : Expression { // a WildcardExpr can occur only in reads clauses and a loop's decreases clauses (with different meanings) + public class SetComprehension : ComprehensionExpr + { + public readonly bool TermIsImplicit; + + public SetComprehension(IToken/*!*/ tok, List/*!*/ bvars, Expression/*!*/ range, Expression term) + : base(tok, bvars, range, term ?? new IdentifierExpr(tok, bvars[0].Name), null) { + Contract.Requires(tok != null); + Contract.Requires(cce.NonNullElements(bvars)); + Contract.Requires(1 <= bvars.Count); + Contract.Requires(range != null); + + TermIsImplicit = term == null; + } + } + + public class WildcardExpr : Expression + { // a WildcardExpr can occur only in reads clauses and a loop's decreases clauses (with different meanings) public WildcardExpr(IToken tok) : base(tok) { Contract.Requires(tok != null); diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs index 983996d1..e707d859 100644 --- a/Source/Dafny/Parser.cs +++ b/Source/Dafny/Parser.cs @@ -25,7 +25,7 @@ public class Parser { const bool T = true; const bool x = false; const int minErrDist = 2; - + public Scanner/*!*/ scanner; public Errors/*!*/ errors; @@ -161,10 +161,10 @@ public static int Parse (string/*!*/ s, string/*!*/ filename, List= minErrDist) errors.SemErr(t, msg); errDist = 0; } - - public void SemErr(IToken/*!*/ tok, string/*!*/ msg) { - Contract.Requires(tok != null); - Contract.Requires(msg != null); + + public void SemErr(IToken/*!*/ tok, string/*!*/ msg) { + Contract.Requires(tok != null); + Contract.Requires(msg != null); errors.SemErr(tok, msg); } @@ -177,15 +177,15 @@ public static int Parse (string/*!*/ s, string/*!*/ filename, List theImports; @@ -1898,6 +1898,10 @@ List/*!*/ decreases) { QuantifierGuts(out e); break; } + case 37: { + ComprehensionExpr(out e); + break; + } default: SynErr(140); break; } } @@ -1956,6 +1960,35 @@ List/*!*/ decreases) { } + void ComprehensionExpr(out Expression/*!*/ q) { + Contract.Ensures(Contract.ValueAtReturn(out q) != null); + IToken/*!*/ x = Token.NoToken; + BoundVar/*!*/ bv; + List bvars = new List(); + Expression/*!*/ range; + Expression body = null; + + Expect(37); + x = t; + parseVarScope.PushMarker(); + IdentTypeOptional(out bv); + bvars.Add(bv); parseVarScope.Push(bv.Name, bv.Name); + while (la.kind == 19) { + Get(); + IdentTypeOptional(out bv); + bvars.Add(bv); parseVarScope.Push(bv.Name, bv.Name); + } + Expect(17); + Expression(out range); + if (la.kind == 105 || la.kind == 106) { + QSep(); + Expression(out body); + } + q = new SetComprehension(x, bvars, range, body); + parseVarScope.PopMarker(); + + } + void IdentOrFuncExpression(out Expression/*!*/ e) { Contract.Ensures(Contract.ValueAtReturn(out e) != null); IToken/*!*/ id; e = dummyExpr; List/*!*/ args; Ident(out id); @@ -2138,13 +2171,13 @@ List/*!*/ decreases) { public void Parse() { la = new Token(); - la.val = ""; + la.val = ""; Get(); Dafny(); - Expect(0); + Expect(0); } - + static readonly bool[,]/*!*/ set = { {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,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,x, x,x,T,x, T,x,x,x, x,T,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}, @@ -2154,15 +2187,15 @@ List/*!*/ decreases) { {x,x,x,x, x,x,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,x, x,x,x,x, x,x,x,x, x,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,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,x,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,T, 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,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,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x}, - {x,T,T,x, x,x,x,T, x,x,x,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,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,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,x, x,T,T,T, T,T,T,T, T,x,x,T, T,T,T,T, T,x,x,x, x}, + {x,T,T,x, x,x,x,T, x,x,x,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,x, T,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,T,x, x,T,T,T, T,T,T,T, T,x,x,T, T,T,T,T, T,x,x,x, x}, {x,T,x,x, x,x,x,T, x,x,x,T, 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,x,x,x, x,x,x,x, T,x,x,T, T,T,x,x, x,x,x,x, T,T,x,T, x,T,T,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,T, T,x,x,x, x,x,x,x, x}, - {x,T,T,x, x,x,x,T, x,x,x,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,x,x,x, x,x,T,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,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,x, x,T,T,T, T,T,T,T, T,x,x,T, T,T,T,T, T,x,x,x, x}, + {x,T,T,x, x,x,x,T, x,x,x,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,T,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,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,x, x,T,T,T, T,T,T,T, T,x,x,T, T,T,T,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, T,x,x,x, x,x,x,x, x,x,x,x, T,x,x,T, T,T,x,x, x,x,x,x, T,T,x,T, x,T,T,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,T, T,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, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,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, x,x,x,x, x,x,x,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,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, 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, 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,T,x, x,x,x,T, 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,T,x,x, x,x,x,x, x,x,x,x, x,x,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, x}, - {x,T,T,x, T,x,x,T, x,x,x,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,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,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,x, x,T,T,T, T,T,T,T, T,x,x,T, T,T,T,T, T,x,x,x, x} + {x,x,T,x, x,x,x,T, 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,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,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, x}, + {x,T,T,x, T,x,x,T, x,x,x,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,x, T,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,T,x, x,T,T,T, T,T,T,T, T,x,x,T, T,T,T,T, T,x,x,x, x} }; } // end Parser @@ -2171,20 +2204,18 @@ List/*!*/ decreases) { public class Errors { public int count = 0; // number of errors detected public System.IO.TextWriter/*!*/ errorStream = Console.Out; // error messages go to this stream - public string errMsgFormat = "{0}({1},{2}): error: {3}"; // 0=filename, 1=line, 2=column, 3=text - public string warningMsgFormat = "{0}({1},{2}): warning: {3}"; // 0=filename, 1=line, 2=column, 3=text - + public string errMsgFormat = "{0}({1},{2}): error: {3}"; // 0=filename, 1=line, 2=column, 3=text + public string warningMsgFormat = "{0}({1},{2}): warning: {3}"; // 0=filename, 1=line, 2=column, 3=text + public void SynErr(string filename, int line, int col, int n) { - SynErr(filename, line, col, GetSyntaxErrorString(n)); - } - - public virtual void SynErr(string filename, int line, int col, string/*!*/ msg) { - Contract.Requires(msg != null); + SynErr(filename, line, col, GetSyntaxErrorString(n)); + } + public virtual void SynErr(string filename, int line, int col, string msg) { + Contract.Requires(msg != null); errorStream.WriteLine(errMsgFormat, filename, line, col, msg); count++; - } - - string GetSyntaxErrorString(int n) { + } + string GetSyntaxErrorString(int n) { string s; switch (n) { case 0: s = "EOF expected"; break; @@ -2340,7 +2371,7 @@ public class Errors { default: s = "error " + n; break; } - return s; + return s; } public void SemErr(IToken/*!*/ tok, string/*!*/ msg) { // semantic errors @@ -2348,9 +2379,8 @@ public class Errors { Contract.Requires(msg != null); SemErr(tok.filename, tok.line, tok.col, msg); } - public virtual void SemErr(string filename, int line, int col, string/*!*/ msg) { - Contract.Requires(msg != null); + Contract.Requires(msg != null); errorStream.WriteLine(errMsgFormat, filename, line, col, msg); count++; } diff --git a/Source/Dafny/Printer.cs b/Source/Dafny/Printer.cs index 6dcf0020..d3c7ecea 100644 --- a/Source/Dafny/Printer.cs +++ b/Source/Dafny/Printer.cs @@ -946,7 +946,28 @@ namespace Microsoft.Dafny { PrintExpression(e.Term); } if (parensNeeded) { wr.Write(")"); } - + + } else if (expr is SetComprehension) { + var e = (SetComprehension)expr; + bool parensNeeded = !isRightmost; + if (parensNeeded) { wr.Write("("); } + wr.Write("set "); + string sep = ""; + foreach (BoundVar bv in e.BoundVars) { + wr.Write("{0}{1}", sep, bv.Name); + sep = ", "; + PrintType(": ", bv.Type); + } + wr.Write(" "); + PrintAttributes(e.Attributes); + wr.Write("| "); + PrintExpression(e.Range); + if (!e.TermIsImplicit) { + wr.Write(" :: "); + PrintExpression(e.Term); + } + if (parensNeeded) { wr.Write(")"); } + } else if (expr is WildcardExpr) { wr.Write("*"); diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index c7deb42f..d8ca3448 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -2247,9 +2247,35 @@ namespace Microsoft.Dafny { expr.Type = Type.Bool; if (prevErrorCount == ErrorCount) { - e.Bounds = DiscoverBounds(e, specContext); + e.Bounds = DiscoverBounds(e.tok, e.BoundVars, e.LogicalBody(), e is ExistsExpr, specContext ? null : "quantifiers in non-ghost contexts must be compilable"); } - + + } else if (expr is SetComprehension) { + var e = (SetComprehension)expr; + 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); + } + ResolveType(v.tok, v.Type); + } + ResolveExpression(e.Range, twoState, specContext); + Contract.Assert(e.Range.Type != null); // follows from postcondition of ResolveExpression + if (!UnifyTypes(e.Range.Type, Type.Bool)) { + Error(expr, "range of comprehension must be of type bool (instead got {0})", e.Range.Type); + } + ResolveExpression(e.Term, twoState, specContext); + Contract.Assert(e.Term.Type != null); // follows from postcondition of ResolveExpression + + ResolveAttributes(e.Attributes, twoState); + scope.PopMarker(); + expr.Type = new SetType(e.Term.Type); + + if (prevErrorCount == ErrorCount) { + e.Bounds = DiscoverBounds(e.tok, e.BoundVars, e.Range, true, "a set comprehension must produce a finite set"); + } + } else if (expr is WildcardExpr) { expr.Type = new SetType(new ObjectType()); @@ -2378,34 +2404,36 @@ namespace Microsoft.Dafny { } /// - /// Tries to find a bounded pool for each of the bound variables of "e". If this process fails, appropriate - /// error messages are reported and "null" is returned, unless "friendlyTry" is "true", in which case no - /// error messages are reported. + /// Tries to find a bounded pool for each of the bound variables "bvars" of "expr". If this process + /// fails, then "null" is returned and: + /// if "errorMessage" is non-null, then appropriate error messages are reported and "null" is returned; + /// if "errorMessage" is null, no error messages are reported. /// Requires "e" to be successfully resolved. /// - List DiscoverBounds(QuantifierExpr e, bool friendlyTry) { - Contract.Requires(e != null); - Contract.Requires(e.Type != null); // a sanity check (but not a complete proof) that "e" has been resolved - Contract.Ensures(Contract.Result>().Count == e.BoundVars.Count); + List DiscoverBounds(IToken tok, List bvars, Expression expr, bool polarity, string errorMessage) { + Contract.Requires(tok != null); + Contract.Requires(bvars != null); + Contract.Requires(expr.Type != null); // a sanity check (but not a complete proof) that "e" has been resolved + Contract.Ensures(Contract.Result>().Count == bvars.Count); var bounds = new List(); - for (int j = 0; j < e.BoundVars.Count; j++) { - var bv = e.BoundVars[j]; - if (bv.Type == Type.Bool) { + for (int j = 0; j < bvars.Count; j++) { + var bv = bvars[j]; + if (bv.Type is BoolType) { // easy bounds.Add(new QuantifierExpr.BoolBoundedPool()); } else { // Go through the conjuncts of the range expression look for bounds. Expression lowerBound = bv.Type is NatType ? new LiteralExpr(bv.tok, new BigInteger(0)) : null; Expression upperBound = null; - foreach (var conjunct in NormalizedConjuncts(e.LogicalBody(), e is ExistsExpr)) { + foreach (var conjunct in NormalizedConjuncts(expr, polarity)) { var c = conjunct as BinaryExpr; if (c == null) { goto CHECK_NEXT_CONJUNCT; } var e0 = c.E0; var e1 = c.E1; - int whereIsBv = SanitizeForBoundDiscovery(e.BoundVars, j, c.ResolvedOp, ref e0, ref e1); + int whereIsBv = SanitizeForBoundDiscovery(bvars, j, c.ResolvedOp, ref e0, ref e1); if (whereIsBv < 0) { goto CHECK_NEXT_CONJUNCT; } @@ -2457,8 +2485,8 @@ namespace Microsoft.Dafny { CHECK_NEXT_CONJUNCT: ; } // we have checked every conjunct in the range expression and still have not discovered good bounds - if (!friendlyTry) { - Error(e, "quantifiers in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{0}'", bv.Name); + if (errorMessage != null) { + Error(tok, "{0}, but Dafny's heuristics can't figure out how to produce a bounded set of values for '{1}'", errorMessage, bv.Name); } return null; } diff --git a/Source/Dafny/Scanner.cs b/Source/Dafny/Scanner.cs index 817df6cd..0af254f3 100644 --- a/Source/Dafny/Scanner.cs +++ b/Source/Dafny/Scanner.cs @@ -19,7 +19,7 @@ public class Buffer { // a) whole stream in buffer // b) part of stream in buffer // 2) non seekable stream (network, console) - + public const int EOF = 65535 + 1; // char.MaxValue + 1; const int MIN_BUFFER_LENGTH = 1024; // 1KB const int MAX_BUFFER_LENGTH = MIN_BUFFER_LENGTH * 64; // 64KB @@ -31,17 +31,15 @@ public class Buffer { Stream/*!*/ stream; // input stream (seekable) bool isUserStream; // was the stream opened by the user? - [ContractInvariantMethod] - void ObjectInvariant(){ - Contract.Invariant(buf != null); - Contract.Invariant(stream != null); - } - -// [NotDelayed] - public Buffer (Stream/*!*/ s, bool isUserStream) : base() { +[ContractInvariantMethod] +void ObjectInvariant(){ + Contract.Invariant(buf != null); + Contract.Invariant(stream != null);} + [NotDelayed] + public Buffer (Stream/*!*/ s, bool isUserStream) :base() { Contract.Requires(s != null); stream = s; this.isUserStream = isUserStream; - + int fl, bl; if (s.CanSeek) { fl = (int) s.Length; @@ -53,12 +51,12 @@ public class Buffer { buf = new byte[(bl>0) ? bl : MIN_BUFFER_LENGTH]; fileLen = fl; bufLen = bl; - + if (fileLen > 0) Pos = 0; // setup buffer to position 0 (start) else bufPos = 0; // index 0 is already after the file, thus Pos = 0 is invalid if (bufLen == fileLen && s.CanSeek) Close(); } - + protected Buffer(Buffer/*!*/ b) { // called in UTF8Buffer constructor Contract.Requires(b != null); buf = b.buf; @@ -75,14 +73,14 @@ public class Buffer { } ~Buffer() { Close(); } - + protected void Close() { if (!isUserStream && stream != null) { stream.Close(); //stream = null; } } - + public virtual int Read () { if (bufPos < bufLen) { return buf[bufPos++]; @@ -102,7 +100,7 @@ public class Buffer { Pos = curPos; return ch; } - + public string/*!*/ GetString (int beg, int end) { Contract.Ensures(Contract.Result() != null); int len = 0; @@ -141,7 +139,7 @@ public class Buffer { } } } - + // Read the next chunk of bytes from the stream, increases the buffer // if needed and updates the fields fileLen and bufLen. // Returns the number of bytes read. @@ -215,20 +213,19 @@ public class Scanner { const int noSym = 107; - [ContractInvariantMethod] - void objectInvariant(){ - Contract.Invariant(buffer!=null); - Contract.Invariant(t != null); - Contract.Invariant(start != null); - Contract.Invariant(tokens != null); - Contract.Invariant(pt != null); - Contract.Invariant(tval != null); - Contract.Invariant(Filename != null); - Contract.Invariant(errorHandler != null); - } - +[ContractInvariantMethod] +void objectInvariant(){ + Contract.Invariant(buffer!=null); + Contract.Invariant(t != null); + Contract.Invariant(start != null); + Contract.Invariant(tokens != null); + Contract.Invariant(pt != null); + Contract.Invariant(tval != null); + Contract.Invariant(Filename != null); + Contract.Invariant(errorHandler != null); +} public Buffer/*!*/ buffer; // scanner buffer - + Token/*!*/ t; // current token int ch; // current input character int pos; // byte position of current character @@ -239,13 +236,13 @@ public class Scanner { Token/*!*/ tokens; // list of tokens already peeked (first token is a dummy) Token/*!*/ pt; // current peek token - + char[]/*!*/ tval = new char[128]; // text of current token int tlen; // length of current token - + private string/*!*/ Filename; private Errors/*!*/ errorHandler; - + static Scanner() { start = new Hashtable(128); for (int i = 39; i <= 39; ++i) start[i] = 1; @@ -294,9 +291,9 @@ public class Scanner { start[Buffer.EOF] = -1; } - -// [NotDelayed] - public Scanner (string/*!*/ fileName, Errors/*!*/ errorHandler) : base() { + + [NotDelayed] + public Scanner (string/*!*/ fileName, Errors/*!*/ errorHandler) :base(){ Contract.Requires(fileName != null); Contract.Requires(errorHandler != null); this.errorHandler = errorHandler; @@ -306,14 +303,15 @@ public class Scanner { Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); buffer = new Buffer(stream, false); Filename = fileName; + Init(); } catch (IOException) { throw new FatalError("Cannot open file " + fileName); } } - -// [NotDelayed] - public Scanner (Stream/*!*/ s, Errors/*!*/ errorHandler, string/*!*/ fileName) : base() { + + [NotDelayed] + public Scanner (Stream/*!*/ s, Errors/*!*/ errorHandler, string/*!*/ fileName) :base(){ Contract.Requires(s != null); Contract.Requires(errorHandler != null); Contract.Requires(fileName != null); @@ -322,9 +320,10 @@ public class Scanner { buffer = new Buffer(s, true); this.errorHandler = errorHandler; this.Filename = fileName; + Init(); } - + void Init() { pos = -1; line = 1; col = 0; oldEols = 0; @@ -345,11 +344,11 @@ public class Scanner { Contract.Ensures(Contract.Result() != null); int p = buffer.Pos; int ch = buffer.Read(); - // replace isolated '\r' by '\n' in order to make + // replace isolated '\r' by '\n' in order to make // eol handling uniform across Windows, Unix and Mac if (ch == '\r' && buffer.Peek() != '\n') ch = EOL; while (ch != EOL && ch != Buffer.EOF){ - ch = buffer.Read(); + ch = buffer.Read(); // replace isolated '\r' by '\n' in order to make // eol handling uniform across Windows, Unix and Mac if (ch == '\r' && buffer.Peek() != '\n') ch = EOL; @@ -360,7 +359,7 @@ public class Scanner { } void NextCh() { - if (oldEols > 0) { ch = EOL; oldEols--; } + if (oldEols > 0) { ch = EOL; oldEols--; } else { // pos = buffer.Pos; // ch = buffer.Read(); col++; @@ -368,9 +367,9 @@ public class Scanner { // // eol handling uniform across Windows, Unix and Mac // if (ch == '\r' && buffer.Peek() != '\n') ch = EOL; // if (ch == EOL) { line++; col = 0; } - + while (true) { - pos = buffer.Pos; + pos = buffer.Pos; ch = buffer.Read(); col++; // replace isolated '\r' by '\n' in order to make // eol handling uniform across Windows, Unix and Mac @@ -420,7 +419,7 @@ public class Scanner { return; } - + } } @@ -559,13 +558,10 @@ public class Scanner { t.pos = pos; t.col = col; t.line = line; t.filename = this.Filename; int state; - if (start.ContainsKey(ch)) { - Contract.Assert(start[ch] != null); - state = (int) start[ch]; - } + if (start.ContainsKey(ch)) { state = (int) cce.NonNull( start[ch]); } else { state = 0; } tlen = 0; AddCh(); - + switch (state) { case -1: { t.kind = eofSym; break; } // NextCh already done case 0: { @@ -773,14 +769,14 @@ public class Scanner { t.val = new String(tval, 0, tlen); return t; } - + private void SetScannerBehindT() { buffer.Pos = t.pos; NextCh(); line = t.line; col = t.col; for (int i = 0; i < tlen; i++) NextCh(); } - + // get the next token (possibly a token already seen during peeking) public Token/*!*/ Scan () { Contract.Ensures(Contract.Result() != null); @@ -801,7 +797,7 @@ public class Scanner { } pt = pt.next; } while (pt.kind > maxT); // skip pragmas - + return pt; } diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index 67559aee..bba7b734 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -1509,9 +1509,12 @@ namespace Microsoft.Dafny { } Bpl.Expr r = BplAnd(t0, t1); return z == null ? r : BplAnd(r, z); - } else if (expr is QuantifierExpr) { - QuantifierExpr e = (QuantifierExpr)expr; - Bpl.Expr total = IsTotal(e.LogicalBody(), etran); + } else if (expr is ComprehensionExpr) { + var e = (ComprehensionExpr)expr; + var total = IsTotal(e.Term, etran); + if (e.Range != null) { + total = BplAnd(IsTotal(e.Range, etran), BplImp(etran.TrExpr(e.Range), total)); + } if (total != Bpl.Expr.True) { Bpl.VariableSeq bvars = new Bpl.VariableSeq(); Bpl.Expr typeAntecedent = etran.TrBoundVariables(e.BoundVars, bvars); @@ -1636,9 +1639,12 @@ namespace Microsoft.Dafny { break; } return BplAnd(t0, t1); - } else if (expr is QuantifierExpr) { - QuantifierExpr e = (QuantifierExpr)expr; - Bpl.Expr total = CanCallAssumption(e.LogicalBody(), etran); + } else if (expr is ComprehensionExpr) { + var e = (ComprehensionExpr)expr; + var total = CanCallAssumption(e.Term, etran); + if (e.Range != null) { + total = BplAnd(CanCallAssumption(e.Range, etran), BplImp(etran.TrExpr(e.Range), total)); + } if (total != Bpl.Expr.True) { Bpl.VariableSeq bvars = new Bpl.VariableSeq(); Bpl.Expr typeAntecedent = etran.TrBoundVariables(e.BoundVars, bvars); @@ -1684,6 +1690,18 @@ namespace Microsoft.Dafny { } } + Bpl.Expr BplImp(Bpl.Expr a, Bpl.Expr b) { + Contract.Requires(a != null); + Contract.Requires(b != null); + Contract.Ensures(Contract.Result() != null); + + if (a == Bpl.Expr.True || b == Bpl.Expr.True) { + return b; + } else { + return Bpl.Expr.Imp(a, b); + } + } + void CheckNonNull(IToken tok, Expression e, Bpl.StmtListBuilder builder, ExpressionTranslator etran, Bpl.QKeyValue kv) { Contract.Requires(tok != null); Contract.Requires(e != null); @@ -1970,8 +1988,8 @@ namespace Microsoft.Dafny { break; } - } else if (expr is QuantifierExpr) { - QuantifierExpr e = (QuantifierExpr)expr; + } else if (expr is ComprehensionExpr) { + var e = (ComprehensionExpr)expr; Dictionary substMap = new Dictionary(); foreach (BoundVar bv in e.BoundVars) { VarDecl local = new VarDecl(bv.tok, bv.Name, bv.Type, bv.IsGhost, null); @@ -1986,8 +2004,18 @@ namespace Microsoft.Dafny { builder.Add(new Bpl.AssumeCmd(bv.tok, wh)); } } - Expression body = Substitute(e.LogicalBody(), null, substMap); - CheckWellformed(body, options, locals, builder, etran); + + Expression body = Substitute(e.Term, null, substMap); + if (e.Range == null) { + CheckWellformed(body, options, locals, builder, etran); + } else { + Expression range = Substitute(e.Range, null, substMap); + CheckWellformed(range, options, locals, builder, etran); + + Bpl.StmtListBuilder b = new Bpl.StmtListBuilder(); + CheckWellformed(body, options, locals, b, etran); + builder.Add(new Bpl.IfCmd(expr.tok, etran.TrExpr(range), b.Collect(expr.tok), null, null)); + } } else if (expr is ITEExpr) { ITEExpr e = (ITEExpr)expr; @@ -4443,7 +4471,24 @@ namespace Microsoft.Dafny { Contract.Assert(e is ExistsExpr); return new Bpl.ExistsExpr(expr.tok, new Bpl.TypeVariableSeq(), bvars, kv, tr, Bpl.Expr.And(antecedent, body)); } - + + } else if (expr is SetComprehension) { + var e = (SetComprehension)expr; + // Translate "set xs | R :: T" into "lambda y: BoxType :: (exists xs :: CorrectType(xs) && R && y==Box(T))". + Bpl.VariableSeq bvars = new Bpl.VariableSeq(); + Bpl.Expr typeAntecedent = TrBoundVariables(e.BoundVars, bvars); + Bpl.QKeyValue kv = TrAttributes(e.Attributes); + + var yVar = new Bpl.BoundVariable(expr.tok, new Bpl.TypedIdent(expr.tok, "$y#" + translator.otherTmpVarCount, predef.BoxType)); + translator.otherTmpVarCount++; + Bpl.Expr y = new Bpl.IdentifierExpr(expr.tok, yVar); + + var eq = Bpl.Expr.Eq(y, BoxIfNecessary(expr.tok, TrExpr(e.Term), e.Term.Type)); + var ebody = Bpl.Expr.And(translator.BplAnd(typeAntecedent, TrExpr(e.Range)), eq); + var exst = new Bpl.ExistsExpr(expr.tok, bvars, ebody); + + return new Bpl.LambdaExpr(expr.tok, new Bpl.TypeVariableSeq(), new VariableSeq(yVar), kv, exst); + } else if (expr is ITEExpr) { ITEExpr e = (ITEExpr)expr; Bpl.Expr g = TrExpr(e.Test); @@ -5470,9 +5515,10 @@ namespace Microsoft.Dafny { } return VarOccursInArgumentToRecursiveFunction(e.E0, n, p) || VarOccursInArgumentToRecursiveFunction(e.E1, n, p); - } else if (expr is QuantifierExpr) { - var e = (QuantifierExpr)expr; - return VarOccursInArgumentToRecursiveFunction(e.LogicalBody(), n, null); + } else if (expr is ComprehensionExpr) { + var e = (ComprehensionExpr)expr; + return (e.Range != null && VarOccursInArgumentToRecursiveFunction(e.Range, n, null)) || + VarOccursInArgumentToRecursiveFunction(e.Term, n, null); } else if (expr is ITEExpr) { var e = (ITEExpr)expr; return VarOccursInArgumentToRecursiveFunction(e.Test, n, null) || // test is not "elevated" @@ -5640,18 +5686,27 @@ namespace Microsoft.Dafny { newExpr = newBin; } - } else if (expr is QuantifierExpr) { - QuantifierExpr e = (QuantifierExpr)expr; + } else if (expr is ComprehensionExpr) { + var e = (ComprehensionExpr)expr; Expression newRange = e.Range == null ? null : Substitute(e.Range, receiverReplacement, substMap); Expression newTerm = Substitute(e.Term, receiverReplacement, substMap); - Triggers newTrigs = SubstTriggers(e.Trigs, receiverReplacement, substMap); Attributes newAttrs = SubstAttributes(e.Attributes, receiverReplacement, substMap); - if (newRange != e.Range || newTerm != e.Term || newTrigs != e.Trigs || newAttrs != e.Attributes) { - if (expr is ForallExpr) { - newExpr = new ForallExpr(expr.tok, e.BoundVars, newRange, newTerm, newTrigs, newAttrs); - } else { - newExpr = new ExistsExpr(expr.tok, e.BoundVars, newRange, newTerm, newTrigs, newAttrs); + if (e is SetComprehension) { + if (newRange != e.Range || newTerm != e.Term || newAttrs != e.Attributes) { + newExpr = new SetComprehension(expr.tok, e.BoundVars, newRange, newTerm); } + } else if (e is QuantifierExpr) { + var q = (QuantifierExpr)e; + Triggers newTrigs = SubstTriggers(q.Trigs, receiverReplacement, substMap); + if (newRange != e.Range || newTerm != e.Term || newAttrs != e.Attributes || newTrigs != q.Trigs) { + if (expr is ForallExpr) { + newExpr = new ForallExpr(expr.tok, e.BoundVars, newRange, newTerm, newTrigs, newAttrs); + } else { + newExpr = new ExistsExpr(expr.tok, e.BoundVars, newRange, newTerm, newTrigs, newAttrs); + } + } + } else { + Contract.Assume(false); // unexpected ComprehensionExpr } } else if (expr is ITEExpr) { diff --git a/Test/dafny0/Answer b/Test/dafny0/Answer index 2463b0e5..34aea30b 100644 --- a/Test/dafny0/Answer +++ b/Test/dafny0/Answer @@ -527,6 +527,19 @@ Execution trace: Dafny program verifier finished with 2 verified, 1 error +-------------------- Comprehensions.dfy -------------------- +Comprehensions.dfy(9,14): Error: assertion violation +Execution trace: + (0,0): anon0 + (0,0): anon9_Then + (0,0): anon10_Then + (0,0): anon4 + (0,0): anon11_Then + (0,0): anon12_Then + (0,0): anon8 + +Dafny program verifier finished with 6 verified, 1 error + -------------------- Termination.dfy -------------------- Termination.dfy(102,3): Error: cannot prove termination; try supplying a decreases clause for the loop Execution trace: diff --git a/Test/dafny0/Comprehensions.dfy b/Test/dafny0/Comprehensions.dfy new file mode 100644 index 00000000..8629a418 --- /dev/null +++ b/Test/dafny0/Comprehensions.dfy @@ -0,0 +1,40 @@ +method M() +{ + var numbers := set i | 0 <= i && i < 100; + var squares := set i | 0 <= i && i < 100 :: Id(i)*i; // verifying properties about set comprehensions with a term expression is hard + + assert 12 in numbers; + assert Id(5) == 5; + assert 25 in squares; + assert 200 in numbers; // error +} + +function method Id(x: int): int { x } // for triggering + +// The following mainly test that set comprehensions can be compiled, but one would +// have to run the resulting program to check that the compiler is doing the right thing. +method Main() +{ + var q := set i,j | 0 <= i && i < 10 && 0 <= j && j < 3 :: i+j; + call PrintSet(q); + q := set b: bool | true :: if b then 3 else 7; + call PrintSet(q); + var m := set k | k in q :: 2*k; + call PrintSet(m); + call PrintSet(set k | k in q && k % 2 == 0); + var sq := [30, 40, 20]; + call PrintSet(set k, i | k in sq && 0 <= i && i < k && i % 7 == 0 :: k + i); + var bb := forall k, i | k in sq && 0 <= i && i < k && i % 7 == 0 :: k + i == 17; +} + +method PrintSet(s: set) { + var q := s; + while (q != {}) + decreases q; + { + var x := choose q; + print x, " "; + q := q - {x}; + } + print "\n"; +} diff --git a/Test/dafny0/runtest.bat b/Test/dafny0/runtest.bat index 12d9bcf4..f67429d2 100644 --- a/Test/dafny0/runtest.bat +++ b/Test/dafny0/runtest.bat @@ -15,6 +15,7 @@ for %%f in (TypeTests.dfy NatTypes.dfy SmallTests.dfy Definedness.dfy FunctionSpecifications.dfy ResolutionErrors.dfy Array.dfy MultiDimArray.dfy NonGhostQuantifiers.dfy AdvancedLHS.dfy Modules0.dfy Modules1.dfy BadFunction.dfy + Comprehensions.dfy Termination.dfy Use.dfy DTypes.dfy TypeParameters.dfy Datatypes.dfy TypeAntecedents.dfy SplitExpr.dfy Refinement.dfy RefinementErrors.dfy) do ( -- cgit v1.2.3 From a48f2cc95e01742e8d1a6c3e69846598ee2a91a2 Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 18 May 2011 23:25:29 -0700 Subject: fixed the axiom about TypeOf --- BCT/BytecodeTranslator/Heap.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/BCT/BytecodeTranslator/Heap.cs b/BCT/BytecodeTranslator/Heap.cs index 28964c15..87d950c4 100644 --- a/BCT/BytecodeTranslator/Heap.cs +++ b/BCT/BytecodeTranslator/Heap.cs @@ -60,7 +60,8 @@ procedure {:inline 1} System.Object.GetType(this: Ref) returns ($result: Ref) { $result := $TypeOf($DynamicType(this)); } -axiom (forall t: Type :: $DynamicType($TypeOf(t)) == t); +function $TypeOfInv(Ref): Type; +axiom (forall t: Type :: {$TypeOf(t)} $TypeOfInv($TypeOf(t)) == t); function $ThreadDelegate(Ref) : Ref; @@ -226,7 +227,8 @@ procedure {:inline 1} System.Object.GetType(this: Ref) returns ($result: Ref) { $result := $TypeOf($DynamicType(this)); } -axiom (forall t: Type :: $DynamicType($TypeOf(t)) == t); +function $TypeOfInv(Ref): Type; +axiom (forall t: Type :: {$TypeOf(t)} $TypeOfInv($TypeOf(t)) == t); function $ThreadDelegate(Ref) : Ref; procedure {:inline 1} System.Threading.Thread.#ctor$System.Threading.ParameterizedThreadStart(this: Ref, start$in: Ref) -- cgit v1.2.3 From 3a76682df837bf7cb391bca0061998a970c2704f Mon Sep 17 00:00:00 2001 From: Unknown Date: Thu, 19 May 2011 12:21:41 +0530 Subject: close the file stream opened by the parser --- Source/Core/BoogiePL.atg | 4 +++- Source/Core/Parser.cs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/Core/BoogiePL.atg b/Source/Core/BoogiePL.atg index b7743983..ec27422e 100644 --- a/Source/Core/BoogiePL.atg +++ b/Source/Core/BoogiePL.atg @@ -40,7 +40,9 @@ Contract.Requires(cce.NonNullElements(defines,true)); FileStream stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); - return Parse(stream, filename, defines, out program); + var ret = Parse(stream, filename, defines, out program); + stream.Close(); + return ret; } diff --git a/Source/Core/Parser.cs b/Source/Core/Parser.cs index 71804945..5e5dd178 100644 --- a/Source/Core/Parser.cs +++ b/Source/Core/Parser.cs @@ -59,7 +59,9 @@ Contract.Requires(cce.NonNullElements(defines,true)); FileStream stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); - return Parse(stream, filename, defines, out program); + var ret = Parse(stream, filename, defines, out program); + stream.Close(); + return ret; } -- cgit v1.2.3 From de0a78a69aac57b2189caf7d8490975f25aea91a Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 19 May 2011 11:53:56 -0700 Subject: Dafny: let verifier, not the resolver, check for missing cases in match expressions/statements --- Source/Dafny/Compiler.cs | 27 +++++++------- Source/Dafny/DafnyAst.cs | 5 +++ Source/Dafny/Resolver.cs | 22 ++++++++++-- Source/Dafny/Translator.cs | 76 ++++++++++++++++++++++++++++++++++----- Test/dafny0/Answer | 36 +++++++++++++++++++ Test/dafny0/ControlStructures.dfy | 65 +++++++++++++++++++++++++++++++++ Test/dafny0/runtest.bat | 2 +- 7 files changed, 208 insertions(+), 25 deletions(-) create mode 100644 Test/dafny0/ControlStructures.dfy diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index f1a649ee..800585bd 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -795,21 +795,22 @@ namespace Microsoft.Dafny { // } else if (true) { // ... // } + if (s.Cases.Count != 0) { + string source = "_source" + tmpVarCount; + tmpVarCount++; + Indent(indent); + wr.Write("{0} {1} = ", TypeName(cce.NonNull(s.Source.Type)), source); + TrExpr(s.Source); + wr.WriteLine(";"); - string source = "_source" + tmpVarCount; - tmpVarCount++; - Indent(indent); - wr.Write("{0} {1} = ", TypeName(cce.NonNull(s.Source.Type)), source); - TrExpr(s.Source); - wr.WriteLine(";"); - - int i = 0; - foreach (MatchCaseStmt mc in s.Cases) { - MatchCasePrelude(source, cce.NonNull(mc.Ctor), mc.Arguments, i, s.Cases.Count, indent); - TrStmtList(mc.Body, indent); - i++; + int i = 0; + foreach (MatchCaseStmt mc in s.Cases) { + MatchCasePrelude(source, cce.NonNull(mc.Ctor), mc.Arguments, i, s.Cases.Count, indent); + TrStmtList(mc.Body, indent); + i++; + } + Indent(indent); wr.WriteLine("}"); } - Indent(indent); wr.WriteLine("}"); } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index d83560e0..04d92539 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -1642,10 +1642,12 @@ namespace Microsoft.Dafny { void ObjectInvariant() { Contract.Invariant(Source != null); Contract.Invariant(cce.NonNullElements(Cases)); + Contract.Invariant(cce.NonNullElements(MissingCases)); } public readonly Expression Source; public readonly List/*!*/ Cases; + public readonly List MissingCases = new List(); // filled in during resolution public MatchStmt(IToken tok, Expression source, [Captured] List/*!*/ cases) : base(tok) { @@ -2490,10 +2492,13 @@ namespace Microsoft.Dafny { public class MatchExpr : Expression { // a MatchExpr is an "extended expression" and is only allowed in certain places public readonly Expression Source; public readonly List/*!*/ Cases; + public readonly List MissingCases = new List(); // filled in during resolution + [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(Source != null); Contract.Invariant(cce.NonNullElements(Cases)); + Contract.Invariant(cce.NonNullElements(MissingCases)); } public MatchExpr(IToken tok, Expression source, [Captured] List/*!*/ cases) diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index d8ca3448..eb40ac82 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -1473,7 +1473,16 @@ namespace Microsoft.Dafny { scope.PopMarker(); } if (dtd != null && memberNamesUsed.Count != dtd.Ctors.Count) { - Error(stmt, "match expression does not cover all constructors"); + // We could complain about the syntactic omission of constructors: + // Error(stmt, "match statement does not cover all constructors"); + // but instead we let the verifier do a semantic check. + // So, for now, record the missing constructors: + foreach (var ctr in dtd.Ctors) { + if (!memberNamesUsed.ContainsKey(ctr.Name)) { + s.MissingCases.Add(ctr); + } + } + Contract.Assert(memberNamesUsed.Count + s.MissingCases.Count == dtd.Ctors.Count); } @@ -2390,7 +2399,16 @@ namespace Microsoft.Dafny { scope.PopMarker(); } if (dtd != null && memberNamesUsed.Count != dtd.Ctors.Count) { - Error(expr, "match expression does not cover all constructors"); + // We could complain about the syntactic omission of constructors: + // Error(expr, "match expression does not cover all constructors"); + // but instead we let the verifier do a semantic check. + // So, for now, record the missing constructors: + foreach (var ctr in dtd.Ctors) { + if (!memberNamesUsed.ContainsKey(ctr.Name)) { + me.MissingCases.Add(ctr); + } + } + Contract.Assert(memberNamesUsed.Count + me.MissingCases.Count == dtd.Ctors.Count); } } else { diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index bba7b734..b529b984 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -1371,8 +1371,26 @@ namespace Microsoft.Dafny { Bpl.IdentifierExpr id = new Bpl.IdentifierExpr(mc.tok, mc.Ctor.FullName, predef.DatatypeType); return new Bpl.NAryExpr(mc.tok, new Bpl.FunctionCall(id), args); } - - Bpl.Expr IsTotal(Expression expr, ExpressionTranslator etran){ + + Bpl.Expr CtorInvocation(IToken tok, DatatypeCtor ctor, ExpressionTranslator etran, Bpl.VariableSeq locals, StmtListBuilder localTypeAssumptions) { + Contract.Requires(tok != null); + Contract.Requires(ctor != null); + Contract.Requires(etran != null); + Contract.Requires(locals != null); + Contract.Requires(localTypeAssumptions != null); + Contract.Requires(predef != null); + Contract.Ensures(Contract.Result() != null); + + VariableSeq bvars; + List args; + CreateBoundVariables(ctor.Formals, out bvars, out args); + locals.AddRange(bvars); + + Bpl.IdentifierExpr id = new Bpl.IdentifierExpr(tok, ctor.FullName, predef.DatatypeType); + return new Bpl.NAryExpr(tok, new Bpl.FunctionCall(id), new ExprSeq(args.ToArray())); + } + + Bpl.Expr IsTotal(Expression expr, ExpressionTranslator etran) { Contract.Requires(expr != null);Contract.Requires(etran != null); Contract.Requires(predef != null); Contract.Ensures(Contract.Result() != null); @@ -2030,20 +2048,40 @@ namespace Microsoft.Dafny { MatchExpr me = (MatchExpr)expr; CheckWellformed(me.Source, options, locals, builder, etran); Bpl.Expr src = etran.TrExpr(me.Source); - Bpl.IfCmd ifcmd = null; + Bpl.IfCmd ifCmd = null; StmtListBuilder elsBldr = new StmtListBuilder(); elsBldr.Add(new Bpl.AssumeCmd(expr.tok, Bpl.Expr.False)); StmtList els = elsBldr.Collect(expr.tok); + foreach (var missingCtor in me.MissingCases) { + // havoc all bound variables + var b = new Bpl.StmtListBuilder(); + VariableSeq newLocals = new VariableSeq(); + Bpl.Expr r = CtorInvocation(me.tok, missingCtor, etran, newLocals, b); + locals.AddRange(newLocals); + + if (newLocals.Length != 0) { + Bpl.IdentifierExprSeq havocIds = new Bpl.IdentifierExprSeq(); + foreach (Variable local in newLocals) { + havocIds.Add(new Bpl.IdentifierExpr(local.tok, local)); + } + builder.Add(new Bpl.HavocCmd(me.tok, havocIds)); + } + b.Add(Assert(me.tok, Bpl.Expr.False, "missing case in case statement: " + missingCtor.Name)); + + Bpl.Expr guard = Bpl.Expr.Eq(src, r); + ifCmd = new Bpl.IfCmd(me.tok, guard, b.Collect(me.tok), ifCmd, els); + els = null; + } for (int i = me.Cases.Count; 0 <= --i; ) { MatchCaseExpr mc = me.Cases[i]; Bpl.StmtListBuilder b = new Bpl.StmtListBuilder(); Bpl.Expr ct = CtorInvocation(mc, etran, locals, b); // generate: if (src == ctor(args)) { assume args-is-well-typed; mc.Body is well-formed; assume Result == TrExpr(case); } else ... CheckWellformedWithResult(mc.Body, options, result, resultType, locals, b, etran); - ifcmd = new Bpl.IfCmd(mc.tok, Bpl.Expr.Eq(src, ct), b.Collect(mc.tok), ifcmd, els); + ifCmd = new Bpl.IfCmd(mc.tok, Bpl.Expr.Eq(src, ct), b.Collect(mc.tok), ifCmd, els); els = null; } - builder.Add(ifcmd); + builder.Add(ifCmd); result = null; } else { @@ -3233,16 +3271,36 @@ namespace Microsoft.Dafny { builder.Add(CaptureState(stmt.Tok)); } else if (stmt is MatchStmt) { - MatchStmt s = (MatchStmt)stmt; + var s = (MatchStmt)stmt; TrStmt_CheckWellformed(s.Source, builder, locals, etran, true); Bpl.Expr source = etran.TrExpr(s.Source); - Bpl.StmtListBuilder b = new Bpl.StmtListBuilder(); + var b = new Bpl.StmtListBuilder(); b.Add(new Bpl.AssumeCmd(stmt.Tok, Bpl.Expr.False)); Bpl.StmtList els = b.Collect(stmt.Tok); Bpl.IfCmd ifCmd = null; + foreach (var missingCtor in s.MissingCases) { + // havoc all bound variables + b = new Bpl.StmtListBuilder(); + VariableSeq newLocals = new VariableSeq(); + Bpl.Expr r = CtorInvocation(s.Tok, missingCtor, etran, newLocals, b); + locals.AddRange(newLocals); + + if (newLocals.Length != 0) { + Bpl.IdentifierExprSeq havocIds = new Bpl.IdentifierExprSeq(); + foreach (Variable local in newLocals) { + havocIds.Add(new Bpl.IdentifierExpr(local.tok, local)); + } + builder.Add(new Bpl.HavocCmd(s.Tok, havocIds)); + } + b.Add(Assert(s.Tok, Bpl.Expr.False, "missing case in case statement: " + missingCtor.Name)); + + Bpl.Expr guard = Bpl.Expr.Eq(source, r); + ifCmd = new Bpl.IfCmd(s.Tok, guard, b.Collect(s.Tok), ifCmd, els); + els = null; + } for (int i = s.Cases.Count; 0 <= --i; ) { - MatchCaseStmt mc = (MatchCaseStmt)s.Cases[i]; + var mc = (MatchCaseStmt)s.Cases[i]; // havoc all bound variables b = new Bpl.StmtListBuilder(); VariableSeq newLocals = new VariableSeq(); @@ -3266,7 +3324,7 @@ namespace Microsoft.Dafny { ifCmd = new Bpl.IfCmd(mc.tok, guard, b.Collect(mc.tok), ifCmd, els); els = null; } - Contract.Assert(ifCmd != null); // follows from the fact that s.Cases.Count != 0. + Contract.Assert(ifCmd != null); // follows from the fact that s.Cases.Count + s.MissingCases.Count != 0. builder.Add(ifCmd); } else { diff --git a/Test/dafny0/Answer b/Test/dafny0/Answer index 34aea30b..28f438af 100644 --- a/Test/dafny0/Answer +++ b/Test/dafny0/Answer @@ -540,6 +540,42 @@ Execution trace: Dafny program verifier finished with 6 verified, 1 error +-------------------- ControlStructures.dfy -------------------- +ControlStructures.dfy(5,3): Error: missing case in case statement: Blue +Execution trace: + (0,0): anon0 + (0,0): anon6_Else + (0,0): anon7_Else + (0,0): anon8_Else + (0,0): anon9_Then +ControlStructures.dfy(5,3): Error: missing case in case statement: Purple +Execution trace: + (0,0): anon0 + (0,0): anon6_Else + (0,0): anon7_Else + (0,0): anon8_Then +ControlStructures.dfy(14,3): Error: missing case in case statement: Purple +Execution trace: + (0,0): anon0 + (0,0): anon6_Else + (0,0): anon7_Else + (0,0): anon8_Then +ControlStructures.dfy(43,5): Error: missing case in case statement: Red +Execution trace: + (0,0): anon0 + (0,0): anon8_Then + (0,0): anon9_Else + (0,0): anon10_Then +ControlStructures.dfy(51,3): Error: missing case in case statement: Red +Execution trace: + (0,0): anon8_Else + (0,0): anon9_Else + (0,0): anon10_Else + (0,0): anon11_Else + (0,0): anon12_Then + +Dafny program verifier finished with 8 verified, 5 errors + -------------------- Termination.dfy -------------------- Termination.dfy(102,3): Error: cannot prove termination; try supplying a decreases clause for the loop Execution trace: diff --git a/Test/dafny0/ControlStructures.dfy b/Test/dafny0/ControlStructures.dfy new file mode 100644 index 00000000..eed74634 --- /dev/null +++ b/Test/dafny0/ControlStructures.dfy @@ -0,0 +1,65 @@ +datatype D = Green | Blue | Red | Purple; + +method M0(d: D) +{ + match (d) { // error: two missing cases: Blue and Purple + case Green => + case Red => + } +} + +method M1(d: D) + requires d != #D.Blue; +{ + match (d) { // error: missing case: Purple + case Green => + case Red => + } +} + +method M2(d: D) + requires d != #D.Blue && d != #D.Purple; +{ + match (d) { + case Green => + case Red => + } +} + +method M3(d: D) + requires d == #D.Green; +{ + if (d != #D.Green) { + match (d) { + // nothing here + } + } +} + +method M4(d: D) + requires d == #D.Green || d == #D.Red; +{ + if (d != #D.Green) { + match (d) { // error: missing case Red + // nothing here + } + } +} + +function F0(d: D): int +{ + match (d) // error: missing cases Red + case Purple => 80 + case Green => 0 + case Blue => 2 +} + +function F1(d: D, x: int): int + requires x < 100; + requires d == #D.Red ==> x == 200; // (an impossibility, given the first precondition, so d != Red) +{ + match (d) + case Purple => 80 + case Green => 0 + case Blue => 2 +} diff --git a/Test/dafny0/runtest.bat b/Test/dafny0/runtest.bat index f67429d2..16bc0e2b 100644 --- a/Test/dafny0/runtest.bat +++ b/Test/dafny0/runtest.bat @@ -15,7 +15,7 @@ for %%f in (TypeTests.dfy NatTypes.dfy SmallTests.dfy Definedness.dfy FunctionSpecifications.dfy ResolutionErrors.dfy Array.dfy MultiDimArray.dfy NonGhostQuantifiers.dfy AdvancedLHS.dfy Modules0.dfy Modules1.dfy BadFunction.dfy - Comprehensions.dfy + Comprehensions.dfy ControlStructures.dfy Termination.dfy Use.dfy DTypes.dfy TypeParameters.dfy Datatypes.dfy TypeAntecedents.dfy SplitExpr.dfy Refinement.dfy RefinementErrors.dfy) do ( -- cgit v1.2.3 From 0a1cb72eec7026923f21a097e80d161f9c86fb44 Mon Sep 17 00:00:00 2001 From: Mike Barnett Date: Thu, 19 May 2011 13:25:23 -0700 Subject: Unify translation of arguments so the same code is used for IMethodCall and ICreateObjectInstance. --- BCT/BytecodeTranslator/ExpressionTraverser.cs | 240 +++++----- .../TranslationTest/GeneralHeapInput.txt | 482 +++++++++++-------- .../TranslationTest/SplitFieldsHeapInput.txt | 528 ++++++++++++--------- 3 files changed, 693 insertions(+), 557 deletions(-) diff --git a/BCT/BytecodeTranslator/ExpressionTraverser.cs b/BCT/BytecodeTranslator/ExpressionTraverser.cs index 052d8bf4..1dd181ae 100644 --- a/BCT/BytecodeTranslator/ExpressionTraverser.cs +++ b/BCT/BytecodeTranslator/ExpressionTraverser.cs @@ -469,85 +469,20 @@ namespace BytecodeTranslator MemberHelper.GetMethodSignature(methodCall.MethodToCall, NameFormattingOptions.None)); } - #region Translate In and Out Parameters - var inexpr = new List(); - var outvars = new List(); - - #region Create the 'this' argument for the function call - Bpl.IdentifierExpr thisExpr = null; - List locals = null; - List args = null; - Bpl.Expr arrayExpr = null; - Bpl.Expr indexExpr = null; - if (!methodCall.IsStaticCall) { - this.collectStructFields = true; - this.structFields = new List(); - this.arrayExpr = null; - this.indexExpr = null; - this.Visit(methodCall.ThisArgument); - this.collectStructFields = false; - args = this.structFields; - arrayExpr = this.arrayExpr; - indexExpr = this.indexExpr; - - var e = this.TranslatedExpressions.Pop(); - inexpr.Add(e); - if (e is Bpl.NAryExpr) { - e = ((Bpl.NAryExpr)e).Args[0]; - } - thisExpr = e as Bpl.IdentifierExpr; - locals = new List(); - Bpl.Variable x = thisExpr.Decl; - locals.Add(x); - for (int i = 0; i < args.Count; i++) { - Bpl.IdentifierExpr g = Bpl.Expr.Ident(this.sink.FindOrCreateFieldVariable(args[i])); - Bpl.Variable y = this.sink.CreateFreshLocal(args[i].Type); - StmtTraverser.StmtBuilder.Add(TranslationHelper.BuildAssignCmd(Bpl.Expr.Ident(y), this.sink.Heap.ReadHeap(Bpl.Expr.Ident(x), g, args[i].ContainingType.ResolvedType.IsStruct ? AccessType.Struct : AccessType.Heap, y.TypedIdent.Type))); - x = y; - locals.Add(y); - } - } - if (!methodCall.IsStaticCall && methodCall.MethodToCall.ContainingType.ResolvedType.IsStruct) { - outvars.Add(thisExpr); - } - #endregion + Bpl.IToken cloc = methodCall.Token(); - Dictionary toBoxed = new Dictionary(); - Dictionary p2eMap = new Dictionary(); - IEnumerator penum = resolvedMethod.Parameters.GetEnumerator(); - penum.MoveNext(); - foreach (IExpression exp in methodCall.Arguments) { - if (penum.Current == null) { - throw new TranslationException("More arguments than parameters in method call"); - } - this.Visit(exp); - Bpl.Expr e = this.TranslatedExpressions.Pop(); - if (penum.Current.Type is IGenericTypeParameter) - inexpr.Add(sink.Heap.Box(methodCall.Token(), this.sink.CciTypeToBoogie(exp.Type), e)); - else - inexpr.Add(e); - if (penum.Current.IsByReference) { - Bpl.IdentifierExpr unboxed = e as Bpl.IdentifierExpr; - if (unboxed == null) { - throw new TranslationException("Trying to pass a complex expression for an out or ref parameter"); - } - if (penum.Current.Type is IGenericTypeParameter) { - Bpl.IdentifierExpr boxed = Bpl.Expr.Ident(sink.CreateFreshLocal(this.sink.Heap.BoxType)); - toBoxed[unboxed] = boxed; - outvars.Add(boxed); - } - else { - outvars.Add(unboxed); - } - } - penum.MoveNext(); - } - #endregion + List inexpr; + List outvars; + Bpl.IdentifierExpr thisExpr; + List locals; + List args; + Bpl.Expr arrayExpr; + Bpl.Expr indexExpr; + Dictionary toBoxed; + var proc = TranslateArgumentsAndReturnProcedure(cloc, methodCall.MethodToCall, resolvedMethod, methodCall.IsStaticCall ? null : methodCall.ThisArgument, methodCall.Arguments, out inexpr, out outvars, out thisExpr, out locals, out args, out arrayExpr, out indexExpr, out toBoxed); - var proc = this.sink.FindOrCreateProcedure(resolvedMethod); string methodname = proc.Name; var translateAsFunctionCall = proc is Bpl.Function; - Bpl.IToken cloc = methodCall.Token(); Bpl.QKeyValue attrib = null; if (!translateAsFunctionCall) { @@ -646,6 +581,80 @@ namespace BytecodeTranslator } } + private Bpl.DeclWithFormals TranslateArgumentsAndReturnProcedure(Bpl.IToken token, IMethodReference methodToCall, IMethodDefinition resolvedMethod, IExpression/*?*/ thisArg, IEnumerable arguments, out List inexpr, out List outvars, out Bpl.IdentifierExpr thisExpr, out List locals, out List args, out Bpl.Expr arrayExpr, out Bpl.Expr indexExpr, out Dictionary toBoxed) { + inexpr = new List(); + outvars = new List(); + + #region Create the 'this' argument for the function call + thisExpr = null; + locals = null; + args = null; + arrayExpr = null; + indexExpr = null; + if (thisArg != null) { + this.collectStructFields = true; + this.structFields = new List(); + this.arrayExpr = null; + this.indexExpr = null; + this.Visit(thisArg); + this.collectStructFields = false; + args = this.structFields; + arrayExpr = this.arrayExpr; + indexExpr = this.indexExpr; + + var e = this.TranslatedExpressions.Pop(); + inexpr.Add(e); + if (e is Bpl.NAryExpr) { + e = ((Bpl.NAryExpr)e).Args[0]; + } + thisExpr = e as Bpl.IdentifierExpr; + locals = new List(); + Bpl.Variable x = thisExpr.Decl; + locals.Add(x); + for (int i = 0; i < args.Count; i++) { + Bpl.IdentifierExpr g = Bpl.Expr.Ident(this.sink.FindOrCreateFieldVariable(args[i])); + Bpl.Variable y = this.sink.CreateFreshLocal(args[i].Type); + StmtTraverser.StmtBuilder.Add(TranslationHelper.BuildAssignCmd(Bpl.Expr.Ident(y), this.sink.Heap.ReadHeap(Bpl.Expr.Ident(x), g, args[i].ContainingType.ResolvedType.IsStruct ? AccessType.Struct : AccessType.Heap, y.TypedIdent.Type))); + x = y; + locals.Add(y); + } + } + if (thisArg != null && methodToCall.ContainingType.ResolvedType.IsStruct) { + outvars.Add(thisExpr); + } + #endregion + + toBoxed = new Dictionary(); + IEnumerator penum = resolvedMethod.Parameters.GetEnumerator(); + penum.MoveNext(); + foreach (IExpression exp in arguments) { + if (penum.Current == null) { + throw new TranslationException("More arguments than parameters in method call"); + } + this.Visit(exp); + Bpl.Expr e = this.TranslatedExpressions.Pop(); + if (penum.Current.Type is IGenericTypeParameter) + inexpr.Add(sink.Heap.Box(token, this.sink.CciTypeToBoogie(exp.Type), e)); + else + inexpr.Add(e); + if (penum.Current.IsByReference) { + Bpl.IdentifierExpr unboxed = e as Bpl.IdentifierExpr; + if (unboxed == null) { + throw new TranslationException("Trying to pass a complex expression for an out or ref parameter"); + } + if (penum.Current.Type is IGenericTypeParameter) { + Bpl.IdentifierExpr boxed = Bpl.Expr.Ident(sink.CreateFreshLocal(this.sink.Heap.BoxType)); + toBoxed[unboxed] = boxed; + outvars.Add(boxed); + } else { + outvars.Add(unboxed); + } + } + penum.MoveNext(); + } + return this.sink.FindOrCreateProcedure(resolvedMethod); + } + #endregion #region Translate Assignments @@ -878,7 +887,40 @@ namespace BytecodeTranslator /// public override void Visit(ICreateObjectInstance createObjectInstance) { - TranslateObjectCreation(createObjectInstance.MethodToCall, createObjectInstance.Arguments, createObjectInstance); + var ctor = createObjectInstance.MethodToCall; + var resolvedMethod = Sink.Unspecialize(ctor).ResolvedMethod; + Bpl.IToken token = createObjectInstance.Token(); + + var a = this.sink.CreateFreshLocal(createObjectInstance.Type); + + // First generate an Alloc() call + this.StmtTraverser.StmtBuilder.Add(new Bpl.CallCmd(token, this.sink.AllocationMethodName, new Bpl.ExprSeq(), new Bpl.IdentifierExprSeq(Bpl.Expr.Ident(a)))); + + // Second, generate the call to the appropriate ctor + + List inexpr; + List outvars; + Bpl.IdentifierExpr thisExpr; + List locals; + List args; + Bpl.Expr arrayExpr; + Bpl.Expr indexExpr; + Dictionary toBoxed; + var proc = TranslateArgumentsAndReturnProcedure(token, ctor, resolvedMethod, null, createObjectInstance.Arguments, out inexpr, out outvars, out thisExpr, out locals, out args, out arrayExpr, out indexExpr, out toBoxed); + + this.StmtTraverser.StmtBuilder.Add(new Bpl.CallCmd(token, proc.Name, inexpr, outvars)); + + // Generate assumption about the dynamic type of the just allocated object + this.StmtTraverser.StmtBuilder.Add( + new Bpl.AssumeCmd(token, + Bpl.Expr.Binary(Bpl.BinaryOperator.Opcode.Eq, + this.sink.Heap.DynamicType(inexpr[0]), + Bpl.Expr.Ident(this.sink.FindOrCreateType(createObjectInstance.Type)) + ) + ) + ); + + TranslatedExpressions.Push(Bpl.Expr.Ident(a)); } public override void Visit(ICreateArray createArrayInstance) @@ -912,52 +954,6 @@ namespace BytecodeTranslator TranslateDelegateCreation(createDelegateInstance.MethodToCallViaDelegate, createDelegateInstance.Type, createDelegateInstance); } - private void TranslateObjectCreation(IMethodReference ctor, IEnumerable arguments, IExpression creationAST) - { - var resolvedMethod = Sink.Unspecialize(ctor).ResolvedMethod; - Bpl.IToken token = creationAST.Token(); - - var a = this.sink.CreateFreshLocal(creationAST.Type); - - // First generate an Alloc() call - this.StmtTraverser.StmtBuilder.Add(new Bpl.CallCmd(token, this.sink.AllocationMethodName, new Bpl.ExprSeq(), new Bpl.IdentifierExprSeq(Bpl.Expr.Ident(a)))); - - // Second, generate the call to the appropriate ctor - var proc = this.sink.FindOrCreateProcedure(resolvedMethod); - Bpl.ExprSeq inexpr = new Bpl.ExprSeq(); - inexpr.Add(Bpl.Expr.Ident(a)); - IEnumerator penum = resolvedMethod.Parameters.GetEnumerator(); - penum.MoveNext(); - foreach (IExpression exp in arguments) { - if (penum.Current == null) { - throw new TranslationException("More Arguments than Parameters in functioncall"); - } - this.Visit(exp); - Bpl.Expr e = this.TranslatedExpressions.Pop(); - if (penum.Current.Type is IGenericTypeParameter) - inexpr.Add(sink.Heap.Box(ctor.Token(), this.sink.CciTypeToBoogie(exp.Type), e)); - else - inexpr.Add(e); - penum.MoveNext(); - } - - Bpl.IdentifierExprSeq outvars = new Bpl.IdentifierExprSeq(); - - this.StmtTraverser.StmtBuilder.Add(new Bpl.CallCmd(token, proc.Name, inexpr, outvars)); - - // Generate assumption about the dynamic type of the just allocated object - this.StmtTraverser.StmtBuilder.Add( - new Bpl.AssumeCmd(token, - Bpl.Expr.Binary(Bpl.BinaryOperator.Opcode.Eq, - this.sink.Heap.DynamicType(inexpr[0]), - Bpl.Expr.Ident(this.sink.FindOrCreateType(creationAST.Type)) - ) - ) - ); - - TranslatedExpressions.Push(Bpl.Expr.Ident(a)); - } - private void TranslateDelegateCreation(IMethodReference methodToCall, ITypeReference type, IExpression creationAST) { Bpl.IToken cloc = creationAST.Token(); diff --git a/BCT/RegressionTests/TranslationTest/GeneralHeapInput.txt b/BCT/RegressionTests/TranslationTest/GeneralHeapInput.txt index 5b24fa32..aac37bf0 100644 --- a/BCT/RegressionTests/TranslationTest/GeneralHeapInput.txt +++ b/BCT/RegressionTests/TranslationTest/GeneralHeapInput.txt @@ -9,7 +9,6 @@ var $Alloc: [Ref]bool; procedure {:inline 1} Alloc() returns (x: Ref); modifies $Alloc; - free ensures x != null; @@ -39,6 +38,73 @@ axiom (forall x: Ref :: { Ref2Box(x) } Box2Ref(Ref2Box(x)) == x); axiom (forall x: Struct :: { Struct2Box(x) } Box2Struct(Struct2Box(x)) == x); +procedure {:inline 1} System.Object.GetType(this: Ref) returns ($result: Ref); + + + +implementation System.Object.GetType(this: Ref) returns ($result: Ref) +{ + $result := $TypeOf($DynamicType(this)); +} + + + +axiom (forall t: Type :: $DynamicType($TypeOf(t)) == t); + +function $ThreadDelegate(Ref) : Ref; + +procedure {:inline 1} System.Threading.Thread.#ctor$System.Threading.ParameterizedThreadStart(this: Ref, start$in: Ref); + + + +implementation System.Threading.Thread.#ctor$System.Threading.ParameterizedThreadStart(this: Ref, start$in: Ref) +{ + assume $ThreadDelegate(this) == start$in; +} + + + +procedure {:inline 1} System.Threading.Thread.Start$System.Object(this: Ref, parameter$in: Ref); + + + +implementation System.Threading.Thread.Start$System.Object(this: Ref, parameter$in: Ref) +{ + call {:async} System.Threading.ParameterizedThreadStart.Invoke$System.Object($ThreadDelegate(this), parameter$in); +} + + + +procedure {:extern} System.Threading.ParameterizedThreadStart.Invoke$System.Object(this: Ref, obj$in: Ref); + + + +procedure {:inline 1} System.Threading.Thread.#ctor$System.Threading.ThreadStart(this: Ref, start$in: Ref); + + + +implementation System.Threading.Thread.#ctor$System.Threading.ThreadStart(this: Ref, start$in: Ref) +{ + assume $ThreadDelegate(this) == start$in; +} + + + +procedure {:inline 1} System.Threading.Thread.Start(this: Ref); + + + +implementation System.Threading.Thread.Start(this: Ref) +{ + call {:async} System.Threading.ThreadStart.Invoke($ThreadDelegate(this)); +} + + + +procedure {:extern} System.Threading.ThreadStart.Invoke(this: Ref); + + + procedure DelegateAdd(a: Ref, b: Ref) returns (c: Ref); @@ -210,7 +276,7 @@ function {:inline true} Write(H: HeapType, o: Ref, f: Field, v: Box) : HeapType var $ArrayContents: [Ref][int]Box; -var $ArrayLength: [Ref]int; +function $ArrayLength(Ref) : int; type Field; @@ -277,6 +343,129 @@ var $Method: [Ref][Ref]int; var $Receiver: [Ref][Ref]Ref; +const unique RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap: Type; + +const unique RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.x: Field; + +const unique RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.y: Field; + +procedure RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.M(this: Ref); + + + +implementation RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.M(this: Ref) +{ + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 130} true; + $Heap := Write($Heap, this, RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.y, Int2Box(Box2Int(Read($Heap, this, RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.x)))); + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 131} true; + return; +} + + + +procedure RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.#ctor(this: Ref); + + + +procedure {:extern} System.Object.#ctor(this: Ref); + + + +implementation RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.#ctor(this: Ref) +{ + $Heap := Write($Heap, this, RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.x, Int2Box(0)); + $Heap := Write($Heap, this, RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.y, Int2Box(0)); + call System.Object.#ctor(this); + return; +} + + + +procedure RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.#cctor(); + + + +implementation RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.#cctor() +{ +} + + + +const unique RegressionTestInput.CreateStruct: Type; + +procedure RegressionTestInput.CreateStruct.Create(this: Ref) returns ($result: [Field]Box); + + + +const unique RegressionTestInput.S.x: Field; + +const unique RegressionTestInput.S.b: Field; + +implementation RegressionTestInput.CreateStruct.Create(this: Ref) returns ($result: [Field]Box) +{ + var local_0: [Field]Box; + var _loc0: int; + var _loc1: bool; + + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 141} true; + local_0 := $DefaultStruct; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 142} true; + _loc0 := local_0; + assert Box2Int(_loc0[RegressionTestInput.S.x]) == 0; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 143} true; + _loc1 := local_0; + assert !Box2Bool(_loc1[RegressionTestInput.S.b]); + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 145} true; + $result := local_0; + return; +} + + + +procedure RegressionTestInput.CreateStruct.AssignThreeToSDotX$RegressionTestInput.S(this: Ref, s$in: [Field]Box) returns ($result: [Field]Box); + + + +implementation RegressionTestInput.CreateStruct.AssignThreeToSDotX$RegressionTestInput.S(this: Ref, s$in: [Field]Box) returns ($result: [Field]Box) +{ + var s: [Field]Box; + var _loc0: int; + + s := s$in; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 147} true; + s := s[RegressionTestInput.S.x := Int2Box(3)]; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 148} true; + _loc0 := s; + assert Box2Int(_loc0[RegressionTestInput.S.x]) == 3; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 150} true; + $result := s; + return; +} + + + +procedure RegressionTestInput.CreateStruct.#ctor(this: Ref); + + + +implementation RegressionTestInput.CreateStruct.#ctor(this: Ref) +{ + call System.Object.#ctor(this); + return; +} + + + +procedure RegressionTestInput.CreateStruct.#cctor(); + + + +implementation RegressionTestInput.CreateStruct.#cctor() +{ +} + + + const unique RegressionTestInput.ClassWithArrayTypes: Type; var RegressionTestInput.ClassWithArrayTypes.s: Ref; @@ -299,28 +488,30 @@ implementation RegressionTestInput.ClassWithArrayTypes.Main1() var $tmp5: int; var $tmp6: int; - assert {:sourceFile "Class1.cs"} {:sourceLine 86} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 86} true; call $tmp0 := Alloc(); + assume $ArrayLength($tmp0) == 1 * 5; local_0 := $tmp0; - assert {:sourceFile "Class1.cs"} {:sourceLine 87} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 87} true; $tmp1 := Box2Int($ArrayContents[local_0][0]); $ArrayContents := $ArrayContents[local_0 := $ArrayContents[local_0][0 := Int2Box(2)]]; - assert {:sourceFile "Class1.cs"} {:sourceLine 88} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 88} true; $tmp2 := Box2Int($ArrayContents[local_0][0]); assert $tmp2 == 2; - assert {:sourceFile "Class1.cs"} {:sourceLine 90} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 90} true; call $tmp3 := Alloc(); + assume $ArrayLength($tmp3) == 1 * 4; local_1 := $tmp3; - assert {:sourceFile "Class1.cs"} {:sourceLine 91} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 91} true; $tmp4 := Box2Int($ArrayContents[local_1][0]); $ArrayContents := $ArrayContents[local_1 := $ArrayContents[local_1][0 := Int2Box(1)]]; - assert {:sourceFile "Class1.cs"} {:sourceLine 92} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 92} true; $tmp5 := Box2Int($ArrayContents[local_1][0]); assert $tmp5 == 1; - assert {:sourceFile "Class1.cs"} {:sourceLine 94} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 94} true; $tmp6 := Box2Int($ArrayContents[local_0][0]); assert $tmp6 == 2; - assert {:sourceFile "Class1.cs"} {:sourceLine 95} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 95} true; return; } @@ -341,28 +532,30 @@ implementation RegressionTestInput.ClassWithArrayTypes.Main2() var $tmp12: int; var $tmp13: int; - assert {:sourceFile "Class1.cs"} {:sourceLine 100} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 100} true; call $tmp7 := Alloc(); + assume $ArrayLength($tmp7) == 1 * 5; RegressionTestInput.ClassWithArrayTypes.s := $tmp7; - assert {:sourceFile "Class1.cs"} {:sourceLine 101} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 101} true; $tmp8 := Box2Int($ArrayContents[RegressionTestInput.ClassWithArrayTypes.s][0]); $ArrayContents := $ArrayContents[RegressionTestInput.ClassWithArrayTypes.s := $ArrayContents[RegressionTestInput.ClassWithArrayTypes.s][0 := Int2Box(2)]]; - assert {:sourceFile "Class1.cs"} {:sourceLine 102} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 102} true; $tmp9 := Box2Int($ArrayContents[RegressionTestInput.ClassWithArrayTypes.s][0]); assert $tmp9 == 2; - assert {:sourceFile "Class1.cs"} {:sourceLine 104} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 104} true; call $tmp10 := Alloc(); + assume $ArrayLength($tmp10) == 1 * 4; local_0 := $tmp10; - assert {:sourceFile "Class1.cs"} {:sourceLine 105} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 105} true; $tmp11 := Box2Int($ArrayContents[local_0][0]); $ArrayContents := $ArrayContents[local_0 := $ArrayContents[local_0][0 := Int2Box(1)]]; - assert {:sourceFile "Class1.cs"} {:sourceLine 106} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 106} true; $tmp12 := Box2Int($ArrayContents[local_0][0]); assert $tmp12 == 1; - assert {:sourceFile "Class1.cs"} {:sourceLine 108} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 108} true; $tmp13 := Box2Int($ArrayContents[RegressionTestInput.ClassWithArrayTypes.s][0]); assert $tmp13 == 2; - assert {:sourceFile "Class1.cs"} {:sourceLine 109} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 109} true; return; } @@ -381,17 +574,17 @@ implementation RegressionTestInput.ClassWithArrayTypes.Main3$System.Int32(this: var $tmp17: int; x := x$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 114} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 114} true; $tmp14 := Box2Int($ArrayContents[this][x]); $ArrayContents := $ArrayContents[this := $ArrayContents[this][x := Int2Box(42)]]; - assert {:sourceFile "Class1.cs"} {:sourceLine 115} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 115} true; $tmp15 := Box2Int($ArrayContents[this][x + 1]); $ArrayContents := $ArrayContents[this := $ArrayContents[this][x + 1 := Int2Box(43)]]; - assert {:sourceFile "Class1.cs"} {:sourceLine 116} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 116} true; $tmp16 := Box2Int($ArrayContents[Box2Ref(Read($Heap, this, RegressionTestInput.ClassWithArrayTypes.a))][x + 1]); $tmp17 := Box2Int($ArrayContents[Box2Ref(Read($Heap, this, RegressionTestInput.ClassWithArrayTypes.a))][x]); assert $tmp16 == $tmp17 + 1; - assert {:sourceFile "Class1.cs"} {:sourceLine 117} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 117} true; return; } @@ -408,9 +601,9 @@ implementation RegressionTestInput.ClassWithArrayTypes.Main4$System.Int32array(t var $tmp19: int; xs := xs$in; - if (!(if xs != null then $ArrayLength[xs] <= 0 else true)) + if (!(if xs != null then $ArrayLength(xs) <= 0 else true)) { - assert {:sourceFile "Class1.cs"} {:sourceLine 121} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 121} true; $tmp18 := Box2Int($ArrayContents[xs][0]); $tmp19 := Box2Int($ArrayContents[this][0]); $ArrayContents := $ArrayContents[this := $ArrayContents[this][0 := Int2Box($tmp18)]]; @@ -419,7 +612,7 @@ implementation RegressionTestInput.ClassWithArrayTypes.Main4$System.Int32array(t { } - assert {:sourceFile "Class1.cs"} {:sourceLine 123} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 123} true; return; } @@ -429,10 +622,6 @@ procedure RegressionTestInput.ClassWithArrayTypes.#ctor(this: Ref); -procedure {:extern} System.Object.#ctor(this: Ref); - - - implementation RegressionTestInput.ClassWithArrayTypes.#ctor(this: Ref) { $Heap := Write($Heap, this, RegressionTestInput.ClassWithArrayTypes.a, Ref2Box(null)); @@ -453,6 +642,34 @@ implementation RegressionTestInput.ClassWithArrayTypes.#cctor() +const unique RegressionTestInput.AsyncAttribute: Type; + +procedure RegressionTestInput.AsyncAttribute.#ctor(this: Ref); + + + +procedure {:extern} System.Attribute.#ctor(this: Ref); + + + +implementation RegressionTestInput.AsyncAttribute.#ctor(this: Ref) +{ + call System.Attribute.#ctor(this); + return; +} + + + +procedure RegressionTestInput.AsyncAttribute.#cctor(); + + + +implementation RegressionTestInput.AsyncAttribute.#cctor() +{ +} + + + const unique RegressionTestInput.RefParameters: Type; procedure RegressionTestInput.RefParameters.M$System.Int32$(x$in: int) returns (x$out: int); @@ -462,9 +679,9 @@ procedure RegressionTestInput.RefParameters.M$System.Int32$(x$in: int) returns ( implementation RegressionTestInput.RefParameters.M$System.Int32$(x$in: int) returns (x$out: int) { x$out := x$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 156} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 156} true; x$out := x$out + 1; - assert {:sourceFile "Class1.cs"} {:sourceLine 157} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 157} true; return; } @@ -492,6 +709,8 @@ implementation RegressionTestInput.RefParameters.#cctor() +const unique RegressionTestInput.S: Type; + const unique RegressionTestInput.Class0: Type; var RegressionTestInput.Class0.StaticInt: int; @@ -505,7 +724,7 @@ implementation RegressionTestInput.Class0.StaticMethod$System.Int32(x$in: int) r var x: int; x := x$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 18} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 18} true; $result := x + 1; return; } @@ -529,13 +748,13 @@ implementation RegressionTestInput.Class0.M$System.Int32(this: Ref, x$in: int) __temp_1 := 5 / $tmp20; x := 3; local_0 := __temp_1 + 3; - assert {:sourceFile "Class1.cs"} {:sourceLine 22} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 22} true; assert (if x == 3 then local_0 <= 8 else false); - assert {:sourceFile "Class1.cs"} {:sourceLine 23} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 23} true; RegressionTestInput.Class0.StaticInt := local_0; - assert {:sourceFile "Class1.cs"} {:sourceLine 24} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 24} true; assert local_0 == RegressionTestInput.Class0.StaticInt; - assert {:sourceFile "Class1.cs"} {:sourceLine 25} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 25} true; return; } @@ -552,7 +771,7 @@ implementation RegressionTestInput.Class0.M$System.Int32$System.Int32(this: Ref, x := x$in; y := y$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 28} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 28} true; return; } @@ -567,7 +786,7 @@ implementation RegressionTestInput.Class0.M$System.Boolean(this: Ref, b$in: bool var b: bool; b := b$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 29} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 29} true; return; } @@ -582,7 +801,7 @@ implementation RegressionTestInput.Class0.M$RegressionTestInput.Class0(this: Ref var c: Ref; c := c$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 30} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 30} true; return; } @@ -596,7 +815,7 @@ implementation RegressionTestInput.Class0.NonVoid(this: Ref) returns ($result: i { var $tmp21: int; - assert {:sourceFile "Class1.cs"} {:sourceLine 34} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 34} true; call $tmp21 := RegressionTestInput.Class0.StaticMethod$System.Int32(3); $result := 3 + RegressionTestInput.Class0.StaticInt + $tmp21; return; @@ -611,9 +830,9 @@ procedure RegressionTestInput.Class0.OutParam$System.Int32$(this: Ref, x$in: int implementation RegressionTestInput.Class0.OutParam$System.Int32$(this: Ref, x$in: int) returns (x$out: int, $result: int) { x$out := x$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 37} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 37} true; x$out := 3 + RegressionTestInput.Class0.StaticInt; - assert {:sourceFile "Class1.cs"} {:sourceLine 39} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 39} true; $result := x$out; return; } @@ -627,11 +846,11 @@ procedure RegressionTestInput.Class0.RefParam$System.Int32$(this: Ref, x$in: int implementation RegressionTestInput.Class0.RefParam$System.Int32$(this: Ref, x$in: int) returns (x$out: int, $result: int) { x$out := x$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 42} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 42} true; x$out := x$out + 1; - assert {:sourceFile "Class1.cs"} {:sourceLine 43} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 43} true; RegressionTestInput.Class0.StaticInt := x$out; - assert {:sourceFile "Class1.cs"} {:sourceLine 45} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 45} true; $result := x$out; return; } @@ -647,11 +866,11 @@ implementation RegressionTestInput.Class0.AssignToInParam$System.Int32(this: Ref var x: int; x := x$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 48} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 48} true; x := x + 1; - assert {:sourceFile "Class1.cs"} {:sourceLine 49} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 49} true; RegressionTestInput.Class0.StaticInt := x; - assert {:sourceFile "Class1.cs"} {:sourceLine 51} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 51} true; $result := x; return; } @@ -667,7 +886,7 @@ implementation RegressionTestInput.Class0.MethodThatRepresentsAnAynchronousMetho var x: int; x := x$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 56} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 56} true; $result := x; return; } @@ -684,7 +903,7 @@ implementation RegressionTestInput.Class0.CallAsyncMethod$System.Int32(this: Ref var $tmp22: int; y := y$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 60} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 60} true; call {:async} $tmp22 := RegressionTestInput.Class0.MethodThatRepresentsAnAynchronousMethod$System.Int32(this, y); $result := $tmp22; return; @@ -715,50 +934,6 @@ implementation RegressionTestInput.Class0.#cctor() -const unique RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap: Type; - -const unique RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.x: Field; - -const unique RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.y: Field; - -procedure RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.M(this: Ref); - - - -implementation RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.M(this: Ref) -{ - assert {:sourceFile "Class1.cs"} {:sourceLine 130} true; - $Heap := Write($Heap, this, RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.y, Int2Box(Box2Int(Read($Heap, this, RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.x)))); - assert {:sourceFile "Class1.cs"} {:sourceLine 131} true; - return; -} - - - -procedure RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.#ctor(this: Ref); - - - -implementation RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.#ctor(this: Ref) -{ - $Heap := Write($Heap, this, RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.x, Int2Box(0)); - $Heap := Write($Heap, this, RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.y, Int2Box(0)); - call System.Object.#ctor(this); - return; -} - - - -procedure RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.#cctor(); - - - -implementation RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.#cctor() -{ -} - - - const unique RegressionTestInput.ClassWithBoolTypes: Type; var RegressionTestInput.ClassWithBoolTypes.staticB: bool; @@ -776,7 +951,7 @@ implementation RegressionTestInput.ClassWithBoolTypes.M$System.Int32$System.Int3 x := x$in; y := y$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 70} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 70} true; $result := x < y; return; } @@ -793,14 +968,14 @@ implementation RegressionTestInput.ClassWithBoolTypes.#ctor$System.Boolean(this: z := z$in; $Heap := Write($Heap, this, RegressionTestInput.ClassWithBoolTypes.b, Bool2Box(false)); - assert {:sourceFile "Class1.cs"} {:sourceLine 72} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 72} true; call System.Object.#ctor(this); - assert {:sourceFile "Class1.cs"} {:sourceLine 73} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 73} true; $Heap := Write($Heap, this, RegressionTestInput.ClassWithBoolTypes.b, Bool2Box(z)); - assert {:sourceFile "Class1.cs"} {:sourceLine 74} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 74} true; if (z) { - assert {:sourceFile "Class1.cs"} {:sourceLine 74} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 74} true; RegressionTestInput.ClassWithBoolTypes.staticB := z; } else @@ -820,9 +995,9 @@ implementation RegressionTestInput.ClassWithBoolTypes.Main() { var $tmp23: bool; - assert {:sourceFile "Class1.cs"} {:sourceLine 78} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 78} true; call $tmp23 := RegressionTestInput.ClassWithBoolTypes.M$System.Int32$System.Int32(3, 4); - assert {:sourceFile "Class1.cs"} {:sourceLine 79} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 79} true; return; } @@ -838,108 +1013,3 @@ implementation RegressionTestInput.ClassWithBoolTypes.#cctor() } - -const unique RegressionTestInput.S: Type; - -const unique RegressionTestInput.S.x: Field; - -const unique RegressionTestInput.S.b: Field; - -const unique RegressionTestInput.AsyncAttribute: Type; - -procedure RegressionTestInput.AsyncAttribute.#ctor(this: Ref); - - - -procedure {:extern} System.Attribute.#ctor(this: Ref); - - - -implementation RegressionTestInput.AsyncAttribute.#ctor(this: Ref) -{ - call System.Attribute.#ctor(this); - return; -} - - - -procedure RegressionTestInput.AsyncAttribute.#cctor(); - - - -implementation RegressionTestInput.AsyncAttribute.#cctor() -{ -} - - - -const unique RegressionTestInput.CreateStruct: Type; - -procedure RegressionTestInput.CreateStruct.Create(this: Ref) returns ($result: [Field]Box); - - - -implementation RegressionTestInput.CreateStruct.Create(this: Ref) returns ($result: [Field]Box) -{ - var local_0: [Field]Box; - var _loc0: int; - var _loc1: bool; - - assert {:sourceFile "Class1.cs"} {:sourceLine 141} true; - local_0 := $DefaultStruct; - assert {:sourceFile "Class1.cs"} {:sourceLine 142} true; - _loc0 := local_0; - assert Box2Int(_loc0[RegressionTestInput.S.x]) == 0; - assert {:sourceFile "Class1.cs"} {:sourceLine 143} true; - _loc1 := local_0; - assert !Box2Bool(_loc1[RegressionTestInput.S.b]); - assert {:sourceFile "Class1.cs"} {:sourceLine 145} true; - $result := local_0; - return; -} - - - -procedure RegressionTestInput.CreateStruct.AssignThreeToSDotX$RegressionTestInput.S(this: Ref, s$in: [Field]Box) returns ($result: [Field]Box); - - - -implementation RegressionTestInput.CreateStruct.AssignThreeToSDotX$RegressionTestInput.S(this: Ref, s$in: [Field]Box) returns ($result: [Field]Box) -{ - var s: [Field]Box; - var _loc0: int; - - s := s$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 147} true; - s := s[RegressionTestInput.S.x := Int2Box(3)]; - assert {:sourceFile "Class1.cs"} {:sourceLine 148} true; - _loc0 := s; - assert Box2Int(_loc0[RegressionTestInput.S.x]) == 3; - assert {:sourceFile "Class1.cs"} {:sourceLine 150} true; - $result := s; - return; -} - - - -procedure RegressionTestInput.CreateStruct.#ctor(this: Ref); - - - -implementation RegressionTestInput.CreateStruct.#ctor(this: Ref) -{ - call System.Object.#ctor(this); - return; -} - - - -procedure RegressionTestInput.CreateStruct.#cctor(); - - - -implementation RegressionTestInput.CreateStruct.#cctor() -{ -} - - diff --git a/BCT/RegressionTests/TranslationTest/SplitFieldsHeapInput.txt b/BCT/RegressionTests/TranslationTest/SplitFieldsHeapInput.txt index 163b9153..73e2000c 100644 --- a/BCT/RegressionTests/TranslationTest/SplitFieldsHeapInput.txt +++ b/BCT/RegressionTests/TranslationTest/SplitFieldsHeapInput.txt @@ -11,7 +11,6 @@ var $Alloc: [Ref]bool; procedure {:inline 1} Alloc() returns (x: Ref); modifies $Alloc; - free ensures x != null; @@ -41,6 +40,73 @@ axiom (forall x: Ref :: { Ref2Box(x) } Box2Ref(Ref2Box(x)) == x); axiom (forall x: Struct :: { Struct2Box(x) } Box2Struct(Struct2Box(x)) == x); +procedure {:inline 1} System.Object.GetType(this: Ref) returns ($result: Ref); + + + +implementation System.Object.GetType(this: Ref) returns ($result: Ref) +{ + $result := $TypeOf($DynamicType(this)); +} + + + +axiom (forall t: Type :: $DynamicType($TypeOf(t)) == t); + +function $ThreadDelegate(Ref) : Ref; + +procedure {:inline 1} System.Threading.Thread.#ctor$System.Threading.ParameterizedThreadStart(this: Ref, start$in: Ref); + + + +implementation System.Threading.Thread.#ctor$System.Threading.ParameterizedThreadStart(this: Ref, start$in: Ref) +{ + assume $ThreadDelegate(this) == start$in; +} + + + +procedure {:inline 1} System.Threading.Thread.Start$System.Object(this: Ref, parameter$in: Ref); + + + +implementation System.Threading.Thread.Start$System.Object(this: Ref, parameter$in: Ref) +{ + call {:async} System.Threading.ParameterizedThreadStart.Invoke$System.Object($ThreadDelegate(this), parameter$in); +} + + + +procedure {:extern} System.Threading.ParameterizedThreadStart.Invoke$System.Object(this: Ref, obj$in: Ref); + + + +procedure {:inline 1} System.Threading.Thread.#ctor$System.Threading.ThreadStart(this: Ref, start$in: Ref); + + + +implementation System.Threading.Thread.#ctor$System.Threading.ThreadStart(this: Ref, start$in: Ref) +{ + assume $ThreadDelegate(this) == start$in; +} + + + +procedure {:inline 1} System.Threading.Thread.Start(this: Ref); + + + +implementation System.Threading.Thread.Start(this: Ref) +{ + call {:async} System.Threading.ThreadStart.Invoke($ThreadDelegate(this)); +} + + + +procedure {:extern} System.Threading.ThreadStart.Invoke(this: Ref); + + + procedure DelegateAdd(a: Ref, b: Ref) returns (c: Ref); @@ -200,7 +266,7 @@ implementation DelegateRemoveHelper(oldi: Ref, m: int, o: Ref) returns (i: Ref) var $ArrayContents: [Ref][int]Box; -var $ArrayLength: [Ref]int; +function $ArrayLength(Ref) : int; type Field; @@ -267,6 +333,129 @@ var $Method: [Ref][Ref]int; var $Receiver: [Ref][Ref]Ref; +const unique RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap: Type; + +var RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.x: [Ref]int; + +var RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.y: [Ref]int; + +procedure RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.M(this: Ref); + + + +implementation RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.M(this: Ref) +{ + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 130} true; + RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.y[this] := RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.x[this]; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 131} true; + return; +} + + + +procedure RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.#ctor(this: Ref); + + + +procedure {:extern} System.Object.#ctor(this: Ref); + + + +implementation RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.#ctor(this: Ref) +{ + RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.x[this] := 0; + RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.y[this] := 0; + call System.Object.#ctor(this); + return; +} + + + +procedure RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.#cctor(); + + + +implementation RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.#cctor() +{ +} + + + +const unique RegressionTestInput.CreateStruct: Type; + +procedure RegressionTestInput.CreateStruct.Create(this: Ref) returns ($result: [Field]Box); + + + +var RegressionTestInput.S.x: [Ref]int; + +var RegressionTestInput.S.b: [Ref]bool; + +implementation RegressionTestInput.CreateStruct.Create(this: Ref) returns ($result: [Field]Box) +{ + var local_0: [Field]Box; + var _loc0: int; + var _loc1: bool; + + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 141} true; + local_0 := $DefaultStruct; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 142} true; + _loc0 := local_0; + assert Box2Int(_loc0[RegressionTestInput.S.x]) == 0; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 143} true; + _loc1 := local_0; + assert !Box2Bool(_loc1[RegressionTestInput.S.b]); + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 145} true; + $result := local_0; + return; +} + + + +procedure RegressionTestInput.CreateStruct.AssignThreeToSDotX$RegressionTestInput.S(this: Ref, s$in: [Field]Box) returns ($result: [Field]Box); + + + +implementation RegressionTestInput.CreateStruct.AssignThreeToSDotX$RegressionTestInput.S(this: Ref, s$in: [Field]Box) returns ($result: [Field]Box) +{ + var s: [Field]Box; + var _loc0: int; + + s := s$in; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 147} true; + s[RegressionTestInput.S.x] := Int2Box(3); + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 148} true; + _loc0 := s; + assert Box2Int(_loc0[RegressionTestInput.S.x]) == 3; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 150} true; + $result := s; + return; +} + + + +procedure RegressionTestInput.CreateStruct.#ctor(this: Ref); + + + +implementation RegressionTestInput.CreateStruct.#ctor(this: Ref) +{ + call System.Object.#ctor(this); + return; +} + + + +procedure RegressionTestInput.CreateStruct.#cctor(); + + + +implementation RegressionTestInput.CreateStruct.#cctor() +{ +} + + + const unique RegressionTestInput.ClassWithArrayTypes: Type; var RegressionTestInput.ClassWithArrayTypes.s: Ref; @@ -289,28 +478,30 @@ implementation RegressionTestInput.ClassWithArrayTypes.Main1() var $tmp5: int; var $tmp6: int; - assert {:sourceFile "Class1.cs"} {:sourceLine 86} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 86} true; call $tmp0 := Alloc(); + assume $ArrayLength($tmp0) == 1 * 5; local_0 := $tmp0; - assert {:sourceFile "Class1.cs"} {:sourceLine 87} true; - $tmp1 := $ArrayContents[local_0][0]; - $ArrayContents := $ArrayContents[local_0 := $ArrayContents[local_0][0 := 2]]; - assert {:sourceFile "Class1.cs"} {:sourceLine 88} true; - $tmp2 := $ArrayContents[local_0][0]; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 87} true; + $tmp1 := Box2Int($ArrayContents[local_0][0]); + $ArrayContents := $ArrayContents[local_0 := $ArrayContents[local_0][0 := Int2Box(2)]]; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 88} true; + $tmp2 := Box2Int($ArrayContents[local_0][0]); assert $tmp2 == 2; - assert {:sourceFile "Class1.cs"} {:sourceLine 90} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 90} true; call $tmp3 := Alloc(); + assume $ArrayLength($tmp3) == 1 * 4; local_1 := $tmp3; - assert {:sourceFile "Class1.cs"} {:sourceLine 91} true; - $tmp4 := $ArrayContents[local_1][0]; - $ArrayContents := $ArrayContents[local_1 := $ArrayContents[local_1][0 := 1]]; - assert {:sourceFile "Class1.cs"} {:sourceLine 92} true; - $tmp5 := $ArrayContents[local_1][0]; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 91} true; + $tmp4 := Box2Int($ArrayContents[local_1][0]); + $ArrayContents := $ArrayContents[local_1 := $ArrayContents[local_1][0 := Int2Box(1)]]; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 92} true; + $tmp5 := Box2Int($ArrayContents[local_1][0]); assert $tmp5 == 1; - assert {:sourceFile "Class1.cs"} {:sourceLine 94} true; - $tmp6 := $ArrayContents[local_0][0]; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 94} true; + $tmp6 := Box2Int($ArrayContents[local_0][0]); assert $tmp6 == 2; - assert {:sourceFile "Class1.cs"} {:sourceLine 95} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 95} true; return; } @@ -331,28 +522,30 @@ implementation RegressionTestInput.ClassWithArrayTypes.Main2() var $tmp12: int; var $tmp13: int; - assert {:sourceFile "Class1.cs"} {:sourceLine 100} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 100} true; call $tmp7 := Alloc(); + assume $ArrayLength($tmp7) == 1 * 5; RegressionTestInput.ClassWithArrayTypes.s := $tmp7; - assert {:sourceFile "Class1.cs"} {:sourceLine 101} true; - $tmp8 := $ArrayContents[RegressionTestInput.ClassWithArrayTypes.s][0]; - $ArrayContents := $ArrayContents[RegressionTestInput.ClassWithArrayTypes.s := $ArrayContents[RegressionTestInput.ClassWithArrayTypes.s][0 := 2]]; - assert {:sourceFile "Class1.cs"} {:sourceLine 102} true; - $tmp9 := $ArrayContents[RegressionTestInput.ClassWithArrayTypes.s][0]; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 101} true; + $tmp8 := Box2Int($ArrayContents[RegressionTestInput.ClassWithArrayTypes.s][0]); + $ArrayContents := $ArrayContents[RegressionTestInput.ClassWithArrayTypes.s := $ArrayContents[RegressionTestInput.ClassWithArrayTypes.s][0 := Int2Box(2)]]; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 102} true; + $tmp9 := Box2Int($ArrayContents[RegressionTestInput.ClassWithArrayTypes.s][0]); assert $tmp9 == 2; - assert {:sourceFile "Class1.cs"} {:sourceLine 104} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 104} true; call $tmp10 := Alloc(); + assume $ArrayLength($tmp10) == 1 * 4; local_0 := $tmp10; - assert {:sourceFile "Class1.cs"} {:sourceLine 105} true; - $tmp11 := $ArrayContents[local_0][0]; - $ArrayContents := $ArrayContents[local_0 := $ArrayContents[local_0][0 := 1]]; - assert {:sourceFile "Class1.cs"} {:sourceLine 106} true; - $tmp12 := $ArrayContents[local_0][0]; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 105} true; + $tmp11 := Box2Int($ArrayContents[local_0][0]); + $ArrayContents := $ArrayContents[local_0 := $ArrayContents[local_0][0 := Int2Box(1)]]; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 106} true; + $tmp12 := Box2Int($ArrayContents[local_0][0]); assert $tmp12 == 1; - assert {:sourceFile "Class1.cs"} {:sourceLine 108} true; - $tmp13 := $ArrayContents[RegressionTestInput.ClassWithArrayTypes.s][0]; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 108} true; + $tmp13 := Box2Int($ArrayContents[RegressionTestInput.ClassWithArrayTypes.s][0]); assert $tmp13 == 2; - assert {:sourceFile "Class1.cs"} {:sourceLine 109} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 109} true; return; } @@ -371,17 +564,17 @@ implementation RegressionTestInput.ClassWithArrayTypes.Main3$System.Int32(this: var $tmp17: int; x := x$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 114} true; - $tmp14 := $ArrayContents[this][x]; - $ArrayContents := $ArrayContents[this := $ArrayContents[this][x := 42]]; - assert {:sourceFile "Class1.cs"} {:sourceLine 115} true; - $tmp15 := $ArrayContents[this][x + 1]; - $ArrayContents := $ArrayContents[this := $ArrayContents[this][x + 1 := 43]]; - assert {:sourceFile "Class1.cs"} {:sourceLine 116} true; - $tmp16 := $ArrayContents[RegressionTestInput.ClassWithArrayTypes.a[this]][x + 1]; - $tmp17 := $ArrayContents[RegressionTestInput.ClassWithArrayTypes.a[this]][x]; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 114} true; + $tmp14 := Box2Int($ArrayContents[this][x]); + $ArrayContents := $ArrayContents[this := $ArrayContents[this][x := Int2Box(42)]]; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 115} true; + $tmp15 := Box2Int($ArrayContents[this][x + 1]); + $ArrayContents := $ArrayContents[this := $ArrayContents[this][x + 1 := Int2Box(43)]]; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 116} true; + $tmp16 := Box2Int($ArrayContents[RegressionTestInput.ClassWithArrayTypes.a[this]][x + 1]); + $tmp17 := Box2Int($ArrayContents[RegressionTestInput.ClassWithArrayTypes.a[this]][x]); assert $tmp16 == $tmp17 + 1; - assert {:sourceFile "Class1.cs"} {:sourceLine 117} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 117} true; return; } @@ -398,18 +591,18 @@ implementation RegressionTestInput.ClassWithArrayTypes.Main4$System.Int32array(t var $tmp19: int; xs := xs$in; - if (!(if xs != null then $ArrayLength[xs] <= 0 else true)) + if (!(if xs != null then $ArrayLength(xs) <= 0 else true)) { - assert {:sourceFile "Class1.cs"} {:sourceLine 121} true; - $tmp18 := $ArrayContents[xs][0]; - $tmp19 := $ArrayContents[this][0]; - $ArrayContents := $ArrayContents[this := $ArrayContents[this][0 := $tmp18]]; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 121} true; + $tmp18 := Box2Int($ArrayContents[xs][0]); + $tmp19 := Box2Int($ArrayContents[this][0]); + $ArrayContents := $ArrayContents[this := $ArrayContents[this][0 := Int2Box($tmp18)]]; } else { } - assert {:sourceFile "Class1.cs"} {:sourceLine 123} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 123} true; return; } @@ -419,10 +612,6 @@ procedure RegressionTestInput.ClassWithArrayTypes.#ctor(this: Ref); -procedure {:extern} System.Object.#ctor(this: Ref); - - - implementation RegressionTestInput.ClassWithArrayTypes.#ctor(this: Ref) { RegressionTestInput.ClassWithArrayTypes.a[this] := null; @@ -443,6 +632,34 @@ implementation RegressionTestInput.ClassWithArrayTypes.#cctor() +const unique RegressionTestInput.AsyncAttribute: Type; + +procedure RegressionTestInput.AsyncAttribute.#ctor(this: Ref); + + + +procedure {:extern} System.Attribute.#ctor(this: Ref); + + + +implementation RegressionTestInput.AsyncAttribute.#ctor(this: Ref) +{ + call System.Attribute.#ctor(this); + return; +} + + + +procedure RegressionTestInput.AsyncAttribute.#cctor(); + + + +implementation RegressionTestInput.AsyncAttribute.#cctor() +{ +} + + + const unique RegressionTestInput.RefParameters: Type; procedure RegressionTestInput.RefParameters.M$System.Int32$(x$in: int) returns (x$out: int); @@ -452,9 +669,9 @@ procedure RegressionTestInput.RefParameters.M$System.Int32$(x$in: int) returns ( implementation RegressionTestInput.RefParameters.M$System.Int32$(x$in: int) returns (x$out: int) { x$out := x$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 156} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 156} true; x$out := x$out + 1; - assert {:sourceFile "Class1.cs"} {:sourceLine 157} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 157} true; return; } @@ -482,6 +699,8 @@ implementation RegressionTestInput.RefParameters.#cctor() +const unique RegressionTestInput.S: Type; + const unique RegressionTestInput.Class0: Type; var RegressionTestInput.Class0.StaticInt: int; @@ -495,7 +714,7 @@ implementation RegressionTestInput.Class0.StaticMethod$System.Int32(x$in: int) r var x: int; x := x$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 18} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 18} true; $result := x + 1; return; } @@ -519,13 +738,13 @@ implementation RegressionTestInput.Class0.M$System.Int32(this: Ref, x$in: int) __temp_1 := 5 / $tmp20; x := 3; local_0 := __temp_1 + 3; - assert {:sourceFile "Class1.cs"} {:sourceLine 22} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 22} true; assert (if x == 3 then local_0 <= 8 else false); - assert {:sourceFile "Class1.cs"} {:sourceLine 23} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 23} true; RegressionTestInput.Class0.StaticInt := local_0; - assert {:sourceFile "Class1.cs"} {:sourceLine 24} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 24} true; assert local_0 == RegressionTestInput.Class0.StaticInt; - assert {:sourceFile "Class1.cs"} {:sourceLine 25} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 25} true; return; } @@ -542,7 +761,7 @@ implementation RegressionTestInput.Class0.M$System.Int32$System.Int32(this: Ref, x := x$in; y := y$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 28} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 28} true; return; } @@ -557,7 +776,7 @@ implementation RegressionTestInput.Class0.M$System.Boolean(this: Ref, b$in: bool var b: bool; b := b$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 29} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 29} true; return; } @@ -572,7 +791,7 @@ implementation RegressionTestInput.Class0.M$RegressionTestInput.Class0(this: Ref var c: Ref; c := c$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 30} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 30} true; return; } @@ -586,7 +805,7 @@ implementation RegressionTestInput.Class0.NonVoid(this: Ref) returns ($result: i { var $tmp21: int; - assert {:sourceFile "Class1.cs"} {:sourceLine 34} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 34} true; call $tmp21 := RegressionTestInput.Class0.StaticMethod$System.Int32(3); $result := 3 + RegressionTestInput.Class0.StaticInt + $tmp21; return; @@ -601,9 +820,9 @@ procedure RegressionTestInput.Class0.OutParam$System.Int32$(this: Ref, x$in: int implementation RegressionTestInput.Class0.OutParam$System.Int32$(this: Ref, x$in: int) returns (x$out: int, $result: int) { x$out := x$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 37} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 37} true; x$out := 3 + RegressionTestInput.Class0.StaticInt; - assert {:sourceFile "Class1.cs"} {:sourceLine 39} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 39} true; $result := x$out; return; } @@ -617,11 +836,11 @@ procedure RegressionTestInput.Class0.RefParam$System.Int32$(this: Ref, x$in: int implementation RegressionTestInput.Class0.RefParam$System.Int32$(this: Ref, x$in: int) returns (x$out: int, $result: int) { x$out := x$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 42} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 42} true; x$out := x$out + 1; - assert {:sourceFile "Class1.cs"} {:sourceLine 43} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 43} true; RegressionTestInput.Class0.StaticInt := x$out; - assert {:sourceFile "Class1.cs"} {:sourceLine 45} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 45} true; $result := x$out; return; } @@ -637,11 +856,11 @@ implementation RegressionTestInput.Class0.AssignToInParam$System.Int32(this: Ref var x: int; x := x$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 48} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 48} true; x := x + 1; - assert {:sourceFile "Class1.cs"} {:sourceLine 49} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 49} true; RegressionTestInput.Class0.StaticInt := x; - assert {:sourceFile "Class1.cs"} {:sourceLine 51} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 51} true; $result := x; return; } @@ -657,7 +876,7 @@ implementation RegressionTestInput.Class0.MethodThatRepresentsAnAynchronousMetho var x: int; x := x$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 56} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 56} true; $result := x; return; } @@ -674,7 +893,7 @@ implementation RegressionTestInput.Class0.CallAsyncMethod$System.Int32(this: Ref var $tmp22: int; y := y$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 60} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 60} true; call {:async} $tmp22 := RegressionTestInput.Class0.MethodThatRepresentsAnAynchronousMethod$System.Int32(this, y); $result := $tmp22; return; @@ -705,50 +924,6 @@ implementation RegressionTestInput.Class0.#cctor() -const unique RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap: Type; - -var RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.x: [Ref]int; - -var RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.y: [Ref]int; - -procedure RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.M(this: Ref); - - - -implementation RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.M(this: Ref) -{ - assert {:sourceFile "Class1.cs"} {:sourceLine 130} true; - RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.y[this] := RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.x[this]; - assert {:sourceFile "Class1.cs"} {:sourceLine 131} true; - return; -} - - - -procedure RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.#ctor(this: Ref); - - - -implementation RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.#ctor(this: Ref) -{ - RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.x[this] := 0; - RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.y[this] := 0; - call System.Object.#ctor(this); - return; -} - - - -procedure RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.#cctor(); - - - -implementation RegressionTestInput.WriteToTheHeapAValueReadFromTheHeap.#cctor() -{ -} - - - const unique RegressionTestInput.ClassWithBoolTypes: Type; var RegressionTestInput.ClassWithBoolTypes.staticB: bool; @@ -766,7 +941,7 @@ implementation RegressionTestInput.ClassWithBoolTypes.M$System.Int32$System.Int3 x := x$in; y := y$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 70} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 70} true; $result := x < y; return; } @@ -783,14 +958,14 @@ implementation RegressionTestInput.ClassWithBoolTypes.#ctor$System.Boolean(this: z := z$in; RegressionTestInput.ClassWithBoolTypes.b[this] := false; - assert {:sourceFile "Class1.cs"} {:sourceLine 72} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 72} true; call System.Object.#ctor(this); - assert {:sourceFile "Class1.cs"} {:sourceLine 73} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 73} true; RegressionTestInput.ClassWithBoolTypes.b[this] := z; - assert {:sourceFile "Class1.cs"} {:sourceLine 74} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 74} true; if (z) { - assert {:sourceFile "Class1.cs"} {:sourceLine 74} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 74} true; RegressionTestInput.ClassWithBoolTypes.staticB := z; } else @@ -810,9 +985,9 @@ implementation RegressionTestInput.ClassWithBoolTypes.Main() { var $tmp23: bool; - assert {:sourceFile "Class1.cs"} {:sourceLine 78} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 78} true; call $tmp23 := RegressionTestInput.ClassWithBoolTypes.M$System.Int32$System.Int32(3, 4); - assert {:sourceFile "Class1.cs"} {:sourceLine 79} true; + assert {:sourceFile "C:\dev\BoogieCodePlex\BCT\RegressionTests\RegressionTestInput\Class1.cs"} {:sourceLine 79} true; return; } @@ -828,108 +1003,3 @@ implementation RegressionTestInput.ClassWithBoolTypes.#cctor() } - -const unique RegressionTestInput.S: Type; - -var RegressionTestInput.S.x: [Ref]int; - -var RegressionTestInput.S.b: [Ref]bool; - -const unique RegressionTestInput.AsyncAttribute: Type; - -procedure RegressionTestInput.AsyncAttribute.#ctor(this: Ref); - - - -procedure {:extern} System.Attribute.#ctor(this: Ref); - - - -implementation RegressionTestInput.AsyncAttribute.#ctor(this: Ref) -{ - call System.Attribute.#ctor(this); - return; -} - - - -procedure RegressionTestInput.AsyncAttribute.#cctor(); - - - -implementation RegressionTestInput.AsyncAttribute.#cctor() -{ -} - - - -const unique RegressionTestInput.CreateStruct: Type; - -procedure RegressionTestInput.CreateStruct.Create(this: Ref) returns ($result: [Field]Box); - - - -implementation RegressionTestInput.CreateStruct.Create(this: Ref) returns ($result: [Field]Box) -{ - var local_0: [Field]Box; - var _loc0: int; - var _loc1: bool; - - assert {:sourceFile "Class1.cs"} {:sourceLine 141} true; - local_0 := $DefaultStruct; - assert {:sourceFile "Class1.cs"} {:sourceLine 142} true; - _loc0 := local_0; - assert _loc0[RegressionTestInput.S.x] == 0; - assert {:sourceFile "Class1.cs"} {:sourceLine 143} true; - _loc1 := local_0; - assert !_loc1[RegressionTestInput.S.b]; - assert {:sourceFile "Class1.cs"} {:sourceLine 145} true; - $result := local_0; - return; -} - - - -procedure RegressionTestInput.CreateStruct.AssignThreeToSDotX$RegressionTestInput.S(this: Ref, s$in: [Field]Box) returns ($result: [Field]Box); - - - -implementation RegressionTestInput.CreateStruct.AssignThreeToSDotX$RegressionTestInput.S(this: Ref, s$in: [Field]Box) returns ($result: [Field]Box) -{ - var s: [Field]Box; - var _loc0: int; - - s := s$in; - assert {:sourceFile "Class1.cs"} {:sourceLine 147} true; - s[RegressionTestInput.S.x] := 3; - assert {:sourceFile "Class1.cs"} {:sourceLine 148} true; - _loc0 := s; - assert _loc0[RegressionTestInput.S.x] == 3; - assert {:sourceFile "Class1.cs"} {:sourceLine 150} true; - $result := s; - return; -} - - - -procedure RegressionTestInput.CreateStruct.#ctor(this: Ref); - - - -implementation RegressionTestInput.CreateStruct.#ctor(this: Ref) -{ - call System.Object.#ctor(this); - return; -} - - - -procedure RegressionTestInput.CreateStruct.#cctor(); - - - -implementation RegressionTestInput.CreateStruct.#cctor() -{ -} - - -- cgit v1.2.3 From 7ca1c590659d2c11a67b138b8251cf5dffc03b38 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 19 May 2011 17:29:04 -0700 Subject: Dafny: added alternative statement and alternative-loop statement --- Source/Dafny/Compiler.cs | 37 +++- Source/Dafny/Dafny.atg | 64 +++++-- Source/Dafny/DafnyAst.cs | 87 +++++++-- Source/Dafny/Parser.cs | 290 +++++++++++++++++------------- Source/Dafny/Printer.cs | 41 ++++- Source/Dafny/Resolver.cs | 54 +++++- Source/Dafny/Scanner.cs | 102 ++++++----- Source/Dafny/Translator.cs | 364 +++++++++++++++++++++++--------------- Test/dafny0/Answer | 6 +- Test/dafny0/ControlStructures.dfy | 72 ++++++++ 10 files changed, 777 insertions(+), 340 deletions(-) diff --git a/Source/Dafny/Compiler.cs b/Source/Dafny/Compiler.cs index 800585bd..6dabace1 100644 --- a/Source/Dafny/Compiler.cs +++ b/Source/Dafny/Compiler.cs @@ -716,7 +716,20 @@ namespace Microsoft.Dafny { Indent(indent); wr.WriteLine("else"); TrStmt(s.Els, indent); } - + + } else if (stmt is AlternativeStmt) { + var s = (AlternativeStmt)stmt; + Indent(indent); + foreach (var alternative in s.Alternatives) { + wr.Write("if ("); + TrExpr(alternative.Guard); + wr.WriteLine(") {"); + TrStmtList(alternative.Body, indent); + Indent(indent); + wr.Write("} else "); + } + wr.WriteLine("{ /*unreachable alternative*/ }"); + } else if (stmt is WhileStmt) { WhileStmt s = (WhileStmt)stmt; if (s.Guard == null) { @@ -730,6 +743,26 @@ namespace Microsoft.Dafny { TrStmt(s.Body, indent); } + } else if (stmt is AlternativeLoopStmt) { + var s = (AlternativeLoopStmt)stmt; + if (s.Alternatives.Count != 0) { + Indent(indent); + wr.WriteLine("while (true) {"); + int ind = indent + IndentAmount; + Indent(ind); + foreach (var alternative in s.Alternatives) { + wr.Write("if ("); + TrExpr(alternative.Guard); + wr.WriteLine(") {"); + TrStmtList(alternative.Body, ind); + Indent(ind); + wr.Write("} else "); + } + wr.WriteLine("{ break; }"); + Indent(indent); + wr.WriteLine("}"); + } + } else if (stmt is ForeachStmt) { ForeachStmt s = (ForeachStmt)stmt; // List> pendingUpdates = new List>(); @@ -1127,7 +1160,7 @@ namespace Microsoft.Dafny { } else if (expr is DatatypeValue) { DatatypeValue dtv = (DatatypeValue)expr; Contract.Assert(dtv.Ctor != null); // since dtv has been successfully resolved - wr.Write("new {0}(new {0}", dtv.DatatypeName, DtCtorName(dtv.Ctor)); + wr.Write("new {0}(new {1}", dtv.DatatypeName, DtCtorName(dtv.Ctor)); if (dtv.InferredTypeArgs.Count != 0) { wr.Write("<{0}>", TypeNames(dtv.InferredTypeArgs)); } diff --git a/Source/Dafny/Dafny.atg b/Source/Dafny/Dafny.atg index f2a0a762..388ae8cd 100644 --- a/Source/Dafny/Dafny.atg +++ b/Source/Dafny/Dafny.atg @@ -881,29 +881,72 @@ IfStmt Statement/*!*/ s; Statement els = null; IToken bodyStart, bodyEnd; + List alternatives; + ifStmt = dummyStmt; // to please the compiler .) "if" (. x = t; .) - Guard - BlockStmt - [ "else" - ( IfStmt (. els = s; .) - | BlockStmt (. els = s; .) - ) - ] - (. ifStmt = new IfStmt(x, guard, thn, els); .) + ( + Guard + BlockStmt + [ "else" + ( IfStmt (. els = s; .) + | BlockStmt (. els = s; .) + ) + ] + (. ifStmt = new IfStmt(x, guard, thn, els); .) + | + AlternativeBlock + (. ifStmt = new AlternativeStmt(x, alternatives); .) + ) + . + +AlternativeBlock<.out List alternatives.> += (. alternatives = new List(); + IToken x; + Expression e; + List body; + .) + "{" + { "case" (. x = t; .) + Expression + "=>" + (. body = new List(); .) + (. parseVarScope.PushMarker(); .) + { Stmt } + (. parseVarScope.PopMarker(); .) + (. alternatives.Add(new GuardedAlternative(x, e, body)); .) + } + "}" . WhileStmt = (. Contract.Ensures(Contract.ValueAtReturn(out stmt) != null); IToken/*!*/ x; Expression guard; - bool isFree; Expression/*!*/ e; List invariants = new List(); List decreases = new List(); Statement/*!*/ body; IToken bodyStart, bodyEnd; + List alternatives; + stmt = dummyStmt; // to please the compiler .) "while" (. x = t; .) - Guard (. Contract.Assume(guard == null || cce.Owner.None(guard)); .) + ( + Guard (. Contract.Assume(guard == null || cce.Owner.None(guard)); .) + LoopSpec + BlockStmt + (. stmt = new WhileStmt(x, guard, invariants, decreases, body); .) + | + LoopSpec + AlternativeBlock + (. stmt = new AlternativeLoopStmt(x, invariants, decreases, alternatives); .) + ) + . + +LoopSpec<.out List invariants, out List decreases.> += (. bool isFree; Expression/*!*/ e; + invariants = new List(); + decreases = new List(); + .) { (. isFree = false; .) [ "free" (. isFree = true; .) ] @@ -916,7 +959,6 @@ WhileStmt } ";" } - BlockStmt (. stmt = new WhileStmt(x, guard, invariants, decreases, body); .) . Guard /* null represents demonic-choice */ diff --git a/Source/Dafny/DafnyAst.cs b/Source/Dafny/DafnyAst.cs index 04d92539..f0a58293 100644 --- a/Source/Dafny/DafnyAst.cs +++ b/Source/Dafny/DafnyAst.cs @@ -1557,8 +1557,6 @@ namespace Microsoft.Dafny { [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(Thn != null); - - Contract.Invariant(Els == null || Els is BlockStmt || Els is IfStmt); } public IfStmt(IToken tok, Expression guard, Statement thn, Statement els) @@ -1574,36 +1572,103 @@ namespace Microsoft.Dafny { } } - public class WhileStmt : Statement { + public class GuardedAlternative + { + public readonly IToken Tok; public readonly Expression Guard; + public readonly List Body; + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(Tok != null); + Contract.Invariant(Guard != null); + Contract.Invariant(Body != null); + } + public GuardedAlternative(IToken tok, Expression guard, List body) + { + Contract.Requires(tok != null); + Contract.Requires(guard != null); + Contract.Requires(body != null); + this.Tok = tok; + this.Guard = guard; + this.Body = body; + } + } + + public class AlternativeStmt : Statement + { + public readonly List Alternatives; + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(Alternatives != null); + } + public AlternativeStmt(IToken tok, List alternatives) + : base(tok) { + Contract.Requires(tok != null); + Contract.Requires(alternatives != null); + this.Alternatives = alternatives; + } + } + + public abstract class LoopStmt : Statement + { public readonly List/*!*/ Invariants; public readonly List/*!*/ Decreases; - public readonly Statement/*!*/ Body; [ContractInvariantMethod] void ObjectInvariant() { - Contract.Invariant(Body != null); Contract.Invariant(cce.NonNullElements(Invariants)); Contract.Invariant(cce.NonNullElements(Decreases)); } + public LoopStmt(IToken tok, List/*!*/ invariants, List/*!*/ decreases) + : base(tok) + { + Contract.Requires(tok != null); + Contract.Requires(cce.NonNullElements(invariants)); + Contract.Requires(cce.NonNullElements(decreases)); + this.Invariants = invariants; + this.Decreases = decreases; + } + } + + public class WhileStmt : LoopStmt + { + public readonly Expression Guard; + public readonly Statement/*!*/ Body; + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(Body != null); + } public WhileStmt(IToken tok, Expression guard, List/*!*/ invariants, List/*!*/ decreases, Statement/*!*/ body) - : base(tok) { + : base(tok, invariants, decreases) { Contract.Requires(tok != null); Contract.Requires(body != null); - Contract.Requires(cce.NonNullElements(invariants)); - Contract.Requires(cce.NonNullElements(decreases)); this.Guard = guard; - this.Invariants = invariants; - this.Decreases = decreases; this.Body = body; + } + } + public class AlternativeLoopStmt : LoopStmt + { + public readonly List Alternatives; + [ContractInvariantMethod] + void ObjectInvariant() { + Contract.Invariant(Alternatives != null); + } + public AlternativeLoopStmt(IToken tok, + List/*!*/ invariants, List/*!*/ decreases, + List alternatives) + : base(tok, invariants, decreases) { + Contract.Requires(tok != null); + Contract.Requires(alternatives != null); + this.Alternatives = alternatives; } } - public class ForeachStmt : Statement { + public class ForeachStmt : Statement + { public readonly BoundVar/*!*/ BoundVar; public readonly Expression/*!*/ Collection; public readonly Expression/*!*/ Range; diff --git a/Source/Dafny/Parser.cs b/Source/Dafny/Parser.cs index e707d859..aa02c796 100644 --- a/Source/Dafny/Parser.cs +++ b/Source/Dafny/Parser.cs @@ -25,7 +25,7 @@ public class Parser { const bool T = true; const bool x = false; const int minErrDist = 2; - + public Scanner/*!*/ scanner; public Errors/*!*/ errors; @@ -161,10 +161,10 @@ public static int Parse (string/*!*/ s, string/*!*/ filename, List= minErrDist) errors.SemErr(t, msg); errDist = 0; } - - public void SemErr(IToken/*!*/ tok, string/*!*/ msg) { - Contract.Requires(tok != null); - Contract.Requires(msg != null); + + public void SemErr(IToken/*!*/ tok, string/*!*/ msg) { + Contract.Requires(tok != null); + Contract.Requires(msg != null); errors.SemErr(tok, msg); } @@ -177,15 +177,15 @@ public static int Parse (string/*!*/ s, string/*!*/ filename, List theImports; @@ -1267,62 +1267,54 @@ List/*!*/ decreases) { Statement/*!*/ s; Statement els = null; IToken bodyStart, bodyEnd; + List alternatives; + ifStmt = dummyStmt; // to please the compiler Expect(57); x = t; - Guard(out guard); - BlockStmt(out thn, out bodyStart, out bodyEnd); - if (la.kind == 58) { - Get(); - if (la.kind == 57) { - IfStmt(out s); - els = s; - } else if (la.kind == 7) { - BlockStmt(out s, out bodyStart, out bodyEnd); - els = s; - } else SynErr(124); - } - ifStmt = new IfStmt(x, guard, thn, els); + if (la.kind == 32) { + Guard(out guard); + BlockStmt(out thn, out bodyStart, out bodyEnd); + if (la.kind == 58) { + Get(); + if (la.kind == 57) { + IfStmt(out s); + els = s; + } else if (la.kind == 7) { + BlockStmt(out s, out bodyStart, out bodyEnd); + els = s; + } else SynErr(124); + } + ifStmt = new IfStmt(x, guard, thn, els); + } else if (la.kind == 7) { + AlternativeBlock(out alternatives); + ifStmt = new AlternativeStmt(x, alternatives); + } else SynErr(125); } void WhileStmt(out Statement/*!*/ stmt) { Contract.Ensures(Contract.ValueAtReturn(out stmt) != null); IToken/*!*/ x; Expression guard; - bool isFree; Expression/*!*/ e; List invariants = new List(); List decreases = new List(); Statement/*!*/ body; IToken bodyStart, bodyEnd; + List alternatives; + stmt = dummyStmt; // to please the compiler Expect(59); x = t; - Guard(out guard); - Contract.Assume(guard == null || cce.Owner.None(guard)); - while (la.kind == 28 || la.kind == 31 || la.kind == 60) { - if (la.kind == 28 || la.kind == 60) { - isFree = false; - if (la.kind == 28) { - Get(); - isFree = true; - } - Expect(60); - Expression(out e); - invariants.Add(new MaybeFreeExpression(e, isFree)); - Expect(15); - } else { - Get(); - PossiblyWildExpression(out e); - decreases.Add(e); - while (la.kind == 19) { - Get(); - PossiblyWildExpression(out e); - decreases.Add(e); - } - Expect(15); - } - } - BlockStmt(out body, out bodyStart, out bodyEnd); - stmt = new WhileStmt(x, guard, invariants, decreases, body); + if (la.kind == 32) { + Guard(out guard); + Contract.Assume(guard == null || cce.Owner.None(guard)); + LoopSpec(out invariants, out decreases); + BlockStmt(out body, out bodyStart, out bodyEnd); + stmt = new WhileStmt(x, guard, invariants, decreases, body); + } else if (StartOf(12)) { + LoopSpec(out invariants, out decreases); + AlternativeBlock(out alternatives); + stmt = new AlternativeLoopStmt(x, invariants, decreases, alternatives); + } else SynErr(126); } void MatchStmt(out Statement/*!*/ s) { @@ -1382,13 +1374,13 @@ List/*!*/ decreases) { if (s is PredicateStmt) { bodyPrefix.Add((PredicateStmt)s); } } } - if (StartOf(12)) { + if (StartOf(13)) { AssignStmt(out s, false); if (s is AssignStmt) { bodyAssign = (AssignStmt)s; } } else if (la.kind == 56) { HavocStmt(out s); if (s is AssignStmt) { bodyAssign = (AssignStmt)s; } - } else SynErr(125); + } else SynErr(127); Expect(8); if (bodyAssign != null) { s = new ForeachStmt(x, new BoundVar(boundVar, boundVar.val, ty), collection, range, bodyPrefix, bodyAssign); @@ -1444,7 +1436,7 @@ List/*!*/ decreases) { } else if (StartOf(8)) { Expression(out e); ee = new List() { e }; - } else SynErr(126); + } else SynErr(128); if (ee == null && ty == null) { ee = new List() { dummyExpr}; } } @@ -1454,7 +1446,7 @@ List/*!*/ decreases) { IdentOrFuncExpression(out e); } else if (la.kind == 32 || la.kind == 99 || la.kind == 100) { ObjectExpression(out e); - } else SynErr(127); + } else SynErr(129); while (la.kind == 52 || la.kind == 54) { SelectOrCallSuffix(ref e); } @@ -1501,10 +1493,63 @@ List/*!*/ decreases) { } else if (StartOf(8)) { Expression(out ee); e = ee; - } else SynErr(128); + } else SynErr(130); Expect(33); } + void AlternativeBlock(out List alternatives) { + alternatives = new List(); + IToken x; + Expression e; + List body; + + Expect(7); + while (la.kind == 45) { + Get(); + x = t; + Expression(out e); + Expect(46); + body = new List(); + parseVarScope.PushMarker(); + while (StartOf(9)) { + Stmt(body); + } + parseVarScope.PopMarker(); + alternatives.Add(new GuardedAlternative(x, e, body)); + } + Expect(8); + } + + void LoopSpec(out List invariants, out List decreases) { + bool isFree; Expression/*!*/ e; + invariants = new List(); + decreases = new List(); + + while (la.kind == 28 || la.kind == 31 || la.kind == 60) { + if (la.kind == 28 || la.kind == 60) { + isFree = false; + if (la.kind == 28) { + Get(); + isFree = true; + } + Expect(60); + Expression(out e); + invariants.Add(new MaybeFreeExpression(e, isFree)); + Expect(15); + } else { + Get(); + PossiblyWildExpression(out e); + decreases.Add(e); + while (la.kind == 19) { + Get(); + PossiblyWildExpression(out e); + decreases.Add(e); + } + Expect(15); + } + } + } + void CaseStatement(out MatchCaseStmt/*!*/ c) { Contract.Ensures(Contract.ValueAtReturn(out c) != null); IToken/*!*/ x, id, arg; @@ -1544,7 +1589,7 @@ List/*!*/ decreases) { } else if (la.kind == 32 || la.kind == 99 || la.kind == 100) { ObjectExpression(out e); SelectOrCallSuffix(ref e); - } else SynErr(129); + } else SynErr(131); while (la.kind == 52 || la.kind == 54) { SelectOrCallSuffix(ref e); } @@ -1558,7 +1603,7 @@ List/*!*/ decreases) { } else if (StartOf(8)) { Expression(out e); arg = new Attributes.Argument(e); - } else SynErr(130); + } else SynErr(132); } void EquivExpression(out Expression/*!*/ e0) { @@ -1588,13 +1633,13 @@ List/*!*/ decreases) { Get(); } else if (la.kind == 69) { Get(); - } else SynErr(131); + } else SynErr(133); } void LogicalExpression(out Expression/*!*/ e0) { Contract.Ensures(Contract.ValueAtReturn(out e0) != null); IToken/*!*/ x; Expression/*!*/ e1; RelationalExpression(out e0); - if (StartOf(13)) { + if (StartOf(14)) { if (la.kind == 72 || la.kind == 73) { AndOp(); x = t; @@ -1626,13 +1671,13 @@ List/*!*/ decreases) { Get(); } else if (la.kind == 71) { Get(); - } else SynErr(132); + } else SynErr(134); } void RelationalExpression(out Expression/*!*/ e0) { Contract.Ensures(Contract.ValueAtReturn(out e0) != null); IToken/*!*/ x; Expression/*!*/ e1; BinaryExpr.Opcode op; Term(out e0); - if (StartOf(14)) { + if (StartOf(15)) { RelOp(out x, out op); Term(out e1); e0 = new BinaryExpr(x, op, e0, e1); @@ -1644,7 +1689,7 @@ List/*!*/ decreases) { Get(); } else if (la.kind == 73) { Get(); - } else SynErr(133); + } else SynErr(135); } void OrOp() { @@ -1652,7 +1697,7 @@ List/*!*/ decreases) { Get(); } else if (la.kind == 75) { Get(); - } else SynErr(134); + } else SynErr(136); } void Term(out Expression/*!*/ e0) { @@ -1728,7 +1773,7 @@ List/*!*/ decreases) { x = t; op = BinaryExpr.Opcode.Ge; break; } - default: SynErr(135); break; + default: SynErr(137); break; } } @@ -1750,7 +1795,7 @@ List/*!*/ decreases) { } else if (la.kind == 86) { Get(); x = t; op = BinaryExpr.Opcode.Sub; - } else SynErr(136); + } else SynErr(138); } void UnaryExpression(out Expression/*!*/ e) { @@ -1765,11 +1810,11 @@ List/*!*/ decreases) { x = t; UnaryExpression(out e); e = new UnaryExpr(x, UnaryExpr.Opcode.Not, e); - } else if (StartOf(12)) { + } else if (StartOf(13)) { SelectExpression(out e); - } else if (StartOf(15)) { + } else if (StartOf(16)) { ConstAtomExpression(out e); - } else SynErr(137); + } else SynErr(139); } void MulOp(out IToken/*!*/ x, out BinaryExpr.Opcode op) { @@ -1783,7 +1828,7 @@ List/*!*/ decreases) { } else if (la.kind == 88) { Get(); x = t; op = BinaryExpr.Opcode.Mod; - } else SynErr(138); + } else SynErr(140); } void NegOp() { @@ -1791,7 +1836,7 @@ List/*!*/ decreases) { Get(); } else if (la.kind == 90) { Get(); - } else SynErr(139); + } else SynErr(141); } void ConstAtomExpression(out Expression/*!*/ e) { @@ -1902,7 +1947,7 @@ List/*!*/ decreases) { ComprehensionExpr(out e); break; } - default: SynErr(140); break; + default: SynErr(142); break; } } @@ -1933,7 +1978,7 @@ List/*!*/ decreases) { } else if (la.kind == 103 || la.kind == 104) { Exists(); x = t; - } else SynErr(141); + } else SynErr(143); parseVarScope.PushMarker(); IdentTypeOptional(out bv); bvars.Add(bv); parseVarScope.Push(bv.Name, bv.Name); @@ -1984,6 +2029,7 @@ List/*!*/ decreases) { QSep(); Expression(out body); } + if (body == null && bvars.Count != 1) { SemErr(t, "a set comprehension with more than one bound variable must have a term expression"); } q = new SetComprehension(x, bvars, range, body); parseVarScope.PopMarker(); @@ -2027,7 +2073,7 @@ List/*!*/ decreases) { Get(); Expression(out e); Expect(33); - } else SynErr(142); + } else SynErr(144); } void SelectOrCallSuffix(ref Expression/*!*/ e) { @@ -2077,12 +2123,12 @@ List/*!*/ decreases) { multipleIndices.Add(ee); } - } else SynErr(143); + } else SynErr(145); } else if (la.kind == 98) { Get(); Expression(out ee); anyDots = true; e1 = ee; - } else SynErr(144); + } else SynErr(146); if (multipleIndices != null) { e = new MultiSelectExpr(x, e, multipleIndices); // make sure an array class with this dimensionality exists @@ -2106,7 +2152,7 @@ List/*!*/ decreases) { } Expect(53); - } else SynErr(145); + } else SynErr(147); } void Forall() { @@ -2114,7 +2160,7 @@ List/*!*/ decreases) { Get(); } else if (la.kind == 102) { Get(); - } else SynErr(146); + } else SynErr(148); } void Exists() { @@ -2122,7 +2168,7 @@ List/*!*/ decreases) { Get(); } else if (la.kind == 104) { Get(); - } else SynErr(147); + } else SynErr(149); } void AttributeOrTrigger(ref Attributes attrs, ref Triggers trigs) { @@ -2135,7 +2181,7 @@ List/*!*/ decreases) { es = new List(); Expressions(es); trigs = new Triggers(es, trigs); - } else SynErr(148); + } else SynErr(150); Expect(8); } @@ -2144,7 +2190,7 @@ List/*!*/ decreases) { Get(); } else if (la.kind == 106) { Get(); - } else SynErr(149); + } else SynErr(151); } void AttributeBody(ref Attributes attrs) { @@ -2155,7 +2201,7 @@ List/*!*/ decreases) { Expect(22); Expect(1); aName = t.val; - if (StartOf(16)) { + if (StartOf(17)) { AttributeArg(out aArg); aArgs.Add(aArg); while (la.kind == 19) { @@ -2171,13 +2217,13 @@ List/*!*/ decreases) { public void Parse() { la = new Token(); - la.val = ""; + la.val = ""; Get(); Dafny(); - Expect(0); + Expect(0); } - + static readonly bool[,]/*!*/ set = { {T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,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,x, x,x,T,x, T,x,x,x, x,T,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}, @@ -2191,6 +2237,7 @@ List/*!*/ decreases) { {x,T,x,x, x,x,x,T, x,x,x,T, 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,x,x,x, x,x,x,x, T,x,x,T, T,T,x,x, x,x,x,x, T,T,x,T, x,T,T,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,T, T,x,x,x, x,x,x,x, x}, {x,T,T,x, x,x,x,T, x,x,x,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,T,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,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,T,x, x,T,T,T, T,T,T,T, T,x,x,T, T,T,T,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, T,x,x,x, x,x,x,x, x,x,x,x, T,x,x,T, T,T,x,x, x,x,x,x, T,T,x,T, x,T,T,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,T, T,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, 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,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,T,x,x, x,x,x,x, x,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,T, T,x,x,x, x,x,x,x, x}, {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,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,T, T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, 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, 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}, @@ -2204,18 +2251,20 @@ List/*!*/ decreases) { public class Errors { public int count = 0; // number of errors detected public System.IO.TextWriter/*!*/ errorStream = Console.Out; // error messages go to this stream - public string errMsgFormat = "{0}({1},{2}): error: {3}"; // 0=filename, 1=line, 2=column, 3=text - public string warningMsgFormat = "{0}({1},{2}): warning: {3}"; // 0=filename, 1=line, 2=column, 3=text - + public string errMsgFormat = "{0}({1},{2}): error: {3}"; // 0=filename, 1=line, 2=column, 3=text + public string warningMsgFormat = "{0}({1},{2}): warning: {3}"; // 0=filename, 1=line, 2=column, 3=text + public void SynErr(string filename, int line, int col, int n) { - SynErr(filename, line, col, GetSyntaxErrorString(n)); - } - public virtual void SynErr(string filename, int line, int col, string msg) { - Contract.Requires(msg != null); + SynErr(filename, line, col, GetSyntaxErrorString(n)); + } + + public virtual void SynErr(string filename, int line, int col, string/*!*/ msg) { + Contract.Requires(msg != null); errorStream.WriteLine(errMsgFormat, filename, line, col, msg); count++; - } - string GetSyntaxErrorString(int n) { + } + + string GetSyntaxErrorString(int n) { string s; switch (n) { case 0: s = "EOF expected"; break; @@ -2343,35 +2392,37 @@ public class Errors { case 122: s = "invalid Stmt"; break; case 123: s = "invalid OneStmt"; break; case 124: s = "invalid IfStmt"; break; - case 125: s = "invalid ForeachStmt"; break; - case 126: s = "invalid AssignRhs"; break; - case 127: s = "invalid SelectExpression"; break; - case 128: s = "invalid Guard"; break; - case 129: s = "invalid CallStmtSubExpr"; break; - case 130: s = "invalid AttributeArg"; break; - case 131: s = "invalid EquivOp"; break; - case 132: s = "invalid ImpliesOp"; break; - case 133: s = "invalid AndOp"; break; - case 134: s = "invalid OrOp"; break; - case 135: s = "invalid RelOp"; break; - case 136: s = "invalid AddOp"; break; - case 137: s = "invalid UnaryExpression"; break; - case 138: s = "invalid MulOp"; break; - case 139: s = "invalid NegOp"; break; - case 140: s = "invalid ConstAtomExpression"; break; - case 141: s = "invalid QuantifierGuts"; break; - case 142: s = "invalid ObjectExpression"; break; - case 143: s = "invalid SelectOrCallSuffix"; break; - case 144: s = "invalid SelectOrCallSuffix"; break; + case 125: s = "invalid IfStmt"; break; + case 126: s = "invalid WhileStmt"; break; + case 127: s = "invalid ForeachStmt"; break; + case 128: s = "invalid AssignRhs"; break; + case 129: s = "invalid SelectExpression"; break; + case 130: s = "invalid Guard"; break; + case 131: s = "invalid CallStmtSubExpr"; break; + case 132: s = "invalid AttributeArg"; break; + case 133: s = "invalid EquivOp"; break; + case 134: s = "invalid ImpliesOp"; break; + case 135: s = "invalid AndOp"; break; + case 136: s = "invalid OrOp"; break; + case 137: s = "invalid RelOp"; break; + case 138: s = "invalid AddOp"; break; + case 139: s = "invalid UnaryExpression"; break; + case 140: s = "invalid MulOp"; break; + case 141: s = "invalid NegOp"; break; + case 142: s = "invalid ConstAtomExpression"; break; + case 143: s = "invalid QuantifierGuts"; break; + case 144: s = "invalid ObjectExpression"; break; case 145: s = "invalid SelectOrCallSuffix"; break; - case 146: s = "invalid Forall"; break; - case 147: s = "invalid Exists"; break; - case 148: s = "invalid AttributeOrTrigger"; break; - case 149: s = "invalid QSep"; break; + case 146: s = "invalid SelectOrCallSuffix"; break; + case 147: s = "invalid SelectOrCallSuffix"; break; + case 148: s = "invalid Forall"; break; + case 149: s = "invalid Exists"; break; + case 150: s = "invalid AttributeOrTrigger"; break; + case 151: s = "invalid QSep"; break; default: s = "error " + n; break; } - return s; + return s; } public void SemErr(IToken/*!*/ tok, string/*!*/ msg) { // semantic errors @@ -2379,8 +2430,9 @@ public class Errors { Contract.Requires(msg != null); SemErr(tok.filename, tok.line, tok.col, msg); } + public virtual void SemErr(string filename, int line, int col, string/*!*/ msg) { - Contract.Requires(msg != null); + Contract.Requires(msg != null); errorStream.WriteLine(errMsgFormat, filename, line, col, msg); count++; } diff --git a/Source/Dafny/Printer.cs b/Source/Dafny/Printer.cs index d3c7ecea..2dd2724f 100644 --- a/Source/Dafny/Printer.cs +++ b/Source/Dafny/Printer.cs @@ -507,6 +507,13 @@ namespace Microsoft.Dafny { break; } } + + } else if (stmt is AlternativeStmt) { + var s = (AlternativeStmt)stmt; + wr.WriteLine("if {"); + PrintAlternatives(indent, s.Alternatives); + Indent(indent); + wr.Write("}"); } else if (stmt is WhileStmt) { WhileStmt s = (WhileStmt)stmt; @@ -514,12 +521,23 @@ namespace Microsoft.Dafny { PrintGuard(s.Guard); wr.WriteLine(")"); - int ind = indent + IndentAmount; - PrintSpec("invariant", s.Invariants, ind); - PrintSpecLine("decreases", s.Decreases, indent); + PrintSpec("invariant", s.Invariants, indent + IndentAmount); + PrintSpecLine("decreases", s.Decreases, indent + IndentAmount); Indent(indent); PrintStatement(s.Body, indent); - + + } else if (stmt is AlternativeLoopStmt) { + var s = (AlternativeLoopStmt)stmt; + wr.WriteLine("while"); + PrintSpec("invariant", s.Invariants, indent + IndentAmount); + PrintSpecLine("decreases", s.Decreases, indent + IndentAmount); + + Indent(indent); + wr.WriteLine("{"); + PrintAlternatives(indent, s.Alternatives); + Indent(indent); + wr.Write("}"); + } else if (stmt is ForeachStmt) { ForeachStmt s = (ForeachStmt)stmt; wr.Write("foreach ({0} in ", s.BoundVar.Name); @@ -572,6 +590,21 @@ namespace Microsoft.Dafny { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement } } + + void PrintAlternatives(int indent, List alternatives) { + int caseInd = indent + IndentAmount; + foreach (var alternative in alternatives) { + Indent(caseInd); + wr.Write("case "); + PrintExpression(alternative.Guard); + wr.WriteLine(" =>"); + foreach (Statement s in alternative.Body) { + Indent(caseInd + IndentAmount); + PrintStatement(s, caseInd + IndentAmount); + wr.WriteLine(); + } + } + } void PrintDeterminedRhs(DeterminedAssignmentRhs rhs) { Contract.Requires(rhs != null); diff --git a/Source/Dafny/Resolver.cs b/Source/Dafny/Resolver.cs index eb40ac82..3549d0db 100644 --- a/Source/Dafny/Resolver.cs +++ b/Source/Dafny/Resolver.cs @@ -1317,7 +1317,11 @@ namespace Microsoft.Dafny { if (s.Els != null) { ResolveStatement(s.Els, branchesAreSpecOnly, method); } - + + } else if (stmt is AlternativeStmt) { + var s = (AlternativeStmt)stmt; + s.IsGhost = ResolveAlternatives(s.Alternatives, specContextOnly, method); + } else if (stmt is WhileStmt) { WhileStmt s = (WhileStmt)stmt; bool bodyMustBeSpecOnly = specContextOnly; @@ -1349,7 +1353,25 @@ namespace Microsoft.Dafny { } s.IsGhost = bodyMustBeSpecOnly; ResolveStatement(s.Body, bodyMustBeSpecOnly, method); - + + } else if (stmt is AlternativeLoopStmt) { + var s = (AlternativeLoopStmt)stmt; + s.IsGhost = ResolveAlternatives(s.Alternatives, specContextOnly, method); + foreach (MaybeFreeExpression inv in s.Invariants) { + ResolveExpression(inv.E, true, true); + Contract.Assert(inv.E.Type != null); // follows from postcondition of ResolveExpression + if (!UnifyTypes(inv.E.Type, Type.Bool)) { + Error(inv.E, "invariant is expected to be of type {0}, but is {1}", Type.Bool, inv.E.Type); + } + } + foreach (Expression e in s.Decreases) { + ResolveExpression(e, true, true); + if (s.IsGhost && e is WildcardExpr) { + Error(e, "'decreases *' is not allowed on ghost loops"); + } + // any type is fine + } + } else if (stmt is ForeachStmt) { ForeachStmt s = (ForeachStmt)stmt; @@ -1491,6 +1513,34 @@ namespace Microsoft.Dafny { } } + bool ResolveAlternatives(List alternatives, bool specContextOnly, Method method) { + Contract.Requires(alternatives != null); + Contract.Requires(method != null); + + bool isGhost = specContextOnly; + // first, resolve the guards, which tells us whether or not the entire statement is a ghost statement + foreach (var alternative in alternatives) { + int prevErrorCount = ErrorCount; + ResolveExpression(alternative.Guard, true, true); + Contract.Assert(alternative.Guard.Type != null); // follows from postcondition of ResolveExpression + bool successfullyResolved = ErrorCount == prevErrorCount; + if (!UnifyTypes(alternative.Guard.Type, Type.Bool)) { + Error(alternative.Guard, "condition is expected to be of type {0}, but is {1}", Type.Bool, alternative.Guard.Type); + } + if (!specContextOnly && successfullyResolved) { + isGhost = isGhost || UsesSpecFeatures(alternative.Guard); + } + } + foreach (var alternative in alternatives) { + scope.PushMarker(); + foreach (Statement ss in alternative.Body) { + ResolveStatement(ss, isGhost, method); + } + scope.PopMarker(); + } + return isGhost; + } + void ResolveCallStmt(CallStmt s, bool specContextOnly, Method method, Type receiverType) { Contract.Requires(s != null); Contract.Requires(method != null); diff --git a/Source/Dafny/Scanner.cs b/Source/Dafny/Scanner.cs index 0af254f3..817df6cd 100644 --- a/Source/Dafny/Scanner.cs +++ b/Source/Dafny/Scanner.cs @@ -19,7 +19,7 @@ public class Buffer { // a) whole stream in buffer // b) part of stream in buffer // 2) non seekable stream (network, console) - + public const int EOF = 65535 + 1; // char.MaxValue + 1; const int MIN_BUFFER_LENGTH = 1024; // 1KB const int MAX_BUFFER_LENGTH = MIN_BUFFER_LENGTH * 64; // 64KB @@ -31,15 +31,17 @@ public class Buffer { Stream/*!*/ stream; // input stream (seekable) bool isUserStream; // was the stream opened by the user? -[ContractInvariantMethod] -void ObjectInvariant(){ - Contract.Invariant(buf != null); - Contract.Invariant(stream != null);} - [NotDelayed] - public Buffer (Stream/*!*/ s, bool isUserStream) :base() { + [ContractInvariantMethod] + void ObjectInvariant(){ + Contract.Invariant(buf != null); + Contract.Invariant(stream != null); + } + +// [NotDelayed] + public Buffer (Stream/*!*/ s, bool isUserStream) : base() { Contract.Requires(s != null); stream = s; this.isUserStream = isUserStream; - + int fl, bl; if (s.CanSeek) { fl = (int) s.Length; @@ -51,12 +53,12 @@ void ObjectInvariant(){ buf = new byte[(bl>0) ? bl : MIN_BUFFER_LENGTH]; fileLen = fl; bufLen = bl; - + if (fileLen > 0) Pos = 0; // setup buffer to position 0 (start) else bufPos = 0; // index 0 is already after the file, thus Pos = 0 is invalid if (bufLen == fileLen && s.CanSeek) Close(); } - + protected Buffer(Buffer/*!*/ b) { // called in UTF8Buffer constructor Contract.Requires(b != null); buf = b.buf; @@ -73,14 +75,14 @@ void ObjectInvariant(){ } ~Buffer() { Close(); } - + protected void Close() { if (!isUserStream && stream != null) { stream.Close(); //stream = null; } } - + public virtual int Read () { if (bufPos < bufLen) { return buf[bufPos++]; @@ -100,7 +102,7 @@ void ObjectInvariant(){ Pos = curPos; return ch; } - + public string/*!*/ GetString (int beg, int end) { Contract.Ensures(Contract.Result() != null); int len = 0; @@ -139,7 +141,7 @@ void ObjectInvariant(){ } } } - + // Read the next chunk of bytes from the stream, increases the buffer // if needed and updates the fields fileLen and bufLen. // Returns the number of bytes read. @@ -213,19 +215,20 @@ public class Scanner { const int noSym = 107; -[ContractInvariantMethod] -void objectInvariant(){ - Contract.Invariant(buffer!=null); - Contract.Invariant(t != null); - Contract.Invariant(start != null); - Contract.Invariant(tokens != null); - Contract.Invariant(pt != null); - Contract.Invariant(tval != null); - Contract.Invariant(Filename != null); - Contract.Invariant(errorHandler != null); -} + [ContractInvariantMethod] + void objectInvariant(){ + Contract.Invariant(buffer!=null); + Contract.Invariant(t != null); + Contract.Invariant(start != null); + Contract.Invariant(tokens != null); + Contract.Invariant(pt != null); + Contract.Invariant(tval != null); + Contract.Invariant(Filename != null); + Contract.Invariant(errorHandler != null); + } + public Buffer/*!*/ buffer; // scanner buffer - + Token/*!*/ t; // current token int ch; // current input character int pos; // byte position of current character @@ -236,13 +239,13 @@ void objectInvariant(){ Token/*!*/ tokens; // list of tokens already peeked (first token is a dummy) Token/*!*/ pt; // current peek token - + char[]/*!*/ tval = new char[128]; // text of current token int tlen; // length of current token - + private string/*!*/ Filename; private Errors/*!*/ errorHandler; - + static Scanner() { start = new Hashtable(128); for (int i = 39; i <= 39; ++i) start[i] = 1; @@ -291,9 +294,9 @@ void objectInvariant(){ start[Buffer.EOF] = -1; } - - [NotDelayed] - public Scanner (string/*!*/ fileName, Errors/*!*/ errorHandler) :base(){ + +// [NotDelayed] + public Scanner (string/*!*/ fileName, Errors/*!*/ errorHandler) : base() { Contract.Requires(fileName != null); Contract.Requires(errorHandler != null); this.errorHandler = errorHandler; @@ -303,15 +306,14 @@ void objectInvariant(){ Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); buffer = new Buffer(stream, false); Filename = fileName; - Init(); } catch (IOException) { throw new FatalError("Cannot open file " + fileName); } } - - [NotDelayed] - public Scanner (Stream/*!*/ s, Errors/*!*/ errorHandler, string/*!*/ fileName) :base(){ + +// [NotDelayed] + public Scanner (Stream/*!*/ s, Errors/*!*/ errorHandler, string/*!*/ fileName) : base() { Contract.Requires(s != null); Contract.Requires(errorHandler != null); Contract.Requires(fileName != null); @@ -320,10 +322,9 @@ void objectInvariant(){ buffer = new Buffer(s, true); this.errorHandler = errorHandler; this.Filename = fileName; - Init(); } - + void Init() { pos = -1; line = 1; col = 0; oldEols = 0; @@ -344,11 +345,11 @@ void objectInvariant(){ Contract.Ensures(Contract.Result() != null); int p = buffer.Pos; int ch = buffer.Read(); - // replace isolated '\r' by '\n' in order to make + // replace isolated '\r' by '\n' in order to make // eol handling uniform across Windows, Unix and Mac if (ch == '\r' && buffer.Peek() != '\n') ch = EOL; while (ch != EOL && ch != Buffer.EOF){ - ch = buffer.Read(); + ch = buffer.Read(); // replace isolated '\r' by '\n' in order to make // eol handling uniform across Windows, Unix and Mac if (ch == '\r' && buffer.Peek() != '\n') ch = EOL; @@ -359,7 +360,7 @@ void objectInvariant(){ } void NextCh() { - if (oldEols > 0) { ch = EOL; oldEols--; } + if (oldEols > 0) { ch = EOL; oldEols--; } else { // pos = buffer.Pos; // ch = buffer.Read(); col++; @@ -367,9 +368,9 @@ void objectInvariant(){ // // eol handling uniform across Windows, Unix and Mac // if (ch == '\r' && buffer.Peek() != '\n') ch = EOL; // if (ch == EOL) { line++; col = 0; } - + while (true) { - pos = buffer.Pos; + pos = buffer.Pos; ch = buffer.Read(); col++; // replace isolated '\r' by '\n' in order to make // eol handling uniform across Windows, Unix and Mac @@ -419,7 +420,7 @@ void objectInvariant(){ return; } - + } } @@ -558,10 +559,13 @@ void objectInvariant(){ t.pos = pos; t.col = col; t.line = line; t.filename = this.Filename; int state; - if (start.ContainsKey(ch)) { state = (int) cce.NonNull( start[ch]); } + if (start.ContainsKey(ch)) { + Contract.Assert(start[ch] != null); + state = (int) start[ch]; + } else { state = 0; } tlen = 0; AddCh(); - + switch (state) { case -1: { t.kind = eofSym; break; } // NextCh already done case 0: { @@ -769,14 +773,14 @@ void objectInvariant(){ t.val = new String(tval, 0, tlen); return t; } - + private void SetScannerBehindT() { buffer.Pos = t.pos; NextCh(); line = t.line; col = t.col; for (int i = 0; i < tlen; i++) NextCh(); } - + // get the next token (possibly a token already seen during peeking) public Token/*!*/ Scan () { Contract.Ensures(Contract.Result() != null); @@ -797,7 +801,7 @@ void objectInvariant(){ } pt = pt.next; } while (pt.kind > maxT); // skip pragmas - + return pt; } diff --git a/Source/Dafny/Translator.cs b/Source/Dafny/Translator.cs index b529b984..e338c0ca 100644 --- a/Source/Dafny/Translator.cs +++ b/Source/Dafny/Translator.cs @@ -2139,15 +2139,16 @@ namespace Microsoft.Dafny { return decr; } - List LoopDecreasesWithDefault(WhileStmt s, out bool inferredDecreases) { - Contract.Requires(s != null); + List LoopDecreasesWithDefault(IToken tok, Expression Guard, List Decreases, out bool inferredDecreases) { + Contract.Requires(tok != null); + Contract.Requires(Decreases != null); - List theDecreases = s.Decreases; + List theDecreases = Decreases; inferredDecreases = false; - if (theDecreases.Count == 0 && s.Guard != null) { + if (theDecreases.Count == 0 && Guard != null) { theDecreases = new List(); Expression prefix = null; - foreach (Expression guardConjunct in Conjuncts(s.Guard)) { + foreach (Expression guardConjunct in Conjuncts(Guard)) { Expression guess = null; BinaryExpr bin = guardConjunct as BinaryExpr; if (bin != null) { @@ -2155,23 +2156,23 @@ namespace Microsoft.Dafny { case BinaryExpr.ResolvedOpcode.Lt: case BinaryExpr.ResolvedOpcode.Le: // for A < B and A <= B, use the decreases B - A - guess = CreateIntSub(s.Tok, bin.E1, bin.E0); + guess = CreateIntSub(tok, bin.E1, bin.E0); break; case BinaryExpr.ResolvedOpcode.Ge: case BinaryExpr.ResolvedOpcode.Gt: // for A >= B and A > B, use the decreases A - B - guess = CreateIntSub(s.Tok, bin.E0, bin.E1); + guess = CreateIntSub(tok, bin.E0, bin.E1); break; case BinaryExpr.ResolvedOpcode.NeqCommon: if (bin.E0.Type is IntType) { // for A != B where A and B are integers, use the absolute difference between A and B (that is: if 0 <= A-B then A-B else B-A) - Expression AminusB = CreateIntSub(s.Tok, bin.E0, bin.E1); - Expression BminusA = CreateIntSub(s.Tok, bin.E1, bin.E0); - Expression zero = CreateIntLiteral(s.Tok, 0); - BinaryExpr test = new BinaryExpr(s.Tok, BinaryExpr.Opcode.Le, zero, AminusB); + Expression AminusB = CreateIntSub(tok, bin.E0, bin.E1); + Expression BminusA = CreateIntSub(tok, bin.E1, bin.E0); + Expression zero = CreateIntLiteral(tok, 0); + BinaryExpr test = new BinaryExpr(tok, BinaryExpr.Opcode.Le, zero, AminusB); test.ResolvedOp = BinaryExpr.ResolvedOpcode.Le; // resolve here test.Type = Type.Bool; // resolve here - guess = CreateIntITE(s.Tok, test, AminusB, BminusA); + guess = CreateIntITE(tok, test, AminusB, BminusA); } break; default: @@ -2181,8 +2182,8 @@ namespace Microsoft.Dafny { if (guess != null) { if (prefix != null) { // Make the following guess: if prefix then guess else -1 - Expression negativeOne = CreateIntLiteral(s.Tok, -1); - guess = CreateIntITE(s.Tok, prefix, guess, negativeOne); + Expression negativeOne = CreateIntLiteral(tok, -1); + guess = CreateIntITE(tok, prefix, guess, negativeOne); } theDecreases.Add(guess); inferredDecreases = true; @@ -2191,7 +2192,7 @@ namespace Microsoft.Dafny { if (prefix == null) { prefix = guardConjunct; } else { - BinaryExpr and = new BinaryExpr(s.Tok, BinaryExpr.Opcode.And, prefix, guardConjunct); + BinaryExpr and = new BinaryExpr(tok, BinaryExpr.Opcode.And, prefix, guardConjunct); and.ResolvedOp = BinaryExpr.ResolvedOpcode.And; // resolve here and.Type = Type.Bool; // resolve here prefix = and; @@ -3013,134 +3014,28 @@ namespace Microsoft.Dafny { } } builder.Add(new Bpl.IfCmd(stmt.Tok, guard, thn, elsIf, els)); - + + } else if (stmt is AlternativeStmt) { + AddComment(builder, stmt, "alternative statement"); + var s = (AlternativeStmt)stmt; + var elseCase = Assert(s.Tok, Bpl.Expr.False, "alternative cases fail to cover all possibilties"); + TrAlternatives(s.Alternatives, elseCase, null, builder, locals, etran); + } else if (stmt is WhileStmt) { AddComment(builder, stmt, "while statement"); - WhileStmt s = (WhileStmt)stmt; - int loopId = loopHeapVarCount; - loopHeapVarCount++; - - // use simple heuristics to create a default decreases clause, if none is given - bool inferredDecreases; - List theDecreases = LoopDecreasesWithDefault(s, out inferredDecreases); - - Bpl.LocalVariable preLoopHeapVar = new Bpl.LocalVariable(stmt.Tok, new Bpl.TypedIdent(stmt.Tok, "$PreLoopHeap" + loopId, predef.HeapType)); - locals.Add(preLoopHeapVar); - Bpl.IdentifierExpr preLoopHeap = new Bpl.IdentifierExpr(stmt.Tok, preLoopHeapVar); - ExpressionTranslator etranPreLoop = new ExpressionTranslator(this, predef, preLoopHeap); - builder.Add(Bpl.Cmd.SimpleAssign(stmt.Tok, preLoopHeap, etran.HeapExpr)); // TODO: does this screw up labeled breaks for this loop? - - List initDecr = null; - if (!Contract.Exists(theDecreases, e => e is WildcardExpr)) { - initDecr = RecordDecreasesValue(theDecreases, builder, locals, etran, "$decr" + loopId + "$init$"); - } - - // the variable w is used to coordinate the definedness checking of the loop invariant - Bpl.LocalVariable wVar = new Bpl.LocalVariable(stmt.Tok, new Bpl.TypedIdent(stmt.Tok, "$w" + loopId, Bpl.Type.Bool)); - Bpl.IdentifierExpr w = new Bpl.IdentifierExpr(stmt.Tok, wVar); - locals.Add(wVar); - // havoc w; - builder.Add(new Bpl.HavocCmd(stmt.Tok, new Bpl.IdentifierExprSeq(w))); - - List invariants = new List(); - Bpl.StmtListBuilder invDefinednessBuilder = new Bpl.StmtListBuilder(); - foreach (MaybeFreeExpression loopInv in s.Invariants) { - TrStmt_CheckWellformed(loopInv.E, invDefinednessBuilder, locals, etran, false); - invDefinednessBuilder.Add(new Bpl.AssumeCmd(loopInv.E.tok, etran.TrExpr(loopInv.E))); - - invariants.Add(new Bpl.AssumeCmd(loopInv.E.tok, Bpl.Expr.Imp(w, CanCallAssumption(loopInv.E, etran)))); - if (loopInv.IsFree) { - invariants.Add(new Bpl.AssumeCmd(loopInv.E.tok, Bpl.Expr.Imp(w, etran.TrExpr(loopInv.E)))); - } else { - bool splitHappened; - var ss = TrSplitExpr(loopInv.E, etran, out splitHappened); - if (!splitHappened) { - var wInv = Bpl.Expr.Imp(w, etran.TrExpr(loopInv.E)); - invariants.Add(Assert(loopInv.E.tok, wInv, "loop invariant violation")); - } else { - foreach (var split in ss) { - var wInv = Bpl.Expr.Binary(split.E.tok, BinaryOperator.Opcode.Imp, w, split.E); - if (split.IsFree) { - invariants.Add(new Bpl.AssumeCmd(split.E.tok, wInv)); - } else { - invariants.Add(Assert(split.E.tok, wInv, "loop invariant violation")); // TODO: it would be fine to have this use {:subsumption 0} - } - } - } - } - } - // check definedness of decreases clause - // TODO: can this check be omitted if the decreases clause is inferred? - foreach (Expression e in theDecreases) { - TrStmt_CheckWellformed(e, invDefinednessBuilder, locals, etran, true); - } - // include boilerplate invariants - foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(stmt.Tok, currentMethod, etranPreLoop, etran)) { - if (tri.IsFree) { - invariants.Add(new Bpl.AssumeCmd(stmt.Tok, tri.Expr)); - } else { - Contract.Assert(tri.ErrorMessage != null); // follows from BoilerplateTriple invariant - invariants.Add(Assert(stmt.Tok, tri.Expr, tri.ErrorMessage)); - } - } - // include a free invariant that says that all completed iterations so far have only decreased the termination metric - if (initDecr != null) { - List toks = new List(); - List types = new List(); - List decrs = new List(); - foreach (Expression e in theDecreases) { - toks.Add(e.tok); - types.Add(cce.NonNull(e.Type)); - decrs.Add(etran.TrExpr(e)); - } - Bpl.Expr decrCheck = DecreasesCheck(toks, types, decrs, initDecr, etran, null, null, true, false); - invariants.Add(new Bpl.AssumeCmd(stmt.Tok, decrCheck)); - } - - Bpl.StmtListBuilder loopBodyBuilder = new Bpl.StmtListBuilder(); - // as the first thing inside the loop, generate: if (!w) { assert IsTotal(inv); assume false; } - invDefinednessBuilder.Add(new Bpl.AssumeCmd(stmt.Tok, Bpl.Expr.False)); - loopBodyBuilder.Add(new Bpl.IfCmd(stmt.Tok, Bpl.Expr.Not(w), invDefinednessBuilder.Collect(stmt.Tok), null, null)); - // generate: assert IsTotal(guard); if (!guard) { break; } - Bpl.Expr guard = null; - if (s.Guard != null) { - TrStmt_CheckWellformed(s.Guard, loopBodyBuilder, locals, etran, true); - guard = Bpl.Expr.Not(etran.TrExpr(s.Guard)); - } - Bpl.StmtListBuilder guardBreak = new Bpl.StmtListBuilder(); - guardBreak.Add(new Bpl.BreakCmd(s.Tok, null)); - loopBodyBuilder.Add(new Bpl.IfCmd(s.Tok, guard, guardBreak.Collect(s.Tok), null, null)); - - loopBodyBuilder.Add(CaptureState(stmt.Tok, "loop entered")); - // termination checking - if (Contract.Exists(theDecreases, e => e is WildcardExpr)) { - // omit termination checking for this loop - TrStmt(s.Body, loopBodyBuilder, locals, etran); - } else { - List oldBfs = RecordDecreasesValue(theDecreases, loopBodyBuilder, locals, etran, "$decr" + loopId + "$"); - // time for the actual loop body - TrStmt(s.Body, loopBodyBuilder, locals, etran); - // check definedness of decreases expressions - List toks = new List(); - List types = new List(); - List decrs = new List(); - foreach (Expression e in theDecreases) { - toks.Add(e.tok); - types.Add(cce.NonNull(e.Type)); - decrs.Add(etran.TrExpr(e)); - } - Bpl.Expr decrCheck = DecreasesCheck(toks, types, decrs, oldBfs, etran, loopBodyBuilder, " at end of loop iteration", false, false); - loopBodyBuilder.Add(Assert(stmt.Tok, decrCheck, inferredDecreases ? "cannot prove termination; try supplying a decreases clause for the loop" : "decreases expression might not decrease")); - } - // Finally, assume the well-formedness of the invariant (which has been checked once and for all above), so that the check - // of invariant-maintenance can use the appropriate canCall predicates. - foreach (MaybeFreeExpression loopInv in s.Invariants) { - loopBodyBuilder.Add(new Bpl.AssumeCmd(loopInv.E.tok, CanCallAssumption(loopInv.E, etran))); - } - Bpl.StmtList body = loopBodyBuilder.Collect(stmt.Tok); - - builder.Add(new Bpl.WhileCmd(stmt.Tok, Bpl.Expr.True, invariants, body)); - builder.Add(CaptureState(stmt.Tok, "loop exit")); + var s = (WhileStmt)stmt; + TrLoop(s, s.Guard, + delegate(Bpl.StmtListBuilder bld) { TrStmt(s.Body, bld, locals, etran); }, + builder, locals, etran); + + } else if (stmt is AlternativeLoopStmt) { + AddComment(builder, stmt, "alternative loop statement"); + var s = (AlternativeLoopStmt)stmt; + var tru = new LiteralExpr(s.Tok, true); + tru.Type = Type.Bool; // resolve here + TrLoop(s, tru, + delegate(Bpl.StmtListBuilder bld) { TrAlternatives(s.Alternatives, null, new Bpl.BreakCmd(s.Tok, null), bld, locals, etran); }, + builder, locals, etran); } else if (stmt is ForeachStmt) { AddComment(builder, stmt, "foreach statement"); @@ -3332,6 +3227,193 @@ namespace Microsoft.Dafny { } } + delegate void BodyTranslator(Bpl.StmtListBuilder builder); + + void TrLoop(LoopStmt s, Expression Guard, BodyTranslator bodyTr, + Bpl.StmtListBuilder builder, Bpl.VariableSeq locals, ExpressionTranslator etran) { + Contract.Requires(s != null); + Contract.Requires(bodyTr != null); + Contract.Requires(builder != null); + Contract.Requires(locals != null); + Contract.Requires(etran != null); + + int loopId = loopHeapVarCount; + loopHeapVarCount++; + + // use simple heuristics to create a default decreases clause, if none is given + bool inferredDecreases; + List theDecreases = LoopDecreasesWithDefault(s.Tok, Guard, s.Decreases, out inferredDecreases); + + Bpl.LocalVariable preLoopHeapVar = new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$PreLoopHeap" + loopId, predef.HeapType)); + locals.Add(preLoopHeapVar); + Bpl.IdentifierExpr preLoopHeap = new Bpl.IdentifierExpr(s.Tok, preLoopHeapVar); + ExpressionTranslator etranPreLoop = new ExpressionTranslator(this, predef, preLoopHeap); + builder.Add(Bpl.Cmd.SimpleAssign(s.Tok, preLoopHeap, etran.HeapExpr)); // TODO: does this screw up labeled breaks for this loop? + + List initDecr = null; + if (!Contract.Exists(theDecreases, e => e is WildcardExpr)) { + initDecr = RecordDecreasesValue(theDecreases, builder, locals, etran, "$decr" + loopId + "$init$"); + } + + // the variable w is used to coordinate the definedness checking of the loop invariant + Bpl.LocalVariable wVar = new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$w" + loopId, Bpl.Type.Bool)); + Bpl.IdentifierExpr w = new Bpl.IdentifierExpr(s.Tok, wVar); + locals.Add(wVar); + // havoc w; + builder.Add(new Bpl.HavocCmd(s.Tok, new Bpl.IdentifierExprSeq(w))); + + List invariants = new List(); + Bpl.StmtListBuilder invDefinednessBuilder = new Bpl.StmtListBuilder(); + foreach (MaybeFreeExpression loopInv in s.Invariants) { + TrStmt_CheckWellformed(loopInv.E, invDefinednessBuilder, locals, etran, false); + invDefinednessBuilder.Add(new Bpl.AssumeCmd(loopInv.E.tok, etran.TrExpr(loopInv.E))); + + invariants.Add(new Bpl.AssumeCmd(loopInv.E.tok, Bpl.Expr.Imp(w, CanCallAssumption(loopInv.E, etran)))); + if (loopInv.IsFree) { + invariants.Add(new Bpl.AssumeCmd(loopInv.E.tok, Bpl.Expr.Imp(w, etran.TrExpr(loopInv.E)))); + } else { + bool splitHappened; + var ss = TrSplitExpr(loopInv.E, etran, out splitHappened); + if (!splitHappened) { + var wInv = Bpl.Expr.Imp(w, etran.TrExpr(loopInv.E)); + invariants.Add(Assert(loopInv.E.tok, wInv, "loop invariant violation")); + } else { + foreach (var split in ss) { + var wInv = Bpl.Expr.Binary(split.E.tok, BinaryOperator.Opcode.Imp, w, split.E); + if (split.IsFree) { + invariants.Add(new Bpl.AssumeCmd(split.E.tok, wInv)); + } else { + invariants.Add(Assert(split.E.tok, wInv, "loop invariant violation")); // TODO: it would be fine to have this use {:subsumption 0} + } + } + } + } + } + // check definedness of decreases clause + // TODO: can this check be omitted if the decreases clause is inferred? + foreach (Expression e in theDecreases) { + TrStmt_CheckWellformed(e, invDefinednessBuilder, locals, etran, true); + } + // include boilerplate invariants + foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(s.Tok, currentMethod, etranPreLoop, etran)) { + if (tri.IsFree) { + invariants.Add(new Bpl.AssumeCmd(s.Tok, tri.Expr)); + } else { + Contract.Assert(tri.ErrorMessage != null); // follows from BoilerplateTriple invariant + invariants.Add(Assert(s.Tok, tri.Expr, tri.ErrorMessage)); + } + } + // include a free invariant that says that all completed iterations so far have only decreased the termination metric + if (initDecr != null) { + List toks = new List(); + List types = new List(); + List decrs = new List(); + foreach (Expression e in theDecreases) { + toks.Add(e.tok); + types.Add(cce.NonNull(e.Type)); + decrs.Add(etran.TrExpr(e)); + } + Bpl.Expr decrCheck = DecreasesCheck(toks, types, decrs, initDecr, etran, null, null, true, false); + invariants.Add(new Bpl.AssumeCmd(s.Tok, decrCheck)); + } + + Bpl.StmtListBuilder loopBodyBuilder = new Bpl.StmtListBuilder(); + // as the first thing inside the loop, generate: if (!w) { assert IsTotal(inv); assume false; } + invDefinednessBuilder.Add(new Bpl.AssumeCmd(s.Tok, Bpl.Expr.False)); + loopBodyBuilder.Add(new Bpl.IfCmd(s.Tok, Bpl.Expr.Not(w), invDefinednessBuilder.Collect(s.Tok), null, null)); + // generate: assert IsTotal(guard); if (!guard) { break; } + Bpl.Expr guard = null; + if (Guard != null) { + TrStmt_CheckWellformed(Guard, loopBodyBuilder, locals, etran, true); + guard = Bpl.Expr.Not(etran.TrExpr(Guard)); + } + Bpl.StmtListBuilder guardBreak = new Bpl.StmtListBuilder(); + guardBreak.Add(new Bpl.BreakCmd(s.Tok, null)); + loopBodyBuilder.Add(new Bpl.IfCmd(s.Tok, guard, guardBreak.Collect(s.Tok), null, null)); + + loopBodyBuilder.Add(CaptureState(s.Tok, "loop entered")); + // termination checking + if (Contract.Exists(theDecreases, e => e is WildcardExpr)) { + // omit termination checking for this loop + bodyTr(loopBodyBuilder); + } else { + List oldBfs = RecordDecreasesValue(theDecreases, loopBodyBuilder, locals, etran, "$decr" + loopId + "$"); + // time for the actual loop body + bodyTr(loopBodyBuilder); + // check definedness of decreases expressions + List toks = new List(); + List types = new List(); + List decrs = new List(); + foreach (Expression e in theDecreases) { + toks.Add(e.tok); + types.Add(cce.NonNull(e.Type)); + decrs.Add(etran.TrExpr(e)); + } + Bpl.Expr decrCheck = DecreasesCheck(toks, types, decrs, oldBfs, etran, loopBodyBuilder, " at end of loop iteration", false, false); + loopBodyBuilder.Add(Assert(s.Tok, decrCheck, inferredDecreases ? "cannot prove termination; try supplying a decreases clause for the loop" : "decreases expression might not decrease")); + } + // Finally, assume the well-formedness of the invariant (which has been checked once and for all above), so that the check + // of invariant-maintenance can use the appropriate canCall predicates. + foreach (MaybeFreeExpression loopInv in s.Invariants) { + loopBodyBuilder.Add(new Bpl.AssumeCmd(loopInv.E.tok, CanCallAssumption(loopInv.E, etran))); + } + Bpl.StmtList body = loopBodyBuilder.Collect(s.Tok); + + builder.Add(new Bpl.WhileCmd(s.Tok, Bpl.Expr.True, invariants, body)); + builder.Add(CaptureState(s.Tok, "loop exit")); + } + + void TrAlternatives(List alternatives, Bpl.Cmd elseCase0, Bpl.StructuredCmd elseCase1, + Bpl.StmtListBuilder builder, VariableSeq locals, ExpressionTranslator etran) { + Contract.Requires(alternatives != null); + Contract.Requires((elseCase0 == null) == (elseCase1 == null)); // ugly way of doing a type union + Contract.Requires(builder != null); + Contract.Requires(locals != null); + Contract.Requires(etran != null); + + if (alternatives.Count == 0) { + if (elseCase0 != null) { + builder.Add(elseCase0); + } else { + builder.Add(elseCase1); + } + return; + } + + // build the negation of the disjunction of all guards (that is, the conjunction of their negations) + Bpl.Expr noGuard = Bpl.Expr.True; + foreach (var alternative in alternatives) { + noGuard = BplAnd(noGuard, Bpl.Expr.Not(etran.TrExpr(alternative.Guard))); + } + + var b = new Bpl.StmtListBuilder(); + var elseTok = elseCase0 != null ? elseCase0.tok : elseCase1.tok; + b.Add(new Bpl.AssumeCmd(elseTok, noGuard)); + if (elseCase0 != null) { + b.Add(elseCase0); + } else { + b.Add(elseCase1); + } + Bpl.StmtList els = b.Collect(elseTok); + + Bpl.IfCmd elsIf = null; + for (int i = alternatives.Count; 0 <= --i; ) { + Contract.Assert(elsIf == null || els == null); // loop invariant + var alternative = alternatives[i]; + b = new Bpl.StmtListBuilder(); + TrStmt_CheckWellformed(alternative.Guard, b, locals, etran, true); + b.Add(new AssumeCmd(alternative.Guard.tok, etran.TrExpr(alternative.Guard))); + foreach (var s in alternative.Body) { + TrStmt(s, b, locals, etran); + } + Bpl.StmtList thn = b.Collect(alternative.Tok); + elsIf = new Bpl.IfCmd(alternative.Tok, null, thn, elsIf, els); + els = null; + } + Contract.Assert(elsIf != null && els == null); // follows from loop invariant and the fact that there's more than one alternative + builder.Add(elsIf); + } + void TrCallStmt(CallStmt s, Bpl.StmtListBuilder builder, Bpl.VariableSeq locals, ExpressionTranslator etran, Bpl.Expr actualReceiver) { Contract.Requires(s != null); Contract.Requires(builder != null); diff --git a/Test/dafny0/Answer b/Test/dafny0/Answer index 28f438af..e0874a66 100644 --- a/Test/dafny0/Answer +++ b/Test/dafny0/Answer @@ -573,8 +573,12 @@ Execution trace: (0,0): anon10_Else (0,0): anon11_Else (0,0): anon12_Then +ControlStructures.dfy(72,3): Error: alternative cases fail to cover all possibilties +Execution trace: + (0,0): anon0 + (0,0): anon5_Else -Dafny program verifier finished with 8 verified, 5 errors +Dafny program verifier finished with 15 verified, 6 errors -------------------- Termination.dfy -------------------- Termination.dfy(102,3): Error: cannot prove termination; try supplying a decreases clause for the loop diff --git a/Test/dafny0/ControlStructures.dfy b/Test/dafny0/ControlStructures.dfy index eed74634..35d939d3 100644 --- a/Test/dafny0/ControlStructures.dfy +++ b/Test/dafny0/ControlStructures.dfy @@ -63,3 +63,75 @@ function F1(d: D, x: int): int case Green => 0 case Blue => 2 } + +// --------------- alternative statements --------------------- + +method A0(x: int) returns (r: int) + ensures 0 <= r; +{ + if { // error: missing case (x == 0) + case x < 0 => r := 12; + case 0 < x => r := 13; + } +} + +method A1(x: int) returns (r: int) + ensures 0 <= r; +{ + if { + case x <= 0 => r := 12; + case 0 <= x => r := 13; + } +} + +method DutchFlag(A: array, N: int, l: int, r: int) returns (result: int) + requires A != null && N == A.Length; + requires 0 <= l && l+2 <= r && r <= N; + modifies A; + ensures l <= result && result < r; + ensures forall k, j :: l <= k && k < result && result <= j && j < r ==> A[k] <= A[j]; + ensures forall k :: l <= k && k < result ==> A[k] <= old(A[l]); + ensures forall k :: result <= k && k < r ==> old(A[l]) <= A[k]; +{ + var pv := A[l]; + var i := l; + var j := r-1; + // swap A[l] and A[j] + var tmp := A[l]; A[l] := A[j]; A[j] := tmp; + + while (i < j) + invariant l <= i && i <= j && j < r; + invariant forall k :: l <= k && k < i ==> A[k] <= pv; + invariant forall k :: j <= k && k < r ==> pv <= A[k]; + { + if { + case A[i] <= pv => + i := i + 1; + case pv <= A[j-1] => + j := j - 1; + case A[j-1] < pv && pv < A[i] => + // swap A[j-1] and A[i] + tmp := A[i]; A[i] := A[j-1]; A[j-1] := tmp; + assert A[i] < pv && pv < A[j-1]; + i := i + 1; + j := j - 1; + } + } + result := i; +} + +// --------------- alternative loop statements --------------- + +method B(x: int) returns (r: int) + ensures r == 0; +{ + r := x; + while + decreases if 0 <= r then r else -r; + { + case r < 0 => + r := r + 1; + case 0 < r => + r := r - 1; + } +} -- cgit v1.2.3 From 1aa1ca45fc066c9f8d94eeff1a2dc140c49db393 Mon Sep 17 00:00:00 2001 From: CodeplexBot Date: Fri, 20 May 2011 07:55:49 +0200 Subject: Boogie build succeeded --- _admin/Boogie/aste/summary.log | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/_admin/Boogie/aste/summary.log b/_admin/Boogie/aste/summary.log index c0185e9c..d974d514 100644 --- a/_admin/Boogie/aste/summary.log +++ b/_admin/Boogie/aste/summary.log @@ -1,17 +1,17 @@ -# Aste started: 2011-05-17 07:00:09 +# Aste started: 2011-05-20 07:00:17 # Host id: Boogiebox -# [2011-05-17 07:02:41] SpecSharp revision: 5fb7af71cc82 -# [2011-05-17 07:02:41] SscBoogie revision: 5fb7af71cc82 -# [2011-05-17 07:03:56] Boogie revision: e6ac2aed917f -[2011-05-17 07:06:14] C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.com Boogie.sln /Rebuild Checked +# [2011-05-20 07:03:02] SpecSharp revision: 590155e890f6 +# [2011-05-20 07:03:02] SscBoogie revision: 590155e890f6 +# [2011-05-20 07:04:18] Boogie revision: c2e1892f0445 +[2011-05-20 07:06:48] C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.com Boogie.sln /Rebuild Checked D:\Temp\aste\Boogie\Source\Core\AbsyType.cs(823,16): warning CS0659: 'Microsoft.Boogie.BasicType' overrides Object.Equals(object o) but does not override Object.GetHashCode() D:\Temp\aste\Boogie\Source\Core\AbsyType.cs(2802,16): warning CS0659: 'Microsoft.Boogie.CtorType' overrides Object.Equals(object o) but does not override Object.GetHashCode() D:\Temp\aste\Boogie\Source\Core\OOLongUtil.cs(109,7): warning CS0162: Unreachable code detected D:\Temp\aste\Boogie\Source\Core\Absy.cs(682,7): warning CC1036: Detected call to method 'Graphing.Graph`1.TopologicalSort' without [Pure] in contracts of method 'Microsoft.Boogie.Program.GraphFromImpl(Microsoft.Boogie.Implementation)'. - D:\Temp\aste\Boogie\Source\Core\Parser.cs(109,3): warning CC1032: Method 'Microsoft.Boogie.Parser+BvBounds.Resolve(Microsoft.Boogie.ResolutionContext)' overrides 'Microsoft.Boogie.Absy.Resolve(Microsoft.Boogie.ResolutionContext)', thus cannot add Requires. - D:\Temp\aste\Boogie\Source\Core\Parser.cs(114,5): warning CC1032: Method 'Microsoft.Boogie.Parser+BvBounds.Emit(Microsoft.Boogie.TokenTextWriter,System.Int32,System.Boolean)' overrides 'Microsoft.Boogie.Expr.Emit(Microsoft.Boogie.TokenTextWriter,System.Int32,System.Boolean)', thus cannot add Requires. - D:\Temp\aste\Boogie\Source\Core\Parser.cs(117,65): warning CC1032: Method 'Microsoft.Boogie.Parser+BvBounds.ComputeFreeVariables(Microsoft.Boogie.Set)' overrides 'Microsoft.Boogie.Expr.ComputeFreeVariables(Microsoft.Boogie.Set)', thus cannot add Requires. + D:\Temp\aste\Boogie\Source\Core\Parser.cs(111,3): warning CC1032: Method 'Microsoft.Boogie.Parser+BvBounds.Resolve(Microsoft.Boogie.ResolutionContext)' overrides 'Microsoft.Boogie.Absy.Resolve(Microsoft.Boogie.ResolutionContext)', thus cannot add Requires. + D:\Temp\aste\Boogie\Source\Core\Parser.cs(116,5): warning CC1032: Method 'Microsoft.Boogie.Parser+BvBounds.Emit(Microsoft.Boogie.TokenTextWriter,System.Int32,System.Boolean)' overrides 'Microsoft.Boogie.Expr.Emit(Microsoft.Boogie.TokenTextWriter,System.Int32,System.Boolean)', thus cannot add Requires. + D:\Temp\aste\Boogie\Source\Core\Parser.cs(119,65): warning CC1032: Method 'Microsoft.Boogie.Parser+BvBounds.ComputeFreeVariables(Microsoft.Boogie.Set)' overrides 'Microsoft.Boogie.Expr.ComputeFreeVariables(Microsoft.Boogie.Set)', thus cannot add Requires. D:\Temp\aste\Boogie\Source\AbsInt\ExprFactories.cs(111,7): warning CS0162: Unreachable code detected D:\Temp\aste\Boogie\Source\AbsInt\ExprFactories.cs(290,7): warning CS0162: Unreachable code detected D:\Temp\aste\Boogie\Source\AbsInt\ExprFactories.cs(309,7): warning CS0162: Unreachable code detected @@ -31,5 +31,5 @@ warning CS0162: Unreachable code detected warning CS0162: Unreachable code detected warning CC1032: Method 'VC.StratifiedVCGen+NormalChecker.CheckVC' overrides 'VC.StratifiedVCGen+StratifiedCheckerInterface.CheckVC', thus cannot add Requires. -[2011-05-17 07:54:06] 0 out of 30 test(s) in D:\Temp\aste\Boogie\Test\alltests.txt failed -# [2011-05-17 07:55:08] Released nightly of Boogie +[2011-05-20 07:54:44] 0 out of 30 test(s) in D:\Temp\aste\Boogie\Test\alltests.txt failed +# [2011-05-20 07:55:49] Released nightly of Boogie -- cgit v1.2.3 From 356651d4f4e2d9825f18858603f506332d582ad5 Mon Sep 17 00:00:00 2001 From: Mike Barnett Date: Sat, 21 May 2011 18:38:18 -0700 Subject: Created an API so that a MetadataTraverser is used to translate a set of assemblies. This enables a translator to do whole-program analyses, such as recording the subtype relationship across all of the input. (Still need to move the delegate translation into this method.) Fixed the existing whole-program translator so it uses the base class functionality for translating the arguments to a method call. Updated the regressions. --- BCT/BytecodeTranslator/ExpressionTraverser.cs | 37 +++++---- BCT/BytecodeTranslator/MetadataTraverser.cs | 20 ++++- BCT/BytecodeTranslator/Program.cs | 30 +++----- BCT/BytecodeTranslator/TraverserFactory.cs | 6 +- BCT/BytecodeTranslator/WholeProgram.cs | 88 ++++++---------------- .../TranslationTest/GeneralHeapInput.txt | 4 +- .../TranslationTest/SplitFieldsHeapInput.txt | 4 +- 7 files changed, 84 insertions(+), 105 deletions(-) diff --git a/BCT/BytecodeTranslator/ExpressionTraverser.cs b/BCT/BytecodeTranslator/ExpressionTraverser.cs index 1dd181ae..08e32248 100644 --- a/BCT/BytecodeTranslator/ExpressionTraverser.cs +++ b/BCT/BytecodeTranslator/ExpressionTraverser.cs @@ -484,21 +484,7 @@ namespace BytecodeTranslator string methodname = proc.Name; var translateAsFunctionCall = proc is Bpl.Function; Bpl.QKeyValue attrib = null; - if (!translateAsFunctionCall) { - if (resolvedMethod.Type.ResolvedType.TypeCode != PrimitiveTypeCode.Void) { - Bpl.Variable v = this.sink.CreateFreshLocal(methodCall.Type.ResolvedType); - Bpl.IdentifierExpr unboxed = new Bpl.IdentifierExpr(cloc, v); - if (resolvedMethod.Type is IGenericTypeParameter) { - Bpl.IdentifierExpr boxed = Bpl.Expr.Ident(this.sink.CreateFreshLocal(this.sink.Heap.BoxType)); - toBoxed[unboxed] = boxed; - outvars.Add(boxed); - } else { - outvars.Add(unboxed); - } - TranslatedExpressions.Push(unboxed); - } - foreach (var a in resolvedMethod.Attributes) { if (TypeHelper.GetTypeName(a.Type).EndsWith("AsyncAttribute")) { attrib = new Bpl.QKeyValue(cloc, "async", new List(), null); @@ -581,7 +567,7 @@ namespace BytecodeTranslator } } - private Bpl.DeclWithFormals TranslateArgumentsAndReturnProcedure(Bpl.IToken token, IMethodReference methodToCall, IMethodDefinition resolvedMethod, IExpression/*?*/ thisArg, IEnumerable arguments, out List inexpr, out List outvars, out Bpl.IdentifierExpr thisExpr, out List locals, out List args, out Bpl.Expr arrayExpr, out Bpl.Expr indexExpr, out Dictionary toBoxed) { + protected Bpl.DeclWithFormals TranslateArgumentsAndReturnProcedure(Bpl.IToken token, IMethodReference methodToCall, IMethodDefinition resolvedMethod, IExpression/*?*/ thisArg, IEnumerable arguments, out List inexpr, out List outvars, out Bpl.IdentifierExpr thisExpr, out List locals, out List args, out Bpl.Expr arrayExpr, out Bpl.Expr indexExpr, out Dictionary toBoxed) { inexpr = new List(); outvars = new List(); @@ -652,7 +638,26 @@ namespace BytecodeTranslator } penum.MoveNext(); } - return this.sink.FindOrCreateProcedure(resolvedMethod); + + var proc = this.sink.FindOrCreateProcedure(resolvedMethod); + + var translateAsFunctionCall = proc is Bpl.Function; + if (!translateAsFunctionCall) { + if (resolvedMethod.Type.ResolvedType.TypeCode != PrimitiveTypeCode.Void) { + Bpl.Variable v = this.sink.CreateFreshLocal(resolvedMethod.Type.ResolvedType); + Bpl.IdentifierExpr unboxed = new Bpl.IdentifierExpr(token, v); + if (resolvedMethod.Type is IGenericTypeParameter) { + Bpl.IdentifierExpr boxed = Bpl.Expr.Ident(this.sink.CreateFreshLocal(this.sink.Heap.BoxType)); + toBoxed[unboxed] = boxed; + outvars.Add(boxed); + } else { + outvars.Add(unboxed); + } + TranslatedExpressions.Push(unboxed); + } + } + + return proc; } #endregion diff --git a/BCT/BytecodeTranslator/MetadataTraverser.cs b/BCT/BytecodeTranslator/MetadataTraverser.cs index e300d3b5..75601140 100644 --- a/BCT/BytecodeTranslator/MetadataTraverser.cs +++ b/BCT/BytecodeTranslator/MetadataTraverser.cs @@ -30,22 +30,25 @@ namespace BytecodeTranslator { public readonly TraverserFactory factory; - public readonly PdbReader/*?*/ PdbReader; + public readonly IDictionary PdbReaders; + public PdbReader/*?*/ PdbReader; - public MetadataTraverser(Sink sink, PdbReader/*?*/ pdbReader) + public MetadataTraverser(Sink sink, IDictionary pdbReaders) : base() { this.sink = sink; this.factory = sink.Factory; - this.PdbReader = pdbReader; + this.PdbReaders = pdbReaders; } #region Overrides public override void Visit(IModule module) { + this.PdbReaders.TryGetValue(module, out this.PdbReader); base.Visit(module); } public override void Visit(IAssembly assembly) { + this.PdbReaders.TryGetValue(assembly, out this.PdbReader); this.sink.BeginAssembly(assembly); try { base.Visit(assembly); @@ -386,6 +389,15 @@ namespace BytecodeTranslator { #endregion + #region Public API + public virtual void TranslateAssemblies(IEnumerable assemblies) { + foreach (var a in assemblies) { + a.Dispatch(this); + } + } + #endregion + + #region Helpers private class FindCtorCall : BaseCodeTraverser { private bool isDeferringCtor = false; public ITypeReference containingType; @@ -407,5 +419,7 @@ namespace BytecodeTranslator { base.Visit(methodCall); } } + #endregion + } } \ No newline at end of file diff --git a/BCT/BytecodeTranslator/Program.cs b/BCT/BytecodeTranslator/Program.cs index d04a8783..50ac68cf 100644 --- a/BCT/BytecodeTranslator/Program.cs +++ b/BCT/BytecodeTranslator/Program.cs @@ -108,7 +108,9 @@ namespace BytecodeTranslator { var host = new CodeContractAwareHostEnvironment(libPaths != null ? libPaths : Enumerable.Empty, true, true); Host = host; - var modules = new List>(); + var modules = new List(); + var contractExtractors = new Dictionary(); + var pdbReaders = new Dictionary(); foreach (var a in assemblyNames) { var module = host.LoadUnitFrom(a) as IModule; if (module == null || module == Dummy.Module || module == Dummy.Assembly) { @@ -122,7 +124,9 @@ namespace BytecodeTranslator { pdbReader = new PdbReader(pdbStream, host); } module = Decompiler.GetCodeModelFromMetadataModel(host, module, pdbReader) as IModule; - modules.Add(Tuple.Create(module, pdbReader)); + modules.Add(module); + contractExtractors.Add(module, host.GetContractExtractor(module.UnitIdentity)); + pdbReaders.Add(module, pdbReader); } if (stubAssemblies != null) { foreach (var s in stubAssemblies) { @@ -153,7 +157,9 @@ namespace BytecodeTranslator { renamer.targetAssembly = mscorlibAssembly; renamer.originalAssemblyIdentity = mscorlibAssembly.AssemblyIdentity; renamer.RewriteChildren(mutableModule); - modules.Add(Tuple.Create((IModule)mutableModule, pdbReader)); + modules.Add((IModule)mutableModule); + contractExtractors.Add(module, host.GetContractExtractor(module.UnitIdentity)); + pdbReaders.Add(module, pdbReader); } } @@ -162,7 +168,7 @@ namespace BytecodeTranslator { return -1; } - var primaryModule = modules[0].Item1; + var primaryModule = modules[0]; TraverserFactory traverserFactory; if (wholeProgram) @@ -173,20 +179,8 @@ namespace BytecodeTranslator { var sink = new Sink(host, traverserFactory, heapFactory); TranslationHelper.tmpVarCounter = 0; - foreach (var tup in modules) { - - var module = tup.Item1; - var pdbReader = tup.Item2; - - IAssembly/*?*/ assembly = null; - MetadataTraverser translator = traverserFactory.MakeMetadataTraverser(sink, host.GetContractExtractor(module.ModuleIdentity), pdbReader); - assembly = module as IAssembly; - if (assembly != null) - translator.Visit(assembly); - else - translator.Visit(module); - - } + MetadataTraverser translator = traverserFactory.MakeMetadataTraverser(sink, contractExtractors, pdbReaders); + translator.TranslateAssemblies(modules); foreach (ITypeDefinition type in sink.delegateTypeToDelegates.Keys) { CreateDispatchMethod(sink, type); diff --git a/BCT/BytecodeTranslator/TraverserFactory.cs b/BCT/BytecodeTranslator/TraverserFactory.cs index cdc1c8a3..78c818bd 100644 --- a/BCT/BytecodeTranslator/TraverserFactory.cs +++ b/BCT/BytecodeTranslator/TraverserFactory.cs @@ -18,9 +18,11 @@ using Bpl = Microsoft.Boogie; namespace BytecodeTranslator { public abstract class TraverserFactory { - public virtual MetadataTraverser MakeMetadataTraverser(Sink sink, IContractProvider contractProvider, PdbReader/*?*/ pdbReader) + public virtual MetadataTraverser MakeMetadataTraverser(Sink sink, + IDictionary contractProviders, // TODO: remove this parameter? + IDictionary sourceLocationProviders) { - return new MetadataTraverser(sink, pdbReader); + return new MetadataTraverser(sink, sourceLocationProviders); } public virtual StatementTraverser MakeStatementTraverser(Sink sink, PdbReader/*?*/ pdbReader, bool contractContext) { return new StatementTraverser(sink, pdbReader, contractContext); diff --git a/BCT/BytecodeTranslator/WholeProgram.cs b/BCT/BytecodeTranslator/WholeProgram.cs index 92154020..dd00768b 100644 --- a/BCT/BytecodeTranslator/WholeProgram.cs +++ b/BCT/BytecodeTranslator/WholeProgram.cs @@ -16,8 +16,7 @@ namespace BytecodeTranslator { class WholeProgram : TraverserFactory { /// - /// Table to be filled by the metadata traverser when it first gets to an assembly. - /// [TODO: It should be full set of assemblies that are being translated (CUA).] + /// Table to be filled by the metadata traverser before visiting any assemblies. /// /// The table lists the direct supertypes of all type definitions that it encounters during the /// traversal. (But the table is organized so that subTypes[T] is the list of type definitions @@ -25,8 +24,10 @@ namespace BytecodeTranslator { /// readonly public Dictionary> subTypes = new Dictionary>(); - public override MetadataTraverser MakeMetadataTraverser(Sink sink, IContractProvider contractProvider, PdbReader pdbReader) { - return new WholeProgramMetadataSemantics(this, sink, pdbReader); + public override MetadataTraverser MakeMetadataTraverser(Sink sink, + IDictionary contractProviders, // TODO: remove this parameter? + IDictionary pdbReaders) { + return new WholeProgramMetadataSemantics(this, sink, pdbReaders); } public class WholeProgramMetadataSemantics : MetadataTraverser { @@ -34,27 +35,23 @@ namespace BytecodeTranslator { readonly WholeProgram parent; readonly Sink sink; - /// - /// TODO: Need to have this populated before any of the assemblies in the CUA are traversed. - /// - readonly Dictionary codeUnderAnalysis = new Dictionary(); + readonly Dictionary codeUnderAnalysis = new Dictionary(); - public WholeProgramMetadataSemantics(WholeProgram parent, Sink sink, PdbReader/*?*/ pdbReader) - : base(sink, pdbReader) { + public WholeProgramMetadataSemantics(WholeProgram parent, Sink sink, IDictionary pdbReaders) + : base(sink, pdbReaders) { this.parent = parent; this.sink = sink; } - public override void Visit(IAssembly assembly) { - - #region When doing whole-program analysis, traverse the assembly gathering type information - this.codeUnderAnalysis.Add(assembly, true); + public override void TranslateAssemblies(IEnumerable assemblies) { + #region traverse all of the units gathering type information var typeRecorder = new RecordSubtypes(this.parent.subTypes); - typeRecorder.Visit(assembly); + foreach (var a in assemblies) { + this.codeUnderAnalysis.Add(a, true); + typeRecorder.Visit(a); + } #endregion - - base.Visit(assembly); - + base.TranslateAssemblies(assemblies); } class RecordSubtypes : BaseMetadataTraverser { @@ -120,54 +117,18 @@ namespace BytecodeTranslator { return; } - #region Translate In Parameters - - var inexpr = new List(); - #region Create the 'this' argument for the function call - this.Visit(methodCall.ThisArgument); - inexpr.Add(this.TranslatedExpressions.Pop()); - #endregion - - Dictionary p2eMap = new Dictionary(); - IEnumerator penum = resolvedMethod.Parameters.GetEnumerator(); - penum.MoveNext(); - foreach (IExpression exp in methodCall.Arguments) { - if (penum.Current == null) { - throw new TranslationException("More Arguments than Parameters in functioncall"); - } - this.Visit(exp); - Bpl.Expr e = this.TranslatedExpressions.Pop(); - - p2eMap.Add(penum.Current, e); - if (!penum.Current.IsOut) { - inexpr.Add(e); - } - - penum.MoveNext(); - } - #endregion - Bpl.IToken token = methodCall.Token(); - #region Translate Out vars - var outvars = new List(); + List inexpr; + List outvars; + Bpl.IdentifierExpr thisExpr; + List locals; + List args; + Bpl.Expr arrayExpr; + Bpl.Expr indexExpr; + Dictionary toBoxed; + var proc = TranslateArgumentsAndReturnProcedure(token, methodCall.MethodToCall, resolvedMethod, methodCall.IsStaticCall ? null : methodCall.ThisArgument, methodCall.Arguments, out inexpr, out outvars, out thisExpr, out locals, out args, out arrayExpr, out indexExpr, out toBoxed); - foreach (KeyValuePair kvp in p2eMap) { - if (kvp.Key.IsByReference) { - Bpl.IdentifierExpr iexp = kvp.Value as Bpl.IdentifierExpr; - if (iexp == null) { - throw new TranslationException("Trying to pass complex expression as out in functioncall"); - } - outvars.Add(iexp); - } - } - #endregion - - if (methodCall.Type.ResolvedType.TypeCode != PrimitiveTypeCode.Void) { - Bpl.Variable v = this.sink.CreateFreshLocal(methodCall.Type.ResolvedType); - outvars.Add(new Bpl.IdentifierExpr(token, v)); - TranslatedExpressions.Push(new Bpl.IdentifierExpr(token, v)); - } Bpl.QKeyValue attrib = null; foreach (var a in resolvedMethod.Attributes) { @@ -179,7 +140,6 @@ namespace BytecodeTranslator { var elseBranch = new Bpl.StmtListBuilder(); - var proc = this.sink.FindOrCreateProcedure(resolvedMethod); var methodname = proc.Name; Bpl.CallCmd call; diff --git a/BCT/RegressionTests/TranslationTest/GeneralHeapInput.txt b/BCT/RegressionTests/TranslationTest/GeneralHeapInput.txt index aac37bf0..295349c9 100644 --- a/BCT/RegressionTests/TranslationTest/GeneralHeapInput.txt +++ b/BCT/RegressionTests/TranslationTest/GeneralHeapInput.txt @@ -49,7 +49,9 @@ implementation System.Object.GetType(this: Ref) returns ($result: Ref) -axiom (forall t: Type :: $DynamicType($TypeOf(t)) == t); +function $TypeOfInv(Ref) : Type; + +axiom (forall t: Type :: { $TypeOf(t) } $TypeOfInv($TypeOf(t)) == t); function $ThreadDelegate(Ref) : Ref; diff --git a/BCT/RegressionTests/TranslationTest/SplitFieldsHeapInput.txt b/BCT/RegressionTests/TranslationTest/SplitFieldsHeapInput.txt index 73e2000c..a0ba97d5 100644 --- a/BCT/RegressionTests/TranslationTest/SplitFieldsHeapInput.txt +++ b/BCT/RegressionTests/TranslationTest/SplitFieldsHeapInput.txt @@ -51,7 +51,9 @@ implementation System.Object.GetType(this: Ref) returns ($result: Ref) -axiom (forall t: Type :: $DynamicType($TypeOf(t)) == t); +function $TypeOfInv(Ref) : Type; + +axiom (forall t: Type :: { $TypeOf(t) } $TypeOfInv($TypeOf(t)) == t); function $ThreadDelegate(Ref) : Ref; -- cgit v1.2.3