From bb65c2d04081851716ad20776a29b941e852f6b6 Mon Sep 17 00:00:00 2001 From: Jason Koenig Date: Wed, 11 Jul 2012 18:02:13 -0700 Subject: Dafny: restored soundness for refinement by disallowing certain updates and method calls --- Source/Dafny/RefinementTransformer.cs | 133 ++++++++++++++++++++++++++++++---- Source/Dafny/Translator.cs | 21 ++++-- Test/dafny2/StoreAndRetrieve.dfy | 5 +- 3 files changed, 134 insertions(+), 25 deletions(-) diff --git a/Source/Dafny/RefinementTransformer.cs b/Source/Dafny/RefinementTransformer.cs index a5fab242..09e17fe1 100644 --- a/Source/Dafny/RefinementTransformer.cs +++ b/Source/Dafny/RefinementTransformer.cs @@ -1134,10 +1134,11 @@ namespace Microsoft.Dafny { * * var x := E; var x; var x := E; * var x := E; var x := *; var x := E; - * var x := E1; var x :| E0; var x := E1; assert E0; + * var x := E1; var x :| P; var x := E1; assert P; * var VarProduction; var VarProduction; * * x := E; x := *; x := E; + * x := E; x :| P; x := E; assert P; * * if ... Then else Else if (G) Then' else Else' if (G) Merge(Then,Then') else Merge(Else,Else') * if (G) Then else Else if (*) Then' else Else' if (G) Merge(Then,Then') else Merge(Else,Else') @@ -1272,22 +1273,26 @@ namespace Microsoft.Dafny { } else if (cur is VarDeclStmt) { var cNew = (VarDeclStmt)cur; - var cOld = oldS as VarDeclStmt; bool doMerge = false; Expression addedAssert = null; - if (cOld != null && cNew.Lhss.Count == 1 && cOld.Lhss.Count == 1 && cNew.Lhss[0].Name == cOld.Lhss[0].Name) { - var update = cNew.Update as UpdateStmt; - if (update != null && update.Rhss.Count == 1 && update.Rhss[0] is ExprRhs) { - // Note, we allow switching between ghost and non-ghost, since that seems unproblematic. - if (cOld.Update == null) { - doMerge = true; - } else if (cOld.Update is AssignSuchThatStmt) { - doMerge = true; - addedAssert = CloneExpr(((AssignSuchThatStmt)cOld.Update).Expr); - } else { - var updateOld = (UpdateStmt)cOld.Update; // if cast fails, there are more ConcreteUpdateStatement subclasses than expected - if (updateOld.Rhss.Count == 1 && updateOld.Rhss[0] is HavocRhs) { + if (oldS is VarDeclStmt) { + var cOld = (VarDeclStmt)oldS; + if (VarDeclAgree(cOld.Lhss, cNew.Lhss)) { + var update = cNew.Update as UpdateStmt; + if (update != null && update.Rhss.TrueForAll(rhs => !rhs.CanAffectPreviouslyKnownExpressions)) { + // Note, we allow switching between ghost and non-ghost, since that seems unproblematic. + if (cOld.Update == null) { + doMerge = true; + } else if (cOld.Update is AssignSuchThatStmt) { + doMerge = true; + addedAssert = CloneExpr(((AssignSuchThatStmt)cOld.Update).Expr); + } else { + var updateOld = (UpdateStmt)cOld.Update; // if cast fails, there are more ConcreteUpdateStatement subclasses than expected doMerge = true; + foreach (var rhs in updateOld.Rhss) { + if (!(rhs is HavocRhs)) + doMerge = false; + } } } } @@ -1297,7 +1302,7 @@ namespace Microsoft.Dafny { body.Add(cNew); i++; j++; if (addedAssert != null) { - body.Add(new AssertStmt(addedAssert.tok, addedAssert, null)); + body.Add(new AssertStmt(new Translator.ForceCheckToken(addedAssert.tok), addedAssert, null)); } } else { MergeAddStatement(cur, body); @@ -1330,6 +1335,38 @@ namespace Microsoft.Dafny { i++; } + } else if (cur is UpdateStmt) { + var nw = (UpdateStmt)cur; + List stmtGenerated = new List(); + bool doMerge = false; + if (oldS is UpdateStmt) { + var s = (UpdateStmt)oldS; + if (LeftHandSidesAgree(s.Lhss, nw.Lhss)) { + doMerge = true; + stmtGenerated.Add(nw); + foreach(var rhs in s.Rhss) { + if (!(rhs is HavocRhs)) + doMerge = false; + } + } + } else if (oldS is AssignSuchThatStmt) { + var s = (AssignSuchThatStmt)oldS; + if (LeftHandSidesAgree(s.Lhss, nw.Lhss)) { + doMerge = true; + stmtGenerated.Add(nw); + var addedAssert = CloneExpr(s.Expr); + stmtGenerated.Add(new AssertStmt(new Translator.ForceCheckToken(addedAssert.tok), addedAssert, null)); + } + } + if (doMerge) { + // Go ahead with the merge: + Contract.Assert(cce.NonNullElements(stmtGenerated)); + body.AddRange(stmtGenerated); + i++; j++; + } else { + MergeAddStatement(cur, body); + i++; + } } else if (cur is IfStmt) { var cNew = (IfStmt)cur; var cOld = oldS as IfStmt; @@ -1367,6 +1404,30 @@ namespace Microsoft.Dafny { return new BlockStmt(skeleton.Tok, body); } + private bool LeftHandSidesAgree(List old, List nw) { + if (old.Count != nw.Count) + return false; + for (int i = 0; i < old.Count; i++) { + var a = old[i].Resolved as IdentifierExpr; + var b = nw[i] as IdentifierSequence; + if (a != null && b != null) + if (b.Tokens.Count == 1 && b.Arguments == null) + if (a.Name == b.Tokens[0].val) + continue; + return false; + } + return true; + } + private bool VarDeclAgree(List old, List nw) { + if (old.Count != nw.Count) + return false; + for (int i = 0; i < old.Count; i++) { + if (old[i].Name != nw[i].Name) + return false; + } + return true; + } + bool PotentialMatch(Statement nxt, Statement other) { Contract.Requires(!(nxt is SkeletonStatement) || ((SkeletonStatement)nxt).S != null); // nxt is not "...;" Contract.Requires(other != null); @@ -1391,6 +1452,9 @@ namespace Microsoft.Dafny { } else if (nxt is WhileStmt) { var oth = other as WhileStmt; return oth != null && oth.Guard == null; + } else if (nxt is VarDeclStmt) { + var oth = other as VarDeclStmt; + return oth != null && VarDeclAgree(((VarDeclStmt)nxt).Lhss, oth.Lhss); } // not a potential match @@ -1474,7 +1538,6 @@ namespace Microsoft.Dafny { for (LList