From 21a92460cc756285022c0211e67eb1d215a11e8c Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 15 Jul 2024 12:39:27 +0200 Subject: [PATCH 01/63] Add tests --- .../ast/expressions/statementExpressions.dfy | 27 +++++++++++++++++++ .../statementExpressions.dfy.expect | 0 2 files changed, 27 insertions(+) create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressions.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressions.dfy.expect diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressions.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressions.dfy new file mode 100644 index 00000000000..e6d4da7a062 --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressions.dfy @@ -0,0 +1,27 @@ +// RUN: %verify --type-system-refresh --allow-axioms --bprint:%t.bpl --isolate-assertions --boogie "/printPruned:%S/pruned" %s > %t +// RUN: %diff "%s.expect" "%t" + +function StmtExprValueAndEnsures(): int + ensures ValueAndEnsures() == 42 +{ + assert true; 42 +} + +function ExpressionWFVersusFunctionEnsures(): int + ensures 10 == 11 // no error, since the statement expression can be used for the ensures clause +{ + assume false; 10 +} + +function ExpressionWFAndLet(): int +{ + var x := assume false; 10; + assert false; // error, since the statement expression does not leak outside of the let. + x +} + +method Bar() { + var x := assume false; 3; + assert false; // error +} + diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressions.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressions.dfy.expect new file mode 100644 index 00000000000..e69de29bb2d From 0ec4c02a2379c7bad556f79e42a43dab7d4ab566 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 15 Jul 2024 12:43:28 +0200 Subject: [PATCH 02/63] Fix test --- .../ast/expressions/statementExpressions.dfy | 22 ++++++++++++++----- .../statementExpressions.dfy.expect | 2 ++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressions.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressions.dfy index e6d4da7a062..127135beb3d 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressions.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressions.dfy @@ -1,18 +1,30 @@ -// RUN: %verify --type-system-refresh --allow-axioms --bprint:%t.bpl --isolate-assertions --boogie "/printPruned:%S/pruned" %s > %t +// RUN: ! %verify --type-system-refresh --allow-axioms --bprint:%t.bpl --isolate-assertions %s > %t // RUN: %diff "%s.expect" "%t" -function StmtExprValueAndEnsures(): int - ensures ValueAndEnsures() == 42 +function StatementExpressionValueAndEnsures(): int + ensures StatementExpressionValueAndEnsures() == 42 { assert true; 42 } -function ExpressionWFVersusFunctionEnsures(): int +function StatementExpressionAssumeAndFunctionEnsures(): int ensures 10 == 11 // no error, since the statement expression can be used for the ensures clause { assume false; 10 } +function StatementExpressionAndSubsetResult(): nat + // no error, since the statement expression can be used for the return type subset constraint +{ + assume -1 > 0; -1 +} + +method StatementExpressionAndSubsetLocal() + // no error, since the statement expression can be used for the return type subset constraint +{ + var x: nat := assume -1 > 0; -1; +} + function ExpressionWFAndLet(): int { var x := assume false; 10; @@ -20,7 +32,7 @@ function ExpressionWFAndLet(): int x } -method Bar() { +method StatementExpressionAssumeDoesNotEscapeExpression() { var x := assume false; 3; assert false; // error } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressions.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressions.dfy.expect index e69de29bb2d..0471f345c93 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressions.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressions.dfy.expect @@ -0,0 +1,2 @@ + +Dafny program verifier finished with 12 verified, 2 errors From a0d6c9dd13a6e92ac67fdc5208110869d46efd5c Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 25 Jul 2024 13:19:22 +0200 Subject: [PATCH 03/63] Update test --- ...ressions.dfy => statementExpressionScope.dfy} | 16 ++++++++++++---- ...xpect => statementExpressionScope.dfy.expect} | 0 2 files changed, 12 insertions(+), 4 deletions(-) rename Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/{statementExpressions.dfy => statementExpressionScope.dfy} (65%) rename Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/{statementExpressions.dfy.expect => statementExpressionScope.dfy.expect} (100%) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressions.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy similarity index 65% rename from Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressions.dfy rename to Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy index 127135beb3d..c420b478a94 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressions.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy @@ -20,20 +20,28 @@ function StatementExpressionAndSubsetResult(): nat } method StatementExpressionAndSubsetLocal() - // no error, since the statement expression can be used for the return type subset constraint + // no error, since the statement expression can be used for the local variable type subset constraint { var x: nat := assume -1 > 0; -1; } -function ExpressionWFAndLet(): int +predicate P(x: int) +method NeedsP(x: int) requires P(x) + +method StatementExpressionAndPrecondition(x: int) +{ + NeedsP(assume P(x); x); // no error, since the statement expression can be used for the requires clause +} + +function StatementExpressionAssumeDoesNotEscapeLetBinding(): int { var x := assume false; 10; assert false; // error, since the statement expression does not leak outside of the let. x } -method StatementExpressionAssumeDoesNotEscapeExpression() { +method StatementExpressionAssumeDoesNotEscapeAssignment() { var x := assume false; 3; - assert false; // error + assert false; // error, since the statement expression does not leak outside of the assignment. } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressions.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy.expect similarity index 100% rename from Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressions.dfy.expect rename to Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy.expect From 42faecaccbb4009189fb7a8c13ef86ca3d174c8e Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 26 Jul 2024 17:15:37 +0200 Subject: [PATCH 04/63] Put function wellformedness check in a separate file, and extract some methods --- .../BoogieGenerator.ExpressionWellformed.cs | 1 + ...oogieGenerator.Functions.Wellformedness.cs | 353 ++++++++++++++++++ .../Verifier/BoogieGenerator.Functions.cs | 287 -------------- .../Verifier/BoogieGenerator.Methods.cs | 2 +- .../Verifier/BoogieGenerator.TrStatement.cs | 49 +-- 5 files changed, 382 insertions(+), 310 deletions(-) create mode 100644 Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 8de7eacb036..917aee73bf9 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -1336,6 +1336,7 @@ void CheckOperand(Expression operand) { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression } + if (result != null) { Contract.Assert(resultType != null); var bResult = etran.TrExpr(expr); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs new file mode 100644 index 00000000000..81b42013a70 --- /dev/null +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs @@ -0,0 +1,353 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; +using DafnyCore.Verifier; +using Microsoft.Boogie; +using static Microsoft.Dafny.Util; +using Bpl = Microsoft.Boogie; +using PODesc = Microsoft.Dafny.ProofObligationDescription; + +namespace Microsoft.Dafny; + +public partial class BoogieGenerator { + class FunctionWellformednessChecker { + private readonly BoogieGenerator generator; + + public FunctionWellformednessChecker(BoogieGenerator generator) { + this.generator = generator; + } + + public void Check(Function f) { + + Contract.Assert(generator.InVerificationScope(f)); + + generator.proofDependencies.SetCurrentDefinition(MethodVerboseName(f.FullDafnyName, + MethodTranslationKind.SpecWellformedness)); + generator.currentModule = f.EnclosingClass.EnclosingModuleDefinition; + generator.codeContext = f; + + Expr prevHeap = null; + Expr currHeap = null; + var ordinaryEtran = new ExpressionTranslator(generator, generator.predef, f.tok, f); + ExpressionTranslator etran; + var inParams_Heap = new List(); + if (f is TwoStateFunction) { + var prevHeapVar = new Bpl.Formal(f.tok, new TypedIdent(f.tok, "previous$Heap", generator.predef.HeapType), true); + var currHeapVar = new Bpl.Formal(f.tok, new TypedIdent(f.tok, "current$Heap", generator.predef.HeapType), true); + inParams_Heap.Add(prevHeapVar); + inParams_Heap.Add(currHeapVar); + prevHeap = new Bpl.IdentifierExpr(f.tok, prevHeapVar); + currHeap = new Bpl.IdentifierExpr(f.tok, currHeapVar); + etran = new ExpressionTranslator(generator, generator.predef, currHeap, prevHeap, f); + } else { + etran = ordinaryEtran; + } + + // parameters of the procedure + var typeInParams = generator.MkTyParamFormals(GetTypeParams(f), true); + var inParams = GetParameters(f, etran); + var outParams = GetWellformednessProcedureOutParameters(f, etran); + var requires = GetWellformednessProcedureRequires(f, etran, prevHeap, ordinaryEtran, currHeap); + + // modifies $Heap + var mod = new List { + ordinaryEtran.HeapCastToIdentifierExpr, + }; + var ensures = GetWellformnessProcedureEnsures(f, etran); + + var proc = new Procedure(f.tok, "CheckWellformed" + NameSeparator + f.FullSanitizedName, + new List(), + Concat(Concat(typeInParams, inParams_Heap), inParams), outParams, + false, requires, mod, ensures, etran.TrAttributes(f.Attributes, null)); + AddVerboseNameAttribute(proc, f.FullDafnyName, MethodTranslationKind.SpecWellformedness); + generator.sink.AddTopLevelDeclaration(proc); + + if (generator.InsertChecksums) { + generator.InsertChecksum(f, proc, true); + } + + Contract.Assert(proc.InParams.Count == typeInParams.Count + inParams_Heap.Count + inParams.Count); + // Changed the next line to strip from inParams instead of proc.InParams + // They should be the same, but hence the added contract + var implInParams = Bpl.Formal.StripWhereClauses(inParams); + var implOutParams = Bpl.Formal.StripWhereClauses(outParams); + var locals = new List(); + var builder = new BoogieStmtListBuilder(generator, generator.options); + var builderInitializationArea = new BoogieStmtListBuilder(generator, generator.options); + builder.Add(new CommentCmd("AddWellformednessCheck for function " + f)); + if (f is TwoStateFunction) { + // $Heap := current$Heap; + var heap = ordinaryEtran.HeapCastToIdentifierExpr; + builder.Add(Cmd.SimpleAssign(f.tok, heap, etran.HeapExpr)); + etran = ordinaryEtran; // we no longer need the special heap names + } + + builder.AddCaptureState(f.tok, false, "initial state"); + + generator.DefineFrame(f.tok, etran.ReadsFrame(f.tok), f.Reads.Expressions, builder, locals, null); + generator.InitializeFuelConstant(f.tok, builder, etran); + + var delayer = new ReadsCheckDelayer(etran, null, locals, builderInitializationArea, builder); + + // Check well-formedness of any default-value expressions (before assuming preconditions). + delayer.DoWithDelayedReadsChecks(true, wfo => { + foreach (var formal in f.Ins.Where(formal => formal.DefaultValue != null)) { + var e = formal.DefaultValue; + generator.CheckWellformed(e, wfo, locals, builder, + etran.WithReadsFrame(etran.readsFrame, null)); // No frame scope for default values + builder.Add(new AssumeCmd(e.tok, etran.CanCallAssumption(e))); + generator.CheckSubrange(e.tok, etran.TrExpr(e), e.Type, formal.Type, e, builder); + + 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)); + } + } + } + }); + + // Check well-formedness of the preconditions (including termination), and then + // assume each one of them. After all that (in particular, after assuming all + // of them), do the postponed reads checks. + delayer.DoWithDelayedReadsChecks(false, wfo => { + foreach (AttributedExpression p in f.Req) { + if (p.Label != null) { + p.Label.E = (f is TwoStateFunction ? ordinaryEtran : etran.Old).TrExpr(p.E); + generator.CheckWellformed(p.E, wfo, locals, builder, etran); + } else { + generator.CheckWellformedAndAssume(p.E, wfo, locals, builder, etran, "requires clause"); + } + } + }); + + // Check well-formedness of the reads clause. Note that this is done after assuming + // the preconditions. In other words, the well-formedness of the reads clause is + // allowed to assume the precondition (yet, the requires clause is checked to + // read only those things indicated in the reads clause). + delayer.DoWithDelayedReadsChecks(false, + wfo => { generator.CheckFrameWellFormed(wfo, f.Reads.Expressions, locals, builder, etran); }); + + // 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"); + generator.CheckFrameEmpty(f.tok, etran, etran.ReadsFrame(f.tok), builder, desc, null); + } + + // check well-formedness of the decreases clauses (including termination, but no reads checks) + foreach (Expression p in f.Decreases.Expressions) { + generator.CheckWellformed(p, new WFOptions(null, false), locals, builder, etran); + } + + // Generate: + // if (*) { + // check well-formedness of postcondition + // assume false; // don't go on to check the postconditions + // } else { + // check well-formedness of body + // // fall through to check the postconditions themselves + // } + // Here go the postconditions (termination checks included, but no reads checks) + BoogieStmtListBuilder postCheckBuilder = new BoogieStmtListBuilder(generator, generator.options); + // Assume the type returned by the call itself respects its type (this matters if the type is "nat", for example) + { + var args = new List(); + foreach (var p in GetTypeParams(f)) { + args.Add(generator.TrTypeParameter(p)); + } + + if (f.IsFuelAware()) { + args.Add(etran.layerInterCluster.GetFunctionFuel(f)); + } + + if (f.IsOpaque || f.IsMadeImplicitlyOpaque(generator.options)) { + args.Add(generator.GetRevealConstant(f)); + } + + if (f is TwoStateFunction) { + args.Add(etran.Old.HeapExpr); + } + + if (f.ReadsHeap) { + args.Add(etran.HeapExpr); + } + + if (!f.IsStatic) { + args.Add(new Bpl.IdentifierExpr(f.tok, etran.This)); + } + + foreach (var p in f.Ins) { + args.Add(new Bpl.IdentifierExpr(p.tok, p.AssignUniqueName(f.IdGenerator), generator.TrType(p.Type))); + } + + Bpl.IdentifierExpr funcID = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName, generator.TrType(f.ResultType)); + Expr funcAppl = new NAryExpr(f.tok, new FunctionCall(funcID), args); + + var wh = generator.GetWhereClause(f.tok, funcAppl, f.ResultType, etran, NOALLOC); + if (wh != null) { + postCheckBuilder.Add(TrAssumeCmd(f.tok, wh)); + } + } + // Now for the ensures clauses + foreach (AttributedExpression p in f.Ens) { + // assume the postcondition for the benefit of checking the remaining postconditions + generator.CheckWellformedAndAssume(p.E, new WFOptions(f, false), locals, postCheckBuilder, etran, "ensures clause"); + } + + // Here goes the body (and include both termination checks and reads checks) + BoogieStmtListBuilder bodyCheckBuilder = new BoogieStmtListBuilder(generator, generator.options); + if (f.Body == null || !generator.RevealedInScope(f)) { + // don't fall through to postcondition checks + bodyCheckBuilder.Add(TrAssumeCmd(f.tok, Expr.False)); + } else { + var funcID = new FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName, generator.TrType(f.ResultType))); + var args = new List(); + foreach (var p in GetTypeParams(f)) { + args.Add(generator.TrTypeParameter(p)); + } + + if (f.IsFuelAware()) { + args.Add(etran.layerInterCluster.GetFunctionFuel(f)); + } + + if (f.IsOpaque || f.IsMadeImplicitlyOpaque(generator.options)) { + args.Add(generator.GetRevealConstant(f)); + } + + if (f is TwoStateFunction) { + args.Add(etran.Old.HeapExpr); + } + + if (f.ReadsHeap) { + args.Add(etran.HeapExpr); + } + + foreach (Variable p in implInParams) { + args.Add(new Bpl.IdentifierExpr(f.tok, p)); + } + + Expr funcAppl = new NAryExpr(f.tok, funcID, args); + + var bodyCheckDelayer = new ReadsCheckDelayer(etran, null, locals, builderInitializationArea, bodyCheckBuilder); + bodyCheckDelayer.DoWithDelayedReadsChecks(false, wfo => { + generator.CheckWellformedWithResult(f.Body, wfo, funcAppl, f.ResultType, locals, bodyCheckBuilder, etran, + "function call result"); + if (f.Result != null) { + var cmd = TrAssumeCmd(f.tok, Expr.Eq(funcAppl, generator.TrVar(f.tok, f.Result))); + generator.proofDependencies?.AddProofDependencyId(cmd, f.tok, new FunctionDefinitionDependency(f)); + bodyCheckBuilder.Add(cmd); + } + }); + + // Enforce 'older' conditions + var (olderParameterCount, olderCondition) = generator.OlderCondition(f, funcAppl, implInParams); + if (olderParameterCount != 0) { + bodyCheckBuilder.Add(generator.Assert(f.tok, olderCondition, + new PODesc.IsOlderProofObligation(olderParameterCount, f.Ins.Count + (f.IsStatic ? 0 : 1)))); + } + } + + // Combine the two, letting the postcondition be checked on after the "bodyCheckBuilder" branch + postCheckBuilder.Add(TrAssumeCmd(f.tok, Expr.False)); + builder.Add(new IfCmd(f.tok, null, postCheckBuilder.Collect(f.tok), null, bodyCheckBuilder.Collect(f.tok))); + + var s0 = builderInitializationArea.Collect(f.tok); + var s1 = builder.Collect(f.tok); + var implBody = new StmtList(new List(s0.BigBlocks.Concat(s1.BigBlocks)), f.tok); + + if (generator.EmitImplementation(f.Attributes)) { + // emit the impl only when there are proof obligations. + QKeyValue kv = etran.TrAttributes(f.Attributes, null); + var impl = generator.AddImplementationWithAttributes(generator.GetToken(f), proc, + Concat(Concat(Bpl.Formal.StripWhereClauses(typeInParams), inParams_Heap), implInParams), + implOutParams, + locals, implBody, kv); + if (generator.InsertChecksums) { + generator.InsertChecksum(f, impl); + } + } + + Contract.Assert(generator.currentModule == f.EnclosingClass.EnclosingModuleDefinition); + Contract.Assert(generator.codeContext == f); + generator.Reset(); + } + + private List GetWellformednessProcedureOutParameters(Function f, ExpressionTranslator etran) { + var outParams = new List(); + if (f.Result != null) { + Formal p = f.Result; + Contract.Assert(!p.IsOld); + Bpl.Type varType = generator.TrType(p.Type); + Expr wh = generator.GetWhereClause(p.tok, new Bpl.IdentifierExpr(p.tok, p.AssignUniqueName(f.IdGenerator), varType), + p.Type, etran, NOALLOC); + outParams.Add(new Bpl.Formal(p.tok, new TypedIdent(p.tok, p.AssignUniqueName(f.IdGenerator), varType, wh), + true)); + } + + return outParams; + } + + private List GetWellformnessProcedureEnsures(Function f, ExpressionTranslator etran) { + var ens = new List(); + foreach (AttributedExpression p in f.Ens) { + var functionHeight = generator.currentModule.CallGraph.GetSCCRepresentativePredecessorCount(f); + var splits = new List(); + bool splitHappened /*we actually don't care*/ = + generator.TrSplitExpr(p.E, splits, true, functionHeight, true, true, etran); + var (errorMessage, successMessage) = generator.CustomErrorMessage(p.Attributes); + foreach (var s in splits) { + if (s.IsChecked && !RefinementToken.IsInherited(s.Tok, generator.currentModule)) { + generator.AddEnsures(ens, generator.EnsuresWithDependencies(s.Tok, false, p.E, s.E, errorMessage, successMessage, null)); + } + } + } + + return ens; + } + + private List GetWellformednessProcedureRequires(Function f, ExpressionTranslator etran, + Expr prevHeap, + ExpressionTranslator ordinaryEtran, Expr currHeap) { + var requires = new List(); + // free requires mh == ModuleContextHeight && fh == FunctionContextHeight; + requires.Add(generator.Requires(f.tok, true, null, etran.HeightContext(f), null, null, null)); + if (f is TwoStateFunction) { + // free requires prevHeap == Heap && HeapSucc(prevHeap, currHeap) && IsHeap(currHeap) + var a0 = Expr.Eq(prevHeap, ordinaryEtran.HeapExpr); + var a1 = generator.HeapSucc(prevHeap, currHeap); + var a2 = generator.FunctionCall(f.tok, BuiltinFunction.IsGoodHeap, null, currHeap); + requires.Add(generator.Requires(f.tok, true, null, BplAnd(a0, BplAnd(a1, a2)), null, null, null)); + } + + foreach (var typeBoundAxiom in generator.TypeBoundAxioms(f.tok, Concat(f.EnclosingClass.TypeArgs, f.TypeArgs))) { + requires.Add(generator.Requires(f.tok, true, null, typeBoundAxiom, null, null, null)); + } + + return requires; + } + + private List GetParameters(Function f, ExpressionTranslator etran) { + var inParams = new List(); + if (!f.IsStatic) { + var th = new Bpl.IdentifierExpr(f.tok, "this", generator.TrReceiverType(f)); + Expr wh = BplAnd( + generator.ReceiverNotNull(th), + (f is TwoStateFunction ? etran.Old : etran).GoodRef(f.tok, th, ModuleResolver.GetReceiverType(f.tok, f))); + Bpl.Formal thVar = new Bpl.Formal(f.tok, new TypedIdent(f.tok, "this", generator.TrReceiverType(f), wh), true); + inParams.Add(thVar); + } + + foreach (Formal parameter in f.Ins) { + Bpl.Type varType = generator.TrType(parameter.Type); + Expr wh = generator.GetWhereClause(parameter.tok, + new Bpl.IdentifierExpr(parameter.tok, parameter.AssignUniqueName(f.IdGenerator), varType), parameter.Type, + parameter.IsOld ? etran.Old : etran, f is TwoStateFunction ? ISALLOC : NOALLOC); + inParams.Add(new Bpl.Formal(parameter.tok, + new TypedIdent(parameter.tok, parameter.AssignUniqueName(f.IdGenerator), varType, wh), true)); + } + + return inParams; + } + } +} \ No newline at end of file diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs index 2dd812c60b9..272766a91ee 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs @@ -13,293 +13,6 @@ namespace Microsoft.Dafny; public partial class BoogieGenerator { - void AddWellformednessCheck(Function f) { - Contract.Requires(f != null); - Contract.Requires(sink != null && predef != null); - Contract.Requires(f.EnclosingClass != null); - Contract.Requires(currentModule == null && codeContext == null && isAllocContext != null); - Contract.Ensures(currentModule == null && codeContext == null && isAllocContext != null); - - Contract.Assert(InVerificationScope(f)); - - proofDependencies.SetCurrentDefinition(MethodVerboseName(f.FullDafnyName, MethodTranslationKind.SpecWellformedness)); - currentModule = f.EnclosingClass.EnclosingModuleDefinition; - codeContext = f; - - Bpl.Expr prevHeap = null; - Bpl.Expr currHeap = null; - var ordinaryEtran = new ExpressionTranslator(this, predef, f.tok, f); - ExpressionTranslator etran; - var inParams_Heap = new List(); - if (f is TwoStateFunction) { - var prevHeapVar = new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "previous$Heap", predef.HeapType), true); - var currHeapVar = new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "current$Heap", predef.HeapType), true); - inParams_Heap.Add(prevHeapVar); - inParams_Heap.Add(currHeapVar); - prevHeap = new Bpl.IdentifierExpr(f.tok, prevHeapVar); - currHeap = new Bpl.IdentifierExpr(f.tok, currHeapVar); - etran = new ExpressionTranslator(this, predef, currHeap, prevHeap, f); - } else { - etran = ordinaryEtran; - } - - // parameters of the procedure - var typeInParams = MkTyParamFormals(GetTypeParams(f), true); - var inParams = new List(); - var outParams = new List(); - if (!f.IsStatic) { - var th = new Bpl.IdentifierExpr(f.tok, "this", TrReceiverType(f)); - Bpl.Expr wh = BplAnd( - ReceiverNotNull(th), - (f is TwoStateFunction ? etran.Old : etran).GoodRef(f.tok, th, ModuleResolver.GetReceiverType(f.tok, f))); - Bpl.Formal thVar = new Bpl.Formal(f.tok, new Bpl.TypedIdent(f.tok, "this", TrReceiverType(f), wh), true); - inParams.Add(thVar); - } - foreach (Formal p in f.Ins) { - Bpl.Type varType = TrType(p.Type); - Bpl.Expr wh = GetWhereClause(p.tok, new Bpl.IdentifierExpr(p.tok, p.AssignUniqueName(f.IdGenerator), varType), p.Type, - p.IsOld ? etran.Old : etran, f is TwoStateFunction ? ISALLOC : NOALLOC); - inParams.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.AssignUniqueName(f.IdGenerator), varType, wh), true)); - } - if (f.Result != null) { - Formal p = f.Result; - Contract.Assert(!p.IsOld); - Bpl.Type varType = TrType(p.Type); - Bpl.Expr wh = GetWhereClause(p.tok, new Bpl.IdentifierExpr(p.tok, p.AssignUniqueName(f.IdGenerator), varType), p.Type, etran, NOALLOC); - outParams.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.AssignUniqueName(f.IdGenerator), varType, wh), true)); - } - // the procedure itself - var req = new List(); - // free requires mh == ModuleContextHeight && fh == FunctionContextHeight; - req.Add(Requires(f.tok, true, null, etran.HeightContext(f), null, null, null)); - if (f is TwoStateFunction) { - // free requires prevHeap == Heap && HeapSucc(prevHeap, currHeap) && IsHeap(currHeap) - var a0 = Bpl.Expr.Eq(prevHeap, ordinaryEtran.HeapExpr); - var a1 = HeapSucc(prevHeap, currHeap); - var a2 = FunctionCall(f.tok, BuiltinFunction.IsGoodHeap, null, currHeap); - req.Add(Requires(f.tok, true, null, BplAnd(a0, BplAnd(a1, a2)), null, null, null)); - } - - foreach (var typeBoundAxiom in TypeBoundAxioms(f.tok, Concat(f.EnclosingClass.TypeArgs, f.TypeArgs))) { - req.Add(Requires(f.tok, true, null, typeBoundAxiom, null, null, null)); - } - - // modifies $Heap - var mod = new List { - ordinaryEtran.HeapCastToIdentifierExpr, - }; - // check that postconditions hold - var ens = new List(); - foreach (AttributedExpression p in f.Ens) { - var functionHeight = currentModule.CallGraph.GetSCCRepresentativePredecessorCount(f); - var splits = new List(); - bool splitHappened /*we actually don't care*/ = TrSplitExpr(p.E, splits, true, functionHeight, true, true, etran); - var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); - foreach (var s in splits) { - if (s.IsChecked && !RefinementToken.IsInherited(s.Tok, currentModule)) { - AddEnsures(ens, EnsuresWithDependencies(s.Tok, false, p.E, s.E, errorMessage, successMessage, null)); - } - } - } - var proc = new Bpl.Procedure(f.tok, "CheckWellformed" + NameSeparator + f.FullSanitizedName, new List(), - Concat(Concat(typeInParams, inParams_Heap), inParams), outParams, - false, req, mod, ens, etran.TrAttributes(f.Attributes, null)); - AddVerboseNameAttribute(proc, f.FullDafnyName, MethodTranslationKind.SpecWellformedness); - sink.AddTopLevelDeclaration(proc); - - if (InsertChecksums) { - InsertChecksum(f, proc, true); - } - - Contract.Assert(proc.InParams.Count == typeInParams.Count + inParams_Heap.Count + inParams.Count); - // Changed the next line to strip from inParams instead of proc.InParams - // They should be the same, but hence the added contract - var implInParams = Bpl.Formal.StripWhereClauses(inParams); - var implOutParams = Bpl.Formal.StripWhereClauses(outParams); - var locals = new List(); - var builder = new BoogieStmtListBuilder(this, options); - var builderInitializationArea = new BoogieStmtListBuilder(this, options); - builder.Add(new CommentCmd("AddWellformednessCheck for function " + f)); - if (f is TwoStateFunction) { - // $Heap := current$Heap; - var heap = ordinaryEtran.HeapCastToIdentifierExpr; - builder.Add(Bpl.Cmd.SimpleAssign(f.tok, heap, etran.HeapExpr)); - etran = ordinaryEtran; // we no longer need the special heap names - } - builder.AddCaptureState(f.tok, false, "initial state"); - - DefineFrame(f.tok, etran.ReadsFrame(f.tok), f.Reads.Expressions, builder, locals, null); - InitializeFuelConstant(f.tok, builder, etran); - - var delayer = new ReadsCheckDelayer(etran, null, locals, builderInitializationArea, builder); - - // Check well-formedness of any default-value expressions (before assuming preconditions). - delayer.DoWithDelayedReadsChecks(true, wfo => { - foreach (var formal in f.Ins.Where(formal => formal.DefaultValue != null)) { - var e = formal.DefaultValue; - CheckWellformed(e, wfo, locals, builder, etran.WithReadsFrame(etran.readsFrame, null)); // No frame scope for default values - builder.Add(new Bpl.AssumeCmd(e.tok, etran.CanCallAssumption(e))); - CheckSubrange(e.tok, etran.TrExpr(e), e.Type, formal.Type, e, builder); - - if (formal.IsOld) { - Bpl.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 function's previous state", e); - builder.Add(Assert(GetToken(e), wh, desc)); - } - } - } - }); - - // Check well-formedness of the preconditions (including termination), and then - // assume each one of them. After all that (in particular, after assuming all - // of them), do the postponed reads checks. - delayer.DoWithDelayedReadsChecks(false, wfo => { - foreach (AttributedExpression p in f.Req) { - if (p.Label != null) { - p.Label.E = (f is TwoStateFunction ? ordinaryEtran : etran.Old).TrExpr(p.E); - CheckWellformed(p.E, wfo, locals, builder, etran); - } else { - CheckWellformedAndAssume(p.E, wfo, locals, builder, etran, "requires clause"); - } - } - }); - - // Check well-formedness of the reads clause. Note that this is done after assuming - // the preconditions. In other words, the well-formedness of the reads clause is - // allowed to assume the precondition (yet, the requires clause is checked to - // read only those things indicated in the reads clause). - delayer.DoWithDelayedReadsChecks(false, wfo => { - CheckFrameWellFormed(wfo, f.Reads.Expressions, locals, builder, etran); - }); - - // 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"); - CheckFrameEmpty(f.tok, etran, etran.ReadsFrame(f.tok), builder, desc, null); - } - - // check well-formedness of the decreases clauses (including termination, but no reads checks) - foreach (Expression p in f.Decreases.Expressions) { - CheckWellformed(p, new WFOptions(null, false), locals, builder, etran); - } - // Generate: - // if (*) { - // check well-formedness of postcondition - // assume false; // don't go on to check the postconditions - // } else { - // check well-formedness of body - // // fall through to check the postconditions themselves - // } - // Here go the postconditions (termination checks included, but no reads checks) - BoogieStmtListBuilder postCheckBuilder = new BoogieStmtListBuilder(this, options); - // Assume the type returned by the call itself respects its type (this matters if the type is "nat", for example) - { - var args = new List(); - foreach (var p in GetTypeParams(f)) { - args.Add(TrTypeParameter(p)); - } - if (f.IsFuelAware()) { - args.Add(etran.layerInterCluster.GetFunctionFuel(f)); - } - - if (f.IsOpaque || f.IsMadeImplicitlyOpaque(options)) { - args.Add(GetRevealConstant(f)); - } - if (f is TwoStateFunction) { - args.Add(etran.Old.HeapExpr); - } - if (f.ReadsHeap) { - args.Add(etran.HeapExpr); - } - if (!f.IsStatic) { - args.Add(new Bpl.IdentifierExpr(f.tok, etran.This)); - } - foreach (var p in f.Ins) { - args.Add(new Bpl.IdentifierExpr(p.tok, p.AssignUniqueName(f.IdGenerator), TrType(p.Type))); - } - Bpl.IdentifierExpr funcID = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName, TrType(f.ResultType)); - Bpl.Expr funcAppl = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(funcID), args); - - var wh = GetWhereClause(f.tok, funcAppl, f.ResultType, etran, NOALLOC); - if (wh != null) { - postCheckBuilder.Add(TrAssumeCmd(f.tok, wh)); - } - } - // Now for the ensures clauses - foreach (AttributedExpression p in f.Ens) { - // assume the postcondition for the benefit of checking the remaining postconditions - CheckWellformedAndAssume(p.E, new WFOptions(f, false), locals, postCheckBuilder, etran, "ensures clause"); - } - // Here goes the body (and include both termination checks and reads checks) - BoogieStmtListBuilder bodyCheckBuilder = new BoogieStmtListBuilder(this, options); - if (f.Body == null || !RevealedInScope(f)) { - // don't fall through to postcondition checks - bodyCheckBuilder.Add(TrAssumeCmd(f.tok, Bpl.Expr.False)); - } else { - var funcID = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName, TrType(f.ResultType))); - var args = new List(); - foreach (var p in GetTypeParams(f)) { - args.Add(TrTypeParameter(p)); - } - if (f.IsFuelAware()) { - args.Add(etran.layerInterCluster.GetFunctionFuel(f)); - } - - if (f.IsOpaque || f.IsMadeImplicitlyOpaque(options)) { - args.Add(GetRevealConstant(f)); - } - if (f is TwoStateFunction) { - args.Add(etran.Old.HeapExpr); - } - if (f.ReadsHeap) { - args.Add(etran.HeapExpr); - } - foreach (Variable p in implInParams) { - args.Add(new Bpl.IdentifierExpr(f.tok, p)); - } - Bpl.Expr funcAppl = new Bpl.NAryExpr(f.tok, funcID, args); - - var bodyCheckDelayer = new ReadsCheckDelayer(etran, null, locals, builderInitializationArea, bodyCheckBuilder); - bodyCheckDelayer.DoWithDelayedReadsChecks(false, wfo => { - CheckWellformedWithResult(f.Body, wfo, funcAppl, f.ResultType, locals, bodyCheckBuilder, etran, "function call result"); - if (f.Result != null) { - var cmd = TrAssumeCmd(f.tok, Bpl.Expr.Eq(funcAppl, TrVar(f.tok, f.Result))); - proofDependencies?.AddProofDependencyId(cmd, f.tok, new FunctionDefinitionDependency(f)); - bodyCheckBuilder.Add(cmd); - } - }); - - // Enforce 'older' conditions - var (olderParameterCount, olderCondition) = OlderCondition(f, funcAppl, implInParams); - if (olderParameterCount != 0) { - bodyCheckBuilder.Add(Assert(f.tok, olderCondition, new PODesc.IsOlderProofObligation(olderParameterCount, f.Ins.Count + (f.IsStatic ? 0 : 1)))); - } - } - // Combine the two, letting the postcondition be checked on after the "bodyCheckBuilder" branch - postCheckBuilder.Add(TrAssumeCmd(f.tok, Bpl.Expr.False)); - builder.Add(new Bpl.IfCmd(f.tok, null, postCheckBuilder.Collect(f.tok), null, bodyCheckBuilder.Collect(f.tok))); - - var s0 = builderInitializationArea.Collect(f.tok); - var s1 = builder.Collect(f.tok); - var implBody = new StmtList(new List(s0.BigBlocks.Concat(s1.BigBlocks)), f.tok); - - if (EmitImplementation(f.Attributes)) { - // emit the impl only when there are proof obligations. - QKeyValue kv = etran.TrAttributes(f.Attributes, null); - var impl = AddImplementationWithAttributes(GetToken(f), proc, - Concat(Concat(Bpl.Formal.StripWhereClauses(typeInParams), inParams_Heap), implInParams), - implOutParams, - locals, implBody, kv); - if (InsertChecksums) { - InsertChecksum(f, impl); - } - } - - Contract.Assert(currentModule == f.EnclosingClass.EnclosingModuleDefinition); - Contract.Assert(codeContext == f); - Reset(); - } void AddFunctionAxiom(Bpl.Function boogieFunction, Function f, Expression body) { Contract.Requires(f != null); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 8bd75fda710..f537becf2dc 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -148,7 +148,7 @@ void AddFunction_Top(Function f, bool includeAllMethods) { AddClassMember_Function(f); if (InVerificationScope(f)) { - AddWellformednessCheck(f); + new FunctionWellformednessChecker(this).Check(f); if (f.OverriddenFunction != null) { //it means that f is overriding its associated parent function AddFunctionOverrideCheckImpl(f); } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs index 52e95ec4edb..25c7d142853 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs @@ -497,28 +497,8 @@ private void TrStmt(Statement stmt, BoogieStmtListBuilder builder, List { bIe })); - Bpl.Expr wh = GetWhereClause(local.Tok, bIe, local.Type, etran, isAllocContext.Var(stmt.IsGhost, local)); - if (wh != null) { - builder.Add(TrAssumeCmd(local.Tok, wh)); - } - } - var varNameGen = CurrentIdGenerator.NestedFreshIdGenerator("let#"); - var pat = s.LHS; - var rhs = s.RHS; - var nm = varNameGen.FreshId(string.Format("#{0}#", 0)); - var r = new Bpl.LocalVariable(pat.tok, new Bpl.TypedIdent(pat.tok, nm, TrType(rhs.Type))); - locals.Add(r); - var rIe = new Bpl.IdentifierExpr(rhs.tok, r); - CheckWellformedWithResult(rhs, new WFOptions(null, false, false), rIe, pat.Expr.Type, locals, builder, etran, "variable declaration RHS"); - CheckCasePatternShape(pat, rhs, rIe, rhs.tok, pat.Expr.Type, builder); - builder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, s.tok, pat.Expr, e => Expr.Eq(e, rIe), "variable declaration")); + } else if (stmt is VarDeclPattern varDeclPattern) { + TranslateVariableDeclaration(builder, locals, etran, varDeclPattern); } else if (stmt is TryRecoverStatement haltRecoveryStatement) { // try/recover statements are currently internal-only AST nodes that cannot be // directly used in user Dafny code. They are only generated by rewriters, and verifying @@ -529,6 +509,31 @@ private void TrStmt(Statement stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + VarDeclPattern varDeclPattern) + { + foreach (var dafnyLocal in varDeclPattern.LocalVars) { + var boogieLocal = new Bpl.LocalVariable(dafnyLocal.Tok, new Bpl.TypedIdent(dafnyLocal.Tok, dafnyLocal.AssignUniqueName(currentDeclaration.IdGenerator), TrType(dafnyLocal.Type))); + locals.Add(boogieLocal); + var variableReference = new Bpl.IdentifierExpr(boogieLocal.tok, boogieLocal); + builder.Add(new Bpl.HavocCmd(dafnyLocal.Tok, new List { variableReference })); + var wh = GetWhereClause(dafnyLocal.Tok, variableReference, dafnyLocal.Type, etran, isAllocContext.Var(varDeclPattern.IsGhost, dafnyLocal)); + if (wh != null) { + builder.Add(TrAssumeCmd(dafnyLocal.Tok, wh)); + } + } + var varNameGen = CurrentIdGenerator.NestedFreshIdGenerator("let#"); + 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 boogieTupleReference = new Bpl.IdentifierExpr(rhs.tok, boogieTupleLocal); + CheckWellformedWithResult(rhs, new WFOptions(null, false, false), boogieTupleReference, pat.Expr.Type, locals, builder, etran, "variable declaration RHS"); + CheckCasePatternShape(pat, rhs, boogieTupleReference, rhs.tok, pat.Expr.Type, builder); + builder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, varDeclPattern.tok, pat.Expr, e => Expr.Eq(e, boogieTupleReference), "variable declaration")); + } + private void TrCalcStmt(CalcStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { Contract.Requires(stmt != null); Contract.Requires(builder != null); From eae6238aa1c94b0b2ec1f0a26adad4a8cf8ce1ec Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 26 Jul 2024 18:29:55 +0200 Subject: [PATCH 05/63] Refactoring --- ...oogieGenerator.Functions.Wellformedness.cs | 321 ++++++++++-------- 1 file changed, 182 insertions(+), 139 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs index 81b42013a70..ac26ff06192 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics.Contracts; using System.Linq; using DafnyCore.Verifier; @@ -26,29 +27,16 @@ public void Check(Function f) { generator.currentModule = f.EnclosingClass.EnclosingModuleDefinition; generator.codeContext = f; - Expr prevHeap = null; - Expr currHeap = null; - var ordinaryEtran = new ExpressionTranslator(generator, generator.predef, f.tok, f); - ExpressionTranslator etran; - var inParams_Heap = new List(); - if (f is TwoStateFunction) { - var prevHeapVar = new Bpl.Formal(f.tok, new TypedIdent(f.tok, "previous$Heap", generator.predef.HeapType), true); - var currHeapVar = new Bpl.Formal(f.tok, new TypedIdent(f.tok, "current$Heap", generator.predef.HeapType), true); - inParams_Heap.Add(prevHeapVar); - inParams_Heap.Add(currHeapVar); - prevHeap = new Bpl.IdentifierExpr(f.tok, prevHeapVar); - currHeap = new Bpl.IdentifierExpr(f.tok, currHeapVar); - etran = new ExpressionTranslator(generator, generator.predef, currHeap, prevHeap, f); - } else { - etran = ordinaryEtran; - } + ExpressionTranslator ordinaryEtran = new ExpressionTranslator(generator, generator.predef, f.tok, f); + var etran = GetExpressionTranslator(f, ordinaryEtran, out var additionalRequires, out var heapParameters); // parameters of the procedure var typeInParams = generator.MkTyParamFormals(GetTypeParams(f), true); - var inParams = GetParameters(f, etran); + var procedureParameters = GetParameters(f, etran); var outParams = GetWellformednessProcedureOutParameters(f, etran); - var requires = GetWellformednessProcedureRequires(f, etran, prevHeap, ordinaryEtran, currHeap); - + var requires = GetWellformednessProcedureRequires(f, etran); + requires.AddRange(additionalRequires); + // modifies $Heap var mod = new List { ordinaryEtran.HeapCastToIdentifierExpr, @@ -57,7 +45,7 @@ public void Check(Function f) { var proc = new Procedure(f.tok, "CheckWellformed" + NameSeparator + f.FullSanitizedName, new List(), - Concat(Concat(typeInParams, inParams_Heap), inParams), outParams, + Concat(Concat(typeInParams, heapParameters), procedureParameters), outParams, false, requires, mod, ensures, etran.TrAttributes(f.Attributes, null)); AddVerboseNameAttribute(proc, f.FullDafnyName, MethodTranslationKind.SpecWellformedness); generator.sink.AddTopLevelDeclaration(proc); @@ -66,15 +54,12 @@ public void Check(Function f) { generator.InsertChecksum(f, proc, true); } - Contract.Assert(proc.InParams.Count == typeInParams.Count + inParams_Heap.Count + inParams.Count); + 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 implInParams = Bpl.Formal.StripWhereClauses(inParams); - var implOutParams = Bpl.Formal.StripWhereClauses(outParams); var locals = new List(); var builder = new BoogieStmtListBuilder(generator, generator.options); var builderInitializationArea = new BoogieStmtListBuilder(generator, generator.options); - builder.Add(new CommentCmd("AddWellformednessCheck for function " + f)); if (f is TwoStateFunction) { // $Heap := current$Heap; var heap = ordinaryEtran.HeapCastToIdentifierExpr; @@ -112,12 +97,13 @@ public void Check(Function f) { // assume each one of them. After all that (in particular, after assuming all // of them), do the postponed reads checks. delayer.DoWithDelayedReadsChecks(false, wfo => { - foreach (AttributedExpression p in f.Req) { - if (p.Label != null) { - p.Label.E = (f is TwoStateFunction ? ordinaryEtran : etran.Old).TrExpr(p.E); - generator.CheckWellformed(p.E, wfo, locals, builder, etran); + builder.Add(new CommentCmd("Check Wfness of preconditions, and then assume them")); + foreach (AttributedExpression require in f.Req) { + if (require.Label != null) { + require.Label.E = (f is TwoStateFunction ? ordinaryEtran : etran.Old).TrExpr(require.E); + generator.CheckWellformed(require.E, wfo, locals, builder, etran); } else { - generator.CheckWellformedAndAssume(p.E, wfo, locals, builder, etran, "requires clause"); + generator.CheckWellformedAndAssume(require.E, wfo, locals, builder, etran, "requires clause"); } } }); @@ -126,20 +112,57 @@ public void Check(Function f) { // the preconditions. In other words, the well-formedness of the reads clause is // allowed to assume the precondition (yet, the requires clause is checked to // read only those things indicated in the reads clause). + builder.Add(new CommentCmd("Check Wfness of the reads clause")); delayer.DoWithDelayedReadsChecks(false, wfo => { generator.CheckFrameWellFormed(wfo, f.Reads.Expressions, locals, builder, etran); }); - // 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"); - generator.CheckFrameEmpty(f.tok, etran, etran.ReadsFrame(f.tok), builder, desc, null); - } + ConcurrentAttributeCheck(f, etran, builder); // check well-formedness of the decreases clauses (including termination, but no reads checks) + builder.Add(new CommentCmd("Check Wfness of the decreases clause")); foreach (Expression p in f.Decreases.Expressions) { generator.CheckWellformed(p, new WFOptions(null, false), locals, builder, etran); } + + var implementationParameters = Bpl.Formal.StripWhereClauses(procedureParameters); + CheckBodyAndEnsuresClauses(f, etran, locals, implementationParameters, builderInitializationArea, builder); + + if (generator.EmitImplementation(f.Attributes)) { + var s0 = builderInitializationArea.Collect(f.tok); + var s1 = builder.Collect(f.tok); + var implBody = new StmtList(new List(s0.BigBlocks.Concat(s1.BigBlocks)), f.tok); + + // emit the impl only when there are proof obligations. + QKeyValue kv = etran.TrAttributes(f.Attributes, null); + var parameters = Concat(Concat(Bpl.Formal.StripWhereClauses(typeInParams), heapParameters), implementationParameters); + var impl = generator.AddImplementationWithAttributes(generator.GetToken(f), proc, + parameters, + Bpl.Formal.StripWhereClauses(outParams), + locals, implBody, kv); + if (generator.InsertChecksums) { + generator.InsertChecksum(f, impl); + } + } + + Contract.Assert(generator.currentModule == f.EnclosingClass.EnclosingModuleDefinition); + Contract.Assert(generator.codeContext == f); + generator.Reset(); + } + // TODO should be moved so its injected + 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"); + generator.CheckFrameEmpty(f.tok, etran, etran.ReadsFrame(f.tok), builder, desc, null); + } + } + + private void CheckBodyAndEnsuresClauses(Function f, ExpressionTranslator etran, List locals, List inParams, + BoogieStmtListBuilder builderInitializationArea, BoogieStmtListBuilder builder) + { + builder.Add(new CommentCmd("Check body and ensures clauses")); // Generate: // if (*) { // check well-formedness of postcondition @@ -149,128 +172,157 @@ public void Check(Function f) { // // fall through to check the postconditions themselves // } // Here go the postconditions (termination checks included, but no reads checks) - BoogieStmtListBuilder postCheckBuilder = new BoogieStmtListBuilder(generator, generator.options); - // Assume the type returned by the call itself respects its type (this matters if the type is "nat", for example) - { - var args = new List(); - foreach (var p in GetTypeParams(f)) { - args.Add(generator.TrTypeParameter(p)); - } + var postCheckBuilder = GetPostCheckBuilder(f, etran, locals); - if (f.IsFuelAware()) { - args.Add(etran.layerInterCluster.GetFunctionFuel(f)); - } - - if (f.IsOpaque || f.IsMadeImplicitlyOpaque(generator.options)) { - args.Add(generator.GetRevealConstant(f)); - } - - if (f is TwoStateFunction) { - args.Add(etran.Old.HeapExpr); - } - - if (f.ReadsHeap) { - args.Add(etran.HeapExpr); - } - - if (!f.IsStatic) { - args.Add(new Bpl.IdentifierExpr(f.tok, etran.This)); - } - - foreach (var p in f.Ins) { - args.Add(new Bpl.IdentifierExpr(p.tok, p.AssignUniqueName(f.IdGenerator), generator.TrType(p.Type))); - } - - Bpl.IdentifierExpr funcID = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName, generator.TrType(f.ResultType)); - Expr funcAppl = new NAryExpr(f.tok, new FunctionCall(funcID), args); + // Here goes the body (and include both termination checks and reads checks) + var bodyCheckBuilder = GetBodyCheckBuilder(f, etran, inParams, locals, builderInitializationArea); - var wh = generator.GetWhereClause(f.tok, funcAppl, f.ResultType, etran, NOALLOC); - if (wh != null) { - postCheckBuilder.Add(TrAssumeCmd(f.tok, wh)); - } - } - // Now for the ensures clauses - foreach (AttributedExpression p in f.Ens) { - // assume the postcondition for the benefit of checking the remaining postconditions - generator.CheckWellformedAndAssume(p.E, new WFOptions(f, false), locals, postCheckBuilder, etran, "ensures clause"); - } + // Combine the two, letting the postcondition be checked on after the "bodyCheckBuilder" branch + builder.Add(new IfCmd(f.tok, null, postCheckBuilder.Collect(f.tok), null, bodyCheckBuilder.Collect(f.tok))); + } - // Here goes the body (and include both termination checks and reads checks) - BoogieStmtListBuilder bodyCheckBuilder = new BoogieStmtListBuilder(generator, generator.options); + private BoogieStmtListBuilder GetBodyCheckBuilder(Function f, ExpressionTranslator etran, + List parameters, + List locals, BoogieStmtListBuilder builderInitializationArea) + { + var selfCall = GetSelfCall(f, etran, parameters); + var bodyCheckBuilder = new BoogieStmtListBuilder(generator, generator.options); + bodyCheckBuilder.Add(new CommentCmd("Check Wfness of body, result subset type constraint, and return to check the postcondition")); if (f.Body == null || !generator.RevealedInScope(f)) { // don't fall through to postcondition checks bodyCheckBuilder.Add(TrAssumeCmd(f.tok, Expr.False)); } else { - var funcID = new FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName, generator.TrType(f.ResultType))); - var args = new List(); - foreach (var p in GetTypeParams(f)) { - args.Add(generator.TrTypeParameter(p)); - } - - if (f.IsFuelAware()) { - args.Add(etran.layerInterCluster.GetFunctionFuel(f)); - } - - if (f.IsOpaque || f.IsMadeImplicitlyOpaque(generator.options)) { - args.Add(generator.GetRevealConstant(f)); - } - - if (f is TwoStateFunction) { - args.Add(etran.Old.HeapExpr); - } - - if (f.ReadsHeap) { - args.Add(etran.HeapExpr); - } - - foreach (Variable p in implInParams) { - args.Add(new Bpl.IdentifierExpr(f.tok, p)); - } - - Expr funcAppl = new NAryExpr(f.tok, funcID, args); - var bodyCheckDelayer = new ReadsCheckDelayer(etran, null, locals, builderInitializationArea, bodyCheckBuilder); bodyCheckDelayer.DoWithDelayedReadsChecks(false, wfo => { - generator.CheckWellformedWithResult(f.Body, wfo, funcAppl, f.ResultType, locals, bodyCheckBuilder, etran, + generator.CheckWellformedWithResult(f.Body, wfo, selfCall, f.ResultType, locals, bodyCheckBuilder, etran, "function call result"); if (f.Result != null) { - var cmd = TrAssumeCmd(f.tok, Expr.Eq(funcAppl, generator.TrVar(f.tok, f.Result))); + var cmd = TrAssumeCmd(f.tok, Expr.Eq(selfCall, generator.TrVar(f.tok, f.Result))); generator.proofDependencies?.AddProofDependencyId(cmd, f.tok, new FunctionDefinitionDependency(f)); bodyCheckBuilder.Add(cmd); } }); // Enforce 'older' conditions - var (olderParameterCount, olderCondition) = generator.OlderCondition(f, funcAppl, implInParams); + var (olderParameterCount, olderCondition) = generator.OlderCondition(f, selfCall, parameters); if (olderParameterCount != 0) { bodyCheckBuilder.Add(generator.Assert(f.tok, olderCondition, new PODesc.IsOlderProofObligation(olderParameterCount, f.Ins.Count + (f.IsStatic ? 0 : 1)))); } } - // Combine the two, letting the postcondition be checked on after the "bodyCheckBuilder" branch - postCheckBuilder.Add(TrAssumeCmd(f.tok, Expr.False)); - builder.Add(new IfCmd(f.tok, null, postCheckBuilder.Collect(f.tok), null, bodyCheckBuilder.Collect(f.tok))); + return bodyCheckBuilder; + } - var s0 = builderInitializationArea.Collect(f.tok); - var s1 = builder.Collect(f.tok); - var implBody = new StmtList(new List(s0.BigBlocks.Concat(s1.BigBlocks)), f.tok); + private Expr GetSelfCall(Function f, ExpressionTranslator etran, List parameters) + { + var funcId = new FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName, generator.TrType(f.ResultType))); + var args = new List(); + foreach (var p in GetTypeParams(f)) { + args.Add(generator.TrTypeParameter(p)); + } - if (generator.EmitImplementation(f.Attributes)) { - // emit the impl only when there are proof obligations. - QKeyValue kv = etran.TrAttributes(f.Attributes, null); - var impl = generator.AddImplementationWithAttributes(generator.GetToken(f), proc, - Concat(Concat(Bpl.Formal.StripWhereClauses(typeInParams), inParams_Heap), implInParams), - implOutParams, - locals, implBody, kv); - if (generator.InsertChecksums) { - generator.InsertChecksum(f, impl); - } + if (f.IsFuelAware()) { + args.Add(etran.layerInterCluster.GetFunctionFuel(f)); } - Contract.Assert(generator.currentModule == f.EnclosingClass.EnclosingModuleDefinition); - Contract.Assert(generator.codeContext == f); - generator.Reset(); + if (f.IsOpaque || f.IsMadeImplicitlyOpaque(generator.options)) { + args.Add(generator.GetRevealConstant(f)); + } + + if (f is TwoStateFunction) { + args.Add(etran.Old.HeapExpr); + } + + if (f.ReadsHeap) { + args.Add(etran.HeapExpr); + } + + foreach (Variable parameter in parameters) { + args.Add(new Bpl.IdentifierExpr(f.tok, parameter)); + } + + Expr funcAppl = new NAryExpr(f.tok, funcId, args); + return funcAppl; + } + + private BoogieStmtListBuilder GetPostCheckBuilder(Function f, ExpressionTranslator etran, List locals) + { + var postCheckBuilder = new BoogieStmtListBuilder(generator, generator.options); + postCheckBuilder.Add(new CommentCmd("Check Wfness of postcondition and assume false")); + + // Assume the type returned by the call itself respects its type (this matters if the type is "nat", for example) + var args = new List(); + foreach (var p in GetTypeParams(f)) { + args.Add(generator.TrTypeParameter(p)); + } + + if (f.IsFuelAware()) { + args.Add(etran.layerInterCluster.GetFunctionFuel(f)); + } + + if (f.IsOpaque || f.IsMadeImplicitlyOpaque(generator.options)) { + args.Add(generator.GetRevealConstant(f)); + } + + if (f is TwoStateFunction) { + args.Add(etran.Old.HeapExpr); + } + + if (f.ReadsHeap) { + args.Add(etran.HeapExpr); + } + + if (!f.IsStatic) { + args.Add(new Bpl.IdentifierExpr(f.tok, etran.This)); + } + + foreach (var p in f.Ins) { + args.Add(new Bpl.IdentifierExpr(p.tok, p.AssignUniqueName(f.IdGenerator), generator.TrType(p.Type))); + } + + Bpl.IdentifierExpr funcID = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName, generator.TrType(f.ResultType)); + Expr funcAppl = new NAryExpr(f.tok, new FunctionCall(funcID), args); + + var wh = generator.GetWhereClause(f.tok, funcAppl, f.ResultType, etran, NOALLOC); + if (wh != null) { + postCheckBuilder.Add(TrAssumeCmd(f.tok, wh)); + } + // Now for the ensures clauses + foreach (AttributedExpression p in f.Ens) { + // assume the postcondition for the benefit of checking the remaining postconditions + generator.CheckWellformedAndAssume(p.E, new WFOptions(f, false), locals, postCheckBuilder, etran, "ensures clause"); + } + + postCheckBuilder.Add(TrAssumeCmd(f.tok, Expr.False)); + return postCheckBuilder; + } + + private ExpressionTranslator GetExpressionTranslator(Function f, ExpressionTranslator ordinaryEtran, + out List additionalRequires, out List inParams_Heap) + { + ExpressionTranslator etran; + additionalRequires = new(); + inParams_Heap = new List(); + if (f is TwoStateFunction) { + var prevHeapVar = new Bpl.Formal(f.tok, new TypedIdent(f.tok, "previous$Heap", generator.predef.HeapType), true); + var currHeapVar = new Bpl.Formal(f.tok, new TypedIdent(f.tok, "current$Heap", generator.predef.HeapType), true); + inParams_Heap.Add(prevHeapVar); + inParams_Heap.Add(currHeapVar); + Expr prevHeap = new Bpl.IdentifierExpr(f.tok, prevHeapVar); + Expr currHeap = new Bpl.IdentifierExpr(f.tok, currHeapVar); + etran = new ExpressionTranslator(generator, generator.predef, currHeap, prevHeap, f); + + // free requires prevHeap == Heap && HeapSucc(prevHeap, currHeap) && IsHeap(currHeap) + var a0 = Expr.Eq(prevHeap, ordinaryEtran.HeapExpr); + var a1 = generator.HeapSucc(prevHeap, currHeap); + var a2 = generator.FunctionCall(f.tok, BuiltinFunction.IsGoodHeap, null, currHeap); + additionalRequires.Add(generator.Requires(f.tok, true, null, BplAnd(a0, BplAnd(a1, a2)), null, null, null)); + } else { + etran = ordinaryEtran; + } + + return etran; } private List GetWellformednessProcedureOutParameters(Function f, ExpressionTranslator etran) { @@ -306,19 +358,10 @@ private List GetWellformnessProcedureEnsures(Function f, ExpressionTran return ens; } - private List GetWellformednessProcedureRequires(Function f, ExpressionTranslator etran, - Expr prevHeap, - ExpressionTranslator ordinaryEtran, Expr currHeap) { + private List GetWellformednessProcedureRequires(Function f, ExpressionTranslator etran) { var requires = new List(); // free requires mh == ModuleContextHeight && fh == FunctionContextHeight; requires.Add(generator.Requires(f.tok, true, null, etran.HeightContext(f), null, null, null)); - if (f is TwoStateFunction) { - // free requires prevHeap == Heap && HeapSucc(prevHeap, currHeap) && IsHeap(currHeap) - var a0 = Expr.Eq(prevHeap, ordinaryEtran.HeapExpr); - var a1 = generator.HeapSucc(prevHeap, currHeap); - var a2 = generator.FunctionCall(f.tok, BuiltinFunction.IsGoodHeap, null, currHeap); - requires.Add(generator.Requires(f.tok, true, null, BplAnd(a0, BplAnd(a1, a2)), null, null, null)); - } foreach (var typeBoundAxiom in generator.TypeBoundAxioms(f.tok, Concat(f.EnclosingClass.TypeArgs, f.TypeArgs))) { requires.Add(generator.Requires(f.tok, true, null, typeBoundAxiom, null, null, null)); From 3e142d9435ab24b691196baefe3919c2bd43a825 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 26 Jul 2024 22:36:05 +0200 Subject: [PATCH 06/63] Add test --- .../LitTest/ast/expressions/statementExpressionScope.dfy | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy index c420b478a94..0fed01f610c 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy @@ -13,6 +13,12 @@ function StatementExpressionAssumeAndFunctionEnsures(): int assume false; 10 } +function StatementExpressionAssumeAndFunctionEnsures(): int + ensures 10 == 11 // error, since the assume false does not leak +{ + var x := (assume false; 10); x +} + function StatementExpressionAndSubsetResult(): nat // no error, since the statement expression can be used for the return type subset constraint { From 2c93cf48f3021e08b3ef096c7cd12432b83c0016 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 26 Jul 2024 22:39:55 +0200 Subject: [PATCH 07/63] Move function ensures clause check from procedure ensures clause to implementation assertion --- ...oogieGenerator.Functions.Wellformedness.cs | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs index ac26ff06192..a344b9120a4 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs @@ -41,12 +41,11 @@ public void Check(Function f) { var mod = new List { ordinaryEtran.HeapCastToIdentifierExpr, }; - var ensures = GetWellformnessProcedureEnsures(f, etran); var proc = new Procedure(f.tok, "CheckWellformed" + NameSeparator + f.FullSanitizedName, new List(), Concat(Concat(typeInParams, heapParameters), procedureParameters), outParams, - false, requires, mod, ensures, etran.TrAttributes(f.Attributes, null)); + false, requires, mod, new List(), etran.TrAttributes(f.Attributes, null)); AddVerboseNameAttribute(proc, f.FullDafnyName, MethodTranslationKind.SpecWellformedness); generator.sink.AddTopLevelDeclaration(proc); @@ -202,6 +201,20 @@ private BoogieStmtListBuilder GetBodyCheckBuilder(Function f, ExpressionTranslat bodyCheckBuilder.Add(cmd); } }); + + foreach (AttributedExpression e in f.Ens) { + var functionHeight = generator.currentModule.CallGraph.GetSCCRepresentativePredecessorCount(f); + var splits = new List(); + bool splitHappened /*we actually don't care*/ = + generator.TrSplitExpr(e.E, splits, true, functionHeight, true, true, etran); + var (errorMessage, successMessage) = generator.CustomErrorMessage(e.Attributes); + foreach (var s in splits) { + if (s.IsChecked && !RefinementToken.IsInherited(s.Tok, generator.currentModule)) { + var ensures = generator.EnsuresWithDependencies(s.Tok, false, e.E, s.E, errorMessage, successMessage, null); + bodyCheckBuilder.Add(new AssertCmd(ensures.tok, ensures.Condition, ensures.Description, ensures.Attributes)); + } + } + } // Enforce 'older' conditions var (olderParameterCount, olderCondition) = generator.OlderCondition(f, selfCall, parameters); @@ -340,24 +353,6 @@ private List GetWellformednessProcedureOutParameters(Function f, Expre return outParams; } - private List GetWellformnessProcedureEnsures(Function f, ExpressionTranslator etran) { - var ens = new List(); - foreach (AttributedExpression p in f.Ens) { - var functionHeight = generator.currentModule.CallGraph.GetSCCRepresentativePredecessorCount(f); - var splits = new List(); - bool splitHappened /*we actually don't care*/ = - generator.TrSplitExpr(p.E, splits, true, functionHeight, true, true, etran); - var (errorMessage, successMessage) = generator.CustomErrorMessage(p.Attributes); - foreach (var s in splits) { - if (s.IsChecked && !RefinementToken.IsInherited(s.Tok, generator.currentModule)) { - generator.AddEnsures(ens, generator.EnsuresWithDependencies(s.Tok, false, p.E, s.E, errorMessage, successMessage, null)); - } - } - } - - return ens; - } - private List GetWellformednessProcedureRequires(Function f, ExpressionTranslator etran) { var requires = new List(); // free requires mh == ModuleContextHeight && fh == FunctionContextHeight; From c6c2bc397437e5eceb8d91e1000104f43e05c396 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 26 Jul 2024 23:01:33 +0200 Subject: [PATCH 08/63] Prepare for injecting the function postcondition into the StmtExpr PathAsideBlock --- .../BoogieGenerator.ExpressionWellformed.cs | 81 ++++++++++--------- ...oogieGenerator.Functions.Wellformedness.cs | 8 +- .../Verifier/BoogieGenerator.TrStatement.cs | 6 +- .../Verifier/BoogieStmtListBuilder.cs | 2 +- 4 files changed, 56 insertions(+), 41 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 917aee73bf9..01f812e4256 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -251,7 +251,7 @@ void CheckWellformed(Expression expr, WFOptions options, List locals, Contract.Requires(builder != null); Contract.Requires(etran != null); Contract.Requires(predef != null); - CheckWellformedWithResult(expr, options, null, null, locals, builder, etran); + CheckWellformedWithResult(expr, options, null, locals, builder, etran); } /// @@ -261,12 +261,11 @@ void CheckWellformed(Expression expr, WFOptions options, List locals, /// assume the equivalent of "result == expr". /// See class WFOptions for descriptions of the specified options. /// - void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Bpl.Expr result, Type resultType, - List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, - string resultDescription = null) { + void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, + Action checkPostcondition, + List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { Contract.Requires(expr != null); Contract.Requires(wfOptions != null); - Contract.Requires((result == null) == (resultType == null)); Contract.Requires(locals != null); Contract.Requires(builder != null); Contract.Requires(etran != null); @@ -283,7 +282,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Bpl.Expr re switch (expr) { case StaticReceiverExpr stexpr: { if (stexpr.ObjectToDiscard != null) { - CheckWellformedWithResult(stexpr.ObjectToDiscard, wfOptions, null, null, locals, builder, etran); + CheckWellformedWithResult(stexpr.ObjectToDiscard, wfOptions, null, locals, builder, etran); } break; @@ -1135,8 +1134,8 @@ void CheckOperand(Expression operand) { break; } case LetExpr letExpr: - CheckWellformedLetExprWithResult(letExpr, wfOptions, result, resultType, locals, builder, etran, true); - result = null; + CheckWellformedLetExprWithResult(letExpr, wfOptions, checkPostcondition, resultType, locals, builder, etran, true); + checkPostcondition = null; break; case ComprehensionExpr comprehensionExpr: { var e = comprehensionExpr; @@ -1211,7 +1210,7 @@ void CheckOperand(Expression operand) { resultIe = new Bpl.IdentifierExpr(body.tok, resultVar); rangeType = lam.Type.AsArrowType.Result; } - CheckWellformedWithResult(body, newOptions, resultIe, rangeType, locals, b, comprehensionEtran, "lambda expression"); + CheckWellformedWithResult(body, newOptions, resultIe, locals, b, comprehensionEtran); }); if (mc != null) { @@ -1262,8 +1261,10 @@ void CheckOperand(Expression operand) { break; } case StmtExpr stmtExpr: - CheckWellformedStmtExpr(stmtExpr, wfOptions, result, resultType, locals, builder, etran); - result = null; + var bodyBuilder = new BoogieStmtListBuilder(this, builder.Options); + PathAsideBlock(stmtExpr.tok, bodyBuilder, builder); + CheckWellformedStmtExpr(stmtExpr, wfOptions, checkPostcondition, locals, bodyBuilder, etran); + checkPostcondition = null; break; case ITEExpr iteExpr: { ITEExpr e = iteExpr; @@ -1276,17 +1277,17 @@ void CheckOperand(Expression operand) { // has already been checked in e.Test var letExpr = (LetExpr)e.Thn; Contract.Assert(letExpr != null); - CheckWellformedLetExprWithResult(letExpr, wfOptions, result, resultType, locals, bThen, etran, false); + CheckWellformedLetExprWithResult(letExpr, wfOptions, checkPostcondition, resultType, locals, bThen, etran, false); } else { - CheckWellformedWithResult(e.Thn, wfOptions, result, resultType, locals, bThen, etran, "if expression then branch"); + CheckWellformedWithResult(e.Thn, wfOptions, checkPostcondition, locals, bThen, etran); } - CheckWellformedWithResult(e.Els, wfOptions, result, resultType, locals, bElse, etran, "if expression else branch"); + CheckWellformedWithResult(e.Els, wfOptions, checkPostcondition, locals, bElse, etran); builder.Add(new Bpl.IfCmd(iteExpr.tok, etran.TrExpr(e.Test), bThen.Collect(iteExpr.tok), null, bElse.Collect(iteExpr.tok))); - result = null; + checkPostcondition = null; break; } case MatchExpr matchExpr: - result = TrMatchExpr(matchExpr, wfOptions, result, resultType, locals, builder, etran); + checkPostcondition = TrMatchExpr(matchExpr, wfOptions, checkPostcondition, resultType, locals, builder, etran); resultDescription = "match expression"; break; case DatatypeUpdateExpr updateExpr: { @@ -1304,19 +1305,19 @@ void CheckOperand(Expression operand) { CheckNotGhostVariant(e.InCompiledContext, updateExpr, e.Root, "update of", e.Members, e.LegalSourceConstructors, builder, etran); - CheckWellformedWithResult(e.ResolvedExpression, wfOptions, result, resultType, locals, builder, etran, resultDescription); - result = null; + CheckWellformedWithResult(e.ResolvedExpression, wfOptions, checkPostcondition, locals, builder, etran); + checkPostcondition = null; break; } case ConcreteSyntaxExpression expression: { var e = expression; - CheckWellformedWithResult(e.ResolvedExpression, wfOptions, result, resultType, locals, builder, etran, resultDescription); - result = null; + CheckWellformedWithResult(e.ResolvedExpression, wfOptions, checkPostcondition, locals, builder, etran); + checkPostcondition = null; break; } case NestedMatchExpr nestedMatchExpr: - CheckWellformedWithResult(nestedMatchExpr.Flattened, wfOptions, result, resultType, locals, builder, etran, resultDescription); - result = null; + CheckWellformedWithResult(nestedMatchExpr.Flattened, wfOptions, checkPostcondition, locals, builder, etran); + checkPostcondition = null; break; case BoogieFunctionCall call: { var e = call; @@ -1337,19 +1338,25 @@ void CheckOperand(Expression operand) { } - if (result != null) { - Contract.Assert(resultType != null); - var bResult = etran.TrExpr(expr); - CheckSubrange(expr.tok, bResult, expr.Type, resultType, expr, builder); - builder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, expr.tok, expr, - e => Bpl.Expr.Eq(result, AdaptBoxing(expr.tok, e, expr.Type, resultType)), - resultDescription)); - builder.Add(TrAssumeCmd(expr.tok, etran.CanCallAssumption(expr))); - builder.Add(new CommentCmd("CheckWellformedWithResult: any expression")); - builder.Add(TrAssumeCmd(expr.tok, MkIs(result, resultType))); + if (checkPostcondition != null) { + checkPostcondition(builder); } } + public void CheckSubsetType(ExpressionTranslator etran, Expression expr, Bpl.Expr selfCall, Type resultType, + BoogieStmtListBuilder builder, string resultDescription = null) { + + Contract.Assert(resultType != null); + var bResult = etran.TrExpr(expr); + CheckSubrange(expr.tok, bResult, expr.Type, resultType, expr, builder); + builder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, expr.tok, expr, + e => Bpl.Expr.Eq(selfCall, AdaptBoxing(expr.tok, e, expr.Type, resultType)), + resultDescription)); + builder.Add(TrAssumeCmd(expr.tok, etran.CanCallAssumption(expr))); + builder.Add(new CommentCmd("CheckWellformedWithResult: any expression")); + builder.Add(TrAssumeCmd(expr.tok, MkIs(selfCall, resultType))); + } + private Expr TrMatchExpr(MatchExpr me, WFOptions wfOptions, Expr result, Type resultType, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { FillMissingCases(me); @@ -1389,7 +1396,7 @@ private Expr TrMatchExpr(MatchExpr me, WFOptions wfOptions, Expr result, Type re BoogieStmtListBuilder b = new BoogieStmtListBuilder(this, options); Bpl.Expr ct = CtorInvocation(mc, me.Source.Type, etran, locals, b, NOALLOC, false); // generate: if (src == ctor(args)) { assume args-is-well-typed; mc.Body is well-formed; assume Result == TrExpr(case); } else ... - CheckWellformedWithResult(mc.Body, wfOptions, result, resultType, locals, b, etran, "match expression branch result"); + CheckWellformedWithResult(mc.Body, wfOptions, result, locals, b, etran); ifCmd = new Bpl.IfCmd(mc.tok, Bpl.Expr.Eq(src, ct), b.Collect(mc.tok), ifCmd, els); els = null; } @@ -1399,7 +1406,7 @@ private Expr TrMatchExpr(MatchExpr me, WFOptions wfOptions, Expr result, Type re return result; } - private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions options, Expr result, Type resultType, List locals, + private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions options, Action checkPostcondition, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { // If we're inside an "old" expression, then "etran" will know how to translate // expressions. However, here, we're also having to translate e.S, which is a @@ -1412,7 +1419,7 @@ private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions options, Expr TrStmt(stmtExpr.S, builder, locals, etran); } - CheckWellformedWithResult(stmtExpr.E, options, result, resultType, locals, builder, etran, "statement expression result"); + CheckWellformedWithResult(stmtExpr.E, options, checkPostcondition, locals, builder, etran); } /// @@ -1496,12 +1503,12 @@ void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, Bpl.Expr r var r = new Bpl.LocalVariable(pat.tok, new Bpl.TypedIdent(pat.tok, nm, TrType(pat.Expr.Type))); locals.Add(r); var rIe = new Bpl.IdentifierExpr(rhs.tok, r); - CheckWellformedWithResult(e.RHSs[i], wfOptions, rIe, pat.Expr.Type, locals, builder, etran, "let expression binding RHS well-formed"); + CheckWellformedWithResult(e.RHSs[i], wfOptions, rIe, locals, builder, etran); CheckCasePatternShape(pat, rhs, rIe, rhs.tok, pat.Expr.Type, builder); var substExpr = Substitute(pat.Expr, null, substMap); builder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, e.tok, substExpr, e => Bpl.Expr.Eq(e, rIe), "let expression binding")); } - CheckWellformedWithResult(Substitute(e.Body, null, substMap), wfOptions, result, resultType, locals, builder, etran, "let expression result"); + CheckWellformedWithResult(Substitute(e.Body, null, substMap), wfOptions, result, locals, builder, etran); } else { // CheckWellformed(var b :| RHS(b); Body(b)) = diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs index a344b9120a4..a44915f7c05 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.Contracts; @@ -193,8 +194,11 @@ private BoogieStmtListBuilder GetBodyCheckBuilder(Function f, ExpressionTranslat } else { var bodyCheckDelayer = new ReadsCheckDelayer(etran, null, locals, builderInitializationArea, bodyCheckBuilder); bodyCheckDelayer.DoWithDelayedReadsChecks(false, wfo => { - generator.CheckWellformedWithResult(f.Body, wfo, selfCall, f.ResultType, locals, bodyCheckBuilder, etran, - "function call result"); + + Action checkPostcondition = b => { + generator.CheckSubsetType(etran, f.Body, selfCall, f.ResultType, b, "variable declaration RHS"); + }; + generator.CheckWellformedWithResult(f.Body, wfo, checkPostcondition, locals, bodyCheckBuilder, etran); if (f.Result != null) { var cmd = TrAssumeCmd(f.tok, Expr.Eq(selfCall, generator.TrVar(f.tok, f.Result))); generator.proofDependencies?.AddProofDependencyId(cmd, f.tok, new FunctionDefinitionDependency(f)); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs index 25c7d142853..0dc8c0d2618 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs @@ -529,7 +529,11 @@ private void TranslateVariableDeclaration(BoogieStmtListBuilder builder, List checkPostcondition = b => { + CheckSubsetType(etran, rhs, boogieTupleReference, pat.Expr.Type, b, "variable declaration RHS"); + }; + CheckWellformedWithResult(rhs, new WFOptions(null, false, false), checkPostcondition, locals, builder, etran); CheckCasePatternShape(pat, rhs, boogieTupleReference, rhs.tok, pat.Expr.Type, builder); builder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, varDeclPattern.tok, pat.Expr, e => Expr.Eq(e, boogieTupleReference), "variable declaration")); } diff --git a/Source/DafnyCore/Verifier/BoogieStmtListBuilder.cs b/Source/DafnyCore/Verifier/BoogieStmtListBuilder.cs index f3f3b247447..0d826b98c10 100644 --- a/Source/DafnyCore/Verifier/BoogieStmtListBuilder.cs +++ b/Source/DafnyCore/Verifier/BoogieStmtListBuilder.cs @@ -2,7 +2,7 @@ using Microsoft.Boogie; namespace Microsoft.Dafny { - internal class BoogieStmtListBuilder { + public class BoogieStmtListBuilder { public DafnyOptions Options { get; } public StmtListBuilder builder; public BoogieGenerator tran; From 1ecd423c964280e01c458aacdad609db36ada524 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Sat, 27 Jul 2024 09:09:09 +0200 Subject: [PATCH 09/63] Change with bugs --- .../BoogieGenerator.ExpressionWellformed.cs | 297 +++++++++--------- ...oogieGenerator.Functions.Wellformedness.cs | 44 +-- .../Verifier/BoogieGenerator.TrStatement.cs | 11 +- 3 files changed, 191 insertions(+), 161 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 01f812e4256..1944c076b2b 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -1134,132 +1134,13 @@ void CheckOperand(Expression operand) { break; } case LetExpr letExpr: - CheckWellformedLetExprWithResult(letExpr, wfOptions, checkPostcondition, resultType, locals, builder, etran, true); + CheckWellformedLetExprWithResult(letExpr, wfOptions, checkPostcondition, locals, builder, etran, true); checkPostcondition = null; break; case ComprehensionExpr comprehensionExpr: { - var e = comprehensionExpr; - var lam = e as LambdaExpr; - var mc = e as MapComprehension; - if (mc is { IsGeneralMapComprehension: false }) { - mc = null; // mc will be non-null when "e" is a general map comprehension - } - - // This is a WF check, so we look at the original quantifier, not the split one. - // This ensures that cases like forall x :: x != null && f(x.a) do not fail to verify. - - builder.Add(new Bpl.CommentCmd("Begin Comprehension WF check")); - BplIfIf(e.tok, lam != null, null, builder, nextBuilder => { - var comprehensionEtran = etran; - if (lam != null) { - // Havoc heap - locals.Add(BplLocalVar(CurrentIdGenerator.FreshId((etran.UsesOldHeap ? "$Heap_at_" : "") + "$lambdaHeap#"), predef.HeapType, out var lambdaHeap)); - comprehensionEtran = new ExpressionTranslator(comprehensionEtran, lambdaHeap); - nextBuilder.Add(new HavocCmd(expr.tok, Singleton((Bpl.IdentifierExpr)comprehensionEtran.HeapExpr))); - nextBuilder.Add(new AssumeCmd(expr.tok, FunctionCall(expr.tok, BuiltinFunction.IsGoodHeap, null, comprehensionEtran.HeapExpr))); - nextBuilder.Add(new AssumeCmd(expr.tok, HeapSameOrSucc(etran.HeapExpr, comprehensionEtran.HeapExpr))); - } - - var substMap = SetupBoundVarsAsLocals(e.BoundVars, out var typeAntecedents, nextBuilder, locals, comprehensionEtran); - BplIfIf(e.tok, true, typeAntecedents, nextBuilder, newBuilder => { - var s = new Substituter(null, substMap, new Dictionary()); - var body = Substitute(e.Term, null, substMap); - var bodyLeft = mc != null ? Substitute(mc.TermLeft, null, substMap) : null; - var substMapPrime = mc != null ? SetupBoundVarsAsLocals(e.BoundVars, newBuilder, locals, comprehensionEtran, "#prime") : null; - var bodyLeftPrime = mc != null ? Substitute(mc.TermLeft, null, substMapPrime) : null; - var bodyPrime = mc != null ? Substitute(e.Term, null, substMapPrime) : null; - List reads = null; - - var newOptions = wfOptions; - if (lam != null) { - // Set up a new frame - var frameName = CurrentIdGenerator.FreshId("$_Frame#l"); - reads = lam.Reads.Expressions.ConvertAll(s.SubstFrameExpr); - comprehensionEtran = comprehensionEtran.WithReadsFrame(frameName, lam); - DefineFrame(e.tok, comprehensionEtran.ReadsFrame(e.tok), reads, newBuilder, locals, frameName, comprehensionEtran); - - // Check frame WF and that it read covers itself - var delayer = new ReadsCheckDelayer(comprehensionEtran, wfOptions.SelfCallsAllowance, locals, builder, newBuilder); - delayer.DoWithDelayedReadsChecks(false, wfo => { - CheckFrameWellFormed(wfo, reads, locals, newBuilder, comprehensionEtran); - }); - - // continue doing reads checks, but don't delay them - newOptions = new WFOptions(wfOptions.SelfCallsAllowance, true, false); - } - - // check requires/range - Bpl.Expr guard = null; - if (e.Range != null) { - var range = Substitute(e.Range, null, substMap); - CheckWellformed(range, newOptions, locals, newBuilder, comprehensionEtran); - guard = comprehensionEtran.TrExpr(range); - } - - if (mc != null) { - Contract.Assert(bodyLeft != null); - BplIfIf(e.tok, guard != null, guard, newBuilder, b => { CheckWellformed(bodyLeft, newOptions, locals, b, comprehensionEtran); }); - } - BplIfIf(e.tok, guard != null, guard, newBuilder, b => { - Bpl.Expr resultIe = null; - Type rangeType = null; - 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); - resultIe = new Bpl.IdentifierExpr(body.tok, resultVar); - rangeType = lam.Type.AsArrowType.Result; - } - CheckWellformedWithResult(body, newOptions, resultIe, locals, b, comprehensionEtran); - }); - - if (mc != null) { - Contract.Assert(substMapPrime != null); - Contract.Assert(bodyLeftPrime != null); - Contract.Assert(bodyPrime != null); - Bpl.Expr guardPrime = null; - if (guard != null) { - Contract.Assert(e.Range != null); - var rangePrime = Substitute(e.Range, null, substMapPrime); - guardPrime = comprehensionEtran.TrExpr(rangePrime); - } - BplIfIf(e.tok, guard != null, BplAnd(guard, guardPrime), newBuilder, b => { - 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))); - }); - } - }); - - if (lam != null) { - // assume false (heap was havoced inside an if) - Contract.Assert(nextBuilder != builder); - nextBuilder.Add(new AssumeCmd(e.tok, Bpl.Expr.False)); - } - }); - - bool needTypeConstraintCheck; - if (lam == null) { - needTypeConstraintCheck = true; - } else { - // omit constraint check if the type is according to the syntax of the expression - var arrowType = (UserDefinedType)e.Type.NormalizeExpandKeepConstraints(); - if (ArrowType.IsPartialArrowTypeName(arrowType.Name)) { - needTypeConstraintCheck = lam.Reads.Expressions.Count != 0; - } else if (ArrowType.IsTotalArrowTypeName(arrowType.Name)) { - needTypeConstraintCheck = lam.Reads.Expressions.Count != 0 || lam.Range != null; - } else { - needTypeConstraintCheck = true; - } - } - if (needTypeConstraintCheck) { - CheckResultToBeInType(e.tok, e, e.Type, locals, builder, etran); - } - - builder.Add(new Bpl.CommentCmd("End Comprehension WF check")); - break; - } + CheckWellformedComprehensionExpression(wfOptions, locals, builder, etran, comprehensionExpr); + break; + } case StmtExpr stmtExpr: var bodyBuilder = new BoogieStmtListBuilder(this, builder.Options); PathAsideBlock(stmtExpr.tok, bodyBuilder, builder); @@ -1277,7 +1158,7 @@ void CheckOperand(Expression operand) { // has already been checked in e.Test var letExpr = (LetExpr)e.Thn; Contract.Assert(letExpr != null); - CheckWellformedLetExprWithResult(letExpr, wfOptions, checkPostcondition, resultType, locals, bThen, etran, false); + CheckWellformedLetExprWithResult(letExpr, wfOptions, checkPostcondition, locals, bThen, etran, false); } else { CheckWellformedWithResult(e.Thn, wfOptions, checkPostcondition, locals, bThen, etran); } @@ -1287,8 +1168,8 @@ void CheckOperand(Expression operand) { break; } case MatchExpr matchExpr: - checkPostcondition = TrMatchExpr(matchExpr, wfOptions, checkPostcondition, resultType, locals, builder, etran); - resultDescription = "match expression"; + TrMatchExpr(matchExpr, wfOptions, checkPostcondition, locals, builder, etran); + checkPostcondition = null; break; case DatatypeUpdateExpr updateExpr: { var e = updateExpr; @@ -1343,6 +1224,134 @@ void CheckOperand(Expression operand) { } } + private void CheckWellformedComprehensionExpression(WFOptions wfOptions, List locals, + BoogieStmtListBuilder builder, ExpressionTranslator etran, ComprehensionExpr expr) { + var lam = expr as LambdaExpr; + var mc = expr as MapComprehension; + if (mc is { IsGeneralMapComprehension: false }) { + mc = null; // mc will be non-null when "e" is a general map comprehension + } + + // This is a WF check, so we look at the original quantifier, not the split one. + // This ensures that cases like forall x :: x != null && f(x.a) do not fail to verify. + + builder.Add(new Bpl.CommentCmd("Begin Comprehension WF check")); + BplIfIf(expr.tok, lam != null, null, builder, nextBuilder => { + var comprehensionEtran = etran; + if (lam != null) { + // Havoc heap + locals.Add(BplLocalVar(CurrentIdGenerator.FreshId((etran.UsesOldHeap ? "$Heap_at_" : "") + "$lambdaHeap#"), predef.HeapType, out var lambdaHeap)); + comprehensionEtran = new ExpressionTranslator(comprehensionEtran, lambdaHeap); + nextBuilder.Add(new HavocCmd(expr.tok, Singleton((Bpl.IdentifierExpr)comprehensionEtran.HeapExpr))); + nextBuilder.Add(new AssumeCmd(expr.tok, FunctionCall(expr.tok, BuiltinFunction.IsGoodHeap, null, comprehensionEtran.HeapExpr))); + nextBuilder.Add(new AssumeCmd(expr.tok, HeapSameOrSucc(etran.HeapExpr, comprehensionEtran.HeapExpr))); + } + + var substMap = SetupBoundVarsAsLocals(expr.BoundVars, out var typeAntecedents, nextBuilder, locals, comprehensionEtran); + BplIfIf(expr.tok, true, typeAntecedents, nextBuilder, newBuilder => { + var s = new Substituter(null, substMap, new Dictionary()); + var body = Substitute(expr.Term, null, substMap); + var bodyLeft = mc != null ? Substitute(mc.TermLeft, null, substMap) : null; + var substMapPrime = mc != null ? SetupBoundVarsAsLocals(expr.BoundVars, newBuilder, locals, comprehensionEtran, "#prime") : null; + var bodyLeftPrime = mc != null ? Substitute(mc.TermLeft, null, substMapPrime) : null; + var bodyPrime = mc != null ? Substitute(expr.Term, null, substMapPrime) : null; + List reads = null; + + var newOptions = wfOptions; + if (lam != null) { + // Set up a new frame + var frameName = CurrentIdGenerator.FreshId("$_Frame#l"); + reads = lam.Reads.Expressions.ConvertAll(s.SubstFrameExpr); + comprehensionEtran = comprehensionEtran.WithReadsFrame(frameName, lam); + DefineFrame(expr.tok, comprehensionEtran.ReadsFrame(expr.tok), reads, newBuilder, locals, frameName, comprehensionEtran); + + // Check frame WF and that it read covers itself + var delayer = new ReadsCheckDelayer(comprehensionEtran, wfOptions.SelfCallsAllowance, locals, builder, newBuilder); + delayer.DoWithDelayedReadsChecks(false, wfo => { + CheckFrameWellFormed(wfo, reads, locals, newBuilder, comprehensionEtran); + }); + + // continue doing reads checks, but don't delay them + newOptions = new WFOptions(wfOptions.SelfCallsAllowance, true, false); + } + + // check requires/range + Bpl.Expr guard = null; + if (expr.Range != null) { + var range = Substitute(expr.Range, null, substMap); + CheckWellformed(range, newOptions, locals, newBuilder, comprehensionEtran); + guard = comprehensionEtran.TrExpr(range); + } + + if (mc != null) { + Contract.Assert(bodyLeft != null); + BplIfIf(expr.tok, guard != null, guard, newBuilder, b => { CheckWellformed(bodyLeft, newOptions, locals, b, comprehensionEtran); }); + } + BplIfIf(expr.tok, guard != null, guard, newBuilder, b => { + Bpl.Expr resultIe = null; + Type rangeType = null; + 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); + resultIe = new Bpl.IdentifierExpr(body.tok, resultVar); + rangeType = lam.Type.AsArrowType.Result; + } + + Action checkPostcondition = builder => { + CheckSubsetType(etran, body, resultIe, rangeType, builder); + }; + CheckWellformedWithResult(body, newOptions, checkPostcondition, locals, b, comprehensionEtran); + }); + + if (mc != null) { + Contract.Assert(substMapPrime != null); + Contract.Assert(bodyLeftPrime != null); + Contract.Assert(bodyPrime != null); + Bpl.Expr guardPrime = null; + if (guard != null) { + Contract.Assert(expr.Range != null); + var rangePrime = Substitute(expr.Range, null, substMapPrime); + guardPrime = comprehensionEtran.TrExpr(rangePrime); + } + BplIfIf(expr.tok, guard != null, BplAnd(guard, guardPrime), newBuilder, b => { + 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))); + }); + } + }); + + if (lam != null) { + // assume false (heap was havoced inside an if) + Contract.Assert(nextBuilder != builder); + nextBuilder.Add(new AssumeCmd(expr.tok, Bpl.Expr.False)); + } + }); + + bool needTypeConstraintCheck; + if (lam == null) { + needTypeConstraintCheck = true; + } else { + // omit constraint check if the type is according to the syntax of the expression + var arrowType = (UserDefinedType)expr.Type.NormalizeExpandKeepConstraints(); + if (ArrowType.IsPartialArrowTypeName(arrowType.Name)) { + needTypeConstraintCheck = lam.Reads.Expressions.Count != 0; + } else if (ArrowType.IsTotalArrowTypeName(arrowType.Name)) { + needTypeConstraintCheck = lam.Reads.Expressions.Count != 0 || lam.Range != null; + } else { + needTypeConstraintCheck = true; + } + } + if (needTypeConstraintCheck) { + CheckResultToBeInType(expr.tok, expr, expr.Type, locals, builder, etran); + } + + builder.Add(new Bpl.CommentCmd("End Comprehension WF check")); + return; + } + public void CheckSubsetType(ExpressionTranslator etran, Expression expr, Bpl.Expr selfCall, Type resultType, BoogieStmtListBuilder builder, string resultDescription = null) { @@ -1357,7 +1366,7 @@ public void CheckSubsetType(ExpressionTranslator etran, Expression expr, Bpl.Exp builder.Add(TrAssumeCmd(expr.tok, MkIs(selfCall, resultType))); } - private Expr TrMatchExpr(MatchExpr me, WFOptions wfOptions, Expr result, Type resultType, List locals, + private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, Action checkPostcondition, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { FillMissingCases(me); @@ -1396,14 +1405,15 @@ private Expr TrMatchExpr(MatchExpr me, WFOptions wfOptions, Expr result, Type re BoogieStmtListBuilder b = new BoogieStmtListBuilder(this, options); Bpl.Expr ct = CtorInvocation(mc, me.Source.Type, etran, locals, b, NOALLOC, false); // generate: if (src == ctor(args)) { assume args-is-well-typed; mc.Body is well-formed; assume Result == TrExpr(case); } else ... - CheckWellformedWithResult(mc.Body, wfOptions, result, locals, b, etran); + + // TODO: + // resultDescription = "match expression"; + CheckWellformedWithResult(mc.Body, wfOptions, checkPostcondition, locals, b, etran); ifCmd = new Bpl.IfCmd(mc.tok, Bpl.Expr.Eq(src, ct), b.Collect(mc.tok), ifCmd, els); els = null; } builder.Add(ifCmd); - result = null; - return result; } private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions options, Action checkPostcondition, List locals, @@ -1490,7 +1500,7 @@ void CheckWellformedSpecialFunction(FunctionCallExpr expr, WFOptions options, Bp } } - void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, Bpl.Expr result, Type resultType, List locals, + void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, Action checkPostcondition, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, bool checkRhs) { if (e.Exact) { var substMap = SetupBoundVarsAsLocals(e.BoundVars.ToList(), builder, locals, etran, "#Z"); @@ -1503,12 +1513,16 @@ void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, Bpl.Expr r var r = new Bpl.LocalVariable(pat.tok, new Bpl.TypedIdent(pat.tok, nm, TrType(pat.Expr.Type))); locals.Add(r); var rIe = new Bpl.IdentifierExpr(rhs.tok, r); - CheckWellformedWithResult(e.RHSs[i], wfOptions, rIe, locals, builder, etran); + var unModifiedI = i; + Action checkPostconditionForLhs = innerBuilder => { + CheckSubsetType(etran, e.RHSs[unModifiedI], rIe, pat.Expr.Type, innerBuilder, "let expression binding RHS well-formed"); + }; + CheckWellformedWithResult(e.RHSs[i], wfOptions, checkPostconditionForLhs, locals, builder, etran); CheckCasePatternShape(pat, rhs, rIe, rhs.tok, pat.Expr.Type, builder); var substExpr = Substitute(pat.Expr, null, substMap); builder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, e.tok, substExpr, e => Bpl.Expr.Eq(e, rIe), "let expression binding")); } - CheckWellformedWithResult(Substitute(e.Body, null, substMap), wfOptions, result, locals, builder, etran); + CheckWellformedWithResult(Substitute(e.Body, null, substMap), wfOptions, checkPostcondition, locals, builder, etran); } else { // CheckWellformed(var b :| RHS(b); Body(b)) = @@ -1561,14 +1575,15 @@ void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, Bpl.Expr r var info = letSuchThatExprInfo[e]; builder.Add(new Bpl.AssumeCmd(e.tok, info.CanCallFunctionCall(this, etran))); // If we are supposed to assume "result" to equal this expression, then use the body of the let-such-that, not the generated $let#... function - if (result != null) { - Contract.Assert(resultType != null); - var bResult = etran.TrExpr(letBody); - CheckSubrange(letBody.tok, bResult, letBody.Type, resultType, letBody, builder); - builder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, e.tok, letBody, e => Expr.Eq(result, e), "let expression")); - builder.Add(TrAssumeCmd(letBody.tok, etran.CanCallAssumption(letBody))); - builder.Add(new CommentCmd("CheckWellformedWithResult: Let expression")); - builder.Add(TrAssumeCmd(letBody.tok, MkIs(result, resultType))); + if (checkPostcondition != null) { + checkPostcondition(builder); + // Contract.Assert(resultType != null); + // var bResult = etran.TrExpr(letBody); + // CheckSubrange(letBody.tok, bResult, letBody.Type, resultType, letBody, builder); + // builder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, e.tok, letBody, e => Expr.Eq(checkPostcondition, e), "let expression")); + // builder.Add(TrAssumeCmd(letBody.tok, etran.CanCallAssumption(letBody))); + // builder.Add(new CommentCmd("CheckWellformedWithResult: Let expression")); + // builder.Add(TrAssumeCmd(letBody.tok, MkIs(checkPostcondition, resultType))); } } } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs index a44915f7c05..ab6910cf6b5 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs @@ -188,37 +188,43 @@ private BoogieStmtListBuilder GetBodyCheckBuilder(Function f, ExpressionTranslat var selfCall = GetSelfCall(f, etran, parameters); var bodyCheckBuilder = new BoogieStmtListBuilder(generator, generator.options); bodyCheckBuilder.Add(new CommentCmd("Check Wfness of body, result subset type constraint, and return to check the postcondition")); - if (f.Body == null || !generator.RevealedInScope(f)) { - // don't fall through to postcondition checks - bodyCheckBuilder.Add(TrAssumeCmd(f.tok, Expr.False)); - } else { + if (f.Body != null && generator.RevealedInScope(f)) { var bodyCheckDelayer = new ReadsCheckDelayer(etran, null, locals, builderInitializationArea, bodyCheckBuilder); bodyCheckDelayer.DoWithDelayedReadsChecks(false, wfo => { - Action checkPostcondition = b => { - generator.CheckSubsetType(etran, f.Body, selfCall, f.ResultType, b, "variable declaration RHS"); + Contract.Assert(f.ResultType != null); + var bResult = etran.TrExpr(f.Body); + generator.CheckSubrange(f.Body.tok, bResult, f.Body.Type, f.ResultType, f.Body, b); }; generator.CheckWellformedWithResult(f.Body, wfo, checkPostcondition, locals, bodyCheckBuilder, etran); + bodyCheckBuilder.Add(generator.TrAssumeCmdWithDependenciesAndExtend(etran, f.Body.tok, f.Body, + e => Bpl.Expr.Eq(selfCall, generator.AdaptBoxing(f.Body.tok, e, f.Body.Type, f.ResultType)), + "function call result")); + bodyCheckBuilder.Add(TrAssumeCmd(f.Body.tok, etran.CanCallAssumption(f.Body))); + bodyCheckBuilder.Add(new CommentCmd("CheckWellformedWithResult: any expression")); + bodyCheckBuilder.Add(TrAssumeCmd(f.Body.tok, generator.MkIs(selfCall, f.ResultType))); if (f.Result != null) { var cmd = TrAssumeCmd(f.tok, Expr.Eq(selfCall, generator.TrVar(f.tok, f.Result))); generator.proofDependencies?.AddProofDependencyId(cmd, f.tok, new FunctionDefinitionDependency(f)); bodyCheckBuilder.Add(cmd); } - }); - - foreach (AttributedExpression e in f.Ens) { - var functionHeight = generator.currentModule.CallGraph.GetSCCRepresentativePredecessorCount(f); - var splits = new List(); - bool splitHappened /*we actually don't care*/ = - generator.TrSplitExpr(e.E, splits, true, functionHeight, true, true, etran); - var (errorMessage, successMessage) = generator.CustomErrorMessage(e.Attributes); - foreach (var s in splits) { - if (s.IsChecked && !RefinementToken.IsInherited(s.Tok, generator.currentModule)) { - var ensures = generator.EnsuresWithDependencies(s.Tok, false, e.E, s.E, errorMessage, successMessage, null); - bodyCheckBuilder.Add(new AssertCmd(ensures.tok, ensures.Condition, ensures.Description, ensures.Attributes)); + + foreach (AttributedExpression e in f.Ens) { + var functionHeight = generator.currentModule.CallGraph.GetSCCRepresentativePredecessorCount(f); + var splits = new List(); + bool splitHappened /*we actually don't care*/ = + generator.TrSplitExpr(e.E, splits, true, functionHeight, true, true, etran); + var (errorMessage, successMessage) = generator.CustomErrorMessage(e.Attributes); + foreach (var s in splits) { + if (s.IsChecked && !RefinementToken.IsInherited(s.Tok, generator.currentModule)) { + var ensures = + generator.EnsuresWithDependencies(s.Tok, false, e.E, s.E, errorMessage, successMessage, null); + bodyCheckBuilder.Add(new AssertCmd(ensures.tok, ensures.Condition, ensures.Description, + ensures.Attributes)); + } } } - } + }); // Enforce 'older' conditions var (olderParameterCount, olderCondition) = generator.OlderCondition(f, selfCall, parameters); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs index 0dc8c0d2618..3f91d74a48c 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs @@ -531,9 +531,18 @@ private void TranslateVariableDeclaration(BoogieStmtListBuilder builder, List checkPostcondition = b => { - CheckSubsetType(etran, rhs, boogieTupleReference, pat.Expr.Type, b, "variable declaration RHS"); + Contract.Assert(pat.Expr.Type != null); + var bResult = etran.TrExpr(rhs); + CheckSubrange(rhs.tok, bResult, rhs.Type, pat.Expr.Type, rhs, b); + b.Add(TrAssumeCmdWithDependenciesAndExtend(etran, rhs.tok, rhs, + e => Bpl.Expr.Eq(boogieTupleReference, AdaptBoxing(rhs.tok, e, rhs.Type, pat.Expr.Type)), + "variable declaration RHS")); }; CheckWellformedWithResult(rhs, new WFOptions(null, false, false), checkPostcondition, locals, builder, etran); + builder.Add(TrAssumeCmd(rhs.tok, etran.CanCallAssumption(rhs))); + builder.Add(new CommentCmd("CheckWellformedWithResult: any expression")); + builder.Add(TrAssumeCmd(rhs.tok, MkIs(boogieTupleReference, pat.Expr.Type))); + CheckCasePatternShape(pat, rhs, boogieTupleReference, rhs.tok, pat.Expr.Type, builder); builder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, varDeclPattern.tok, pat.Expr, e => Expr.Eq(e, boogieTupleReference), "variable declaration")); } From 6d757b40d810f012ac56810aeaec08dd372d66fd Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Sat, 27 Jul 2024 09:35:09 +0200 Subject: [PATCH 10/63] Slightly better behavior --- .../BoogieGenerator.ExpressionWellformed.cs | 59 ++++++++--------- ...oogieGenerator.Functions.Wellformedness.cs | 64 ++++++++++--------- .../Verifier/BoogieGenerator.TrStatement.cs | 10 +-- .../expressions/statementExpressionScope.dfy | 6 +- .../statementExpressionScope.dfy.expect | 5 +- 5 files changed, 73 insertions(+), 71 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 1944c076b2b..9e3c487a676 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -13,6 +13,7 @@ using DafnyCore.Verifier; using Bpl = Microsoft.Boogie; using Microsoft.Boogie; +using Std.Wrappers; using static Microsoft.Dafny.Util; using PODesc = Microsoft.Dafny.ProofObligationDescription; @@ -262,8 +263,9 @@ void CheckWellformed(Expression expr, WFOptions options, List locals, /// See class WFOptions for descriptions of the specified options. /// void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, - Action checkPostcondition, - List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { + CheckPostcondition checkPostcondition, + List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, + string resultDescription = null) { Contract.Requires(expr != null); Contract.Requires(wfOptions != null); Contract.Requires(locals != null); @@ -1143,8 +1145,8 @@ void CheckOperand(Expression operand) { } case StmtExpr stmtExpr: var bodyBuilder = new BoogieStmtListBuilder(this, builder.Options); - PathAsideBlock(stmtExpr.tok, bodyBuilder, builder); CheckWellformedStmtExpr(stmtExpr, wfOptions, checkPostcondition, locals, bodyBuilder, etran); + PathAsideBlock(stmtExpr.tok, bodyBuilder, builder); checkPostcondition = null; break; case ITEExpr iteExpr: { @@ -1160,9 +1162,9 @@ void CheckOperand(Expression operand) { Contract.Assert(letExpr != null); CheckWellformedLetExprWithResult(letExpr, wfOptions, checkPostcondition, locals, bThen, etran, false); } else { - CheckWellformedWithResult(e.Thn, wfOptions, checkPostcondition, locals, bThen, etran); + CheckWellformedWithResult(e.Thn, wfOptions, checkPostcondition, locals, bThen, etran, "if expression then branch"); } - CheckWellformedWithResult(e.Els, wfOptions, checkPostcondition, locals, bElse, etran); + CheckWellformedWithResult(e.Els, wfOptions, checkPostcondition, locals, bElse, etran, "if expression else branch"); builder.Add(new Bpl.IfCmd(iteExpr.tok, etran.TrExpr(e.Test), bThen.Collect(iteExpr.tok), null, bElse.Collect(iteExpr.tok))); checkPostcondition = null; break; @@ -1186,18 +1188,18 @@ void CheckOperand(Expression operand) { CheckNotGhostVariant(e.InCompiledContext, updateExpr, e.Root, "update of", e.Members, e.LegalSourceConstructors, builder, etran); - CheckWellformedWithResult(e.ResolvedExpression, wfOptions, checkPostcondition, locals, builder, etran); + CheckWellformedWithResult(e.ResolvedExpression, wfOptions, checkPostcondition, locals, builder, etran, resultDescription); checkPostcondition = null; break; } case ConcreteSyntaxExpression expression: { var e = expression; - CheckWellformedWithResult(e.ResolvedExpression, wfOptions, checkPostcondition, locals, builder, etran); + CheckWellformedWithResult(e.ResolvedExpression, wfOptions, checkPostcondition, locals, builder, etran, resultDescription); checkPostcondition = null; break; } case NestedMatchExpr nestedMatchExpr: - CheckWellformedWithResult(nestedMatchExpr.Flattened, wfOptions, checkPostcondition, locals, builder, etran); + CheckWellformedWithResult(nestedMatchExpr.Flattened, wfOptions, checkPostcondition, locals, builder, etran, resultDescription); checkPostcondition = null; break; case BoogieFunctionCall call: { @@ -1220,7 +1222,7 @@ void CheckOperand(Expression operand) { if (checkPostcondition != null) { - checkPostcondition(builder); + checkPostcondition(builder, expr, true, resultDescription); } } @@ -1298,8 +1300,8 @@ private void CheckWellformedComprehensionExpression(WFOptions wfOptions, List checkPostcondition = builder => { - CheckSubsetType(etran, body, resultIe, rangeType, builder); + CheckPostcondition checkPostcondition = (innerBuilder, innerBody, adaptBox, prefix) => { + CheckSubsetType(etran, innerBody, resultIe, rangeType, innerBuilder, adaptBox, prefix); }; CheckWellformedWithResult(body, newOptions, checkPostcondition, locals, b, comprehensionEtran); }); @@ -1353,20 +1355,20 @@ private void CheckWellformedComprehensionExpression(WFOptions wfOptions, List Bpl.Expr.Eq(selfCall, AdaptBoxing(expr.tok, e, expr.Type, resultType)), + e => Bpl.Expr.Eq(selfCall, adaptBoxing ? AdaptBoxing(expr.tok, e, expr.Type, resultType) : e), resultDescription)); builder.Add(TrAssumeCmd(expr.tok, etran.CanCallAssumption(expr))); builder.Add(new CommentCmd("CheckWellformedWithResult: any expression")); builder.Add(TrAssumeCmd(expr.tok, MkIs(selfCall, resultType))); } - private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, Action checkPostcondition, List locals, + private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, CheckPostcondition checkPostcondition, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { FillMissingCases(me); @@ -1408,7 +1410,7 @@ private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, Action checkPostcondition, List locals, + private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions wfOptions, CheckPostcondition checkPostcondition, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { // If we're inside an "old" expression, then "etran" will know how to translate // expressions. However, here, we're also having to translate e.S, which is a @@ -1429,7 +1431,7 @@ private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions options, Actio TrStmt(stmtExpr.S, builder, locals, etran); } - CheckWellformedWithResult(stmtExpr.E, options, checkPostcondition, locals, builder, etran); + CheckWellformedWithResult(stmtExpr.E, wfOptions, checkPostcondition, locals, builder, etran, "statement expression result"); } /// @@ -1500,7 +1502,10 @@ void CheckWellformedSpecialFunction(FunctionCallExpr expr, WFOptions options, Bp } } - void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, Action checkPostcondition, List locals, + delegate void CheckPostcondition(BoogieStmtListBuilder builder, Expression body, bool adaptBox, + string errorMessagePrefix); + + void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, CheckPostcondition checkPostcondition, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, bool checkRhs) { if (e.Exact) { var substMap = SetupBoundVarsAsLocals(e.BoundVars.ToList(), builder, locals, etran, "#Z"); @@ -1513,16 +1518,15 @@ void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, Action checkPostconditionForLhs = innerBuilder => { - CheckSubsetType(etran, e.RHSs[unModifiedI], rIe, pat.Expr.Type, innerBuilder, "let expression binding RHS well-formed"); + CheckPostcondition checkPostconditionForLhs = (innerBuilder, body, adaptBoxing, prefix) => { + CheckSubsetType(etran, body, rIe, pat.Expr.Type, innerBuilder, adaptBoxing, prefix); }; - CheckWellformedWithResult(e.RHSs[i], wfOptions, checkPostconditionForLhs, locals, builder, etran); + CheckWellformedWithResult(e.RHSs[i], wfOptions, checkPostconditionForLhs, locals, builder, etran, "let expression binding RHS well-formed"); CheckCasePatternShape(pat, rhs, rIe, rhs.tok, pat.Expr.Type, builder); var substExpr = Substitute(pat.Expr, null, substMap); builder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, e.tok, substExpr, e => Bpl.Expr.Eq(e, rIe), "let expression binding")); } - CheckWellformedWithResult(Substitute(e.Body, null, substMap), wfOptions, checkPostcondition, locals, builder, etran); + CheckWellformedWithResult(Substitute(e.Body, null, substMap), wfOptions, checkPostcondition, locals, builder, etran, "let expression result"); } else { // CheckWellformed(var b :| RHS(b); Body(b)) = @@ -1576,14 +1580,7 @@ void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, Action Expr.Eq(checkPostcondition, e), "let expression")); - // builder.Add(TrAssumeCmd(letBody.tok, etran.CanCallAssumption(letBody))); - // builder.Add(new CommentCmd("CheckWellformedWithResult: Let expression")); - // builder.Add(TrAssumeCmd(letBody.tok, MkIs(checkPostcondition, resultType))); + checkPostcondition(builder, letBody, false, "let expression"); } } } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs index ab6910cf6b5..a5bcfeae717 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs @@ -187,43 +187,45 @@ private BoogieStmtListBuilder GetBodyCheckBuilder(Function f, ExpressionTranslat { var selfCall = GetSelfCall(f, etran, parameters); var bodyCheckBuilder = new BoogieStmtListBuilder(generator, generator.options); - bodyCheckBuilder.Add(new CommentCmd("Check Wfness of body, result subset type constraint, and return to check the postcondition")); + bodyCheckBuilder.Add(new CommentCmd("Check Wfness of body and result subset type constraint")); if (f.Body != null && generator.RevealedInScope(f)) { var bodyCheckDelayer = new ReadsCheckDelayer(etran, null, locals, builderInitializationArea, bodyCheckBuilder); bodyCheckDelayer.DoWithDelayedReadsChecks(false, wfo => { - Action checkPostcondition = b => { - Contract.Assert(f.ResultType != null); - var bResult = etran.TrExpr(f.Body); - generator.CheckSubrange(f.Body.tok, bResult, f.Body.Type, f.ResultType, f.Body, b); - }; - generator.CheckWellformedWithResult(f.Body, wfo, checkPostcondition, locals, bodyCheckBuilder, etran); - bodyCheckBuilder.Add(generator.TrAssumeCmdWithDependenciesAndExtend(etran, f.Body.tok, f.Body, - e => Bpl.Expr.Eq(selfCall, generator.AdaptBoxing(f.Body.tok, e, f.Body.Type, f.ResultType)), - "function call result")); - bodyCheckBuilder.Add(TrAssumeCmd(f.Body.tok, etran.CanCallAssumption(f.Body))); - bodyCheckBuilder.Add(new CommentCmd("CheckWellformedWithResult: any expression")); - bodyCheckBuilder.Add(TrAssumeCmd(f.Body.tok, generator.MkIs(selfCall, f.ResultType))); - if (f.Result != null) { - var cmd = TrAssumeCmd(f.tok, Expr.Eq(selfCall, generator.TrVar(f.tok, f.Result))); - generator.proofDependencies?.AddProofDependencyId(cmd, f.tok, new FunctionDefinitionDependency(f)); - bodyCheckBuilder.Add(cmd); - } + CheckPostcondition checkPostcondition = (b, innerBody, adaptBoxing, prefix) => { + generator.CheckSubsetType(etran, innerBody, selfCall, f.Body.Type, b, adaptBoxing, prefix); + // Contract.Assert(f.ResultType != null); + // var bResult = etran.TrExpr(innerBody); + // generator.CheckSubrange(f.Body.tok, bResult, f.Body.Type, f.ResultType, f.Body, b); + // + // bodyCheckBuilder.Add(generator.TrAssumeCmdWithDependenciesAndExtend(etran, f.Body.tok, f.Body, + // e => Bpl.Expr.Eq(selfCall, generator.AdaptBoxing(f.Body.tok, e, f.Body.Type, f.ResultType)), + // prefix)); + // bodyCheckBuilder.Add(TrAssumeCmd(f.Body.tok, etran.CanCallAssumption(f.Body))); + // bodyCheckBuilder.Add(new CommentCmd("CheckWellformedWithResult: any expression")); + // bodyCheckBuilder.Add(TrAssumeCmd(f.Body.tok, generator.MkIs(selfCall, f.ResultType))); + if (f.Result != null) { + var cmd = TrAssumeCmd(f.tok, Expr.Eq(selfCall, generator.TrVar(f.tok, f.Result))); + generator.proofDependencies?.AddProofDependencyId(cmd, f.tok, new FunctionDefinitionDependency(f)); + b.Add(cmd); + } - foreach (AttributedExpression e in f.Ens) { - var functionHeight = generator.currentModule.CallGraph.GetSCCRepresentativePredecessorCount(f); - var splits = new List(); - bool splitHappened /*we actually don't care*/ = - generator.TrSplitExpr(e.E, splits, true, functionHeight, true, true, etran); - var (errorMessage, successMessage) = generator.CustomErrorMessage(e.Attributes); - foreach (var s in splits) { - if (s.IsChecked && !RefinementToken.IsInherited(s.Tok, generator.currentModule)) { - var ensures = - generator.EnsuresWithDependencies(s.Tok, false, e.E, s.E, errorMessage, successMessage, null); - bodyCheckBuilder.Add(new AssertCmd(ensures.tok, ensures.Condition, ensures.Description, - ensures.Attributes)); + foreach (AttributedExpression e in f.Ens) { + var functionHeight = generator.currentModule.CallGraph.GetSCCRepresentativePredecessorCount(f); + var splits = new List(); + bool splitHappened /*we actually don't care*/ = + generator.TrSplitExpr(e.E, splits, true, functionHeight, true, true, etran); + var (errorMessage, successMessage) = generator.CustomErrorMessage(e.Attributes); + foreach (var s in splits) { + if (s.IsChecked && !RefinementToken.IsInherited(s.Tok, generator.currentModule)) { + var ensures = + generator.EnsuresWithDependencies(s.Tok, false, e.E, s.E, errorMessage, successMessage, null); + b.Add(new AssertCmd(ensures.tok, ensures.Condition, ensures.Description, + ensures.Attributes)); + } } } - } + }; + generator.CheckWellformedWithResult(f.Body, wfo, checkPostcondition, locals, bodyCheckBuilder, etran, "function call result"); }); // Enforce 'older' conditions diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs index 3f91d74a48c..2f773fd5a3b 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs @@ -530,15 +530,15 @@ private void TranslateVariableDeclaration(BoogieStmtListBuilder builder, List checkPostcondition = b => { + CheckPostcondition checkPostcondition = (b, body, adaptBoxing, prefix) => { Contract.Assert(pat.Expr.Type != null); - var bResult = etran.TrExpr(rhs); + var bResult = etran.TrExpr(body); CheckSubrange(rhs.tok, bResult, rhs.Type, pat.Expr.Type, rhs, b); b.Add(TrAssumeCmdWithDependenciesAndExtend(etran, rhs.tok, rhs, - e => Bpl.Expr.Eq(boogieTupleReference, AdaptBoxing(rhs.tok, e, rhs.Type, pat.Expr.Type)), - "variable declaration RHS")); + e => Bpl.Expr.Eq(boogieTupleReference, adaptBoxing ? AdaptBoxing(rhs.tok, e, rhs.Type, pat.Expr.Type) : e), + prefix)); }; - CheckWellformedWithResult(rhs, new WFOptions(null, false, false), checkPostcondition, locals, builder, etran); + CheckWellformedWithResult(rhs, new WFOptions(null, false, false), checkPostcondition, locals, builder, etran, "variable declaration RHS"); builder.Add(TrAssumeCmd(rhs.tok, etran.CanCallAssumption(rhs))); builder.Add(new CommentCmd("CheckWellformedWithResult: any expression")); builder.Add(TrAssumeCmd(rhs.tok, MkIs(boogieTupleReference, pat.Expr.Type))); diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy index 0fed01f610c..0f3a062562e 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy @@ -10,11 +10,11 @@ function StatementExpressionValueAndEnsures(): int function StatementExpressionAssumeAndFunctionEnsures(): int ensures 10 == 11 // no error, since the statement expression can be used for the ensures clause { - assume false; 10 + assume false; 42 } -function StatementExpressionAssumeAndFunctionEnsures(): int - ensures 10 == 11 // error, since the assume false does not leak +function StatementExpressionAssumeInLetAndFunctionEnsures(): int + ensures 10 == 11 // error, since the assume false does not leak from a let { var x := (assume false; 10); x } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy.expect index 0471f345c93..529ffa2c168 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy.expect @@ -1,2 +1,5 @@ +statementExpressionScope.dfy(17,13): Error: this is the postcondition that could not be proved +statementExpressionScope.dfy(45,9): Error: assertion might not hold +statementExpressionScope.dfy(51,9): Error: assertion might not hold -Dafny program verifier finished with 12 verified, 2 errors +Dafny program verifier finished with 12 verified, 3 errors From cbb8b9b5dd95827b7f6de11f0029e379d90f88df Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Sat, 27 Jul 2024 09:41:26 +0200 Subject: [PATCH 11/63] Update test --- .../LitTest/ast/expressions/statementExpressionScope.dfy | 8 ++++++++ .../ast/expressions/statementExpressionScope.dfy.expect | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy index 0f3a062562e..9b985e35115 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy @@ -31,6 +31,14 @@ method StatementExpressionAndSubsetLocal() var x: nat := assume -1 > 0; -1; } +method StatementExpressionAndSubsetLocal2() + // no error, since the statement expression can be used for the local variable type subset constraint +{ + var x: nat := assume -1 > 0; -1; + assert x >= 0; // no error; + assert StatementExpressionAndSubsetResult() >= 0; // no error +} + predicate P(x: int) method NeedsP(x: int) requires P(x) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy.expect index 529ffa2c168..54d13e6d8a7 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy.expect @@ -1,5 +1,5 @@ statementExpressionScope.dfy(17,13): Error: this is the postcondition that could not be proved -statementExpressionScope.dfy(45,9): Error: assertion might not hold -statementExpressionScope.dfy(51,9): Error: assertion might not hold +statementExpressionScope.dfy(53,9): Error: assertion might not hold +statementExpressionScope.dfy(59,9): Error: assertion might not hold Dafny program verifier finished with 12 verified, 3 errors From 0726bc5ec06b1030a8ee09f1379c43f2302f67f6 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Sat, 27 Jul 2024 09:51:42 +0200 Subject: [PATCH 12/63] One more passing test --- .../Verifier/BoogieGenerator.TrStatement.cs | 15 ++++++++++----- .../ast/expressions/statementExpressionScope.dfy | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs index 2f773fd5a3b..f72b3b1a23c 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs @@ -2585,10 +2585,14 @@ Bpl.Expr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bGivenLhs, IVariable lhs if (rhs is ExprRhs) { var e = (ExprRhs)rhs; - TrStmt_CheckWellformed(e.Expr, builder, locals, etran, true); + TrStmt_CheckWellformed(e.Expr, builder, locals, etran, true, checkPostcondition: + (b, innerBody, adaptBoxing, prefix) => { + Bpl.Expr bRhs = etran.TrExpr(innerBody); + CheckSubrange(tok, bRhs, e.Expr.Type, rhsTypeConstraint, e.Expr, b); + }); - Bpl.Expr bRhs = etran.TrExpr(e.Expr); - CheckSubrange(tok, bRhs, e.Expr.Type, rhsTypeConstraint, e.Expr, builder); + var bRhs = etran.TrExpr(e.Expr); + builder.Add(TrAssumeCmd(e.tok, MkIs(bRhs, lhsType))); if (bGivenLhs != null) { Contract.Assert(bGivenLhs == bLhs); // box the RHS, then do the assignment @@ -2798,7 +2802,8 @@ void TrStmtList(List stmts, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, bool subsumption, bool lValueContext = false) { + void TrStmt_CheckWellformed(Expression expr, BoogieStmtListBuilder builder, List locals, + ExpressionTranslator etran, bool subsumption, bool lValueContext = false, CheckPostcondition checkPostcondition = null) { Contract.Requires(expr != null); Contract.Requires(builder != null); Contract.Requires(locals != null); @@ -2823,7 +2828,7 @@ void TrStmt_CheckWellformed(Expression expr, BoogieStmtListBuilder builder, List if (lValueContext) { options = options.WithLValueContext(true); } - CheckWellformed(expr, options, locals, builder, etran); + CheckWellformedWithResult(expr, options, checkPostcondition, locals, builder, etran); builder.Add(TrAssumeCmd(expr.tok, etran.CanCallAssumption(expr))); } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy index 9b985e35115..3881556b9e4 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy @@ -36,7 +36,7 @@ method StatementExpressionAndSubsetLocal2() { var x: nat := assume -1 > 0; -1; assert x >= 0; // no error; - assert StatementExpressionAndSubsetResult() >= 0; // no error + // really weird that this changes things // assert StatementExpressionAndSubsetResult() >= 0; // no error } predicate P(x: int) From ae1309171333abb27ed6a4fcc728f73ad2736009 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 29 Jul 2024 12:45:38 +0200 Subject: [PATCH 13/63] Ran formatter --- .../BoogieGenerator.ExpressionWellformed.cs | 14 ++++---- ...oogieGenerator.Functions.Wellformedness.cs | 32 ++++++++----------- .../Verifier/BoogieGenerator.TrStatement.cs | 9 +++--- .../expressions/statementExpressionScope.dfy | 4 ++- .../statementExpressionScope.dfy.expect | 9 ++++-- 5 files changed, 33 insertions(+), 35 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 9e3c487a676..0a0bfee970d 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -1140,9 +1140,9 @@ void CheckOperand(Expression operand) { checkPostcondition = null; break; case ComprehensionExpr comprehensionExpr: { - CheckWellformedComprehensionExpression(wfOptions, locals, builder, etran, comprehensionExpr); - break; - } + CheckWellformedComprehensionExpression(wfOptions, locals, builder, etran, comprehensionExpr); + break; + } case StmtExpr stmtExpr: var bodyBuilder = new BoogieStmtListBuilder(this, builder.Options); CheckWellformedStmtExpr(stmtExpr, wfOptions, checkPostcondition, locals, bodyBuilder, etran); @@ -1220,7 +1220,7 @@ void CheckOperand(Expression operand) { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression } - + if (checkPostcondition != null) { checkPostcondition(builder, expr, true, resultDescription); } @@ -1356,7 +1356,7 @@ private void CheckWellformedComprehensionExpression(WFOptions wfOptions, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, bool checkRhs) { if (e.Exact) { diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs index a5bcfeae717..493c8da1a38 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs @@ -37,7 +37,7 @@ public void Check(Function f) { var outParams = GetWellformednessProcedureOutParameters(f, etran); var requires = GetWellformednessProcedureRequires(f, etran); requires.AddRange(additionalRequires); - + // modifies $Heap var mod = new List { ordinaryEtran.HeapCastToIdentifierExpr, @@ -123,15 +123,15 @@ public void Check(Function f) { foreach (Expression p in f.Decreases.Expressions) { generator.CheckWellformed(p, new WFOptions(null, false), locals, builder, etran); } - + var implementationParameters = Bpl.Formal.StripWhereClauses(procedureParameters); CheckBodyAndEnsuresClauses(f, etran, locals, implementationParameters, builderInitializationArea, builder); - + if (generator.EmitImplementation(f.Attributes)) { var s0 = builderInitializationArea.Collect(f.tok); var s1 = builder.Collect(f.tok); var implBody = new StmtList(new List(s0.BigBlocks.Concat(s1.BigBlocks)), f.tok); - + // emit the impl only when there are proof obligations. QKeyValue kv = etran.TrAttributes(f.Attributes, null); var parameters = Concat(Concat(Bpl.Formal.StripWhereClauses(typeInParams), heapParameters), implementationParameters); @@ -150,8 +150,7 @@ public void Check(Function f) { } // TODO should be moved so its injected - private void ConcurrentAttributeCheck(Function f, ExpressionTranslator etran, BoogieStmtListBuilder builder) - { + 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"); @@ -160,8 +159,7 @@ private void ConcurrentAttributeCheck(Function f, ExpressionTranslator etran, Bo } private void CheckBodyAndEnsuresClauses(Function f, ExpressionTranslator etran, List locals, List inParams, - BoogieStmtListBuilder builderInitializationArea, BoogieStmtListBuilder builder) - { + BoogieStmtListBuilder builderInitializationArea, BoogieStmtListBuilder builder) { builder.Add(new CommentCmd("Check body and ensures clauses")); // Generate: // if (*) { @@ -183,8 +181,7 @@ private void CheckBodyAndEnsuresClauses(Function f, ExpressionTranslator etran, private BoogieStmtListBuilder GetBodyCheckBuilder(Function f, ExpressionTranslator etran, List parameters, - List locals, BoogieStmtListBuilder builderInitializationArea) - { + List locals, BoogieStmtListBuilder builderInitializationArea) { var selfCall = GetSelfCall(f, etran, parameters); var bodyCheckBuilder = new BoogieStmtListBuilder(generator, generator.options); bodyCheckBuilder.Add(new CommentCmd("Check Wfness of body and result subset type constraint")); @@ -239,8 +236,7 @@ private BoogieStmtListBuilder GetBodyCheckBuilder(Function f, ExpressionTranslat return bodyCheckBuilder; } - private Expr GetSelfCall(Function f, ExpressionTranslator etran, List parameters) - { + private Expr GetSelfCall(Function f, ExpressionTranslator etran, List parameters) { var funcId = new FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName, generator.TrType(f.ResultType))); var args = new List(); foreach (var p in GetTypeParams(f)) { @@ -271,11 +267,10 @@ 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, List locals) { var postCheckBuilder = new BoogieStmtListBuilder(generator, generator.options); postCheckBuilder.Add(new CommentCmd("Check Wfness of postcondition and assume false")); - + // Assume the type returned by the call itself respects its type (this matters if the type is "nat", for example) var args = new List(); foreach (var p in GetTypeParams(f)) { @@ -311,7 +306,7 @@ private BoogieStmtListBuilder GetPostCheckBuilder(Function f, ExpressionTranslat var wh = generator.GetWhereClause(f.tok, funcAppl, f.ResultType, etran, NOALLOC); if (wh != null) { - postCheckBuilder.Add(TrAssumeCmd(f.tok, wh)); + postCheckBuilder.Add(TrAssumeCmd(f.tok, wh)); } // Now for the ensures clauses foreach (AttributedExpression p in f.Ens) { @@ -324,8 +319,7 @@ private BoogieStmtListBuilder GetPostCheckBuilder(Function f, ExpressionTranslat } private ExpressionTranslator GetExpressionTranslator(Function f, ExpressionTranslator ordinaryEtran, - out List additionalRequires, out List inParams_Heap) - { + out List additionalRequires, out List inParams_Heap) { ExpressionTranslator etran; additionalRequires = new(); inParams_Heap = new List(); @@ -337,7 +331,7 @@ private ExpressionTranslator GetExpressionTranslator(Function f, ExpressionTrans Expr prevHeap = new Bpl.IdentifierExpr(f.tok, prevHeapVar); Expr currHeap = new Bpl.IdentifierExpr(f.tok, currHeapVar); etran = new ExpressionTranslator(generator, generator.predef, currHeap, prevHeap, f); - + // free requires prevHeap == Heap && HeapSucc(prevHeap, currHeap) && IsHeap(currHeap) var a0 = Expr.Eq(prevHeap, ordinaryEtran.HeapExpr); var a1 = generator.HeapSucc(prevHeap, currHeap); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs index f72b3b1a23c..9ea4e65d067 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs @@ -510,8 +510,7 @@ private void TrStmt(Statement stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, - VarDeclPattern varDeclPattern) - { + VarDeclPattern varDeclPattern) { foreach (var dafnyLocal in varDeclPattern.LocalVars) { var boogieLocal = new Bpl.LocalVariable(dafnyLocal.Tok, new Bpl.TypedIdent(dafnyLocal.Tok, dafnyLocal.AssignUniqueName(currentDeclaration.IdGenerator), TrType(dafnyLocal.Type))); locals.Add(boogieLocal); @@ -542,7 +541,7 @@ private void TranslateVariableDeclaration(BoogieStmtListBuilder builder, List Expr.Eq(e, boogieTupleReference), "variable declaration")); } @@ -1782,7 +1781,7 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E // Translate receiver argument, if any Expression receiver = bReceiver == null ? dafnyReceiver : new BoogieWrapper(bReceiver, dafnyReceiver.Type); - if (!method.IsStatic && !(method is Constructor)) { + if (!method.IsStatic && method is not Constructor) { if (bReceiver == null) { TrStmt_CheckWellformed(dafnyReceiver, builder, locals, etran, true); if (!(dafnyReceiver is ThisExpr)) { @@ -2802,7 +2801,7 @@ void TrStmtList(List stmts, BoogieStmtListBuilder builder, List locals, + void TrStmt_CheckWellformed(Expression expr, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, bool subsumption, bool lValueContext = false, CheckPostcondition checkPostcondition = null) { Contract.Requires(expr != null); Contract.Requires(builder != null); diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy index 3881556b9e4..5f1a9808c16 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy @@ -40,11 +40,13 @@ method StatementExpressionAndSubsetLocal2() } predicate P(x: int) +method NeedsNat(x: nat) method NeedsP(x: int) requires P(x) method StatementExpressionAndPrecondition(x: int) { - NeedsP(assume P(x); x); // no error, since the statement expression can be used for the requires clause + NeedsNat(assume x > 0; x); // error, altough it could be nice not to have it + NeedsP(assume P(x); x); // error, since the statement expression can not be used for the requires clause } function StatementExpressionAssumeDoesNotEscapeLetBinding(): int diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy.expect index 54d13e6d8a7..18b1c94a5b9 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy.expect @@ -1,5 +1,8 @@ statementExpressionScope.dfy(17,13): Error: this is the postcondition that could not be proved -statementExpressionScope.dfy(53,9): Error: assertion might not hold -statementExpressionScope.dfy(59,9): Error: assertion might not hold +statementExpressionScope.dfy(48,11): Error: value does not satisfy the subset constraints of 'nat' +statementExpressionScope.dfy(49,8): Error: a precondition for this call could not be proved +statementExpressionScope.dfy(44,31): Related location: this is the precondition that could not be proved +statementExpressionScope.dfy(55,9): Error: assertion might not hold +statementExpressionScope.dfy(61,9): Error: assertion might not hold -Dafny program verifier finished with 12 verified, 3 errors +Dafny program verifier finished with 15 verified, 5 errors From fb1ab6e7032e664e996b3cfe5a7e38632b5aebe2 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 30 Jul 2024 11:00:01 +0200 Subject: [PATCH 14/63] Remove unused code --- .../Synchronization/VerificationStatusTest.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/DafnyLanguageServer.Test/Synchronization/VerificationStatusTest.cs b/Source/DafnyLanguageServer.Test/Synchronization/VerificationStatusTest.cs index 90ffba0e032..86803500d0e 100644 --- a/Source/DafnyLanguageServer.Test/Synchronization/VerificationStatusTest.cs +++ b/Source/DafnyLanguageServer.Test/Synchronization/VerificationStatusTest.cs @@ -25,8 +25,6 @@ await SetUp(options => { Directory.CreateDirectory(directory); await File.WriteAllTextAsync(Path.Combine(directory, "dfyconfig.toml"), ""); await File.WriteAllTextAsync(Path.Combine(directory, "a.dfy"), "module A {}"); - // var project = CreateAndOpenTestDocument("", Path.Combine(directory, "dfyconfig.toml")); - // var a = CreateAndOpenTestDocument("module A {}", Path.Combine(directory, "a.dfy")); var b = CreateAndOpenTestDocument("module A.B { \n method Test() { assert true; }\n}", Path.Combine(directory, "b.dfy")); var methodHeader = new Position(1, 11); From 837f5871acf324f4f447b1c9af5bd3819005ff7a Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 31 Jul 2024 14:20:53 +0200 Subject: [PATCH 15/63] Add test-case --- .../TestFiles/LitTests/LitTest/ast/reveal/revealFunctions.dfy | 3 +++ .../LitTests/LitTest/ast/reveal/revealFunctions.dfy.expect | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/reveal/revealFunctions.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/reveal/revealFunctions.dfy index a68e222f887..b22a059f208 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/reveal/revealFunctions.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/reveal/revealFunctions.dfy @@ -55,6 +55,9 @@ method FunctionSubsetResult() { hide *; var x: nat := Natty(); assert x >= 0; // no error + + var y: int := Natty(); + assert y >= 0; // no error } function Natty(): nat { diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/reveal/revealFunctions.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/reveal/revealFunctions.dfy.expect index fe0885c997b..56a5eda7b47 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/reveal/revealFunctions.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/reveal/revealFunctions.dfy.expect @@ -2,4 +2,4 @@ revealFunctions.dfy(15,12): Error: assertion might not hold revealFunctions.dfy(22,12): Error: assertion might not hold revealFunctions.dfy(49,21): Error: assertion might not hold -Dafny program verifier finished with 26 verified, 3 errors +Dafny program verifier finished with 25 verified, 3 errors From b460fb6b102bf7b06c8259043a9bc29ac31433ff Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 31 Jul 2024 15:24:05 +0200 Subject: [PATCH 16/63] Fixes --- .../BoogieGenerator.ExpressionWellformed.cs | 4 +- .../Verifier/BoogieGenerator.TrStatement.cs | 74 +------------------ 2 files changed, 4 insertions(+), 74 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index e732d95b827..d13335a60e9 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -1301,7 +1301,9 @@ private void CheckWellformedComprehensionExpression(WFOptions wfOptions, List { - CheckSubsetType(etran, innerBody, resultIe, rangeType, innerBuilder, adaptBox, prefix); + if (rangeType != null) { + CheckSubsetType(etran, innerBody, resultIe, rangeType, innerBuilder, adaptBox, prefix); + } }; CheckWellformedWithResult(body, newOptions, checkPostcondition, locals, b, comprehensionEtran); }); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs index 060df197c0f..ca4891ad0cf 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs @@ -386,28 +386,6 @@ private void TrStmt(Statement stmt, BoogieStmtListBuilder builder, private void TranslateVariableDeclaration(BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, VarDeclPattern varDeclPattern) { - - // var s = (VarDeclPattern)stmt; - // foreach (var local in s.LocalVars) { - // Bpl.LocalVariable bvar = new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator), TrType(local.Type))); - // locals.Add(bvar); - // var bIe = new Bpl.IdentifierExpr(bvar.tok, bvar); - // builder.Add(new Bpl.HavocCmd(local.Tok, new List { bIe })); - // Bpl.Expr wh = GetWhereClause(local.Tok, bIe, local.Type, etran, isAllocContext.Var(stmt.IsGhost, local)); - // if (wh != null) { - // builder.Add(TrAssumeCmd(local.Tok, wh)); - // } - // } - // var varNameGen = CurrentIdGenerator.NestedFreshIdGenerator("let#"); - // var pat = s.LHS; - // var rhs = s.RHS; - // var nm = varNameGen.FreshId(string.Format("#{0}#", 0)); - // var r = new Bpl.LocalVariable(pat.tok, new Bpl.TypedIdent(pat.tok, nm, TrType(rhs.Type))); - // locals.Add(r); - // var rIe = new Bpl.IdentifierExpr(rhs.tok, r); - // CheckWellformedWithResult(rhs, new WFOptions(null, false, false), rIe, pat.Expr.Type, locals, builder, etran, "variable declaration RHS"); - // CheckCasePatternShape(pat, rhs, rIe, rhs.tok, pat.Expr.Type, builder); - // builder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, s.tok, pat.Expr, e => Expr.Eq(e, rIe), "variable declaration")); foreach (var dafnyLocal in varDeclPattern.LocalVars) { var boogieLocal = new Bpl.LocalVariable(dafnyLocal.Tok, new Bpl.TypedIdent(dafnyLocal.Tok, dafnyLocal.AssignUniqueName(currentDeclaration.IdGenerator), @@ -483,56 +461,6 @@ private void TrUpdateStmt(BoogieStmtListBuilder builder, List locals, } void TrVarDeclStmt(VarDeclStmt varDeclStmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { - // var newLocalIds = new List(); - // int i = 0; - // foreach (var local in s.Locals) { - // Bpl.Type varType = TrType(local.Type); - // Bpl.Expr wh = GetWhereClause(local.Tok, - // new Bpl.IdentifierExpr(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator), varType), - // local.Type, etran, isAllocContext.Var(stmt.IsGhost, local)); - // // if needed, register definite-assignment tracking for this local - // var needDefiniteAssignmentTracking = s.Update == null || s.Update is AssignSuchThatStmt; - // if (s.Update is UpdateStmt) { - // // there is an initial assignment, but we need to look out for "*" being that assignment - // var us = (UpdateStmt)s.Update; - // if (i < us.Rhss.Count && us.Rhss[i] is HavocRhs) { - // needDefiniteAssignmentTracking = true; - // } - // } - // if (!local.Type.IsNonempty) { - // // This prevents generating an unsatisfiable where clause for possibly empty types - // needDefiniteAssignmentTracking = true; - // } - // if (needDefiniteAssignmentTracking) { - // var defassExpr = AddDefiniteAssignmentTracker(local, locals); - // if (wh != null && defassExpr != null) { - // // make the "where" expression be "defass ==> wh", because we don't want to assume anything - // // before the variable has been assigned (for a variable that needs definite-assignment tracking - // // in the first place) - // wh = BplImp(defassExpr, wh); - // } - // } - // // 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.Attributes = etran.TrAttributes(local.Attributes, null); - // newLocalIds.Add(new Bpl.IdentifierExpr(local.Tok, var)); - // locals.Add(var); - // i++; - // } - // if (s.Update == null) { - // // it is necessary to do a havoc here in order to give the variable the correct allocation state - // builder.Add(new HavocCmd(s.Tok, newLocalIds)); - // } - // // processing of "assumption" variables happens after the variable is possibly havocked above - // foreach (var local in s.Locals) { - // if (Attributes.Contains(local.Attributes, "assumption")) { - // Bpl.Type varType = TrType(local.Type); - // builder.Add(new AssumeCmd(local.Tok, new Bpl.IdentifierExpr(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator), varType), new QKeyValue(local.Tok, "assumption_variable_initialization", new List(), null))); - // } - // } - // if (s.Update != null) { - // TrStmt(s.Update, builder, locals, etran); - // } var newLocalIds = new List(); int i = 0; @@ -2704,7 +2632,7 @@ Bpl.Expr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bGivenLhs, IVariable lhs }); var bRhs = etran.TrExpr(e.Expr); - builder.Add(TrAssumeCmd(e.tok, MkIs(bRhs, lhsType))); + builder.Add(TrAssumeCmd(e.tok, MkIs(bRhs, e.Expr.Type))); if (bGivenLhs != null) { Contract.Assert(bGivenLhs == bLhs); // box the RHS, then do the assignment From 4a5bc5c69b4ace84f851a4a552e6b9013c7a2b26 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 31 Jul 2024 15:33:14 +0200 Subject: [PATCH 17/63] Fix --- .../Verifier/BoogieGenerator.TrStatement.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs index ca4891ad0cf..f9c1134045f 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs @@ -2625,14 +2625,18 @@ Bpl.Expr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bGivenLhs, IVariable lhs if (rhs is ExprRhs) { var e = (ExprRhs)rhs; + var bRhs = etran.TrExpr(e.Expr); + var cre = GetSubrangeCheck(tok, bRhs, e.Expr.Type, rhsTypeConstraint, e.Expr, null, out var desc, ""); TrStmt_CheckWellformed(e.Expr, builder, locals, etran, true, checkPostcondition: - (b, innerBody, adaptBoxing, prefix) => { - Bpl.Expr bRhs = etran.TrExpr(innerBody); - CheckSubrange(tok, bRhs, e.Expr.Type, rhsTypeConstraint, e.Expr, b); + (b, _, _, _) => { + if (cre != null) { + b.Add(Assert(tok, cre, desc)); + } }); + if (cre != null) { + builder.Add(new AssumeCmd(tok, cre)); //e.tok, MkIs(bRhs, rhsTypeConstraint))); + } - var bRhs = etran.TrExpr(e.Expr); - builder.Add(TrAssumeCmd(e.tok, MkIs(bRhs, e.Expr.Type))); if (bGivenLhs != null) { Contract.Assert(bGivenLhs == bLhs); // box the RHS, then do the assignment From d8fa9c569a933ecbdf328f857accda088255a567 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 8 Aug 2024 17:07:37 +0200 Subject: [PATCH 18/63] Fixes --- ...oogieGenerator.Functions.Wellformedness.cs | 49 +++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs index 9813631cfa6..960422e41c6 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs @@ -42,11 +42,25 @@ public void Check(Function f) { var mod = new List { ordinaryEtran.HeapCastToIdentifierExpr, }; + + var context = new BodyTranslationContext(f.ContainsHide); + var ens = new List(); + 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); + var (errorMessage, successMessage) = generator.CustomErrorMessage(ensures.Attributes); + foreach (var s in splits) { + if (s.IsChecked && !RefinementToken.IsInherited(s.Tok, generator.currentModule)) { + generator.AddEnsures(ens, generator.EnsuresWithDependencies(s.Tok, false, ensures.E, s.E, errorMessage, successMessage, null)); + } + } + } var proc = new Procedure(f.tok, "CheckWellformed" + NameSeparator + f.FullSanitizedName, new List(), Concat(Concat(typeInParams, heapParameters), procedureParameters), outParams, - false, requires, mod, new List(), etran.TrAttributes(f.Attributes, null)); + false, requires, mod, ens, etran.TrAttributes(f.Attributes, null)); AddVerboseNameAttribute(proc, f.FullDafnyName, MethodTranslationKind.SpecWellformedness); generator.sink.AddTopLevelDeclaration(proc); @@ -57,7 +71,6 @@ 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 context = new BodyTranslationContext(f.ContainsHide); var locals = new List(); var builder = new BoogieStmtListBuilder(generator, generator.options, context); var builderInitializationArea = new BoogieStmtListBuilder(generator, generator.options, context); @@ -126,7 +139,7 @@ public void Check(Function f) { } var implementationParameters = Bpl.Formal.StripWhereClauses(procedureParameters); - CheckBodyAndEnsuresClauses(f, etran, locals, implementationParameters, builderInitializationArea, builder); + CheckBodyAndEnsuresClauseWellformedness(f, etran, locals, implementationParameters, builderInitializationArea, builder); if (generator.EmitImplementation(f.Attributes)) { var s0 = builderInitializationArea.Collect(f.tok); @@ -159,7 +172,7 @@ private void ConcurrentAttributeCheck(Function f, ExpressionTranslator etran, Bo } } - private void CheckBodyAndEnsuresClauses(Function f, ExpressionTranslator etran, List locals, List inParams, + private void CheckBodyAndEnsuresClauseWellformedness(Function f, ExpressionTranslator etran, List locals, List inParams, BoogieStmtListBuilder builderInitializationArea, BoogieStmtListBuilder builder) { builder.Add(new CommentCmd("Check body and ensures clauses")); // Generate: @@ -190,8 +203,8 @@ private BoogieStmtListBuilder GetBodyCheckBuilder(Function f, ExpressionTranslat if (f.Body != null && generator.RevealedInScope(f)) { var bodyCheckDelayer = new ReadsCheckDelayer(etran, null, locals, builderInitializationArea, bodyCheckBuilder); bodyCheckDelayer.DoWithDelayedReadsChecks(false, wfo => { - CheckPostcondition checkPostcondition = (b, innerBody, adaptBoxing, prefix) => { - generator.CheckSubsetType(etran, innerBody, selfCall, f.Body.Type, b, adaptBoxing, prefix); + void CheckPostcondition(BoogieStmtListBuilder innerBuilder, Expression innerBody, bool adaptBoxing, string prefix) { + generator.CheckSubsetType(etran, innerBody, selfCall, f.Body.Type, innerBuilder, adaptBoxing, prefix); // Contract.Assert(f.ResultType != null); // var bResult = etran.TrExpr(innerBody); // generator.CheckSubrange(f.Body.tok, bResult, f.Body.Type, f.ResultType, f.Body, b); @@ -205,29 +218,14 @@ private BoogieStmtListBuilder GetBodyCheckBuilder(Function f, ExpressionTranslat if (f.Result != null) { var cmd = TrAssumeCmd(f.tok, Expr.Eq(selfCall, generator.TrVar(f.tok, f.Result))); generator.proofDependencies?.AddProofDependencyId(cmd, f.tok, new FunctionDefinitionDependency(f)); - b.Add(cmd); + innerBuilder.Add(cmd); } + } - var context = new BodyTranslationContext(f.ContainsHide); - foreach (AttributedExpression e in f.Ens) { - var functionHeight = generator.currentModule.CallGraph.GetSCCRepresentativePredecessorCount(f); - var splits = new List(); - bool splitHappened /*we actually don't care*/ = - generator.TrSplitExpr(context, e.E, splits, true, functionHeight, true, true, etran); - var (errorMessage, successMessage) = generator.CustomErrorMessage(e.Attributes); - foreach (var s in splits) { - if (s.IsChecked && !RefinementToken.IsInherited(s.Tok, generator.currentModule)) { - var ensures = - generator.EnsuresWithDependencies(s.Tok, false, e.E, s.E, errorMessage, successMessage, null); - b.Add(new AssertCmd(ensures.tok, ensures.Condition, ensures.Description, - ensures.Attributes)); - } - } - } - }; - generator.CheckWellformedWithResult(f.Body, wfo, checkPostcondition, locals, bodyCheckBuilder, etran, "function call result"); + generator.CheckWellformedWithResult(f.Body, wfo, CheckPostcondition, locals, bodyCheckBuilder, etran, "function call result"); }); + // Enforce 'older' conditions var (olderParameterCount, olderCondition) = generator.OlderCondition(f, selfCall, parameters); if (olderParameterCount != 0) { @@ -235,6 +233,7 @@ private BoogieStmtListBuilder GetBodyCheckBuilder(Function f, ExpressionTranslat new PODesc.IsOlderProofObligation(olderParameterCount, f.Ins.Count + (f.IsStatic ? 0 : 1)))); } } + bodyCheckBuilder.Add(TrAssumeCmd(f.tok, Expr.False)); return bodyCheckBuilder; } From bbe156c1a936debe9eae1b77e24401f954b472e6 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 9 Aug 2024 12:33:57 +0200 Subject: [PATCH 19/63] Add testcase for ensures clause on function reporting --- .../ast/functions/ensuresReporting.dfy | 0 .../ast/functions/ensuresReporting.dfy.expect | 29 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy.expect diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy new file mode 100644 index 00000000000..e69de29bb2d diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy.expect new file mode 100644 index 00000000000..0aea0607093 --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy.expect @@ -0,0 +1,29 @@ +// RUN: ! %verify %s &> "%t" +// RUN: %diff "%s.expect" "%t" + +function ElseError(x: int): int + ensures ElseErorr(x) == 0 +{ + if x == 0 then + 0 + else + 1 +} +function ThenError(x: int): int + ensures ThenError(x) == 0 +{ + if x == 0 then + 0 + else + 1 +} + +function CaseError(x: int): int + ensures CaseError(x) == 1 +{ + match x { + case 0 => 0 + case 1 => 1 + case 2 => 2 + } +} \ No newline at end of file From fffe0ee72eb12fac54e7e26587a216b3605f4ca6 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 9 Aug 2024 12:35:32 +0200 Subject: [PATCH 20/63] Fixes --- ...oogieGenerator.Functions.Wellformedness.cs | 2 +- .../ast/functions/ensuresReporting.dfy | 29 +++++++++++++++ .../ast/functions/ensuresReporting.dfy.expect | 35 ++++--------------- .../LitTest/dafny0/Definedness.dfy.expect | 4 +-- .../dafny0/FunctionSpecifications.dfy.expect | 8 ++--- .../LitTest/dafny0/Refinement.dfy.expect | 2 +- 6 files changed, 44 insertions(+), 36 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs index 960422e41c6..2d38e8e7887 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs @@ -220,11 +220,11 @@ void CheckPostcondition(BoogieStmtListBuilder innerBuilder, Expression innerBody generator.proofDependencies?.AddProofDependencyId(cmd, f.tok, new FunctionDefinitionDependency(f)); innerBuilder.Add(cmd); } + innerBuilder.Add(new ReturnCmd(innerBody.Tok)); } generator.CheckWellformedWithResult(f.Body, wfo, CheckPostcondition, locals, bodyCheckBuilder, etran, "function call result"); }); - // Enforce 'older' conditions var (olderParameterCount, olderCondition) = generator.OlderCondition(f, selfCall, parameters); diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy index e69de29bb2d..299b8bfb5d9 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy @@ -0,0 +1,29 @@ +// RUN: ! %verify %s &> "%t" +// RUN: %diff "%s.expect" "%t" + +function ElseError(x: int): int + ensures ElseError(x) == 0 +{ + if x == 0 then + 0 + else + 1 // error +} +function ThenError(x: int): int + ensures ThenError(x) == 0 +{ + if x == 0 then + 1 // error + else + 0 +} + +function CaseError(x: int): int + ensures CaseError(x) == 1 +{ + match x { + case 0 => 1 + case 1 => 0 // error + case _ => 1 + } +} \ No newline at end of file diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy.expect index 0aea0607093..c5df2f77b15 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy.expect @@ -1,29 +1,8 @@ -// RUN: ! %verify %s &> "%t" -// RUN: %diff "%s.expect" "%t" +ensuresReporting.dfy(10,4): Error: a postcondition could not be proved on this return path +ensuresReporting.dfy(5,23): Related location: this is the postcondition that could not be proved +ensuresReporting.dfy(16,4): Error: a postcondition could not be proved on this return path +ensuresReporting.dfy(13,23): Related location: this is the postcondition that could not be proved +ensuresReporting.dfy(26,14): Error: a postcondition could not be proved on this return path +ensuresReporting.dfy(22,23): Related location: this is the postcondition that could not be proved -function ElseError(x: int): int - ensures ElseErorr(x) == 0 -{ - if x == 0 then - 0 - else - 1 -} -function ThenError(x: int): int - ensures ThenError(x) == 0 -{ - if x == 0 then - 0 - else - 1 -} - -function CaseError(x: int): int - ensures CaseError(x) == 1 -{ - match x { - case 0 => 0 - case 1 => 1 - case 2 => 2 - } -} \ No newline at end of file +Dafny program verifier finished with 0 verified, 3 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Definedness.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Definedness.dfy.expect index 22b363ea16b..9a6e1e5b3d8 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Definedness.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Definedness.dfy.expect @@ -45,10 +45,10 @@ Definedness.dfy(196,18): Error: possible division by zero Definedness.dfy(196,22): Error: this loop invariant could not be proved on entry Related message: loop invariant violation Definedness.dfy(196,27): Error: possible division by zero -Definedness.dfy(215,15): Error: a postcondition could not be proved on this return path +Definedness.dfy(219,2): Error: a postcondition could not be proved on this return path Definedness.dfy(217,45): Related location: this is the postcondition that could not be proved Definedness.dfy(224,21): Error: target object might be null -Definedness.dfy(237,15): Error: a postcondition could not be proved on this return path +Definedness.dfy(242,2): Error: a postcondition could not be proved on this return path Definedness.dfy(240,23): Related location: this is the postcondition that could not be proved Dafny program verifier finished with 9 verified, 37 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/FunctionSpecifications.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/FunctionSpecifications.dfy.expect index 6bc52c3a0c8..b20a56e2f1c 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/FunctionSpecifications.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/FunctionSpecifications.dfy.expect @@ -1,15 +1,15 @@ -FunctionSpecifications.dfy(35,24): Error: a postcondition could not be proved on this return path +FunctionSpecifications.dfy(35,45): Error: a postcondition could not be proved on this return path FunctionSpecifications.dfy(31,12): Related location: this is the postcondition that could not be proved -FunctionSpecifications.dfy(45,2): Error: a postcondition could not be proved on this return path +FunctionSpecifications.dfy(45,16): Error: a postcondition could not be proved on this return path FunctionSpecifications.dfy(40,23): Related location: this is the postcondition that could not be proved FunctionSpecifications.dfy(61,10): Error: cannot prove termination; try supplying a decreases clause -FunctionSpecifications.dfy(68,15): Error: a postcondition could not be proved on this return path +FunctionSpecifications.dfy(71,4): Error: a postcondition could not be proved on this return path FunctionSpecifications.dfy(69,21): Related location: this is the postcondition that could not be proved FunctionSpecifications.dfy(117,22): Error: assertion might not hold FunctionSpecifications.dfy(120,22): Error: assertion might not hold FunctionSpecifications.dfy(135,26): Error: assertion might not hold FunctionSpecifications.dfy(139,26): Error: assertion might not hold -FunctionSpecifications.dfy(144,25): Error: a postcondition could not be proved on this return path +FunctionSpecifications.dfy(148,4): Error: a postcondition could not be proved on this return path FunctionSpecifications.dfy(146,28): Related location: this is the postcondition that could not be proved FunctionSpecifications.dfy(155,2): Error: decreases clause might not decrease FunctionSpecifications.dfy(162,2): Error: decreases clause might not decrease diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Refinement.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Refinement.dfy.expect index fa7229dd7ca..eba5fb51b86 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Refinement.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Refinement.dfy.expect @@ -22,7 +22,7 @@ Refinement.dfy[B](15,4): Error: a postcondition could not be proved on this retu Refinement.dfy(33,19): Related location: this is the postcondition that could not be proved Refinement.dfy(69,15): Error: assertion might not hold Refinement.dfy(80,16): Error: assertion might not hold -Refinement.dfy(99,17): Error: a postcondition could not be proved on this return path +Refinement.dfy(100,4): Error: a postcondition could not be proved on this return path Refinement.dfy(78,14): Related location: this is the postcondition that could not be proved Refinement.dfy(102,2): Error: a postcondition could not be proved on this return path Refinement.dfy(83,14): Related location: this is the postcondition that could not be proved From e2514f55bffef2ffc7197d3774155cfb8c40ef62 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 9 Aug 2024 13:14:43 +0200 Subject: [PATCH 21/63] Fix witness expression proofs --- .../Verifier/BoogieGenerator.Types.cs | 88 +++++++++++-------- .../ast/functions/ensuresReporting.dfy | 7 ++ .../ast/functions/ensuresReporting.dfy.expect | 4 +- 3 files changed, 63 insertions(+), 36 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs index 7ec946ea67e..5165d55b670 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs @@ -1486,12 +1486,7 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { // } // check well-formedness of the constraint (including termination, and delayed reads checks) - var constraintCheckBuilder = new BoogieStmtListBuilder(this, options, context); - var builderInitializationArea = new BoogieStmtListBuilder(this, options, context); - var delayer = new ReadsCheckDelayer(etran, null, locals, builderInitializationArea, constraintCheckBuilder); - delayer.DoWithDelayedReadsChecks(false, wfo => { - CheckWellformedAndAssume(decl.Constraint, wfo, locals, constraintCheckBuilder, etran, "predicate subtype constraint"); - }); + var builderInitializationArea = CheckConstraintWellformedness(decl, context, etran, locals, builder); // Check that the type is inhabited. // Note, the possible witness in this check should be coordinated with the compiler, so the compiler knows how to do the initialization @@ -1502,15 +1497,22 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { // check well-formedness of the witness expression (including termination, and reads checks) var ghostCodeContext = codeContext; codeContext = decl.WitnessKind == SubsetTypeDecl.WKind.Compiled ? new CallableWrapper(decl, false) : ghostCodeContext; - CheckWellformed(decl.Witness, new WFOptions(null, true), locals, witnessCheckBuilder, etran); + CheckWellformedWithResult(decl.Witness, new WFOptions(null, true), (innerBuilder, innerWitness, _, _) => { + // check that the witness is assignable to the type of the given bound variable + if (decl is SubsetTypeDecl) { + // Note, for new-types, this has already been checked by CheckWellformed. + CheckResultToBeInType(decl.Witness.tok, innerWitness, decl.Var.Type, locals, innerBuilder, etran); + } + + // check that the witness expression checks out + witnessExpr = Substitute(decl.Constraint, decl.Var, innerWitness); + witnessExpr.tok = decl.Witness.Tok; + var desc = new PODesc.WitnessCheck(witnessString, witnessExpr); + + SplitAndAssertExpression(innerBuilder, witnessExpr, etran, context, desc); + }, locals, witnessCheckBuilder, etran); codeContext = ghostCodeContext; - // check that the witness is assignable to the type of the given bound variable - if (decl is SubsetTypeDecl) { - // Note, for new-types, this has already been checked by CheckWellformed. - CheckResultToBeInType(decl.Witness.tok, decl.Witness, decl.Var.Type, locals, witnessCheckBuilder, etran); - } - // check that the witness expression checks out - witnessExpr = Substitute(decl.Constraint, decl.Var, decl.Witness); + } else if (decl.WitnessKind == SubsetTypeDecl.WKind.CompiledZero) { var witness = Zero(decl.tok, decl.Var.Type); if (witness == null) { @@ -1521,29 +1523,13 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { witnessString = Printer.ExprToString(options, witness); CheckResultToBeInType(decl.tok, witness, decl.Var.Type, locals, witnessCheckBuilder, etran, $"trying witness {witnessString}: "); witnessExpr = Substitute(decl.Constraint, decl.Var, witness); + + witnessExpr.tok = decl.tok; + var desc = new PODesc.WitnessCheck(witnessString, witnessExpr); + SplitAndAssertExpression(witnessCheckBuilder, witnessExpr, etran, context, desc); } } - if (witnessExpr != null) { - var witnessCheckTok = decl.Witness != null ? GetToken(decl.Witness) : decl.tok; - witnessCheckBuilder.Add(new Bpl.AssumeCmd(witnessCheckTok, etran.CanCallAssumption(witnessExpr))); - var witnessCheck = etran.TrExpr(witnessExpr); - - bool splitHappened; - var ss = TrSplitExpr(context, witnessExpr, etran, true, out splitHappened); - var desc = new PODesc.WitnessCheck(witnessString, witnessExpr); - if (!splitHappened) { - witnessCheckBuilder.Add(Assert(witnessCheckTok, etran.TrExpr(witnessExpr), desc)); - } else { - foreach (var split in ss) { - if (split.IsChecked) { - var tok = witnessCheckTok is IToken t ? new NestedToken(t, split.Tok) : witnessCheckTok; - witnessCheckBuilder.Add(AssertNS(tok, split.E, desc)); - } - } - } - } - - builder.Add(new Bpl.IfCmd(decl.tok, null, constraintCheckBuilder.Collect(decl.tok), null, witnessCheckBuilder.Collect(decl.tok))); + PathAsideBlock(decl.Tok, witnessCheckBuilder, builder); var s0 = builderInitializationArea.Collect(decl.tok); var s1 = builder.Collect(decl.tok); @@ -1563,4 +1549,36 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { isAllocContext = null; Reset(); } + + private void SplitAndAssertExpression(BoogieStmtListBuilder witnessCheckBuilder, Expression witnessExpr, + ExpressionTranslator etran, BodyTranslationContext context, PODesc.WitnessCheck desc) + { + witnessCheckBuilder.Add(new Bpl.AssumeCmd(witnessExpr.tok, etran.CanCallAssumption(witnessExpr))); + var witnessCheck = etran.TrExpr(witnessExpr); + + var ss = TrSplitExpr(context, witnessExpr, etran, true, out var splitHappened); + if (!splitHappened) { + witnessCheckBuilder.Add(Assert(witnessExpr.tok, etran.TrExpr(witnessExpr), desc)); + } 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)); + } + } + } + } + + private BoogieStmtListBuilder CheckConstraintWellformedness(RedirectingTypeDecl decl, BodyTranslationContext context, + ExpressionTranslator etran, List locals, BoogieStmtListBuilder builder) + { + var constraintCheckBuilder = new BoogieStmtListBuilder(this, options, context); + var builderInitializationArea = new BoogieStmtListBuilder(this, options, context); + var delayer = new ReadsCheckDelayer(etran, null, locals, builderInitializationArea, constraintCheckBuilder); + delayer.DoWithDelayedReadsChecks(false, wfo => { + CheckWellformedAndAssume(decl.Constraint, wfo, locals, constraintCheckBuilder, etran, "predicate subtype constraint"); + }); + PathAsideBlock(decl.Tok, constraintCheckBuilder, builder); + return builderInitializationArea; + } } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy index 299b8bfb5d9..ef31e255dbb 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy @@ -26,4 +26,11 @@ function CaseError(x: int): int case 1 => 0 // error case _ => 1 } +} + +function LetError(x: int): int + ensures LetError(x) == 1 +{ + var r := 3; + r } \ No newline at end of file diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy.expect index c5df2f77b15..8eaf561c25b 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/functions/ensuresReporting.dfy.expect @@ -4,5 +4,7 @@ ensuresReporting.dfy(16,4): Error: a postcondition could not be proved on this r ensuresReporting.dfy(13,23): Related location: this is the postcondition that could not be proved ensuresReporting.dfy(26,14): Error: a postcondition could not be proved on this return path ensuresReporting.dfy(22,23): Related location: this is the postcondition that could not be proved +ensuresReporting.dfy(35,2): Error: a postcondition could not be proved on this return path +ensuresReporting.dfy(32,22): Related location: this is the postcondition that could not be proved -Dafny program verifier finished with 0 verified, 3 errors +Dafny program verifier finished with 0 verified, 4 errors From 813a5fec1b504716d5e9a5db86303d92534667d2 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 9 Aug 2024 13:17:21 +0200 Subject: [PATCH 22/63] Fix expect file --- .../git-issues/git-issue-1180b.dfy.expect | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-1180b.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-1180b.dfy.expect index e7c31658280..531ba00737a 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-1180b.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-1180b.dfy.expect @@ -1,40 +1,40 @@ -git-issue-1180b.dfy(28,15): Error: a postcondition could not be proved on this return path +git-issue-1180b.dfy(28,32): Error: a postcondition could not be proved on this return path git-issue-1180b.dfy(12,21): Related location: this is the postcondition that could not be proved git-issue-1180b.dfy(29,40): Error: a postcondition could not be proved on this return path git-issue-1180b.dfy(15,18): Related location: this is the postcondition that could not be proved -git-issue-1180b.dfy(34,15): Error: a postcondition could not be proved on this return path +git-issue-1180b.dfy(34,32): Error: a postcondition could not be proved on this return path git-issue-1180b.dfy(12,21): Related location: this is the postcondition that could not be proved git-issue-1180b.dfy(35,40): Error: a postcondition could not be proved on this return path git-issue-1180b.dfy(15,18): Related location: this is the postcondition that could not be proved -git-issue-1180b.dfy(40,15): Error: a postcondition could not be proved on this return path +git-issue-1180b.dfy(40,32): Error: a postcondition could not be proved on this return path git-issue-1180b.dfy(12,21): Related location: this is the postcondition that could not be proved git-issue-1180b.dfy(41,40): Error: a postcondition could not be proved on this return path git-issue-1180b.dfy(15,18): Related location: this is the postcondition that could not be proved -git-issue-1180b.dfy(46,15): Error: a postcondition could not be proved on this return path +git-issue-1180b.dfy(46,32): Error: a postcondition could not be proved on this return path git-issue-1180b.dfy(12,21): Related location: this is the postcondition that could not be proved git-issue-1180b.dfy(47,40): Error: a postcondition could not be proved on this return path git-issue-1180b.dfy(15,18): Related location: this is the postcondition that could not be proved -git-issue-1180b.dfy(56,15): Error: a postcondition could not be proved on this return path +git-issue-1180b.dfy(56,32): Error: a postcondition could not be proved on this return path git-issue-1180b.dfy(12,21): Related location: this is the postcondition that could not be proved git-issue-1180b.dfy(57,40): Error: a postcondition could not be proved on this return path git-issue-1180b.dfy(15,18): Related location: this is the postcondition that could not be proved -git-issue-1180b.dfy(63,15): Error: a postcondition could not be proved on this return path +git-issue-1180b.dfy(63,32): Error: a postcondition could not be proved on this return path git-issue-1180b.dfy(12,21): Related location: this is the postcondition that could not be proved git-issue-1180b.dfy(64,40): Error: a postcondition could not be proved on this return path git-issue-1180b.dfy(15,18): Related location: this is the postcondition that could not be proved -git-issue-1180b.dfy(83,15): Error: a postcondition could not be proved on this return path +git-issue-1180b.dfy(83,32): Error: a postcondition could not be proved on this return path git-issue-1180b.dfy(75,21): Related location: this is the postcondition that could not be proved git-issue-1180b.dfy(84,40): Error: a postcondition could not be proved on this return path git-issue-1180b.dfy(78,18): Related location: this is the postcondition that could not be proved -git-issue-1180b.dfy(103,15): Error: a postcondition could not be proved on this return path +git-issue-1180b.dfy(103,32): Error: a postcondition could not be proved on this return path git-issue-1180b.dfy(95,21): Related location: this is the postcondition that could not be proved git-issue-1180b.dfy(104,40): Error: a postcondition could not be proved on this return path git-issue-1180b.dfy(98,18): Related location: this is the postcondition that could not be proved -git-issue-1180b.dfy(123,15): Error: a postcondition could not be proved on this return path +git-issue-1180b.dfy(123,32): Error: a postcondition could not be proved on this return path git-issue-1180b.dfy(115,21): Related location: this is the postcondition that could not be proved git-issue-1180b.dfy(124,40): Error: a postcondition could not be proved on this return path git-issue-1180b.dfy(118,18): Related location: this is the postcondition that could not be proved -git-issue-1180b.dfy(143,15): Error: a postcondition could not be proved on this return path +git-issue-1180b.dfy(143,32): Error: a postcondition could not be proved on this return path git-issue-1180b.dfy(135,21): Related location: this is the postcondition that could not be proved git-issue-1180b.dfy(144,40): Error: a postcondition could not be proved on this return path git-issue-1180b.dfy(138,18): Related location: this is the postcondition that could not be proved From 50319de01484c64f8d57dec103ff5a04b0a72306 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 9 Aug 2024 14:09:15 +0200 Subject: [PATCH 23/63] Fix subset type check --- .../BoogieGenerator.Functions.Wellformedness.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs index 2d38e8e7887..5a8018c5261 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs @@ -204,17 +204,7 @@ private BoogieStmtListBuilder GetBodyCheckBuilder(Function f, ExpressionTranslat var bodyCheckDelayer = new ReadsCheckDelayer(etran, null, locals, builderInitializationArea, bodyCheckBuilder); bodyCheckDelayer.DoWithDelayedReadsChecks(false, wfo => { void CheckPostcondition(BoogieStmtListBuilder innerBuilder, Expression innerBody, bool adaptBoxing, string prefix) { - generator.CheckSubsetType(etran, innerBody, selfCall, f.Body.Type, innerBuilder, adaptBoxing, prefix); - // Contract.Assert(f.ResultType != null); - // var bResult = etran.TrExpr(innerBody); - // generator.CheckSubrange(f.Body.tok, bResult, f.Body.Type, f.ResultType, f.Body, b); - // - // bodyCheckBuilder.Add(generator.TrAssumeCmdWithDependenciesAndExtend(etran, f.Body.tok, f.Body, - // e => Bpl.Expr.Eq(selfCall, generator.AdaptBoxing(f.Body.tok, e, f.Body.Type, f.ResultType)), - // prefix)); - // bodyCheckBuilder.Add(TrAssumeCmd(f.Body.tok, etran.CanCallAssumption(f.Body))); - // bodyCheckBuilder.Add(new CommentCmd("CheckWellformedWithResult: any expression")); - // bodyCheckBuilder.Add(TrAssumeCmd(f.Body.tok, generator.MkIs(selfCall, f.ResultType))); + generator.CheckSubsetType(etran, innerBody, selfCall, f.ResultType, innerBuilder, adaptBoxing, prefix); if (f.Result != null) { var cmd = TrAssumeCmd(f.tok, Expr.Eq(selfCall, generator.TrVar(f.tok, f.Result))); generator.proofDependencies?.AddProofDependencyId(cmd, f.tok, new FunctionDefinitionDependency(f)); From 597000c6c9354f8d0745d6601129ce3bc3b4bcc3 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 9 Aug 2024 14:33:06 +0200 Subject: [PATCH 24/63] Cleanup WF check for StmtExpr, to reduce nesting --- .../BoogieGenerator.ExpressionWellformed.cs | 30 ++++++++++++++----- .../LitTest/git-issues/git-issue-3804c.dfy | 6 ++-- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index d13335a60e9..2bb5d48fbbb 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -1144,9 +1144,7 @@ void CheckOperand(Expression operand) { break; } case StmtExpr stmtExpr: - var bodyBuilder = new BoogieStmtListBuilder(this, builder.Options, builder.Context); - CheckWellformedStmtExpr(stmtExpr, wfOptions, checkPostcondition, locals, bodyBuilder, etran); - PathAsideBlock(stmtExpr.tok, bodyBuilder, builder); + CheckWellformedStmtExpr(stmtExpr, wfOptions, checkPostcondition, locals, builder, etran); checkPostcondition = null; break; case ITEExpr iteExpr: { @@ -1422,18 +1420,36 @@ private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, CheckPostcondition c private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions wfOptions, CheckPostcondition checkPostcondition, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { + + var bodyBuilder = new BoogieStmtListBuilder(this, builder.Options, builder.Context); + + var statements = new List() { stmtExpr.S }; + Expression expression = stmtExpr.E; + while (expression is StmtExpr nestedStmtExpr) { + statements.Add(nestedStmtExpr.S); + expression = nestedStmtExpr.E; + } + // If we're inside an "old" expression, then "etran" will know how to translate // expressions. However, here, we're also having to translate e.S, which is a // Statement. Since statement translation (in particular, translation of CallStmt's) // work directly on the global variable $Heap, we temporarily change its value here. if (etran.UsesOldHeap) { - BuildWithHeapAs(stmtExpr.S.Tok, etran.HeapExpr, "StmtExpr#", locals, builder, - () => TrStmt(stmtExpr.S, builder, locals, etran)); + BuildWithHeapAs(stmtExpr.S.Tok, etran.HeapExpr, "StmtExpr#", locals, bodyBuilder, + () => { + foreach (var statement in statements) { + TrStmt(statement, bodyBuilder, locals, etran); + } + }); } else { - TrStmt(stmtExpr.S, builder, locals, etran); + foreach (var statement in statements) { + TrStmt(statement, bodyBuilder, locals, etran); + } } - CheckWellformedWithResult(stmtExpr.E, wfOptions, checkPostcondition, locals, builder, etran, "statement expression result"); + CheckWellformedWithResult(expression, wfOptions, checkPostcondition, locals, bodyBuilder, etran, "statement expression result"); + + PathAsideBlock(stmtExpr.tok, bodyBuilder, builder); } /// diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3804c.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3804c.dfy index cbf89f9392c..ec453e7ad8d 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3804c.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3804c.dfy @@ -8,7 +8,7 @@ function Test(x: bool): int { assert p1: x ==> P(1) by { reveal r; } assert p2: !x ==> P(2) by { reveal r; } - var x := + var y := match x { case true => assert p11: P(1) by { reveal p1; } @@ -21,6 +21,6 @@ function Test(x: bool): int assert P(2) by { reveal p21; } // p21 can be revealed and this assertion is visible outside. 2 }; - assert P(x); - x + assert P(y); + y } \ No newline at end of file From 243c5478325da73ad8ccd9ad5e522265c2795522 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 9 Aug 2024 14:44:14 +0200 Subject: [PATCH 25/63] Refactoring --- .../BoogieGenerator.ExpressionWellformed.cs | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 2bb5d48fbbb..6c4e3758c3b 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -1532,14 +1532,16 @@ void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, CheckPostc for (int i = 0; i < e.LHSs.Count; i++) { var pat = e.LHSs[i]; var rhs = e.RHSs[i]; - var nm = varNameGen.FreshId(string.Format("#{0}#", i)); + var nm = varNameGen.FreshId($"#{i}#"); var r = new Bpl.LocalVariable(pat.tok, new Bpl.TypedIdent(pat.tok, nm, TrType(pat.Expr.Type))); locals.Add(r); var rIe = new Bpl.IdentifierExpr(rhs.tok, r); - CheckPostcondition checkPostconditionForLhs = (innerBuilder, body, adaptBoxing, prefix) => { + + void CheckPostconditionForRhs(BoogieStmtListBuilder innerBuilder, Expression body, bool adaptBoxing, string prefix) { CheckSubsetType(etran, body, rIe, pat.Expr.Type, innerBuilder, adaptBoxing, prefix); - }; - CheckWellformedWithResult(e.RHSs[i], wfOptions, checkPostconditionForLhs, locals, builder, etran, "let expression binding RHS well-formed"); + } + + CheckWellformedWithResult(e.RHSs[i], wfOptions, CheckPostconditionForRhs, locals, builder, etran, "let expression binding RHS well-formed"); CheckCasePatternShape(pat, rhs, rIe, rhs.tok, pat.Expr.Type, builder); var substExpr = Substitute(pat.Expr, null, substMap); builder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, e.tok, substExpr, e => Bpl.Expr.Eq(e, rIe), "let expression binding")); @@ -1578,17 +1580,17 @@ void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, CheckPostc var letBody = Substitute(e.Body, null, substMap); CheckWellformed(letBody, wfOptions, locals, builder, etran); if (e.Constraint_Bounds != null) { - var substMap_prime = SetupBoundVarsAsLocals(lhsVars, builder, locals, etran); - var nonGhostMap_prime = new Dictionary(); + var substMapPrime = SetupBoundVarsAsLocals(lhsVars, builder, locals, etran); + var nonGhostMapPrime = new Dictionary(); foreach (BoundVar bv in lhsVars) { - nonGhostMap_prime.Add(bv, bv.IsGhost ? substMap[bv] : substMap_prime[bv]); + nonGhostMapPrime.Add(bv, bv.IsGhost ? substMap[bv] : substMapPrime[bv]); } - var rhs_prime = Substitute(e.RHSs[0], null, nonGhostMap_prime); - var letBody_prime = Substitute(e.Body, null, nonGhostMap_prime); - builder.Add(TrAssumeCmd(e.tok, etran.CanCallAssumption(rhs_prime))); - builder.Add(TrAssumeCmdWithDependencies(etran, e.tok, rhs_prime, "assign-such-that constraint")); - builder.Add(TrAssumeCmd(e.tok, etran.CanCallAssumption(letBody_prime))); - var eq = Expression.CreateEq(letBody, letBody_prime, e.Body.Type); + var rhsPrime = Substitute(e.RHSs[0], null, nonGhostMapPrime); + var letBodyPrime = Substitute(e.Body, null, nonGhostMapPrime); + builder.Add(TrAssumeCmd(e.tok, etran.CanCallAssumption(rhsPrime))); + builder.Add(TrAssumeCmdWithDependencies(etran, e.tok, rhsPrime, "assign-such-that constraint")); + builder.Add(TrAssumeCmd(e.tok, etran.CanCallAssumption(letBodyPrime))); + var eq = Expression.CreateEq(letBody, letBodyPrime, e.Body.Type); builder.Add(Assert(GetToken(e), etran.TrExpr(eq), new PODesc.LetSuchThatUnique(e.RHSs[0], e.BoundVars.ToList()))); } From aa43a4fc9c608bc99e503a0948a66a050c8d8de3 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 9 Aug 2024 16:10:48 +0200 Subject: [PATCH 26/63] Fix bug --- .../BoogieGenerator.ExpressionWellformed.cs | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 6c4e3758c3b..f24bf73bf20 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -678,12 +678,6 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, builder.Add(new Bpl.CommentCmd("assume allocatedness for receiver argument to function")); builder.Add(TrAssumeCmd(e.Receiver.tok, MkIsAllocBox(BoxIfNecessary(e.Receiver.tok, etran.TrExpr(e.Receiver), e.Receiver.Type), et, etran.HeapExpr))); } - // check well-formedness of the other parameters - foreach (Expression arg in e.Args) { - if (!(arg is DefaultValueExpression)) { - CheckWellformed(arg, wfOptions, locals, builder, etran); - } - } // create a local variable for each formal parameter, and assign each actual parameter to the corresponding local Dictionary substMap = new Dictionary(); Dictionary directSubstMap = new Dictionary(); @@ -694,14 +688,22 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Type et = p.Type.Subst(e.GetTypeArgumentSubstitutions()); LocalVariable local = new LocalVariable(p.RangeToken, "##" + p.Name, et, p.IsGhost); local.type = local.SyntacticType; // resolve local here - IdentifierExpr ie = new IdentifierExpr(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator)); - ie.Var = local; ie.Type = ie.Var.Type; // resolve ie here + var ie = new IdentifierExpr(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator)) + { + Var = local + }; + 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)))); Bpl.IdentifierExpr lhs = (Bpl.IdentifierExpr)etran.TrExpr(ie); // TODO: is this cast always justified? Expression ee = e.Args[i]; directSubstMap.Add(p, ee); - CheckSubrange(ee.tok, etran.TrExpr(ee), ee.Type, et, ee, builder); + + if (!(ee is DefaultValueExpression)) { + CheckWellformedWithResult(ee, wfOptions, (innerBuilder, innerBody, _, _) => { + CheckSubrange(innerBody.tok, etran.TrExpr(innerBody), ee.Type, et, ee, innerBuilder); + }, locals, builder, etran); + } Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(p.tok, lhs, AdaptBoxing(p.tok, etran.TrExpr(ee), cce.NonNull(ee.Type), et)); builder.Add(cmd); if (!etran.UsesOldHeap) { From 0d08aefc15913461f21e78d728563a6e4d5d499c Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 9 Aug 2024 17:05:49 +0200 Subject: [PATCH 27/63] Fixes --- ...oogieGenerator.Functions.Wellformedness.cs | 40 ++++++++++--------- .../func-depth-fail.dfy.expect | 2 +- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs index 5a8018c5261..cc613f79275 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs @@ -201,27 +201,31 @@ private BoogieStmtListBuilder GetBodyCheckBuilder(Function f, ExpressionTranslat var bodyCheckBuilder = new BoogieStmtListBuilder(generator, generator.options, context); bodyCheckBuilder.Add(new CommentCmd("Check Wfness of body and result subset type constraint")); if (f.Body != null && generator.RevealedInScope(f)) { - var bodyCheckDelayer = new ReadsCheckDelayer(etran, null, locals, builderInitializationArea, bodyCheckBuilder); - bodyCheckDelayer.DoWithDelayedReadsChecks(false, wfo => { - void CheckPostcondition(BoogieStmtListBuilder innerBuilder, Expression innerBody, bool adaptBoxing, string prefix) { - generator.CheckSubsetType(etran, innerBody, selfCall, f.ResultType, innerBuilder, adaptBoxing, prefix); - if (f.Result != null) { - var cmd = TrAssumeCmd(f.tok, Expr.Eq(selfCall, generator.TrVar(f.tok, f.Result))); - generator.proofDependencies?.AddProofDependencyId(cmd, f.tok, new FunctionDefinitionDependency(f)); - innerBuilder.Add(cmd); - } - innerBuilder.Add(new ReturnCmd(innerBody.Tok)); + var doReadsChecks = etran.readsFrame != null; + var wfo = new WFOptions(null, doReadsChecks, doReadsChecks, false); + + void CheckPostcondition(BoogieStmtListBuilder innerBuilder, Expression innerBody, bool adaptBoxing, string prefix) { + generator.CheckSubsetType(etran, innerBody, selfCall, f.ResultType, innerBuilder, adaptBoxing, prefix); + if (f.Result != null) { + var cmd = TrAssumeCmd(f.tok, Expr.Eq(selfCall, generator.TrVar(f.tok, f.Result))); + generator.proofDependencies?.AddProofDependencyId(cmd, f.tok, new FunctionDefinitionDependency(f)); + innerBuilder.Add(cmd); + } + if (doReadsChecks) { + wfo.ProcessSavedReadsChecks(locals, builderInitializationArea, innerBuilder); } - - generator.CheckWellformedWithResult(f.Body, wfo, CheckPostcondition, locals, bodyCheckBuilder, etran, "function call result"); - }); - // Enforce 'older' conditions - var (olderParameterCount, olderCondition) = generator.OlderCondition(f, selfCall, parameters); - if (olderParameterCount != 0) { - bodyCheckBuilder.Add(generator.Assert(f.tok, olderCondition, - new PODesc.IsOlderProofObligation(olderParameterCount, f.Ins.Count + (f.IsStatic ? 0 : 1)))); + // Enforce 'older' conditions + var (olderParameterCount, olderCondition) = generator.OlderCondition(f, selfCall, parameters); + if (olderParameterCount != 0) { + innerBuilder.Add(generator.Assert(f.tok, olderCondition, + new PODesc.IsOlderProofObligation(olderParameterCount, f.Ins.Count + (f.IsStatic ? 0 : 1)))); + } + innerBuilder.Add(new ReturnCmd(innerBody.Tok)); } + + generator.CheckWellformedWithResult(f.Body, wfo, CheckPostcondition, locals, bodyCheckBuilder, etran, "function call result"); + } bodyCheckBuilder.Add(TrAssumeCmd(f.tok, Expr.False)); diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/autoRevealDependencies/func-depth-fail.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/autoRevealDependencies/func-depth-fail.dfy.expect index 49a66e17ae7..2f69d3fbc83 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/autoRevealDependencies/func-depth-fail.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/autoRevealDependencies/func-depth-fail.dfy.expect @@ -1,4 +1,4 @@ -func-depth-fail.dfy(9,37): Error: a postcondition could not be proved on this return path +func-depth-fail.dfy(12,2): Error: a postcondition could not be proved on this return path func-depth-fail.dfy(10,10): Related location: this is the postcondition that could not be proved Dafny program verifier finished with 3 verified, 1 error From 1374af24ef7aff9dddae0bbc23f06cba16b6243b Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 9 Aug 2024 17:13:48 +0200 Subject: [PATCH 28/63] Fix for default parameters --- Source/DafnyCore/Verifier/BoogieGenerator.cs | 10 ++++++---- .../LitTest/dafny0/DefaultParameters.dfy.expect | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index e30bd6c6a5e..e5714f40317 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -2338,10 +2338,12 @@ void AddWellformednessCheck(DatatypeCtor ctor) { // check well-formedness of each default-value expression foreach (var formal in ctor.Formals.Where(formal => formal.DefaultValue != null)) { var e = formal.DefaultValue; - CheckWellformed(e, new WFOptions(null, true, - false, true), locals, builder, etran); - builder.Add(new Bpl.AssumeCmd(e.tok, etran.CanCallAssumption(e))); - CheckSubrange(e.tok, etran.TrExpr(e), e.Type, formal.Type, e, builder); + CheckWellformedWithResult(e, new WFOptions(null, true, + false, true), + (innerBuilder, innerExpr, _, _) => { + builder.Add(new Bpl.AssumeCmd(e.tok, etran.CanCallAssumption(e))); + CheckSubrange(innerExpr.tok, etran.TrExpr(innerExpr), e.Type, formal.Type, e, innerBuilder); + } ,locals, builder, etran); } if (EmitImplementation(ctor.Attributes)) { diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/DefaultParameters.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/DefaultParameters.dfy.expect index 374679dc3f9..91e6a31a26e 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/DefaultParameters.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/DefaultParameters.dfy.expect @@ -28,7 +28,7 @@ DefaultParameters.dfy(178,41): Error: value does not satisfy the subset constrai DefaultParameters.dfy(179,33): Error: value does not satisfy the subset constraints of 'nat' DefaultParameters.dfy(180,35): Error: value does not satisfy the subset constraints of 'nat' DefaultParameters.dfy(180,35): Error: value does not satisfy the subset constraints of 'nat' -DefaultParameters.dfy(181,40): Error: value does not satisfy the subset constraints of 'nat' +DefaultParameters.dfy(181,37): Error: value does not satisfy the subset constraints of 'nat' DefaultParameters.dfy(194,39): Error: default-value expression is not allowed to involve recursive or mutually recursive calls DefaultParameters.dfy(200,40): Error: default-value expression is not allowed to involve recursive or mutually recursive calls DefaultParameters.dfy(211,62): Error: default-value expression is not allowed to involve recursive or mutually recursive calls From d92ebf79ea00a805d3d1ca75a17478c3cb193834 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 9 Aug 2024 17:26:19 +0200 Subject: [PATCH 29/63] Fix some expect files --- .../LitTests/LitTest/dafny0/SmallTests.dfy.expect | 2 +- .../LitTest/dafny0/Superposition.legacy.dfy.expect | 4 ++-- .../LitTests/LitTest/dafny4/git-issue245.dfy.expect | 8 ++++---- .../LitTest/git-issues/git-issue-3804b.dfy.expect | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SmallTests.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SmallTests.dfy.expect index 2759917e76c..13af3fb09b0 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SmallTests.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SmallTests.dfy.expect @@ -41,7 +41,7 @@ SmallTests.dfy(396,3): Error: cannot prove termination; try supplying a decrease SmallTests.dfy(408,11): Error: assertion might not hold SmallTests.dfy(418,11): Error: assertion might not hold SmallTests.dfy(428,5): Error: cannot prove termination; try supplying a decreases clause -SmallTests.dfy(440,15): Error: a postcondition could not be proved on this return path +SmallTests.dfy(445,2): Error: a postcondition could not be proved on this return path SmallTests.dfy(443,40): Related location: this is the postcondition that could not be proved SmallTests.dfy(604,11): Error: assertion might not hold SmallTests.dfy(618,19): Error: left-hand sides n.next and n.next.next might refer to the same location diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Superposition.legacy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Superposition.legacy.dfy.expect index c6273819d6e..d2c642f6533 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Superposition.legacy.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Superposition.legacy.dfy.expect @@ -6,12 +6,12 @@ Verifying M0.C.M (correctness) ... Verifying M0.C.Q (well-formedness) ... [3 proof obligations] error -Superposition.legacy.dfy(20,20): Error: a postcondition could not be proved on this return path +Superposition.legacy.dfy(23,6): Error: a postcondition could not be proved on this return path Superposition.legacy.dfy(21,25): Related location: this is the postcondition that could not be proved Verifying M0.C.R (well-formedness) ... [3 proof obligations] error -Superposition.legacy.dfy(26,20): Error: a postcondition could not be proved on this return path +Superposition.legacy.dfy(29,6): Error: a postcondition could not be proved on this return path Superposition.legacy.dfy(27,25): Related location: this is the postcondition that could not be proved Verifying M1.C.M (correctness) ... diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/git-issue245.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/git-issue245.dfy.expect index 3e75899048f..78d5583cf15 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/git-issue245.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/git-issue245.dfy.expect @@ -6,13 +6,13 @@ git-issue245.dfy(64,11): Error: the function must provide an equal or more detai git-issue245.dfy(68,11): Error: the function must provide an equal or more detailed postcondition than in its parent trait git-issue245.dfy(72,11): Error: the function must provide an equal or more detailed postcondition than in its parent trait git-issue245.dfy(76,11): Error: the function must provide an equal or more detailed postcondition than in its parent trait -git-issue245.dfy(84,11): Error: a postcondition could not be proved on this return path +git-issue245.dfy(86,7): Error: a postcondition could not be proved on this return path git-issue245.dfy(85,17): Related location: this is the postcondition that could not be proved -git-issue245.dfy(88,11): Error: a postcondition could not be proved on this return path +git-issue245.dfy(90,7): Error: a postcondition could not be proved on this return path git-issue245.dfy(89,19): Related location: this is the postcondition that could not be proved -git-issue245.dfy(92,11): Error: a postcondition could not be proved on this return path +git-issue245.dfy(94,7): Error: a postcondition could not be proved on this return path git-issue245.dfy(93,19): Related location: this is the postcondition that could not be proved -git-issue245.dfy(96,11): Error: a postcondition could not be proved on this return path +git-issue245.dfy(98,7): Error: a postcondition could not be proved on this return path git-issue245.dfy(97,17): Related location: this is the postcondition that could not be proved Dafny program verifier finished with 24 verified, 12 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3804b.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3804b.dfy.expect index 04076ef15b3..4f107c71bec 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3804b.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3804b.dfy.expect @@ -1,6 +1,6 @@ -git-issue-3804b.dfy(14,9): Error: a postcondition could not be proved on this return path +git-issue-3804b.dfy(18,2): Error: a postcondition could not be proved on this return path git-issue-3804b.dfy(16,12): Related location: this is the postcondition that could not be proved -git-issue-3804b.dfy(23,9): Error: a postcondition could not be proved on this return path +git-issue-3804b.dfy(27,2): Error: a postcondition could not be proved on this return path git-issue-3804b.dfy(25,12): Related location: this is the postcondition that could not be proved git-issue-3804b.dfy(40,20): Error: assertion might not hold git-issue-3804b.dfy(48,21): Error: assertion might not hold From 75f8b7209a43680ee5c70bd8f672b9d5042027c6 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Sat, 10 Aug 2024 16:19:56 +0200 Subject: [PATCH 30/63] Fixed reads bug --- .../BoogieGenerator.ExpressionWellformed.cs | 25 +++++++++---------- ...oogieGenerator.Functions.Wellformedness.cs | 19 ++++++++++---- .../Verifier/BoogieGenerator.Types.cs | 14 +++++------ Source/DafnyCore/Verifier/BoogieGenerator.cs | 2 +- .../Verifier/ProofDependencyManager.cs | 8 ++++++ .../LitTest/git-issues/git-issue-3804c.dfy | 6 ++--- 6 files changed, 44 insertions(+), 30 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index f24bf73bf20..98bdf4d0050 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -39,7 +39,7 @@ private class WFOptions { 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 List Asserts; + public readonly List> Asserts; public readonly bool LValueContext; public readonly Bpl.QKeyValue AssertKv; @@ -54,12 +54,12 @@ public WFOptions(Function selfCallsAllowance, bool doReadsChecks, DoOnlyCoarseGrainedTerminationChecks = doOnlyCoarseGrainedTerminationChecks; if (saveReadsChecks) { Locals = new List(); - Asserts = new List(); + Asserts = new(); } } private WFOptions(Function selfCallsAllowance, bool doReadsChecks, bool doOnlyCoarseGrainedTerminationChecks, - List locals, List asserts, bool lValueContext, Bpl.QKeyValue assertKv) { + List locals, List> asserts, bool lValueContext, Bpl.QKeyValue assertKv) { SelfCallsAllowance = selfCallsAllowance; DoReadsChecks = doReadsChecks; DoOnlyCoarseGrainedTerminationChecks = doOnlyCoarseGrainedTerminationChecks; @@ -93,7 +93,7 @@ public WFOptions WithLValueContext(bool lValueContext) { return (t, e, d, qk) => { if (Locals != null) { var b = BplLocalVar(tran.CurrentIdGenerator.FreshId("b$reqreads#"), Bpl.Type.Bool, Locals); - Asserts.Add(tran.Assert(t, b, d, qk)); + Asserts.Add(() => tran.Assert(t, b, d, qk)); builder.Add(Bpl.Cmd.SimpleAssign(e.tok, (Bpl.IdentifierExpr)b, e)); } else { builder.Add(tran.Assert(t, e, d, qk)); @@ -125,7 +125,7 @@ public void ProcessSavedReadsChecks(List locals, BoogieStmtListBuilder } // assert b$reads_guards#0; ... foreach (var a in Asserts) { - builder.Add(a); + builder.Add(a()); } } } @@ -688,17 +688,16 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Type et = p.Type.Subst(e.GetTypeArgumentSubstitutions()); LocalVariable local = new LocalVariable(p.RangeToken, "##" + p.Name, et, p.IsGhost); local.type = local.SyntacticType; // resolve local here - var ie = new IdentifierExpr(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator)) - { - Var = local - }; + var ie = new IdentifierExpr(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator)) { + Var = local + }; 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)))); Bpl.IdentifierExpr lhs = (Bpl.IdentifierExpr)etran.TrExpr(ie); // TODO: is this cast always justified? Expression ee = e.Args[i]; directSubstMap.Add(p, ee); - + if (!(ee is DefaultValueExpression)) { CheckWellformedWithResult(ee, wfOptions, (innerBuilder, innerBody, _, _) => { CheckSubrange(innerBody.tok, etran.TrExpr(innerBody), ee.Type, et, ee, innerBuilder); @@ -1422,7 +1421,7 @@ private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, CheckPostcondition c private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions wfOptions, CheckPostcondition checkPostcondition, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { - + var bodyBuilder = new BoogieStmtListBuilder(this, builder.Options, builder.Context); var statements = new List() { stmtExpr.S }; @@ -1431,7 +1430,7 @@ private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions wfOptions, Che statements.Add(nestedStmtExpr.S); expression = nestedStmtExpr.E; } - + // If we're inside an "old" expression, then "etran" will know how to translate // expressions. However, here, we're also having to translate e.S, which is a // Statement. Since statement translation (in particular, translation of CallStmt's) @@ -1450,7 +1449,7 @@ private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions wfOptions, Che } CheckWellformedWithResult(expression, wfOptions, checkPostcondition, locals, bodyBuilder, etran, "statement expression result"); - + PathAsideBlock(stmtExpr.tok, bodyBuilder, builder); } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs index cc613f79275..c2ab37131d0 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs @@ -42,7 +42,7 @@ public void Check(Function f) { var mod = new List { ordinaryEtran.HeapCastToIdentifierExpr, }; - + var context = new BodyTranslationContext(f.ContainsHide); var ens = new List(); foreach (AttributedExpression ensures in f.Ens) { @@ -203,7 +203,7 @@ private BoogieStmtListBuilder GetBodyCheckBuilder(Function f, ExpressionTranslat if (f.Body != null && generator.RevealedInScope(f)) { var doReadsChecks = etran.readsFrame != null; var wfo = new WFOptions(null, doReadsChecks, doReadsChecks, false); - + void CheckPostcondition(BoogieStmtListBuilder innerBuilder, Expression innerBody, bool adaptBoxing, string prefix) { generator.CheckSubsetType(etran, innerBody, selfCall, f.ResultType, innerBuilder, adaptBoxing, prefix); if (f.Result != null) { @@ -212,9 +212,12 @@ void CheckPostcondition(BoogieStmtListBuilder innerBuilder, Expression innerBody innerBuilder.Add(cmd); } if (doReadsChecks) { - wfo.ProcessSavedReadsChecks(locals, builderInitializationArea, innerBuilder); + // assert b$reads_guards#0; ... + foreach (var a in wfo.Asserts) { + innerBuilder.Add(a()); + } } - + // Enforce 'older' conditions var (olderParameterCount, olderCondition) = generator.OlderCondition(f, selfCall, parameters); if (olderParameterCount != 0) { @@ -225,7 +228,13 @@ void CheckPostcondition(BoogieStmtListBuilder innerBuilder, Expression innerBody } generator.CheckWellformedWithResult(f.Body, wfo, CheckPostcondition, locals, bodyCheckBuilder, etran, "function call result"); - + + // var b$reads_guards#0 : bool ... + locals.AddRange(wfo.Locals); + // b$reads_guards#0 := true ... + foreach (var cmd in wfo.AssignLocals) { + builderInitializationArea.Add(cmd); + } } bodyCheckBuilder.Add(TrAssumeCmd(f.tok, Expr.False)); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs index 5165d55b670..17d699b7859 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs @@ -1503,16 +1503,16 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { // Note, for new-types, this has already been checked by CheckWellformed. CheckResultToBeInType(decl.Witness.tok, innerWitness, decl.Var.Type, locals, innerBuilder, etran); } - + // check that the witness expression checks out witnessExpr = Substitute(decl.Constraint, decl.Var, innerWitness); witnessExpr.tok = decl.Witness.Tok; var desc = new PODesc.WitnessCheck(witnessString, witnessExpr); - + SplitAndAssertExpression(innerBuilder, witnessExpr, etran, context, desc); }, locals, witnessCheckBuilder, etran); codeContext = ghostCodeContext; - + } else if (decl.WitnessKind == SubsetTypeDecl.WKind.CompiledZero) { var witness = Zero(decl.tok, decl.Var.Type); if (witness == null) { @@ -1523,7 +1523,7 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { witnessString = Printer.ExprToString(options, witness); CheckResultToBeInType(decl.tok, witness, decl.Var.Type, locals, witnessCheckBuilder, etran, $"trying witness {witnessString}: "); witnessExpr = Substitute(decl.Constraint, decl.Var, witness); - + witnessExpr.tok = decl.tok; var desc = new PODesc.WitnessCheck(witnessString, witnessExpr); SplitAndAssertExpression(witnessCheckBuilder, witnessExpr, etran, context, desc); @@ -1551,8 +1551,7 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { } private void SplitAndAssertExpression(BoogieStmtListBuilder witnessCheckBuilder, Expression witnessExpr, - ExpressionTranslator etran, BodyTranslationContext context, PODesc.WitnessCheck desc) - { + ExpressionTranslator etran, BodyTranslationContext context, PODesc.WitnessCheck desc) { witnessCheckBuilder.Add(new Bpl.AssumeCmd(witnessExpr.tok, etran.CanCallAssumption(witnessExpr))); var witnessCheck = etran.TrExpr(witnessExpr); @@ -1570,8 +1569,7 @@ private void SplitAndAssertExpression(BoogieStmtListBuilder witnessCheckBuilder, } private BoogieStmtListBuilder CheckConstraintWellformedness(RedirectingTypeDecl decl, BodyTranslationContext context, - ExpressionTranslator etran, List locals, BoogieStmtListBuilder builder) - { + ExpressionTranslator etran, List locals, BoogieStmtListBuilder builder) { var constraintCheckBuilder = new BoogieStmtListBuilder(this, options, context); var builderInitializationArea = new BoogieStmtListBuilder(this, options, context); var delayer = new ReadsCheckDelayer(etran, null, locals, builderInitializationArea, constraintCheckBuilder); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index e5714f40317..9fd96bf5f21 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -2343,7 +2343,7 @@ void AddWellformednessCheck(DatatypeCtor ctor) { (innerBuilder, innerExpr, _, _) => { builder.Add(new Bpl.AssumeCmd(e.tok, etran.CanCallAssumption(e))); CheckSubrange(innerExpr.tok, etran.TrExpr(innerExpr), e.Type, formal.Type, e, innerBuilder); - } ,locals, builder, etran); + }, locals, builder, etran); } if (EmitImplementation(ctor.Attributes)) { diff --git a/Source/DafnyCore/Verifier/ProofDependencyManager.cs b/Source/DafnyCore/Verifier/ProofDependencyManager.cs index 52ec364eeb2..5145a148fa9 100644 --- a/Source/DafnyCore/Verifier/ProofDependencyManager.cs +++ b/Source/DafnyCore/Verifier/ProofDependencyManager.cs @@ -32,6 +32,14 @@ public string GetProofDependencyId(ProofDependency dep) { return idString; } + public ProofDependency GetProofDependency(ICarriesAttributes declaration) { + var id = QKeyValue.FindStringAttribute(declaration.Attributes, idAttributeName); + if (id == null) { + return null; + } + return ProofDependenciesById[id]; + } + public void SetCurrentDefinition(string verificationScopeId) { currentDefinition = verificationScopeId; } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3804c.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3804c.dfy index ec453e7ad8d..0552545b9f1 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3804c.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3804c.dfy @@ -8,7 +8,7 @@ function Test(x: bool): int { assert p1: x ==> P(1) by { reveal r; } assert p2: !x ==> P(2) by { reveal r; } - var y := + var x := match x { case true => assert p11: P(1) by { reveal p1; } @@ -21,6 +21,6 @@ function Test(x: bool): int assert P(2) by { reveal p21; } // p21 can be revealed and this assertion is visible outside. 2 }; - assert P(y); - y + assert P(x); // No longer verifies since we scoped expression proofs + x } \ No newline at end of file From 3823092fbb9534815fcbec45ebce8cd87a890ae7 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 12 Aug 2024 13:26:22 +0200 Subject: [PATCH 31/63] Remove unused method --- Source/DafnyCore/Verifier/ProofDependencyManager.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Source/DafnyCore/Verifier/ProofDependencyManager.cs b/Source/DafnyCore/Verifier/ProofDependencyManager.cs index 5145a148fa9..52ec364eeb2 100644 --- a/Source/DafnyCore/Verifier/ProofDependencyManager.cs +++ b/Source/DafnyCore/Verifier/ProofDependencyManager.cs @@ -32,14 +32,6 @@ public string GetProofDependencyId(ProofDependency dep) { return idString; } - public ProofDependency GetProofDependency(ICarriesAttributes declaration) { - var id = QKeyValue.FindStringAttribute(declaration.Attributes, idAttributeName); - if (id == null) { - return null; - } - return ProofDependenciesById[id]; - } - public void SetCurrentDefinition(string verificationScopeId) { currentDefinition = verificationScopeId; } From c2859db7c625b658350f88353b49e720ce1e257b Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 12 Aug 2024 13:30:03 +0200 Subject: [PATCH 32/63] Removed PathAside for StmtExpr, and remove statementExpressionScope.dfy test --- .../BoogieGenerator.ExpressionWellformed.cs | 12 ++-- .../expressions/statementExpressionScope.dfy | 63 ------------------- .../statementExpressionScope.dfy.expect | 8 --- 3 files changed, 4 insertions(+), 79 deletions(-) delete mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy delete mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy.expect diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 98bdf4d0050..5e755e2d8f8 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -1422,8 +1422,6 @@ private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, CheckPostcondition c private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions wfOptions, CheckPostcondition checkPostcondition, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { - var bodyBuilder = new BoogieStmtListBuilder(this, builder.Options, builder.Context); - var statements = new List() { stmtExpr.S }; Expression expression = stmtExpr.E; while (expression is StmtExpr nestedStmtExpr) { @@ -1436,21 +1434,19 @@ private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions wfOptions, Che // Statement. Since statement translation (in particular, translation of CallStmt's) // work directly on the global variable $Heap, we temporarily change its value here. if (etran.UsesOldHeap) { - BuildWithHeapAs(stmtExpr.S.Tok, etran.HeapExpr, "StmtExpr#", locals, bodyBuilder, + BuildWithHeapAs(stmtExpr.S.Tok, etran.HeapExpr, "StmtExpr#", locals, builder, () => { foreach (var statement in statements) { - TrStmt(statement, bodyBuilder, locals, etran); + TrStmt(statement, builder, locals, etran); } }); } else { foreach (var statement in statements) { - TrStmt(statement, bodyBuilder, locals, etran); + TrStmt(statement, builder, locals, etran); } } - CheckWellformedWithResult(expression, wfOptions, checkPostcondition, locals, bodyBuilder, etran, "statement expression result"); - - PathAsideBlock(stmtExpr.tok, bodyBuilder, builder); + CheckWellformedWithResult(expression, wfOptions, checkPostcondition, locals, builder, etran, "statement expression result"); } /// diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy deleted file mode 100644 index 5f1a9808c16..00000000000 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy +++ /dev/null @@ -1,63 +0,0 @@ -// RUN: ! %verify --type-system-refresh --allow-axioms --bprint:%t.bpl --isolate-assertions %s > %t -// RUN: %diff "%s.expect" "%t" - -function StatementExpressionValueAndEnsures(): int - ensures StatementExpressionValueAndEnsures() == 42 -{ - assert true; 42 -} - -function StatementExpressionAssumeAndFunctionEnsures(): int - ensures 10 == 11 // no error, since the statement expression can be used for the ensures clause -{ - assume false; 42 -} - -function StatementExpressionAssumeInLetAndFunctionEnsures(): int - ensures 10 == 11 // error, since the assume false does not leak from a let -{ - var x := (assume false; 10); x -} - -function StatementExpressionAndSubsetResult(): nat - // no error, since the statement expression can be used for the return type subset constraint -{ - assume -1 > 0; -1 -} - -method StatementExpressionAndSubsetLocal() - // no error, since the statement expression can be used for the local variable type subset constraint -{ - var x: nat := assume -1 > 0; -1; -} - -method StatementExpressionAndSubsetLocal2() - // no error, since the statement expression can be used for the local variable type subset constraint -{ - var x: nat := assume -1 > 0; -1; - assert x >= 0; // no error; - // really weird that this changes things // assert StatementExpressionAndSubsetResult() >= 0; // no error -} - -predicate P(x: int) -method NeedsNat(x: nat) -method NeedsP(x: int) requires P(x) - -method StatementExpressionAndPrecondition(x: int) -{ - NeedsNat(assume x > 0; x); // error, altough it could be nice not to have it - NeedsP(assume P(x); x); // error, since the statement expression can not be used for the requires clause -} - -function StatementExpressionAssumeDoesNotEscapeLetBinding(): int -{ - var x := assume false; 10; - assert false; // error, since the statement expression does not leak outside of the let. - x -} - -method StatementExpressionAssumeDoesNotEscapeAssignment() { - var x := assume false; 3; - assert false; // error, since the statement expression does not leak outside of the assignment. -} - diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy.expect deleted file mode 100644 index 18b1c94a5b9..00000000000 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expressions/statementExpressionScope.dfy.expect +++ /dev/null @@ -1,8 +0,0 @@ -statementExpressionScope.dfy(17,13): Error: this is the postcondition that could not be proved -statementExpressionScope.dfy(48,11): Error: value does not satisfy the subset constraints of 'nat' -statementExpressionScope.dfy(49,8): Error: a precondition for this call could not be proved -statementExpressionScope.dfy(44,31): Related location: this is the precondition that could not be proved -statementExpressionScope.dfy(55,9): Error: assertion might not hold -statementExpressionScope.dfy(61,9): Error: assertion might not hold - -Dafny program verifier finished with 15 verified, 5 errors From 5ab40e83b99036213cbc1b062e541746c26234ea Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 12 Aug 2024 14:12:15 +0200 Subject: [PATCH 33/63] Remove some messy code to see what it breaks --- .../BoogieGenerator.ExpressionWellformed.cs | 84 +++++++++---------- ...oogieGenerator.Functions.Wellformedness.cs | 6 +- .../Verifier/BoogieGenerator.TrStatement.cs | 25 +++--- .../Verifier/BoogieGenerator.Types.cs | 8 +- Source/DafnyCore/Verifier/BoogieGenerator.cs | 4 +- 5 files changed, 61 insertions(+), 66 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 5e755e2d8f8..cdd1a177660 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -264,9 +264,8 @@ void CheckWellformed(Expression expr, WFOptions wfOptions, List locals /// See class WFOptions for descriptions of the specified options. /// void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, - CheckPostcondition checkPostcondition, - List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, - string resultDescription = null) { + AddResultCommands addResultCommands, + List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { Contract.Requires(expr != null); Contract.Requires(wfOptions != null); Contract.Requires(locals != null); @@ -699,8 +698,8 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, directSubstMap.Add(p, ee); if (!(ee is DefaultValueExpression)) { - CheckWellformedWithResult(ee, wfOptions, (innerBuilder, innerBody, _, _) => { - CheckSubrange(innerBody.tok, etran.TrExpr(innerBody), ee.Type, et, ee, innerBuilder); + CheckWellformedWithResult(ee, wfOptions, (returnBuilder, result) => { + CheckSubrange(result.Tok, etran.TrExpr(result), ee.Type, et, ee, returnBuilder); }, locals, builder, etran); } Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(p.tok, lhs, AdaptBoxing(p.tok, etran.TrExpr(ee), cce.NonNull(ee.Type), et)); @@ -1137,16 +1136,16 @@ void CheckOperand(Expression operand) { break; } case LetExpr letExpr: - CheckWellformedLetExprWithResult(letExpr, wfOptions, checkPostcondition, locals, builder, etran, true); - checkPostcondition = null; + CheckWellformedLetExprWithResult(letExpr, wfOptions, addResultCommands, locals, builder, etran, true); + addResultCommands = null; break; case ComprehensionExpr comprehensionExpr: { CheckWellformedComprehensionExpression(wfOptions, locals, builder, etran, comprehensionExpr); break; } case StmtExpr stmtExpr: - CheckWellformedStmtExpr(stmtExpr, wfOptions, checkPostcondition, locals, builder, etran); - checkPostcondition = null; + CheckWellformedStmtExpr(stmtExpr, wfOptions, addResultCommands, locals, builder, etran); + addResultCommands = null; break; case ITEExpr iteExpr: { ITEExpr e = iteExpr; @@ -1159,18 +1158,18 @@ void CheckOperand(Expression operand) { // has already been checked in e.Test var letExpr = (LetExpr)e.Thn; Contract.Assert(letExpr != null); - CheckWellformedLetExprWithResult(letExpr, wfOptions, checkPostcondition, locals, bThen, etran, false); + CheckWellformedLetExprWithResult(letExpr, wfOptions, addResultCommands, locals, bThen, etran, false); } else { - CheckWellformedWithResult(e.Thn, wfOptions, checkPostcondition, locals, bThen, etran, "if expression then branch"); + CheckWellformedWithResult(e.Thn, wfOptions, addResultCommands, locals, bThen, etran); } - CheckWellformedWithResult(e.Els, wfOptions, checkPostcondition, locals, bElse, etran, "if expression else branch"); + CheckWellformedWithResult(e.Els, wfOptions, addResultCommands, locals, bElse, etran); builder.Add(new Bpl.IfCmd(iteExpr.tok, etran.TrExpr(e.Test), bThen.Collect(iteExpr.tok), null, bElse.Collect(iteExpr.tok))); - checkPostcondition = null; + addResultCommands = null; break; } case MatchExpr matchExpr: - TrMatchExpr(matchExpr, wfOptions, checkPostcondition, locals, builder, etran); - checkPostcondition = null; + TrMatchExpr(matchExpr, wfOptions, addResultCommands, locals, builder, etran); + addResultCommands = null; break; case DatatypeUpdateExpr updateExpr: { var e = updateExpr; @@ -1187,19 +1186,19 @@ void CheckOperand(Expression operand) { CheckNotGhostVariant(e.InCompiledContext, updateExpr, e.Root, "update of", e.Members, e.LegalSourceConstructors, builder, etran); - CheckWellformedWithResult(e.ResolvedExpression, wfOptions, checkPostcondition, locals, builder, etran, resultDescription); - checkPostcondition = null; + CheckWellformedWithResult(e.ResolvedExpression, wfOptions, addResultCommands, locals, builder, etran); + addResultCommands = null; break; } case ConcreteSyntaxExpression expression: { var e = expression; - CheckWellformedWithResult(e.ResolvedExpression, wfOptions, checkPostcondition, locals, builder, etran, resultDescription); - checkPostcondition = null; + CheckWellformedWithResult(e.ResolvedExpression, wfOptions, addResultCommands, locals, builder, etran); + addResultCommands = null; break; } case NestedMatchExpr nestedMatchExpr: - CheckWellformedWithResult(nestedMatchExpr.Flattened, wfOptions, checkPostcondition, locals, builder, etran, resultDescription); - checkPostcondition = null; + CheckWellformedWithResult(nestedMatchExpr.Flattened, wfOptions, addResultCommands, locals, builder, etran); + addResultCommands = null; break; case BoogieFunctionCall call: { var e = call; @@ -1220,8 +1219,8 @@ void CheckOperand(Expression operand) { } - if (checkPostcondition != null) { - checkPostcondition(builder, expr, true, resultDescription); + if (addResultCommands != null) { + addResultCommands(builder, expr); } } @@ -1299,12 +1298,13 @@ private void CheckWellformedComprehensionExpression(WFOptions wfOptions, List { + void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { if (rangeType != null) { - CheckSubsetType(etran, innerBody, resultIe, rangeType, innerBuilder, adaptBox, prefix); + CheckSubsetType(etran, result, resultIe, rangeType, returnBuilder); } - }; - CheckWellformedWithResult(body, newOptions, checkPostcondition, locals, b, comprehensionEtran); + } + + CheckWellformedWithResult(body, newOptions, AddResultCommands, locals, b, comprehensionEtran); }); if (mc != null) { @@ -1356,20 +1356,19 @@ private void CheckWellformedComprehensionExpression(WFOptions wfOptions, List Bpl.Expr.Eq(selfCall, adaptBoxing ? AdaptBoxing(expr.tok, e, expr.Type, resultType) : e), - resultDescription)); + e => Bpl.Expr.Eq(selfCall, AdaptBoxing(expr.tok, e, expr.Type, resultType)))); builder.Add(TrAssumeCmd(expr.tok, etran.CanCallAssumption(expr))); builder.Add(new CommentCmd("CheckWellformedWithResult: any expression")); builder.Add(TrAssumeCmd(expr.tok, MkIs(selfCall, resultType))); } - private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, CheckPostcondition checkPostcondition, List locals, + private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, AddResultCommands addResultCommands, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { FillMissingCases(me); @@ -1411,7 +1410,7 @@ private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, CheckPostcondition c // TODO: // resultDescription = "match expression"; - CheckWellformedWithResult(mc.Body, wfOptions, checkPostcondition, locals, b, etran, "match expression branch result"); + CheckWellformedWithResult(mc.Body, wfOptions, addResultCommands, locals, b, etran); ifCmd = new Bpl.IfCmd(mc.tok, Bpl.Expr.Eq(src, ct), b.Collect(mc.tok), ifCmd, els); els = null; } @@ -1419,7 +1418,7 @@ private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, CheckPostcondition c builder.Add(ifCmd); } - private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions wfOptions, CheckPostcondition checkPostcondition, List locals, + private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions wfOptions, AddResultCommands addResultCommands, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { var statements = new List() { stmtExpr.S }; @@ -1446,7 +1445,7 @@ private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions wfOptions, Che } } - CheckWellformedWithResult(expression, wfOptions, checkPostcondition, locals, builder, etran, "statement expression result"); + CheckWellformedWithResult(expression, wfOptions, addResultCommands, locals, builder, etran); } /// @@ -1517,10 +1516,9 @@ void CheckWellformedSpecialFunction(FunctionCallExpr expr, WFOptions options, Bp } } - delegate void CheckPostcondition(BoogieStmtListBuilder builder, Expression body, bool adaptBox, - string errorMessagePrefix); + delegate void AddResultCommands(BoogieStmtListBuilder builder, Expression result); - void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, CheckPostcondition checkPostcondition, List locals, + void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, AddResultCommands addResultCommands, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, bool checkRhs) { if (e.Exact) { var substMap = SetupBoundVarsAsLocals(e.BoundVars.ToList(), builder, locals, etran, "#Z"); @@ -1534,16 +1532,16 @@ void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, CheckPostc locals.Add(r); var rIe = new Bpl.IdentifierExpr(rhs.tok, r); - void CheckPostconditionForRhs(BoogieStmtListBuilder innerBuilder, Expression body, bool adaptBoxing, string prefix) { - CheckSubsetType(etran, body, rIe, pat.Expr.Type, innerBuilder, adaptBoxing, prefix); + void CheckPostconditionForRhs(BoogieStmtListBuilder innerBuilder, Expression body) { + CheckSubsetType(etran, body, rIe, pat.Expr.Type, innerBuilder); } - CheckWellformedWithResult(e.RHSs[i], wfOptions, CheckPostconditionForRhs, locals, builder, etran, "let expression binding RHS well-formed"); + CheckWellformedWithResult(e.RHSs[i], wfOptions, CheckPostconditionForRhs, locals, builder, etran); CheckCasePatternShape(pat, rhs, rIe, rhs.tok, pat.Expr.Type, builder); var substExpr = Substitute(pat.Expr, null, substMap); builder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, e.tok, substExpr, e => Bpl.Expr.Eq(e, rIe), "let expression binding")); } - CheckWellformedWithResult(Substitute(e.Body, null, substMap), wfOptions, checkPostcondition, locals, builder, etran, "let expression result"); + CheckWellformedWithResult(Substitute(e.Body, null, substMap), wfOptions, addResultCommands, locals, builder, etran); } else { // CheckWellformed(var b :| RHS(b); Body(b)) = @@ -1596,9 +1594,7 @@ void CheckPostconditionForRhs(BoogieStmtListBuilder innerBuilder, Expression bod var info = letSuchThatExprInfo[e]; builder.Add(new Bpl.AssumeCmd(e.tok, info.CanCallFunctionCall(this, etran))); // If we are supposed to assume "result" to equal this expression, then use the body of the let-such-that, not the generated $let#... function - if (checkPostcondition != null) { - checkPostcondition(builder, letBody, false, "let expression"); - } + addResultCommands?.Invoke(builder, letBody); } } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs index c2ab37131d0..14daf0f9f29 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs @@ -204,8 +204,8 @@ private BoogieStmtListBuilder GetBodyCheckBuilder(Function f, ExpressionTranslat var doReadsChecks = etran.readsFrame != null; var wfo = new WFOptions(null, doReadsChecks, doReadsChecks, false); - void CheckPostcondition(BoogieStmtListBuilder innerBuilder, Expression innerBody, bool adaptBoxing, string prefix) { - generator.CheckSubsetType(etran, innerBody, selfCall, f.ResultType, innerBuilder, adaptBoxing, prefix); + void CheckPostcondition(BoogieStmtListBuilder innerBuilder, Expression innerBody) { + generator.CheckSubsetType(etran, innerBody, selfCall, f.ResultType, innerBuilder); if (f.Result != null) { var cmd = TrAssumeCmd(f.tok, Expr.Eq(selfCall, generator.TrVar(f.tok, f.Result))); generator.proofDependencies?.AddProofDependencyId(cmd, f.tok, new FunctionDefinitionDependency(f)); @@ -227,7 +227,7 @@ void CheckPostcondition(BoogieStmtListBuilder innerBuilder, Expression innerBody innerBuilder.Add(new ReturnCmd(innerBody.Tok)); } - generator.CheckWellformedWithResult(f.Body, wfo, CheckPostcondition, locals, bodyCheckBuilder, etran, "function call result"); + generator.CheckWellformedWithResult(f.Body, wfo, CheckPostcondition, locals, bodyCheckBuilder, etran); // var b$reads_guards#0 : bool ... locals.AddRange(wfo.Locals); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs index f9c1134045f..d1deea8a981 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs @@ -408,16 +408,14 @@ private void TranslateVariableDeclaration(BoogieStmtListBuilder builder, List { + AddResultCommands addResultCommands = (returnBuilder, result) => { Contract.Assert(pat.Expr.Type != null); - var bResult = etran.TrExpr(body); - CheckSubrange(rhs.tok, bResult, rhs.Type, pat.Expr.Type, rhs, b); - b.Add(TrAssumeCmdWithDependenciesAndExtend(etran, rhs.tok, rhs, - e => Bpl.Expr.Eq(boogieTupleReference, adaptBoxing ? AdaptBoxing(rhs.tok, e, rhs.Type, pat.Expr.Type) : e), - prefix)); + var bResult = etran.TrExpr(result); + CheckSubrange(rhs.tok, bResult, rhs.Type, pat.Expr.Type, rhs, returnBuilder); + returnBuilder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, rhs.tok, rhs, + e => Bpl.Expr.Eq(boogieTupleReference, AdaptBoxing(rhs.tok, e, rhs.Type, pat.Expr.Type)))); }; - CheckWellformedWithResult(rhs, new WFOptions(null, false, false), checkPostcondition, locals, builder, etran, - "variable declaration RHS"); + CheckWellformedWithResult(rhs, new WFOptions(null, false, false), addResultCommands, locals, builder, etran); builder.Add(TrAssumeCmd(rhs.tok, etran.CanCallAssumption(rhs))); builder.Add(new CommentCmd("CheckWellformedWithResult: any expression")); builder.Add(TrAssumeCmd(rhs.tok, MkIs(boogieTupleReference, pat.Expr.Type))); @@ -2626,11 +2624,12 @@ Bpl.Expr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bGivenLhs, IVariable lhs var e = (ExprRhs)rhs; var bRhs = etran.TrExpr(e.Expr); + // TODO should GetSubrangeCheck not be moved inside? var cre = GetSubrangeCheck(tok, bRhs, e.Expr.Type, rhsTypeConstraint, e.Expr, null, out var desc, ""); - TrStmt_CheckWellformed(e.Expr, builder, locals, etran, true, checkPostcondition: - (b, _, _, _) => { + TrStmt_CheckWellformed(e.Expr, builder, locals, etran, true, addResultCommands: + (returnBuilder, result) => { if (cre != null) { - b.Add(Assert(tok, cre, desc)); + returnBuilder.Add(Assert(tok, cre, desc)); } }); if (cre != null) { @@ -2885,7 +2884,7 @@ void TrStmtList(List stmts, BoogieStmtListBuilder builder, List locals, - ExpressionTranslator etran, bool subsumption, bool lValueContext = false, CheckPostcondition checkPostcondition = null) { + ExpressionTranslator etran, bool subsumption, bool lValueContext = false, AddResultCommands addResultCommands = null) { Contract.Requires(expr != null); Contract.Requires(builder != null); Contract.Requires(locals != null); @@ -2910,7 +2909,7 @@ void TrStmt_CheckWellformed(Expression expr, BoogieStmtListBuilder builder, List if (lValueContext) { options = options.WithLValueContext(true); } - CheckWellformedWithResult(expr, options, checkPostcondition, locals, builder, etran); + CheckWellformedWithResult(expr, options, addResultCommands, locals, builder, etran); builder.Add(TrAssumeCmd(expr.tok, etran.CanCallAssumption(expr))); } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs index 17d699b7859..63aa6f6b7ff 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs @@ -1497,19 +1497,19 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { // check well-formedness of the witness expression (including termination, and reads checks) var ghostCodeContext = codeContext; codeContext = decl.WitnessKind == SubsetTypeDecl.WKind.Compiled ? new CallableWrapper(decl, false) : ghostCodeContext; - CheckWellformedWithResult(decl.Witness, new WFOptions(null, true), (innerBuilder, innerWitness, _, _) => { + CheckWellformedWithResult(decl.Witness, new WFOptions(null, true), (returnBuilder, result) => { // check that the witness is assignable to the type of the given bound variable if (decl is SubsetTypeDecl) { // Note, for new-types, this has already been checked by CheckWellformed. - CheckResultToBeInType(decl.Witness.tok, innerWitness, decl.Var.Type, locals, innerBuilder, etran); + CheckResultToBeInType(decl.Witness.tok, result, decl.Var.Type, locals, returnBuilder, etran); } // check that the witness expression checks out - witnessExpr = Substitute(decl.Constraint, decl.Var, innerWitness); + witnessExpr = Substitute(decl.Constraint, decl.Var, result); witnessExpr.tok = decl.Witness.Tok; var desc = new PODesc.WitnessCheck(witnessString, witnessExpr); - SplitAndAssertExpression(innerBuilder, witnessExpr, etran, context, desc); + SplitAndAssertExpression(returnBuilder, witnessExpr, etran, context, desc); }, locals, witnessCheckBuilder, etran); codeContext = ghostCodeContext; diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index 9fd96bf5f21..71f5148d31f 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -2340,9 +2340,9 @@ void AddWellformednessCheck(DatatypeCtor ctor) { var e = formal.DefaultValue; CheckWellformedWithResult(e, new WFOptions(null, true, false, true), - (innerBuilder, innerExpr, _, _) => { + (returnBuilder, result) => { builder.Add(new Bpl.AssumeCmd(e.tok, etran.CanCallAssumption(e))); - CheckSubrange(innerExpr.tok, etran.TrExpr(innerExpr), e.Type, formal.Type, e, innerBuilder); + CheckSubrange(result.tok, etran.TrExpr(result), e.Type, formal.Type, e, returnBuilder); }, locals, builder, etran); } From 1a536cda54de5b9ec9c5bfc7519ceb36ba52ae5c Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 12 Aug 2024 14:20:04 +0200 Subject: [PATCH 34/63] Fix IDE test --- Source/DafnyLanguageServer.Test/Diagnostics/DiagnosticsTest.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/DafnyLanguageServer.Test/Diagnostics/DiagnosticsTest.cs b/Source/DafnyLanguageServer.Test/Diagnostics/DiagnosticsTest.cs index 5d319be686d..59801b3a029 100644 --- a/Source/DafnyLanguageServer.Test/Diagnostics/DiagnosticsTest.cs +++ b/Source/DafnyLanguageServer.Test/Diagnostics/DiagnosticsTest.cs @@ -279,8 +279,9 @@ then bullspec(s[1..],u[1..]) var diagnostics1 = diagnosticsReceiver.GetLatestAndClearQueue(documentItem); Assert.Equal(4, diagnostics1.Length); ApplyChange(ref documentItem, ((7, 25), (10, 17)), ""); + await GetNextDiagnostics(documentItem); // Migrated verification diagnostics. var diagnostics2 = await GetNextDiagnostics(documentItem); - Assert.Equal(5, diagnostics2.Length); + Assert.Equal(3, diagnostics2.Length); Assert.Equal("Parser", diagnostics2[0].Source); Assert.Equal(DiagnosticSeverity.Error, diagnostics2[0].Severity); ApplyChange(ref documentItem, ((7, 20), (7, 25)), ""); From 7caa143f8207ddb21952011551aa29e77ca58531 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 12 Aug 2024 14:37:51 +0200 Subject: [PATCH 35/63] Update expect files --- .../NameResolutionAndTypeInference.cs | 2 +- .../BoogieGenerator.ExpressionWellformed.cs | 512 +++++++++--------- .../LitTest/cli/measure-complexity.dfy.expect | 22 +- .../dafny0/CustomErrorMesage.dfy.expect | 2 +- .../dafny0/RevealConsistency.dfy.expect | 2 +- .../LitTests/LitTest/dafny4/Bug160.dfy.expect | 2 +- .../exports/OpaqueFunctions.dfy.expect | 2 +- .../NonReferenceTraitsVerify.dfy.expect | Bin 2110 -> 4153 bytes 8 files changed, 275 insertions(+), 269 deletions(-) diff --git a/Source/DafnyCore/Resolver/NameResolutionAndTypeInference/NameResolutionAndTypeInference.cs b/Source/DafnyCore/Resolver/NameResolutionAndTypeInference/NameResolutionAndTypeInference.cs index e0160e3cbc2..3b5db2e8d8d 100644 --- a/Source/DafnyCore/Resolver/NameResolutionAndTypeInference/NameResolutionAndTypeInference.cs +++ b/Source/DafnyCore/Resolver/NameResolutionAndTypeInference/NameResolutionAndTypeInference.cs @@ -5989,7 +5989,7 @@ public MethodCallInformation ResolveApplySuffix(ApplySuffix e, ResolutionContext } if (callee != null) { // produce a FunctionCallExpr instead of an ApplyExpr(MemberSelectExpr) - var rr = new FunctionCallExpr(e.Lhs.tok, callee.Name, mse.Obj, e.tok, e.CloseParen, e.Bindings, atLabel) { + var rr = new FunctionCallExpr(e.Tok, callee.Name, mse.Obj, e.tok, e.CloseParen, e.Bindings, atLabel) { Function = callee, TypeApplication_AtEnclosingClass = mse.TypeApplication_AtEnclosingClass, TypeApplication_JustFunction = mse.TypeApplication_JustMember diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index cdd1a177660..741b09a9e67 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -659,260 +659,10 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, break; } case FunctionCallExpr callExpr: { - FunctionCallExpr e = callExpr; - Contract.Assert(e.Function != null); // follows from the fact that expr has been successfully resolved - if (e.Function is SpecialFunction) { - CheckWellformedSpecialFunction(e, wfOptions, null, null, locals, builder, etran); - } else { - // check well-formedness of receiver - CheckWellformed(e.Receiver, wfOptions, locals, builder, etran); - if (!e.Function.IsStatic && !(e.Receiver is ThisExpr) && !e.Receiver.Type.IsArrowType) { - CheckNonNull(callExpr.tok, e.Receiver, builder, etran, wfOptions.AssertKv); - } else if (e.Receiver.Type.IsArrowType) { - CheckFunctionSelectWF("function specification", builder, etran, e.Receiver, ""); - } - if (!e.Function.IsStatic && !etran.UsesOldHeap) { - // the argument can't be assumed to be allocated for the old heap - Type et = UserDefinedType.FromTopLevelDecl(e.tok, e.Function.EnclosingClass).Subst(e.GetTypeArgumentSubstitutions()); - builder.Add(new Bpl.CommentCmd("assume allocatedness for receiver argument to function")); - builder.Add(TrAssumeCmd(e.Receiver.tok, MkIsAllocBox(BoxIfNecessary(e.Receiver.tok, etran.TrExpr(e.Receiver), e.Receiver.Type), et, etran.HeapExpr))); - } - // create a local variable for each formal parameter, and assign each actual parameter to the corresponding local - Dictionary substMap = new Dictionary(); - Dictionary directSubstMap = new Dictionary(); - for (int i = 0; i < e.Function.Ins.Count; i++) { - Formal p = e.Function.Ins[i]; - // Note, in the following, the "##" makes the variable invisible in BVD. An alternative would be to communicate - // to BVD what this variable stands for and display it as such to the user. - Type et = p.Type.Subst(e.GetTypeArgumentSubstitutions()); - LocalVariable local = new LocalVariable(p.RangeToken, "##" + p.Name, et, p.IsGhost); - local.type = local.SyntacticType; // resolve local here - var ie = new IdentifierExpr(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator)) { - Var = local - }; - 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)))); - Bpl.IdentifierExpr lhs = (Bpl.IdentifierExpr)etran.TrExpr(ie); // TODO: is this cast always justified? - Expression ee = e.Args[i]; - directSubstMap.Add(p, ee); + CheckFunctionCallWellformed(expr, wfOptions, locals, builder, etran, callExpr, readFrames); - if (!(ee is DefaultValueExpression)) { - CheckWellformedWithResult(ee, wfOptions, (returnBuilder, result) => { - CheckSubrange(result.Tok, etran.TrExpr(result), ee.Type, et, ee, returnBuilder); - }, locals, builder, etran); - } - Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(p.tok, lhs, AdaptBoxing(p.tok, etran.TrExpr(ee), cce.NonNull(ee.Type), et)); - builder.Add(cmd); - if (!etran.UsesOldHeap) { - // the argument can't be assumed to be allocated for the old heap - builder.Add(new Bpl.CommentCmd("assume allocatedness for argument to function")); - builder.Add(TrAssumeCmd(e.Args[i].tok, MkIsAlloc(lhs, et, etran.HeapExpr))); - } - } - - // Check that every parameter is available in the state in which the function is invoked; this means checking that it has - // the right type and is allocated. These checks usually hold trivially, on account of that the Dafny language only gives - // access to expressions of the appropriate type and that are allocated in the current state. However, if the function is - // invoked in the 'old' state or if the function invoked is a two-state function with a non-new parameter, then we need to - // check that its arguments were all available at that time as well. - if (etran.UsesOldHeap) { - 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)); - } - } - 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)); - } - } - } 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)); - } - } - Contract.Assert(e.Function.Ins.Count == e.Args.Count); - for (int i = 0; i < e.Args.Count; i++) { - var formal = e.Function.Ins[i]; - if (formal.IsOld) { - Expression ee = e.Args[i]; - 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( - $"argument{pIdx} for parameter '{formal.Name}'", - "in the two-state function's previous state" + PODesc.IsAllocated.HelperFormal(formal), - ee, - e.AtLabel - ); - builder.Add(Assert(GetToken(ee), wh, desc)); - } - } - } - } - // check that the preconditions for the call hold - // the check for .reads function must be translated explicitly: their declaration lacks - // an explicit precondition, which is added as an axiom in Translator.cs - 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))); - - if (wfOptions.DoReadsChecks) { - // check that the callee reads only what the caller is already allowed to read - Type objset = program.SystemModuleManager.ObjectSetType(); - Expression wrap = new BoogieWrapper( - FunctionCall(expr.tok, Reads(e.Args.Count()), TrType(objset), arguments), - objset); - var reads = new FrameExpression(expr.tok, wrap, null); - List requiredFrames; - switch (e.Receiver.Resolved) { - case MemberSelectExpr { Member: MethodOrFunction readsReceiver }: { - var receiverReplacement = readsReceiver.IsStatic - ? null - : new ThisExpr(readsReceiver); - var receiverSubstMap = readsReceiver.Ins.Zip(e.Args) - .ToDictionary(fa => fa.First as IVariable, fa => fa.Second); - var subst = new Substituter(receiverReplacement, receiverSubstMap, e.GetTypeArgumentSubstitutions()); - requiredFrames = readsReceiver.Reads.Expressions.ConvertAll(subst.SubstFrameExpr); - break; - } - default: - var readsCall = new ApplyExpr( - Token.NoToken, - new ExprDotName(Token.NoToken, e.Receiver.Resolved, "reads", null), - e.Args, - Token.NoToken - ); - readsCall.Type = objset; - requiredFrames = new() { new FrameExpression(Token.NoToken, readsCall, null) }; - break; - } - var desc = new PODesc.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); - } - - } else { - // Directly substitutes arguments for formals, to be displayed to the user - var argSubstMap = e.Function.Ins.Zip(e.Args).ToDictionary(fa => fa.First as IVariable, fa => fa.Second); - var directSub = new Substituter(e.Receiver, argSubstMap, e.GetTypeArgumentSubstitutions()); - - foreach (AttributedExpression p in e.Function.Req) { - var directPrecond = directSub.Substitute(p.E); - - Expression precond = Substitute(p.E, e.Receiver, substMap, e.GetTypeArgumentSubstitutions()); - var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); - 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); - if (wfOptions.AssertKv != null) { - // use the given assert attribute only - builder.Add(Assert(tok, ss.E, desc, wfOptions.AssertKv)); - } else { - builder.Add(AssertNS(tok, ss.E, desc)); - } - } - } - if (wfOptions.AssertKv == null) { - // assume only if no given assert attribute is given - builder.Add(TrAssumeCmd(callExpr.tok, etran.TrExpr(precond))); - } - } - if (wfOptions.DoReadsChecks) { - // check that the callee reads only what the caller is already allowed to read - - // 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); - - // ... but that substitution isn't needed for frames passed to CheckFrameSubset - var readsSubst = new Substituter(null, new Dictionary(), e.GetTypeArgumentSubstitutions()); - CheckFrameSubset(callExpr.tok, - e.Function.Reads.Expressions.ConvertAll(readsSubst.SubstFrameExpr), - e.Receiver, substMap, etran, etran.ReadsFrame(callExpr.tok), wfOptions.AssertSink(this, builder), desc, wfOptions.AssertKv); - } - } - Expression allowance = null; - if (codeContext != null && e.CoCall != FunctionCallExpr.CoCallResolution.Yes && !(e.Function is ExtremePredicate)) { - // check that the decreases measure goes down - var calleeSCCLookup = e.IsByMethodCall ? (ICallable)e.Function.ByMethodDecl : e.Function; - Contract.Assert(calleeSCCLookup != null); - if (ModuleDefinition.InSameSCC(calleeSCCLookup, codeContext)) { - if (wfOptions.DoOnlyCoarseGrainedTerminationChecks) { - builder.Add(Assert(GetToken(expr), Bpl.Expr.False, new PODesc.IsNonRecursive())); - } else { - List contextDecreases = codeContext.Decreases.Expressions; - List calleeDecreases = e.Function.Decreases.Expressions; - if (e.Function == wfOptions.SelfCallsAllowance) { - allowance = Expression.CreateBoolLiteral(e.tok, true); - if (!e.Function.IsStatic) { - allowance = Expression.CreateAnd(allowance, Expression.CreateEq(e.Receiver, new ThisExpr(e.Function), e.Receiver.Type)); - } - for (int i = 0; i < e.Args.Count; i++) { - Expression ee = e.Args[i]; - Formal ff = e.Function.Ins[i]; - allowance = Expression.CreateAnd(allowance, Expression.CreateEq(ee, Expression.CreateIdentExpr(ff), ff.Type)); - } - } - string hint; - switch (e.CoCall) { - case FunctionCallExpr.CoCallResolution.NoBecauseFunctionHasSideEffects: - hint = "note that only functions without side effects can be called co-recursively"; - break; - case FunctionCallExpr.CoCallResolution.NoBecauseFunctionHasPostcondition: - hint = "note that only functions without any ensures clause can be called co-recursively"; - break; - case FunctionCallExpr.CoCallResolution.NoBecauseIsNotGuarded: - hint = "note that the call is not sufficiently guarded to be used co-recursively"; - break; - case FunctionCallExpr.CoCallResolution.NoBecauseRecursiveCallsAreNotAllowedInThisContext: - hint = "note that calls cannot be co-recursive in this context"; - break; - case FunctionCallExpr.CoCallResolution.NoBecauseRecursiveCallsInDestructiveContext: - hint = "note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts"; - break; - case FunctionCallExpr.CoCallResolution.No: - hint = null; - break; - default: - Contract.Assert(false); // unexpected CoCallResolution - goto case FunctionCallExpr.CoCallResolution.No; // please the compiler - } - if (e.CoCallHint != null) { - hint = hint == null ? e.CoCallHint : string.Format("{0}; {1}", hint, e.CoCallHint); - } - CheckCallTermination(callExpr.tok, contextDecreases, calleeDecreases, allowance, e.Receiver, substMap, directSubstMap, e.GetTypeArgumentSubstitutions(), - etran, false, builder, codeContext.InferredDecreases, hint); - } - } - } - // all is okay, so allow this function application access to the function's axiom, except if it was okay because of the self-call allowance. - Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(callExpr.tok, e.Function.FullSanitizedName + "#canCall", Bpl.Type.Bool); - List args = etran.FunctionInvocationArguments(e, null, null); - Bpl.Expr canCallFuncAppl = new Bpl.NAryExpr(GetToken(expr), new Bpl.FunctionCall(canCallFuncID), args); - builder.Add(TrAssumeCmd(callExpr.tok, allowance == null ? canCallFuncAppl : BplOr(etran.TrExpr(allowance), canCallFuncAppl))); - - var returnType = e.Type.AsDatatype; - if (returnType != null && returnType.Ctors.Count == 1) { - var correctConstructor = FunctionCall(e.tok, returnType.Ctors[0].QueryField.FullSanitizedName, Bpl.Type.Bool, etran.TrExpr(e)); - // There is only one constructor, so the value must be been constructed by it; might as well assume that here. - builder.Add(TrAssumeCmd(callExpr.tok, correctConstructor)); - } - } - - break; - } + break; + } case SeqConstructionExpr constructionExpr: { var e = constructionExpr; CheckWellformed(e.N, wfOptions, locals, builder, etran); @@ -1224,6 +974,262 @@ void CheckOperand(Expression operand) { } } + private void CheckFunctionCallWellformed(Expression expr, WFOptions wfOptions, List locals, + BoogieStmtListBuilder builder, ExpressionTranslator etran, FunctionCallExpr callExpr, List readFrames) + { + FunctionCallExpr e = callExpr; + Contract.Assert(e.Function != null); // follows from the fact that expr has been successfully resolved + if (e.Function is SpecialFunction) { + CheckWellformedSpecialFunction(e, wfOptions, null, null, locals, builder, etran); + } else { + // check well-formedness of receiver + CheckWellformed(e.Receiver, wfOptions, locals, builder, etran); + if (!e.Function.IsStatic && !(e.Receiver is ThisExpr) && !e.Receiver.Type.IsArrowType) { + CheckNonNull(callExpr.tok, e.Receiver, builder, etran, wfOptions.AssertKv); + } else if (e.Receiver.Type.IsArrowType) { + CheckFunctionSelectWF("function specification", builder, etran, e.Receiver, ""); + } + if (!e.Function.IsStatic && !etran.UsesOldHeap) { + // the argument can't be assumed to be allocated for the old heap + Type et = UserDefinedType.FromTopLevelDecl(e.tok, e.Function.EnclosingClass).Subst(e.GetTypeArgumentSubstitutions()); + builder.Add(new Bpl.CommentCmd("assume allocatedness for receiver argument to function")); + builder.Add(TrAssumeCmd(e.Receiver.tok, MkIsAllocBox(BoxIfNecessary(e.Receiver.tok, etran.TrExpr(e.Receiver), e.Receiver.Type), et, etran.HeapExpr))); + } + // create a local variable for each formal parameter, and assign each actual parameter to the corresponding local + Dictionary substMap = new Dictionary(); + Dictionary directSubstMap = new Dictionary(); + for (int i = 0; i < e.Function.Ins.Count; i++) { + Formal p = e.Function.Ins[i]; + // Note, in the following, the "##" makes the variable invisible in BVD. An alternative would be to communicate + // to BVD what this variable stands for and display it as such to the user. + Type et = p.Type.Subst(e.GetTypeArgumentSubstitutions()); + LocalVariable local = new LocalVariable(p.RangeToken, "##" + p.Name, et, p.IsGhost); + local.type = local.SyntacticType; // resolve local here + var ie = new IdentifierExpr(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator)) { + Var = local + }; + 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)))); + Bpl.IdentifierExpr lhs = (Bpl.IdentifierExpr)etran.TrExpr(ie); // TODO: is this cast always justified? + Expression ee = e.Args[i]; + directSubstMap.Add(p, ee); + + if (!(ee is DefaultValueExpression)) { + CheckWellformedWithResult(ee, wfOptions, (returnBuilder, result) => { + CheckSubrange(result.Tok, etran.TrExpr(result), ee.Type, et, ee, returnBuilder); + }, locals, builder, etran); + } + Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(p.tok, lhs, AdaptBoxing(p.tok, etran.TrExpr(ee), cce.NonNull(ee.Type), et)); + builder.Add(cmd); + if (!etran.UsesOldHeap) { + // the argument can't be assumed to be allocated for the old heap + builder.Add(new Bpl.CommentCmd("assume allocatedness for argument to function")); + builder.Add(TrAssumeCmd(e.Args[i].tok, MkIsAlloc(lhs, et, etran.HeapExpr))); + } + } + + // Check that every parameter is available in the state in which the function is invoked; this means checking that it has + // the right type and is allocated. These checks usually hold trivially, on account of that the Dafny language only gives + // access to expressions of the appropriate type and that are allocated in the current state. However, if the function is + // invoked in the 'old' state or if the function invoked is a two-state function with a non-new parameter, then we need to + // check that its arguments were all available at that time as well. + if (etran.UsesOldHeap) { + 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)); + } + } + 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)); + } + } + } 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)); + } + } + Contract.Assert(e.Function.Ins.Count == e.Args.Count); + for (int i = 0; i < e.Args.Count; i++) { + var formal = e.Function.Ins[i]; + if (formal.IsOld) { + Expression ee = e.Args[i]; + 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( + $"argument{pIdx} for parameter '{formal.Name}'", + "in the two-state function's previous state" + PODesc.IsAllocated.HelperFormal(formal), + ee, + e.AtLabel + ); + builder.Add(Assert(GetToken(ee), wh, desc)); + } + } + } + } + // check that the preconditions for the call hold + // the check for .reads function must be translated explicitly: their declaration lacks + // an explicit precondition, which is added as an axiom in Translator.cs + 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))); + + if (wfOptions.DoReadsChecks) { + // check that the callee reads only what the caller is already allowed to read + Type objset = program.SystemModuleManager.ObjectSetType(); + Expression wrap = new BoogieWrapper( + FunctionCall(expr.tok, Reads(e.Args.Count()), TrType(objset), arguments), + objset); + var reads = new FrameExpression(expr.tok, wrap, null); + List requiredFrames; + switch (e.Receiver.Resolved) { + case MemberSelectExpr { Member: MethodOrFunction readsReceiver }: { + var receiverReplacement = readsReceiver.IsStatic + ? null + : new ThisExpr(readsReceiver); + var receiverSubstMap = readsReceiver.Ins.Zip(e.Args) + .ToDictionary(fa => fa.First as IVariable, fa => fa.Second); + var subst = new Substituter(receiverReplacement, receiverSubstMap, e.GetTypeArgumentSubstitutions()); + requiredFrames = readsReceiver.Reads.Expressions.ConvertAll(subst.SubstFrameExpr); + break; + } + default: + var readsCall = new ApplyExpr( + Token.NoToken, + new ExprDotName(Token.NoToken, e.Receiver.Resolved, "reads", null), + e.Args, + Token.NoToken + ); + readsCall.Type = objset; + requiredFrames = new() { new FrameExpression(Token.NoToken, readsCall, null) }; + break; + } + var desc = new PODesc.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); + } + + } else { + // Directly substitutes arguments for formals, to be displayed to the user + var argSubstMap = e.Function.Ins.Zip(e.Args).ToDictionary(fa => fa.First as IVariable, fa => fa.Second); + var directSub = new Substituter(e.Receiver, argSubstMap, e.GetTypeArgumentSubstitutions()); + + foreach (AttributedExpression p in e.Function.Req) { + var directPrecond = directSub.Substitute(p.E); + + Expression precond = Substitute(p.E, e.Receiver, substMap, e.GetTypeArgumentSubstitutions()); + var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); + 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); + if (wfOptions.AssertKv != null) { + // use the given assert attribute only + builder.Add(Assert(tok, ss.E, desc, wfOptions.AssertKv)); + } else { + builder.Add(AssertNS(tok, ss.E, desc)); + } + } + } + if (wfOptions.AssertKv == null) { + // assume only if no given assert attribute is given + builder.Add(TrAssumeCmd(callExpr.tok, etran.TrExpr(precond))); + } + } + if (wfOptions.DoReadsChecks) { + // check that the callee reads only what the caller is already allowed to read + + // 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); + + // ... but that substitution isn't needed for frames passed to CheckFrameSubset + var readsSubst = new Substituter(null, new Dictionary(), e.GetTypeArgumentSubstitutions()); + CheckFrameSubset(callExpr.tok, + e.Function.Reads.Expressions.ConvertAll(readsSubst.SubstFrameExpr), + e.Receiver, substMap, etran, etran.ReadsFrame(callExpr.tok), wfOptions.AssertSink(this, builder), desc, wfOptions.AssertKv); + } + } + Expression allowance = null; + if (codeContext != null && e.CoCall != FunctionCallExpr.CoCallResolution.Yes && !(e.Function is ExtremePredicate)) { + // check that the decreases measure goes down + var calleeSCCLookup = e.IsByMethodCall ? (ICallable)e.Function.ByMethodDecl : e.Function; + Contract.Assert(calleeSCCLookup != null); + if (ModuleDefinition.InSameSCC(calleeSCCLookup, codeContext)) { + if (wfOptions.DoOnlyCoarseGrainedTerminationChecks) { + builder.Add(Assert(GetToken(expr), Bpl.Expr.False, new PODesc.IsNonRecursive())); + } else { + List contextDecreases = codeContext.Decreases.Expressions; + List calleeDecreases = e.Function.Decreases.Expressions; + if (e.Function == wfOptions.SelfCallsAllowance) { + allowance = Expression.CreateBoolLiteral(e.tok, true); + if (!e.Function.IsStatic) { + allowance = Expression.CreateAnd(allowance, Expression.CreateEq(e.Receiver, new ThisExpr(e.Function), e.Receiver.Type)); + } + for (int i = 0; i < e.Args.Count; i++) { + Expression ee = e.Args[i]; + Formal ff = e.Function.Ins[i]; + allowance = Expression.CreateAnd(allowance, Expression.CreateEq(ee, Expression.CreateIdentExpr(ff), ff.Type)); + } + } + string hint; + switch (e.CoCall) { + case FunctionCallExpr.CoCallResolution.NoBecauseFunctionHasSideEffects: + hint = "note that only functions without side effects can be called co-recursively"; + break; + case FunctionCallExpr.CoCallResolution.NoBecauseFunctionHasPostcondition: + hint = "note that only functions without any ensures clause can be called co-recursively"; + break; + case FunctionCallExpr.CoCallResolution.NoBecauseIsNotGuarded: + hint = "note that the call is not sufficiently guarded to be used co-recursively"; + break; + case FunctionCallExpr.CoCallResolution.NoBecauseRecursiveCallsAreNotAllowedInThisContext: + hint = "note that calls cannot be co-recursive in this context"; + break; + case FunctionCallExpr.CoCallResolution.NoBecauseRecursiveCallsInDestructiveContext: + hint = "note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts"; + break; + case FunctionCallExpr.CoCallResolution.No: + hint = null; + break; + default: + Contract.Assert(false); // unexpected CoCallResolution + goto case FunctionCallExpr.CoCallResolution.No; // please the compiler + } + if (e.CoCallHint != null) { + hint = hint == null ? e.CoCallHint : string.Format("{0}; {1}", hint, e.CoCallHint); + } + CheckCallTermination(callExpr.tok, contextDecreases, calleeDecreases, allowance, e.Receiver, substMap, directSubstMap, e.GetTypeArgumentSubstitutions(), + etran, false, builder, codeContext.InferredDecreases, hint); + } + } + } + // all is okay, so allow this function application access to the function's axiom, except if it was okay because of the self-call allowance. + Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(callExpr.tok, e.Function.FullSanitizedName + "#canCall", Bpl.Type.Bool); + List args = etran.FunctionInvocationArguments(e, null, null); + Bpl.Expr canCallFuncAppl = new Bpl.NAryExpr(GetToken(expr), new Bpl.FunctionCall(canCallFuncID), args); + builder.Add(TrAssumeCmd(callExpr.tok, allowance == null ? canCallFuncAppl : BplOr(etran.TrExpr(allowance), canCallFuncAppl))); + + var returnType = e.Type.AsDatatype; + if (returnType != null && returnType.Ctors.Count == 1) { + var correctConstructor = FunctionCall(e.tok, returnType.Ctors[0].QueryField.FullSanitizedName, Bpl.Type.Bool, etran.TrExpr(e)); + // There is only one constructor, so the value must be been constructed by it; might as well assume that here. + builder.Add(TrAssumeCmd(callExpr.tok, correctConstructor)); + } + } + } + private void CheckWellformedComprehensionExpression(WFOptions wfOptions, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, ComprehensionExpr expr) { var lam = expr as LambdaExpr; diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/cli/measure-complexity.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/cli/measure-complexity.dfy.expect index 0c1adb59b33..28ceaeb3255 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/cli/measure-complexity.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/cli/measure-complexity.dfy.expect @@ -6,27 +6,27 @@ measure-complexity.dfy(9,18): measure-complexity.dfy(8,18): measure-complexity.dfy(9,15): measure-complexity.dfy(7,18): -measure-complexity.dfy(19,4): +measure-complexity.dfy(19,7): measure-complexity.dfy(6,18): measure-complexity.dfy(7,15): -measure-complexity.dfy(19,10): +measure-complexity.dfy(19,18): measure-complexity.dfy(7,13): -measure-complexity.dfy(17,4): -measure-complexity.dfy(17,15): -measure-complexity.dfy(19,15): measure-complexity.dfy(19,24): +measure-complexity.dfy(17,7): +measure-complexity.dfy(17,15): measure-complexity.dfy(17,10): +measure-complexity.dfy(19,10): measure-complexity.dfy(15,6): -measure-complexity.dfy(19,4): -measure-complexity.dfy(19,4): +measure-complexity.dfy(19,7): +measure-complexity.dfy(19,7): measure-complexity.dfy(9,13): -measure-complexity.dfy(17,4): +measure-complexity.dfy(17,7): +measure-complexity.dfy(19,18): measure-complexity.dfy(6,13): measure-complexity.dfy(8,15): -measure-complexity.dfy(17,4): +measure-complexity.dfy(17,7): measure-complexity.dfy(8,13): -measure-complexity.dfy(19,15): measure-complexity.dfy(6,15): -measure-complexity.dfy(19,15): +measure-complexity.dfy(19,18): measure-complexity.dfy(12,9): measure-complexity.dfy(5,7): diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CustomErrorMesage.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CustomErrorMesage.dfy.expect index bfe1cb69107..4a8e4870fe4 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CustomErrorMesage.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CustomErrorMesage.dfy.expect @@ -2,7 +2,7 @@ CustomErrorMesage.dfy(6,45): Error: m: x must be positive CustomErrorMesage.dfy(10,45): Error: f: x must be positive CustomErrorMesage.dfy(15,2): Error: when calling foo, you must supply a positive x CustomErrorMesage.dfy(19,71): Related location: this proposition could not be proved -CustomErrorMesage.dfy(18,15): Error: a postcondition could not be proved on this return path +CustomErrorMesage.dfy(22,2): Error: a postcondition could not be proved on this return path CustomErrorMesage.dfy(20,85): Related location: cannot establish that return value of foo is always negative CustomErrorMesage.dfy(26,14): Error: a precondition for this call could not be proved CustomErrorMesage.dfy(30,71): Related location: when calling bar, you must supply a positive x diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/RevealConsistency.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/RevealConsistency.dfy.expect index d441109d535..4f47a8bab56 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/RevealConsistency.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/RevealConsistency.dfy.expect @@ -1,4 +1,4 @@ -RevealConsistency.dfy(7,15): Error: a postcondition could not be proved on this return path +RevealConsistency.dfy(11,2): Error: a postcondition could not be proved on this return path RevealConsistency.dfy(8,14): Related location: this is the postcondition that could not be proved Dafny program verifier finished with 1 verified, 1 error diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Bug160.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Bug160.dfy.expect index 885e8ba5c59..adf98276d91 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Bug160.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Bug160.dfy.expect @@ -1,4 +1,4 @@ -Bug160.dfy(26,18): Error: a postcondition could not be proved on this return path +Bug160.dfy(29,4): Error: a postcondition could not be proved on this return path Bug160.dfy(27,13): Related location: this is the postcondition that could not be proved Dafny program verifier finished with 2 verified, 1 error diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/exports/OpaqueFunctions.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/exports/OpaqueFunctions.dfy.expect index 2748ea237d8..8c7c7f11ac5 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/exports/OpaqueFunctions.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/exports/OpaqueFunctions.dfy.expect @@ -1,4 +1,4 @@ -OpaqueFunctions.dfy(16,17): Error: a postcondition could not be proved on this return path +OpaqueFunctions.dfy(18,4): Error: a postcondition could not be proved on this return path OpaqueFunctions.dfy(17,14): Related location: this is the postcondition that could not be proved OpaqueFunctions.dfy(58,2): Error: a postcondition could not be proved on this return path OpaqueFunctions.dfy(57,16): Related location: this is the postcondition that could not be proved diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/traits/NonReferenceTraitsVerify.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/traits/NonReferenceTraitsVerify.dfy.expect index 8391abdb033f1ace414d9b53846d70d4d6f295f8..014bd0420fc1afe51bd1039298c8d58593e3f483 100644 GIT binary patch literal 4153 zcmeI0O;5ux42C)HSNI50g_f>kA7{AkFd^=tafzii8IC)&e@`-m1_Jefv}h8odTJy) ze%|NeZoJ!KgN&}mN7f|a37MM1t!WO|vqf=tvxWy|&uh@I^FeCw3<-G$weMR4&I>#v z?AY(oK>RG00x(L?4t84XWHZ=eUX*3*vBg$PEYSK|r|RpEISy46>4^xCNXMyS08if`XI+V$RKD3_}c3hJVWO>qD1Z}1lp&}@RVkR z>VV=aA;uPc5Rl}$3s>(}nb&Hfsw#SU?eaS7zz9{0+V~)xI&joaB9iG`~CJTV2R5-|M@2$Of-4ELS@ delta 45 pcmdm~uuouu9E-7zq47q=B}`z(M8){c>damU&Sh3@rpbT!Z2=?y4FLcE From f07779497357e7ad55a9046a9cc6d5d2322a43ab Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 12 Aug 2024 14:47:33 +0200 Subject: [PATCH 36/63] Change signature --- .../BoogieGenerator.ExpressionWellformed.cs | 32 +++++++++---------- ...oogieGenerator.Functions.Wellformedness.cs | 2 +- .../Verifier/BoogieGenerator.TrStatement.cs | 4 +-- .../Verifier/BoogieGenerator.Types.cs | 4 +-- Source/DafnyCore/Verifier/BoogieGenerator.cs | 9 +++--- 5 files changed, 25 insertions(+), 26 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 741b09a9e67..22337fdb6fa 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -253,7 +253,7 @@ void CheckWellformed(Expression expr, WFOptions wfOptions, List locals Contract.Requires(builder != null); Contract.Requires(etran != null); Contract.Requires(predef != null); - CheckWellformedWithResult(expr, wfOptions, null, locals, builder, etran); + CheckWellformedWithResult(expr, wfOptions, locals, builder, etran, null); } /// @@ -264,8 +264,8 @@ void CheckWellformed(Expression expr, WFOptions wfOptions, List locals /// See class WFOptions for descriptions of the specified options. /// void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, - AddResultCommands addResultCommands, - List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { + List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, + AddResultCommands addResultCommands) { Contract.Requires(expr != null); Contract.Requires(wfOptions != null); Contract.Requires(locals != null); @@ -283,7 +283,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, switch (expr) { case StaticReceiverExpr stexpr: { if (stexpr.ObjectToDiscard != null) { - CheckWellformedWithResult(stexpr.ObjectToDiscard, wfOptions, null, locals, builder, etran); + CheckWellformedWithResult(stexpr.ObjectToDiscard, wfOptions, locals, builder, etran, null); } break; @@ -910,9 +910,9 @@ void CheckOperand(Expression operand) { Contract.Assert(letExpr != null); CheckWellformedLetExprWithResult(letExpr, wfOptions, addResultCommands, locals, bThen, etran, false); } else { - CheckWellformedWithResult(e.Thn, wfOptions, addResultCommands, locals, bThen, etran); + CheckWellformedWithResult(e.Thn, wfOptions, locals, bThen, etran, addResultCommands); } - CheckWellformedWithResult(e.Els, wfOptions, addResultCommands, locals, bElse, etran); + CheckWellformedWithResult(e.Els, wfOptions, locals, bElse, etran, addResultCommands); builder.Add(new Bpl.IfCmd(iteExpr.tok, etran.TrExpr(e.Test), bThen.Collect(iteExpr.tok), null, bElse.Collect(iteExpr.tok))); addResultCommands = null; break; @@ -936,18 +936,18 @@ void CheckOperand(Expression operand) { CheckNotGhostVariant(e.InCompiledContext, updateExpr, e.Root, "update of", e.Members, e.LegalSourceConstructors, builder, etran); - CheckWellformedWithResult(e.ResolvedExpression, wfOptions, addResultCommands, locals, builder, etran); + CheckWellformedWithResult(e.ResolvedExpression, wfOptions, locals, builder, etran, addResultCommands); addResultCommands = null; break; } case ConcreteSyntaxExpression expression: { var e = expression; - CheckWellformedWithResult(e.ResolvedExpression, wfOptions, addResultCommands, locals, builder, etran); + CheckWellformedWithResult(e.ResolvedExpression, wfOptions, locals, builder, etran, addResultCommands); addResultCommands = null; break; } case NestedMatchExpr nestedMatchExpr: - CheckWellformedWithResult(nestedMatchExpr.Flattened, wfOptions, addResultCommands, locals, builder, etran); + CheckWellformedWithResult(nestedMatchExpr.Flattened, wfOptions, locals, builder, etran, addResultCommands); addResultCommands = null; break; case BoogieFunctionCall call: { @@ -1016,9 +1016,9 @@ private void CheckFunctionCallWellformed(Expression expr, WFOptions wfOptions, L directSubstMap.Add(p, ee); if (!(ee is DefaultValueExpression)) { - CheckWellformedWithResult(ee, wfOptions, (returnBuilder, result) => { + CheckWellformedWithResult(ee, wfOptions, locals, builder, etran, (returnBuilder, result) => { CheckSubrange(result.Tok, etran.TrExpr(result), ee.Type, et, ee, returnBuilder); - }, locals, builder, etran); + }); } Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(p.tok, lhs, AdaptBoxing(p.tok, etran.TrExpr(ee), cce.NonNull(ee.Type), et)); builder.Add(cmd); @@ -1310,7 +1310,7 @@ void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { } } - CheckWellformedWithResult(body, newOptions, AddResultCommands, locals, b, comprehensionEtran); + CheckWellformedWithResult(body, newOptions, locals, b, comprehensionEtran, AddResultCommands); }); if (mc != null) { @@ -1416,7 +1416,7 @@ private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, AddResultCommands ad // TODO: // resultDescription = "match expression"; - CheckWellformedWithResult(mc.Body, wfOptions, addResultCommands, locals, b, etran); + CheckWellformedWithResult(mc.Body, wfOptions, locals, b, etran, addResultCommands); ifCmd = new Bpl.IfCmd(mc.tok, Bpl.Expr.Eq(src, ct), b.Collect(mc.tok), ifCmd, els); els = null; } @@ -1451,7 +1451,7 @@ private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions wfOptions, Add } } - CheckWellformedWithResult(expression, wfOptions, addResultCommands, locals, builder, etran); + CheckWellformedWithResult(expression, wfOptions, locals, builder, etran, addResultCommands); } /// @@ -1542,12 +1542,12 @@ void CheckPostconditionForRhs(BoogieStmtListBuilder innerBuilder, Expression bod CheckSubsetType(etran, body, rIe, pat.Expr.Type, innerBuilder); } - CheckWellformedWithResult(e.RHSs[i], wfOptions, CheckPostconditionForRhs, locals, builder, etran); + CheckWellformedWithResult(e.RHSs[i], wfOptions, locals, builder, etran, CheckPostconditionForRhs); CheckCasePatternShape(pat, rhs, rIe, rhs.tok, pat.Expr.Type, builder); var substExpr = Substitute(pat.Expr, null, substMap); builder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, e.tok, substExpr, e => Bpl.Expr.Eq(e, rIe), "let expression binding")); } - CheckWellformedWithResult(Substitute(e.Body, null, substMap), wfOptions, addResultCommands, locals, builder, etran); + CheckWellformedWithResult(Substitute(e.Body, null, substMap), wfOptions, locals, builder, etran, addResultCommands); } else { // CheckWellformed(var b :| RHS(b); Body(b)) = diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs index 14daf0f9f29..c234d3f9ae0 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs @@ -227,7 +227,7 @@ void CheckPostcondition(BoogieStmtListBuilder innerBuilder, Expression innerBody innerBuilder.Add(new ReturnCmd(innerBody.Tok)); } - generator.CheckWellformedWithResult(f.Body, wfo, CheckPostcondition, locals, bodyCheckBuilder, etran); + generator.CheckWellformedWithResult(f.Body, wfo, locals, bodyCheckBuilder, etran, CheckPostcondition); // var b$reads_guards#0 : bool ... locals.AddRange(wfo.Locals); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs index d1deea8a981..7b9c017b928 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs @@ -415,7 +415,7 @@ private void TranslateVariableDeclaration(BoogieStmtListBuilder builder, List Bpl.Expr.Eq(boogieTupleReference, AdaptBoxing(rhs.tok, e, rhs.Type, pat.Expr.Type)))); }; - CheckWellformedWithResult(rhs, new WFOptions(null, false, false), addResultCommands, locals, builder, etran); + CheckWellformedWithResult(rhs, new WFOptions(null, false, false), locals, builder, etran, addResultCommands); builder.Add(TrAssumeCmd(rhs.tok, etran.CanCallAssumption(rhs))); builder.Add(new CommentCmd("CheckWellformedWithResult: any expression")); builder.Add(TrAssumeCmd(rhs.tok, MkIs(boogieTupleReference, pat.Expr.Type))); @@ -2909,7 +2909,7 @@ void TrStmt_CheckWellformed(Expression expr, BoogieStmtListBuilder builder, List if (lValueContext) { options = options.WithLValueContext(true); } - CheckWellformedWithResult(expr, options, addResultCommands, locals, builder, etran); + CheckWellformedWithResult(expr, options, locals, builder, etran, addResultCommands); builder.Add(TrAssumeCmd(expr.tok, etran.CanCallAssumption(expr))); } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs index 63aa6f6b7ff..7545f602c8d 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs @@ -1497,7 +1497,7 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { // check well-formedness of the witness expression (including termination, and reads checks) var ghostCodeContext = codeContext; codeContext = decl.WitnessKind == SubsetTypeDecl.WKind.Compiled ? new CallableWrapper(decl, false) : ghostCodeContext; - CheckWellformedWithResult(decl.Witness, new WFOptions(null, true), (returnBuilder, result) => { + CheckWellformedWithResult(decl.Witness, new WFOptions(null, true), locals, witnessCheckBuilder, etran, (returnBuilder, result) => { // check that the witness is assignable to the type of the given bound variable if (decl is SubsetTypeDecl) { // Note, for new-types, this has already been checked by CheckWellformed. @@ -1510,7 +1510,7 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { var desc = new PODesc.WitnessCheck(witnessString, witnessExpr); SplitAndAssertExpression(returnBuilder, witnessExpr, etran, context, desc); - }, locals, witnessCheckBuilder, etran); + }); codeContext = ghostCodeContext; } else if (decl.WitnessKind == SubsetTypeDecl.WKind.CompiledZero) { diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index 71f5148d31f..44cfe826596 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -2339,11 +2339,10 @@ void AddWellformednessCheck(DatatypeCtor ctor) { foreach (var formal in ctor.Formals.Where(formal => formal.DefaultValue != null)) { var e = formal.DefaultValue; CheckWellformedWithResult(e, new WFOptions(null, true, - false, true), - (returnBuilder, result) => { - builder.Add(new Bpl.AssumeCmd(e.tok, etran.CanCallAssumption(e))); - CheckSubrange(result.tok, etran.TrExpr(result), e.Type, formal.Type, e, returnBuilder); - }, locals, builder, etran); + 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)) { From fb3401aa374b24d84d732bda76a329753ea70ce9 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 12 Aug 2024 14:48:12 +0200 Subject: [PATCH 37/63] Undo extract method --- .../BoogieGenerator.ExpressionWellformed.cs | 768 +++++++++--------- 1 file changed, 378 insertions(+), 390 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 22337fdb6fa..d78946bb438 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -659,7 +659,257 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, break; } case FunctionCallExpr callExpr: { - CheckFunctionCallWellformed(expr, wfOptions, locals, builder, etran, callExpr, readFrames); + FunctionCallExpr e = callExpr; + Contract.Assert(e.Function != null); // follows from the fact that expr has been successfully resolved + if (e.Function is SpecialFunction) { + CheckWellformedSpecialFunction(e, wfOptions, null, null, locals, builder, etran); + } else { + // check well-formedness of receiver + CheckWellformed(e.Receiver, wfOptions, locals, builder, etran); + if (!e.Function.IsStatic && !(e.Receiver is ThisExpr) && !e.Receiver.Type.IsArrowType) { + CheckNonNull(callExpr.tok, e.Receiver, builder, etran, wfOptions.AssertKv); + } else if (e.Receiver.Type.IsArrowType) { + CheckFunctionSelectWF("function specification", builder, etran, e.Receiver, ""); + } + if (!e.Function.IsStatic && !etran.UsesOldHeap) { + // the argument can't be assumed to be allocated for the old heap + Type et = UserDefinedType.FromTopLevelDecl(e.tok, e.Function.EnclosingClass).Subst(e.GetTypeArgumentSubstitutions()); + builder.Add(new Bpl.CommentCmd("assume allocatedness for receiver argument to function")); + builder.Add(TrAssumeCmd(e.Receiver.tok, MkIsAllocBox(BoxIfNecessary(e.Receiver.tok, etran.TrExpr(e.Receiver), e.Receiver.Type), et, etran.HeapExpr))); + } + // create a local variable for each formal parameter, and assign each actual parameter to the corresponding local + Dictionary substMap = new Dictionary(); + Dictionary directSubstMap = new Dictionary(); + for (int i = 0; i < e.Function.Ins.Count; i++) { + Formal p = e.Function.Ins[i]; + // Note, in the following, the "##" makes the variable invisible in BVD. An alternative would be to communicate + // to BVD what this variable stands for and display it as such to the user. + Type et = p.Type.Subst(e.GetTypeArgumentSubstitutions()); + LocalVariable local = new LocalVariable(p.RangeToken, "##" + p.Name, et, p.IsGhost); + local.type = local.SyntacticType; // resolve local here + var ie = new IdentifierExpr(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator)) { + Var = local + }; + 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)))); + Bpl.IdentifierExpr lhs = (Bpl.IdentifierExpr)etran.TrExpr(ie); // TODO: is this cast always justified? + Expression ee = e.Args[i]; + directSubstMap.Add(p, ee); + + if (!(ee is DefaultValueExpression)) { + CheckWellformedWithResult(ee, wfOptions, locals, builder, etran, (returnBuilder, result) => { + CheckSubrange(result.Tok, etran.TrExpr(result), ee.Type, et, ee, returnBuilder); + }); + } + Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(p.tok, lhs, AdaptBoxing(p.tok, etran.TrExpr(ee), cce.NonNull(ee.Type), et)); + builder.Add(cmd); + if (!etran.UsesOldHeap) { + // the argument can't be assumed to be allocated for the old heap + builder.Add(new Bpl.CommentCmd("assume allocatedness for argument to function")); + builder.Add(TrAssumeCmd(e.Args[i].tok, MkIsAlloc(lhs, et, etran.HeapExpr))); + } + } + + // Check that every parameter is available in the state in which the function is invoked; this means checking that it has + // the right type and is allocated. These checks usually hold trivially, on account of that the Dafny language only gives + // access to expressions of the appropriate type and that are allocated in the current state. However, if the function is + // invoked in the 'old' state or if the function invoked is a two-state function with a non-new parameter, then we need to + // check that its arguments were all available at that time as well. + if (etran.UsesOldHeap) { + 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)); + } + } + 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)); + } + } + } 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)); + } + } + Contract.Assert(e.Function.Ins.Count == e.Args.Count); + for (int i = 0; i < e.Args.Count; i++) { + var formal = e.Function.Ins[i]; + if (formal.IsOld) { + Expression ee = e.Args[i]; + 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( + $"argument{pIdx} for parameter '{formal.Name}'", + "in the two-state function's previous state" + PODesc.IsAllocated.HelperFormal(formal), + ee, + e.AtLabel + ); + builder.Add(Assert(GetToken(ee), wh, desc)); + } + } + } + } + // check that the preconditions for the call hold + // the check for .reads function must be translated explicitly: their declaration lacks + // an explicit precondition, which is added as an axiom in Translator.cs + 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))); + + if (wfOptions.DoReadsChecks) { + // check that the callee reads only what the caller is already allowed to read + Type objset = program.SystemModuleManager.ObjectSetType(); + Expression wrap = new BoogieWrapper( + FunctionCall(expr.tok, Reads(e.Args.Count()), TrType(objset), arguments), + objset); + var reads = new FrameExpression(expr.tok, wrap, null); + List requiredFrames; + switch (e.Receiver.Resolved) { + case MemberSelectExpr { Member: MethodOrFunction readsReceiver }: { + var receiverReplacement = readsReceiver.IsStatic + ? null + : new ThisExpr(readsReceiver); + var receiverSubstMap = readsReceiver.Ins.Zip(e.Args) + .ToDictionary(fa => fa.First as IVariable, fa => fa.Second); + var subst = new Substituter(receiverReplacement, receiverSubstMap, e.GetTypeArgumentSubstitutions()); + requiredFrames = readsReceiver.Reads.Expressions.ConvertAll(subst.SubstFrameExpr); + break; + } + default: + var readsCall = new ApplyExpr( + Token.NoToken, + new ExprDotName(Token.NoToken, e.Receiver.Resolved, "reads", null), + e.Args, + Token.NoToken + ); + readsCall.Type = objset; + requiredFrames = new() { new FrameExpression(Token.NoToken, readsCall, null) }; + break; + } + var desc = new PODesc.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); + } + + } else { + // Directly substitutes arguments for formals, to be displayed to the user + var argSubstMap = e.Function.Ins.Zip(e.Args).ToDictionary(fa => fa.First as IVariable, fa => fa.Second); + var directSub = new Substituter(e.Receiver, argSubstMap, e.GetTypeArgumentSubstitutions()); + + foreach (AttributedExpression p in e.Function.Req) { + var directPrecond = directSub.Substitute(p.E); + + Expression precond = Substitute(p.E, e.Receiver, substMap, e.GetTypeArgumentSubstitutions()); + var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); + 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); + if (wfOptions.AssertKv != null) { + // use the given assert attribute only + builder.Add(Assert(tok, ss.E, desc, wfOptions.AssertKv)); + } else { + builder.Add(AssertNS(tok, ss.E, desc)); + } + } + } + if (wfOptions.AssertKv == null) { + // assume only if no given assert attribute is given + builder.Add(TrAssumeCmd(callExpr.tok, etran.TrExpr(precond))); + } + } + if (wfOptions.DoReadsChecks) { + // check that the callee reads only what the caller is already allowed to read + + // 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); + + // ... but that substitution isn't needed for frames passed to CheckFrameSubset + var readsSubst = new Substituter(null, new Dictionary(), e.GetTypeArgumentSubstitutions()); + CheckFrameSubset(callExpr.tok, + e.Function.Reads.Expressions.ConvertAll(readsSubst.SubstFrameExpr), + e.Receiver, substMap, etran, etran.ReadsFrame(callExpr.tok), wfOptions.AssertSink(this, builder), desc, wfOptions.AssertKv); + } + } + Expression allowance = null; + if (codeContext != null && e.CoCall != FunctionCallExpr.CoCallResolution.Yes && !(e.Function is ExtremePredicate)) { + // check that the decreases measure goes down + var calleeSCCLookup = e.IsByMethodCall ? (ICallable)e.Function.ByMethodDecl : e.Function; + Contract.Assert(calleeSCCLookup != null); + if (ModuleDefinition.InSameSCC(calleeSCCLookup, codeContext)) { + if (wfOptions.DoOnlyCoarseGrainedTerminationChecks) { + builder.Add(Assert(GetToken(expr), Bpl.Expr.False, new PODesc.IsNonRecursive())); + } else { + List contextDecreases = codeContext.Decreases.Expressions; + List calleeDecreases = e.Function.Decreases.Expressions; + if (e.Function == wfOptions.SelfCallsAllowance) { + allowance = Expression.CreateBoolLiteral(e.tok, true); + if (!e.Function.IsStatic) { + allowance = Expression.CreateAnd(allowance, Expression.CreateEq(e.Receiver, new ThisExpr(e.Function), e.Receiver.Type)); + } + for (int i = 0; i < e.Args.Count; i++) { + Expression ee = e.Args[i]; + Formal ff = e.Function.Ins[i]; + allowance = Expression.CreateAnd(allowance, Expression.CreateEq(ee, Expression.CreateIdentExpr(ff), ff.Type)); + } + } + string hint; + switch (e.CoCall) { + case FunctionCallExpr.CoCallResolution.NoBecauseFunctionHasSideEffects: + hint = "note that only functions without side effects can be called co-recursively"; + break; + case FunctionCallExpr.CoCallResolution.NoBecauseFunctionHasPostcondition: + hint = "note that only functions without any ensures clause can be called co-recursively"; + break; + case FunctionCallExpr.CoCallResolution.NoBecauseIsNotGuarded: + hint = "note that the call is not sufficiently guarded to be used co-recursively"; + break; + case FunctionCallExpr.CoCallResolution.NoBecauseRecursiveCallsAreNotAllowedInThisContext: + hint = "note that calls cannot be co-recursive in this context"; + break; + case FunctionCallExpr.CoCallResolution.NoBecauseRecursiveCallsInDestructiveContext: + hint = "note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts"; + break; + case FunctionCallExpr.CoCallResolution.No: + hint = null; + break; + default: + Contract.Assert(false); // unexpected CoCallResolution + goto case FunctionCallExpr.CoCallResolution.No; // please the compiler + } + if (e.CoCallHint != null) { + hint = hint == null ? e.CoCallHint : string.Format("{0}; {1}", hint, e.CoCallHint); + } + CheckCallTermination(callExpr.tok, contextDecreases, calleeDecreases, allowance, e.Receiver, substMap, directSubstMap, e.GetTypeArgumentSubstitutions(), + etran, false, builder, codeContext.InferredDecreases, hint); + } + } + } + // all is okay, so allow this function application access to the function's axiom, except if it was okay because of the self-call allowance. + Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(callExpr.tok, e.Function.FullSanitizedName + "#canCall", Bpl.Type.Bool); + List args = etran.FunctionInvocationArguments(e, null, null); + Bpl.Expr canCallFuncAppl = new Bpl.NAryExpr(GetToken(expr), new Bpl.FunctionCall(canCallFuncID), args); + builder.Add(TrAssumeCmd(callExpr.tok, allowance == null ? canCallFuncAppl : BplOr(etran.TrExpr(allowance), canCallFuncAppl))); + + var returnType = e.Type.AsDatatype; + if (returnType != null && returnType.Ctors.Count == 1) { + var correctConstructor = FunctionCall(e.tok, returnType.Ctors[0].QueryField.FullSanitizedName, Bpl.Type.Bool, etran.TrExpr(e)); + // There is only one constructor, so the value must be been constructed by it; might as well assume that here. + builder.Add(TrAssumeCmd(callExpr.tok, correctConstructor)); + } + } break; } @@ -890,8 +1140,133 @@ void CheckOperand(Expression operand) { addResultCommands = null; break; case ComprehensionExpr comprehensionExpr: { - CheckWellformedComprehensionExpression(wfOptions, locals, builder, etran, comprehensionExpr); - break; + var lam = comprehensionExpr as LambdaExpr; + var mc = comprehensionExpr as MapComprehension; + if (mc is { IsGeneralMapComprehension: false }) { + mc = null; // mc will be non-null when "e" is a general map comprehension + } + + // This is a WF check, so we look at the original quantifier, not the split one. + // This ensures that cases like forall x :: x != null && f(x.a) do not fail to verify. + + builder.Add(new Bpl.CommentCmd("Begin Comprehension WF check")); + BplIfIf(comprehensionExpr.tok, lam != null, null, builder, nextBuilder => { + var comprehensionEtran = etran; + if (lam != null) { + // Havoc heap + locals.Add(BplLocalVar(CurrentIdGenerator.FreshId((etran.UsesOldHeap ? "$Heap_at_" : "") + "$lambdaHeap#"), predef.HeapType, out var lambdaHeap)); + comprehensionEtran = new ExpressionTranslator(comprehensionEtran, lambdaHeap); + nextBuilder.Add(new HavocCmd(comprehensionExpr.tok, Singleton((Bpl.IdentifierExpr)comprehensionEtran.HeapExpr))); + nextBuilder.Add(new AssumeCmd(comprehensionExpr.tok, FunctionCall(comprehensionExpr.tok, BuiltinFunction.IsGoodHeap, null, comprehensionEtran.HeapExpr))); + nextBuilder.Add(new AssumeCmd(comprehensionExpr.tok, HeapSameOrSucc(etran.HeapExpr, comprehensionEtran.HeapExpr))); + } + + var substMap = SetupBoundVarsAsLocals(comprehensionExpr.BoundVars, out var typeAntecedents, nextBuilder, locals, comprehensionEtran); + BplIfIf(comprehensionExpr.tok, true, typeAntecedents, nextBuilder, newBuilder => { + var s = new Substituter(null, substMap, new Dictionary()); + var body = Substitute(comprehensionExpr.Term, null, substMap); + var bodyLeft = mc != null ? Substitute(mc.TermLeft, null, substMap) : null; + var substMapPrime = mc != null ? SetupBoundVarsAsLocals(comprehensionExpr.BoundVars, newBuilder, locals, comprehensionEtran, "#prime") : null; + var bodyLeftPrime = mc != null ? Substitute(mc.TermLeft, null, substMapPrime) : null; + var bodyPrime = mc != null ? Substitute(comprehensionExpr.Term, null, substMapPrime) : null; + List reads = null; + + var newOptions = wfOptions; + if (lam != null) { + // Set up a new frame + var frameName = CurrentIdGenerator.FreshId("$_Frame#l"); + reads = lam.Reads.Expressions.ConvertAll(s.SubstFrameExpr); + comprehensionEtran = comprehensionEtran.WithReadsFrame(frameName, lam); + DefineFrame(comprehensionExpr.tok, comprehensionEtran.ReadsFrame(comprehensionExpr.tok), reads, newBuilder, locals, frameName, comprehensionEtran); + + // Check frame WF and that it read covers itself + var delayer = new ReadsCheckDelayer(comprehensionEtran, wfOptions.SelfCallsAllowance, locals, builder, newBuilder); + delayer.DoWithDelayedReadsChecks(false, wfo => { + CheckFrameWellFormed(wfo, reads, locals, newBuilder, comprehensionEtran); + }); + + // continue doing reads checks, but don't delay them + newOptions = new WFOptions(wfOptions.SelfCallsAllowance, true, false); + } + + // check requires/range + Bpl.Expr guard = null; + if (comprehensionExpr.Range != null) { + var range = Substitute(comprehensionExpr.Range, null, substMap); + CheckWellformed(range, newOptions, locals, newBuilder, comprehensionEtran); + guard = comprehensionEtran.TrExpr(range); + } + + if (mc != null) { + Contract.Assert(bodyLeft != null); + BplIfIf(comprehensionExpr.tok, guard != null, guard, newBuilder, b => { CheckWellformed(bodyLeft, newOptions, locals, b, comprehensionEtran); }); + } + BplIfIf(comprehensionExpr.tok, guard != null, guard, newBuilder, b => { + Bpl.Expr resultIe = null; + Type rangeType = null; + 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); + resultIe = new Bpl.IdentifierExpr(body.tok, resultVar); + rangeType = lam.Type.AsArrowType.Result; + } + + void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { + if (rangeType != null) { + CheckSubsetType(etran, result, resultIe, rangeType, returnBuilder); + } + } + + CheckWellformedWithResult(body, newOptions, locals, b, comprehensionEtran, AddResultCommands); + }); + + if (mc != null) { + Contract.Assert(substMapPrime != null); + Contract.Assert(bodyLeftPrime != null); + Contract.Assert(bodyPrime != null); + Bpl.Expr guardPrime = null; + if (guard != null) { + Contract.Assert(comprehensionExpr.Range != null); + var rangePrime = Substitute(comprehensionExpr.Range, null, substMapPrime); + guardPrime = comprehensionEtran.TrExpr(rangePrime); + } + BplIfIf(comprehensionExpr.tok, guard != null, BplAnd(guard, guardPrime), newBuilder, b => { + 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))); + }); + } + }); + + if (lam != null) { + // assume false (heap was havoced inside an if) + Contract.Assert(nextBuilder != builder); + nextBuilder.Add(new AssumeCmd(comprehensionExpr.tok, Bpl.Expr.False)); + } + }); + + bool needTypeConstraintCheck; + if (lam == null) { + needTypeConstraintCheck = true; + } else { + // omit constraint check if the type is according to the syntax of the expression + var arrowType = (UserDefinedType)comprehensionExpr.Type.NormalizeExpandKeepConstraints(); + if (ArrowType.IsPartialArrowTypeName(arrowType.Name)) { + needTypeConstraintCheck = lam.Reads.Expressions.Count != 0; + } else if (ArrowType.IsTotalArrowTypeName(arrowType.Name)) { + needTypeConstraintCheck = lam.Reads.Expressions.Count != 0 || lam.Range != null; + } else { + needTypeConstraintCheck = true; + } + } + if (needTypeConstraintCheck) { + CheckResultToBeInType(comprehensionExpr.tok, comprehensionExpr, comprehensionExpr.Type, locals, builder, etran); + } + + builder.Add(new Bpl.CommentCmd("End Comprehension WF check")); + break; } case StmtExpr stmtExpr: CheckWellformedStmtExpr(stmtExpr, wfOptions, addResultCommands, locals, builder, etran); @@ -974,393 +1349,6 @@ void CheckOperand(Expression operand) { } } - private void CheckFunctionCallWellformed(Expression expr, WFOptions wfOptions, List locals, - BoogieStmtListBuilder builder, ExpressionTranslator etran, FunctionCallExpr callExpr, List readFrames) - { - FunctionCallExpr e = callExpr; - Contract.Assert(e.Function != null); // follows from the fact that expr has been successfully resolved - if (e.Function is SpecialFunction) { - CheckWellformedSpecialFunction(e, wfOptions, null, null, locals, builder, etran); - } else { - // check well-formedness of receiver - CheckWellformed(e.Receiver, wfOptions, locals, builder, etran); - if (!e.Function.IsStatic && !(e.Receiver is ThisExpr) && !e.Receiver.Type.IsArrowType) { - CheckNonNull(callExpr.tok, e.Receiver, builder, etran, wfOptions.AssertKv); - } else if (e.Receiver.Type.IsArrowType) { - CheckFunctionSelectWF("function specification", builder, etran, e.Receiver, ""); - } - if (!e.Function.IsStatic && !etran.UsesOldHeap) { - // the argument can't be assumed to be allocated for the old heap - Type et = UserDefinedType.FromTopLevelDecl(e.tok, e.Function.EnclosingClass).Subst(e.GetTypeArgumentSubstitutions()); - builder.Add(new Bpl.CommentCmd("assume allocatedness for receiver argument to function")); - builder.Add(TrAssumeCmd(e.Receiver.tok, MkIsAllocBox(BoxIfNecessary(e.Receiver.tok, etran.TrExpr(e.Receiver), e.Receiver.Type), et, etran.HeapExpr))); - } - // create a local variable for each formal parameter, and assign each actual parameter to the corresponding local - Dictionary substMap = new Dictionary(); - Dictionary directSubstMap = new Dictionary(); - for (int i = 0; i < e.Function.Ins.Count; i++) { - Formal p = e.Function.Ins[i]; - // Note, in the following, the "##" makes the variable invisible in BVD. An alternative would be to communicate - // to BVD what this variable stands for and display it as such to the user. - Type et = p.Type.Subst(e.GetTypeArgumentSubstitutions()); - LocalVariable local = new LocalVariable(p.RangeToken, "##" + p.Name, et, p.IsGhost); - local.type = local.SyntacticType; // resolve local here - var ie = new IdentifierExpr(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator)) { - Var = local - }; - 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)))); - Bpl.IdentifierExpr lhs = (Bpl.IdentifierExpr)etran.TrExpr(ie); // TODO: is this cast always justified? - Expression ee = e.Args[i]; - directSubstMap.Add(p, ee); - - if (!(ee is DefaultValueExpression)) { - CheckWellformedWithResult(ee, wfOptions, locals, builder, etran, (returnBuilder, result) => { - CheckSubrange(result.Tok, etran.TrExpr(result), ee.Type, et, ee, returnBuilder); - }); - } - Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(p.tok, lhs, AdaptBoxing(p.tok, etran.TrExpr(ee), cce.NonNull(ee.Type), et)); - builder.Add(cmd); - if (!etran.UsesOldHeap) { - // the argument can't be assumed to be allocated for the old heap - builder.Add(new Bpl.CommentCmd("assume allocatedness for argument to function")); - builder.Add(TrAssumeCmd(e.Args[i].tok, MkIsAlloc(lhs, et, etran.HeapExpr))); - } - } - - // Check that every parameter is available in the state in which the function is invoked; this means checking that it has - // the right type and is allocated. These checks usually hold trivially, on account of that the Dafny language only gives - // access to expressions of the appropriate type and that are allocated in the current state. However, if the function is - // invoked in the 'old' state or if the function invoked is a two-state function with a non-new parameter, then we need to - // check that its arguments were all available at that time as well. - if (etran.UsesOldHeap) { - 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)); - } - } - 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)); - } - } - } 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)); - } - } - Contract.Assert(e.Function.Ins.Count == e.Args.Count); - for (int i = 0; i < e.Args.Count; i++) { - var formal = e.Function.Ins[i]; - if (formal.IsOld) { - Expression ee = e.Args[i]; - 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( - $"argument{pIdx} for parameter '{formal.Name}'", - "in the two-state function's previous state" + PODesc.IsAllocated.HelperFormal(formal), - ee, - e.AtLabel - ); - builder.Add(Assert(GetToken(ee), wh, desc)); - } - } - } - } - // check that the preconditions for the call hold - // the check for .reads function must be translated explicitly: their declaration lacks - // an explicit precondition, which is added as an axiom in Translator.cs - 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))); - - if (wfOptions.DoReadsChecks) { - // check that the callee reads only what the caller is already allowed to read - Type objset = program.SystemModuleManager.ObjectSetType(); - Expression wrap = new BoogieWrapper( - FunctionCall(expr.tok, Reads(e.Args.Count()), TrType(objset), arguments), - objset); - var reads = new FrameExpression(expr.tok, wrap, null); - List requiredFrames; - switch (e.Receiver.Resolved) { - case MemberSelectExpr { Member: MethodOrFunction readsReceiver }: { - var receiverReplacement = readsReceiver.IsStatic - ? null - : new ThisExpr(readsReceiver); - var receiverSubstMap = readsReceiver.Ins.Zip(e.Args) - .ToDictionary(fa => fa.First as IVariable, fa => fa.Second); - var subst = new Substituter(receiverReplacement, receiverSubstMap, e.GetTypeArgumentSubstitutions()); - requiredFrames = readsReceiver.Reads.Expressions.ConvertAll(subst.SubstFrameExpr); - break; - } - default: - var readsCall = new ApplyExpr( - Token.NoToken, - new ExprDotName(Token.NoToken, e.Receiver.Resolved, "reads", null), - e.Args, - Token.NoToken - ); - readsCall.Type = objset; - requiredFrames = new() { new FrameExpression(Token.NoToken, readsCall, null) }; - break; - } - var desc = new PODesc.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); - } - - } else { - // Directly substitutes arguments for formals, to be displayed to the user - var argSubstMap = e.Function.Ins.Zip(e.Args).ToDictionary(fa => fa.First as IVariable, fa => fa.Second); - var directSub = new Substituter(e.Receiver, argSubstMap, e.GetTypeArgumentSubstitutions()); - - foreach (AttributedExpression p in e.Function.Req) { - var directPrecond = directSub.Substitute(p.E); - - Expression precond = Substitute(p.E, e.Receiver, substMap, e.GetTypeArgumentSubstitutions()); - var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); - 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); - if (wfOptions.AssertKv != null) { - // use the given assert attribute only - builder.Add(Assert(tok, ss.E, desc, wfOptions.AssertKv)); - } else { - builder.Add(AssertNS(tok, ss.E, desc)); - } - } - } - if (wfOptions.AssertKv == null) { - // assume only if no given assert attribute is given - builder.Add(TrAssumeCmd(callExpr.tok, etran.TrExpr(precond))); - } - } - if (wfOptions.DoReadsChecks) { - // check that the callee reads only what the caller is already allowed to read - - // 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); - - // ... but that substitution isn't needed for frames passed to CheckFrameSubset - var readsSubst = new Substituter(null, new Dictionary(), e.GetTypeArgumentSubstitutions()); - CheckFrameSubset(callExpr.tok, - e.Function.Reads.Expressions.ConvertAll(readsSubst.SubstFrameExpr), - e.Receiver, substMap, etran, etran.ReadsFrame(callExpr.tok), wfOptions.AssertSink(this, builder), desc, wfOptions.AssertKv); - } - } - Expression allowance = null; - if (codeContext != null && e.CoCall != FunctionCallExpr.CoCallResolution.Yes && !(e.Function is ExtremePredicate)) { - // check that the decreases measure goes down - var calleeSCCLookup = e.IsByMethodCall ? (ICallable)e.Function.ByMethodDecl : e.Function; - Contract.Assert(calleeSCCLookup != null); - if (ModuleDefinition.InSameSCC(calleeSCCLookup, codeContext)) { - if (wfOptions.DoOnlyCoarseGrainedTerminationChecks) { - builder.Add(Assert(GetToken(expr), Bpl.Expr.False, new PODesc.IsNonRecursive())); - } else { - List contextDecreases = codeContext.Decreases.Expressions; - List calleeDecreases = e.Function.Decreases.Expressions; - if (e.Function == wfOptions.SelfCallsAllowance) { - allowance = Expression.CreateBoolLiteral(e.tok, true); - if (!e.Function.IsStatic) { - allowance = Expression.CreateAnd(allowance, Expression.CreateEq(e.Receiver, new ThisExpr(e.Function), e.Receiver.Type)); - } - for (int i = 0; i < e.Args.Count; i++) { - Expression ee = e.Args[i]; - Formal ff = e.Function.Ins[i]; - allowance = Expression.CreateAnd(allowance, Expression.CreateEq(ee, Expression.CreateIdentExpr(ff), ff.Type)); - } - } - string hint; - switch (e.CoCall) { - case FunctionCallExpr.CoCallResolution.NoBecauseFunctionHasSideEffects: - hint = "note that only functions without side effects can be called co-recursively"; - break; - case FunctionCallExpr.CoCallResolution.NoBecauseFunctionHasPostcondition: - hint = "note that only functions without any ensures clause can be called co-recursively"; - break; - case FunctionCallExpr.CoCallResolution.NoBecauseIsNotGuarded: - hint = "note that the call is not sufficiently guarded to be used co-recursively"; - break; - case FunctionCallExpr.CoCallResolution.NoBecauseRecursiveCallsAreNotAllowedInThisContext: - hint = "note that calls cannot be co-recursive in this context"; - break; - case FunctionCallExpr.CoCallResolution.NoBecauseRecursiveCallsInDestructiveContext: - hint = "note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts"; - break; - case FunctionCallExpr.CoCallResolution.No: - hint = null; - break; - default: - Contract.Assert(false); // unexpected CoCallResolution - goto case FunctionCallExpr.CoCallResolution.No; // please the compiler - } - if (e.CoCallHint != null) { - hint = hint == null ? e.CoCallHint : string.Format("{0}; {1}", hint, e.CoCallHint); - } - CheckCallTermination(callExpr.tok, contextDecreases, calleeDecreases, allowance, e.Receiver, substMap, directSubstMap, e.GetTypeArgumentSubstitutions(), - etran, false, builder, codeContext.InferredDecreases, hint); - } - } - } - // all is okay, so allow this function application access to the function's axiom, except if it was okay because of the self-call allowance. - Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(callExpr.tok, e.Function.FullSanitizedName + "#canCall", Bpl.Type.Bool); - List args = etran.FunctionInvocationArguments(e, null, null); - Bpl.Expr canCallFuncAppl = new Bpl.NAryExpr(GetToken(expr), new Bpl.FunctionCall(canCallFuncID), args); - builder.Add(TrAssumeCmd(callExpr.tok, allowance == null ? canCallFuncAppl : BplOr(etran.TrExpr(allowance), canCallFuncAppl))); - - var returnType = e.Type.AsDatatype; - if (returnType != null && returnType.Ctors.Count == 1) { - var correctConstructor = FunctionCall(e.tok, returnType.Ctors[0].QueryField.FullSanitizedName, Bpl.Type.Bool, etran.TrExpr(e)); - // There is only one constructor, so the value must be been constructed by it; might as well assume that here. - builder.Add(TrAssumeCmd(callExpr.tok, correctConstructor)); - } - } - } - - private void CheckWellformedComprehensionExpression(WFOptions wfOptions, List locals, - BoogieStmtListBuilder builder, ExpressionTranslator etran, ComprehensionExpr expr) { - var lam = expr as LambdaExpr; - var mc = expr as MapComprehension; - if (mc is { IsGeneralMapComprehension: false }) { - mc = null; // mc will be non-null when "e" is a general map comprehension - } - - // This is a WF check, so we look at the original quantifier, not the split one. - // This ensures that cases like forall x :: x != null && f(x.a) do not fail to verify. - - builder.Add(new Bpl.CommentCmd("Begin Comprehension WF check")); - BplIfIf(expr.tok, lam != null, null, builder, nextBuilder => { - var comprehensionEtran = etran; - if (lam != null) { - // Havoc heap - locals.Add(BplLocalVar(CurrentIdGenerator.FreshId((etran.UsesOldHeap ? "$Heap_at_" : "") + "$lambdaHeap#"), predef.HeapType, out var lambdaHeap)); - comprehensionEtran = new ExpressionTranslator(comprehensionEtran, lambdaHeap); - nextBuilder.Add(new HavocCmd(expr.tok, Singleton((Bpl.IdentifierExpr)comprehensionEtran.HeapExpr))); - nextBuilder.Add(new AssumeCmd(expr.tok, FunctionCall(expr.tok, BuiltinFunction.IsGoodHeap, null, comprehensionEtran.HeapExpr))); - nextBuilder.Add(new AssumeCmd(expr.tok, HeapSameOrSucc(etran.HeapExpr, comprehensionEtran.HeapExpr))); - } - - var substMap = SetupBoundVarsAsLocals(expr.BoundVars, out var typeAntecedents, nextBuilder, locals, comprehensionEtran); - BplIfIf(expr.tok, true, typeAntecedents, nextBuilder, newBuilder => { - var s = new Substituter(null, substMap, new Dictionary()); - var body = Substitute(expr.Term, null, substMap); - var bodyLeft = mc != null ? Substitute(mc.TermLeft, null, substMap) : null; - var substMapPrime = mc != null ? SetupBoundVarsAsLocals(expr.BoundVars, newBuilder, locals, comprehensionEtran, "#prime") : null; - var bodyLeftPrime = mc != null ? Substitute(mc.TermLeft, null, substMapPrime) : null; - var bodyPrime = mc != null ? Substitute(expr.Term, null, substMapPrime) : null; - List reads = null; - - var newOptions = wfOptions; - if (lam != null) { - // Set up a new frame - var frameName = CurrentIdGenerator.FreshId("$_Frame#l"); - reads = lam.Reads.Expressions.ConvertAll(s.SubstFrameExpr); - comprehensionEtran = comprehensionEtran.WithReadsFrame(frameName, lam); - DefineFrame(expr.tok, comprehensionEtran.ReadsFrame(expr.tok), reads, newBuilder, locals, frameName, comprehensionEtran); - - // Check frame WF and that it read covers itself - var delayer = new ReadsCheckDelayer(comprehensionEtran, wfOptions.SelfCallsAllowance, locals, builder, newBuilder); - delayer.DoWithDelayedReadsChecks(false, wfo => { - CheckFrameWellFormed(wfo, reads, locals, newBuilder, comprehensionEtran); - }); - - // continue doing reads checks, but don't delay them - newOptions = new WFOptions(wfOptions.SelfCallsAllowance, true, false); - } - - // check requires/range - Bpl.Expr guard = null; - if (expr.Range != null) { - var range = Substitute(expr.Range, null, substMap); - CheckWellformed(range, newOptions, locals, newBuilder, comprehensionEtran); - guard = comprehensionEtran.TrExpr(range); - } - - if (mc != null) { - Contract.Assert(bodyLeft != null); - BplIfIf(expr.tok, guard != null, guard, newBuilder, b => { CheckWellformed(bodyLeft, newOptions, locals, b, comprehensionEtran); }); - } - BplIfIf(expr.tok, guard != null, guard, newBuilder, b => { - Bpl.Expr resultIe = null; - Type rangeType = null; - 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); - resultIe = new Bpl.IdentifierExpr(body.tok, resultVar); - rangeType = lam.Type.AsArrowType.Result; - } - - void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { - if (rangeType != null) { - CheckSubsetType(etran, result, resultIe, rangeType, returnBuilder); - } - } - - CheckWellformedWithResult(body, newOptions, locals, b, comprehensionEtran, AddResultCommands); - }); - - if (mc != null) { - Contract.Assert(substMapPrime != null); - Contract.Assert(bodyLeftPrime != null); - Contract.Assert(bodyPrime != null); - Bpl.Expr guardPrime = null; - if (guard != null) { - Contract.Assert(expr.Range != null); - var rangePrime = Substitute(expr.Range, null, substMapPrime); - guardPrime = comprehensionEtran.TrExpr(rangePrime); - } - BplIfIf(expr.tok, guard != null, BplAnd(guard, guardPrime), newBuilder, b => { - 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))); - }); - } - }); - - if (lam != null) { - // assume false (heap was havoced inside an if) - Contract.Assert(nextBuilder != builder); - nextBuilder.Add(new AssumeCmd(expr.tok, Bpl.Expr.False)); - } - }); - - bool needTypeConstraintCheck; - if (lam == null) { - needTypeConstraintCheck = true; - } else { - // omit constraint check if the type is according to the syntax of the expression - var arrowType = (UserDefinedType)expr.Type.NormalizeExpandKeepConstraints(); - if (ArrowType.IsPartialArrowTypeName(arrowType.Name)) { - needTypeConstraintCheck = lam.Reads.Expressions.Count != 0; - } else if (ArrowType.IsTotalArrowTypeName(arrowType.Name)) { - needTypeConstraintCheck = lam.Reads.Expressions.Count != 0 || lam.Range != null; - } else { - needTypeConstraintCheck = true; - } - } - if (needTypeConstraintCheck) { - CheckResultToBeInType(expr.tok, expr, expr.Type, locals, builder, etran); - } - - builder.Add(new Bpl.CommentCmd("End Comprehension WF check")); - return; - } - public void CheckSubsetType(ExpressionTranslator etran, Expression expr, Bpl.Expr selfCall, Type resultType, BoogieStmtListBuilder builder) { From bdbfa6ea79dbc962b29dd3ae82d204e5324832eb Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 12 Aug 2024 14:49:48 +0200 Subject: [PATCH 38/63] Undo formatting changes --- .../BoogieGenerator.ExpressionWellformed.cs | 666 +++++++++--------- 1 file changed, 333 insertions(+), 333 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index d78946bb438..81387cfa8cf 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -659,259 +659,259 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, break; } case FunctionCallExpr callExpr: { - FunctionCallExpr e = callExpr; - Contract.Assert(e.Function != null); // follows from the fact that expr has been successfully resolved - if (e.Function is SpecialFunction) { - CheckWellformedSpecialFunction(e, wfOptions, null, null, locals, builder, etran); - } else { - // check well-formedness of receiver - CheckWellformed(e.Receiver, wfOptions, locals, builder, etran); - if (!e.Function.IsStatic && !(e.Receiver is ThisExpr) && !e.Receiver.Type.IsArrowType) { - CheckNonNull(callExpr.tok, e.Receiver, builder, etran, wfOptions.AssertKv); - } else if (e.Receiver.Type.IsArrowType) { - CheckFunctionSelectWF("function specification", builder, etran, e.Receiver, ""); - } - if (!e.Function.IsStatic && !etran.UsesOldHeap) { - // the argument can't be assumed to be allocated for the old heap - Type et = UserDefinedType.FromTopLevelDecl(e.tok, e.Function.EnclosingClass).Subst(e.GetTypeArgumentSubstitutions()); - builder.Add(new Bpl.CommentCmd("assume allocatedness for receiver argument to function")); - builder.Add(TrAssumeCmd(e.Receiver.tok, MkIsAllocBox(BoxIfNecessary(e.Receiver.tok, etran.TrExpr(e.Receiver), e.Receiver.Type), et, etran.HeapExpr))); - } - // create a local variable for each formal parameter, and assign each actual parameter to the corresponding local - Dictionary substMap = new Dictionary(); - Dictionary directSubstMap = new Dictionary(); - for (int i = 0; i < e.Function.Ins.Count; i++) { - Formal p = e.Function.Ins[i]; - // Note, in the following, the "##" makes the variable invisible in BVD. An alternative would be to communicate - // to BVD what this variable stands for and display it as such to the user. - Type et = p.Type.Subst(e.GetTypeArgumentSubstitutions()); - LocalVariable local = new LocalVariable(p.RangeToken, "##" + p.Name, et, p.IsGhost); - local.type = local.SyntacticType; // resolve local here - var ie = new IdentifierExpr(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator)) { - Var = local - }; - 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)))); - Bpl.IdentifierExpr lhs = (Bpl.IdentifierExpr)etran.TrExpr(ie); // TODO: is this cast always justified? - Expression ee = e.Args[i]; - directSubstMap.Add(p, ee); - - if (!(ee is DefaultValueExpression)) { - CheckWellformedWithResult(ee, wfOptions, locals, builder, etran, (returnBuilder, result) => { - CheckSubrange(result.Tok, etran.TrExpr(result), ee.Type, et, ee, returnBuilder); - }); + FunctionCallExpr e = callExpr; + Contract.Assert(e.Function != null); // follows from the fact that expr has been successfully resolved + if (e.Function is SpecialFunction) { + CheckWellformedSpecialFunction(e, wfOptions, null, null, locals, builder, etran); + } else { + // check well-formedness of receiver + CheckWellformed(e.Receiver, wfOptions, locals, builder, etran); + if (!e.Function.IsStatic && !(e.Receiver is ThisExpr) && !e.Receiver.Type.IsArrowType) { + CheckNonNull(callExpr.tok, e.Receiver, builder, etran, wfOptions.AssertKv); + } else if (e.Receiver.Type.IsArrowType) { + CheckFunctionSelectWF("function specification", builder, etran, e.Receiver, ""); } - Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(p.tok, lhs, AdaptBoxing(p.tok, etran.TrExpr(ee), cce.NonNull(ee.Type), et)); - builder.Add(cmd); - if (!etran.UsesOldHeap) { + if (!e.Function.IsStatic && !etran.UsesOldHeap) { // the argument can't be assumed to be allocated for the old heap - builder.Add(new Bpl.CommentCmd("assume allocatedness for argument to function")); - builder.Add(TrAssumeCmd(e.Args[i].tok, MkIsAlloc(lhs, et, etran.HeapExpr))); + Type et = UserDefinedType.FromTopLevelDecl(e.tok, e.Function.EnclosingClass).Subst(e.GetTypeArgumentSubstitutions()); + builder.Add(new Bpl.CommentCmd("assume allocatedness for receiver argument to function")); + builder.Add(TrAssumeCmd(e.Receiver.tok, MkIsAllocBox(BoxIfNecessary(e.Receiver.tok, etran.TrExpr(e.Receiver), e.Receiver.Type), et, etran.HeapExpr))); } - } + // create a local variable for each formal parameter, and assign each actual parameter to the corresponding local + Dictionary substMap = new Dictionary(); + Dictionary directSubstMap = new Dictionary(); + for (int i = 0; i < e.Function.Ins.Count; i++) { + Formal p = e.Function.Ins[i]; + // Note, in the following, the "##" makes the variable invisible in BVD. An alternative would be to communicate + // to BVD what this variable stands for and display it as such to the user. + Type et = p.Type.Subst(e.GetTypeArgumentSubstitutions()); + LocalVariable local = new LocalVariable(p.RangeToken, "##" + p.Name, et, p.IsGhost); + local.type = local.SyntacticType; // resolve local here + var ie = new IdentifierExpr(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator)) { + Var = local + }; + 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)))); + Bpl.IdentifierExpr lhs = (Bpl.IdentifierExpr)etran.TrExpr(ie); // TODO: is this cast always justified? + Expression ee = e.Args[i]; + directSubstMap.Add(p, ee); - // Check that every parameter is available in the state in which the function is invoked; this means checking that it has - // the right type and is allocated. These checks usually hold trivially, on account of that the Dafny language only gives - // access to expressions of the appropriate type and that are allocated in the current state. However, if the function is - // invoked in the 'old' state or if the function invoked is a two-state function with a non-new parameter, then we need to - // check that its arguments were all available at that time as well. - if (etran.UsesOldHeap) { - 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)); + if (!(ee is DefaultValueExpression)) { + CheckWellformedWithResult(ee, wfOptions, locals, builder, etran, (returnBuilder, result) => { + CheckSubrange(result.Tok, etran.TrExpr(result), ee.Type, et, ee, returnBuilder); + }); } - } - 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)); + Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(p.tok, lhs, AdaptBoxing(p.tok, etran.TrExpr(ee), cce.NonNull(ee.Type), et)); + builder.Add(cmd); + if (!etran.UsesOldHeap) { + // the argument can't be assumed to be allocated for the old heap + builder.Add(new Bpl.CommentCmd("assume allocatedness for argument to function")); + builder.Add(TrAssumeCmd(e.Args[i].tok, MkIsAlloc(lhs, et, etran.HeapExpr))); } } - } 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)); + + // Check that every parameter is available in the state in which the function is invoked; this means checking that it has + // the right type and is allocated. These checks usually hold trivially, on account of that the Dafny language only gives + // access to expressions of the appropriate type and that are allocated in the current state. However, if the function is + // invoked in the 'old' state or if the function invoked is a two-state function with a non-new parameter, then we need to + // check that its arguments were all available at that time as well. + if (etran.UsesOldHeap) { + 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)); + } } - } - Contract.Assert(e.Function.Ins.Count == e.Args.Count); - for (int i = 0; i < e.Args.Count; i++) { - var formal = e.Function.Ins[i]; - if (formal.IsOld) { + 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.OldAt(e.AtLabel), ISALLOC, true); + Bpl.Expr wh = GetWhereClause(ee.tok, etran.TrExpr(ee), ee.Type, etran, ISALLOC, true); if (wh != null) { - var pIdx = e.Args.Count == 1 ? "" : " at index " + i; - var desc = new PODesc.IsAllocated( - $"argument{pIdx} for parameter '{formal.Name}'", - "in the two-state function's previous state" + PODesc.IsAllocated.HelperFormal(formal), - ee, - e.AtLabel - ); + 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)); } } - } - } - // check that the preconditions for the call hold - // the check for .reads function must be translated explicitly: their declaration lacks - // an explicit precondition, which is added as an axiom in Translator.cs - 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))); - - if (wfOptions.DoReadsChecks) { - // check that the callee reads only what the caller is already allowed to read - Type objset = program.SystemModuleManager.ObjectSetType(); - Expression wrap = new BoogieWrapper( - FunctionCall(expr.tok, Reads(e.Args.Count()), TrType(objset), arguments), - objset); - var reads = new FrameExpression(expr.tok, wrap, null); - List requiredFrames; - switch (e.Receiver.Resolved) { - case MemberSelectExpr { Member: MethodOrFunction readsReceiver }: { - var receiverReplacement = readsReceiver.IsStatic - ? null - : new ThisExpr(readsReceiver); - var receiverSubstMap = readsReceiver.Ins.Zip(e.Args) - .ToDictionary(fa => fa.First as IVariable, fa => fa.Second); - var subst = new Substituter(receiverReplacement, receiverSubstMap, e.GetTypeArgumentSubstitutions()); - requiredFrames = readsReceiver.Reads.Expressions.ConvertAll(subst.SubstFrameExpr); - break; + } 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)); + } + } + Contract.Assert(e.Function.Ins.Count == e.Args.Count); + for (int i = 0; i < e.Args.Count; i++) { + var formal = e.Function.Ins[i]; + if (formal.IsOld) { + Expression ee = e.Args[i]; + 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( + $"argument{pIdx} for parameter '{formal.Name}'", + "in the two-state function's previous state" + PODesc.IsAllocated.HelperFormal(formal), + ee, + e.AtLabel + ); + builder.Add(Assert(GetToken(ee), wh, desc)); + } } - default: - var readsCall = new ApplyExpr( - Token.NoToken, - new ExprDotName(Token.NoToken, e.Receiver.Resolved, "reads", null), - e.Args, - Token.NoToken - ); - readsCall.Type = objset; - requiredFrames = new() { new FrameExpression(Token.NoToken, readsCall, null) }; - break; } - var desc = new PODesc.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); } + // check that the preconditions for the call hold + // the check for .reads function must be translated explicitly: their declaration lacks + // an explicit precondition, which is added as an axiom in Translator.cs + 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))); + + if (wfOptions.DoReadsChecks) { + // check that the callee reads only what the caller is already allowed to read + Type objset = program.SystemModuleManager.ObjectSetType(); + Expression wrap = new BoogieWrapper( + FunctionCall(expr.tok, Reads(e.Args.Count()), TrType(objset), arguments), + objset); + var reads = new FrameExpression(expr.tok, wrap, null); + List requiredFrames; + switch (e.Receiver.Resolved) { + case MemberSelectExpr { Member: MethodOrFunction readsReceiver }: { + var receiverReplacement = readsReceiver.IsStatic + ? null + : new ThisExpr(readsReceiver); + var receiverSubstMap = readsReceiver.Ins.Zip(e.Args) + .ToDictionary(fa => fa.First as IVariable, fa => fa.Second); + var subst = new Substituter(receiverReplacement, receiverSubstMap, e.GetTypeArgumentSubstitutions()); + requiredFrames = readsReceiver.Reads.Expressions.ConvertAll(subst.SubstFrameExpr); + break; + } + default: + var readsCall = new ApplyExpr( + Token.NoToken, + new ExprDotName(Token.NoToken, e.Receiver.Resolved, "reads", null), + e.Args, + Token.NoToken + ); + readsCall.Type = objset; + requiredFrames = new() { new FrameExpression(Token.NoToken, readsCall, null) }; + break; + } + var desc = new PODesc.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); + } - } else { - // Directly substitutes arguments for formals, to be displayed to the user - var argSubstMap = e.Function.Ins.Zip(e.Args).ToDictionary(fa => fa.First as IVariable, fa => fa.Second); - var directSub = new Substituter(e.Receiver, argSubstMap, e.GetTypeArgumentSubstitutions()); - - foreach (AttributedExpression p in e.Function.Req) { - var directPrecond = directSub.Substitute(p.E); - - Expression precond = Substitute(p.E, e.Receiver, substMap, e.GetTypeArgumentSubstitutions()); - var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); - 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); - if (wfOptions.AssertKv != null) { - // use the given assert attribute only - builder.Add(Assert(tok, ss.E, desc, wfOptions.AssertKv)); - } else { - builder.Add(AssertNS(tok, ss.E, desc)); + } else { + // Directly substitutes arguments for formals, to be displayed to the user + var argSubstMap = e.Function.Ins.Zip(e.Args).ToDictionary(fa => fa.First as IVariable, fa => fa.Second); + var directSub = new Substituter(e.Receiver, argSubstMap, e.GetTypeArgumentSubstitutions()); + + foreach (AttributedExpression p in e.Function.Req) { + var directPrecond = directSub.Substitute(p.E); + + Expression precond = Substitute(p.E, e.Receiver, substMap, e.GetTypeArgumentSubstitutions()); + var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); + 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); + if (wfOptions.AssertKv != null) { + // use the given assert attribute only + builder.Add(Assert(tok, ss.E, desc, wfOptions.AssertKv)); + } else { + builder.Add(AssertNS(tok, ss.E, desc)); + } } } + if (wfOptions.AssertKv == null) { + // assume only if no given assert attribute is given + builder.Add(TrAssumeCmd(callExpr.tok, etran.TrExpr(precond))); + } } - if (wfOptions.AssertKv == null) { - // assume only if no given assert attribute is given - builder.Add(TrAssumeCmd(callExpr.tok, etran.TrExpr(precond))); + if (wfOptions.DoReadsChecks) { + // check that the callee reads only what the caller is already allowed to read + + // 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); + + // ... but that substitution isn't needed for frames passed to CheckFrameSubset + var readsSubst = new Substituter(null, new Dictionary(), e.GetTypeArgumentSubstitutions()); + CheckFrameSubset(callExpr.tok, + e.Function.Reads.Expressions.ConvertAll(readsSubst.SubstFrameExpr), + e.Receiver, substMap, etran, etran.ReadsFrame(callExpr.tok), wfOptions.AssertSink(this, builder), desc, wfOptions.AssertKv); } } - if (wfOptions.DoReadsChecks) { - // check that the callee reads only what the caller is already allowed to read - - // 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); - - // ... but that substitution isn't needed for frames passed to CheckFrameSubset - var readsSubst = new Substituter(null, new Dictionary(), e.GetTypeArgumentSubstitutions()); - CheckFrameSubset(callExpr.tok, - e.Function.Reads.Expressions.ConvertAll(readsSubst.SubstFrameExpr), - e.Receiver, substMap, etran, etran.ReadsFrame(callExpr.tok), wfOptions.AssertSink(this, builder), desc, wfOptions.AssertKv); - } - } - Expression allowance = null; - if (codeContext != null && e.CoCall != FunctionCallExpr.CoCallResolution.Yes && !(e.Function is ExtremePredicate)) { - // check that the decreases measure goes down - var calleeSCCLookup = e.IsByMethodCall ? (ICallable)e.Function.ByMethodDecl : e.Function; - Contract.Assert(calleeSCCLookup != null); - if (ModuleDefinition.InSameSCC(calleeSCCLookup, codeContext)) { - if (wfOptions.DoOnlyCoarseGrainedTerminationChecks) { - builder.Add(Assert(GetToken(expr), Bpl.Expr.False, new PODesc.IsNonRecursive())); - } else { - List contextDecreases = codeContext.Decreases.Expressions; - List calleeDecreases = e.Function.Decreases.Expressions; - if (e.Function == wfOptions.SelfCallsAllowance) { - allowance = Expression.CreateBoolLiteral(e.tok, true); - if (!e.Function.IsStatic) { - allowance = Expression.CreateAnd(allowance, Expression.CreateEq(e.Receiver, new ThisExpr(e.Function), e.Receiver.Type)); + Expression allowance = null; + if (codeContext != null && e.CoCall != FunctionCallExpr.CoCallResolution.Yes && !(e.Function is ExtremePredicate)) { + // check that the decreases measure goes down + var calleeSCCLookup = e.IsByMethodCall ? (ICallable)e.Function.ByMethodDecl : e.Function; + Contract.Assert(calleeSCCLookup != null); + if (ModuleDefinition.InSameSCC(calleeSCCLookup, codeContext)) { + if (wfOptions.DoOnlyCoarseGrainedTerminationChecks) { + builder.Add(Assert(GetToken(expr), Bpl.Expr.False, new PODesc.IsNonRecursive())); + } else { + List contextDecreases = codeContext.Decreases.Expressions; + List calleeDecreases = e.Function.Decreases.Expressions; + if (e.Function == wfOptions.SelfCallsAllowance) { + allowance = Expression.CreateBoolLiteral(e.tok, true); + if (!e.Function.IsStatic) { + allowance = Expression.CreateAnd(allowance, Expression.CreateEq(e.Receiver, new ThisExpr(e.Function), e.Receiver.Type)); + } + for (int i = 0; i < e.Args.Count; i++) { + Expression ee = e.Args[i]; + Formal ff = e.Function.Ins[i]; + allowance = Expression.CreateAnd(allowance, Expression.CreateEq(ee, Expression.CreateIdentExpr(ff), ff.Type)); + } } - for (int i = 0; i < e.Args.Count; i++) { - Expression ee = e.Args[i]; - Formal ff = e.Function.Ins[i]; - allowance = Expression.CreateAnd(allowance, Expression.CreateEq(ee, Expression.CreateIdentExpr(ff), ff.Type)); + string hint; + switch (e.CoCall) { + case FunctionCallExpr.CoCallResolution.NoBecauseFunctionHasSideEffects: + hint = "note that only functions without side effects can be called co-recursively"; + break; + case FunctionCallExpr.CoCallResolution.NoBecauseFunctionHasPostcondition: + hint = "note that only functions without any ensures clause can be called co-recursively"; + break; + case FunctionCallExpr.CoCallResolution.NoBecauseIsNotGuarded: + hint = "note that the call is not sufficiently guarded to be used co-recursively"; + break; + case FunctionCallExpr.CoCallResolution.NoBecauseRecursiveCallsAreNotAllowedInThisContext: + hint = "note that calls cannot be co-recursive in this context"; + break; + case FunctionCallExpr.CoCallResolution.NoBecauseRecursiveCallsInDestructiveContext: + hint = "note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts"; + break; + case FunctionCallExpr.CoCallResolution.No: + hint = null; + break; + default: + Contract.Assert(false); // unexpected CoCallResolution + goto case FunctionCallExpr.CoCallResolution.No; // please the compiler } + if (e.CoCallHint != null) { + hint = hint == null ? e.CoCallHint : string.Format("{0}; {1}", hint, e.CoCallHint); + } + CheckCallTermination(callExpr.tok, contextDecreases, calleeDecreases, allowance, e.Receiver, substMap, directSubstMap, e.GetTypeArgumentSubstitutions(), + etran, false, builder, codeContext.InferredDecreases, hint); } - string hint; - switch (e.CoCall) { - case FunctionCallExpr.CoCallResolution.NoBecauseFunctionHasSideEffects: - hint = "note that only functions without side effects can be called co-recursively"; - break; - case FunctionCallExpr.CoCallResolution.NoBecauseFunctionHasPostcondition: - hint = "note that only functions without any ensures clause can be called co-recursively"; - break; - case FunctionCallExpr.CoCallResolution.NoBecauseIsNotGuarded: - hint = "note that the call is not sufficiently guarded to be used co-recursively"; - break; - case FunctionCallExpr.CoCallResolution.NoBecauseRecursiveCallsAreNotAllowedInThisContext: - hint = "note that calls cannot be co-recursive in this context"; - break; - case FunctionCallExpr.CoCallResolution.NoBecauseRecursiveCallsInDestructiveContext: - hint = "note that a call can be co-recursive only if all intra-cluster calls are in non-destructive contexts"; - break; - case FunctionCallExpr.CoCallResolution.No: - hint = null; - break; - default: - Contract.Assert(false); // unexpected CoCallResolution - goto case FunctionCallExpr.CoCallResolution.No; // please the compiler - } - if (e.CoCallHint != null) { - hint = hint == null ? e.CoCallHint : string.Format("{0}; {1}", hint, e.CoCallHint); - } - CheckCallTermination(callExpr.tok, contextDecreases, calleeDecreases, allowance, e.Receiver, substMap, directSubstMap, e.GetTypeArgumentSubstitutions(), - etran, false, builder, codeContext.InferredDecreases, hint); } } + // all is okay, so allow this function application access to the function's axiom, except if it was okay because of the self-call allowance. + Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(callExpr.tok, e.Function.FullSanitizedName + "#canCall", Bpl.Type.Bool); + List args = etran.FunctionInvocationArguments(e, null, null); + Bpl.Expr canCallFuncAppl = new Bpl.NAryExpr(GetToken(expr), new Bpl.FunctionCall(canCallFuncID), args); + builder.Add(TrAssumeCmd(callExpr.tok, allowance == null ? canCallFuncAppl : BplOr(etran.TrExpr(allowance), canCallFuncAppl))); + + var returnType = e.Type.AsDatatype; + if (returnType != null && returnType.Ctors.Count == 1) { + var correctConstructor = FunctionCall(e.tok, returnType.Ctors[0].QueryField.FullSanitizedName, Bpl.Type.Bool, etran.TrExpr(e)); + // There is only one constructor, so the value must be been constructed by it; might as well assume that here. + builder.Add(TrAssumeCmd(callExpr.tok, correctConstructor)); + } } - // all is okay, so allow this function application access to the function's axiom, except if it was okay because of the self-call allowance. - Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(callExpr.tok, e.Function.FullSanitizedName + "#canCall", Bpl.Type.Bool); - List args = etran.FunctionInvocationArguments(e, null, null); - Bpl.Expr canCallFuncAppl = new Bpl.NAryExpr(GetToken(expr), new Bpl.FunctionCall(canCallFuncID), args); - builder.Add(TrAssumeCmd(callExpr.tok, allowance == null ? canCallFuncAppl : BplOr(etran.TrExpr(allowance), canCallFuncAppl))); - - var returnType = e.Type.AsDatatype; - if (returnType != null && returnType.Ctors.Count == 1) { - var correctConstructor = FunctionCall(e.tok, returnType.Ctors[0].QueryField.FullSanitizedName, Bpl.Type.Bool, etran.TrExpr(e)); - // There is only one constructor, so the value must be been constructed by it; might as well assume that here. - builder.Add(TrAssumeCmd(callExpr.tok, correctConstructor)); - } - } - break; + break; } case SeqConstructionExpr constructionExpr: { var e = constructionExpr; @@ -1140,133 +1140,133 @@ void CheckOperand(Expression operand) { addResultCommands = null; break; case ComprehensionExpr comprehensionExpr: { - var lam = comprehensionExpr as LambdaExpr; - var mc = comprehensionExpr as MapComprehension; - if (mc is { IsGeneralMapComprehension: false }) { - mc = null; // mc will be non-null when "e" is a general map comprehension - } - - // This is a WF check, so we look at the original quantifier, not the split one. - // This ensures that cases like forall x :: x != null && f(x.a) do not fail to verify. - - builder.Add(new Bpl.CommentCmd("Begin Comprehension WF check")); - BplIfIf(comprehensionExpr.tok, lam != null, null, builder, nextBuilder => { - var comprehensionEtran = etran; - if (lam != null) { - // Havoc heap - locals.Add(BplLocalVar(CurrentIdGenerator.FreshId((etran.UsesOldHeap ? "$Heap_at_" : "") + "$lambdaHeap#"), predef.HeapType, out var lambdaHeap)); - comprehensionEtran = new ExpressionTranslator(comprehensionEtran, lambdaHeap); - nextBuilder.Add(new HavocCmd(comprehensionExpr.tok, Singleton((Bpl.IdentifierExpr)comprehensionEtran.HeapExpr))); - nextBuilder.Add(new AssumeCmd(comprehensionExpr.tok, FunctionCall(comprehensionExpr.tok, BuiltinFunction.IsGoodHeap, null, comprehensionEtran.HeapExpr))); - nextBuilder.Add(new AssumeCmd(comprehensionExpr.tok, HeapSameOrSucc(etran.HeapExpr, comprehensionEtran.HeapExpr))); + var lam = comprehensionExpr as LambdaExpr; + var mc = comprehensionExpr as MapComprehension; + if (mc is { IsGeneralMapComprehension: false }) { + mc = null; // mc will be non-null when "e" is a general map comprehension } - var substMap = SetupBoundVarsAsLocals(comprehensionExpr.BoundVars, out var typeAntecedents, nextBuilder, locals, comprehensionEtran); - BplIfIf(comprehensionExpr.tok, true, typeAntecedents, nextBuilder, newBuilder => { - var s = new Substituter(null, substMap, new Dictionary()); - var body = Substitute(comprehensionExpr.Term, null, substMap); - var bodyLeft = mc != null ? Substitute(mc.TermLeft, null, substMap) : null; - var substMapPrime = mc != null ? SetupBoundVarsAsLocals(comprehensionExpr.BoundVars, newBuilder, locals, comprehensionEtran, "#prime") : null; - var bodyLeftPrime = mc != null ? Substitute(mc.TermLeft, null, substMapPrime) : null; - var bodyPrime = mc != null ? Substitute(comprehensionExpr.Term, null, substMapPrime) : null; - List reads = null; - - var newOptions = wfOptions; - if (lam != null) { - // Set up a new frame - var frameName = CurrentIdGenerator.FreshId("$_Frame#l"); - reads = lam.Reads.Expressions.ConvertAll(s.SubstFrameExpr); - comprehensionEtran = comprehensionEtran.WithReadsFrame(frameName, lam); - DefineFrame(comprehensionExpr.tok, comprehensionEtran.ReadsFrame(comprehensionExpr.tok), reads, newBuilder, locals, frameName, comprehensionEtran); - - // Check frame WF and that it read covers itself - var delayer = new ReadsCheckDelayer(comprehensionEtran, wfOptions.SelfCallsAllowance, locals, builder, newBuilder); - delayer.DoWithDelayedReadsChecks(false, wfo => { - CheckFrameWellFormed(wfo, reads, locals, newBuilder, comprehensionEtran); - }); - - // continue doing reads checks, but don't delay them - newOptions = new WFOptions(wfOptions.SelfCallsAllowance, true, false); - } + // This is a WF check, so we look at the original quantifier, not the split one. + // This ensures that cases like forall x :: x != null && f(x.a) do not fail to verify. - // check requires/range - Bpl.Expr guard = null; - if (comprehensionExpr.Range != null) { - var range = Substitute(comprehensionExpr.Range, null, substMap); - CheckWellformed(range, newOptions, locals, newBuilder, comprehensionEtran); - guard = comprehensionEtran.TrExpr(range); + builder.Add(new Bpl.CommentCmd("Begin Comprehension WF check")); + BplIfIf(comprehensionExpr.tok, lam != null, null, builder, nextBuilder => { + var comprehensionEtran = etran; + if (lam != null) { + // Havoc heap + locals.Add(BplLocalVar(CurrentIdGenerator.FreshId((etran.UsesOldHeap ? "$Heap_at_" : "") + "$lambdaHeap#"), predef.HeapType, out var lambdaHeap)); + comprehensionEtran = new ExpressionTranslator(comprehensionEtran, lambdaHeap); + nextBuilder.Add(new HavocCmd(comprehensionExpr.tok, Singleton((Bpl.IdentifierExpr)comprehensionEtran.HeapExpr))); + nextBuilder.Add(new AssumeCmd(comprehensionExpr.tok, FunctionCall(comprehensionExpr.tok, BuiltinFunction.IsGoodHeap, null, comprehensionEtran.HeapExpr))); + nextBuilder.Add(new AssumeCmd(comprehensionExpr.tok, HeapSameOrSucc(etran.HeapExpr, comprehensionEtran.HeapExpr))); } - if (mc != null) { - Contract.Assert(bodyLeft != null); - BplIfIf(comprehensionExpr.tok, guard != null, guard, newBuilder, b => { CheckWellformed(bodyLeft, newOptions, locals, b, comprehensionEtran); }); - } - BplIfIf(comprehensionExpr.tok, guard != null, guard, newBuilder, b => { - Bpl.Expr resultIe = null; - Type rangeType = null; + var substMap = SetupBoundVarsAsLocals(comprehensionExpr.BoundVars, out var typeAntecedents, nextBuilder, locals, comprehensionEtran); + BplIfIf(comprehensionExpr.tok, true, typeAntecedents, nextBuilder, newBuilder => { + var s = new Substituter(null, substMap, new Dictionary()); + var body = Substitute(comprehensionExpr.Term, null, substMap); + var bodyLeft = mc != null ? Substitute(mc.TermLeft, null, substMap) : null; + var substMapPrime = mc != null ? SetupBoundVarsAsLocals(comprehensionExpr.BoundVars, newBuilder, locals, comprehensionEtran, "#prime") : null; + var bodyLeftPrime = mc != null ? Substitute(mc.TermLeft, null, substMapPrime) : null; + var bodyPrime = mc != null ? Substitute(comprehensionExpr.Term, null, substMapPrime) : null; + List reads = null; + + var newOptions = wfOptions; 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); - resultIe = new Bpl.IdentifierExpr(body.tok, resultVar); - rangeType = lam.Type.AsArrowType.Result; + // Set up a new frame + var frameName = CurrentIdGenerator.FreshId("$_Frame#l"); + reads = lam.Reads.Expressions.ConvertAll(s.SubstFrameExpr); + comprehensionEtran = comprehensionEtran.WithReadsFrame(frameName, lam); + DefineFrame(comprehensionExpr.tok, comprehensionEtran.ReadsFrame(comprehensionExpr.tok), reads, newBuilder, locals, frameName, comprehensionEtran); + + // Check frame WF and that it read covers itself + var delayer = new ReadsCheckDelayer(comprehensionEtran, wfOptions.SelfCallsAllowance, locals, builder, newBuilder); + delayer.DoWithDelayedReadsChecks(false, wfo => { + CheckFrameWellFormed(wfo, reads, locals, newBuilder, comprehensionEtran); + }); + + // continue doing reads checks, but don't delay them + newOptions = new WFOptions(wfOptions.SelfCallsAllowance, true, false); } - void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { - if (rangeType != null) { - CheckSubsetType(etran, result, resultIe, rangeType, returnBuilder); - } + // check requires/range + Bpl.Expr guard = null; + if (comprehensionExpr.Range != null) { + var range = Substitute(comprehensionExpr.Range, null, substMap); + CheckWellformed(range, newOptions, locals, newBuilder, comprehensionEtran); + guard = comprehensionEtran.TrExpr(range); } - CheckWellformedWithResult(body, newOptions, locals, b, comprehensionEtran, AddResultCommands); - }); - - if (mc != null) { - Contract.Assert(substMapPrime != null); - Contract.Assert(bodyLeftPrime != null); - Contract.Assert(bodyPrime != null); - Bpl.Expr guardPrime = null; - if (guard != null) { - Contract.Assert(comprehensionExpr.Range != null); - var rangePrime = Substitute(comprehensionExpr.Range, null, substMapPrime); - guardPrime = comprehensionEtran.TrExpr(rangePrime); + if (mc != null) { + Contract.Assert(bodyLeft != null); + BplIfIf(comprehensionExpr.tok, guard != null, guard, newBuilder, b => { CheckWellformed(bodyLeft, newOptions, locals, b, comprehensionEtran); }); } - BplIfIf(comprehensionExpr.tok, guard != null, BplAnd(guard, guardPrime), newBuilder, b => { - 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))); + BplIfIf(comprehensionExpr.tok, guard != null, guard, newBuilder, b => { + Bpl.Expr resultIe = null; + Type rangeType = null; + 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); + resultIe = new Bpl.IdentifierExpr(body.tok, resultVar); + rangeType = lam.Type.AsArrowType.Result; + } + + void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { + if (rangeType != null) { + CheckSubsetType(etran, result, resultIe, rangeType, returnBuilder); + } + } + + CheckWellformedWithResult(body, newOptions, locals, b, comprehensionEtran, AddResultCommands); }); + + if (mc != null) { + Contract.Assert(substMapPrime != null); + Contract.Assert(bodyLeftPrime != null); + Contract.Assert(bodyPrime != null); + Bpl.Expr guardPrime = null; + if (guard != null) { + Contract.Assert(comprehensionExpr.Range != null); + var rangePrime = Substitute(comprehensionExpr.Range, null, substMapPrime); + guardPrime = comprehensionEtran.TrExpr(rangePrime); + } + BplIfIf(comprehensionExpr.tok, guard != null, BplAnd(guard, guardPrime), newBuilder, b => { + 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))); + }); + } + }); + + if (lam != null) { + // assume false (heap was havoced inside an if) + Contract.Assert(nextBuilder != builder); + nextBuilder.Add(new AssumeCmd(comprehensionExpr.tok, Bpl.Expr.False)); } }); - if (lam != null) { - // assume false (heap was havoced inside an if) - Contract.Assert(nextBuilder != builder); - nextBuilder.Add(new AssumeCmd(comprehensionExpr.tok, Bpl.Expr.False)); - } - }); - - bool needTypeConstraintCheck; - if (lam == null) { - needTypeConstraintCheck = true; - } else { - // omit constraint check if the type is according to the syntax of the expression - var arrowType = (UserDefinedType)comprehensionExpr.Type.NormalizeExpandKeepConstraints(); - if (ArrowType.IsPartialArrowTypeName(arrowType.Name)) { - needTypeConstraintCheck = lam.Reads.Expressions.Count != 0; - } else if (ArrowType.IsTotalArrowTypeName(arrowType.Name)) { - needTypeConstraintCheck = lam.Reads.Expressions.Count != 0 || lam.Range != null; - } else { + bool needTypeConstraintCheck; + if (lam == null) { needTypeConstraintCheck = true; + } else { + // omit constraint check if the type is according to the syntax of the expression + var arrowType = (UserDefinedType)comprehensionExpr.Type.NormalizeExpandKeepConstraints(); + if (ArrowType.IsPartialArrowTypeName(arrowType.Name)) { + needTypeConstraintCheck = lam.Reads.Expressions.Count != 0; + } else if (ArrowType.IsTotalArrowTypeName(arrowType.Name)) { + needTypeConstraintCheck = lam.Reads.Expressions.Count != 0 || lam.Range != null; + } else { + needTypeConstraintCheck = true; + } + } + if (needTypeConstraintCheck) { + CheckResultToBeInType(comprehensionExpr.tok, comprehensionExpr, comprehensionExpr.Type, locals, builder, etran); } - } - if (needTypeConstraintCheck) { - CheckResultToBeInType(comprehensionExpr.tok, comprehensionExpr, comprehensionExpr.Type, locals, builder, etran); - } - builder.Add(new Bpl.CommentCmd("End Comprehension WF check")); - break; + builder.Add(new Bpl.CommentCmd("End Comprehension WF check")); + break; } case StmtExpr stmtExpr: CheckWellformedStmtExpr(stmtExpr, wfOptions, addResultCommands, locals, builder, etran); From c11f66721cd60e13887ce115bfa12ef71e0f14ff Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 12 Aug 2024 14:58:42 +0200 Subject: [PATCH 39/63] Cleanup and improve error reporting for witness --- .../BoogieGenerator.ExpressionWellformed.cs | 6 +- .../Verifier/BoogieGenerator.TrStatement.cs | 83 +++++++++---------- .../Verifier/BoogieGenerator.Types.cs | 7 +- .../ast/subsetTypes/errorReporting.dfy | 6 ++ .../ast/subsetTypes/errorReporting.dfy.expect | 3 + 5 files changed, 54 insertions(+), 51 deletions(-) create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/subsetTypes/errorReporting.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/subsetTypes/errorReporting.dfy.expect diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 81387cfa8cf..94d6ac12c39 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -1344,9 +1344,7 @@ void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { } - if (addResultCommands != null) { - addResultCommands(builder, expr); - } + addResultCommands?.Invoke(builder, expr); } public void CheckSubsetType(ExpressionTranslator etran, Expression expr, Bpl.Expr selfCall, Type resultType, @@ -1402,8 +1400,6 @@ private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, AddResultCommands ad Bpl.Expr ct = CtorInvocation(mc, me.Source.Type, etran, locals, b, NOALLOC, false); // generate: if (src == ctor(args)) { assume args-is-well-typed; mc.Body is well-formed; assume Result == TrExpr(case); } else ... - // TODO: - // resultDescription = "match expression"; CheckWellformedWithResult(mc.Body, wfOptions, locals, b, etran, addResultCommands); ifCmd = new Bpl.IfCmd(mc.tok, Bpl.Expr.Eq(src, ct), b.Collect(mc.tok), ifCmd, els); els = null; diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs index 7b9c017b928..4c14c5db23f 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs @@ -371,7 +371,46 @@ private void TrStmt(Statement stmt, BoogieStmtListBuilder builder, var s = (VarDeclStmt)stmt; TrVarDeclStmt(s, builder, locals, etran); } else if (stmt is VarDeclPattern varDeclPattern) { - TranslateVariableDeclaration(builder, locals, etran, varDeclPattern); + foreach (var dafnyLocal in varDeclPattern.LocalVars) { + var boogieLocal = new Bpl.LocalVariable(dafnyLocal.Tok, + new Bpl.TypedIdent(dafnyLocal.Tok, dafnyLocal.AssignUniqueName(currentDeclaration.IdGenerator), + TrType(dafnyLocal.Type))); + locals.Add(boogieLocal); + var variableReference = new Bpl.IdentifierExpr(boogieLocal.tok, boogieLocal); + builder.Add(new Bpl.HavocCmd(dafnyLocal.Tok, new List + { + variableReference + })); + var wh = GetWhereClause(dafnyLocal.Tok, variableReference, dafnyLocal.Type, etran, + isAllocContext.Var(varDeclPattern.IsGhost, dafnyLocal)); + if (wh != null) { + builder.Add(TrAssumeCmd(dafnyLocal.Tok, wh)); + } + } + + var varNameGen = CurrentIdGenerator.NestedFreshIdGenerator("let#"); + 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 boogieTupleReference = new Bpl.IdentifierExpr(rhs.tok, boogieTupleLocal); + + AddResultCommands addResultCommands = (returnBuilder, result) => { + Contract.Assert(pat.Expr.Type != null); + var bResult = etran.TrExpr(result); + CheckSubrange(rhs.tok, bResult, rhs.Type, pat.Expr.Type, rhs, returnBuilder); + returnBuilder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, rhs.tok, rhs, + e => Bpl.Expr.Eq(boogieTupleReference, AdaptBoxing(rhs.tok, e, rhs.Type, pat.Expr.Type)))); + }; + CheckWellformedWithResult(rhs, new WFOptions(null, false, false), locals, builder, etran, addResultCommands); + builder.Add(TrAssumeCmd(rhs.tok, etran.CanCallAssumption(rhs))); + builder.Add(new CommentCmd("CheckWellformedWithResult: any expression")); + builder.Add(TrAssumeCmd(rhs.tok, MkIs(boogieTupleReference, pat.Expr.Type))); + + CheckCasePatternShape(pat, rhs, boogieTupleReference, rhs.tok, pat.Expr.Type, builder); + builder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, varDeclPattern.tok, pat.Expr, + e => Expr.Eq(e, boogieTupleReference), "variable declaration")); } else if (stmt is TryRecoverStatement haltRecoveryStatement) { // try/recover statements are currently internal-only AST nodes that cannot be // directly used in user Dafny code. They are only generated by rewriters, and verifying @@ -383,48 +422,6 @@ private void TrStmt(Statement stmt, BoogieStmtListBuilder builder, } - private void TranslateVariableDeclaration(BoogieStmtListBuilder builder, List locals, - ExpressionTranslator etran, - VarDeclPattern varDeclPattern) { - foreach (var dafnyLocal in varDeclPattern.LocalVars) { - var boogieLocal = new Bpl.LocalVariable(dafnyLocal.Tok, - new Bpl.TypedIdent(dafnyLocal.Tok, dafnyLocal.AssignUniqueName(currentDeclaration.IdGenerator), - TrType(dafnyLocal.Type))); - locals.Add(boogieLocal); - var variableReference = new Bpl.IdentifierExpr(boogieLocal.tok, boogieLocal); - builder.Add(new Bpl.HavocCmd(dafnyLocal.Tok, new List { variableReference })); - var wh = GetWhereClause(dafnyLocal.Tok, variableReference, dafnyLocal.Type, etran, - isAllocContext.Var(varDeclPattern.IsGhost, dafnyLocal)); - if (wh != null) { - builder.Add(TrAssumeCmd(dafnyLocal.Tok, wh)); - } - } - - var varNameGen = CurrentIdGenerator.NestedFreshIdGenerator("let#"); - 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 boogieTupleReference = new Bpl.IdentifierExpr(rhs.tok, boogieTupleLocal); - - AddResultCommands addResultCommands = (returnBuilder, result) => { - Contract.Assert(pat.Expr.Type != null); - var bResult = etran.TrExpr(result); - CheckSubrange(rhs.tok, bResult, rhs.Type, pat.Expr.Type, rhs, returnBuilder); - returnBuilder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, rhs.tok, rhs, - e => Bpl.Expr.Eq(boogieTupleReference, AdaptBoxing(rhs.tok, e, rhs.Type, pat.Expr.Type)))); - }; - CheckWellformedWithResult(rhs, new WFOptions(null, false, false), locals, builder, etran, addResultCommands); - builder.Add(TrAssumeCmd(rhs.tok, etran.CanCallAssumption(rhs))); - builder.Add(new CommentCmd("CheckWellformedWithResult: any expression")); - builder.Add(TrAssumeCmd(rhs.tok, MkIs(boogieTupleReference, pat.Expr.Type))); - - CheckCasePatternShape(pat, rhs, boogieTupleReference, rhs.tok, pat.Expr.Type, builder); - builder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, varDeclPattern.tok, pat.Expr, - e => Expr.Eq(e, boogieTupleReference), "variable declaration")); - } - private void TrUpdateStmt(BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, UpdateStmt 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. diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs index 7545f602c8d..3621a96b2f8 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs @@ -1497,16 +1497,17 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { // check well-formedness of the witness expression (including termination, and reads checks) var ghostCodeContext = codeContext; codeContext = decl.WitnessKind == SubsetTypeDecl.WKind.Compiled ? new CallableWrapper(decl, false) : ghostCodeContext; - CheckWellformedWithResult(decl.Witness, new WFOptions(null, true), locals, witnessCheckBuilder, etran, (returnBuilder, result) => { + CheckWellformedWithResult(decl.Witness, new WFOptions(null, true), locals, witnessCheckBuilder, etran, + (returnBuilder, result) => { // check that the witness is assignable to the type of the given bound variable if (decl is SubsetTypeDecl) { // Note, for new-types, this has already been checked by CheckWellformed. - CheckResultToBeInType(decl.Witness.tok, result, decl.Var.Type, locals, returnBuilder, etran); + CheckResultToBeInType(result.Tok, result, decl.Var.Type, locals, returnBuilder, etran); } // check that the witness expression checks out witnessExpr = Substitute(decl.Constraint, decl.Var, result); - witnessExpr.tok = decl.Witness.Tok; + witnessExpr.tok = result.Tok; var desc = new PODesc.WitnessCheck(witnessString, witnessExpr); SplitAndAssertExpression(returnBuilder, witnessExpr, etran, context, desc); diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/subsetTypes/errorReporting.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/subsetTypes/errorReporting.dfy new file mode 100644 index 00000000000..c78b69a9aff --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/subsetTypes/errorReporting.dfy @@ -0,0 +1,6 @@ +// RUN: ! %verify %s &> "%t" +// RUN: %diff "%s.expect" "%t" + +type ten = x: int | x == 10 witness if true then + 11 else + 10 \ No newline at end of file diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/subsetTypes/errorReporting.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/subsetTypes/errorReporting.dfy.expect new file mode 100644 index 00000000000..1d79115ac6a --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/subsetTypes/errorReporting.dfy.expect @@ -0,0 +1,3 @@ +ensuresReporting.dfy(5,2): Error: the given witness expression might not satisfy constraint + +Dafny program verifier finished with 0 verified, 1 errors From 9887a5b4ebbdffd3af640416621d2f0131b96127 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 12 Aug 2024 15:01:45 +0200 Subject: [PATCH 40/63] Add test case for reporting off function call expr --- .../LitTests/LitTest/ast/expression/functionCall.dfy | 12 ++++++++++++ .../LitTest/ast/expression/functionCall.dfy.expect | 3 +++ 2 files changed, 15 insertions(+) create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expression/functionCall.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expression/functionCall.dfy.expect diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expression/functionCall.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expression/functionCall.dfy new file mode 100644 index 00000000000..9a8fd2fdd15 --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expression/functionCall.dfy @@ -0,0 +1,12 @@ +// RUN: ! %verify %s &> "%t" +// RUN: %diff "%s.expect" "%t" + +function Callee(x: nat): nat { + x +} + +method Caller(b: bool) { + var r := Callee(if b then + 10 else + -10); +} \ No newline at end of file diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expression/functionCall.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expression/functionCall.dfy.expect new file mode 100644 index 00000000000..9acd41004d5 --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/expression/functionCall.dfy.expect @@ -0,0 +1,3 @@ +functionCall.dfy(11,4): Error: value does not satisfy the subset constraints of 'nat' + +Dafny program verifier finished with 0 verified, 1 error From e7e7917b6396394b857dd20e3d184af4ded4c916 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 12 Aug 2024 15:17:50 +0200 Subject: [PATCH 41/63] Reduce changes --- .../BoogieGenerator.ExpressionWellformed.cs | 109 +++++++++--------- ...oogieGenerator.Functions.Wellformedness.cs | 2 +- 2 files changed, 53 insertions(+), 58 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 94d6ac12c39..019b9c05118 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -39,7 +39,7 @@ private class WFOptions { 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 List> Asserts; + public readonly List> CreateAsserts; public readonly bool LValueContext; public readonly Bpl.QKeyValue AssertKv; @@ -54,17 +54,17 @@ public WFOptions(Function selfCallsAllowance, bool doReadsChecks, DoOnlyCoarseGrainedTerminationChecks = doOnlyCoarseGrainedTerminationChecks; if (saveReadsChecks) { Locals = new List(); - Asserts = new(); + CreateAsserts = new(); } } private WFOptions(Function selfCallsAllowance, bool doReadsChecks, bool doOnlyCoarseGrainedTerminationChecks, - List locals, List> asserts, bool lValueContext, Bpl.QKeyValue assertKv) { + List locals, List> createAsserts, bool lValueContext, Bpl.QKeyValue assertKv) { SelfCallsAllowance = selfCallsAllowance; DoReadsChecks = doReadsChecks; DoOnlyCoarseGrainedTerminationChecks = doOnlyCoarseGrainedTerminationChecks; Locals = locals; - Asserts = asserts; + CreateAsserts = createAsserts; LValueContext = lValueContext; AssertKv = assertKv; } @@ -78,7 +78,7 @@ public WFOptions(Bpl.QKeyValue kv) { /// public WFOptions WithReadsChecks(bool doReadsChecks) { return new WFOptions(SelfCallsAllowance, doReadsChecks, DoOnlyCoarseGrainedTerminationChecks, - Locals, Asserts, LValueContext, AssertKv); + Locals, CreateAsserts, LValueContext, AssertKv); } /// @@ -86,14 +86,14 @@ public WFOptions WithReadsChecks(bool doReadsChecks) { /// public WFOptions WithLValueContext(bool lValueContext) { return new WFOptions(SelfCallsAllowance, DoReadsChecks, DoOnlyCoarseGrainedTerminationChecks, - Locals, Asserts, lValueContext, AssertKv); + Locals, CreateAsserts, lValueContext, AssertKv); } public Action AssertSink(BoogieGenerator tran, BoogieStmtListBuilder builder) { return (t, e, d, qk) => { if (Locals != null) { var b = BplLocalVar(tran.CurrentIdGenerator.FreshId("b$reqreads#"), Bpl.Type.Bool, Locals); - Asserts.Add(() => tran.Assert(t, b, d, qk)); + CreateAsserts.Add(() => tran.Assert(t, b, d, qk)); builder.Add(Bpl.Cmd.SimpleAssign(e.tok, (Bpl.IdentifierExpr)b, e)); } else { builder.Add(tran.Assert(t, e, d, qk)); @@ -115,7 +115,7 @@ public void ProcessSavedReadsChecks(List locals, BoogieStmtListBuilder Contract.Requires(locals != null); Contract.Requires(builderInitializationArea != null); Contract.Requires(builder != null); - Contract.Requires(Locals != null && Asserts != null); // ProcessSavedReadsChecks should be called only if the constructor was called with saveReadsChecks + 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); @@ -124,7 +124,7 @@ public void ProcessSavedReadsChecks(List locals, BoogieStmtListBuilder builderInitializationArea.Add(cmd); } // assert b$reads_guards#0; ... - foreach (var a in Asserts) { + foreach (var a in CreateAsserts) { builder.Add(a()); } } @@ -266,12 +266,6 @@ void CheckWellformed(Expression expr, WFOptions wfOptions, List locals void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, AddResultCommands addResultCommands) { - Contract.Requires(expr != null); - Contract.Requires(wfOptions != null); - Contract.Requires(locals != null); - Contract.Requires(builder != null); - Contract.Requires(etran != null); - Contract.Requires(predef != null); var origOptions = wfOptions; if (wfOptions.LValueContext) { // Turn off LValueContext for any recursive call @@ -777,15 +771,15 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, List requiredFrames; switch (e.Receiver.Resolved) { case MemberSelectExpr { Member: MethodOrFunction readsReceiver }: { - var receiverReplacement = readsReceiver.IsStatic - ? null - : new ThisExpr(readsReceiver); - var receiverSubstMap = readsReceiver.Ins.Zip(e.Args) - .ToDictionary(fa => fa.First as IVariable, fa => fa.Second); - var subst = new Substituter(receiverReplacement, receiverSubstMap, e.GetTypeArgumentSubstitutions()); - requiredFrames = readsReceiver.Reads.Expressions.ConvertAll(subst.SubstFrameExpr); - break; - } + var receiverReplacement = readsReceiver.IsStatic + ? null + : new ThisExpr(readsReceiver); + var receiverSubstMap = readsReceiver.Ins.Zip(e.Args) + .ToDictionary(fa => fa.First as IVariable, fa => fa.Second); + var subst = new Substituter(receiverReplacement, receiverSubstMap, e.GetTypeArgumentSubstitutions()); + requiredFrames = readsReceiver.Reads.Expressions.ConvertAll(subst.SubstFrameExpr); + break; + } default: var readsCall = new ApplyExpr( Token.NoToken, @@ -912,7 +906,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, } break; - } + } case SeqConstructionExpr constructionExpr: { var e = constructionExpr; CheckWellformed(e.N, wfOptions, locals, builder, etran); @@ -1136,12 +1130,13 @@ void CheckOperand(Expression operand) { break; } case LetExpr letExpr: - CheckWellformedLetExprWithResult(letExpr, wfOptions, addResultCommands, locals, builder, etran, true); + CheckWellformedLetExprWithResult(letExpr, wfOptions, locals, builder, etran, true, addResultCommands); addResultCommands = null; break; case ComprehensionExpr comprehensionExpr: { - var lam = comprehensionExpr as LambdaExpr; - var mc = comprehensionExpr as MapComprehension; + var e = comprehensionExpr; + var lam = e as LambdaExpr; + var mc = e as MapComprehension; if (mc is { IsGeneralMapComprehension: false }) { mc = null; // mc will be non-null when "e" is a general map comprehension } @@ -1150,25 +1145,25 @@ void CheckOperand(Expression operand) { // This ensures that cases like forall x :: x != null && f(x.a) do not fail to verify. builder.Add(new Bpl.CommentCmd("Begin Comprehension WF check")); - BplIfIf(comprehensionExpr.tok, lam != null, null, builder, nextBuilder => { + BplIfIf(e.tok, lam != null, null, builder, nextBuilder => { var comprehensionEtran = etran; if (lam != null) { // Havoc heap locals.Add(BplLocalVar(CurrentIdGenerator.FreshId((etran.UsesOldHeap ? "$Heap_at_" : "") + "$lambdaHeap#"), predef.HeapType, out var lambdaHeap)); comprehensionEtran = new ExpressionTranslator(comprehensionEtran, lambdaHeap); - nextBuilder.Add(new HavocCmd(comprehensionExpr.tok, Singleton((Bpl.IdentifierExpr)comprehensionEtran.HeapExpr))); - nextBuilder.Add(new AssumeCmd(comprehensionExpr.tok, FunctionCall(comprehensionExpr.tok, BuiltinFunction.IsGoodHeap, null, comprehensionEtran.HeapExpr))); - nextBuilder.Add(new AssumeCmd(comprehensionExpr.tok, HeapSameOrSucc(etran.HeapExpr, comprehensionEtran.HeapExpr))); + 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))); + nextBuilder.Add(new AssumeCmd(e.tok, HeapSameOrSucc(etran.HeapExpr, comprehensionEtran.HeapExpr))); } - var substMap = SetupBoundVarsAsLocals(comprehensionExpr.BoundVars, out var typeAntecedents, nextBuilder, locals, comprehensionEtran); - BplIfIf(comprehensionExpr.tok, true, typeAntecedents, nextBuilder, newBuilder => { + var substMap = SetupBoundVarsAsLocals(e.BoundVars, out var typeAntecedents, nextBuilder, locals, comprehensionEtran); + BplIfIf(e.tok, true, typeAntecedents, nextBuilder, newBuilder => { var s = new Substituter(null, substMap, new Dictionary()); - var body = Substitute(comprehensionExpr.Term, null, substMap); + var body = Substitute(e.Term, null, substMap); var bodyLeft = mc != null ? Substitute(mc.TermLeft, null, substMap) : null; - var substMapPrime = mc != null ? SetupBoundVarsAsLocals(comprehensionExpr.BoundVars, newBuilder, locals, comprehensionEtran, "#prime") : null; + var substMapPrime = mc != null ? SetupBoundVarsAsLocals(e.BoundVars, newBuilder, locals, comprehensionEtran, "#prime") : null; var bodyLeftPrime = mc != null ? Substitute(mc.TermLeft, null, substMapPrime) : null; - var bodyPrime = mc != null ? Substitute(comprehensionExpr.Term, null, substMapPrime) : null; + var bodyPrime = mc != null ? Substitute(e.Term, null, substMapPrime) : null; List reads = null; var newOptions = wfOptions; @@ -1177,7 +1172,7 @@ void CheckOperand(Expression operand) { var frameName = CurrentIdGenerator.FreshId("$_Frame#l"); reads = lam.Reads.Expressions.ConvertAll(s.SubstFrameExpr); comprehensionEtran = comprehensionEtran.WithReadsFrame(frameName, lam); - DefineFrame(comprehensionExpr.tok, comprehensionEtran.ReadsFrame(comprehensionExpr.tok), reads, newBuilder, locals, frameName, comprehensionEtran); + DefineFrame(e.tok, comprehensionEtran.ReadsFrame(e.tok), reads, newBuilder, locals, frameName, comprehensionEtran); // Check frame WF and that it read covers itself var delayer = new ReadsCheckDelayer(comprehensionEtran, wfOptions.SelfCallsAllowance, locals, builder, newBuilder); @@ -1191,17 +1186,17 @@ void CheckOperand(Expression operand) { // check requires/range Bpl.Expr guard = null; - if (comprehensionExpr.Range != null) { - var range = Substitute(comprehensionExpr.Range, null, substMap); + if (e.Range != null) { + var range = Substitute(e.Range, null, substMap); CheckWellformed(range, newOptions, locals, newBuilder, comprehensionEtran); guard = comprehensionEtran.TrExpr(range); } if (mc != null) { Contract.Assert(bodyLeft != null); - BplIfIf(comprehensionExpr.tok, guard != null, guard, newBuilder, b => { CheckWellformed(bodyLeft, newOptions, locals, b, comprehensionEtran); }); + BplIfIf(e.tok, guard != null, guard, newBuilder, b => { CheckWellformed(bodyLeft, newOptions, locals, b, comprehensionEtran); }); } - BplIfIf(comprehensionExpr.tok, guard != null, guard, newBuilder, b => { + BplIfIf(e.tok, guard != null, guard, newBuilder, b => { Bpl.Expr resultIe = null; Type rangeType = null; if (lam != null) { @@ -1227,11 +1222,11 @@ void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { Contract.Assert(bodyPrime != null); Bpl.Expr guardPrime = null; if (guard != null) { - Contract.Assert(comprehensionExpr.Range != null); - var rangePrime = Substitute(comprehensionExpr.Range, null, substMapPrime); + Contract.Assert(e.Range != null); + var rangePrime = Substitute(e.Range, null, substMapPrime); guardPrime = comprehensionEtran.TrExpr(rangePrime); } - BplIfIf(comprehensionExpr.tok, guard != null, BplAnd(guard, guardPrime), newBuilder, b => { + BplIfIf(e.tok, guard != null, BplAnd(guard, guardPrime), newBuilder, b => { var different = BplOr( Bpl.Expr.Neq(comprehensionEtran.TrExpr(bodyLeft), comprehensionEtran.TrExpr(bodyLeftPrime)), Bpl.Expr.Eq(comprehensionEtran.TrExpr(body), comprehensionEtran.TrExpr(bodyPrime))); @@ -1243,7 +1238,7 @@ void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { if (lam != null) { // assume false (heap was havoced inside an if) Contract.Assert(nextBuilder != builder); - nextBuilder.Add(new AssumeCmd(comprehensionExpr.tok, Bpl.Expr.False)); + nextBuilder.Add(new AssumeCmd(e.tok, Bpl.Expr.False)); } }); @@ -1252,7 +1247,7 @@ void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { needTypeConstraintCheck = true; } else { // omit constraint check if the type is according to the syntax of the expression - var arrowType = (UserDefinedType)comprehensionExpr.Type.NormalizeExpandKeepConstraints(); + var arrowType = (UserDefinedType)e.Type.NormalizeExpandKeepConstraints(); if (ArrowType.IsPartialArrowTypeName(arrowType.Name)) { needTypeConstraintCheck = lam.Reads.Expressions.Count != 0; } else if (ArrowType.IsTotalArrowTypeName(arrowType.Name)) { @@ -1262,14 +1257,14 @@ void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { } } if (needTypeConstraintCheck) { - CheckResultToBeInType(comprehensionExpr.tok, comprehensionExpr, comprehensionExpr.Type, locals, builder, etran); + CheckResultToBeInType(e.tok, e, e.Type, locals, builder, etran); } builder.Add(new Bpl.CommentCmd("End Comprehension WF check")); break; } case StmtExpr stmtExpr: - CheckWellformedStmtExpr(stmtExpr, wfOptions, addResultCommands, locals, builder, etran); + CheckWellformedStmtExpr(stmtExpr, wfOptions, locals, builder, etran, addResultCommands); addResultCommands = null; break; case ITEExpr iteExpr: { @@ -1283,7 +1278,7 @@ void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { // has already been checked in e.Test var letExpr = (LetExpr)e.Thn; Contract.Assert(letExpr != null); - CheckWellformedLetExprWithResult(letExpr, wfOptions, addResultCommands, locals, bThen, etran, false); + CheckWellformedLetExprWithResult(letExpr, wfOptions, locals, bThen, etran, false, addResultCommands); } else { CheckWellformedWithResult(e.Thn, wfOptions, locals, bThen, etran, addResultCommands); } @@ -1293,7 +1288,7 @@ void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { break; } case MatchExpr matchExpr: - TrMatchExpr(matchExpr, wfOptions, addResultCommands, locals, builder, etran); + TrMatchExpr(matchExpr, wfOptions, locals, builder, etran, addResultCommands); addResultCommands = null; break; case DatatypeUpdateExpr updateExpr: { @@ -1360,8 +1355,8 @@ public void CheckSubsetType(ExpressionTranslator etran, Expression expr, Bpl.Exp builder.Add(TrAssumeCmd(expr.tok, MkIs(selfCall, resultType))); } - private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, AddResultCommands addResultCommands, List locals, - BoogieStmtListBuilder builder, ExpressionTranslator etran) { + private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, List locals, + BoogieStmtListBuilder builder, ExpressionTranslator etran, AddResultCommands addResultCommands) { FillMissingCases(me); CheckWellformed(me.Source, wfOptions, locals, builder, etran); @@ -1408,8 +1403,8 @@ private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, AddResultCommands ad builder.Add(ifCmd); } - private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions wfOptions, AddResultCommands addResultCommands, List locals, - BoogieStmtListBuilder builder, ExpressionTranslator etran) { + private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions wfOptions, List locals, + BoogieStmtListBuilder builder, ExpressionTranslator etran, AddResultCommands addResultCommands) { var statements = new List() { stmtExpr.S }; Expression expression = stmtExpr.E; @@ -1508,8 +1503,8 @@ void CheckWellformedSpecialFunction(FunctionCallExpr expr, WFOptions options, Bp delegate void AddResultCommands(BoogieStmtListBuilder builder, Expression result); - void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, AddResultCommands addResultCommands, List locals, - BoogieStmtListBuilder builder, ExpressionTranslator etran, bool checkRhs) { + void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, List locals, + BoogieStmtListBuilder builder, ExpressionTranslator etran, bool checkRhs, AddResultCommands addResultCommands) { if (e.Exact) { var substMap = SetupBoundVarsAsLocals(e.BoundVars.ToList(), builder, locals, etran, "#Z"); Contract.Assert(e.LHSs.Count == e.RHSs.Count); // checked by resolution diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs index c234d3f9ae0..a7139e39d20 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs @@ -213,7 +213,7 @@ void CheckPostcondition(BoogieStmtListBuilder innerBuilder, Expression innerBody } if (doReadsChecks) { // assert b$reads_guards#0; ... - foreach (var a in wfo.Asserts) { + foreach (var a in wfo.CreateAsserts) { innerBuilder.Add(a()); } } From e5d4b676d09aaa9364e516074dac65587aa1a5e6 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 12 Aug 2024 15:47:46 +0200 Subject: [PATCH 42/63] Undo a fix --- .../NameResolutionAndTypeInference.cs | 2 +- .../TestFiles/LitTests/LitTest/dafny0/NatTypes.dfy.expect | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/DafnyCore/Resolver/NameResolutionAndTypeInference/NameResolutionAndTypeInference.cs b/Source/DafnyCore/Resolver/NameResolutionAndTypeInference/NameResolutionAndTypeInference.cs index 3b5db2e8d8d..b814ba08984 100644 --- a/Source/DafnyCore/Resolver/NameResolutionAndTypeInference/NameResolutionAndTypeInference.cs +++ b/Source/DafnyCore/Resolver/NameResolutionAndTypeInference/NameResolutionAndTypeInference.cs @@ -5989,7 +5989,7 @@ public MethodCallInformation ResolveApplySuffix(ApplySuffix e, ResolutionContext } if (callee != null) { // produce a FunctionCallExpr instead of an ApplyExpr(MemberSelectExpr) - var rr = new FunctionCallExpr(e.Tok, callee.Name, mse.Obj, e.tok, e.CloseParen, e.Bindings, atLabel) { + var rr = new FunctionCallExpr(e.Lhs.Tok, callee.Name, mse.Obj, e.tok, e.CloseParen, e.Bindings, atLabel) { Function = callee, TypeApplication_AtEnclosingClass = mse.TypeApplication_AtEnclosingClass, TypeApplication_JustFunction = mse.TypeApplication_JustMember diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/NatTypes.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/NatTypes.dfy.expect index dea384654bd..593777cfcbd 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/NatTypes.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/NatTypes.dfy.expect @@ -9,7 +9,7 @@ NatTypes.dfy(74,15): Error: assertion might not hold NatTypes.dfy(91,15): Error: assertion might not hold NatTypes.dfy(105,15): Error: assertion might not hold NatTypes.dfy(141,44): Error: value does not satisfy the subset constraints of 'nat' -NatTypes.dfy(164,34): Error: value does not satisfy the subset constraints of 'nat' +NatTypes.dfy(164,20): Error: value does not satisfy the subset constraints of 'nat' NatTypes.dfy(184,16): Error: value does not satisfy the subset constraints of 'nat' Dafny program verifier finished with 7 verified, 13 errors From f42b9150625cdc78561e635fb1ac851742ead652 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 12 Aug 2024 15:50:32 +0200 Subject: [PATCH 43/63] Ran formatter --- .../Verifier/BoogieGenerator.Types.cs | 24 +++++++++---------- Source/DafnyCore/Verifier/BoogieGenerator.cs | 6 ++--- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs index 3621a96b2f8..6987c2a91ed 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs @@ -1497,21 +1497,21 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { // check well-formedness of the witness expression (including termination, and reads checks) var ghostCodeContext = codeContext; codeContext = decl.WitnessKind == SubsetTypeDecl.WKind.Compiled ? new CallableWrapper(decl, false) : ghostCodeContext; - CheckWellformedWithResult(decl.Witness, new WFOptions(null, true), locals, witnessCheckBuilder, etran, + CheckWellformedWithResult(decl.Witness, new WFOptions(null, true), locals, witnessCheckBuilder, etran, (returnBuilder, result) => { - // check that the witness is assignable to the type of the given bound variable - if (decl is SubsetTypeDecl) { - // Note, for new-types, this has already been checked by CheckWellformed. - CheckResultToBeInType(result.Tok, result, decl.Var.Type, locals, returnBuilder, etran); - } + // check that the witness is assignable to the type of the given bound variable + if (decl is SubsetTypeDecl) { + // Note, for new-types, this has already been checked by CheckWellformed. + CheckResultToBeInType(result.Tok, result, decl.Var.Type, locals, returnBuilder, etran); + } - // check that the witness expression checks out - witnessExpr = Substitute(decl.Constraint, decl.Var, result); - witnessExpr.tok = result.Tok; - var desc = new PODesc.WitnessCheck(witnessString, witnessExpr); + // check that the witness expression checks out + witnessExpr = Substitute(decl.Constraint, decl.Var, result); + witnessExpr.tok = result.Tok; + var desc = new PODesc.WitnessCheck(witnessString, witnessExpr); - SplitAndAssertExpression(returnBuilder, witnessExpr, etran, context, desc); - }); + SplitAndAssertExpression(returnBuilder, witnessExpr, etran, context, desc); + }); codeContext = ghostCodeContext; } else if (decl.WitnessKind == SubsetTypeDecl.WKind.CompiledZero) { diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index 44cfe826596..f006de8c232 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -2340,9 +2340,9 @@ void AddWellformednessCheck(DatatypeCtor ctor) { 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); - }); + 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)) { From 91597f245873c5955e44a7f80a33f252ec965d44 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 12 Aug 2024 16:12:03 +0200 Subject: [PATCH 44/63] Add extra test and fix --- .../Verifier/BoogieGenerator.TrStatement.cs | 17 ++++++++--------- .../LitTest/ast/statement/localAssignment.dfy | 8 ++++++++ .../ast/statement/localAssignment.dfy.expect | 3 +++ 3 files changed, 19 insertions(+), 9 deletions(-) create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/localAssignment.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/localAssignment.dfy.expect diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs index 4c14c5db23f..90d579f3715 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs @@ -396,14 +396,14 @@ private void TrStmt(Statement stmt, BoogieStmtListBuilder builder, locals.Add(boogieTupleLocal); var boogieTupleReference = new Bpl.IdentifierExpr(rhs.tok, boogieTupleLocal); - AddResultCommands addResultCommands = (returnBuilder, result) => { + void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { Contract.Assert(pat.Expr.Type != null); var bResult = etran.TrExpr(result); - CheckSubrange(rhs.tok, bResult, rhs.Type, pat.Expr.Type, rhs, returnBuilder); - returnBuilder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, rhs.tok, rhs, - e => Bpl.Expr.Eq(boogieTupleReference, AdaptBoxing(rhs.tok, e, rhs.Type, pat.Expr.Type)))); - }; - CheckWellformedWithResult(rhs, new WFOptions(null, false, false), locals, builder, etran, addResultCommands); + CheckSubrange(result.tok, bResult, rhs.Type, pat.Expr.Type, rhs, returnBuilder); + returnBuilder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, rhs.tok, rhs, e => Bpl.Expr.Eq(boogieTupleReference, AdaptBoxing(rhs.tok, e, rhs.Type, pat.Expr.Type)))); + } + + CheckWellformedWithResult(rhs, new WFOptions(null, false, false), locals, builder, etran, AddResultCommands); builder.Add(TrAssumeCmd(rhs.tok, etran.CanCallAssumption(rhs))); builder.Add(new CommentCmd("CheckWellformedWithResult: any expression")); builder.Add(TrAssumeCmd(rhs.tok, MkIs(boogieTupleReference, pat.Expr.Type))); @@ -2621,16 +2621,15 @@ Bpl.Expr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bGivenLhs, IVariable lhs var e = (ExprRhs)rhs; var bRhs = etran.TrExpr(e.Expr); - // TODO should GetSubrangeCheck not be moved inside? var cre = GetSubrangeCheck(tok, bRhs, e.Expr.Type, rhsTypeConstraint, e.Expr, null, out var desc, ""); TrStmt_CheckWellformed(e.Expr, builder, locals, etran, true, addResultCommands: (returnBuilder, result) => { if (cre != null) { - returnBuilder.Add(Assert(tok, cre, desc)); + returnBuilder.Add(Assert(result.Tok, cre, desc)); } }); if (cre != null) { - builder.Add(new AssumeCmd(tok, cre)); //e.tok, MkIs(bRhs, rhsTypeConstraint))); + builder.Add(new AssumeCmd(tok, cre)); } if (bGivenLhs != null) { diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/localAssignment.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/localAssignment.dfy new file mode 100644 index 00000000000..8e344fb85d9 --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/localAssignment.dfy @@ -0,0 +1,8 @@ +// RUN: ! %verify %s &> "%t" +// RUN: %diff "%s.expect" "%t" + +method AssignToNat(b: bool) { + var r: nat := if b then + 10 else + -10; +} \ No newline at end of file diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/localAssignment.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/localAssignment.dfy.expect new file mode 100644 index 00000000000..54f553fdee4 --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/localAssignment.dfy.expect @@ -0,0 +1,3 @@ +localAssignment.dfy(7,4): Error: value does not satisfy the subset constraints of 'nat' + +Dafny program verifier finished with 0 verified, 1 error From bb8e2e306e77ddf3adcabbd9135deeb36da0794e Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 12 Aug 2024 16:21:37 +0200 Subject: [PATCH 45/63] Update measure-complexity expect file --- .../LitTest/cli/measure-complexity.dfy.expect | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/cli/measure-complexity.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/cli/measure-complexity.dfy.expect index 28ceaeb3255..ae1ad99b239 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/cli/measure-complexity.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/cli/measure-complexity.dfy.expect @@ -6,27 +6,27 @@ measure-complexity.dfy(9,18): measure-complexity.dfy(8,18): measure-complexity.dfy(9,15): measure-complexity.dfy(7,18): -measure-complexity.dfy(19,7): +measure-complexity.dfy(19,4): measure-complexity.dfy(6,18): measure-complexity.dfy(7,15): -measure-complexity.dfy(19,18): +measure-complexity.dfy(19,15): measure-complexity.dfy(7,13): measure-complexity.dfy(19,24): -measure-complexity.dfy(17,7): +measure-complexity.dfy(17,4): measure-complexity.dfy(17,15): measure-complexity.dfy(17,10): measure-complexity.dfy(19,10): measure-complexity.dfy(15,6): -measure-complexity.dfy(19,7): -measure-complexity.dfy(19,7): +measure-complexity.dfy(19,4): +measure-complexity.dfy(19,4): measure-complexity.dfy(9,13): -measure-complexity.dfy(17,7): -measure-complexity.dfy(19,18): +measure-complexity.dfy(17,4): +measure-complexity.dfy(19,15): measure-complexity.dfy(6,13): measure-complexity.dfy(8,15): -measure-complexity.dfy(17,7): +measure-complexity.dfy(17,4): measure-complexity.dfy(8,13): measure-complexity.dfy(6,15): -measure-complexity.dfy(19,18): +measure-complexity.dfy(19,15): measure-complexity.dfy(12,9): measure-complexity.dfy(5,7): From 51b4ff04b8c4e5757667b4b1d67063c0a06d451c Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 12 Aug 2024 17:24:04 +0200 Subject: [PATCH 46/63] Fix expect files --- .../ast/reveal/revealFunctions.dfy.expect | 2 +- .../ast/subsetTypes/errorReporting.dfy.expect | 4 ++-- .../LitTest/comp/rust/bymethod.dfy.expect | 2 +- .../LitTest/dafny0/ByMethod.dfy.expect | 8 ++++---- .../LitTest/dafny0/Include.dfy.expect | 4 ++-- .../dafny0/NonZeroInitialization.dfy.expect | 2 +- .../NonReferenceTraitsVerify.dfy.expect | Bin 4153 -> 2107 bytes 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/reveal/revealFunctions.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/reveal/revealFunctions.dfy.expect index 56a5eda7b47..d13f4c4f56f 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/reveal/revealFunctions.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/reveal/revealFunctions.dfy.expect @@ -2,4 +2,4 @@ revealFunctions.dfy(15,12): Error: assertion might not hold revealFunctions.dfy(22,12): Error: assertion might not hold revealFunctions.dfy(49,21): Error: assertion might not hold -Dafny program verifier finished with 25 verified, 3 errors +Dafny program verifier finished with 27 verified, 3 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/subsetTypes/errorReporting.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/subsetTypes/errorReporting.dfy.expect index 1d79115ac6a..c4f213a1fba 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/subsetTypes/errorReporting.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/subsetTypes/errorReporting.dfy.expect @@ -1,3 +1,3 @@ -ensuresReporting.dfy(5,2): Error: the given witness expression might not satisfy constraint +errorReporting.dfy(5,2): Error: the given witness expression might not satisfy constraint -Dafny program verifier finished with 0 verified, 1 errors +Dafny program verifier finished with 0 verified, 1 error diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/comp/rust/bymethod.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/comp/rust/bymethod.dfy.expect index ea1a3554507..585ba6c28b2 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/comp/rust/bymethod.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/comp/rust/bymethod.dfy.expect @@ -1,3 +1,3 @@ -Dafny program verifier finished with 9 verified, 0 errors +Dafny program verifier finished with 8 verified, 0 errors Everything ok \ No newline at end of file diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/ByMethod.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/ByMethod.dfy.expect index aedb5a1ea5c..0b4010a5344 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/ByMethod.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/ByMethod.dfy.expect @@ -2,17 +2,17 @@ ByMethod.dfy(38,18): Error: this invariant could not be proved to be maintained Related message: loop invariant violation ByMethod.dfy(42,4): Error: a postcondition could not be proved on this return path ByMethod.dfy(35,4): Related location: this is the postcondition that could not be proved -ByMethod.dfy(47,11): Error: a postcondition could not be proved on this return path +ByMethod.dfy(50,4): Error: a postcondition could not be proved on this return path ByMethod.dfy(48,12): Related location: this is the postcondition that could not be proved -ByMethod.dfy(55,11): Error: a postcondition could not be proved on this return path +ByMethod.dfy(58,4): Error: a postcondition could not be proved on this return path ByMethod.dfy(56,12): Related location: this is the postcondition that could not be proved ByMethod.dfy(60,4): Error: a postcondition could not be proved on this return path ByMethod.dfy(59,4): Related location: this is the postcondition that could not be proved -ByMethod.dfy(63,12): Error: a postcondition could not be proved on this return path +ByMethod.dfy(66,12): Error: a postcondition could not be proved on this return path ByMethod.dfy(64,27): Related location: this is the postcondition that could not be proved ByMethod.dfy(68,4): Error: a postcondition could not be proved on this return path ByMethod.dfy(67,4): Related location: this is the postcondition that could not be proved -ByMethod.dfy(71,12): Error: a postcondition could not be proved on this return path +ByMethod.dfy(74,12): Error: a postcondition could not be proved on this return path ByMethod.dfy(72,27): Related location: this is the postcondition that could not be proved ByMethod.dfy(93,11): Error: decreases clause might not decrease ByMethod.dfy(102,11): Error: decreases clause might not decrease diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Include.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Include.dfy.expect index 2533bfdace8..00551fbdf26 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Include.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Include.dfy.expect @@ -1,5 +1,5 @@ Include.dfy(32,7): Warning: the ... refinement feature in statements is deprecated -Include.dfy(20,11): Error: a postcondition could not be proved on this return path +Include.dfy(22,6): Error: a postcondition could not be proved on this return path Includee.dfy(17,19): Related location: this is the postcondition that could not be proved Includee.dfy[Concrete](22,15): Error: assertion might not hold Include.dfy(28,6): Error: a postcondition could not be proved on this return path @@ -13,7 +13,7 @@ Includee.dfy(7,0): Error: out-parameter 'y', which is subject to definite-assign Includee.dfy(21,2): Error: a postcondition could not be proved on this return path Includee.dfy(20,14): Related location: this is the postcondition that could not be proved Includee.dfy(24,17): Error: assertion might not hold -Include.dfy(20,11): Error: a postcondition could not be proved on this return path +Include.dfy(22,6): Error: a postcondition could not be proved on this return path Includee.dfy(17,19): Related location: this is the postcondition that could not be proved Includee.dfy[Concrete](22,15): Error: assertion might not hold Include.dfy(28,6): Error: a postcondition could not be proved on this return path diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/NonZeroInitialization.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/NonZeroInitialization.dfy.expect index a89662782f9..4d41cf1ff70 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/NonZeroInitialization.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/NonZeroInitialization.dfy.expect @@ -7,7 +7,7 @@ NonZeroInitialization.dfy(17,61): Error: result of operation might violate newty NonZeroInitialization.dfy(18,46): Error: result of operation might violate newtype constraint for 'NewSix' NonZeroInitialization.dfy(19,8): Error: trying witness 0: result of operation might violate newtype constraint for 'NewSix' NonZeroInitialization.dfy(37,5): Error: cannot find witness that shows type is inhabited; try giving a hint through a 'witness' or 'ghost witness' clause, or use 'witness *' to treat as a possibly empty type -NonZeroInitialization.dfy(38,70): Error: result of operation might violate subset type constraint for 'ListTwo' +NonZeroInitialization.dfy(38,66): Error: result of operation might violate subset type constraint for 'ListTwo' NonZeroInitialization.dfy(39,5): Error: cannot find witness that shows type is inhabited; try giving a hint through a 'witness' or 'ghost witness' clause, or use 'witness *' to treat as a possibly empty type NonZeroInitialization.dfy(53,0): Error: out-parameter 'g', which is subject to definite-assignment rules, might be uninitialized at this return point NonZeroInitialization.dfy(58,7): Error: unless an initializer is provided for the array elements, a new array of 'Yt' must have empty size diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/traits/NonReferenceTraitsVerify.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/traits/NonReferenceTraitsVerify.dfy.expect index 014bd0420fc1afe51bd1039298c8d58593e3f483..6e76a955be63b463dfd61ef56682849b3b0d2ec3 100644 GIT binary patch delta 11 Scmdm~uv=imKlaIg_-z3o(*-pE literal 4153 zcmeI0O;5ux42C)HSNI50g_f>kA7{AkFd^=tafzii8IC)&e@`-m1_Jefv}h8odTJy) ze%|NeZoJ!KgN&}mN7f|a37MM1t!WO|vqf=tvxWy|&uh@I^FeCw3<-G$weMR4&I>#v z?AY(oK>RG00x(L?4t84XWHZ=eUX*3*vBg$PEYSK|r|RpEISy46>4^xCNXMyS08if`XI+V$RKD3_}c3hJVWO>qD1Z}1lp&}@RVkR z>VV=aA;uPc5Rl}$3s>(}nb&Hfsw#SU?eaS7zz9{0+V~)xI&joaB9iG`~CJTV2R5-|M@2$Of-4ELS@ From 288d1515afd176ca593e74a3057d8d1ce476408d Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 13 Aug 2024 17:01:08 +0200 Subject: [PATCH 47/63] Fix ProofDependencyLogging test --- .../Verifier/BoogieGenerator.ExpressionWellformed.cs | 8 ++++---- .../Verifier/BoogieGenerator.Functions.Wellformedness.cs | 2 +- .../LitTests/LitTest/logger/ProofDependencyLogging.dfy | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 019b9c05118..efce2a85667 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -1209,7 +1209,7 @@ void CheckOperand(Expression operand) { void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { if (rangeType != null) { - CheckSubsetType(etran, result, resultIe, rangeType, returnBuilder); + CheckSubsetType(etran, result, resultIe, rangeType, returnBuilder, "lambda expression"); } } @@ -1343,13 +1343,13 @@ void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { } public void CheckSubsetType(ExpressionTranslator etran, Expression expr, Bpl.Expr selfCall, Type resultType, - BoogieStmtListBuilder builder) { + BoogieStmtListBuilder builder, string comment) { Contract.Assert(resultType != null); var bResult = etran.TrExpr(expr); CheckSubrange(expr.tok, bResult, expr.Type, resultType, expr, builder); builder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, expr.tok, expr, - e => Bpl.Expr.Eq(selfCall, AdaptBoxing(expr.tok, e, expr.Type, resultType)))); + e => Bpl.Expr.Eq(selfCall, AdaptBoxing(expr.tok, e, expr.Type, resultType)), comment)); builder.Add(TrAssumeCmd(expr.tok, etran.CanCallAssumption(expr))); builder.Add(new CommentCmd("CheckWellformedWithResult: any expression")); builder.Add(TrAssumeCmd(expr.tok, MkIs(selfCall, resultType))); @@ -1518,7 +1518,7 @@ void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, List Date: Wed, 14 Aug 2024 11:33:06 +0200 Subject: [PATCH 48/63] Add --performance-stats option and use this on the SchorrWaite test --- Source/DafnyDriver/CliCompilation.cs | 2 ++ Source/DafnyDriver/Commands/VerifyCommand.cs | 15 +++++++++++++++ .../LitTests/LitTest/dafny1/SchorrWaite.dfy | 2 +- .../LitTest/dafny1/SchorrWaite.dfy.expect | 2 ++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Source/DafnyDriver/CliCompilation.cs b/Source/DafnyDriver/CliCompilation.cs index cb6e0d8119b..1e3f1cad5a3 100644 --- a/Source/DafnyDriver/CliCompilation.cs +++ b/Source/DafnyDriver/CliCompilation.cs @@ -315,4 +315,6 @@ record VerificationStatistics { public int OutOfResourceCount; public int OutOfMemoryCount; public int SolverExceptionCount; + public int TotalResourcesUsed; + public int MaxVcResourcesUsed; } diff --git a/Source/DafnyDriver/Commands/VerifyCommand.cs b/Source/DafnyDriver/Commands/VerifyCommand.cs index 8c0f6a97e0e..c4f1d766375 100644 --- a/Source/DafnyDriver/Commands/VerifyCommand.cs +++ b/Source/DafnyDriver/Commands/VerifyCommand.cs @@ -20,8 +20,13 @@ static VerifyCommand() { // they can't be specified when building a doo file. OptionRegistry.RegisterOption(FilterSymbol, OptionScope.Cli); OptionRegistry.RegisterOption(FilterPosition, OptionScope.Cli); + OptionRegistry.RegisterOption(PerformanceStatisticsOption, OptionScope.Cli); } + public static readonly Option PerformanceStatisticsOption = new("--performance-stats", + @"Report a summary of the verification performance."); + + public static readonly Option FilterSymbol = new("--filter-symbol", @"Filter what gets verified by selecting only symbols whose fully qualified name contains the given argument. For example: ""--filter-symbol=MyNestedModule.MyFooFunction"""); @@ -40,6 +45,7 @@ public static Command Create() { private static IReadOnlyList