From c5387177afc8592177bfbdf7f059b06ce4eac516 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 7 Oct 2024 11:21:02 +0200 Subject: [PATCH] Penetrating by blocks (#5779) ### Description - Make all by block proofs penetrate the entire LHS ### How has this been tested? - Updated tests for call statements and the three types of assignment statements (`:=,` `:-`, `:|`) By submitting this pull request, I confirm that my contribution is made under the terms of the [MIT license](https://github.com/dafny-lang/dafny/blob/master/LICENSE.txt). --- Source/DafnyCore/AST/Expressions/StmtExpr.cs | 33 ++- .../AST/Grammar/Printer/Printer.Statement.cs | 121 ++------- .../DafnyCore/AST/Grammar/Printer/Printer.cs | 4 +- .../AST/Grammar/TokenNewIndentCollector.cs | 2 +- Source/DafnyCore/AST/Members/ConstantField.cs | 6 + Source/DafnyCore/AST/Members/ICodeContext.cs | 13 +- .../Assignment/AssignOrReturnStmt.cs | 21 +- .../Statements/Assignment/AssignStatement.cs | 16 +- .../AST/Statements/BlockByProofStmt.cs | 52 ++++ .../AST/Statements/Methods/CallStmt.cs | 13 +- .../AST/Statements/Verification/AssertStmt.cs | 16 +- .../Statements/Verification/HideRevealStmt.cs | 2 +- Source/DafnyCore/AST/Substituter.cs | 7 +- .../AST/TypeDeclarations/DatatypeDecl.cs | 6 + .../AST/TypeDeclarations/NewtypeDecl.cs | 5 + .../TypeDeclarations/TypeSynonymDeclBase.cs | 11 +- Source/DafnyCore/Dafny.atg | 24 +- Source/DafnyCore/Generic/OrderedDictionary.cs | 50 ++++ .../DafnyCore/Generic/ReporterExtensions.cs | 2 +- Source/DafnyCore/Pipeline/Compilation.cs | 2 +- Source/DafnyCore/ProofDependencyWarnings.cs | 2 - .../Resolver/CheckLocalityVisitor.cs | 45 +++ .../Resolver/GhostInterestVisitor.cs | 31 +-- Source/DafnyCore/Resolver/ModuleResolver.cs | 217 ++++++--------- .../PreType/PreTypeResolve.Statements.cs | 16 +- Source/DafnyCore/Resolver/TailRecursion.cs | 2 + .../Rewriters/RefinementTransformer.cs | 6 +- .../Verifier/BoogieGenerator.BoogieFactory.cs | 4 +- .../Verifier/BoogieGenerator.Decreases.cs | 6 +- .../BoogieGenerator.DefiniteAssignment.cs | 74 ++--- .../BoogieGenerator.ExpressionTranslator.cs | 2 +- .../BoogieGenerator.ExpressionWellformed.cs | 243 +++++++++-------- .../Verifier/BoogieGenerator.Fields.cs | 4 +- ...oogieGenerator.Functions.Wellformedness.cs | 20 +- .../Verifier/BoogieGenerator.Iterators.cs | 33 ++- .../Verifier/BoogieGenerator.Methods.cs | 151 +++++------ .../Verifier/BoogieGenerator.SplitExpr.cs | 56 ++-- .../Verifier/BoogieGenerator.Types.cs | 51 ++-- Source/DafnyCore/Verifier/BoogieGenerator.cs | 256 ++++++------------ .../Verifier/BoogieStmtListBuilder.cs | 4 +- .../BoogieGenerator.DataTypes.cs | 83 +++++- Source/DafnyCore/Verifier/ProofDependency.cs | 8 +- .../Verifier/ProofDependencyManager.cs | 6 +- .../Verifier/ProofObligationDescription.cs | 2 +- .../Statements/BlockByProofStmtVerifier.cs | 24 ++ .../BoogieGenerator.TrAssignment.cs | 51 ++-- .../Statements/BoogieGenerator.TrCall.cs | 85 +++--- .../BoogieGenerator.TrForallStmt.cs | 16 +- .../Statements/BoogieGenerator.TrLoop.cs | 145 +++++----- .../BoogieGenerator.TrPredicateStatement.cs | 44 ++- .../Statements/BoogieGenerator.TrStatement.cs | 116 ++++---- .../Statements/OpaqueBlockVerifier.cs | 23 +- Source/DafnyCore/Verifier/Variables.cs | 9 + .../Handlers/DafnyHoverHandler.cs | 4 - ...licitFailingAssertionCodeActionProvider.cs | 2 +- .../Language/SyntaxTreeVisitor.cs | 1 - .../ImplicitAssertionTest.cs | 3 - Source/DafnyPipeline.Test/TranslatorTest.cs | 8 +- Source/DafnyStandardLibraries/Makefile | 2 +- .../binaries/DafnyStandardLibraries-cs.doo | Bin 1518 -> 1518 bytes .../binaries/DafnyStandardLibraries-go.doo | Bin 1539 -> 1539 bytes .../binaries/DafnyStandardLibraries-java.doo | Bin 1509 -> 1509 bytes .../binaries/DafnyStandardLibraries-js.doo | Bin 2027 -> 2027 bytes .../DafnyStandardLibraries-notarget.doo | Bin 1498 -> 1498 bytes .../binaries/DafnyStandardLibraries-py.doo | Bin 1505 -> 1505 bytes .../binaries/DafnyStandardLibraries.doo | Bin 57213 -> 57213 bytes .../examples/JSON/JSONExamples.dfy | 25 +- .../AddImplementationForCallsRewriter.cs | 3 +- Source/DafnyTestGeneration/TestGenerator.cs | 2 +- Source/DafnyTestGeneration/Utils.cs | 2 +- .../{dafny0 => ast/statement}/AssertBy.dfy | 6 + .../statement}/AssertBy.dfy.expect | 9 +- .../LitTest/ast/statement/assignOrReturn.dfy | 12 + .../ast/statement/assignOrReturn.dfy.expect | 2 + .../LitTest/ast/statement/assignSuchThat.dfy | 8 + .../ast/statement/assignSuchThat.dfy.expect | 2 + .../LitTest/ast/statement/assignment.dfy | 8 + .../ast/statement/assignment.dfy.expect | 2 + .../statement/calls}/CallBy.dfy | 6 + .../statement/calls}/CallBy.dfy.expect | 2 +- .../statement/calls}/CallByHide.dfy | 2 +- .../ast/statement/calls/CallByHide.dfy.expect | 3 + .../statement/calls}/CallByResolution0.dfy | 0 .../calls}/CallByResolution0.dfy.expect | 3 +- .../statement/calls}/CallByResolution1.dfy | 0 .../calls}/CallByResolution1.dfy.expect | 0 .../LitTest/dafny0/CallByHide.dfy.expect | 3 - .../GhostAllocations-Resolution.dfy.expect | 28 +- .../LabeledAssertsResolution.dfy.expect | 28 +- .../LitTest/dafny0/SubsetTypes.dfy.expect | 2 +- .../Snapshots0.run.legacy.dfy.expect | 12 +- .../Snapshots1.run.legacy.dfy.expect | 6 +- .../Snapshots2.run.legacy.dfy.expect | 22 +- .../Snapshots5.run.legacy.dfy.expect | 14 +- .../Snapshots8.run.legacy.dfy.expect | 22 +- .../LitTests/LitTest/dafny1/ListContents.dfy | 1 - .../TestFiles/LitTests/LitTest/dafny4/gcd.dfy | 2 +- 97 files changed, 1281 insertions(+), 1217 deletions(-) create mode 100644 Source/DafnyCore/AST/Statements/BlockByProofStmt.cs create mode 100644 Source/DafnyCore/Generic/OrderedDictionary.cs create mode 100644 Source/DafnyCore/Resolver/CheckLocalityVisitor.cs rename Source/DafnyCore/Verifier/{ => Datatypes}/BoogieGenerator.DataTypes.cs (90%) create mode 100644 Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs create mode 100644 Source/DafnyCore/Verifier/Variables.cs rename Source/IntegrationTests/TestFiles/LitTests/LitTest/{dafny0 => ast/statement}/AssertBy.dfy (83%) rename Source/IntegrationTests/TestFiles/LitTests/LitTest/{dafny0 => ast/statement}/AssertBy.dfy.expect (71%) create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignOrReturn.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignOrReturn.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignSuchThat.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignSuchThat.dfy.expect create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignment.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignment.dfy.expect rename Source/IntegrationTests/TestFiles/LitTests/LitTest/{dafny0 => ast/statement/calls}/CallBy.dfy (93%) rename Source/IntegrationTests/TestFiles/LitTests/LitTest/{dafny0 => ast/statement/calls}/CallBy.dfy.expect (84%) rename Source/IntegrationTests/TestFiles/LitTests/LitTest/{dafny0 => ast/statement/calls}/CallByHide.dfy (73%) create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByHide.dfy.expect rename Source/IntegrationTests/TestFiles/LitTests/LitTest/{dafny0 => ast/statement/calls}/CallByResolution0.dfy (100%) rename Source/IntegrationTests/TestFiles/LitTests/LitTest/{dafny0 => ast/statement/calls}/CallByResolution0.dfy.expect (68%) rename Source/IntegrationTests/TestFiles/LitTests/LitTest/{dafny0 => ast/statement/calls}/CallByResolution1.dfy (100%) rename Source/IntegrationTests/TestFiles/LitTests/LitTest/{dafny0 => ast/statement/calls}/CallByResolution1.dfy.expect (100%) delete mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByHide.dfy.expect diff --git a/Source/DafnyCore/AST/Expressions/StmtExpr.cs b/Source/DafnyCore/AST/Expressions/StmtExpr.cs index 0e0fcdac78e..ff34de75e37 100644 --- a/Source/DafnyCore/AST/Expressions/StmtExpr.cs +++ b/Source/DafnyCore/AST/Expressions/StmtExpr.cs @@ -53,20 +53,25 @@ public override IEnumerable TerminalExpressions { /// S is executed. /// This method should be called only after successful resolution of the expression. /// - public Expression GetSConclusion() { - // this is one place where we actually investigate what kind of statement .S is - if (S is PredicateStmt) { - var s = (PredicateStmt)S; - return s.Expr; - } else if (S is CalcStmt) { - var s = (CalcStmt)S; - return s.Result; - } else if (S is HideRevealStmt) { - return CreateBoolLiteral(tok, true); // one could use the definition axiom or the referenced labeled assertions, but "true" is conservative and much simpler :) - } else if (S is AssignStatement) { - return CreateBoolLiteral(tok, true); // one could use the postcondition of the method, suitably instantiated, but "true" is conservative and much simpler :) - } else { - Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement + public Expression GetStatementConclusion() { + return GetStatementConclusion(S); + } + + private Expression GetStatementConclusion(Statement statement) { + switch (statement) { + // this is one place where we actually investigate what kind of statement .S is + case PredicateStmt stmt: + return stmt.Expr; + case CalcStmt stmt: + return stmt.Result; + case HideRevealStmt: + return CreateBoolLiteral(tok, true); // one could use the definition axiom or the referenced labeled assertions, but "true" is conservative and much simpler :) + case AssignStatement: + return CreateBoolLiteral(tok, true); // one could use the postcondition of the method, suitably instantiated, but "true" is conservative and much simpler :) + case BlockByProofStmt stmt: + return GetStatementConclusion(stmt.Body); + default: + Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement } } diff --git a/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs b/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs index 43d093748d0..faf9c3104e8 100644 --- a/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs +++ b/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs @@ -19,6 +19,10 @@ namespace Microsoft.Dafny { + interface ICanPrint { + void Render(TextWriter wr, Printer printer, int indent); + } + public partial class Printer { /// @@ -26,7 +30,7 @@ public partial class Printer { /// If the statement requires several lines, subsequent lines are indented at "indent". /// No newline is printed after the statement. /// - public void PrintStatement(Statement stmt, int indent) { + public void PrintStatement(Statement stmt, int indent, bool includeSemicolon = true) { Contract.Requires(stmt != null); if (stmt.IsGhost && printMode == PrintModes.NoGhostOrIncludes) { @@ -40,38 +44,13 @@ public void PrintStatement(Statement stmt, int indent) { } } - if (stmt is PredicateStmt) { - if (printMode == PrintModes.NoGhostOrIncludes) { - return; - } - - Expression expr = ((PredicateStmt)stmt).Expr; - var assertStmt = stmt as AssertStmt; - var expectStmt = stmt as ExpectStmt; - wr.Write(assertStmt != null ? "assert" : - expectStmt != null ? "expect" : - "assume"); - if (stmt.Attributes != null) { - PrintAttributes(stmt.Attributes); - } - - wr.Write(" "); - if (assertStmt != null && assertStmt.Label != null) { - wr.Write("{0}: ", assertStmt.Label.Name); - } - - PrintExpression(expr, true); - if (assertStmt != null && assertStmt.Proof != null) { - wr.Write(" by "); - PrintStatement(assertStmt.Proof, indent); - } else if (expectStmt != null && expectStmt.Message != null) { - wr.Write(", "); - PrintExpression(expectStmt.Message, true); - wr.Write(";"); - } else { - wr.Write(";"); - } + if (stmt is ICanPrint canPrint) { + canPrint.Render(wr, this, indent); + return; + } + if (stmt is PredicateStmt) { + PrintPredicateStmt(stmt, includeSemicolon); } else if (stmt is PrintStmt) { PrintStmt s = (PrintStmt)stmt; wr.Write("print"); @@ -88,7 +67,6 @@ public void PrintStatement(Statement stmt, int indent) { for (int i = 0; i < s.BreakAndContinueCount - 1; i++) { wr.Write("break "); } - wr.Write($"{s.Kind};"); } @@ -103,7 +81,6 @@ public void PrintStatement(Statement stmt, int indent) { sep = ", "; } } - wr.Write(";"); } else if (stmt is SingleAssignStmt) { @@ -122,7 +99,6 @@ public void PrintStatement(Statement stmt, int indent) { PrintStatement(s, ind); wr.WriteLine(); } - if (sbs.BodyProper.Count != 0 || sbs.SeparatorTok != null) { Indent(indent + IndentAmount); wr.WriteLine("new;"); @@ -132,7 +108,6 @@ public void PrintStatement(Statement stmt, int indent) { wr.WriteLine(); } } - Indent(indent); wr.Write("}"); @@ -149,7 +124,6 @@ public void PrintStatement(Statement stmt, int indent) { if (s.UsesOptionalBraces) { wr.Write(" {"); } - PrintAlternatives(indent + (s.UsesOptionalBraces ? IndentAmount : 0), s.Alternatives); if (s.UsesOptionalBraces) { wr.WriteLine(); @@ -176,10 +150,8 @@ public void PrintStatement(Statement stmt, int indent) { } else { wr.Write(" "); } - wr.Write("{"); } - Contract.Assert(s.Alternatives.Count != 0); PrintAlternatives(indent + (s.UsesOptionalBraces ? IndentAmount : 0), s.Alternatives); if (s.UsesOptionalBraces) { @@ -198,7 +170,6 @@ public void PrintStatement(Statement stmt, int indent) { foreach (var expr in s.EffectiveEnsuresClauses) { PrintExpression(expr, false, new string(' ', indent + IndentAmount) + "ensures "); } - if (s.Body != null) { wr.WriteLine(); Indent(indent); @@ -209,7 +180,6 @@ public void PrintStatement(Statement stmt, int indent) { wr.Write(" "); PrintQuantifierDomain(s.BoundVars, s.Attributes, s.Range); } - PrintSpec("ensures", s.Ens, indent + IndentAmount); if (s.Body != null) { if (s.Ens.Count == 0) { @@ -220,7 +190,6 @@ public void PrintStatement(Statement stmt, int indent) { } } } - if (s.Body != null) { PrintStatement(s.Body, indent); } @@ -231,10 +200,7 @@ public void PrintStatement(Statement stmt, int indent) { } else if (stmt is CalcStmt) { CalcStmt s = (CalcStmt)stmt; - if (printMode == PrintModes.NoGhostOrIncludes) { - return; - } // Calcs don't get a "ghost" attribute, but they are. - + if (printMode == PrintModes.NoGhostOrIncludes) { return; } // Calcs don't get a "ghost" attribute, but they are. wr.Write("calc"); PrintAttributes(stmt.Attributes); wr.Write(" "); @@ -245,11 +211,9 @@ public void PrintStatement(Statement stmt, int indent) { PrintCalcOp(s.Op); wr.Write(" "); } - wr.WriteLine("{"); int lineInd = indent + IndentAmount; - int lineCount = - s.Lines.Count == 0 ? 0 : s.Lines.Count - 1; // if nonempty, .Lines always contains a duplicated last line + int lineCount = s.Lines.Count == 0 ? 0 : s.Lines.Count - 1; // if nonempty, .Lines always contains a duplicated last line // The number of op/hints is commonly one less than the number of lines, but // it can also equal the number of lines for empty calc's and for calc's with // a dangling hint. @@ -265,14 +229,12 @@ public void PrintStatement(Statement stmt, int indent) { if (i == hintCount) { break; } - // print the operator, if any if (op != null || (options.DafnyPrintResolvedFile != null && s.Op != null)) { Indent(indent); // this lines up with the "calc" PrintCalcOp(op ?? s.Op); wr.WriteLine(); } - // print the hints foreach (var st in h.Body) { Indent(lineInd); @@ -280,7 +242,6 @@ public void PrintStatement(Statement stmt, int indent) { wr.WriteLine(); } } - Indent(indent); wr.Write("}"); } else if (stmt is NestedMatchStmt) { @@ -290,22 +251,18 @@ public void PrintStatement(Statement stmt, int indent) { if (s.Flattened != null && options.DafnyPrintResolvedFile != null) { wr.WriteLine(); if (!printingDesugared) { - Indent(indent); - wr.WriteLine("/*---------- flattened ----------"); + Indent(indent); wr.WriteLine("/*---------- flattened ----------"); } var savedDesugarMode = printingDesugared; printingDesugared = true; - Indent(indent); - PrintStatement(s.Flattened, indent); + Indent(indent); PrintStatement(s.Flattened, indent); wr.WriteLine(); printingDesugared = savedDesugarMode; if (!printingDesugared) { - Indent(indent); - wr.WriteLine("---------- end flattened ----------*/"); + Indent(indent); wr.WriteLine("---------- end flattened ----------*/"); } - Indent(indent); } @@ -317,7 +274,6 @@ public void PrintStatement(Statement stmt, int indent) { if (s.UsesOptionalBraces) { wr.Write(" {"); } - int caseInd = indent + (s.UsesOptionalBraces ? IndentAmount : 0); foreach (NestedMatchCaseStmt mc in s.Cases) { wr.WriteLine(); @@ -333,7 +289,6 @@ public void PrintStatement(Statement stmt, int indent) { PrintStatement(bs, caseInd + IndentAmount); } } - if (s.UsesOptionalBraces) { wr.WriteLine(); Indent(indent); @@ -375,8 +330,9 @@ public void PrintStatement(Statement stmt, int indent) { Indent(indent); wr.Write("}"); } + } else if (stmt is ConcreteAssignStatement concreteAssignStatement) { - PrintConcreteUpdateStatement(concreteAssignStatement, indent); + PrintConcreteUpdateStatement(concreteAssignStatement, indent, includeSemicolon); } else if (stmt is CallStmt) { // Most calls are printed from their concrete syntax given in the input. However, recursive calls to // prefix lemmas end up as CallStmt's by the end of resolution and they may need to be printed here. @@ -386,16 +342,12 @@ public void PrintStatement(Statement stmt, int indent) { } else if (stmt is VarDeclStmt) { var s = (VarDeclStmt)stmt; - if (s.Locals.Exists(v => v.IsGhost) && printMode == PrintModes.NoGhostOrIncludes) { - return; - } - + if (s.Locals.Exists(v => v.IsGhost) && printMode == PrintModes.NoGhostOrIncludes) { return; } if (s.Locals.TrueForAll((v => v.IsGhost))) { // Emit the "ghost" modifier if all of the variables are ghost. If some are ghost, but not others, // then some of these ghosts are auto-converted to ghost, so we should not emit the "ghost" keyword. wr.Write("ghost "); } - wr.Write("var"); string sep = ""; foreach (var local in s.Locals) { @@ -403,28 +355,26 @@ public void PrintStatement(Statement stmt, int indent) { if (local.Attributes != null) { PrintAttributes(local.Attributes); } - wr.Write(" {0}", local.DisplayName); PrintType(": ", local.SyntacticType); sep = ","; } - if (s.Assign != null) { wr.Write(" "); PrintUpdateRHS(s.Assign, indent); } - PrintBy(s.Assign, indent); + if (includeSemicolon) { + wr.Write(";"); + } } else if (stmt is VarDeclPattern) { var s = (VarDeclPattern)stmt; if (s.tok is AutoGeneratedToken) { wr.Write("/* "); } - if (s.HasGhostModifier) { wr.Write("ghost "); } - wr.Write("var "); PrintCasePattern(s.LHS); wr.Write(" := "); @@ -473,26 +423,11 @@ public void PrintStatement(Statement stmt, int indent) { PrintStatement(haltRecoveryStatement.RecoverBody, ind); wr.Write("[[ } ]]"); } else { - Contract.Assert(false); - throw new cce.UnreachableException(); // unexpected statement - } - } - - void PrintBy(ConcreteAssignStatement statement, int indent) { - var proof = statement switch { - AssignStatement updateStmt => updateStmt.Proof, - AssignOrReturnStmt returnStmt => returnStmt.Proof, - _ => null - }; - if (proof != null) { - wr.Write(" by "); - PrintStatement(proof, indent); - } else { - wr.Write(";"); + Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement } } - public void PrintConcreteUpdateStatement(ConcreteAssignStatement stmt, int indent) { + public void PrintConcreteUpdateStatement(ConcreteAssignStatement stmt, int indent, bool includeSemicolon = true) { string sep = ""; foreach (var lhs in stmt.Lhss) { wr.Write(sep); @@ -503,7 +438,9 @@ public void PrintConcreteUpdateStatement(ConcreteAssignStatement stmt, int inden wr.Write(" "); } PrintUpdateRHS(stmt, indent); - PrintBy(stmt, indent); + if (includeSemicolon) { + wr.Write(";"); + } } public void PrintBlockStmt(BlockStmt stmt, int indent) { @@ -773,8 +710,8 @@ void PrintRhs(AssignmentRhs rhs) { PrintType(t.EType); } if (options.DafnyPrintResolvedFile == null && - t.InitDisplay != null && t.ArrayDimensions.Count == 1 && - AutoGeneratedToken.Is(t.ArrayDimensions[0].tok)) { + t.InitDisplay != null && t.ArrayDimensions.Count == 1 && + AutoGeneratedToken.Is(t.ArrayDimensions[0].tok)) { // elide the size wr.Write("[]"); } else { diff --git a/Source/DafnyCore/AST/Grammar/Printer/Printer.cs b/Source/DafnyCore/AST/Grammar/Printer/Printer.cs index 5cd7c07e953..87afafd59a9 100644 --- a/Source/DafnyCore/AST/Grammar/Printer/Printer.cs +++ b/Source/DafnyCore/AST/Grammar/Printer/Printer.cs @@ -1112,7 +1112,9 @@ void PrintFormal(Formal f, bool showNewKeyword) { internal void PrintDecreasesSpec(Specification decs, int indent) { Contract.Requires(decs != null); - if (printMode == PrintModes.NoGhostOrIncludes) { return; } + if (printMode == PrintModes.NoGhostOrIncludes) { + return; + } if (decs.Expressions != null && decs.Expressions.Count != 0) { wr.WriteLine(); Indent(indent); diff --git a/Source/DafnyCore/AST/Grammar/TokenNewIndentCollector.cs b/Source/DafnyCore/AST/Grammar/TokenNewIndentCollector.cs index e12e85c5e81..e1e9a0ee645 100644 --- a/Source/DafnyCore/AST/Grammar/TokenNewIndentCollector.cs +++ b/Source/DafnyCore/AST/Grammar/TokenNewIndentCollector.cs @@ -559,7 +559,7 @@ public bool SetIndentAssertLikeStatement(Statement stmt, int indent) { } } - if (stmt is AssertStmt { Proof: { StartToken: { } startToken } }) { + if (stmt is BlockByProofStmt { Proof: { StartToken: { } startToken } }) { SetOpeningIndentedRegion(startToken, indent); } diff --git a/Source/DafnyCore/AST/Members/ConstantField.cs b/Source/DafnyCore/AST/Members/ConstantField.cs index 2aba49f9ccc..3df0fed129f 100644 --- a/Source/DafnyCore/AST/Members/ConstantField.cs +++ b/Source/DafnyCore/AST/Members/ConstantField.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; @@ -24,6 +25,11 @@ public override bool CanBeRevealed() { return true; } + public bool ContainsHide { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + public new bool IsGhost { get { return this.isGhost; } } public List TypeArgs { get { return new List(); } } public List Ins { get { return new List(); } } diff --git a/Source/DafnyCore/AST/Members/ICodeContext.cs b/Source/DafnyCore/AST/Members/ICodeContext.cs index d01e78f5b6f..78f2b8ad682 100644 --- a/Source/DafnyCore/AST/Members/ICodeContext.cs +++ b/Source/DafnyCore/AST/Members/ICodeContext.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using OmniSharp.Extensions.LanguageServer.Protocol.Models; @@ -8,6 +9,7 @@ namespace Microsoft.Dafny; /// An ICodeContext is an ICallable or a NoContext. /// public interface ICodeContext : IASTVisitorContext { + bool ContainsHide { get; set; } bool IsGhost { get; } List TypeArgs { get; } List Ins { get; } @@ -31,6 +33,11 @@ public CodeContextWrapper(ICodeContext inner, bool isGhostContext) { this.isGhostContext = isGhostContext; } + public bool ContainsHide { + get => inner.ContainsHide; + set => inner.ContainsHide = value; + } + public bool IsGhost => isGhostContext; public List TypeArgs => inner.TypeArgs; public List Ins => inner.Ins; @@ -49,7 +56,6 @@ public static ICodeContext Unwrap(ICodeContext codeContext) { } interface ICodeContainer { - bool ContainsHide { get; set; } } /// @@ -124,6 +130,11 @@ public class NoContext : ICodeContext { public NoContext(ModuleDefinition module) { this.Module = module; } + + public bool ContainsHide { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } bool ICodeContext.IsGhost { get { return true; } } List ICodeContext.TypeArgs { get { return new List(); } } List ICodeContext.Ins { get { return new List(); } } diff --git a/Source/DafnyCore/AST/Statements/Assignment/AssignOrReturnStmt.cs b/Source/DafnyCore/AST/Statements/Assignment/AssignOrReturnStmt.cs index 131904ee327..249f3fed37c 100644 --- a/Source/DafnyCore/AST/Statements/Assignment/AssignOrReturnStmt.cs +++ b/Source/DafnyCore/AST/Statements/Assignment/AssignOrReturnStmt.cs @@ -12,8 +12,7 @@ public class AssignOrReturnStmt : ConcreteAssignStatement, ICloneable Rhss; public readonly AttributedToken KeywordToken; - public readonly BlockStmt Proof; - [FilledInDuringResolution] public readonly List ResolvedStatements = new List(); + [FilledInDuringResolution] public readonly List ResolvedStatements = new(); public override IEnumerable SubStatements => ResolvedStatements; public override IToken Tok { get { @@ -26,7 +25,8 @@ public override IToken Tok { } } - public override IEnumerable Children => ResolvedStatements.Concat(Proof?.Children ?? new List()); + public override IEnumerable Children => ResolvedStatements; + public override IEnumerable PreResolveSubStatements => Enumerable.Empty(); [ContractInvariantMethod] @@ -47,14 +47,13 @@ public AssignOrReturnStmt(Cloner cloner, AssignOrReturnStmt original) : base(clo Rhs = (ExprRhs)cloner.CloneRHS(original.Rhs); Rhss = original.Rhss.ConvertAll(cloner.CloneRHS); KeywordToken = cloner.AttributedTok(original.KeywordToken); - Proof = cloner.CloneBlockStmt(original.Proof); if (cloner.CloneResolvedFields) { ResolvedStatements = original.ResolvedStatements.Select(stmt => cloner.CloneStmt(stmt, false)).ToList(); } } - public AssignOrReturnStmt(RangeToken rangeToken, List lhss, ExprRhs rhs, AttributedToken keywordToken, List rhss, BlockStmt proof = null) + public AssignOrReturnStmt(RangeToken rangeToken, List lhss, ExprRhs rhs, AttributedToken keywordToken, List rhss) : base(rangeToken, lhss) { Contract.Requires(rangeToken != null); Contract.Requires(lhss != null); @@ -64,7 +63,6 @@ public AssignOrReturnStmt(RangeToken rangeToken, List lhss, ExprRhs Rhs = rhs; Rhss = rhss; KeywordToken = keywordToken; - Proof = proof; } public override IEnumerable PreResolveSubExpressions { @@ -196,8 +194,6 @@ public override void Resolve(ModuleResolver resolver, ResolutionContext resoluti return; } - ModuleResolver.ResolveByProof(resolver, Proof, resolutionContext); - Expression lhsExtract = null; if (expectExtract) { if (resolutionContext.CodeContext is Method caller && caller.Outs.Count == 0 && KeywordToken == null) { @@ -293,7 +289,7 @@ private void DesugarElephantStatement(bool expectExtract, Expression lhsExtract, } } // " temp, ... := MethodOrExpression, ...;" - var up = new AssignStatement(RangeToken, lhss2, rhss2, Proof); + var up = new AssignStatement(RangeToken, lhss2, rhss2); if (expectExtract) { up.OriginalInitialLhs = Lhss.Count == 0 ? null : Lhss[0]; } @@ -309,7 +305,7 @@ private void DesugarElephantStatement(bool expectExtract, Expression lhsExtract, } else if (token.val == "assume") { ss = new AssumeStmt(new RangeToken(token, EndToken), notFailureExpr, SystemModuleManager.AxiomAttribute(KeywordToken.Attrs)); } else if (token.val == "assert") { - ss = new AssertStmt(new RangeToken(token, EndToken), notFailureExpr, null, null, KeywordToken.Attrs); + ss = new AssertStmt(new RangeToken(token, EndToken), notFailureExpr, null, KeywordToken.Attrs); } else { Contract.Assert(false, $"Invalid token in :- statement: {token.val}"); } @@ -329,8 +325,7 @@ private void DesugarElephantStatement(bool expectExtract, Expression lhsExtract, new BlockStmt(RangeToken, new List() { new AssignStatement(RangeToken, new List() { ident }, - new List() { new ExprRhs(resolver.VarDotMethod(Tok, temp, "PropagateFailure")) }, - Proof + new List() { new ExprRhs(resolver.VarDotMethod(Tok, temp, "PropagateFailure")) } ), new ReturnStmt(RangeToken, null), }), @@ -346,7 +341,7 @@ private void DesugarElephantStatement(bool expectExtract, Expression lhsExtract, new AssignStatement(RangeToken, new List() { lhsExtract }, new List() { new ExprRhs(resolver.VarDotMethod(Tok, temp, "Extract")) } - , Proof)); + )); // The following check is not necessary, because the ghost mismatch is caught later. // However the error message here is much clearer. var m = resolver.ResolveMember(Tok, firstType, "Extract", out _); diff --git a/Source/DafnyCore/AST/Statements/Assignment/AssignStatement.cs b/Source/DafnyCore/AST/Statements/Assignment/AssignStatement.cs index 6948b76aa79..3e0ac6073eb 100644 --- a/Source/DafnyCore/AST/Statements/Assignment/AssignStatement.cs +++ b/Source/DafnyCore/AST/Statements/Assignment/AssignStatement.cs @@ -11,7 +11,6 @@ public class AssignStatement : ConcreteAssignStatement, ICloneable Rhss; public readonly bool CanMutateKnownState; public Expression OriginalInitialLhs = null; - public readonly BlockStmt Proof; public override IToken Tok { get { @@ -26,7 +25,7 @@ public override IToken Tok { } [FilledInDuringResolution] public List ResolvedStatements; - public override IEnumerable SubStatements => Children.OfType().Concat(Proof != null ? Proof.SubStatements : new List()); + public override IEnumerable SubStatements => Children.OfType(); public override IEnumerable NonSpecificationSubExpressions => ResolvedStatements == null ? Rhss.SelectMany(r => r.NonSpecificationSubExpressions) : Enumerable.Empty(); @@ -49,30 +48,26 @@ public AssignStatement Clone(Cloner cloner) { public AssignStatement(Cloner cloner, AssignStatement original) : base(cloner, original) { Rhss = original.Rhss.Select(cloner.CloneRHS).ToList(); CanMutateKnownState = original.CanMutateKnownState; - Proof = cloner.CloneBlockStmt(original.Proof); if (cloner.CloneResolvedFields) { ResolvedStatements = original.ResolvedStatements.Select(stmt => cloner.CloneStmt(stmt, false)).ToList(); } } - public AssignStatement(RangeToken rangeToken, List lhss, List rhss, BlockStmt proof = null) + public AssignStatement(RangeToken rangeToken, List lhss, List rhss) : base(rangeToken, lhss) { Contract.Requires(cce.NonNullElements(lhss)); Contract.Requires(cce.NonNullElements(rhss)); Contract.Requires(lhss.Count != 0 || rhss.Count == 1); Rhss = rhss; CanMutateKnownState = false; - Proof = proof; } - - public AssignStatement(RangeToken rangeToken, List lhss, List rhss, bool mutate, BlockStmt proof = null) + public AssignStatement(RangeToken rangeToken, List lhss, List rhss, bool mutate) : base(rangeToken, lhss) { Contract.Requires(cce.NonNullElements(lhss)); Contract.Requires(cce.NonNullElements(rhss)); Contract.Requires(lhss.Count != 0 || rhss.Count == 1); Rhss = rhss; CanMutateKnownState = mutate; - Proof = proof; } public override IEnumerable PreResolveSubExpressions { @@ -131,9 +126,6 @@ public override void Resolve(ModuleResolver resolver, ResolutionContext resoluti resolver.ResolveAttributes(rhs, resolutionContext); } - // resolve proof - ModuleResolver.ResolveByProof(resolver, Proof, resolutionContext); - // figure out what kind of UpdateStmt this is if (firstEffectfulRhs == null) { if (Lhss.Count == 0) { @@ -189,7 +181,7 @@ public override void Resolve(ModuleResolver resolver, ResolutionContext resoluti foreach (var ll in Lhss) { resolvedLhss.Add(ll.Resolved); } - CallStmt a = new CallStmt(RangeToken, resolvedLhss, methodCallInfo.Callee, methodCallInfo.ActualParameters, methodCallInfo.Tok, Proof); + CallStmt a = new CallStmt(RangeToken, resolvedLhss, methodCallInfo.Callee, methodCallInfo.ActualParameters, methodCallInfo.Tok); a.OriginalInitialLhs = OriginalInitialLhs; ResolvedStatements.Add(a); } diff --git a/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs b/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs new file mode 100644 index 00000000000..1d90222c84b --- /dev/null +++ b/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Microsoft.Dafny; + +public class BlockByProofStmt : Statement, ICanResolveNewAndOld, ICanPrint, ICloneable { + + public Statement Body { get; } + public BlockStmt Proof { get; } + public BlockByProofStmt(RangeToken range, BlockStmt proof, Statement body) : base(range) { + Proof = proof; + Body = body; + } + public BlockByProofStmt(Cloner cloner, BlockByProofStmt original) : base(cloner, original) { + Proof = cloner.CloneBlockStmt(original.Proof); + Body = cloner.CloneStmt(original.Body, false); + } + + public override IEnumerable SubStatements => new[] { Body, Proof }; + + public override void GenResolve(INewOrOldResolver resolver, ResolutionContext resolutionContext) { + resolver.ResolveStatement(Body, resolutionContext); + ResolveByProof(resolver, Proof, resolutionContext); + base.GenResolve(resolver, resolutionContext); + } + + internal static void ResolveByProof(INewOrOldResolver resolver, BlockStmt proof, ResolutionContext resolutionContext) { + if (proof == null) { + return; + } + + // clear the labels for the duration of checking the proof body, because break statements are not allowed to leave the proof body + var prevLblStmts = resolver.EnclosingStatementLabels; + var prevLoopStack = resolver.LoopStack; + resolver.EnclosingStatementLabels = new Scope(resolver.Options); + resolver.LoopStack = new List(); + resolver.ResolveStatement(proof, resolutionContext); + resolver.EnclosingStatementLabels = prevLblStmts; + resolver.LoopStack = prevLoopStack; + } + + public void Render(TextWriter wr, Printer printer, int indent) { + printer.PrintStatement(Body, indent, false); + wr.Write(" by "); + printer.PrintBlockStmt(Proof, indent); + } + + public Statement Clone(Cloner cloner) { + return new BlockByProofStmt(cloner, this); + } +} \ No newline at end of file diff --git a/Source/DafnyCore/AST/Statements/Methods/CallStmt.cs b/Source/DafnyCore/AST/Statements/Methods/CallStmt.cs index 22b17ce7c8a..ef7e40c2afa 100644 --- a/Source/DafnyCore/AST/Statements/Methods/CallStmt.cs +++ b/Source/DafnyCore/AST/Statements/Methods/CallStmt.cs @@ -29,12 +29,11 @@ public override IEnumerable GetAssignedLocals() { public readonly ActualBindings Bindings; public List Args => Bindings.Arguments; public Expression OriginalInitialLhs = null; - public readonly BlockStmt Proof; - public Expression Receiver { get { return MethodSelect.Obj; } } - public Method Method { get { return (Method)MethodSelect.Member; } } + public Expression Receiver => MethodSelect.Obj; + public Method Method => (Method)MethodSelect.Member; - public CallStmt(RangeToken rangeToken, List lhs, MemberSelectExpr memSel, List args, IToken overrideToken = null, BlockStmt proof = null) + public CallStmt(RangeToken rangeToken, List lhs, MemberSelectExpr memSel, List args, IToken overrideToken = null) : base(rangeToken) { Contract.Requires(rangeToken != null); Contract.Requires(cce.NonNullElements(lhs)); @@ -46,7 +45,6 @@ public CallStmt(RangeToken rangeToken, List lhs, MemberSelectExpr me this.MethodSelect = memSel; this.overrideToken = overrideToken; this.Bindings = new ActualBindings(args); - Proof = proof; } public CallStmt Clone(Cloner cloner) { @@ -58,15 +56,14 @@ public CallStmt(Cloner cloner, CallStmt original) : base(cloner, original) { Lhs = original.Lhs.Select(cloner.CloneExpr).ToList(); Bindings = new ActualBindings(cloner, original.Bindings); overrideToken = original.overrideToken; - Proof = cloner.CloneBlockStmt(original.Proof); } /// /// This constructor is intended to be used when constructing a resolved CallStmt. The "args" are expected /// to be already resolved, and are all given positionally. /// - public CallStmt(RangeToken rangeToken, List lhs, MemberSelectExpr memSel, List args, BlockStmt proof = null) - : this(rangeToken, lhs, memSel, args.ConvertAll(e => new ActualBinding(null, e)), proof: proof) { + public CallStmt(RangeToken rangeToken, List lhs, MemberSelectExpr memSel, List args) + : this(rangeToken, lhs, memSel, args.ConvertAll(e => new ActualBinding(null, e))) { Bindings.AcceptArgumentExpressionsAsExactParameterList(); } diff --git a/Source/DafnyCore/AST/Statements/Verification/AssertStmt.cs b/Source/DafnyCore/AST/Statements/Verification/AssertStmt.cs index 306f1d4815b..db48548e9e9 100644 --- a/Source/DafnyCore/AST/Statements/Verification/AssertStmt.cs +++ b/Source/DafnyCore/AST/Statements/Verification/AssertStmt.cs @@ -5,7 +5,6 @@ namespace Microsoft.Dafny; public class AssertStmt : PredicateStmt, ICloneable, ICanFormat { - public readonly BlockStmt Proof; public readonly AssertLabel Label; public AssertStmt Clone(Cloner cloner) { @@ -13,7 +12,6 @@ public AssertStmt Clone(Cloner cloner) { } public AssertStmt(Cloner cloner, AssertStmt original) : base(cloner, original) { - Proof = cloner.CloneBlockStmt(original.Proof); Label = original.Label == null ? null : new AssertLabel(cloner.Tok(original.Label.Tok), original.Label.Name); } @@ -22,26 +20,18 @@ public static AssertStmt CreateErrorAssert(INode node, string message, Expressio errorMessage.Type = new SeqType(Type.Char); var attr = new Attributes("error", new List { errorMessage }, null); guard ??= Expression.CreateBoolLiteral(node.Tok, false); - var assertFalse = new AssertStmt(node.RangeToken, guard, null, null, attr); + var assertFalse = new AssertStmt(node.RangeToken, guard, null, attr); assertFalse.IsGhost = true; return assertFalse; } - public AssertStmt(RangeToken rangeToken, Expression expr, BlockStmt/*?*/ proof, AssertLabel/*?*/ label, Attributes attrs) + public AssertStmt(RangeToken rangeToken, Expression expr, AssertLabel/*?*/ label, Attributes attrs) : base(rangeToken, expr, attrs) { Contract.Requires(rangeToken != null); Contract.Requires(expr != null); - Proof = proof; Label = label; } - public override IEnumerable SubStatements { - get { - if (Proof != null) { - yield return Proof; - } - } - } public void AddCustomizedErrorMessage(IToken tok, string s) { var args = new List() { new StringLiteralExpr(tok, s, true) }; IToken openBrace = tok; @@ -88,8 +78,6 @@ public override void GenResolve(INewOrOldResolver resolver, ResolutionContext co } base.GenResolve(resolver, context); - - ModuleResolver.ResolveByProof(resolver, Proof, context); } public bool HasAssertOnlyAttribute(out AssertOnlyKind assertOnlyKind) { diff --git a/Source/DafnyCore/AST/Statements/Verification/HideRevealStmt.cs b/Source/DafnyCore/AST/Statements/Verification/HideRevealStmt.cs index 63e5eb3937b..dd0ec23b214 100644 --- a/Source/DafnyCore/AST/Statements/Verification/HideRevealStmt.cs +++ b/Source/DafnyCore/AST/Statements/Verification/HideRevealStmt.cs @@ -73,7 +73,7 @@ public bool SetIndent(int indentBefore, TokenNewIndentCollector formatter) { } public override void GenResolve(INewOrOldResolver resolver, ResolutionContext resolutionContext) { - ((ICodeContainer)resolutionContext.CodeContext).ContainsHide |= Mode == HideRevealCmd.Modes.Hide; + resolutionContext.CodeContext.ContainsHide |= Mode == HideRevealCmd.Modes.Hide; if (Wildcard) { return; diff --git a/Source/DafnyCore/AST/Substituter.cs b/Source/DafnyCore/AST/Substituter.cs index e939024d885..3ec9bbae469 100644 --- a/Source/DafnyCore/AST/Substituter.cs +++ b/Source/DafnyCore/AST/Substituter.cs @@ -766,7 +766,7 @@ protected virtual Statement SubstStmt(Statement stmt) { return null; } else if (stmt is AssertStmt) { var s = (AssertStmt)stmt; - r = new AssertStmt(s.RangeToken, Substitute(s.Expr), SubstBlockStmt(s.Proof), s.Label, SubstAttributes(s.Attributes)); + r = new AssertStmt(s.RangeToken, Substitute(s.Expr), s.Label, SubstAttributes(s.Attributes)); } else if (stmt is ExpectStmt) { var s = (ExpectStmt)stmt; r = new ExpectStmt(s.RangeToken, Substitute(s.Expr), Substitute(s.Message), SubstAttributes(s.Attributes)); @@ -879,6 +879,11 @@ protected virtual Statement SubstStmt(Statement stmt) { rr.ResolvedStatements.AddRange(revealStmt.ResolvedStatements.ConvertAll(SubstStmt)); rr.OffsetMembers = revealStmt.OffsetMembers.ToList(); r = rr; + } else if (stmt is BlockByProofStmt blockByProofStmt) { + var rr = new BlockByProofStmt(blockByProofStmt.RangeToken, + (BlockStmt)SubstStmt(blockByProofStmt.Proof), + SubstStmt(blockByProofStmt.Body)); + r = rr; } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement } diff --git a/Source/DafnyCore/AST/TypeDeclarations/DatatypeDecl.cs b/Source/DafnyCore/AST/TypeDeclarations/DatatypeDecl.cs index e6cb1c6facc..81daff7479f 100644 --- a/Source/DafnyCore/AST/TypeDeclarations/DatatypeDecl.cs +++ b/Source/DafnyCore/AST/TypeDeclarations/DatatypeDecl.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; @@ -49,6 +50,11 @@ public bool IsRecordType { public TopLevelDecl AsTopLevelDecl => this; public TypeDeclSynonymInfo SynonymInfo { get; set; } + public bool ContainsHide { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + bool ICodeContext.IsGhost { get { return true; } } List ICodeContext.TypeArgs { get { return TypeArgs; } } List ICodeContext.Ins { get { return new List(); } } diff --git a/Source/DafnyCore/AST/TypeDeclarations/NewtypeDecl.cs b/Source/DafnyCore/AST/TypeDeclarations/NewtypeDecl.cs index ba9804f4edd..fb3bd7156dd 100644 --- a/Source/DafnyCore/AST/TypeDeclarations/NewtypeDecl.cs +++ b/Source/DafnyCore/AST/TypeDeclarations/NewtypeDecl.cs @@ -116,6 +116,11 @@ public TypeParameter.EqualitySupportValue EqualitySupport { Expression RedirectingTypeDecl.Witness { get { return Witness; } } VerificationIdGenerator RedirectingTypeDecl.IdGenerator { get { return IdGenerator; } } + public bool ContainsHide { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + bool ICodeContext.IsGhost { get { throw new NotSupportedException(); } // if .IsGhost is needed, the object should always be wrapped in an CodeContextWrapper } diff --git a/Source/DafnyCore/AST/TypeDeclarations/TypeSynonymDeclBase.cs b/Source/DafnyCore/AST/TypeDeclarations/TypeSynonymDeclBase.cs index 3466a7113f6..b7b2ecc40ce 100644 --- a/Source/DafnyCore/AST/TypeDeclarations/TypeSynonymDeclBase.cs +++ b/Source/DafnyCore/AST/TypeDeclarations/TypeSynonymDeclBase.cs @@ -77,11 +77,14 @@ bool RedirectingTypeDecl.ConstraintIsCompilable { Expression RedirectingTypeDecl.Witness { get { return null; } } VerificationIdGenerator RedirectingTypeDecl.IdGenerator { get { return IdGenerator; } } - bool ICodeContext.IsGhost { - get { throw new NotSupportedException(); } // if .IsGhost is needed, the object should always be wrapped in an CodeContextWrapper + public bool ContainsHide { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); } - List ICodeContext.TypeArgs { get { return TypeArgs; } } - List ICodeContext.Ins { get { return new List(); } } + + bool ICodeContext.IsGhost => throw new NotSupportedException(); // if .IsGhost is needed, the object should always be wrapped in an CodeContextWrapper + List ICodeContext.TypeArgs => TypeArgs; + List ICodeContext.Ins => new(); ModuleDefinition IASTVisitorContext.EnclosingModule { get { return EnclosingModuleDefinition; } } bool ICodeContext.MustReverify { get { return false; } } bool ICodeContext.AllowsNontermination { get { return false; } } diff --git a/Source/DafnyCore/Dafny.atg b/Source/DafnyCore/Dafny.atg index 37ad2678c71..e41632969e0 100644 --- a/Source/DafnyCore/Dafny.atg +++ b/Source/DafnyCore/Dafny.atg @@ -2395,12 +2395,18 @@ AssignStatement if (suchThat != null) { s = new AssignSuchThatStmt(rangeToken, lhss, suchThat, suchThatAssume, null); } else if (exceptionRhs != null) { - s = new AssignOrReturnStmt(rangeToken, lhss, exceptionRhs, keywordToken, rhss, proof); + s = new AssignOrReturnStmt(rangeToken, lhss, exceptionRhs, keywordToken, rhss); + if (proof != null) { + s = new BlockByProofStmt(rangeToken, proof, s); + } } else { if (lhss.Count == 0 && rhss.Count == 0) { s = new BlockStmt(rangeToken, new List()); // error, give empty statement } else { - s = new AssignStatement(rangeToken, lhss, rhss, proof); + s = new AssignStatement(rangeToken, lhss, rhss); + if (proof != null) { + s = new BlockByProofStmt(rangeToken, proof, s); + } } } .) @@ -2557,13 +2563,16 @@ VarDeclStatement<.out Statement/*!*/ s.> if (suchThat != null) { assignment = new AssignSuchThatStmt(updateRangeToken, lhsExprs, suchThat, suchThatAssume, attrs); } else if (exceptionRhs != null) { - assignment = new AssignOrReturnStmt(updateRangeToken, lhsExprs, exceptionRhs, keywordToken, rhss, proof); + assignment = new AssignOrReturnStmt(updateRangeToken, lhsExprs, exceptionRhs, keywordToken, rhss); } else if (rhss.Count == 0) { assignment = null; } else { - assignment = new AssignStatement(updateRangeToken, lhsExprs, rhss, proof); + assignment = new AssignStatement(updateRangeToken, lhsExprs, rhss); } s = new VarDeclStmt(rangeToken, lhss, assignment); + if (proof != null) { + s = new BlockByProofStmt(s.RangeToken, proof, s); + } .) | (. CasePattern pat; Expression e = dummyExpr; @@ -2980,10 +2989,13 @@ AssertStmt ";" ) (. if (dotdotdot != null) { - s = new AssertStmt(new RangeToken(startToken, t), new LiteralExpr(x, true), null, null, attrs); + s = new AssertStmt(new RangeToken(startToken, t), new LiteralExpr(x, true), null, attrs); s = new SkeletonStatement(s, dotdotdot, null); } else { - s = new AssertStmt(new RangeToken(startToken, t), e, proof, lbl == null ? null : new AssertLabel(lbl, lbl.val), attrs); + s = new AssertStmt(new RangeToken(startToken, t), e, lbl == null ? null : new AssertLabel(lbl, lbl.val), attrs); + if (proof != null) { + s = new BlockByProofStmt(s.RangeToken, proof, s); + } } .) . diff --git a/Source/DafnyCore/Generic/OrderedDictionary.cs b/Source/DafnyCore/Generic/OrderedDictionary.cs new file mode 100644 index 00000000000..2f6d72621c5 --- /dev/null +++ b/Source/DafnyCore/Generic/OrderedDictionary.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Dafny; + +public class OrderedDictionary { + private readonly Dictionary keyToValue = new(); + private readonly List keyOrder = new(); + private readonly Func getKey; + + public OrderedDictionary(Func getKey) { + this.getKey = getKey; + } + public IEnumerable Values => keyOrder.Select(key => keyToValue[key]); + + public void AddRange(IEnumerable values) { + foreach (var value in values) { + Add(value); + } + } + + public TValue GetOrAdd(TValue value) { + var key = getKey(value); + return GetOrCreate(key, () => value); + } + + public TValue GetOrCreate(TKey key, Func createValue) { + if (keyToValue.TryGetValue(key, out var result)) { + return result; + } + + result = createValue(); + keyToValue[key] = result; + keyOrder.Add(key); + return result; + } + + public void Add(TValue value) { + var key = getKey(value); + keyOrder.Add(key); + keyToValue[key] = value; + } + + public TValue GetValueOrDefault(TKey key) { + return keyToValue.GetValueOrDefault(key); + } + + public int Count => keyOrder.Count; +} \ No newline at end of file diff --git a/Source/DafnyCore/Generic/ReporterExtensions.cs b/Source/DafnyCore/Generic/ReporterExtensions.cs index 1a3efcdc2e0..c36a5f234b4 100644 --- a/Source/DafnyCore/Generic/ReporterExtensions.cs +++ b/Source/DafnyCore/Generic/ReporterExtensions.cs @@ -47,7 +47,7 @@ public static void ReportBoogieError(this ErrorReporter reporter, ErrorInformati public const string RelatedLocationMessage = RelatedLocationCategory; private const string RelatedMessageCategory = "Related message"; public const string AssertedExprCategory = "Asserted expression"; - public static readonly string PostConditionFailingMessage = new ProofObligationDescription.EnsuresDescription(null, null, null).FailureDescription; + public static readonly string PostConditionFailingMessage = new EnsuresDescription(null, null, null).FailureDescription; private static string FormatRelated(string related) { return $"Could not prove: {related}"; } diff --git a/Source/DafnyCore/Pipeline/Compilation.cs b/Source/DafnyCore/Pipeline/Compilation.cs index 82a0c1734ee..bbcd676e88e 100644 --- a/Source/DafnyCore/Pipeline/Compilation.cs +++ b/Source/DafnyCore/Pipeline/Compilation.cs @@ -541,7 +541,7 @@ private static void AddAssertedExprToCounterExampleErrorInfo( throw new ArgumentOutOfRangeException($"Unexpected ErrorKind: {errorInformation.Kind}"); } - if (boogieProofObligationDesc is ProofObligationDescription.ProofObligationDescription dafnyProofObligationDesc) { + if (boogieProofObligationDesc is ProofObligationDescription dafnyProofObligationDesc) { var expr = dafnyProofObligationDesc.GetAssertedExpr(options); string? msg = null; if (expr != null) { diff --git a/Source/DafnyCore/ProofDependencyWarnings.cs b/Source/DafnyCore/ProofDependencyWarnings.cs index 319302df28f..31e8f8caddc 100644 --- a/Source/DafnyCore/ProofDependencyWarnings.cs +++ b/Source/DafnyCore/ProofDependencyWarnings.cs @@ -1,9 +1,7 @@ -using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using DafnyCore.Verifier; using Microsoft.Boogie; -using Microsoft.Dafny.ProofObligationDescription; using VC; namespace Microsoft.Dafny; diff --git a/Source/DafnyCore/Resolver/CheckLocalityVisitor.cs b/Source/DafnyCore/Resolver/CheckLocalityVisitor.cs new file mode 100644 index 00000000000..e4c26870e88 --- /dev/null +++ b/Source/DafnyCore/Resolver/CheckLocalityVisitor.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; + +namespace Microsoft.Dafny; + +class CheckLocalityVisitor : ResolverBottomUpVisitor { + readonly ICodeContext codeContext; + public CheckLocalityVisitor(ModuleResolver resolver, ICodeContext codeContext) + : base(resolver) { + Contract.Requires(resolver != null); + Contract.Requires(codeContext != null); + this.codeContext = codeContext; + } + protected override void VisitOneExpr(Expression expr) { + if (expr is StmtExpr) { + var e = (StmtExpr)expr; + resolver.ComputeGhostInterest(e.S, true, "a statement expression", codeContext); + } else if (expr is LetExpr) { + var e = (LetExpr)expr; + if (codeContext.IsGhost) { + foreach (var bv in e.BoundVars) { + bv.MakeGhost(); + } + } + } + } + + protected override void VisitOneStmt(Statement stmt) { + switch (stmt) { + case CalcStmt calc: { + foreach (var h in calc.Hints) { + resolver.CheckLocalityUpdates(h, new HashSet(), "a hint"); + } + + break; + } + case BlockByProofStmt blockByProofStmt: + resolver.CheckLocalityUpdates(blockByProofStmt.Proof, new HashSet(), "a by block"); + break; + case ForallStmt { Body: not null } forall: + resolver.CheckLocalityUpdates(forall.Body, new HashSet(), "a forall statement"); + break; + } + } +} \ No newline at end of file diff --git a/Source/DafnyCore/Resolver/GhostInterestVisitor.cs b/Source/DafnyCore/Resolver/GhostInterestVisitor.cs index dfccb3ec3e6..985bc783d19 100644 --- a/Source/DafnyCore/Resolver/GhostInterestVisitor.cs +++ b/Source/DafnyCore/Resolver/GhostInterestVisitor.cs @@ -75,26 +75,18 @@ public void Visit(Statement stmt, bool mustBeErasable, [CanBeNull] string proofC Contract.Assume(mustBeErasable || proofContext == null); // (this is really a precondition) !mustBeErasable ==> proofContext == null switch (stmt) { - case AssertStmt: - case AssumeStmt: { - stmt.IsGhost = true; - var assertStmt = stmt as AssertStmt; - if (assertStmt != null && assertStmt.Proof != null) { - Visit(assertStmt.Proof, true, "an assert-by body"); - } - - break; - } + case AssertStmt or AssumeStmt: + stmt.IsGhost = true; + break; case ExpectStmt expectStmt: { expectStmt.IsGhost = false; - var s = expectStmt; if (mustBeErasable) { Error(ErrorId.r_expect_statement_is_not_ghost, expectStmt, "expect statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); } else { - ExpressionTester.CheckIsCompilable(resolver, reporter, s.Expr, codeContext); + ExpressionTester.CheckIsCompilable(resolver, reporter, expectStmt.Expr, codeContext); // If not provided, the message is populated with a default value in resolution - Contract.Assert(s.Message != null); - ExpressionTester.CheckIsCompilable(resolver, reporter, s.Message, codeContext); + Contract.Assert(expectStmt.Message != null); + ExpressionTester.CheckIsCompilable(resolver, reporter, expectStmt.Message, codeContext); } break; @@ -160,10 +152,6 @@ public void Visit(Statement stmt, bool mustBeErasable, [CanBeNull] string proofC var s = statement; s.ResolvedStatements.ForEach(ss => Visit(ss, mustBeErasable, proofContext)); s.IsGhost = s.ResolvedStatements.All(ss => ss.IsGhost); - if (s.Proof != null) { - Visit(s.Proof, true, "a call-by body"); - } - break; } case AssignOrReturnStmt returnStmt: { @@ -534,6 +522,13 @@ public void Visit(Statement stmt, bool mustBeErasable, [CanBeNull] string proofC break; } + case BlockByProofStmt blockByProofStmt: + blockByProofStmt.IsGhost = mustBeErasable; // set .IsGhost before descending into substatements (since substatements may do a 'break' out of this block) + Visit(blockByProofStmt.Body, mustBeErasable, proofContext); + blockByProofStmt.IsGhost = blockByProofStmt.IsGhost || blockByProofStmt.Body.IsGhost; + + Visit(blockByProofStmt.Proof, true, "a by block"); + break; default: Contract.Assert(false); throw new cce.UnreachableException(); } diff --git a/Source/DafnyCore/Resolver/ModuleResolver.cs b/Source/DafnyCore/Resolver/ModuleResolver.cs index 6df5431e8b2..249f82d2967 100644 --- a/Source/DafnyCore/Resolver/ModuleResolver.cs +++ b/Source/DafnyCore/Resolver/ModuleResolver.cs @@ -1221,81 +1221,8 @@ public void ResolveTopLevelDecls_Core(List declarations, } } - // Compute ghost interests, figure out native types, check agreement among datatype destructors, and determine tail calls. if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { - foreach (TopLevelDecl d in declarations) { - if (d is IteratorDecl) { - var iter = (IteratorDecl)d; - iter.SubExpressions.ForEach(e => CheckExpression(e, this, iter)); - if (iter.Body != null) { - CheckExpression(iter.Body, this, iter); - } - - } else if (d is SubsetTypeDecl subsetTypeDecl) { - Contract.Assert(subsetTypeDecl.Constraint != null); - CheckExpression(subsetTypeDecl.Constraint, this, new CodeContextWrapper(subsetTypeDecl, true)); - - if (subsetTypeDecl.Witness != null) { - CheckExpression(subsetTypeDecl.Witness, this, - new CodeContextWrapper(subsetTypeDecl, subsetTypeDecl.WitnessKind == SubsetTypeDecl.WKind.Ghost)); - if (subsetTypeDecl.WitnessKind == SubsetTypeDecl.WKind.Compiled) { - var codeContext = new CodeContextWrapper(subsetTypeDecl, subsetTypeDecl.WitnessKind == SubsetTypeDecl.WKind.Ghost); - ExpressionTester.CheckIsCompilable(Options, this, subsetTypeDecl.Witness, codeContext); - } - } - - } else if (d is NewtypeDecl newtypeDecl) { - if (newtypeDecl.Var != null) { - Contract.Assert(newtypeDecl.Constraint != null); - CheckExpression(newtypeDecl.Constraint, this, new CodeContextWrapper(newtypeDecl, true)); - } - - if (newtypeDecl.Witness != null) { - CheckExpression(newtypeDecl.Witness, this, new CodeContextWrapper(newtypeDecl, newtypeDecl.WitnessKind == SubsetTypeDecl.WKind.Ghost)); - if (newtypeDecl.WitnessKind == SubsetTypeDecl.WKind.Compiled) { - var codeContext = new CodeContextWrapper(newtypeDecl, newtypeDecl.WitnessKind == SubsetTypeDecl.WKind.Ghost); - ExpressionTester.CheckIsCompilable(Options, this, newtypeDecl.Witness, codeContext); - } - } - - new NativeTypeAnalysis(reporter).FigureOutNativeType(newtypeDecl, Options); - - } else if (d is DatatypeDecl) { - var dd = (DatatypeDecl)d; - foreach (var member in GetClassMembers(dd)!.Values) { - var dtor = member as DatatypeDestructor; - if (dtor != null) { - var rolemodel = dtor.CorrespondingFormals[0]; - for (int i = 1; i < dtor.CorrespondingFormals.Count; i++) { - var other = dtor.CorrespondingFormals[i]; - if (rolemodel.IsGhost != other.IsGhost) { - reporter.Error(MessageSource.Resolver, other, - "shared destructors must agree on whether or not they are ghost, but '{0}' is {1} in constructor '{2}' and {3} in constructor '{4}'", - rolemodel.Name, - rolemodel.IsGhost ? "ghost" : "non-ghost", dtor.EnclosingCtors[0].Name, - other.IsGhost ? "ghost" : "non-ghost", dtor.EnclosingCtors[i].Name); - } - } - } - } - foreach (var ctor in dd.Ctors) { - CheckParameterDefaultValuesAreCompilable(ctor.Formals, dd); - } - } - } - - AnalyzeTypeConstraints.AssignConstraintIsCompilable(declarations, Options); - - // Now that we have filled in the .ConstraintIsCompilable field of all subset types and newtypes, we're ready to - // visit iterator bodies and members (which will make calls to CheckIsCompilable). - foreach (TopLevelDecl d in declarations) { - if (d is IteratorDecl { Body: { } iterBody } iter) { - ComputeGhostInterest(iter.Body, false, null, iter); - } - if (d is TopLevelDeclWithMembers cl) { - ResolveClassMembers_Pass1(cl); - } - } + ComputeGhostInterestAndMisc(declarations); } // ---------------------------------- Pass 2 ---------------------------------- @@ -1639,6 +1566,85 @@ public void ResolveTopLevelDecls_Core(List declarations, } } + /// + /// Compute ghost interests, figure out native types, check agreement among datatype destructors, and determine tail calls. + /// + private void ComputeGhostInterestAndMisc(List declarations) { + foreach (TopLevelDecl d in declarations) { + if (d is IteratorDecl) { + var iter = (IteratorDecl)d; + iter.SubExpressions.ForEach(e => CheckExpression(e, this, iter)); + if (iter.Body != null) { + CheckExpression(iter.Body, this, iter); + } + + } else if (d is SubsetTypeDecl subsetTypeDecl) { + Contract.Assert(subsetTypeDecl.Constraint != null); + CheckExpression(subsetTypeDecl.Constraint, this, new CodeContextWrapper(subsetTypeDecl, true)); + + if (subsetTypeDecl.Witness != null) { + CheckExpression(subsetTypeDecl.Witness, this, + new CodeContextWrapper(subsetTypeDecl, subsetTypeDecl.WitnessKind == SubsetTypeDecl.WKind.Ghost)); + if (subsetTypeDecl.WitnessKind == SubsetTypeDecl.WKind.Compiled) { + var codeContext = new CodeContextWrapper(subsetTypeDecl, subsetTypeDecl.WitnessKind == SubsetTypeDecl.WKind.Ghost); + ExpressionTester.CheckIsCompilable(Options, this, subsetTypeDecl.Witness, codeContext); + } + } + + } else if (d is NewtypeDecl newtypeDecl) { + if (newtypeDecl.Var != null) { + Contract.Assert(newtypeDecl.Constraint != null); + CheckExpression(newtypeDecl.Constraint, this, new CodeContextWrapper(newtypeDecl, true)); + } + + if (newtypeDecl.Witness != null) { + CheckExpression(newtypeDecl.Witness, this, new CodeContextWrapper(newtypeDecl, newtypeDecl.WitnessKind == SubsetTypeDecl.WKind.Ghost)); + if (newtypeDecl.WitnessKind == SubsetTypeDecl.WKind.Compiled) { + var codeContext = new CodeContextWrapper(newtypeDecl, newtypeDecl.WitnessKind == SubsetTypeDecl.WKind.Ghost); + ExpressionTester.CheckIsCompilable(Options, this, newtypeDecl.Witness, codeContext); + } + } + + new NativeTypeAnalysis(reporter).FigureOutNativeType(newtypeDecl, Options); + + } else if (d is DatatypeDecl) { + var dd = (DatatypeDecl)d; + foreach (var member in GetClassMembers(dd)!.Values) { + var dtor = member as DatatypeDestructor; + if (dtor != null) { + var rolemodel = dtor.CorrespondingFormals[0]; + for (int i = 1; i < dtor.CorrespondingFormals.Count; i++) { + var other = dtor.CorrespondingFormals[i]; + if (rolemodel.IsGhost != other.IsGhost) { + reporter.Error(MessageSource.Resolver, other, + "shared destructors must agree on whether or not they are ghost, but '{0}' is {1} in constructor '{2}' and {3} in constructor '{4}'", + rolemodel.Name, + rolemodel.IsGhost ? "ghost" : "non-ghost", dtor.EnclosingCtors[0].Name, + other.IsGhost ? "ghost" : "non-ghost", dtor.EnclosingCtors[i].Name); + } + } + } + } + foreach (var ctor in dd.Ctors) { + CheckParameterDefaultValuesAreCompilable(ctor.Formals, dd); + } + } + } + + AnalyzeTypeConstraints.AssignConstraintIsCompilable(declarations, Options); + + // Now that we have filled in the .ConstraintIsCompilable field of all subset types and newtypes, we're ready to + // visit iterator bodies and members (which will make calls to CheckIsCompilable). + foreach (TopLevelDecl d in declarations) { + if (d is IteratorDecl { Body: { } iterBody } iter) { + ComputeGhostInterest(iter.Body, false, null, iter); + } + if (d is TopLevelDeclWithMembers cl) { + ResolveClassMembers_Pass1(cl); + } + } + } + private void CheckForCyclesAmongRedirectingTypes(RedirectingTypeDecl dd, HashSet cycleErrorHasBeenReported) { var enclosingModule = dd.EnclosingModule; if (enclosingModule.CallGraph.GetSCCSize(dd) != 1) { @@ -1964,7 +1970,7 @@ void CheckExpression(Expression expr, ModuleResolver resolver, ICodeContext code Contract.Requires(expr != null); Contract.Requires(resolver != null); Contract.Requires(codeContext != null); - var v = new CheckExpression_Visitor(resolver, codeContext); + var v = new CheckLocalityVisitor(resolver, codeContext); v.Visit(expr); } /// @@ -1977,43 +1983,10 @@ void CheckExpression(Statement stmt, ModuleResolver resolver, ICodeContext codeC Contract.Requires(stmt != null); Contract.Requires(resolver != null); Contract.Requires(codeContext != null); - var v = new CheckExpression_Visitor(resolver, codeContext); + var v = new CheckLocalityVisitor(resolver, codeContext); v.Visit(stmt); } - class CheckExpression_Visitor : ResolverBottomUpVisitor { - readonly ICodeContext CodeContext; - public CheckExpression_Visitor(ModuleResolver resolver, ICodeContext codeContext) - : base(resolver) { - Contract.Requires(resolver != null); - Contract.Requires(codeContext != null); - CodeContext = codeContext; - } - protected override void VisitOneExpr(Expression expr) { - if (expr is StmtExpr) { - var e = (StmtExpr)expr; - resolver.ComputeGhostInterest(e.S, true, "a statement expression", CodeContext); - } else if (expr is LetExpr) { - var e = (LetExpr)expr; - if (CodeContext.IsGhost) { - foreach (var bv in e.BoundVars) { - bv.MakeGhost(); - } - } - } - } - protected override void VisitOneStmt(Statement stmt) { - if (stmt is CalcStmt calc) { - foreach (var h in calc.Hints) { - resolver.CheckLocalityUpdates(h, new HashSet(), "a hint"); - } - } else if (stmt is AssertStmt astmt && astmt.Proof != null) { - resolver.CheckLocalityUpdates(astmt.Proof, new HashSet(), "an assert-by body"); - } else if (stmt is ForallStmt forall && forall.Body != null) { - resolver.CheckLocalityUpdates(forall.Body, new HashSet(), "a forall statement"); - } - } - } #endregion void ExtremePredicateChecks(Expression expr, ExtremePredicate context, CallingPosition cp) { @@ -3216,18 +3189,17 @@ void CheckIsFunction([CanBeNull] MemberDecl memberDecl, bool allowMethod) { } /// - /// Check that "stmt" is a valid statment for the body of an assert-by, forall, + /// Check that "stmt" is a valid statement for the body of an assert-by, forall, /// or calc-hint statement. In particular, check that the local variables assigned in /// the bodies of these statements are declared in the statements, not in some enclosing /// context. /// public void CheckLocalityUpdates(Statement stmt, ISet localsAllowedInUpdates, string where) { - // TODO it looks like this method has no side-effects and doesn't return anything. Contract.Requires(stmt != null); Contract.Requires(localsAllowedInUpdates != null); Contract.Requires(where != null); - if (stmt is AssertStmt || stmt is ForallStmt || stmt is CalcStmt || stmt is ModifyStmt) { + if (stmt is AssertStmt or ForallStmt or CalcStmt or ModifyStmt) { // don't recurse, since CheckHintRestrictions will be called on that assert-by separately return; } else if (stmt is AssignSuchThatStmt) { @@ -3251,6 +3223,12 @@ public void CheckLocalityUpdates(Statement stmt, ISet localsAllow } else if (stmt is BlockStmt) { localsAllowedInUpdates = new HashSet(localsAllowedInUpdates); // use this new set for the recursive calls + } else if (stmt is BlockByProofStmt blockByProofStmt) { + localsAllowedInUpdates = new HashSet(localsAllowedInUpdates); + // use this new set for the recursive calls + + CheckLocalityUpdates(blockByProofStmt.Body, localsAllowedInUpdates, where); + return; } foreach (var ss in stmt.SubStatements) { @@ -3332,21 +3310,6 @@ internal LetExpr LetVarIn(IToken tok, string name, Type tp, Expression rhs, Expr return LetPatIn(tok, lhs, rhs, body); } - internal static void ResolveByProof(INewOrOldResolver resolver, BlockStmt proof, ResolutionContext resolutionContext) { - if (proof == null) { - return; - } - - // clear the labels for the duration of checking the proof body, because break statements are not allowed to leave the proof body - var prevLblStmts = resolver.EnclosingStatementLabels; - var prevLoopStack = resolver.LoopStack; - resolver.EnclosingStatementLabels = new Scope(resolver.Options); - resolver.LoopStack = new List(); - resolver.ResolveStatement(proof, resolutionContext); - resolver.EnclosingStatementLabels = prevLblStmts; - resolver.LoopStack = prevLoopStack; - } - /// /// If expr.Lhs != null: Desugars "var x: T :- E; F" into "var temp := E; if temp.IsFailure() then temp.PropagateFailure() else var x: T := temp.Extract(); F" /// If expr.Lhs == null: Desugars " :- E; F" into "var temp := E; if temp.IsFailure() then temp.PropagateFailure() else F" diff --git a/Source/DafnyCore/Resolver/PreType/PreTypeResolve.Statements.cs b/Source/DafnyCore/Resolver/PreType/PreTypeResolve.Statements.cs index e57544e0a4c..c894222c06a 100644 --- a/Source/DafnyCore/Resolver/PreType/PreTypeResolve.Statements.cs +++ b/Source/DafnyCore/Resolver/PreType/PreTypeResolve.Statements.cs @@ -644,9 +644,6 @@ private void ResolveUpdateStmt(AssignStatement update, ResolutionContext resolut ResolveAttributes(rhs, resolutionContext, false); } - // resolve proof - ModuleResolver.ResolveByProof(this, update.Proof, resolutionContext); - // figure out what kind of UpdateStmt this is if (firstEffectfulRhs == null) { if (update.Lhss.Count == 0) { @@ -706,8 +703,7 @@ private void ResolveUpdateStmt(AssignStatement update, ResolutionContext resolut } else if (ErrorCount == errorCountBeforeCheckingStmt) { // a call statement var resolvedLhss = update.Lhss.ConvertAll(ll => ll.Resolved); - var a = new CallStmt(update.RangeToken, resolvedLhss, methodCallInfo.Callee, methodCallInfo.ActualParameters, - methodCallInfo.Tok, update.Proof); + var a = new CallStmt(update.RangeToken, resolvedLhss, methodCallInfo.Callee, methodCallInfo.ActualParameters, methodCallInfo.Tok); a.OriginalInitialLhs = update.OriginalInitialLhs; update.ResolvedStatements.Add(a); } @@ -933,8 +929,6 @@ private void ResolveAssignOrReturnStmt(AssignOrReturnStmt s, ResolutionContext r return; } - ModuleResolver.ResolveByProof(this, s.Proof, resolutionContext); - Expression lhsExtract = null; if (expectExtract) { if (enclosingMethod.Outs.Count == 0 && s.KeywordToken == null) { @@ -997,7 +991,7 @@ private void ResolveAssignOrReturnStmt(AssignOrReturnStmt s, ResolutionContext r } } // " temp, ... := MethodOrExpression, ...;" - var up = new AssignStatement(s.RangeToken, lhss2, rhss2, s.Proof); + var up = new AssignStatement(s.RangeToken, lhss2, rhss2); if (expectExtract) { up.OriginalInitialLhs = s.Lhss.Count == 0 ? null : s.Lhss[0]; } @@ -1013,7 +1007,7 @@ private void ResolveAssignOrReturnStmt(AssignOrReturnStmt s, ResolutionContext r } else if (s.KeywordToken.Token.val == "assume") { ss = new AssumeStmt(new RangeToken(token, s.EndToken), notFailureExpr, SystemModuleManager.AxiomAttribute(s.KeywordToken.Attrs)); } else if (s.KeywordToken.Token.val == "assert") { - ss = new AssertStmt(new RangeToken(token, s.EndToken), notFailureExpr, null, null, s.KeywordToken.Attrs); + ss = new AssertStmt(new RangeToken(token, s.EndToken), notFailureExpr, null, s.KeywordToken.Attrs); } else { Contract.Assert(false, $"Invalid token in :- statement: {token.val}"); } @@ -1035,7 +1029,7 @@ private void ResolveAssignOrReturnStmt(AssignOrReturnStmt s, ResolutionContext r new AssignStatement(s.RangeToken, new List() { ident }, new List() {new ExprRhs(resolver.VarDotMethod(s.Tok, temp, "PropagateFailure"))} - , s.Proof), + ), new ReturnStmt(s.RangeToken, null), }), // ELSE: no else block @@ -1050,7 +1044,7 @@ private void ResolveAssignOrReturnStmt(AssignOrReturnStmt s, ResolutionContext r new AssignStatement(s.RangeToken, new List() { lhsExtract }, new List() { new ExprRhs(resolver.VarDotMethod(s.Tok, temp, "Extract")) } - , s.Proof)); + )); } s.ResolvedStatements.ForEach(a => ResolveStatement(a, resolutionContext)); diff --git a/Source/DafnyCore/Resolver/TailRecursion.cs b/Source/DafnyCore/Resolver/TailRecursion.cs index 61d6fe559f9..4fabda02dd6 100644 --- a/Source/DafnyCore/Resolver/TailRecursion.cs +++ b/Source/DafnyCore/Resolver/TailRecursion.cs @@ -267,6 +267,8 @@ TailRecursionStatus CheckTailRecursive(Statement stmt, Method enclosingMethod, r } } else if (stmt is VarDeclPattern) { } else if (stmt is ExpectStmt) { + } else if (stmt is BlockByProofStmt blockByProofStmt) { + return CheckTailRecursive(blockByProofStmt.Body, enclosingMethod, ref tailCall, reportErrors); } else { Contract.Assert(false); // unexpected statement type } diff --git a/Source/DafnyCore/Rewriters/RefinementTransformer.cs b/Source/DafnyCore/Rewriters/RefinementTransformer.cs index 3ce39d320d5..dfbce39d164 100644 --- a/Source/DafnyCore/Rewriters/RefinementTransformer.cs +++ b/Source/DafnyCore/Rewriters/RefinementTransformer.cs @@ -1071,7 +1071,7 @@ List MergeStmtList(List skeleton, List oldStmt, var e = refinementCloner.CloneExpr(oldAssume.Expr); var attrs = refinementCloner.MergeAttributes(oldAssume.Attributes, skel.Attributes); body.Add(new AssertStmt(new RangeToken(new BoogieGenerator.ForceCheckToken(skel.RangeToken.StartToken), skel.RangeToken.EndToken), - e, skel.Proof, skel.Label, new Attributes("_prependAssertToken", new List(), attrs))); + e, skel.Label, new Attributes("_prependAssertToken", new List(), attrs))); Reporter.Info(MessageSource.RefinementTransformer, c.ConditionEllipsis, "assume->assert: " + Printer.ExprToString(Reporter.Options, e)); i++; j++; } @@ -1217,7 +1217,7 @@ List MergeStmtList(List skeleton, List oldStmt, i++; j++; if (addedAssert != null) { var tok = new BoogieGenerator.ForceCheckToken(addedAssert.RangeToken.StartToken); - body.Add(new AssertStmt(new RangeToken(tok, addedAssert.RangeToken.EndToken), addedAssert, null, null, null)); + body.Add(new AssertStmt(new RangeToken(tok, addedAssert.RangeToken.EndToken), addedAssert, null, null)); } } else { MergeAddStatement(cur, body); @@ -1274,7 +1274,7 @@ List MergeStmtList(List skeleton, List oldStmt, stmtGenerated.Add(nw); var addedAssert = refinementCloner.CloneExpr(s.Expr); var tok = new RangeToken(addedAssert.RangeToken.StartToken, addedAssert.RangeToken.EndToken); - stmtGenerated.Add(new AssertStmt(tok, addedAssert, null, null, null)); + stmtGenerated.Add(new AssertStmt(tok, addedAssert, null, null)); } } if (doMerge) { diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.BoogieFactory.cs b/Source/DafnyCore/Verifier/BoogieGenerator.BoogieFactory.cs index 20900f58c02..0773f1ac449 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.BoogieFactory.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.BoogieFactory.cs @@ -829,8 +829,8 @@ static Bpl.Axiom BplAxiom(Bpl.Expr e) { return new Bpl.Axiom(e.tok, e); } - public static Bpl.Expr BplLocalVar(string name, Bpl.Type ty, List lvars) { - lvars.Add(BplLocalVar(name, ty, out var v)); + public static Bpl.Expr BplLocalVar(string name, Bpl.Type ty, Variables lvars) { + lvars.GetOrAdd(BplLocalVar(name, ty, out var v)); return v; } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Decreases.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Decreases.cs index 6a52c81f018..c9b395b372a 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Decreases.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Decreases.cs @@ -91,8 +91,8 @@ void CheckCallTermination(IToken tok, List contextDecreases, List @@ -168,7 +168,7 @@ Bpl.Expr DecreasesCheck(List toks, List prevGhostLocals, Expression dafnyBound = Expression.CreateOr(boundedDafny, EqDafny[k]); Bpl.Cmd cmd = Assert(toks[k], BplOr(bounded, Eq[k]), - new PODesc.DecreasesBoundedBelow(N, k, zeroStr, prevGhostLocals, dafnyBound, suffixMsg)); + new DecreasesBoundedBelow(N, k, zeroStr, prevGhostLocals, dafnyBound, suffixMsg), builder.Context); builder.Add(cmd); } } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs index e690fc86a5f..87fb7cb6cbb 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs @@ -38,7 +38,7 @@ bool NeedsDefiniteAssignmentTracker(bool isGhost, Type type, bool isField) { return true; } - Bpl.Expr /*?*/ AddDefiniteAssignmentTracker(IVariable p, List localVariables, bool isOutParam = false, + Bpl.Expr /*?*/ AddDefiniteAssignmentTracker(IVariable p, Variables localVariables, bool isOutParam = false, bool forceGhostVar = false) { Contract.Requires(p != null); Contract.Requires(localVariables != null); @@ -54,23 +54,29 @@ bool NeedsDefiniteAssignmentTracker(bool isGhost, Type type, bool isField) { tracker = new Bpl.LocalVariable(p.Tok, new Bpl.TypedIdent(p.Tok, DefassPrefix + p.UniqueName, Bpl.Type.Bool)); } - localVariables.Add(tracker); + tracker = localVariables.GetOrAdd(tracker); var ie = new Bpl.IdentifierExpr(p.Tok, tracker); - DefiniteAssignmentTrackers.Add(p.UniqueName, ie); + DefiniteAssignmentTrackers = DefiniteAssignmentTrackers.Add(p.UniqueName, ie); return ie; } void AddExistingDefiniteAssignmentTracker(IVariable p, bool forceGhostVar) { Contract.Requires(p != null); - if (NeedsDefiniteAssignmentTracker(p.IsGhost || forceGhostVar, p.Type, false)) { - var ie = new Bpl.IdentifierExpr(p.Tok, DefassPrefix + p.UniqueName, Bpl.Type.Bool); - DefiniteAssignmentTrackers.Add(p.UniqueName, ie); + if (DefiniteAssignmentTrackers.ContainsKey(p.UniqueName)) { + return; + } + + if (!NeedsDefiniteAssignmentTracker(p.IsGhost || forceGhostVar, p.Type, false)) { + return; } + + var ie = new Bpl.IdentifierExpr(p.Tok, DefassPrefix + p.UniqueName, Bpl.Type.Bool); + DefiniteAssignmentTrackers = DefiniteAssignmentTrackers.Add(p.UniqueName, ie); } void AddDefiniteAssignmentTrackerSurrogate(Field field, TopLevelDeclWithMembers enclosingClass, - List localVariables, bool forceGhostVar) { + Variables localVariables, bool forceGhostVar) { Contract.Requires(field != null); Contract.Requires(localVariables != null); @@ -80,45 +86,9 @@ void AddDefiniteAssignmentTrackerSurrogate(Field field, TopLevelDeclWithMembers } var nm = SurrogateName(field); - var tracker = new Bpl.LocalVariable(field.tok, new Bpl.TypedIdent(field.tok, DefassPrefix + nm, Bpl.Type.Bool)); - localVariables.Add(tracker); + var tracker = localVariables.GetOrAdd(new Bpl.LocalVariable(field.tok, new Bpl.TypedIdent(field.tok, DefassPrefix + nm, Bpl.Type.Bool))); var ie = new Bpl.IdentifierExpr(field.tok, tracker); - DefiniteAssignmentTrackers.Add(nm, ie); - } - - public void RemoveDefiniteAssignmentTrackers(List ss, int prevDefAssTrackerCount) { - Contract.Requires(ss != null); - foreach (var s in ss) { - if (s is VarDeclStmt vdecl) { - if (vdecl.Assign is AssignOrReturnStmt ars) { - foreach (var sx in ars.ResolvedStatements) { - if (sx is VarDeclStmt vdecl2) { - vdecl2.Locals.ForEach(RemoveDefiniteAssignmentTracker); - } - } - } - - vdecl.Locals.ForEach(RemoveDefiniteAssignmentTracker); - } else if (s is AssignOrReturnStmt ars) { - foreach (var sx in ars.ResolvedStatements) { - if (sx is VarDeclStmt vdecl2) { - vdecl2.Locals.ForEach(RemoveDefiniteAssignmentTracker); - } - } - } - } - - Contract.Assert(prevDefAssTrackerCount == DefiniteAssignmentTrackers.Count); - } - - void RemoveDefiniteAssignmentTracker(IVariable p) { - Contract.Requires(p != null); - DefiniteAssignmentTrackers.Remove(p.UniqueName); - } - - void RemoveDefiniteAssignmentTrackerSurrogate(Field field) { - Contract.Requires(field != null); - DefiniteAssignmentTrackers.Remove(SurrogateName(field)); + DefiniteAssignmentTrackers = DefiniteAssignmentTrackers.Add(nm, ie); } void MarkDefiniteAssignmentTracker(IdentifierExpr expr, BoogieStmtListBuilder builder) { @@ -129,7 +99,6 @@ void MarkDefiniteAssignmentTracker(IdentifierExpr expr, BoogieStmtListBuilder bu void MarkDefiniteAssignmentTracker(IToken tok, string name, BoogieStmtListBuilder builder) { Contract.Requires(tok != null); - Contract.Requires(name != null); Contract.Requires(builder != null); Bpl.IdentifierExpr ie; @@ -165,7 +134,7 @@ void CheckDefiniteAssignment(IdentifierExpr expr, BoogieStmtListBuilder builder) Bpl.IdentifierExpr ie; if (DefiniteAssignmentTrackers.TryGetValue(expr.Var.UniqueName, out ie)) { builder.Add(Assert(GetToken(expr), ie, - new PODesc.DefiniteAssignment("variable", expr.Var.Name, "here"))); + new DefiniteAssignment("variable", expr.Var.Name, "here"), builder.Context)); } } @@ -187,11 +156,10 @@ void CheckDefiniteAssignmentSurrogate(IToken tok, Field field, bool atNew, Boogi Contract.Requires(builder != null); var nm = SurrogateName(field); - Bpl.IdentifierExpr ie; - if (DefiniteAssignmentTrackers.TryGetValue(nm, out ie)) { - var desc = new PODesc.DefiniteAssignment( + if (DefiniteAssignmentTrackers.TryGetValue(nm, out var ie)) { + var desc = new DefiniteAssignment( "field", field.Name, atNew ? "at this point in the constructor body" : "here"); - builder.Add(Assert(tok, ie, desc)); + builder.Add(Assert(tok, ie, desc, builder.Context)); } } @@ -232,8 +200,8 @@ void CheckDefiniteAssignmentReturn(IToken tok, Formal p, BoogieStmtListBuilder b Bpl.IdentifierExpr ie; if (DefiniteAssignmentTrackers.TryGetValue(p.UniqueName, out ie)) { - var desc = new PODesc.DefiniteAssignment("out-parameter", p.Name, "at this return point"); - builder.Add(Assert(tok, ie, desc)); + var desc = new DefiniteAssignment("out-parameter", p.Name, "at this return point"); + builder.Add(Assert(tok, ie, desc, builder.Context)); } } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index 663b8ed47f6..10d78a37498 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -704,7 +704,7 @@ public Boogie.Expr TrExpr(Expression expr) { result = BoogieGenerator.CondApplyUnbox(GetToken(e), result, e.Function.ResultType, e.Type); bool callIsLit = argsAreLit - && BoogieGenerator.FunctionBodyIsAvailable(e.Function, BoogieGenerator.currentModule, BoogieGenerator.currentScope, true) + && BoogieGenerator.FunctionBodyIsAvailable(e.Function, BoogieGenerator.currentModule, BoogieGenerator.currentScope) && !e.Function.Reads.Expressions.Any(); // Function could depend on external values if (callIsLit) { result = MaybeLit(result, ty); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 1157343bdac..d7c3aa02760 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -38,7 +38,7 @@ public class WFOptions { public readonly Function SelfCallsAllowance; public readonly bool DoReadsChecks; public readonly bool DoOnlyCoarseGrainedTerminationChecks; // termination checks don't look at decreases clause, but reports errors for any intra-SCC call (this is used in default-value expressions) - public readonly List Locals; + public readonly Variables Locals; public readonly List> CreateAsserts; public readonly bool LValueContext; public readonly Bpl.QKeyValue AssertKv; @@ -53,13 +53,13 @@ public WFOptions(Function selfCallsAllowance, bool doReadsChecks, DoReadsChecks = doReadsChecks; DoOnlyCoarseGrainedTerminationChecks = doOnlyCoarseGrainedTerminationChecks; if (saveReadsChecks) { - Locals = new List(); + Locals = new Variables(); CreateAsserts = new(); } } private WFOptions(Function selfCallsAllowance, bool doReadsChecks, bool doOnlyCoarseGrainedTerminationChecks, - List locals, List> createAsserts, bool lValueContext, Bpl.QKeyValue assertKv) { + Variables locals, List> createAsserts, bool lValueContext, Bpl.QKeyValue assertKv) { SelfCallsAllowance = selfCallsAllowance; DoReadsChecks = doReadsChecks; DoOnlyCoarseGrainedTerminationChecks = doOnlyCoarseGrainedTerminationChecks; @@ -89,21 +89,21 @@ public WFOptions WithLValueContext(bool lValueContext) { Locals, CreateAsserts, lValueContext, AssertKv); } - public Action AssertSink(BoogieGenerator tran, BoogieStmtListBuilder builder) { + public Action AssertSink(BoogieGenerator tran, BoogieStmtListBuilder builder) { return (t, e, d, qk) => { if (Locals != null) { var b = BoogieGenerator.BplLocalVar(tran.CurrentIdGenerator.FreshId("b$reqreads#"), Bpl.Type.Bool, Locals); - CreateAsserts.Add(() => tran.Assert(t, b, d, qk)); + CreateAsserts.Add(() => tran.Assert(t, b, d, builder.Context, qk)); builder.Add(Bpl.Cmd.SimpleAssign(e.tok, (Bpl.IdentifierExpr)b, e)); } else { - builder.Add(tran.Assert(t, e, d, qk)); + builder.Add(tran.Assert(t, e, d, builder.Context, qk)); } }; } public List AssignLocals { get { - return Map(Locals, l => + return Map(Locals.Values, l => Bpl.Cmd.SimpleAssign(l.tok, new Bpl.IdentifierExpr(Token.NoToken, l), Bpl.Expr.True) @@ -111,14 +111,14 @@ public List AssignLocals { } } - public void ProcessSavedReadsChecks(List locals, BoogieStmtListBuilder builderInitializationArea, BoogieStmtListBuilder builder) { + public void ProcessSavedReadsChecks(Variables locals, BoogieStmtListBuilder builderInitializationArea, BoogieStmtListBuilder builder) { Contract.Requires(locals != null); Contract.Requires(builderInitializationArea != null); Contract.Requires(builder != null); Contract.Requires(Locals != null && CreateAsserts != null); // ProcessSavedReadsChecks should be called only if the constructor was called with saveReadsChecks // var b$reads_guards#0 : bool ... - locals.AddRange(Locals); + locals.AddRange(Locals.Values); // b$reads_guards#0 := true ... foreach (var cmd in AssignLocals) { builderInitializationArea.Add(cmd); @@ -132,7 +132,7 @@ public void ProcessSavedReadsChecks(List locals, BoogieStmtListBuilder public partial class BoogieGenerator { - public void CheckWellformedAndAssume(Expression expr, WFOptions wfOptions, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, string comment) { + public void CheckWellformedAndAssume(Expression expr, WFOptions wfOptions, Variables locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, string comment) { Contract.Requires(expr != null); Contract.Requires(expr.Type != null && expr.Type.IsBoolType); Contract.Requires(wfOptions != null); @@ -233,7 +233,7 @@ public void CheckWellformedAndAssume(Expression expr, WFOptions wfOptions, List< // Also encapsulates the handling for the optimization to not declare a $_ReadsFrame field if the reads clause is *: // if etran.readsFrame is null, the block is called with a WFOption with DoReadsChecks set to false instead. private record ReadsCheckDelayer(ExpressionTranslator etran, Function selfCallsAllowance, - List localVariables, BoogieStmtListBuilder builderInitializationArea, BoogieStmtListBuilder builder) { + Variables localVariables, BoogieStmtListBuilder builderInitializationArea, BoogieStmtListBuilder builder) { public void DoWithDelayedReadsChecks(bool doOnlyCoarseGrainedTerminationChecks, Action action) { var doReadsChecks = etran.readsFrame != null; @@ -248,7 +248,7 @@ public void DoWithDelayedReadsChecks(bool doOnlyCoarseGrainedTerminationChecks, /// /// Check the well-formedness of "expr" (but don't leave hanging around any assumptions that affect control flow) /// - public void CheckWellformed(Expression expr, WFOptions wfOptions, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { + public void CheckWellformed(Expression expr, WFOptions wfOptions, Variables locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { Contract.Requires(expr != null); Contract.Requires(wfOptions != null); Contract.Requires(locals != null); @@ -266,7 +266,7 @@ public void CheckWellformed(Expression expr, WFOptions wfOptions, List /// See class WFOptions for descriptions of the specified options. /// void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, - List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, + Variables locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, AddResultCommands addResultCommands) { var origOptions = wfOptions; if (wfOptions.LValueContext) { @@ -349,7 +349,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, builder.Add(TrAssumeCmd(selectExpr.tok, correctConstructor)); } else { builder.Add(Assert(GetToken(expr), correctConstructor, - new PODesc.DestructorValid(dtor, e.Obj, dtor.EnclosingCtors))); + new DestructorValid(dtor, e.Obj, dtor.EnclosingCtors), builder.Context)); } CheckNotGhostVariant(e, "destructor", dtor.EnclosingCtors, builder, etran); } else if (e.Member is DatatypeDiscriminator discriminator) { @@ -359,21 +359,21 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, if (e.Member is TwoStateFunction) { Bpl.Expr wh = GetWhereClause(selectExpr.tok, etran.TrExpr(e.Obj), e.Obj.Type, etran.OldAt(e.AtLabel), ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("receiver argument", "in the two-state function's previous state", e.Obj, e.AtLabel); - builder.Add(Assert(GetToken(expr), wh, desc)); + var desc = new IsAllocated("receiver argument", "in the two-state function's previous state", e.Obj, e.AtLabel); + builder.Add(Assert(GetToken(expr), wh, desc, builder.Context)); } } else if (etran.UsesOldHeap) { Bpl.Expr wh = GetWhereClause(selectExpr.tok, etran.TrExpr(e.Obj), e.Obj.Type, etran, ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("receiver", + var desc = new IsAllocated("receiver", $"in the state in which its {(e.Member is Field ? "fields" : "members")} are accessed", e.Obj, e.AtLabel); - builder.Add(Assert(GetToken(expr), wh, desc)); + builder.Add(Assert(GetToken(expr), wh, desc, builder.Context)); } } } if (!origOptions.LValueContext && wfOptions.DoReadsChecks && e.Member is Field { IsMutable: true } f) { var requiredFrame = new FrameExpression(Token.NoToken, e.Obj, f.Name); - var desc = new PODesc.ReadFrameSubset("read field", requiredFrame, readFrames, selectExpr, etran.scope); + var desc = new ReadFrameSubset("read field", requiredFrame, readFrames, selectExpr, etran.scope); wfOptions.AssertSink(this, builder)(selectExpr.tok, Bpl.Expr.SelectTok(selectExpr.tok, etran.ReadsFrame(selectExpr.tok), etran.TrExpr(e.Obj), GetField(e)), desc, wfOptions.AssertKv); } @@ -387,9 +387,11 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, CheckWellformed(e.Seq, wfOptions, locals, builder, etran); Bpl.Expr seq = etran.TrExpr(e.Seq); if (eSeqType.IsArrayType) { - builder.Add(Assert(GetToken(e.Seq), Bpl.Expr.Neq(seq, predef.Null), new PODesc.NonNull("array", e.Seq))); + builder.Add(Assert(GetToken(e.Seq), Bpl.Expr.Neq(seq, predef.Null), + new NonNull("array", e.Seq), builder.Context)); if (etran.UsesOldHeap) { - builder.Add(Assert(GetToken(e.Seq), MkIsAlloc(seq, eSeqType, etran.HeapExpr), new PODesc.IsAllocated("array", null, e.Seq))); + builder.Add(Assert(GetToken(e.Seq), MkIsAlloc(seq, eSeqType, etran.HeapExpr), + new IsAllocated("array", null, e.Seq), builder.Context)); } } Bpl.Expr e0 = null; @@ -400,7 +402,8 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, var f = finite ? BuiltinFunction.MapDomain : BuiltinFunction.IMapDomain; Bpl.Expr inDomain = FunctionCall(selectExpr.tok, f, finite ? predef.MapType : predef.IMapType, seq); inDomain = Bpl.Expr.Select(inDomain, BoxIfNecessary(e.tok, e0, e.E0.Type)); - builder.Add(Assert(GetToken(expr), inDomain, new PODesc.ElementInDomain(e.Seq, e.E0), wfOptions.AssertKv)); + builder.Add(Assert(GetToken(expr), inDomain, + new ElementInDomain(e.Seq, e.E0), builder.Context, wfOptions.AssertKv)); } else if (eSeqType is MultiSetType) { // cool @@ -408,8 +411,10 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, if (e.E0 != null) { e0 = etran.TrExpr(e.E0); CheckWellformed(e.E0, wfOptions, locals, builder, etran); - var desc = new PODesc.InRange(e.Seq, e.E0, e.SelectOne, e.SelectOne ? "index" : "lower bound"); - builder.Add(Assert(GetToken(expr), InSeqRange(selectExpr.tok, e0, e.E0.Type, seq, isSequence, null, !e.SelectOne), desc, wfOptions.AssertKv)); + var desc = new InRange(e.Seq, e.E0, e.SelectOne, e.SelectOne ? "index" : "lower bound"); + builder.Add(Assert(GetToken(expr), + InSeqRange(selectExpr.tok, e0, e.E0.Type, seq, isSequence, null, !e.SelectOne), + desc, builder.Context, wfOptions.AssertKv)); } if (e.E1 != null) { CheckWellformed(e.E1, wfOptions, locals, builder, etran); @@ -419,8 +424,10 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, } else { lowerBound = e0; } - builder.Add(Assert(GetToken(expr), InSeqRange(selectExpr.tok, etran.TrExpr(e.E1), e.E1.Type, seq, isSequence, lowerBound, true), - new PODesc.SequenceSelectRangeValid(e.Seq, e.E0, e.E1, isSequence ? "sequence" : "array"), wfOptions.AssertKv)); + builder.Add(Assert(GetToken(expr), + InSeqRange(selectExpr.tok, etran.TrExpr(e.E1), e.E1.Type, seq, isSequence, lowerBound, true), + new SequenceSelectRangeValid(e.Seq, e.E0, e.E1, isSequence ? "sequence" : "array"), + builder.Context, wfOptions.AssertKv)); } } if (!origOptions.LValueContext && wfOptions.DoReadsChecks && eSeqType.IsArrayType) { @@ -430,7 +437,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, i = ConvertExpression(selectExpr.tok, i, e.E0.Type, Type.Int); Bpl.Expr fieldName = FunctionCall(selectExpr.tok, BuiltinFunction.IndexField, null, i); var requiredFrame = new FrameExpression(Token.NoToken, e.Seq, null); - var desc = new PODesc.ReadFrameSubset("read array element", requiredFrame, readFrames, e, etran.scope); + var desc = new ReadFrameSubset("read array element", requiredFrame, readFrames, e, etran.scope); wfOptions.AssertSink(this, builder)(selectExpr.tok, Bpl.Expr.SelectTok(selectExpr.tok, etran.ReadsFrame(selectExpr.tok), seq, fieldName), desc, wfOptions.AssertKv); } else { @@ -446,7 +453,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, var trigger = BplTrigger(allowedToRead); // Note, the assertion we're about to produce only seems useful in the check-only mode (that is, with subsumption 0), but if it were to be assumed, we'll use this entire RHS as the trigger var qq = new Bpl.ForallExpr(e.tok, new List { iVar }, trigger, BplImp(range, allowedToRead)); var requiredFrame = new FrameExpression(Token.NoToken, e.Seq, null); - var desc = new PODesc.ReadFrameSubset("read the indicated range of array elements", requiredFrame, readFrames, e, etran.scope); + var desc = new ReadFrameSubset("read the indicated range of array elements", requiredFrame, readFrames, e, etran.scope); wfOptions.AssertSink(this, builder)(selectExpr.tok, qq, desc, wfOptions.AssertKv); } } @@ -460,9 +467,11 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, MultiSelectExpr e = selectExpr; CheckWellformed(e.Array, wfOptions, locals, builder, etran); Bpl.Expr array = etran.TrExpr(e.Array); - builder.Add(Assert(GetToken(e.Array), Bpl.Expr.Neq(array, predef.Null), new PODesc.NonNull("array", e.Array))); + builder.Add(Assert(GetToken(e.Array), Bpl.Expr.Neq(array, predef.Null), + new NonNull("array", e.Array), builder.Context)); if (etran.UsesOldHeap) { - builder.Add(Assert(GetToken(e.Array), MkIsAlloc(array, e.Array.Type, etran.HeapExpr), new PODesc.IsAllocated("array", null, e.Array))); + builder.Add(Assert(GetToken(e.Array), MkIsAlloc(array, e.Array.Type, etran.HeapExpr), + new IsAllocated("array", null, e.Array), builder.Context)); } for (int idxId = 0; idxId < e.Indices.Count; idxId++) { var idx = e.Indices[idxId]; @@ -475,13 +484,13 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, var upper = Bpl.Expr.Lt(index, length); var tok = idx is IdentifierExpr ? e.tok : idx.tok; // TODO: Reusing the token of an identifier expression would underline its definition. but this is still not perfect. - var desc = new PODesc.InRange(e.Array, e.Indices[idxId], true, $"index {idxId}", idxId); - builder.Add(Assert(tok, BplAnd(lower, upper), desc, wfOptions.AssertKv)); + var desc = new InRange(e.Array, e.Indices[idxId], true, $"index {idxId}", idxId); + builder.Add(Assert(tok, BplAnd(lower, upper), desc, builder.Context, wfOptions.AssertKv)); } if (wfOptions.DoReadsChecks) { Bpl.Expr fieldName = etran.GetArrayIndexFieldName(e.tok, e.Indices); var requiredFrame = new FrameExpression(Token.NoToken, e.Array, null); - var desc = new PODesc.ReadFrameSubset("read array element", requiredFrame, readFrames, selectExpr, etran.scope); + var desc = new ReadFrameSubset("read array element", requiredFrame, readFrames, selectExpr, etran.scope); wfOptions.AssertSink(this, builder)(selectExpr.tok, Bpl.Expr.SelectTok(selectExpr.tok, etran.ReadsFrame(selectExpr.tok), array, fieldName), desc, wfOptions.AssertKv); } @@ -498,8 +507,10 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, // validate index CheckWellformed(e.Index, wfOptions, locals, builder, etran); if (collectionType is SeqType) { - var desc = new PODesc.InRange(e.Seq, e.Index, true, "index"); - builder.Add(Assert(GetToken(e.Index), InSeqRange(updateExpr.tok, index, e.Index.Type, seq, true, null, false), desc, wfOptions.AssertKv)); + var desc = new InRange(e.Seq, e.Index, true, "index"); + builder.Add(Assert(GetToken(e.Index), + InSeqRange(updateExpr.tok, index, e.Index.Type, seq, true, null, false), + desc, builder.Context, wfOptions.AssertKv)); } else { CheckSubrange(e.Index.tok, index, e.Index.Type, collectionType.Arg, e.Index, builder); } @@ -510,8 +521,9 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, } else if (collectionType is MapType mapType) { CheckSubrange(e.Value.tok, value, e.Value.Type, mapType.Range, e.Value, builder); } else if (collectionType is MultiSetType) { - var desc = new PODesc.NonNegative("new number of occurrences", e.Value); - builder.Add(Assert(GetToken(e.Value), Bpl.Expr.Le(Bpl.Expr.Literal(0), value), desc, wfOptions.AssertKv)); + var desc = new NonNegative("new number of occurrences", e.Value); + builder.Add(Assert(GetToken(e.Value), Bpl.Expr.Le(Bpl.Expr.Literal(0), value), + desc, builder.Context, wfOptions.AssertKv)); } else { Contract.Assert(false); } @@ -540,15 +552,15 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, if (etran.UsesOldHeap) { Bpl.Expr wh = GetWhereClause(e.Function.tok, etran.TrExpr(e.Function), e.Function.Type, etran, ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("function", "in the state in which the function is invoked", e.Function); - builder.Add(Assert(GetToken(e.Function), wh, desc)); + var desc = new IsAllocated("function", "in the state in which the function is invoked", e.Function); + builder.Add(Assert(GetToken(e.Function), wh, desc, builder.Context)); } for (int i = 0; i < e.Args.Count; i++) { Expression ee = e.Args[i]; wh = GetWhereClause(ee.tok, etran.TrExpr(ee), ee.Type, etran, ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("argument", "in the state in which the function is invoked", ee); - builder.Add(Assert(GetToken(ee), wh, desc)); + var desc = new IsAllocated("argument", "in the state in which the function is invoked", ee); + builder.Add(Assert(GetToken(ee), wh, desc, builder.Context)); } } } @@ -608,7 +620,8 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, // check precond var bPrecond = FunctionCall(e.tok, Requires(arity), Bpl.Type.Bool, args); - builder.Add(Assert(GetToken(expr), bPrecond, new PODesc.PreconditionSatisfied(dPrecond, null, null))); + builder.Add(Assert(GetToken(expr), bPrecond, + new PreconditionSatisfied(dPrecond, null, null), builder.Context)); } if (wfOptions.DoReadsChecks && !fnCoreType.IsArrowTypeWithoutReadEffects) { @@ -627,7 +640,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, ); readsCall.Type = objset; var requiredFrame = new FrameExpression(Token.NoToken, readsCall, null); - var desc = new PODesc.ReadFrameSubset("invoke function", requiredFrame, readFrames); + var desc = new ReadFrameSubset("invoke function", requiredFrame, readFrames); CheckFrameSubset(applyExpr.tok, new List { wrappedReads }, null, null, etran, etran.ReadsFrame(applyExpr.tok), wfOptions.AssertSink(this, builder), desc, wfOptions.AssertKv); @@ -688,7 +701,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, }; ie.Type = ie.Var.Type; // resolve ie here substMap.Add(p, ie); - locals.Add(new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator), TrType(local.Type)))); + locals.GetOrAdd(new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator), TrType(local.Type)))); Bpl.IdentifierExpr lhs = (Bpl.IdentifierExpr)etran.TrExpr(ie); // TODO: is this cast always justified? Expression ee = e.Args[i]; directSubstMap.Add(p, ee); @@ -716,24 +729,24 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, if (!e.Function.IsStatic) { Bpl.Expr wh = GetWhereClause(e.Receiver.tok, etran.TrExpr(e.Receiver), e.Receiver.Type, etran, ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("receiver argument", "in the state in which the function is invoked", e.Receiver, e.AtLabel); - builder.Add(Assert(GetToken(e.Receiver), wh, desc)); + var desc = new IsAllocated("receiver argument", "in the state in which the function is invoked", e.Receiver, e.AtLabel); + builder.Add(Assert(GetToken(e.Receiver), wh, desc, builder.Context)); } } for (int i = 0; i < e.Args.Count; i++) { Expression ee = e.Args[i]; Bpl.Expr wh = GetWhereClause(ee.tok, etran.TrExpr(ee), ee.Type, etran, ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("argument", "in the state in which the function is invoked", ee, e.AtLabel); - builder.Add(Assert(GetToken(ee), wh, desc)); + var desc = new IsAllocated("argument", "in the state in which the function is invoked", ee, e.AtLabel); + builder.Add(Assert(GetToken(ee), wh, desc, builder.Context)); } } } else if (e.Function is TwoStateFunction) { if (!e.Function.IsStatic) { Bpl.Expr wh = GetWhereClause(e.Receiver.tok, etran.TrExpr(e.Receiver), e.Receiver.Type, etran.OldAt(e.AtLabel), ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("receiver argument", "in the two-state function's previous state", e.Receiver, e.AtLabel); - builder.Add(Assert(GetToken(e.Receiver), wh, desc)); + var desc = new IsAllocated("receiver argument", "in the two-state function's previous state", e.Receiver, e.AtLabel); + builder.Add(Assert(GetToken(e.Receiver), wh, desc, builder.Context)); } } Contract.Assert(e.Function.Ins.Count == e.Args.Count); @@ -744,13 +757,13 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Bpl.Expr wh = GetWhereClause(ee.tok, etran.TrExpr(ee), ee.Type, etran.OldAt(e.AtLabel), ISALLOC, true); if (wh != null) { var pIdx = e.Args.Count == 1 ? "" : " at index " + i; - var desc = new PODesc.IsAllocated( + var desc = new IsAllocated( $"argument{pIdx} for parameter '{formal.Name}'", - "in the two-state function's previous state" + PODesc.IsAllocated.HelperFormal(formal), + "in the two-state function's previous state" + IsAllocated.HelperFormal(formal), ee, e.AtLabel ); - builder.Add(Assert(GetToken(ee), wh, desc)); + builder.Add(Assert(GetToken(ee), wh, desc, builder.Context)); } } } @@ -761,7 +774,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, if (e.Function.Name == "reads" && !e.Receiver.Type.IsArrowTypeWithoutReadEffects) { var arguments = etran.FunctionInvocationArguments(e, null, null); var precondition = FunctionCall(e.tok, Requires(e.Args.Count), Bpl.Type.Bool, arguments); - builder.Add(Assert(GetToken(expr), precondition, new PODesc.PreconditionSatisfied(null, null, null))); + builder.Add(Assert(GetToken(expr), precondition, new PreconditionSatisfied(null, null, null), builder.Context)); if (wfOptions.DoReadsChecks) { // check that the callee reads only what the caller is already allowed to read @@ -793,7 +806,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, requiredFrames = new() { new FrameExpression(Token.NoToken, readsCall, null) }; break; } - var desc = new PODesc.ReadFrameSubset("invoke function", requiredFrames, readFrames); + var desc = new ReadFrameSubset("invoke function", requiredFrames, readFrames); CheckFrameSubset(expr.tok, new List { reads }, null, null, etran, etran.ReadsFrame(expr.tok), wfOptions.AssertSink(this, builder), desc, wfOptions.AssertKv); } @@ -811,12 +824,12 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, foreach (var ss in TrSplitExpr(builder.Context, precond, etran, true, out _)) { if (ss.IsChecked) { var tok = new NestedToken(GetToken(expr), ss.Tok); - var desc = new PODesc.PreconditionSatisfied(directPrecond, errorMessage, successMessage); + var desc = new PreconditionSatisfied(directPrecond, errorMessage, successMessage); if (wfOptions.AssertKv != null) { // use the given assert attribute only - builder.Add(Assert(tok, ss.E, desc, wfOptions.AssertKv)); + builder.Add(Assert(tok, ss.E, desc, builder.Context, wfOptions.AssertKv)); } else { - builder.Add(AssertNS(tok, ss.E, desc)); + builder.Add(AssertAndForget(builder.Context, tok, ss.E, desc)); } } } @@ -830,7 +843,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, // substitute actual args for parameters in description expression frames... var requiredFrames = e.Function.Reads.Expressions.ConvertAll(directSub.SubstFrameExpr); - var desc = new PODesc.ReadFrameSubset("invoke function", requiredFrames, readFrames); + var desc = new ReadFrameSubset("invoke function", requiredFrames, readFrames); // ... but that substitution isn't needed for frames passed to CheckFrameSubset var readsSubst = new Substituter(null, new Dictionary(), e.GetTypeArgumentSubstitutions()); @@ -846,7 +859,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Contract.Assert(calleeSCCLookup != null); if (ModuleDefinition.InSameSCC(calleeSCCLookup, codeContext)) { if (wfOptions.DoOnlyCoarseGrainedTerminationChecks) { - builder.Add(Assert(GetToken(expr), Bpl.Expr.False, new PODesc.IsNonRecursive())); + builder.Add(Assert(GetToken(expr), Bpl.Expr.False, new IsNonRecursive(), builder.Context)); } else { List contextDecreases = codeContext.Decreases.Expressions; List calleeDecreases = e.Function.Decreases.Expressions; @@ -912,8 +925,8 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, case SeqConstructionExpr constructionExpr: { var e = constructionExpr; CheckWellformed(e.N, wfOptions, locals, builder, etran); - var desc = new PODesc.NonNegative("sequence size", e.N); - builder.Add(Assert(GetToken(e.N), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(e.N)), desc)); + var desc = new NonNegative("sequence size", e.N); + builder.Add(Assert(GetToken(e.N), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(e.N)), desc, builder.Context)); CheckWellformed(e.Initializer, wfOptions, locals, builder, etran); var eType = e.Type.NormalizeToAncestorType().AsSeqType.Arg; @@ -947,18 +960,19 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, } else { Contract.Assert(ty.IsRefType); nonNull = Bpl.Expr.Neq(r, predef.Null); - builder.Add(Assert(GetToken(fe.E), BplImp(ante, nonNull), new PODesc.NonNull(description, fe.E, description != "object"))); + builder.Add(Assert(GetToken(fe.E), BplImp(ante, nonNull), + new NonNull(description, fe.E, description != "object"), builder.Context)); } // check that "r" was allocated in the "e.AtLabel" state Bpl.Expr wh = GetWhereClause(fe.E.tok, r, ty, etran.OldAt(e.AtLabel), ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated(description, "in the old-state of the 'unchanged' predicate", + var desc = new IsAllocated(description, "in the old-state of the 'unchanged' predicate", fe.E, e.AtLabel, description != "object"); - builder.Add(Assert(GetToken(fe.E), BplImp(BplAnd(ante, nonNull), wh), desc)); + builder.Add(Assert(GetToken(fe.E), BplImp(BplAnd(ante, nonNull), wh), desc, builder.Context)); } // check that the 'unchanged' argument reads only what the context is allowed to read if (wfOptions.DoReadsChecks) { - var desc = new PODesc.ReadFrameSubset($"read state of 'unchanged' {description}", fe, contextReadsFrames); + var desc = new ReadFrameSubset($"read state of 'unchanged' {description}", fe, contextReadsFrames); CheckFrameSubset(fe.E.tok, new List() { fe }, null, new Dictionary(), etran, etran.ReadsFrame(fe.E.tok), wfOptions.AssertSink(this, builder), @@ -1006,22 +1020,26 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, CheckWellformed(e.E1, wfOptions, locals, builder, etran); if (e.ResolvedOp == BinaryExpr.ResolvedOpcode.Sub && e.E0.Type.IsBigOrdinalType) { var rhsIsNat = FunctionCall(binaryExpr.tok, "ORD#IsNat", Bpl.Type.Bool, etran.TrExpr(e.E1)); - builder.Add(Assert(GetToken(expr), rhsIsNat, new PODesc.OrdinalSubtractionIsNatural(e.E1))); + builder.Add(Assert(GetToken(expr), rhsIsNat, + new OrdinalSubtractionIsNatural(e.E1), builder.Context)); var offset0 = FunctionCall(binaryExpr.tok, "ORD#Offset", Bpl.Type.Int, etran.TrExpr(e.E0)); var offset1 = FunctionCall(binaryExpr.tok, "ORD#Offset", Bpl.Type.Int, etran.TrExpr(e.E1)); - builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(offset1, offset0), new PODesc.OrdinalSubtractionUnderflow(e.E0, e.E1))); + builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(offset1, offset0), + new OrdinalSubtractionUnderflow(e.E0, e.E1), builder.Context)); } else if (e.Type.NormalizeToAncestorType().IsCharType) { var e0 = FunctionCall(binaryExpr.tok, "char#ToInt", Bpl.Type.Int, etran.TrExpr(e.E0)); var e1 = FunctionCall(binaryExpr.tok, "char#ToInt", Bpl.Type.Int, etran.TrExpr(e.E1)); if (e.ResolvedOp == BinaryExpr.ResolvedOpcode.Add) { builder.Add(Assert(GetToken(expr), FunctionCall(Token.NoToken, BuiltinFunction.IsChar, null, - Bpl.Expr.Binary(BinaryOperator.Opcode.Add, e0, e1)), new PODesc.CharOverflow(e.E0, e.E1))); + Bpl.Expr.Binary(BinaryOperator.Opcode.Add, e0, e1)), + new CharOverflow(e.E0, e.E1), builder.Context)); } else { Contract.Assert(e.ResolvedOp == BinaryExpr.ResolvedOpcode.Sub); // .Mul is not supported for char builder.Add(Assert(GetToken(expr), FunctionCall(Token.NoToken, BuiltinFunction.IsChar, null, - Bpl.Expr.Binary(BinaryOperator.Opcode.Sub, e0, e1)), new PODesc.CharUnderflow(e.E0, e.E1))); + Bpl.Expr.Binary(BinaryOperator.Opcode.Sub, e0, e1)), + new CharUnderflow(e.E0, e.E1), builder.Context)); } } break; @@ -1036,14 +1054,15 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, zero = Bpl.Expr.Literal(0); } CheckWellformed(e.E1, wfOptions, locals, builder, etran); - builder.Add(Assert(GetToken(expr), Bpl.Expr.Neq(etran.TrExpr(e.E1), zero), new PODesc.DivisorNonZero(e.E1), wfOptions.AssertKv)); + builder.Add(Assert(GetToken(expr), Bpl.Expr.Neq(etran.TrExpr(e.E1), zero), + new DivisorNonZero(e.E1), builder.Context, wfOptions.AssertKv)); } break; case BinaryExpr.ResolvedOpcode.LeftShift: case BinaryExpr.ResolvedOpcode.RightShift: { CheckWellformed(e.E1, wfOptions, locals, builder, etran); var w = e.Type.NormalizeToAncestorType().AsBitVectorType.Width; - var upperDesc = new PODesc.ShiftUpperBound(w, true, e.E1); + var upperDesc = new ShiftUpperBound(w, true, e.E1); if (e.E1.Type.NormalizeToAncestorType().AsBitVectorType is { } bitvectorType) { // Known to be non-negative, so we don't need to check lower bound. // Check upper bound, that is, check "E1 <= w" @@ -1052,7 +1071,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, // w is a number that can be represented in the e.E1.Type, so do the comparison in that bitvector type. var bound = BplBvLiteralExpr(e.tok, BaseTypes.BigNum.FromInt(w), e1Width); var cmp = etran.TrToFunctionCall(binaryExpr.tok, "le_bv" + e1Width, Bpl.Type.Bool, etran.TrExpr(e.E1), bound, false); - builder.Add(Assert(GetToken(expr), cmp, upperDesc, wfOptions.AssertKv)); + builder.Add(Assert(GetToken(expr), cmp, upperDesc, builder.Context, wfOptions.AssertKv)); } else { // In the previous branch, we had: // w < 2^e1Width (*) @@ -1065,9 +1084,11 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, // already holds, so there is no reason to check it. } } else { - var positiveDesc = new PODesc.ShiftLowerBound(true, e.E1); - builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(e.E1)), positiveDesc, wfOptions.AssertKv)); - builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(etran.TrExpr(e.E1), Bpl.Expr.Literal(w)), upperDesc, wfOptions.AssertKv)); + var positiveDesc = new ShiftLowerBound(true, e.E1); + builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(e.E1)), + positiveDesc, builder.Context, wfOptions.AssertKv)); + builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(etran.TrExpr(e.E1), Bpl.Expr.Literal(w)), + upperDesc, builder.Context, wfOptions.AssertKv)); } } break; @@ -1095,7 +1116,7 @@ void CheckOperand(Expression operand) { var notGhostCtor = BplAnd(ghostConstructors.ConvertAll( ctor => Bpl.Expr.Not(FunctionCall(expr.tok, ctor.QueryField.FullSanitizedName, Bpl.Type.Bool, value)))); builder.Add(Assert(GetToken(expr), notGhostCtor, - new PODesc.NotGhostVariant("equality", operand, ghostConstructors))); + new NotGhostVariant("equality", operand, ghostConstructors), builder.Context)); } CheckOperand(e.E0); @@ -1120,7 +1141,8 @@ void CheckOperand(Expression operand) { case TernaryExpr.Opcode.PrefixEqOp: case TernaryExpr.Opcode.PrefixNeqOp: if (e.E0.Type.IsNumericBased(Type.NumericPersuasion.Int)) { - builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(e.E0)), new PODesc.PrefixEqualityLimit(e.E0), wfOptions.AssertKv)); + builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(e.E0)), + new PrefixEqualityLimit(e.E0), builder.Context, wfOptions.AssertKv)); } break; default: @@ -1151,7 +1173,7 @@ void CheckOperand(Expression operand) { var comprehensionEtran = etran; if (lam != null) { // Havoc heap - locals.Add(BplLocalVar(CurrentIdGenerator.FreshId((etran.UsesOldHeap ? "$Heap_at_" : "") + "$lambdaHeap#"), predef.HeapType, out var lambdaHeap)); + locals.GetOrAdd(BplLocalVar(CurrentIdGenerator.FreshId((etran.UsesOldHeap ? "$Heap_at_" : "") + "$lambdaHeap#"), predef.HeapType, out var lambdaHeap)); comprehensionEtran = new ExpressionTranslator(comprehensionEtran, lambdaHeap); nextBuilder.Add(new HavocCmd(e.tok, Singleton((Bpl.IdentifierExpr)comprehensionEtran.HeapExpr))); nextBuilder.Add(new AssumeCmd(e.tok, FunctionCall(e.tok, BuiltinFunction.IsGoodHeap, null, comprehensionEtran.HeapExpr))); @@ -1204,7 +1226,7 @@ void CheckOperand(Expression operand) { if (lam != null) { var resultName = CurrentIdGenerator.FreshId("lambdaResult#"); var resultVar = new Bpl.LocalVariable(body.tok, new Bpl.TypedIdent(body.tok, resultName, TrType(body.Type))); - locals.Add(resultVar); + locals.GetOrAdd(resultVar); resultIe = new Bpl.IdentifierExpr(body.tok, resultVar); rangeType = lam.Type.AsArrowType.Result; } @@ -1232,7 +1254,8 @@ void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { var different = BplOr( Bpl.Expr.Neq(comprehensionEtran.TrExpr(bodyLeft), comprehensionEtran.TrExpr(bodyLeftPrime)), Bpl.Expr.Eq(comprehensionEtran.TrExpr(body), comprehensionEtran.TrExpr(bodyPrime))); - b.Add(Assert(GetToken(mc.TermLeft), different, new PODesc.ComprehensionNoAlias(mc.BoundVars, mc.Range, mc.TermLeft, mc.Term))); + b.Add(Assert(GetToken(mc.TermLeft), different, + new ComprehensionNoAlias(mc.BoundVars, mc.Range, mc.TermLeft, mc.Term), builder.Context)); }); } }); @@ -1302,7 +1325,7 @@ void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { // Every constructor has this destructor; no need to check anything } else { builder.Add(Assert(GetToken(expr), correctConstructor, - new PODesc.ValidConstructorNames(updateExpr.Root, e.LegalSourceConstructors))); + new ValidConstructorNames(updateExpr.Root, e.LegalSourceConstructors), builder.Context)); } CheckNotGhostVariant(e.InCompiledContext, updateExpr, e.Root, "update of", e.Members, @@ -1357,7 +1380,7 @@ public void CheckSubsetType(ExpressionTranslator etran, Expression expr, Bpl.Exp builder.Add(TrAssumeCmd(expr.tok, MkIs(selfCall, resultType))); } - private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, List locals, + private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, Variables locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, AddResultCommands addResultCommands) { FillMissingCases(me); @@ -1370,13 +1393,13 @@ private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, List local foreach (var missingCtor in me.MissingCases) { // havoc all bound variables var b = new BoogieStmtListBuilder(this, options, builder.Context); - List newLocals = new List(); + Variables newLocals = new(); Bpl.Expr r = CtorInvocation(me.tok, missingCtor, etran, newLocals, b); - locals.AddRange(newLocals); + locals.AddRange(newLocals.Values); if (newLocals.Count != 0) { List havocIds = new List(); - foreach (Variable local in newLocals) { + foreach (Variable local in newLocals.Values) { havocIds.Add(new Bpl.IdentifierExpr(local.tok, local)); } @@ -1384,7 +1407,8 @@ private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, List local } String missingStr = me.Context.FillHole(new IdCtx(missingCtor)).AbstractAllHoles().ToString(); - b.Add(Assert(GetToken(me), Bpl.Expr.False, new PODesc.MatchIsComplete("expression", missingStr))); + b.Add(Assert(GetToken(me), Bpl.Expr.False, + new MatchIsComplete("expression", missingStr), builder.Context)); Bpl.Expr guard = Bpl.Expr.Eq(src, r); ifCmd = new Bpl.IfCmd(me.tok, guard, b.Collect(me.tok), ifCmd, els); @@ -1405,7 +1429,7 @@ private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, List local builder.Add(ifCmd); } - private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions wfOptions, List locals, + private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions wfOptions, Variables locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, AddResultCommands addResultCommands) { var statements = new List() { stmtExpr.S }; @@ -1442,11 +1466,10 @@ private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions wfOptions, Lis /// end of the code generated. (Any other exit would cause control flow to miss /// BuildWithHeapAs's assignment that restores the value of $Heap.) /// - void BuildWithHeapAs(IToken token, Bpl.Expr temporaryHeap, string heapVarSuffix, List locals, + void BuildWithHeapAs(IToken token, Bpl.Expr temporaryHeap, string heapVarSuffix, Variables locals, BoogieStmtListBuilder builder, System.Action build) { var suffix = CurrentIdGenerator.FreshId(heapVarSuffix); - var tmpHeapVar = new Bpl.LocalVariable(token, new Bpl.TypedIdent(token, "Heap$" + suffix, predef.HeapType)); - locals.Add(tmpHeapVar); + var tmpHeapVar = locals.GetOrAdd(new Bpl.LocalVariable(token, new Bpl.TypedIdent(token, "Heap$" + suffix, predef.HeapType))); var tmpHeap = new Bpl.IdentifierExpr(token, tmpHeapVar); var generalEtran = new ExpressionTranslator(this, predef, token, null); var theHeap = generalEtran.HeapCastToIdentifierExpr; @@ -1478,16 +1501,16 @@ private void CheckNotGhostVariant(bool inCompiledContext, Expression exprUsedFor var notGhostCtor = BplAnd(enclosingGhostConstructors.ConvertAll( ctor => Bpl.Expr.Not(FunctionCall(exprUsedForToken.tok, ctor.QueryField.FullSanitizedName, Bpl.Type.Bool, obj)))); builder.Add(Assert(GetToken(exprUsedForToken), notGhostCtor, - new PODesc.NotGhostVariant(whatKind, + new NotGhostVariant(whatKind, Util.PrintableNameList(members.ConvertAll(member => member.Name), "and"), datatypeValue, - enclosingGhostConstructors))); + enclosingGhostConstructors), builder.Context)); } } } void CheckWellformedSpecialFunction(FunctionCallExpr expr, WFOptions options, Bpl.Expr result, Type resultType, - List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { + Variables locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { Contract.Requires(expr.Function is SpecialFunction); CheckWellformed(expr.Receiver, options, locals, builder, etran); @@ -1497,15 +1520,16 @@ void CheckWellformedSpecialFunction(FunctionCallExpr expr, WFOptions options, Bp if (expr.Function.Name is "RotateLeft" or "RotateRight") { var w = expr.Type.AsBitVectorType.Width; var arg = expr.Args[0]; - builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(arg)), new PODesc.ShiftLowerBound(false, arg), options.AssertKv)); - builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(etran.TrExpr(arg), Bpl.Expr.Literal(w)), new PODesc.ShiftUpperBound(w, false, arg), - options.AssertKv)); + builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(arg)), + new ShiftLowerBound(false, arg), builder.Context, options.AssertKv)); + builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(etran.TrExpr(arg), Bpl.Expr.Literal(w)), + new ShiftUpperBound(w, false, arg), builder.Context, options.AssertKv)); } } delegate void AddResultCommands(BoogieStmtListBuilder builder, Expression result); - void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, List locals, + void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, Variables locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, bool checkRhs, AddResultCommands addResultCommands) { if (e.Exact) { var substMap = SetupBoundVarsAsLocals(e.BoundVars.ToList(), builder, locals, etran, "#Z"); @@ -1515,8 +1539,7 @@ void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, List fes, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { + void CheckFrameWellFormed(WFOptions wfo, List fes, Variables locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { Contract.Requires(fes != null); Contract.Requires(locals != null); Contract.Requires(builder != null); @@ -1593,7 +1616,7 @@ void CheckFrameWellFormed(WFOptions wfo, List fes, List dims, // check precond var pre = FunctionCall(tok, Requires(dims.Count), Bpl.Type.Bool, args); var q = new Bpl.ForallExpr(tok, bvs, BplImp(ante, pre)); - var indicesDesc = new PODesc.IndicesInDomain(forArray ? "array" : "sequence", dims, init); - builder.Add(AssertNS(tok, q, indicesDesc)); + var indicesDesc = new IndicesInDomain(forArray ? "array" : "sequence", dims, init); + builder.Add(AssertAndForget(builder.Context, tok, q, indicesDesc)); if (!forArray && options.DoReadsChecks) { // unwrap renamed local lambdas var unwrappedFunc = init; @@ -1656,12 +1679,12 @@ private void CheckElementInit(IToken tok, bool forArray, List dims, FunctionCall(tok, Reads(1), TrType(objset), args), objset); var reads = new FrameExpression(tok, wrap, null); - Action maker = (t, e, d, qk) => { + Action maker = (t, e, d, qk) => { var qe = new Bpl.ForallExpr(t, bvs, BplImp(ante, e)); options.AssertSink(this, builder)(t, qe, d, qk); }; - PODesc.Utils.MakeQuantifierVarsForDims(dims, out var indexVars, out var indexVarExprs, out var indicesRange); + Utils.MakeQuantifierVarsForDims(dims, out var indexVars, out var indexVarExprs, out var indicesRange); var readsCall = new ApplyExpr( Token.NoToken, new ExprDotName(Token.NoToken, unwrappedFunc, "reads", null), @@ -1675,10 +1698,10 @@ private void CheckElementInit(IToken tok, bool forArray, List dims, RangeToken.NoToken, indexVars, indicesRange, - PODesc.Utils.MakeDafnyFrameCheck(contextReads, readsCall, null), + Utils.MakeDafnyFrameCheck(contextReads, readsCall, null), null ); - var readsDesc = new PODesc.ReadFrameSubset("invoke the function passed as an argument to the sequence constructor", readsDescExpr); + var readsDesc = new ReadFrameSubset("invoke the function passed as an argument to the sequence constructor", readsDescExpr); CheckFrameSubset(tok, new List { reads }, null, null, etran, etran.ReadsFrame(tok), maker, readsDesc, options.AssertKv); } @@ -1691,7 +1714,7 @@ private void CheckElementInit(IToken tok, bool forArray, List dims, // assert (forall i0,i1,i2,... :: // 0 <= i0 < ... && ... ==> init.requires(i0,i1,i2,...) is Subtype); q = new Bpl.ForallExpr(tok, bvs, BplImp(ante, cre)); - builder.Add(AssertNS(init.tok, q, subrangeDesc)); + builder.Add(AssertAndForget(builder.Context, init.tok, q, subrangeDesc)); } if (forArray) { diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Fields.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Fields.cs index f6fe288ec92..9931ef0d429 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Fields.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Fields.cs @@ -205,9 +205,9 @@ void AddWellformednessCheck(ConstantField decl) { sink.AddTopLevelDeclaration(proc); var implInParams = Bpl.Formal.StripWhereClauses(inParams); - var locals = new List(); + var locals = new Variables(); var builder = new BoogieStmtListBuilder(this, options, new BodyTranslationContext(false)); - builder.Add(new CommentCmd(string.Format("AddWellformednessCheck for {0} {1}", decl.WhatKind, decl))); + builder.Add(new CommentCmd($"AddWellformednessCheck for {decl.WhatKind} {decl}")); builder.AddCaptureState(decl.tok, false, "initial state"); isAllocContext = new IsAllocContext(options, true); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs index d2b73c28414..6dd5b67cb3c 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs @@ -46,7 +46,7 @@ public void Check(Function f) { foreach (AttributedExpression ensures in f.Ens) { var functionHeight = generator.currentModule.CallGraph.GetSCCRepresentativePredecessorCount(f); var splits = new List(); - bool splitHappened /*we actually don't care*/ = generator.TrSplitExpr(context, ensures.E, splits, true, functionHeight, true, true, etran); + bool splitHappened /*we actually don't care*/ = generator.TrSplitExpr(context, ensures.E, splits, true, functionHeight, true, etran); var (errorMessage, successMessage) = generator.CustomErrorMessage(ensures.Attributes); foreach (var s in splits) { if (s.IsChecked && !RefinementToken.IsInherited(s.Tok, generator.currentModule)) { @@ -60,7 +60,7 @@ public void Check(Function f) { var (olderParameterCount, olderCondition) = generator.OlderCondition(f, selfCall, procedureParameters); if (olderParameterCount != 0) { generator.AddEnsures(ens, new Ensures(f.tok, false, olderCondition, null) { - Description = new PODesc.IsOlderProofObligation(olderParameterCount, f.Ins.Count + (f.IsStatic ? 0 : 1)) + Description = new IsOlderProofObligation(olderParameterCount, f.Ins.Count + (f.IsStatic ? 0 : 1)) }); } @@ -78,7 +78,7 @@ public void Check(Function f) { Contract.Assert(proc.InParams.Count == typeInParams.Count + heapParameters.Count + procedureParameters.Count); // Changed the next line to strip from inParams instead of proc.InParams // They should be the same, but hence the added contract - var locals = new List(); + var locals = new Variables(); var builder = new BoogieStmtListBuilder(generator, generator.options, context); var builderInitializationArea = new BoogieStmtListBuilder(generator, generator.options, context); if (f is TwoStateFunction) { @@ -107,8 +107,8 @@ public void Check(Function f) { if (formal.IsOld) { Expr wh = generator.GetWhereClause(e.tok, etran.TrExpr(e), e.Type, etran.Old, ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("default value", "in the two-state function's previous state", e); - builder.Add(generator.Assert(generator.GetToken(e), wh, desc)); + var desc = new IsAllocated("default value", "in the two-state function's previous state", e); + builder.Add(generator.Assert(generator.GetToken(e), wh, desc, builder.Context)); } } } @@ -173,12 +173,12 @@ public void Check(Function f) { private void ConcurrentAttributeCheck(Function f, ExpressionTranslator etran, BoogieStmtListBuilder builder) { // If the function is marked as {:concurrent}, check that the reads clause is empty. if (Attributes.Contains(f.Attributes, Attributes.ConcurrentAttributeName)) { - var desc = new PODesc.ConcurrentFrameEmpty(f, "reads"); + var desc = new ConcurrentFrameEmpty(f, "reads"); generator.CheckFrameEmpty(f.tok, etran, etran.ReadsFrame(f.tok), builder, desc, null); } } - private void CheckBodyAndEnsuresClauseWellformedness(Function f, ExpressionTranslator etran, List locals, List inParams, + private void CheckBodyAndEnsuresClauseWellformedness(Function f, ExpressionTranslator etran, Variables locals, List inParams, BoogieStmtListBuilder builderInitializationArea, BoogieStmtListBuilder builder) { builder.Add(new CommentCmd("Check body and ensures clauses")); // Generate: @@ -201,7 +201,7 @@ private void CheckBodyAndEnsuresClauseWellformedness(Function f, ExpressionTrans private BoogieStmtListBuilder GetBodyCheckBuilder(Function f, ExpressionTranslator etran, List parameters, - List locals, BoogieStmtListBuilder builderInitializationArea) { + Variables locals, BoogieStmtListBuilder builderInitializationArea) { var selfCall = GetSelfCall(f, etran, parameters); var context = new BodyTranslationContext(f.ContainsHide); var bodyCheckBuilder = new BoogieStmtListBuilder(generator, generator.options, context); @@ -230,7 +230,7 @@ void CheckPostcondition(BoogieStmtListBuilder innerBuilder, Expression innerBody generator.CheckWellformedWithResult(f.Body, wfo, locals, bodyCheckBuilder, etran, CheckPostcondition); // var b$reads_guards#0 : bool ... - locals.AddRange(wfo.Locals); + locals.AddRange(wfo.Locals.Values); // b$reads_guards#0 := true ... foreach (var cmd in wfo.AssignLocals) { builderInitializationArea.Add(cmd); @@ -272,7 +272,7 @@ private Expr GetSelfCall(Function f, ExpressionTranslator etran, List return funcAppl; } - private BoogieStmtListBuilder GetPostCheckBuilder(Function f, ExpressionTranslator etran, List locals) { + private BoogieStmtListBuilder GetPostCheckBuilder(Function f, ExpressionTranslator etran, Variables locals) { var context = new BodyTranslationContext(f.ContainsHide); var postCheckBuilder = new BoogieStmtListBuilder(generator, generator.options, context); postCheckBuilder.Add(new CommentCmd("Check well-formedness of postcondition and assume false")); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs index 6ba4aef2c77..70fc46b6243 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs @@ -72,8 +72,8 @@ Bpl.Procedure AddIteratorProc(IteratorDecl iter, MethodTranslationKind kind) { var etran = new ExpressionTranslator(this, predef, iter.tok, iter); var inParams = new List(); - List outParams; - GenerateMethodParametersChoose(iter.tok, iter, kind, true, true, false, etran, inParams, out outParams); + GenerateMethodParametersChoose(iter.tok, iter, kind, + true, true, false, etran, inParams, out var outParams); var req = new List(); var mod = new List(); @@ -95,7 +95,7 @@ Bpl.Procedure AddIteratorProc(IteratorDecl iter, MethodTranslationKind kind) { p.Label.E = etran.Old.TrExpr(p.E); } else { foreach (var s in TrSplitExprForMethodSpec(new BodyTranslationContext(false), p.E, etran, kind)) { - if (kind == MethodTranslationKind.CallPre && RefinementToken.IsInherited(s.Tok, currentModule)) { + if (kind == MethodTranslationKind.Call && RefinementToken.IsInherited(s.Tok, currentModule)) { // this precondition was inherited into this module, so just ignore it } else { req.Add(Requires(s.Tok, s.IsOnlyFree, p.E, s.E, errorMessage, successMessage, comment)); @@ -122,7 +122,8 @@ Bpl.Procedure AddIteratorProc(IteratorDecl iter, MethodTranslationKind kind) { } var name = MethodName(iter, kind); - var proc = new Bpl.Procedure(iter.tok, name, new List(), inParams, outParams, false, req, mod, ens, etran.TrAttributes(iter.Attributes, null)); + var proc = new Bpl.Procedure(iter.tok, name, new List(), + inParams, outParams.Values.ToList(), false, req, mod, ens, etran.TrAttributes(iter.Attributes, null)); AddVerboseNameAttribute(proc, iter.FullDafnyName, kind); currentModule = null; @@ -150,7 +151,7 @@ void AddIteratorWellformednessCheck(IteratorDecl iter, Procedure proc) { // Don't do reads checks since iterator reads clauses mean something else. // See comment inside GenerateIteratorImplPrelude(). etran = etran.WithReadsFrame(null); - var localVariables = new List(); + var localVariables = new Variables(); GenerateIteratorImplPrelude(iter, inParams, new List(), builder, localVariables, etran); // check well-formedness of any default-value expressions (before assuming preconditions) @@ -182,8 +183,8 @@ void AddIteratorWellformednessCheck(IteratorDecl iter, Procedure proc) { var th = new ThisExpr(iter); // resolve here var rds = new MemberSelectExpr(iter.tok, th, iter.Member_Reads); var mod = new MemberSelectExpr(iter.tok, th, iter.Member_Modifies); - builder.Add(new Bpl.CallCmd(iter.tok, "$IterHavoc0", - new List() { etran.TrExpr(th), etran.TrExpr(rds), etran.TrExpr(mod) }, + builder.Add(Call(builder.Context, iter.tok, "$IterHavoc0", + new List() { etran.TrExpr(th), etran.TrExpr(rds), etran.TrExpr(mod) }, new List())); // assume the automatic yield-requires precondition (which is always well-formed): this.Valid() @@ -201,12 +202,11 @@ void AddIteratorWellformednessCheck(IteratorDecl iter, Procedure proc) { } // save the heap (representing the state where yield-requires holds): $_OldIterHeap := Heap; - var oldIterHeap = new Bpl.LocalVariable(iter.tok, new Bpl.TypedIdent(iter.tok, "$_OldIterHeap", predef.HeapType)); - localVariables.Add(oldIterHeap); + var oldIterHeap = localVariables.GetOrAdd(new Bpl.LocalVariable(iter.tok, new Bpl.TypedIdent(iter.tok, "$_OldIterHeap", predef.HeapType))); builder.Add(Bpl.Cmd.SimpleAssign(iter.tok, new Bpl.IdentifierExpr(iter.tok, oldIterHeap), etran.HeapExpr)); // simulate a modifies this, this._modifies, this._new; var nw = new MemberSelectExpr(iter.tok, th, iter.Member_New); - builder.Add(new Bpl.CallCmd(iter.tok, "$IterHavoc1", + builder.Add(Call(builder.Context, iter.tok, "$IterHavoc1", new List() { etran.TrExpr(th), etran.TrExpr(mod), etran.TrExpr(nw) }, new List())); // assume the implicit postconditions promised by MoveNext: @@ -284,7 +284,7 @@ void AddIteratorImpl(IteratorDecl iter, Bpl.Procedure proc) { // Don't do reads checks since iterator reads clauses mean something else. // See comment inside GenerateIteratorImplPrelude(). etran = etran.WithReadsFrame(null); - var localVariables = new List(); + var localVariables = new Variables(); GenerateIteratorImplPrelude(iter, inParams, new List(), builder, localVariables, etran); // add locals for the yield-history variables and the extra variables @@ -302,17 +302,16 @@ void AddIteratorImpl(IteratorDecl iter, Bpl.Procedure proc) { builder.Add(TrAssumeCmdWithDependencies(etran, p.E.tok, p.E, "iterator constructor ensures clause")); } // add the _yieldCount variable, and assume its initial value to be 0 - yieldCountVariable = new Bpl.LocalVariable(iter.tok, - new Bpl.TypedIdent(iter.tok, iter.YieldCountVariable.AssignUniqueName(currentDeclaration.IdGenerator), TrType(iter.YieldCountVariable.Type))); + yieldCountVariable = (Bpl.LocalVariable)localVariables.GetOrAdd(new Bpl.LocalVariable(iter.tok, + new Bpl.TypedIdent(iter.tok, iter.YieldCountVariable.AssignUniqueName(currentDeclaration.IdGenerator), TrType(iter.YieldCountVariable.Type)))); yieldCountVariable.TypedIdent.WhereExpr = YieldCountAssumption(iter, etran); // by doing this after setting "yieldCountVariable", the variable can be used by YieldCountAssumption - localVariables.Add(yieldCountVariable); builder.Add(TrAssumeCmd(iter.tok, Bpl.Expr.Eq(new Bpl.IdentifierExpr(iter.tok, yieldCountVariable), Bpl.Expr.Literal(0)))); // add a variable $_OldIterHeap var oih = new Bpl.IdentifierExpr(iter.tok, "$_OldIterHeap", predef.HeapType); Bpl.Expr wh = BplAnd( FunctionCall(iter.tok, BuiltinFunction.IsGoodHeap, null, oih), HeapSucc(oih, etran.HeapExpr)); - localVariables.Add(new Bpl.LocalVariable(iter.tok, new Bpl.TypedIdent(iter.tok, "$_OldIterHeap", predef.HeapType, wh))); + localVariables.GetOrAdd(new Bpl.LocalVariable(iter.tok, new Bpl.TypedIdent(iter.tok, "$_OldIterHeap", predef.HeapType, wh))); // do an initial YieldHavoc YieldHavoc(iter.tok, iter, builder, etran); @@ -349,7 +348,7 @@ Bpl.Expr YieldCountAssumption(IteratorDecl iter, ExpressionTranslator etran) { } void GenerateIteratorImplPrelude(IteratorDecl iter, List inParams, List outParams, - BoogieStmtListBuilder builder, List localVariables, ExpressionTranslator etran) { + BoogieStmtListBuilder builder, Variables localVariables, ExpressionTranslator etran) { Contract.Requires(iter != null); Contract.Requires(inParams != null); Contract.Requires(outParams != null); @@ -387,7 +386,7 @@ void YieldHavoc(IToken tok, IteratorDecl iter, BoogieStmtListBuilder builder, Ex var th = new ThisExpr(iter); var rds = new MemberSelectExpr(tok, th, iter.Member_Reads); var nw = new MemberSelectExpr(tok, th, iter.Member_New); - builder.Add(new Bpl.CallCmd(tok, "$YieldHavoc", + builder.Add(Call(builder.Context, tok, "$YieldHavoc", new List() { etran.TrExpr(th), etran.TrExpr(rds), etran.TrExpr(nw) }, new List())); // assume YieldRequires; diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 8948fff7be9..d6fc9b6adaf 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -193,16 +193,13 @@ void AddMethod_Top(Method m, bool isByMethod, bool includeAllMethods) { } // the method spec itself if (!isByMethod) { - sink.AddTopLevelDeclaration(AddMethod(m, MethodTranslationKind.CallPre)); - sink.AddTopLevelDeclaration(AddMethod(m, MethodTranslationKind.CallPost)); - + sink.AddTopLevelDeclaration(AddMethod(m, MethodTranslationKind.Call)); } if (m is ExtremeLemma) { // Let the CoCall and Impl forms to use m.PrefixLemma signature and specification (and // note that m.PrefixLemma.Body == m.Body. m = ((ExtremeLemma)m).PrefixLemma; - sink.AddTopLevelDeclaration(AddMethod(m, MethodTranslationKind.CoCallPre)); - sink.AddTopLevelDeclaration(AddMethod(m, MethodTranslationKind.CoCallPost)); + sink.AddTopLevelDeclaration(AddMethod(m, MethodTranslationKind.CoCall)); } if (!m.HasVerifyFalseAttribute && m.Body != null && InVerificationScope(m)) { @@ -555,14 +552,13 @@ private void AddMethodImpl(Method m, Bpl.Procedure proc, bool wellformednessProc etran = etran.WithReadsFrame(null); } InitializeFuelConstant(m.tok, builder, etran); - var localVariables = new List(); + var localVariables = new Variables(); GenerateImplPrelude(m, wellformednessProc, inParams, outParams, builder, localVariables, etran); if (UseOptimizationInZ3) { // We ask Z3 to minimize all parameters of type 'nat'. foreach (var f in m.Ins) { - var udt = f.Type.NormalizeExpandKeepConstraints() as UserDefinedType; - if (udt != null && udt.Name == "nat") { + if (f.Type.NormalizeExpandKeepConstraints() is UserDefinedType udt && udt.Name == "nat") { builder.Add(optimizeExpr(true, new IdentifierExpr(f.tok, f), f.Tok, etran)); } } @@ -587,7 +583,7 @@ private void AddMethodImpl(Method m, Bpl.Procedure proc, bool wellformednessProc Reset(); } - private StmtList TrMethodContractWellformednessCheck(Method m, ExpressionTranslator etran, List localVariables, + private StmtList TrMethodContractWellformednessCheck(Method m, ExpressionTranslator etran, Variables localVariables, BoogieStmtListBuilder builder, List outParams) { var builderInitializationArea = new BoogieStmtListBuilder(this, options, builder.Context); StmtList stmts; @@ -604,8 +600,8 @@ private StmtList TrMethodContractWellformednessCheck(Method m, ExpressionTransla if (formal.IsOld) { Boogie.Expr wh = GetWhereClause(e.tok, etran.TrExpr(e), e.Type, etran.Old, ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("default value", "in the two-state lemma's previous state", e); - builder.Add(Assert(e.RangeToken, wh, desc)); + var desc = new IsAllocated("default value", "in the two-state lemma's previous state", e); + builder.Add(Assert(e.RangeToken, wh, desc, builder.Context)); } } } @@ -626,13 +622,13 @@ private StmtList TrMethodContractWellformednessCheck(Method m, ExpressionTransla // on the method, and {:assume_concurrent} is NOT present on the reads clause. if (Attributes.Contains(m.Attributes, Attributes.ConcurrentAttributeName) && !Attributes.Contains(m.Reads.Attributes, Attributes.AssumeConcurrentAttributeName)) { - var desc = new PODesc.ConcurrentFrameEmpty(m, "reads"); + var desc = new ConcurrentFrameEmpty(m, "reads"); if (etran.readsFrame != null) { CheckFrameEmpty(m.tok, etran, etran.ReadsFrame(m.tok), builder, desc, null); } else { // etran.readsFrame being null indicates the default of reads *, // so this is an automatic failure. - builder.Add(Assert(m.tok, Expr.False, desc)); + builder.Add(Assert(m.tok, Expr.False, desc, builder.Context)); } } @@ -644,7 +640,7 @@ private StmtList TrMethodContractWellformednessCheck(Method m, ExpressionTransla // and {:assume_concurrent} is NOT present on the modifies clause. if (Attributes.Contains(m.Attributes, Attributes.ConcurrentAttributeName) && !Attributes.Contains(m.Mod.Attributes, Attributes.AssumeConcurrentAttributeName)) { - var desc = new PODesc.ConcurrentFrameEmpty(m, "modifies"); + var desc = new ConcurrentFrameEmpty(m, "modifies"); CheckFrameEmpty(m.tok, etran, etran.ModifiesFrame(m.tok), builder, desc, null); } @@ -699,7 +695,7 @@ public void ApplyModifiesEffect(INode node, ExpressionTranslator etran, BoogieSt } } - private StmtList TrMethodBody(Method m, BoogieStmtListBuilder builder, List localVariables, + private StmtList TrMethodBody(Method m, BoogieStmtListBuilder builder, Variables localVariables, ExpressionTranslator etran) { var inductionVars = ApplyInduction(m.Ins, m.Attributes); if (inductionVars.Count != 0) { @@ -786,8 +782,8 @@ private StmtList TrMethodBody(Method m, BoogieStmtListBuilder builder, List AddExistingDefiniteAssignmentTracker(p, m.IsGhost)); // translate the body TrStmt(m.Body, builder, localVariables, etran); @@ -810,10 +807,7 @@ private StmtList TrMethodBody(Method m, BoogieStmtListBuilder builder, List(); + var localVariables = new Variables(); InitializeFuelConstant(m.tok, builder, etran); // assume traitTypeParameter == G(overrideTypeParameters); @@ -903,7 +897,7 @@ private void AddMethodOverrideCheckImpl(Method m, Boogie.Procedure proc) { Reset(); } - private void HavocMethodFrameLocations(Method m, BoogieStmtListBuilder builder, ExpressionTranslator etran, List localVariables) { + private void HavocMethodFrameLocations(Method m, BoogieStmtListBuilder builder, ExpressionTranslator etran, Variables localVariables) { Contract.Requires(m != null); Contract.Requires(m.EnclosingClass != null && m.EnclosingClass is ClassLikeDecl); @@ -1019,7 +1013,7 @@ private void AddFunctionOverrideCheckImpl(Function f) { //List outParams = Bpl.Formal.StripWhereClauses(proc.OutParams); BoogieStmtListBuilder builder = new BoogieStmtListBuilder(this, options, new BodyTranslationContext(false)); - List localVariables = new List(); + var localVariables = new Variables(); InitializeFuelConstant(f.tok, builder, etran); @@ -1137,12 +1131,12 @@ private void AddFunctionOverrideEnsChk(Function f, BoogieStmtListBuilder builder var constraint = allOverrideEns == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Imp, allOverrideEns, subEn); - builder.Add(Assert(f.tok, s.E, new PODesc.FunctionContractOverride(true, constraint))); + builder.Add(Assert(f.tok, s.E, new FunctionContractOverride(true, constraint), builder.Context)); } } } - private void AddOverrideCheckTypeArgumentInstantiations(MemberDecl member, BoogieStmtListBuilder builder, List localVariables) { + private void AddOverrideCheckTypeArgumentInstantiations(MemberDecl member, BoogieStmtListBuilder builder, Variables localVariables) { Contract.Requires(member is Function || member is Method); Contract.Requires(member.EnclosingClass is TopLevelDeclWithMembers); Contract.Requires(builder != null); @@ -1161,15 +1155,14 @@ private void AddOverrideCheckTypeArgumentInstantiations(MemberDecl member, Boogi } var typeMap = GetTypeArgumentSubstitutionMap(overriddenMember, member); foreach (var tp in Util.Concat(overriddenMember.EnclosingClass.TypeArgs, overriddenTypeParameters)) { - var local = BplLocalVar(NameTypeParam(tp), predef.Ty, out var lhs); - localVariables.Add(local); + localVariables.GetOrAdd(BplLocalVar(NameTypeParam(tp), predef.Ty, out var lhs)); var rhs = TypeToTy(typeMap[tp]); builder.Add(new Boogie.AssumeCmd(tp.tok, Boogie.Expr.Eq(lhs, rhs))); } } - private void AddFunctionOverrideSubsetChk(Function func, BoogieStmtListBuilder builder, ExpressionTranslator etran, List localVariables, + private void AddFunctionOverrideSubsetChk(Function func, BoogieStmtListBuilder builder, ExpressionTranslator etran, Variables localVariables, Dictionary substMap, Dictionary typeMap) { //getting framePrime @@ -1189,8 +1182,7 @@ private void AddFunctionOverrideSubsetChk(Function func, BoogieStmtListBuilder b Bpl.IdentifierExpr traitFrame = etran.ReadsFrame(func.OverriddenFunction.tok); // this is a throw-away expression, used only to extract the type and name of the $_ReadsFrame variable traitFrame.Name = func.EnclosingClass.Name + "_" + traitFrame.Name; Contract.Assert(traitFrame.Type != null); // follows from the postcondition of ReadsFrame - Bpl.LocalVariable frame = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, null ?? traitFrame.Name, traitFrame.Type)); - localVariables.Add(frame); + var frame = localVariables.GetOrAdd(new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, null ?? traitFrame.Name, traitFrame.Type))); // $_ReadsFrame := (lambda $o: ref, $f: Field :: $o != null && $Heap[$o,alloc] ==> ($o,$f) in Modifies/Reads-Clause); Bpl.BoundVariable oVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$o", predef.RefType)); Bpl.IdentifierExpr o = new Bpl.IdentifierExpr(tok, oVar); @@ -1209,7 +1201,8 @@ private void AddFunctionOverrideSubsetChk(Function func, BoogieStmtListBuilder b Bpl.Expr consequent2 = InRWClause(tok, o, f, traitFrameExps, etran, null, null); Bpl.Expr q = new Bpl.ForallExpr(tok, new List(), new List { oVar, fVar }, BplImp(BplAnd(ante, oInCallee), consequent2)); - builder.Add(Assert(tok, q, new PODesc.TraitFrame(func.WhatKind, false, func.Reads.Expressions, traitFrameExps), kv)); + var description = new TraitFrame(func.WhatKind, false, func.Reads.Expressions, traitFrameExps); + builder.Add(Assert(tok, q, description, builder.Context, kv)); } private void AddFunctionOverrideReqsChk(Function f, BoogieStmtListBuilder builder, ExpressionTranslator etran, @@ -1236,7 +1229,7 @@ private void AddFunctionOverrideReqsChk(Function f, BoogieStmtListBuilder builde var constraint = allTraitReqs == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Imp, allTraitReqs, req.E); - builder.Add(Assert(f.tok, s.E, new PODesc.FunctionContractOverride(false, constraint))); + builder.Add(Assert(f.tok, s.E, new FunctionContractOverride(false, constraint), builder.Context)); } } } @@ -1484,7 +1477,7 @@ private void AddMethodOverrideEnsChk(Method m, BoogieStmtListBuilder builder, Ex var constraint = allOverrideEns == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Imp, allOverrideEns, subEn); - builder.Add(Assert(m.RangeToken, s.E, new PODesc.EnsuresStronger(constraint))); + builder.Add(Assert(m.RangeToken, s.E, new EnsuresStronger(constraint), builder.Context)); } } } @@ -1513,7 +1506,7 @@ private void AddMethodOverrideReqsChk(Method m, BoogieStmtListBuilder builder, E var constraint = allTraitReqs == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Imp, allTraitReqs, req.E); - builder.Add(Assert(m.RangeToken, s.E, new PODesc.RequiresWeaker(constraint))); + builder.Add(Assert(m.RangeToken, s.E, new RequiresWeaker(constraint), builder.Context)); } } } @@ -1596,11 +1589,11 @@ private void AddOverrideTerminationChk(ICallable original, ICallable overryd, Bo contextDecreases.Select(sub.Substitute).ToList(), calleeDecreases, true); - var desc = new PODesc.TraitDecreases(original.WhatKind, assertedExpr); - builder.Add(Assert(original.RangeToken, decrChk, desc)); + var desc = new TraitDecreases(original.WhatKind, assertedExpr); + builder.Add(Assert(original.RangeToken, decrChk, desc, builder.Context)); } - private void AddMethodOverrideFrameSubsetChk(Method m, bool isModifies, BoogieStmtListBuilder builder, ExpressionTranslator etran, List localVariables, + private void AddMethodOverrideFrameSubsetChk(Method m, bool isModifies, BoogieStmtListBuilder builder, ExpressionTranslator etran, Variables localVariables, Dictionary substMap, Dictionary typeMap) { @@ -1642,7 +1635,8 @@ private void AddMethodOverrideFrameSubsetChk(Method m, bool isModifies, BoogieSt var consequent2 = InRWClause(tok, o, f, traitFrameExps, etran, null, null); var q = new Boogie.ForallExpr(tok, new List(), new List { oVar, fVar }, BplImp(BplAnd(ante, oInCallee), consequent2)); - builder.Add(Assert(m.RangeToken, q, new PODesc.TraitFrame(m.WhatKind, isModifies, classFrameExps, traitFrameExps), kv)); + var description = new TraitFrame(m.WhatKind, isModifies, classFrameExps, traitFrameExps); + builder.Add(Assert(m.RangeToken, q, description, builder.Context, kv)); } // Return a way to know if an assertion should be converted to an assumption @@ -1723,6 +1717,25 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { GenerateMethodParameters(m.tok, m, kind, etran, inParams, out var outParams); + + var name = MethodName(m, kind); + var req = GetRequires(); + var mod = new List { ordinaryEtran.HeapCastToIdentifierExpr }; + var ens = GetEnsures(); + var proc = new Bpl.Procedure(m.tok, name, new List(), + inParams, outParams.Values.ToList(), false, req, mod, ens, etran.TrAttributes(m.Attributes, null)); + AddVerboseNameAttribute(proc, m.FullDafnyName, kind); + + if (InsertChecksums) { + InsertChecksum(m, proc, true); + } + + currentModule = null; + codeContext = null; + isAllocContext = null; + + return proc; + List GetRequires() { var req = new List(); // FREE PRECONDITIONS @@ -1748,8 +1761,8 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { if (formal.IsOld) { var dafnyFormalIdExpr = new IdentifierExpr(formal.tok, formal); var pIdx = m.Ins.Count == 1 ? "" : " at index " + index; - var desc = new PODesc.IsAllocated($"argument{pIdx} for parameter '{formal.Name}'", - "in the two-state lemma's previous state" + PODesc.IsAllocated.HelperFormal(formal), + var desc = new IsAllocated($"argument{pIdx} for parameter '{formal.Name}'", + "in the two-state lemma's previous state" + IsAllocated.HelperFormal(formal), dafnyFormalIdExpr ); var require = Requires(formal.tok, false, null, MkIsAlloc(etran.TrExpr(dafnyFormalIdExpr), formal.Type, prevHeap), @@ -1791,8 +1804,8 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { return req; } - List GetEnsures() { - var ens = new List(); + List GetEnsures() { + var ens = new List(); if (kind is MethodTranslationKind.SpecWellformedness or MethodTranslationKind.OverrideCheck) { return ens; } @@ -1818,7 +1831,7 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { } } } - if (m is Constructor && kind == MethodTranslationKind.CallPost) { + if (m is Constructor && kind == MethodTranslationKind.Call) { var dafnyFresh = new OldExpr(Token.NoToken, new UnaryOpExpr(Token.NoToken, UnaryOpExpr.Opcode.Not, new UnaryOpExpr(Token.NoToken, UnaryOpExpr.Opcode.Allocated, new IdentifierExpr(Token.NoToken, "this")))); @@ -1832,51 +1845,17 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { // add the fuel assumption for the reveal method of a opaque method if (IsOpaqueRevealLemma(m)) { List args = Attributes.FindExpressions(m.Attributes, "revealedFunction"); - if (args != null) { - MemberSelectExpr selectExpr = args[0].Resolved as MemberSelectExpr; - if (selectExpr != null) { - Function f = selectExpr.Member as Function; - AddEnsures(ens, Ensures(m.tok, true, null, GetRevealConstant(f), null, null, null)); - } + if (args == null) { + return ens; + } + + if (args[0].Resolved is MemberSelectExpr selectExpr) { + Function f = selectExpr.Member as Function; + AddEnsures(ens, Ensures(m.tok, true, null, GetRevealConstant(f), null, null, null)); } } return ens; } - - var req = new List(); - var mod = new List(); - var ens = new List(); - - var name = MethodName(m, kind); - switch (kind) { - case MethodTranslationKind.CallPre: - case MethodTranslationKind.CoCallPre: - outParams = new List(); - req = GetRequires(); - break; - case MethodTranslationKind.CallPost: - case MethodTranslationKind.CoCallPost: - mod.Add(ordinaryEtran.HeapCastToIdentifierExpr); - ens = GetEnsures(); - break; - default: - req = GetRequires(); - mod.Add(ordinaryEtran.HeapCastToIdentifierExpr); - ens = GetEnsures(); - break; - } - var proc = new Boogie.Procedure(m.tok, name, new List(), inParams, outParams, false, req, mod, ens, etran.TrAttributes(m.Attributes, null)); - AddVerboseNameAttribute(proc, m.FullDafnyName, kind); - - if (InsertChecksums) { - InsertChecksum(m, proc, true); - } - - currentModule = null; - codeContext = null; - isAllocContext = null; - - return proc; } private void InsertChecksum(Method m, Boogie.Declaration decl, bool specificationOnly = false) { diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs b/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs index e6d3f985a76..e3041c4e1bf 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs @@ -30,11 +30,9 @@ using static Microsoft.Dafny.GenericErrors; namespace Microsoft.Dafny { - public record BodyTranslationContext(bool ContainsHide, int ScopeDepth = 0, bool ReturnPosition = true); + public record BodyTranslationContext(bool ContainsHide, int ScopeDepth = 0, bool ReturnPosition = true, AssertMode AssertMode = AssertMode.Keep); public partial class BoogieGenerator { - - /// /// Tries to split the expression into tactical conjuncts (if "position") or disjuncts (if "!position"). /// If a (necessarily boolean) function call appears as a top-level conjunct, then inline the function @@ -42,7 +40,7 @@ public partial class BoogieGenerator { /// passed in as 0, then no functions will be inlined). /// bool TrSplitExpr(BodyTranslationContext context, Expression expr, List splits, /*!*/ /*!*/ - bool position, int heightLimit, bool inlineProtectedFunctions, bool applyInduction, ExpressionTranslator etran) { + bool position, int heightLimit, bool applyInduction, ExpressionTranslator etran) { Contract.Requires(expr != null); Contract.Requires(expr.Type.IsBoolType || (expr is BoxingCastExpr && ((BoxingCastExpr)expr).E.Type.IsBoolType)); Contract.Requires(splits != null); @@ -52,7 +50,7 @@ bool TrSplitExpr(BodyTranslationContext context, Expression expr, List(); - if (TrSplitExpr(context, bce.E, ss, position, heightLimit, inlineProtectedFunctions, applyInduction, etran)) { + if (TrSplitExpr(context, bce.E, ss, position, heightLimit, applyInduction, etran)) { foreach (var s in ss) { splits.Add(ToSplitExprInfo(s.Kind, CondApplyBox(s.Tok, s.E, bce.FromType, bce.ToType))); } @@ -63,18 +61,18 @@ bool TrSplitExpr(BodyTranslationContext context, Expression expr, List(); - if (TrSplitExpr(context, e.Body, ss, position, heightLimit, inlineProtectedFunctions, applyInduction, etran)) { + if (TrSplitExpr(context, e.Body, ss, position, heightLimit, applyInduction, etran)) { // We don't know where the RHSs of the let are used in the body. In particular, we don't know if a RHS // will end up in a spot where TrSplitExpr would like to increase the Layer offset or not. In fact, different // uses of the same let variable may end up needing different Layer constants. The following code will @@ -101,7 +99,7 @@ bool TrSplitExpr(BodyTranslationContext context, Expression expr, List { fe }, e.At) { AtLabel = e.AtLabel }; ee.Type = Type.Bool; // resolve here - TrSplitExpr(context, ee, splits, position, heightLimit, inlineProtectedFunctions, applyInduction, etran); + TrSplitExpr(context, ee, splits, position, heightLimit, applyInduction, etran); } return true; } @@ -112,7 +110,7 @@ bool TrSplitExpr(BodyTranslationContext context, Expression expr, List(); - if (TrSplitExpr(context, e.E, ss, !position, heightLimit, inlineProtectedFunctions, applyInduction, etran)) { + if (TrSplitExpr(context, e.E, ss, !position, heightLimit, applyInduction, etran)) { foreach (var s in ss) { splits.Add(ToSplitExprInfo(s.Kind, Bpl.Expr.Unary(s.E.tok, UnaryOperator.Opcode.Not, s.E))); } @@ -125,13 +123,13 @@ bool TrSplitExpr(BodyTranslationContext context, Expression expr, List(); - TrSplitExpr(context, bin.E1, ss, position, heightLimit, inlineProtectedFunctions, applyInduction, etran); + TrSplitExpr(context, bin.E1, ss, position, heightLimit, applyInduction, etran); foreach (var s in ss) { // as the source location in the following implication, use that of the translated "s" splits.Add(ToSplitExprInfo(s.Kind, Bpl.Expr.Binary(s.E.tok, BinaryOperator.Opcode.Imp, lhs, s.E))); } } else { var ss = new List(); - TrSplitExpr(context, bin.E0, ss, !position, heightLimit, inlineProtectedFunctions, applyInduction, etran); + TrSplitExpr(context, bin.E0, ss, !position, heightLimit, applyInduction, etran); var rhs = etran.TrExpr(bin.E1); foreach (var s in ss) { // as the source location in the following implication, use that of the translated "s" @@ -220,8 +218,8 @@ bool TrSplitExpr(BodyTranslationContext context, Expression expr, List(); var ssElse = new List(); - TrSplitExpr(context, ite.Thn, ssThen, position, heightLimit, inlineProtectedFunctions, applyInduction, etran); - TrSplitExpr(context, ite.Els, ssElse, position, heightLimit, inlineProtectedFunctions, applyInduction, etran); + TrSplitExpr(context, ite.Thn, ssThen, position, heightLimit, applyInduction, etran); + TrSplitExpr(context, ite.Els, ssElse, position, heightLimit, applyInduction, etran); var op = position ? BinaryOperator.Opcode.Imp : BinaryOperator.Opcode.And; var test = etran.TrExpr(ite.Test); @@ -241,7 +239,7 @@ bool TrSplitExpr(BodyTranslationContext context, Expression expr, List E". if (position) { - var conclusion = etran.TrExpr(e.GetSConclusion()); + var conclusion = etran.TrExpr(e.GetStatementConclusion()); var ss = new List(); - TrSplitExpr(context, e.E, ss, position, heightLimit, inlineProtectedFunctions, applyInduction, etran); + TrSplitExpr(context, e.E, ss, position, heightLimit, applyInduction, etran); foreach (var s in ss) { // as the source location in the following implication, use that of the translated "s" splits.Add(ToSplitExprInfo(s.Kind, Bpl.Expr.Binary(s.E.tok, BinaryOperator.Opcode.Imp, conclusion, s.E))); } } else { var ss = new List(); - TrSplitExpr(context, e.GetSConclusion(), ss, !position, heightLimit, inlineProtectedFunctions, applyInduction, etran); + TrSplitExpr(context, e.GetStatementConclusion(), ss, !position, heightLimit, applyInduction, etran); var rhs = etran.TrExpr(e.E); foreach (var s in ss) { // as the source location in the following implication, use that of the translated "s" @@ -269,18 +267,18 @@ bool TrSplitExpr(BodyTranslationContext context, Expression expr, List splits, int heightLimit, bool inlineProtectedFunctions, + Expression expr, List splits, int heightLimit, bool applyInduction, ExpressionTranslator etran, FunctionCallExpr fexp) { var f = fexp.Function; Contract.Assert(f != null); // filled in during resolution @@ -489,7 +487,7 @@ private bool TrSplitFunctionCallExpr(BodyTranslationContext context, (codeContext == null || !codeContext.MustReverify)) { // The function was inherited as body-less but is now given a body. Don't inline the body (since, apparently, everything // that needed to be proved about the function was proved already in the previous module, even without the body definition). - } else if (!FunctionBodyIsAvailable(f, currentModule, currentScope, inlineProtectedFunctions)) { + } else if (!FunctionBodyIsAvailable(f, currentModule, currentScope)) { // Don't inline opaque functions } else if (context.ContainsHide) { // Do not inline in a blind context @@ -535,7 +533,7 @@ private bool TrSplitFunctionCallExpr(BodyTranslationContext context, // recurse on body var ss = new List(); - TrSplitExpr(context, typeSpecializedBody, ss, true, functionHeight, inlineProtectedFunctions, applyInduction, etran); + TrSplitExpr(context, typeSpecializedBody, ss, true, functionHeight, applyInduction, etran); var needsTokenAdjust = TrSplitNeedsTokenAdjustment(typeSpecializedBody); foreach (var s in ss) { if (s.IsChecked) { diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs index 53ade4626fe..cc8fa9b19bd 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs @@ -652,7 +652,7 @@ private void GenerateAndCheckGuesses(IToken tok, List bvars, List>, Expression>> GeneratePartialGuesses(List bvars, Expression expression) { @@ -1194,7 +1194,7 @@ private Bpl.Expr IntToBV(IToken tok, Bpl.Expr r, Type toType) { /// /// Emit checks that "expr" (which may or may not be a value of type "expr.Type"!) is a value of type "toType". /// - void CheckResultToBeInType(IToken tok, Expression expr, Type toType, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, string errorMsgPrefix = "") { + void CheckResultToBeInType(IToken tok, Expression expr, Type toType, Variables locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, string errorMsgPrefix = "") { Contract.Requires(tok != null); Contract.Requires(expr != null); Contract.Requires(toType != null); @@ -1211,8 +1211,7 @@ void CheckResultToBeInType(IToken tok, Expression expr, Type toType, List(); + var locals = new Variables(); var context = new BodyTranslationContext(false); var builder = new BoogieStmtListBuilder(this, options, context); builder.Add(new CommentCmd(string.Format("AddWellformednessCheck for {0} {1}", decl.WhatKind, decl))); @@ -1522,7 +1521,7 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { witnessExpr = decl.Constraint != null ? Substitute(decl.Constraint, decl.Var, decl.Witness) : null; if (witnessExpr != null) { witnessExpr.tok = result.Tok; - var desc = new PODesc.WitnessCheck(witnessString, witnessExpr); + var desc = new WitnessCheck(witnessString, witnessExpr); SplitAndAssertExpression(returnBuilder, witnessExpr, etran, context, desc); } }); @@ -1531,7 +1530,7 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { var witness = Zero(decl.tok, baseType); if (witness == null) { witnessString = ""; - witnessCheckBuilder.Add(Assert(decl.tok, Bpl.Expr.False, new PODesc.WitnessCheck(witnessString))); + witnessCheckBuilder.Add(Assert(decl.tok, Bpl.Expr.False, new WitnessCheck(witnessString), builder.Context)); } else { // before trying 0 as a witness, check that 0 can be assigned to baseType witnessString = Printer.ExprToString(options, witness); @@ -1539,7 +1538,7 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { witnessExpr = decl.Constraint != null ? Substitute(decl.Constraint, decl.Var, witness) : null; if (witnessExpr != null) { witnessExpr.tok = decl.tok; - var desc = new PODesc.WitnessCheck(witnessString, witnessExpr); + var desc = new WitnessCheck(witnessString, witnessExpr); SplitAndAssertExpression(witnessCheckBuilder, witnessExpr, etran, context, desc); } } @@ -1566,24 +1565,24 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { } private void SplitAndAssertExpression(BoogieStmtListBuilder witnessCheckBuilder, Expression witnessExpr, - ExpressionTranslator etran, BodyTranslationContext context, PODesc.WitnessCheck desc) { + ExpressionTranslator etran, BodyTranslationContext context, WitnessCheck desc) { witnessCheckBuilder.Add(new Bpl.AssumeCmd(witnessExpr.tok, etran.CanCallAssumption(witnessExpr))); var ss = TrSplitExpr(context, witnessExpr, etran, true, out var splitHappened); if (!splitHappened) { - witnessCheckBuilder.Add(Assert(witnessExpr.tok, etran.TrExpr(witnessExpr), desc)); + witnessCheckBuilder.Add(Assert(witnessExpr.tok, etran.TrExpr(witnessExpr), desc, context)); } else { foreach (var split in ss) { if (split.IsChecked) { var tok = witnessExpr.tok is { } t ? new NestedToken(t, split.Tok) : witnessExpr.tok; - witnessCheckBuilder.Add(AssertNS(tok, split.E, desc)); + witnessCheckBuilder.Add(AssertAndForget(witnessCheckBuilder.Context, tok, split.E, desc)); } } } } private BoogieStmtListBuilder CheckConstraintWellformedness(RedirectingTypeDecl decl, BodyTranslationContext context, - Bpl.Expr whereClause, ExpressionTranslator etran, List locals, BoogieStmtListBuilder builder) { + Bpl.Expr whereClause, ExpressionTranslator etran, Variables locals, BoogieStmtListBuilder builder) { var constraintCheckBuilder = new BoogieStmtListBuilder(this, options, context); var builderInitializationArea = new BoogieStmtListBuilder(this, options, context); if (decl.Constraint == null) { diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index 33e24efc17c..a32575bf5da 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -7,6 +7,7 @@ //----------------------------------------------------------------------------- using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Numerics; using System.Diagnostics.Contracts; @@ -28,6 +29,7 @@ public partial class BoogieGenerator { private DafnyOptions options; public DafnyOptions Options => options; public const string NameSeparator = "$$"; + public const string CallPrefix = "Call"; private bool filterOnlyMembers; ErrorReporter reporter; @@ -1375,7 +1377,7 @@ public Bpl.Expr GetArrayIndexFieldName(IToken tok, List indices) { /// - "f" has a body /// - "f" is not opaque /// - bool FunctionBodyIsAvailable(Function f, ModuleDefinition context, VisibilityScope scope, bool revealProtectedBody) { + bool FunctionBodyIsAvailable(Function f, ModuleDefinition context, VisibilityScope scope) { Contract.Requires(f != null); Contract.Requires(context != null); return f.Body != null && !IsOpaque(f, options) && f.IsRevealedInScope(scope); @@ -1402,10 +1404,10 @@ void AddEnsures(List list, Bpl.Ensures ens) { } private Implementation AddImplementationWithAttributes(IToken tok, Procedure proc, List inParams, - List outParams, List localVariables, StmtList stmts, QKeyValue kv) { + List outParams, Variables localVariables, StmtList stmts, QKeyValue kv) { Bpl.Implementation impl = new Bpl.Implementation(tok, proc.Name, new List(), inParams, outParams, - localVariables, stmts, kv); + localVariables.Values.ToList(), stmts, kv); AddVerboseNameAttribute(impl, proc.VerboseName); if (options.IsUsingZ3()) { if (DisableNonLinearArithmetic) { @@ -1433,6 +1435,7 @@ private void Reset() { CurrentIdGenerator.Reset(); _tmpIEs.Clear(); assertionCount = 0; + DefiniteAssignmentTrackers.Clear(); } public static Bpl.QKeyValue InlineAttribute(Bpl.IToken tok, Bpl.QKeyValue/*?*/ next = null) { @@ -1758,9 +1761,8 @@ Bpl.Expr InSeqRange(IToken tok, Bpl.Expr index, Type indexType, Bpl.Expr seq, bo Bpl.LocalVariable yieldCountVariable = null; // non-null when an iterator body is being translated bool inBodyInitContext = false; // true during the translation of the .BodyInit portion of a divided constructor body - public Dictionary DefiniteAssignmentTrackers { get; } = new(); + public ImmutableDictionary DefiniteAssignmentTrackers { get; set; } = ImmutableDictionary.Empty; - bool assertAsAssume = false; // generate assume statements instead of assert statements Func assertionOnlyFilter = null; // generate assume statements instead of assert statements if not targeted by {:only} public enum StmtType { NONE, ASSERT, ASSUME }; public StmtType stmtContext = StmtType.NONE; // the Statement that is currently being translated @@ -1782,7 +1784,7 @@ public VerificationIdGenerator CurrentIdGenerator { public int assertionCount = 0; - Bpl.IdentifierExpr GetTmpVar_IdExpr(Bpl.IToken tok, string name, Bpl.Type ty, List locals) // local variable that's shared between statements that need it + Bpl.IdentifierExpr GetTmpVar_IdExpr(Bpl.IToken tok, string name, Bpl.Type ty, Variables locals) // local variable that's shared between statements that need it { Contract.Requires(tok != null); Contract.Requires(name != null); @@ -1795,15 +1797,14 @@ Bpl.IdentifierExpr GetTmpVar_IdExpr(Bpl.IToken tok, string name, Bpl.Type ty, Li Contract.Assume(ie.Type.Equals(ty)); } else { // the "tok" and "ty" of the first request for this variable is the one we use - var v = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, name, ty)); // important for the "$nw" client: no where clause (see GetNewVar_IdExpr) - locals.Add(v); + var v = locals.GetOrAdd(new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, name, ty))); // important for the "$nw" client: no where clause (see GetNewVar_IdExpr) ie = new Bpl.IdentifierExpr(tok, v); _tmpIEs.Add(name, ie); } return ie; } - Bpl.IdentifierExpr GetPrevHeapVar_IdExpr(IToken tok, List locals) // local variable that's shared between statements that need it + Bpl.IdentifierExpr GetPrevHeapVar_IdExpr(IToken tok, Variables locals) // local variable that's shared between statements that need it { Contract.Requires(tok != null); Contract.Requires(locals != null); Contract.Requires(predef != null); @@ -1812,7 +1813,7 @@ Bpl.IdentifierExpr GetPrevHeapVar_IdExpr(IToken tok, List locals) // return GetTmpVar_IdExpr(tok, "$prevHeap", predef.HeapType, locals); } - Bpl.IdentifierExpr GetNewVar_IdExpr(IToken tok, List locals) // local variable that's shared between statements that need it + Bpl.IdentifierExpr GetNewVar_IdExpr(IToken tok, Variables locals) // local variable that's shared between statements that need it { Contract.Requires(tok != null); Contract.Requires(locals != null); @@ -1830,7 +1831,7 @@ Bpl.IdentifierExpr GetNewVar_IdExpr(IToken tok, List locals) // local /// have the same type "ty" and that these variables can be shared. /// As an optimization, if "otherExprsCanAffectPreviouslyKnownExpressions" is "false", then "expr" itself is returned. /// - Bpl.Expr SaveInTemp(Bpl.Expr expr, bool otherExprsCanAffectPreviouslyKnownExpressions, string name, Bpl.Type ty, BoogieStmtListBuilder builder, List locals) { + Bpl.Expr SaveInTemp(Bpl.Expr expr, bool otherExprsCanAffectPreviouslyKnownExpressions, string name, Bpl.Type ty, BoogieStmtListBuilder builder, Variables locals) { Contract.Requires(expr != null); Contract.Requires(name != null); Contract.Requires(ty != null); @@ -2021,7 +2022,7 @@ public void InsertUniqueIdForImplementation(Bpl.Declaration decl) { } void GenerateImplPrelude(Method m, bool wellformednessProc, List inParams, List outParams, - BoogieStmtListBuilder builder, List localVariables, ExpressionTranslator etran) { + BoogieStmtListBuilder builder, Variables localVariables, ExpressionTranslator etran) { Contract.Requires(m != null); Contract.Requires(inParams != null); Contract.Requires(outParams != null); @@ -2051,13 +2052,12 @@ void GenerateImplPrelude(Method m, bool wellformednessProc, List inPar } public void DefineFrame(IToken/*!*/ tok, Boogie.IdentifierExpr frameIdentifier, List/*!*/ frameClause, - BoogieStmtListBuilder/*!*/ builder, List/*!*/ localVariables, string name, ExpressionTranslator/*?*/ etran = null) { + BoogieStmtListBuilder/*!*/ builder, Variables localVariables, string name, ExpressionTranslator/*?*/ etran = null) { Contract.Requires(tok != null); Contract.Requires(frameIdentifier != null); Contract.Requires(frameIdentifier.Type != null); Contract.Requires(cce.NonNullElements(frameClause)); Contract.Requires(builder != null); - Contract.Requires(cce.NonNullElements(localVariables)); Contract.Requires(predef != null); if (etran == null) { @@ -2067,8 +2067,7 @@ public void DefineFrame(IToken/*!*/ tok, Boogie.IdentifierExpr frameIdentifier, etran = new ExpressionTranslator(this, predef, tok, null); } // Declare a local variable $_Frame: [ref, Field]bool - Bpl.LocalVariable frame = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, name ?? frameIdentifier.Name, frameIdentifier.Type)); - localVariables.Add(frame); + var frame = localVariables.GetOrAdd(new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, name ?? frameIdentifier.Name, frameIdentifier.Type))); // $_Frame := (lambda $o: ref, $f: Field :: $o != null && $Heap[$o,alloc] ==> ($o,$f) in Modifies/Reads-Clause); // $_Frame := (lambda $o: ref, $f: Field :: $o != null ==> ($o,$f) in Modifies/Reads-Clause); Bpl.BoundVariable oVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$o", predef.RefType)); @@ -2088,17 +2087,17 @@ public void CheckFrameSubset(IToken tok, List calleeFrame, Expression receiverReplacement, Dictionary substMap, ExpressionTranslator /*!*/ etran, Boogie.IdentifierExpr /*!*/ enclosingFrame, BoogieStmtListBuilder /*!*/ builder, - PODesc.ProofObligationDescription desc, + ProofObligationDescription desc, Bpl.QKeyValue kv) { CheckFrameSubset(tok, calleeFrame, receiverReplacement, substMap, etran, enclosingFrame, - (t, e, d, q) => builder.Add(Assert(t, e, d, q)), desc, kv); + (t, e, d, q) => builder.Add(Assert(t, e, d, builder.Context, q)), desc, kv); } void CheckFrameSubset(IToken tok, List calleeFrame, Expression receiverReplacement, Dictionary substMap, ExpressionTranslator/*!*/ etran, Boogie.IdentifierExpr /*!*/ enclosingFrame, - Action makeAssert, - PODesc.ProofObligationDescription desc, + Action makeAssert, + ProofObligationDescription desc, Bpl.QKeyValue kv) { Contract.Requires(tok != null); Contract.Requires(calleeFrame != null); @@ -2126,7 +2125,7 @@ void CheckFrameSubset(IToken tok, List calleeFrame, void CheckFrameEmpty(IToken tok, ExpressionTranslator/*!*/ etran, Boogie.IdentifierExpr /*!*/ frame, - BoogieStmtListBuilder/*!*/ builder, PODesc.ProofObligationDescription desc, + BoogieStmtListBuilder/*!*/ builder, ProofObligationDescription desc, Bpl.QKeyValue kv) { Contract.Requires(tok != null); Contract.Requires(etran != null); @@ -2148,7 +2147,7 @@ void CheckFrameEmpty(IToken tok, if (IsExprAlways(q, true)) { return; } - builder.Add(Assert(tok, q, desc, kv)); + builder.Add(Assert(tok, q, desc, builder.Context, kv)); } /// @@ -2296,88 +2295,12 @@ Bpl.Expr InRWClause_Aux(IToken tok, Bpl.Expr o, Bpl.Expr boxO, Bpl.Expr f, List< return disjunction; } - void AddWellformednessCheck(DatatypeCtor ctor) { - Contract.Requires(ctor != null); - Contract.Requires(sink != null && predef != null); - Contract.Requires(currentModule == null && codeContext == null && isAllocContext == null && fuelContext == null); - Contract.Ensures(currentModule == null && codeContext == null && isAllocContext == null && fuelContext == null); - - proofDependencies.SetCurrentDefinition(MethodVerboseName(ctor.FullName, MethodTranslationKind.SpecWellformedness)); - - if (!InVerificationScope(ctor)) { - // Checked in other file - return; - } - - // If there are no parameters with default values, there's nothing to do - if (ctor.Formals.TrueForAll(f => f.DefaultValue == null)) { - return; - } - - currentModule = ctor.EnclosingDatatype.EnclosingModuleDefinition; - codeContext = ctor.EnclosingDatatype; - fuelContext = FuelSetting.NewFuelContext(ctor.EnclosingDatatype); - var etran = new ExpressionTranslator(this, predef, ctor.tok, null); - - // parameters of the procedure - List inParams = MkTyParamFormals(GetTypeParams(ctor.EnclosingDatatype), true); - foreach (var p in ctor.Formals) { - Bpl.Type varType = TrType(p.Type); - Bpl.Expr wh = GetWhereClause(p.tok, new Bpl.IdentifierExpr(p.tok, p.AssignUniqueName(ctor.IdGenerator), varType), p.Type, etran, NOALLOC); - inParams.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.AssignUniqueName(ctor.IdGenerator), varType, wh), true)); - } - - // the procedure itself - var req = new List(); - // free requires mh == ModuleContextHeight && fh == TypeContextHeight; - req.Add(Requires(ctor.tok, true, null, etran.HeightContext(ctor.EnclosingDatatype), null, null, null)); - var heapVar = new Bpl.IdentifierExpr(ctor.tok, "$Heap", false); - var varlist = new List { heapVar }; - var proc = new Bpl.Procedure(ctor.tok, "CheckWellformed" + NameSeparator + ctor.FullName, new List(), - inParams, new List(), - false, req, varlist, new List(), etran.TrAttributes(ctor.Attributes, null)); - AddVerboseNameAttribute(proc, ctor.FullName, MethodTranslationKind.SpecWellformedness); - sink.AddTopLevelDeclaration(proc); - - var implInParams = Bpl.Formal.StripWhereClauses(inParams); - var locals = new List(); - var builder = new BoogieStmtListBuilder(this, options, new BodyTranslationContext(false)); - builder.Add(new CommentCmd(string.Format("AddWellformednessCheck for datatype constructor {0}", ctor))); - builder.AddCaptureState(ctor.tok, false, "initial state"); - isAllocContext = new IsAllocContext(options, true); - - DefineFrame(ctor.tok, etran.ReadsFrame(ctor.tok), new List(), builder, locals, null); - - // check well-formedness of each default-value expression - foreach (var formal in ctor.Formals.Where(formal => formal.DefaultValue != null)) { - var e = formal.DefaultValue; - CheckWellformedWithResult(e, new WFOptions(null, true, - false, true), locals, builder, etran, (returnBuilder, result) => { - builder.Add(new Bpl.AssumeCmd(e.tok, etran.CanCallAssumption(e))); - CheckSubrange(result.tok, etran.TrExpr(result), e.Type, formal.Type, e, returnBuilder); - }); - } - - if (EmitImplementation(ctor.Attributes)) { - // emit the impl only when there are proof obligations. - QKeyValue kv = etran.TrAttributes(ctor.Attributes, null); - var implBody = builder.Collect(ctor.tok); - AddImplementationWithAttributes(GetToken(ctor), proc, implInParams, - new List(), locals, implBody, kv); - } - - Contract.Assert(currentModule == ctor.EnclosingDatatype.EnclosingModuleDefinition); - Contract.Assert(codeContext == ctor.EnclosingDatatype); - isAllocContext = null; - fuelContext = null; - Reset(); - } /// /// If "declareLocals" is "false", then the locals are added only if they are new, that is, if /// they don't already exist in "locals". /// - Bpl.Expr CtorInvocation(MatchCase mc, Type sourceType, ExpressionTranslator etran, List locals, BoogieStmtListBuilder localTypeAssumptions, IsAllocType isAlloc, bool declareLocals = true) { + Bpl.Expr CtorInvocation(MatchCase mc, Type sourceType, ExpressionTranslator etran, Variables locals, BoogieStmtListBuilder localTypeAssumptions, IsAllocType isAlloc, bool declareLocals = true) { Contract.Requires(mc != null); Contract.Requires(sourceType != null); Contract.Requires(etran != null); @@ -2397,10 +2320,9 @@ Bpl.Expr CtorInvocation(MatchCase mc, Type sourceType, ExpressionTranslator etra for (int i = 0; i < mc.Arguments.Count; i++) { BoundVar p = mc.Arguments[i]; var nm = p.AssignUniqueName(currentDeclaration.IdGenerator); - Bpl.Variable local = declareLocals ? null : locals.FirstOrDefault(v => v.Name == nm); // find previous local + var local = declareLocals ? null : locals.GetValueOrDefault(nm); // find previous local if (local == null) { - local = new Bpl.LocalVariable(p.tok, new Bpl.TypedIdent(p.tok, nm, TrType(p.Type))); - locals.Add(local); + local = locals.GetOrAdd(new Bpl.LocalVariable(p.tok, new Bpl.TypedIdent(p.tok, nm, TrType(p.Type)))); } else { Contract.Assert(Bpl.Type.Equals(local.TypedIdent.Type, TrType(p.Type))); } @@ -2417,7 +2339,7 @@ Bpl.Expr CtorInvocation(MatchCase mc, Type sourceType, ExpressionTranslator etra return new Bpl.NAryExpr(mc.tok, new Bpl.FunctionCall(id), args); } - Bpl.Expr CtorInvocation(IToken tok, DatatypeCtor ctor, ExpressionTranslator etran, List locals, BoogieStmtListBuilder localTypeAssumptions) { + Bpl.Expr CtorInvocation(IToken tok, DatatypeCtor ctor, ExpressionTranslator etran, Variables locals, BoogieStmtListBuilder localTypeAssumptions) { Contract.Requires(tok != null); Contract.Requires(ctor != null); Contract.Requires(etran != null); @@ -2432,8 +2354,7 @@ Bpl.Expr CtorInvocation(IToken tok, DatatypeCtor ctor, ExpressionTranslator etra foreach (Formal arg in ctor.Formals) { Contract.Assert(arg != null); var nm = varNameGen.FreshId(string.Format("#{0}#", args.Count)); - Bpl.Variable bv = new Bpl.LocalVariable(arg.tok, new Bpl.TypedIdent(arg.tok, nm, TrType(arg.Type))); - locals.Add(bv); + var bv = locals.GetOrAdd(new Bpl.LocalVariable(arg.tok, new Bpl.TypedIdent(arg.tok, nm, TrType(arg.Type)))); args.Add(new Bpl.IdentifierExpr(arg.tok, bv)); } @@ -2518,7 +2439,7 @@ void CheckCasePatternShape(CasePattern pat, Expression dRhs, Bpl.Expr rh // There is only one constructor, so the value must have been constructed by it; might as well assume that here. builder.Add(TrAssumeCmd(pat.tok, correctConstructor)); } else { - builder.Add(Assert(pat.tok, correctConstructor, new PODesc.PatternShapeIsValid(dRhs, ctor.Name))); + builder.Add(Assert(pat.tok, correctConstructor, new PatternShapeIsValid(dRhs, ctor.Name), builder.Context)); } for (int i = 0; i < pat.Arguments.Count; i++) { var arg = pat.Arguments[i]; @@ -2546,14 +2467,15 @@ void CheckNonNull(IToken tok, Expression e, BoogieStmtListBuilder builder, Expre } else if (e is StaticReceiverExpr) { // also ok } else { - builder.Add(Assert(tok, Bpl.Expr.Neq(etran.TrExpr(e), predef.Null), new PODesc.NonNull("target object", e), kv)); + builder.Add(Assert(tok, Bpl.Expr.Neq(etran.TrExpr(e), predef.Null), + new NonNull("target object", e), builder.Context, kv)); } } void CheckFunctionSelectWF(string what, BoogieStmtListBuilder builder, ExpressionTranslator etran, Expression e, string hint) { if (e is MemberSelectExpr sel && sel.Member is Function fn) { Bpl.Expr assertion = !InVerificationScope(fn) ? Bpl.Expr.True : Bpl.Expr.Not(etran.HeightContext(fn)); - builder.Add(Assert(GetToken(e), assertion, new PODesc.ValidInRecursion(what, hint))); + builder.Add(Assert(GetToken(e), assertion, new ValidInRecursion(what, hint), builder.Context)); } } @@ -2650,7 +2572,7 @@ private Expr NewOneHeapExpr(IToken tok) { /// -- "obj" as an expression "e[i]", where "i" is a new identifier, and /// -- "antecedent" as "0 <= i < |e|". /// - void EachReferenceInFrameExpression(Expression e, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, + void EachReferenceInFrameExpression(Expression e, Variables locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, out string description, out Type type, out Bpl.Expr obj, out Bpl.Expr antecedent) { Contract.Requires(e != null); Contract.Requires(locals != null); @@ -2674,8 +2596,7 @@ void EachReferenceInFrameExpression(Expression e, List locals, Boo type = sType.Arg; // var $x var name = CurrentIdGenerator.FreshId("$unchanged#x"); - var xVar = new Bpl.LocalVariable(e.tok, new Bpl.TypedIdent(e.tok, name, isSetType || isMultisetType ? TrType(type) : Bpl.Type.Int)); - locals.Add(xVar); + var xVar = locals.GetOrAdd(new Bpl.LocalVariable(e.tok, new Bpl.TypedIdent(e.tok, name, isSetType || isMultisetType ? TrType(type) : Bpl.Type.Int))); var x = new Bpl.IdentifierExpr(e.tok, xVar); // havoc $x builder.Add(new Bpl.HavocCmd(e.tok, new List() { x })); @@ -2877,15 +2798,13 @@ private Bpl.Function GetCanCallFunction(Function f) { /// Note that SpecWellformedness and Implementation have procedure implementations /// but no callers, and vice versa for InterModuleCall, IntraModuleCall, and CoCall. /// - enum MethodTranslationKind { SpecWellformedness, CallPre, CallPost, CoCallPre, CoCallPost, Implementation, OverrideCheck } + enum MethodTranslationKind { SpecWellformedness, Call, CoCall, Implementation, OverrideCheck } private static readonly Dictionary kindSanitizedPrefix = new() { { MethodTranslationKind.SpecWellformedness, "CheckWellFormed" }, - { MethodTranslationKind.CallPre, "CallPre" }, - { MethodTranslationKind.CallPost, "CallPost" }, - { MethodTranslationKind.CoCallPre, "CoCallPre" }, - { MethodTranslationKind.CoCallPost, "CoCallPost" }, + { MethodTranslationKind.Call, CallPrefix }, + { MethodTranslationKind.CoCall, "CoCall" }, { MethodTranslationKind.Implementation, "Impl" }, { MethodTranslationKind.OverrideCheck, "OverrideCheck" }, }; @@ -2896,12 +2815,10 @@ static string MethodName(ICodeContext m, MethodTranslationKind kind) { } private static readonly Dictionary kindDescription = - new Dictionary() { + new() { {MethodTranslationKind.SpecWellformedness, "well-formedness"}, - {MethodTranslationKind.CallPre, "call precondtion"}, - {MethodTranslationKind.CallPost, "call postcondition"}, - {MethodTranslationKind.CoCallPre, "co-call precondtion"}, - {MethodTranslationKind.CoCallPost, "co-call postcondition"}, + {MethodTranslationKind.Call, "call"}, + {MethodTranslationKind.CoCall, "co-call"}, {MethodTranslationKind.Implementation, "correctness"}, {MethodTranslationKind.OverrideCheck, "override check"}, }; @@ -2924,25 +2841,28 @@ private static void AddSmtOptionAttribute(Bpl.NamedDeclaration targetDecl, strin targetDecl.Attributes = new QKeyValue(targetDecl.tok, "smt_option", new List() { name, value }, targetDecl.Attributes); } - private static CallCmd Call(IToken tok, string methodName, List ins, List outs) { + private static CallCmd Call(BodyTranslationContext context, IToken tok, string methodName, + List ins, List outs) { Contract.Requires(tok != null); Contract.Requires(methodName != null); Contract.Requires(ins != null); Contract.Requires(outs != null); - CallCmd call; - call = new CallCmd(tok, methodName, ins, outs); + var call = new CallCmd(tok, methodName, ins, outs) { + IsFree = context.AssertMode == AssertMode.Assume + }; // CLEMENT enable this: call.ErrorData = "possible violation of function precondition"; return call; } - private void GenerateMethodParameters(IToken tok, Method m, MethodTranslationKind kind, ExpressionTranslator etran, List inParams, out List outParams) { + private void GenerateMethodParameters(IToken tok, Method m, MethodTranslationKind kind, ExpressionTranslator etran, + List inParams, out Variables outParams) { GenerateMethodParametersChoose(tok, m, kind, !m.IsStatic, true, true, etran, inParams, out outParams); } private void GenerateMethodParametersChoose(IToken tok, IMethodCodeContext m, MethodTranslationKind kind, bool includeReceiver, bool includeInParams, bool includeOutParams, - ExpressionTranslator etran, List inParams, out List outParams) { - outParams = new List(); + ExpressionTranslator etran, List inParams, out Variables outParams) { + outParams = new Variables(); // Add type parameters first, always! inParams.AddRange(MkTyParamFormals(GetTypeParams(m), true)); if (includeReceiver) { @@ -2966,7 +2886,7 @@ private void GenerateMethodParametersChoose(IToken tok, IMethodCodeContext m, Me if (thVar.InComing) { inParams.Add(thVar); } else { - outParams.Add(thVar); + outParams.GetOrAdd(thVar); } } if (includeInParams) { @@ -2980,6 +2900,7 @@ private void GenerateMethodParametersChoose(IToken tok, IMethodCodeContext m, Me } } if (includeOutParams) { + var beforeTrackers = DefiniteAssignmentTrackers; Contract.Assume(DefiniteAssignmentTrackers.Count == 0); foreach (Formal p in m.Outs) { Contract.Assert(VisibleInScope(p.Type)); @@ -2994,14 +2915,12 @@ private void GenerateMethodParametersChoose(IToken tok, IMethodCodeContext m, Me wh = BplImp(tracker, wh); } } - outParams.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.AssignUniqueName(currentDeclaration.IdGenerator), varType, wh), false)); + outParams.GetOrAdd(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.AssignUniqueName(currentDeclaration.IdGenerator), varType, wh), false)); + DefiniteAssignmentTrackers = beforeTrackers; } - // tear down definite-assignment trackers - m.Outs.ForEach(RemoveDefiniteAssignmentTracker); - Contract.Assert(DefiniteAssignmentTrackers.Count == 0); if (kind == MethodTranslationKind.Implementation) { - outParams.Add(new Bpl.Formal(tok, new Bpl.TypedIdent(tok, "$_reverifyPost", Bpl.Type.Bool), false)); + outParams.GetOrAdd(new Bpl.Formal(tok, new Bpl.TypedIdent(tok, "$_reverifyPost", Bpl.Type.Bool), false)); } } } @@ -3414,20 +3333,21 @@ public override IToken WithVal(string newVal) { } } - public Bpl.PredicateCmd Assert(IToken tok, Bpl.Expr condition, PODesc.ProofObligationDescription description, Bpl.QKeyValue kv = null) { - var cmd = Assert(tok, condition, description, tok, kv); - return cmd; + public Bpl.PredicateCmd Assert(IToken tok, Bpl.Expr condition, ProofObligationDescription description, + BodyTranslationContext context, Bpl.QKeyValue kv = null) { + return Assert(tok, condition, description, tok, context, kv); } - Bpl.PredicateCmd Assert(IToken tok, Bpl.Expr condition, PODesc.ProofObligationDescription description, IToken refinesToken, Bpl.QKeyValue kv = null) { + private PredicateCmd Assert(IToken tok, Expr condition, ProofObligationDescription description, + IToken refinesToken, BodyTranslationContext context, QKeyValue kv = null) { Contract.Requires(tok != null); Contract.Requires(condition != null); Contract.Ensures(Contract.Result() != null); Bpl.PredicateCmd cmd; - if (assertAsAssume + if (context.AssertMode == AssertMode.Assume || (assertionOnlyFilter != null && !assertionOnlyFilter(tok)) - || (RefinementToken.IsInherited(refinesToken, currentModule) && (codeContext == null || !codeContext.MustReverify))) { + || (RefinementToken.IsInherited(refinesToken, currentModule) && codeContext is not { MustReverify: true })) { // produce an assume instead cmd = TrAssumeCmd(tok, condition, kv); proofDependencies?.AddProofDependencyId(cmd, tok, new AssumedProofObligationDependency(tok, description)); @@ -3438,18 +3358,19 @@ Bpl.PredicateCmd Assert(IToken tok, Bpl.Expr condition, PODesc.ProofObligationDe return cmd; } - Bpl.PredicateCmd AssertNS(IToken tok, Bpl.Expr condition, PODesc.ProofObligationDescription desc) { - return AssertNS(tok, condition, desc, tok, null); + PredicateCmd AssertAndForget(BodyTranslationContext context, IToken tok, Bpl.Expr condition, ProofObligationDescription desc) { + return AssertAndForget(context, tok, condition, desc, tok, null); } - Bpl.PredicateCmd AssertNS(IToken tok, Bpl.Expr condition, PODesc.ProofObligationDescription desc, IToken refinesTok, Bpl.QKeyValue kv) { + PredicateCmd AssertAndForget(BodyTranslationContext context, IToken tok, Bpl.Expr condition, ProofObligationDescription desc, IToken refinesTok, Bpl.QKeyValue kv) { Contract.Requires(tok != null); Contract.Requires(desc != null); Contract.Requires(condition != null); Contract.Ensures(Contract.Result() != null); - Bpl.PredicateCmd cmd; - if ((assertionOnlyFilter != null && !assertionOnlyFilter(tok)) || + PredicateCmd cmd; + if (context.AssertMode == AssertMode.Assume || + (assertionOnlyFilter != null && !assertionOnlyFilter(tok)) || (RefinementToken.IsInherited(refinesTok, currentModule) && (codeContext == null || !codeContext.MustReverify))) { // produce a "skip" instead cmd = TrAssumeCmd(tok, Bpl.Expr.True, kv); @@ -3481,7 +3402,7 @@ Bpl.Ensures Ensures(IToken tok, bool free, Expression dafnyCondition, Bpl.Expr c var unwrappedToken = ForceCheckToken.Unwrap(tok); Bpl.Ensures ens = new Bpl.Ensures(unwrappedToken, free, condition, comment); - var description = new PODesc.EnsuresDescription(dafnyCondition, errorMessage, successMessage); + var description = new EnsuresDescription(dafnyCondition, errorMessage, successMessage); ens.Description = description; if (!free) { ReportAssertion(unwrappedToken, description); @@ -3504,12 +3425,12 @@ Bpl.Requires Requires(IToken tok, bool free, Expression dafnyCondition, Bpl.Expr Contract.Requires(bCondition != null); Contract.Ensures(Contract.Result() != null); Bpl.Requires req = new Bpl.Requires(ForceCheckToken.Unwrap(tok), free, bCondition, comment); - req.Description = new PODesc.RequiresDescription(dafnyCondition, errorMessage, successMessage); + req.Description = new RequiresDescription(dafnyCondition, errorMessage, successMessage); return req; } Bpl.StmtList TrStmt2StmtList(BoogieStmtListBuilder builder, - Statement block, List locals, ExpressionTranslator etran, bool introduceScope = false) { + Statement block, Variables locals, ExpressionTranslator etran, bool introduceScope = false) { Contract.Requires(builder != null); Contract.Requires(block != null); Contract.Requires(locals != null); @@ -3578,13 +3499,14 @@ Bpl.AssertCmd TrAssertCmd(IToken tok, Bpl.Expr expr, Bpl.QKeyValue attributes = return attributes == null ? new Bpl.AssertCmd(tok, expr) : new Bpl.AssertCmd(tok, expr, attributes); } - Bpl.AssertCmd TrAssertCmdDesc(IToken tok, Bpl.Expr expr, PODesc.ProofObligationDescription description, Bpl.QKeyValue attributes = null) { + Bpl.AssertCmd TrAssertCmdDesc(IToken tok, Bpl.Expr expr, + ProofObligationDescription description, Bpl.QKeyValue attributes = null) { ReportAssertion(tok, description); return new Bpl.AssertCmd(tok, expr, description, attributes); } private ISet<(Uri, int)> reportedAssertions = new HashSet<(Uri, int)>(); - private void ReportAssertion(IToken tok, PODesc.ProofObligationDescription description) { + private void ReportAssertion(IToken tok, ProofObligationDescription description) { if (!reportedAssertions.Add((tok.Uri, tok.pos))) { return; } @@ -3607,7 +3529,7 @@ private void ReportAssertion(IToken tok, PODesc.ProofObligationDescription descr } Dictionary SetupBoundVarsAsLocals(List boundVars, out Bpl.Expr typeAntecedent, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, string nameSuffix = null) { Contract.Requires(boundVars != null); Contract.Requires(builder != null); @@ -3624,7 +3546,7 @@ Dictionary SetupBoundVarsAsLocals(List boundVar ie.Var = local; ie.Type = ie.Var.Type; // resolve ie here substMap.Add(bv, ie); Bpl.LocalVariable bvar = new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator), TrType(local.Type))); - locals.Add(bvar); + locals.GetOrAdd(bvar); var bIe = new Bpl.IdentifierExpr(bvar.tok, bvar); builder.Add(new Bpl.HavocCmd(bv.tok, new List { bIe })); Bpl.Expr wh = GetWhereClause(bv.tok, bIe, local.Type, etran, IsAllocType.ISALLOC); @@ -3636,7 +3558,7 @@ Dictionary SetupBoundVarsAsLocals(List boundVar } Dictionary SetupBoundVarsAsLocals(List boundVars, BoogieStmtListBuilder builder, - List locals, ExpressionTranslator etran, + Variables locals, ExpressionTranslator etran, string nameSuffix = null) { Contract.Requires(boundVars != null); Contract.Requires(builder != null); @@ -3656,7 +3578,7 @@ Dictionary SetupBoundVarsAsLocals(List boundVar /// is NOT emitted; rather, it is returned by this method. /// Bpl.Expr SetupVariableAsLocal(IVariable v, Dictionary substMap, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { Contract.Requires(v != null); Contract.Requires(substMap != null); Contract.Requires(builder != null); @@ -3670,14 +3592,14 @@ Bpl.Expr SetupVariableAsLocal(IVariable v, Dictionary sub substMap.Add(v, ie); var bvar = new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator), TrType(local.Type))); - locals.Add(bvar); + locals.GetOrAdd(bvar); var bIe = new Bpl.IdentifierExpr(bvar.tok, bvar); builder.Add(new Bpl.HavocCmd(v.Tok, new List { bIe })); var wh = GetWhereClause(v.Tok, bIe, local.Type, etran, ISALLOC); return wh ?? Bpl.Expr.True; } - List RecordDecreasesValue(List decreases, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, string varPrefix) { + List RecordDecreasesValue(List decreases, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, string varPrefix) { Contract.Requires(locals != null); Contract.Requires(etran != null); Contract.Requires(varPrefix != null); @@ -3688,7 +3610,7 @@ Bpl.Expr SetupVariableAsLocal(IVariable v, Dictionary sub foreach (Expression e in decreases) { Contract.Assert(e != null); Bpl.LocalVariable bfVar = new Bpl.LocalVariable(e.tok, new Bpl.TypedIdent(e.tok, idGen.FreshId(varPrefix), TrType(cce.NonNull(e.Type)))); - locals.Add(bfVar); + locals.GetOrAdd(bfVar); Bpl.IdentifierExpr bf = new Bpl.IdentifierExpr(e.tok, bfVar); oldBfs.Add(bf); // record value of each decreases expression at beginning of the loop iteration @@ -4021,7 +3943,7 @@ Bpl.Expr GetSubrangeCheck( // allow null for checked expressions that cannot necessarily be named without side effects, such as method out-params [CanBeNull] Expression source, [CanBeNull] SubrangeCheckContext context, - out PODesc.ProofObligationDescription desc, string errorMessagePrefix = "") { + out ProofObligationDescription desc, string errorMessagePrefix = "") { Contract.Requires(bSource != null); Contract.Requires(sourceType != null); Contract.Requires(targetType != null); @@ -4061,19 +3983,19 @@ Bpl.Expr GetSubrangeCheck( certain = udt.ResolvedClass.TypeArgs.Count == 0; cause = "it may be null"; } - desc = new PODesc.SubrangeCheck(errorMessagePrefix, sourceType.ToString(), targetType.ToString(), false, certain, cause, dafnyCheck); + desc = new SubrangeCheck(errorMessagePrefix, sourceType.ToString(), targetType.ToString(), false, certain, cause, dafnyCheck); } else if (udt != null && ArrowType.IsTotalArrowTypeName(udt.Name)) { - desc = new PODesc.SubrangeCheck(errorMessagePrefix, sourceType.ToString(), targetType.ToString(), true, false, + desc = new SubrangeCheck(errorMessagePrefix, sourceType.ToString(), targetType.ToString(), true, false, "it may be partial or have read effects", canTestFunctionTypes ? dafnyCheck : null ); } else if (udt != null && ArrowType.IsPartialArrowTypeName(udt.Name)) { - desc = new PODesc.SubrangeCheck(errorMessagePrefix, sourceType.ToString(), targetType.ToString(), true, false, + desc = new SubrangeCheck(errorMessagePrefix, sourceType.ToString(), targetType.ToString(), true, false, "it may have read effects", canTestFunctionTypes ? dafnyCheck : null ); } else { - desc = new PODesc.SubrangeCheck(errorMessagePrefix, sourceType.ToString(), targetType.ToString(), + desc = new SubrangeCheck(errorMessagePrefix, sourceType.ToString(), targetType.ToString(), targetType.NormalizeExpandKeepConstraints() is UserDefinedType { ResolvedClass: SubsetTypeDecl or NewtypeDecl { Var: { } } }, @@ -4091,7 +4013,7 @@ void CheckSubrange(IToken tok, Bpl.Expr bSource, Type sourceType, Type targetTyp var cre = GetSubrangeCheck(tok, bSource, sourceType, targetType, source, null, out var desc, errorMsgPrefix); if (cre != null) { - builder.Add(Assert(tok, cre, desc)); + builder.Add(Assert(tok, cre, desc, builder.Context)); } } @@ -4109,7 +4031,7 @@ void Check_NewRestrictions(IToken tok, Expression dObj, Bpl.Expr obj, Field f, B var fId = new Bpl.IdentifierExpr(tok, GetField(f)); var subset = FunctionCall(tok, BuiltinFunction.SetSubset, null, rhs, ApplyUnbox(tok, ReadHeap(tok, etran.HeapExpr, obj, fId), predef.SetType)); - builder.Add(Assert(tok, subset, new PODesc.AssignmentShrinks(dObj, f.Name))); + builder.Add(Assert(tok, subset, new AssignmentShrinks(dObj, f.Name), builder.Context)); } } @@ -4605,7 +4527,7 @@ internal IsAllocType Var(bool ghostStmt, NonglobalVariable var) { Contract.Ensures(Contract.Result>() != null); var splits = new List(); - splitHappened = TrSplitExpr(context, expr, splits, true, int.MaxValue, true, applyInduction, etran); + splitHappened = TrSplitExpr(context, expr, splits, true, int.MaxValue, applyInduction, etran); return splits; } @@ -4616,8 +4538,7 @@ List TrSplitExprForMethodSpec(BodyTranslationContext context, Exp var splits = new List(); var applyInduction = kind == MethodTranslationKind.Implementation; - bool splitHappened; // we don't actually care - splitHappened = TrSplitExpr(context, expr, splits, true, int.MaxValue, kind != MethodTranslationKind.CallPost, applyInduction, etran); + TrSplitExpr(context, expr, splits, true, int.MaxValue, applyInduction, etran); return splits; } @@ -4945,4 +4866,7 @@ public Expr GetRevealConstant(Function f) { return this.functionReveals[f]; } } + + public enum AssertMode { Keep, Assume, Check } } + diff --git a/Source/DafnyCore/Verifier/BoogieStmtListBuilder.cs b/Source/DafnyCore/Verifier/BoogieStmtListBuilder.cs index 8053210c2e3..7377c668bfa 100644 --- a/Source/DafnyCore/Verifier/BoogieStmtListBuilder.cs +++ b/Source/DafnyCore/Verifier/BoogieStmtListBuilder.cs @@ -36,9 +36,9 @@ public BoogieStmtListBuilder(BoogieGenerator tran, DafnyOptions options, BodyTra public void Add(Cmd cmd) { Commands.Add(cmd); builder.Add(cmd); - if (cmd is Boogie.AssertCmd) { + if (cmd is AssertCmd) { tran.assertionCount++; - } else if (cmd is Boogie.CallCmd call) { + } else if (cmd is CallCmd call) { // A call command may involve a precondition, but we can't tell for sure until the callee // procedure has been generated. Therefore, to be on the same side, we count this call // as a possible assertion, unless it's a procedure that's part of the translation and diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.DataTypes.cs b/Source/DafnyCore/Verifier/Datatypes/BoogieGenerator.DataTypes.cs similarity index 90% rename from Source/DafnyCore/Verifier/BoogieGenerator.DataTypes.cs rename to Source/DafnyCore/Verifier/Datatypes/BoogieGenerator.DataTypes.cs index 57a92cc6779..396815f117c 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.DataTypes.cs +++ b/Source/DafnyCore/Verifier/Datatypes/BoogieGenerator.DataTypes.cs @@ -786,12 +786,89 @@ private void AddsIsConstructorAxiom(DatatypeCtor ctor, Bpl.Function ctorFunction private Axiom CreateConstructorIdentifierAxiom(DatatypeCtor ctor, Expr c) { // Add: axiom (forall params :: DatatypeCtorId(#dt.ctor(params)) == ##dt.ctor); CreateBoundVariables(ctor.Formals, out var bvs, out var args); - var constructor_call = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); - var lhs = FunctionCall(ctor.tok, BuiltinFunction.DatatypeCtorId, null, constructor_call); + var constructorCall = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); + var lhs = FunctionCall(ctor.tok, BuiltinFunction.DatatypeCtorId, null, constructorCall); Bpl.Expr q = Bpl.Expr.Eq(lhs, c); - var trigger = BplTrigger(constructor_call); + var trigger = BplTrigger(constructorCall); var axiom = new Bpl.Axiom(ctor.tok, BplForall(bvs, trigger, q), "Constructor identifier"); return axiom; } + + void AddWellformednessCheck(DatatypeCtor ctor) { + Contract.Requires(ctor != null); + Contract.Requires(sink != null && predef != null); + Contract.Requires(currentModule == null && codeContext == null && isAllocContext == null && fuelContext == null); + Contract.Ensures(currentModule == null && codeContext == null && isAllocContext == null && fuelContext == null); + + proofDependencies.SetCurrentDefinition(MethodVerboseName(ctor.FullName, MethodTranslationKind.SpecWellformedness)); + + if (!InVerificationScope(ctor)) { + // Checked in other file + return; + } + + // If there are no parameters with default values, there's nothing to do + if (ctor.Formals.TrueForAll(f => f.DefaultValue == null)) { + return; + } + + currentModule = ctor.EnclosingDatatype.EnclosingModuleDefinition; + codeContext = ctor.EnclosingDatatype; + fuelContext = FuelSetting.NewFuelContext(ctor.EnclosingDatatype); + var etran = new ExpressionTranslator(this, predef, ctor.tok, null); + + // parameters of the procedure + List inParams = MkTyParamFormals(GetTypeParams(ctor.EnclosingDatatype), true); + foreach (var p in ctor.Formals) { + Bpl.Type varType = TrType(p.Type); + Bpl.Expr wh = GetWhereClause(p.tok, new Bpl.IdentifierExpr(p.tok, p.AssignUniqueName(ctor.IdGenerator), varType), p.Type, etran, NOALLOC); + inParams.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.AssignUniqueName(ctor.IdGenerator), varType, wh), true)); + } + + // the procedure itself + var req = new List(); + // free requires mh == ModuleContextHeight && fh == TypeContextHeight; + req.Add(Requires(ctor.tok, true, null, etran.HeightContext(ctor.EnclosingDatatype), null, null, null)); + var heapVar = new Bpl.IdentifierExpr(ctor.tok, "$Heap", false); + var varlist = new List { heapVar }; + var proc = new Bpl.Procedure(ctor.tok, "CheckWellformed" + NameSeparator + ctor.FullName, new List(), + inParams, new List(), + false, req, varlist, new List(), etran.TrAttributes(ctor.Attributes, null)); + AddVerboseNameAttribute(proc, ctor.FullName, MethodTranslationKind.SpecWellformedness); + sink.AddTopLevelDeclaration(proc); + + var implInParams = Bpl.Formal.StripWhereClauses(inParams); + var locals = new Variables(); + var builder = new BoogieStmtListBuilder(this, options, new BodyTranslationContext(false)); + builder.Add(new CommentCmd($"AddWellformednessCheck for datatype constructor {ctor}")); + builder.AddCaptureState(ctor.tok, false, "initial state"); + isAllocContext = new IsAllocContext(options, true); + + DefineFrame(ctor.tok, etran.ReadsFrame(ctor.tok), new List(), builder, locals, null); + + // check well-formedness of each default-value expression + foreach (var formal in ctor.Formals.Where(formal => formal.DefaultValue != null)) { + var e = formal.DefaultValue; + CheckWellformedWithResult(e, new WFOptions(null, true, + false, true), locals, builder, etran, (returnBuilder, result) => { + builder.Add(new Bpl.AssumeCmd(e.tok, etran.CanCallAssumption(e))); + CheckSubrange(result.tok, etran.TrExpr(result), e.Type, formal.Type, e, returnBuilder); + }); + } + + if (EmitImplementation(ctor.Attributes)) { + // emit the impl only when there are proof obligations. + QKeyValue kv = etran.TrAttributes(ctor.Attributes, null); + var implBody = builder.Collect(ctor.tok); + AddImplementationWithAttributes(GetToken(ctor), proc, implInParams, + new List(), locals, implBody, kv); + } + + Contract.Assert(currentModule == ctor.EnclosingDatatype.EnclosingModuleDefinition); + Contract.Assert(codeContext == ctor.EnclosingDatatype); + isAllocContext = null; + fuelContext = null; + Reset(); + } } } diff --git a/Source/DafnyCore/Verifier/ProofDependency.cs b/Source/DafnyCore/Verifier/ProofDependency.cs index e7ab874bfad..78f556603bb 100644 --- a/Source/DafnyCore/Verifier/ProofDependency.cs +++ b/Source/DafnyCore/Verifier/ProofDependency.cs @@ -55,12 +55,12 @@ public string OriginalString() { public class ProofObligationDependency : ProofDependency { public override RangeToken Range { get; } - public PODesc.ProofObligationDescription ProofObligation { get; } + public ProofObligationDescription ProofObligation { get; } public override string Description => $"{ProofObligation.SuccessDescription}"; - public ProofObligationDependency(Microsoft.Boogie.IToken tok, PODesc.ProofObligationDescription proofObligation) { + public ProofObligationDependency(Microsoft.Boogie.IToken tok, ProofObligationDescription proofObligation) { Range = BoogieGenerator.ToDafnyToken(true, tok).ToRange(); ProofObligation = proofObligation; } @@ -69,12 +69,12 @@ public ProofObligationDependency(Microsoft.Boogie.IToken tok, PODesc.ProofObliga public class AssumedProofObligationDependency : ProofDependency { public override RangeToken Range { get; } - public PODesc.ProofObligationDescription ProofObligation { get; } + public ProofObligationDescription ProofObligation { get; } public override string Description => $"assumption that {ProofObligation.SuccessDescription}"; - public AssumedProofObligationDependency(IToken tok, PODesc.ProofObligationDescription proofObligation) { + public AssumedProofObligationDependency(IToken tok, ProofObligationDescription proofObligation) { Range = tok as RangeToken ?? new RangeToken(tok, tok); ProofObligation = proofObligation; } diff --git a/Source/DafnyCore/Verifier/ProofDependencyManager.cs b/Source/DafnyCore/Verifier/ProofDependencyManager.cs index 52ec364eeb2..9dce0cbb56c 100644 --- a/Source/DafnyCore/Verifier/ProofDependencyManager.cs +++ b/Source/DafnyCore/Verifier/ProofDependencyManager.cs @@ -37,7 +37,11 @@ public void SetCurrentDefinition(string verificationScopeId) { } public IEnumerable GetPotentialDependenciesForDefinition(string defName) { - return idsByMemberName[defName]; + if (idsByMemberName.TryGetValue(defName, out var result)) { + return result; + } + + return Enumerable.Empty(); } public IEnumerable GetAllPotentialDependencies() { diff --git a/Source/DafnyCore/Verifier/ProofObligationDescription.cs b/Source/DafnyCore/Verifier/ProofObligationDescription.cs index a83a9be9c58..e3e647d9a23 100644 --- a/Source/DafnyCore/Verifier/ProofObligationDescription.cs +++ b/Source/DafnyCore/Verifier/ProofObligationDescription.cs @@ -8,7 +8,7 @@ using JetBrains.Annotations; using Microsoft.Boogie; -namespace Microsoft.Dafny.ProofObligationDescription; +namespace Microsoft.Dafny; public abstract class ProofObligationDescription : Boogie.ProofObligationDescription { public virtual bool IsImplicit => true; diff --git a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs new file mode 100644 index 00000000000..c104e23608a --- /dev/null +++ b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs @@ -0,0 +1,24 @@ +using Microsoft.Dafny; + +namespace Microsoft.Dafny; + +public class BlockByProofStmtVerifier { + public static void EmitBoogie(BoogieGenerator generator, BlockByProofStmt block, BoogieStmtListBuilder builder, + Variables locals, BoogieGenerator.ExpressionTranslator etran, ICodeContext codeContext) { + var proofBuilder = new BoogieStmtListBuilder(generator, builder.Options, builder.Context); + + var previousTrackers = generator.DefiniteAssignmentTrackers; + generator.CurrentIdGenerator.Push(); + generator.TrStmtList(block.Proof.Body, proofBuilder, locals, etran); + generator.CurrentIdGenerator.Pop(); + generator.DefiniteAssignmentTrackers = previousTrackers; + + generator.TrStmt(block.Body, proofBuilder, locals, etran); + + generator.PathAsideBlock(block.Tok, proofBuilder, builder); + generator.TrStmt(block.Body, builder.WithContext(builder.Context with { + AssertMode = AssertMode.Assume + }), locals, etran); + + } +} \ No newline at end of file diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs index 94e2ec95826..0d78e61af61 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs @@ -14,14 +14,13 @@ public partial class BoogieGenerator { /// "lhs" is expected to be a resolved form of an expression, i.e., not a concrete-syntax expression. /// void TrAssignment(Statement stmt, Expression lhs, AssignmentRhs rhs, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { Contract.Requires(stmt != null); Contract.Requires(lhs != null); Contract.Requires(!(lhs is ConcreteSyntaxExpression)); Contract.Requires(!(lhs is SeqSelectExpr && !((SeqSelectExpr)lhs).SelectOne)); // these were once allowed, but their functionality is now provided by 'forall' statements Contract.Requires(rhs != null); Contract.Requires(builder != null); - Contract.Requires(cce.NonNullElements(locals)); Contract.Requires(etran != null); Contract.Requires(predef != null); @@ -37,13 +36,12 @@ void TrAssignment(Statement stmt, Expression lhs, AssignmentRhs rhs, void ProcessRhss(List lhsBuilder, List bLhss, List lhss, List rhss, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, Statement stmt) { + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, Statement stmt) { Contract.Requires(lhsBuilder != null); Contract.Requires(bLhss != null); Contract.Requires(cce.NonNullElements(lhss)); Contract.Requires(cce.NonNullElements(rhss)); Contract.Requires(builder != null); - Contract.Requires(cce.NonNullElements(locals)); Contract.Requires(etran != null); Contract.Requires(predef != null); @@ -90,12 +88,11 @@ void ProcessRhss(List lhsBuilder, List ProcessUpdateAssignRhss(List lhss, List rhss, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, Statement stmt) { Contract.Requires(cce.NonNullElements(lhss)); Contract.Requires(cce.NonNullElements(rhss)); Contract.Requires(builder != null); - Contract.Requires(cce.NonNullElements(locals)); Contract.Requires(etran != null); Contract.Requires(predef != null); Contract.Ensures(Contract.ForAll(Contract.Result>(), i => i != null)); @@ -171,8 +168,8 @@ private void CheckLhssDistinctness(List rhs, List rhsOr void AssertDistinctness(Expression lhsa, Expression lhsb, BoogieStmtListBuilder builder, ExpressionTranslator etran) { CheckDistinctness(lhsa, lhsb, etran, out var dExpr, out var bExpr); if (bExpr != null) { - builder.Add(Assert(GetToken(lhsa), bExpr, new PODesc.DistinctLHS(Printer.ExprToString(options, lhsa), - Printer.ExprToString(options, lhsb), bExpr != Bpl.Expr.False, false, dExpr))); + builder.Add(Assert(GetToken(lhsa), bExpr, new DistinctLHS(Printer.ExprToString(options, lhsa), + Printer.ExprToString(options, lhsb), bExpr != Bpl.Expr.False, false, dExpr), builder.Context)); } } @@ -180,8 +177,8 @@ void AssertDistinctness(Expression lhsa, Expression lhsb, Bpl.Expr rhsa, Bpl.Exp CheckDistinctness(lhsa, lhsb, etran, out var dExpr, out var bExpr); if (bExpr != null) { bExpr = BplOr(bExpr, Bpl.Expr.Eq(rhsa, rhsb)); - builder.Add(Assert(GetToken(lhsa), bExpr, new PODesc.DistinctLHS(Printer.ExprToString(options, lhsa), - Printer.ExprToString(options, lhsb), false, true, dExpr))); + builder.Add(Assert(GetToken(lhsa), bExpr, new DistinctLHS(Printer.ExprToString(options, lhsa), + Printer.ExprToString(options, lhsb), false, true, dExpr), builder.Context)); } } @@ -192,13 +189,12 @@ void AssertDistinctness(Expression lhsa, Expression lhsb, Bpl.Expr rhsa, Bpl.Exp /// Checks that they denote different locations iff checkDistinctness is true. /// void ProcessLhss(List lhss, bool rhsCanAffectPreviouslyKnownExpressions, bool checkDistinctness, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, Statement stmt, + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, Statement stmt, out List lhsBuilders, out List bLhss, out Bpl.Expr[] prevObj, out Bpl.Expr[] prevIndex, out string[] prevNames, Expression originalInitialLhs = null) { Contract.Requires(cce.NonNullElements(lhss)); Contract.Requires(builder != null); - Contract.Requires(cce.NonNullElements(locals)); Contract.Requires(etran != null); Contract.Requires(predef != null); Contract.Ensures(Contract.ValueAtReturn(out lhsBuilders).Count == lhss.Count); @@ -267,8 +263,8 @@ void ProcessLhss(List lhss, bool rhsCanAffectPreviouslyKnownExpressi prevObj[i] = obj; if (!useSurrogateLocal) { // check that the enclosing modifies clause allows this object to be written: assert $_ModifiesFrame[obj]); - var desc = new PODesc.Modifiable("an object", contextModFrames, fse.Obj, field); - builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.ModifiesFrame(tok), obj, GetField(fse)), desc)); + var desc = new Modifiable("an object", contextModFrames, fse.Obj, field); + builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.ModifiesFrame(tok), obj, GetField(fse)), desc, builder.Context)); } if (useSurrogateLocal) { @@ -317,8 +313,8 @@ void ProcessLhss(List lhss, bool rhsCanAffectPreviouslyKnownExpressi prevObj[i] = obj; prevIndex[i] = fieldName; // check that the enclosing modifies clause allows this object to be written: assert $_Frame[obj,index]); - var desc = new PODesc.Modifiable("an array element", contextModFrames, sel.Seq, null); - builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.ModifiesFrame(tok), obj, fieldName), desc)); + var desc = new Modifiable("an array element", contextModFrames, sel.Seq, null); + builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.ModifiesFrame(tok), obj, fieldName), desc, builder.Context)); bLhss.Add(null); lhsBuilders.Add(delegate (Bpl.Expr rhs, bool origRhsIsHavoc, BoogieStmtListBuilder bldr, ExpressionTranslator et) { @@ -342,8 +338,8 @@ void ProcessLhss(List lhss, bool rhsCanAffectPreviouslyKnownExpressi "$index" + i, predef.FieldName(mse.tok), builder, locals); prevObj[i] = obj; prevIndex[i] = fieldName; - var desc = new PODesc.Modifiable("an array element", contextModFrames, mse.Array, null); - builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.ModifiesFrame(tok), obj, fieldName), desc)); + var desc = new Modifiable("an array element", contextModFrames, mse.Array, null); + builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.ModifiesFrame(tok), obj, fieldName), desc, builder.Context)); bLhss.Add(null); lhsBuilders.Add(delegate (Bpl.Expr rhs, bool origRhsIsHavoc, BoogieStmtListBuilder bldr, ExpressionTranslator et) { @@ -382,7 +378,7 @@ void ProcessLhss(List lhss, bool rhsCanAffectPreviouslyKnownExpressi /// Bpl.Expr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bGivenLhs, IVariable lhsVar, Type lhsType, AssignmentRhs rhs, Type rhsTypeConstraint, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, Statement stmt) { Contract.Requires(tok != null); Contract.Requires(rhs != null); @@ -419,8 +415,7 @@ Bpl.Expr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bGivenLhs, IVariable lhs // "where" wouldn't provide additional information over the assigned value. wh = null; } - var v = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, nm, ty, wh)); - locals.Add(v); + var v = locals.GetOrCreate(nm, () => new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, nm, ty, wh))); bLhs = new Bpl.IdentifierExpr(tok, v); } @@ -432,7 +427,7 @@ Bpl.Expr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bGivenLhs, IVariable lhs TrStmt_CheckWellformed(e.Expr, builder, locals, etran, true, addResultCommands: (returnBuilder, result) => { if (cre != null) { - returnBuilder.Add(Assert(result.Tok, cre, desc)); + returnBuilder.Add(Assert(result.Tok, cre, desc, builder.Context)); } }); @@ -467,17 +462,17 @@ Bpl.Expr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bGivenLhs, IVariable lhs int i = 0; foreach (Expression dim in tRhs.ArrayDimensions) { CheckWellformed(dim, new WFOptions(), locals, builder, etran); - var desc = new PODesc.NonNegative(tRhs.ArrayDimensions.Count == 1 + var desc = new NonNegative(tRhs.ArrayDimensions.Count == 1 ? "array size" : $"array size (dimension {i})", dim); - builder.Add(Assert(GetToken(dim), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(dim)), desc)); + builder.Add(Assert(GetToken(dim), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(dim)), desc, builder.Context)); i++; } if (tRhs.ElementInit != null) { CheckWellformed(tRhs.ElementInit, new WFOptions(), locals, builder, etran); } else if (tRhs.InitDisplay != null) { var dim = tRhs.ArrayDimensions[0]; - var desc = new PODesc.ArrayInitSizeValid(tRhs, dim); - builder.Add(Assert(GetToken(dim), Bpl.Expr.Eq(etran.TrExpr(dim), Bpl.Expr.Literal(tRhs.InitDisplay.Count)), desc)); + var desc = new ArrayInitSizeValid(tRhs, dim); + builder.Add(Assert(GetToken(dim), Bpl.Expr.Eq(etran.TrExpr(dim), Bpl.Expr.Literal(tRhs.InitDisplay.Count)), desc, builder.Context)); foreach (var v in tRhs.InitDisplay) { CheckWellformed(v, new WFOptions(), locals, builder, etran); } @@ -491,8 +486,8 @@ Bpl.Expr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bGivenLhs, IVariable lhs foreach (Expression dim in tRhs.ArrayDimensions) { zeroSize = BplOr(zeroSize, Bpl.Expr.Eq(Bpl.Expr.Literal(0), etran.TrExpr(dim))); } - var desc = new PODesc.ArrayInitEmpty(tRhs.EType.ToString(), tRhs.ArrayDimensions); - builder.Add(Assert(tRhs.Tok, zeroSize, desc)); + var desc = new ArrayInitEmpty(tRhs.EType.ToString(), tRhs.ArrayDimensions); + builder.Add(Assert(tRhs.Tok, zeroSize, desc, builder.Context)); } } diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs index 215d22a05b6..5110548c1f7 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs @@ -14,7 +14,7 @@ namespace Microsoft.Dafny; public partial class BoogieGenerator { - void TrCallStmt(CallStmt s, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, Bpl.IdentifierExpr actualReceiver) { + void TrCallStmt(CallStmt s, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, Bpl.IdentifierExpr actualReceiver) { Contract.Requires(s != null); Contract.Requires(builder != null); Contract.Requires(locals != null); @@ -40,8 +40,7 @@ void TrCallStmt(CallStmt s, BoogieStmtListBuilder builder, List locals string nm = CurrentIdGenerator.FreshId("$rhs##"); var formalOutType = s.Method.Outs[i].Type.Subst(tySubst); var ty = TrType(formalOutType); - Bpl.LocalVariable var = new Bpl.LocalVariable(lhs.tok, new Bpl.TypedIdent(lhs.tok, nm, ty)); - locals.Add(var); + var var = locals.GetOrCreate(nm, () => new Bpl.LocalVariable(lhs.tok, new Bpl.TypedIdent(lhs.tok, nm, ty))); bLhss[i] = new Bpl.IdentifierExpr(lhs.tok, var.Name, ty); } } @@ -98,7 +97,7 @@ void TrCallStmt(CallStmt s, BoogieStmtListBuilder builder, List locals void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.Expr bReceiver, List Lhss, List LhsTypes, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { Contract.Requires(cs != null); Contract.Requires(Lhss != null); @@ -185,10 +184,11 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E var formal = callee.Ins[i]; var local = new LocalVariable(formal.RangeToken, formal.Name + "#", formal.Type.Subst(tySubst), formal.IsGhost); local.type = local.SyntacticType; // resolve local here - var ie = new IdentifierExpr(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator)); + var localName = local.AssignUniqueName(currentDeclaration.IdGenerator); + var ie = new IdentifierExpr(local.Tok, localName); ie.Var = local; ie.Type = ie.Var.Type; // resolve ie here substMap.Add(formal, ie); - locals.Add(new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator), TrType(local.Type)))); + locals.GetOrCreate(localName, () => new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, localName, TrType(local.Type)))); var param = (Bpl.IdentifierExpr)etran.TrExpr(ie); // TODO: is this cast always justified? Bpl.Expr bActual; @@ -237,24 +237,24 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E if (!method.IsStatic && !(method is Constructor)) { Bpl.Expr wh = GetWhereClause(receiver.tok, etran.TrExpr(receiver), receiver.Type, etran, ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("receiver argument", "in the state in which the method is invoked", receiver); - builder.Add(Assert(receiver.tok, wh, desc)); + var desc = new IsAllocated("receiver argument", "in the state in which the method is invoked", receiver); + builder.Add(Assert(receiver.tok, wh, desc, builder.Context)); } } for (int i = 0; i < Args.Count; i++) { Expression ee = Args[i]; Bpl.Expr wh = GetWhereClause(ee.tok, etran.TrExpr(ee), ee.Type, etran, ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("argument", "in the state in which the method is invoked", ee); - builder.Add(Assert(ee.tok, wh, desc)); + var desc = new IsAllocated("argument", "in the state in which the method is invoked", ee); + builder.Add(Assert(ee.tok, wh, desc, builder.Context)); } } } else if (method is TwoStateLemma) { if (!method.IsStatic) { Bpl.Expr wh = GetWhereClause(receiver.tok, etran.TrExpr(receiver), receiver.Type, etran.OldAt(atLabel), ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("receiver argument", "in the two-state lemma's previous state", receiver, atLabel); - builder.Add(Assert(receiver.tok, wh, desc)); + var desc = new IsAllocated("receiver argument", "in the two-state lemma's previous state", receiver, atLabel); + builder.Add(Assert(receiver.tok, wh, desc, builder.Context)); } } Contract.Assert(callee.Ins.Count == Args.Count); @@ -265,13 +265,13 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E Bpl.Expr wh = GetWhereClause(ee.tok, etran.TrExpr(ee), ee.Type, etran.OldAt(atLabel), ISALLOC, true); if (wh != null) { var pIdx = Args.Count == 1 ? "" : " at index " + i; - var desc = new PODesc.IsAllocated( + var desc = new IsAllocated( $"argument{pIdx} for parameter '{formal.Name}'", - "in the two-state lemma's previous state" + PODesc.IsAllocated.HelperFormal(formal), + "in the two-state lemma's previous state" + IsAllocated.HelperFormal(formal), ee, atLabel ); - builder.Add(Assert(ee.tok, wh, desc)); + builder.Add(Assert(ee.tok, wh, desc, builder.Context)); } } } @@ -284,7 +284,7 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E if (etran.readsFrame != null) { // substitute actual args for parameters in description expression frames... var requiredFrames = callee.Reads.Expressions.ConvertAll(directSub.SubstFrameExpr); - var desc = new PODesc.ReadFrameSubset("call", requiredFrames, GetContextReadsFrames()); + var desc = new ReadFrameSubset("call", requiredFrames, GetContextReadsFrames()); // ... but that substitution isn't needed for frames passed to CheckFrameSubset var readsSubst = new Substituter(null, new Dictionary(), tySubst); @@ -297,7 +297,7 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E // Check that the modifies clause of a subcall is a subset of the current modifies frame, // but only if we're in a context that defines a modifies frame. if (codeContext is IMethodCodeContext methodCodeContext) { - var desc = new PODesc.ModifyFrameSubset( + var desc = new ModifyFrameSubset( "call", frameExpressions, methodCodeContext.Modifies.Expressions @@ -313,7 +313,7 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E if (isRecursiveCall) { Contract.Assert(codeContext != null); if (codeContext is DatatypeDecl) { - builder.Add(Assert(tok, Bpl.Expr.False, new PODesc.IsNonRecursive())); + builder.Add(Assert(tok, Bpl.Expr.False, new IsNonRecursive(), builder.Context)); } else { List contextDecreases = codeContext.Decreases.Expressions; List calleeDecreases = callee.Decreases.Expressions; @@ -344,42 +344,21 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E } } - if (cs.Proof == null) { - AddCall(builder); - } else { - var callBuilder = new BoogieStmtListBuilder(this, options, builder.Context); - AddComment(callBuilder, cs, "call statement proof"); - CurrentIdGenerator.Push(); - TrStmt(cs.Proof, callBuilder, locals, etran); - CurrentIdGenerator.Pop(); - AddCall(callBuilder); - PathAsideBlock(cs.Tok, callBuilder, builder); - } - - void AddCall(BoogieStmtListBuilder callBuilder) { - callBuilder.Add(new CommentCmd($"ProcessCallStmt: Check precondition")); - // Make the call - AddReferencedMember(callee); - var call = Call(tok, MethodName(callee, isCoCall ? MethodTranslationKind.CoCallPre : MethodTranslationKind.CallPre), ins, new List()); - proofDependencies?.AddProofDependencyId(call, tok, new CallDependency(cs)); - if ( - (assertionOnlyFilter != null && !assertionOnlyFilter(tok)) || - (module != currentModule && RefinementToken.IsInherited(tok, currentModule) && (codeContext == null || !codeContext.MustReverify))) { - // The call statement is inherited, so the refined module already checked that the precondition holds. Note, - // preconditions are not allowed to be strengthened, except if they use a predicate whose body has been strengthened. - // But if the callee sits in a different module, then any predicate it uses will be treated as opaque (that is, - // uninterpreted) anyway, so the refined module will have checked the call precondition for all possible definitions - // of the predicate. - call.IsFree = true; - } - callBuilder.Add(call); + AddReferencedMember(callee); + var calleeName = MethodName(callee, isCoCall ? MethodTranslationKind.CoCall : MethodTranslationKind.Call); + var call = Call(builder.Context, tok, calleeName, ins, outs); + proofDependencies?.AddProofDependencyId(call, tok, new CallDependency(cs)); + if ( + (assertionOnlyFilter != null && !assertionOnlyFilter(tok)) || + (module != currentModule && RefinementToken.IsInherited(tok, currentModule) && (codeContext == null || !codeContext.MustReverify))) { + // The call statement is inherited, so the refined module already checked that the precondition holds. Note, + // preconditions are not allowed to be strengthened, except if they use a predicate whose body has been strengthened. + // But if the callee sits in a different module, then any predicate it uses will be treated as opaque (that is, + // uninterpreted) anyway, so the refined module will have checked the call precondition for all possible definitions + // of the predicate. + call.IsFree = true; } - - builder.Add(new CommentCmd("ProcessCallStmt: Make the call")); - var post = Call(tok, - MethodName(callee, isCoCall ? MethodTranslationKind.CoCallPost : MethodTranslationKind.CallPost), ins, outs); - proofDependencies?.AddProofDependencyId(post, tok, new CallDependency(cs)); - builder.Add(post); + builder.Add(call); // Unbox results as needed for (int i = 0; i < Lhss.Count; i++) { diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs index 00f6c7460b1..ab7e6ae4a58 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs @@ -10,7 +10,7 @@ namespace Microsoft.Dafny; public partial class BoogieGenerator { - private void TrForallStmt(BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + private void TrForallStmt(BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, ForallStmt forallStmt) { this.fuelContext = FuelSetting.ExpandFuelContext(forallStmt.Attributes, forallStmt.Tok, this.fuelContext, this.reporter); @@ -72,7 +72,7 @@ private void TrForallStmt(BoogieStmtListBuilder builder, List locals, void TrForallStmtCall(IToken tok, List boundVars, List bounds, Expression range, ExpressionConverter additionalRange, List forallExpressions, CallStmt s0, - BoogieStmtListBuilder definedness, BoogieStmtListBuilder exporter, List locals, ExpressionTranslator etran) { + BoogieStmtListBuilder definedness, BoogieStmtListBuilder exporter, Variables locals, ExpressionTranslator etran) { Contract.Requires(tok != null); Contract.Requires(boundVars != null); Contract.Requires(bounds != null); @@ -210,7 +210,7 @@ void TrForallStmtCall(IToken tok, List boundVars, List bo } void TrForallAssign(ForallStmt s, SingleAssignStmt s0, - BoogieStmtListBuilder definedness, BoogieStmtListBuilder updater, List locals, ExpressionTranslator etran) { + BoogieStmtListBuilder definedness, BoogieStmtListBuilder updater, Variables locals, ExpressionTranslator etran) { // The statement: // forall (x,y | Range(x,y)) { // (a) E(x,y) . f := G(x,y); @@ -294,9 +294,9 @@ void TrForallAssign(ForallStmt s, SingleAssignStmt s0, MultiSelectExpr e => (e.Array, null), _ => throw new cce.UnreachableException() }; - var desc = new PODesc.Modifiable(description, GetContextModifiesFrames(), lhsObj, lhsField); + var desc = new Modifiable(description, GetContextModifiesFrames(), lhsObj, lhsField); definedness.Add(Assert(lhs.tok, Bpl.Expr.SelectTok(lhs.tok, etran.ModifiesFrame(lhs.tok), obj, F), - desc)); + desc, definedness.Context)); if (s0.Rhs is ExprRhs) { var r = (ExprRhs)s0.Rhs; var rhs = Substitute(r.Expr, null, substMap); @@ -349,7 +349,7 @@ void TrForallAssign(ForallStmt s, SingleAssignStmt s0, BplOr( BplOr(Bpl.Expr.Neq(obj, objPrime), Bpl.Expr.Neq(F, FPrime)), Bpl.Expr.Eq(rhs, rhsPrime)), - new PODesc.ForallLHSUnique(s.BoundVars, s.Range, lhsComponents, Rhs))); + new ForallLHSUnique(s.BoundVars, s.Range, lhsComponents, Rhs), definedness.Context)); } definedness.Add(TrAssumeCmd(s.Tok, Bpl.Expr.False)); @@ -457,7 +457,7 @@ IEnumerable TransitiveSubstatements(Statement s) { } void TrForallProof(ForallStmt forallStmt, BoogieStmtListBuilder definedness, BoogieStmtListBuilder exporter, - List locals, ExpressionTranslator etran) { + Variables locals, ExpressionTranslator etran) { // Translate: // forall (x,y | Range(x,y)) // ensures Post(x,y); @@ -511,7 +511,7 @@ void TrForallProof(ForallStmt forallStmt, BoogieStmtListBuilder definedness, Boo foreach (var ens in forallStmt.Ens) { foreach (var split in TrSplitExpr(definedness.Context, ens.E, etran, true, out var splitHappened)) { if (split.IsChecked) { - definedness.Add(Assert(split.Tok, split.E, new PODesc.ForallPostcondition(ens.E))); + definedness.Add(Assert(split.Tok, split.E, new ForallPostcondition(ens.E), definedness.Context)); } } } diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs index 459cfa8bfac..cebd7141fd1 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs @@ -11,7 +11,7 @@ namespace Microsoft.Dafny; public partial class BoogieGenerator { - private void TrAlternativeLoopStmt(AlternativeLoopStmt stmt, BoogieStmtListBuilder builder, List locals, + private void TrAlternativeLoopStmt(AlternativeLoopStmt stmt, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { AddComment(builder, stmt, "alternative loop statement"); var tru = Expression.CreateBoolLiteral(stmt.Tok, true); @@ -30,7 +30,7 @@ private void TrAlternativeLoopStmt(AlternativeLoopStmt stmt, BoogieStmtListBuild builder, locals, etran); } - private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { Contract.Requires(stmt != null); Contract.Requires(builder != null); Contract.Requires(locals != null); @@ -41,8 +41,7 @@ private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, List new Bpl.LocalVariable(indexVar.tok, new Bpl.TypedIdent(indexVar.Tok, indexVarName, TrType(indexVar.Type)))); var bIndex = new Bpl.IdentifierExpr(indexVar.tok, indexVarName); var lo = stmt.GoingUp ? stmt.Start : stmt.End; @@ -53,8 +52,7 @@ private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, List new Bpl.LocalVariable(lo.tok, new Bpl.TypedIdent(lo.tok, name, Bpl.Type.Int))); bLo = new Bpl.IdentifierExpr(lo.tok, name); CheckWellformed(lo, new WFOptions(null, false), locals, builder, etran); builder.Add(Bpl.Cmd.SimpleAssign(lo.tok, bLo, etran.TrExpr(lo))); @@ -62,8 +60,7 @@ private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, List new Bpl.LocalVariable(hi.tok, new Bpl.TypedIdent(hi.tok, name, Bpl.Type.Int))); bHi = new Bpl.IdentifierExpr(hi.tok, name); CheckWellformed(hi, new WFOptions(null, false), locals, builder, etran); builder.Add(Bpl.Cmd.SimpleAssign(hi.tok, bHi, etran.TrExpr(hi))); @@ -72,7 +69,7 @@ private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, List Is(x, typ) { @@ -83,7 +80,6 @@ private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, List new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, name, Bpl.Type.Int))); builder.Add(new Bpl.HavocCmd(tok, new List() { x })); builder.Add(new Bpl.AssumeCmd(tok, ForLoopBounds(x, bLo, bHi))); List dafnyRangeBounds = new(); @@ -110,7 +106,7 @@ private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, List { indexVar }, dafnyRange, new TypeTestExpr(indexVar.tok, dIndex, indexVar.Type), null); - builder.Add(Assert(tok, cre, new PODesc.ForRangeAssignable(desc, dafnyAssertion))); + builder.Add(Assert(tok, cre, new ForRangeAssignable(desc, dafnyAssertion), builder.Context)); } } @@ -147,7 +143,7 @@ private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + private void TrWhileStmt(WhileStmt stmt, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { Contract.Requires(stmt != null); Contract.Requires(builder != null); Contract.Requires(locals != null); @@ -169,47 +165,55 @@ private void TrWhileStmt(WhileStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + void TrLoop(LoopStmt loop, Expression Guard, BodyTranslator/*?*/ bodyTr, + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, Bpl.Expr freeInvariant = null, bool includeTerminationCheck = true) { - Contract.Requires(s != null); + Contract.Requires(loop != null); Contract.Requires(builder != null); Contract.Requires(locals != null); Contract.Requires(etran != null); - s.ScopeDepth = builder.Context.ScopeDepth; + loop.ScopeDepth = builder.Context.ScopeDepth; var suffix = CurrentIdGenerator.FreshId("loop#"); - var theDecreases = s.Decreases.Expressions; + var theDecreases = loop.Decreases.Expressions; - Bpl.LocalVariable preLoopHeapVar = new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$PreLoopHeap$" + suffix, predef.HeapType)); - locals.Add(preLoopHeapVar); - Bpl.IdentifierExpr preLoopHeap = new Bpl.IdentifierExpr(s.Tok, preLoopHeapVar); + var preloopheap = "$PreLoopHeap$" + suffix; + var preLoopHeapVar = locals.GetOrCreate(preloopheap, () => new Bpl.LocalVariable(loop.Tok, new Bpl.TypedIdent(loop.Tok, preloopheap, predef.HeapType))); + Bpl.IdentifierExpr preLoopHeap = new Bpl.IdentifierExpr(loop.Tok, preLoopHeapVar); ExpressionTranslator etranPreLoop = new ExpressionTranslator(this, predef, preLoopHeap, etran.scope); ExpressionTranslator updatedFrameEtran; string loopFrameName = FrameVariablePrefix + suffix; - if (s.Mod.Expressions != null) { + if (loop.Mod.Expressions != null) { updatedFrameEtran = etran.WithModifiesFrame(loopFrameName); } else { updatedFrameEtran = etran; } - if (s.Mod.Expressions != null) { // check well-formedness and that the modifies is a subset - CheckFrameWellFormed(new WFOptions(), s.Mod.Expressions, locals, builder, etran); - var desc = new PODesc.ModifyFrameSubset("loop modifies clause", s.Mod.Expressions, GetContextModifiesFrames()); - CheckFrameSubset(s.Tok, s.Mod.Expressions, null, null, etran, etran.ModifiesFrame(s.Tok), builder, desc, null); - DefineFrame(s.Tok, etran.ModifiesFrame(s.Tok), s.Mod.Expressions, builder, locals, loopFrameName); + if (loop.Mod.Expressions != null) { // check well-formedness and that the modifies is a subset + CheckFrameWellFormed(new WFOptions(), loop.Mod.Expressions, locals, builder, etran); + var desc = new ModifyFrameSubset("loop modifies clause", loop.Mod.Expressions, GetContextModifiesFrames()); + CheckFrameSubset(loop.Tok, loop.Mod.Expressions, null, null, etran, etran.ModifiesFrame(loop.Tok), builder, desc, null); + DefineFrame(loop.Tok, etran.ModifiesFrame(loop.Tok), loop.Mod.Expressions, builder, locals, loopFrameName); } - builder.Add(Bpl.Cmd.SimpleAssign(s.Tok, preLoopHeap, etran.HeapExpr)); + builder.Add(Bpl.Cmd.SimpleAssign(loop.Tok, preLoopHeap, etran.HeapExpr)); + + var assignedVariables = loop.DescendantsAndSelf. + SelectMany(s => s.GetAssignedLocals()).Select(ie => ie.Var) + .ToHashSet(); var daTrackersMonotonicity = new List>(); - foreach (var dat in DefiniteAssignmentTrackers.Values) { // TODO: the order is non-deterministic and may change between invocations of Dafny - var preLoopDat = new Bpl.LocalVariable(dat.tok, new Bpl.TypedIdent(dat.tok, "preLoop$" + suffix + "$" + dat.Name, dat.Type)); - locals.Add(preLoopDat); - var ie = new Bpl.IdentifierExpr(s.Tok, preLoopDat); + foreach (var local in assignedVariables) { + if (local.UniqueName == null || !DefiniteAssignmentTrackers.TryGetValue(local.UniqueName, out var dat)) { + continue; + } + + var name = "preLoop$" + suffix + "$" + dat.Name; + var preLoopDat = locals.GetOrCreate(name, () => new Bpl.LocalVariable(dat.tok, new Bpl.TypedIdent(dat.tok, name, dat.Type))); + var ie = new Bpl.IdentifierExpr(loop.Tok, preLoopDat); daTrackersMonotonicity.Add(new Tuple(ie, dat)); - builder.Add(Bpl.Cmd.SimpleAssign(s.Tok, ie, dat)); + builder.Add(Cmd.SimpleAssign(loop.Tok, ie, dat)); } List initDecr = null; @@ -219,18 +223,17 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, // The variable w is used to coordinate the definedness checking of the loop invariant. // It is also used for body-less loops to turn off invariant checking after the generated body. - Bpl.LocalVariable wVar = new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$w$" + suffix, Bpl.Type.Bool)); - Bpl.IdentifierExpr w = new Bpl.IdentifierExpr(s.Tok, wVar); - locals.Add(wVar); + var wVar = locals.GetOrAdd(new Bpl.LocalVariable(loop.Tok, new Bpl.TypedIdent(loop.Tok, "$w$" + suffix, Bpl.Type.Bool))); + Bpl.IdentifierExpr w = new Bpl.IdentifierExpr(loop.Tok, wVar); // havoc w; - builder.Add(new Bpl.HavocCmd(s.Tok, new List { w })); + builder.Add(new Bpl.HavocCmd(loop.Tok, new List { w })); List invariants = new List(); if (freeInvariant != null) { invariants.Add(new Bpl.AssumeCmd(freeInvariant.tok, freeInvariant)); } BoogieStmtListBuilder invDefinednessBuilder = new BoogieStmtListBuilder(this, options, builder.Context); - foreach (AttributedExpression loopInv in s.Invariants) { + foreach (AttributedExpression loopInv in loop.Invariants) { var (errorMessage, successMessage) = CustomErrorMessage(loopInv.Attributes); TrStmt_CheckWellformed(loopInv.E, invDefinednessBuilder, locals, etran, false); invDefinednessBuilder.Add(TrAssumeCmdWithDependencies(etran, loopInv.E.tok, loopInv.E, "loop invariant")); @@ -239,12 +242,12 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, var ss = TrSplitExpr(builder.Context, loopInv.E, etran, false, out var splitHappened); if (!splitHappened) { var wInv = BplImp(w, etran.TrExpr(loopInv.E)); - invariants.Add(Assert(loopInv.E.tok, wInv, new PODesc.LoopInvariant(loopInv.E, errorMessage, successMessage))); + invariants.Add(Assert(loopInv.E.tok, wInv, new LoopInvariant(loopInv.E, errorMessage, successMessage), builder.Context)); } else { foreach (var split in ss) { var wInv = Bpl.Expr.Binary(split.E.tok, BinaryOperator.Opcode.Imp, w, split.E); if (split.IsChecked) { - invariants.Add(Assert(split.Tok, wInv, new PODesc.LoopInvariant(loopInv.E, errorMessage, successMessage))); // TODO: it would be fine to have this use {:subsumption 0} + invariants.Add(Assert(split.Tok, wInv, new LoopInvariant(loopInv.E, errorMessage, successMessage), builder.Context)); // TODO: it would be fine to have this use {:subsumption 0} } else { var cmd = TrAssumeCmd(split.E.tok, wInv); proofDependencies?.AddProofDependencyId(cmd, loopInv.E.tok, new InvariantDependency(loopInv.E)); @@ -263,34 +266,34 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, // add "this" to the explicit modifies clause var explicitModifies = modifiesClause; modifiesClause = new List(); - modifiesClause.Add(new FrameExpression(s.Tok, new ThisExpr((IteratorDecl)codeContext), null)); + modifiesClause.Add(new FrameExpression(loop.Tok, new ThisExpr((IteratorDecl)codeContext), null)); modifiesClause.AddRange(explicitModifies); } // include boilerplate invariants - foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(s.Tok, modifiesClause, s.IsGhost, codeContext.AllowsAllocation, etranPreLoop, etran, etran.Old)) { + foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(loop.Tok, modifiesClause, loop.IsGhost, codeContext.AllowsAllocation, etranPreLoop, etran, etran.Old)) { if (tri.IsFree) { - invariants.Add(TrAssumeCmd(s.Tok, tri.Expr)); + invariants.Add(TrAssumeCmd(loop.Tok, tri.Expr)); } else { Contract.Assert(tri.ErrorMessage != null); // follows from BoilerplateTriple invariant - invariants.Add(Assert(s.Tok, tri.Expr, new PODesc.BoilerplateTriple(tri.ErrorMessage, tri.SuccessMessage, tri.Comment))); + invariants.Add(Assert(loop.Tok, tri.Expr, new Microsoft.Dafny.BoilerplateTriple(tri.ErrorMessage, tri.SuccessMessage, tri.Comment), builder.Context)); } } // add a free invariant which says that the heap hasn't changed outside of the modifies clause. - invariants.Add(TrAssumeCmd(s.Tok, FrameConditionUsingDefinedFrame(s.Tok, etranPreLoop, etran, updatedFrameEtran, updatedFrameEtran.ModifiesFrame(s.Tok)))); + invariants.Add(TrAssumeCmd(loop.Tok, FrameConditionUsingDefinedFrame(loop.Tok, etranPreLoop, etran, updatedFrameEtran, updatedFrameEtran.ModifiesFrame(loop.Tok)))); // for iterators, add "fresh(_new)" as an invariant if (codeContext is IteratorDecl iter) { var th = new ThisExpr(iter); - var thisDotNew = new MemberSelectExpr(s.Tok, th, iter.Member_New); - var fr = new FreshExpr(s.Tok, thisDotNew); + var thisDotNew = new MemberSelectExpr(loop.Tok, th, iter.Member_New); + var fr = new FreshExpr(loop.Tok, thisDotNew); fr.Type = Type.Bool; - invariants.Add(TrAssertCmd(s.Tok, etran.TrExpr(fr))); + invariants.Add(TrAssertCmd(loop.Tok, etran.TrExpr(fr))); } } // include a free invariant that says that all definite-assignment trackers have only become more "true" foreach (var pair in daTrackersMonotonicity) { Bpl.Expr monotonic = BplImp(pair.Item1, pair.Item2); - invariants.Add(TrAssumeCmd(s.Tok, monotonic)); + invariants.Add(TrAssumeCmd(loop.Tok, monotonic)); } // include a free invariant that says that all completed iterations so far have only decreased the termination metric @@ -304,21 +307,21 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, toks.Add(e.tok); decrsDafny.Add(e); decrs.Add(etran.TrExpr(e)); - var (prevVars, eInit) = TranslateToLoopEntry(s, e, "LoopEntry"); + var (prevVars, eInit) = TranslateToLoopEntry(loop, e, "LoopEntry"); prevGhostLocals.AddRange(prevVars); initDecrsDafny.Add(eInit); } Bpl.Expr decrCheck = DecreasesCheck(toks, prevGhostLocals, decrsDafny, initDecrsDafny, decrs, initDecr, null, null, true, false); - invariants.Add(TrAssumeCmd(s.Tok, decrCheck)); + invariants.Add(TrAssumeCmd(loop.Tok, decrCheck)); } var loopBodyBuilder = new BoogieStmtListBuilder(this, options, builder.Context); - loopBodyBuilder.AddCaptureState(s.Tok, true, CaptureStateExtensions.AfterLoopIterationsStateMarker); + loopBodyBuilder.AddCaptureState(loop.Tok, true, CaptureStateExtensions.AfterLoopIterationsStateMarker); // As the first thing inside the loop, generate: if (!w) { CheckWellformed(inv); assume false; } - invDefinednessBuilder.Add(TrAssumeCmd(s.Tok, Bpl.Expr.False)); - loopBodyBuilder.Add(new Bpl.IfCmd(s.Tok, Bpl.Expr.Not(w), invDefinednessBuilder.Collect(s.Tok), null, null)); + invDefinednessBuilder.Add(TrAssumeCmd(loop.Tok, Bpl.Expr.False)); + loopBodyBuilder.Add(new Bpl.IfCmd(loop.Tok, Bpl.Expr.Not(w), invDefinednessBuilder.Collect(loop.Tok), null, null)); // Generate: CheckWellformed(guard); if (!guard) { break; } // but if this is a body-less loop, put all of that inside: if (*) { ... } @@ -326,7 +329,7 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, // on entry to the loop, and then Boogie wouldn't consider this a loop at all. (See also comment // in methods GuardAlwaysHoldsOnEntry_BodyLessLoop and GuardAlwaysHoldsOnEntry_LoopWithBody in // Test/dafny0/DirtyLoops.dfy.) - var isBodyLessLoop = s is OneBodyLoopStmt { BodySurrogate: { } }; + var isBodyLessLoop = loop is OneBodyLoopStmt { BodySurrogate: { } }; var whereToBuildLoopGuard = isBodyLessLoop ? new BoogieStmtListBuilder(this, options, builder.Context) : loopBodyBuilder; Bpl.Expr guard = null; if (Guard != null) { @@ -334,10 +337,10 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, guard = Bpl.Expr.Not(etran.TrExpr(Guard)); } var guardBreak = new BoogieStmtListBuilder(this, options, builder.Context); - guardBreak.Add(new Bpl.BreakCmd(s.Tok, null)); - whereToBuildLoopGuard.Add(new Bpl.IfCmd(s.Tok, guard, guardBreak.Collect(s.Tok), null, null)); + guardBreak.Add(new Bpl.BreakCmd(loop.Tok, null)); + whereToBuildLoopGuard.Add(new Bpl.IfCmd(loop.Tok, guard, guardBreak.Collect(loop.Tok), null, null)); if (isBodyLessLoop) { - loopBodyBuilder.Add(new Bpl.IfCmd(s.Tok, null, whereToBuildLoopGuard.Collect(s.Tok), null, null)); + loopBodyBuilder.Add(new Bpl.IfCmd(loop.Tok, null, whereToBuildLoopGuard.Collect(loop.Tok), null, null)); } if (bodyTr != null) { @@ -360,42 +363,42 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, // Note: the label "LoopEntry" doesn't exist in the program, and is // useful only for explanatory purposes. decrsDafny.Add(e); - var (prevVars, eInit) = TranslateToLoopEntry(s, e, "LoopEntry"); + var (prevVars, eInit) = TranslateToLoopEntry(loop, e, "LoopEntry"); prevGhostLocals.AddRange(prevVars); initDecrsDafny.Add(eInit); decrs.Add(etran.TrExpr(e)); } if (includeTerminationCheck) { - AddComment(loopBodyBuilder, s, "loop termination check"); + AddComment(loopBodyBuilder, loop, "loop termination check"); Bpl.Expr decrCheck = DecreasesCheck(toks, prevGhostLocals, decrsDafny, initDecrsDafny, decrs, oldBfs, loopBodyBuilder, " at end of loop iteration", false, false); var description = new - PODesc.Terminates(s.InferredDecreases, prevGhostLocals, null, initDecrsDafny, theDecreases, false); - loopBodyBuilder.Add(Assert(s.Tok, decrCheck, description)); + Terminates(loop.InferredDecreases, prevGhostLocals, null, initDecrsDafny, theDecreases, false); + loopBodyBuilder.Add(Assert(loop.Tok, decrCheck, description, builder.Context)); } } } else if (isBodyLessLoop) { - var bodySurrogate = ((OneBodyLoopStmt)s).BodySurrogate; + var bodySurrogate = ((OneBodyLoopStmt)loop).BodySurrogate; // This is a body-less loop. Havoc the targets and then set w to false, to make the loop-invariant // maintenance check vaccuous. - var bplTargets = bodySurrogate.LocalLoopTargets.ConvertAll(v => TrVar(s.Tok, v)); + var bplTargets = bodySurrogate.LocalLoopTargets.ConvertAll(v => TrVar(loop.Tok, v)); if (bodySurrogate.UsesHeap) { bplTargets.Add(etran.HeapCastToIdentifierExpr); } - loopBodyBuilder.Add(new Bpl.HavocCmd(s.Tok, bplTargets)); - loopBodyBuilder.Add(Bpl.Cmd.SimpleAssign(s.Tok, w, Bpl.Expr.False)); + loopBodyBuilder.Add(new Bpl.HavocCmd(loop.Tok, bplTargets)); + loopBodyBuilder.Add(Bpl.Cmd.SimpleAssign(loop.Tok, w, Bpl.Expr.False)); } // 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. Note, it is important (see Test/git-issues/git-issue-1812.dfy) // that each CanCall assumption uses the preceding invariants as antecedents--this is achieved by treating all "invariant" // declarations as one big conjunction, because then CanCallAssumption will add the needed antecedents. - if (s.Invariants.Any()) { - var allInvariants = s.Invariants.Select(inv => inv.E).Aggregate((a, b) => Expression.CreateAnd(a, b)); - loopBodyBuilder.Add(TrAssumeCmd(s.Tok, etran.CanCallAssumption(allInvariants))); + if (loop.Invariants.Any()) { + var allInvariants = loop.Invariants.Select(inv => inv.E).Aggregate((a, b) => Expression.CreateAnd(a, b)); + loopBodyBuilder.Add(TrAssumeCmd(loop.Tok, etran.CanCallAssumption(allInvariants))); } - Bpl.StmtList body = loopBodyBuilder.Collect(s.Tok); - builder.Add(new Bpl.WhileCmd(s.Tok, Bpl.Expr.True, invariants, new List(), body)); + Bpl.StmtList body = loopBodyBuilder.Collect(loop.Tok); + builder.Add(new Bpl.WhileCmd(loop.Tok, Bpl.Expr.True, invariants, new List(), body)); } // Return the version of e that holds at the beginnging of the loop, diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs index 4dd4e4c0ce8..39d271c2c56 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs @@ -13,7 +13,7 @@ namespace Microsoft.Dafny { public partial class BoogieGenerator { private void TrPredicateStmt(PredicateStmt stmt, BoogieStmtListBuilder builder, - List locals, ExpressionTranslator etran) { + Variables locals, ExpressionTranslator etran) { Contract.Requires(stmt != null); Contract.Requires(builder != null); Contract.Requires(locals != null); @@ -30,7 +30,7 @@ private void TrPredicateStmt(PredicateStmt stmt, BoogieStmtListBuilder builder, fuelContext = FuelSetting.PopFuelContext(); } - private void TrAssumeStmt(AssumeStmt assumeStmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + private void TrAssumeStmt(AssumeStmt assumeStmt, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { AddComment(builder, assumeStmt, "assume statement"); stmtContext = StmtType.ASSUME; TrStmt_CheckWellformed(assumeStmt.Expr, builder, locals, etran, false); @@ -42,7 +42,7 @@ private void TrAssumeStmt(AssumeStmt assumeStmt, BoogieStmtListBuilder builder, } } - private void TrExpectStmt(BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, ExpectStmt expectStmt) { + private void TrExpectStmt(BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, ExpectStmt expectStmt) { AddComment(builder, expectStmt, "expect statement"); stmtContext = StmtType.ASSUME; TrStmt_CheckWellformed(expectStmt.Expr, builder, locals, etran, false); @@ -64,7 +64,7 @@ private void TrExpectStmt(BoogieStmtListBuilder builder, List locals, stmtContext = StmtType.NONE; // done with translating expect stmt. } - public void TrAssertStmt(PredicateStmt stmt, BoogieStmtListBuilder builder, List locals, + public void TrAssertStmt(PredicateStmt stmt, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { var stmtBuilder = new BoogieStmtListBuilder(this, options, builder.Context); var defineFuel = DefineFuelConstant(stmt.Tok, stmt.Attributes, stmtBuilder, etran); @@ -76,23 +76,14 @@ public void TrAssertStmt(PredicateStmt stmt, BoogieStmtListBuilder builder, List var hiddenProof = false; BoogieStmtListBuilder proofBuilder = null; var assertStmt = stmt as AssertStmt; - if (assertStmt != null) { - if (assertStmt.Proof != null) { - hiddenProof = true; - proofBuilder = new BoogieStmtListBuilder(this, options, builder.Context); - AddComment(proofBuilder, stmt, "assert statement proof"); - CurrentIdGenerator.Push(); - TrStmt(((AssertStmt)stmt).Proof, proofBuilder, locals, etran); - CurrentIdGenerator.Pop(); - } else if (assertStmt.Label != null) { - hiddenProof = true; - proofBuilder = new BoogieStmtListBuilder(this, options, builder.Context); - AddComment(proofBuilder, stmt, "assert statement proof"); - } + if (assertStmt is { Label: not null }) { + hiddenProof = true; + proofBuilder = new BoogieStmtListBuilder(this, options, builder.Context); + AddComment(proofBuilder, stmt, "assert statement proof"); } proofBuilder ??= b; - var splitHappened = TrAssertCondition(stmt, builder, etran, proofBuilder); + var splitHappened = TrAssertCondition(stmt, etran, proofBuilder); if (hiddenProof) { PathAsideBlock(stmt.Tok, proofBuilder, b); @@ -103,8 +94,7 @@ public void TrAssertStmt(PredicateStmt stmt, BoogieStmtListBuilder builder, List if (assertStmt is { Label: not null }) { // make copies of the variables used in the assertion var name = "$Heap_at_" + assertStmt.Label.AssignUniqueId(CurrentIdGenerator); - var heapAt = new Bpl.LocalVariable(stmt.Tok, new Bpl.TypedIdent(stmt.Tok, name, predef.HeapType)); - locals.Add(heapAt); + var heapAt = locals.GetOrAdd(new Bpl.LocalVariable(stmt.Tok, new Bpl.TypedIdent(stmt.Tok, name, predef.HeapType))); var heapReference = new Bpl.IdentifierExpr(stmt.Tok, heapAt); b.Add(Bpl.Cmd.SimpleAssign(stmt.Tok, heapReference, etran.HeapExpr)); var substMap = new Dictionary(); @@ -118,7 +108,7 @@ public void TrAssertStmt(PredicateStmt stmt, BoogieStmtListBuilder builder, List ie.Var = vcopy; ie.Type = ie.Var.Type; // resolve ie here substMap.Add(v, ie); - locals.Add(new Bpl.LocalVariable(vcopy.Tok, + locals.GetOrAdd(new Bpl.LocalVariable(vcopy.Tok, new Bpl.TypedIdent(vcopy.Tok, vcopy.AssignUniqueName(currentDeclaration.IdGenerator), TrType(vcopy.Type)))); b.Add(Bpl.Cmd.SimpleAssign(stmt.Tok, TrVar(stmt.Tok, vcopy), TrVar(stmt.Tok, v))); @@ -153,7 +143,7 @@ public void TrAssertStmt(PredicateStmt stmt, BoogieStmtListBuilder builder, List } } - private bool TrAssertCondition(PredicateStmt stmt, BoogieStmtListBuilder builder, + private bool TrAssertCondition(PredicateStmt stmt, ExpressionTranslator etran, BoogieStmtListBuilder proofBuilder) { IToken enclosingToken = null; if (Attributes.Contains(stmt.Attributes, "_prependAssertToken")) { @@ -161,18 +151,18 @@ private bool TrAssertCondition(PredicateStmt stmt, BoogieStmtListBuilder builder } var (errorMessage, successMessage) = CustomErrorMessage(stmt.Attributes); - var splits = TrSplitExpr(builder.Context, stmt.Expr, etran, true, out var splitHappened); + var splits = TrSplitExpr(proofBuilder.Context, stmt.Expr, etran, true, out var splitHappened); if (!splitHappened) { var tok = enclosingToken == null ? GetToken(stmt.Expr) : new NestedToken(enclosingToken, GetToken(stmt.Expr)); - var desc = new PODesc.AssertStatementDescription(stmt, errorMessage, successMessage); - proofBuilder.Add(Assert(tok, etran.TrExpr(stmt.Expr), desc, stmt.Tok, + var desc = new AssertStatementDescription(stmt, errorMessage, successMessage); + proofBuilder.Add(Assert(tok, etran.TrExpr(stmt.Expr), desc, stmt.Tok, proofBuilder.Context, etran.TrAttributes(stmt.Attributes, null))); } else { foreach (var split in splits) { if (split.IsChecked) { var tok = enclosingToken == null ? split.E.tok : new NestedToken(enclosingToken, split.Tok); - var desc = new PODesc.AssertStatementDescription(stmt, errorMessage, successMessage); - proofBuilder.Add(AssertNS(ToDafnyToken(flags.ReportRanges, tok), split.E, desc, stmt.Tok, + var desc = new AssertStatementDescription(stmt, errorMessage, successMessage); + proofBuilder.Add(AssertAndForget(proofBuilder.Context, ToDafnyToken(flags.ReportRanges, tok), split.E, desc, stmt.Tok, etran.TrAttributes(stmt.Attributes, null))); // attributes go on every split } } diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs index f3866215a38..1c34054edad 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs @@ -5,9 +5,7 @@ using DafnyCore.Verifier; using Microsoft.Boogie; using Bpl = Microsoft.Boogie; -using BplParser = Microsoft.Boogie.Parser; using static Microsoft.Dafny.Util; -using Action = System.Action; using PODesc = Microsoft.Dafny.ProofObligationDescription; namespace Microsoft.Dafny; @@ -15,8 +13,8 @@ namespace Microsoft.Dafny; public partial class BoogieGenerator { public const string FrameVariablePrefix = "$Frame$"; - private void TrStmt(Statement stmt, BoogieStmtListBuilder builder, - List locals, ExpressionTranslator etran) { + public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, + Variables locals, ExpressionTranslator etran) { stmt.ScopeDepth = builder.Context.ScopeDepth; @@ -131,8 +129,8 @@ private void TrStmt(Statement stmt, BoogieStmtListBuilder builder, // this postcondition was inherited into this module, so just ignore it } else if (split.IsChecked) { var yieldToken = new NestedToken(s.Tok, split.Tok); - var desc = new PODesc.YieldEnsures(fieldSub.Substitute(p.E)); - builder.Add(AssertNS(yieldToken, split.E, desc, stmt.Tok, null)); + var desc = new YieldEnsures(fieldSub.Substitute(p.E)); + builder.Add(AssertAndForget(builder.Context, yieldToken, split.E, desc, stmt.Tok, null)); } } builder.Add(TrAssumeCmdWithDependencies(yeEtran, stmt.Tok, p.E, "yield ensures clause")); @@ -252,7 +250,7 @@ private void TrStmt(Statement stmt, BoogieStmtListBuilder builder, } else if (stmt is DividedBlockStmt) { var s = (DividedBlockStmt)stmt; AddComment(builder, stmt, "divided block before new;"); - var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; + var previousTrackers = DefiniteAssignmentTrackers; var tok = s.SeparatorTok ?? s.Tok; // a DividedBlockStmt occurs only inside a Constructor body of a class var cl = (ClassDecl)((Constructor)codeContext).EnclosingClass; @@ -261,6 +259,7 @@ private void TrStmt(Statement stmt, BoogieStmtListBuilder builder, fields.RemoveAll(f => f == null); var localSurrogates = fields.ConvertAll(f => new Bpl.LocalVariable(f.tok, new TypedIdent(f.tok, SurrogateName(f), TrType(f.Type)))); locals.AddRange(localSurrogates); + var beforeTrackers = DefiniteAssignmentTrackers; fields.ForEach(f => AddDefiniteAssignmentTrackerSurrogate(f, cl, locals, codeContext is Constructor && codeContext.IsGhost)); Contract.Assert(!inBodyInitContext); @@ -272,7 +271,7 @@ private void TrStmt(Statement stmt, BoogieStmtListBuilder builder, // The "new;" translates into an allocation of "this" AddComment(builder, stmt, "new;"); fields.ForEach(f => CheckDefiniteAssignmentSurrogate(s.SeparatorTok ?? s.RangeToken.EndToken, f, true, builder)); - fields.ForEach(RemoveDefiniteAssignmentTrackerSurrogate); + DefiniteAssignmentTrackers = beforeTrackers; var th = new ThisExpr(cl); var bplThis = (Bpl.IdentifierExpr)etran.TrExpr(th); SelectAllocateObject(tok, bplThis, th.Type, false, builder, etran); @@ -287,21 +286,23 @@ private void TrStmt(Statement stmt, BoogieStmtListBuilder builder, AddComment(builder, stmt, "divided block after new;"); TrStmtList(s.BodyProper, builder, locals, etran); - RemoveDefiniteAssignmentTrackers(s.Body, prevDefiniteAssignmentTrackerCount); + DefiniteAssignmentTrackers = previousTrackers; } else if (stmt is OpaqueBlock opaqueBlock) { OpaqueBlockVerifier.EmitBoogie(this, opaqueBlock, builder, locals, etran, (IMethodCodeContext)codeContext); + } else if (stmt is BlockByProofStmt blockByProof) { + BlockByProofStmtVerifier.EmitBoogie(this, blockByProof, builder, locals, etran, codeContext); } else if (stmt is BlockStmt blockStmt) { - var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; + var previousTrackers = DefiniteAssignmentTrackers; TrStmtList(blockStmt.Body, builder, locals, etran, blockStmt.RangeToken); - RemoveDefiniteAssignmentTrackers(blockStmt.Body, prevDefiniteAssignmentTrackerCount); + DefiniteAssignmentTrackers = previousTrackers; } else if (stmt is IfStmt ifStmt) { TrIfStmt(ifStmt, builder, locals, etran); } else if (stmt is AlternativeStmt) { AddComment(builder, stmt, "alternative statement"); var s = (AlternativeStmt)stmt; - var elseCase = Assert(s.Tok, Bpl.Expr.False, new PODesc.AlternativeIsComplete()); + var elseCase = Assert(s.Tok, Bpl.Expr.False, new AlternativeIsComplete(), builder.Context); TrAlternatives(s.Alternatives, s.Tok, b => b.Add(elseCase), builder, locals, etran, stmt.IsGhost); } else if (stmt is WhileStmt whileStmt) { @@ -319,13 +320,12 @@ private void TrStmt(Statement stmt, BoogieStmtListBuilder builder, var wfOptions = new WFOptions(); CheckFrameWellFormed(wfOptions, s.Mod.Expressions, locals, builder, etran); // check that the modifies is a subset - var desc = new PODesc.ModifyFrameSubset("modify statement", s.Mod.Expressions, GetContextModifiesFrames()); + var desc = new ModifyFrameSubset("modify statement", s.Mod.Expressions, GetContextModifiesFrames()); CheckFrameSubset(s.Tok, s.Mod.Expressions, null, null, etran, etran.ModifiesFrame(s.Tok), builder, desc, null); // cause the change of the heap according to the given frame var suffix = CurrentIdGenerator.FreshId("modify#"); string modifyFrameName = FrameVariablePrefix + suffix; - var preModifyHeapVar = new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$PreModifyHeap$" + suffix, predef.HeapType)); - locals.Add(preModifyHeapVar); + var preModifyHeapVar = locals.GetOrAdd(new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$PreModifyHeap$" + suffix, predef.HeapType))); DefineFrame(s.Tok, etran.ModifiesFrame(s.Tok), s.Mod.Expressions, builder, locals, modifyFrameName); if (s.Body == null) { var preModifyHeap = new Bpl.IdentifierExpr(s.Tok, preModifyHeapVar); @@ -361,10 +361,9 @@ private void TrStmt(Statement stmt, BoogieStmtListBuilder builder, TrVarDeclStmt(s, builder, locals, etran); } else if (stmt is VarDeclPattern varDeclPattern) { foreach (var dafnyLocal in varDeclPattern.LocalVars) { - var boogieLocal = new Bpl.LocalVariable(dafnyLocal.Tok, + var boogieLocal = locals.GetOrAdd(new Bpl.LocalVariable(dafnyLocal.Tok, new Bpl.TypedIdent(dafnyLocal.Tok, dafnyLocal.AssignUniqueName(currentDeclaration.IdGenerator), - TrType(dafnyLocal.Type))); - locals.Add(boogieLocal); + TrType(dafnyLocal.Type)))); var variableReference = new Bpl.IdentifierExpr(boogieLocal.tok, boogieLocal); builder.Add(new Bpl.HavocCmd(dafnyLocal.Tok, new List { @@ -381,8 +380,7 @@ private void TrStmt(Statement stmt, BoogieStmtListBuilder builder, var pat = varDeclPattern.LHS; var rhs = varDeclPattern.RHS; var nm = varNameGen.FreshId("#0#"); - var boogieTupleLocal = new Bpl.LocalVariable(pat.tok, new TypedIdent(pat.tok, nm, TrType(rhs.Type))); - locals.Add(boogieTupleLocal); + var boogieTupleLocal = locals.GetOrAdd(new Bpl.LocalVariable(pat.tok, new TypedIdent(pat.tok, nm, TrType(rhs.Type)))); var boogieTupleReference = new Bpl.IdentifierExpr(rhs.tok, boogieTupleLocal); void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { @@ -411,7 +409,7 @@ void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { } } - private void TrUpdateStmt(BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, AssignStatement statement) { + private void TrUpdateStmt(BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, AssignStatement statement) { // This UpdateStmt can be single-target assignment, a multi-assignment, a call statement, or // an array-range update. Handle the multi-assignment here and handle the others as for .ResolvedStatements. var resolved = statement.ResolvedStatements; @@ -444,7 +442,7 @@ private void TrUpdateStmt(BoogieStmtListBuilder builder, List locals, } } - void TrVarDeclStmt(VarDeclStmt varDeclStmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + void TrVarDeclStmt(VarDeclStmt varDeclStmt, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { var newLocalIds = new List(); int i = 0; @@ -476,10 +474,9 @@ void TrVarDeclStmt(VarDeclStmt varDeclStmt, BoogieStmtListBuilder builder, List< } } // create the variable itself (now that "wh" may mention the definite-assignment tracker) - Bpl.LocalVariable var = new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator), varType, wh)); + var var = locals.GetOrAdd(new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator), varType, wh))); var.Attributes = etran.TrAttributes(local.Attributes, null); newLocalIds.Add(new Bpl.IdentifierExpr(local.Tok, var)); - locals.Add(var); i++; } if (varDeclStmt.Assign == null) { @@ -498,7 +495,7 @@ void TrVarDeclStmt(VarDeclStmt varDeclStmt, BoogieStmtListBuilder builder, List< } } - private void TranslateRevealStmt(BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + private void TranslateRevealStmt(BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, HideRevealStmt revealStmt) { AddComment(builder, revealStmt, "hide/reveal statement"); foreach (var la in revealStmt.LabeledAsserts) { @@ -519,7 +516,7 @@ private void TranslateRevealStmt(BoogieStmtListBuilder builder, List l TrStmtList(revealStmt.ResolvedStatements, builder, locals, etran); } - private void TrCalcStmt(CalcStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + private void TrCalcStmt(CalcStmt stmt, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { Contract.Requires(stmt != null); Contract.Requires(builder != null); Contract.Requires(locals != null); @@ -562,9 +559,9 @@ private void TrCalcStmt(CalcStmt stmt, BoogieStmtListBuilder builder, List]: AddComment(b, stmt, "assume wf[lhs]"); CurrentIdGenerator.Push(); - assertAsAssume = true; - TrStmt_CheckWellformed(CalcStmt.Lhs(stmt.Steps[i]), b, locals, etran, false); - assertAsAssume = false; + TrStmt_CheckWellformed(CalcStmt.Lhs(stmt.Steps[i]), b.WithContext(b.Context with { + AssertMode = AssertMode.Assume + }), locals, etran, false); if (stmt.Steps[i] is BinaryExpr && (((BinaryExpr)stmt.Steps[i]).ResolvedOp == BinaryExpr.ResolvedOpcode.Imp)) { // assume line: AddComment(b, stmt, "assume lhs"); @@ -583,8 +580,8 @@ private void TrCalcStmt(CalcStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + private void TrMatchStmt(MatchStmt stmt, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { Contract.Requires(stmt != null); Contract.Requires(builder != null); Contract.Requires(locals != null); @@ -639,21 +636,21 @@ private void TrMatchStmt(MatchStmt stmt, BoogieStmtListBuilder builder, List newLocals = new List(); + var newLocals = new Variables(); Bpl.Expr r = CtorInvocation(stmt.Tok, missingCtor, etran, newLocals, b); - locals.AddRange(newLocals); + locals.AddRange(newLocals.Values); if (newLocals.Count != 0) { List havocIds = new List(); - foreach (Variable local in newLocals) { + foreach (Variable local in newLocals.Values) { havocIds.Add(new Bpl.IdentifierExpr(local.tok, local)); } builder.Add(new Bpl.HavocCmd(stmt.Tok, havocIds)); } String missingStr = stmt.Context.FillHole(new IdCtx(missingCtor)).AbstractAllHoles() .ToString(); - var desc = new PODesc.MatchIsComplete("statement", missingStr); - b.Add(Assert(stmt.Tok, Bpl.Expr.False, desc)); + var desc = new MatchIsComplete("statement", missingStr); + b.Add(Assert(stmt.Tok, Bpl.Expr.False, desc, builder.Context)); Bpl.Expr guard = Bpl.Expr.Eq(source, r); ifCmd = new Bpl.IfCmd(stmt.Tok, guard, b.Collect(stmt.Tok), ifCmd, els); @@ -664,22 +661,22 @@ private void TrMatchStmt(MatchStmt stmt, BoogieStmtListBuilder builder, List newLocals = new List(); + var newLocals = new Variables(); Bpl.Expr r = CtorInvocation(mc, stmt.Source.Type, etran, newLocals, b, stmt.IsGhost ? NOALLOC : ISALLOC); - locals.AddRange(newLocals); + locals.AddRange(newLocals.Values); if (newLocals.Count != 0) { List havocIds = new List(); - foreach (Variable local in newLocals) { + foreach (Variable local in newLocals.Values) { havocIds.Add(new Bpl.IdentifierExpr(local.tok, local)); } builder.Add(new Bpl.HavocCmd(mc.tok, havocIds)); } // translate the body into b - var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; + var prevDefiniteAssignmentTrackers = DefiniteAssignmentTrackers; TrStmtList(mc.Body, b, locals, etran); - RemoveDefiniteAssignmentTrackers(mc.Body, prevDefiniteAssignmentTrackerCount); + DefiniteAssignmentTrackers = prevDefiniteAssignmentTrackers; Bpl.Expr guard = Bpl.Expr.Eq(source, r); ifCmd = new Bpl.IfCmd(mc.tok, guard, b.Collect(mc.tok), ifCmd, els); @@ -729,6 +726,7 @@ void FillMissingCases(IMatch match) { Contract.Assert(memberNamesUsed.Count + match.MissingCases.Count == dtd.Ctors.Count); } } + private static SubrangeCheckContext MakeNumericBoundsSubrangeCheckContext(BoundVar bvar, Expression lo, Expression hi) { var source = new IdentifierExpr(Token.NoToken, bvar); var loBound = lo == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Le, lo, source); @@ -750,7 +748,7 @@ private static SubrangeCheckContext MakeNumericBoundsSubrangeCheckContext(BoundV return CheckContext; } - private void TrIfStmt(IfStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + private void TrIfStmt(IfStmt stmt, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { Contract.Requires(stmt != null); Contract.Requires(builder != null); Contract.Requires(locals != null); @@ -797,8 +795,9 @@ private void TrIfStmt(IfStmt stmt, BoogieStmtListBuilder builder, List builder.Add(new Bpl.IfCmd(stmt.Tok, guard == null || stmt.IsBindingGuard ? null : etran.TrExpr(guard), thn, elsIf, els)); } + void TrAlternatives(List alternatives, IToken elseToken, Action buildElseCase, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, bool isGhost) { + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, bool isGhost) { Contract.Requires(alternatives != null); Contract.Requires(builder != null); Contract.Requires(locals != null); @@ -837,9 +836,9 @@ void TrAlternatives(List alternatives, IToken elseToken, Act } else { b.Add(TrAssumeCmdWithDependencies(etran, alternative.Guard.tok, alternative.Guard, "alternative guard")); } - var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; + var prevDefiniteAssignmentTrackers = DefiniteAssignmentTrackers; TrStmtList(alternative.Body, b, locals, etran, alternative.RangeToken); - RemoveDefiniteAssignmentTrackers(alternative.Body, prevDefiniteAssignmentTrackerCount); + DefiniteAssignmentTrackers = prevDefiniteAssignmentTrackers; Bpl.StmtList thn = b.Collect(alternative.Tok); elsIf = new Bpl.IfCmd(alternative.Tok, null, thn, elsIf, els); els = null; @@ -851,7 +850,7 @@ void TrAlternatives(List alternatives, IToken elseToken, Act void RecordNewObjectsIn_New(IToken tok, IteratorDecl iter, Bpl.Expr initHeap, Bpl.IdentifierExpr currentHeap, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { Contract.Requires(tok != null); Contract.Requires(iter != null); Contract.Requires(initHeap != null); @@ -860,13 +859,12 @@ void RecordNewObjectsIn_New(IToken tok, IteratorDecl iter, Bpl.Expr initHeap, Bp Contract.Requires(locals != null); Contract.Requires(etran != null); // Add all newly allocated objects to the set this._new - var updatedSet = new Bpl.LocalVariable(iter.tok, new Bpl.TypedIdent(iter.tok, CurrentIdGenerator.FreshId("$iter_newUpdate"), predef.SetType)); - locals.Add(updatedSet); + var updatedSet = locals.GetOrAdd(new Bpl.LocalVariable(iter.tok, new Bpl.TypedIdent(iter.tok, CurrentIdGenerator.FreshId("$iter_newUpdate"), predef.SetType))); var updatedSetIE = new Bpl.IdentifierExpr(iter.tok, updatedSet); // call $iter_newUpdate := $IterCollectNewObjects(initHeap, $Heap, this, _new); var th = new Bpl.IdentifierExpr(iter.tok, etran.This, predef.RefType); var nwField = new Bpl.IdentifierExpr(tok, GetField(iter.Member_New)); - Bpl.Cmd cmd = new CallCmd(iter.tok, "$IterCollectNewObjects", + Cmd cmd = Call(builder.Context, iter.tok, "$IterCollectNewObjects", new List() { initHeap, etran.HeapExpr, th, nwField }, new List() { updatedSetIE }); builder.Add(cmd); @@ -946,7 +944,7 @@ private void CommitAllocatedObject(IToken tok, Bpl.IdentifierExpr nw, Bpl.Cmd ex } - private void IntroduceAndAssignExistentialVars(ExistsExpr exists, BoogieStmtListBuilder builder, BoogieStmtListBuilder builderOutsideIfConstruct, List locals, ExpressionTranslator etran, bool isGhost) { + private void IntroduceAndAssignExistentialVars(ExistsExpr exists, BoogieStmtListBuilder builder, BoogieStmtListBuilder builderOutsideIfConstruct, Variables locals, ExpressionTranslator etran, bool isGhost) { Contract.Requires(exists != null); Contract.Requires(exists.Range == null); Contract.Requires(builder != null); @@ -960,15 +958,14 @@ private void IntroduceAndAssignExistentialVars(ExistsExpr exists, BoogieStmtList Bpl.Expr wh = GetWhereClause(bv.Tok, new Bpl.IdentifierExpr(bv.Tok, bv.AssignUniqueName(currentDeclaration.IdGenerator), varType), bv.Type, etran, isAllocContext.Var(isGhost, bv)); - Bpl.Variable local = new Bpl.LocalVariable(bv.Tok, new Bpl.TypedIdent(bv.Tok, bv.AssignUniqueName(currentDeclaration.IdGenerator), varType, wh)); - locals.Add(local); + Bpl.Variable local = locals.GetOrAdd(new Bpl.LocalVariable(bv.Tok, new Bpl.TypedIdent(bv.Tok, bv.AssignUniqueName(currentDeclaration.IdGenerator), varType, wh))); iesForHavoc.Add(new Bpl.IdentifierExpr(local.tok, local)); } builderOutsideIfConstruct.Add(new Bpl.HavocCmd(exists.tok, iesForHavoc)); builder.Add(TrAssumeCmd(exists.tok, etran.TrExpr(exists.Term))); } - public void TrStmtList(List stmts, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + public void TrStmtList(List stmts, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, RangeToken scopeRange = null, bool processLabels = true) { Contract.Requires(stmts != null); Contract.Requires(builder != null); @@ -1006,9 +1003,8 @@ public void TrStmtList(List stmts, BoogieStmtListBuilder builder, Lis var indexBuilder = innerBuilder.WithContext(indexContext); if (processLabels) { for (var l = ss.Labels; l != null; l = l.Next) { - var heapAt = new Bpl.LocalVariable(ss.Tok, - new Bpl.TypedIdent(ss.Tok, "$Heap_at_" + l.Data.AssignUniqueId(CurrentIdGenerator), predef.HeapType)); - locals.Add(heapAt); + var heapAt = locals.GetOrAdd(new Bpl.LocalVariable(ss.Tok, + new Bpl.TypedIdent(ss.Tok, "$Heap_at_" + l.Data.AssignUniqueId(CurrentIdGenerator), predef.HeapType))); builder.Add(Bpl.Cmd.SimpleAssign(ss.Tok, new Bpl.IdentifierExpr(ss.Tok, heapAt), etran.HeapExpr)); } } @@ -1024,7 +1020,7 @@ public void TrStmtList(List stmts, BoogieStmtListBuilder builder, Lis } } - void TrStmt_CheckWellformed(Expression expr, BoogieStmtListBuilder builder, List locals, + void TrStmt_CheckWellformed(Expression expr, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, bool subsumption, bool lValueContext = false, AddResultCommands addResultCommands = null) { Contract.Requires(expr != null); Contract.Requires(builder != null); diff --git a/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs b/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs index bc890a890aa..1035b84138f 100644 --- a/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs +++ b/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs @@ -1,27 +1,22 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Boogie; -using Microsoft.Dafny; -using Microsoft.Dafny.ProofObligationDescription; -using Formal = Microsoft.Dafny.Formal; using DafnyIdentifierExpr = Microsoft.Dafny.IdentifierExpr; using BoogieIdentifierExpr = Microsoft.Boogie.IdentifierExpr; -using ProofObligationDescription = Microsoft.Dafny.ProofObligationDescription.ProofObligationDescription; -using Token = Microsoft.Dafny.Token; -namespace DafnyCore.Verifier; +namespace Microsoft.Dafny; public static class OpaqueBlockVerifier { public static void EmitBoogie(BoogieGenerator generator, OpaqueBlock block, BoogieStmtListBuilder builder, - List locals, BoogieGenerator.ExpressionTranslator etran, IMethodCodeContext codeContext) { + Variables locals, BoogieGenerator.ExpressionTranslator etran, IMethodCodeContext codeContext) { var hasModifiesClause = block.Modifies.Expressions.Any(); var blockBuilder = new BoogieStmtListBuilder(generator, builder.Options, builder.Context); var bodyTranslator = GetBodyTranslator(generator, block, locals, etran, hasModifiesClause, blockBuilder); - var prevDefiniteAssignmentTrackerCount = generator.DefiniteAssignmentTrackers.Count; + var prevDefiniteAssignmentTrackers = generator.DefiniteAssignmentTrackers; generator.TrStmtList(block.Body, blockBuilder, locals, bodyTranslator, block.RangeToken); - generator.RemoveDefiniteAssignmentTrackers(block.Body, prevDefiniteAssignmentTrackerCount); + generator.DefiniteAssignmentTrackers = prevDefiniteAssignmentTrackers; var assignedVariables = block.DescendantsAndSelf. SelectMany(s => s.GetAssignedLocals()).Select(ie => ie.Var) @@ -30,21 +25,21 @@ public static void EmitBoogie(BoogieGenerator generator, OpaqueBlock block, Boog var variablesUsedInEnsures = block.Ensures.SelectMany(ae => ae.E.DescendantsAndSelf). OfType().DistinctBy(ie => ie.Var); - var implicitAssignedIdentifiers = variablesUsedInEnsures.Where( - v => assignedVariables.Contains(v.Var) && generator.DefiniteAssignmentTrackers.ContainsKey(v.Var.UniqueName)); + var implicitAssignedIdentifiers = + variablesUsedInEnsures.Where(v => assignedVariables.Contains(v.Var) && generator.DefiniteAssignmentTrackers.ContainsKey(v.Var.UniqueName)); foreach (var v in implicitAssignedIdentifiers) { var expression = new AttributedExpression(Expression.CreateAssigned(v.Tok, v)); totalEnsures.Add(expression); blockBuilder.Add(generator.Assert( v.Tok, etran.TrExpr(expression.E), - new DefiniteAssignment("variable", v.Var.Name, "here"))); + new DefiniteAssignment("variable", v.Var.Name, "here"), builder.Context)); } foreach (var ensure in block.Ensures) { totalEnsures.Add(ensure); blockBuilder.Add(generator.Assert( ensure.Tok, etran.TrExpr(ensure.E), - new OpaqueEnsuresDescription(), + new OpaqueEnsuresDescription(), builder.Context, etran.TrAttributes(ensure.Attributes, null))); } @@ -74,7 +69,7 @@ public static void EmitBoogie(BoogieGenerator generator, OpaqueBlock block, Boog } } - private static BoogieGenerator.ExpressionTranslator GetBodyTranslator(BoogieGenerator generator, OpaqueBlock block, List locals, + private static BoogieGenerator.ExpressionTranslator GetBodyTranslator(BoogieGenerator generator, OpaqueBlock block, Variables locals, BoogieGenerator.ExpressionTranslator etran, bool hasModifiesClause, BoogieStmtListBuilder blockBuilder) { BoogieGenerator.ExpressionTranslator bodyTranslator; if (hasModifiesClause) { diff --git a/Source/DafnyCore/Verifier/Variables.cs b/Source/DafnyCore/Verifier/Variables.cs new file mode 100644 index 00000000000..bd112bd9d83 --- /dev/null +++ b/Source/DafnyCore/Verifier/Variables.cs @@ -0,0 +1,9 @@ +using Microsoft.Boogie; + +namespace Microsoft.Dafny; + +public class Variables : OrderedDictionary { + public Variables() : base(v => v.Name) { + } + +} \ No newline at end of file diff --git a/Source/DafnyLanguageServer/Handlers/DafnyHoverHandler.cs b/Source/DafnyLanguageServer/Handlers/DafnyHoverHandler.cs index 0fda0ac80bc..959c6a92339 100644 --- a/Source/DafnyLanguageServer/Handlers/DafnyHoverHandler.cs +++ b/Source/DafnyLanguageServer/Handlers/DafnyHoverHandler.cs @@ -11,11 +11,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Boogie; -using Microsoft.Dafny.LanguageServer.Language; -using Microsoft.Dafny.LanguageServer.Util; using Microsoft.Dafny.LanguageServer.Workspace.Notifications; -using OmniSharp.Extensions.JsonRpc.Server; -using EnsuresDescription = Microsoft.Dafny.ProofObligationDescription.EnsuresDescription; namespace Microsoft.Dafny.LanguageServer.Handlers { public class DafnyHoverHandler : HoverHandlerBase { diff --git a/Source/DafnyLanguageServer/Language/ImplicitFailingAssertionCodeActionProvider.cs b/Source/DafnyLanguageServer/Language/ImplicitFailingAssertionCodeActionProvider.cs index 70c9006fe21..1f43f7c8b68 100644 --- a/Source/DafnyLanguageServer/Language/ImplicitFailingAssertionCodeActionProvider.cs +++ b/Source/DafnyLanguageServer/Language/ImplicitFailingAssertionCodeActionProvider.cs @@ -123,7 +123,7 @@ public override IEnumerable GetEdits() { assertTree.Finished && assertTree.Range.Intersects(selection) && assertTree.StatusVerification is GutterVerificationStatus.Error or GutterVerificationStatus.Inconclusive && - assertTree.GetAssertion()?.Description is ProofObligationDescription.ProofObligationDescription description && + assertTree.GetAssertion()?.Description is ProofObligationDescription description && description.GetAssertedExpr(options) is { } assertedExpr) { failingExpressions.Add(assertedExpr); } diff --git a/Source/DafnyLanguageServer/Language/SyntaxTreeVisitor.cs b/Source/DafnyLanguageServer/Language/SyntaxTreeVisitor.cs index ca77573e451..7e6f1170b96 100644 --- a/Source/DafnyLanguageServer/Language/SyntaxTreeVisitor.cs +++ b/Source/DafnyLanguageServer/Language/SyntaxTreeVisitor.cs @@ -347,7 +347,6 @@ public virtual void Visit(AssignStatement updateStatement) { public virtual void Visit(AssertStmt assertStatement) { VisitNullableAttributes(assertStatement.Attributes); Visit(assertStatement.Expr); - VisitNullableStatement(assertStatement.Proof); } public virtual void Visit(ReturnStmt returnStatement) { diff --git a/Source/DafnyPipeline.Test/ImplicitAssertionTest.cs b/Source/DafnyPipeline.Test/ImplicitAssertionTest.cs index 35f46ca15ff..a28848a9e26 100644 --- a/Source/DafnyPipeline.Test/ImplicitAssertionTest.cs +++ b/Source/DafnyPipeline.Test/ImplicitAssertionTest.cs @@ -6,9 +6,6 @@ using Bpl = Microsoft.Boogie; using Xunit; using Microsoft.Dafny; -using Microsoft.Dafny.ProofObligationDescription; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; namespace DafnyPipeline.Test; diff --git a/Source/DafnyPipeline.Test/TranslatorTest.cs b/Source/DafnyPipeline.Test/TranslatorTest.cs index 6c9dab4d4ca..bb55852ab6c 100644 --- a/Source/DafnyPipeline.Test/TranslatorTest.cs +++ b/Source/DafnyPipeline.Test/TranslatorTest.cs @@ -1,13 +1,7 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; +using System.Collections.Generic; using Bpl = Microsoft.Boogie; using Xunit; using Microsoft.Dafny; -using Microsoft.Dafny.ProofObligationDescription; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; namespace DafnyPipeline.Test; diff --git a/Source/DafnyStandardLibraries/Makefile b/Source/DafnyStandardLibraries/Makefile index c33e85b8949..ac2a52d50d7 100644 --- a/Source/DafnyStandardLibraries/Makefile +++ b/Source/DafnyStandardLibraries/Makefile @@ -2,7 +2,7 @@ # Invoking the CLI this way just to stay platform-independent DAFNY = dotnet run --project ../Dafny --no-build --roll-forward LatestMajor -- -NO_VERIFY := false +NO_VERIFY := false # Use make update-binary NO_VERIFY=true to generate the binaries without verification DOO_FILE_SOURCE=build/DafnyStandardLibraries.doo DOO_FILE_TARGET=binaries/DafnyStandardLibraries.doo diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-cs.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-cs.doo index 3338a62be566ca701c52bcf30cdf425ebc3a4757..472917014fca63fcff0f941f6ff184cb89df615d 100644 GIT binary patch delta 47 wcmaFI{f?VAz?+#xgn@y9gCV)XZ6oh=MrI(rc{$@*W)Nd?IjaSj-o$DH05M?=nE(I) delta 47 wcmaFI{f?VAz?+#xgn@y9gW*-D!A9QcjLblK^K!J&)A}08Y;jF#rGn diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-py.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-py.doo index c953a7a143e1de953d2195db9cc4c364e45e1962..e823114e5352bbc0bdbd57342180bf7541090020 100644 GIT binary patch delta 47 wcmaFJ{g9hCz?+#xgn@y9gCV!VZ6oh=MrI(rc{$?&W)Nd?7OMrAUdn0%04^a7fB*mh delta 47 wcmaFJ{g9hCz?+#xgn@y9gW*f3!A9QcjLblK^K!-m%pk_(ELICJy_D4k09GjvhyVZp diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries.doo index c92218089d899e4b00caebe1634a006c5241b195..66b99929968ffbb3534c212b61b9ffa1e30d8de9 100644 GIT binary patch delta 49 ycmeynkNNLDX5IjAW)=|!1_lm>mr=12Hd8acn1L@7n8Ds8(7?auVTYzcD`!)cr3ldTQ diff --git a/Source/DafnyStandardLibraries/examples/JSON/JSONExamples.dfy b/Source/DafnyStandardLibraries/examples/JSON/JSONExamples.dfy index 35832b56c5b..aa07a78eee7 100644 --- a/Source/DafnyStandardLibraries/examples/JSON/JSONExamples.dfy +++ b/Source/DafnyStandardLibraries/examples/JSON/JSONExamples.dfy @@ -254,20 +254,21 @@ module {:options "-functionSyntax:4"} ConcreteSyntax { // BUG(https://github.com/dafny-lang/dafny/issues/2184) // BUG(https://github.com/dafny-lang/dafny/issues/2690) var fn' := (sf: Suffixed) requires (ghost var in_sq := sf => sf in sq; in_sq(sf)) => sf.(t := fn(sf)); - var sq' := Seq.Map(fn', sq); + Seq.Map(fn', sq) + } - assert NoTrailingSuffix(sq') by { - forall idx | 0 <= idx < |sq'| ensures sq'[idx].suffix.Empty? <==> idx == |sq'| - 1 { - calc { - sq'[idx].suffix.Empty?; - fn'(sq[idx]).suffix.Empty?; - sq[idx].suffix.Empty?; - idx == |sq| - 1; - idx == |sq'| - 1; - } + lemma MapSuffixedSequenceNoTrailingSuffix(sq: SuffixedSequence, fn: Suffixed --> D) + requires forall suffixed | suffixed in sq :: fn.requires(suffixed) + ensures NoTrailingSuffix(MapSuffixedSequence(sq, fn)) + { + var sq' := MapSuffixedSequence(sq, fn); + forall idx | 0 <= idx < |sq'| ensures sq'[idx].suffix.Empty? <==> idx == |sq'| - 1 { + calc { + sq'[idx].suffix.Empty?; + sq[idx].suffix.Empty?; + idx == |sq| - 1; + idx == |sq'| - 1; } } - - sq' } } diff --git a/Source/DafnyTestGeneration/Inlining/AddImplementationForCallsRewriter.cs b/Source/DafnyTestGeneration/Inlining/AddImplementationForCallsRewriter.cs index 9513ddb806f..f601b316ba8 100644 --- a/Source/DafnyTestGeneration/Inlining/AddImplementationForCallsRewriter.cs +++ b/Source/DafnyTestGeneration/Inlining/AddImplementationForCallsRewriter.cs @@ -21,7 +21,6 @@ namespace DafnyTestGeneration.Inlining; /// public class AddImplementationsForCallsRewriter : ReadOnlyVisitor { - private const string CallPrefix = "CallPost$$"; private readonly DafnyOptions options; private List implsToAdd = new(); @@ -32,7 +31,7 @@ public AddImplementationsForCallsRewriter(DafnyOptions options) { } public override Procedure /*?*/ VisitProcedure(Procedure /*?*/ node) { - if (node == null || !node.Name.StartsWith(CallPrefix) || + if (node == null || !node.Name.StartsWith(BoogieGenerator.CallPrefix + BoogieGenerator.NameSeparator) || node.Name.EndsWith(ProgramModifier.CtorPostfix)) { return node; } diff --git a/Source/DafnyTestGeneration/TestGenerator.cs b/Source/DafnyTestGeneration/TestGenerator.cs index 94a1f1e5b6e..b33c1f167ca 100644 --- a/Source/DafnyTestGeneration/TestGenerator.cs +++ b/Source/DafnyTestGeneration/TestGenerator.cs @@ -91,7 +91,7 @@ private static void AddVerificationGoalsToEntryPoints(Program program) { foreach (var entryPoint in Utils.AllMemberDeclarationsWithAttribute(program.DefaultModule, TestGenerationOptions.TestEntryAttribute)) { var trivialAssertion = new AssertStmt(entryPoint.RangeToken, - new LiteralExpr(entryPoint.StartToken, true), null, null, null); + new LiteralExpr(entryPoint.StartToken, true), null, null); if (entryPoint is Method method && method.Body != null && method.Body.Body != null) { method.Body.Body.Insert(0, trivialAssertion); } else if (entryPoint is Function function && function.Body != null) { diff --git a/Source/DafnyTestGeneration/Utils.cs b/Source/DafnyTestGeneration/Utils.cs index f244c914be9..67ffd2a8da6 100644 --- a/Source/DafnyTestGeneration/Utils.cs +++ b/Source/DafnyTestGeneration/Utils.cs @@ -113,7 +113,7 @@ public static Microsoft.Boogie.Program DeepCloneProgram(DafnyOptions options, Mi /// public static Microsoft.Boogie.Program DeepCloneResolvedProgram(Microsoft.Boogie.Program program, DafnyOptions options) { program = DeepCloneProgram(options, program); - program.Resolve(options); + var resolutionErrors = program.Resolve(options); program.Typecheck(options); return program; } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AssertBy.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/AssertBy.dfy similarity index 83% rename from Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AssertBy.dfy rename to Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/AssertBy.dfy index c0086af0120..d7f12194342 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AssertBy.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/AssertBy.dfy @@ -15,3 +15,9 @@ method M1(x: int, y: int) } assert y == 8; // error (yes, still -- the previous assumption should not be in effect here) } + +method WellFormedness(x: int) { + assert 3 / x == 1by { + assume x == 3; + } +} \ No newline at end of file diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AssertBy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/AssertBy.dfy.expect similarity index 71% rename from Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AssertBy.dfy.expect rename to Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/AssertBy.dfy.expect index 93e5a7c7672..78047077579 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AssertBy.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/AssertBy.dfy.expect @@ -14,8 +14,15 @@ method M1(x: int, y: int) } assert y == 8; } + +method WellFormedness(x: int) +{ + assert 3 / x == 1 by { + assume x == 3; + } +} AssertBy.dfy(6,11): Error: assertion might not hold AssertBy.dfy(7,11): Error: assertion might not hold AssertBy.dfy(16,11): Error: assertion might not hold -Dafny program verifier finished with 0 verified, 3 errors +Dafny program verifier finished with 1 verified, 3 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignOrReturn.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignOrReturn.dfy new file mode 100644 index 00000000000..a6b739c14e9 --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignOrReturn.dfy @@ -0,0 +1,12 @@ +// RUN: %verify %s --standard-libraries=true &> "%t" +// RUN: %diff "%s.expect" "%t" + +import opened Std.Wrappers + +method ByWellformedness(x: int) returns (r: Option) { + var p: int :- Some(3 / x) by { + assume {:axiom} x > 0; + } + var q: int :- None; + r := Some(4); +} \ No newline at end of file diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignOrReturn.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignOrReturn.dfy.expect new file mode 100644 index 00000000000..823a60a105c --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignOrReturn.dfy.expect @@ -0,0 +1,2 @@ + +Dafny program verifier finished with 1 verified, 0 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignSuchThat.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignSuchThat.dfy new file mode 100644 index 00000000000..43edfe82cb3 --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignSuchThat.dfy @@ -0,0 +1,8 @@ +// RUN: %verify %s &> "%t" +// RUN: %diff "%s.expect" "%t" + +method ByClause(b: bool) { + var r: int :| false by { + assume {:axiom} false; + } +} \ No newline at end of file diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignSuchThat.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignSuchThat.dfy.expect new file mode 100644 index 00000000000..823a60a105c --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignSuchThat.dfy.expect @@ -0,0 +1,2 @@ + +Dafny program verifier finished with 1 verified, 0 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignment.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignment.dfy new file mode 100644 index 00000000000..a602eee60fe --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignment.dfy @@ -0,0 +1,8 @@ +// RUN: %verify %s &> "%t" +// RUN: %diff "%s.expect" "%t" + +method ByWellformedness(x: int) { + var p: int := 3 / x by { + assume {:axiom} x > 0; + } +} \ No newline at end of file diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignment.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignment.dfy.expect new file mode 100644 index 00000000000..823a60a105c --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignment.dfy.expect @@ -0,0 +1,2 @@ + +Dafny program verifier finished with 1 verified, 0 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallBy.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallBy.dfy similarity index 93% rename from Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallBy.dfy rename to Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallBy.dfy index 6d83e1da819..c35d7cea7d8 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallBy.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallBy.dfy @@ -64,4 +64,10 @@ greatest lemma F(x: int) { F(x-2) by { ProveP(); } assert P(); // should fail +} + +method ArgumentWellformedness(x: int) { + F(3 / x) by { + assume x > 0; + } } \ No newline at end of file diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallBy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallBy.dfy.expect similarity index 84% rename from Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallBy.dfy.expect rename to Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallBy.dfy.expect index b4849841fcd..8dbe5b18e58 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallBy.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallBy.dfy.expect @@ -5,4 +5,4 @@ CallBy.dfy(50,10): Error: assertion might not hold CallBy.dfy(56,10): Error: assertion might not hold CallBy.dfy(66,10): Error: assertion might not hold -Dafny program verifier finished with 3 verified, 6 errors +Dafny program verifier finished with 4 verified, 6 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByHide.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByHide.dfy similarity index 73% rename from Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByHide.dfy rename to Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByHide.dfy index 31e5fe5fb51..68a4d9142c4 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByHide.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByHide.dfy @@ -1,4 +1,4 @@ -// RUN: ! %verify --type-system-refresh %s > %t +// RUN: ! %verify --type-system-refresh --isolate-assertions %s > %t // RUN: %diff "%s.expect" "%t" predicate P() { true } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByHide.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByHide.dfy.expect new file mode 100644 index 00000000000..953f23283c0 --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByHide.dfy.expect @@ -0,0 +1,3 @@ +CallByHide.dfy(15,10): Error: assertion might not hold + +Dafny program verifier finished with 2 verified, 1 error diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByResolution0.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByResolution0.dfy similarity index 100% rename from Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByResolution0.dfy rename to Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByResolution0.dfy diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByResolution0.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByResolution0.dfy.expect similarity index 68% rename from Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByResolution0.dfy.expect rename to Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByResolution0.dfy.expect index d6ed0c40058..a3fd6773b9d 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByResolution0.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByResolution0.dfy.expect @@ -1,3 +1,4 @@ CallByResolution0.dfy(8,6): Error: assignment to non-ghost variable is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression CallByResolution0.dfy(9,4): Error: return statement is not allowed in this context (because it is guarded by a specification-only expression) -2 resolution/type errors detected in CallByResolution0.dfy +CallByResolution0.dfy(8,4): Error: a by block is not allowed to update a variable it doesn't declare +3 resolution/type errors detected in CallByResolution0.dfy diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByResolution1.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByResolution1.dfy similarity index 100% rename from Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByResolution1.dfy rename to Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByResolution1.dfy diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByResolution1.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByResolution1.dfy.expect similarity index 100% rename from Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByResolution1.dfy.expect rename to Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByResolution1.dfy.expect diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByHide.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByHide.dfy.expect deleted file mode 100644 index ef939499206..00000000000 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByHide.dfy.expect +++ /dev/null @@ -1,3 +0,0 @@ -CallByHide.dfy(15,10): Error: assertion might not hold - -Dafny program verifier finished with 0 verified, 1 error diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/GhostAllocations-Resolution.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/GhostAllocations-Resolution.dfy.expect index cf476f9a6da..248c5a5b270 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/GhostAllocations-Resolution.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/GhostAllocations-Resolution.dfy.expect @@ -33,9 +33,9 @@ GhostAllocations-Resolution.dfy(240,6): Error: print statement is not allowed in GhostAllocations-Resolution.dfy(248,22): Error: a hint is not allowed to use 'new' GhostAllocations-Resolution.dfy(249,22): Error: a hint is not allowed to use 'new' GhostAllocations-Resolution.dfy(249,22): Error: in a hint, calls are allowed only to lemmas -GhostAllocations-Resolution.dfy(254,18): Error: an assert-by body is not allowed to use 'new' -GhostAllocations-Resolution.dfy(255,18): Error: an assert-by body is not allowed to use 'new' -GhostAllocations-Resolution.dfy(255,18): Error: in an assert-by body, calls are allowed only to lemmas +GhostAllocations-Resolution.dfy(254,18): Error: a by block is not allowed to use 'new' +GhostAllocations-Resolution.dfy(255,18): Error: a by block is not allowed to use 'new' +GhostAllocations-Resolution.dfy(255,18): Error: in a by block, calls are allowed only to lemmas GhostAllocations-Resolution.dfy(285,11): Error: ghost variables such as b are allowed only in specification contexts. b was inferred to be ghost based on its declaration or initialization. GhostAllocations-Resolution.dfy(274,11): Error: assignment to non-ghost field is not allowed in this context, because this is a ghost method GhostAllocations-Resolution.dfy(303,13): Error: a lemma is not allowed to use 'new' @@ -49,7 +49,7 @@ GhostAllocations-Resolution.dfy(314,13): Error: a lemma is not allowed to use 'n GhostAllocations-Resolution.dfy(318,13): Error: a twostate lemma is not allowed to use 'new' GhostAllocations-Resolution.dfy(322,13): Error: a greatest lemma is not allowed to use 'new' GhostAllocations-Resolution.dfy(349,15): Error: in a statement expression, calls are allowed only to lemmas -GhostAllocations-Resolution.dfy(361,7): Error: in an assert-by body, calls are allowed only to lemmas +GhostAllocations-Resolution.dfy(361,7): Error: in a by block, calls are allowed only to lemmas GhostAllocations-Resolution.dfy(369,5): Error: in a lemma, calls are allowed only to lemmas GhostAllocations-Resolution.dfy(376,11): Error: in a hint, calls are allowed only to lemmas GhostAllocations-Resolution.dfy(384,11): Error: in a hint, calls are allowed only to lemmas @@ -60,12 +60,12 @@ GhostAllocations-Resolution.dfy(406,4): Error: a modify statement is not allowed GhostAllocations-Resolution.dfy(407,4): Error: a modify statement is not allowed in a lemma GhostAllocations-Resolution.dfy(409,13): Error: a lemma is not allowed to use 'new' GhostAllocations-Resolution.dfy(410,6): Error: a lemma is not allowed to make heap updates -GhostAllocations-Resolution.dfy(416,17): Error: a loop in an assert-by body is not allowed to use 'modifies' clauses -GhostAllocations-Resolution.dfy(421,17): Error: a loop in an assert-by body is not allowed to use 'modifies' clauses -GhostAllocations-Resolution.dfy(426,17): Error: a loop in an assert-by body is not allowed to use 'modifies' clauses -GhostAllocations-Resolution.dfy(430,6): Error: a modify statement is not allowed in an assert-by body -GhostAllocations-Resolution.dfy(431,6): Error: a modify statement is not allowed in an assert-by body -GhostAllocations-Resolution.dfy(433,15): Error: an assert-by body is not allowed to use 'new' +GhostAllocations-Resolution.dfy(416,17): Error: a loop in a by block is not allowed to use 'modifies' clauses +GhostAllocations-Resolution.dfy(421,17): Error: a loop in a by block is not allowed to use 'modifies' clauses +GhostAllocations-Resolution.dfy(426,17): Error: a loop in a by block is not allowed to use 'modifies' clauses +GhostAllocations-Resolution.dfy(430,6): Error: a modify statement is not allowed in a by block +GhostAllocations-Resolution.dfy(431,6): Error: a modify statement is not allowed in a by block +GhostAllocations-Resolution.dfy(433,15): Error: a by block is not allowed to use 'new' GhostAllocations-Resolution.dfy(485,17): Error: a loop in a forall statement is not allowed to use 'modifies' clauses GhostAllocations-Resolution.dfy(490,17): Error: a loop in a forall statement is not allowed to use 'modifies' clauses GhostAllocations-Resolution.dfy(495,17): Error: a loop in a forall statement is not allowed to use 'modifies' clauses @@ -103,11 +103,11 @@ GhostAllocations-Resolution.dfy(600,8): Error: a hint is not allowed to perform GhostAllocations-Resolution.dfy(604,13): Error: a hint is not allowed to make heap updates GhostAllocations-Resolution.dfy(605,8): Error: a hint is not allowed to perform an aggregate heap update GhostAllocations-Resolution.dfy(613,11): Error: assignment to array element is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression -GhostAllocations-Resolution.dfy(614,6): Error: an assert-by body is not allowed to perform an aggregate heap update +GhostAllocations-Resolution.dfy(614,6): Error: a by block is not allowed to perform an aggregate heap update GhostAllocations-Resolution.dfy(618,16): Error: assignment to non-ghost field is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression -GhostAllocations-Resolution.dfy(619,6): Error: an assert-by body is not allowed to perform an aggregate heap update -GhostAllocations-Resolution.dfy(623,11): Error: an assert-by body is not allowed to make heap updates -GhostAllocations-Resolution.dfy(624,6): Error: an assert-by body is not allowed to perform an aggregate heap update +GhostAllocations-Resolution.dfy(619,6): Error: a by block is not allowed to perform an aggregate heap update +GhostAllocations-Resolution.dfy(623,11): Error: a by block is not allowed to make heap updates +GhostAllocations-Resolution.dfy(624,6): Error: a by block is not allowed to perform an aggregate heap update GhostAllocations-Resolution.dfy(632,11): Error: assignment to array element is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression GhostAllocations-Resolution.dfy(633,6): Error: a forall statement is not allowed to perform an aggregate heap update GhostAllocations-Resolution.dfy(637,16): Error: assignment to non-ghost field is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/LabeledAssertsResolution.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/LabeledAssertsResolution.dfy.expect index 0c054459d6f..c9b3e299ee0 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/LabeledAssertsResolution.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/LabeledAssertsResolution.dfy.expect @@ -10,34 +10,34 @@ LabeledAssertsResolution.dfy(94,17): Error: no label 'XYZ' in scope at this time LabeledAssertsResolution.dfy(115,14): Error: unresolved identifier: X LabeledAssertsResolution.dfy(52,11): Error: assert label shadows a dominating label LabeledAssertsResolution.dfy(54,11): Error: assert label shadows a dominating label -LabeledAssertsResolution.dfy(125,6): Error: an assert-by body is not allowed to update a variable it doesn't declare +LabeledAssertsResolution.dfy(125,6): Error: a by block is not allowed to update a variable it doesn't declare LabeledAssertsResolution.dfy(139,8): Error: assignment to non-ghost variable is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression -LabeledAssertsResolution.dfy(139,6): Error: an assert-by body is not allowed to update a variable it doesn't declare -LabeledAssertsResolution.dfy(170,16): Error: in an assert-by body, calls are allowed only to lemmas -LabeledAssertsResolution.dfy(172,10): Error: an assert-by body is not allowed to make heap updates +LabeledAssertsResolution.dfy(139,6): Error: a by block is not allowed to update a variable it doesn't declare +LabeledAssertsResolution.dfy(170,16): Error: in a by block, calls are allowed only to lemmas +LabeledAssertsResolution.dfy(172,10): Error: a by block is not allowed to make heap updates LabeledAssertsResolution.dfy(173,13): Error: assignment to array element is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression LabeledAssertsResolution.dfy(174,15): Error: assignment to array element is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression -LabeledAssertsResolution.dfy(175,8): Error: a modify statement is not allowed in an assert-by body -LabeledAssertsResolution.dfy(177,8): Error: an assert-by body is not allowed to make heap updates +LabeledAssertsResolution.dfy(175,8): Error: a modify statement is not allowed in a by block +LabeledAssertsResolution.dfy(177,8): Error: a by block is not allowed to make heap updates LabeledAssertsResolution.dfy(178,11): Error: assignment to array element is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression LabeledAssertsResolution.dfy(179,13): Error: assignment to array element is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression -LabeledAssertsResolution.dfy(180,6): Error: a modify statement is not allowed in an assert-by body +LabeledAssertsResolution.dfy(180,6): Error: a modify statement is not allowed in a by block LabeledAssertsResolution.dfy(187,12): Error: a hint is not allowed to make heap updates LabeledAssertsResolution.dfy(188,15): Error: assignment to array element is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression LabeledAssertsResolution.dfy(189,17): Error: assignment to array element is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression LabeledAssertsResolution.dfy(190,10): Error: a modify statement is not allowed in a hint LabeledAssertsResolution.dfy(195,18): Error: in a hint, calls are allowed only to lemmas -LabeledAssertsResolution.dfy(169,8): Error: an assert-by body is not allowed to update a variable it doesn't declare -LabeledAssertsResolution.dfy(170,8): Error: an assert-by body is not allowed to update a variable it doesn't declare -LabeledAssertsResolution.dfy(171,8): Error: an assert-by body is not allowed to update a variable it doesn't declare +LabeledAssertsResolution.dfy(169,8): Error: a by block is not allowed to update a variable it doesn't declare +LabeledAssertsResolution.dfy(170,8): Error: a by block is not allowed to update a variable it doesn't declare +LabeledAssertsResolution.dfy(171,8): Error: a by block is not allowed to update a variable it doesn't declare LabeledAssertsResolution.dfy(203,18): Error: a hint is not allowed to update a variable it doesn't declare LabeledAssertsResolution.dfy(204,18): Error: a hint is not allowed to update a variable it doesn't declare -LabeledAssertsResolution.dfy(199,12): Error: an assert-by body is not allowed to update a variable it doesn't declare -LabeledAssertsResolution.dfy(200,12): Error: an assert-by body is not allowed to update a variable it doesn't declare +LabeledAssertsResolution.dfy(199,12): Error: a by block is not allowed to update a variable it doesn't declare +LabeledAssertsResolution.dfy(200,12): Error: a by block is not allowed to update a variable it doesn't declare LabeledAssertsResolution.dfy(185,10): Error: a hint is not allowed to update a variable it doesn't declare LabeledAssertsResolution.dfy(195,10): Error: a hint is not allowed to update a variable it doesn't declare -LabeledAssertsResolution.dfy(219,8): Error: an assert-by body is not allowed to update a variable it doesn't declare -LabeledAssertsResolution.dfy(220,8): Error: an assert-by body is not allowed to update a variable it doesn't declare +LabeledAssertsResolution.dfy(219,8): Error: a by block is not allowed to update a variable it doesn't declare +LabeledAssertsResolution.dfy(220,8): Error: a by block is not allowed to update a variable it doesn't declare LabeledAssertsResolution.dfy(222,31): Error: return statement is not allowed in this context (because it is guarded by a specification-only expression) LabeledAssertsResolution.dfy(223,43): Error: return statement is not allowed in this context (because it is guarded by a specification-only expression) LabeledAssertsResolution.dfy(232,14): Error: return statement is not allowed in this context (because it is guarded by a specification-only expression) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect index c26e020b6a6..ad13e6b5b66 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect @@ -91,5 +91,5 @@ SubsetTypes.dfy(459,15): Error: assertion might not hold SubsetTypes.dfy(464,13): Error: assertion might not hold Dafny program verifier finished with 13 verified, 91 errors -Total resources used is 775020 +Total resources used is 774980 Max resources used by VC is 101850 diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots0.run.legacy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots0.run.legacy.dfy.expect index bc7f9c7e992..e1e675464dc 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots0.run.legacy.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots0.run.legacy.dfy.expect @@ -1,13 +1,13 @@ -Processing command (at Snapshots0.v0.dfy(4,10)) assert {:id "id2"} Lit(false); +Processing command (at Snapshots0.v0.dfy(4,10)) assert {:id "id1"} Lit(false); >>> DoNothingToAssert Dafny program verifier finished with 1 verified, 0 errors -Processing call to procedure bar (call precondtion) in implementation foo (correctness) (at Snapshots0.v1.dfy(3,6)): - >>> added after: a##cached##0 := a##cached##0 && true; -Processing call to procedure bar (call postcondition) in implementation foo (correctness) (at Snapshots0.v1.dfy(3,6)): +Processing call to procedure bar (call) in implementation foo (correctness) (at Snapshots0.v1.dfy(3,6)): >>> added axiom: (forall call0old#AT#$Heap: Heap, $Heap: Heap :: {:weight 30} { ##extracted_function##1(call0old#AT#$Heap, $Heap) } ##extracted_function##1(call0old#AT#$Heap, $Heap) == (true && Lit(false) && (forall $o: ref :: { $Heap[$o] } $o != null && $Unbox(read(call0old#AT#$Heap, $o, alloc)): bool ==> $Heap[$o] == call0old#AT#$Heap[$o]) && $HeapSucc(call0old#AT#$Heap, $Heap))) - >>> added after: a##cached##1 := a##cached##1 && ##extracted_function##1(call0old#AT#$Heap, $Heap); -Processing command (at Snapshots0.v1.dfy(4,10)) assert {:id "id7"} Lit(false); + >>> added after: a##cached##0 := a##cached##0 && ##extracted_function##1(call0old#AT#$Heap, $Heap); +Processing command (at ) a##cached##0 := a##cached##0 && ##extracted_function##1(call0old#AT#$Heap, $Heap); + >>> AssumeNegationOfAssumptionVariable +Processing command (at Snapshots0.v1.dfy(4,10)) assert {:id "id5"} Lit(false); >>> MarkAsPartiallyVerified Snapshots0.v1.dfy(4,9): Error: assertion might not hold diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots1.run.legacy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots1.run.legacy.dfy.expect index 6c278ab5388..a2ef2d3d341 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots1.run.legacy.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots1.run.legacy.dfy.expect @@ -1,10 +1,10 @@ -Processing command (at Snapshots1.v0.dfy(4,10)) assert {:id "id2"} Lit(false); +Processing command (at Snapshots1.v0.dfy(4,10)) assert {:id "id1"} Lit(false); >>> DoNothingToAssert Dafny program verifier finished with 1 verified, 0 errors -Processing call to procedure N (call postcondition) in implementation M (correctness) (at Snapshots1.v1.dfy(3,4)): +Processing call to procedure N (call) in implementation M (correctness) (at Snapshots1.v1.dfy(3,4)): >>> added after: a##cached##0 := a##cached##0 && false; -Processing command (at Snapshots1.v1.dfy(4,10)) assert {:id "id8"} Lit(false); +Processing command (at Snapshots1.v1.dfy(4,10)) assert {:id "id6"} Lit(false); >>> DoNothingToAssert Snapshots1.v1.dfy(4,9): Error: assertion might not hold diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots2.run.legacy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots2.run.legacy.dfy.expect index 401acbc1f66..768491421f5 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots2.run.legacy.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots2.run.legacy.dfy.expect @@ -1,31 +1,31 @@ -Processing command (at Snapshots2.v0.dfy(4,10)) assert {:id "id2"} Lit(false); +Processing command (at Snapshots2.v0.dfy(4,10)) assert {:id "id1"} Lit(false); >>> DoNothingToAssert -Processing command (at Snapshots2.v0.dfy(11,11)) assert {:id "id6"} Lit(true); +Processing command (at Snapshots2.v0.dfy(11,11)) assert {:id "id5"} Lit(true); >>> DoNothingToAssert -Processing command (at Snapshots2.v0.dfy(11,15)) assert {:id "id5"} _module.__default.P() <==> _module.__default.Q(); +Processing command (at Snapshots2.v0.dfy(11,15)) assert {:id "id4"} _module.__default.P() <==> _module.__default.Q(); >>> DoNothingToAssert -Processing command (at Snapshots2.v0.dfy(14,11)) assert {:id "id9"} Lit(true); +Processing command (at Snapshots2.v0.dfy(14,11)) assert {:id "id8"} Lit(true); >>> DoNothingToAssert -Processing command (at Snapshots2.v0.dfy(14,15)) assert {:id "id8"} _module.__default.Q() <==> Lit(_module.__default.R()); +Processing command (at Snapshots2.v0.dfy(14,15)) assert {:id "id7"} _module.__default.Q() <==> Lit(_module.__default.R()); >>> DoNothingToAssert Dafny program verifier finished with 3 verified, 0 errors -Processing call to procedure N (call postcondition) in implementation M (correctness) (at Snapshots2.v1.dfy(3,4)): +Processing call to procedure N (call) in implementation M (correctness) (at Snapshots2.v1.dfy(3,4)): >>> added after: a##cached##0 := a##cached##0 && false; Processing implementation P (well-formedness) (at Snapshots2.v1.dfy(10,11)): >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && false; Processing implementation Q (well-formedness) (at Snapshots2.v1.dfy(13,11)): >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && false; -Processing command (at Snapshots2.v1.dfy(4,10)) assert {:id "id14"} Lit(false); +Processing command (at Snapshots2.v1.dfy(4,10)) assert {:id "id12"} Lit(false); >>> DoNothingToAssert Snapshots2.v1.dfy(4,9): Error: assertion might not hold -Processing command (at Snapshots2.v1.dfy(11,11)) assert {:id "id18"} Lit(true); +Processing command (at Snapshots2.v1.dfy(11,11)) assert {:id "id16"} Lit(true); >>> DoNothingToAssert -Processing command (at Snapshots2.v1.dfy(11,15)) assert {:id "id17"} _module.__default.P() <==> _module.__default.Q(); +Processing command (at Snapshots2.v1.dfy(11,15)) assert {:id "id15"} _module.__default.P() <==> _module.__default.Q(); >>> DoNothingToAssert -Processing command (at Snapshots2.v1.dfy(14,11)) assert {:id "id21"} Lit(true); +Processing command (at Snapshots2.v1.dfy(14,11)) assert {:id "id19"} Lit(true); >>> DoNothingToAssert -Processing command (at Snapshots2.v1.dfy(14,15)) assert {:id "id20"} _module.__default.Q() <==> Lit(_module.__default.R()); +Processing command (at Snapshots2.v1.dfy(14,15)) assert {:id "id18"} _module.__default.Q() <==> Lit(_module.__default.R()); >>> DoNothingToAssert Dafny program verifier finished with 2 verified, 1 error diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots5.run.legacy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots5.run.legacy.dfy.expect index 8a6d82c8b15..5566f116ad4 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots5.run.legacy.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots5.run.legacy.dfy.expect @@ -2,11 +2,11 @@ Snapshots5.v0.dfy(10,12): Warning: Could not find a trigger for this quantifier. Snapshots5.v0.dfy(13,10): Warning: Could not find a trigger for this quantifier. Without a trigger, the quantifier may cause brittle verification. To silence this warning, add an explicit trigger using the {:trigger} attribute. For more information, see the section quantifier instantiation rules in the reference manual. Snapshots5.v0.dfy(20,12): Warning: Could not find a trigger for this quantifier. Without a trigger, the quantifier may cause brittle verification. To silence this warning, add an explicit trigger using the {:trigger} attribute. For more information, see the section quantifier instantiation rules in the reference manual. Snapshots5.v0.dfy(26,11): Warning: Could not find a trigger for this quantifier. Without a trigger, the quantifier may cause brittle verification. To silence this warning, add an explicit trigger using the {:trigger} attribute. For more information, see the section quantifier instantiation rules in the reference manual. -Processing command (at Snapshots5.v0.dfy(10,40)) assert {:id "id2"} (forall b#1_1: bool :: b#1_1 || !b#1_1) || 0 != 0; +Processing command (at Snapshots5.v0.dfy(10,40)) assert {:id "id1"} (forall b#1_1: bool :: b#1_1 || !b#1_1) || 0 != 0; >>> DoNothingToAssert -Processing command (at Snapshots5.v0.dfy(13,38)) assert {:id "id5"} (forall b#1: bool :: b#1 || !b#1) || 3 != 3; +Processing command (at Snapshots5.v0.dfy(13,38)) assert {:id "id3"} (forall b#1: bool :: b#1 || !b#1) || 3 != 3; >>> DoNothingToAssert -Processing command (at Snapshots5.v0.dfy(20,40)) assert {:id "id6"} (forall b#3_1: bool :: b#3_1 || !b#3_1) || 1 != 1; +Processing command (at Snapshots5.v0.dfy(20,40)) assert {:id "id4"} (forall b#3_1: bool :: b#3_1 || !b#3_1) || 1 != 1; >>> DoNothingToAssert Dafny program verifier finished with 1 verified, 0 errors @@ -15,13 +15,13 @@ Snapshots5.v1.dfy(13,10): Warning: Could not find a trigger for this quantifier. Snapshots5.v1.dfy(20,12): Warning: Could not find a trigger for this quantifier. Without a trigger, the quantifier may cause brittle verification. To silence this warning, add an explicit trigger using the {:trigger} attribute. For more information, see the section quantifier instantiation rules in the reference manual. Snapshots5.v1.dfy(22,10): Warning: Could not find a trigger for this quantifier. Without a trigger, the quantifier may cause brittle verification. To silence this warning, add an explicit trigger using the {:trigger} attribute. For more information, see the section quantifier instantiation rules in the reference manual. Snapshots5.v1.dfy(27,11): Warning: Could not find a trigger for this quantifier. Without a trigger, the quantifier may cause brittle verification. To silence this warning, add an explicit trigger using the {:trigger} attribute. For more information, see the section quantifier instantiation rules in the reference manual. -Processing command (at Snapshots5.v1.dfy(10,40)) assert {:id "id13"} (forall b#1_1: bool :: b#1_1 || !b#1_1) || 0 != 0; +Processing command (at Snapshots5.v1.dfy(10,40)) assert {:id "id10"} (forall b#1_1: bool :: b#1_1 || !b#1_1) || 0 != 0; >>> MarkAsFullyVerified -Processing command (at Snapshots5.v1.dfy(13,38)) assert {:id "id16"} (forall b#1: bool :: b#1 || !b#1) || 3 != 3; +Processing command (at Snapshots5.v1.dfy(13,38)) assert {:id "id12"} (forall b#1: bool :: b#1 || !b#1) || 3 != 3; >>> MarkAsFullyVerified -Processing command (at Snapshots5.v1.dfy(20,37)) assert {:id "id17"} (exists b#3_1: bool :: Lit(true)) || 4 != 4; +Processing command (at Snapshots5.v1.dfy(20,37)) assert {:id "id13"} (exists b#3_1: bool :: Lit(true)) || 4 != 4; >>> DoNothingToAssert -Processing command (at Snapshots5.v1.dfy(22,35)) assert {:id "id18"} (exists b#3: bool :: Lit(true)) || 5 != 5; +Processing command (at Snapshots5.v1.dfy(22,35)) assert {:id "id14"} (exists b#3: bool :: Lit(true)) || 5 != 5; >>> DoNothingToAssert Dafny program verifier finished with 1 verified, 0 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots8.run.legacy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots8.run.legacy.dfy.expect index 114c1a89a2e..0b3564e7be2 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots8.run.legacy.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots8.run.legacy.dfy.expect @@ -3,40 +3,40 @@ Processing command (at Snapshots8.v0.dfy(2,37)) assert {:id "id0"} x#0 < 20 || L >>> DoNothingToAssert Processing command (at Snapshots8.v0.dfy(3,12)) assert {:id "id1"} x#0 < 10; >>> DoNothingToAssert -Processing command (at Snapshots8.v0.dfy(4,8)) assert {:id "id5$id2$requires"} {:id "id2"} LitInt(0) <= call0formal#AT#y#0; +Processing command (at Snapshots8.v0.dfy(4,8)) assert {:id "id4$id2$requires"} {:id "id2"} LitInt(0) <= call0formal#AT#y#0; >>> DoNothingToAssert Snapshots8.v0.dfy(3,11): Error: assertion might not hold Snapshots8.v0.dfy(4,7): Error: a precondition for this call could not be proved Snapshots8.v0.dfy(8,13): Related location: this is the precondition that could not be proved -Processing command (at Snapshots8.v0.dfy(13,13)) assert {:id "id9"} LitInt(2) <= z#0; +Processing command (at Snapshots8.v0.dfy(13,13)) assert {:id "id8"} LitInt(2) <= z#0; >>> DoNothingToAssert Snapshots8.v0.dfy(17,9): Error: a postcondition could not be proved on this return path Snapshots8.v0.dfy(13,12): Related location: this is the postcondition that could not be proved -Processing command (at Snapshots8.v0.dfy(23,12)) assert {:id "id11"} u#0 != 53; +Processing command (at Snapshots8.v0.dfy(23,12)) assert {:id "id10"} u#0 != 53; >>> DoNothingToAssert Snapshots8.v0.dfy(23,11): Error: assertion might not hold -Processing command (at Snapshots8.v0.dfy(28,10)) assert {:id "id12"} Lit(true); +Processing command (at Snapshots8.v0.dfy(28,10)) assert {:id "id11"} Lit(true); >>> DoNothingToAssert Dafny program verifier finished with 1 verified, 4 errors -Processing command (at Snapshots8.v1.dfy(30,17)) assert {:id "id26"} u#0 != 53; +Processing command (at Snapshots8.v1.dfy(30,17)) assert {:id "id24"} u#0 != 53; >>> RecycleError Snapshots8.v1.dfy(30,16): Error: assertion might not hold -Processing command (at Snapshots8.v1.dfy(3,15)) assert {:id "id13"} x#0 < 20 || LitInt(10) <= x#0; +Processing command (at Snapshots8.v1.dfy(3,15)) assert {:id "id12"} x#0 < 20 || LitInt(10) <= x#0; >>> MarkAsFullyVerified -Processing command (at Snapshots8.v1.dfy(5,17)) assert {:id "id14"} x#0 < 10; +Processing command (at Snapshots8.v1.dfy(5,17)) assert {:id "id13"} x#0 < 10; >>> RecycleError -Processing command (at Snapshots8.v1.dfy(6,8)) assert {:id "id19$id15$requires"} {:id "id15"} LitInt(0) <= call0formal#AT#y#0; +Processing command (at Snapshots8.v1.dfy(6,8)) assert {:id "id17$id14$requires"} {:id "id14"} LitInt(0) <= call0formal#AT#y#0; >>> RecycleError -Processing command (at Snapshots8.v1.dfy(7,12)) assert {:id "id17"} x#0 == LitInt(7); +Processing command (at Snapshots8.v1.dfy(7,12)) assert {:id "id15"} x#0 == LitInt(7); >>> DoNothingToAssert Snapshots8.v1.dfy(5,16): Error: assertion might not hold Snapshots8.v1.dfy(6,7): Error: a precondition for this call could not be proved Snapshots8.v1.dfy(12,20): Related location: this is the precondition that could not be proved Snapshots8.v1.dfy(7,11): Error: assertion might not hold -Processing command (at Snapshots8.v1.dfy(23,12)) assert {:id "id25"} Lit(true); +Processing command (at Snapshots8.v1.dfy(23,12)) assert {:id "id23"} Lit(true); >>> DoNothingToAssert -Processing command (at Snapshots8.v1.dfy(19,13)) assert {:id "id23"} LitInt(2) <= z#0; +Processing command (at Snapshots8.v1.dfy(19,13)) assert {:id "id21"} LitInt(2) <= z#0; >>> DoNothingToAssert Snapshots8.v1.dfy(24,9): Error: a postcondition could not be proved on this return path Snapshots8.v1.dfy(19,12): Related location: this is the postcondition that could not be proved diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/ListContents.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/ListContents.dfy index 19f1d30c985..97c144db9cb 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/ListContents.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/ListContents.dfy @@ -1,6 +1,5 @@ // RUN: %testDafnyForEachResolver "%s" - class Node { ghost var List: seq ghost var Repr: set> diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy index 1c214141942..e74a60c9ad7 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy @@ -205,7 +205,7 @@ method EuclidGcd(X: pos, Y: pos) returns (gcd: pos) // ------------------------------------------------------------------------------------------------------ // The alternative definitions that follow allow the two cases in the GCD algorithm to look more similar. -lemma GcdSubtractAlt(x: pos, y: pos) +lemma {:isolate_assertions} GcdSubtractAlt(x: pos, y: pos) requires x < y ensures Gcd(y, x) == Gcd(x, y - x) // this says Gcd(y, x) instead of Gcd(x, y) as in GcdSubtract above {