From d6f39513d225702cdac4d162ff8ab37f71812a81 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 16 Sep 2024 19:10:37 +0200 Subject: [PATCH 01/47] Add proof by statement --- .../DafnyCore/AST/Statements/ProofByStmt.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 Source/DafnyCore/AST/Statements/ProofByStmt.cs diff --git a/Source/DafnyCore/AST/Statements/ProofByStmt.cs b/Source/DafnyCore/AST/Statements/ProofByStmt.cs new file mode 100644 index 00000000000..00ef16bc669 --- /dev/null +++ b/Source/DafnyCore/AST/Statements/ProofByStmt.cs @@ -0,0 +1,19 @@ +namespace Microsoft.Dafny; + +class ProofByStmt : Statement, ICanResolveNewAndOld { + public ProofByStmt(RangeToken range, BlockStmt proof, Statement body) : base(range) { + Proof = proof; + Body = body; + } + + private Statement Body { get; } + private BlockStmt Proof { get; } + + public override void GenResolve(INewOrOldResolver resolver, ResolutionContext resolutionContext) { + resolver.ResolveStatement(Body, resolutionContext); + resolver.ResolveBlockStatement(Proof, resolutionContext with { + CodeContext = new CodeContextWrapper(resolutionContext.CodeContext, true) + }); + base.GenResolve(resolver, resolutionContext); + } +} \ No newline at end of file From a2f6b5cc77d40b2aa41208a7bd7399276d095704 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 16 Sep 2024 22:57:12 +0200 Subject: [PATCH 02/47] Pass AssertMode to all Assert calls --- .../{ProofByStmt.cs => BlockByProofStmt.cs} | 8 +- .../Verifier/BoogieGenerator.Decreases.cs | 4 +- .../BoogieGenerator.DefiniteAssignment.cs | 6 +- .../BoogieGenerator.ExpressionWellformed.cs | 127 +++++++++++------- ...oogieGenerator.Functions.Wellformedness.cs | 2 +- .../Verifier/BoogieGenerator.Methods.cs | 20 +-- .../Verifier/BoogieGenerator.SplitExpr.cs | 2 +- .../Verifier/BoogieGenerator.Types.cs | 28 ++-- Source/DafnyCore/Verifier/BoogieGenerator.cs | 114 +++------------- .../BoogieGenerator.DataTypes.cs | 83 +++++++++++- .../Statements/BlockByProofStmtVerifier.cs | 18 +++ .../BoogieGenerator.TrPredicateStatement.cs | 10 +- .../BoogieGenerator.TrStatement.cs | 72 +++++----- .../{ => Statements}/OpaqueBlockVerifier.cs | 4 +- 14 files changed, 276 insertions(+), 222 deletions(-) rename Source/DafnyCore/AST/Statements/{ProofByStmt.cs => BlockByProofStmt.cs} (65%) rename Source/DafnyCore/Verifier/{ => Datatypes}/BoogieGenerator.DataTypes.cs (90%) create mode 100644 Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs rename Source/DafnyCore/Verifier/{ => Statements}/BoogieGenerator.TrPredicateStatement.cs (95%) rename Source/DafnyCore/Verifier/{ => Statements}/BoogieGenerator.TrStatement.cs (98%) rename Source/DafnyCore/Verifier/{ => Statements}/OpaqueBlockVerifier.cs (97%) diff --git a/Source/DafnyCore/AST/Statements/ProofByStmt.cs b/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs similarity index 65% rename from Source/DafnyCore/AST/Statements/ProofByStmt.cs rename to Source/DafnyCore/AST/Statements/BlockByProofStmt.cs index 00ef16bc669..80b2480ade3 100644 --- a/Source/DafnyCore/AST/Statements/ProofByStmt.cs +++ b/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs @@ -1,13 +1,13 @@ namespace Microsoft.Dafny; -class ProofByStmt : Statement, ICanResolveNewAndOld { - public ProofByStmt(RangeToken range, BlockStmt proof, Statement body) : base(range) { +public class BlockByProofStmt : Statement, ICanResolveNewAndOld { + public BlockByProofStmt(RangeToken range, BlockStmt proof, Statement body) : base(range) { Proof = proof; Body = body; } - private Statement Body { get; } - private BlockStmt Proof { get; } + public Statement Body { get; } + public BlockStmt Proof { get; } public override void GenResolve(INewOrOldResolver resolver, ResolutionContext resolutionContext) { resolver.ResolveStatement(Body, resolutionContext); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Decreases.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Decreases.cs index 6a52c81f018..77f5fc63d0f 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Decreases.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Decreases.cs @@ -92,7 +92,7 @@ void CheckCallTermination(IToken tok, List contextDecreases, List @@ -168,7 +168,7 @@ Bpl.Expr DecreasesCheck(List toks, List prevGhostLocals, Expression dafnyBound = Expression.CreateOr(boundedDafny, EqDafny[k]); Bpl.Cmd cmd = Assert(toks[k], BplOr(bounded, Eq[k]), - new PODesc.DecreasesBoundedBelow(N, k, zeroStr, prevGhostLocals, dafnyBound, suffixMsg)); + new PODesc.DecreasesBoundedBelow(N, k, zeroStr, prevGhostLocals, dafnyBound, suffixMsg), builder.Context); builder.Add(cmd); } } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs index b595aeec5f7..d0b3ed230ff 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs @@ -165,7 +165,7 @@ void CheckDefiniteAssignment(IdentifierExpr expr, BoogieStmtListBuilder builder) Bpl.IdentifierExpr ie; if (DefiniteAssignmentTrackers.TryGetValue(expr.Var.UniqueName, out ie)) { builder.Add(Assert(GetToken(expr), ie, - new PODesc.DefiniteAssignment("variable", expr.Var.Name, "here"))); + new PODesc.DefiniteAssignment("variable", expr.Var.Name, "here"), builder.Context)); } } @@ -191,7 +191,7 @@ void CheckDefiniteAssignmentSurrogate(IToken tok, Field field, bool atNew, Boogi if (DefiniteAssignmentTrackers.TryGetValue(nm, out ie)) { var desc = new PODesc.DefiniteAssignment( "field", field.Name, atNew ? "at this point in the constructor body" : "here"); - builder.Add(Assert(tok, ie, desc)); + builder.Add(Assert(tok, ie, desc, builder.Context)); } } @@ -233,7 +233,7 @@ void CheckDefiniteAssignmentReturn(IToken tok, Formal p, BoogieStmtListBuilder b Bpl.IdentifierExpr ie; if (DefiniteAssignmentTrackers.TryGetValue(p.UniqueName, out ie)) { var desc = new PODesc.DefiniteAssignment("out-parameter", p.Name, "at this return point"); - builder.Add(Assert(tok, ie, desc)); + builder.Add(Assert(tok, ie, desc, builder.Context)); } } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 1157343bdac..35b41d10f68 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -93,10 +93,10 @@ public WFOptions WithLValueContext(bool lValueContext) { return (t, e, d, qk) => { if (Locals != null) { var b = BoogieGenerator.BplLocalVar(tran.CurrentIdGenerator.FreshId("b$reqreads#"), Bpl.Type.Bool, Locals); - CreateAsserts.Add(() => tran.Assert(t, b, d, qk)); + CreateAsserts.Add(() => tran.Assert(t, b, d, builder.Context, qk)); builder.Add(Bpl.Cmd.SimpleAssign(e.tok, (Bpl.IdentifierExpr)b, e)); } else { - builder.Add(tran.Assert(t, e, d, qk)); + builder.Add(tran.Assert(t, e, d, builder.Context, qk)); } }; } @@ -349,7 +349,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, builder.Add(TrAssumeCmd(selectExpr.tok, correctConstructor)); } else { builder.Add(Assert(GetToken(expr), correctConstructor, - new PODesc.DestructorValid(dtor, e.Obj, dtor.EnclosingCtors))); + new PODesc.DestructorValid(dtor, e.Obj, dtor.EnclosingCtors), builder.Context)); } CheckNotGhostVariant(e, "destructor", dtor.EnclosingCtors, builder, etran); } else if (e.Member is DatatypeDiscriminator discriminator) { @@ -360,14 +360,14 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Bpl.Expr wh = GetWhereClause(selectExpr.tok, etran.TrExpr(e.Obj), e.Obj.Type, etran.OldAt(e.AtLabel), ISALLOC, true); if (wh != null) { var desc = new PODesc.IsAllocated("receiver argument", "in the two-state function's previous state", e.Obj, e.AtLabel); - builder.Add(Assert(GetToken(expr), wh, desc)); + builder.Add(Assert(GetToken(expr), wh, desc, builder.Context)); } } else if (etran.UsesOldHeap) { Bpl.Expr wh = GetWhereClause(selectExpr.tok, etran.TrExpr(e.Obj), e.Obj.Type, etran, ISALLOC, true); if (wh != null) { var desc = new PODesc.IsAllocated("receiver", $"in the state in which its {(e.Member is Field ? "fields" : "members")} are accessed", e.Obj, e.AtLabel); - builder.Add(Assert(GetToken(expr), wh, desc)); + builder.Add(Assert(GetToken(expr), wh, desc, builder.Context)); } } } @@ -387,9 +387,11 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, CheckWellformed(e.Seq, wfOptions, locals, builder, etran); Bpl.Expr seq = etran.TrExpr(e.Seq); if (eSeqType.IsArrayType) { - builder.Add(Assert(GetToken(e.Seq), Bpl.Expr.Neq(seq, predef.Null), new PODesc.NonNull("array", e.Seq))); + builder.Add(Assert(GetToken(e.Seq), Bpl.Expr.Neq(seq, predef.Null), + new PODesc.NonNull("array", e.Seq), builder.Context)); if (etran.UsesOldHeap) { - builder.Add(Assert(GetToken(e.Seq), MkIsAlloc(seq, eSeqType, etran.HeapExpr), new PODesc.IsAllocated("array", null, e.Seq))); + builder.Add(Assert(GetToken(e.Seq), MkIsAlloc(seq, eSeqType, etran.HeapExpr), + new PODesc.IsAllocated("array", null, e.Seq), builder.Context)); } } Bpl.Expr e0 = null; @@ -400,7 +402,8 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, var f = finite ? BuiltinFunction.MapDomain : BuiltinFunction.IMapDomain; Bpl.Expr inDomain = FunctionCall(selectExpr.tok, f, finite ? predef.MapType : predef.IMapType, seq); inDomain = Bpl.Expr.Select(inDomain, BoxIfNecessary(e.tok, e0, e.E0.Type)); - builder.Add(Assert(GetToken(expr), inDomain, new PODesc.ElementInDomain(e.Seq, e.E0), wfOptions.AssertKv)); + builder.Add(Assert(GetToken(expr), inDomain, + new PODesc.ElementInDomain(e.Seq, e.E0), builder.Context, wfOptions.AssertKv)); } else if (eSeqType is MultiSetType) { // cool @@ -409,7 +412,9 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, e0 = etran.TrExpr(e.E0); CheckWellformed(e.E0, wfOptions, locals, builder, etran); var desc = new PODesc.InRange(e.Seq, e.E0, e.SelectOne, e.SelectOne ? "index" : "lower bound"); - builder.Add(Assert(GetToken(expr), InSeqRange(selectExpr.tok, e0, e.E0.Type, seq, isSequence, null, !e.SelectOne), desc, wfOptions.AssertKv)); + builder.Add(Assert(GetToken(expr), + InSeqRange(selectExpr.tok, e0, e.E0.Type, seq, isSequence, null, !e.SelectOne), + desc, builder.Context, wfOptions.AssertKv)); } if (e.E1 != null) { CheckWellformed(e.E1, wfOptions, locals, builder, etran); @@ -419,8 +424,10 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, } else { lowerBound = e0; } - builder.Add(Assert(GetToken(expr), InSeqRange(selectExpr.tok, etran.TrExpr(e.E1), e.E1.Type, seq, isSequence, lowerBound, true), - new PODesc.SequenceSelectRangeValid(e.Seq, e.E0, e.E1, isSequence ? "sequence" : "array"), wfOptions.AssertKv)); + builder.Add(Assert(GetToken(expr), + InSeqRange(selectExpr.tok, etran.TrExpr(e.E1), e.E1.Type, seq, isSequence, lowerBound, true), + new PODesc.SequenceSelectRangeValid(e.Seq, e.E0, e.E1, isSequence ? "sequence" : "array"), + builder.Context, wfOptions.AssertKv)); } } if (!origOptions.LValueContext && wfOptions.DoReadsChecks && eSeqType.IsArrayType) { @@ -460,9 +467,11 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, MultiSelectExpr e = selectExpr; CheckWellformed(e.Array, wfOptions, locals, builder, etran); Bpl.Expr array = etran.TrExpr(e.Array); - builder.Add(Assert(GetToken(e.Array), Bpl.Expr.Neq(array, predef.Null), new PODesc.NonNull("array", e.Array))); + builder.Add(Assert(GetToken(e.Array), Bpl.Expr.Neq(array, predef.Null), + new PODesc.NonNull("array", e.Array), builder.Context)); if (etran.UsesOldHeap) { - builder.Add(Assert(GetToken(e.Array), MkIsAlloc(array, e.Array.Type, etran.HeapExpr), new PODesc.IsAllocated("array", null, e.Array))); + builder.Add(Assert(GetToken(e.Array), MkIsAlloc(array, e.Array.Type, etran.HeapExpr), + new PODesc.IsAllocated("array", null, e.Array), builder.Context)); } for (int idxId = 0; idxId < e.Indices.Count; idxId++) { var idx = e.Indices[idxId]; @@ -476,7 +485,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, var tok = idx is IdentifierExpr ? e.tok : idx.tok; // TODO: Reusing the token of an identifier expression would underline its definition. but this is still not perfect. var desc = new PODesc.InRange(e.Array, e.Indices[idxId], true, $"index {idxId}", idxId); - builder.Add(Assert(tok, BplAnd(lower, upper), desc, wfOptions.AssertKv)); + builder.Add(Assert(tok, BplAnd(lower, upper), desc, builder.Context, wfOptions.AssertKv)); } if (wfOptions.DoReadsChecks) { Bpl.Expr fieldName = etran.GetArrayIndexFieldName(e.tok, e.Indices); @@ -499,7 +508,9 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, CheckWellformed(e.Index, wfOptions, locals, builder, etran); if (collectionType is SeqType) { var desc = new PODesc.InRange(e.Seq, e.Index, true, "index"); - builder.Add(Assert(GetToken(e.Index), InSeqRange(updateExpr.tok, index, e.Index.Type, seq, true, null, false), desc, wfOptions.AssertKv)); + builder.Add(Assert(GetToken(e.Index), + InSeqRange(updateExpr.tok, index, e.Index.Type, seq, true, null, false), + desc, builder.Context, wfOptions.AssertKv)); } else { CheckSubrange(e.Index.tok, index, e.Index.Type, collectionType.Arg, e.Index, builder); } @@ -511,7 +522,8 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, CheckSubrange(e.Value.tok, value, e.Value.Type, mapType.Range, e.Value, builder); } else if (collectionType is MultiSetType) { var desc = new PODesc.NonNegative("new number of occurrences", e.Value); - builder.Add(Assert(GetToken(e.Value), Bpl.Expr.Le(Bpl.Expr.Literal(0), value), desc, wfOptions.AssertKv)); + builder.Add(Assert(GetToken(e.Value), Bpl.Expr.Le(Bpl.Expr.Literal(0), value), + desc, builder.Context, wfOptions.AssertKv)); } else { Contract.Assert(false); } @@ -541,14 +553,14 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Bpl.Expr wh = GetWhereClause(e.Function.tok, etran.TrExpr(e.Function), e.Function.Type, etran, ISALLOC, true); if (wh != null) { var desc = new PODesc.IsAllocated("function", "in the state in which the function is invoked", e.Function); - builder.Add(Assert(GetToken(e.Function), wh, desc)); + builder.Add(Assert(GetToken(e.Function), wh, desc, builder.Context)); } for (int i = 0; i < e.Args.Count; i++) { Expression ee = e.Args[i]; wh = GetWhereClause(ee.tok, etran.TrExpr(ee), ee.Type, etran, ISALLOC, true); if (wh != null) { var desc = new PODesc.IsAllocated("argument", "in the state in which the function is invoked", ee); - builder.Add(Assert(GetToken(ee), wh, desc)); + builder.Add(Assert(GetToken(ee), wh, desc, builder.Context)); } } } @@ -608,7 +620,8 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, // check precond var bPrecond = FunctionCall(e.tok, Requires(arity), Bpl.Type.Bool, args); - builder.Add(Assert(GetToken(expr), bPrecond, new PODesc.PreconditionSatisfied(dPrecond, null, null))); + builder.Add(Assert(GetToken(expr), bPrecond, + new PODesc.PreconditionSatisfied(dPrecond, null, null), builder.Context)); } if (wfOptions.DoReadsChecks && !fnCoreType.IsArrowTypeWithoutReadEffects) { @@ -717,7 +730,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, 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)); + builder.Add(Assert(GetToken(e.Receiver), wh, desc, builder.Context)); } } for (int i = 0; i < e.Args.Count; i++) { @@ -725,7 +738,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, 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)); + builder.Add(Assert(GetToken(ee), wh, desc, builder.Context)); } } } else if (e.Function is TwoStateFunction) { @@ -733,7 +746,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, 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)); + builder.Add(Assert(GetToken(e.Receiver), wh, desc, builder.Context)); } } Contract.Assert(e.Function.Ins.Count == e.Args.Count); @@ -750,7 +763,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, ee, e.AtLabel ); - builder.Add(Assert(GetToken(ee), wh, desc)); + builder.Add(Assert(GetToken(ee), wh, desc, builder.Context)); } } } @@ -761,7 +774,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, if (e.Function.Name == "reads" && !e.Receiver.Type.IsArrowTypeWithoutReadEffects) { var arguments = etran.FunctionInvocationArguments(e, null, null); var precondition = FunctionCall(e.tok, Requires(e.Args.Count), Bpl.Type.Bool, arguments); - builder.Add(Assert(GetToken(expr), precondition, new PODesc.PreconditionSatisfied(null, null, null))); + builder.Add(Assert(GetToken(expr), precondition, new PODesc.PreconditionSatisfied(null, null, null), builder.Context)); if (wfOptions.DoReadsChecks) { // check that the callee reads only what the caller is already allowed to read @@ -814,9 +827,9 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, 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)); + builder.Add(Assert(tok, ss.E, desc, builder.Context, wfOptions.AssertKv)); } else { - builder.Add(AssertNS(tok, ss.E, desc)); + builder.Add(AssertAndForget(tok, ss.E, desc)); } } } @@ -846,7 +859,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Contract.Assert(calleeSCCLookup != null); if (ModuleDefinition.InSameSCC(calleeSCCLookup, codeContext)) { if (wfOptions.DoOnlyCoarseGrainedTerminationChecks) { - builder.Add(Assert(GetToken(expr), Bpl.Expr.False, new PODesc.IsNonRecursive())); + builder.Add(Assert(GetToken(expr), Bpl.Expr.False, new PODesc.IsNonRecursive(), builder.Context)); } else { List contextDecreases = codeContext.Decreases.Expressions; List calleeDecreases = e.Function.Decreases.Expressions; @@ -913,7 +926,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, var e = constructionExpr; CheckWellformed(e.N, wfOptions, locals, builder, etran); var desc = new PODesc.NonNegative("sequence size", e.N); - builder.Add(Assert(GetToken(e.N), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(e.N)), desc)); + builder.Add(Assert(GetToken(e.N), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(e.N)), desc, builder.Context)); CheckWellformed(e.Initializer, wfOptions, locals, builder, etran); var eType = e.Type.NormalizeToAncestorType().AsSeqType.Arg; @@ -947,14 +960,15 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, } else { Contract.Assert(ty.IsRefType); nonNull = Bpl.Expr.Neq(r, predef.Null); - builder.Add(Assert(GetToken(fe.E), BplImp(ante, nonNull), new PODesc.NonNull(description, fe.E, description != "object"))); + builder.Add(Assert(GetToken(fe.E), BplImp(ante, nonNull), + new PODesc.NonNull(description, fe.E, description != "object"), builder.Context)); } // check that "r" was allocated in the "e.AtLabel" state Bpl.Expr wh = GetWhereClause(fe.E.tok, r, ty, etran.OldAt(e.AtLabel), ISALLOC, true); if (wh != null) { var desc = new PODesc.IsAllocated(description, "in the old-state of the 'unchanged' predicate", fe.E, e.AtLabel, description != "object"); - builder.Add(Assert(GetToken(fe.E), BplImp(BplAnd(ante, nonNull), wh), desc)); + builder.Add(Assert(GetToken(fe.E), BplImp(BplAnd(ante, nonNull), wh), desc, builder.Context)); } // check that the 'unchanged' argument reads only what the context is allowed to read if (wfOptions.DoReadsChecks) { @@ -1006,22 +1020,26 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, CheckWellformed(e.E1, wfOptions, locals, builder, etran); if (e.ResolvedOp == BinaryExpr.ResolvedOpcode.Sub && e.E0.Type.IsBigOrdinalType) { var rhsIsNat = FunctionCall(binaryExpr.tok, "ORD#IsNat", Bpl.Type.Bool, etran.TrExpr(e.E1)); - builder.Add(Assert(GetToken(expr), rhsIsNat, new PODesc.OrdinalSubtractionIsNatural(e.E1))); + builder.Add(Assert(GetToken(expr), rhsIsNat, + new PODesc.OrdinalSubtractionIsNatural(e.E1), builder.Context)); var offset0 = FunctionCall(binaryExpr.tok, "ORD#Offset", Bpl.Type.Int, etran.TrExpr(e.E0)); var offset1 = FunctionCall(binaryExpr.tok, "ORD#Offset", Bpl.Type.Int, etran.TrExpr(e.E1)); - builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(offset1, offset0), new PODesc.OrdinalSubtractionUnderflow(e.E0, e.E1))); + builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(offset1, offset0), + new PODesc.OrdinalSubtractionUnderflow(e.E0, e.E1), builder.Context)); } else if (e.Type.NormalizeToAncestorType().IsCharType) { var e0 = FunctionCall(binaryExpr.tok, "char#ToInt", Bpl.Type.Int, etran.TrExpr(e.E0)); var e1 = FunctionCall(binaryExpr.tok, "char#ToInt", Bpl.Type.Int, etran.TrExpr(e.E1)); if (e.ResolvedOp == BinaryExpr.ResolvedOpcode.Add) { builder.Add(Assert(GetToken(expr), FunctionCall(Token.NoToken, BuiltinFunction.IsChar, null, - Bpl.Expr.Binary(BinaryOperator.Opcode.Add, e0, e1)), new PODesc.CharOverflow(e.E0, e.E1))); + Bpl.Expr.Binary(BinaryOperator.Opcode.Add, e0, e1)), + new PODesc.CharOverflow(e.E0, e.E1), builder.Context)); } else { Contract.Assert(e.ResolvedOp == BinaryExpr.ResolvedOpcode.Sub); // .Mul is not supported for char builder.Add(Assert(GetToken(expr), FunctionCall(Token.NoToken, BuiltinFunction.IsChar, null, - Bpl.Expr.Binary(BinaryOperator.Opcode.Sub, e0, e1)), new PODesc.CharUnderflow(e.E0, e.E1))); + Bpl.Expr.Binary(BinaryOperator.Opcode.Sub, e0, e1)), + new PODesc.CharUnderflow(e.E0, e.E1), builder.Context)); } } break; @@ -1036,7 +1054,8 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, zero = Bpl.Expr.Literal(0); } CheckWellformed(e.E1, wfOptions, locals, builder, etran); - builder.Add(Assert(GetToken(expr), Bpl.Expr.Neq(etran.TrExpr(e.E1), zero), new PODesc.DivisorNonZero(e.E1), wfOptions.AssertKv)); + builder.Add(Assert(GetToken(expr), Bpl.Expr.Neq(etran.TrExpr(e.E1), zero), + new PODesc.DivisorNonZero(e.E1), builder.Context, wfOptions.AssertKv)); } break; case BinaryExpr.ResolvedOpcode.LeftShift: @@ -1052,7 +1071,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, // w is a number that can be represented in the e.E1.Type, so do the comparison in that bitvector type. var bound = BplBvLiteralExpr(e.tok, BaseTypes.BigNum.FromInt(w), e1Width); var cmp = etran.TrToFunctionCall(binaryExpr.tok, "le_bv" + e1Width, Bpl.Type.Bool, etran.TrExpr(e.E1), bound, false); - builder.Add(Assert(GetToken(expr), cmp, upperDesc, wfOptions.AssertKv)); + builder.Add(Assert(GetToken(expr), cmp, upperDesc, builder.Context, wfOptions.AssertKv)); } else { // In the previous branch, we had: // w < 2^e1Width (*) @@ -1066,8 +1085,10 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, } } else { var positiveDesc = new PODesc.ShiftLowerBound(true, e.E1); - builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(e.E1)), positiveDesc, wfOptions.AssertKv)); - builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(etran.TrExpr(e.E1), Bpl.Expr.Literal(w)), upperDesc, wfOptions.AssertKv)); + builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(e.E1)), + positiveDesc, builder.Context, wfOptions.AssertKv)); + builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(etran.TrExpr(e.E1), Bpl.Expr.Literal(w)), + upperDesc, builder.Context, wfOptions.AssertKv)); } } break; @@ -1095,7 +1116,7 @@ void CheckOperand(Expression operand) { var notGhostCtor = BplAnd(ghostConstructors.ConvertAll( ctor => Bpl.Expr.Not(FunctionCall(expr.tok, ctor.QueryField.FullSanitizedName, Bpl.Type.Bool, value)))); builder.Add(Assert(GetToken(expr), notGhostCtor, - new PODesc.NotGhostVariant("equality", operand, ghostConstructors))); + new PODesc.NotGhostVariant("equality", operand, ghostConstructors), builder.Context)); } CheckOperand(e.E0); @@ -1120,7 +1141,8 @@ void CheckOperand(Expression operand) { case TernaryExpr.Opcode.PrefixEqOp: case TernaryExpr.Opcode.PrefixNeqOp: if (e.E0.Type.IsNumericBased(Type.NumericPersuasion.Int)) { - builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(e.E0)), new PODesc.PrefixEqualityLimit(e.E0), wfOptions.AssertKv)); + builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(e.E0)), + new PODesc.PrefixEqualityLimit(e.E0), builder.Context, wfOptions.AssertKv)); } break; default: @@ -1232,7 +1254,8 @@ void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { var different = BplOr( Bpl.Expr.Neq(comprehensionEtran.TrExpr(bodyLeft), comprehensionEtran.TrExpr(bodyLeftPrime)), Bpl.Expr.Eq(comprehensionEtran.TrExpr(body), comprehensionEtran.TrExpr(bodyPrime))); - b.Add(Assert(GetToken(mc.TermLeft), different, new PODesc.ComprehensionNoAlias(mc.BoundVars, mc.Range, mc.TermLeft, mc.Term))); + b.Add(Assert(GetToken(mc.TermLeft), different, + new PODesc.ComprehensionNoAlias(mc.BoundVars, mc.Range, mc.TermLeft, mc.Term), builder.Context)); }); } }); @@ -1302,7 +1325,7 @@ void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { // Every constructor has this destructor; no need to check anything } else { builder.Add(Assert(GetToken(expr), correctConstructor, - new PODesc.ValidConstructorNames(updateExpr.Root, e.LegalSourceConstructors))); + new PODesc.ValidConstructorNames(updateExpr.Root, e.LegalSourceConstructors), builder.Context)); } CheckNotGhostVariant(e.InCompiledContext, updateExpr, e.Root, "update of", e.Members, @@ -1384,7 +1407,8 @@ private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, List local } String missingStr = me.Context.FillHole(new IdCtx(missingCtor)).AbstractAllHoles().ToString(); - b.Add(Assert(GetToken(me), Bpl.Expr.False, new PODesc.MatchIsComplete("expression", missingStr))); + b.Add(Assert(GetToken(me), Bpl.Expr.False, + new PODesc.MatchIsComplete("expression", missingStr), builder.Context)); Bpl.Expr guard = Bpl.Expr.Eq(src, r); ifCmd = new Bpl.IfCmd(me.tok, guard, b.Collect(me.tok), ifCmd, els); @@ -1481,7 +1505,7 @@ private void CheckNotGhostVariant(bool inCompiledContext, Expression exprUsedFor new PODesc.NotGhostVariant(whatKind, Util.PrintableNameList(members.ConvertAll(member => member.Name), "and"), datatypeValue, - enclosingGhostConstructors))); + enclosingGhostConstructors), builder.Context)); } } } @@ -1497,9 +1521,10 @@ void CheckWellformedSpecialFunction(FunctionCallExpr expr, WFOptions options, Bp if (expr.Function.Name is "RotateLeft" or "RotateRight") { var w = expr.Type.AsBitVectorType.Width; var arg = expr.Args[0]; - builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(arg)), new PODesc.ShiftLowerBound(false, arg), options.AssertKv)); - builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(etran.TrExpr(arg), Bpl.Expr.Literal(w)), new PODesc.ShiftUpperBound(w, false, arg), - options.AssertKv)); + builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(arg)), + new PODesc.ShiftLowerBound(false, arg), builder.Context, options.AssertKv)); + builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(etran.TrExpr(arg), Bpl.Expr.Literal(w)), + new PODesc.ShiftUpperBound(w, false, arg), builder.Context, options.AssertKv)); } } @@ -1574,7 +1599,7 @@ void CheckPostconditionForRhs(BoogieStmtListBuilder innerBuilder, Expression bod 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()))); + new PODesc.LetSuchThatUnique(e.RHSs[0], e.BoundVars.ToList()), builder.Context)); } // assume $let$canCall(g); etran.LetDesugaring(e); // call LetDesugaring to prepare the desugaring and populate letSuchThatExprInfo with something for e @@ -1593,7 +1618,7 @@ void CheckFrameWellFormed(WFOptions wfo, List fes, List dims, var pre = FunctionCall(tok, Requires(dims.Count), Bpl.Type.Bool, args); var q = new Bpl.ForallExpr(tok, bvs, BplImp(ante, pre)); var indicesDesc = new PODesc.IndicesInDomain(forArray ? "array" : "sequence", dims, init); - builder.Add(AssertNS(tok, q, indicesDesc)); + builder.Add(AssertAndForget(tok, q, indicesDesc)); if (!forArray && options.DoReadsChecks) { // unwrap renamed local lambdas var unwrappedFunc = init; @@ -1691,7 +1716,7 @@ private void CheckElementInit(IToken tok, bool forArray, List dims, // assert (forall i0,i1,i2,... :: // 0 <= i0 < ... && ... ==> init.requires(i0,i1,i2,...) is Subtype); q = new Bpl.ForallExpr(tok, bvs, BplImp(ante, cre)); - builder.Add(AssertNS(init.tok, q, subrangeDesc)); + builder.Add(AssertAndForget(init.tok, q, subrangeDesc)); } if (forArray) { diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs index d2b73c28414..5784de766f8 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs @@ -108,7 +108,7 @@ public void Check(Function f) { 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)); + builder.Add(generator.Assert(generator.GetToken(e), wh, desc, builder.Context)); } } } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 380f149ef57..73f1e3acb41 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -605,7 +605,7 @@ private StmtList TrMethodContractWellformednessCheck(Method m, ExpressionTransla Boogie.Expr wh = GetWhereClause(e.tok, etran.TrExpr(e), e.Type, etran.Old, ISALLOC, true); if (wh != null) { var desc = new PODesc.IsAllocated("default value", "in the two-state lemma's previous state", e); - builder.Add(Assert(e.RangeToken, wh, desc)); + builder.Add(Assert(e.RangeToken, wh, desc, builder.Context)); } } } @@ -632,7 +632,7 @@ private StmtList TrMethodContractWellformednessCheck(Method m, ExpressionTransla } else { // etran.readsFrame being null indicates the default of reads *, // so this is an automatic failure. - builder.Add(Assert(m.tok, Expr.False, desc)); + builder.Add(Assert(m.tok, Expr.False, desc, builder.Context)); } } @@ -1137,7 +1137,7 @@ private void AddFunctionOverrideEnsChk(Function f, BoogieStmtListBuilder builder var constraint = allOverrideEns == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Imp, allOverrideEns, subEn); - builder.Add(Assert(f.tok, s.E, new PODesc.FunctionContractOverride(true, constraint))); + builder.Add(Assert(f.tok, s.E, new PODesc.FunctionContractOverride(true, constraint), builder.Context)); } } } @@ -1209,7 +1209,8 @@ private void AddFunctionOverrideSubsetChk(Function func, BoogieStmtListBuilder b Bpl.Expr consequent2 = InRWClause(tok, o, f, traitFrameExps, etran, null, null); Bpl.Expr q = new Bpl.ForallExpr(tok, new List(), new List { oVar, fVar }, BplImp(BplAnd(ante, oInCallee), consequent2)); - builder.Add(Assert(tok, q, new PODesc.TraitFrame(func.WhatKind, false, func.Reads.Expressions, traitFrameExps), kv)); + var description = new PODesc.TraitFrame(func.WhatKind, false, func.Reads.Expressions, traitFrameExps); + builder.Add(Assert(tok, q, description, builder.Context, kv)); } private void AddFunctionOverrideReqsChk(Function f, BoogieStmtListBuilder builder, ExpressionTranslator etran, @@ -1236,7 +1237,7 @@ private void AddFunctionOverrideReqsChk(Function f, BoogieStmtListBuilder builde var constraint = allTraitReqs == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Imp, allTraitReqs, req.E); - builder.Add(Assert(f.tok, s.E, new PODesc.FunctionContractOverride(false, constraint))); + builder.Add(Assert(f.tok, s.E, new PODesc.FunctionContractOverride(false, constraint), builder.Context)); } } } @@ -1484,7 +1485,7 @@ private void AddMethodOverrideEnsChk(Method m, BoogieStmtListBuilder builder, Ex var constraint = allOverrideEns == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Imp, allOverrideEns, subEn); - builder.Add(Assert(m.RangeToken, s.E, new PODesc.EnsuresStronger(constraint))); + builder.Add(Assert(m.RangeToken, s.E, new PODesc.EnsuresStronger(constraint), builder.Context)); } } } @@ -1513,7 +1514,7 @@ private void AddMethodOverrideReqsChk(Method m, BoogieStmtListBuilder builder, E var constraint = allTraitReqs == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Imp, allTraitReqs, req.E); - builder.Add(Assert(m.RangeToken, s.E, new PODesc.RequiresWeaker(constraint))); + builder.Add(Assert(m.RangeToken, s.E, new PODesc.RequiresWeaker(constraint), builder.Context)); } } } @@ -1597,7 +1598,7 @@ private void AddOverrideTerminationChk(ICallable original, ICallable overryd, Bo calleeDecreases, true); var desc = new PODesc.TraitDecreases(original.WhatKind, assertedExpr); - builder.Add(Assert(original.RangeToken, decrChk, desc)); + builder.Add(Assert(original.RangeToken, decrChk, desc, builder.Context)); } private void AddMethodOverrideFrameSubsetChk(Method m, bool isModifies, BoogieStmtListBuilder builder, ExpressionTranslator etran, List localVariables, @@ -1642,7 +1643,8 @@ private void AddMethodOverrideFrameSubsetChk(Method m, bool isModifies, BoogieSt var consequent2 = InRWClause(tok, o, f, traitFrameExps, etran, null, null); var q = new Boogie.ForallExpr(tok, new List(), new List { oVar, fVar }, BplImp(BplAnd(ante, oInCallee), consequent2)); - builder.Add(Assert(m.RangeToken, q, new PODesc.TraitFrame(m.WhatKind, isModifies, classFrameExps, traitFrameExps), kv)); + var description = new PODesc.TraitFrame(m.WhatKind, isModifies, classFrameExps, traitFrameExps); + builder.Add(Assert(m.RangeToken, q, description, builder.Context, kv)); } // Return a way to know if an assertion should be converted to an assumption diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs b/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs index e6d3f985a76..ef2c45ed6d0 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs @@ -30,7 +30,7 @@ using static Microsoft.Dafny.GenericErrors; namespace Microsoft.Dafny { - public record BodyTranslationContext(bool ContainsHide, int ScopeDepth = 0, bool ReturnPosition = true); + public record BodyTranslationContext(bool ContainsHide, int ScopeDepth = 0, bool ReturnPosition = true, AssertMode AssertMode = AssertMode.Keep); public partial class BoogieGenerator { diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs index 53ade4626fe..a3119698c44 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs @@ -652,7 +652,7 @@ private void GenerateAndCheckGuesses(IToken tok, List bvars, List>, Expression>> GeneratePartialGuesses(List bvars, Expression expression) { @@ -1242,13 +1242,13 @@ void PutSourceIntoLocal() { Bpl.Expr from = FunctionCall(tok, BuiltinFunction.RealToInt, null, o); Bpl.Expr e = FunctionCall(tok, BuiltinFunction.IntToReal, null, from); e = Bpl.Expr.Binary(tok, Bpl.BinaryOperator.Opcode.Eq, e, o); - builder.Add(Assert(tok, e, new PODesc.IsInteger(expr, errorMsgPrefix))); + builder.Add(Assert(tok, e, new PODesc.IsInteger(expr, errorMsgPrefix), builder.Context)); } if (fromType.IsBigOrdinalType && !toType.IsBigOrdinalType) { PutSourceIntoLocal(); Bpl.Expr boundsCheck = FunctionCall(tok, "ORD#IsNat", Bpl.Type.Bool, o); - builder.Add(Assert(tok, boundsCheck, new PODesc.ConversionIsNatural(errorMsgPrefix, expr))); + builder.Add(Assert(tok, boundsCheck, new PODesc.ConversionIsNatural(errorMsgPrefix, expr), builder.Context)); } if (toTypeFamily.IsBitVectorType) { @@ -1300,7 +1300,7 @@ void PutSourceIntoLocal() { } if (boundsCheck != null) { - builder.Add(Assert(tok, boundsCheck, new PODesc.ConversionFit("value", toType, dafnyBoundsCheck, errorMsgPrefix))); + builder.Add(Assert(tok, boundsCheck, new PODesc.ConversionFit("value", toType, dafnyBoundsCheck, errorMsgPrefix), builder.Context)); } } else if (toType.IsCharType) { @@ -1308,14 +1308,14 @@ void PutSourceIntoLocal() { PutSourceIntoLocal(); var boundsCheck = FunctionCall(Token.NoToken, BuiltinFunction.IsChar, null, o); var dafnyBoundsCheck = PODesc.Utils.MakeCharBoundsCheck(options, expr); - builder.Add(Assert(tok, boundsCheck, new PODesc.ConversionFit("value", toType, dafnyBoundsCheck, errorMsgPrefix))); + builder.Add(Assert(tok, boundsCheck, new PODesc.ConversionFit("value", toType, dafnyBoundsCheck, errorMsgPrefix), builder.Context)); } else if (fromType.IsNumericBased(Type.NumericPersuasion.Real)) { PutSourceIntoLocal(); var oi = FunctionCall(tok, BuiltinFunction.RealToInt, null, o); var boundsCheck = FunctionCall(Token.NoToken, BuiltinFunction.IsChar, null, oi); Expression intExpr = new ExprDotName(expr.tok, expr, "Floor", null); var dafnyBoundsCheck = PODesc.Utils.MakeCharBoundsCheck(options, intExpr); - builder.Add(Assert(tok, boundsCheck, new PODesc.ConversionFit("real value", toType, dafnyBoundsCheck, errorMsgPrefix))); + builder.Add(Assert(tok, boundsCheck, new PODesc.ConversionFit("real value", toType, dafnyBoundsCheck, errorMsgPrefix), builder.Context)); } else if (fromType.IsBitVectorType) { PutSourceIntoLocal(); var fromWidth = fromType.AsBitVectorType.Width; @@ -1328,7 +1328,7 @@ void PutSourceIntoLocal() { var boundsCheck = FunctionCall(expr.tok, "lt_bv" + fromWidth, Bpl.Type.Bool, o, bound); var dafnyBound = new BinaryExpr(expr.tok, BinaryExpr.Opcode.LeftShift, Expression.CreateIntLiteral(expr.tok, 1), Expression.CreateIntLiteral(expr.tok, toWidth)); var dafnyBoundsCheck = new BinaryExpr(expr.tok, BinaryExpr.Opcode.Lt, expr, dafnyBound); - builder.Add(Assert(tok, boundsCheck, new PODesc.ConversionFit("bit-vector value", toType, dafnyBoundsCheck, errorMsgPrefix))); + builder.Add(Assert(tok, boundsCheck, new PODesc.ConversionFit("bit-vector value", toType, dafnyBoundsCheck, errorMsgPrefix), builder.Context)); } } else if (fromType.IsBigOrdinalType) { PutSourceIntoLocal(); @@ -1340,7 +1340,7 @@ void PutSourceIntoLocal() { var dafnyBound = new BinaryExpr(expr.tok, BinaryExpr.Opcode.LeftShift, Expression.CreateIntLiteral(expr.tok, 1), Expression.CreateIntLiteral(expr.tok, toWidth)); var offset = new ExprDotName(expr.tok, expr, "Offset", null); var dafnyBoundsCheck = new BinaryExpr(expr.tok, BinaryExpr.Opcode.Lt, offset, dafnyBound); - builder.Add(Assert(tok, boundsCheck, new PODesc.ConversionFit("ORDINAL value", toType, dafnyBoundsCheck, errorMsgPrefix))); + builder.Add(Assert(tok, boundsCheck, new PODesc.ConversionFit("ORDINAL value", toType, dafnyBoundsCheck, errorMsgPrefix), builder.Context)); } } else if (toType.IsBigOrdinalType) { @@ -1348,7 +1348,7 @@ void PutSourceIntoLocal() { PutSourceIntoLocal(); Bpl.Expr boundsCheck = Bpl.Expr.Le(Bpl.Expr.Literal(0), o); var desc = new PODesc.ConversionPositive("integer", toType, expr, errorMsgPrefix); - builder.Add(Assert(tok, boundsCheck, desc)); + builder.Add(Assert(tok, boundsCheck, desc, builder.Context)); } if (fromType.IsNumericBased(Type.NumericPersuasion.Real)) { PutSourceIntoLocal(); @@ -1356,7 +1356,7 @@ void PutSourceIntoLocal() { Bpl.Expr boundsCheck = Bpl.Expr.Le(Bpl.Expr.Literal(0), oi); var intExpr = new ExprDotName(expr.tok, expr, "Floor", null); var desc = new PODesc.ConversionPositive("real", toType, intExpr, errorMsgPrefix); - builder.Add(Assert(tok, boundsCheck, desc)); + builder.Add(Assert(tok, boundsCheck, desc, builder.Context)); } } else if (toType.IsNumericBased(Type.NumericPersuasion.Int)) { @@ -1415,7 +1415,7 @@ void CheckResultToBeInType_Aux(IToken tok, Expression boogieExpr, Expression ori var typeMap = TypeParameter.SubstitutionMap(rdt.TypeArgs, udt.TypeArgs); var dafnyConstraint = Substitute(rdt.Constraint, null, new() { { rdt.Var, origExpr } }, typeMap); var boogieConstraint = etran.TrExpr(Substitute(rdt.Constraint, null, new() { { rdt.Var, boogieExpr } }, typeMap)); - builder.Add(Assert(tok, boogieConstraint, new PODesc.ConversionSatisfiesConstraints(errorMsgPrefix, kind, rdt.Name, dafnyConstraint))); + builder.Add(Assert(tok, boogieConstraint, new PODesc.ConversionSatisfiesConstraints(errorMsgPrefix, kind, rdt.Name, dafnyConstraint), builder.Context)); } } @@ -1531,7 +1531,7 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { var witness = Zero(decl.tok, baseType); if (witness == null) { witnessString = ""; - witnessCheckBuilder.Add(Assert(decl.tok, Bpl.Expr.False, new PODesc.WitnessCheck(witnessString))); + witnessCheckBuilder.Add(Assert(decl.tok, Bpl.Expr.False, new PODesc.WitnessCheck(witnessString), builder.Context)); } else { // before trying 0 as a witness, check that 0 can be assigned to baseType witnessString = Printer.ExprToString(options, witness); @@ -1571,12 +1571,12 @@ private void SplitAndAssertExpression(BoogieStmtListBuilder witnessCheckBuilder, var ss = TrSplitExpr(context, witnessExpr, etran, true, out var splitHappened); if (!splitHappened) { - witnessCheckBuilder.Add(Assert(witnessExpr.tok, etran.TrExpr(witnessExpr), desc)); + witnessCheckBuilder.Add(Assert(witnessExpr.tok, etran.TrExpr(witnessExpr), desc, context)); } else { foreach (var split in ss) { if (split.IsChecked) { var tok = witnessExpr.tok is { } t ? new NestedToken(t, split.Tok) : witnessExpr.tok; - witnessCheckBuilder.Add(AssertNS(tok, split.E, desc)); + witnessCheckBuilder.Add(AssertAndForget(tok, split.E, desc)); } } } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index 0652b8baeb0..2103ab76752 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -1760,7 +1760,6 @@ Bpl.Expr InSeqRange(IToken tok, Bpl.Expr index, Type indexType, Bpl.Expr seq, bo public Dictionary DefiniteAssignmentTrackers { get; } = new(); - bool assertAsAssume = false; // generate assume statements instead of assert statements Func assertionOnlyFilter = null; // generate assume statements instead of assert statements if not targeted by {:only} public enum StmtType { NONE, ASSERT, ASSUME }; public StmtType stmtContext = StmtType.NONE; // the Statement that is currently being translated @@ -2091,7 +2090,7 @@ public void CheckFrameSubset(IToken tok, List calleeFrame, PODesc.ProofObligationDescription desc, Bpl.QKeyValue kv) { CheckFrameSubset(tok, calleeFrame, receiverReplacement, substMap, etran, enclosingFrame, - (t, e, d, q) => builder.Add(Assert(t, e, d, q)), desc, kv); + (t, e, d, q) => builder.Add(Assert(t, e, d, builder.Context, q)), desc, kv); } void CheckFrameSubset(IToken tok, List calleeFrame, @@ -2148,7 +2147,7 @@ void CheckFrameEmpty(IToken tok, if (IsExprAlways(q, true)) { return; } - builder.Add(Assert(tok, q, desc, kv)); + builder.Add(Assert(tok, q, desc, builder.Context, kv)); } /// @@ -2296,82 +2295,6 @@ Bpl.Expr InRWClause_Aux(IToken tok, Bpl.Expr o, Bpl.Expr boxO, Bpl.Expr f, List< return disjunction; } - void AddWellformednessCheck(DatatypeCtor ctor) { - Contract.Requires(ctor != null); - Contract.Requires(sink != null && predef != null); - Contract.Requires(currentModule == null && codeContext == null && isAllocContext == null && fuelContext == null); - Contract.Ensures(currentModule == null && codeContext == null && isAllocContext == null && fuelContext == null); - - proofDependencies.SetCurrentDefinition(MethodVerboseName(ctor.FullName, MethodTranslationKind.SpecWellformedness)); - - if (!InVerificationScope(ctor)) { - // Checked in other file - return; - } - - // If there are no parameters with default values, there's nothing to do - if (ctor.Formals.TrueForAll(f => f.DefaultValue == null)) { - return; - } - - currentModule = ctor.EnclosingDatatype.EnclosingModuleDefinition; - codeContext = ctor.EnclosingDatatype; - fuelContext = FuelSetting.NewFuelContext(ctor.EnclosingDatatype); - var etran = new ExpressionTranslator(this, predef, ctor.tok, null); - - // parameters of the procedure - List inParams = MkTyParamFormals(GetTypeParams(ctor.EnclosingDatatype), true); - foreach (var p in ctor.Formals) { - Bpl.Type varType = TrType(p.Type); - Bpl.Expr wh = GetWhereClause(p.tok, new Bpl.IdentifierExpr(p.tok, p.AssignUniqueName(ctor.IdGenerator), varType), p.Type, etran, NOALLOC); - inParams.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.AssignUniqueName(ctor.IdGenerator), varType, wh), true)); - } - - // the procedure itself - var req = new List(); - // free requires mh == ModuleContextHeight && fh == TypeContextHeight; - req.Add(Requires(ctor.tok, true, null, etran.HeightContext(ctor.EnclosingDatatype), null, null, null)); - var heapVar = new Bpl.IdentifierExpr(ctor.tok, "$Heap", false); - var varlist = new List { heapVar }; - var proc = new Bpl.Procedure(ctor.tok, "CheckWellformed" + NameSeparator + ctor.FullName, new List(), - inParams, new List(), - false, req, varlist, new List(), etran.TrAttributes(ctor.Attributes, null)); - AddVerboseNameAttribute(proc, ctor.FullName, MethodTranslationKind.SpecWellformedness); - sink.AddTopLevelDeclaration(proc); - - var implInParams = Bpl.Formal.StripWhereClauses(inParams); - var locals = new List(); - var builder = new BoogieStmtListBuilder(this, options, new BodyTranslationContext(false)); - builder.Add(new CommentCmd(string.Format("AddWellformednessCheck for datatype constructor {0}", ctor))); - builder.AddCaptureState(ctor.tok, false, "initial state"); - isAllocContext = new IsAllocContext(options, true); - - DefineFrame(ctor.tok, etran.ReadsFrame(ctor.tok), new List(), builder, locals, null); - - // check well-formedness of each default-value expression - foreach (var formal in ctor.Formals.Where(formal => formal.DefaultValue != null)) { - var e = formal.DefaultValue; - CheckWellformedWithResult(e, new WFOptions(null, true, - false, true), locals, builder, etran, (returnBuilder, result) => { - builder.Add(new Bpl.AssumeCmd(e.tok, etran.CanCallAssumption(e))); - CheckSubrange(result.tok, etran.TrExpr(result), e.Type, formal.Type, e, returnBuilder); - }); - } - - if (EmitImplementation(ctor.Attributes)) { - // emit the impl only when there are proof obligations. - QKeyValue kv = etran.TrAttributes(ctor.Attributes, null); - var implBody = builder.Collect(ctor.tok); - AddImplementationWithAttributes(GetToken(ctor), proc, implInParams, - new List(), locals, implBody, kv); - } - - Contract.Assert(currentModule == ctor.EnclosingDatatype.EnclosingModuleDefinition); - Contract.Assert(codeContext == ctor.EnclosingDatatype); - isAllocContext = null; - fuelContext = null; - Reset(); - } /// /// If "declareLocals" is "false", then the locals are added only if they are new, that is, if @@ -2518,7 +2441,7 @@ void CheckCasePatternShape(CasePattern pat, Expression dRhs, Bpl.Expr rh // There is only one constructor, so the value must have been constructed by it; might as well assume that here. builder.Add(TrAssumeCmd(pat.tok, correctConstructor)); } else { - builder.Add(Assert(pat.tok, correctConstructor, new PODesc.PatternShapeIsValid(dRhs, ctor.Name))); + builder.Add(Assert(pat.tok, correctConstructor, new PODesc.PatternShapeIsValid(dRhs, ctor.Name), builder.Context)); } for (int i = 0; i < pat.Arguments.Count; i++) { var arg = pat.Arguments[i]; @@ -2546,14 +2469,15 @@ void CheckNonNull(IToken tok, Expression e, BoogieStmtListBuilder builder, Expre } else if (e is StaticReceiverExpr) { // also ok } else { - builder.Add(Assert(tok, Bpl.Expr.Neq(etran.TrExpr(e), predef.Null), new PODesc.NonNull("target object", e), kv)); + builder.Add(Assert(tok, Bpl.Expr.Neq(etran.TrExpr(e), predef.Null), + new PODesc.NonNull("target object", e), builder.Context, kv)); } } void CheckFunctionSelectWF(string what, BoogieStmtListBuilder builder, ExpressionTranslator etran, Expression e, string hint) { if (e is MemberSelectExpr sel && sel.Member is Function fn) { Bpl.Expr assertion = !InVerificationScope(fn) ? Bpl.Expr.True : Bpl.Expr.Not(etran.HeightContext(fn)); - builder.Add(Assert(GetToken(e), assertion, new PODesc.ValidInRecursion(what, hint))); + builder.Add(Assert(GetToken(e), assertion, new PODesc.ValidInRecursion(what, hint), builder.Context)); } } @@ -3414,20 +3338,21 @@ public override IToken WithVal(string newVal) { } } - public Bpl.PredicateCmd Assert(IToken tok, Bpl.Expr condition, PODesc.ProofObligationDescription description, Bpl.QKeyValue kv = null) { - var cmd = Assert(tok, condition, description, tok, kv); - return cmd; + public Bpl.PredicateCmd Assert(IToken tok, Bpl.Expr condition, PODesc.ProofObligationDescription description, + BodyTranslationContext context, Bpl.QKeyValue kv = null) { + return Assert(tok, condition, description, tok, context, kv); } - Bpl.PredicateCmd Assert(IToken tok, Bpl.Expr condition, PODesc.ProofObligationDescription description, IToken refinesToken, Bpl.QKeyValue kv = null) { + private PredicateCmd Assert(IToken tok, Expr condition, PODesc.ProofObligationDescription description, + IToken refinesToken, BodyTranslationContext context, QKeyValue kv = null) { Contract.Requires(tok != null); Contract.Requires(condition != null); Contract.Ensures(Contract.Result() != null); Bpl.PredicateCmd cmd; - if (assertAsAssume + if (context.AssertMode == AssertMode.Assume || (assertionOnlyFilter != null && !assertionOnlyFilter(tok)) - || (RefinementToken.IsInherited(refinesToken, currentModule) && (codeContext == null || !codeContext.MustReverify))) { + || (RefinementToken.IsInherited(refinesToken, currentModule) && codeContext is not { MustReverify: true })) { // produce an assume instead cmd = TrAssumeCmd(tok, condition, kv); proofDependencies?.AddProofDependencyId(cmd, tok, new AssumedProofObligationDependency(tok, description)); @@ -3438,11 +3363,11 @@ Bpl.PredicateCmd Assert(IToken tok, Bpl.Expr condition, PODesc.ProofObligationDe return cmd; } - Bpl.PredicateCmd AssertNS(IToken tok, Bpl.Expr condition, PODesc.ProofObligationDescription desc) { - return AssertNS(tok, condition, desc, tok, null); + Bpl.PredicateCmd AssertAndForget(IToken tok, Bpl.Expr condition, PODesc.ProofObligationDescription desc) { + return AssertAndForget(tok, condition, desc, tok, null); } - Bpl.PredicateCmd AssertNS(IToken tok, Bpl.Expr condition, PODesc.ProofObligationDescription desc, IToken refinesTok, Bpl.QKeyValue kv) { + Bpl.PredicateCmd AssertAndForget(IToken tok, Bpl.Expr condition, PODesc.ProofObligationDescription desc, IToken refinesTok, Bpl.QKeyValue kv) { Contract.Requires(tok != null); Contract.Requires(desc != null); Contract.Requires(condition != null); @@ -4091,7 +4016,7 @@ void CheckSubrange(IToken tok, Bpl.Expr bSource, Type sourceType, Type targetTyp var cre = GetSubrangeCheck(tok, bSource, sourceType, targetType, source, null, out var desc, errorMsgPrefix); if (cre != null) { - builder.Add(Assert(tok, cre, desc)); + builder.Add(Assert(tok, cre, desc, builder.Context)); } } @@ -4109,7 +4034,7 @@ void Check_NewRestrictions(IToken tok, Expression dObj, Bpl.Expr obj, Field f, B var fId = new Bpl.IdentifierExpr(tok, GetField(f)); var subset = FunctionCall(tok, BuiltinFunction.SetSubset, null, rhs, ApplyUnbox(tok, ReadHeap(tok, etran.HeapExpr, obj, fId), predef.SetType)); - builder.Add(Assert(tok, subset, new PODesc.AssignmentShrinks(dObj, f.Name))); + builder.Add(Assert(tok, subset, new PODesc.AssignmentShrinks(dObj, f.Name), builder.Context)); } } @@ -4945,4 +4870,7 @@ public Expr GetRevealConstant(Function f) { return this.functionReveals[f]; } } + + public enum AssertMode { Keep, Assume, Check } } + diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.DataTypes.cs b/Source/DafnyCore/Verifier/Datatypes/BoogieGenerator.DataTypes.cs similarity index 90% rename from Source/DafnyCore/Verifier/BoogieGenerator.DataTypes.cs rename to Source/DafnyCore/Verifier/Datatypes/BoogieGenerator.DataTypes.cs index 57a92cc6779..91de9daa3ea 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.DataTypes.cs +++ b/Source/DafnyCore/Verifier/Datatypes/BoogieGenerator.DataTypes.cs @@ -786,12 +786,89 @@ private void AddsIsConstructorAxiom(DatatypeCtor ctor, Bpl.Function ctorFunction private Axiom CreateConstructorIdentifierAxiom(DatatypeCtor ctor, Expr c) { // Add: axiom (forall params :: DatatypeCtorId(#dt.ctor(params)) == ##dt.ctor); CreateBoundVariables(ctor.Formals, out var bvs, out var args); - var constructor_call = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); - var lhs = FunctionCall(ctor.tok, BuiltinFunction.DatatypeCtorId, null, constructor_call); + var constructorCall = FunctionCall(ctor.tok, ctor.FullName, predef.DatatypeType, args); + var lhs = FunctionCall(ctor.tok, BuiltinFunction.DatatypeCtorId, null, constructorCall); Bpl.Expr q = Bpl.Expr.Eq(lhs, c); - var trigger = BplTrigger(constructor_call); + var trigger = BplTrigger(constructorCall); var axiom = new Bpl.Axiom(ctor.tok, BplForall(bvs, trigger, q), "Constructor identifier"); return axiom; } + + void AddWellformednessCheck(DatatypeCtor ctor) { + Contract.Requires(ctor != null); + Contract.Requires(sink != null && predef != null); + Contract.Requires(currentModule == null && codeContext == null && isAllocContext == null && fuelContext == null); + Contract.Ensures(currentModule == null && codeContext == null && isAllocContext == null && fuelContext == null); + + proofDependencies.SetCurrentDefinition(MethodVerboseName(ctor.FullName, MethodTranslationKind.SpecWellformedness)); + + if (!InVerificationScope(ctor)) { + // Checked in other file + return; + } + + // If there are no parameters with default values, there's nothing to do + if (ctor.Formals.TrueForAll(f => f.DefaultValue == null)) { + return; + } + + currentModule = ctor.EnclosingDatatype.EnclosingModuleDefinition; + codeContext = ctor.EnclosingDatatype; + fuelContext = FuelSetting.NewFuelContext(ctor.EnclosingDatatype); + var etran = new ExpressionTranslator(this, predef, ctor.tok, null); + + // parameters of the procedure + List inParams = MkTyParamFormals(GetTypeParams(ctor.EnclosingDatatype), true); + foreach (var p in ctor.Formals) { + Bpl.Type varType = TrType(p.Type); + Bpl.Expr wh = GetWhereClause(p.tok, new Bpl.IdentifierExpr(p.tok, p.AssignUniqueName(ctor.IdGenerator), varType), p.Type, etran, NOALLOC); + inParams.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.AssignUniqueName(ctor.IdGenerator), varType, wh), true)); + } + + // the procedure itself + var req = new List(); + // free requires mh == ModuleContextHeight && fh == TypeContextHeight; + req.Add(Requires(ctor.tok, true, null, etran.HeightContext(ctor.EnclosingDatatype), null, null, null)); + var heapVar = new Bpl.IdentifierExpr(ctor.tok, "$Heap", false); + var varlist = new List { heapVar }; + var proc = new Bpl.Procedure(ctor.tok, "CheckWellformed" + NameSeparator + ctor.FullName, new List(), + inParams, new List(), + false, req, varlist, new List(), etran.TrAttributes(ctor.Attributes, null)); + AddVerboseNameAttribute(proc, ctor.FullName, MethodTranslationKind.SpecWellformedness); + sink.AddTopLevelDeclaration(proc); + + var implInParams = Bpl.Formal.StripWhereClauses(inParams); + var locals = new List(); + var builder = new BoogieStmtListBuilder(this, options, new BodyTranslationContext(false)); + builder.Add(new CommentCmd($"AddWellformednessCheck for datatype constructor {ctor}")); + builder.AddCaptureState(ctor.tok, false, "initial state"); + isAllocContext = new IsAllocContext(options, true); + + DefineFrame(ctor.tok, etran.ReadsFrame(ctor.tok), new List(), builder, locals, null); + + // check well-formedness of each default-value expression + foreach (var formal in ctor.Formals.Where(formal => formal.DefaultValue != null)) { + var e = formal.DefaultValue; + CheckWellformedWithResult(e, new WFOptions(null, true, + false, true), locals, builder, etran, (returnBuilder, result) => { + builder.Add(new Bpl.AssumeCmd(e.tok, etran.CanCallAssumption(e))); + CheckSubrange(result.tok, etran.TrExpr(result), e.Type, formal.Type, e, returnBuilder); + }); + } + + if (EmitImplementation(ctor.Attributes)) { + // emit the impl only when there are proof obligations. + QKeyValue kv = etran.TrAttributes(ctor.Attributes, null); + var implBody = builder.Collect(ctor.tok); + AddImplementationWithAttributes(GetToken(ctor), proc, implInParams, + new List(), locals, implBody, kv); + } + + Contract.Assert(currentModule == ctor.EnclosingDatatype.EnclosingModuleDefinition); + Contract.Assert(codeContext == ctor.EnclosingDatatype); + isAllocContext = null; + fuelContext = null; + Reset(); + } } } diff --git a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs new file mode 100644 index 00000000000..58ccea0705c --- /dev/null +++ b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using Microsoft.Boogie; +using Microsoft.Dafny; + +namespace DafnyCore.Verifier.Statements; + +public class BlockByProofStmtVerifier { + public static void EmitBoogie(BoogieGenerator generator, BlockByProofStmt block, BoogieStmtListBuilder builder, + List locals, BoogieGenerator.ExpressionTranslator etran, ICodeContext codeContext) { + var proofBuilder = new BoogieStmtListBuilder(generator, builder.Options, builder.Context); + generator.TrStmtList(block.Proof.Body, proofBuilder, locals, etran); + generator.TrStmt(block.Body, proofBuilder, locals, etran); + generator.PathAsideBlock(block.Tok, proofBuilder, builder); + generator.TrStmt(block.Body, builder.WithContext(builder.Context with { + AssertMode = AssertMode.Assume + }), locals, etran); + } +} \ No newline at end of file diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.TrPredicateStatement.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs similarity index 95% rename from Source/DafnyCore/Verifier/BoogieGenerator.TrPredicateStatement.cs rename to Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs index 4dd4e4c0ce8..ed227805b25 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.TrPredicateStatement.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs @@ -92,7 +92,7 @@ public void TrAssertStmt(PredicateStmt stmt, BoogieStmtListBuilder builder, List } proofBuilder ??= b; - var splitHappened = TrAssertCondition(stmt, builder, etran, proofBuilder); + var splitHappened = TrAssertCondition(stmt, etran, proofBuilder); if (hiddenProof) { PathAsideBlock(stmt.Tok, proofBuilder, b); @@ -153,7 +153,7 @@ public void TrAssertStmt(PredicateStmt stmt, BoogieStmtListBuilder builder, List } } - private bool TrAssertCondition(PredicateStmt stmt, BoogieStmtListBuilder builder, + private bool TrAssertCondition(PredicateStmt stmt, ExpressionTranslator etran, BoogieStmtListBuilder proofBuilder) { IToken enclosingToken = null; if (Attributes.Contains(stmt.Attributes, "_prependAssertToken")) { @@ -161,18 +161,18 @@ private bool TrAssertCondition(PredicateStmt stmt, BoogieStmtListBuilder builder } var (errorMessage, successMessage) = CustomErrorMessage(stmt.Attributes); - var splits = TrSplitExpr(builder.Context, stmt.Expr, etran, true, out var splitHappened); + var splits = TrSplitExpr(proofBuilder.Context, stmt.Expr, etran, true, out var splitHappened); if (!splitHappened) { var tok = enclosingToken == null ? GetToken(stmt.Expr) : new NestedToken(enclosingToken, GetToken(stmt.Expr)); var desc = new PODesc.AssertStatementDescription(stmt, errorMessage, successMessage); - proofBuilder.Add(Assert(tok, etran.TrExpr(stmt.Expr), desc, stmt.Tok, + proofBuilder.Add(Assert(tok, etran.TrExpr(stmt.Expr), desc, stmt.Tok, proofBuilder.Context, etran.TrAttributes(stmt.Attributes, null))); } else { foreach (var split in splits) { if (split.IsChecked) { var tok = enclosingToken == null ? split.E.tok : new NestedToken(enclosingToken, split.Tok); var desc = new PODesc.AssertStatementDescription(stmt, errorMessage, successMessage); - proofBuilder.Add(AssertNS(ToDafnyToken(flags.ReportRanges, tok), split.E, desc, stmt.Tok, + proofBuilder.Add(AssertAndForget(ToDafnyToken(flags.ReportRanges, tok), split.E, desc, stmt.Tok, etran.TrAttributes(stmt.Attributes, null))); // attributes go on every split } } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs similarity index 98% rename from Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs rename to Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs index c166e512b95..20243141035 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs @@ -3,6 +3,7 @@ using System.Diagnostics.Contracts; using System.Linq; using DafnyCore.Verifier; +using DafnyCore.Verifier.Statements; using Microsoft.Boogie; using Bpl = Microsoft.Boogie; using BplParser = Microsoft.Boogie.Parser; @@ -14,7 +15,7 @@ namespace Microsoft.Dafny { public partial class BoogieGenerator { public const string FrameVariablePrefix = "$Frame$"; - private void TrStmt(Statement stmt, BoogieStmtListBuilder builder, + public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { stmt.ScopeDepth = builder.Context.ScopeDepth; @@ -131,7 +132,7 @@ private void TrStmt(Statement stmt, BoogieStmtListBuilder builder, } else if (split.IsChecked) { var yieldToken = new NestedToken(s.Tok, split.Tok); var desc = new PODesc.YieldEnsures(fieldSub.Substitute(p.E)); - builder.Add(AssertNS(yieldToken, split.E, desc, stmt.Tok, null)); + builder.Add(AssertAndForget(yieldToken, split.E, desc, stmt.Tok, null)); } } builder.Add(TrAssumeCmdWithDependencies(yeEtran, stmt.Tok, p.E, "yield ensures clause")); @@ -290,6 +291,8 @@ private void TrStmt(Statement stmt, BoogieStmtListBuilder builder, } else if (stmt is OpaqueBlock opaqueBlock) { OpaqueBlockVerifier.EmitBoogie(this, opaqueBlock, builder, locals, etran, (IMethodCodeContext)codeContext); + } else if (stmt is BlockByProofStmt blockByProof) { + BlockByProofStmtVerifier.EmitBoogie(this, blockByProof, builder, locals, etran, codeContext); } else if (stmt is BlockStmt blockStmt) { var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; TrStmtList(blockStmt.Body, builder, locals, etran, blockStmt.RangeToken); @@ -300,7 +303,7 @@ private void TrStmt(Statement stmt, BoogieStmtListBuilder builder, } else if (stmt is AlternativeStmt) { AddComment(builder, stmt, "alternative statement"); var s = (AlternativeStmt)stmt; - var elseCase = Assert(s.Tok, Bpl.Expr.False, new PODesc.AlternativeIsComplete()); + var elseCase = Assert(s.Tok, Bpl.Expr.False, new PODesc.AlternativeIsComplete(), builder.Context); TrAlternatives(s.Alternatives, s.Tok, b => b.Add(elseCase), builder, locals, etran, stmt.IsGhost); } else if (stmt is WhileStmt whileStmt) { @@ -637,9 +640,9 @@ private void TrCalcStmt(CalcStmt stmt, BoogieStmtListBuilder builder, List]: AddComment(b, stmt, "assume wf[lhs]"); CurrentIdGenerator.Push(); - assertAsAssume = true; - TrStmt_CheckWellformed(CalcStmt.Lhs(stmt.Steps[i]), b, locals, etran, false); - assertAsAssume = false; + TrStmt_CheckWellformed(CalcStmt.Lhs(stmt.Steps[i]), b.WithContext(b.Context with { + AssertMode = AssertMode.Assume + }), locals, etran, false); if (stmt.Steps[i] is BinaryExpr && (((BinaryExpr)stmt.Steps[i]).ResolvedOp == BinaryExpr.ResolvedOpcode.Imp)) { // assume line: AddComment(b, stmt, "assume lhs"); @@ -659,7 +662,7 @@ private void TrCalcStmt(CalcStmt stmt, BoogieStmtListBuilder builder, List Is(x, typ) { @@ -885,7 +888,7 @@ private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, List { indexVar }, dafnyRange, new TypeTestExpr(indexVar.tok, dIndex, indexVar.Type), null); - builder.Add(Assert(tok, cre, new PODesc.ForRangeAssignable(desc, dafnyAssertion))); + builder.Add(Assert(tok, cre, new PODesc.ForRangeAssignable(desc, dafnyAssertion), builder.Context)); } } @@ -1067,7 +1070,7 @@ void TrForallProof(ForallStmt forallStmt, BoogieStmtListBuilder definedness, Boo foreach (var ens in forallStmt.Ens) { foreach (var split in TrSplitExpr(definedness.Context, ens.E, etran, true, out var splitHappened)) { if (split.IsChecked) { - definedness.Add(Assert(split.Tok, split.E, new PODesc.ForallPostcondition(ens.E))); + definedness.Add(Assert(split.Tok, split.E, new PODesc.ForallPostcondition(ens.E), definedness.Context)); } } } @@ -1201,7 +1204,7 @@ void TrForallAssign(ForallStmt s, AssignStmt s0, }; var desc = new PODesc.Modifiable(description, GetContextModifiesFrames(), lhsObj, lhsField); definedness.Add(Assert(lhs.tok, Bpl.Expr.SelectTok(lhs.tok, etran.ModifiesFrame(lhs.tok), obj, F), - desc)); + desc, definedness.Context)); if (s0.Rhs is ExprRhs) { var r = (ExprRhs)s0.Rhs; var rhs = Substitute(r.Expr, null, substMap); @@ -1254,7 +1257,7 @@ void TrForallAssign(ForallStmt s, AssignStmt s0, BplOr( BplOr(Bpl.Expr.Neq(obj, objPrime), Bpl.Expr.Neq(F, FPrime)), Bpl.Expr.Eq(rhs, rhsPrime)), - new PODesc.ForallLHSUnique(s.BoundVars, s.Range, lhsComponents, Rhs))); + new PODesc.ForallLHSUnique(s.BoundVars, s.Range, lhsComponents, Rhs), definedness.Context)); } definedness.Add(TrAssumeCmd(s.Tok, Bpl.Expr.False)); @@ -1424,12 +1427,12 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, var ss = TrSplitExpr(builder.Context, loopInv.E, etran, false, out var splitHappened); if (!splitHappened) { var wInv = BplImp(w, etran.TrExpr(loopInv.E)); - invariants.Add(Assert(loopInv.E.tok, wInv, new PODesc.LoopInvariant(loopInv.E, errorMessage, successMessage))); + invariants.Add(Assert(loopInv.E.tok, wInv, new PODesc.LoopInvariant(loopInv.E, errorMessage, successMessage), builder.Context)); } else { foreach (var split in ss) { var wInv = Bpl.Expr.Binary(split.E.tok, BinaryOperator.Opcode.Imp, w, split.E); if (split.IsChecked) { - invariants.Add(Assert(split.Tok, wInv, new PODesc.LoopInvariant(loopInv.E, errorMessage, successMessage))); // TODO: it would be fine to have this use {:subsumption 0} + invariants.Add(Assert(split.Tok, wInv, new PODesc.LoopInvariant(loopInv.E, errorMessage, successMessage), builder.Context)); // TODO: it would be fine to have this use {:subsumption 0} } else { var cmd = TrAssumeCmd(split.E.tok, wInv); proofDependencies?.AddProofDependencyId(cmd, loopInv.E.tok, new InvariantDependency(loopInv.E)); @@ -1457,7 +1460,7 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, invariants.Add(TrAssumeCmd(s.Tok, tri.Expr)); } else { Contract.Assert(tri.ErrorMessage != null); // follows from BoilerplateTriple invariant - invariants.Add(Assert(s.Tok, tri.Expr, new PODesc.BoilerplateTriple(tri.ErrorMessage, tri.SuccessMessage, tri.Comment))); + invariants.Add(Assert(s.Tok, tri.Expr, new PODesc.BoilerplateTriple(tri.ErrorMessage, tri.SuccessMessage, tri.Comment), builder.Context)); } } // add a free invariant which says that the heap hasn't changed outside of the modifies clause. @@ -1554,8 +1557,9 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, AddComment(loopBodyBuilder, s, "loop termination check"); Bpl.Expr decrCheck = DecreasesCheck(toks, prevGhostLocals, decrsDafny, initDecrsDafny, decrs, oldBfs, loopBodyBuilder, " at end of loop iteration", false, false); - loopBodyBuilder.Add(Assert(s.Tok, decrCheck, new - PODesc.Terminates(s.InferredDecreases, prevGhostLocals, null, initDecrsDafny, theDecreases, false))); + var description = new + PODesc.Terminates(s.InferredDecreases, prevGhostLocals, null, initDecrsDafny, theDecreases, false); + loopBodyBuilder.Add(Assert(s.Tok, decrCheck, description, builder.Context)); } } } else if (isBodyLessLoop) { @@ -1900,7 +1904,7 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E Bpl.Expr wh = GetWhereClause(receiver.tok, etran.TrExpr(receiver), receiver.Type, etran, ISALLOC, true); if (wh != null) { var desc = new PODesc.IsAllocated("receiver argument", "in the state in which the method is invoked", receiver); - builder.Add(Assert(receiver.tok, wh, desc)); + builder.Add(Assert(receiver.tok, wh, desc, builder.Context)); } } for (int i = 0; i < Args.Count; i++) { @@ -1908,7 +1912,7 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E Bpl.Expr wh = GetWhereClause(ee.tok, etran.TrExpr(ee), ee.Type, etran, ISALLOC, true); if (wh != null) { var desc = new PODesc.IsAllocated("argument", "in the state in which the method is invoked", ee); - builder.Add(Assert(ee.tok, wh, desc)); + builder.Add(Assert(ee.tok, wh, desc, builder.Context)); } } } else if (method is TwoStateLemma) { @@ -1916,7 +1920,7 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E Bpl.Expr wh = GetWhereClause(receiver.tok, etran.TrExpr(receiver), receiver.Type, etran.OldAt(atLabel), ISALLOC, true); if (wh != null) { var desc = new PODesc.IsAllocated("receiver argument", "in the two-state lemma's previous state", receiver, atLabel); - builder.Add(Assert(receiver.tok, wh, desc)); + builder.Add(Assert(receiver.tok, wh, desc, builder.Context)); } } Contract.Assert(callee.Ins.Count == Args.Count); @@ -1933,7 +1937,7 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E ee, atLabel ); - builder.Add(Assert(ee.tok, wh, desc)); + builder.Add(Assert(ee.tok, wh, desc, builder.Context)); } } } @@ -1975,7 +1979,7 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E if (isRecursiveCall) { Contract.Assert(codeContext != null); if (codeContext is DatatypeDecl) { - builder.Add(Assert(tok, Bpl.Expr.False, new PODesc.IsNonRecursive())); + builder.Add(Assert(tok, Bpl.Expr.False, new PODesc.IsNonRecursive(), builder.Context)); } else { List contextDecreases = codeContext.Decreases.Expressions; List calleeDecreases = callee.Decreases.Expressions; @@ -2388,7 +2392,7 @@ void AssertDistinctness(Expression lhsa, Expression lhsb, BoogieStmtListBuilder CheckDistinctness(lhsa, lhsb, etran, out var dExpr, out var bExpr); if (bExpr != null) { builder.Add(Assert(GetToken(lhsa), bExpr, new PODesc.DistinctLHS(Printer.ExprToString(options, lhsa), - Printer.ExprToString(options, lhsb), bExpr != Bpl.Expr.False, false, dExpr))); + Printer.ExprToString(options, lhsb), bExpr != Bpl.Expr.False, false, dExpr), builder.Context)); } } @@ -2397,7 +2401,7 @@ void AssertDistinctness(Expression lhsa, Expression lhsb, Bpl.Expr rhsa, Bpl.Exp if (bExpr != null) { bExpr = BplOr(bExpr, Bpl.Expr.Eq(rhsa, rhsb)); builder.Add(Assert(GetToken(lhsa), bExpr, new PODesc.DistinctLHS(Printer.ExprToString(options, lhsa), - Printer.ExprToString(options, lhsb), false, true, dExpr))); + Printer.ExprToString(options, lhsb), false, true, dExpr), builder.Context)); } } @@ -2484,7 +2488,7 @@ void ProcessLhss(List lhss, bool rhsCanAffectPreviouslyKnownExpressi if (!useSurrogateLocal) { // check that the enclosing modifies clause allows this object to be written: assert $_ModifiesFrame[obj]); var desc = new PODesc.Modifiable("an object", contextModFrames, fse.Obj, field); - builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.ModifiesFrame(tok), obj, GetField(fse)), desc)); + builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.ModifiesFrame(tok), obj, GetField(fse)), desc, builder.Context)); } if (useSurrogateLocal) { @@ -2534,7 +2538,7 @@ void ProcessLhss(List lhss, bool rhsCanAffectPreviouslyKnownExpressi prevIndex[i] = fieldName; // check that the enclosing modifies clause allows this object to be written: assert $_Frame[obj,index]); var desc = new PODesc.Modifiable("an array element", contextModFrames, sel.Seq, null); - builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.ModifiesFrame(tok), obj, fieldName), desc)); + builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.ModifiesFrame(tok), obj, fieldName), desc, builder.Context)); bLhss.Add(null); lhsBuilders.Add(delegate (Bpl.Expr rhs, bool origRhsIsHavoc, BoogieStmtListBuilder bldr, ExpressionTranslator et) { @@ -2559,7 +2563,7 @@ void ProcessLhss(List lhss, bool rhsCanAffectPreviouslyKnownExpressi prevObj[i] = obj; prevIndex[i] = fieldName; var desc = new PODesc.Modifiable("an array element", contextModFrames, mse.Array, null); - builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.ModifiesFrame(tok), obj, fieldName), desc)); + builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.ModifiesFrame(tok), obj, fieldName), desc, builder.Context)); bLhss.Add(null); lhsBuilders.Add(delegate (Bpl.Expr rhs, bool origRhsIsHavoc, BoogieStmtListBuilder bldr, ExpressionTranslator et) { @@ -2648,7 +2652,7 @@ Bpl.Expr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bGivenLhs, IVariable lhs TrStmt_CheckWellformed(e.Expr, builder, locals, etran, true, addResultCommands: (returnBuilder, result) => { if (cre != null) { - returnBuilder.Add(Assert(result.Tok, cre, desc)); + returnBuilder.Add(Assert(result.Tok, cre, desc, builder.Context)); } }); @@ -2685,7 +2689,7 @@ Bpl.Expr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bGivenLhs, IVariable lhs CheckWellformed(dim, new WFOptions(), locals, builder, etran); var desc = new PODesc.NonNegative(tRhs.ArrayDimensions.Count == 1 ? "array size" : $"array size (dimension {i})", dim); - builder.Add(Assert(GetToken(dim), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(dim)), desc)); + builder.Add(Assert(GetToken(dim), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(dim)), desc, builder.Context)); i++; } if (tRhs.ElementInit != null) { @@ -2693,7 +2697,7 @@ Bpl.Expr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bGivenLhs, IVariable lhs } else if (tRhs.InitDisplay != null) { var dim = tRhs.ArrayDimensions[0]; var desc = new PODesc.ArrayInitSizeValid(tRhs, dim); - builder.Add(Assert(GetToken(dim), Bpl.Expr.Eq(etran.TrExpr(dim), Bpl.Expr.Literal(tRhs.InitDisplay.Count)), desc)); + builder.Add(Assert(GetToken(dim), Bpl.Expr.Eq(etran.TrExpr(dim), Bpl.Expr.Literal(tRhs.InitDisplay.Count)), desc, builder.Context)); foreach (var v in tRhs.InitDisplay) { CheckWellformed(v, new WFOptions(), locals, builder, etran); } @@ -2708,7 +2712,7 @@ Bpl.Expr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bGivenLhs, IVariable lhs zeroSize = BplOr(zeroSize, Bpl.Expr.Eq(Bpl.Expr.Literal(0), etran.TrExpr(dim))); } var desc = new PODesc.ArrayInitEmpty(tRhs.EType.ToString(), tRhs.ArrayDimensions); - builder.Add(Assert(tRhs.Tok, zeroSize, desc)); + builder.Add(Assert(tRhs.Tok, zeroSize, desc, builder.Context)); } } diff --git a/Source/DafnyCore/Verifier/OpaqueBlockVerifier.cs b/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs similarity index 97% rename from Source/DafnyCore/Verifier/OpaqueBlockVerifier.cs rename to Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs index bc890a890aa..56fce24c17a 100644 --- a/Source/DafnyCore/Verifier/OpaqueBlockVerifier.cs +++ b/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs @@ -37,14 +37,14 @@ public static void EmitBoogie(BoogieGenerator generator, OpaqueBlock block, Boog totalEnsures.Add(expression); blockBuilder.Add(generator.Assert( v.Tok, etran.TrExpr(expression.E), - new DefiniteAssignment("variable", v.Var.Name, "here"))); + new DefiniteAssignment("variable", v.Var.Name, "here"), builder.Context)); } foreach (var ensure in block.Ensures) { totalEnsures.Add(ensure); blockBuilder.Add(generator.Assert( ensure.Tok, etran.TrExpr(ensure.E), - new OpaqueEnsuresDescription(), + new OpaqueEnsuresDescription(), builder.Context, etran.TrAttributes(ensure.Attributes, null))); } From 313ad7ec5334b9e5952db7266c8d1611774b844d Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 20 Sep 2024 15:34:32 +0200 Subject: [PATCH 03/47] Use free calls inside by blocks --- Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs | 8 ++++---- Source/DafnyCore/Verifier/BoogieGenerator.cs | 9 ++++++--- Source/DafnyCore/Verifier/BoogieStmtListBuilder.cs | 4 ++-- .../Verifier/Statements/BoogieGenerator.TrStatement.cs | 7 ++++--- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs index 6ba4aef2c77..9636597ed57 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs @@ -182,8 +182,8 @@ void AddIteratorWellformednessCheck(IteratorDecl iter, Procedure proc) { var th = new ThisExpr(iter); // resolve here var rds = new MemberSelectExpr(iter.tok, th, iter.Member_Reads); var mod = new MemberSelectExpr(iter.tok, th, iter.Member_Modifies); - builder.Add(new Bpl.CallCmd(iter.tok, "$IterHavoc0", - new List() { etran.TrExpr(th), etran.TrExpr(rds), etran.TrExpr(mod) }, + builder.Add(Call(builder.Context, iter.tok, "$IterHavoc0", + new List() { etran.TrExpr(th), etran.TrExpr(rds), etran.TrExpr(mod) }, new List())); // assume the automatic yield-requires precondition (which is always well-formed): this.Valid() @@ -206,7 +206,7 @@ void AddIteratorWellformednessCheck(IteratorDecl iter, Procedure proc) { builder.Add(Bpl.Cmd.SimpleAssign(iter.tok, new Bpl.IdentifierExpr(iter.tok, oldIterHeap), etran.HeapExpr)); // simulate a modifies this, this._modifies, this._new; var nw = new MemberSelectExpr(iter.tok, th, iter.Member_New); - builder.Add(new Bpl.CallCmd(iter.tok, "$IterHavoc1", + builder.Add(Call(builder.Context, iter.tok, "$IterHavoc1", new List() { etran.TrExpr(th), etran.TrExpr(mod), etran.TrExpr(nw) }, new List())); // assume the implicit postconditions promised by MoveNext: @@ -387,7 +387,7 @@ void YieldHavoc(IToken tok, IteratorDecl iter, BoogieStmtListBuilder builder, Ex var th = new ThisExpr(iter); var rds = new MemberSelectExpr(tok, th, iter.Member_Reads); var nw = new MemberSelectExpr(tok, th, iter.Member_New); - builder.Add(new Bpl.CallCmd(tok, "$YieldHavoc", + builder.Add(Call(builder.Context, tok, "$YieldHavoc", new List() { etran.TrExpr(th), etran.TrExpr(rds), etran.TrExpr(nw) }, new List())); // assume YieldRequires; diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index 2103ab76752..6433df53dec 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -2801,6 +2801,7 @@ private Bpl.Function GetCanCallFunction(Function f) { /// Note that SpecWellformedness and Implementation have procedure implementations /// but no callers, and vice versa for InterModuleCall, IntraModuleCall, and CoCall. /// + /// Remy: TODO Simplify enum MethodTranslationKind { SpecWellformedness, CallPre, CallPost, CoCallPre, CoCallPost, Implementation, OverrideCheck } private static readonly Dictionary kindSanitizedPrefix = @@ -2848,14 +2849,16 @@ private static void AddSmtOptionAttribute(Bpl.NamedDeclaration targetDecl, strin targetDecl.Attributes = new QKeyValue(targetDecl.tok, "smt_option", new List() { name, value }, targetDecl.Attributes); } - private static CallCmd Call(IToken tok, string methodName, List ins, List outs) { + private static CallCmd Call(BodyTranslationContext context, IToken tok, string methodName, + List ins, List outs) { Contract.Requires(tok != null); Contract.Requires(methodName != null); Contract.Requires(ins != null); Contract.Requires(outs != null); - CallCmd call; - call = new CallCmd(tok, methodName, ins, outs); + var call = new CallCmd(tok, methodName, ins, outs) { + IsFree = context.AssertMode == AssertMode.Assume + }; // CLEMENT enable this: call.ErrorData = "possible violation of function precondition"; return call; } diff --git a/Source/DafnyCore/Verifier/BoogieStmtListBuilder.cs b/Source/DafnyCore/Verifier/BoogieStmtListBuilder.cs index 8053210c2e3..7377c668bfa 100644 --- a/Source/DafnyCore/Verifier/BoogieStmtListBuilder.cs +++ b/Source/DafnyCore/Verifier/BoogieStmtListBuilder.cs @@ -36,9 +36,9 @@ public BoogieStmtListBuilder(BoogieGenerator tran, DafnyOptions options, BodyTra public void Add(Cmd cmd) { Commands.Add(cmd); builder.Add(cmd); - if (cmd is Boogie.AssertCmd) { + if (cmd is AssertCmd) { tran.assertionCount++; - } else if (cmd is Boogie.CallCmd call) { + } else if (cmd is CallCmd call) { // A call command may involve a precondition, but we can't tell for sure until the callee // procedure has been generated. Therefore, to be on the same side, we count this call // as a possible assertion, unless it's a procedure that's part of the translation and diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs index 20243141035..b9312cfffcb 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs @@ -2026,7 +2026,7 @@ void AddCall(BoogieStmtListBuilder callBuilder) { callBuilder.Add(new CommentCmd($"ProcessCallStmt: Check precondition")); // Make the call AddReferencedMember(callee); - Bpl.CallCmd call = Call(tok, MethodName(callee, isCoCall ? MethodTranslationKind.CoCallPre : MethodTranslationKind.CallPre), ins, new List()); + var call = Call(callBuilder.Context, tok, MethodName(callee, isCoCall ? MethodTranslationKind.CoCallPre : MethodTranslationKind.CallPre), ins, new List()); proofDependencies?.AddProofDependencyId(call, tok, new CallDependency(cs)); if ( (assertionOnlyFilter != null && !assertionOnlyFilter(tok)) || @@ -2042,7 +2042,8 @@ void AddCall(BoogieStmtListBuilder callBuilder) { } builder.Add(new CommentCmd("ProcessCallStmt: Make the call")); - CallCmd post = Call(tok, MethodName(callee, isCoCall ? MethodTranslationKind.CoCallPost : MethodTranslationKind.CallPost), ins, outs); + var post = Call(builder.Context, tok, + MethodName(callee, isCoCall ? MethodTranslationKind.CoCallPost : MethodTranslationKind.CallPost), ins, outs); proofDependencies?.AddProofDependencyId(post, tok, new CallDependency(cs)); builder.Add(post); @@ -2220,7 +2221,7 @@ void RecordNewObjectsIn_New(IToken tok, IteratorDecl iter, Bpl.Expr initHeap, Bp // call $iter_newUpdate := $IterCollectNewObjects(initHeap, $Heap, this, _new); var th = new Bpl.IdentifierExpr(iter.tok, etran.This, predef.RefType); var nwField = new Bpl.IdentifierExpr(tok, GetField(iter.Member_New)); - Bpl.Cmd cmd = new CallCmd(iter.tok, "$IterCollectNewObjects", + var cmd = Call(builder.Context, iter.tok, "$IterCollectNewObjects", new List() { initHeap, etran.HeapExpr, th, nwField }, new List() { updatedSetIE }); builder.Add(cmd); From 174abf872a7c585df6dfecfa58d9e6841d9ddd4c Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 20 Sep 2024 15:41:45 +0200 Subject: [PATCH 04/47] Move code from BoogieGenerator.TrStatement into separate files --- .../BoogieGenerator.TrAssignment.cs | 560 +++++ .../Statements/BoogieGenerator.TrCall.cs | 403 ++++ .../BoogieGenerator.TrForallStmt.cs | 535 +++++ .../Statements/BoogieGenerator.TrLoop.cs | 436 ++++ .../Statements/BoogieGenerator.TrStatement.cs | 1880 +---------------- 5 files changed, 1936 insertions(+), 1878 deletions(-) create mode 100644 Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs create mode 100644 Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs create mode 100644 Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs create mode 100644 Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs new file mode 100644 index 00000000000..6188d9a26c6 --- /dev/null +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs @@ -0,0 +1,560 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using DafnyCore.Verifier; +using Microsoft.Boogie; +using Bpl = Microsoft.Boogie; +using PODesc = Microsoft.Dafny.ProofObligationDescription; + +namespace Microsoft.Dafny; + +public partial class BoogieGenerator { + + + /// + /// "lhs" is expected to be a resolved form of an expression, i.e., not a concrete-syntax expression. + /// + void TrAssignment(Statement stmt, Expression lhs, AssignmentRhs rhs, + BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + Contract.Requires(stmt != null); + Contract.Requires(lhs != null); + Contract.Requires(!(lhs is ConcreteSyntaxExpression)); + Contract.Requires(!(lhs is SeqSelectExpr && !((SeqSelectExpr)lhs).SelectOne)); // these were once allowed, but their functionality is now provided by 'forall' statements + Contract.Requires(rhs != null); + Contract.Requires(builder != null); + Contract.Requires(cce.NonNullElements(locals)); + Contract.Requires(etran != null); + Contract.Requires(predef != null); + + var lhss = new List() { lhs }; + ProcessLhss(lhss, rhs.CanAffectPreviouslyKnownExpressions, true, builder, locals, etran, stmt, + out var lhsBuilder, out var bLhss, out var ignore1, out var ignore2, out var ignore3); + Contract.Assert(lhsBuilder.Count == 1 && bLhss.Count == 1); // guaranteed by postcondition of ProcessLhss + + var rhss = new List() { rhs }; + ProcessRhss(lhsBuilder, bLhss, lhss, rhss, builder, locals, etran, stmt); + builder.AddCaptureState(stmt); + } + + void ProcessRhss(List lhsBuilder, List bLhss, + List lhss, List rhss, + BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, Statement stmt) { + Contract.Requires(lhsBuilder != null); + Contract.Requires(bLhss != null); + Contract.Requires(cce.NonNullElements(lhss)); + Contract.Requires(cce.NonNullElements(rhss)); + Contract.Requires(builder != null); + Contract.Requires(cce.NonNullElements(locals)); + Contract.Requires(etran != null); + Contract.Requires(predef != null); + + var finalRhss = new List(); + for (int i = 0; i < lhss.Count; i++) { + var lhs = lhss[i]; + // the following assumes are part of the precondition, really + Contract.Assume(!(lhs is ConcreteSyntaxExpression)); + Contract.Assume(!(lhs is SeqSelectExpr && !((SeqSelectExpr)lhs).SelectOne)); // array-range assignments are not allowed + + Type lhsType, rhsTypeConstraint; + if (lhs is IdentifierExpr) { + var ide = (IdentifierExpr)lhs; + lhsType = ide.Var.Type; + rhsTypeConstraint = lhsType; + } else if (lhs is MemberSelectExpr) { + var fse = (MemberSelectExpr)lhs; + var field = (Field)fse.Member; + Contract.Assert(VisibleInScope(field)); + lhsType = field.Type; + rhsTypeConstraint = lhsType.Subst(fse.TypeArgumentSubstitutionsWithParents()); + } else if (lhs is SeqSelectExpr) { + var e = (SeqSelectExpr)lhs; + lhsType = null; // for an array update, always make sure the value assigned is boxed + rhsTypeConstraint = e.Seq.Type.NormalizeExpand().TypeArgs[0]; + } else { + var e = (MultiSelectExpr)lhs; + lhsType = null; // for an array update, always make sure the value assigned is boxed + rhsTypeConstraint = e.Array.Type.NormalizeExpand().TypeArgs[0]; + } + var bRhs = TrAssignmentRhs(rhss[i].Tok, bLhss[i], null, lhsType, rhss[i], rhsTypeConstraint, builder, locals, etran, stmt); + if (bLhss[i] != null) { + Contract.Assert(bRhs == bLhss[i]); // this is what the postcondition of TrAssignmentRhs promises + // assignment has already been done by TrAssignmentRhs + finalRhss.Add(null); + } else { + Contract.Assert(bRhs != null); // this is what the postcondition of TrAssignmentRhs promises + finalRhss.Add(bRhs); + } + } + for (int i = 0; i < lhss.Count; i++) { + lhsBuilder[i](finalRhss[i], rhss[i] is HavocRhs, builder, etran); + } + } + + List ProcessUpdateAssignRhss(List lhss, List rhss, + BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + Statement stmt) { + Contract.Requires(cce.NonNullElements(lhss)); + Contract.Requires(cce.NonNullElements(rhss)); + Contract.Requires(builder != null); + Contract.Requires(cce.NonNullElements(locals)); + Contract.Requires(etran != null); + Contract.Requires(predef != null); + Contract.Ensures(Contract.ForAll(Contract.Result>(), i => i != null)); + + var finalRhss = new List(); + for (int i = 0; i < lhss.Count; i++) { + var lhs = lhss[i]; + // the following assumes are part of the precondition, really + Contract.Assume(!(lhs is ConcreteSyntaxExpression)); + Contract.Assume(!(lhs is SeqSelectExpr && !((SeqSelectExpr)lhs).SelectOne)); // array-range assignments are not allowed + + Type lhsType, rhsTypeConstraint; + if (lhs is IdentifierExpr) { + lhsType = ((IdentifierExpr)lhs).Var.Type; + rhsTypeConstraint = lhsType; + } else if (lhs is MemberSelectExpr) { + var fse = (MemberSelectExpr)lhs; + var field = (Field)fse.Member; + Contract.Assert(VisibleInScope(field)); + lhsType = field.Type; + rhsTypeConstraint = lhsType.Subst(fse.TypeArgumentSubstitutionsWithParents()); + } else if (lhs is SeqSelectExpr) { + var e = (SeqSelectExpr)lhs; + lhsType = null; // for an array update, always make sure the value assigned is boxed + rhsTypeConstraint = e.Seq.Type.NormalizeExpand().TypeArgs[0]; + } else { + var e = (MultiSelectExpr)lhs; + lhsType = null; // for an array update, always make sure the value assigned is boxed + rhsTypeConstraint = e.Array.Type.NormalizeExpand().TypeArgs[0]; + } + var bRhs = TrAssignmentRhs(rhss[i].Tok, null, (lhs as IdentifierExpr)?.Var, lhsType, rhss[i], rhsTypeConstraint, builder, locals, etran, stmt); + finalRhss.Add(bRhs); + } + return finalRhss; + } + + + private void CheckLhssDistinctness(List rhs, List rhsOriginal, List lhss, + BoogieStmtListBuilder builder, ExpressionTranslator etran, + Bpl.Expr[] objs, Bpl.Expr[] fields, string[] names, Expression originalInitialLhs = null) { + Contract.Requires(rhs != null); + Contract.Requires(rhsOriginal != null); + Contract.Requires(lhss != null); + Contract.Requires(rhs.Count == rhsOriginal.Count); + Contract.Requires(lhss.Count == rhsOriginal.Count); + Contract.Requires(builder != null); + Contract.Requires(etran != null); + Contract.Requires(predef != null); + + for (int i = 0; i < lhss.Count; i++) { + var lhs = lhss[i]; + Contract.Assume(!(lhs is ConcreteSyntaxExpression)); + if (originalInitialLhs != null) { + // TODO - check RHS values? + AssertDistinctness(lhs, originalInitialLhs, builder, etran); + } + for (int j = 0; j < i; j++) { + if (rhsOriginal[i] is HavocRhs || rhsOriginal[j] is HavocRhs) { + AssertDistinctness(lhs, lhss[j], builder, etran); + } else { + AssertDistinctness(lhs, lhss[j], rhs[i], rhs[j], builder, etran); + } + } + } + } + + /// + /// Note, if "rhs" is "null", then the assignment has already been done elsewhere. However, any other bookkeeping + /// is still done. + /// + delegate void AssignToLhs(Bpl.Expr/*?*/ rhs, bool origRhsIsHavoc, BoogieStmtListBuilder builder, ExpressionTranslator etran); + + void AssertDistinctness(Expression lhsa, Expression lhsb, BoogieStmtListBuilder builder, ExpressionTranslator etran) { + CheckDistinctness(lhsa, lhsb, etran, out var dExpr, out var bExpr); + if (bExpr != null) { + builder.Add(Assert(GetToken(lhsa), bExpr, new PODesc.DistinctLHS(Printer.ExprToString(options, lhsa), + Printer.ExprToString(options, lhsb), bExpr != Bpl.Expr.False, false, dExpr), builder.Context)); + } + } + + void AssertDistinctness(Expression lhsa, Expression lhsb, Bpl.Expr rhsa, Bpl.Expr rhsb, BoogieStmtListBuilder builder, ExpressionTranslator etran) { + CheckDistinctness(lhsa, lhsb, etran, out var dExpr, out var bExpr); + if (bExpr != null) { + bExpr = BplOr(bExpr, Bpl.Expr.Eq(rhsa, rhsb)); + builder.Add(Assert(GetToken(lhsa), bExpr, new PODesc.DistinctLHS(Printer.ExprToString(options, lhsa), + Printer.ExprToString(options, lhsb), false, true, dExpr), builder.Context)); + } + } + + /// + /// Creates a list of protected Boogie LHSs for the given Dafny LHSs. Along the way, + /// builds code that checks that the LHSs are well-defined, + /// and are allowed by the enclosing reads and modifies clause. + /// Checks that they denote different locations iff checkDistinctness is true. + /// + void ProcessLhss(List lhss, bool rhsCanAffectPreviouslyKnownExpressions, bool checkDistinctness, + BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, Statement stmt, + out List lhsBuilders, out List bLhss, + out Bpl.Expr[] prevObj, out Bpl.Expr[] prevIndex, out string[] prevNames, Expression originalInitialLhs = null) { + + Contract.Requires(cce.NonNullElements(lhss)); + Contract.Requires(builder != null); + Contract.Requires(cce.NonNullElements(locals)); + Contract.Requires(etran != null); + Contract.Requires(predef != null); + Contract.Ensures(Contract.ValueAtReturn(out lhsBuilders).Count == lhss.Count); + Contract.Ensures(Contract.ValueAtReturn(out lhsBuilders).Count == Contract.ValueAtReturn(out bLhss).Count); + + rhsCanAffectPreviouslyKnownExpressions = rhsCanAffectPreviouslyKnownExpressions || lhss.Count != 1; + + // for each Dafny LHS, build a protected Boogie LHS for the eventual assignment + lhsBuilders = new List(); + bLhss = new List(); + prevObj = new Bpl.Expr[lhss.Count]; + prevIndex = new Bpl.Expr[lhss.Count]; + prevNames = new string[lhss.Count]; + int i = 0; + + var lhsNameSet = new Dictionary(); + + var contextModFrames = GetContextModifiesFrames(); + + // Note, the resolver does not check for duplicate IdentifierExpr's in LHSs, so do it here. + foreach (var lhs in lhss) { + Contract.Assume(!(lhs is ConcreteSyntaxExpression)); + if (checkDistinctness) { + if (originalInitialLhs != null) { + AssertDistinctness(lhs, originalInitialLhs.Resolved, builder, etran); + } + for (int j = 0; j < i; j++) { + AssertDistinctness(lhs, lhss[j], builder, etran); + } + } + i++; + } + + i = 0; + foreach (var lhs in lhss) { + IToken tok = lhs.tok; + TrStmt_CheckWellformed(lhs, builder, locals, etran, true, true); + + if (lhs is IdentifierExpr) { + var ie = (IdentifierExpr)lhs; + prevNames[i] = ie.Name; + var bLhs = (Bpl.IdentifierExpr)etran.TrExpr(lhs); // TODO: is this cast always justified? + bLhss.Add(rhsCanAffectPreviouslyKnownExpressions ? null : bLhs); + lhsBuilders.Add(delegate (Bpl.Expr rhs, bool origRhsIsHavoc, BoogieStmtListBuilder bldr, ExpressionTranslator et) { + if (rhs != null) { + var cmd = Bpl.Cmd.SimpleAssign(tok, bLhs, rhs); + proofDependencies?.AddProofDependencyId(cmd, lhs.tok, new AssignmentDependency(stmt.RangeToken)); + bldr.Add(cmd); + } + + if (!origRhsIsHavoc || ie.Type.HavocCountsAsDefiniteAssignment(ie.Var.IsGhost)) { + MarkDefiniteAssignmentTracker(ie, bldr); + } + }); + + } else if (lhs is MemberSelectExpr) { + var fse = (MemberSelectExpr)lhs; + var field = fse.Member as Field; + Contract.Assert(field != null); + Contract.Assert(VisibleInScope(field)); + + var useSurrogateLocal = inBodyInitContext && Expression.AsThis(fse.Obj) != null; + + var obj = SaveInTemp(etran.TrExpr(fse.Obj), rhsCanAffectPreviouslyKnownExpressions, + "$obj" + i, predef.RefType, builder, locals); + prevObj[i] = obj; + if (!useSurrogateLocal) { + // check that the enclosing modifies clause allows this object to be written: assert $_ModifiesFrame[obj]); + var desc = new PODesc.Modifiable("an object", contextModFrames, fse.Obj, field); + builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.ModifiesFrame(tok), obj, GetField(fse)), desc, builder.Context)); + } + + if (useSurrogateLocal) { + var nm = SurrogateName(field); + var bLhs = new Bpl.IdentifierExpr(fse.tok, nm, TrType(field.Type)); + bLhss.Add(rhsCanAffectPreviouslyKnownExpressions ? null : bLhs); + lhsBuilders.Add(delegate (Bpl.Expr rhs, bool origRhsIsHavoc, BoogieStmtListBuilder bldr, ExpressionTranslator et) { + if (rhs != null) { + var cmd = Bpl.Cmd.SimpleAssign(tok, bLhs, rhs); + proofDependencies?.AddProofDependencyId(cmd, fse.tok, new AssignmentDependency(stmt.RangeToken)); + bldr.Add(cmd); + } + + if (!origRhsIsHavoc || field.Type.HavocCountsAsDefiniteAssignment(field.IsGhost)) { + MarkDefiniteAssignmentTracker(lhs.tok, nm, bldr); + } + }); + } else { + bLhss.Add(null); + lhsBuilders.Add(delegate (Bpl.Expr rhs, bool origRhsIsHavoc, BoogieStmtListBuilder bldr, ExpressionTranslator et) { + if (rhs != null) { + var fseField = fse.Member as Field; + Contract.Assert(fseField != null); + Check_NewRestrictions(tok, fse.Obj, obj, fseField, rhs, bldr, et); + var h = (Bpl.IdentifierExpr)et.HeapExpr; // TODO: is this cast always justified? + var cmd = Bpl.Cmd.SimpleAssign(tok, h, UpdateHeap(tok, h, obj, new Bpl.IdentifierExpr(tok, GetField(fseField)), rhs)); + proofDependencies?.AddProofDependencyId(cmd, lhs.tok, new AssignmentDependency(stmt.RangeToken)); + bldr.Add(cmd); + // assume $IsGoodHeap($Heap); + bldr.Add(AssumeGoodHeap(tok, et)); + } + }); + } + + } else if (lhs is SeqSelectExpr) { + SeqSelectExpr sel = (SeqSelectExpr)lhs; + Contract.Assert(sel.SelectOne); // array-range assignments are not allowed + Contract.Assert(sel.Seq.Type != null && sel.Seq.Type.IsArrayType); + Contract.Assert(sel.E0 != null); + var obj = SaveInTemp(etran.TrExpr(sel.Seq), rhsCanAffectPreviouslyKnownExpressions, + "$obj" + i, predef.RefType, builder, locals); + var idx = etran.TrExpr(sel.E0); + idx = ConvertExpression(sel.E0.tok, idx, sel.E0.Type, Type.Int); + var fieldName = SaveInTemp(FunctionCall(tok, BuiltinFunction.IndexField, null, idx), rhsCanAffectPreviouslyKnownExpressions, + "$index" + i, predef.FieldName(tok), builder, locals); + prevObj[i] = obj; + prevIndex[i] = fieldName; + // check that the enclosing modifies clause allows this object to be written: assert $_Frame[obj,index]); + var desc = new PODesc.Modifiable("an array element", contextModFrames, sel.Seq, null); + builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.ModifiesFrame(tok), obj, fieldName), desc, builder.Context)); + + bLhss.Add(null); + lhsBuilders.Add(delegate (Bpl.Expr rhs, bool origRhsIsHavoc, BoogieStmtListBuilder bldr, ExpressionTranslator et) { + if (rhs != null) { + var h = (Bpl.IdentifierExpr)et.HeapExpr; // TODO: is this cast always justified? + var cmd = Bpl.Cmd.SimpleAssign(tok, h, UpdateHeap(tok, h, obj, fieldName, rhs)); + proofDependencies?.AddProofDependencyId(cmd, lhs.tok, new AssignmentDependency(stmt.RangeToken)); + bldr.Add(cmd); + // assume $IsGoodHeap($Heap); + bldr.Add(AssumeGoodHeap(tok, et)); + } + }); + + } else { + MultiSelectExpr mse = (MultiSelectExpr)lhs; + Contract.Assert(mse.Array.Type != null && mse.Array.Type.IsArrayType); + + var obj = SaveInTemp(etran.TrExpr(mse.Array), rhsCanAffectPreviouslyKnownExpressions, + "$obj" + i, predef.RefType, builder, locals); + var fieldName = SaveInTemp(etran.GetArrayIndexFieldName(mse.tok, mse.Indices), rhsCanAffectPreviouslyKnownExpressions, + "$index" + i, predef.FieldName(mse.tok), builder, locals); + prevObj[i] = obj; + prevIndex[i] = fieldName; + var desc = new PODesc.Modifiable("an array element", contextModFrames, mse.Array, null); + builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.ModifiesFrame(tok), obj, fieldName), desc, builder.Context)); + + bLhss.Add(null); + lhsBuilders.Add(delegate (Bpl.Expr rhs, bool origRhsIsHavoc, BoogieStmtListBuilder bldr, ExpressionTranslator et) { + if (rhs != null) { + var h = (Bpl.IdentifierExpr)et.HeapExpr; // TODO: is this cast always justified? + var cmd = Bpl.Cmd.SimpleAssign(tok, h, UpdateHeap(tok, h, obj, fieldName, rhs)); + proofDependencies?.AddProofDependencyId(cmd, lhs.tok, new AssignmentDependency(stmt.RangeToken)); + bldr.Add(cmd); + // assume $IsGoodHeap($Heap); + bldr.Add(AssumeGoodHeap(tok, etran)); + } + }); + } + + i++; + } + } + + /// + /// if "bGivenLhs" is non-null, generates an assignment of the translation of "rhs" to "bGivenLhs" and then returns "bGivenLhs". + /// If "bGivenLhs" is null, then this method will return an expression that in a stable way denotes the translation of "rhs"; + /// this is achieved by creating a new temporary Boogie variable to hold the result and returning an expression that mentions + /// that new temporary variable. + /// + /// Before the assignment, the generated code will check that "rhs" obeys any subrange requirements entailed by "rhsTypeConstraint". + /// + /// The purpose of "lhsVar" is to determine an appropriate Boogie "where" clause for any temporary variable generated. + /// If passed in as non-null, it says that "lhsVar" is the LHS of the assignment being translated. If the type is subject to + /// definite-assignment rules and the RHS is "*", then the "where" clause of the temporary variable will have the form + /// "defass#lhs ==> wh" where "defass#lhs" is the definite-assignment tracker for "lhsVar" and "wh" is the "where" + /// clause for type "lhsType" for the temporary variable. + /// + /// The purpose of "lhsType" is to determine if the expression should be boxed before doing the assignment. It is allowed to be null, + /// which indicates that the result should always be a box. Note that "lhsType" may refer to a formal type parameter that is not in + /// scope; this is okay, since the purpose of "lhsType" is just to say whether or not the result should be boxed. + /// + Bpl.Expr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bGivenLhs, IVariable lhsVar, Type lhsType, + AssignmentRhs rhs, Type rhsTypeConstraint, + BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + Statement stmt) { + Contract.Requires(tok != null); + Contract.Requires(rhs != null); + Contract.Requires(rhsTypeConstraint != null); + Contract.Requires(builder != null); + Contract.Requires(locals != null); + Contract.Requires(etran != null); + Contract.Requires(predef != null); + Contract.Ensures(Contract.Result() != null); + Contract.Ensures(bGivenLhs == null || Contract.Result() == bGivenLhs); + + Bpl.IdentifierExpr bLhs; + if (bGivenLhs != null) { + bLhs = bGivenLhs; + } else { + Type localType = rhsTypeConstraint; // this is a type that is appropriate for capturing the value of the RHS + var ty = TrType(localType); + var nm = CurrentIdGenerator.FreshId("$rhs#"); + Bpl.Expr wh; + if (rhs is HavocRhs && localType.IsNonempty) { + wh = GetWhereClause(tok, new Bpl.IdentifierExpr(tok, nm, ty), localType, etran, NOALLOC); + } else if (rhs is HavocRhs && lhsVar != null && GetDefiniteAssignmentTracker(lhsVar) != null) { + // This "where" clause expresses that the new variable has a value of the given type only if + // the variable has already been definitely assigned. (If it has not already been assigned, + // then the variable will get a new value, but Dafny's definite-assginment rules prevent that + // value from being used, so it's appropriate to use effectively-"true" as the "where" clause + // in that case. + wh = BplImp(GetDefiniteAssignmentTracker(lhsVar), + GetWhereClause(tok, new Bpl.IdentifierExpr(tok, nm, ty), localType, etran, NOALLOC)); + } else { + // In this case, it could be unsound to use a "where" clause, see issue #1619. + // Luckily, leaving it out is harmless, because we don't need a "where" clause here in the first + // place--because the variable is short lived, we know it will not be havoc'ed by Boogie, so a + // "where" wouldn't provide additional information over the assigned value. + wh = null; + } + var v = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, nm, ty, wh)); + locals.Add(v); + bLhs = new Bpl.IdentifierExpr(tok, v); + } + + 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, addResultCommands: + (returnBuilder, result) => { + if (cre != null) { + returnBuilder.Add(Assert(result.Tok, cre, desc, builder.Context)); + } + }); + + if (bGivenLhs != null) { + Contract.Assert(bGivenLhs == bLhs); + // box the RHS, then do the assignment + var cmd = Bpl.Cmd.SimpleAssign(tok, bGivenLhs, AdaptBoxing(tok, bRhs, e.Expr.Type, lhsType)); + proofDependencies?.AddProofDependencyId(cmd, tok, new AssignmentDependency(stmt.RangeToken)); + builder.Add(cmd); + return bGivenLhs; + } else { + // box from RHS type to tmp-var type, then do the assignment; then return LHS, boxed from tmp-var type to result type + var cmd = Bpl.Cmd.SimpleAssign(tok, bLhs, AdaptBoxing(tok, bRhs, e.Expr.Type, rhsTypeConstraint)); + proofDependencies?.AddProofDependencyId(cmd, tok, new AssignmentDependency(stmt.RangeToken)); + builder.Add(cmd); + return CondApplyBox(tok, bLhs, rhsTypeConstraint, lhsType); + } + + } else if (rhs is HavocRhs) { + builder.Add(new Bpl.HavocCmd(tok, new List { bLhs })); + return CondApplyBox(tok, bLhs, rhsTypeConstraint, lhsType); + } else { + // x := new Something + Contract.Assert(rhs is TypeRhs); // otherwise, an unexpected AssignmentRhs + TypeRhs tRhs = (TypeRhs)rhs; + + var callsConstructor = tRhs.InitCall != null && tRhs.InitCall.Method is Constructor; + + if (tRhs.ArrayDimensions == null) { + Contract.Assert(tRhs.ElementInit == null && tRhs.InitDisplay == null); + } else { + int i = 0; + foreach (Expression dim in tRhs.ArrayDimensions) { + CheckWellformed(dim, new WFOptions(), locals, builder, etran); + var desc = new PODesc.NonNegative(tRhs.ArrayDimensions.Count == 1 + ? "array size" : $"array size (dimension {i})", dim); + builder.Add(Assert(GetToken(dim), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(dim)), desc, builder.Context)); + i++; + } + if (tRhs.ElementInit != null) { + CheckWellformed(tRhs.ElementInit, new WFOptions(), locals, builder, etran); + } else if (tRhs.InitDisplay != null) { + var dim = tRhs.ArrayDimensions[0]; + var desc = new PODesc.ArrayInitSizeValid(tRhs, dim); + builder.Add(Assert(GetToken(dim), Bpl.Expr.Eq(etran.TrExpr(dim), Bpl.Expr.Literal(tRhs.InitDisplay.Count)), desc, builder.Context)); + foreach (var v in tRhs.InitDisplay) { + CheckWellformed(v, new WFOptions(), locals, builder, etran); + } + } else if (options.DefiniteAssignmentLevel == 0) { + // cool + } else if ((2 <= options.DefiniteAssignmentLevel && options.DefiniteAssignmentLevel != 4) || + options.Get(CommonOptionBag.EnforceDeterminism) || + !tRhs.EType.HasCompilableValue) { + // this is allowed only if the array size is such that it has no elements + Bpl.Expr zeroSize = Bpl.Expr.False; + foreach (Expression dim in tRhs.ArrayDimensions) { + zeroSize = BplOr(zeroSize, Bpl.Expr.Eq(Bpl.Expr.Literal(0), etran.TrExpr(dim))); + } + var desc = new PODesc.ArrayInitEmpty(tRhs.EType.ToString(), tRhs.ArrayDimensions); + builder.Add(Assert(tRhs.Tok, zeroSize, desc, builder.Context)); + } + } + + Bpl.IdentifierExpr nw = GetNewVar_IdExpr(tok, locals); + if (!callsConstructor) { + SelectAllocateObject(tok, nw, tRhs.Type, true, builder, etran); + if (tRhs.ArrayDimensions != null) { + int i = 0; + foreach (Expression dim in tRhs.ArrayDimensions) { + // assume Array#Length($nw, i) == arraySize; + Bpl.Expr arrayLength = ArrayLength(tok, nw, tRhs.ArrayDimensions.Count, i); + builder.Add(TrAssumeCmd(tok, Bpl.Expr.Eq(arrayLength, etran.TrExpr(dim)))); + i++; + } + if (tRhs.ElementInit != null) { + CheckElementInit(tok, true, tRhs.ArrayDimensions, tRhs.EType, tRhs.ElementInit, nw, builder, etran, new WFOptions()); + } else if (tRhs.InitDisplay != null) { + int ii = 0; + foreach (var v in tRhs.InitDisplay) { + var EE_ii = etran.TrExpr(v); + // assert EE_ii satisfies any subset-type constraints; + CheckSubrange(v.tok, EE_ii, v.Type, tRhs.EType, v, builder); + // assume nw[ii] == EE_ii; + var ai = ReadHeap(tok, etran.HeapExpr, nw, GetArrayIndexFieldName(tok, new List { Bpl.Expr.Literal(ii) })); + builder.Add(new Bpl.AssumeCmd(tok, Bpl.Expr.Eq(UnboxUnlessInherentlyBoxed(ai, tRhs.EType), AdaptBoxing(tok, EE_ii, v.Type, tRhs.EType)))); + ii++; + } + } + } + Bpl.Cmd heapAllocationRecorder = null; + if (codeContext is IteratorDecl) { + var iter = (IteratorDecl)codeContext; + // $Heap[this, _new] := Set#UnionOne($Heap[this, _new], $Box($nw)); + var th = new Bpl.IdentifierExpr(tok, etran.This, predef.RefType); + var nwField = new Bpl.IdentifierExpr(tok, GetField(iter.Member_New)); + var thisDotNew = ApplyUnbox(tok, ReadHeap(tok, etran.HeapExpr, th, nwField), predef.SetType); + var unionOne = FunctionCall(tok, BuiltinFunction.SetUnionOne, predef.BoxType, thisDotNew, ApplyBox(tok, nw)); + var heapRhs = UpdateHeap(tok, etran.HeapExpr, th, nwField, unionOne); + heapAllocationRecorder = Bpl.Cmd.SimpleAssign(tok, etran.HeapCastToIdentifierExpr, heapRhs); + } + CommitAllocatedObject(tok, nw, heapAllocationRecorder, builder, etran); + } + if (tRhs.InitCall != null) { + AddComment(builder, tRhs.InitCall, "init call statement"); + TrCallStmt(tRhs.InitCall, builder, locals, etran, nw); + } + // bLhs := $nw; + CheckSubrange(tok, nw, tRhs.Type, rhsTypeConstraint, null, builder); + if (bGivenLhs != null) { + Contract.Assert(bGivenLhs == bLhs); + // box the RHS, then do the assignment + var cmd = Bpl.Cmd.SimpleAssign(tok, bGivenLhs, CondApplyBox(tok, nw, tRhs.Type, lhsType)); + proofDependencies?.AddProofDependencyId(cmd, tok, new AssignmentDependency(stmt.RangeToken)); + builder.Add(cmd); + return bGivenLhs; + } else { + // do the assignment, then box the result + var cmd = Bpl.Cmd.SimpleAssign(tok, bLhs, nw); + proofDependencies?.AddProofDependencyId(cmd, tok, new AssignmentDependency(stmt.RangeToken)); + builder.Add(cmd); + return CondApplyBox(tok, bLhs, tRhs.Type, lhsType); + } + } + } +} \ No newline at end of file diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs new file mode 100644 index 00000000000..89830f477e4 --- /dev/null +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs @@ -0,0 +1,403 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; +using DafnyCore.Verifier; +using DafnyCore.Verifier.Statements; +using Microsoft.Boogie; +using Bpl = Microsoft.Boogie; +using BplParser = Microsoft.Boogie.Parser; +using static Microsoft.Dafny.Util; +using Action = System.Action; +using PODesc = Microsoft.Dafny.ProofObligationDescription; + +namespace Microsoft.Dafny; + +public partial class BoogieGenerator { + + void TrCallStmt(CallStmt s, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, Bpl.IdentifierExpr actualReceiver) { + Contract.Requires(s != null); + Contract.Requires(builder != null); + Contract.Requires(locals != null); + Contract.Requires(etran != null); + Contract.Requires(!(s.Method is Constructor) || (s.Lhs.Count == 0 && actualReceiver != null)); + + var tySubst = s.MethodSelect.TypeArgumentSubstitutionsWithParents(); + ProcessLhss(s.Lhs, true, true, builder, locals, etran, s, out var lhsBuilders, out var bLhss, + out _, out _, out _, s.OriginalInitialLhs); + Contract.Assert(s.Lhs.Count == lhsBuilders.Count); + Contract.Assert(s.Lhs.Count == bLhss.Count); + var lhsTypes = new List(); + if (s.Method is Constructor) { + lhsTypes.Add(s.Receiver.Type); + bLhss.Add(actualReceiver); + } else { + for (int i = 0; i < s.Lhs.Count; i++) { + var lhs = s.Lhs[i]; + lhsTypes.Add(lhs.Type); + builder.Add(new CommentCmd("TrCallStmt: Adding lhs with type " + lhs.Type)); + if (bLhss[i] == null) { // (in the current implementation, the second parameter "true" to ProcessLhss implies that all bLhss[*] will be null) + // create temporary local and assign it to bLhss[i] + string nm = CurrentIdGenerator.FreshId("$rhs##"); + var formalOutType = s.Method.Outs[i].Type.Subst(tySubst); + var ty = TrType(formalOutType); + Bpl.LocalVariable var = new Bpl.LocalVariable(lhs.tok, new Bpl.TypedIdent(lhs.tok, nm, ty)); + locals.Add(var); + bLhss[i] = new Bpl.IdentifierExpr(lhs.tok, var.Name, ty); + } + } + } + Bpl.IdentifierExpr initHeap = null; + if (codeContext is IteratorDecl) { + // var initHeap := $Heap; + var initHeapVar = new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, CurrentIdGenerator.FreshId("$initHeapCallStmt#"), predef.HeapType)); + locals.Add(initHeapVar); + initHeap = new Bpl.IdentifierExpr(s.Tok, initHeapVar); + // initHeap := $Heap; + builder.Add(Bpl.Cmd.SimpleAssign(s.Tok, initHeap, etran.HeapExpr)); + } + builder.Add(new CommentCmd("TrCallStmt: Before ProcessCallStmt")); + ProcessCallStmt(s, tySubst, actualReceiver, bLhss, lhsTypes, builder, locals, etran); + builder.Add(new CommentCmd("TrCallStmt: After ProcessCallStmt")); + for (int i = 0; i < lhsBuilders.Count; i++) { + var lhs = s.Lhs[i]; + Type lhsType, rhsTypeConstraint; + if (lhs is IdentifierExpr) { + var ide = (IdentifierExpr)lhs; + lhsType = ide.Var.Type; + rhsTypeConstraint = lhsType; + } else if (lhs is MemberSelectExpr) { + var fse = (MemberSelectExpr)lhs; + var field = (Field)fse.Member; + Contract.Assert(field != null); + Contract.Assert(VisibleInScope(field)); + lhsType = field.Type; + rhsTypeConstraint = lhsType.Subst(fse.TypeArgumentSubstitutionsWithParents()); + } else if (lhs is SeqSelectExpr) { + var e = (SeqSelectExpr)lhs; + lhsType = null; // for arrays, always make sure the value assigned is boxed + rhsTypeConstraint = e.Seq.Type.TypeArgs[0]; + } else { + var e = (MultiSelectExpr)lhs; + lhsType = null; // for arrays, always make sure the value assigned is boxed + rhsTypeConstraint = e.Array.Type.TypeArgs[0]; + } + + Bpl.Expr bRhs = bLhss[i]; // the RHS (bRhs) of the assignment to the actual call-LHS (lhs) was a LHS (bLhss[i]) in the Boogie call statement + CheckSubrange(lhs.tok, bRhs, s.Method.Outs[i].Type.Subst(tySubst), rhsTypeConstraint, null, builder); + bRhs = CondApplyBox(lhs.tok, bRhs, lhs.Type, lhsType); + + lhsBuilders[i](bRhs, false, builder, etran); + } + if (codeContext is IteratorDecl) { + var iter = (IteratorDecl)codeContext; + Contract.Assert(initHeap != null); + RecordNewObjectsIn_New(s.Tok, iter, initHeap, etran.HeapCastToIdentifierExpr, builder, locals, etran); + } + builder.AddCaptureState(s); + } + + void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.Expr bReceiver, + List Lhss, List LhsTypes, + BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + + Contract.Requires(cs != null); + Contract.Requires(Lhss != null); + Contract.Requires(LhsTypes != null); + // Note, a Dafny class constructor is declared to have no output parameters, but it is encoded in Boogie as + // having an output parameter. + Contract.Requires(cs.Method is Constructor || cs.Method.Outs.Count == Lhss.Count); + Contract.Requires(cs.Method is Constructor || cs.Method.Outs.Count == LhsTypes.Count); + Contract.Requires(!(cs.Method is Constructor) || (cs.Method.Outs.Count == 0 && Lhss.Count == 1 && LhsTypes.Count == 1)); + Contract.Requires(builder != null); + Contract.Requires(locals != null); + Contract.Requires(etran != null); + Contract.Requires(tySubst != null); + var tok = GetToken(cs); + var tyArgs = GetTypeParams(cs.Method); + var dafnyReceiver = cs.Receiver; + var method = cs.Method; + var atLabel = cs.MethodSelect.AtLabel; + var Args = cs.Args; + + // Figure out if the call is recursive or not, which will be used below to determine the need for a + // termination check and the need to include an implicit _k-1 argument. + bool isRecursiveCall = false; + // consult the call graph to figure out if this is a recursive call + var module = method.EnclosingClass.EnclosingModuleDefinition; + if (codeContext != null && module == currentModule) { + // Note, prefix lemmas are not recorded in the call graph, but their corresponding greatest lemmas are. + // Similarly, an iterator is not recorded in the call graph, but its MoveNext method is. + ICallable cllr = + codeContext is PrefixLemma ? ((PrefixLemma)codeContext).ExtremeLemma : + codeContext is IteratorDecl ? ((IteratorDecl)codeContext).Member_MoveNext : + codeContext; + if (ModuleDefinition.InSameSCC(method, cllr)) { + isRecursiveCall = true; + } + } + + bool isCoCall = false; + var callee = method; + if (method is ExtremeLemma && isRecursiveCall) { + isCoCall = true; + callee = ((ExtremeLemma)method).PrefixLemma; + } else if (method is PrefixLemma) { + // an explicit call to a prefix lemma is allowed only inside the SCC of the corresponding greatest lemma, + // so we consider this to be a co-call + isCoCall = true; + } + + var ins = new List(); + if (callee is TwoStateLemma) { + ins.Add(etran.OldAt(atLabel).HeapExpr); + ins.Add(etran.HeapExpr); + } + // Add type arguments + ins.AddRange(TrTypeArgs(tySubst, tyArgs)); + + // Translate receiver argument, if any + Expression receiver = bReceiver == null ? dafnyReceiver : new BoogieWrapper(bReceiver, dafnyReceiver.Type); + if (!method.IsStatic && method is not Constructor) { + if (bReceiver == null) { + TrStmt_CheckWellformed(dafnyReceiver, builder, locals, etran, true); + if (!(dafnyReceiver is ThisExpr)) { + CheckNonNull(dafnyReceiver.tok, dafnyReceiver, builder, etran, null); + } + } + var obj = etran.TrExpr(receiver); + if (bReceiver == null) { + obj = BoxifyForTraitParent(tok, obj, method, dafnyReceiver.Type); + } + ins.Add(obj); + } else if (receiver is StaticReceiverExpr stexpr) { + if (stexpr.ObjectToDiscard != null) { + TrStmt_CheckWellformed(stexpr.ObjectToDiscard, builder, locals, etran, true); + } + } + + // Ideally, the modifies and decreases checks would be done after the precondition check, + // but Boogie doesn't give us a hook for that. So, we set up our own local variables here to + // store the actual parameters. + // Create a local variable for each formal parameter, and assign each actual parameter to the corresponding local + var substMap = new Dictionary(); + var directSubstMap = new Dictionary(); + for (int i = 0; i < callee.Ins.Count; i++) { + var formal = callee.Ins[i]; + var local = new LocalVariable(formal.RangeToken, formal.Name + "#", formal.Type.Subst(tySubst), formal.IsGhost); + local.type = local.SyntacticType; // resolve local here + var ie = new IdentifierExpr(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator)); + ie.Var = local; ie.Type = ie.Var.Type; // resolve ie here + substMap.Add(formal, ie); + locals.Add(new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator), TrType(local.Type)))); + + var param = (Bpl.IdentifierExpr)etran.TrExpr(ie); // TODO: is this cast always justified? + Bpl.Expr bActual; + Expression dActual; + if (i == 0 && method is ExtremeLemma && isRecursiveCall) { + // Treat this call to M(args) as a call to the corresponding prefix lemma M#(_k - 1, args), so insert an argument here. + var k = ((PrefixLemma)callee).K; + var bplK = new Bpl.IdentifierExpr(k.tok, k.AssignUniqueName(currentDeclaration.IdGenerator), TrType(k.Type)); + dActual = Expression.CreateSubtract(Expression.CreateIdentExpr(k), Expression.CreateNatLiteral(k.tok, 1, k.Type)); + if (k.Type.IsBigOrdinalType) { + bActual = FunctionCall(k.tok, "ORD#Minus", predef.BigOrdinalType, + bplK, + FunctionCall(k.tok, "ORD#FromNat", predef.BigOrdinalType, Bpl.Expr.Literal(1))); + } else { + bActual = Bpl.Expr.Sub(bplK, Bpl.Expr.Literal(1)); + } + } else { + Expression actual; + if (method is ExtremeLemma && isRecursiveCall) { + actual = Args[i - 1]; + } else { + actual = Args[i]; + } + if (!(actual is DefaultValueExpression)) { + TrStmt_CheckWellformed(actual, builder, locals, etran, true); + } + builder.Add(new CommentCmd("ProcessCallStmt: CheckSubrange")); + // Check the subrange without boxing + var beforeBox = etran.TrExpr(actual); + CheckSubrange(actual.tok, beforeBox, actual.Type, formal.Type.Subst(tySubst), actual, builder); + bActual = AdaptBoxing(actual.tok, beforeBox, actual.Type, formal.Type.Subst(tySubst)); + dActual = actual; + } + directSubstMap.Add(formal, dActual); + Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(formal.tok, param, bActual); + builder.Add(cmd); + ins.Add(AdaptBoxing(ToDafnyToken(flags.ReportRanges, param.tok), param, formal.Type.Subst(tySubst), formal.Type)); + } + + // Check that every parameter is available in the state in which the method 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 method is + // invoked in the 'old' state or if the method invoked is a two-state lemma 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 (!method.IsStatic && !(method is Constructor)) { + Bpl.Expr wh = GetWhereClause(receiver.tok, etran.TrExpr(receiver), receiver.Type, etran, ISALLOC, true); + if (wh != null) { + var desc = new PODesc.IsAllocated("receiver argument", "in the state in which the method is invoked", receiver); + builder.Add(Assert(receiver.tok, wh, desc, builder.Context)); + } + } + for (int i = 0; i < Args.Count; i++) { + Expression ee = Args[i]; + Bpl.Expr wh = GetWhereClause(ee.tok, etran.TrExpr(ee), ee.Type, etran, ISALLOC, true); + if (wh != null) { + var desc = new PODesc.IsAllocated("argument", "in the state in which the method is invoked", ee); + builder.Add(Assert(ee.tok, wh, desc, builder.Context)); + } + } + } else if (method is TwoStateLemma) { + if (!method.IsStatic) { + Bpl.Expr wh = GetWhereClause(receiver.tok, etran.TrExpr(receiver), receiver.Type, etran.OldAt(atLabel), ISALLOC, true); + if (wh != null) { + var desc = new PODesc.IsAllocated("receiver argument", "in the two-state lemma's previous state", receiver, atLabel); + builder.Add(Assert(receiver.tok, wh, desc, builder.Context)); + } + } + Contract.Assert(callee.Ins.Count == Args.Count); + for (int i = 0; i < Args.Count; i++) { + var formal = callee.Ins[i]; + if (formal.IsOld) { + Expression ee = Args[i]; + Bpl.Expr wh = GetWhereClause(ee.tok, etran.TrExpr(ee), ee.Type, etran.OldAt(atLabel), ISALLOC, true); + if (wh != null) { + var pIdx = Args.Count == 1 ? "" : " at index " + i; + var desc = new PODesc.IsAllocated( + $"argument{pIdx} for parameter '{formal.Name}'", + "in the two-state lemma's previous state" + PODesc.IsAllocated.HelperFormal(formal), + ee, + atLabel + ); + builder.Add(Assert(ee.tok, wh, desc, builder.Context)); + } + } + } + } + + var directSub = new Substituter(null, directSubstMap, tySubst); + + // Check that the reads clause of a subcall is a subset of the current reads frame, + // but support the optimization that we don't define a reads frame at all if it's `reads *`. + if (etran.readsFrame != null) { + // substitute actual args for parameters in description expression frames... + var requiredFrames = callee.Reads.Expressions.ConvertAll(directSub.SubstFrameExpr); + var desc = new PODesc.ReadFrameSubset("call", requiredFrames, GetContextReadsFrames()); + + // ... but that substitution isn't needed for frames passed to CheckFrameSubset + var readsSubst = new Substituter(null, new Dictionary(), tySubst); + CheckFrameSubset(tok, callee.Reads.Expressions.ConvertAll(readsSubst.SubstFrameExpr), + receiver, substMap, etran, etran.ReadsFrame(tok), builder, desc, null); + } + + // substitute actual args for parameters in description expression frames... + var frameExpressions = callee.Mod.Expressions.ConvertAll(directSub.SubstFrameExpr); + // Check that the modifies clause of a subcall is a subset of the current modifies frame, + // but only if we're in a context that defines a modifies frame. + if (codeContext is IMethodCodeContext methodCodeContext) { + var desc = new PODesc.ModifyFrameSubset( + "call", + frameExpressions, + methodCodeContext.Modifies.Expressions + ); + // ... but that substitution isn't needed for frames passed to CheckFrameSubset + var modifiesSubst = new Substituter(null, new(), tySubst); + CheckFrameSubset( + tok, callee.Mod.Expressions.ConvertAll(modifiesSubst.SubstFrameExpr), + receiver, substMap, etran, etran.ModifiesFrame(tok), builder, desc, null); + } + + // Check termination + if (isRecursiveCall) { + Contract.Assert(codeContext != null); + if (codeContext is DatatypeDecl) { + builder.Add(Assert(tok, Bpl.Expr.False, new PODesc.IsNonRecursive(), builder.Context)); + } else { + List contextDecreases = codeContext.Decreases.Expressions; + List calleeDecreases = callee.Decreases.Expressions; + CheckCallTermination(tok, contextDecreases, calleeDecreases, null, receiver, substMap, directSubstMap, tySubst, etran, true, builder, codeContext.InferredDecreases, null); + } + } + + // Create variables to hold the output parameters of the call, so that appropriate unboxes can be introduced. + var outs = new List(); + var tmpOuts = new List(); + if (method is Constructor) { + tmpOuts.Add(null); + outs.Add(Lhss[0]); + } else { + for (int i = 0; i < Lhss.Count; i++) { + var bLhs = Lhss[i]; + if (ModeledAsBoxType(callee.Outs[i].Type) && !ModeledAsBoxType(LhsTypes[i])) { + // we need an Unbox + Bpl.LocalVariable var = new Bpl.LocalVariable(bLhs.tok, new Bpl.TypedIdent(bLhs.tok, CurrentIdGenerator.FreshId("$tmp##"), predef.BoxType)); + locals.Add(var); + Bpl.IdentifierExpr varIdE = new Bpl.IdentifierExpr(bLhs.tok, var.Name, predef.BoxType); + tmpOuts.Add(varIdE); + outs.Add(varIdE); + } else { + tmpOuts.Add(null); + outs.Add(bLhs); + } + } + } + + if (cs.Proof == null) { + AddCall(builder); + } else { + var callBuilder = new BoogieStmtListBuilder(this, options, builder.Context); + AddComment(callBuilder, cs, "call statement proof"); + CurrentIdGenerator.Push(); + TrStmt(cs.Proof, callBuilder, locals, etran); + CurrentIdGenerator.Pop(); + AddCall(callBuilder); + PathAsideBlock(cs.Tok, callBuilder, builder); + } + + void AddCall(BoogieStmtListBuilder callBuilder) { + callBuilder.Add(new CommentCmd($"ProcessCallStmt: Check precondition")); + // Make the call + AddReferencedMember(callee); + var call = Call(callBuilder.Context, tok, MethodName(callee, isCoCall ? MethodTranslationKind.CoCallPre : MethodTranslationKind.CallPre), ins, new List()); + proofDependencies?.AddProofDependencyId(call, tok, new CallDependency(cs)); + if ( + (assertionOnlyFilter != null && !assertionOnlyFilter(tok)) || + (module != currentModule && RefinementToken.IsInherited(tok, currentModule) && (codeContext == null || !codeContext.MustReverify))) { + // The call statement is inherited, so the refined module already checked that the precondition holds. Note, + // preconditions are not allowed to be strengthened, except if they use a predicate whose body has been strengthened. + // But if the callee sits in a different module, then any predicate it uses will be treated as opaque (that is, + // uninterpreted) anyway, so the refined module will have checked the call precondition for all possible definitions + // of the predicate. + call.IsFree = true; + } + callBuilder.Add(call); + } + + builder.Add(new CommentCmd("ProcessCallStmt: Make the call")); + var post = Call(builder.Context, tok, + MethodName(callee, isCoCall ? MethodTranslationKind.CoCallPost : MethodTranslationKind.CallPost), ins, outs); + proofDependencies?.AddProofDependencyId(post, tok, new CallDependency(cs)); + builder.Add(post); + + // Unbox results as needed + for (int i = 0; i < Lhss.Count; i++) { + Bpl.IdentifierExpr bLhs = Lhss[i]; + Bpl.IdentifierExpr tmpVarIdE = tmpOuts[i]; + if (tmpVarIdE != null) { + // Instead of an assignment: + // e := UnBox(tmpVar); + // we use: + // havoc e; assume e == UnBox(tmpVar); + // because that will reap the benefits of e's where clause, so that some additional type information will be known about + // the out-parameter. + Bpl.Cmd cmd = new Bpl.HavocCmd(bLhs.tok, new List { bLhs }); + builder.Add(cmd); + cmd = TrAssumeCmd(bLhs.tok, Bpl.Expr.Eq(bLhs, FunctionCall(bLhs.tok, BuiltinFunction.Unbox, TrType(LhsTypes[i]), tmpVarIdE))); + builder.Add(cmd); + } + } + } +} \ No newline at end of file diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs new file mode 100644 index 00000000000..da0abd4c8bf --- /dev/null +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs @@ -0,0 +1,535 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; +using Microsoft.Boogie; +using Bpl = Microsoft.Boogie; +using PODesc = Microsoft.Dafny.ProofObligationDescription; + +namespace Microsoft.Dafny; + +public partial class BoogieGenerator { + + + private void TrForallStmt(BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + ForallStmt forallStmt) { + this.fuelContext = FuelSetting.ExpandFuelContext(forallStmt.Attributes, forallStmt.Tok, this.fuelContext, this.reporter); + + CurrentIdGenerator.Push(); + if (forallStmt.Kind == ForallStmt.BodyKind.Assign) { + AddComment(builder, forallStmt, "forall statement (assign)"); + Contract.Assert(forallStmt.Ens.Count == 0); + if (forallStmt.BoundVars.Count == 0) { + TrStmt(forallStmt.Body, builder, locals, etran); + } else { + var s0 = (AssignStmt)forallStmt.S0; + var definedness = new BoogieStmtListBuilder(this, options, builder.Context); + var updater = new BoogieStmtListBuilder(this, options, builder.Context); + DefineFuelConstant(forallStmt.Tok, forallStmt.Attributes, definedness, etran); + TrForallAssign(forallStmt, s0, definedness, updater, locals, etran); + // All done, so put the two pieces together + builder.Add(new Bpl.IfCmd(forallStmt.Tok, null, definedness.Collect(forallStmt.Tok), null, updater.Collect(forallStmt.Tok))); + builder.AddCaptureState(forallStmt); + } + + } else if (forallStmt.Kind == ForallStmt.BodyKind.Call) { + AddComment(builder, forallStmt, "forall statement (call)"); + Contract.Assert(forallStmt.Ens.Count == 0); + if (forallStmt.BoundVars.Count == 0) { + Contract.Assert(LiteralExpr.IsTrue(forallStmt.Range)); // follows from the invariant of ForallStmt + TrStmt(forallStmt.Body, builder, locals, etran); + } else { + var s0 = (CallStmt)forallStmt.S0; + if (Attributes.Contains(forallStmt.Attributes, "_trustWellformed")) { + TrForallStmtCall(forallStmt.Tok, forallStmt.BoundVars, forallStmt.Bounds, forallStmt.Range, null, forallStmt.EffectiveEnsuresClauses, s0, null, builder, locals, etran); + } else { + var definedness = new BoogieStmtListBuilder(this, options, builder.Context); + DefineFuelConstant(forallStmt.Tok, forallStmt.Attributes, definedness, etran); + var exporter = new BoogieStmtListBuilder(this, options, builder.Context); + TrForallStmtCall(forallStmt.Tok, forallStmt.BoundVars, forallStmt.Bounds, forallStmt.Range, null, forallStmt.EffectiveEnsuresClauses, s0, definedness, exporter, locals, etran); + // All done, so put the two pieces together + builder.Add(new Bpl.IfCmd(forallStmt.Tok, null, definedness.Collect(forallStmt.Tok), null, exporter.Collect(forallStmt.Tok))); + } + builder.AddCaptureState(forallStmt); + } + + } else if (forallStmt.Kind == ForallStmt.BodyKind.Proof) { + AddComment(builder, forallStmt, "forall statement (proof)"); + var definedness = new BoogieStmtListBuilder(this, options, builder.Context); + var exporter = new BoogieStmtListBuilder(this, options, builder.Context); + DefineFuelConstant(forallStmt.Tok, forallStmt.Attributes, definedness, etran); + TrForallProof(forallStmt, definedness, exporter, locals, etran); + // All done, so put the two pieces together + builder.Add(new Bpl.IfCmd(forallStmt.Tok, null, definedness.Collect(forallStmt.Tok), null, exporter.Collect(forallStmt.Tok))); + builder.AddCaptureState(forallStmt); + + } else { + Contract.Assert(false); // unexpected kind + } + CurrentIdGenerator.Pop(); + this.fuelContext = FuelSetting.PopFuelContext(); + } + + + void TrForallStmtCall(IToken tok, List boundVars, List bounds, + Expression range, ExpressionConverter additionalRange, List forallExpressions, CallStmt s0, + BoogieStmtListBuilder definedness, BoogieStmtListBuilder exporter, List locals, ExpressionTranslator etran) { + Contract.Requires(tok != null); + Contract.Requires(boundVars != null); + Contract.Requires(bounds != null); + Contract.Requires(range != null); + // additionalRange is allowed to be null + Contract.Requires(s0 != null); + // definedness is allowed to be null + Contract.Requires(exporter != null); + Contract.Requires(locals != null); + Contract.Requires(etran != null); + + // Translate: + // forall (x,y | Range(x,y)) { + // E(x,y) . M( Args(x,y) ); + // } + // as: + // if (*) { + // var x,y; + // havoc x,y; + // CheckWellformed( Range ); + // assume Range(x,y); + // assume additionalRange; + // Tr( Call ); + // assume false; + // } else { + // initHeap := $Heap; + // advance $Heap; + // assume (forall x,y :: (Range(x,y) && additionalRange)[INIT] && + // ==> Post[old($Heap) := initHeap]( E(x,y)[INIT], Args(x,y)[INIT] )); + // } + // where Post(this,args) is the postcondition of method M and + // INIT is the substitution [old($Heap),$Heap := old($Heap),initHeap]. + + if (definedness != null) { + if (boundVars.Count != 0) { + // Note, it would be nicer (and arguably more appropriate) to do a SetupBoundVarsAsLocals + // here (rather than a TrBoundVariables). However, there is currently no way to apply + // a substMap to a statement (in particular, to s.Body), so that doesn't work here. + List freeOfAlloc = BoundedPool.HasBounds(bounds, BoundedPool.PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc); + List bvars = new List(); + var ante = etran.TrBoundVariables(boundVars, bvars, true, freeOfAlloc); + locals.AddRange(bvars); + var havocIds = new List(); + foreach (Bpl.Variable bv in bvars) { + havocIds.Add(new Bpl.IdentifierExpr(tok, bv)); + } + definedness.Add(new Bpl.HavocCmd(tok, havocIds)); + definedness.Add(TrAssumeCmd(tok, ante)); + } + TrStmt_CheckWellformed(range, definedness, locals, etran, false); + definedness.Add(TrAssumeCmd(range.tok, etran.TrExpr(range))); + if (additionalRange != null) { + var es = additionalRange(new Dictionary(), etran); + definedness.Add(TrAssumeCmd(es.tok, es)); + } + + TrStmt(s0, definedness, locals, etran); + + definedness.Add(TrAssumeCmd(tok, Bpl.Expr.False)); + } + + // Now for the other branch, where the postcondition of the call is exported. + { + var initHeapVar = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, CurrentIdGenerator.FreshId("$initHeapForallStmt#"), predef.HeapType)); + locals.Add(initHeapVar); + var initHeap = new Bpl.IdentifierExpr(tok, initHeapVar); + var initEtran = new ExpressionTranslator(this, predef, initHeap, etran.Old.HeapExpr, etran.scope); + // initHeap := $Heap; + exporter.Add(Bpl.Cmd.SimpleAssign(tok, initHeap, etran.HeapExpr)); + var heapIdExpr = etran.HeapCastToIdentifierExpr; + // advance $Heap; + exporter.Add(new Bpl.HavocCmd(tok, new List { heapIdExpr })); + Contract.Assert(s0.Method.Mod.Expressions.Count == 0); // checked by the resolver + foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(tok, new List(), s0.IsGhost, s0.Method.AllowsAllocation, initEtran, etran, initEtran)) { + if (tri.IsFree) { + exporter.Add(TrAssumeCmd(tok, tri.Expr)); + } + } + if (codeContext is IteratorDecl) { + var iter = (IteratorDecl)codeContext; + RecordNewObjectsIn_New(tok, iter, initHeap, heapIdExpr, exporter, locals, etran); + } + + // Note, in the following, we need to do a bit of a song and dance. The actual arguments of the + // call should be translated using "initEtran", whereas the method postcondition should be translated + // using "callEtran". To accomplish this, we translate the argument and then tuck the resulting + // Boogie expressions into BoogieExprWrappers that are used in the DafnyExpr-to-DafnyExpr substitution. + var bvars = new List(); + Dictionary substMap; + Bpl.Trigger antitriggerBoundVarTypes; + Bpl.Expr ante; + var argsSubstMap = new Dictionary(); // maps formal arguments to actuals + Contract.Assert(s0.Method.Ins.Count == s0.Args.Count); + var callEtran = new ExpressionTranslator(this, predef, etran.HeapExpr, initHeap, etran.scope); + Bpl.Expr post = Bpl.Expr.True; + Bpl.Trigger tr; + if (forallExpressions != null) { + // translate based on the forallExpressions since the triggers are computed based on it already. + QuantifierExpr expr = (QuantifierExpr)forallExpressions[0]; + while (expr.SplitQuantifier != null) { + expr = (QuantifierExpr)expr.SplitQuantifierExpression; + } + boundVars = expr.BoundVars; + ante = initEtran.TrBoundVariablesRename(boundVars, bvars, out substMap, out antitriggerBoundVarTypes); + ante = BplAnd(ante, initEtran.TrExpr(Substitute(expr.Range, null, substMap))); + if (additionalRange != null) { + ante = BplAnd(ante, additionalRange(substMap, initEtran)); + } + tr = TrTrigger(callEtran, expr.Attributes, expr.tok, bvars, substMap, s0.MethodSelect.TypeArgumentSubstitutionsWithParents()); + post = callEtran.TrExpr(Substitute(expr.Term, null, substMap)); + } else { + ante = initEtran.TrBoundVariablesRename(boundVars, bvars, out substMap, out antitriggerBoundVarTypes); + for (int i = 0; i < s0.Method.Ins.Count; i++) { + var arg = Substitute(s0.Args[i], null, substMap, s0.MethodSelect.TypeArgumentSubstitutionsWithParents()); // substitute the renamed bound variables for the declared ones + argsSubstMap.Add(s0.Method.Ins[i], new BoogieWrapper(initEtran.TrExpr(arg), s0.Args[i].Type)); + } + ante = BplAnd(ante, initEtran.TrExpr(Substitute(range, null, substMap))); + if (additionalRange != null) { + ante = BplAnd(ante, additionalRange(substMap, initEtran)); + } + var receiver = new BoogieWrapper(initEtran.TrExpr(Substitute(s0.Receiver, null, substMap, s0.MethodSelect.TypeArgumentSubstitutionsWithParents())), s0.Receiver.Type); + foreach (var ens in s0.Method.Ens) { + var p = Substitute(ens.E, receiver, argsSubstMap, s0.MethodSelect.TypeArgumentSubstitutionsWithParents()); // substitute the call's actuals for the method's formals + post = BplAnd(post, callEtran.TrExpr(p)); + } + tr = antitriggerBoundVarTypes; + } + + // TRIG (forall $ih#s0#0: Seq :: $Is($ih#s0#0, TSeq(TChar)) && $IsAlloc($ih#s0#0, TSeq(TChar), $initHeapForallStmt#0) && Seq#Length($ih#s0#0) != 0 && Seq#Rank($ih#s0#0) < Seq#Rank(s#0) ==> (forall i#2: int :: true ==> LitInt(0) <= i#2 && i#2 < Seq#Length($ih#s0#0) ==> char#ToInt(_module.CharChar.MinChar($LS($LZ), $Heap, this, $ih#s0#0)) <= char#ToInt($Unbox(Seq#Index($ih#s0#0, i#2)): char))) + // TRIG (forall $ih#pat0#0: Seq, $ih#a0#0: Seq :: $Is($ih#pat0#0, TSeq(_module._default.Same0$T)) && $IsAlloc($ih#pat0#0, TSeq(_module._default.Same0$T), $initHeapForallStmt#0) && $Is($ih#a0#0, TSeq(_module._default.Same0$T)) && $IsAlloc($ih#a0#0, TSeq(_module._default.Same0$T), $initHeapForallStmt#0) && Seq#Length($ih#pat0#0) <= Seq#Length($ih#a0#0) && Seq#SameUntil($ih#pat0#0, $ih#a0#0, Seq#Length($ih#pat0#0)) && (Seq#Rank($ih#pat0#0) < Seq#Rank(pat#0) || (Seq#Rank($ih#pat0#0) == Seq#Rank(pat#0) && Seq#Rank($ih#a0#0) < Seq#Rank(a#0))) ==> _module.__default.IsRelaxedPrefixAux(_module._default.Same0$T, $LS($LZ), $Heap, $ih#pat0#0, $ih#a0#0, LitInt(1)))' + // TRIG (forall $ih#m0#0: DatatypeType, $ih#n0#0: DatatypeType :: $Is($ih#m0#0, Tclass._module.Nat()) && $IsAlloc($ih#m0#0, Tclass._module.Nat(), $initHeapForallStmt#0) && $Is($ih#n0#0, Tclass._module.Nat()) && $IsAlloc($ih#n0#0, Tclass._module.Nat(), $initHeapForallStmt#0) && Lit(true) && (DtRank($ih#m0#0) < DtRank(m#0) || (DtRank($ih#m0#0) == DtRank(m#0) && DtRank($ih#n0#0) < DtRank(n#0))) ==> _module.__default.mult($LS($LZ), $Heap, $ih#m0#0, _module.__default.plus($LS($LZ), $Heap, $ih#n0#0, $ih#n0#0)) == _module.__default.mult($LS($LZ), $Heap, _module.__default.plus($LS($LZ), $Heap, $ih#m0#0, $ih#m0#0), $ih#n0#0)) + var qq = new Bpl.ForallExpr(tok, bvars, tr, BplImp(ante, post)); // TODO: Add a SMART_TRIGGER here. If we can't find one, abort the attempt to do induction automatically + exporter.Add(TrAssumeCmd(tok, qq)); + } + } + + void TrForallAssign(ForallStmt s, AssignStmt s0, + BoogieStmtListBuilder definedness, BoogieStmtListBuilder updater, List locals, ExpressionTranslator etran) { + // The statement: + // forall (x,y | Range(x,y)) { + // (a) E(x,y) . f := G(x,y); + // (b) A(x,y) [ I0(x,y), I1(x,y), ... ] := G(x,y); + // } + // translate into: + // if (*) { + // // check definedness of Range + // var x,y; + // havoc x,y; + // CheckWellformed( Range ); + // assume Range; + // // check definedness of the other expressions + // (a) + // CheckWellformed( E.F ); + // check that E.f is in the modifies frame; + // CheckWellformed( G ); + // check nat restrictions for the RHS + // (b) + // CheckWellformed( A[I0,I1,...] ); + // check that A[I0,I1,...] is in the modifies frame; + // CheckWellformed( G ); + // check nat restrictions for the RHS + // // check for duplicate LHSs + // var x', y'; + // havoc x', y'; + // assume Range[x,y := x',y']; + // assume !(x == x' && y == y'); + // (a) + // assert E(x,y) != E(x',y') || G(x,y) == G(x',y'); + // (b) + // assert !( A(x,y)==A(x',y') && I0(x,y)==I0(x',y') && I1(x,y)==I1(x',y') && ... ) || G(x,y) == G(x',y'); + // + // assume false; + // + // } else { + // var oldHeap := $Heap; + // havoc $Heap; + // assume $HeapSucc(oldHeap, $Heap); + // (a) + // assume (forall o: ref, F: Field :: + // { $Heap[o,F] } + // $Heap[o,F] = oldHeap[o,F] || + // (exists x,y :: Range(x,y) && o == E(x,y) && F = f)); + // assume (forall x,y :: Range ==> $Heap[ E[$Heap:=oldHeap], F] == G[$Heap:=oldHeap]); (**) + // (b) + // assume (forall o: ref, F: Field :: + // { $Heap[o,F] } + // $Heap[o,F] = oldHeap[o,F] || + // (exists x,y :: Range(x,y) && o == A(x,y) && F = Index(I0,I1,...))); + // assume (forall x,y :: Range ==> $Heap[ A[$Heap:=oldHeap], Index(I0,I1,...)] == G[$Heap:=oldHeap]); (**) + // } + // + // Note: In order to get a good trigger for the quantifiers (**), we will attempt to make the parameters + // that select from $Heap in the LHS of the equalities as plain as possible. This involves taking the inverse + // of an expression, which isn't always easy or possible, so we settle for handling some common cases. In + // particular, we change: + // 0: forall i | R(i) { F(i).f := E(i); } + // 1: forall i | R(i) { A[F(i)] := E(i); } + // 2: forall i | R(i) { F(i)[N] := E(i); } + // where f is some field and A and N are expressions that do not depend on i, into: + // 0: forall j | Q(j) { j.f := E(F-1(j)); } + // 1: forall j | Q(j) { A[j] := E(F-1(j)); } + // 2: forall j | Q(j) { j[N] := E(F-1(j)); } + // where we ensure that, for all i and j: + // R(i) && j == F(i) <==> Q(j) && F-1(j) == i + // If the transformation succeeds, we use, respectively, j.f, A[j], and j[N] (each evaluated in the new heap) as + // the trigger of the quantifier generated. + + var substMap = SetupBoundVarsAsLocals(s.BoundVars, definedness, locals, etran); + Expression range = Substitute(s.Range, null, substMap); + TrStmt_CheckWellformed(range, definedness, locals, etran, false); + definedness.Add(TrAssumeCmd(s.Range.tok, etran.TrExpr(range))); + + var lhs = Substitute(s0.Lhs.Resolved, null, substMap); + TrStmt_CheckWellformed(lhs, definedness, locals, etran, false); + string description = GetObjFieldDetails(lhs, etran, out var obj, out var F); + var (lhsObj, lhsField) = lhs switch { + MemberSelectExpr e => (e.Obj, e.Member as Field), + SeqSelectExpr e => (e.Seq, null), + MultiSelectExpr e => (e.Array, null), + _ => throw new cce.UnreachableException() + }; + var desc = new PODesc.Modifiable(description, GetContextModifiesFrames(), lhsObj, lhsField); + definedness.Add(Assert(lhs.tok, Bpl.Expr.SelectTok(lhs.tok, etran.ModifiesFrame(lhs.tok), obj, F), + desc, definedness.Context)); + if (s0.Rhs is ExprRhs) { + var r = (ExprRhs)s0.Rhs; + var rhs = Substitute(r.Expr, null, substMap); + TrStmt_CheckWellformed(rhs, definedness, locals, etran, false); + // check nat restrictions for the RHS + Type lhsType; + if (lhs is MemberSelectExpr) { + lhsType = ((MemberSelectExpr)lhs).Type; + } else if (lhs is SeqSelectExpr) { + lhsType = ((SeqSelectExpr)lhs).Type; + } else { + lhsType = ((MultiSelectExpr)lhs).Type; + } + var translatedRhs = etran.TrExpr(rhs); + CheckSubrange(r.Tok, translatedRhs, rhs.Type, lhsType, rhs, definedness); + if (lhs is MemberSelectExpr) { + var fse = (MemberSelectExpr)lhs; + Contract.Assert(lhsField != null); + Check_NewRestrictions(fse.tok, fse.Obj, obj, lhsField, translatedRhs, definedness, etran); + } + } + + // check for duplicate LHSs + if (s0.Rhs is ExprRhs) { // if Rhs denotes a havoc, then no duplicate check is performed + var substMapPrime = SetupBoundVarsAsLocals(s.BoundVars, definedness, locals, etran); + var lhsPrime = Substitute(s0.Lhs.Resolved, null, substMapPrime); + range = Substitute(s.Range, null, substMapPrime); + definedness.Add(TrAssumeCmd(range.tok, etran.TrExpr(range))); + // assume !(x == x' && y == y'); + Bpl.Expr eqs = Bpl.Expr.True; + foreach (var bv in s.BoundVars) { + var x = substMap[bv]; + var xPrime = substMapPrime[bv]; + // TODO: in the following line, is the term equality okay, or does it have to include things like Set#Equal sometimes too? + eqs = BplAnd(eqs, Bpl.Expr.Eq(etran.TrExpr(x), etran.TrExpr(xPrime))); + } + definedness.Add(TrAssumeCmd(s.Tok, Bpl.Expr.Not(eqs))); + GetObjFieldDetails(lhsPrime, etran, out var objPrime, out var FPrime); + var Rhs = ((ExprRhs)s0.Rhs).Expr; + var rhs = etran.TrExpr(Substitute(Rhs, null, substMap)); + var rhsPrime = etran.TrExpr(Substitute(Rhs, null, substMapPrime)); + var lhsComponents = new List { lhsObj }; + if (lhs is SeqSelectExpr sse) { + lhsComponents.Add(sse.E0); + } else if (lhs is MultiSelectExpr multi) { + lhsComponents.AddRange(multi.Indices); + } + + definedness.Add(Assert(s0.Tok, + BplOr( + BplOr(Bpl.Expr.Neq(obj, objPrime), Bpl.Expr.Neq(F, FPrime)), + Bpl.Expr.Eq(rhs, rhsPrime)), + new PODesc.ForallLHSUnique(s.BoundVars, s.Range, lhsComponents, Rhs), definedness.Context)); + } + + definedness.Add(TrAssumeCmd(s.Tok, Bpl.Expr.False)); + + // Now for the translation of the update itself + + Bpl.IdentifierExpr prevHeap = GetPrevHeapVar_IdExpr(s.Tok, locals); + var prevEtran = new ExpressionTranslator(this, predef, prevHeap, etran.scope); + updater.Add(Bpl.Cmd.SimpleAssign(s.Tok, prevHeap, etran.HeapExpr)); + updater.Add(new Bpl.HavocCmd(s.Tok, new List { etran.HeapCastToIdentifierExpr })); + updater.Add(TrAssumeCmd(s.Tok, HeapSucc(prevHeap, etran.HeapExpr))); + + // Here comes: + // assume (forall o: ref, f: Field :: + // { $Heap[o,f] } + // $Heap[o,f] = oldHeap[o,f] || + // (exists x,y :: Range(x,y)[$Heap:=oldHeap] && + // o == Object(x,y)[$Heap:=oldHeap] && f == Field(x,y)[$Heap:=oldHeap])); + Bpl.BoundVariable oVar = new Bpl.BoundVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$o", predef.RefType)); + Bpl.IdentifierExpr o = new Bpl.IdentifierExpr(s.Tok, oVar); + Bpl.BoundVariable fVar = new Bpl.BoundVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$f", predef.FieldName(s.Tok))); + Bpl.IdentifierExpr f = new Bpl.IdentifierExpr(s.Tok, fVar); + Bpl.Expr heapOF = ReadHeap(s.Tok, etran.HeapExpr, o, f); + Bpl.Expr oldHeapOF = ReadHeap(s.Tok, prevHeap, o, f); + List freeOfAlloc = BoundedPool.HasBounds(s.Bounds, BoundedPool.PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc); + List xBvars = new List(); + var xBody = etran.TrBoundVariables(s.BoundVars, xBvars, false, freeOfAlloc); + xBody = BplAnd(xBody, prevEtran.TrExpr(s.Range)); + GetObjFieldDetails(s0.Lhs.Resolved, prevEtran, out var xObj, out var xField); + xBody = BplAnd(xBody, Bpl.Expr.Eq(o, xObj)); + xBody = BplAnd(xBody, Bpl.Expr.Eq(f, xField)); + //TRIG (exists k#2: int :: (k#2 == LitInt(0 - 3) || k#2 == LitInt(4)) && $o == read($prevHeap, this, _module.MyClass.arr) && $f == MultiIndexField(IndexField(i#0), j#0)) + Bpl.Expr xObjField = new Bpl.ExistsExpr(s.Tok, xBvars, xBody); // LL_TRIGGER + Bpl.Expr body = BplOr(Bpl.Expr.Eq(heapOF, oldHeapOF), xObjField); + var tr = new Trigger(s.Tok, true, new List() { heapOF }); + Bpl.Expr qq = new Bpl.ForallExpr(s.Tok, new List { }, new List { oVar, fVar }, null, tr, body); + updater.Add(TrAssumeCmd(s.Tok, qq)); + + if (s.EffectiveEnsuresClauses != null) { + foreach (ForallExpr expr in s.EffectiveEnsuresClauses) { + BinaryExpr term = (BinaryExpr)expr.Term; + Contract.Assert(term != null); + var e0 = ((BinaryExpr)term).E0.Resolved; + var e1 = ((BinaryExpr)term).E1; + qq = TrForall_NewValueAssumption(expr.tok, expr.BoundVars, expr.Bounds, expr.Range, e0, e1, expr.Attributes, etran, prevEtran); + updater.Add(TrAssumeCmd(s.Tok, qq)); + } + } + } + + /// + /// Generate: + /// assume (forall x,y :: Range(x,y)[$Heap:=oldHeap] ==> + /// $Heap[ Object(x,y)[$Heap:=oldHeap], Field(x,y)[$Heap:=oldHeap] ] == G[$Heap:=oldHeap] )); + /// where + /// x,y represent boundVars + /// Object(x,y) is the first part of lhs + /// Field(x,y) is the second part of lhs + /// G is rhs + /// If lhsAsTrigger is true, then use the LHS of the equality above as the trigger; otherwise, don't specify any trigger. + /// + private Bpl.Expr TrForall_NewValueAssumption(IToken tok, List boundVars, List bounds, Expression range, Expression lhs, Expression rhs, Attributes attributes, ExpressionTranslator etran, ExpressionTranslator prevEtran) { + Contract.Requires(tok != null); + Contract.Requires(boundVars != null); + Contract.Requires(bounds != null); + Contract.Requires(range != null); + Contract.Requires(lhs != null); + Contract.Requires(rhs != null); + Contract.Requires(etran != null); + Contract.Requires(prevEtran != null); + + List freeOfAlloc = BoundedPool.HasBounds(bounds, BoundedPool.PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc); + var xBvars = new List(); + Bpl.Expr xAnte = etran.TrBoundVariables(boundVars, xBvars, false, freeOfAlloc); + xAnte = BplAnd(xAnte, prevEtran.TrExpr(range)); + var g = prevEtran.TrExpr(rhs); + GetObjFieldDetails(lhs, prevEtran, out var obj, out var field); + var xHeapOF = ReadHeap(tok, etran.HeapExpr, obj, field); + + g = BoxIfNotNormallyBoxed(rhs.tok, g, rhs.Type); + + Bpl.Trigger tr = null; + var argsEtran = etran.WithNoLits(); + foreach (var aa in attributes.AsEnumerable()) { + if (aa.Name == "trigger") { + List tt = new List(); + foreach (var arg in aa.Args) { + if (arg == lhs) { + tt.Add(xHeapOF); + } else { + tt.Add(argsEtran.TrExpr(arg)); + } + } + tr = new Bpl.Trigger(tok, true, tt, tr); + } + } + return new Bpl.ForallExpr(tok, xBvars, tr, BplImp(xAnte, Bpl.Expr.Eq(xHeapOF, g))); + } + + IEnumerable TransitiveSubstatements(Statement s) { + yield return s; + foreach (var ss in s.SubStatements.SelectMany(TransitiveSubstatements)) { + yield return ss; + } + } + + void TrForallProof(ForallStmt forallStmt, BoogieStmtListBuilder definedness, BoogieStmtListBuilder exporter, + List locals, ExpressionTranslator etran) { + // Translate: + // forall (x,y | Range(x,y)) + // ensures Post(x,y); + // { + // Body; + // } + // as: + // if (*) { + // var x,y; + // havoc x,y; + // CheckWellformed( Range ); + // assume Range(x,y); + // CheckWellformed( Post ); + // Tr( Body ); // include only if there is a Body + // assert Post; // include only if there is a Body + // assume false; + // } else { + // assume (forall x,y :: Range(x,y) ==> Post(x,y)); + // } + + if (forallStmt.BoundVars.Count != 0) { + // Note, it would be nicer (and arguably more appropriate) to do a SetupBoundVarsAsLocals + // here (rather than a TrBoundVariables). However, there is currently no way to apply + // a substMap to a statement (in particular, to s.Body), so that doesn't work here. + List freeOfAlloc = BoundedPool.HasBounds(forallStmt.Bounds, BoundedPool.PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc); + + var bVars = new List(); + var typeAntecedent = etran.TrBoundVariables(forallStmt.BoundVars, bVars, true, freeOfAlloc); + locals.AddRange(bVars); + var havocIds = new List(); + foreach (Bpl.Variable bv in bVars) { + havocIds.Add(new Bpl.IdentifierExpr(forallStmt.Tok, bv)); + } + definedness.Add(new Bpl.HavocCmd(forallStmt.Tok, havocIds)); + definedness.Add(TrAssumeCmd(forallStmt.Tok, typeAntecedent)); + } + TrStmt_CheckWellformed(forallStmt.Range, definedness, locals, etran, false); + definedness.Add(TrAssumeCmdWithDependencies(etran, forallStmt.Range.tok, forallStmt.Range, "forall statement range")); + + var ensuresDefinedness = new BoogieStmtListBuilder(this, options, definedness.Context); + foreach (var ens in forallStmt.Ens) { + TrStmt_CheckWellformed(ens.E, ensuresDefinedness, locals, etran, false); + ensuresDefinedness.Add(TrAssumeCmdWithDependencies(etran, ens.E.tok, ens.E, "forall statement ensures clause")); + } + PathAsideBlock(forallStmt.Tok, ensuresDefinedness, definedness); + + if (forallStmt.Body != null) { + TrStmt(forallStmt.Body, definedness, locals, etran); + + // check that postconditions hold + foreach (var ens in forallStmt.Ens) { + foreach (var split in TrSplitExpr(definedness.Context, ens.E, etran, true, out var splitHappened)) { + if (split.IsChecked) { + definedness.Add(Assert(split.Tok, split.E, new PODesc.ForallPostcondition(ens.E), definedness.Context)); + } + } + } + } + + definedness.Add(TrAssumeCmd(forallStmt.Tok, Bpl.Expr.False)); + + // Now for the other branch, where the ensures clauses are exported. + // If the forall body has side effect such as call to a reveal function, + // it needs to be exported too. + var se = forallStmt.Body == null ? Bpl.Expr.True : TrFunctionSideEffect(forallStmt.Body, etran); + var substMap = new Dictionary(); + var p = Substitute(forallStmt.EffectiveEnsuresClauses[0], null, substMap); + var qq = etran.TrExpr(p); + if (forallStmt.BoundVars.Count != 0) { + exporter.Add(TrAssumeCmd(forallStmt.Tok, BplAnd(se, qq))); + } else { + exporter.Add(TrAssumeCmd(forallStmt.Tok, BplAnd(se, ((Bpl.ForallExpr)qq).Body))); + } + } +} \ No newline at end of file diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs new file mode 100644 index 00000000000..5634dca404a --- /dev/null +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs @@ -0,0 +1,436 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; +using DafnyCore.Verifier; +using Microsoft.Boogie; +using Bpl = Microsoft.Boogie; +using PODesc = Microsoft.Dafny.ProofObligationDescription; + +namespace Microsoft.Dafny; + +public partial class BoogieGenerator { + + private void TrAlternativeLoopStmt(AlternativeLoopStmt stmt, BoogieStmtListBuilder builder, List locals, + ExpressionTranslator etran) + { + AddComment(builder, stmt, "alternative loop statement"); + var tru = Expression.CreateBoolLiteral(stmt.Tok, true); + TrLoop(stmt, tru, + delegate (BoogieStmtListBuilder bld, ExpressionTranslator e) { + TrAlternatives(stmt.Alternatives, stmt.Tok, + b => { + foreach (var _ in Enumerable.Range(0, b.Context.ScopeDepth - builder.Context.ScopeDepth)) { + b.Add(new ChangeScope(stmt.Tok, ChangeScope.Modes.Pop)); + } + b.Add(new Bpl.BreakCmd(stmt.Tok, null)); + }, + bld, locals, e, stmt.IsGhost); + InsertContinueTarget(stmt, bld); + }, + builder, locals, etran); + } + + private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + Contract.Requires(stmt != null); + Contract.Requires(builder != null); + Contract.Requires(locals != null); + Contract.Requires(etran != null); + + AddComment(builder, stmt, "for-loop statement"); + + var indexVar = stmt.LoopIndex; + var indexVarName = indexVar.AssignUniqueName(currentDeclaration.IdGenerator); + var dIndex = new IdentifierExpr(indexVar.tok, indexVar); + var bIndexVar = new Bpl.LocalVariable(indexVar.tok, new Bpl.TypedIdent(indexVar.Tok, indexVarName, TrType(indexVar.Type))); + locals.Add(bIndexVar); + var bIndex = new Bpl.IdentifierExpr(indexVar.tok, indexVarName); + + var lo = stmt.GoingUp ? stmt.Start : stmt.End; + var hi = stmt.GoingUp ? stmt.End : stmt.Start; + Expression dLo = null; + Expression dHi = null; + Bpl.IdentifierExpr bLo = null; + Bpl.IdentifierExpr bHi = null; + if (lo != null) { + var name = indexVarName + "#lo"; + var bLoVar = new Bpl.LocalVariable(lo.tok, new Bpl.TypedIdent(lo.tok, name, Bpl.Type.Int)); + locals.Add(bLoVar); + bLo = new Bpl.IdentifierExpr(lo.tok, name); + CheckWellformed(lo, new WFOptions(null, false), locals, builder, etran); + builder.Add(Bpl.Cmd.SimpleAssign(lo.tok, bLo, etran.TrExpr(lo))); + dLo = new BoogieWrapper(bLo, lo.Type); + } + if (hi != null) { + var name = indexVarName + "#hi"; + var bHiVar = new Bpl.LocalVariable(hi.tok, new Bpl.TypedIdent(hi.tok, name, Bpl.Type.Int)); + locals.Add(bHiVar); + bHi = new Bpl.IdentifierExpr(hi.tok, name); + CheckWellformed(hi, new WFOptions(null, false), locals, builder, etran); + builder.Add(Bpl.Cmd.SimpleAssign(hi.tok, bHi, etran.TrExpr(hi))); + dHi = new BoogieWrapper(bHi, hi.Type); + } + + // check lo <= hi + if (lo != null && hi != null) { + builder.Add(Assert(lo.tok, Bpl.Expr.Le(bLo, bHi), new PODesc.ForRangeBoundsValid(lo, hi), builder.Context)); + } + // check forall x :: lo <= x <= hi ==> Is(x, typ) + { + // The check, if needed, is performed like this: + // var x: int; + // havoc x; + // assume lo <= x <= hi; + // assert Is(x, typ); + var tok = indexVar.tok; + var name = indexVarName + "#x"; + var xVar = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, name, Bpl.Type.Int)); + var x = new Bpl.IdentifierExpr(tok, name); + + var sourceBoundVar = new BoundVar(Token.NoToken, "x", Type.Int); + var checkContext = MakeNumericBoundsSubrangeCheckContext(sourceBoundVar, dLo, dHi); + var cre = GetSubrangeCheck( + x.tok, x, Type.Int, indexVar.Type, + new IdentifierExpr(Token.NoToken, sourceBoundVar), + checkContext, out var desc); + + if (cre != null) { + locals.Add(xVar); + builder.Add(new Bpl.HavocCmd(tok, new List() { x })); + builder.Add(new Bpl.AssumeCmd(tok, ForLoopBounds(x, bLo, bHi))); + List dafnyRangeBounds = new(); + if (lo != null) { + dafnyRangeBounds.Add(new BinaryExpr(stmt.tok, BinaryExpr.Opcode.Le, lo, dIndex)); + } + if (hi != null) { + dafnyRangeBounds.Add(new BinaryExpr(stmt.tok, BinaryExpr.Opcode.Le, dIndex, hi)); + } + + Expression dafnyRange = dafnyRangeBounds.Count == 1 + ? dafnyRangeBounds[0] + : new BinaryExpr(stmt.tok, BinaryExpr.Opcode.And, dafnyRangeBounds[0], dafnyRangeBounds[1]); + var dafnyAssertion = new ForallExpr(stmt.tok, stmt.RangeToken, new List { indexVar }, + dafnyRange, new TypeTestExpr(indexVar.tok, dIndex, indexVar.Type), null); + builder.Add(Assert(tok, cre, new PODesc.ForRangeAssignable(desc, dafnyAssertion), builder.Context)); + } + } + + // initialize the index variable + builder.Add(Bpl.Cmd.SimpleAssign(indexVar.tok, bIndex, stmt.GoingUp ? bLo : bHi)); + + // build the guard expression + Expression guard; + if (lo == null || hi == null) { + guard = LiteralExpr.CreateBoolLiteral(stmt.Tok, true); + } else { + guard = Expression.CreateNot(stmt.Tok, Expression.CreateEq(dIndex, stmt.GoingUp ? dHi : dLo, indexVar.Type)); + } + + // free invariant lo <= i <= hi + var freeInvariant = ForLoopBounds(bIndex, bLo, bHi); + + BodyTranslator bodyTr = null; + if (stmt.Body != null) { + bodyTr = delegate (BoogieStmtListBuilder bld, ExpressionTranslator e) { + CurrentIdGenerator.Push(); + if (!stmt.GoingUp) { + bld.Add(Bpl.Cmd.SimpleAssign(stmt.Tok, bIndex, Bpl.Expr.Sub(bIndex, Bpl.Expr.Literal(1)))); + } + TrStmt(stmt.Body, bld, locals, e); + InsertContinueTarget(stmt, bld); + if (stmt.GoingUp) { + bld.Add(Bpl.Cmd.SimpleAssign(stmt.Tok, bIndex, Bpl.Expr.Add(bIndex, Bpl.Expr.Literal(1)))); + } + CurrentIdGenerator.Pop(); + }; + } + + TrLoop(stmt, guard, bodyTr, builder, locals, etran, freeInvariant, stmt.Decreases.Expressions.Count != 0); + } + + private void TrWhileStmt(WhileStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + Contract.Requires(stmt != null); + Contract.Requires(builder != null); + Contract.Requires(locals != null); + Contract.Requires(etran != null); + + AddComment(builder, stmt, "while statement"); + this.fuelContext = FuelSetting.ExpandFuelContext(stmt.Attributes, stmt.Tok, this.fuelContext, this.reporter); + DefineFuelConstant(stmt.Tok, stmt.Attributes, builder, etran); + BodyTranslator bodyTr = null; + if (stmt.Body != null) { + bodyTr = delegate (BoogieStmtListBuilder bld, ExpressionTranslator e) { + CurrentIdGenerator.Push(); + TrStmt(stmt.Body, bld, locals, e); + InsertContinueTarget(stmt, bld); + CurrentIdGenerator.Pop(); + }; + } + TrLoop(stmt, stmt.Guard, bodyTr, builder, locals, etran); + this.fuelContext = FuelSetting.PopFuelContext(); + } + + void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, + BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + Bpl.Expr freeInvariant = null, bool includeTerminationCheck = true) { + Contract.Requires(s != null); + Contract.Requires(builder != null); + Contract.Requires(locals != null); + Contract.Requires(etran != null); + + s.ScopeDepth = builder.Context.ScopeDepth; + + var suffix = CurrentIdGenerator.FreshId("loop#"); + + var theDecreases = s.Decreases.Expressions; + + Bpl.LocalVariable preLoopHeapVar = new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$PreLoopHeap$" + suffix, predef.HeapType)); + locals.Add(preLoopHeapVar); + Bpl.IdentifierExpr preLoopHeap = new Bpl.IdentifierExpr(s.Tok, preLoopHeapVar); + ExpressionTranslator etranPreLoop = new ExpressionTranslator(this, predef, preLoopHeap, etran.scope); + ExpressionTranslator updatedFrameEtran; + string loopFrameName = FrameVariablePrefix + suffix; + if (s.Mod.Expressions != null) { + updatedFrameEtran = etran.WithModifiesFrame(loopFrameName); + } else { + updatedFrameEtran = etran; + } + + if (s.Mod.Expressions != null) { // check well-formedness and that the modifies is a subset + CheckFrameWellFormed(new WFOptions(), s.Mod.Expressions, locals, builder, etran); + var desc = new PODesc.ModifyFrameSubset("loop modifies clause", s.Mod.Expressions, GetContextModifiesFrames()); + CheckFrameSubset(s.Tok, s.Mod.Expressions, null, null, etran, etran.ModifiesFrame(s.Tok), builder, desc, null); + DefineFrame(s.Tok, etran.ModifiesFrame(s.Tok), s.Mod.Expressions, builder, locals, loopFrameName); + } + builder.Add(Bpl.Cmd.SimpleAssign(s.Tok, preLoopHeap, etran.HeapExpr)); + + var daTrackersMonotonicity = new List>(); + foreach (var dat in DefiniteAssignmentTrackers.Values) { // TODO: the order is non-deterministic and may change between invocations of Dafny + var preLoopDat = new Bpl.LocalVariable(dat.tok, new Bpl.TypedIdent(dat.tok, "preLoop$" + suffix + "$" + dat.Name, dat.Type)); + locals.Add(preLoopDat); + var ie = new Bpl.IdentifierExpr(s.Tok, preLoopDat); + daTrackersMonotonicity.Add(new Tuple(ie, dat)); + builder.Add(Bpl.Cmd.SimpleAssign(s.Tok, ie, dat)); + } + + List initDecr = null; + if (!Contract.Exists(theDecreases, e => e is WildcardExpr)) { + initDecr = RecordDecreasesValue(theDecreases, builder, locals, etran, "$decr_init$" + suffix); + } + + // The variable w is used to coordinate the definedness checking of the loop invariant. + // It is also used for body-less loops to turn off invariant checking after the generated body. + Bpl.LocalVariable wVar = new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$w$" + suffix, Bpl.Type.Bool)); + Bpl.IdentifierExpr w = new Bpl.IdentifierExpr(s.Tok, wVar); + locals.Add(wVar); + // havoc w; + builder.Add(new Bpl.HavocCmd(s.Tok, new List { w })); + + List invariants = new List(); + if (freeInvariant != null) { + invariants.Add(new Bpl.AssumeCmd(freeInvariant.tok, freeInvariant)); + } + BoogieStmtListBuilder invDefinednessBuilder = new BoogieStmtListBuilder(this, options, builder.Context); + foreach (AttributedExpression loopInv in s.Invariants) { + var (errorMessage, successMessage) = CustomErrorMessage(loopInv.Attributes); + TrStmt_CheckWellformed(loopInv.E, invDefinednessBuilder, locals, etran, false); + invDefinednessBuilder.Add(TrAssumeCmdWithDependencies(etran, loopInv.E.tok, loopInv.E, "loop invariant")); + + invariants.Add(TrAssumeCmd(loopInv.E.tok, BplImp(w, etran.CanCallAssumption(loopInv.E)))); + var ss = TrSplitExpr(builder.Context, loopInv.E, etran, false, out var splitHappened); + if (!splitHappened) { + var wInv = BplImp(w, etran.TrExpr(loopInv.E)); + invariants.Add(Assert(loopInv.E.tok, wInv, new PODesc.LoopInvariant(loopInv.E, errorMessage, successMessage), builder.Context)); + } else { + foreach (var split in ss) { + var wInv = Bpl.Expr.Binary(split.E.tok, BinaryOperator.Opcode.Imp, w, split.E); + if (split.IsChecked) { + invariants.Add(Assert(split.Tok, wInv, new PODesc.LoopInvariant(loopInv.E, errorMessage, successMessage), builder.Context)); // TODO: it would be fine to have this use {:subsumption 0} + } else { + var cmd = TrAssumeCmd(split.E.tok, wInv); + proofDependencies?.AddProofDependencyId(cmd, loopInv.E.tok, new InvariantDependency(loopInv.E)); + invariants.Add(cmd); + } + } + } + } + // check definedness of decreases clause + foreach (Expression e in theDecreases) { + TrStmt_CheckWellformed(e, invDefinednessBuilder, locals, etran, true); + } + if (codeContext is IMethodCodeContext) { + var modifiesClause = ((IMethodCodeContext)codeContext).Modifies.Expressions; + if (codeContext is IteratorDecl) { + // add "this" to the explicit modifies clause + var explicitModifies = modifiesClause; + modifiesClause = new List(); + modifiesClause.Add(new FrameExpression(s.Tok, new ThisExpr((IteratorDecl)codeContext), null)); + modifiesClause.AddRange(explicitModifies); + } + // include boilerplate invariants + foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(s.Tok, modifiesClause, s.IsGhost, codeContext.AllowsAllocation, etranPreLoop, etran, etran.Old)) { + if (tri.IsFree) { + invariants.Add(TrAssumeCmd(s.Tok, tri.Expr)); + } else { + Contract.Assert(tri.ErrorMessage != null); // follows from BoilerplateTriple invariant + invariants.Add(Assert(s.Tok, tri.Expr, new PODesc.BoilerplateTriple(tri.ErrorMessage, tri.SuccessMessage, tri.Comment), builder.Context)); + } + } + // add a free invariant which says that the heap hasn't changed outside of the modifies clause. + invariants.Add(TrAssumeCmd(s.Tok, FrameConditionUsingDefinedFrame(s.Tok, etranPreLoop, etran, updatedFrameEtran, updatedFrameEtran.ModifiesFrame(s.Tok)))); + // for iterators, add "fresh(_new)" as an invariant + if (codeContext is IteratorDecl iter) { + var th = new ThisExpr(iter); + var thisDotNew = new MemberSelectExpr(s.Tok, th, iter.Member_New); + var fr = new FreshExpr(s.Tok, thisDotNew); + fr.Type = Type.Bool; + invariants.Add(TrAssertCmd(s.Tok, etran.TrExpr(fr))); + } + } + + // include a free invariant that says that all definite-assignment trackers have only become more "true" + foreach (var pair in daTrackersMonotonicity) { + Bpl.Expr monotonic = BplImp(pair.Item1, pair.Item2); + invariants.Add(TrAssumeCmd(s.Tok, monotonic)); + } + + // include a free invariant that says that all completed iterations so far have only decreased the termination metric + if (initDecr != null) { + var toks = new List(); + var decrs = new List(); + var decrsDafny = new List(); + var initDecrsDafny = new List(); + var prevGhostLocals = new List(); + foreach (Expression e in theDecreases) { + toks.Add(e.tok); + decrsDafny.Add(e); + decrs.Add(etran.TrExpr(e)); + var (prevVars, eInit) = TranslateToLoopEntry(s, e, "LoopEntry"); + prevGhostLocals.AddRange(prevVars); + initDecrsDafny.Add(eInit); + } + Bpl.Expr decrCheck = DecreasesCheck(toks, prevGhostLocals, decrsDafny, initDecrsDafny, decrs, initDecr, + null, null, true, false); + invariants.Add(TrAssumeCmd(s.Tok, decrCheck)); + } + + var loopBodyBuilder = new BoogieStmtListBuilder(this, options, builder.Context); + loopBodyBuilder.AddCaptureState(s.Tok, true, CaptureStateExtensions.AfterLoopIterationsStateMarker); + + // As the first thing inside the loop, generate: if (!w) { CheckWellformed(inv); assume false; } + invDefinednessBuilder.Add(TrAssumeCmd(s.Tok, Bpl.Expr.False)); + loopBodyBuilder.Add(new Bpl.IfCmd(s.Tok, Bpl.Expr.Not(w), invDefinednessBuilder.Collect(s.Tok), null, null)); + + // Generate: CheckWellformed(guard); if (!guard) { break; } + // but if this is a body-less loop, put all of that inside: if (*) { ... } + // Without this, Boogie's abstract interpreter may figure out that the loop guard is always false + // on entry to the loop, and then Boogie wouldn't consider this a loop at all. (See also comment + // in methods GuardAlwaysHoldsOnEntry_BodyLessLoop and GuardAlwaysHoldsOnEntry_LoopWithBody in + // Test/dafny0/DirtyLoops.dfy.) + var isBodyLessLoop = s is OneBodyLoopStmt { BodySurrogate: { } }; + var whereToBuildLoopGuard = isBodyLessLoop ? new BoogieStmtListBuilder(this, options, builder.Context) : loopBodyBuilder; + Bpl.Expr guard = null; + if (Guard != null) { + TrStmt_CheckWellformed(Guard, whereToBuildLoopGuard, locals, etran, true); + guard = Bpl.Expr.Not(etran.TrExpr(Guard)); + } + var guardBreak = new BoogieStmtListBuilder(this, options, builder.Context); + guardBreak.Add(new Bpl.BreakCmd(s.Tok, null)); + whereToBuildLoopGuard.Add(new Bpl.IfCmd(s.Tok, guard, guardBreak.Collect(s.Tok), null, null)); + if (isBodyLessLoop) { + loopBodyBuilder.Add(new Bpl.IfCmd(s.Tok, null, whereToBuildLoopGuard.Collect(s.Tok), null, null)); + } + + if (bodyTr != null) { + // termination checking + if (Contract.Exists(theDecreases, e => e is WildcardExpr)) { + // omit termination checking for this loop + bodyTr(loopBodyBuilder, updatedFrameEtran); + } else { + List oldBfs = RecordDecreasesValue(theDecreases, loopBodyBuilder, locals, etran, "$decr$" + suffix); + // time for the actual loop body + bodyTr(loopBodyBuilder, updatedFrameEtran); + // check definedness of decreases expressions + var toks = new List(); + var decrs = new List(); + var decrsDafny = new List(); + var initDecrsDafny = new List(); + var prevGhostLocals = new List(); + foreach (Expression e in theDecreases) { + toks.Add(e.tok); + // Note: the label "LoopEntry" doesn't exist in the program, and is + // useful only for explanatory purposes. + decrsDafny.Add(e); + var (prevVars, eInit) = TranslateToLoopEntry(s, e, "LoopEntry"); + prevGhostLocals.AddRange(prevVars); + initDecrsDafny.Add(eInit); + decrs.Add(etran.TrExpr(e)); + } + if (includeTerminationCheck) { + AddComment(loopBodyBuilder, s, "loop termination check"); + Bpl.Expr decrCheck = DecreasesCheck(toks, prevGhostLocals, decrsDafny, initDecrsDafny, decrs, oldBfs, + loopBodyBuilder, " at end of loop iteration", false, false); + var description = new + PODesc.Terminates(s.InferredDecreases, prevGhostLocals, null, initDecrsDafny, theDecreases, false); + loopBodyBuilder.Add(Assert(s.Tok, decrCheck, description, builder.Context)); + } + } + } else if (isBodyLessLoop) { + var bodySurrogate = ((OneBodyLoopStmt)s).BodySurrogate; + // This is a body-less loop. Havoc the targets and then set w to false, to make the loop-invariant + // maintenance check vaccuous. + var bplTargets = bodySurrogate.LocalLoopTargets.ConvertAll(v => TrVar(s.Tok, v)); + if (bodySurrogate.UsesHeap) { + bplTargets.Add(etran.HeapCastToIdentifierExpr); + } + loopBodyBuilder.Add(new Bpl.HavocCmd(s.Tok, bplTargets)); + loopBodyBuilder.Add(Bpl.Cmd.SimpleAssign(s.Tok, w, Bpl.Expr.False)); + } + // Finally, assume the well-formedness of the invariant (which has been checked once and for all above), so that the check + // of invariant-maintenance can use the appropriate canCall predicates. Note, it is important (see Test/git-issues/git-issue-1812.dfy) + // that each CanCall assumption uses the preceding invariants as antecedents--this is achieved by treating all "invariant" + // declarations as one big conjunction, because then CanCallAssumption will add the needed antecedents. + if (s.Invariants.Any()) { + var allInvariants = s.Invariants.Select(inv => inv.E).Aggregate((a, b) => Expression.CreateAnd(a, b)); + loopBodyBuilder.Add(TrAssumeCmd(s.Tok, etran.CanCallAssumption(allInvariants))); + } + + Bpl.StmtList body = loopBodyBuilder.Collect(s.Tok); + builder.Add(new Bpl.WhileCmd(s.Tok, Bpl.Expr.True, invariants, new List(), body)); + } + + // Return the version of e that holds at the beginnging of the loop, + // Along with the local variable assignments that need to happen at + // the beginning of the loop for it to be valid. + private (List, Expression) TranslateToLoopEntry(LoopStmt loop, Expression e, string loopLabel) { + var prevGhostLocals = new List(); + Expression olde = new OldExpr(e.tok, e, loopLabel) { + Type = e.Type + }; + + var subStmts = TransitiveSubstatements(loop); + var modifiedVars = + subStmts + .OfType() + .Select(s => s.Lhs) + .OfType(); + foreach (var ie in modifiedVars) { + var prevName = $"prev_{ie.Name}"; + var prevDecl = Statement.CreateLocalVariable(RangeToken.NoToken, prevName, ie); + var prevRef = Expression.CreateIdentExpr(prevDecl.Locals[0]); + olde = Substitute(olde, ie.Var, prevRef); + prevGhostLocals.Add(prevDecl); + } + + return (prevGhostLocals, olde); + + } + + void InsertContinueTarget(LoopStmt loop, BoogieStmtListBuilder builder) { + Contract.Requires(loop != null); + Contract.Requires(builder != null); + if (loop.Labels != null) { + builder.AddLabelCmd("continue_" + loop.Labels.Data.AssignUniqueId(CurrentIdGenerator)); + } + } +} \ No newline at end of file diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs index b9312cfffcb..a90ef8dd0a9 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs @@ -309,24 +309,8 @@ public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, } else if (stmt is WhileStmt whileStmt) { TrWhileStmt(whileStmt, builder, locals, etran); - } else if (stmt is AlternativeLoopStmt) { - AddComment(builder, stmt, "alternative loop statement"); - var s = (AlternativeLoopStmt)stmt; - var tru = Expression.CreateBoolLiteral(s.Tok, true); - TrLoop(s, tru, - delegate (BoogieStmtListBuilder bld, ExpressionTranslator e) { - TrAlternatives(s.Alternatives, s.Tok, - b => { - foreach (var _ in Enumerable.Range(0, b.Context.ScopeDepth - builder.Context.ScopeDepth)) { - b.Add(new ChangeScope(s.Tok, ChangeScope.Modes.Pop)); - } - b.Add(new Bpl.BreakCmd(s.Tok, null)); - }, - bld, locals, e, stmt.IsGhost); - InsertContinueTarget(s, bld); - }, - builder, locals, etran); - + } else if (stmt is AlternativeLoopStmt alternativeLoopStmt) { + TrAlternativeLoopStmt(alternativeLoopStmt, builder, locals, etran); } else if (stmt is ForLoopStmt forLoopStmt) { TrForLoop(forLoopStmt, builder, locals, etran); @@ -429,7 +413,6 @@ void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { } } - 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. @@ -517,65 +500,6 @@ void TrVarDeclStmt(VarDeclStmt varDeclStmt, BoogieStmtListBuilder builder, List< } } - private void TrForallStmt(BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, - ForallStmt forallStmt) { - this.fuelContext = FuelSetting.ExpandFuelContext(forallStmt.Attributes, forallStmt.Tok, this.fuelContext, this.reporter); - - CurrentIdGenerator.Push(); - if (forallStmt.Kind == ForallStmt.BodyKind.Assign) { - AddComment(builder, forallStmt, "forall statement (assign)"); - Contract.Assert(forallStmt.Ens.Count == 0); - if (forallStmt.BoundVars.Count == 0) { - TrStmt(forallStmt.Body, builder, locals, etran); - } else { - var s0 = (AssignStmt)forallStmt.S0; - var definedness = new BoogieStmtListBuilder(this, options, builder.Context); - var updater = new BoogieStmtListBuilder(this, options, builder.Context); - DefineFuelConstant(forallStmt.Tok, forallStmt.Attributes, definedness, etran); - TrForallAssign(forallStmt, s0, definedness, updater, locals, etran); - // All done, so put the two pieces together - builder.Add(new Bpl.IfCmd(forallStmt.Tok, null, definedness.Collect(forallStmt.Tok), null, updater.Collect(forallStmt.Tok))); - builder.AddCaptureState(forallStmt); - } - - } else if (forallStmt.Kind == ForallStmt.BodyKind.Call) { - AddComment(builder, forallStmt, "forall statement (call)"); - Contract.Assert(forallStmt.Ens.Count == 0); - if (forallStmt.BoundVars.Count == 0) { - Contract.Assert(LiteralExpr.IsTrue(forallStmt.Range)); // follows from the invariant of ForallStmt - TrStmt(forallStmt.Body, builder, locals, etran); - } else { - var s0 = (CallStmt)forallStmt.S0; - if (Attributes.Contains(forallStmt.Attributes, "_trustWellformed")) { - TrForallStmtCall(forallStmt.Tok, forallStmt.BoundVars, forallStmt.Bounds, forallStmt.Range, null, forallStmt.EffectiveEnsuresClauses, s0, null, builder, locals, etran); - } else { - var definedness = new BoogieStmtListBuilder(this, options, builder.Context); - DefineFuelConstant(forallStmt.Tok, forallStmt.Attributes, definedness, etran); - var exporter = new BoogieStmtListBuilder(this, options, builder.Context); - TrForallStmtCall(forallStmt.Tok, forallStmt.BoundVars, forallStmt.Bounds, forallStmt.Range, null, forallStmt.EffectiveEnsuresClauses, s0, definedness, exporter, locals, etran); - // All done, so put the two pieces together - builder.Add(new Bpl.IfCmd(forallStmt.Tok, null, definedness.Collect(forallStmt.Tok), null, exporter.Collect(forallStmt.Tok))); - } - builder.AddCaptureState(forallStmt); - } - - } else if (forallStmt.Kind == ForallStmt.BodyKind.Proof) { - AddComment(builder, forallStmt, "forall statement (proof)"); - var definedness = new BoogieStmtListBuilder(this, options, builder.Context); - var exporter = new BoogieStmtListBuilder(this, options, builder.Context); - DefineFuelConstant(forallStmt.Tok, forallStmt.Attributes, definedness, etran); - TrForallProof(forallStmt, definedness, exporter, locals, etran); - // All done, so put the two pieces together - builder.Add(new Bpl.IfCmd(forallStmt.Tok, null, definedness.Collect(forallStmt.Tok), null, exporter.Collect(forallStmt.Tok))); - builder.AddCaptureState(forallStmt); - - } else { - Contract.Assert(false); // unexpected kind - } - CurrentIdGenerator.Pop(); - this.fuelContext = FuelSetting.PopFuelContext(); - } - private void TranslateRevealStmt(BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, HideRevealStmt revealStmt) { AddComment(builder, revealStmt, "hide/reveal statement"); @@ -808,123 +732,6 @@ void FillMissingCases(IMatch match) { } } - private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { - Contract.Requires(stmt != null); - Contract.Requires(builder != null); - Contract.Requires(locals != null); - Contract.Requires(etran != null); - - AddComment(builder, stmt, "for-loop statement"); - - var indexVar = stmt.LoopIndex; - var indexVarName = indexVar.AssignUniqueName(currentDeclaration.IdGenerator); - var dIndex = new IdentifierExpr(indexVar.tok, indexVar); - var bIndexVar = new Bpl.LocalVariable(indexVar.tok, new Bpl.TypedIdent(indexVar.Tok, indexVarName, TrType(indexVar.Type))); - locals.Add(bIndexVar); - var bIndex = new Bpl.IdentifierExpr(indexVar.tok, indexVarName); - - var lo = stmt.GoingUp ? stmt.Start : stmt.End; - var hi = stmt.GoingUp ? stmt.End : stmt.Start; - Expression dLo = null; - Expression dHi = null; - Bpl.IdentifierExpr bLo = null; - Bpl.IdentifierExpr bHi = null; - if (lo != null) { - var name = indexVarName + "#lo"; - var bLoVar = new Bpl.LocalVariable(lo.tok, new Bpl.TypedIdent(lo.tok, name, Bpl.Type.Int)); - locals.Add(bLoVar); - bLo = new Bpl.IdentifierExpr(lo.tok, name); - CheckWellformed(lo, new WFOptions(null, false), locals, builder, etran); - builder.Add(Bpl.Cmd.SimpleAssign(lo.tok, bLo, etran.TrExpr(lo))); - dLo = new BoogieWrapper(bLo, lo.Type); - } - if (hi != null) { - var name = indexVarName + "#hi"; - var bHiVar = new Bpl.LocalVariable(hi.tok, new Bpl.TypedIdent(hi.tok, name, Bpl.Type.Int)); - locals.Add(bHiVar); - bHi = new Bpl.IdentifierExpr(hi.tok, name); - CheckWellformed(hi, new WFOptions(null, false), locals, builder, etran); - builder.Add(Bpl.Cmd.SimpleAssign(hi.tok, bHi, etran.TrExpr(hi))); - dHi = new BoogieWrapper(bHi, hi.Type); - } - - // check lo <= hi - if (lo != null && hi != null) { - builder.Add(Assert(lo.tok, Bpl.Expr.Le(bLo, bHi), new PODesc.ForRangeBoundsValid(lo, hi), builder.Context)); - } - // check forall x :: lo <= x <= hi ==> Is(x, typ) - { - // The check, if needed, is performed like this: - // var x: int; - // havoc x; - // assume lo <= x <= hi; - // assert Is(x, typ); - var tok = indexVar.tok; - var name = indexVarName + "#x"; - var xVar = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, name, Bpl.Type.Int)); - var x = new Bpl.IdentifierExpr(tok, name); - - var sourceBoundVar = new BoundVar(Token.NoToken, "x", Type.Int); - var checkContext = MakeNumericBoundsSubrangeCheckContext(sourceBoundVar, dLo, dHi); - var cre = GetSubrangeCheck( - x.tok, x, Type.Int, indexVar.Type, - new IdentifierExpr(Token.NoToken, sourceBoundVar), - checkContext, out var desc); - - if (cre != null) { - locals.Add(xVar); - builder.Add(new Bpl.HavocCmd(tok, new List() { x })); - builder.Add(new Bpl.AssumeCmd(tok, ForLoopBounds(x, bLo, bHi))); - List dafnyRangeBounds = new(); - if (lo != null) { - dafnyRangeBounds.Add(new BinaryExpr(stmt.tok, BinaryExpr.Opcode.Le, lo, dIndex)); - } - if (hi != null) { - dafnyRangeBounds.Add(new BinaryExpr(stmt.tok, BinaryExpr.Opcode.Le, dIndex, hi)); - } - - Expression dafnyRange = dafnyRangeBounds.Count == 1 - ? dafnyRangeBounds[0] - : new BinaryExpr(stmt.tok, BinaryExpr.Opcode.And, dafnyRangeBounds[0], dafnyRangeBounds[1]); - var dafnyAssertion = new ForallExpr(stmt.tok, stmt.RangeToken, new List { indexVar }, - dafnyRange, new TypeTestExpr(indexVar.tok, dIndex, indexVar.Type), null); - builder.Add(Assert(tok, cre, new PODesc.ForRangeAssignable(desc, dafnyAssertion), builder.Context)); - } - } - - // initialize the index variable - builder.Add(Bpl.Cmd.SimpleAssign(indexVar.tok, bIndex, stmt.GoingUp ? bLo : bHi)); - - // build the guard expression - Expression guard; - if (lo == null || hi == null) { - guard = LiteralExpr.CreateBoolLiteral(stmt.Tok, true); - } else { - guard = Expression.CreateNot(stmt.Tok, Expression.CreateEq(dIndex, stmt.GoingUp ? dHi : dLo, indexVar.Type)); - } - - // free invariant lo <= i <= hi - var freeInvariant = ForLoopBounds(bIndex, bLo, bHi); - - BodyTranslator bodyTr = null; - if (stmt.Body != null) { - bodyTr = delegate (BoogieStmtListBuilder bld, ExpressionTranslator e) { - CurrentIdGenerator.Push(); - if (!stmt.GoingUp) { - bld.Add(Bpl.Cmd.SimpleAssign(stmt.Tok, bIndex, Bpl.Expr.Sub(bIndex, Bpl.Expr.Literal(1)))); - } - TrStmt(stmt.Body, bld, locals, e); - InsertContinueTarget(stmt, bld); - if (stmt.GoingUp) { - bld.Add(Bpl.Cmd.SimpleAssign(stmt.Tok, bIndex, Bpl.Expr.Add(bIndex, Bpl.Expr.Literal(1)))); - } - CurrentIdGenerator.Pop(); - }; - } - - TrLoop(stmt, guard, bodyTr, builder, locals, etran, freeInvariant, stmt.Decreases.Expressions.Count != 0); - } - private static SubrangeCheckContext MakeNumericBoundsSubrangeCheckContext(BoundVar bvar, Expression lo, Expression hi) { var source = new IdentifierExpr(Token.NoToken, bvar); var loBound = lo == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Le, lo, source); @@ -946,28 +753,6 @@ private static SubrangeCheckContext MakeNumericBoundsSubrangeCheckContext(BoundV return CheckContext; } - private void TrWhileStmt(WhileStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { - Contract.Requires(stmt != null); - Contract.Requires(builder != null); - Contract.Requires(locals != null); - Contract.Requires(etran != null); - - AddComment(builder, stmt, "while statement"); - this.fuelContext = FuelSetting.ExpandFuelContext(stmt.Attributes, stmt.Tok, this.fuelContext, this.reporter); - DefineFuelConstant(stmt.Tok, stmt.Attributes, builder, etran); - BodyTranslator bodyTr = null; - if (stmt.Body != null) { - bodyTr = delegate (BoogieStmtListBuilder bld, ExpressionTranslator e) { - CurrentIdGenerator.Push(); - TrStmt(stmt.Body, bld, locals, e); - InsertContinueTarget(stmt, bld); - CurrentIdGenerator.Pop(); - }; - } - TrLoop(stmt, stmt.Guard, bodyTr, builder, locals, etran); - this.fuelContext = FuelSetting.PopFuelContext(); - } - private void TrIfStmt(IfStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { Contract.Requires(stmt != null); Contract.Requires(builder != null); @@ -1015,618 +800,6 @@ private void TrIfStmt(IfStmt stmt, BoogieStmtListBuilder builder, List builder.Add(new Bpl.IfCmd(stmt.Tok, guard == null || stmt.IsBindingGuard ? null : etran.TrExpr(guard), thn, elsIf, els)); } - void TrForallProof(ForallStmt forallStmt, BoogieStmtListBuilder definedness, BoogieStmtListBuilder exporter, - List locals, ExpressionTranslator etran) { - // Translate: - // forall (x,y | Range(x,y)) - // ensures Post(x,y); - // { - // Body; - // } - // as: - // if (*) { - // var x,y; - // havoc x,y; - // CheckWellformed( Range ); - // assume Range(x,y); - // CheckWellformed( Post ); - // Tr( Body ); // include only if there is a Body - // assert Post; // include only if there is a Body - // assume false; - // } else { - // assume (forall x,y :: Range(x,y) ==> Post(x,y)); - // } - - if (forallStmt.BoundVars.Count != 0) { - // Note, it would be nicer (and arguably more appropriate) to do a SetupBoundVarsAsLocals - // here (rather than a TrBoundVariables). However, there is currently no way to apply - // a substMap to a statement (in particular, to s.Body), so that doesn't work here. - List freeOfAlloc = BoundedPool.HasBounds(forallStmt.Bounds, BoundedPool.PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc); - - var bVars = new List(); - var typeAntecedent = etran.TrBoundVariables(forallStmt.BoundVars, bVars, true, freeOfAlloc); - locals.AddRange(bVars); - var havocIds = new List(); - foreach (Bpl.Variable bv in bVars) { - havocIds.Add(new Bpl.IdentifierExpr(forallStmt.Tok, bv)); - } - definedness.Add(new Bpl.HavocCmd(forallStmt.Tok, havocIds)); - definedness.Add(TrAssumeCmd(forallStmt.Tok, typeAntecedent)); - } - TrStmt_CheckWellformed(forallStmt.Range, definedness, locals, etran, false); - definedness.Add(TrAssumeCmdWithDependencies(etran, forallStmt.Range.tok, forallStmt.Range, "forall statement range")); - - var ensuresDefinedness = new BoogieStmtListBuilder(this, options, definedness.Context); - foreach (var ens in forallStmt.Ens) { - TrStmt_CheckWellformed(ens.E, ensuresDefinedness, locals, etran, false); - ensuresDefinedness.Add(TrAssumeCmdWithDependencies(etran, ens.E.tok, ens.E, "forall statement ensures clause")); - } - PathAsideBlock(forallStmt.Tok, ensuresDefinedness, definedness); - - if (forallStmt.Body != null) { - TrStmt(forallStmt.Body, definedness, locals, etran); - - // check that postconditions hold - foreach (var ens in forallStmt.Ens) { - foreach (var split in TrSplitExpr(definedness.Context, ens.E, etran, true, out var splitHappened)) { - if (split.IsChecked) { - definedness.Add(Assert(split.Tok, split.E, new PODesc.ForallPostcondition(ens.E), definedness.Context)); - } - } - } - } - - definedness.Add(TrAssumeCmd(forallStmt.Tok, Bpl.Expr.False)); - - // Now for the other branch, where the ensures clauses are exported. - // If the forall body has side effect such as call to a reveal function, - // it needs to be exported too. - var se = forallStmt.Body == null ? Bpl.Expr.True : TrFunctionSideEffect(forallStmt.Body, etran); - var substMap = new Dictionary(); - var p = Substitute(forallStmt.EffectiveEnsuresClauses[0], null, substMap); - var qq = etran.TrExpr(p); - if (forallStmt.BoundVars.Count != 0) { - exporter.Add(TrAssumeCmd(forallStmt.Tok, BplAnd(se, qq))); - } else { - exporter.Add(TrAssumeCmd(forallStmt.Tok, BplAnd(se, ((Bpl.ForallExpr)qq).Body))); - } - } - - /// - /// "lhs" is expected to be a resolved form of an expression, i.e., not a concrete-syntax expression. - /// - void TrAssignment(Statement stmt, Expression lhs, AssignmentRhs rhs, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { - Contract.Requires(stmt != null); - Contract.Requires(lhs != null); - Contract.Requires(!(lhs is ConcreteSyntaxExpression)); - Contract.Requires(!(lhs is SeqSelectExpr && !((SeqSelectExpr)lhs).SelectOne)); // these were once allowed, but their functionality is now provided by 'forall' statements - Contract.Requires(rhs != null); - Contract.Requires(builder != null); - Contract.Requires(cce.NonNullElements(locals)); - Contract.Requires(etran != null); - Contract.Requires(predef != null); - - var lhss = new List() { lhs }; - ProcessLhss(lhss, rhs.CanAffectPreviouslyKnownExpressions, true, builder, locals, etran, stmt, - out var lhsBuilder, out var bLhss, out var ignore1, out var ignore2, out var ignore3); - Contract.Assert(lhsBuilder.Count == 1 && bLhss.Count == 1); // guaranteed by postcondition of ProcessLhss - - var rhss = new List() { rhs }; - ProcessRhss(lhsBuilder, bLhss, lhss, rhss, builder, locals, etran, stmt); - builder.AddCaptureState(stmt); - } - - void TrForallAssign(ForallStmt s, AssignStmt s0, - BoogieStmtListBuilder definedness, BoogieStmtListBuilder updater, List locals, ExpressionTranslator etran) { - // The statement: - // forall (x,y | Range(x,y)) { - // (a) E(x,y) . f := G(x,y); - // (b) A(x,y) [ I0(x,y), I1(x,y), ... ] := G(x,y); - // } - // translate into: - // if (*) { - // // check definedness of Range - // var x,y; - // havoc x,y; - // CheckWellformed( Range ); - // assume Range; - // // check definedness of the other expressions - // (a) - // CheckWellformed( E.F ); - // check that E.f is in the modifies frame; - // CheckWellformed( G ); - // check nat restrictions for the RHS - // (b) - // CheckWellformed( A[I0,I1,...] ); - // check that A[I0,I1,...] is in the modifies frame; - // CheckWellformed( G ); - // check nat restrictions for the RHS - // // check for duplicate LHSs - // var x', y'; - // havoc x', y'; - // assume Range[x,y := x',y']; - // assume !(x == x' && y == y'); - // (a) - // assert E(x,y) != E(x',y') || G(x,y) == G(x',y'); - // (b) - // assert !( A(x,y)==A(x',y') && I0(x,y)==I0(x',y') && I1(x,y)==I1(x',y') && ... ) || G(x,y) == G(x',y'); - // - // assume false; - // - // } else { - // var oldHeap := $Heap; - // havoc $Heap; - // assume $HeapSucc(oldHeap, $Heap); - // (a) - // assume (forall o: ref, F: Field :: - // { $Heap[o,F] } - // $Heap[o,F] = oldHeap[o,F] || - // (exists x,y :: Range(x,y) && o == E(x,y) && F = f)); - // assume (forall x,y :: Range ==> $Heap[ E[$Heap:=oldHeap], F] == G[$Heap:=oldHeap]); (**) - // (b) - // assume (forall o: ref, F: Field :: - // { $Heap[o,F] } - // $Heap[o,F] = oldHeap[o,F] || - // (exists x,y :: Range(x,y) && o == A(x,y) && F = Index(I0,I1,...))); - // assume (forall x,y :: Range ==> $Heap[ A[$Heap:=oldHeap], Index(I0,I1,...)] == G[$Heap:=oldHeap]); (**) - // } - // - // Note: In order to get a good trigger for the quantifiers (**), we will attempt to make the parameters - // that select from $Heap in the LHS of the equalities as plain as possible. This involves taking the inverse - // of an expression, which isn't always easy or possible, so we settle for handling some common cases. In - // particular, we change: - // 0: forall i | R(i) { F(i).f := E(i); } - // 1: forall i | R(i) { A[F(i)] := E(i); } - // 2: forall i | R(i) { F(i)[N] := E(i); } - // where f is some field and A and N are expressions that do not depend on i, into: - // 0: forall j | Q(j) { j.f := E(F-1(j)); } - // 1: forall j | Q(j) { A[j] := E(F-1(j)); } - // 2: forall j | Q(j) { j[N] := E(F-1(j)); } - // where we ensure that, for all i and j: - // R(i) && j == F(i) <==> Q(j) && F-1(j) == i - // If the transformation succeeds, we use, respectively, j.f, A[j], and j[N] (each evaluated in the new heap) as - // the trigger of the quantifier generated. - - var substMap = SetupBoundVarsAsLocals(s.BoundVars, definedness, locals, etran); - Expression range = Substitute(s.Range, null, substMap); - TrStmt_CheckWellformed(range, definedness, locals, etran, false); - definedness.Add(TrAssumeCmd(s.Range.tok, etran.TrExpr(range))); - - var lhs = Substitute(s0.Lhs.Resolved, null, substMap); - TrStmt_CheckWellformed(lhs, definedness, locals, etran, false); - string description = GetObjFieldDetails(lhs, etran, out var obj, out var F); - var (lhsObj, lhsField) = lhs switch { - MemberSelectExpr e => (e.Obj, e.Member as Field), - SeqSelectExpr e => (e.Seq, null), - MultiSelectExpr e => (e.Array, null), - _ => throw new cce.UnreachableException() - }; - var desc = new PODesc.Modifiable(description, GetContextModifiesFrames(), lhsObj, lhsField); - definedness.Add(Assert(lhs.tok, Bpl.Expr.SelectTok(lhs.tok, etran.ModifiesFrame(lhs.tok), obj, F), - desc, definedness.Context)); - if (s0.Rhs is ExprRhs) { - var r = (ExprRhs)s0.Rhs; - var rhs = Substitute(r.Expr, null, substMap); - TrStmt_CheckWellformed(rhs, definedness, locals, etran, false); - // check nat restrictions for the RHS - Type lhsType; - if (lhs is MemberSelectExpr) { - lhsType = ((MemberSelectExpr)lhs).Type; - } else if (lhs is SeqSelectExpr) { - lhsType = ((SeqSelectExpr)lhs).Type; - } else { - lhsType = ((MultiSelectExpr)lhs).Type; - } - var translatedRhs = etran.TrExpr(rhs); - CheckSubrange(r.Tok, translatedRhs, rhs.Type, lhsType, rhs, definedness); - if (lhs is MemberSelectExpr) { - var fse = (MemberSelectExpr)lhs; - Contract.Assert(lhsField != null); - Check_NewRestrictions(fse.tok, fse.Obj, obj, lhsField, translatedRhs, definedness, etran); - } - } - - // check for duplicate LHSs - if (s0.Rhs is ExprRhs) { // if Rhs denotes a havoc, then no duplicate check is performed - var substMapPrime = SetupBoundVarsAsLocals(s.BoundVars, definedness, locals, etran); - var lhsPrime = Substitute(s0.Lhs.Resolved, null, substMapPrime); - range = Substitute(s.Range, null, substMapPrime); - definedness.Add(TrAssumeCmd(range.tok, etran.TrExpr(range))); - // assume !(x == x' && y == y'); - Bpl.Expr eqs = Bpl.Expr.True; - foreach (var bv in s.BoundVars) { - var x = substMap[bv]; - var xPrime = substMapPrime[bv]; - // TODO: in the following line, is the term equality okay, or does it have to include things like Set#Equal sometimes too? - eqs = BplAnd(eqs, Bpl.Expr.Eq(etran.TrExpr(x), etran.TrExpr(xPrime))); - } - definedness.Add(TrAssumeCmd(s.Tok, Bpl.Expr.Not(eqs))); - GetObjFieldDetails(lhsPrime, etran, out var objPrime, out var FPrime); - var Rhs = ((ExprRhs)s0.Rhs).Expr; - var rhs = etran.TrExpr(Substitute(Rhs, null, substMap)); - var rhsPrime = etran.TrExpr(Substitute(Rhs, null, substMapPrime)); - var lhsComponents = new List { lhsObj }; - if (lhs is SeqSelectExpr sse) { - lhsComponents.Add(sse.E0); - } else if (lhs is MultiSelectExpr multi) { - lhsComponents.AddRange(multi.Indices); - } - - definedness.Add(Assert(s0.Tok, - BplOr( - BplOr(Bpl.Expr.Neq(obj, objPrime), Bpl.Expr.Neq(F, FPrime)), - Bpl.Expr.Eq(rhs, rhsPrime)), - new PODesc.ForallLHSUnique(s.BoundVars, s.Range, lhsComponents, Rhs), definedness.Context)); - } - - definedness.Add(TrAssumeCmd(s.Tok, Bpl.Expr.False)); - - // Now for the translation of the update itself - - Bpl.IdentifierExpr prevHeap = GetPrevHeapVar_IdExpr(s.Tok, locals); - var prevEtran = new ExpressionTranslator(this, predef, prevHeap, etran.scope); - updater.Add(Bpl.Cmd.SimpleAssign(s.Tok, prevHeap, etran.HeapExpr)); - updater.Add(new Bpl.HavocCmd(s.Tok, new List { etran.HeapCastToIdentifierExpr })); - updater.Add(TrAssumeCmd(s.Tok, HeapSucc(prevHeap, etran.HeapExpr))); - - // Here comes: - // assume (forall o: ref, f: Field :: - // { $Heap[o,f] } - // $Heap[o,f] = oldHeap[o,f] || - // (exists x,y :: Range(x,y)[$Heap:=oldHeap] && - // o == Object(x,y)[$Heap:=oldHeap] && f == Field(x,y)[$Heap:=oldHeap])); - Bpl.BoundVariable oVar = new Bpl.BoundVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$o", predef.RefType)); - Bpl.IdentifierExpr o = new Bpl.IdentifierExpr(s.Tok, oVar); - Bpl.BoundVariable fVar = new Bpl.BoundVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$f", predef.FieldName(s.Tok))); - Bpl.IdentifierExpr f = new Bpl.IdentifierExpr(s.Tok, fVar); - Bpl.Expr heapOF = ReadHeap(s.Tok, etran.HeapExpr, o, f); - Bpl.Expr oldHeapOF = ReadHeap(s.Tok, prevHeap, o, f); - List freeOfAlloc = BoundedPool.HasBounds(s.Bounds, BoundedPool.PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc); - List xBvars = new List(); - var xBody = etran.TrBoundVariables(s.BoundVars, xBvars, false, freeOfAlloc); - xBody = BplAnd(xBody, prevEtran.TrExpr(s.Range)); - GetObjFieldDetails(s0.Lhs.Resolved, prevEtran, out var xObj, out var xField); - xBody = BplAnd(xBody, Bpl.Expr.Eq(o, xObj)); - xBody = BplAnd(xBody, Bpl.Expr.Eq(f, xField)); - //TRIG (exists k#2: int :: (k#2 == LitInt(0 - 3) || k#2 == LitInt(4)) && $o == read($prevHeap, this, _module.MyClass.arr) && $f == MultiIndexField(IndexField(i#0), j#0)) - Bpl.Expr xObjField = new Bpl.ExistsExpr(s.Tok, xBvars, xBody); // LL_TRIGGER - Bpl.Expr body = BplOr(Bpl.Expr.Eq(heapOF, oldHeapOF), xObjField); - var tr = new Trigger(s.Tok, true, new List() { heapOF }); - Bpl.Expr qq = new Bpl.ForallExpr(s.Tok, new List { }, new List { oVar, fVar }, null, tr, body); - updater.Add(TrAssumeCmd(s.Tok, qq)); - - if (s.EffectiveEnsuresClauses != null) { - foreach (ForallExpr expr in s.EffectiveEnsuresClauses) { - BinaryExpr term = (BinaryExpr)expr.Term; - Contract.Assert(term != null); - var e0 = ((BinaryExpr)term).E0.Resolved; - var e1 = ((BinaryExpr)term).E1; - qq = TrForall_NewValueAssumption(expr.tok, expr.BoundVars, expr.Bounds, expr.Range, e0, e1, expr.Attributes, etran, prevEtran); - updater.Add(TrAssumeCmd(s.Tok, qq)); - } - } - } - - /// - /// Generate: - /// assume (forall x,y :: Range(x,y)[$Heap:=oldHeap] ==> - /// $Heap[ Object(x,y)[$Heap:=oldHeap], Field(x,y)[$Heap:=oldHeap] ] == G[$Heap:=oldHeap] )); - /// where - /// x,y represent boundVars - /// Object(x,y) is the first part of lhs - /// Field(x,y) is the second part of lhs - /// G is rhs - /// If lhsAsTrigger is true, then use the LHS of the equality above as the trigger; otherwise, don't specify any trigger. - /// - private Bpl.Expr TrForall_NewValueAssumption(IToken tok, List boundVars, List bounds, Expression range, Expression lhs, Expression rhs, Attributes attributes, ExpressionTranslator etran, ExpressionTranslator prevEtran) { - Contract.Requires(tok != null); - Contract.Requires(boundVars != null); - Contract.Requires(bounds != null); - Contract.Requires(range != null); - Contract.Requires(lhs != null); - Contract.Requires(rhs != null); - Contract.Requires(etran != null); - Contract.Requires(prevEtran != null); - - List freeOfAlloc = BoundedPool.HasBounds(bounds, BoundedPool.PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc); - var xBvars = new List(); - Bpl.Expr xAnte = etran.TrBoundVariables(boundVars, xBvars, false, freeOfAlloc); - xAnte = BplAnd(xAnte, prevEtran.TrExpr(range)); - var g = prevEtran.TrExpr(rhs); - GetObjFieldDetails(lhs, prevEtran, out var obj, out var field); - var xHeapOF = ReadHeap(tok, etran.HeapExpr, obj, field); - - g = BoxIfNotNormallyBoxed(rhs.tok, g, rhs.Type); - - Bpl.Trigger tr = null; - var argsEtran = etran.WithNoLits(); - foreach (var aa in attributes.AsEnumerable()) { - if (aa.Name == "trigger") { - List tt = new List(); - foreach (var arg in aa.Args) { - if (arg == lhs) { - tt.Add(xHeapOF); - } else { - tt.Add(argsEtran.TrExpr(arg)); - } - } - tr = new Bpl.Trigger(tok, true, tt, tr); - } - } - return new Bpl.ForallExpr(tok, xBvars, tr, BplImp(xAnte, Bpl.Expr.Eq(xHeapOF, g))); - } - - void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, - Bpl.Expr freeInvariant = null, bool includeTerminationCheck = true) { - Contract.Requires(s != null); - Contract.Requires(builder != null); - Contract.Requires(locals != null); - Contract.Requires(etran != null); - - s.ScopeDepth = builder.Context.ScopeDepth; - - var suffix = CurrentIdGenerator.FreshId("loop#"); - - var theDecreases = s.Decreases.Expressions; - - Bpl.LocalVariable preLoopHeapVar = new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$PreLoopHeap$" + suffix, predef.HeapType)); - locals.Add(preLoopHeapVar); - Bpl.IdentifierExpr preLoopHeap = new Bpl.IdentifierExpr(s.Tok, preLoopHeapVar); - ExpressionTranslator etranPreLoop = new ExpressionTranslator(this, predef, preLoopHeap, etran.scope); - ExpressionTranslator updatedFrameEtran; - string loopFrameName = FrameVariablePrefix + suffix; - if (s.Mod.Expressions != null) { - updatedFrameEtran = etran.WithModifiesFrame(loopFrameName); - } else { - updatedFrameEtran = etran; - } - - if (s.Mod.Expressions != null) { // check well-formedness and that the modifies is a subset - CheckFrameWellFormed(new WFOptions(), s.Mod.Expressions, locals, builder, etran); - var desc = new PODesc.ModifyFrameSubset("loop modifies clause", s.Mod.Expressions, GetContextModifiesFrames()); - CheckFrameSubset(s.Tok, s.Mod.Expressions, null, null, etran, etran.ModifiesFrame(s.Tok), builder, desc, null); - DefineFrame(s.Tok, etran.ModifiesFrame(s.Tok), s.Mod.Expressions, builder, locals, loopFrameName); - } - builder.Add(Bpl.Cmd.SimpleAssign(s.Tok, preLoopHeap, etran.HeapExpr)); - - var daTrackersMonotonicity = new List>(); - foreach (var dat in DefiniteAssignmentTrackers.Values) { // TODO: the order is non-deterministic and may change between invocations of Dafny - var preLoopDat = new Bpl.LocalVariable(dat.tok, new Bpl.TypedIdent(dat.tok, "preLoop$" + suffix + "$" + dat.Name, dat.Type)); - locals.Add(preLoopDat); - var ie = new Bpl.IdentifierExpr(s.Tok, preLoopDat); - daTrackersMonotonicity.Add(new Tuple(ie, dat)); - builder.Add(Bpl.Cmd.SimpleAssign(s.Tok, ie, dat)); - } - - List initDecr = null; - if (!Contract.Exists(theDecreases, e => e is WildcardExpr)) { - initDecr = RecordDecreasesValue(theDecreases, builder, locals, etran, "$decr_init$" + suffix); - } - - // The variable w is used to coordinate the definedness checking of the loop invariant. - // It is also used for body-less loops to turn off invariant checking after the generated body. - Bpl.LocalVariable wVar = new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$w$" + suffix, Bpl.Type.Bool)); - Bpl.IdentifierExpr w = new Bpl.IdentifierExpr(s.Tok, wVar); - locals.Add(wVar); - // havoc w; - builder.Add(new Bpl.HavocCmd(s.Tok, new List { w })); - - List invariants = new List(); - if (freeInvariant != null) { - invariants.Add(new Bpl.AssumeCmd(freeInvariant.tok, freeInvariant)); - } - BoogieStmtListBuilder invDefinednessBuilder = new BoogieStmtListBuilder(this, options, builder.Context); - foreach (AttributedExpression loopInv in s.Invariants) { - var (errorMessage, successMessage) = CustomErrorMessage(loopInv.Attributes); - TrStmt_CheckWellformed(loopInv.E, invDefinednessBuilder, locals, etran, false); - invDefinednessBuilder.Add(TrAssumeCmdWithDependencies(etran, loopInv.E.tok, loopInv.E, "loop invariant")); - - invariants.Add(TrAssumeCmd(loopInv.E.tok, BplImp(w, etran.CanCallAssumption(loopInv.E)))); - var ss = TrSplitExpr(builder.Context, loopInv.E, etran, false, out var splitHappened); - if (!splitHappened) { - var wInv = BplImp(w, etran.TrExpr(loopInv.E)); - invariants.Add(Assert(loopInv.E.tok, wInv, new PODesc.LoopInvariant(loopInv.E, errorMessage, successMessage), builder.Context)); - } else { - foreach (var split in ss) { - var wInv = Bpl.Expr.Binary(split.E.tok, BinaryOperator.Opcode.Imp, w, split.E); - if (split.IsChecked) { - invariants.Add(Assert(split.Tok, wInv, new PODesc.LoopInvariant(loopInv.E, errorMessage, successMessage), builder.Context)); // TODO: it would be fine to have this use {:subsumption 0} - } else { - var cmd = TrAssumeCmd(split.E.tok, wInv); - proofDependencies?.AddProofDependencyId(cmd, loopInv.E.tok, new InvariantDependency(loopInv.E)); - invariants.Add(cmd); - } - } - } - } - // check definedness of decreases clause - foreach (Expression e in theDecreases) { - TrStmt_CheckWellformed(e, invDefinednessBuilder, locals, etran, true); - } - if (codeContext is IMethodCodeContext) { - var modifiesClause = ((IMethodCodeContext)codeContext).Modifies.Expressions; - if (codeContext is IteratorDecl) { - // add "this" to the explicit modifies clause - var explicitModifies = modifiesClause; - modifiesClause = new List(); - modifiesClause.Add(new FrameExpression(s.Tok, new ThisExpr((IteratorDecl)codeContext), null)); - modifiesClause.AddRange(explicitModifies); - } - // include boilerplate invariants - foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(s.Tok, modifiesClause, s.IsGhost, codeContext.AllowsAllocation, etranPreLoop, etran, etran.Old)) { - if (tri.IsFree) { - invariants.Add(TrAssumeCmd(s.Tok, tri.Expr)); - } else { - Contract.Assert(tri.ErrorMessage != null); // follows from BoilerplateTriple invariant - invariants.Add(Assert(s.Tok, tri.Expr, new PODesc.BoilerplateTriple(tri.ErrorMessage, tri.SuccessMessage, tri.Comment), builder.Context)); - } - } - // add a free invariant which says that the heap hasn't changed outside of the modifies clause. - invariants.Add(TrAssumeCmd(s.Tok, FrameConditionUsingDefinedFrame(s.Tok, etranPreLoop, etran, updatedFrameEtran, updatedFrameEtran.ModifiesFrame(s.Tok)))); - // for iterators, add "fresh(_new)" as an invariant - if (codeContext is IteratorDecl iter) { - var th = new ThisExpr(iter); - var thisDotNew = new MemberSelectExpr(s.Tok, th, iter.Member_New); - var fr = new FreshExpr(s.Tok, thisDotNew); - fr.Type = Type.Bool; - invariants.Add(TrAssertCmd(s.Tok, etran.TrExpr(fr))); - } - } - - // include a free invariant that says that all definite-assignment trackers have only become more "true" - foreach (var pair in daTrackersMonotonicity) { - Bpl.Expr monotonic = BplImp(pair.Item1, pair.Item2); - invariants.Add(TrAssumeCmd(s.Tok, monotonic)); - } - - // include a free invariant that says that all completed iterations so far have only decreased the termination metric - if (initDecr != null) { - var toks = new List(); - var decrs = new List(); - var decrsDafny = new List(); - var initDecrsDafny = new List(); - var prevGhostLocals = new List(); - foreach (Expression e in theDecreases) { - toks.Add(e.tok); - decrsDafny.Add(e); - decrs.Add(etran.TrExpr(e)); - var (prevVars, eInit) = TranslateToLoopEntry(s, e, "LoopEntry"); - prevGhostLocals.AddRange(prevVars); - initDecrsDafny.Add(eInit); - } - Bpl.Expr decrCheck = DecreasesCheck(toks, prevGhostLocals, decrsDafny, initDecrsDafny, decrs, initDecr, - null, null, true, false); - invariants.Add(TrAssumeCmd(s.Tok, decrCheck)); - } - - var loopBodyBuilder = new BoogieStmtListBuilder(this, options, builder.Context); - loopBodyBuilder.AddCaptureState(s.Tok, true, CaptureStateExtensions.AfterLoopIterationsStateMarker); - - // As the first thing inside the loop, generate: if (!w) { CheckWellformed(inv); assume false; } - invDefinednessBuilder.Add(TrAssumeCmd(s.Tok, Bpl.Expr.False)); - loopBodyBuilder.Add(new Bpl.IfCmd(s.Tok, Bpl.Expr.Not(w), invDefinednessBuilder.Collect(s.Tok), null, null)); - - // Generate: CheckWellformed(guard); if (!guard) { break; } - // but if this is a body-less loop, put all of that inside: if (*) { ... } - // Without this, Boogie's abstract interpreter may figure out that the loop guard is always false - // on entry to the loop, and then Boogie wouldn't consider this a loop at all. (See also comment - // in methods GuardAlwaysHoldsOnEntry_BodyLessLoop and GuardAlwaysHoldsOnEntry_LoopWithBody in - // Test/dafny0/DirtyLoops.dfy.) - var isBodyLessLoop = s is OneBodyLoopStmt { BodySurrogate: { } }; - var whereToBuildLoopGuard = isBodyLessLoop ? new BoogieStmtListBuilder(this, options, builder.Context) : loopBodyBuilder; - Bpl.Expr guard = null; - if (Guard != null) { - TrStmt_CheckWellformed(Guard, whereToBuildLoopGuard, locals, etran, true); - guard = Bpl.Expr.Not(etran.TrExpr(Guard)); - } - var guardBreak = new BoogieStmtListBuilder(this, options, builder.Context); - guardBreak.Add(new Bpl.BreakCmd(s.Tok, null)); - whereToBuildLoopGuard.Add(new Bpl.IfCmd(s.Tok, guard, guardBreak.Collect(s.Tok), null, null)); - if (isBodyLessLoop) { - loopBodyBuilder.Add(new Bpl.IfCmd(s.Tok, null, whereToBuildLoopGuard.Collect(s.Tok), null, null)); - } - - if (bodyTr != null) { - // termination checking - if (Contract.Exists(theDecreases, e => e is WildcardExpr)) { - // omit termination checking for this loop - bodyTr(loopBodyBuilder, updatedFrameEtran); - } else { - List oldBfs = RecordDecreasesValue(theDecreases, loopBodyBuilder, locals, etran, "$decr$" + suffix); - // time for the actual loop body - bodyTr(loopBodyBuilder, updatedFrameEtran); - // check definedness of decreases expressions - var toks = new List(); - var decrs = new List(); - var decrsDafny = new List(); - var initDecrsDafny = new List(); - var prevGhostLocals = new List(); - foreach (Expression e in theDecreases) { - toks.Add(e.tok); - // Note: the label "LoopEntry" doesn't exist in the program, and is - // useful only for explanatory purposes. - decrsDafny.Add(e); - var (prevVars, eInit) = TranslateToLoopEntry(s, e, "LoopEntry"); - prevGhostLocals.AddRange(prevVars); - initDecrsDafny.Add(eInit); - decrs.Add(etran.TrExpr(e)); - } - if (includeTerminationCheck) { - AddComment(loopBodyBuilder, s, "loop termination check"); - Bpl.Expr decrCheck = DecreasesCheck(toks, prevGhostLocals, decrsDafny, initDecrsDafny, decrs, oldBfs, - loopBodyBuilder, " at end of loop iteration", false, false); - var description = new - PODesc.Terminates(s.InferredDecreases, prevGhostLocals, null, initDecrsDafny, theDecreases, false); - loopBodyBuilder.Add(Assert(s.Tok, decrCheck, description, builder.Context)); - } - } - } else if (isBodyLessLoop) { - var bodySurrogate = ((OneBodyLoopStmt)s).BodySurrogate; - // This is a body-less loop. Havoc the targets and then set w to false, to make the loop-invariant - // maintenance check vaccuous. - var bplTargets = bodySurrogate.LocalLoopTargets.ConvertAll(v => TrVar(s.Tok, v)); - if (bodySurrogate.UsesHeap) { - bplTargets.Add(etran.HeapCastToIdentifierExpr); - } - loopBodyBuilder.Add(new Bpl.HavocCmd(s.Tok, bplTargets)); - loopBodyBuilder.Add(Bpl.Cmd.SimpleAssign(s.Tok, w, Bpl.Expr.False)); - } - // Finally, assume the well-formedness of the invariant (which has been checked once and for all above), so that the check - // of invariant-maintenance can use the appropriate canCall predicates. Note, it is important (see Test/git-issues/git-issue-1812.dfy) - // that each CanCall assumption uses the preceding invariants as antecedents--this is achieved by treating all "invariant" - // declarations as one big conjunction, because then CanCallAssumption will add the needed antecedents. - if (s.Invariants.Any()) { - var allInvariants = s.Invariants.Select(inv => inv.E).Aggregate((a, b) => Expression.CreateAnd(a, b)); - loopBodyBuilder.Add(TrAssumeCmd(s.Tok, etran.CanCallAssumption(allInvariants))); - } - - Bpl.StmtList body = loopBodyBuilder.Collect(s.Tok); - builder.Add(new Bpl.WhileCmd(s.Tok, Bpl.Expr.True, invariants, new List(), body)); - } - - // Return the version of e that holds at the beginnging of the loop, - // Along with the local variable assignments that need to happen at - // the beginning of the loop for it to be valid. - private (List, Expression) TranslateToLoopEntry(LoopStmt loop, Expression e, string loopLabel) { - var prevGhostLocals = new List(); - Expression olde = new OldExpr(e.tok, e, loopLabel) { - Type = e.Type - }; - - var subStmts = TransitiveSubstatements(loop); - var modifiedVars = - subStmts - .OfType() - .Select(s => s.Lhs) - .OfType(); - foreach (var ie in modifiedVars) { - var prevName = $"prev_{ie.Name}"; - var prevDecl = Statement.CreateLocalVariable(RangeToken.NoToken, prevName, ie); - var prevRef = Expression.CreateIdentExpr(prevDecl.Locals[0]); - olde = Substitute(olde, ie.Var, prevRef); - prevGhostLocals.Add(prevDecl); - } - - return (prevGhostLocals, olde); - - } - - IEnumerable TransitiveSubstatements(Statement s) { - yield return s; - foreach (var ss in s.SubStatements.SelectMany(TransitiveSubstatements)) { - yield return ss; - } - } - - void InsertContinueTarget(LoopStmt loop, BoogieStmtListBuilder builder) { - Contract.Requires(loop != null); - Contract.Requires(builder != null); - if (loop.Labels != null) { - builder.AddLabelCmd("continue_" + loop.Labels.Data.AssignUniqueId(CurrentIdGenerator)); - } - } void TrAlternatives(List alternatives, IToken elseToken, Action buildElseCase, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, bool isGhost) { @@ -1680,530 +853,6 @@ void TrAlternatives(List alternatives, IToken elseToken, Act builder.Add(elsIf); } - void TrCallStmt(CallStmt s, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, Bpl.IdentifierExpr actualReceiver) { - Contract.Requires(s != null); - Contract.Requires(builder != null); - Contract.Requires(locals != null); - Contract.Requires(etran != null); - Contract.Requires(!(s.Method is Constructor) || (s.Lhs.Count == 0 && actualReceiver != null)); - - var tySubst = s.MethodSelect.TypeArgumentSubstitutionsWithParents(); - ProcessLhss(s.Lhs, true, true, builder, locals, etran, s, out var lhsBuilders, out var bLhss, - out _, out _, out _, s.OriginalInitialLhs); - Contract.Assert(s.Lhs.Count == lhsBuilders.Count); - Contract.Assert(s.Lhs.Count == bLhss.Count); - var lhsTypes = new List(); - if (s.Method is Constructor) { - lhsTypes.Add(s.Receiver.Type); - bLhss.Add(actualReceiver); - } else { - for (int i = 0; i < s.Lhs.Count; i++) { - var lhs = s.Lhs[i]; - lhsTypes.Add(lhs.Type); - builder.Add(new CommentCmd("TrCallStmt: Adding lhs with type " + lhs.Type)); - if (bLhss[i] == null) { // (in the current implementation, the second parameter "true" to ProcessLhss implies that all bLhss[*] will be null) - // create temporary local and assign it to bLhss[i] - string nm = CurrentIdGenerator.FreshId("$rhs##"); - var formalOutType = s.Method.Outs[i].Type.Subst(tySubst); - var ty = TrType(formalOutType); - Bpl.LocalVariable var = new Bpl.LocalVariable(lhs.tok, new Bpl.TypedIdent(lhs.tok, nm, ty)); - locals.Add(var); - bLhss[i] = new Bpl.IdentifierExpr(lhs.tok, var.Name, ty); - } - } - } - Bpl.IdentifierExpr initHeap = null; - if (codeContext is IteratorDecl) { - // var initHeap := $Heap; - var initHeapVar = new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, CurrentIdGenerator.FreshId("$initHeapCallStmt#"), predef.HeapType)); - locals.Add(initHeapVar); - initHeap = new Bpl.IdentifierExpr(s.Tok, initHeapVar); - // initHeap := $Heap; - builder.Add(Bpl.Cmd.SimpleAssign(s.Tok, initHeap, etran.HeapExpr)); - } - builder.Add(new CommentCmd("TrCallStmt: Before ProcessCallStmt")); - ProcessCallStmt(s, tySubst, actualReceiver, bLhss, lhsTypes, builder, locals, etran); - builder.Add(new CommentCmd("TrCallStmt: After ProcessCallStmt")); - for (int i = 0; i < lhsBuilders.Count; i++) { - var lhs = s.Lhs[i]; - Type lhsType, rhsTypeConstraint; - if (lhs is IdentifierExpr) { - var ide = (IdentifierExpr)lhs; - lhsType = ide.Var.Type; - rhsTypeConstraint = lhsType; - } else if (lhs is MemberSelectExpr) { - var fse = (MemberSelectExpr)lhs; - var field = (Field)fse.Member; - Contract.Assert(field != null); - Contract.Assert(VisibleInScope(field)); - lhsType = field.Type; - rhsTypeConstraint = lhsType.Subst(fse.TypeArgumentSubstitutionsWithParents()); - } else if (lhs is SeqSelectExpr) { - var e = (SeqSelectExpr)lhs; - lhsType = null; // for arrays, always make sure the value assigned is boxed - rhsTypeConstraint = e.Seq.Type.TypeArgs[0]; - } else { - var e = (MultiSelectExpr)lhs; - lhsType = null; // for arrays, always make sure the value assigned is boxed - rhsTypeConstraint = e.Array.Type.TypeArgs[0]; - } - - Bpl.Expr bRhs = bLhss[i]; // the RHS (bRhs) of the assignment to the actual call-LHS (lhs) was a LHS (bLhss[i]) in the Boogie call statement - CheckSubrange(lhs.tok, bRhs, s.Method.Outs[i].Type.Subst(tySubst), rhsTypeConstraint, null, builder); - bRhs = CondApplyBox(lhs.tok, bRhs, lhs.Type, lhsType); - - lhsBuilders[i](bRhs, false, builder, etran); - } - if (codeContext is IteratorDecl) { - var iter = (IteratorDecl)codeContext; - Contract.Assert(initHeap != null); - RecordNewObjectsIn_New(s.Tok, iter, initHeap, etran.HeapCastToIdentifierExpr, builder, locals, etran); - } - builder.AddCaptureState(s); - } - - void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.Expr bReceiver, - List Lhss, List LhsTypes, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { - - Contract.Requires(cs != null); - Contract.Requires(Lhss != null); - Contract.Requires(LhsTypes != null); - // Note, a Dafny class constructor is declared to have no output parameters, but it is encoded in Boogie as - // having an output parameter. - Contract.Requires(cs.Method is Constructor || cs.Method.Outs.Count == Lhss.Count); - Contract.Requires(cs.Method is Constructor || cs.Method.Outs.Count == LhsTypes.Count); - Contract.Requires(!(cs.Method is Constructor) || (cs.Method.Outs.Count == 0 && Lhss.Count == 1 && LhsTypes.Count == 1)); - Contract.Requires(builder != null); - Contract.Requires(locals != null); - Contract.Requires(etran != null); - Contract.Requires(tySubst != null); - var tok = GetToken(cs); - var tyArgs = GetTypeParams(cs.Method); - var dafnyReceiver = cs.Receiver; - var method = cs.Method; - var atLabel = cs.MethodSelect.AtLabel; - var Args = cs.Args; - - // Figure out if the call is recursive or not, which will be used below to determine the need for a - // termination check and the need to include an implicit _k-1 argument. - bool isRecursiveCall = false; - // consult the call graph to figure out if this is a recursive call - var module = method.EnclosingClass.EnclosingModuleDefinition; - if (codeContext != null && module == currentModule) { - // Note, prefix lemmas are not recorded in the call graph, but their corresponding greatest lemmas are. - // Similarly, an iterator is not recorded in the call graph, but its MoveNext method is. - ICallable cllr = - codeContext is PrefixLemma ? ((PrefixLemma)codeContext).ExtremeLemma : - codeContext is IteratorDecl ? ((IteratorDecl)codeContext).Member_MoveNext : - codeContext; - if (ModuleDefinition.InSameSCC(method, cllr)) { - isRecursiveCall = true; - } - } - - bool isCoCall = false; - var callee = method; - if (method is ExtremeLemma && isRecursiveCall) { - isCoCall = true; - callee = ((ExtremeLemma)method).PrefixLemma; - } else if (method is PrefixLemma) { - // an explicit call to a prefix lemma is allowed only inside the SCC of the corresponding greatest lemma, - // so we consider this to be a co-call - isCoCall = true; - } - - var ins = new List(); - if (callee is TwoStateLemma) { - ins.Add(etran.OldAt(atLabel).HeapExpr); - ins.Add(etran.HeapExpr); - } - // Add type arguments - ins.AddRange(TrTypeArgs(tySubst, tyArgs)); - - // Translate receiver argument, if any - Expression receiver = bReceiver == null ? dafnyReceiver : new BoogieWrapper(bReceiver, dafnyReceiver.Type); - if (!method.IsStatic && method is not Constructor) { - if (bReceiver == null) { - TrStmt_CheckWellformed(dafnyReceiver, builder, locals, etran, true); - if (!(dafnyReceiver is ThisExpr)) { - CheckNonNull(dafnyReceiver.tok, dafnyReceiver, builder, etran, null); - } - } - var obj = etran.TrExpr(receiver); - if (bReceiver == null) { - obj = BoxifyForTraitParent(tok, obj, method, dafnyReceiver.Type); - } - ins.Add(obj); - } else if (receiver is StaticReceiverExpr stexpr) { - if (stexpr.ObjectToDiscard != null) { - TrStmt_CheckWellformed(stexpr.ObjectToDiscard, builder, locals, etran, true); - } - } - - // Ideally, the modifies and decreases checks would be done after the precondition check, - // but Boogie doesn't give us a hook for that. So, we set up our own local variables here to - // store the actual parameters. - // Create a local variable for each formal parameter, and assign each actual parameter to the corresponding local - var substMap = new Dictionary(); - var directSubstMap = new Dictionary(); - for (int i = 0; i < callee.Ins.Count; i++) { - var formal = callee.Ins[i]; - var local = new LocalVariable(formal.RangeToken, formal.Name + "#", formal.Type.Subst(tySubst), formal.IsGhost); - local.type = local.SyntacticType; // resolve local here - var ie = new IdentifierExpr(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator)); - ie.Var = local; ie.Type = ie.Var.Type; // resolve ie here - substMap.Add(formal, ie); - locals.Add(new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator), TrType(local.Type)))); - - var param = (Bpl.IdentifierExpr)etran.TrExpr(ie); // TODO: is this cast always justified? - Bpl.Expr bActual; - Expression dActual; - if (i == 0 && method is ExtremeLemma && isRecursiveCall) { - // Treat this call to M(args) as a call to the corresponding prefix lemma M#(_k - 1, args), so insert an argument here. - var k = ((PrefixLemma)callee).K; - var bplK = new Bpl.IdentifierExpr(k.tok, k.AssignUniqueName(currentDeclaration.IdGenerator), TrType(k.Type)); - dActual = Expression.CreateSubtract(Expression.CreateIdentExpr(k), Expression.CreateNatLiteral(k.tok, 1, k.Type)); - if (k.Type.IsBigOrdinalType) { - bActual = FunctionCall(k.tok, "ORD#Minus", predef.BigOrdinalType, - bplK, - FunctionCall(k.tok, "ORD#FromNat", predef.BigOrdinalType, Bpl.Expr.Literal(1))); - } else { - bActual = Bpl.Expr.Sub(bplK, Bpl.Expr.Literal(1)); - } - } else { - Expression actual; - if (method is ExtremeLemma && isRecursiveCall) { - actual = Args[i - 1]; - } else { - actual = Args[i]; - } - if (!(actual is DefaultValueExpression)) { - TrStmt_CheckWellformed(actual, builder, locals, etran, true); - } - builder.Add(new CommentCmd("ProcessCallStmt: CheckSubrange")); - // Check the subrange without boxing - var beforeBox = etran.TrExpr(actual); - CheckSubrange(actual.tok, beforeBox, actual.Type, formal.Type.Subst(tySubst), actual, builder); - bActual = AdaptBoxing(actual.tok, beforeBox, actual.Type, formal.Type.Subst(tySubst)); - dActual = actual; - } - directSubstMap.Add(formal, dActual); - Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(formal.tok, param, bActual); - builder.Add(cmd); - ins.Add(AdaptBoxing(ToDafnyToken(flags.ReportRanges, param.tok), param, formal.Type.Subst(tySubst), formal.Type)); - } - - // Check that every parameter is available in the state in which the method 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 method is - // invoked in the 'old' state or if the method invoked is a two-state lemma 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 (!method.IsStatic && !(method is Constructor)) { - Bpl.Expr wh = GetWhereClause(receiver.tok, etran.TrExpr(receiver), receiver.Type, etran, ISALLOC, true); - if (wh != null) { - var desc = new PODesc.IsAllocated("receiver argument", "in the state in which the method is invoked", receiver); - builder.Add(Assert(receiver.tok, wh, desc, builder.Context)); - } - } - for (int i = 0; i < Args.Count; i++) { - Expression ee = Args[i]; - Bpl.Expr wh = GetWhereClause(ee.tok, etran.TrExpr(ee), ee.Type, etran, ISALLOC, true); - if (wh != null) { - var desc = new PODesc.IsAllocated("argument", "in the state in which the method is invoked", ee); - builder.Add(Assert(ee.tok, wh, desc, builder.Context)); - } - } - } else if (method is TwoStateLemma) { - if (!method.IsStatic) { - Bpl.Expr wh = GetWhereClause(receiver.tok, etran.TrExpr(receiver), receiver.Type, etran.OldAt(atLabel), ISALLOC, true); - if (wh != null) { - var desc = new PODesc.IsAllocated("receiver argument", "in the two-state lemma's previous state", receiver, atLabel); - builder.Add(Assert(receiver.tok, wh, desc, builder.Context)); - } - } - Contract.Assert(callee.Ins.Count == Args.Count); - for (int i = 0; i < Args.Count; i++) { - var formal = callee.Ins[i]; - if (formal.IsOld) { - Expression ee = Args[i]; - Bpl.Expr wh = GetWhereClause(ee.tok, etran.TrExpr(ee), ee.Type, etran.OldAt(atLabel), ISALLOC, true); - if (wh != null) { - var pIdx = Args.Count == 1 ? "" : " at index " + i; - var desc = new PODesc.IsAllocated( - $"argument{pIdx} for parameter '{formal.Name}'", - "in the two-state lemma's previous state" + PODesc.IsAllocated.HelperFormal(formal), - ee, - atLabel - ); - builder.Add(Assert(ee.tok, wh, desc, builder.Context)); - } - } - } - } - - var directSub = new Substituter(null, directSubstMap, tySubst); - - // Check that the reads clause of a subcall is a subset of the current reads frame, - // but support the optimization that we don't define a reads frame at all if it's `reads *`. - if (etran.readsFrame != null) { - // substitute actual args for parameters in description expression frames... - var requiredFrames = callee.Reads.Expressions.ConvertAll(directSub.SubstFrameExpr); - var desc = new PODesc.ReadFrameSubset("call", requiredFrames, GetContextReadsFrames()); - - // ... but that substitution isn't needed for frames passed to CheckFrameSubset - var readsSubst = new Substituter(null, new Dictionary(), tySubst); - CheckFrameSubset(tok, callee.Reads.Expressions.ConvertAll(readsSubst.SubstFrameExpr), - receiver, substMap, etran, etran.ReadsFrame(tok), builder, desc, null); - } - - // substitute actual args for parameters in description expression frames... - var frameExpressions = callee.Mod.Expressions.ConvertAll(directSub.SubstFrameExpr); - // Check that the modifies clause of a subcall is a subset of the current modifies frame, - // but only if we're in a context that defines a modifies frame. - if (codeContext is IMethodCodeContext methodCodeContext) { - var desc = new PODesc.ModifyFrameSubset( - "call", - frameExpressions, - methodCodeContext.Modifies.Expressions - ); - // ... but that substitution isn't needed for frames passed to CheckFrameSubset - var modifiesSubst = new Substituter(null, new(), tySubst); - CheckFrameSubset( - tok, callee.Mod.Expressions.ConvertAll(modifiesSubst.SubstFrameExpr), - receiver, substMap, etran, etran.ModifiesFrame(tok), builder, desc, null); - } - - // Check termination - if (isRecursiveCall) { - Contract.Assert(codeContext != null); - if (codeContext is DatatypeDecl) { - builder.Add(Assert(tok, Bpl.Expr.False, new PODesc.IsNonRecursive(), builder.Context)); - } else { - List contextDecreases = codeContext.Decreases.Expressions; - List calleeDecreases = callee.Decreases.Expressions; - CheckCallTermination(tok, contextDecreases, calleeDecreases, null, receiver, substMap, directSubstMap, tySubst, etran, true, builder, codeContext.InferredDecreases, null); - } - } - - // Create variables to hold the output parameters of the call, so that appropriate unboxes can be introduced. - var outs = new List(); - var tmpOuts = new List(); - if (method is Constructor) { - tmpOuts.Add(null); - outs.Add(Lhss[0]); - } else { - for (int i = 0; i < Lhss.Count; i++) { - var bLhs = Lhss[i]; - if (ModeledAsBoxType(callee.Outs[i].Type) && !ModeledAsBoxType(LhsTypes[i])) { - // we need an Unbox - Bpl.LocalVariable var = new Bpl.LocalVariable(bLhs.tok, new Bpl.TypedIdent(bLhs.tok, CurrentIdGenerator.FreshId("$tmp##"), predef.BoxType)); - locals.Add(var); - Bpl.IdentifierExpr varIdE = new Bpl.IdentifierExpr(bLhs.tok, var.Name, predef.BoxType); - tmpOuts.Add(varIdE); - outs.Add(varIdE); - } else { - tmpOuts.Add(null); - outs.Add(bLhs); - } - } - } - - if (cs.Proof == null) { - AddCall(builder); - } else { - var callBuilder = new BoogieStmtListBuilder(this, options, builder.Context); - AddComment(callBuilder, cs, "call statement proof"); - CurrentIdGenerator.Push(); - TrStmt(cs.Proof, callBuilder, locals, etran); - CurrentIdGenerator.Pop(); - AddCall(callBuilder); - PathAsideBlock(cs.Tok, callBuilder, builder); - } - - void AddCall(BoogieStmtListBuilder callBuilder) { - callBuilder.Add(new CommentCmd($"ProcessCallStmt: Check precondition")); - // Make the call - AddReferencedMember(callee); - var call = Call(callBuilder.Context, tok, MethodName(callee, isCoCall ? MethodTranslationKind.CoCallPre : MethodTranslationKind.CallPre), ins, new List()); - proofDependencies?.AddProofDependencyId(call, tok, new CallDependency(cs)); - if ( - (assertionOnlyFilter != null && !assertionOnlyFilter(tok)) || - (module != currentModule && RefinementToken.IsInherited(tok, currentModule) && (codeContext == null || !codeContext.MustReverify))) { - // The call statement is inherited, so the refined module already checked that the precondition holds. Note, - // preconditions are not allowed to be strengthened, except if they use a predicate whose body has been strengthened. - // But if the callee sits in a different module, then any predicate it uses will be treated as opaque (that is, - // uninterpreted) anyway, so the refined module will have checked the call precondition for all possible definitions - // of the predicate. - call.IsFree = true; - } - callBuilder.Add(call); - } - - builder.Add(new CommentCmd("ProcessCallStmt: Make the call")); - var post = Call(builder.Context, tok, - MethodName(callee, isCoCall ? MethodTranslationKind.CoCallPost : MethodTranslationKind.CallPost), ins, outs); - proofDependencies?.AddProofDependencyId(post, tok, new CallDependency(cs)); - builder.Add(post); - - // Unbox results as needed - for (int i = 0; i < Lhss.Count; i++) { - Bpl.IdentifierExpr bLhs = Lhss[i]; - Bpl.IdentifierExpr tmpVarIdE = tmpOuts[i]; - if (tmpVarIdE != null) { - // Instead of an assignment: - // e := UnBox(tmpVar); - // we use: - // havoc e; assume e == UnBox(tmpVar); - // because that will reap the benefits of e's where clause, so that some additional type information will be known about - // the out-parameter. - Bpl.Cmd cmd = new Bpl.HavocCmd(bLhs.tok, new List { bLhs }); - builder.Add(cmd); - cmd = TrAssumeCmd(bLhs.tok, Bpl.Expr.Eq(bLhs, FunctionCall(bLhs.tok, BuiltinFunction.Unbox, TrType(LhsTypes[i]), tmpVarIdE))); - builder.Add(cmd); - } - } - } - - void TrForallStmtCall(IToken tok, List boundVars, List bounds, - Expression range, ExpressionConverter additionalRange, List forallExpressions, CallStmt s0, - BoogieStmtListBuilder definedness, BoogieStmtListBuilder exporter, List locals, ExpressionTranslator etran) { - Contract.Requires(tok != null); - Contract.Requires(boundVars != null); - Contract.Requires(bounds != null); - Contract.Requires(range != null); - // additionalRange is allowed to be null - Contract.Requires(s0 != null); - // definedness is allowed to be null - Contract.Requires(exporter != null); - Contract.Requires(locals != null); - Contract.Requires(etran != null); - - // Translate: - // forall (x,y | Range(x,y)) { - // E(x,y) . M( Args(x,y) ); - // } - // as: - // if (*) { - // var x,y; - // havoc x,y; - // CheckWellformed( Range ); - // assume Range(x,y); - // assume additionalRange; - // Tr( Call ); - // assume false; - // } else { - // initHeap := $Heap; - // advance $Heap; - // assume (forall x,y :: (Range(x,y) && additionalRange)[INIT] && - // ==> Post[old($Heap) := initHeap]( E(x,y)[INIT], Args(x,y)[INIT] )); - // } - // where Post(this,args) is the postcondition of method M and - // INIT is the substitution [old($Heap),$Heap := old($Heap),initHeap]. - - if (definedness != null) { - if (boundVars.Count != 0) { - // Note, it would be nicer (and arguably more appropriate) to do a SetupBoundVarsAsLocals - // here (rather than a TrBoundVariables). However, there is currently no way to apply - // a substMap to a statement (in particular, to s.Body), so that doesn't work here. - List freeOfAlloc = BoundedPool.HasBounds(bounds, BoundedPool.PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc); - List bvars = new List(); - var ante = etran.TrBoundVariables(boundVars, bvars, true, freeOfAlloc); - locals.AddRange(bvars); - var havocIds = new List(); - foreach (Bpl.Variable bv in bvars) { - havocIds.Add(new Bpl.IdentifierExpr(tok, bv)); - } - definedness.Add(new Bpl.HavocCmd(tok, havocIds)); - definedness.Add(TrAssumeCmd(tok, ante)); - } - TrStmt_CheckWellformed(range, definedness, locals, etran, false); - definedness.Add(TrAssumeCmd(range.tok, etran.TrExpr(range))); - if (additionalRange != null) { - var es = additionalRange(new Dictionary(), etran); - definedness.Add(TrAssumeCmd(es.tok, es)); - } - - TrStmt(s0, definedness, locals, etran); - - definedness.Add(TrAssumeCmd(tok, Bpl.Expr.False)); - } - - // Now for the other branch, where the postcondition of the call is exported. - { - var initHeapVar = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, CurrentIdGenerator.FreshId("$initHeapForallStmt#"), predef.HeapType)); - locals.Add(initHeapVar); - var initHeap = new Bpl.IdentifierExpr(tok, initHeapVar); - var initEtran = new ExpressionTranslator(this, predef, initHeap, etran.Old.HeapExpr, etran.scope); - // initHeap := $Heap; - exporter.Add(Bpl.Cmd.SimpleAssign(tok, initHeap, etran.HeapExpr)); - var heapIdExpr = etran.HeapCastToIdentifierExpr; - // advance $Heap; - exporter.Add(new Bpl.HavocCmd(tok, new List { heapIdExpr })); - Contract.Assert(s0.Method.Mod.Expressions.Count == 0); // checked by the resolver - foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(tok, new List(), s0.IsGhost, s0.Method.AllowsAllocation, initEtran, etran, initEtran)) { - if (tri.IsFree) { - exporter.Add(TrAssumeCmd(tok, tri.Expr)); - } - } - if (codeContext is IteratorDecl) { - var iter = (IteratorDecl)codeContext; - RecordNewObjectsIn_New(tok, iter, initHeap, heapIdExpr, exporter, locals, etran); - } - - // Note, in the following, we need to do a bit of a song and dance. The actual arguments of the - // call should be translated using "initEtran", whereas the method postcondition should be translated - // using "callEtran". To accomplish this, we translate the argument and then tuck the resulting - // Boogie expressions into BoogieExprWrappers that are used in the DafnyExpr-to-DafnyExpr substitution. - var bvars = new List(); - Dictionary substMap; - Bpl.Trigger antitriggerBoundVarTypes; - Bpl.Expr ante; - var argsSubstMap = new Dictionary(); // maps formal arguments to actuals - Contract.Assert(s0.Method.Ins.Count == s0.Args.Count); - var callEtran = new ExpressionTranslator(this, predef, etran.HeapExpr, initHeap, etran.scope); - Bpl.Expr post = Bpl.Expr.True; - Bpl.Trigger tr; - if (forallExpressions != null) { - // translate based on the forallExpressions since the triggers are computed based on it already. - QuantifierExpr expr = (QuantifierExpr)forallExpressions[0]; - while (expr.SplitQuantifier != null) { - expr = (QuantifierExpr)expr.SplitQuantifierExpression; - } - boundVars = expr.BoundVars; - ante = initEtran.TrBoundVariablesRename(boundVars, bvars, out substMap, out antitriggerBoundVarTypes); - ante = BplAnd(ante, initEtran.TrExpr(Substitute(expr.Range, null, substMap))); - if (additionalRange != null) { - ante = BplAnd(ante, additionalRange(substMap, initEtran)); - } - tr = TrTrigger(callEtran, expr.Attributes, expr.tok, bvars, substMap, s0.MethodSelect.TypeArgumentSubstitutionsWithParents()); - post = callEtran.TrExpr(Substitute(expr.Term, null, substMap)); - } else { - ante = initEtran.TrBoundVariablesRename(boundVars, bvars, out substMap, out antitriggerBoundVarTypes); - for (int i = 0; i < s0.Method.Ins.Count; i++) { - var arg = Substitute(s0.Args[i], null, substMap, s0.MethodSelect.TypeArgumentSubstitutionsWithParents()); // substitute the renamed bound variables for the declared ones - argsSubstMap.Add(s0.Method.Ins[i], new BoogieWrapper(initEtran.TrExpr(arg), s0.Args[i].Type)); - } - ante = BplAnd(ante, initEtran.TrExpr(Substitute(range, null, substMap))); - if (additionalRange != null) { - ante = BplAnd(ante, additionalRange(substMap, initEtran)); - } - var receiver = new BoogieWrapper(initEtran.TrExpr(Substitute(s0.Receiver, null, substMap, s0.MethodSelect.TypeArgumentSubstitutionsWithParents())), s0.Receiver.Type); - foreach (var ens in s0.Method.Ens) { - var p = Substitute(ens.E, receiver, argsSubstMap, s0.MethodSelect.TypeArgumentSubstitutionsWithParents()); // substitute the call's actuals for the method's formals - post = BplAnd(post, callEtran.TrExpr(p)); - } - tr = antitriggerBoundVarTypes; - } - - // TRIG (forall $ih#s0#0: Seq :: $Is($ih#s0#0, TSeq(TChar)) && $IsAlloc($ih#s0#0, TSeq(TChar), $initHeapForallStmt#0) && Seq#Length($ih#s0#0) != 0 && Seq#Rank($ih#s0#0) < Seq#Rank(s#0) ==> (forall i#2: int :: true ==> LitInt(0) <= i#2 && i#2 < Seq#Length($ih#s0#0) ==> char#ToInt(_module.CharChar.MinChar($LS($LZ), $Heap, this, $ih#s0#0)) <= char#ToInt($Unbox(Seq#Index($ih#s0#0, i#2)): char))) - // TRIG (forall $ih#pat0#0: Seq, $ih#a0#0: Seq :: $Is($ih#pat0#0, TSeq(_module._default.Same0$T)) && $IsAlloc($ih#pat0#0, TSeq(_module._default.Same0$T), $initHeapForallStmt#0) && $Is($ih#a0#0, TSeq(_module._default.Same0$T)) && $IsAlloc($ih#a0#0, TSeq(_module._default.Same0$T), $initHeapForallStmt#0) && Seq#Length($ih#pat0#0) <= Seq#Length($ih#a0#0) && Seq#SameUntil($ih#pat0#0, $ih#a0#0, Seq#Length($ih#pat0#0)) && (Seq#Rank($ih#pat0#0) < Seq#Rank(pat#0) || (Seq#Rank($ih#pat0#0) == Seq#Rank(pat#0) && Seq#Rank($ih#a0#0) < Seq#Rank(a#0))) ==> _module.__default.IsRelaxedPrefixAux(_module._default.Same0$T, $LS($LZ), $Heap, $ih#pat0#0, $ih#a0#0, LitInt(1)))' - // TRIG (forall $ih#m0#0: DatatypeType, $ih#n0#0: DatatypeType :: $Is($ih#m0#0, Tclass._module.Nat()) && $IsAlloc($ih#m0#0, Tclass._module.Nat(), $initHeapForallStmt#0) && $Is($ih#n0#0, Tclass._module.Nat()) && $IsAlloc($ih#n0#0, Tclass._module.Nat(), $initHeapForallStmt#0) && Lit(true) && (DtRank($ih#m0#0) < DtRank(m#0) || (DtRank($ih#m0#0) == DtRank(m#0) && DtRank($ih#n0#0) < DtRank(n#0))) ==> _module.__default.mult($LS($LZ), $Heap, $ih#m0#0, _module.__default.plus($LS($LZ), $Heap, $ih#n0#0, $ih#n0#0)) == _module.__default.mult($LS($LZ), $Heap, _module.__default.plus($LS($LZ), $Heap, $ih#m0#0, $ih#m0#0), $ih#n0#0)) - var qq = new Bpl.ForallExpr(tok, bvars, tr, BplImp(ante, post)); // TODO: Add a SMART_TRIGGER here. If we can't find one, abort the attempt to do induction automatically - exporter.Add(TrAssumeCmd(tok, qq)); - } - } void RecordNewObjectsIn_New(IToken tok, IteratorDecl iter, Bpl.Expr initHeap, Bpl.IdentifierExpr currentHeap, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { @@ -2255,531 +904,6 @@ private string GetObjFieldDetails(Expression lhs, ExpressionTranslator etran, ou return description; } - - void ProcessRhss(List lhsBuilder, List bLhss, - List lhss, List rhss, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, Statement stmt) { - Contract.Requires(lhsBuilder != null); - Contract.Requires(bLhss != null); - Contract.Requires(cce.NonNullElements(lhss)); - Contract.Requires(cce.NonNullElements(rhss)); - Contract.Requires(builder != null); - Contract.Requires(cce.NonNullElements(locals)); - Contract.Requires(etran != null); - Contract.Requires(predef != null); - - var finalRhss = new List(); - for (int i = 0; i < lhss.Count; i++) { - var lhs = lhss[i]; - // the following assumes are part of the precondition, really - Contract.Assume(!(lhs is ConcreteSyntaxExpression)); - Contract.Assume(!(lhs is SeqSelectExpr && !((SeqSelectExpr)lhs).SelectOne)); // array-range assignments are not allowed - - Type lhsType, rhsTypeConstraint; - if (lhs is IdentifierExpr) { - var ide = (IdentifierExpr)lhs; - lhsType = ide.Var.Type; - rhsTypeConstraint = lhsType; - } else if (lhs is MemberSelectExpr) { - var fse = (MemberSelectExpr)lhs; - var field = (Field)fse.Member; - Contract.Assert(VisibleInScope(field)); - lhsType = field.Type; - rhsTypeConstraint = lhsType.Subst(fse.TypeArgumentSubstitutionsWithParents()); - } else if (lhs is SeqSelectExpr) { - var e = (SeqSelectExpr)lhs; - lhsType = null; // for an array update, always make sure the value assigned is boxed - rhsTypeConstraint = e.Seq.Type.NormalizeExpand().TypeArgs[0]; - } else { - var e = (MultiSelectExpr)lhs; - lhsType = null; // for an array update, always make sure the value assigned is boxed - rhsTypeConstraint = e.Array.Type.NormalizeExpand().TypeArgs[0]; - } - var bRhs = TrAssignmentRhs(rhss[i].Tok, bLhss[i], null, lhsType, rhss[i], rhsTypeConstraint, builder, locals, etran, stmt); - if (bLhss[i] != null) { - Contract.Assert(bRhs == bLhss[i]); // this is what the postcondition of TrAssignmentRhs promises - // assignment has already been done by TrAssignmentRhs - finalRhss.Add(null); - } else { - Contract.Assert(bRhs != null); // this is what the postcondition of TrAssignmentRhs promises - finalRhss.Add(bRhs); - } - } - for (int i = 0; i < lhss.Count; i++) { - lhsBuilder[i](finalRhss[i], rhss[i] is HavocRhs, builder, etran); - } - } - - List ProcessUpdateAssignRhss(List lhss, List rhss, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, - Statement stmt) { - Contract.Requires(cce.NonNullElements(lhss)); - Contract.Requires(cce.NonNullElements(rhss)); - Contract.Requires(builder != null); - Contract.Requires(cce.NonNullElements(locals)); - Contract.Requires(etran != null); - Contract.Requires(predef != null); - Contract.Ensures(Contract.ForAll(Contract.Result>(), i => i != null)); - - var finalRhss = new List(); - for (int i = 0; i < lhss.Count; i++) { - var lhs = lhss[i]; - // the following assumes are part of the precondition, really - Contract.Assume(!(lhs is ConcreteSyntaxExpression)); - Contract.Assume(!(lhs is SeqSelectExpr && !((SeqSelectExpr)lhs).SelectOne)); // array-range assignments are not allowed - - Type lhsType, rhsTypeConstraint; - if (lhs is IdentifierExpr) { - lhsType = ((IdentifierExpr)lhs).Var.Type; - rhsTypeConstraint = lhsType; - } else if (lhs is MemberSelectExpr) { - var fse = (MemberSelectExpr)lhs; - var field = (Field)fse.Member; - Contract.Assert(VisibleInScope(field)); - lhsType = field.Type; - rhsTypeConstraint = lhsType.Subst(fse.TypeArgumentSubstitutionsWithParents()); - } else if (lhs is SeqSelectExpr) { - var e = (SeqSelectExpr)lhs; - lhsType = null; // for an array update, always make sure the value assigned is boxed - rhsTypeConstraint = e.Seq.Type.NormalizeExpand().TypeArgs[0]; - } else { - var e = (MultiSelectExpr)lhs; - lhsType = null; // for an array update, always make sure the value assigned is boxed - rhsTypeConstraint = e.Array.Type.NormalizeExpand().TypeArgs[0]; - } - var bRhs = TrAssignmentRhs(rhss[i].Tok, null, (lhs as IdentifierExpr)?.Var, lhsType, rhss[i], rhsTypeConstraint, builder, locals, etran, stmt); - finalRhss.Add(bRhs); - } - return finalRhss; - } - - - private void CheckLhssDistinctness(List rhs, List rhsOriginal, List lhss, - BoogieStmtListBuilder builder, ExpressionTranslator etran, - Bpl.Expr[] objs, Bpl.Expr[] fields, string[] names, Expression originalInitialLhs = null) { - Contract.Requires(rhs != null); - Contract.Requires(rhsOriginal != null); - Contract.Requires(lhss != null); - Contract.Requires(rhs.Count == rhsOriginal.Count); - Contract.Requires(lhss.Count == rhsOriginal.Count); - Contract.Requires(builder != null); - Contract.Requires(etran != null); - Contract.Requires(predef != null); - - for (int i = 0; i < lhss.Count; i++) { - var lhs = lhss[i]; - Contract.Assume(!(lhs is ConcreteSyntaxExpression)); - if (originalInitialLhs != null) { - // TODO - check RHS values? - AssertDistinctness(lhs, originalInitialLhs, builder, etran); - } - for (int j = 0; j < i; j++) { - if (rhsOriginal[i] is HavocRhs || rhsOriginal[j] is HavocRhs) { - AssertDistinctness(lhs, lhss[j], builder, etran); - } else { - AssertDistinctness(lhs, lhss[j], rhs[i], rhs[j], builder, etran); - } - } - } - } - - /// - /// Note, if "rhs" is "null", then the assignment has already been done elsewhere. However, any other bookkeeping - /// is still done. - /// - delegate void AssignToLhs(Bpl.Expr/*?*/ rhs, bool origRhsIsHavoc, BoogieStmtListBuilder builder, ExpressionTranslator etran); - - void AssertDistinctness(Expression lhsa, Expression lhsb, BoogieStmtListBuilder builder, ExpressionTranslator etran) { - CheckDistinctness(lhsa, lhsb, etran, out var dExpr, out var bExpr); - if (bExpr != null) { - builder.Add(Assert(GetToken(lhsa), bExpr, new PODesc.DistinctLHS(Printer.ExprToString(options, lhsa), - Printer.ExprToString(options, lhsb), bExpr != Bpl.Expr.False, false, dExpr), builder.Context)); - } - } - - void AssertDistinctness(Expression lhsa, Expression lhsb, Bpl.Expr rhsa, Bpl.Expr rhsb, BoogieStmtListBuilder builder, ExpressionTranslator etran) { - CheckDistinctness(lhsa, lhsb, etran, out var dExpr, out var bExpr); - if (bExpr != null) { - bExpr = BplOr(bExpr, Bpl.Expr.Eq(rhsa, rhsb)); - builder.Add(Assert(GetToken(lhsa), bExpr, new PODesc.DistinctLHS(Printer.ExprToString(options, lhsa), - Printer.ExprToString(options, lhsb), false, true, dExpr), builder.Context)); - } - } - - /// - /// Creates a list of protected Boogie LHSs for the given Dafny LHSs. Along the way, - /// builds code that checks that the LHSs are well-defined, - /// and are allowed by the enclosing reads and modifies clause. - /// Checks that they denote different locations iff checkDistinctness is true. - /// - void ProcessLhss(List lhss, bool rhsCanAffectPreviouslyKnownExpressions, bool checkDistinctness, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, Statement stmt, - out List lhsBuilders, out List bLhss, - out Bpl.Expr[] prevObj, out Bpl.Expr[] prevIndex, out string[] prevNames, Expression originalInitialLhs = null) { - - Contract.Requires(cce.NonNullElements(lhss)); - Contract.Requires(builder != null); - Contract.Requires(cce.NonNullElements(locals)); - Contract.Requires(etran != null); - Contract.Requires(predef != null); - Contract.Ensures(Contract.ValueAtReturn(out lhsBuilders).Count == lhss.Count); - Contract.Ensures(Contract.ValueAtReturn(out lhsBuilders).Count == Contract.ValueAtReturn(out bLhss).Count); - - rhsCanAffectPreviouslyKnownExpressions = rhsCanAffectPreviouslyKnownExpressions || lhss.Count != 1; - - // for each Dafny LHS, build a protected Boogie LHS for the eventual assignment - lhsBuilders = new List(); - bLhss = new List(); - prevObj = new Bpl.Expr[lhss.Count]; - prevIndex = new Bpl.Expr[lhss.Count]; - prevNames = new string[lhss.Count]; - int i = 0; - - var lhsNameSet = new Dictionary(); - - var contextModFrames = GetContextModifiesFrames(); - - // Note, the resolver does not check for duplicate IdentifierExpr's in LHSs, so do it here. - foreach (var lhs in lhss) { - Contract.Assume(!(lhs is ConcreteSyntaxExpression)); - if (checkDistinctness) { - if (originalInitialLhs != null) { - AssertDistinctness(lhs, originalInitialLhs.Resolved, builder, etran); - } - for (int j = 0; j < i; j++) { - AssertDistinctness(lhs, lhss[j], builder, etran); - } - } - i++; - } - - i = 0; - foreach (var lhs in lhss) { - IToken tok = lhs.tok; - TrStmt_CheckWellformed(lhs, builder, locals, etran, true, true); - - if (lhs is IdentifierExpr) { - var ie = (IdentifierExpr)lhs; - prevNames[i] = ie.Name; - var bLhs = (Bpl.IdentifierExpr)etran.TrExpr(lhs); // TODO: is this cast always justified? - bLhss.Add(rhsCanAffectPreviouslyKnownExpressions ? null : bLhs); - lhsBuilders.Add(delegate (Bpl.Expr rhs, bool origRhsIsHavoc, BoogieStmtListBuilder bldr, ExpressionTranslator et) { - if (rhs != null) { - var cmd = Bpl.Cmd.SimpleAssign(tok, bLhs, rhs); - proofDependencies?.AddProofDependencyId(cmd, lhs.tok, new AssignmentDependency(stmt.RangeToken)); - bldr.Add(cmd); - } - - if (!origRhsIsHavoc || ie.Type.HavocCountsAsDefiniteAssignment(ie.Var.IsGhost)) { - MarkDefiniteAssignmentTracker(ie, bldr); - } - }); - - } else if (lhs is MemberSelectExpr) { - var fse = (MemberSelectExpr)lhs; - var field = fse.Member as Field; - Contract.Assert(field != null); - Contract.Assert(VisibleInScope(field)); - - var useSurrogateLocal = inBodyInitContext && Expression.AsThis(fse.Obj) != null; - - var obj = SaveInTemp(etran.TrExpr(fse.Obj), rhsCanAffectPreviouslyKnownExpressions, - "$obj" + i, predef.RefType, builder, locals); - prevObj[i] = obj; - if (!useSurrogateLocal) { - // check that the enclosing modifies clause allows this object to be written: assert $_ModifiesFrame[obj]); - var desc = new PODesc.Modifiable("an object", contextModFrames, fse.Obj, field); - builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.ModifiesFrame(tok), obj, GetField(fse)), desc, builder.Context)); - } - - if (useSurrogateLocal) { - var nm = SurrogateName(field); - var bLhs = new Bpl.IdentifierExpr(fse.tok, nm, TrType(field.Type)); - bLhss.Add(rhsCanAffectPreviouslyKnownExpressions ? null : bLhs); - lhsBuilders.Add(delegate (Bpl.Expr rhs, bool origRhsIsHavoc, BoogieStmtListBuilder bldr, ExpressionTranslator et) { - if (rhs != null) { - var cmd = Bpl.Cmd.SimpleAssign(tok, bLhs, rhs); - proofDependencies?.AddProofDependencyId(cmd, fse.tok, new AssignmentDependency(stmt.RangeToken)); - bldr.Add(cmd); - } - - if (!origRhsIsHavoc || field.Type.HavocCountsAsDefiniteAssignment(field.IsGhost)) { - MarkDefiniteAssignmentTracker(lhs.tok, nm, bldr); - } - }); - } else { - bLhss.Add(null); - lhsBuilders.Add(delegate (Bpl.Expr rhs, bool origRhsIsHavoc, BoogieStmtListBuilder bldr, ExpressionTranslator et) { - if (rhs != null) { - var fseField = fse.Member as Field; - Contract.Assert(fseField != null); - Check_NewRestrictions(tok, fse.Obj, obj, fseField, rhs, bldr, et); - var h = (Bpl.IdentifierExpr)et.HeapExpr; // TODO: is this cast always justified? - var cmd = Bpl.Cmd.SimpleAssign(tok, h, UpdateHeap(tok, h, obj, new Bpl.IdentifierExpr(tok, GetField(fseField)), rhs)); - proofDependencies?.AddProofDependencyId(cmd, lhs.tok, new AssignmentDependency(stmt.RangeToken)); - bldr.Add(cmd); - // assume $IsGoodHeap($Heap); - bldr.Add(AssumeGoodHeap(tok, et)); - } - }); - } - - } else if (lhs is SeqSelectExpr) { - SeqSelectExpr sel = (SeqSelectExpr)lhs; - Contract.Assert(sel.SelectOne); // array-range assignments are not allowed - Contract.Assert(sel.Seq.Type != null && sel.Seq.Type.IsArrayType); - Contract.Assert(sel.E0 != null); - var obj = SaveInTemp(etran.TrExpr(sel.Seq), rhsCanAffectPreviouslyKnownExpressions, - "$obj" + i, predef.RefType, builder, locals); - var idx = etran.TrExpr(sel.E0); - idx = ConvertExpression(sel.E0.tok, idx, sel.E0.Type, Type.Int); - var fieldName = SaveInTemp(FunctionCall(tok, BuiltinFunction.IndexField, null, idx), rhsCanAffectPreviouslyKnownExpressions, - "$index" + i, predef.FieldName(tok), builder, locals); - prevObj[i] = obj; - prevIndex[i] = fieldName; - // check that the enclosing modifies clause allows this object to be written: assert $_Frame[obj,index]); - var desc = new PODesc.Modifiable("an array element", contextModFrames, sel.Seq, null); - builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.ModifiesFrame(tok), obj, fieldName), desc, builder.Context)); - - bLhss.Add(null); - lhsBuilders.Add(delegate (Bpl.Expr rhs, bool origRhsIsHavoc, BoogieStmtListBuilder bldr, ExpressionTranslator et) { - if (rhs != null) { - var h = (Bpl.IdentifierExpr)et.HeapExpr; // TODO: is this cast always justified? - var cmd = Bpl.Cmd.SimpleAssign(tok, h, UpdateHeap(tok, h, obj, fieldName, rhs)); - proofDependencies?.AddProofDependencyId(cmd, lhs.tok, new AssignmentDependency(stmt.RangeToken)); - bldr.Add(cmd); - // assume $IsGoodHeap($Heap); - bldr.Add(AssumeGoodHeap(tok, et)); - } - }); - - } else { - MultiSelectExpr mse = (MultiSelectExpr)lhs; - Contract.Assert(mse.Array.Type != null && mse.Array.Type.IsArrayType); - - var obj = SaveInTemp(etran.TrExpr(mse.Array), rhsCanAffectPreviouslyKnownExpressions, - "$obj" + i, predef.RefType, builder, locals); - var fieldName = SaveInTemp(etran.GetArrayIndexFieldName(mse.tok, mse.Indices), rhsCanAffectPreviouslyKnownExpressions, - "$index" + i, predef.FieldName(mse.tok), builder, locals); - prevObj[i] = obj; - prevIndex[i] = fieldName; - var desc = new PODesc.Modifiable("an array element", contextModFrames, mse.Array, null); - builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.ModifiesFrame(tok), obj, fieldName), desc, builder.Context)); - - bLhss.Add(null); - lhsBuilders.Add(delegate (Bpl.Expr rhs, bool origRhsIsHavoc, BoogieStmtListBuilder bldr, ExpressionTranslator et) { - if (rhs != null) { - var h = (Bpl.IdentifierExpr)et.HeapExpr; // TODO: is this cast always justified? - var cmd = Bpl.Cmd.SimpleAssign(tok, h, UpdateHeap(tok, h, obj, fieldName, rhs)); - proofDependencies?.AddProofDependencyId(cmd, lhs.tok, new AssignmentDependency(stmt.RangeToken)); - bldr.Add(cmd); - // assume $IsGoodHeap($Heap); - bldr.Add(AssumeGoodHeap(tok, etran)); - } - }); - } - - i++; - } - } - - /// - /// if "bGivenLhs" is non-null, generates an assignment of the translation of "rhs" to "bGivenLhs" and then returns "bGivenLhs". - /// If "bGivenLhs" is null, then this method will return an expression that in a stable way denotes the translation of "rhs"; - /// this is achieved by creating a new temporary Boogie variable to hold the result and returning an expression that mentions - /// that new temporary variable. - /// - /// Before the assignment, the generated code will check that "rhs" obeys any subrange requirements entailed by "rhsTypeConstraint". - /// - /// The purpose of "lhsVar" is to determine an appropriate Boogie "where" clause for any temporary variable generated. - /// If passed in as non-null, it says that "lhsVar" is the LHS of the assignment being translated. If the type is subject to - /// definite-assignment rules and the RHS is "*", then the "where" clause of the temporary variable will have the form - /// "defass#lhs ==> wh" where "defass#lhs" is the definite-assignment tracker for "lhsVar" and "wh" is the "where" - /// clause for type "lhsType" for the temporary variable. - /// - /// The purpose of "lhsType" is to determine if the expression should be boxed before doing the assignment. It is allowed to be null, - /// which indicates that the result should always be a box. Note that "lhsType" may refer to a formal type parameter that is not in - /// scope; this is okay, since the purpose of "lhsType" is just to say whether or not the result should be boxed. - /// - Bpl.Expr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bGivenLhs, IVariable lhsVar, Type lhsType, - AssignmentRhs rhs, Type rhsTypeConstraint, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, - Statement stmt) { - Contract.Requires(tok != null); - Contract.Requires(rhs != null); - Contract.Requires(rhsTypeConstraint != null); - Contract.Requires(builder != null); - Contract.Requires(locals != null); - Contract.Requires(etran != null); - Contract.Requires(predef != null); - Contract.Ensures(Contract.Result() != null); - Contract.Ensures(bGivenLhs == null || Contract.Result() == bGivenLhs); - - Bpl.IdentifierExpr bLhs; - if (bGivenLhs != null) { - bLhs = bGivenLhs; - } else { - Type localType = rhsTypeConstraint; // this is a type that is appropriate for capturing the value of the RHS - var ty = TrType(localType); - var nm = CurrentIdGenerator.FreshId("$rhs#"); - Bpl.Expr wh; - if (rhs is HavocRhs && localType.IsNonempty) { - wh = GetWhereClause(tok, new Bpl.IdentifierExpr(tok, nm, ty), localType, etran, NOALLOC); - } else if (rhs is HavocRhs && lhsVar != null && GetDefiniteAssignmentTracker(lhsVar) != null) { - // This "where" clause expresses that the new variable has a value of the given type only if - // the variable has already been definitely assigned. (If it has not already been assigned, - // then the variable will get a new value, but Dafny's definite-assginment rules prevent that - // value from being used, so it's appropriate to use effectively-"true" as the "where" clause - // in that case. - wh = BplImp(GetDefiniteAssignmentTracker(lhsVar), - GetWhereClause(tok, new Bpl.IdentifierExpr(tok, nm, ty), localType, etran, NOALLOC)); - } else { - // In this case, it could be unsound to use a "where" clause, see issue #1619. - // Luckily, leaving it out is harmless, because we don't need a "where" clause here in the first - // place--because the variable is short lived, we know it will not be havoc'ed by Boogie, so a - // "where" wouldn't provide additional information over the assigned value. - wh = null; - } - var v = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, nm, ty, wh)); - locals.Add(v); - bLhs = new Bpl.IdentifierExpr(tok, v); - } - - 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, addResultCommands: - (returnBuilder, result) => { - if (cre != null) { - returnBuilder.Add(Assert(result.Tok, cre, desc, builder.Context)); - } - }); - - if (bGivenLhs != null) { - Contract.Assert(bGivenLhs == bLhs); - // box the RHS, then do the assignment - var cmd = Bpl.Cmd.SimpleAssign(tok, bGivenLhs, AdaptBoxing(tok, bRhs, e.Expr.Type, lhsType)); - proofDependencies?.AddProofDependencyId(cmd, tok, new AssignmentDependency(stmt.RangeToken)); - builder.Add(cmd); - return bGivenLhs; - } else { - // box from RHS type to tmp-var type, then do the assignment; then return LHS, boxed from tmp-var type to result type - var cmd = Bpl.Cmd.SimpleAssign(tok, bLhs, AdaptBoxing(tok, bRhs, e.Expr.Type, rhsTypeConstraint)); - proofDependencies?.AddProofDependencyId(cmd, tok, new AssignmentDependency(stmt.RangeToken)); - builder.Add(cmd); - return CondApplyBox(tok, bLhs, rhsTypeConstraint, lhsType); - } - - } else if (rhs is HavocRhs) { - builder.Add(new Bpl.HavocCmd(tok, new List { bLhs })); - return CondApplyBox(tok, bLhs, rhsTypeConstraint, lhsType); - } else { - // x := new Something - Contract.Assert(rhs is TypeRhs); // otherwise, an unexpected AssignmentRhs - TypeRhs tRhs = (TypeRhs)rhs; - - var callsConstructor = tRhs.InitCall != null && tRhs.InitCall.Method is Constructor; - - if (tRhs.ArrayDimensions == null) { - Contract.Assert(tRhs.ElementInit == null && tRhs.InitDisplay == null); - } else { - int i = 0; - foreach (Expression dim in tRhs.ArrayDimensions) { - CheckWellformed(dim, new WFOptions(), locals, builder, etran); - var desc = new PODesc.NonNegative(tRhs.ArrayDimensions.Count == 1 - ? "array size" : $"array size (dimension {i})", dim); - builder.Add(Assert(GetToken(dim), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(dim)), desc, builder.Context)); - i++; - } - if (tRhs.ElementInit != null) { - CheckWellformed(tRhs.ElementInit, new WFOptions(), locals, builder, etran); - } else if (tRhs.InitDisplay != null) { - var dim = tRhs.ArrayDimensions[0]; - var desc = new PODesc.ArrayInitSizeValid(tRhs, dim); - builder.Add(Assert(GetToken(dim), Bpl.Expr.Eq(etran.TrExpr(dim), Bpl.Expr.Literal(tRhs.InitDisplay.Count)), desc, builder.Context)); - foreach (var v in tRhs.InitDisplay) { - CheckWellformed(v, new WFOptions(), locals, builder, etran); - } - } else if (options.DefiniteAssignmentLevel == 0) { - // cool - } else if ((2 <= options.DefiniteAssignmentLevel && options.DefiniteAssignmentLevel != 4) || - options.Get(CommonOptionBag.EnforceDeterminism) || - !tRhs.EType.HasCompilableValue) { - // this is allowed only if the array size is such that it has no elements - Bpl.Expr zeroSize = Bpl.Expr.False; - foreach (Expression dim in tRhs.ArrayDimensions) { - zeroSize = BplOr(zeroSize, Bpl.Expr.Eq(Bpl.Expr.Literal(0), etran.TrExpr(dim))); - } - var desc = new PODesc.ArrayInitEmpty(tRhs.EType.ToString(), tRhs.ArrayDimensions); - builder.Add(Assert(tRhs.Tok, zeroSize, desc, builder.Context)); - } - } - - Bpl.IdentifierExpr nw = GetNewVar_IdExpr(tok, locals); - if (!callsConstructor) { - SelectAllocateObject(tok, nw, tRhs.Type, true, builder, etran); - if (tRhs.ArrayDimensions != null) { - int i = 0; - foreach (Expression dim in tRhs.ArrayDimensions) { - // assume Array#Length($nw, i) == arraySize; - Bpl.Expr arrayLength = ArrayLength(tok, nw, tRhs.ArrayDimensions.Count, i); - builder.Add(TrAssumeCmd(tok, Bpl.Expr.Eq(arrayLength, etran.TrExpr(dim)))); - i++; - } - if (tRhs.ElementInit != null) { - CheckElementInit(tok, true, tRhs.ArrayDimensions, tRhs.EType, tRhs.ElementInit, nw, builder, etran, new WFOptions()); - } else if (tRhs.InitDisplay != null) { - int ii = 0; - foreach (var v in tRhs.InitDisplay) { - var EE_ii = etran.TrExpr(v); - // assert EE_ii satisfies any subset-type constraints; - CheckSubrange(v.tok, EE_ii, v.Type, tRhs.EType, v, builder); - // assume nw[ii] == EE_ii; - var ai = ReadHeap(tok, etran.HeapExpr, nw, GetArrayIndexFieldName(tok, new List { Bpl.Expr.Literal(ii) })); - builder.Add(new Bpl.AssumeCmd(tok, Bpl.Expr.Eq(UnboxUnlessInherentlyBoxed(ai, tRhs.EType), AdaptBoxing(tok, EE_ii, v.Type, tRhs.EType)))); - ii++; - } - } - } - Bpl.Cmd heapAllocationRecorder = null; - if (codeContext is IteratorDecl) { - var iter = (IteratorDecl)codeContext; - // $Heap[this, _new] := Set#UnionOne($Heap[this, _new], $Box($nw)); - var th = new Bpl.IdentifierExpr(tok, etran.This, predef.RefType); - var nwField = new Bpl.IdentifierExpr(tok, GetField(iter.Member_New)); - var thisDotNew = ApplyUnbox(tok, ReadHeap(tok, etran.HeapExpr, th, nwField), predef.SetType); - var unionOne = FunctionCall(tok, BuiltinFunction.SetUnionOne, predef.BoxType, thisDotNew, ApplyBox(tok, nw)); - var heapRhs = UpdateHeap(tok, etran.HeapExpr, th, nwField, unionOne); - heapAllocationRecorder = Bpl.Cmd.SimpleAssign(tok, etran.HeapCastToIdentifierExpr, heapRhs); - } - CommitAllocatedObject(tok, nw, heapAllocationRecorder, builder, etran); - } - if (tRhs.InitCall != null) { - AddComment(builder, tRhs.InitCall, "init call statement"); - TrCallStmt(tRhs.InitCall, builder, locals, etran, nw); - } - // bLhs := $nw; - CheckSubrange(tok, nw, tRhs.Type, rhsTypeConstraint, null, builder); - if (bGivenLhs != null) { - Contract.Assert(bGivenLhs == bLhs); - // box the RHS, then do the assignment - var cmd = Bpl.Cmd.SimpleAssign(tok, bGivenLhs, CondApplyBox(tok, nw, tRhs.Type, lhsType)); - proofDependencies?.AddProofDependencyId(cmd, tok, new AssignmentDependency(stmt.RangeToken)); - builder.Add(cmd); - return bGivenLhs; - } else { - // do the assignment, then box the result - var cmd = Bpl.Cmd.SimpleAssign(tok, bLhs, nw); - proofDependencies?.AddProofDependencyId(cmd, tok, new AssignmentDependency(stmt.RangeToken)); - builder.Add(cmd); - return CondApplyBox(tok, bLhs, tRhs.Type, lhsType); - } - } - } - - private void SelectAllocateObject(IToken tok, Bpl.IdentifierExpr nw, Type type, bool includeHavoc, BoogieStmtListBuilder builder, ExpressionTranslator etran) { Contract.Requires(tok != null); Contract.Requires(nw != null); From 5a2f37c62b6e24dcbfe47cc759d0d0b25b40922d Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 20 Sep 2024 15:43:08 +0200 Subject: [PATCH 05/47] Use namespace directive --- .../Statements/BoogieGenerator.TrStatement.cs | 1904 ++++++++--------- 1 file changed, 951 insertions(+), 953 deletions(-) diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs index a90ef8dd0a9..357ce38cb43 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs @@ -6,1064 +6,1062 @@ using DafnyCore.Verifier.Statements; using Microsoft.Boogie; using Bpl = Microsoft.Boogie; -using BplParser = Microsoft.Boogie.Parser; using static Microsoft.Dafny.Util; -using Action = System.Action; using PODesc = Microsoft.Dafny.ProofObligationDescription; -namespace Microsoft.Dafny { - public partial class BoogieGenerator { - public const string FrameVariablePrefix = "$Frame$"; +namespace Microsoft.Dafny; - public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, - List locals, ExpressionTranslator etran) { +public partial class BoogieGenerator { + public const string FrameVariablePrefix = "$Frame$"; - stmt.ScopeDepth = builder.Context.ScopeDepth; + public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, + List locals, ExpressionTranslator etran) { - Contract.Requires(stmt != null); - Contract.Requires(builder != null); - Contract.Requires(locals != null); - Contract.Requires(etran != null); - Contract.Requires(codeContext != null && predef != null); - Contract.Ensures(fuelContext == Contract.OldValue(fuelContext)); + stmt.ScopeDepth = builder.Context.ScopeDepth; - stmtContext = StmtType.NONE; - adjustFuelForExists = true; // fuel for exists might need to be adjusted based on whether it's in an assert or assume stmt. - if (stmt is PredicateStmt predicateStmt) { - TrPredicateStmt(predicateStmt, builder, locals, etran); + Contract.Requires(stmt != null); + Contract.Requires(builder != null); + Contract.Requires(locals != null); + Contract.Requires(etran != null); + Contract.Requires(codeContext != null && predef != null); + Contract.Ensures(fuelContext == Contract.OldValue(fuelContext)); - } else if (stmt is PrintStmt) { - AddComment(builder, stmt, "print statement"); - PrintStmt s = (PrintStmt)stmt; - foreach (var arg in s.Args) { - TrStmt_CheckWellformed(arg, builder, locals, etran, false); - } - if (options.TestGenOptions.Mode != TestGenerationOptions.Modes.None) { - builder.AddCaptureState(s); - } - - } else if (stmt is HideRevealStmt revealStmt) { - TranslateRevealStmt(builder, locals, etran, revealStmt); - } else if (stmt is BreakStmt) { - var s = (BreakStmt)stmt; - AddComment(builder, stmt, $"{s.Kind} statement"); - foreach (var _ in Enumerable.Range(0, builder.Context.ScopeDepth - s.TargetStmt.ScopeDepth)) { - builder.Add(new ChangeScope(s.Tok, ChangeScope.Modes.Pop)); - } - var lbl = (s.IsContinue ? "continue_" : "after_") + s.TargetStmt.Labels.Data.AssignUniqueId(CurrentIdGenerator); - builder.Add(new GotoCmd(s.Tok, new List { lbl })); - } else if (stmt is ReturnStmt) { - var s = (ReturnStmt)stmt; - AddComment(builder, stmt, "return statement"); - if (s.ReverifyPost) { - // $_reverifyPost := true; - builder.Add(Bpl.Cmd.SimpleAssign(s.Tok, new Bpl.IdentifierExpr(s.Tok, "$_reverifyPost", Bpl.Type.Bool), Bpl.Expr.True)); - } - if (s.HiddenUpdate != null) { - TrStmt(s.HiddenUpdate, builder, locals, etran); - } - if (codeContext is IMethodCodeContext) { - var method = (IMethodCodeContext)codeContext; - method.Outs.ForEach(p => CheckDefiniteAssignmentReturn(stmt.Tok, p, builder)); - } - - if (codeContext is Method { FunctionFromWhichThisIsByMethodDecl: { ByMethodTok: { } } fun } method2) { - AssumeCanCallForByMethodDecl(method2, builder); - } + stmtContext = StmtType.NONE; + adjustFuelForExists = true; // fuel for exists might need to be adjusted based on whether it's in an assert or assume stmt. + if (stmt is PredicateStmt predicateStmt) { + TrPredicateStmt(predicateStmt, builder, locals, etran); - foreach (var _ in Enumerable.Range(0, builder.Context.ScopeDepth)) { - builder.Add(new ChangeScope(s.Tok, ChangeScope.Modes.Pop)); - } - builder.Add(new Bpl.ReturnCmd(stmt.Tok)); - } else if (stmt is YieldStmt) { - var s = (YieldStmt)stmt; - AddComment(builder, s, "yield statement"); - Contract.Assert(codeContext is IteratorDecl); - var iter = (IteratorDecl)codeContext; - // if the yield statement has arguments, do them first - if (s.HiddenUpdate != null) { - TrStmt(s.HiddenUpdate, builder, locals, etran); - } - // this.ys := this.ys + [this.y]; - var th = new ThisExpr(iter); - var dafnyOutExprs = new List(); - Contract.Assert(iter.OutsFields.Count == iter.OutsHistoryFields.Count); - for (int i = 0; i < iter.OutsFields.Count; i++) { - var y = iter.OutsFields[i]; - var dafnyY = new MemberSelectExpr(s.Tok, th, y); - dafnyOutExprs.Add(dafnyY); - var ys = iter.OutsHistoryFields[i]; - var dafnyYs = new MemberSelectExpr(s.Tok, th, ys); - var dafnySingletonY = new SeqDisplayExpr(s.Tok, new List() { dafnyY }); - dafnySingletonY.Type = ys.Type; // resolve here - var rhs = new BinaryExpr(s.Tok, BinaryExpr.Opcode.Add, dafnyYs, dafnySingletonY); - rhs.ResolvedOp = BinaryExpr.ResolvedOpcode.Concat; - rhs.Type = ys.Type; // resolve here - var cmd = Bpl.Cmd.SimpleAssign(s.Tok, etran.HeapCastToIdentifierExpr, - UpdateHeap(s.Tok, etran.HeapExpr, etran.TrExpr(th), new Bpl.IdentifierExpr(s.Tok, GetField(ys)), etran.TrExpr(rhs))); - builder.Add(cmd); - } - // yieldCount := yieldCount + 1; assume yieldCount == |ys|; - var yc = new Bpl.IdentifierExpr(s.Tok, yieldCountVariable); - var incYieldCount = Bpl.Cmd.SimpleAssign(s.Tok, yc, Bpl.Expr.Binary(s.Tok, Bpl.BinaryOperator.Opcode.Add, yc, Bpl.Expr.Literal(1))); - builder.Add(incYieldCount); - builder.Add(TrAssumeCmd(s.Tok, YieldCountAssumption(iter, etran))); - // assume $IsGoodHeap($Heap); - builder.Add(AssumeGoodHeap(s.Tok, etran)); - // assert YieldEnsures[subst]; // where 'subst' replaces "old(E)" with "E" being evaluated in $_OldIterHeap - var yeEtran = new ExpressionTranslator(this, predef, etran.HeapExpr, new Bpl.IdentifierExpr(s.Tok, "$_OldIterHeap", predef.HeapType), iter); - - var rhss = s.Rhss == null - ? dafnyOutExprs - : s.Rhss.Select(rhs => rhs is ExprRhs e ? e.Expr : null).ToList(); - var fieldSubstMap = iter.OutsFields.Zip(rhss) - .Where(outRhs => outRhs.Second != null) - .ToDictionary( - outRhs => outRhs.First.Name, - outRhs => outRhs.Second - ); - var fieldSub = new SpecialFieldSubstituter(fieldSubstMap); - - foreach (var p in iter.YieldEnsures) { - var ss = TrSplitExpr(builder.Context, p.E, yeEtran, true, out var splitHappened); - foreach (var split in ss) { - if (RefinementToken.IsInherited(split.Tok, currentModule)) { - // this postcondition was inherited into this module, so just ignore it - } else if (split.IsChecked) { - var yieldToken = new NestedToken(s.Tok, split.Tok); - var desc = new PODesc.YieldEnsures(fieldSub.Substitute(p.E)); - builder.Add(AssertAndForget(yieldToken, split.E, desc, stmt.Tok, null)); - } - } - builder.Add(TrAssumeCmdWithDependencies(yeEtran, stmt.Tok, p.E, "yield ensures clause")); - } - YieldHavoc(iter.tok, iter, builder, etran); + } else if (stmt is PrintStmt) { + AddComment(builder, stmt, "print statement"); + PrintStmt s = (PrintStmt)stmt; + foreach (var arg in s.Args) { + TrStmt_CheckWellformed(arg, builder, locals, etran, false); + } + if (options.TestGenOptions.Mode != TestGenerationOptions.Modes.None) { builder.AddCaptureState(s); + } - } else if (stmt is AssignSuchThatStmt) { - var s = (AssignSuchThatStmt)stmt; - AddComment(builder, s, "assign-such-that statement"); - // Essentially, treat like an assert (that values for the LHS exist), a havoc (of the LHS), and an - // assume (of the RHS). However, we also need to check the well-formedness of the LHS and RHS. - // The well-formedness of the LHS is done by the havoc. The well-formedness of the RHS is most - // easily done after that havoc, but we need to be careful about two things: - // - We should not generate any errors for uses of LHSs. This is not an issue for fields or - // array elements, because they already have values before reaching the assign-such-that statement. - // (Note, "this.f" is not allowed as a LHS in the first division of a constructor.) - // For any local variable or out-parameter x that's a LHS, we create a new variable x' and - // substitute x' for x in the RHS before doing the well-formedness check. - // - The well-formedness checks need to be able to assume that each x' has a value of its - // type. However, this assumption must not carry over to the existence assertion, because - // then everything will be provable if x' is of a known-empty type. Instead, the well-formedness - // check is wrapped inside an "if" whose guard is the type antecedent. After the existence - // check, the type antecedent is assumed of the original x, the RHS is assumed, and x is - // marked off has having been definitely assigned. - // - // So, the Dafny statement - // E.f, x :| RHS(x); - // is translated into the following Boogie code: - // var x'; - // Tr[[ E.f := *; ]] // this havoc is translated like a Dafny assignment statement, which means - // // the well-formedness of E is checked here - // if (typeAntecedent(x')) { - // check well-formedness of RHS(x'); - // } - // assert (exists x'' :: RHS(x'')); // for ":| assume", omit this line; for ":|", LHS is only allowed to contain simple variables - // defass#x := true; - // havoc x; - // assume RHS(x); - - var simpleLHSs = new List(); - Bpl.Expr typeAntecedent = Bpl.Expr.True; - var havocLHSs = new List(); - var havocRHSs = new List(); - var substMap = new Dictionary(); - foreach (var lhs in s.Lhss) { - var lvalue = lhs.Resolved; - if (lvalue is IdentifierExpr ide) { - simpleLHSs.Add(ide); - var wh = SetupVariableAsLocal(ide.Var, substMap, builder, locals, etran); - typeAntecedent = BplAnd(typeAntecedent, wh); - } else { - havocLHSs.Add(lvalue); - havocRHSs.Add(new HavocRhs(lhs.tok)); // note, a HavocRhs is constructed as already resolved - } - } - ProcessLhss(havocLHSs, false, true, builder, locals, etran, stmt, out var lhsBuilder, out var bLhss, out _, out _, out _); - ProcessRhss(lhsBuilder, bLhss, havocLHSs, havocRHSs, builder, locals, etran, stmt); - - // Here comes the well-formedness check - var wellFormednessBuilder = new BoogieStmtListBuilder(this, options, builder.Context); - var rhs = Substitute(s.Expr, null, substMap); - TrStmt_CheckWellformed(rhs, wellFormednessBuilder, locals, etran, false); - var ifCmd = new Bpl.IfCmd(s.Tok, typeAntecedent, wellFormednessBuilder.Collect(s.Tok), null, null); - builder.Add(ifCmd); - - // Here comes the assert part - if (s.AssumeToken == null) { - substMap = new Dictionary(); - var bvars = new List(); - foreach (var lhs in s.Lhss) { - var l = lhs.Resolved; - if (l is IdentifierExpr x) { - CloneVariableAsBoundVar(x.tok, x.Var, "$as#" + x.Name, out var bv, out var ie); - bvars.Add(bv); - substMap.Add(x.Var, ie); - } else { - // other forms of LHSs have been ruled out by the resolver (it would be possible to - // handle them, but it would involve heap-update expressions--the translation would take - // effort, and it's not certain that the existential would be successful in verification). - Contract.Assume(false); // unexpected case - } - } + } else if (stmt is HideRevealStmt revealStmt) { + TranslateRevealStmt(builder, locals, etran, revealStmt); + } else if (stmt is BreakStmt) { + var s = (BreakStmt)stmt; + AddComment(builder, stmt, $"{s.Kind} statement"); + foreach (var _ in Enumerable.Range(0, builder.Context.ScopeDepth - s.TargetStmt.ScopeDepth)) { + builder.Add(new ChangeScope(s.Tok, ChangeScope.Modes.Pop)); + } + var lbl = (s.IsContinue ? "continue_" : "after_") + s.TargetStmt.Labels.Data.AssignUniqueId(CurrentIdGenerator); + builder.Add(new GotoCmd(s.Tok, new List { lbl })); + } else if (stmt is ReturnStmt) { + var s = (ReturnStmt)stmt; + AddComment(builder, stmt, "return statement"); + if (s.ReverifyPost) { + // $_reverifyPost := true; + builder.Add(Bpl.Cmd.SimpleAssign(s.Tok, new Bpl.IdentifierExpr(s.Tok, "$_reverifyPost", Bpl.Type.Bool), Bpl.Expr.True)); + } + if (s.HiddenUpdate != null) { + TrStmt(s.HiddenUpdate, builder, locals, etran); + } + if (codeContext is IMethodCodeContext) { + var method = (IMethodCodeContext)codeContext; + method.Outs.ForEach(p => CheckDefiniteAssignmentReturn(stmt.Tok, p, builder)); + } - GenerateAndCheckGuesses(s.Tok, bvars, s.Bounds, Substitute(s.Expr, null, substMap), TrTrigger(etran, s.Attributes, s.Tok, substMap), builder, etran); - } + if (codeContext is Method { FunctionFromWhichThisIsByMethodDecl: { ByMethodTok: { } } fun } method2) { + AssumeCanCallForByMethodDecl(method2, builder); + } - // Mark off the simple variables as having definitely been assigned AND THEN havoc their values. By doing them - // in this order, the type antecedents will in effect be assumed. - var bHavocLHSs = new List(); - foreach (var lhs in simpleLHSs) { - MarkDefiniteAssignmentTracker(lhs, builder); - bHavocLHSs.Add((Bpl.IdentifierExpr)etran.TrExpr(lhs)); - } - builder.Add(new Bpl.HavocCmd(s.Tok, bHavocLHSs)); - - // End by doing the assume - builder.Add(TrAssumeCmdWithDependencies(etran, s.Tok, s.Expr, "assign-such-that constraint")); - builder.AddCaptureState(s); // just do one capture state--here, at the very end (that is, don't do one before the assume) - - } else if (stmt is UpdateStmt statement) { - TrUpdateStmt(builder, locals, etran, statement); - } else if (stmt is AssignOrReturnStmt) { - AddComment(builder, stmt, "assign-or-return statement (:-)"); - AssignOrReturnStmt s = (AssignOrReturnStmt)stmt; - TrStmtList(s.ResolvedStatements, builder, locals, etran); - - } else if (stmt is AssignStmt) { - AddComment(builder, stmt, "assignment statement"); - AssignStmt s = (AssignStmt)stmt; - TrAssignment(stmt, s.Lhs.Resolved, s.Rhs, builder, locals, etran); - - } else if (stmt is CallStmt) { - AddComment(builder, stmt, "call statement"); - TrCallStmt((CallStmt)stmt, builder, locals, etran, null); - - } else if (stmt is DividedBlockStmt) { - var s = (DividedBlockStmt)stmt; - AddComment(builder, stmt, "divided block before new;"); - var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; - var tok = s.SeparatorTok ?? s.Tok; - // a DividedBlockStmt occurs only inside a Constructor body of a class - var cl = (ClassDecl)((Constructor)codeContext).EnclosingClass; - var fields = Concat(cl.InheritedMembers, cl.Members).ConvertAll(member => - member is Field && !member.IsStatic && !member.IsInstanceIndependentConstant ? (Field)member : null); - fields.RemoveAll(f => f == null); - var localSurrogates = fields.ConvertAll(f => new Bpl.LocalVariable(f.tok, new TypedIdent(f.tok, SurrogateName(f), TrType(f.Type)))); - locals.AddRange(localSurrogates); - fields.ForEach(f => AddDefiniteAssignmentTrackerSurrogate(f, cl, locals, codeContext is Constructor && codeContext.IsGhost)); - - Contract.Assert(!inBodyInitContext); - inBodyInitContext = true; - TrStmtList(s.BodyInit, builder, locals, etran); - Contract.Assert(inBodyInitContext); - inBodyInitContext = false; - - // The "new;" translates into an allocation of "this" - AddComment(builder, stmt, "new;"); - fields.ForEach(f => CheckDefiniteAssignmentSurrogate(s.SeparatorTok ?? s.RangeToken.EndToken, f, true, builder)); - fields.ForEach(RemoveDefiniteAssignmentTrackerSurrogate); - var th = new ThisExpr(cl); - var bplThis = (Bpl.IdentifierExpr)etran.TrExpr(th); - SelectAllocateObject(tok, bplThis, th.Type, false, builder, etran); - for (int i = 0; i < fields.Count; i++) { - // assume $Heap[this, f] == this.f; - var mse = new MemberSelectExpr(tok, th, fields[i]); - Bpl.Expr surr = new Bpl.IdentifierExpr(tok, localSurrogates[i]); - surr = CondApplyUnbox(tok, surr, fields[i].Type, mse.Type); - builder.Add(new Bpl.AssumeCmd(tok, Bpl.Expr.Eq(etran.TrExpr(mse), surr))); + foreach (var _ in Enumerable.Range(0, builder.Context.ScopeDepth)) { + builder.Add(new ChangeScope(s.Tok, ChangeScope.Modes.Pop)); + } + builder.Add(new Bpl.ReturnCmd(stmt.Tok)); + } else if (stmt is YieldStmt) { + var s = (YieldStmt)stmt; + AddComment(builder, s, "yield statement"); + Contract.Assert(codeContext is IteratorDecl); + var iter = (IteratorDecl)codeContext; + // if the yield statement has arguments, do them first + if (s.HiddenUpdate != null) { + TrStmt(s.HiddenUpdate, builder, locals, etran); + } + // this.ys := this.ys + [this.y]; + var th = new ThisExpr(iter); + var dafnyOutExprs = new List(); + Contract.Assert(iter.OutsFields.Count == iter.OutsHistoryFields.Count); + for (int i = 0; i < iter.OutsFields.Count; i++) { + var y = iter.OutsFields[i]; + var dafnyY = new MemberSelectExpr(s.Tok, th, y); + dafnyOutExprs.Add(dafnyY); + var ys = iter.OutsHistoryFields[i]; + var dafnyYs = new MemberSelectExpr(s.Tok, th, ys); + var dafnySingletonY = new SeqDisplayExpr(s.Tok, new List() { dafnyY }); + dafnySingletonY.Type = ys.Type; // resolve here + var rhs = new BinaryExpr(s.Tok, BinaryExpr.Opcode.Add, dafnyYs, dafnySingletonY); + rhs.ResolvedOp = BinaryExpr.ResolvedOpcode.Concat; + rhs.Type = ys.Type; // resolve here + var cmd = Bpl.Cmd.SimpleAssign(s.Tok, etran.HeapCastToIdentifierExpr, + UpdateHeap(s.Tok, etran.HeapExpr, etran.TrExpr(th), new Bpl.IdentifierExpr(s.Tok, GetField(ys)), etran.TrExpr(rhs))); + builder.Add(cmd); + } + // yieldCount := yieldCount + 1; assume yieldCount == |ys|; + var yc = new Bpl.IdentifierExpr(s.Tok, yieldCountVariable); + var incYieldCount = Bpl.Cmd.SimpleAssign(s.Tok, yc, Bpl.Expr.Binary(s.Tok, Bpl.BinaryOperator.Opcode.Add, yc, Bpl.Expr.Literal(1))); + builder.Add(incYieldCount); + builder.Add(TrAssumeCmd(s.Tok, YieldCountAssumption(iter, etran))); + // assume $IsGoodHeap($Heap); + builder.Add(AssumeGoodHeap(s.Tok, etran)); + // assert YieldEnsures[subst]; // where 'subst' replaces "old(E)" with "E" being evaluated in $_OldIterHeap + var yeEtran = new ExpressionTranslator(this, predef, etran.HeapExpr, new Bpl.IdentifierExpr(s.Tok, "$_OldIterHeap", predef.HeapType), iter); + + var rhss = s.Rhss == null + ? dafnyOutExprs + : s.Rhss.Select(rhs => rhs is ExprRhs e ? e.Expr : null).ToList(); + var fieldSubstMap = iter.OutsFields.Zip(rhss) + .Where(outRhs => outRhs.Second != null) + .ToDictionary( + outRhs => outRhs.First.Name, + outRhs => outRhs.Second + ); + var fieldSub = new SpecialFieldSubstituter(fieldSubstMap); + + foreach (var p in iter.YieldEnsures) { + var ss = TrSplitExpr(builder.Context, p.E, yeEtran, true, out var splitHappened); + foreach (var split in ss) { + if (RefinementToken.IsInherited(split.Tok, currentModule)) { + // this postcondition was inherited into this module, so just ignore it + } else if (split.IsChecked) { + var yieldToken = new NestedToken(s.Tok, split.Tok); + var desc = new PODesc.YieldEnsures(fieldSub.Substitute(p.E)); + builder.Add(AssertAndForget(yieldToken, split.E, desc, stmt.Tok, null)); + } } - CommitAllocatedObject(tok, bplThis, null, builder, etran); - - AddComment(builder, stmt, "divided block after new;"); - TrStmtList(s.BodyProper, builder, locals, etran); - RemoveDefiniteAssignmentTrackers(s.Body, prevDefiniteAssignmentTrackerCount); - - } else if (stmt is OpaqueBlock opaqueBlock) { - OpaqueBlockVerifier.EmitBoogie(this, opaqueBlock, builder, locals, etran, (IMethodCodeContext)codeContext); - } else if (stmt is BlockByProofStmt blockByProof) { - BlockByProofStmtVerifier.EmitBoogie(this, blockByProof, builder, locals, etran, codeContext); - } else if (stmt is BlockStmt blockStmt) { - var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; - TrStmtList(blockStmt.Body, builder, locals, etran, blockStmt.RangeToken); - RemoveDefiniteAssignmentTrackers(blockStmt.Body, prevDefiniteAssignmentTrackerCount); - } else if (stmt is IfStmt ifStmt) { - TrIfStmt(ifStmt, builder, locals, etran); - - } else if (stmt is AlternativeStmt) { - AddComment(builder, stmt, "alternative statement"); - var s = (AlternativeStmt)stmt; - var elseCase = Assert(s.Tok, Bpl.Expr.False, new PODesc.AlternativeIsComplete(), builder.Context); - TrAlternatives(s.Alternatives, s.Tok, b => b.Add(elseCase), builder, locals, etran, stmt.IsGhost); - - } else if (stmt is WhileStmt whileStmt) { - TrWhileStmt(whileStmt, builder, locals, etran); - - } else if (stmt is AlternativeLoopStmt alternativeLoopStmt) { - TrAlternativeLoopStmt(alternativeLoopStmt, builder, locals, etran); - } else if (stmt is ForLoopStmt forLoopStmt) { - TrForLoop(forLoopStmt, builder, locals, etran); - - } else if (stmt is ModifyStmt) { - AddComment(builder, stmt, "modify statement"); - var s = (ModifyStmt)stmt; - // check well-formedness of the modifies clauses - var wfOptions = new WFOptions(); - CheckFrameWellFormed(wfOptions, s.Mod.Expressions, locals, builder, etran); - // check that the modifies is a subset - var desc = new PODesc.ModifyFrameSubset("modify statement", s.Mod.Expressions, GetContextModifiesFrames()); - CheckFrameSubset(s.Tok, s.Mod.Expressions, null, null, etran, etran.ModifiesFrame(s.Tok), builder, desc, null); - // cause the change of the heap according to the given frame - var suffix = CurrentIdGenerator.FreshId("modify#"); - string modifyFrameName = FrameVariablePrefix + suffix; - var preModifyHeapVar = new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$PreModifyHeap$" + suffix, predef.HeapType)); - locals.Add(preModifyHeapVar); - DefineFrame(s.Tok, etran.ModifiesFrame(s.Tok), s.Mod.Expressions, builder, locals, modifyFrameName); - if (s.Body == null) { - var preModifyHeap = new Bpl.IdentifierExpr(s.Tok, preModifyHeapVar); - // preModifyHeap := $Heap; - builder.Add(Bpl.Cmd.SimpleAssign(s.Tok, preModifyHeap, etran.HeapExpr)); - // havoc $Heap; - builder.Add(new Bpl.HavocCmd(s.Tok, new List { etran.HeapCastToIdentifierExpr })); - // assume $HeapSucc(preModifyHeap, $Heap); OR $HeapSuccGhost - builder.Add(TrAssumeCmd(s.Tok, HeapSucc(preModifyHeap, etran.HeapExpr, s.IsGhost))); - // assume nothing outside the frame was changed - var etranPreLoop = new ExpressionTranslator(this, predef, preModifyHeap, this.currentDeclaration is IFrameScope fs ? fs : null); - var updatedFrameEtran = etran.WithModifiesFrame(modifyFrameName); - builder.Add(TrAssumeCmd(s.Tok, FrameConditionUsingDefinedFrame(s.Tok, etranPreLoop, etran, updatedFrameEtran, updatedFrameEtran.ModifiesFrame(s.Tok)))); + builder.Add(TrAssumeCmdWithDependencies(yeEtran, stmt.Tok, p.E, "yield ensures clause")); + } + YieldHavoc(iter.tok, iter, builder, etran); + builder.AddCaptureState(s); + + } else if (stmt is AssignSuchThatStmt) { + var s = (AssignSuchThatStmt)stmt; + AddComment(builder, s, "assign-such-that statement"); + // Essentially, treat like an assert (that values for the LHS exist), a havoc (of the LHS), and an + // assume (of the RHS). However, we also need to check the well-formedness of the LHS and RHS. + // The well-formedness of the LHS is done by the havoc. The well-formedness of the RHS is most + // easily done after that havoc, but we need to be careful about two things: + // - We should not generate any errors for uses of LHSs. This is not an issue for fields or + // array elements, because they already have values before reaching the assign-such-that statement. + // (Note, "this.f" is not allowed as a LHS in the first division of a constructor.) + // For any local variable or out-parameter x that's a LHS, we create a new variable x' and + // substitute x' for x in the RHS before doing the well-formedness check. + // - The well-formedness checks need to be able to assume that each x' has a value of its + // type. However, this assumption must not carry over to the existence assertion, because + // then everything will be provable if x' is of a known-empty type. Instead, the well-formedness + // check is wrapped inside an "if" whose guard is the type antecedent. After the existence + // check, the type antecedent is assumed of the original x, the RHS is assumed, and x is + // marked off has having been definitely assigned. + // + // So, the Dafny statement + // E.f, x :| RHS(x); + // is translated into the following Boogie code: + // var x'; + // Tr[[ E.f := *; ]] // this havoc is translated like a Dafny assignment statement, which means + // // the well-formedness of E is checked here + // if (typeAntecedent(x')) { + // check well-formedness of RHS(x'); + // } + // assert (exists x'' :: RHS(x'')); // for ":| assume", omit this line; for ":|", LHS is only allowed to contain simple variables + // defass#x := true; + // havoc x; + // assume RHS(x); + + var simpleLHSs = new List(); + Bpl.Expr typeAntecedent = Bpl.Expr.True; + var havocLHSs = new List(); + var havocRHSs = new List(); + var substMap = new Dictionary(); + foreach (var lhs in s.Lhss) { + var lvalue = lhs.Resolved; + if (lvalue is IdentifierExpr ide) { + simpleLHSs.Add(ide); + var wh = SetupVariableAsLocal(ide.Var, substMap, builder, locals, etran); + typeAntecedent = BplAnd(typeAntecedent, wh); } else { - // do the body, but with preModifyHeapVar as the governing frame - var updatedFrameEtran = etran.WithModifiesFrame(modifyFrameName); - CurrentIdGenerator.Push(); - TrStmt(s.Body, builder, locals, updatedFrameEtran); - CurrentIdGenerator.Pop(); + havocLHSs.Add(lvalue); + havocRHSs.Add(new HavocRhs(lhs.tok)); // note, a HavocRhs is constructed as already resolved } - builder.AddCaptureState(stmt); - - } else if (stmt is ForallStmt forallStmt) { - TrForallStmt(builder, locals, etran, forallStmt); - } else if (stmt is CalcStmt calcStmt) { - TrCalcStmt(calcStmt, builder, locals, etran); - } else if (stmt is NestedMatchStmt nestedMatchStmt) { - TrStmt(nestedMatchStmt.Flattened, builder, locals, etran); - } else if (stmt is MatchStmt matchStmt) { - TrMatchStmt(matchStmt, builder, locals, etran); - } else if (stmt is VarDeclStmt) { - var s = (VarDeclStmt)stmt; - TrVarDeclStmt(s, builder, locals, etran); - } else if (stmt is 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)); + } + ProcessLhss(havocLHSs, false, true, builder, locals, etran, stmt, out var lhsBuilder, out var bLhss, out _, out _, out _); + ProcessRhss(lhsBuilder, bLhss, havocLHSs, havocRHSs, builder, locals, etran, stmt); + + // Here comes the well-formedness check + var wellFormednessBuilder = new BoogieStmtListBuilder(this, options, builder.Context); + var rhs = Substitute(s.Expr, null, substMap); + TrStmt_CheckWellformed(rhs, wellFormednessBuilder, locals, etran, false); + var ifCmd = new Bpl.IfCmd(s.Tok, typeAntecedent, wellFormednessBuilder.Collect(s.Tok), null, null); + builder.Add(ifCmd); + + // Here comes the assert part + if (s.AssumeToken == null) { + substMap = new Dictionary(); + var bvars = new List(); + foreach (var lhs in s.Lhss) { + var l = lhs.Resolved; + if (l is IdentifierExpr x) { + CloneVariableAsBoundVar(x.tok, x.Var, "$as#" + x.Name, out var bv, out var ie); + bvars.Add(bv); + substMap.Add(x.Var, ie); + } else { + // other forms of LHSs have been ruled out by the resolver (it would be possible to + // handle them, but it would involve heap-update expressions--the translation would take + // effort, and it's not certain that the existential would be successful in verification). + Contract.Assume(false); // unexpected case } } - 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); - - void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { - Contract.Assert(pat.Expr.Type != null); - var bResult = etran.TrExpr(result); - 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))); - - 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 - // their use is low value. - throw new NotSupportedException("Verification of try/recover statements is not supported"); - } else { - Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement + GenerateAndCheckGuesses(s.Tok, bvars, s.Bounds, Substitute(s.Expr, null, substMap), TrTrigger(etran, s.Attributes, s.Tok, substMap), builder, etran); } - } - 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. - var resolved = statement.ResolvedStatements; - if (resolved.Count == 1) { - TrStmt(resolved[0], builder, locals, etran); + // Mark off the simple variables as having definitely been assigned AND THEN havoc their values. By doing them + // in this order, the type antecedents will in effect be assumed. + var bHavocLHSs = new List(); + foreach (var lhs in simpleLHSs) { + MarkDefiniteAssignmentTracker(lhs, builder); + bHavocLHSs.Add((Bpl.IdentifierExpr)etran.TrExpr(lhs)); + } + builder.Add(new Bpl.HavocCmd(s.Tok, bHavocLHSs)); + + // End by doing the assume + builder.Add(TrAssumeCmdWithDependencies(etran, s.Tok, s.Expr, "assign-such-that constraint")); + builder.AddCaptureState(s); // just do one capture state--here, at the very end (that is, don't do one before the assume) + + } else if (stmt is UpdateStmt statement) { + TrUpdateStmt(builder, locals, etran, statement); + } else if (stmt is AssignOrReturnStmt) { + AddComment(builder, stmt, "assign-or-return statement (:-)"); + AssignOrReturnStmt s = (AssignOrReturnStmt)stmt; + TrStmtList(s.ResolvedStatements, builder, locals, etran); + + } else if (stmt is AssignStmt) { + AddComment(builder, stmt, "assignment statement"); + AssignStmt s = (AssignStmt)stmt; + TrAssignment(stmt, s.Lhs.Resolved, s.Rhs, builder, locals, etran); + + } else if (stmt is CallStmt) { + AddComment(builder, stmt, "call statement"); + TrCallStmt((CallStmt)stmt, builder, locals, etran, null); + + } else if (stmt is DividedBlockStmt) { + var s = (DividedBlockStmt)stmt; + AddComment(builder, stmt, "divided block before new;"); + var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; + var tok = s.SeparatorTok ?? s.Tok; + // a DividedBlockStmt occurs only inside a Constructor body of a class + var cl = (ClassDecl)((Constructor)codeContext).EnclosingClass; + var fields = Concat(cl.InheritedMembers, cl.Members).ConvertAll(member => + member is Field && !member.IsStatic && !member.IsInstanceIndependentConstant ? (Field)member : null); + fields.RemoveAll(f => f == null); + var localSurrogates = fields.ConvertAll(f => new Bpl.LocalVariable(f.tok, new TypedIdent(f.tok, SurrogateName(f), TrType(f.Type)))); + locals.AddRange(localSurrogates); + fields.ForEach(f => AddDefiniteAssignmentTrackerSurrogate(f, cl, locals, codeContext is Constructor && codeContext.IsGhost)); + + Contract.Assert(!inBodyInitContext); + inBodyInitContext = true; + TrStmtList(s.BodyInit, builder, locals, etran); + Contract.Assert(inBodyInitContext); + inBodyInitContext = false; + + // The "new;" translates into an allocation of "this" + AddComment(builder, stmt, "new;"); + fields.ForEach(f => CheckDefiniteAssignmentSurrogate(s.SeparatorTok ?? s.RangeToken.EndToken, f, true, builder)); + fields.ForEach(RemoveDefiniteAssignmentTrackerSurrogate); + var th = new ThisExpr(cl); + var bplThis = (Bpl.IdentifierExpr)etran.TrExpr(th); + SelectAllocateObject(tok, bplThis, th.Type, false, builder, etran); + for (int i = 0; i < fields.Count; i++) { + // assume $Heap[this, f] == this.f; + var mse = new MemberSelectExpr(tok, th, fields[i]); + Bpl.Expr surr = new Bpl.IdentifierExpr(tok, localSurrogates[i]); + surr = CondApplyUnbox(tok, surr, fields[i].Type, mse.Type); + builder.Add(new Bpl.AssumeCmd(tok, Bpl.Expr.Eq(etran.TrExpr(mse), surr))); + } + CommitAllocatedObject(tok, bplThis, null, builder, etran); + + AddComment(builder, stmt, "divided block after new;"); + TrStmtList(s.BodyProper, builder, locals, etran); + RemoveDefiniteAssignmentTrackers(s.Body, prevDefiniteAssignmentTrackerCount); + + } else if (stmt is OpaqueBlock opaqueBlock) { + OpaqueBlockVerifier.EmitBoogie(this, opaqueBlock, builder, locals, etran, (IMethodCodeContext)codeContext); + } else if (stmt is BlockByProofStmt blockByProof) { + BlockByProofStmtVerifier.EmitBoogie(this, blockByProof, builder, locals, etran, codeContext); + } else if (stmt is BlockStmt blockStmt) { + var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; + TrStmtList(blockStmt.Body, builder, locals, etran, blockStmt.RangeToken); + RemoveDefiniteAssignmentTrackers(blockStmt.Body, prevDefiniteAssignmentTrackerCount); + } else if (stmt is IfStmt ifStmt) { + TrIfStmt(ifStmt, builder, locals, etran); + + } else if (stmt is AlternativeStmt) { + AddComment(builder, stmt, "alternative statement"); + var s = (AlternativeStmt)stmt; + var elseCase = Assert(s.Tok, Bpl.Expr.False, new PODesc.AlternativeIsComplete(), builder.Context); + TrAlternatives(s.Alternatives, s.Tok, b => b.Add(elseCase), builder, locals, etran, stmt.IsGhost); + + } else if (stmt is WhileStmt whileStmt) { + TrWhileStmt(whileStmt, builder, locals, etran); + + } else if (stmt is AlternativeLoopStmt alternativeLoopStmt) { + TrAlternativeLoopStmt(alternativeLoopStmt, builder, locals, etran); + } else if (stmt is ForLoopStmt forLoopStmt) { + TrForLoop(forLoopStmt, builder, locals, etran); + + } else if (stmt is ModifyStmt) { + AddComment(builder, stmt, "modify statement"); + var s = (ModifyStmt)stmt; + // check well-formedness of the modifies clauses + var wfOptions = new WFOptions(); + CheckFrameWellFormed(wfOptions, s.Mod.Expressions, locals, builder, etran); + // check that the modifies is a subset + var desc = new PODesc.ModifyFrameSubset("modify statement", s.Mod.Expressions, GetContextModifiesFrames()); + CheckFrameSubset(s.Tok, s.Mod.Expressions, null, null, etran, etran.ModifiesFrame(s.Tok), builder, desc, null); + // cause the change of the heap according to the given frame + var suffix = CurrentIdGenerator.FreshId("modify#"); + string modifyFrameName = FrameVariablePrefix + suffix; + var preModifyHeapVar = new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$PreModifyHeap$" + suffix, predef.HeapType)); + locals.Add(preModifyHeapVar); + DefineFrame(s.Tok, etran.ModifiesFrame(s.Tok), s.Mod.Expressions, builder, locals, modifyFrameName); + if (s.Body == null) { + var preModifyHeap = new Bpl.IdentifierExpr(s.Tok, preModifyHeapVar); + // preModifyHeap := $Heap; + builder.Add(Bpl.Cmd.SimpleAssign(s.Tok, preModifyHeap, etran.HeapExpr)); + // havoc $Heap; + builder.Add(new Bpl.HavocCmd(s.Tok, new List { etran.HeapCastToIdentifierExpr })); + // assume $HeapSucc(preModifyHeap, $Heap); OR $HeapSuccGhost + builder.Add(TrAssumeCmd(s.Tok, HeapSucc(preModifyHeap, etran.HeapExpr, s.IsGhost))); + // assume nothing outside the frame was changed + var etranPreLoop = new ExpressionTranslator(this, predef, preModifyHeap, this.currentDeclaration is IFrameScope fs ? fs : null); + var updatedFrameEtran = etran.WithModifiesFrame(modifyFrameName); + builder.Add(TrAssumeCmd(s.Tok, FrameConditionUsingDefinedFrame(s.Tok, etranPreLoop, etran, updatedFrameEtran, updatedFrameEtran.ModifiesFrame(s.Tok)))); } else { - AddComment(builder, statement, "update statement"); - var assignStmts = resolved.Cast().ToList(); - var lhss = assignStmts.Select(a => a.Lhs).ToList(); - var rhss = assignStmts.Select(a => a.Rhs).ToList(); - // note: because we have more than one expression, we always must assign to Boogie locals in a two - // phase operation. Thus rhssCanAffectPreviouslyKnownExpressions is just true. - Contract.Assert(1 < lhss.Count); - - ProcessLhss(lhss, true, false, builder, locals, etran, statement, out var lhsBuilder, out var bLhss, out var lhsObjs, out var lhsFields, out var lhsNames); - // We know that, because the translation saves to a local variable, that the RHS always need to - // generate a new local, i.e. bLhss is just all nulls. - Contract.Assert(Contract.ForAll(bLhss, lhs => lhs == null)); - // This generates the assignments, and gives them to us as finalRhss. - var finalRhss = ProcessUpdateAssignRhss(lhss, rhss, builder, locals, etran, statement); - // ProcessLhss has laid down framing conditions and the ProcessUpdateAssignRhss will check subranges (nats), - // but we need to generate the distinctness condition (two LHS are equal only when the RHS is also - // equal). We need both the LHS and the RHS to do this, which is why we need to do it here. - CheckLhssDistinctness(finalRhss, statement.Rhss, lhss, builder, etran, lhsObjs, lhsFields, lhsNames, statement.OriginalInitialLhs); - // Now actually perform the assignments to the LHS. - for (int i = 0; i < lhss.Count; i++) { - lhsBuilder[i](finalRhss[i], statement.Rhss[i] is HavocRhs, builder, etran); + // do the body, but with preModifyHeapVar as the governing frame + var updatedFrameEtran = etran.WithModifiesFrame(modifyFrameName); + CurrentIdGenerator.Push(); + TrStmt(s.Body, builder, locals, updatedFrameEtran); + CurrentIdGenerator.Pop(); + } + builder.AddCaptureState(stmt); + + } else if (stmt is ForallStmt forallStmt) { + TrForallStmt(builder, locals, etran, forallStmt); + } else if (stmt is CalcStmt calcStmt) { + TrCalcStmt(calcStmt, builder, locals, etran); + } else if (stmt is NestedMatchStmt nestedMatchStmt) { + TrStmt(nestedMatchStmt.Flattened, builder, locals, etran); + } else if (stmt is MatchStmt matchStmt) { + TrMatchStmt(matchStmt, builder, locals, etran); + } else if (stmt is VarDeclStmt) { + var s = (VarDeclStmt)stmt; + TrVarDeclStmt(s, builder, locals, etran); + } else if (stmt is 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)); } - builder.AddCaptureState(statement); } + + 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); + + void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { + Contract.Assert(pat.Expr.Type != null); + var bResult = etran.TrExpr(result); + 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))); + + 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 + // their use is low value. + throw new NotSupportedException("Verification of try/recover statements is not supported"); + } else { + Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement } + } - void TrVarDeclStmt(VarDeclStmt varDeclStmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + 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. + var resolved = statement.ResolvedStatements; + if (resolved.Count == 1) { + TrStmt(resolved[0], builder, locals, etran); + } else { + AddComment(builder, statement, "update statement"); + var assignStmts = resolved.Cast().ToList(); + var lhss = assignStmts.Select(a => a.Lhs).ToList(); + var rhss = assignStmts.Select(a => a.Rhs).ToList(); + // note: because we have more than one expression, we always must assign to Boogie locals in a two + // phase operation. Thus rhssCanAffectPreviouslyKnownExpressions is just true. + Contract.Assert(1 < lhss.Count); + + ProcessLhss(lhss, true, false, builder, locals, etran, statement, out var lhsBuilder, out var bLhss, out var lhsObjs, out var lhsFields, out var lhsNames); + // We know that, because the translation saves to a local variable, that the RHS always need to + // generate a new local, i.e. bLhss is just all nulls. + Contract.Assert(Contract.ForAll(bLhss, lhs => lhs == null)); + // This generates the assignments, and gives them to us as finalRhss. + var finalRhss = ProcessUpdateAssignRhss(lhss, rhss, builder, locals, etran, statement); + // ProcessLhss has laid down framing conditions and the ProcessUpdateAssignRhss will check subranges (nats), + // but we need to generate the distinctness condition (two LHS are equal only when the RHS is also + // equal). We need both the LHS and the RHS to do this, which is why we need to do it here. + CheckLhssDistinctness(finalRhss, statement.Rhss, lhss, builder, etran, lhsObjs, lhsFields, lhsNames, statement.OriginalInitialLhs); + // Now actually perform the assignments to the LHS. + for (int i = 0; i < lhss.Count; i++) { + lhsBuilder[i](finalRhss[i], statement.Rhss[i] is HavocRhs, builder, etran); + } + builder.AddCaptureState(statement); + } + } - var newLocalIds = new List(); - int i = 0; - foreach (var local in varDeclStmt.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(varDeclStmt.IsGhost, local)); - // if needed, register definite-assignment tracking for this local - var needDefiniteAssignmentTracking = varDeclStmt.Update == null || varDeclStmt.Update is AssignSuchThatStmt; - if (varDeclStmt.Update is UpdateStmt) { - // there is an initial assignment, but we need to look out for "*" being that assignment - var us = (UpdateStmt)varDeclStmt.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 + void TrVarDeclStmt(VarDeclStmt varDeclStmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + + var newLocalIds = new List(); + int i = 0; + foreach (var local in varDeclStmt.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(varDeclStmt.IsGhost, local)); + // if needed, register definite-assignment tracking for this local + var needDefiniteAssignmentTracking = varDeclStmt.Update == null || varDeclStmt.Update is AssignSuchThatStmt; + if (varDeclStmt.Update is UpdateStmt) { + // there is an initial assignment, but we need to look out for "*" being that assignment + var us = (UpdateStmt)varDeclStmt.Update; + if (i < us.Rhss.Count && us.Rhss[i] is HavocRhs) { 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 (varDeclStmt.Update == null) { - // it is necessary to do a havoc here in order to give the variable the correct allocation state - builder.Add(new HavocCmd(varDeclStmt.Tok, newLocalIds)); + if (!local.Type.IsNonempty) { + // This prevents generating an unsatisfiable where clause for possibly empty types + needDefiniteAssignmentTracking = true; } - // processing of "assumption" variables happens after the variable is possibly havocked above - foreach (var local in varDeclStmt.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 (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); } } - if (varDeclStmt.Update != null) { - TrStmt(varDeclStmt.Update, builder, locals, etran); + // 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 (varDeclStmt.Update == null) { + // it is necessary to do a havoc here in order to give the variable the correct allocation state + builder.Add(new HavocCmd(varDeclStmt.Tok, newLocalIds)); + } + // processing of "assumption" variables happens after the variable is possibly havocked above + foreach (var local in varDeclStmt.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 (varDeclStmt.Update != null) { + TrStmt(varDeclStmt.Update, builder, locals, etran); + } + } - private void TranslateRevealStmt(BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, - HideRevealStmt revealStmt) { - AddComment(builder, revealStmt, "hide/reveal statement"); - foreach (var la in revealStmt.LabeledAsserts) { - Contract.Assert(la.E != null); // this should have been filled in by now - builder.Add(new Bpl.AssumeCmd(revealStmt.Tok, la.E)); - } + private void TranslateRevealStmt(BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + HideRevealStmt revealStmt) { + AddComment(builder, revealStmt, "hide/reveal statement"); + foreach (var la in revealStmt.LabeledAsserts) { + Contract.Assert(la.E != null); // this should have been filled in by now + builder.Add(new Bpl.AssumeCmd(revealStmt.Tok, la.E)); + } - if (builder.Context.ContainsHide) { - if (revealStmt.Wildcard) { - builder.Add(new HideRevealCmd(revealStmt.Tok, revealStmt.Mode)); - } else { - foreach (var member in revealStmt.OffsetMembers) { - builder.Add(new HideRevealCmd(new Bpl.IdentifierExpr(revealStmt.Tok, member.FullSanitizedName), revealStmt.Mode)); - } + if (builder.Context.ContainsHide) { + if (revealStmt.Wildcard) { + builder.Add(new HideRevealCmd(revealStmt.Tok, revealStmt.Mode)); + } else { + foreach (var member in revealStmt.OffsetMembers) { + builder.Add(new HideRevealCmd(new Bpl.IdentifierExpr(revealStmt.Tok, member.FullSanitizedName), revealStmt.Mode)); } } - - TrStmtList(revealStmt.ResolvedStatements, builder, locals, etran); } - private void TrCalcStmt(CalcStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { - Contract.Requires(stmt != null); - Contract.Requires(builder != null); - Contract.Requires(locals != null); - Contract.Requires(etran != null); - - /* Translate into: - if (*) { - assert wf(line0); - } else if (*) { - assume wf(line0); - // if op is ==>: assume line0; - hint0; - assert wf(line1); - assert line0 op line1; - assume false; - } else if (*) { ... - } else if (*) { - assume wf(line); - // if op is ==>: assume line; - hint; - assert wf(line); - assert line op line; - assume false; + TrStmtList(revealStmt.ResolvedStatements, builder, locals, etran); + } + + private void TrCalcStmt(CalcStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + Contract.Requires(stmt != null); + Contract.Requires(builder != null); + Contract.Requires(locals != null); + Contract.Requires(etran != null); + + /* Translate into: + if (*) { + assert wf(line0); + } else if (*) { + assume wf(line0); + // if op is ==>: assume line0; + hint0; + assert wf(line1); + assert line0 op line1; + assume false; + } else if (*) { ... + } else if (*) { + assume wf(line); + // if op is ==>: assume line; + hint; + assert wf(line); + assert line op line; + assume false; + } + assume line<0> op line; + */ + Contract.Assert(stmt.Steps.Count == stmt.Hints.Count); // established by the resolver + AddComment(builder, stmt, "calc statement"); + this.fuelContext = FuelSetting.ExpandFuelContext(stmt.Attributes, stmt.Tok, this.fuelContext, this.reporter); + DefineFuelConstant(stmt.Tok, stmt.Attributes, builder, etran); + CurrentIdGenerator.Push(); // put the entire calc statement within its own sub-branch + if (stmt.Lines.Count > 0) { + Bpl.IfCmd ifCmd = null; + BoogieStmtListBuilder b; + // if the dangling hint is empty, do not generate anything for the dummy step + var stepCount = stmt.Hints.Last().Body.Count == 0 ? stmt.Steps.Count - 1 : stmt.Steps.Count; + // check steps: + for (int i = stepCount; 0 <= --i;) { + b = new BoogieStmtListBuilder(this, options, builder.Context); + // assume wf[line]: + AddComment(b, stmt, "assume wf[lhs]"); + CurrentIdGenerator.Push(); + TrStmt_CheckWellformed(CalcStmt.Lhs(stmt.Steps[i]), b.WithContext(b.Context with { + AssertMode = AssertMode.Assume + }), locals, etran, false); + if (stmt.Steps[i] is BinaryExpr && (((BinaryExpr)stmt.Steps[i]).ResolvedOp == BinaryExpr.ResolvedOpcode.Imp)) { + // assume line: + AddComment(b, stmt, "assume lhs"); + b.Add(TrAssumeCmdWithDependencies(etran, stmt.Tok, CalcStmt.Lhs(stmt.Steps[i]), "calc statement step")); } - assume line<0> op line; - */ - Contract.Assert(stmt.Steps.Count == stmt.Hints.Count); // established by the resolver - AddComment(builder, stmt, "calc statement"); - this.fuelContext = FuelSetting.ExpandFuelContext(stmt.Attributes, stmt.Tok, this.fuelContext, this.reporter); - DefineFuelConstant(stmt.Tok, stmt.Attributes, builder, etran); - CurrentIdGenerator.Push(); // put the entire calc statement within its own sub-branch - if (stmt.Lines.Count > 0) { - Bpl.IfCmd ifCmd = null; - BoogieStmtListBuilder b; - // if the dangling hint is empty, do not generate anything for the dummy step - var stepCount = stmt.Hints.Last().Body.Count == 0 ? stmt.Steps.Count - 1 : stmt.Steps.Count; - // check steps: - for (int i = stepCount; 0 <= --i;) { - b = new BoogieStmtListBuilder(this, options, builder.Context); - // assume wf[line]: - AddComment(b, stmt, "assume wf[lhs]"); - CurrentIdGenerator.Push(); - TrStmt_CheckWellformed(CalcStmt.Lhs(stmt.Steps[i]), b.WithContext(b.Context with { - AssertMode = AssertMode.Assume - }), locals, etran, false); - if (stmt.Steps[i] is BinaryExpr && (((BinaryExpr)stmt.Steps[i]).ResolvedOp == BinaryExpr.ResolvedOpcode.Imp)) { - // assume line: - AddComment(b, stmt, "assume lhs"); - b.Add(TrAssumeCmdWithDependencies(etran, stmt.Tok, CalcStmt.Lhs(stmt.Steps[i]), "calc statement step")); - } - // hint: - AddComment(b, stmt, "Hint" + i.ToString()); - - TrStmt(stmt.Hints[i], b, locals, etran); - if (i < stmt.Steps.Count - 1) { - // non-dummy step - // check well formedness of the goal line: - AddComment(b, stmt, "assert wf[rhs]"); - if (stmt.Steps[i] is TernaryExpr) { - // check the prefix-equality limit - var index = ((TernaryExpr)stmt.Steps[i]).E0; - TrStmt_CheckWellformed(index, b, locals, etran, false); - if (index.Type.IsNumericBased(Type.NumericPersuasion.Int)) { - var desc = new PODesc.PrefixEqualityLimit(index); - b.Add(AssertAndForget(index.tok, Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(index)), desc)); - } + // hint: + AddComment(b, stmt, "Hint" + i.ToString()); + + TrStmt(stmt.Hints[i], b, locals, etran); + if (i < stmt.Steps.Count - 1) { + // non-dummy step + // check well formedness of the goal line: + AddComment(b, stmt, "assert wf[rhs]"); + if (stmt.Steps[i] is TernaryExpr) { + // check the prefix-equality limit + var index = ((TernaryExpr)stmt.Steps[i]).E0; + TrStmt_CheckWellformed(index, b, locals, etran, false); + if (index.Type.IsNumericBased(Type.NumericPersuasion.Int)) { + var desc = new PODesc.PrefixEqualityLimit(index); + b.Add(AssertAndForget(index.tok, Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(index)), desc)); } - TrStmt_CheckWellformed(CalcStmt.Rhs(stmt.Steps[i]), b, locals, etran, false); - var ss = TrSplitExpr(builder.Context, stmt.Steps[i], etran, true, out var splitHappened); - // assert step: - AddComment(b, stmt, "assert line" + i.ToString() + " " + (stmt.StepOps[i] ?? stmt.Op).ToString() + " line" + (i + 1).ToString()); - if (!splitHappened) { - b.Add(AssertAndForget(stmt.Lines[i + 1].tok, etran.TrExpr(stmt.Steps[i]), new PODesc.CalculationStep(stmt.Steps[i], stmt.Hints[i]))); - } else { - foreach (var split in ss) { - if (split.IsChecked) { - b.Add(AssertAndForget(stmt.Lines[i + 1].tok, split.E, new PODesc.CalculationStep(stmt.Steps[i], stmt.Hints[i]))); - } + } + TrStmt_CheckWellformed(CalcStmt.Rhs(stmt.Steps[i]), b, locals, etran, false); + var ss = TrSplitExpr(builder.Context, stmt.Steps[i], etran, true, out var splitHappened); + // assert step: + AddComment(b, stmt, "assert line" + i.ToString() + " " + (stmt.StepOps[i] ?? stmt.Op).ToString() + " line" + (i + 1).ToString()); + if (!splitHappened) { + b.Add(AssertAndForget(stmt.Lines[i + 1].tok, etran.TrExpr(stmt.Steps[i]), new PODesc.CalculationStep(stmt.Steps[i], stmt.Hints[i]))); + } else { + foreach (var split in ss) { + if (split.IsChecked) { + b.Add(AssertAndForget(stmt.Lines[i + 1].tok, split.E, new PODesc.CalculationStep(stmt.Steps[i], stmt.Hints[i]))); } } } - b.Add(TrAssumeCmd(stmt.Tok, Bpl.Expr.False)); - ifCmd = new Bpl.IfCmd(stmt.Tok, null, b.Collect(stmt.Tok), ifCmd, null); - CurrentIdGenerator.Pop(); } - // check well formedness of the first line: - b = new BoogieStmtListBuilder(this, options, builder.Context); - AddComment(b, stmt, "assert wf[initial]"); - Contract.Assert(stmt.Result != null); // established by the resolver - TrStmt_CheckWellformed(CalcStmt.Lhs(stmt.Result), b, locals, etran, false); b.Add(TrAssumeCmd(stmt.Tok, Bpl.Expr.False)); ifCmd = new Bpl.IfCmd(stmt.Tok, null, b.Collect(stmt.Tok), ifCmd, null); - builder.Add(ifCmd); - // assume result: - if (stmt.Steps.Count > 1) { - builder.Add(TrAssumeCmdWithDependencies(etran, stmt.Tok, stmt.Result, "calc statement result")); - } + CurrentIdGenerator.Pop(); + } + // check well formedness of the first line: + b = new BoogieStmtListBuilder(this, options, builder.Context); + AddComment(b, stmt, "assert wf[initial]"); + Contract.Assert(stmt.Result != null); // established by the resolver + TrStmt_CheckWellformed(CalcStmt.Lhs(stmt.Result), b, locals, etran, false); + b.Add(TrAssumeCmd(stmt.Tok, Bpl.Expr.False)); + ifCmd = new Bpl.IfCmd(stmt.Tok, null, b.Collect(stmt.Tok), ifCmd, null); + builder.Add(ifCmd); + // assume result: + if (stmt.Steps.Count > 1) { + builder.Add(TrAssumeCmdWithDependencies(etran, stmt.Tok, stmt.Result, "calc statement result")); } - CurrentIdGenerator.Pop(); - this.fuelContext = FuelSetting.PopFuelContext(); } + CurrentIdGenerator.Pop(); + this.fuelContext = FuelSetting.PopFuelContext(); + } - private void TrMatchStmt(MatchStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { - Contract.Requires(stmt != null); - Contract.Requires(builder != null); - Contract.Requires(locals != null); - Contract.Requires(etran != null); - - FillMissingCases(stmt); - - TrStmt_CheckWellformed(stmt.Source, builder, locals, etran, true); - Bpl.Expr source = etran.TrExpr(stmt.Source); - var b = new BoogieStmtListBuilder(this, options, builder.Context); - b.Add(TrAssumeCmd(stmt.Tok, Bpl.Expr.False)); - Bpl.StmtList els = b.Collect(stmt.Tok); - Bpl.IfCmd ifCmd = null; - foreach (var missingCtor in stmt.MissingCases) { - // havoc all bound variables - b = new BoogieStmtListBuilder(this, options, builder.Context); - List newLocals = new List(); - Bpl.Expr r = CtorInvocation(stmt.Tok, missingCtor, etran, newLocals, b); - locals.AddRange(newLocals); - - if (newLocals.Count != 0) { - List havocIds = new List(); - foreach (Variable local in newLocals) { - havocIds.Add(new Bpl.IdentifierExpr(local.tok, local)); - } - builder.Add(new Bpl.HavocCmd(stmt.Tok, havocIds)); + private void TrMatchStmt(MatchStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + Contract.Requires(stmt != null); + Contract.Requires(builder != null); + Contract.Requires(locals != null); + Contract.Requires(etran != null); + + FillMissingCases(stmt); + + TrStmt_CheckWellformed(stmt.Source, builder, locals, etran, true); + Bpl.Expr source = etran.TrExpr(stmt.Source); + var b = new BoogieStmtListBuilder(this, options, builder.Context); + b.Add(TrAssumeCmd(stmt.Tok, Bpl.Expr.False)); + Bpl.StmtList els = b.Collect(stmt.Tok); + Bpl.IfCmd ifCmd = null; + foreach (var missingCtor in stmt.MissingCases) { + // havoc all bound variables + b = new BoogieStmtListBuilder(this, options, builder.Context); + List newLocals = new List(); + Bpl.Expr r = CtorInvocation(stmt.Tok, missingCtor, etran, newLocals, b); + locals.AddRange(newLocals); + + if (newLocals.Count != 0) { + List havocIds = new List(); + foreach (Variable local in newLocals) { + havocIds.Add(new Bpl.IdentifierExpr(local.tok, local)); } - String missingStr = stmt.Context.FillHole(new IdCtx(missingCtor)).AbstractAllHoles() - .ToString(); - var desc = new PODesc.MatchIsComplete("statement", missingStr); - b.Add(Assert(stmt.Tok, Bpl.Expr.False, desc, builder.Context)); - - Bpl.Expr guard = Bpl.Expr.Eq(source, r); - ifCmd = new Bpl.IfCmd(stmt.Tok, guard, b.Collect(stmt.Tok), ifCmd, els); - els = null; + builder.Add(new Bpl.HavocCmd(stmt.Tok, havocIds)); } - for (int i = stmt.Cases.Count; 0 <= --i;) { - var mc = (MatchCaseStmt)stmt.Cases[i]; - CurrentIdGenerator.Push(); - // havoc all bound variables - b = new BoogieStmtListBuilder(this, options, builder.Context); - List newLocals = new List(); - Bpl.Expr r = CtorInvocation(mc, stmt.Source.Type, etran, newLocals, b, stmt.IsGhost ? NOALLOC : ISALLOC); - locals.AddRange(newLocals); - - if (newLocals.Count != 0) { - List havocIds = new List(); - foreach (Variable local in newLocals) { - havocIds.Add(new Bpl.IdentifierExpr(local.tok, local)); - } - builder.Add(new Bpl.HavocCmd(mc.tok, havocIds)); + String missingStr = stmt.Context.FillHole(new IdCtx(missingCtor)).AbstractAllHoles() + .ToString(); + var desc = new PODesc.MatchIsComplete("statement", missingStr); + b.Add(Assert(stmt.Tok, Bpl.Expr.False, desc, builder.Context)); + + Bpl.Expr guard = Bpl.Expr.Eq(source, r); + ifCmd = new Bpl.IfCmd(stmt.Tok, guard, b.Collect(stmt.Tok), ifCmd, els); + els = null; + } + for (int i = stmt.Cases.Count; 0 <= --i;) { + var mc = (MatchCaseStmt)stmt.Cases[i]; + CurrentIdGenerator.Push(); + // havoc all bound variables + b = new BoogieStmtListBuilder(this, options, builder.Context); + List newLocals = new List(); + Bpl.Expr r = CtorInvocation(mc, stmt.Source.Type, etran, newLocals, b, stmt.IsGhost ? NOALLOC : ISALLOC); + locals.AddRange(newLocals); + + if (newLocals.Count != 0) { + List havocIds = new List(); + foreach (Variable local in newLocals) { + havocIds.Add(new Bpl.IdentifierExpr(local.tok, local)); } + builder.Add(new Bpl.HavocCmd(mc.tok, havocIds)); + } - // translate the body into b - var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; - TrStmtList(mc.Body, b, locals, etran); - RemoveDefiniteAssignmentTrackers(mc.Body, prevDefiniteAssignmentTrackerCount); + // translate the body into b + var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; + TrStmtList(mc.Body, b, locals, etran); + RemoveDefiniteAssignmentTrackers(mc.Body, prevDefiniteAssignmentTrackerCount); - Bpl.Expr guard = Bpl.Expr.Eq(source, r); - ifCmd = new Bpl.IfCmd(mc.tok, guard, b.Collect(mc.tok), ifCmd, els); - els = null; - CurrentIdGenerator.Pop(); - } - if (ifCmd != null) { - builder.Add(ifCmd); - } + Bpl.Expr guard = Bpl.Expr.Eq(source, r); + ifCmd = new Bpl.IfCmd(mc.tok, guard, b.Collect(mc.tok), ifCmd, els); + els = null; + CurrentIdGenerator.Pop(); } + if (ifCmd != null) { + builder.Add(ifCmd); + } + } - void FillMissingCases(IMatch match) { - Contract.Requires(match != null); - if (match.MissingCases.Any()) { - return; - } + void FillMissingCases(IMatch match) { + Contract.Requires(match != null); + if (match.MissingCases.Any()) { + return; + } - var dtd = match.Source.Type.AsDatatype; - var constructors = dtd?.ConstructorsByName; + var dtd = match.Source.Type.AsDatatype; + var constructors = dtd?.ConstructorsByName; - ISet memberNamesUsed = new HashSet(); + ISet memberNamesUsed = new HashSet(); - foreach (var matchCase in match.Cases) { - if (constructors != null) { - Contract.Assert(dtd != null); - var ctorId = matchCase.Ctor.Name; - if (match.Source.Type.AsDatatype is TupleTypeDecl) { - var tuple = (TupleTypeDecl)match.Source.Type.AsDatatype; - ctorId = SystemModuleManager.TupleTypeCtorName(tuple.Dims); - } + foreach (var matchCase in match.Cases) { + if (constructors != null) { + Contract.Assert(dtd != null); + var ctorId = matchCase.Ctor.Name; + if (match.Source.Type.AsDatatype is TupleTypeDecl) { + var tuple = (TupleTypeDecl)match.Source.Type.AsDatatype; + ctorId = SystemModuleManager.TupleTypeCtorName(tuple.Dims); + } - if (constructors.ContainsKey(ctorId)) { - memberNamesUsed.Add(ctorId); // add mc.Id to the set of names used - } + if (constructors.ContainsKey(ctorId)) { + memberNamesUsed.Add(ctorId); // add mc.Id to the set of names used } } - if (dtd != null && memberNamesUsed.Count != dtd.Ctors.Count) { - // We could complain about the syntactic omission of constructors: - // Reporter.Error(MessageSource.Resolver, stmt, "match statement does not cover all constructors"); - // but instead we let the verifier do a semantic check. - // So, for now, record the missing constructors: - foreach (var ctr in dtd.Ctors) { - if (!memberNamesUsed.Contains(ctr.Name)) { - match.MissingCases.Add(ctr); - } + } + if (dtd != null && memberNamesUsed.Count != dtd.Ctors.Count) { + // We could complain about the syntactic omission of constructors: + // Reporter.Error(MessageSource.Resolver, stmt, "match statement does not cover all constructors"); + // but instead we let the verifier do a semantic check. + // So, for now, record the missing constructors: + foreach (var ctr in dtd.Ctors) { + if (!memberNamesUsed.Contains(ctr.Name)) { + match.MissingCases.Add(ctr); } - Contract.Assert(memberNamesUsed.Count + match.MissingCases.Count == dtd.Ctors.Count); } + Contract.Assert(memberNamesUsed.Count + match.MissingCases.Count == dtd.Ctors.Count); } + } - private static SubrangeCheckContext MakeNumericBoundsSubrangeCheckContext(BoundVar bvar, Expression lo, Expression hi) { - var source = new IdentifierExpr(Token.NoToken, bvar); - var loBound = lo == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Le, lo, source); - var hiBound = hi == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Lt, source, hi); - var bounds = (loBound, hiBound) switch { - (not null, not null) => new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.And, loBound, hiBound), - (not null, null) => loBound, - (null, not null) => hiBound, - }; - Expression CheckContext(Expression check) => new ForallExpr( - Token.NoToken, - RangeToken.NoToken, - new() { bvar }, - bounds, - check, - null - ); - - return CheckContext; - } - - private void TrIfStmt(IfStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { - Contract.Requires(stmt != null); - Contract.Requires(builder != null); - Contract.Requires(locals != null); - Contract.Requires(etran != null); + private static SubrangeCheckContext MakeNumericBoundsSubrangeCheckContext(BoundVar bvar, Expression lo, Expression hi) { + var source = new IdentifierExpr(Token.NoToken, bvar); + var loBound = lo == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Le, lo, source); + var hiBound = hi == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Lt, source, hi); + var bounds = (loBound, hiBound) switch { + (not null, not null) => new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.And, loBound, hiBound), + (not null, null) => loBound, + (null, not null) => hiBound, + }; + Expression CheckContext(Expression check) => new ForallExpr( + Token.NoToken, + RangeToken.NoToken, + new() { bvar }, + bounds, + check, + null + ); + + return CheckContext; + } - AddComment(builder, stmt, "if statement"); - Expression guard; - if (stmt.Guard == null) { - guard = null; - } else { - guard = stmt.IsBindingGuard ? ((ExistsExpr)stmt.Guard).AlphaRename("eg$") : stmt.Guard; - TrStmt_CheckWellformed(guard, builder, locals, etran, true); - } - BoogieStmtListBuilder b = new BoogieStmtListBuilder(this, options, builder.Context); - if (stmt.IsBindingGuard) { - CurrentIdGenerator.Push(); - var exists = (ExistsExpr)stmt.Guard; // the original (that is, not alpha-renamed) guard - IntroduceAndAssignExistentialVars(exists, b, builder, locals, etran, stmt.IsGhost); - CurrentIdGenerator.Pop(); - } + private void TrIfStmt(IfStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + Contract.Requires(stmt != null); + Contract.Requires(builder != null); + Contract.Requires(locals != null); + Contract.Requires(etran != null); + + AddComment(builder, stmt, "if statement"); + Expression guard; + if (stmt.Guard == null) { + guard = null; + } else { + guard = stmt.IsBindingGuard ? ((ExistsExpr)stmt.Guard).AlphaRename("eg$") : stmt.Guard; + TrStmt_CheckWellformed(guard, builder, locals, etran, true); + } + BoogieStmtListBuilder b = new BoogieStmtListBuilder(this, options, builder.Context); + if (stmt.IsBindingGuard) { CurrentIdGenerator.Push(); - Bpl.StmtList thn = TrStmt2StmtList(b, stmt.Thn, locals, etran, stmt.Thn is not BlockStmt); + var exists = (ExistsExpr)stmt.Guard; // the original (that is, not alpha-renamed) guard + IntroduceAndAssignExistentialVars(exists, b, builder, locals, etran, stmt.IsGhost); CurrentIdGenerator.Pop(); - Bpl.StmtList els; - Bpl.IfCmd elsIf = null; - b = new BoogieStmtListBuilder(this, options, builder.Context); - if (stmt.IsBindingGuard) { - b.Add(TrAssumeCmdWithDependenciesAndExtend(etran, guard.tok, guard, Expr.Not, "if statement binding guard")); - } - if (stmt.Els == null) { - els = b.Collect(stmt.Tok); - } else { - CurrentIdGenerator.Push(); - els = TrStmt2StmtList(b, stmt.Els, locals, etran, stmt.Els is not BlockStmt); - CurrentIdGenerator.Pop(); - if (els.BigBlocks.Count == 1) { - Bpl.BigBlock bb = els.BigBlocks[0]; - if (bb.LabelName == null && bb.simpleCmds.Count == 0 && bb.ec is Bpl.IfCmd) { - elsIf = (Bpl.IfCmd)bb.ec; - els = null; - } + } + CurrentIdGenerator.Push(); + Bpl.StmtList thn = TrStmt2StmtList(b, stmt.Thn, locals, etran, stmt.Thn is not BlockStmt); + CurrentIdGenerator.Pop(); + Bpl.StmtList els; + Bpl.IfCmd elsIf = null; + b = new BoogieStmtListBuilder(this, options, builder.Context); + if (stmt.IsBindingGuard) { + b.Add(TrAssumeCmdWithDependenciesAndExtend(etran, guard.tok, guard, Expr.Not, "if statement binding guard")); + } + if (stmt.Els == null) { + els = b.Collect(stmt.Tok); + } else { + CurrentIdGenerator.Push(); + els = TrStmt2StmtList(b, stmt.Els, locals, etran, stmt.Els is not BlockStmt); + CurrentIdGenerator.Pop(); + if (els.BigBlocks.Count == 1) { + Bpl.BigBlock bb = els.BigBlocks[0]; + if (bb.LabelName == null && bb.simpleCmds.Count == 0 && bb.ec is Bpl.IfCmd) { + elsIf = (Bpl.IfCmd)bb.ec; + els = null; } } - builder.Add(new Bpl.IfCmd(stmt.Tok, guard == null || stmt.IsBindingGuard ? null : etran.TrExpr(guard), thn, elsIf, els)); } + builder.Add(new Bpl.IfCmd(stmt.Tok, guard == null || stmt.IsBindingGuard ? null : etran.TrExpr(guard), thn, elsIf, els)); + } - void TrAlternatives(List alternatives, IToken elseToken, Action buildElseCase, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, bool isGhost) { - Contract.Requires(alternatives != null); - Contract.Requires(builder != null); - Contract.Requires(locals != null); - Contract.Requires(etran != null); + void TrAlternatives(List alternatives, IToken elseToken, Action buildElseCase, + BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, bool isGhost) { + Contract.Requires(alternatives != null); + Contract.Requires(builder != null); + Contract.Requires(locals != null); + Contract.Requires(etran != null); - if (alternatives.Count == 0) { - buildElseCase(builder); - return; - } + if (alternatives.Count == 0) { + buildElseCase(builder); + return; + } - // alpha-rename any binding guards - var guards = alternatives.ConvertAll(alt => alt.IsBindingGuard ? ((ExistsExpr)alt.Guard).AlphaRename("eg$") : alt.Guard); + // alpha-rename any binding guards + var guards = alternatives.ConvertAll(alt => alt.IsBindingGuard ? ((ExistsExpr)alt.Guard).AlphaRename("eg$") : alt.Guard); - // build the negation of the disjunction of all guards (that is, the conjunction of their negations) - Bpl.Expr noGuard = Bpl.Expr.True; - var b = new BoogieStmtListBuilder(this, options, builder.Context); - foreach (var g in guards) { - b.Add(TrAssumeCmd(g.tok, etran.CanCallAssumption(g))); - noGuard = BplAnd(noGuard, Bpl.Expr.Not(etran.TrExpr(g))); - } + // build the negation of the disjunction of all guards (that is, the conjunction of their negations) + Bpl.Expr noGuard = Bpl.Expr.True; + var b = new BoogieStmtListBuilder(this, options, builder.Context); + foreach (var g in guards) { + b.Add(TrAssumeCmd(g.tok, etran.CanCallAssumption(g))); + noGuard = BplAnd(noGuard, Bpl.Expr.Not(etran.TrExpr(g))); + } - b.Add(TrAssumeCmd(elseToken, noGuard)); - buildElseCase(b); - Bpl.StmtList els = b.Collect(elseToken); + b.Add(TrAssumeCmd(elseToken, noGuard)); + buildElseCase(b); + Bpl.StmtList els = b.Collect(elseToken); - Bpl.IfCmd elsIf = null; - for (int i = alternatives.Count; 0 <= --i;) { - Contract.Assert(elsIf == null || els == null); // loop invariant - CurrentIdGenerator.Push(); - var alternative = alternatives[i]; - b = new BoogieStmtListBuilder(this, options, builder.Context); - TrStmt_CheckWellformed(guards[i], b, locals, etran, true); - if (alternative.IsBindingGuard) { - var exists = (ExistsExpr)alternative.Guard; // the original (that is, not alpha-renamed) guard - IntroduceAndAssignExistentialVars(exists, b, builder, locals, etran, isGhost); - } else { - b.Add(TrAssumeCmdWithDependencies(etran, alternative.Guard.tok, alternative.Guard, "alternative guard")); - } - var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; - TrStmtList(alternative.Body, b, locals, etran, alternative.RangeToken); - RemoveDefiniteAssignmentTrackers(alternative.Body, prevDefiniteAssignmentTrackerCount); - Bpl.StmtList thn = b.Collect(alternative.Tok); - elsIf = new Bpl.IfCmd(alternative.Tok, null, thn, elsIf, els); - els = null; - CurrentIdGenerator.Pop(); + Bpl.IfCmd elsIf = null; + for (int i = alternatives.Count; 0 <= --i;) { + Contract.Assert(elsIf == null || els == null); // loop invariant + CurrentIdGenerator.Push(); + var alternative = alternatives[i]; + b = new BoogieStmtListBuilder(this, options, builder.Context); + TrStmt_CheckWellformed(guards[i], b, locals, etran, true); + if (alternative.IsBindingGuard) { + var exists = (ExistsExpr)alternative.Guard; // the original (that is, not alpha-renamed) guard + IntroduceAndAssignExistentialVars(exists, b, builder, locals, etran, isGhost); + } else { + b.Add(TrAssumeCmdWithDependencies(etran, alternative.Guard.tok, alternative.Guard, "alternative guard")); } - Contract.Assert(elsIf != null && els == null); // follows from loop invariant and the fact that there's more than one alternative - builder.Add(elsIf); + var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; + TrStmtList(alternative.Body, b, locals, etran, alternative.RangeToken); + RemoveDefiniteAssignmentTrackers(alternative.Body, prevDefiniteAssignmentTrackerCount); + Bpl.StmtList thn = b.Collect(alternative.Tok); + elsIf = new Bpl.IfCmd(alternative.Tok, null, thn, elsIf, els); + els = null; + CurrentIdGenerator.Pop(); } + Contract.Assert(elsIf != null && els == null); // follows from loop invariant and the fact that there's more than one alternative + builder.Add(elsIf); + } - void RecordNewObjectsIn_New(IToken tok, IteratorDecl iter, Bpl.Expr initHeap, Bpl.IdentifierExpr currentHeap, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { - Contract.Requires(tok != null); - Contract.Requires(iter != null); - Contract.Requires(initHeap != null); - Contract.Requires(currentHeap != null); - Contract.Requires(builder != null); - Contract.Requires(locals != null); - Contract.Requires(etran != null); - // Add all newly allocated objects to the set this._new - var updatedSet = new Bpl.LocalVariable(iter.tok, new Bpl.TypedIdent(iter.tok, CurrentIdGenerator.FreshId("$iter_newUpdate"), predef.SetType)); - locals.Add(updatedSet); - var updatedSetIE = new Bpl.IdentifierExpr(iter.tok, updatedSet); - // call $iter_newUpdate := $IterCollectNewObjects(initHeap, $Heap, this, _new); - var th = new Bpl.IdentifierExpr(iter.tok, etran.This, predef.RefType); - var nwField = new Bpl.IdentifierExpr(tok, GetField(iter.Member_New)); - var cmd = Call(builder.Context, iter.tok, "$IterCollectNewObjects", - new List() { initHeap, etran.HeapExpr, th, nwField }, - new List() { updatedSetIE }); - builder.Add(cmd); - // $Heap[this, _new] := $iter_newUpdate; - cmd = Bpl.Cmd.SimpleAssign(iter.tok, currentHeap, UpdateHeap(iter.tok, currentHeap, th, nwField, updatedSetIE)); - builder.Add(cmd); - // assume $IsGoodHeap($Heap) - builder.Add(AssumeGoodHeap(tok, etran)); - } + void RecordNewObjectsIn_New(IToken tok, IteratorDecl iter, Bpl.Expr initHeap, Bpl.IdentifierExpr currentHeap, + BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + Contract.Requires(tok != null); + Contract.Requires(iter != null); + Contract.Requires(initHeap != null); + Contract.Requires(currentHeap != null); + Contract.Requires(builder != null); + Contract.Requires(locals != null); + Contract.Requires(etran != null); + // Add all newly allocated objects to the set this._new + var updatedSet = new Bpl.LocalVariable(iter.tok, new Bpl.TypedIdent(iter.tok, CurrentIdGenerator.FreshId("$iter_newUpdate"), predef.SetType)); + locals.Add(updatedSet); + var updatedSetIE = new Bpl.IdentifierExpr(iter.tok, updatedSet); + // call $iter_newUpdate := $IterCollectNewObjects(initHeap, $Heap, this, _new); + var th = new Bpl.IdentifierExpr(iter.tok, etran.This, predef.RefType); + var nwField = new Bpl.IdentifierExpr(tok, GetField(iter.Member_New)); + var cmd = Call(builder.Context, iter.tok, "$IterCollectNewObjects", + new List() { initHeap, etran.HeapExpr, th, nwField }, + new List() { updatedSetIE }); + builder.Add(cmd); + // $Heap[this, _new] := $iter_newUpdate; + cmd = Bpl.Cmd.SimpleAssign(iter.tok, currentHeap, UpdateHeap(iter.tok, currentHeap, th, nwField, updatedSetIE)); + builder.Add(cmd); + // assume $IsGoodHeap($Heap) + builder.Add(AssumeGoodHeap(tok, etran)); + } - private string GetObjFieldDetails(Expression lhs, ExpressionTranslator etran, out Bpl.Expr obj, out Bpl.Expr F) { - string description; - if (lhs is MemberSelectExpr) { - var fse = (MemberSelectExpr)lhs; - obj = etran.TrExpr(fse.Obj); - F = GetField(fse); - description = "an object field"; - } else if (lhs is SeqSelectExpr) { - var sel = (SeqSelectExpr)lhs; - obj = etran.TrExpr(sel.Seq); - var idx = etran.TrExpr(sel.E0); - idx = ConvertExpression(sel.E0.tok, idx, sel.E0.Type, Type.Int); - F = FunctionCall(sel.tok, BuiltinFunction.IndexField, null, idx); - description = "an array element"; - } else { - MultiSelectExpr mse = (MultiSelectExpr)lhs; - obj = etran.TrExpr(mse.Array); - F = etran.GetArrayIndexFieldName(mse.tok, mse.Indices); - description = "an array element"; - } - return description; + private string GetObjFieldDetails(Expression lhs, ExpressionTranslator etran, out Bpl.Expr obj, out Bpl.Expr F) { + string description; + if (lhs is MemberSelectExpr) { + var fse = (MemberSelectExpr)lhs; + obj = etran.TrExpr(fse.Obj); + F = GetField(fse); + description = "an object field"; + } else if (lhs is SeqSelectExpr) { + var sel = (SeqSelectExpr)lhs; + obj = etran.TrExpr(sel.Seq); + var idx = etran.TrExpr(sel.E0); + idx = ConvertExpression(sel.E0.tok, idx, sel.E0.Type, Type.Int); + F = FunctionCall(sel.tok, BuiltinFunction.IndexField, null, idx); + description = "an array element"; + } else { + MultiSelectExpr mse = (MultiSelectExpr)lhs; + obj = etran.TrExpr(mse.Array); + F = etran.GetArrayIndexFieldName(mse.tok, mse.Indices); + description = "an array element"; } + return description; + } - private void SelectAllocateObject(IToken tok, Bpl.IdentifierExpr nw, Type type, bool includeHavoc, BoogieStmtListBuilder builder, ExpressionTranslator etran) { - Contract.Requires(tok != null); - Contract.Requires(nw != null); - Contract.Requires(type != null); - Contract.Requires(builder != null); - Contract.Requires(etran != null); - var udt = type as UserDefinedType; - if (udt != null && udt.ResolvedClass is NonNullTypeDecl) { - var nnt = (NonNullTypeDecl)udt.ResolvedClass; - type = nnt.RhsWithArgument(type.TypeArgs); - } - if (includeHavoc) { - // havoc $nw; - builder.Add(new Bpl.HavocCmd(tok, new List { nw })); - // assume $nw != null && $Is($nw, type); - var nwNotNull = Bpl.Expr.Neq(nw, predef.Null); - // drop the $Is conjunct if the type is "object", because "new object" allocates an object of an arbitrary type - var rightType = type.IsObjectQ ? Bpl.Expr.True : MkIs(nw, type); - builder.Add(TrAssumeCmd(tok, BplAnd(nwNotNull, rightType))); - } - // assume !$Heap[$nw, alloc]; - var notAlloc = Bpl.Expr.Not(etran.IsAlloced(tok, nw)); - builder.Add(TrAssumeCmd(tok, notAlloc)); + private void SelectAllocateObject(IToken tok, Bpl.IdentifierExpr nw, Type type, bool includeHavoc, BoogieStmtListBuilder builder, ExpressionTranslator etran) { + Contract.Requires(tok != null); + Contract.Requires(nw != null); + Contract.Requires(type != null); + Contract.Requires(builder != null); + Contract.Requires(etran != null); + var udt = type as UserDefinedType; + if (udt != null && udt.ResolvedClass is NonNullTypeDecl) { + var nnt = (NonNullTypeDecl)udt.ResolvedClass; + type = nnt.RhsWithArgument(type.TypeArgs); } + if (includeHavoc) { + // havoc $nw; + builder.Add(new Bpl.HavocCmd(tok, new List { nw })); + // assume $nw != null && $Is($nw, type); + var nwNotNull = Bpl.Expr.Neq(nw, predef.Null); + // drop the $Is conjunct if the type is "object", because "new object" allocates an object of an arbitrary type + var rightType = type.IsObjectQ ? Bpl.Expr.True : MkIs(nw, type); + builder.Add(TrAssumeCmd(tok, BplAnd(nwNotNull, rightType))); + } + // assume !$Heap[$nw, alloc]; + var notAlloc = Bpl.Expr.Not(etran.IsAlloced(tok, nw)); + builder.Add(TrAssumeCmd(tok, notAlloc)); + } - private void CommitAllocatedObject(IToken tok, Bpl.IdentifierExpr nw, Bpl.Cmd extraCmd, BoogieStmtListBuilder builder, ExpressionTranslator etran) { - Contract.Requires(tok != null); - Contract.Requires(nw != null); - Contract.Requires(builder != null); - Contract.Requires(etran != null); - - // $Heap[$nw, alloc] := true; - Bpl.Expr alloc = predef.Alloc(tok); - Bpl.IdentifierExpr heap = etran.HeapCastToIdentifierExpr; - Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(tok, heap, UpdateHeap(tok, heap, nw, alloc, Bpl.Expr.True)); - builder.Add(cmd); - if (extraCmd != null) { - builder.Add(extraCmd); - } - // assume $IsGoodHeap($Heap); - builder.Add(AssumeGoodHeap(tok, etran)); - // assume $IsHeapAnchor($Heap); - builder.Add(new Bpl.AssumeCmd(tok, FunctionCall(tok, BuiltinFunction.IsHeapAnchor, null, etran.HeapExpr))); + private void CommitAllocatedObject(IToken tok, Bpl.IdentifierExpr nw, Bpl.Cmd extraCmd, BoogieStmtListBuilder builder, ExpressionTranslator etran) { + Contract.Requires(tok != null); + Contract.Requires(nw != null); + Contract.Requires(builder != null); + Contract.Requires(etran != null); + + // $Heap[$nw, alloc] := true; + Bpl.Expr alloc = predef.Alloc(tok); + Bpl.IdentifierExpr heap = etran.HeapCastToIdentifierExpr; + Bpl.Cmd cmd = Bpl.Cmd.SimpleAssign(tok, heap, UpdateHeap(tok, heap, nw, alloc, Bpl.Expr.True)); + builder.Add(cmd); + if (extraCmd != null) { + builder.Add(extraCmd); } + // assume $IsGoodHeap($Heap); + builder.Add(AssumeGoodHeap(tok, etran)); + // assume $IsHeapAnchor($Heap); + builder.Add(new Bpl.AssumeCmd(tok, FunctionCall(tok, BuiltinFunction.IsHeapAnchor, null, etran.HeapExpr))); + } - private void IntroduceAndAssignExistentialVars(ExistsExpr exists, BoogieStmtListBuilder builder, BoogieStmtListBuilder builderOutsideIfConstruct, List locals, ExpressionTranslator etran, bool isGhost) { - Contract.Requires(exists != null); - Contract.Requires(exists.Range == null); - Contract.Requires(builder != null); - Contract.Requires(builderOutsideIfConstruct != null); - Contract.Requires(locals != null); - Contract.Requires(etran != null); - // declare and havoc the bound variables of 'exists' as local variables - var iesForHavoc = new List(); - foreach (var bv in exists.BoundVars) { - Bpl.Type varType = TrType(bv.Type); - Bpl.Expr wh = GetWhereClause(bv.Tok, - new Bpl.IdentifierExpr(bv.Tok, bv.AssignUniqueName(currentDeclaration.IdGenerator), varType), - bv.Type, etran, isAllocContext.Var(isGhost, bv)); - Bpl.Variable local = new Bpl.LocalVariable(bv.Tok, new Bpl.TypedIdent(bv.Tok, bv.AssignUniqueName(currentDeclaration.IdGenerator), varType, wh)); - locals.Add(local); - iesForHavoc.Add(new Bpl.IdentifierExpr(local.tok, local)); - } - builderOutsideIfConstruct.Add(new Bpl.HavocCmd(exists.tok, iesForHavoc)); - builder.Add(TrAssumeCmd(exists.tok, etran.TrExpr(exists.Term))); + private void IntroduceAndAssignExistentialVars(ExistsExpr exists, BoogieStmtListBuilder builder, BoogieStmtListBuilder builderOutsideIfConstruct, List locals, ExpressionTranslator etran, bool isGhost) { + Contract.Requires(exists != null); + Contract.Requires(exists.Range == null); + Contract.Requires(builder != null); + Contract.Requires(builderOutsideIfConstruct != null); + Contract.Requires(locals != null); + Contract.Requires(etran != null); + // declare and havoc the bound variables of 'exists' as local variables + var iesForHavoc = new List(); + foreach (var bv in exists.BoundVars) { + Bpl.Type varType = TrType(bv.Type); + Bpl.Expr wh = GetWhereClause(bv.Tok, + new Bpl.IdentifierExpr(bv.Tok, bv.AssignUniqueName(currentDeclaration.IdGenerator), varType), + bv.Type, etran, isAllocContext.Var(isGhost, bv)); + Bpl.Variable local = new Bpl.LocalVariable(bv.Tok, new Bpl.TypedIdent(bv.Tok, bv.AssignUniqueName(currentDeclaration.IdGenerator), varType, wh)); + locals.Add(local); + iesForHavoc.Add(new Bpl.IdentifierExpr(local.tok, local)); } + builderOutsideIfConstruct.Add(new Bpl.HavocCmd(exists.tok, iesForHavoc)); + builder.Add(TrAssumeCmd(exists.tok, etran.TrExpr(exists.Term))); + } - public void TrStmtList(List stmts, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, - RangeToken scopeRange = null, bool processLabels = true) { - Contract.Requires(stmts != null); - Contract.Requires(builder != null); - Contract.Requires(locals != null); - Contract.Requires(etran != null); - - BoogieStmtListBuilder innerBuilder = builder; - if (scopeRange != null && !builder.Context.ReturnPosition) { - /* - * Boogie's reports at which return location - * the postconditions of a procedure could not be satisfied. - * - * The return locations are the sinks of the Boogie control flow graph. - * However, adding a command to the end of a Boogie block, can cause previous sinks in the CFG to then lead - * to that new command, which changes the reported error location. - * - * Because of this reason, we need to make sure not to pop at points right before the implementation returns, - * which is why we check builder.Context.ReturnPosition - * - * A more reliable way of Boogie error reporting would be not to rely on sinks in the CFG, - * By for example reporting all points at which a control flow decision was made. - */ - builder.Add(new ChangeScope(scopeRange.StartToken, ChangeScope.Modes.Push)); - innerBuilder = builder.WithContext(builder.Context with { - ScopeDepth = builder.Context.ScopeDepth + 1 - }); - } - - for (var index = 0; index < stmts.Count; index++) { - var ss = stmts[index]; - var last = index == stmts.Count - 1; - var indexContext = innerBuilder.Context with { - ReturnPosition = innerBuilder.Context.ReturnPosition && last - }; - var indexBuilder = innerBuilder.WithContext(indexContext); - if (processLabels) { - for (var l = ss.Labels; l != null; l = l.Next) { - var heapAt = new Bpl.LocalVariable(ss.Tok, - new Bpl.TypedIdent(ss.Tok, "$Heap_at_" + l.Data.AssignUniqueId(CurrentIdGenerator), predef.HeapType)); - locals.Add(heapAt); - builder.Add(Bpl.Cmd.SimpleAssign(ss.Tok, new Bpl.IdentifierExpr(ss.Tok, heapAt), etran.HeapExpr)); - } - } + public void TrStmtList(List stmts, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + RangeToken scopeRange = null, bool processLabels = true) { + Contract.Requires(stmts != null); + Contract.Requires(builder != null); + Contract.Requires(locals != null); + Contract.Requires(etran != null); + + BoogieStmtListBuilder innerBuilder = builder; + if (scopeRange != null && !builder.Context.ReturnPosition) { + /* + * Boogie's reports at which return location + * the postconditions of a procedure could not be satisfied. + * + * The return locations are the sinks of the Boogie control flow graph. + * However, adding a command to the end of a Boogie block, can cause previous sinks in the CFG to then lead + * to that new command, which changes the reported error location. + * + * Because of this reason, we need to make sure not to pop at points right before the implementation returns, + * which is why we check builder.Context.ReturnPosition + * + * A more reliable way of Boogie error reporting would be not to rely on sinks in the CFG, + * By for example reporting all points at which a control flow decision was made. + */ + builder.Add(new ChangeScope(scopeRange.StartToken, ChangeScope.Modes.Push)); + innerBuilder = builder.WithContext(builder.Context with { + ScopeDepth = builder.Context.ScopeDepth + 1 + }); + } - TrStmt(ss, indexBuilder, locals, etran); - if (processLabels && ss.Labels != null) { - builder.AddLabelCmd("after_" + ss.Labels.Data.AssignUniqueId(CurrentIdGenerator)); + for (var index = 0; index < stmts.Count; index++) { + var ss = stmts[index]; + var last = index == stmts.Count - 1; + var indexContext = innerBuilder.Context with { + ReturnPosition = innerBuilder.Context.ReturnPosition && last + }; + var indexBuilder = innerBuilder.WithContext(indexContext); + if (processLabels) { + for (var l = ss.Labels; l != null; l = l.Next) { + var heapAt = new Bpl.LocalVariable(ss.Tok, + new Bpl.TypedIdent(ss.Tok, "$Heap_at_" + l.Data.AssignUniqueId(CurrentIdGenerator), predef.HeapType)); + locals.Add(heapAt); + builder.Add(Bpl.Cmd.SimpleAssign(ss.Tok, new Bpl.IdentifierExpr(ss.Tok, heapAt), etran.HeapExpr)); } } - if (scopeRange != null && !builder.Context.ReturnPosition) { - builder.Add(new ChangeScope(scopeRange.EndToken, ChangeScope.Modes.Pop)); + TrStmt(ss, indexBuilder, locals, etran); + if (processLabels && ss.Labels != null) { + builder.AddLabelCmd("after_" + ss.Labels.Data.AssignUniqueId(CurrentIdGenerator)); } } - void TrStmt_CheckWellformed(Expression expr, BoogieStmtListBuilder builder, List locals, - ExpressionTranslator etran, bool subsumption, bool lValueContext = false, AddResultCommands addResultCommands = null) { - Contract.Requires(expr != null); - Contract.Requires(builder != null); - Contract.Requires(locals != null); - Contract.Requires(etran != null); - Contract.Requires(predef != null); - - Bpl.QKeyValue kv; - if (subsumption) { - kv = null; // this is the default behavior of Boogie's assert - } else { - List args = new List(); - // {:subsumption 0} - args.Add(Bpl.Expr.Literal(0)); - kv = new Bpl.QKeyValue(expr.tok, "subsumption", args, null); - } - var options = new WFOptions(kv); - // Only do reads checks if reads clauses on methods are enabled and the reads clause is not *. - // The latter is important to avoid any extra verification cost for backwards compatibility. - if (etran.readsFrame != null) { - options = options.WithReadsChecks(true); - } - if (lValueContext) { - options = options.WithLValueContext(true); - } - CheckWellformedWithResult(expr, options, locals, builder, etran, addResultCommands); - builder.Add(TrAssumeCmd(expr.tok, etran.CanCallAssumption(expr))); + if (scopeRange != null && !builder.Context.ReturnPosition) { + builder.Add(new ChangeScope(scopeRange.EndToken, ChangeScope.Modes.Pop)); } + } - List GetContextReadsFrames() { - return (codeContext as MethodOrFunction)?.Reads?.Expressions ?? new(); + void TrStmt_CheckWellformed(Expression expr, BoogieStmtListBuilder builder, List locals, + ExpressionTranslator etran, bool subsumption, bool lValueContext = false, AddResultCommands addResultCommands = null) { + Contract.Requires(expr != null); + Contract.Requires(builder != null); + Contract.Requires(locals != null); + Contract.Requires(etran != null); + Contract.Requires(predef != null); + + Bpl.QKeyValue kv; + if (subsumption) { + kv = null; // this is the default behavior of Boogie's assert + } else { + List args = new List(); + // {:subsumption 0} + args.Add(Bpl.Expr.Literal(0)); + kv = new Bpl.QKeyValue(expr.tok, "subsumption", args, null); } - - List GetContextModifiesFrames() { - return (codeContext as IMethodCodeContext)?.Modifies?.Expressions ?? new(); + var options = new WFOptions(kv); + // Only do reads checks if reads clauses on methods are enabled and the reads clause is not *. + // The latter is important to avoid any extra verification cost for backwards compatibility. + if (etran.readsFrame != null) { + options = options.WithReadsChecks(true); } + if (lValueContext) { + options = options.WithLValueContext(true); + } + CheckWellformedWithResult(expr, options, locals, builder, etran, addResultCommands); + builder.Add(TrAssumeCmd(expr.tok, etran.CanCallAssumption(expr))); + } + + List GetContextReadsFrames() { + return (codeContext as MethodOrFunction)?.Reads?.Expressions ?? new(); + } + + List GetContextModifiesFrames() { + return (codeContext as IMethodCodeContext)?.Modifies?.Expressions ?? new(); } -} +} \ No newline at end of file From ae6f06039e117f502ec60b4428152282bf96b34a Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 20 Sep 2024 15:52:28 +0200 Subject: [PATCH 06/47] Start with removing pre and post call stuff --- .../BoogieGenerator.ExpressionTranslator.cs | 2 +- .../Verifier/BoogieGenerator.Iterators.cs | 2 +- .../Verifier/BoogieGenerator.Methods.cs | 94 +++++++++---------- .../Verifier/BoogieGenerator.SplitExpr.cs | 2 +- Source/DafnyCore/Verifier/BoogieGenerator.cs | 19 ++-- .../Statements/BoogieGenerator.TrCall.cs | 2 +- 6 files changed, 57 insertions(+), 64 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index 663b8ed47f6..10d78a37498 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -704,7 +704,7 @@ public Boogie.Expr TrExpr(Expression expr) { result = BoogieGenerator.CondApplyUnbox(GetToken(e), result, e.Function.ResultType, e.Type); bool callIsLit = argsAreLit - && BoogieGenerator.FunctionBodyIsAvailable(e.Function, BoogieGenerator.currentModule, BoogieGenerator.currentScope, true) + && BoogieGenerator.FunctionBodyIsAvailable(e.Function, BoogieGenerator.currentModule, BoogieGenerator.currentScope) && !e.Function.Reads.Expressions.Any(); // Function could depend on external values if (callIsLit) { result = MaybeLit(result, ty); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs index 9636597ed57..dd161356a05 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs @@ -95,7 +95,7 @@ Bpl.Procedure AddIteratorProc(IteratorDecl iter, MethodTranslationKind kind) { p.Label.E = etran.Old.TrExpr(p.E); } else { foreach (var s in TrSplitExprForMethodSpec(new BodyTranslationContext(false), p.E, etran, kind)) { - if (kind == MethodTranslationKind.CallPre && RefinementToken.IsInherited(s.Tok, currentModule)) { + if (kind == MethodTranslationKind.Call && RefinementToken.IsInherited(s.Tok, currentModule)) { // this precondition was inherited into this module, so just ignore it } else { req.Add(Requires(s.Tok, s.IsOnlyFree, p.E, s.E, errorMessage, successMessage, comment)); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 73f1e3acb41..84801b5a398 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -193,16 +193,13 @@ void AddMethod_Top(Method m, bool isByMethod, bool includeAllMethods) { } // the method spec itself if (!isByMethod) { - sink.AddTopLevelDeclaration(AddMethod(m, MethodTranslationKind.CallPre)); - sink.AddTopLevelDeclaration(AddMethod(m, MethodTranslationKind.CallPost)); - + sink.AddTopLevelDeclaration(AddMethod(m, MethodTranslationKind.Call)); } if (m is ExtremeLemma) { // Let the CoCall and Impl forms to use m.PrefixLemma signature and specification (and // note that m.PrefixLemma.Body == m.Body. m = ((ExtremeLemma)m).PrefixLemma; - sink.AddTopLevelDeclaration(AddMethod(m, MethodTranslationKind.CoCallPre)); - sink.AddTopLevelDeclaration(AddMethod(m, MethodTranslationKind.CoCallPost)); + sink.AddTopLevelDeclaration(AddMethod(m, MethodTranslationKind.CoCall)); } if (!m.HasVerifyFalseAttribute && m.Body != null && InVerificationScope(m)) { @@ -1793,7 +1790,42 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { return req; } - List GetEnsures() { + var req = new List(); + var mod = new List(); + var ens = new List(); + + var name = MethodName(m, kind); + switch (kind) { + case MethodTranslationKind.Call: + case MethodTranslationKind.CoCall: + outParams = new List(); + req = GetRequires(); + break; + case MethodTranslationKind.CallPost: + case MethodTranslationKind.CoCallPost: + mod.Add(ordinaryEtran.HeapCastToIdentifierExpr); + ens = GetEnsures(); + break; + default: + req = GetRequires(); + mod.Add(ordinaryEtran.HeapCastToIdentifierExpr); + ens = GetEnsures(); + break; + } + var proc = new Boogie.Procedure(m.tok, name, new List(), inParams, outParams, false, req, mod, ens, etran.TrAttributes(m.Attributes, null)); + AddVerboseNameAttribute(proc, m.FullDafnyName, kind); + + if (InsertChecksums) { + InsertChecksum(m, proc, true); + } + + currentModule = null; + codeContext = null; + isAllocContext = null; + + return proc; + + List GetEnsures() { var ens = new List(); if (kind is MethodTranslationKind.SpecWellformedness or MethodTranslationKind.OverrideCheck) { return ens; @@ -1820,7 +1852,7 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { } } } - if (m is Constructor && kind == MethodTranslationKind.CallPost) { + if (m is Constructor) { var dafnyFresh = new OldExpr(Token.NoToken, new UnaryOpExpr(Token.NoToken, UnaryOpExpr.Opcode.Not, new UnaryOpExpr(Token.NoToken, UnaryOpExpr.Opcode.Allocated, new IdentifierExpr(Token.NoToken, "this")))); @@ -1834,51 +1866,17 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { // add the fuel assumption for the reveal method of a opaque method if (IsOpaqueRevealLemma(m)) { List args = Attributes.FindExpressions(m.Attributes, "revealedFunction"); - if (args != null) { - MemberSelectExpr selectExpr = args[0].Resolved as MemberSelectExpr; - if (selectExpr != null) { - Function f = selectExpr.Member as Function; - AddEnsures(ens, Ensures(m.tok, true, null, GetRevealConstant(f), null, null, null)); - } + if (args == null) { + return ens; + } + + if (args[0].Resolved is MemberSelectExpr selectExpr) { + Function f = selectExpr.Member as Function; + AddEnsures(ens, Ensures(m.tok, true, null, GetRevealConstant(f), null, null, null)); } } return ens; } - - var req = new List(); - var mod = new List(); - var ens = new List(); - - var name = MethodName(m, kind); - switch (kind) { - case MethodTranslationKind.CallPre: - case MethodTranslationKind.CoCallPre: - outParams = new List(); - req = GetRequires(); - break; - case MethodTranslationKind.CallPost: - case MethodTranslationKind.CoCallPost: - mod.Add(ordinaryEtran.HeapCastToIdentifierExpr); - ens = GetEnsures(); - break; - default: - req = GetRequires(); - mod.Add(ordinaryEtran.HeapCastToIdentifierExpr); - ens = GetEnsures(); - break; - } - var proc = new Boogie.Procedure(m.tok, name, new List(), inParams, outParams, false, req, mod, ens, etran.TrAttributes(m.Attributes, null)); - AddVerboseNameAttribute(proc, m.FullDafnyName, kind); - - if (InsertChecksums) { - InsertChecksum(m, proc, true); - } - - currentModule = null; - codeContext = null; - isAllocContext = null; - - return proc; } private void InsertChecksum(Method m, Boogie.Declaration decl, bool specificationOnly = false) { diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs b/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs index ef2c45ed6d0..f7ffceb01c4 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs @@ -489,7 +489,7 @@ private bool TrSplitFunctionCallExpr(BodyTranslationContext context, (codeContext == null || !codeContext.MustReverify)) { // The function was inherited as body-less but is now given a body. Don't inline the body (since, apparently, everything // that needed to be proved about the function was proved already in the previous module, even without the body definition). - } else if (!FunctionBodyIsAvailable(f, currentModule, currentScope, inlineProtectedFunctions)) { + } else if (!FunctionBodyIsAvailable(f, currentModule, currentScope)) { // Don't inline opaque functions } else if (context.ContainsHide) { // Do not inline in a blind context diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index 6433df53dec..66c4355c576 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -1375,7 +1375,7 @@ public Bpl.Expr GetArrayIndexFieldName(IToken tok, List indices) { /// - "f" has a body /// - "f" is not opaque /// - bool FunctionBodyIsAvailable(Function f, ModuleDefinition context, VisibilityScope scope, bool revealProtectedBody) { + bool FunctionBodyIsAvailable(Function f, ModuleDefinition context, VisibilityScope scope) { Contract.Requires(f != null); Contract.Requires(context != null); return f.Body != null && !IsOpaque(f) && f.IsRevealedInScope(scope); @@ -2802,15 +2802,13 @@ private Bpl.Function GetCanCallFunction(Function f) { /// but no callers, and vice versa for InterModuleCall, IntraModuleCall, and CoCall. /// /// Remy: TODO Simplify - enum MethodTranslationKind { SpecWellformedness, CallPre, CallPost, CoCallPre, CoCallPost, Implementation, OverrideCheck } + enum MethodTranslationKind { SpecWellformedness, Call, CoCall, Implementation, OverrideCheck } private static readonly Dictionary kindSanitizedPrefix = new() { { MethodTranslationKind.SpecWellformedness, "CheckWellFormed" }, - { MethodTranslationKind.CallPre, "CallPre" }, - { MethodTranslationKind.CallPost, "CallPost" }, - { MethodTranslationKind.CoCallPre, "CoCallPre" }, - { MethodTranslationKind.CoCallPost, "CoCallPost" }, + { MethodTranslationKind.Call, "Call" }, + { MethodTranslationKind.CoCall, "CoCall" }, { MethodTranslationKind.Implementation, "Impl" }, { MethodTranslationKind.OverrideCheck, "OverrideCheck" }, }; @@ -2823,10 +2821,8 @@ static string MethodName(ICodeContext m, MethodTranslationKind kind) { private static readonly Dictionary kindDescription = new Dictionary() { {MethodTranslationKind.SpecWellformedness, "well-formedness"}, - {MethodTranslationKind.CallPre, "call precondtion"}, - {MethodTranslationKind.CallPost, "call postcondition"}, - {MethodTranslationKind.CoCallPre, "co-call precondtion"}, - {MethodTranslationKind.CoCallPost, "co-call postcondition"}, + {MethodTranslationKind.Call, "call"}, + {MethodTranslationKind.CoCall, "co-call"}, {MethodTranslationKind.Implementation, "correctness"}, {MethodTranslationKind.OverrideCheck, "override check"}, }; @@ -4544,8 +4540,7 @@ List TrSplitExprForMethodSpec(BodyTranslationContext context, Exp var splits = new List(); var applyInduction = kind == MethodTranslationKind.Implementation; - bool splitHappened; // we don't actually care - splitHappened = TrSplitExpr(context, expr, splits, true, int.MaxValue, kind != MethodTranslationKind.CallPost, applyInduction, etran); + TrSplitExpr(context, expr, splits, true, int.MaxValue, kind != MethodTranslationKind.CallPost, applyInduction, etran); return splits; } diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs index 89830f477e4..2daa8d0a570 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs @@ -361,7 +361,7 @@ void AddCall(BoogieStmtListBuilder callBuilder) { callBuilder.Add(new CommentCmd($"ProcessCallStmt: Check precondition")); // Make the call AddReferencedMember(callee); - var call = Call(callBuilder.Context, tok, MethodName(callee, isCoCall ? MethodTranslationKind.CoCallPre : MethodTranslationKind.CallPre), ins, new List()); + var call = Call(callBuilder.Context, tok, MethodName(callee, isCoCall ? MethodTranslationKind.CoCall : MethodTranslationKind.Call), ins, new List()); proofDependencies?.AddProofDependencyId(call, tok, new CallDependency(cs)); if ( (assertionOnlyFilter != null && !assertionOnlyFilter(tok)) || From 3cdae7279a8d1713bf3efad27ec933510300b21f Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 20 Sep 2024 16:00:30 +0200 Subject: [PATCH 07/47] Finish pre/post call cleanup --- ...oogieGenerator.Functions.Wellformedness.cs | 2 +- .../Verifier/BoogieGenerator.Methods.cs | 27 ++------- .../Verifier/BoogieGenerator.SplitExpr.cs | 48 ++++++++-------- Source/DafnyCore/Verifier/BoogieGenerator.cs | 4 +- .../Statements/BoogieGenerator.TrCall.cs | 57 +++++++++---------- .../Statements/BoogieGenerator.TrStatement.cs | 2 +- 6 files changed, 60 insertions(+), 80 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs index 5784de766f8..e4401d21a61 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs @@ -46,7 +46,7 @@ public void Check(Function f) { foreach (AttributedExpression ensures in f.Ens) { var functionHeight = generator.currentModule.CallGraph.GetSCCRepresentativePredecessorCount(f); var splits = new List(); - bool splitHappened /*we actually don't care*/ = generator.TrSplitExpr(context, ensures.E, splits, true, functionHeight, true, true, etran); + bool splitHappened /*we actually don't care*/ = generator.TrSplitExpr(context, ensures.E, splits, true, functionHeight, true, etran); var (errorMessage, successMessage) = generator.CustomErrorMessage(ensures.Attributes); foreach (var s in splits) { if (s.IsChecked && !RefinementToken.IsInherited(s.Tok, generator.currentModule)) { diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 84801b5a398..e9adfe3fa2e 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -1790,29 +1790,12 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { return req; } - var req = new List(); - var mod = new List(); - var ens = new List(); var name = MethodName(m, kind); - switch (kind) { - case MethodTranslationKind.Call: - case MethodTranslationKind.CoCall: - outParams = new List(); - req = GetRequires(); - break; - case MethodTranslationKind.CallPost: - case MethodTranslationKind.CoCallPost: - mod.Add(ordinaryEtran.HeapCastToIdentifierExpr); - ens = GetEnsures(); - break; - default: - req = GetRequires(); - mod.Add(ordinaryEtran.HeapCastToIdentifierExpr); - ens = GetEnsures(); - break; - } - var proc = new Boogie.Procedure(m.tok, name, new List(), inParams, outParams, false, req, mod, ens, etran.TrAttributes(m.Attributes, null)); + var req = GetRequires(); + var mod = new List { ordinaryEtran.HeapCastToIdentifierExpr }; + var ens = GetEnsures(); + var proc = new Bpl.Procedure(m.tok, name, new List(), inParams, outParams, false, req, mod, ens, etran.TrAttributes(m.Attributes, null)); AddVerboseNameAttribute(proc, m.FullDafnyName, kind); if (InsertChecksums) { @@ -1826,7 +1809,7 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { return proc; List GetEnsures() { - var ens = new List(); + var ens = new List(); if (kind is MethodTranslationKind.SpecWellformedness or MethodTranslationKind.OverrideCheck) { return ens; } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs b/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs index f7ffceb01c4..cf365d79ac9 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs @@ -42,7 +42,7 @@ public partial class BoogieGenerator { /// passed in as 0, then no functions will be inlined). /// bool TrSplitExpr(BodyTranslationContext context, Expression expr, List splits, /*!*/ /*!*/ - bool position, int heightLimit, bool inlineProtectedFunctions, bool applyInduction, ExpressionTranslator etran) { + bool position, int heightLimit, bool applyInduction, ExpressionTranslator etran) { Contract.Requires(expr != null); Contract.Requires(expr.Type.IsBoolType || (expr is BoxingCastExpr && ((BoxingCastExpr)expr).E.Type.IsBoolType)); Contract.Requires(splits != null); @@ -52,7 +52,7 @@ bool TrSplitExpr(BodyTranslationContext context, Expression expr, List(); - if (TrSplitExpr(context, bce.E, ss, position, heightLimit, inlineProtectedFunctions, applyInduction, etran)) { + if (TrSplitExpr(context, bce.E, ss, position, heightLimit, applyInduction, etran)) { foreach (var s in ss) { splits.Add(ToSplitExprInfo(s.Kind, CondApplyBox(s.Tok, s.E, bce.FromType, bce.ToType))); } @@ -63,18 +63,18 @@ bool TrSplitExpr(BodyTranslationContext context, Expression expr, List(); - if (TrSplitExpr(context, e.Body, ss, position, heightLimit, inlineProtectedFunctions, applyInduction, etran)) { + if (TrSplitExpr(context, e.Body, ss, position, heightLimit, applyInduction, etran)) { // We don't know where the RHSs of the let are used in the body. In particular, we don't know if a RHS // will end up in a spot where TrSplitExpr would like to increase the Layer offset or not. In fact, different // uses of the same let variable may end up needing different Layer constants. The following code will @@ -101,7 +101,7 @@ bool TrSplitExpr(BodyTranslationContext context, Expression expr, List { fe }, e.At) { AtLabel = e.AtLabel }; ee.Type = Type.Bool; // resolve here - TrSplitExpr(context, ee, splits, position, heightLimit, inlineProtectedFunctions, applyInduction, etran); + TrSplitExpr(context, ee, splits, position, heightLimit, applyInduction, etran); } return true; } @@ -112,7 +112,7 @@ bool TrSplitExpr(BodyTranslationContext context, Expression expr, List(); - if (TrSplitExpr(context, e.E, ss, !position, heightLimit, inlineProtectedFunctions, applyInduction, etran)) { + if (TrSplitExpr(context, e.E, ss, !position, heightLimit, applyInduction, etran)) { foreach (var s in ss) { splits.Add(ToSplitExprInfo(s.Kind, Bpl.Expr.Unary(s.E.tok, UnaryOperator.Opcode.Not, s.E))); } @@ -125,13 +125,13 @@ bool TrSplitExpr(BodyTranslationContext context, Expression expr, List(); - TrSplitExpr(context, bin.E1, ss, position, heightLimit, inlineProtectedFunctions, applyInduction, etran); + TrSplitExpr(context, bin.E1, ss, position, heightLimit, applyInduction, etran); foreach (var s in ss) { // as the source location in the following implication, use that of the translated "s" splits.Add(ToSplitExprInfo(s.Kind, Bpl.Expr.Binary(s.E.tok, BinaryOperator.Opcode.Imp, lhs, s.E))); } } else { var ss = new List(); - TrSplitExpr(context, bin.E0, ss, !position, heightLimit, inlineProtectedFunctions, applyInduction, etran); + TrSplitExpr(context, bin.E0, ss, !position, heightLimit, applyInduction, etran); var rhs = etran.TrExpr(bin.E1); foreach (var s in ss) { // as the source location in the following implication, use that of the translated "s" @@ -220,8 +220,8 @@ bool TrSplitExpr(BodyTranslationContext context, Expression expr, List(); var ssElse = new List(); - TrSplitExpr(context, ite.Thn, ssThen, position, heightLimit, inlineProtectedFunctions, applyInduction, etran); - TrSplitExpr(context, ite.Els, ssElse, position, heightLimit, inlineProtectedFunctions, applyInduction, etran); + TrSplitExpr(context, ite.Thn, ssThen, position, heightLimit, applyInduction, etran); + TrSplitExpr(context, ite.Els, ssElse, position, heightLimit, applyInduction, etran); var op = position ? BinaryOperator.Opcode.Imp : BinaryOperator.Opcode.And; var test = etran.TrExpr(ite.Test); @@ -241,7 +241,7 @@ bool TrSplitExpr(BodyTranslationContext context, Expression expr, List(); - TrSplitExpr(context, e.E, ss, position, heightLimit, inlineProtectedFunctions, applyInduction, etran); + TrSplitExpr(context, e.E, ss, position, heightLimit, applyInduction, etran); foreach (var s in ss) { // as the source location in the following implication, use that of the translated "s" splits.Add(ToSplitExprInfo(s.Kind, Bpl.Expr.Binary(s.E.tok, BinaryOperator.Opcode.Imp, conclusion, s.E))); } } else { var ss = new List(); - TrSplitExpr(context, e.GetSConclusion(), ss, !position, heightLimit, inlineProtectedFunctions, applyInduction, etran); + TrSplitExpr(context, e.GetSConclusion(), ss, !position, heightLimit, applyInduction, etran); var rhs = etran.TrExpr(e.E); foreach (var s in ss) { // as the source location in the following implication, use that of the translated "s" @@ -269,18 +269,18 @@ bool TrSplitExpr(BodyTranslationContext context, Expression expr, List splits, int heightLimit, bool inlineProtectedFunctions, + Expression expr, List splits, int heightLimit, bool applyInduction, ExpressionTranslator etran, FunctionCallExpr fexp) { var f = fexp.Function; Contract.Assert(f != null); // filled in during resolution @@ -535,7 +535,7 @@ private bool TrSplitFunctionCallExpr(BodyTranslationContext context, // recurse on body var ss = new List(); - TrSplitExpr(context, typeSpecializedBody, ss, true, functionHeight, inlineProtectedFunctions, applyInduction, etran); + TrSplitExpr(context, typeSpecializedBody, ss, true, functionHeight, applyInduction, etran); var needsTokenAdjust = TrSplitNeedsTokenAdjustment(typeSpecializedBody); foreach (var s in ss) { if (s.IsChecked) { diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index 66c4355c576..da52a2e953e 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -4529,7 +4529,7 @@ internal IsAllocType Var(bool ghostStmt, NonglobalVariable var) { Contract.Ensures(Contract.Result>() != null); var splits = new List(); - splitHappened = TrSplitExpr(context, expr, splits, true, int.MaxValue, true, applyInduction, etran); + splitHappened = TrSplitExpr(context, expr, splits, true, int.MaxValue, applyInduction, etran); return splits; } @@ -4540,7 +4540,7 @@ List TrSplitExprForMethodSpec(BodyTranslationContext context, Exp var splits = new List(); var applyInduction = kind == MethodTranslationKind.Implementation; - TrSplitExpr(context, expr, splits, true, int.MaxValue, kind != MethodTranslationKind.CallPost, applyInduction, etran); + TrSplitExpr(context, expr, splits, true, int.MaxValue, applyInduction, etran); return splits; } diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs index 2daa8d0a570..b713a351e8b 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs @@ -348,40 +348,17 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E if (cs.Proof == null) { AddCall(builder); } else { - var callBuilder = new BoogieStmtListBuilder(this, options, builder.Context); - AddComment(callBuilder, cs, "call statement proof"); + var proofBuilder = new BoogieStmtListBuilder(this, options, builder.Context); + AddComment(proofBuilder, cs, "call statement proof"); CurrentIdGenerator.Push(); - TrStmt(cs.Proof, callBuilder, locals, etran); + TrStmt(cs.Proof, proofBuilder, locals, etran); CurrentIdGenerator.Pop(); - AddCall(callBuilder); - PathAsideBlock(cs.Tok, callBuilder, builder); + AddCall(proofBuilder); + PathAsideBlock(cs.Tok, proofBuilder, builder); + var freeCall = AddCall(builder); + freeCall.IsFree = true; } - void AddCall(BoogieStmtListBuilder callBuilder) { - callBuilder.Add(new CommentCmd($"ProcessCallStmt: Check precondition")); - // Make the call - AddReferencedMember(callee); - var call = Call(callBuilder.Context, tok, MethodName(callee, isCoCall ? MethodTranslationKind.CoCall : MethodTranslationKind.Call), ins, new List()); - proofDependencies?.AddProofDependencyId(call, tok, new CallDependency(cs)); - if ( - (assertionOnlyFilter != null && !assertionOnlyFilter(tok)) || - (module != currentModule && RefinementToken.IsInherited(tok, currentModule) && (codeContext == null || !codeContext.MustReverify))) { - // The call statement is inherited, so the refined module already checked that the precondition holds. Note, - // preconditions are not allowed to be strengthened, except if they use a predicate whose body has been strengthened. - // But if the callee sits in a different module, then any predicate it uses will be treated as opaque (that is, - // uninterpreted) anyway, so the refined module will have checked the call precondition for all possible definitions - // of the predicate. - call.IsFree = true; - } - callBuilder.Add(call); - } - - builder.Add(new CommentCmd("ProcessCallStmt: Make the call")); - var post = Call(builder.Context, tok, - MethodName(callee, isCoCall ? MethodTranslationKind.CoCallPost : MethodTranslationKind.CallPost), ins, outs); - proofDependencies?.AddProofDependencyId(post, tok, new CallDependency(cs)); - builder.Add(post); - // Unbox results as needed for (int i = 0; i < Lhss.Count; i++) { Bpl.IdentifierExpr bLhs = Lhss[i]; @@ -399,5 +376,25 @@ void AddCall(BoogieStmtListBuilder callBuilder) { builder.Add(cmd); } } + + return; + + CallCmd AddCall(BoogieStmtListBuilder callBuilder) { + AddReferencedMember(callee); + var call = Call(callBuilder.Context, tok, MethodName(callee, isCoCall ? MethodTranslationKind.CoCall : MethodTranslationKind.Call), ins, new List()); + proofDependencies?.AddProofDependencyId(call, tok, new CallDependency(cs)); + if ( + (assertionOnlyFilter != null && !assertionOnlyFilter(tok)) || + (module != currentModule && RefinementToken.IsInherited(tok, currentModule) && (codeContext == null || !codeContext.MustReverify))) { + // The call statement is inherited, so the refined module already checked that the precondition holds. Note, + // preconditions are not allowed to be strengthened, except if they use a predicate whose body has been strengthened. + // But if the callee sits in a different module, then any predicate it uses will be treated as opaque (that is, + // uninterpreted) anyway, so the refined module will have checked the call precondition for all possible definitions + // of the predicate. + call.IsFree = true; + } + callBuilder.Add(call); + return call; + } } } \ No newline at end of file diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs index 357ce38cb43..4cf7eb56713 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs @@ -869,7 +869,7 @@ void RecordNewObjectsIn_New(IToken tok, IteratorDecl iter, Bpl.Expr initHeap, Bp // call $iter_newUpdate := $IterCollectNewObjects(initHeap, $Heap, this, _new); var th = new Bpl.IdentifierExpr(iter.tok, etran.This, predef.RefType); var nwField = new Bpl.IdentifierExpr(tok, GetField(iter.Member_New)); - var cmd = Call(builder.Context, iter.tok, "$IterCollectNewObjects", + Cmd cmd = Call(builder.Context, iter.tok, "$IterCollectNewObjects", new List() { initHeap, etran.HeapExpr, th, nwField }, new List() { updatedSetIE }); builder.Add(cmd); From 9e7a558d050d98e9cbda97cc1eb0677275912a75 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 20 Sep 2024 16:17:54 +0200 Subject: [PATCH 08/47] Remove the proof field everywhere but in BlockByProof --- .../AST/Grammar/Printer/Printer.Statement.cs | 23 ++-------- .../AST/Grammar/TokenNewIndentCollector.cs | 6 +-- .../Assignment/AssignOrReturnStmt.cs | 16 +++---- .../AST/Statements/Assignment/UpdateStmt.cs | 15 ++----- .../AST/Statements/BlockByProofStmt.cs | 28 +++++++++++- .../AST/Statements/Methods/CallStmt.cs | 13 +++--- .../AST/Statements/Verification/AssertStmt.cs | 16 +------ Source/DafnyCore/AST/Substituter.cs | 2 +- .../Resolver/GhostInterestVisitor.cs | 24 +++------- Source/DafnyCore/Resolver/ModuleResolver.cs | 27 +++-------- .../PreType/PreTypeResolve.Statements.cs | 12 ++--- .../Rewriters/RefinementTransformer.cs | 6 +-- .../Statements/BlockByProofStmtVerifier.cs | 4 ++ .../Statements/BoogieGenerator.TrCall.cs | 45 ++++++------------- .../BoogieGenerator.TrPredicateStatement.cs | 17 +++---- 15 files changed, 95 insertions(+), 159 deletions(-) diff --git a/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs b/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs index ef97d0be06f..9a829e060bf 100644 --- a/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs +++ b/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs @@ -53,10 +53,7 @@ public void PrintStatement(Statement stmt, int indent) { wr.Write("{0}: ", assertStmt.Label.Name); } PrintExpression(expr, true); - if (assertStmt != null && assertStmt.Proof != null) { - wr.Write(" by "); - PrintStatement(assertStmt.Proof, indent); - } else if (expectStmt != null && expectStmt.Message != null) { + if (expectStmt is { Message: not null }) { wr.Write(", "); PrintExpression(expectStmt.Message, true); wr.Write(";"); @@ -363,7 +360,7 @@ public void PrintStatement(Statement stmt, int indent) { wr.Write(" "); } PrintUpdateRHS(s, indent); - PrintBy(s); + wr.Write(";"); } else if (stmt is CallStmt) { // Most calls are printed from their concrete syntax given in the input. However, recursive calls to @@ -395,7 +392,7 @@ public void PrintStatement(Statement stmt, int indent) { wr.Write(" "); PrintUpdateRHS(s.Update, indent); } - PrintBy(s.Update); + wr.Write(";"); } else if (stmt is VarDeclPattern) { var s = (VarDeclPattern)stmt; if (s.tok is AutoGeneratedToken) { @@ -454,20 +451,6 @@ public void PrintStatement(Statement stmt, int indent) { } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement } - - void PrintBy(ConcreteUpdateStatement statement) { - BlockStmt proof = statement switch { - UpdateStmt updateStmt => updateStmt.Proof, - AssignOrReturnStmt returnStmt => returnStmt.Proof, - _ => null - }; - if (proof != null) { - wr.Write(" by "); - PrintStatement(proof, indent); - } else { - wr.Write(";"); - } - } } private void PrintHideReveal(HideRevealStmt revealStmt) { diff --git a/Source/DafnyCore/AST/Grammar/TokenNewIndentCollector.cs b/Source/DafnyCore/AST/Grammar/TokenNewIndentCollector.cs index 0a2c2180651..45e6630af7b 100644 --- a/Source/DafnyCore/AST/Grammar/TokenNewIndentCollector.cs +++ b/Source/DafnyCore/AST/Grammar/TokenNewIndentCollector.cs @@ -559,9 +559,9 @@ public bool SetIndentAssertLikeStatement(Statement stmt, int indent) { } } - if (stmt is AssertStmt { Proof: { StartToken: { } startToken } }) { - SetOpeningIndentedRegion(startToken, indent); - } + // if (stmt is AssertStmt { Proof: { StartToken: { } startToken } }) { + // SetOpeningIndentedRegion(startToken, indent); + // } return true; } diff --git a/Source/DafnyCore/AST/Statements/Assignment/AssignOrReturnStmt.cs b/Source/DafnyCore/AST/Statements/Assignment/AssignOrReturnStmt.cs index a598f530af8..42c571a9c2a 100644 --- a/Source/DafnyCore/AST/Statements/Assignment/AssignOrReturnStmt.cs +++ b/Source/DafnyCore/AST/Statements/Assignment/AssignOrReturnStmt.cs @@ -9,8 +9,7 @@ public class AssignOrReturnStmt : ConcreteUpdateStatement, ICloneable Rhss; public readonly AttributedToken KeywordToken; - public readonly BlockStmt Proof; - [FilledInDuringResolution] public readonly List ResolvedStatements = new List(); + [FilledInDuringResolution] public readonly List ResolvedStatements = new(); public override IEnumerable SubStatements => ResolvedStatements; public override IToken Tok { get { @@ -23,7 +22,8 @@ public override IToken Tok { } } - public override IEnumerable Children => ResolvedStatements.Concat(Proof?.Children ?? new List()); + public override IEnumerable Children => ResolvedStatements; + public override IEnumerable PreResolveSubStatements => Enumerable.Empty(); [ContractInvariantMethod] @@ -44,14 +44,13 @@ public AssignOrReturnStmt(Cloner cloner, AssignOrReturnStmt original) : base(clo Rhs = (ExprRhs)cloner.CloneRHS(original.Rhs); Rhss = original.Rhss.ConvertAll(cloner.CloneRHS); KeywordToken = cloner.AttributedTok(original.KeywordToken); - Proof = cloner.CloneBlockStmt(original.Proof); if (cloner.CloneResolvedFields) { ResolvedStatements = original.ResolvedStatements.Select(stmt => cloner.CloneStmt(stmt, false)).ToList(); } } - public AssignOrReturnStmt(RangeToken rangeToken, List lhss, ExprRhs rhs, AttributedToken keywordToken, List rhss, BlockStmt proof = null) + public AssignOrReturnStmt(RangeToken rangeToken, List lhss, ExprRhs rhs, AttributedToken keywordToken, List rhss) : base(rangeToken, lhss) { Contract.Requires(rangeToken != null); Contract.Requires(lhss != null); @@ -61,7 +60,6 @@ public AssignOrReturnStmt(RangeToken rangeToken, List lhss, ExprRhs Rhs = rhs; Rhss = rhss; KeywordToken = keywordToken; - Proof = proof; } public override IEnumerable PreResolveSubExpressions { @@ -193,8 +191,6 @@ public override void Resolve(ModuleResolver resolver, ResolutionContext resoluti return; } - ModuleResolver.ResolveByProof(resolver, Proof, resolutionContext); - Expression lhsExtract = null; if (expectExtract) { if (resolutionContext.CodeContext is Method caller && caller.Outs.Count == 0 && KeywordToken == null) { @@ -290,7 +286,7 @@ private void DesugarElephantStatement(bool expectExtract, Expression lhsExtract, } } // " temp, ... := MethodOrExpression, ...;" - UpdateStmt up = new UpdateStmt(RangeToken, lhss2, rhss2, Proof); + var up = new UpdateStmt(RangeToken, lhss2, rhss2); if (expectExtract) { up.OriginalInitialLhs = Lhss.Count == 0 ? null : Lhss[0]; } @@ -306,7 +302,7 @@ private void DesugarElephantStatement(bool expectExtract, Expression lhsExtract, } else if (token.val == "assume") { ss = new AssumeStmt(new RangeToken(token, EndToken), notFailureExpr, SystemModuleManager.AxiomAttribute(KeywordToken.Attrs)); } else if (token.val == "assert") { - ss = new AssertStmt(new RangeToken(token, EndToken), notFailureExpr, null, null, KeywordToken.Attrs); + ss = new AssertStmt(new RangeToken(token, EndToken), notFailureExpr, null, KeywordToken.Attrs); } else { Contract.Assert(false, $"Invalid token in :- statement: {token.val}"); } diff --git a/Source/DafnyCore/AST/Statements/Assignment/UpdateStmt.cs b/Source/DafnyCore/AST/Statements/Assignment/UpdateStmt.cs index 0b61df6ab30..cc3fe0507f1 100644 --- a/Source/DafnyCore/AST/Statements/Assignment/UpdateStmt.cs +++ b/Source/DafnyCore/AST/Statements/Assignment/UpdateStmt.cs @@ -8,7 +8,6 @@ public class UpdateStmt : ConcreteUpdateStatement, ICloneable, ICanR public readonly List Rhss; public readonly bool CanMutateKnownState; public Expression OriginalInitialLhs = null; - public readonly BlockStmt Proof; public override IToken Tok { get { @@ -23,7 +22,7 @@ public override IToken Tok { } [FilledInDuringResolution] public List ResolvedStatements; - public override IEnumerable SubStatements => Children.OfType().Concat(Proof != null ? Proof.SubStatements : new List()); + public override IEnumerable SubStatements => Children.OfType(); public override IEnumerable NonSpecificationSubExpressions => ResolvedStatements == null ? Rhss.SelectMany(r => r.NonSpecificationSubExpressions) : Enumerable.Empty(); @@ -46,29 +45,26 @@ public UpdateStmt Clone(Cloner cloner) { public UpdateStmt(Cloner cloner, UpdateStmt original) : base(cloner, original) { Rhss = original.Rhss.Select(cloner.CloneRHS).ToList(); CanMutateKnownState = original.CanMutateKnownState; - Proof = cloner.CloneBlockStmt(original.Proof); if (cloner.CloneResolvedFields) { ResolvedStatements = original.ResolvedStatements.Select(stmt => cloner.CloneStmt(stmt, false)).ToList(); } } - public UpdateStmt(RangeToken rangeToken, List lhss, List rhss, BlockStmt proof = null) + public UpdateStmt(RangeToken rangeToken, List lhss, List rhss) : base(rangeToken, lhss) { Contract.Requires(cce.NonNullElements(lhss)); Contract.Requires(cce.NonNullElements(rhss)); Contract.Requires(lhss.Count != 0 || rhss.Count == 1); Rhss = rhss; CanMutateKnownState = false; - Proof = proof; } - public UpdateStmt(RangeToken rangeToken, List lhss, List rhss, bool mutate, BlockStmt proof = null) + public UpdateStmt(RangeToken rangeToken, List lhss, List rhss, bool mutate) : base(rangeToken, lhss) { Contract.Requires(cce.NonNullElements(lhss)); Contract.Requires(cce.NonNullElements(rhss)); Contract.Requires(lhss.Count != 0 || rhss.Count == 1); Rhss = rhss; CanMutateKnownState = mutate; - Proof = proof; } public override IEnumerable PreResolveSubExpressions { @@ -127,9 +123,6 @@ public override void Resolve(ModuleResolver resolver, ResolutionContext resoluti resolver.ResolveAttributes(rhs, resolutionContext); } - // resolve proof - ModuleResolver.ResolveByProof(resolver, Proof, resolutionContext); - // figure out what kind of UpdateStmt this is if (firstEffectfulRhs == null) { if (Lhss.Count == 0) { @@ -185,7 +178,7 @@ public override void Resolve(ModuleResolver resolver, ResolutionContext resoluti foreach (var ll in Lhss) { resolvedLhss.Add(ll.Resolved); } - CallStmt a = new CallStmt(RangeToken, resolvedLhss, methodCallInfo.Callee, methodCallInfo.ActualParameters, methodCallInfo.Tok, Proof); + CallStmt a = new CallStmt(RangeToken, resolvedLhss, methodCallInfo.Callee, methodCallInfo.ActualParameters, methodCallInfo.Tok); a.OriginalInitialLhs = OriginalInitialLhs; ResolvedStatements.Add(a); } diff --git a/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs b/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs index 80b2480ade3..783028c923e 100644 --- a/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs +++ b/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; + namespace Microsoft.Dafny; public class BlockByProofStmt : Statement, ICanResolveNewAndOld { @@ -9,11 +11,35 @@ public BlockByProofStmt(RangeToken range, BlockStmt proof, Statement body) : bas public Statement Body { get; } public BlockStmt Proof { get; } + public override IEnumerable SubStatements => new[] { Body, Proof }; + public override void GenResolve(INewOrOldResolver resolver, ResolutionContext resolutionContext) { resolver.ResolveStatement(Body, resolutionContext); - resolver.ResolveBlockStatement(Proof, resolutionContext with { + ResolveByProof(resolver, Proof, resolutionContext with { CodeContext = new CodeContextWrapper(resolutionContext.CodeContext, true) }); base.GenResolve(resolver, resolutionContext); } + + // CheckLocalityUpdates + + // GhostInterestVisitor + // if (s.Proof != null) { + // Visit(s.Proof, true, "a call-by body"); + // } + + internal static void ResolveByProof(INewOrOldResolver resolver, BlockStmt proof, ResolutionContext resolutionContext) { + if (proof == null) { + return; + } + + // clear the labels for the duration of checking the proof body, because break statements are not allowed to leave the proof body + var prevLblStmts = resolver.EnclosingStatementLabels; + var prevLoopStack = resolver.LoopStack; + resolver.EnclosingStatementLabels = new Scope(resolver.Options); + resolver.LoopStack = new List(); + resolver.ResolveStatement(proof, resolutionContext); + resolver.EnclosingStatementLabels = prevLblStmts; + resolver.LoopStack = prevLoopStack; + } } \ No newline at end of file diff --git a/Source/DafnyCore/AST/Statements/Methods/CallStmt.cs b/Source/DafnyCore/AST/Statements/Methods/CallStmt.cs index 22b17ce7c8a..ef7e40c2afa 100644 --- a/Source/DafnyCore/AST/Statements/Methods/CallStmt.cs +++ b/Source/DafnyCore/AST/Statements/Methods/CallStmt.cs @@ -29,12 +29,11 @@ public override IEnumerable GetAssignedLocals() { public readonly ActualBindings Bindings; public List Args => Bindings.Arguments; public Expression OriginalInitialLhs = null; - public readonly BlockStmt Proof; - public Expression Receiver { get { return MethodSelect.Obj; } } - public Method Method { get { return (Method)MethodSelect.Member; } } + public Expression Receiver => MethodSelect.Obj; + public Method Method => (Method)MethodSelect.Member; - public CallStmt(RangeToken rangeToken, List lhs, MemberSelectExpr memSel, List args, IToken overrideToken = null, BlockStmt proof = null) + public CallStmt(RangeToken rangeToken, List lhs, MemberSelectExpr memSel, List args, IToken overrideToken = null) : base(rangeToken) { Contract.Requires(rangeToken != null); Contract.Requires(cce.NonNullElements(lhs)); @@ -46,7 +45,6 @@ public CallStmt(RangeToken rangeToken, List lhs, MemberSelectExpr me this.MethodSelect = memSel; this.overrideToken = overrideToken; this.Bindings = new ActualBindings(args); - Proof = proof; } public CallStmt Clone(Cloner cloner) { @@ -58,15 +56,14 @@ public CallStmt(Cloner cloner, CallStmt original) : base(cloner, original) { Lhs = original.Lhs.Select(cloner.CloneExpr).ToList(); Bindings = new ActualBindings(cloner, original.Bindings); overrideToken = original.overrideToken; - Proof = cloner.CloneBlockStmt(original.Proof); } /// /// This constructor is intended to be used when constructing a resolved CallStmt. The "args" are expected /// to be already resolved, and are all given positionally. /// - public CallStmt(RangeToken rangeToken, List lhs, MemberSelectExpr memSel, List args, BlockStmt proof = null) - : this(rangeToken, lhs, memSel, args.ConvertAll(e => new ActualBinding(null, e)), proof: proof) { + public CallStmt(RangeToken rangeToken, List lhs, MemberSelectExpr memSel, List args) + : this(rangeToken, lhs, memSel, args.ConvertAll(e => new ActualBinding(null, e))) { Bindings.AcceptArgumentExpressionsAsExactParameterList(); } diff --git a/Source/DafnyCore/AST/Statements/Verification/AssertStmt.cs b/Source/DafnyCore/AST/Statements/Verification/AssertStmt.cs index 306f1d4815b..db48548e9e9 100644 --- a/Source/DafnyCore/AST/Statements/Verification/AssertStmt.cs +++ b/Source/DafnyCore/AST/Statements/Verification/AssertStmt.cs @@ -5,7 +5,6 @@ namespace Microsoft.Dafny; public class AssertStmt : PredicateStmt, ICloneable, ICanFormat { - public readonly BlockStmt Proof; public readonly AssertLabel Label; public AssertStmt Clone(Cloner cloner) { @@ -13,7 +12,6 @@ public AssertStmt Clone(Cloner cloner) { } public AssertStmt(Cloner cloner, AssertStmt original) : base(cloner, original) { - Proof = cloner.CloneBlockStmt(original.Proof); Label = original.Label == null ? null : new AssertLabel(cloner.Tok(original.Label.Tok), original.Label.Name); } @@ -22,26 +20,18 @@ public static AssertStmt CreateErrorAssert(INode node, string message, Expressio errorMessage.Type = new SeqType(Type.Char); var attr = new Attributes("error", new List { errorMessage }, null); guard ??= Expression.CreateBoolLiteral(node.Tok, false); - var assertFalse = new AssertStmt(node.RangeToken, guard, null, null, attr); + var assertFalse = new AssertStmt(node.RangeToken, guard, null, attr); assertFalse.IsGhost = true; return assertFalse; } - public AssertStmt(RangeToken rangeToken, Expression expr, BlockStmt/*?*/ proof, AssertLabel/*?*/ label, Attributes attrs) + public AssertStmt(RangeToken rangeToken, Expression expr, AssertLabel/*?*/ label, Attributes attrs) : base(rangeToken, expr, attrs) { Contract.Requires(rangeToken != null); Contract.Requires(expr != null); - Proof = proof; Label = label; } - public override IEnumerable SubStatements { - get { - if (Proof != null) { - yield return Proof; - } - } - } public void AddCustomizedErrorMessage(IToken tok, string s) { var args = new List() { new StringLiteralExpr(tok, s, true) }; IToken openBrace = tok; @@ -88,8 +78,6 @@ public override void GenResolve(INewOrOldResolver resolver, ResolutionContext co } base.GenResolve(resolver, context); - - ModuleResolver.ResolveByProof(resolver, Proof, context); } public bool HasAssertOnlyAttribute(out AssertOnlyKind assertOnlyKind) { diff --git a/Source/DafnyCore/AST/Substituter.cs b/Source/DafnyCore/AST/Substituter.cs index 4af3641b4f3..9db507cc3ae 100644 --- a/Source/DafnyCore/AST/Substituter.cs +++ b/Source/DafnyCore/AST/Substituter.cs @@ -766,7 +766,7 @@ protected virtual Statement SubstStmt(Statement stmt) { return null; } else if (stmt is AssertStmt) { var s = (AssertStmt)stmt; - r = new AssertStmt(s.RangeToken, Substitute(s.Expr), SubstBlockStmt(s.Proof), s.Label, SubstAttributes(s.Attributes)); + r = new AssertStmt(s.RangeToken, Substitute(s.Expr), s.Label, SubstAttributes(s.Attributes)); } else if (stmt is ExpectStmt) { var s = (ExpectStmt)stmt; r = new ExpectStmt(s.RangeToken, Substitute(s.Expr), Substitute(s.Message), SubstAttributes(s.Attributes)); diff --git a/Source/DafnyCore/Resolver/GhostInterestVisitor.cs b/Source/DafnyCore/Resolver/GhostInterestVisitor.cs index 149e330a040..c50a47983af 100644 --- a/Source/DafnyCore/Resolver/GhostInterestVisitor.cs +++ b/Source/DafnyCore/Resolver/GhostInterestVisitor.cs @@ -74,23 +74,17 @@ public void Visit(Statement stmt, bool mustBeErasable, [CanBeNull] string proofC Contract.Assume(!codeContext.IsGhost || mustBeErasable); // (this is really a precondition) CodeContext.IsGhost ==> mustBeErasable Contract.Assume(mustBeErasable || proofContext == null); // (this is really a precondition) !mustBeErasable ==> proofContext == null - if (stmt is AssertStmt || stmt is AssumeStmt) { + if (stmt is AssertStmt or AssumeStmt) { stmt.IsGhost = true; - var assertStmt = stmt as AssertStmt; - if (assertStmt != null && assertStmt.Proof != null) { - Visit(assertStmt.Proof, true, "an assert-by body"); - } - - } else if (stmt is ExpectStmt) { - stmt.IsGhost = false; - var s = (ExpectStmt)stmt; + } else if (stmt is ExpectStmt expectStmt) { + expectStmt.IsGhost = false; if (mustBeErasable) { - Error(ErrorId.r_expect_statement_is_not_ghost, stmt, "expect statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); + Error(ErrorId.r_expect_statement_is_not_ghost, expectStmt, "expect statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); } else { - ExpressionTester.CheckIsCompilable(resolver, reporter, s.Expr, codeContext); + ExpressionTester.CheckIsCompilable(resolver, reporter, expectStmt.Expr, codeContext); // If not provided, the message is populated with a default value in resolution - Contract.Assert(s.Message != null); - ExpressionTester.CheckIsCompilable(resolver, reporter, s.Message, codeContext); + Contract.Assert(expectStmt.Message != null); + ExpressionTester.CheckIsCompilable(resolver, reporter, expectStmt.Message, codeContext); } } else if (stmt is PrintStmt) { @@ -145,10 +139,6 @@ public void Visit(Statement stmt, bool mustBeErasable, [CanBeNull] string proofC var s = (UpdateStmt)stmt; s.ResolvedStatements.ForEach(ss => Visit(ss, mustBeErasable, proofContext)); s.IsGhost = s.ResolvedStatements.All(ss => ss.IsGhost); - if (s.Proof != null) { - Visit(s.Proof, true, "a call-by body"); - } - } else if (stmt is AssignOrReturnStmt) { var s = (AssignOrReturnStmt)stmt; s.ResolvedStatements.ForEach(ss => Visit(ss, mustBeErasable, proofContext)); diff --git a/Source/DafnyCore/Resolver/ModuleResolver.cs b/Source/DafnyCore/Resolver/ModuleResolver.cs index 2e1ba07fc05..6e1a54782a9 100644 --- a/Source/DafnyCore/Resolver/ModuleResolver.cs +++ b/Source/DafnyCore/Resolver/ModuleResolver.cs @@ -1964,7 +1964,7 @@ void CheckExpression(Expression expr, ModuleResolver resolver, ICodeContext code Contract.Requires(expr != null); Contract.Requires(resolver != null); Contract.Requires(codeContext != null); - var v = new CheckExpression_Visitor(resolver, codeContext); + var v = new CheckExpressionVisitor(resolver, codeContext); v.Visit(expr); } /// @@ -1977,12 +1977,12 @@ void CheckExpression(Statement stmt, ModuleResolver resolver, ICodeContext codeC Contract.Requires(stmt != null); Contract.Requires(resolver != null); Contract.Requires(codeContext != null); - var v = new CheckExpression_Visitor(resolver, codeContext); + var v = new CheckExpressionVisitor(resolver, codeContext); v.Visit(stmt); } - class CheckExpression_Visitor : ResolverBottomUpVisitor { + class CheckExpressionVisitor : ResolverBottomUpVisitor { readonly ICodeContext CodeContext; - public CheckExpression_Visitor(ModuleResolver resolver, ICodeContext codeContext) + public CheckExpressionVisitor(ModuleResolver resolver, ICodeContext codeContext) : base(resolver) { Contract.Requires(resolver != null); Contract.Requires(codeContext != null); @@ -2007,8 +2007,6 @@ protected override void VisitOneStmt(Statement stmt) { foreach (var h in calc.Hints) { resolver.CheckLocalityUpdates(h, new HashSet(), "a hint"); } - } else if (stmt is AssertStmt astmt && astmt.Proof != null) { - resolver.CheckLocalityUpdates(astmt.Proof, new HashSet(), "an assert-by body"); } else if (stmt is ForallStmt forall && forall.Body != null) { resolver.CheckLocalityUpdates(forall.Body, new HashSet(), "a forall statement"); } @@ -3216,7 +3214,7 @@ void CheckIsFunction([CanBeNull] MemberDecl memberDecl, bool allowMethod) { } /// - /// Check that "stmt" is a valid statment for the body of an assert-by, forall, + /// Check that "stmt" is a valid statement for the body of an assert-by, forall, /// or calc-hint statement. In particular, check that the local variables assigned in /// the bodies of these statements are declared in the statements, not in some enclosing /// context. @@ -3332,21 +3330,6 @@ internal LetExpr LetVarIn(IToken tok, string name, Type tp, Expression rhs, Expr return LetPatIn(tok, lhs, rhs, body); } - internal static void ResolveByProof(INewOrOldResolver resolver, BlockStmt proof, ResolutionContext resolutionContext) { - if (proof == null) { - return; - } - - // clear the labels for the duration of checking the proof body, because break statements are not allowed to leave the proof body - var prevLblStmts = resolver.EnclosingStatementLabels; - var prevLoopStack = resolver.LoopStack; - resolver.EnclosingStatementLabels = new Scope(resolver.Options); - resolver.LoopStack = new List(); - resolver.ResolveStatement(proof, resolutionContext); - resolver.EnclosingStatementLabels = prevLblStmts; - resolver.LoopStack = prevLoopStack; - } - /// /// If expr.Lhs != null: Desugars "var x: T :- E; F" into "var temp := E; if temp.IsFailure() then temp.PropagateFailure() else var x: T := temp.Extract(); F" /// If expr.Lhs == null: Desugars " :- E; F" into "var temp := E; if temp.IsFailure() then temp.PropagateFailure() else F" diff --git a/Source/DafnyCore/Resolver/PreType/PreTypeResolve.Statements.cs b/Source/DafnyCore/Resolver/PreType/PreTypeResolve.Statements.cs index 8992a33b3d1..3bd7dd2ab1b 100644 --- a/Source/DafnyCore/Resolver/PreType/PreTypeResolve.Statements.cs +++ b/Source/DafnyCore/Resolver/PreType/PreTypeResolve.Statements.cs @@ -646,9 +646,6 @@ private void ResolveUpdateStmt(UpdateStmt update, ResolutionContext resolutionCo ResolveAttributes(rhs, resolutionContext, false); } - // resolve proof - ModuleResolver.ResolveByProof(this, update.Proof, resolutionContext); - // figure out what kind of UpdateStmt this is if (firstEffectfulRhs == null) { if (update.Lhss.Count == 0) { @@ -708,8 +705,7 @@ private void ResolveUpdateStmt(UpdateStmt update, ResolutionContext resolutionCo } else if (ErrorCount == errorCountBeforeCheckingStmt) { // a call statement var resolvedLhss = update.Lhss.ConvertAll(ll => ll.Resolved); - var a = new CallStmt(update.RangeToken, resolvedLhss, methodCallInfo.Callee, methodCallInfo.ActualParameters, - methodCallInfo.Tok, update.Proof); + var a = new CallStmt(update.RangeToken, resolvedLhss, methodCallInfo.Callee, methodCallInfo.ActualParameters, methodCallInfo.Tok); a.OriginalInitialLhs = update.OriginalInitialLhs; update.ResolvedStatements.Add(a); } @@ -935,8 +931,6 @@ private void ResolveAssignOrReturnStmt(AssignOrReturnStmt s, ResolutionContext r return; } - ModuleResolver.ResolveByProof(this, s.Proof, resolutionContext); - Expression lhsExtract = null; if (expectExtract) { if (enclosingMethod.Outs.Count == 0 && s.KeywordToken == null) { @@ -999,7 +993,7 @@ private void ResolveAssignOrReturnStmt(AssignOrReturnStmt s, ResolutionContext r } } // " temp, ... := MethodOrExpression, ...;" - UpdateStmt up = new UpdateStmt(s.RangeToken, lhss2, rhss2, s.Proof); + var up = new UpdateStmt(s.RangeToken, lhss2, rhss2); if (expectExtract) { up.OriginalInitialLhs = s.Lhss.Count == 0 ? null : s.Lhss[0]; } @@ -1015,7 +1009,7 @@ private void ResolveAssignOrReturnStmt(AssignOrReturnStmt s, ResolutionContext r } else if (s.KeywordToken.Token.val == "assume") { ss = new AssumeStmt(new RangeToken(token, s.EndToken), notFailureExpr, SystemModuleManager.AxiomAttribute(s.KeywordToken.Attrs)); } else if (s.KeywordToken.Token.val == "assert") { - ss = new AssertStmt(new RangeToken(token, s.EndToken), notFailureExpr, null, null, s.KeywordToken.Attrs); + ss = new AssertStmt(new RangeToken(token, s.EndToken), notFailureExpr, null, s.KeywordToken.Attrs); } else { Contract.Assert(false, $"Invalid token in :- statement: {token.val}"); } diff --git a/Source/DafnyCore/Rewriters/RefinementTransformer.cs b/Source/DafnyCore/Rewriters/RefinementTransformer.cs index e3d14e3d44e..8342c9a3909 100644 --- a/Source/DafnyCore/Rewriters/RefinementTransformer.cs +++ b/Source/DafnyCore/Rewriters/RefinementTransformer.cs @@ -1071,7 +1071,7 @@ List MergeStmtList(List skeleton, List oldStmt, var e = refinementCloner.CloneExpr(oldAssume.Expr); var attrs = refinementCloner.MergeAttributes(oldAssume.Attributes, skel.Attributes); body.Add(new AssertStmt(new RangeToken(new BoogieGenerator.ForceCheckToken(skel.RangeToken.StartToken), skel.RangeToken.EndToken), - e, skel.Proof, skel.Label, new Attributes("_prependAssertToken", new List(), attrs))); + e, skel.Label, new Attributes("_prependAssertToken", new List(), attrs))); Reporter.Info(MessageSource.RefinementTransformer, c.ConditionEllipsis, "assume->assert: " + Printer.ExprToString(Reporter.Options, e)); i++; j++; } @@ -1217,7 +1217,7 @@ List MergeStmtList(List skeleton, List oldStmt, i++; j++; if (addedAssert != null) { var tok = new BoogieGenerator.ForceCheckToken(addedAssert.RangeToken.StartToken); - body.Add(new AssertStmt(new RangeToken(tok, addedAssert.RangeToken.EndToken), addedAssert, null, null, null)); + body.Add(new AssertStmt(new RangeToken(tok, addedAssert.RangeToken.EndToken), addedAssert, null, null)); } } else { MergeAddStatement(cur, body); @@ -1274,7 +1274,7 @@ List MergeStmtList(List skeleton, List oldStmt, stmtGenerated.Add(nw); var addedAssert = refinementCloner.CloneExpr(s.Expr); var tok = new RangeToken(addedAssert.RangeToken.StartToken, addedAssert.RangeToken.EndToken); - stmtGenerated.Add(new AssertStmt(tok, addedAssert, null, null, null)); + stmtGenerated.Add(new AssertStmt(tok, addedAssert, null, null)); } } if (doMerge) { diff --git a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs index 58ccea0705c..dc5c83716e3 100644 --- a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs +++ b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs @@ -8,7 +8,11 @@ public class BlockByProofStmtVerifier { public static void EmitBoogie(BoogieGenerator generator, BlockByProofStmt block, BoogieStmtListBuilder builder, List locals, BoogieGenerator.ExpressionTranslator etran, ICodeContext codeContext) { var proofBuilder = new BoogieStmtListBuilder(generator, builder.Options, builder.Context); + + //generator.CurrentIdGenerator.Push(); generator.TrStmtList(block.Proof.Body, proofBuilder, locals, etran); + //generator.CurrentIdGenerator.Pop(); + generator.TrStmt(block.Body, proofBuilder, locals, etran); generator.PathAsideBlock(block.Tok, proofBuilder, builder); generator.TrStmt(block.Body, builder.WithContext(builder.Context with { diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs index b713a351e8b..309ce010797 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs @@ -345,19 +345,20 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E } } - if (cs.Proof == null) { - AddCall(builder); - } else { - var proofBuilder = new BoogieStmtListBuilder(this, options, builder.Context); - AddComment(proofBuilder, cs, "call statement proof"); - CurrentIdGenerator.Push(); - TrStmt(cs.Proof, proofBuilder, locals, etran); - CurrentIdGenerator.Pop(); - AddCall(proofBuilder); - PathAsideBlock(cs.Tok, proofBuilder, builder); - var freeCall = AddCall(builder); - freeCall.IsFree = true; + AddReferencedMember(callee); + var call = Call(builder.Context, tok, MethodName(callee, isCoCall ? MethodTranslationKind.CoCall : MethodTranslationKind.Call), ins, new List()); + proofDependencies?.AddProofDependencyId(call, tok, new CallDependency(cs)); + if ( + (assertionOnlyFilter != null && !assertionOnlyFilter(tok)) || + (module != currentModule && RefinementToken.IsInherited(tok, currentModule) && (codeContext == null || !codeContext.MustReverify))) { + // The call statement is inherited, so the refined module already checked that the precondition holds. Note, + // preconditions are not allowed to be strengthened, except if they use a predicate whose body has been strengthened. + // But if the callee sits in a different module, then any predicate it uses will be treated as opaque (that is, + // uninterpreted) anyway, so the refined module will have checked the call precondition for all possible definitions + // of the predicate. + call.IsFree = true; } + builder.Add(call); // Unbox results as needed for (int i = 0; i < Lhss.Count; i++) { @@ -376,25 +377,5 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E builder.Add(cmd); } } - - return; - - CallCmd AddCall(BoogieStmtListBuilder callBuilder) { - AddReferencedMember(callee); - var call = Call(callBuilder.Context, tok, MethodName(callee, isCoCall ? MethodTranslationKind.CoCall : MethodTranslationKind.Call), ins, new List()); - proofDependencies?.AddProofDependencyId(call, tok, new CallDependency(cs)); - if ( - (assertionOnlyFilter != null && !assertionOnlyFilter(tok)) || - (module != currentModule && RefinementToken.IsInherited(tok, currentModule) && (codeContext == null || !codeContext.MustReverify))) { - // The call statement is inherited, so the refined module already checked that the precondition holds. Note, - // preconditions are not allowed to be strengthened, except if they use a predicate whose body has been strengthened. - // But if the callee sits in a different module, then any predicate it uses will be treated as opaque (that is, - // uninterpreted) anyway, so the refined module will have checked the call precondition for all possible definitions - // of the predicate. - call.IsFree = true; - } - callBuilder.Add(call); - return call; - } } } \ No newline at end of file diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs index ed227805b25..4737723e297 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs @@ -77,14 +77,15 @@ public void TrAssertStmt(PredicateStmt stmt, BoogieStmtListBuilder builder, List BoogieStmtListBuilder proofBuilder = null; var assertStmt = stmt as AssertStmt; if (assertStmt != null) { - if (assertStmt.Proof != null) { - hiddenProof = true; - proofBuilder = new BoogieStmtListBuilder(this, options, builder.Context); - AddComment(proofBuilder, stmt, "assert statement proof"); - CurrentIdGenerator.Push(); - TrStmt(((AssertStmt)stmt).Proof, proofBuilder, locals, etran); - CurrentIdGenerator.Pop(); - } else if (assertStmt.Label != null) { + // if (assertStmt.Proof != null) { + // hiddenProof = true; + // proofBuilder = new BoogieStmtListBuilder(this, options, builder.Context); + // AddComment(proofBuilder, stmt, "assert statement proof"); + // CurrentIdGenerator.Push(); + // TrStmt(((AssertStmt)stmt).Proof, proofBuilder, locals, etran); + // CurrentIdGenerator.Pop(); + // } else + if (assertStmt.Label != null) { hiddenProof = true; proofBuilder = new BoogieStmtListBuilder(this, options, builder.Context); AddComment(proofBuilder, stmt, "assert statement proof"); From 2b17edc18fa6abacab94999a1b633cc7c2f89236 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 20 Sep 2024 16:22:18 +0200 Subject: [PATCH 09/47] Update parser --- .../Assignment/AssignOrReturnStmt.cs | 2 +- .../AST/Statements/BlockByProofStmt.cs | 6 ++--- Source/DafnyCore/Dafny.atg | 24 ++++++++++++++----- Source/DafnyCore/Verifier/BoogieGenerator.cs | 2 +- .../Statements/BlockByProofStmtVerifier.cs | 4 ++-- .../BoogieGenerator.TrAssignment.cs | 4 ++-- .../BoogieGenerator.TrForallStmt.cs | 8 +++---- .../Statements/BoogieGenerator.TrLoop.cs | 11 ++++----- .../Language/SyntaxTreeVisitor.cs | 1 - Source/DafnyTestGeneration/TestGenerator.cs | 2 +- 10 files changed, 37 insertions(+), 27 deletions(-) diff --git a/Source/DafnyCore/AST/Statements/Assignment/AssignOrReturnStmt.cs b/Source/DafnyCore/AST/Statements/Assignment/AssignOrReturnStmt.cs index 42c571a9c2a..17574d3dc47 100644 --- a/Source/DafnyCore/AST/Statements/Assignment/AssignOrReturnStmt.cs +++ b/Source/DafnyCore/AST/Statements/Assignment/AssignOrReturnStmt.cs @@ -23,7 +23,7 @@ public override IToken Tok { } public override IEnumerable Children => ResolvedStatements; - + public override IEnumerable PreResolveSubStatements => Enumerable.Empty(); [ContractInvariantMethod] diff --git a/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs b/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs index 783028c923e..fb96706b4d0 100644 --- a/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs +++ b/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs @@ -20,14 +20,14 @@ public override void GenResolve(INewOrOldResolver resolver, ResolutionContext re }); base.GenResolve(resolver, resolutionContext); } - + // CheckLocalityUpdates - + // GhostInterestVisitor // if (s.Proof != null) { // Visit(s.Proof, true, "a call-by body"); // } - + internal static void ResolveByProof(INewOrOldResolver resolver, BlockStmt proof, ResolutionContext resolutionContext) { if (proof == null) { return; diff --git a/Source/DafnyCore/Dafny.atg b/Source/DafnyCore/Dafny.atg index 2ac3fb6598f..50d4c25156e 100644 --- a/Source/DafnyCore/Dafny.atg +++ b/Source/DafnyCore/Dafny.atg @@ -2395,12 +2395,18 @@ UpdateStmt if (suchThat != null) { s = new AssignSuchThatStmt(rangeToken, lhss, suchThat, suchThatAssume, null); } else if (exceptionRhs != null) { - s = new AssignOrReturnStmt(rangeToken, lhss, exceptionRhs, keywordToken, rhss, proof); + s = new AssignOrReturnStmt(rangeToken, lhss, exceptionRhs, keywordToken, rhss); + if (proof != null) { + s = new BlockByProofStmt(rangeToken, proof, s); + } } else { if (lhss.Count == 0 && rhss.Count == 0) { s = new BlockStmt(rangeToken, new List()); // error, give empty statement } else { - s = new UpdateStmt(rangeToken, lhss, rhss, proof); + s = new UpdateStmt(rangeToken, lhss, rhss); + if (proof != null) { + s = new BlockByProofStmt(rangeToken, proof, s); + } } } .) @@ -2557,13 +2563,16 @@ VarDeclStatement<.out Statement/*!*/ s.> if (suchThat != null) { update = new AssignSuchThatStmt(updateRangeToken, lhsExprs, suchThat, suchThatAssume, attrs); } else if (exceptionRhs != null) { - update = new AssignOrReturnStmt(updateRangeToken, lhsExprs, exceptionRhs, keywordToken, rhss, proof); + update = new AssignOrReturnStmt(updateRangeToken, lhsExprs, exceptionRhs, keywordToken, rhss); } else if (rhss.Count == 0) { update = null; } else { - update = new UpdateStmt(updateRangeToken, lhsExprs, rhss, proof); + update = new UpdateStmt(updateRangeToken, lhsExprs, rhss); } s = new VarDeclStmt(rangeToken, lhss, update); + if (proof != null) { + s = new BlockByProofStmt(s.RangeToken, proof, s); + } .) | (. CasePattern pat; Expression e = dummyExpr; @@ -2980,10 +2989,13 @@ AssertStmt ";" ) (. if (dotdotdot != null) { - s = new AssertStmt(new RangeToken(startToken, t), new LiteralExpr(x, true), null, null, attrs); + s = new AssertStmt(new RangeToken(startToken, t), new LiteralExpr(x, true), null, attrs); s = new SkeletonStatement(s, dotdotdot, null); } else { - s = new AssertStmt(new RangeToken(startToken, t), e, proof, lbl == null ? null : new AssertLabel(lbl, lbl.val), attrs); + s = new AssertStmt(new RangeToken(startToken, t), e, lbl == null ? null : new AssertLabel(lbl, lbl.val), attrs); + if (proof != null) { + s = new BlockByProofStmt(s.RangeToken, proof, s); + } } .) . diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index da52a2e953e..962a7f5bafd 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -2845,7 +2845,7 @@ private static void AddSmtOptionAttribute(Bpl.NamedDeclaration targetDecl, strin targetDecl.Attributes = new QKeyValue(targetDecl.tok, "smt_option", new List() { name, value }, targetDecl.Attributes); } - private static CallCmd Call(BodyTranslationContext context, IToken tok, string methodName, + private static CallCmd Call(BodyTranslationContext context, IToken tok, string methodName, List ins, List outs) { Contract.Requires(tok != null); Contract.Requires(methodName != null); diff --git a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs index dc5c83716e3..0b053f17177 100644 --- a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs +++ b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs @@ -8,11 +8,11 @@ public class BlockByProofStmtVerifier { public static void EmitBoogie(BoogieGenerator generator, BlockByProofStmt block, BoogieStmtListBuilder builder, List locals, BoogieGenerator.ExpressionTranslator etran, ICodeContext codeContext) { var proofBuilder = new BoogieStmtListBuilder(generator, builder.Options, builder.Context); - + //generator.CurrentIdGenerator.Push(); generator.TrStmtList(block.Proof.Body, proofBuilder, locals, etran); //generator.CurrentIdGenerator.Pop(); - + generator.TrStmt(block.Body, proofBuilder, locals, etran); generator.PathAsideBlock(block.Tok, proofBuilder, builder); generator.TrStmt(block.Body, builder.WithContext(builder.Context with { diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs index 6188d9a26c6..1d216b7d55f 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs @@ -8,7 +8,7 @@ namespace Microsoft.Dafny; public partial class BoogieGenerator { - + /// /// "lhs" is expected to be a resolved form of an expression, i.e., not a concrete-syntax expression. @@ -34,7 +34,7 @@ void TrAssignment(Statement stmt, Expression lhs, AssignmentRhs rhs, ProcessRhss(lhsBuilder, bLhss, lhss, rhss, builder, locals, etran, stmt); builder.AddCaptureState(stmt); } - + void ProcessRhss(List lhsBuilder, List bLhss, List lhss, List rhss, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, Statement stmt) { diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs index da0abd4c8bf..17613aea422 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs @@ -8,7 +8,7 @@ namespace Microsoft.Dafny; public partial class BoogieGenerator { - + private void TrForallStmt(BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, ForallStmt forallStmt) { @@ -68,7 +68,7 @@ private void TrForallStmt(BoogieStmtListBuilder builder, List locals, CurrentIdGenerator.Pop(); this.fuelContext = FuelSetting.PopFuelContext(); } - + void TrForallStmtCall(IToken tok, List boundVars, List bounds, Expression range, ExpressionConverter additionalRange, List forallExpressions, CallStmt s0, @@ -208,7 +208,7 @@ void TrForallStmtCall(IToken tok, List boundVars, List bo exporter.Add(TrAssumeCmd(tok, qq)); } } - + void TrForallAssign(ForallStmt s, AssignStmt s0, BoogieStmtListBuilder definedness, BoogieStmtListBuilder updater, List locals, ExpressionTranslator etran) { // The statement: @@ -455,7 +455,7 @@ IEnumerable TransitiveSubstatements(Statement s) { yield return ss; } } - + void TrForallProof(ForallStmt forallStmt, BoogieStmtListBuilder definedness, BoogieStmtListBuilder exporter, List locals, ExpressionTranslator etran) { // Translate: diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs index 5634dca404a..8feffecb48c 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs @@ -10,10 +10,9 @@ namespace Microsoft.Dafny; public partial class BoogieGenerator { - + private void TrAlternativeLoopStmt(AlternativeLoopStmt stmt, BoogieStmtListBuilder builder, List locals, - ExpressionTranslator etran) - { + ExpressionTranslator etran) { AddComment(builder, stmt, "alternative loop statement"); var tru = Expression.CreateBoolLiteral(stmt.Tok, true); TrLoop(stmt, tru, @@ -30,7 +29,7 @@ private void TrAlternativeLoopStmt(AlternativeLoopStmt stmt, BoogieStmtListBuild }, builder, locals, etran); } - + private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { Contract.Requires(stmt != null); Contract.Requires(builder != null); @@ -147,7 +146,7 @@ private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { Contract.Requires(stmt != null); Contract.Requires(builder != null); @@ -169,7 +168,7 @@ private void TrWhileStmt(WhileStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, Bpl.Expr freeInvariant = null, bool includeTerminationCheck = true) { diff --git a/Source/DafnyLanguageServer/Language/SyntaxTreeVisitor.cs b/Source/DafnyLanguageServer/Language/SyntaxTreeVisitor.cs index 7a215cf552d..38000562ddb 100644 --- a/Source/DafnyLanguageServer/Language/SyntaxTreeVisitor.cs +++ b/Source/DafnyLanguageServer/Language/SyntaxTreeVisitor.cs @@ -347,7 +347,6 @@ public virtual void Visit(UpdateStmt updateStatement) { public virtual void Visit(AssertStmt assertStatement) { VisitNullableAttributes(assertStatement.Attributes); Visit(assertStatement.Expr); - VisitNullableStatement(assertStatement.Proof); } public virtual void Visit(ReturnStmt returnStatement) { diff --git a/Source/DafnyTestGeneration/TestGenerator.cs b/Source/DafnyTestGeneration/TestGenerator.cs index 94a1f1e5b6e..b33c1f167ca 100644 --- a/Source/DafnyTestGeneration/TestGenerator.cs +++ b/Source/DafnyTestGeneration/TestGenerator.cs @@ -91,7 +91,7 @@ private static void AddVerificationGoalsToEntryPoints(Program program) { foreach (var entryPoint in Utils.AllMemberDeclarationsWithAttribute(program.DefaultModule, TestGenerationOptions.TestEntryAttribute)) { var trivialAssertion = new AssertStmt(entryPoint.RangeToken, - new LiteralExpr(entryPoint.StartToken, true), null, null, null); + new LiteralExpr(entryPoint.StartToken, true), null, null); if (entryPoint is Method method && method.Body != null && method.Body.Body != null) { method.Body.Body.Insert(0, trivialAssertion); } else if (entryPoint is Function function && function.Body != null) { From 0774dc4a6a1f2cc28445143db848e8636395de60 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 20 Sep 2024 16:33:59 +0200 Subject: [PATCH 10/47] Fix a few bugs --- .../AST/Grammar/Printer/Printer.Statement.cs | 15 ++++++++++++- .../AST/Statements/BlockByProofStmt.cs | 21 +++++++++++++++---- .../Resolver/GhostInterestVisitor.cs | 8 +++++++ Source/DafnyCore/Resolver/TailRecursion.cs | 4 ++++ 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs b/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs index 9a829e060bf..c16aeaf7292 100644 --- a/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs +++ b/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs @@ -19,6 +19,10 @@ namespace Microsoft.Dafny { + interface ICanPrint { + void Render(TextWriter wr, Printer printer); + } + public partial class Printer { /// @@ -29,13 +33,22 @@ public partial class Printer { public void PrintStatement(Statement stmt, int indent) { Contract.Requires(stmt != null); - if (stmt.IsGhost && printMode == PrintModes.NoGhostOrIncludes) { return; } + + if (stmt.IsGhost && printMode == PrintModes.NoGhostOrIncludes) { + return; + } + for (LList public interface ICodeContext : IASTVisitorContext { + bool ContainsHide { get; set; } bool IsGhost { get; } List TypeArgs { get; } List Ins { get; } @@ -31,6 +33,11 @@ public CodeContextWrapper(ICodeContext inner, bool isGhostContext) { this.isGhostContext = isGhostContext; } + public bool ContainsHide { + get => inner.ContainsHide; + set => inner.ContainsHide = value; + } + public bool IsGhost => isGhostContext; public List TypeArgs => inner.TypeArgs; public List Ins => inner.Ins; @@ -49,7 +56,6 @@ public static ICodeContext Unwrap(ICodeContext codeContext) { } interface ICodeContainer { - bool ContainsHide { get; set; } } /// @@ -124,6 +130,11 @@ public class NoContext : ICodeContext { public NoContext(ModuleDefinition module) { this.Module = module; } + + public bool ContainsHide { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } bool ICodeContext.IsGhost { get { return true; } } List ICodeContext.TypeArgs { get { return new List(); } } List ICodeContext.Ins { get { return new List(); } } diff --git a/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs b/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs index 02770f8f0b3..2a39a67262e 100644 --- a/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs +++ b/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; @@ -48,8 +49,18 @@ internal static void ResolveByProof(INewOrOldResolver resolver, BlockStmt proof, resolver.LoopStack = prevLoopStack; } - public void Render(TextWriter wr, Printer printer) { - // TODO + public void Render(TextWriter wr, Printer printer, int indent) { + if (Body is AssertStmt assertStmt) { + printer.PrintPredicateStmt(assertStmt, false); + } else if (Body is ConcreteUpdateStatement updateStmt) { + printer.PrintConcreteUpdateStatement(updateStmt, indent, false); + } else if (Body is BlockStmt blockStmt) { + printer.PrintBlockStmt(blockStmt, indent); + } else { + throw new NotImplementedException(); + } + wr.Write(" by "); + printer.PrintBlockStmt(Proof, indent); } public Statement Clone(Cloner cloner) { diff --git a/Source/DafnyCore/AST/Statements/Verification/HideRevealStmt.cs b/Source/DafnyCore/AST/Statements/Verification/HideRevealStmt.cs index f78a07109bd..2cd6e90e723 100644 --- a/Source/DafnyCore/AST/Statements/Verification/HideRevealStmt.cs +++ b/Source/DafnyCore/AST/Statements/Verification/HideRevealStmt.cs @@ -73,7 +73,7 @@ public bool SetIndent(int indentBefore, TokenNewIndentCollector formatter) { } public void Resolve(PreTypeResolver resolver, ResolutionContext resolutionContext) { - ((ICodeContainer)resolutionContext.CodeContext).ContainsHide |= Mode == HideRevealCmd.Modes.Hide; + resolutionContext.CodeContext.ContainsHide |= Mode == HideRevealCmd.Modes.Hide; if (Wildcard) { return; diff --git a/Source/DafnyCore/AST/TypeDeclarations/DatatypeDecl.cs b/Source/DafnyCore/AST/TypeDeclarations/DatatypeDecl.cs index e6cb1c6facc..55ba4e23ec7 100644 --- a/Source/DafnyCore/AST/TypeDeclarations/DatatypeDecl.cs +++ b/Source/DafnyCore/AST/TypeDeclarations/DatatypeDecl.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; @@ -49,6 +50,11 @@ public bool IsRecordType { public TopLevelDecl AsTopLevelDecl => this; public TypeDeclSynonymInfo SynonymInfo { get; set; } + public bool ContainsHide { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + bool ICodeContext.IsGhost { get { return true; } } List ICodeContext.TypeArgs { get { return TypeArgs; } } List ICodeContext.Ins { get { return new List(); } } diff --git a/Source/DafnyCore/AST/TypeDeclarations/NewtypeDecl.cs b/Source/DafnyCore/AST/TypeDeclarations/NewtypeDecl.cs index ba9804f4edd..fb3bd7156dd 100644 --- a/Source/DafnyCore/AST/TypeDeclarations/NewtypeDecl.cs +++ b/Source/DafnyCore/AST/TypeDeclarations/NewtypeDecl.cs @@ -116,6 +116,11 @@ public TypeParameter.EqualitySupportValue EqualitySupport { Expression RedirectingTypeDecl.Witness { get { return Witness; } } VerificationIdGenerator RedirectingTypeDecl.IdGenerator { get { return IdGenerator; } } + public bool ContainsHide { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + bool ICodeContext.IsGhost { get { throw new NotSupportedException(); } // if .IsGhost is needed, the object should always be wrapped in an CodeContextWrapper } diff --git a/Source/DafnyCore/AST/TypeDeclarations/TypeSynonymDeclBase.cs b/Source/DafnyCore/AST/TypeDeclarations/TypeSynonymDeclBase.cs index 3466a7113f6..b7b2ecc40ce 100644 --- a/Source/DafnyCore/AST/TypeDeclarations/TypeSynonymDeclBase.cs +++ b/Source/DafnyCore/AST/TypeDeclarations/TypeSynonymDeclBase.cs @@ -77,11 +77,14 @@ bool RedirectingTypeDecl.ConstraintIsCompilable { Expression RedirectingTypeDecl.Witness { get { return null; } } VerificationIdGenerator RedirectingTypeDecl.IdGenerator { get { return IdGenerator; } } - bool ICodeContext.IsGhost { - get { throw new NotSupportedException(); } // if .IsGhost is needed, the object should always be wrapped in an CodeContextWrapper + public bool ContainsHide { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); } - List ICodeContext.TypeArgs { get { return TypeArgs; } } - List ICodeContext.Ins { get { return new List(); } } + + bool ICodeContext.IsGhost => throw new NotSupportedException(); // if .IsGhost is needed, the object should always be wrapped in an CodeContextWrapper + List ICodeContext.TypeArgs => TypeArgs; + List ICodeContext.Ins => new(); ModuleDefinition IASTVisitorContext.EnclosingModule { get { return EnclosingModuleDefinition; } } bool ICodeContext.MustReverify { get { return false; } } bool ICodeContext.AllowsNontermination { get { return false; } } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs b/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs index cf365d79ac9..b029b543c58 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs @@ -33,8 +33,6 @@ namespace Microsoft.Dafny { public record BodyTranslationContext(bool ContainsHide, int ScopeDepth = 0, bool ReturnPosition = true, AssertMode AssertMode = AssertMode.Keep); public partial class BoogieGenerator { - - /// /// Tries to split the expression into tactical conjuncts (if "position") or disjuncts (if "!position"). /// If a (necessarily boolean) function call appears as a top-level conjunct, then inline the function From 0391450bbf25f49fac12f25d9eddf9327f6d16d6 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 20 Sep 2024 17:21:18 +0200 Subject: [PATCH 13/47] Rename of UpdateStmt --- Source/DafnyCore/AST/AstVisitor.cs | 2 +- Source/DafnyCore/AST/Expressions/StmtExpr.cs | 2 +- .../AST/Grammar/Printer/Printer.Statement.cs | 18 +++---- .../AST/Grammar/TokenNewIndentCollector.cs | 4 +- .../Assignment/AssignOrReturnStmt.cs | 10 ++-- .../{UpdateStmt.cs => AssignStatement.cs} | 18 +++---- .../Assignment/AssignSuchThatStmt.cs | 2 +- ...tatement.cs => ConcreteAssignStatement.cs} | 6 +-- .../{AssignStmt.cs => SingleAssignStmt.cs} | 10 ++-- .../AST/Statements/Assignment/VarDeclStmt.cs | 12 ++--- .../AST/Statements/BlockByProofStmt.cs | 2 +- .../AST/Statements/ControlFlow/ForallStmt.cs | 4 +- .../AST/Statements/Methods/ProduceStmt.cs | 4 +- Source/DafnyCore/AST/Statements/Statement.cs | 4 +- Source/DafnyCore/AST/Substituter.cs | 18 +++---- .../Backends/Dafny/DafnyCodeGenerator.cs | 2 +- .../Backends/Java/JavaCodeGenerator.cs | 6 +-- .../SinglePassCodeGenerator.Statement.cs | 26 +++++----- .../SinglePassCodeGenerator.cs | 8 +-- .../CounterExampleGeneration/PartialState.cs | 2 +- Source/DafnyCore/Dafny.atg | 22 ++++---- Source/DafnyCore/Resolver/CallGraphBuilder.cs | 2 +- .../CheckDividedConstructorInit_Visitor.cs | 4 +- .../CheckTypeCharacteristics_Visitor.cs | 4 +- .../Resolver/GhostInterestVisitor.cs | 44 ++++++++-------- .../HigherOrderHeapAllocationChecker.cs | 2 +- ...erOrderHeapAllocationCheckerConstructor.cs | 2 +- Source/DafnyCore/Resolver/ModuleResolver.cs | 6 +-- .../NameResolutionAndTypeInference.cs | 26 +++++----- .../Resolver/ObjectConstructorChecker.cs | 2 +- .../PreType/PreTypeResolve.Expressions.cs | 2 +- .../PreType/PreTypeResolve.Statements.cs | 50 +++++++++---------- .../Resolver/PreType/PreTypeToType.cs | 2 +- .../Resolver/PreType/TypeRefinementVisitor.cs | 2 +- Source/DafnyCore/Resolver/TailRecursion.cs | 12 ++--- .../Rewriters/AutoContractsRewriter.cs | 12 ++--- Source/DafnyCore/Rewriters/ExpectContracts.cs | 4 +- .../DafnyCore/Rewriters/ForallStmtRewriter.cs | 2 +- .../Rewriters/RefinementTransformer.cs | 48 +++++++++--------- .../Rewriters/RunAllTestsMainMethod.cs | 2 +- .../BoogieGenerator.DefiniteAssignment.cs | 2 +- .../BoogieGenerator.TrForallStmt.cs | 4 +- .../Statements/BoogieGenerator.TrLoop.cs | 2 +- .../Statements/BoogieGenerator.TrStatement.cs | 22 ++++---- .../Language/GhostStateDiagnosticCollector.cs | 4 +- ...licitFailingAssertionCodeActionProvider.cs | 2 +- .../Language/SyntaxTreeVisitor.cs | 8 +-- Source/DafnyServer/SuperLegacySymbolTable.cs | 14 +++--- .../ShortCircuitRemoval.cs | 8 +-- .../FunctionCallToMethodCallRewriter.cs | 6 +-- .../Inlining/RemoveShortCircuitRewriter.cs | 42 ++++++++-------- 51 files changed, 262 insertions(+), 262 deletions(-) rename Source/DafnyCore/AST/Statements/Assignment/{UpdateStmt.cs => AssignStatement.cs} (91%) rename Source/DafnyCore/AST/Statements/Assignment/{ConcreteUpdateStatement.cs => ConcreteAssignStatement.cs} (86%) rename Source/DafnyCore/AST/Statements/Assignment/{AssignStmt.cs => SingleAssignStmt.cs} (92%) diff --git a/Source/DafnyCore/AST/AstVisitor.cs b/Source/DafnyCore/AST/AstVisitor.cs index cf1388b50d9..08bcd71bf56 100644 --- a/Source/DafnyCore/AST/AstVisitor.cs +++ b/Source/DafnyCore/AST/AstVisitor.cs @@ -336,7 +336,7 @@ protected virtual void VisitStatement(Statement stmt, VisitorContext context) { VisitUserProvidedType(local.SyntacticType, context); } - } else if (stmt is AssignStmt assignStmt) { + } else if (stmt is SingleAssignStmt assignStmt) { if (assignStmt.Rhs is TypeRhs typeRhs) { if (typeRhs.EType != null) { VisitUserProvidedType(typeRhs.EType, context); diff --git a/Source/DafnyCore/AST/Expressions/StmtExpr.cs b/Source/DafnyCore/AST/Expressions/StmtExpr.cs index ea509b3c1a3..0e0fcdac78e 100644 --- a/Source/DafnyCore/AST/Expressions/StmtExpr.cs +++ b/Source/DafnyCore/AST/Expressions/StmtExpr.cs @@ -63,7 +63,7 @@ public Expression GetSConclusion() { return s.Result; } else if (S is HideRevealStmt) { return CreateBoolLiteral(tok, true); // one could use the definition axiom or the referenced labeled assertions, but "true" is conservative and much simpler :) - } else if (S is UpdateStmt) { + } else if (S is AssignStatement) { return CreateBoolLiteral(tok, true); // one could use the postcondition of the method, suitably instantiated, but "true" is conservative and much simpler :) } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement diff --git a/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs b/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs index dc43c1c3534..69846e90996 100644 --- a/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs +++ b/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs @@ -83,8 +83,8 @@ public void PrintStatement(Statement stmt, int indent) { } wr.Write(";"); - } else if (stmt is AssignStmt) { - AssignStmt s = (AssignStmt)stmt; + } else if (stmt is SingleAssignStmt) { + SingleAssignStmt s = (SingleAssignStmt)stmt; PrintExpression(s.Lhs, true); wr.Write(" := "); PrintRhs(s.Rhs); @@ -329,7 +329,7 @@ public void PrintStatement(Statement stmt, int indent) { wr.Write("}"); } - } else if (stmt is ConcreteUpdateStatement) { + } else if (stmt is ConcreteAssignStatement) { PrintConcreteUpdateStatement(stmt, indent); } else if (stmt is CallStmt) { // Most calls are printed from their concrete syntax given in the input. However, recursive calls to @@ -357,9 +357,9 @@ public void PrintStatement(Statement stmt, int indent) { PrintType(": ", local.SyntacticType); sep = ","; } - if (s.Update != null) { + if (s.Assign != null) { wr.Write(" "); - PrintUpdateRHS(s.Update, indent); + PrintUpdateRHS(s.Assign, indent); } wr.Write(";"); } else if (stmt is VarDeclPattern) { @@ -424,7 +424,7 @@ public void PrintStatement(Statement stmt, int indent) { public void PrintConcreteUpdateStatement(Statement stmt, int indent, bool includeSemicolon = true) { - var s = (ConcreteUpdateStatement)stmt; + var s = (ConcreteAssignStatement)stmt; string sep = ""; foreach (var lhs in s.Lhss) { wr.Write(sep); @@ -535,10 +535,10 @@ private void PrintModifyStmt(int indent, ModifyStmt s, bool omitFrame) { /// Does not print LHS, nor the space one might want between LHS and RHS, /// because if there's no LHS, we don't want to start with a space /// - void PrintUpdateRHS(ConcreteUpdateStatement s, int indent) { + void PrintUpdateRHS(ConcreteAssignStatement s, int indent) { Contract.Requires(s != null); - if (s is UpdateStmt) { - var update = (UpdateStmt)s; + if (s is AssignStatement) { + var update = (AssignStatement)s; if (update.Lhss.Count != 0) { wr.Write(":= "); } diff --git a/Source/DafnyCore/AST/Grammar/TokenNewIndentCollector.cs b/Source/DafnyCore/AST/Grammar/TokenNewIndentCollector.cs index 45e6630af7b..81fe8b1fa38 100644 --- a/Source/DafnyCore/AST/Grammar/TokenNewIndentCollector.cs +++ b/Source/DafnyCore/AST/Grammar/TokenNewIndentCollector.cs @@ -609,7 +609,7 @@ public bool SetIndentPrintRevealStmt(int indent, IEnumerable ownedTokens return true; } - public bool SetIndentUpdateStmt(ConcreteUpdateStatement stmt, int indent, bool inner) { + public bool SetIndentUpdateStmt(ConcreteAssignStatement stmt, int indent, bool inner) { var ownedTokens = stmt.OwnedTokens.ToList(); var opIndentDefault = stmt is AssignOrReturnStmt assignStmt && assignStmt.Lhss.Count == 0 @@ -622,7 +622,7 @@ public bool SetIndentUpdateStmt(ConcreteUpdateStatement stmt, int indent, bool i var commaIndent = indent + SpaceTab; SetIndentations(startToken, startAssignmentIndent, startAssignmentIndent, afterStartIndent); - var rhss = stmt is UpdateStmt updateStmt ? updateStmt.Rhss + var rhss = stmt is AssignStatement updateStmt ? updateStmt.Rhss : stmt is AssignOrReturnStmt assignOrReturnStmt ? new List { assignOrReturnStmt.Rhs } .Concat(assignOrReturnStmt.Rhss).ToList() : new List(); diff --git a/Source/DafnyCore/AST/Statements/Assignment/AssignOrReturnStmt.cs b/Source/DafnyCore/AST/Statements/Assignment/AssignOrReturnStmt.cs index 17574d3dc47..dc6b087f523 100644 --- a/Source/DafnyCore/AST/Statements/Assignment/AssignOrReturnStmt.cs +++ b/Source/DafnyCore/AST/Statements/Assignment/AssignOrReturnStmt.cs @@ -5,7 +5,7 @@ namespace Microsoft.Dafny; -public class AssignOrReturnStmt : ConcreteUpdateStatement, ICloneable, ICanResolve { +public class AssignOrReturnStmt : ConcreteAssignStatement, ICloneable, ICanResolve { public readonly ExprRhs Rhs; // this is the unresolved RHS, and thus can also be a method call public readonly List Rhss; public readonly AttributedToken KeywordToken; @@ -286,7 +286,7 @@ private void DesugarElephantStatement(bool expectExtract, Expression lhsExtract, } } // " temp, ... := MethodOrExpression, ...;" - var up = new UpdateStmt(RangeToken, lhss2, rhss2); + var up = new AssignStatement(RangeToken, lhss2, rhss2); if (expectExtract) { up.OriginalInitialLhs = Lhss.Count == 0 ? null : Lhss[0]; } @@ -320,7 +320,7 @@ private void DesugarElephantStatement(bool expectExtract, Expression lhsExtract, new IfStmt(RangeToken, false, resolver.VarDotMethod(Tok, temp, "IsFailure"), // THEN: { out := temp.PropagateFailure(); return; } new BlockStmt(RangeToken, new List() { - new UpdateStmt(RangeToken, + new AssignStatement(RangeToken, new List() { ident }, new List() { new ExprRhs(resolver.VarDotMethod(Tok, temp, "PropagateFailure")) } ), @@ -335,14 +335,14 @@ private void DesugarElephantStatement(bool expectExtract, Expression lhsExtract, // "y := temp.Extract();" var lhs = Lhss[0]; ResolvedStatements.Add( - new UpdateStmt(RangeToken, + new AssignStatement(RangeToken, new List() { lhsExtract }, new List() { new ExprRhs(resolver.VarDotMethod(Tok, temp, "Extract")) } )); // The following check is not necessary, because the ghost mismatch is caught later. // However the error message here is much clearer. var m = resolver.ResolveMember(Tok, firstType, "Extract", out _); - if (m != null && m.IsGhost && !AssignStmt.LhsIsToGhostOrAutoGhost(lhs)) { + if (m != null && m.IsGhost && !SingleAssignStmt.LhsIsToGhostOrAutoGhost(lhs)) { resolver.reporter.Error(MessageSource.Resolver, lhs.tok, "The Extract member may not be ghost unless the initial LHS is ghost"); } diff --git a/Source/DafnyCore/AST/Statements/Assignment/UpdateStmt.cs b/Source/DafnyCore/AST/Statements/Assignment/AssignStatement.cs similarity index 91% rename from Source/DafnyCore/AST/Statements/Assignment/UpdateStmt.cs rename to Source/DafnyCore/AST/Statements/Assignment/AssignStatement.cs index cc3fe0507f1..2c05b223a27 100644 --- a/Source/DafnyCore/AST/Statements/Assignment/UpdateStmt.cs +++ b/Source/DafnyCore/AST/Statements/Assignment/AssignStatement.cs @@ -4,7 +4,7 @@ namespace Microsoft.Dafny; -public class UpdateStmt : ConcreteUpdateStatement, ICloneable, ICanResolve { +public class AssignStatement : ConcreteAssignStatement, ICloneable, ICanResolve { public readonly List Rhss; public readonly bool CanMutateKnownState; public Expression OriginalInitialLhs = null; @@ -38,11 +38,11 @@ void ObjectInvariant() { Contract.Invariant(cce.NonNullElements(Rhss)); } - public UpdateStmt Clone(Cloner cloner) { - return new UpdateStmt(cloner, this); + public AssignStatement Clone(Cloner cloner) { + return new AssignStatement(cloner, this); } - public UpdateStmt(Cloner cloner, UpdateStmt original) : base(cloner, original) { + public AssignStatement(Cloner cloner, AssignStatement original) : base(cloner, original) { Rhss = original.Rhss.Select(cloner.CloneRHS).ToList(); CanMutateKnownState = original.CanMutateKnownState; if (cloner.CloneResolvedFields) { @@ -50,7 +50,7 @@ public UpdateStmt(Cloner cloner, UpdateStmt original) : base(cloner, original) { } } - public UpdateStmt(RangeToken rangeToken, List lhss, List rhss) + public AssignStatement(RangeToken rangeToken, List lhss, List rhss) : base(rangeToken, lhss) { Contract.Requires(cce.NonNullElements(lhss)); Contract.Requires(cce.NonNullElements(rhss)); @@ -58,7 +58,7 @@ public UpdateStmt(RangeToken rangeToken, List lhss, List lhss, List rhss, bool mutate) + public AssignStatement(RangeToken rangeToken, List lhss, List rhss, bool mutate) : base(rangeToken, lhss) { Contract.Requires(cce.NonNullElements(lhss)); Contract.Requires(cce.NonNullElements(rhss)); @@ -133,7 +133,7 @@ public override void Resolve(ModuleResolver resolver, ResolutionContext resoluti } else if (resolver.Reporter.Count(ErrorLevel.Error) == errorCountBeforeCheckingLhs) { // add the statements here in a sequence, but don't use that sequence later for translation (instead, should translate properly as multi-assignment) for (int i = 0; i < Lhss.Count; i++) { - var a = new AssignStmt(RangeToken, Lhss[i].Resolved, Rhss[i]); + var a = new SingleAssignStmt(RangeToken, Lhss[i].Resolved, Rhss[i]); ResolvedStatements.Add(a); } } @@ -153,7 +153,7 @@ public override void Resolve(ModuleResolver resolver, ResolutionContext resoluti if (tr.CanAffectPreviouslyKnownExpressions) { resolver.Reporter.Error(MessageSource.Resolver, tr.Tok, "can only have initialization methods which modify at most 'this'."); } else if (resolver.Reporter.Count(ErrorLevel.Error) == errorCountBeforeCheckingLhs) { - var a = new AssignStmt(RangeToken, Lhss[0].Resolved, tr); + var a = new SingleAssignStmt(RangeToken, Lhss[0].Resolved, tr); ResolvedStatements.Add(a); } } @@ -169,7 +169,7 @@ public override void Resolve(ModuleResolver resolver, ResolutionContext resoluti Contract.Assert(2 <= Lhss.Count); // the parser allows 0 Lhss only if the whole statement looks like an expression (not a TypeRhs) resolver.Reporter.Error(MessageSource.Resolver, Lhss[1].tok, "the number of left-hand sides ({0}) and right-hand sides ({1}) must match for a multi-assignment", Lhss.Count, Rhss.Count); } else if (resolver.Reporter.Count(ErrorLevel.Error) == errorCountBeforeCheckingLhs) { - var a = new AssignStmt(RangeToken, Lhss[0].Resolved, Rhss[0]); + var a = new SingleAssignStmt(RangeToken, Lhss[0].Resolved, Rhss[0]); ResolvedStatements.Add(a); } } else if (resolver.Reporter.Count(ErrorLevel.Error) == errorCountBeforeCheckingLhs) { diff --git a/Source/DafnyCore/AST/Statements/Assignment/AssignSuchThatStmt.cs b/Source/DafnyCore/AST/Statements/Assignment/AssignSuchThatStmt.cs index 9e28b8bfce7..de8d5966704 100644 --- a/Source/DafnyCore/AST/Statements/Assignment/AssignSuchThatStmt.cs +++ b/Source/DafnyCore/AST/Statements/Assignment/AssignSuchThatStmt.cs @@ -5,7 +5,7 @@ namespace Microsoft.Dafny; -public class AssignSuchThatStmt : ConcreteUpdateStatement, ICloneable, ICanResolveNewAndOld { +public class AssignSuchThatStmt : ConcreteAssignStatement, ICloneable, ICanResolveNewAndOld { public readonly Expression Expr; public readonly AttributedToken AssumeToken; diff --git a/Source/DafnyCore/AST/Statements/Assignment/ConcreteUpdateStatement.cs b/Source/DafnyCore/AST/Statements/Assignment/ConcreteAssignStatement.cs similarity index 86% rename from Source/DafnyCore/AST/Statements/Assignment/ConcreteUpdateStatement.cs rename to Source/DafnyCore/AST/Statements/Assignment/ConcreteAssignStatement.cs index 4288038b6c3..55ce888d81e 100644 --- a/Source/DafnyCore/AST/Statements/Assignment/ConcreteUpdateStatement.cs +++ b/Source/DafnyCore/AST/Statements/Assignment/ConcreteAssignStatement.cs @@ -7,14 +7,14 @@ namespace Microsoft.Dafny; /// /// Common superclass of UpdateStmt, AssignSuchThatStmt and AssignOrReturnStmt /// -public abstract class ConcreteUpdateStatement : Statement, ICanFormat { +public abstract class ConcreteAssignStatement : Statement, ICanFormat { public readonly List Lhss; - protected ConcreteUpdateStatement(Cloner cloner, ConcreteUpdateStatement original) : base(cloner, original) { + protected ConcreteAssignStatement(Cloner cloner, ConcreteAssignStatement original) : base(cloner, original) { Lhss = original.Lhss.Select(cloner.CloneExpr).ToList(); } - public ConcreteUpdateStatement(RangeToken rangeToken, List lhss, Attributes attrs = null) + public ConcreteAssignStatement(RangeToken rangeToken, List lhss, Attributes attrs = null) : base(rangeToken, attrs) { Contract.Requires(cce.NonNullElements(lhss)); Lhss = lhss; diff --git a/Source/DafnyCore/AST/Statements/Assignment/AssignStmt.cs b/Source/DafnyCore/AST/Statements/Assignment/SingleAssignStmt.cs similarity index 92% rename from Source/DafnyCore/AST/Statements/Assignment/AssignStmt.cs rename to Source/DafnyCore/AST/Statements/Assignment/SingleAssignStmt.cs index ab73b05d1a1..d9f285111c4 100644 --- a/Source/DafnyCore/AST/Statements/Assignment/AssignStmt.cs +++ b/Source/DafnyCore/AST/Statements/Assignment/SingleAssignStmt.cs @@ -4,7 +4,7 @@ namespace Microsoft.Dafny; -public class AssignStmt : Statement, ICloneable { +public class SingleAssignStmt : Statement, ICloneable { public readonly Expression Lhs; public readonly AssignmentRhs Rhs; public override IEnumerable Children => new List { Lhs, Rhs }.Where(x => x != null); @@ -36,16 +36,16 @@ public override IToken Tok { } } - public AssignStmt Clone(Cloner cloner) { - return new AssignStmt(cloner, this); + public SingleAssignStmt Clone(Cloner cloner) { + return new SingleAssignStmt(cloner, this); } - public AssignStmt(Cloner cloner, AssignStmt original) : base(cloner, original) { + public SingleAssignStmt(Cloner cloner, SingleAssignStmt original) : base(cloner, original) { Lhs = cloner.CloneExpr(original.Lhs); Rhs = cloner.CloneRHS(original.Rhs); } - public AssignStmt(RangeToken rangeToken, Expression lhs, AssignmentRhs rhs) + public SingleAssignStmt(RangeToken rangeToken, Expression lhs, AssignmentRhs rhs) : base(rangeToken) { Contract.Requires(rangeToken != null); Contract.Requires(lhs != null); diff --git a/Source/DafnyCore/AST/Statements/Assignment/VarDeclStmt.cs b/Source/DafnyCore/AST/Statements/Assignment/VarDeclStmt.cs index 72bbed6ffc0..be29bc4897a 100644 --- a/Source/DafnyCore/AST/Statements/Assignment/VarDeclStmt.cs +++ b/Source/DafnyCore/AST/Statements/Assignment/VarDeclStmt.cs @@ -6,7 +6,7 @@ namespace Microsoft.Dafny; public class VarDeclStmt : Statement, ICloneable, ICanFormat { public readonly List Locals; - public readonly ConcreteUpdateStatement Update; + public readonly ConcreteAssignStatement Assign; [ContractInvariantMethod] void ObjectInvariant() { Contract.Invariant(cce.NonNullElements(Locals)); @@ -19,20 +19,20 @@ public VarDeclStmt Clone(Cloner cloner) { public VarDeclStmt(Cloner cloner, VarDeclStmt original) : base(cloner, original) { Locals = original.Locals.Select(l => cloner.CloneLocalVariable(l, false)).ToList(); - Update = (ConcreteUpdateStatement)cloner.CloneStmt(original.Update, false); + Assign = (ConcreteAssignStatement)cloner.CloneStmt(original.Assign, false); } - public VarDeclStmt(RangeToken rangeToken, List locals, ConcreteUpdateStatement update) + public VarDeclStmt(RangeToken rangeToken, List locals, ConcreteAssignStatement assign) : base(rangeToken) { Contract.Requires(locals != null); Contract.Requires(locals.Count != 0); Locals = locals; - Update = update; + Assign = assign; } public override IEnumerable SubStatements { - get { if (Update != null) { yield return Update; } } + get { if (Assign != null) { yield return Assign; } } } public override IEnumerable SpecificationSubExpressions { @@ -51,6 +51,6 @@ public override IEnumerable SpecificationSubExpressions { public override IEnumerable PreResolveChildren => Children; public bool SetIndent(int indentBefore, TokenNewIndentCollector formatter) { var result = formatter.SetIndentVarDeclStmt(indentBefore, OwnedTokens, false, false); - return Update != null ? formatter.SetIndentUpdateStmt(Update, indentBefore, true) : result; + return Assign != null ? formatter.SetIndentUpdateStmt(Assign, indentBefore, true) : result; } } \ No newline at end of file diff --git a/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs b/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs index 2a39a67262e..05286b81aa2 100644 --- a/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs +++ b/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs @@ -52,7 +52,7 @@ internal static void ResolveByProof(INewOrOldResolver resolver, BlockStmt proof, public void Render(TextWriter wr, Printer printer, int indent) { if (Body is AssertStmt assertStmt) { printer.PrintPredicateStmt(assertStmt, false); - } else if (Body is ConcreteUpdateStatement updateStmt) { + } else if (Body is ConcreteAssignStatement updateStmt) { printer.PrintConcreteUpdateStatement(updateStmt, indent, false); } else if (Body is BlockStmt blockStmt) { printer.PrintBlockStmt(blockStmt, indent); diff --git a/Source/DafnyCore/AST/Statements/ControlFlow/ForallStmt.cs b/Source/DafnyCore/AST/Statements/ControlFlow/ForallStmt.cs index 6a71378ac49..4b2c4133314 100644 --- a/Source/DafnyCore/AST/Statements/ControlFlow/ForallStmt.cs +++ b/Source/DafnyCore/AST/Statements/ControlFlow/ForallStmt.cs @@ -85,8 +85,8 @@ public Statement S0 { if (block != null && block.Body.Count == 1) { s = block.Body[0]; // dig further into s - } else if (s is UpdateStmt) { - var update = (UpdateStmt)s; + } else if (s is AssignStatement) { + var update = (AssignStatement)s; if (update.ResolvedStatements?.Count == 1) { s = update.ResolvedStatements[0]; // dig further into s diff --git a/Source/DafnyCore/AST/Statements/Methods/ProduceStmt.cs b/Source/DafnyCore/AST/Statements/Methods/ProduceStmt.cs index 5d2ca2f5269..19e7a9c2280 100644 --- a/Source/DafnyCore/AST/Statements/Methods/ProduceStmt.cs +++ b/Source/DafnyCore/AST/Statements/Methods/ProduceStmt.cs @@ -6,7 +6,7 @@ namespace Microsoft.Dafny; public abstract class ProduceStmt : Statement { public List Rhss; [FilledInDuringResolution] - public UpdateStmt HiddenUpdate; + public AssignStatement HiddenUpdate; protected ProduceStmt(Cloner cloner, ProduceStmt original) : base(cloner, original) { if (original.Rhss != null) { @@ -14,7 +14,7 @@ protected ProduceStmt(Cloner cloner, ProduceStmt original) : base(cloner, origin } if (cloner.CloneResolvedFields) { if (original.HiddenUpdate != null) { - HiddenUpdate = new UpdateStmt(cloner, original.HiddenUpdate); + HiddenUpdate = new AssignStatement(cloner, original.HiddenUpdate); } } } diff --git a/Source/DafnyCore/AST/Statements/Statement.cs b/Source/DafnyCore/AST/Statements/Statement.cs index 67c8c7fdabd..9d1e2ed7f34 100644 --- a/Source/DafnyCore/AST/Statements/Statement.cs +++ b/Source/DafnyCore/AST/Statements/Statement.cs @@ -165,9 +165,9 @@ public static VarDeclStmt CreateLocalVariable(IToken tok, string name, Expressio var variable = new LocalVariable(rangeToken, name, value.Type, false); variable.type = value.Type; Expression variableExpr = new IdentifierExpr(tok, variable); - var variableUpdateStmt = new UpdateStmt(rangeToken, Util.Singleton(variableExpr), + var variableUpdateStmt = new AssignStatement(rangeToken, Util.Singleton(variableExpr), Util.Singleton(new ExprRhs(value))); - var variableAssignStmt = new AssignStmt(rangeToken, variableUpdateStmt.Lhss[0], variableUpdateStmt.Rhss[0]); + var variableAssignStmt = new SingleAssignStmt(rangeToken, variableUpdateStmt.Lhss[0], variableUpdateStmt.Rhss[0]); variableUpdateStmt.ResolvedStatements = new List() { variableAssignStmt }; return new VarDeclStmt(rangeToken, Util.Singleton(variable), variableUpdateStmt); } diff --git a/Source/DafnyCore/AST/Substituter.cs b/Source/DafnyCore/AST/Substituter.cs index 9db507cc3ae..f0999d9a4f4 100644 --- a/Source/DafnyCore/AST/Substituter.cs +++ b/Source/DafnyCore/AST/Substituter.cs @@ -788,9 +788,9 @@ protected virtual Statement SubstStmt(Statement stmt) { } breaks.Add(rr); r = rr; - } else if (stmt is AssignStmt) { - var s = (AssignStmt)stmt; - r = new AssignStmt(s.RangeToken, Substitute(s.Lhs), SubstRHS(s.Rhs)); + } else if (stmt is SingleAssignStmt) { + var s = (SingleAssignStmt)stmt; + r = new SingleAssignStmt(s.RangeToken, Substitute(s.Lhs), SubstRHS(s.Rhs)); } else if (stmt is CallStmt) { var s = (CallStmt)stmt; var rr = new CallStmt(s.RangeToken, s.Lhs.ConvertAll(Substitute), (MemberSelectExpr)Substitute(s.MethodSelect), s.Args.ConvertAll(Substitute)); @@ -847,15 +847,15 @@ protected virtual Statement SubstStmt(Statement stmt) { r = new AssignSuchThatStmt(s.RangeToken, s.Lhss.ConvertAll(Substitute), Substitute(s.Expr), s.AssumeToken, null) { Bounds = SubstituteBoundedPoolList(s.Bounds) }; - } else if (stmt is UpdateStmt) { - var s = (UpdateStmt)stmt; + } else if (stmt is AssignStatement) { + var s = (AssignStatement)stmt; var resolved = s.ResolvedStatements; - UpdateStmt rr; + AssignStatement rr; if (resolved.Count == 1) { // when later translating this UpdateStmt, the s.Lhss and s.Rhss components won't be used, only s.ResolvedStatements - rr = new UpdateStmt(s.RangeToken, s.Lhss, s.Rhss, s.CanMutateKnownState); + rr = new AssignStatement(s.RangeToken, s.Lhss, s.Rhss, s.CanMutateKnownState); } else { - rr = new UpdateStmt(s.RangeToken, s.Lhss.ConvertAll(Substitute), s.Rhss.ConvertAll(SubstRHS), s.CanMutateKnownState); + rr = new AssignStatement(s.RangeToken, s.Lhss.ConvertAll(Substitute), s.Rhss.ConvertAll(SubstRHS), s.CanMutateKnownState); } if (s.ResolvedStatements != null) { @@ -865,7 +865,7 @@ protected virtual Statement SubstStmt(Statement stmt) { } else if (stmt is VarDeclStmt) { var s = (VarDeclStmt)stmt; var lhss = CreateLocalVarSubstitutions(s.Locals, false); - var rr = new VarDeclStmt(s.RangeToken, lhss, (ConcreteUpdateStatement)SubstStmt(s.Update)); + var rr = new VarDeclStmt(s.RangeToken, lhss, (ConcreteAssignStatement)SubstStmt(s.Assign)); r = rr; } else if (stmt is VarDeclPattern) { var s = (VarDeclPattern)stmt; diff --git a/Source/DafnyCore/Backends/Dafny/DafnyCodeGenerator.cs b/Source/DafnyCore/Backends/Dafny/DafnyCodeGenerator.cs index 9c5a9b9ddd9..b0a5f557c05 100644 --- a/Source/DafnyCore/Backends/Dafny/DafnyCodeGenerator.cs +++ b/Source/DafnyCore/Backends/Dafny/DafnyCodeGenerator.cs @@ -1591,7 +1591,7 @@ protected override void EmitEmptyTupleList(string tupleTypeArgs, ConcreteSyntaxT } protected override ConcreteSyntaxTree EmitIngredients(ConcreteSyntaxTree wr, string ingredients, int L, - string tupleTypeArgs, ForallStmt s, AssignStmt s0, Expression rhs) { + string tupleTypeArgs, ForallStmt s, SingleAssignStmt s0, Expression rhs) { AddUnsupportedFeature(Token.NoToken, Feature.NonSequentializableForallStatements); return wr; } diff --git a/Source/DafnyCore/Backends/Java/JavaCodeGenerator.cs b/Source/DafnyCore/Backends/Java/JavaCodeGenerator.cs index 8760a7df39b..c84c401d0aa 100644 --- a/Source/DafnyCore/Backends/Java/JavaCodeGenerator.cs +++ b/Source/DafnyCore/Backends/Java/JavaCodeGenerator.cs @@ -221,7 +221,7 @@ protected override void EmitCastOutParameterSplits(string outCollector, List tupleTypeArgsList, ConcreteSyntaxTree wr, string tup) { + protected override void EmitSeqSelect(SingleAssignStmt s0, List tupleTypeArgsList, ConcreteSyntaxTree wr, string tup) { wr.Write("("); var lhs = (SeqSelectExpr)s0.Lhs; EmitIndexCollectionUpdate(lhs.Seq.Type, out var wColl, out var wIndex, out var wValue, wr, nativeIndex: true); @@ -236,7 +236,7 @@ protected override void EmitSeqSelect(AssignStmt s0, List tupleTypeArgsLis EndStmt(wr); } - protected override void EmitMultiSelect(AssignStmt s0, List tupleTypeArgsList, ConcreteSyntaxTree wr, string tup, int L) { + protected override void EmitMultiSelect(SingleAssignStmt s0, List tupleTypeArgsList, ConcreteSyntaxTree wr, string tup, int L) { var arrayType = tupleTypeArgsList[0]; var rhsType = tupleTypeArgsList[L - 1]; @@ -265,7 +265,7 @@ protected override void WriteCast(string s, ConcreteSyntaxTree wr) { wr.Write($"({s})"); } - protected override ConcreteSyntaxTree EmitIngredients(ConcreteSyntaxTree wr, string ingredients, int L, string tupleTypeArgs, ForallStmt s, AssignStmt s0, Expression rhs) { + protected override ConcreteSyntaxTree EmitIngredients(ConcreteSyntaxTree wr, string ingredients, int L, string tupleTypeArgs, ForallStmt s, SingleAssignStmt s0, Expression rhs) { var wStmts = wr.Fork(); var wrVarInit = wr; wrVarInit.Write($"java.util.ArrayList<{DafnyTupleClass(L)}<{tupleTypeArgs}>> {ingredients} = "); diff --git a/Source/DafnyCore/Backends/SinglePassCodeGenerator/SinglePassCodeGenerator.Statement.cs b/Source/DafnyCore/Backends/SinglePassCodeGenerator/SinglePassCodeGenerator.Statement.cs index ecce27f2eea..8ae0c901eb5 100644 --- a/Source/DafnyCore/Backends/SinglePassCodeGenerator/SinglePassCodeGenerator.Statement.cs +++ b/Source/DafnyCore/Backends/SinglePassCodeGenerator/SinglePassCodeGenerator.Statement.cs @@ -22,7 +22,7 @@ public abstract partial class SinglePassCodeGenerator { private int innerExtractIndex = -1; private bool IsExtractStatement(Statement stmt, string expectedLeftName) { - return stmt is UpdateStmt updateStmt + return stmt is AssignStatement updateStmt && updateStmt.Rhss.Count() == 1 && updateStmt.Lhss.Count() == 1 && updateStmt.Lhss[0] is IdentifierExpr { Name: var leftName } @@ -69,7 +69,7 @@ protected void TrStmt(Statement stmt, ConcreteSyntaxTree wr, ConcreteSyntaxTree if (s.HiddenUpdate != null) { TrStmt(s.HiddenUpdate, wr); var ss = s.HiddenUpdate.ResolvedStatements; - if (ss.Count == 1 && ss[0] is AssignStmt assign && assign.Rhs is ExprRhs eRhs && eRhs.Expr.Resolved is FunctionCallExpr fce && IsTailRecursiveByMethodCall(fce)) { + if (ss.Count == 1 && ss[0] is SingleAssignStmt assign && assign.Rhs is ExprRhs eRhs && eRhs.Expr.Resolved is FunctionCallExpr fce && IsTailRecursiveByMethodCall(fce)) { isTailRecursiveResult = true; } } @@ -81,13 +81,13 @@ protected void TrStmt(Statement stmt, ConcreteSyntaxTree wr, ConcreteSyntaxTree break; } - case UpdateStmt updateStmt: { + case AssignStatement updateStmt: { var s = updateStmt; var resolved = s.ResolvedStatements; if (resolved.Count == 1) { TrStmt(resolved[0], wr); } else { - var assignStmts = resolved.Cast().Where(assignStmt => !assignStmt.IsGhost).ToList(); + var assignStmts = resolved.Cast().Where(assignStmt => !assignStmt.IsGhost).ToList(); var lhss = new List(); var rhss = new List(); @@ -121,7 +121,7 @@ protected void TrStmt(Statement stmt, ConcreteSyntaxTree wr, ConcreteSyntaxTree break; } - case AssignStmt assignStmt: { + case SingleAssignStmt assignStmt: { var s = assignStmt; Contract.Assert(s.Lhs is not SeqSelectExpr expr || expr.SelectOne); // multi-element array assignments are not allowed if (s.Rhs is HavocRhs) { @@ -167,13 +167,13 @@ protected void TrStmt(Statement stmt, ConcreteSyntaxTree wr, ConcreteSyntaxTree var s = assignOrReturnStmt; var stmts = s.ResolvedStatements.ToList(); if (innerExtractIndex != -1 && - enclosingVarDecl is { Update: var stmtUpdate, Locals: { Count: > 0 } locals } + enclosingVarDecl is { Assign: var stmtUpdate, Locals: { Count: > 0 } locals } && stmtUpdate == assignOrReturnStmt) { // Wrap this UpdateStmt with a VarDecl containing this Local that we haven't emitted yet. stmts[innerExtractIndex] = new VarDeclStmt(enclosingVarDecl.RangeToken, new List() { locals[0] }, - (UpdateStmt)stmts[innerExtractIndex]); + (AssignStatement)stmts[innerExtractIndex]); } TrStmtList(stmts, wr); @@ -386,7 +386,7 @@ protected void TrStmt(Statement stmt, ConcreteSyntaxTree wr, ConcreteSyntaxTree TrStmt(s.Body, wr); return; } - var s0 = (AssignStmt)s.S0; + var s0 = (SingleAssignStmt)s.S0; if (s0.Rhs is HavocRhs) { // The forall statement says to havoc a bunch of things. This can be efficiently compiled // into doing nothing. @@ -506,14 +506,14 @@ protected void TrStmt(Statement stmt, ConcreteSyntaxTree wr, ConcreteSyntaxTree // var o :- B; // We won't declare o until we assign it with o := tmp.Extract(); var indexExtract = -1; - if (s.Update is AssignOrReturnStmt { ResolvedStatements: var stmts } + if (s.Assign is AssignOrReturnStmt { ResolvedStatements: var stmts } && s.Locals.Count > 0) { indexExtract = FindExtractStatement(stmts, s.Locals[0].Name); } foreach (var local in s.Locals) { - bool hasRhs = s.Update is AssignSuchThatStmt || s.Update is AssignOrReturnStmt; - if (!hasRhs && s.Update is UpdateStmt u) { + bool hasRhs = s.Assign is AssignSuchThatStmt || s.Assign is AssignOrReturnStmt; + if (!hasRhs && s.Assign is AssignStatement u) { if (i < u.Rhss.Count && u.Rhss[i] is HavocRhs) { // there's no specific initial value } else { @@ -531,8 +531,8 @@ protected void TrStmt(Statement stmt, ConcreteSyntaxTree wr, ConcreteSyntaxTree enclosingVarDecl = s; innerExtractIndex = indexExtract; - if (s.Update != null) { - TrStmt(s.Update, wr); + if (s.Assign != null) { + TrStmt(s.Assign, wr); } enclosingVarDecl = null; innerExtractIndex = -1; diff --git a/Source/DafnyCore/Backends/SinglePassCodeGenerator/SinglePassCodeGenerator.cs b/Source/DafnyCore/Backends/SinglePassCodeGenerator/SinglePassCodeGenerator.cs index c7993c0365a..a81cc0aa17a 100644 --- a/Source/DafnyCore/Backends/SinglePassCodeGenerator/SinglePassCodeGenerator.cs +++ b/Source/DafnyCore/Backends/SinglePassCodeGenerator/SinglePassCodeGenerator.cs @@ -3251,7 +3251,7 @@ void TrStmtNonempty(Statement stmt, ConcreteSyntaxTree wr, ConcreteSyntaxTree wS } } - protected virtual ConcreteSyntaxTree EmitIngredients(ConcreteSyntaxTree wr, string ingredients, int L, string tupleTypeArgs, ForallStmt s, AssignStmt s0, Expression rhs) { + protected virtual ConcreteSyntaxTree EmitIngredients(ConcreteSyntaxTree wr, string ingredients, int L, string tupleTypeArgs, ForallStmt s, SingleAssignStmt s0, Expression rhs) { var wStmts = wr.Fork(); var wrVarInit = DeclareLocalVar(ingredients, null, null, wr); { @@ -3292,7 +3292,7 @@ bool IsTailRecursiveByMethodCall(FunctionCallExpr fce) { return fce.IsByMethodCall && fce.Function.ByMethodDecl == enclosingMethod && fce.Function.ByMethodDecl.IsTailRecursive; } - protected virtual void EmitMemberSelect(AssignStmt s0, List tupleTypeArgsList, ConcreteSyntaxTree wr, string tup) { + protected virtual void EmitMemberSelect(SingleAssignStmt s0, List tupleTypeArgsList, ConcreteSyntaxTree wr, string tup) { var lhs = (MemberSelectExpr)s0.Lhs; var typeArgs = TypeArgumentInstantiation.ListFromMember(lhs.Member, null, lhs.TypeApplicationJustMember); @@ -3306,7 +3306,7 @@ protected virtual void EmitMemberSelect(AssignStmt s0, List tupleTypeArgsL EmitTupleSelect(tup, 1, wCoerced); } - protected virtual void EmitSeqSelect(AssignStmt s0, List tupleTypeArgsList, ConcreteSyntaxTree wr, string tup) { + protected virtual void EmitSeqSelect(SingleAssignStmt s0, List tupleTypeArgsList, ConcreteSyntaxTree wr, string tup) { var lhs = (SeqSelectExpr)s0.Lhs; EmitIndexCollectionUpdate(lhs.Seq.Type, out var wColl, out var wIndex, out var wValue, wr, nativeIndex: true); var wCoerce = EmitCoercionIfNecessary(from: null, to: lhs.Seq.Type, tok: s0.Tok, wr: wColl); @@ -3317,7 +3317,7 @@ protected virtual void EmitSeqSelect(AssignStmt s0, List tupleTypeArgsList EndStmt(wr); } - protected virtual void EmitMultiSelect(AssignStmt s0, List tupleTypeArgsList, ConcreteSyntaxTree wr, string tup, int L) { + protected virtual void EmitMultiSelect(SingleAssignStmt s0, List tupleTypeArgsList, ConcreteSyntaxTree wr, string tup, int L) { var lhs = (MultiSelectExpr)s0.Lhs; var wArray = new ConcreteSyntaxTree(wr.RelativeIndentLevel); var wCoerced = EmitCoercionIfNecessary(from: null, to: tupleTypeArgsList[0], tok: s0.Tok, wr: wArray); diff --git a/Source/DafnyCore/CounterExampleGeneration/PartialState.cs b/Source/DafnyCore/CounterExampleGeneration/PartialState.cs index 29a069ddd4f..d16095a5e71 100644 --- a/Source/DafnyCore/CounterExampleGeneration/PartialState.cs +++ b/Source/DafnyCore/CounterExampleGeneration/PartialState.cs @@ -150,7 +150,7 @@ public Statement AsAssumption() { if (!IsLoopEntryState) { return new AssumeStmt(RangeToken.NoToken, expression, null); } - return new UpdateStmt(RangeToken.NoToken, new List() { new IdentifierExpr(Token.NoToken, LoopGuards.Last()) }, + return new AssignStatement(RangeToken.NoToken, new List() { new IdentifierExpr(Token.NoToken, LoopGuards.Last()) }, new List() { new ExprRhs(expression) }); } diff --git a/Source/DafnyCore/Dafny.atg b/Source/DafnyCore/Dafny.atg index 50d4c25156e..e41632969e0 100644 --- a/Source/DafnyCore/Dafny.atg +++ b/Source/DafnyCore/Dafny.atg @@ -2211,7 +2211,7 @@ OneStmt ( BlockStmt (. s = bs; .) | OpaqueBlock | IF(IsReveal(scanner.Peek())) RevealStmt - | UpdateStmt // includes UpdateFailure + | AssignStatement // includes UpdateFailure | VarDeclStatement | ReturnStmt | IfStmt @@ -2319,7 +2319,7 @@ YieldStmt . /*------------------------------------------------------------------------*/ -UpdateStmt +AssignStatement = (. List lhss = new List(); List rhss = new List(); Expression e; @@ -2403,7 +2403,7 @@ UpdateStmt if (lhss.Count == 0 && rhss.Count == 0) { s = new BlockStmt(rangeToken, new List()); // error, give empty statement } else { - s = new UpdateStmt(rangeToken, lhss, rhss); + s = new AssignStatement(rangeToken, lhss, rhss); if (proof != null) { s = new BlockByProofStmt(rangeToken, proof, s); } @@ -2547,7 +2547,7 @@ VarDeclStatement<.out Statement/*!*/ s.> BlockStmt | ";" (. endTok = t; .) ) - (. ConcreteUpdateStatement update; + (. ConcreteAssignStatement assignment; var lhsExprs = new List(); if (isGhost || (rhss.Count == 0 && exceptionRhs == null && suchThat == null)) { // explicitly ghost or no init foreach (var lhs in lhss) { @@ -2561,15 +2561,15 @@ VarDeclStatement<.out Statement/*!*/ s.> var rangeToken = new RangeToken(startToken, t); var updateRangeToken = new RangeToken(assignTok, t); if (suchThat != null) { - update = new AssignSuchThatStmt(updateRangeToken, lhsExprs, suchThat, suchThatAssume, attrs); + assignment = new AssignSuchThatStmt(updateRangeToken, lhsExprs, suchThat, suchThatAssume, attrs); } else if (exceptionRhs != null) { - update = new AssignOrReturnStmt(updateRangeToken, lhsExprs, exceptionRhs, keywordToken, rhss); + assignment = new AssignOrReturnStmt(updateRangeToken, lhsExprs, exceptionRhs, keywordToken, rhss); } else if (rhss.Count == 0) { - update = null; + assignment = null; } else { - update = new UpdateStmt(updateRangeToken, lhsExprs, rhss); + assignment = new AssignStatement(updateRangeToken, lhsExprs, rhss); } - s = new VarDeclStmt(rangeToken, lhss, update); + s = new VarDeclStmt(rangeToken, lhss, assignment); if (proof != null) { s = new BlockByProofStmt(s.RangeToken, proof, s); } @@ -3338,7 +3338,7 @@ Expression (. var startToken = e.StartToken; e = new StmtExpr(e0.Tok, - new UpdateStmt(e.RangeToken, new List(), new List() { new ExprRhs(e) }), + new AssignStatement(e.RangeToken, new List(), new List() { new ExprRhs(e) }), e0); e.RangeToken = new RangeToken(startToken, e0.EndToken); .) @@ -4521,7 +4521,7 @@ QuantifierVariableDecl<.out QuantifiedVar qvar, ref Attributes attrs, bool allow { IF( la.kind == _lbracecolon) Attribute } // Ambiguity -- if the next optional block is not present, // this {Attribute} ends a QuantifierVariableDecl so it can end a SetComprehensionExpr // so it can end an Expression - // But an Expression can be followed by {Attribute} as in VarDeclStatement, Rhs, UpdateStmt + // But an Expression can be followed by {Attribute} as in VarDeclStatement, Rhs, AssignStatement [ // Coco complains about this ambiguity, thinking that a "|" can follow a body-less forall statement. // That can happen because the grammar allows a QuantifierDecl to end a QuantifierDomain, which can end // a SetComprehensionExpr, and a Expression can be followed by a bitvector '|'. diff --git a/Source/DafnyCore/Resolver/CallGraphBuilder.cs b/Source/DafnyCore/Resolver/CallGraphBuilder.cs index 8b8ab83723f..4f193d74e9d 100644 --- a/Source/DafnyCore/Resolver/CallGraphBuilder.cs +++ b/Source/DafnyCore/Resolver/CallGraphBuilder.cs @@ -213,7 +213,7 @@ private bool IsFunctionReturnValue(Function fn, Expression receiver, List Visit(ss, mustBeErasable, proofContext)); s.IsGhost = s.ResolvedStatements.All(ss => ss.IsGhost); } else if (stmt is AssignOrReturnStmt) { @@ -152,10 +152,10 @@ public void Visit(Statement stmt, bool mustBeErasable, [CanBeNull] string proofC local.MakeGhost(); } } - if (s.Update != null) { - Visit(s.Update, mustBeErasable, proofContext); + if (s.Assign != null) { + Visit(s.Assign, mustBeErasable, proofContext); } - s.IsGhost = (s.Update == null || s.Update.IsGhost) && s.Locals.All(v => v.IsGhost); + s.IsGhost = (s.Assign == null || s.Assign.IsGhost) && s.Locals.All(v => v.IsGhost); // Check on "assumption" variables foreach (var local in s.Locals) { @@ -195,8 +195,8 @@ public void Visit(Statement stmt, bool mustBeErasable, [CanBeNull] string proofC s.IsGhost = spec; } - } else if (stmt is AssignStmt) { - var s = (AssignStmt)stmt; + } else if (stmt is SingleAssignStmt) { + var s = (SingleAssignStmt)stmt; CheckAssignStmt(s, mustBeErasable, proofContext); } else if (stmt is CallStmt) { @@ -488,7 +488,7 @@ public void Visit(Statement stmt, bool mustBeErasable, [CanBeNull] string proofC } } - private void CheckAssignStmt(AssignStmt s, bool mustBeErasable, [CanBeNull] string proofContext) { + private void CheckAssignStmt(SingleAssignStmt s, bool mustBeErasable, [CanBeNull] string proofContext) { Contract.Requires(s != null); Contract.Requires(mustBeErasable || proofContext == null); @@ -515,8 +515,8 @@ private void CheckAssignStmt(AssignStmt s, bool mustBeErasable, [CanBeNull] stri Error(ErrorId.r_new_forbidden_in_proof, s.Rhs.Tok, $"{proofContext} is not allowed to use 'new'"); } - var gk = AssignStmt.LhsIsToGhost_Which(lhs); - if (gk == AssignStmt.NonGhostKind.IsGhost) { + var gk = SingleAssignStmt.LhsIsToGhost_Which(lhs); + if (gk == SingleAssignStmt.NonGhostKind.IsGhost) { s.IsGhost = true; if (proofContext != null && !(lhs is IdentifierExpr)) { Error(ErrorId.r_no_heap_update_in_proof, lhs.tok, $"{proofContext} is not allowed to make heap updates"); @@ -524,7 +524,7 @@ private void CheckAssignStmt(AssignStmt s, bool mustBeErasable, [CanBeNull] stri if (s.Rhs is TypeRhs tRhs && tRhs.InitCall != null) { Visit(tRhs.InitCall, true, proofContext); } - } else if (gk == AssignStmt.NonGhostKind.Variable && codeContext.IsGhost) { + } else if (gk == SingleAssignStmt.NonGhostKind.Variable && codeContext.IsGhost) { // cool } else if (mustBeErasable) { if (inConstructorInitializationPhase && codeContext is Constructor && codeContext.IsGhost && lhs is MemberSelectExpr mse && @@ -537,19 +537,19 @@ private void CheckAssignStmt(AssignStmt s, bool mustBeErasable, [CanBeNull] stri } else { reason = "the statement is in a ghost context; e.g., it may be guarded by a specification-only expression"; } - Error(ErrorId.r_assignment_forbidden_in_context, s, $"assignment to {AssignStmt.NonGhostKind_To_String(gk)} is not allowed in this context, because {reason}"); + Error(ErrorId.r_assignment_forbidden_in_context, s, $"assignment to {SingleAssignStmt.NonGhostKind_To_String(gk)} is not allowed in this context, because {reason}"); } } else { - if (gk == AssignStmt.NonGhostKind.Field) { + if (gk == SingleAssignStmt.NonGhostKind.Field) { var mse = (MemberSelectExpr)lhs; ExpressionTester.CheckIsCompilable(resolver, reporter, mse.Obj, codeContext); - } else if (gk == AssignStmt.NonGhostKind.ArrayElement) { + } else if (gk == SingleAssignStmt.NonGhostKind.ArrayElement) { ExpressionTester.CheckIsCompilable(resolver, reporter, lhs, codeContext); } if (s.Rhs is ExprRhs) { var rhs = (ExprRhs)s.Rhs; - if (!AssignStmt.LhsIsToGhost(lhs)) { + if (!SingleAssignStmt.LhsIsToGhost(lhs)) { ExpressionTester.CheckIsCompilable(resolver, reporter, rhs.Expr, codeContext); } } else if (s.Rhs is HavocRhs) { diff --git a/Source/DafnyCore/Resolver/HigherOrderHeapAllocationChecker.cs b/Source/DafnyCore/Resolver/HigherOrderHeapAllocationChecker.cs index 137aeafb291..f3ba5dfa937 100644 --- a/Source/DafnyCore/Resolver/HigherOrderHeapAllocationChecker.cs +++ b/Source/DafnyCore/Resolver/HigherOrderHeapAllocationChecker.cs @@ -59,7 +59,7 @@ protected override bool VisitOneStatement(Statement stmt, IASTVisitorContext con // Since all updates and variable declarations are eventually broken down into // assignments, we need only consider an AssignStmt. - if (stmt is AssignStmt { Lhs: { } lhs, Rhs: { } rhs }) { + if (stmt is SingleAssignStmt { Lhs: { } lhs, Rhs: { } rhs }) { // Memory can be updated either by writing to a sequence-like collection // or by writing to an object's field. diff --git a/Source/DafnyCore/Resolver/HigherOrderHeapAllocationCheckerConstructor.cs b/Source/DafnyCore/Resolver/HigherOrderHeapAllocationCheckerConstructor.cs index f6d51f663f3..99c898293bb 100644 --- a/Source/DafnyCore/Resolver/HigherOrderHeapAllocationCheckerConstructor.cs +++ b/Source/DafnyCore/Resolver/HigherOrderHeapAllocationCheckerConstructor.cs @@ -75,7 +75,7 @@ private bool Occurs(Type obj, Type rhs, bool left) { protected override bool VisitOneStatement(Statement stmt, IASTVisitorContext context) { // Assigments to constant fields in constructors boil down to an assignment. - if (stmt is AssignStmt assign) { + if (stmt is SingleAssignStmt assign) { var lhs = assign.Lhs; Type lhsType; diff --git a/Source/DafnyCore/Resolver/ModuleResolver.cs b/Source/DafnyCore/Resolver/ModuleResolver.cs index 6e1a54782a9..f719c6375b6 100644 --- a/Source/DafnyCore/Resolver/ModuleResolver.cs +++ b/Source/DafnyCore/Resolver/ModuleResolver.cs @@ -3153,7 +3153,7 @@ public Expression makeTemp(String prefix, AssignOrReturnStmt s, ResolutionContex var idlist = new List() { id }; var lhss = new List() { locvar }; var rhss = new List() { new ExprRhs(ex) }; - var up = new UpdateStmt(s.RangeToken, idlist, rhss); + var up = new AssignStatement(s.RangeToken, idlist, rhss); s.ResolvedStatements.Add(new VarDeclStmt(s.RangeToken, lhss, up)); return id; } @@ -3233,8 +3233,8 @@ public void CheckLocalityUpdates(Statement stmt, ISet localsAllow foreach (var lhs in s.Lhss) { CheckLocalityUpdatesLhs(lhs, localsAllowedInUpdates, @where); } - } else if (stmt is AssignStmt) { - var s = (AssignStmt)stmt; + } else if (stmt is SingleAssignStmt) { + var s = (SingleAssignStmt)stmt; CheckLocalityUpdatesLhs(s.Lhs, localsAllowedInUpdates, @where); } else if (stmt is CallStmt) { var s = (CallStmt)stmt; diff --git a/Source/DafnyCore/Resolver/NameResolutionAndTypeInference/NameResolutionAndTypeInference.cs b/Source/DafnyCore/Resolver/NameResolutionAndTypeInference/NameResolutionAndTypeInference.cs index d5f65401db7..6e5c121b540 100644 --- a/Source/DafnyCore/Resolver/NameResolutionAndTypeInference/NameResolutionAndTypeInference.cs +++ b/Source/DafnyCore/Resolver/NameResolutionAndTypeInference/NameResolutionAndTypeInference.cs @@ -1140,7 +1140,7 @@ public void ResolveExpressionX(Expression expr, ResolutionContext resolutionCont ResolveStatement(e.S, resolutionContext); if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { - var r = e.S as UpdateStmt; + var r = e.S as AssignStatement; if (r != null && r.ResolvedStatements.Count == 1) { var call = r.ResolvedStatements[0] as CallStmt; if (call.Method is TwoStateLemma && !resolutionContext.IsTwoState) { @@ -3611,7 +3611,7 @@ public void ResolveStatement(Statement stmt, ResolutionContext resolutionContext } formals.Add(produceLhs); } - s.HiddenUpdate = new UpdateStmt(s.RangeToken, formals, s.Rhss, true); + s.HiddenUpdate = new AssignStatement(s.RangeToken, formals, s.Rhss, true); // resolving the update statement will check for return/yield statement specifics. ResolveStatement(s.HiddenUpdate, resolutionContext); } @@ -3621,7 +3621,7 @@ public void ResolveStatement(Statement stmt, ResolutionContext resolutionContext } else if (stmt is VarDeclStmt) { var s = (VarDeclStmt)stmt; // We have four cases. - Contract.Assert(s.Update == null || s.Update is AssignSuchThatStmt || s.Update is UpdateStmt || s.Update is AssignOrReturnStmt); + Contract.Assert(s.Assign == null || s.Assign is AssignSuchThatStmt || s.Assign is AssignStatement || s.Assign is AssignOrReturnStmt); // 0. There is no .Update. This is easy, we will just resolve the locals. // 1. The .Update is an AssignSuchThatStmt. This is also straightforward: first // resolve the locals, which adds them to the scope, and then resolve the .Update. @@ -3648,18 +3648,18 @@ public void ResolveStatement(Statement stmt, ResolutionContext resolutionContext } } // Resolve the UpdateStmt, if any - if (s.Update is UpdateStmt or AssignOrReturnStmt) { + if (s.Assign is AssignStatement or AssignOrReturnStmt) { // resolve the LHS - Contract.Assert(s.Update.Lhss.Count == s.Locals.Count); - for (int i = 0; i < s.Update.Lhss.Count; i++) { + Contract.Assert(s.Assign.Lhss.Count == s.Locals.Count); + for (int i = 0; i < s.Assign.Lhss.Count; i++) { var local = s.Locals[i]; - var lhs = (IdentifierExpr)s.Update.Lhss[i]; // the LHS in this case will be an IdentifierExpr, because that's how the parser creates the VarDeclStmt + var lhs = (IdentifierExpr)s.Assign.Lhss[i]; // the LHS in this case will be an IdentifierExpr, because that's how the parser creates the VarDeclStmt Contract.Assert(lhs.Type == null); // not yet resolved lhs.Var = local; lhs.Type = local.Type; } // resolve the whole thing - s.Update.Resolve(this, resolutionContext); + s.Assign.Resolve(this, resolutionContext); } // Add the locals to the scope foreach (var local in s.Locals) { @@ -3670,7 +3670,7 @@ public void ResolveStatement(Statement stmt, ResolutionContext resolutionContext ResolveAttributes(local, resolutionContext); } // Resolve the AssignSuchThatStmt, if any - if (s.Update is AssignSuchThatStmt assignSuchThatStmt) { + if (s.Assign is AssignSuchThatStmt assignSuchThatStmt) { assignSuchThatStmt.Resolve(this, resolutionContext); } } else if (stmt is VarDeclPattern) { @@ -3696,8 +3696,8 @@ public void ResolveStatement(Statement stmt, ResolutionContext resolutionContext // Every identifier-looking thing in the pattern resolved to a constructor; that is, this LHS is a constant literal reporter.Error(MessageSource.Resolver, s.LHS.tok, "LHS is a constant literal; to be legal, it must introduce at least one bound variable"); } - } else if (stmt is AssignStmt) { - AssignStmt s = (AssignStmt)stmt; + } else if (stmt is SingleAssignStmt) { + SingleAssignStmt s = (SingleAssignStmt)stmt; int prevErrorCount = reporter.Count(ErrorLevel.Error); ResolveExpression(s.Lhs, resolutionContext); // allow ghosts for now, tighted up below bool lhsResolvedSuccessfully = reporter.Count(ErrorLevel.Error) == prevErrorCount; @@ -3876,10 +3876,10 @@ public void ResolveStatement(Statement stmt, ResolutionContext resolutionContext // The effect of Assign and the postcondition of Call will be seen outside the forall // statement. Statement s0 = s.S0; - if (s0 is AssignStmt) { + if (s0 is SingleAssignStmt) { s.Kind = ForallStmt.BodyKind.Assign; - var rhs = ((AssignStmt)s0).Rhs; + var rhs = ((SingleAssignStmt)s0).Rhs; if (rhs is TypeRhs) { reporter.Error(MessageSource.Resolver, rhs.Tok, "new allocation not supported in aggregate assignments"); } diff --git a/Source/DafnyCore/Resolver/ObjectConstructorChecker.cs b/Source/DafnyCore/Resolver/ObjectConstructorChecker.cs index 88076014d36..620d1900ed7 100644 --- a/Source/DafnyCore/Resolver/ObjectConstructorChecker.cs +++ b/Source/DafnyCore/Resolver/ObjectConstructorChecker.cs @@ -19,7 +19,7 @@ public override IASTVisitorContext GetContext(IASTVisitorContext astVisitorConte } protected override bool VisitOneStatement(Statement stmt, IASTVisitorContext context) { - if (stmt is AssignStmt { Rhs: TypeRhs rr } && rr.ArrayDimensions == null && (rr.Bindings == null || rr.InitCall.Method is not Constructor)) { + if (stmt is SingleAssignStmt { Rhs: TypeRhs rr } && rr.ArrayDimensions == null && (rr.Bindings == null || rr.InitCall.Method is not Constructor)) { // this is an AssignStmt that allocates one object and does not call a constructor var udt = (UserDefinedType)rr.EType.NormalizeExpand(); var cl = (ClassLikeDecl)udt.ResolvedClass; diff --git a/Source/DafnyCore/Resolver/PreType/PreTypeResolve.Expressions.cs b/Source/DafnyCore/Resolver/PreType/PreTypeResolve.Expressions.cs index 0c452ce41d3..c322843be8e 100644 --- a/Source/DafnyCore/Resolver/PreType/PreTypeResolve.Expressions.cs +++ b/Source/DafnyCore/Resolver/PreType/PreTypeResolve.Expressions.cs @@ -706,7 +706,7 @@ resolutionContext.CodeContext is ConstantField || int prevErrorCount = ErrorCount; ResolveStatement(e.S, resolutionContext); if (ErrorCount == prevErrorCount) { - if (e.S is UpdateStmt updateStmt && updateStmt.ResolvedStatements.Count == 1) { + if (e.S is AssignStatement updateStmt && updateStmt.ResolvedStatements.Count == 1) { var call = (CallStmt)updateStmt.ResolvedStatements[0]; if (call.Method is TwoStateLemma && !resolutionContext.IsTwoState) { ReportError(call, "two-state lemmas can only be used in two-state contexts"); diff --git a/Source/DafnyCore/Resolver/PreType/PreTypeResolve.Statements.cs b/Source/DafnyCore/Resolver/PreType/PreTypeResolve.Statements.cs index 3bd7dd2ab1b..1f083a172fd 100644 --- a/Source/DafnyCore/Resolver/PreType/PreTypeResolve.Statements.cs +++ b/Source/DafnyCore/Resolver/PreType/PreTypeResolve.Statements.cs @@ -184,17 +184,17 @@ public void ResolveStatement(Statement stmt, ResolutionContext resolutionContext } formals.Add(produceLhs); } - s.HiddenUpdate = new UpdateStmt(s.RangeToken, formals, s.Rhss, true); + s.HiddenUpdate = new AssignStatement(s.RangeToken, formals, s.Rhss, true); // resolving the update statement will check for return/yield statement specifics. ResolveStatement(s.HiddenUpdate, resolutionContext); } } - } else if (stmt is ConcreteUpdateStatement concreteUpdateStatement) { + } else if (stmt is ConcreteAssignStatement concreteUpdateStatement) { ResolveConcreteUpdateStmt(concreteUpdateStatement, null, resolutionContext); } else if (stmt is VarDeclStmt varDeclStmt) { - ResolveConcreteUpdateStmt(varDeclStmt.Update, varDeclStmt.Locals, resolutionContext); + ResolveConcreteUpdateStmt(varDeclStmt.Assign, varDeclStmt.Locals, resolutionContext); } else if (stmt is VarDeclPattern) { var s = (VarDeclPattern)stmt; @@ -217,8 +217,8 @@ public void ResolveStatement(Statement stmt, ResolutionContext resolutionContext ReportError(s.LHS.tok, "LHS is a constant literal; to be legal, it must introduce at least one bound variable"); } - } else if (stmt is AssignStmt) { - var s = (AssignStmt)stmt; + } else if (stmt is SingleAssignStmt) { + var s = (SingleAssignStmt)stmt; int prevErrorCount = ErrorCount; ResolveExpression(s.Lhs, resolutionContext); // allow ghosts for now, tightened up below bool lhsResolvedSuccessfully = ErrorCount == prevErrorCount; @@ -328,10 +328,10 @@ public void ResolveStatement(Statement stmt, ResolutionContext resolutionContext // The effect of Assign and the postcondition of Call will be seen outside the forall // statement. Statement s0 = s.S0; - if (s0 is AssignStmt) { + if (s0 is SingleAssignStmt) { s.Kind = ForallStmt.BodyKind.Assign; - var rhs = ((AssignStmt)s0).Rhs; + var rhs = ((SingleAssignStmt)s0).Rhs; if (rhs is TypeRhs) { ReportError(rhs.Tok, "new allocation not supported in aggregate assignments"); } @@ -525,10 +525,10 @@ private void ResolveCalc(CalcStmt s, ResolutionContext resolutionContext) { Contract.Assert(prevErrorCount != ErrorCount || s.Steps.Count == s.Hints.Count); } - private void ResolveConcreteUpdateStmt(ConcreteUpdateStatement update, List locals, ResolutionContext resolutionContext) { - Contract.Requires(update != null || locals != null); + private void ResolveConcreteUpdateStmt(ConcreteAssignStatement assign, List locals, ResolutionContext resolutionContext) { + Contract.Requires(assign != null || locals != null); // We have four cases. - Contract.Assert(update == null || update is AssignSuchThatStmt || update is UpdateStmt || update is AssignOrReturnStmt); + Contract.Assert(assign == null || assign is AssignSuchThatStmt || assign is AssignStatement || assign is AssignOrReturnStmt); // 0. There is no update. This is easy, we will just resolve the locals. // 1. The update is an AssignSuchThatStmt. This is also straightforward: first // resolve the locals, which adds them to the scope, and then resolve the update. @@ -542,11 +542,11 @@ private void ResolveConcreteUpdateStmt(ConcreteUpdateStatement update, List - private void ResolveUpdateStmt(UpdateStmt update, ResolutionContext resolutionContext, + private void ResolveUpdateStmt(AssignStatement update, ResolutionContext resolutionContext, int errorCountBeforeCheckingStmt) { Contract.Requires(update != null); Contract.Requires(resolutionContext != null); @@ -658,7 +658,7 @@ private void ResolveUpdateStmt(UpdateStmt update, ResolutionContext resolutionCo } else if (ErrorCount == errorCountBeforeCheckingStmt) { // add the statements here in a sequence, but don't use that sequence later for translation (instead, should translate properly as multi-assignment) for (var i = 0; i < update.Lhss.Count; i++) { - var a = new AssignStmt(update.RangeToken, update.Lhss[i].Resolved, update.Rhss[i]); + var a = new SingleAssignStmt(update.RangeToken, update.Lhss[i].Resolved, update.Rhss[i]); update.ResolvedStatements.Add(a); } } @@ -678,7 +678,7 @@ private void ResolveUpdateStmt(UpdateStmt update, ResolutionContext resolutionCo if (tr.CanAffectPreviouslyKnownExpressions) { ReportError(tr.Tok, "can only have initialization methods which modify at most 'this'."); } else if (ErrorCount == errorCountBeforeCheckingStmt) { - var a = new AssignStmt(update.RangeToken, update.Lhss[0].Resolved, tr); + var a = new SingleAssignStmt(update.RangeToken, update.Lhss[0].Resolved, tr); update.ResolvedStatements.Add(a); } } @@ -699,7 +699,7 @@ private void ResolveUpdateStmt(UpdateStmt update, ResolutionContext resolutionCo "the number of left-hand sides ({0}) and right-hand sides ({1}) must match for a multi-assignment", update.Lhss.Count, update.Rhss.Count); } else if (ErrorCount == errorCountBeforeCheckingStmt) { - var a = new AssignStmt(update.RangeToken, update.Lhss[0].Resolved, update.Rhss[0]); + var a = new SingleAssignStmt(update.RangeToken, update.Lhss[0].Resolved, update.Rhss[0]); update.ResolvedStatements.Add(a); } } else if (ErrorCount == errorCountBeforeCheckingStmt) { @@ -993,7 +993,7 @@ private void ResolveAssignOrReturnStmt(AssignOrReturnStmt s, ResolutionContext r } } // " temp, ... := MethodOrExpression, ...;" - var up = new UpdateStmt(s.RangeToken, lhss2, rhss2); + var up = new AssignStatement(s.RangeToken, lhss2, rhss2); if (expectExtract) { up.OriginalInitialLhs = s.Lhss.Count == 0 ? null : s.Lhss[0]; } @@ -1028,7 +1028,7 @@ private void ResolveAssignOrReturnStmt(AssignOrReturnStmt s, ResolutionContext r new IfStmt(s.RangeToken, false, resolver.VarDotMethod(s.Tok, temp, "IsFailure"), // THEN: { out := temp.PropagateFailure(); return; } new BlockStmt(s.RangeToken, new List() { - new UpdateStmt(s.RangeToken, + new AssignStatement(s.RangeToken, new List() { ident }, new List() {new ExprRhs(resolver.VarDotMethod(s.Tok, temp, "PropagateFailure"))} ), @@ -1043,7 +1043,7 @@ private void ResolveAssignOrReturnStmt(AssignOrReturnStmt s, ResolutionContext r // "y := temp.Extract();" var lhs = s.Lhss[0]; s.ResolvedStatements.Add( - new UpdateStmt(s.RangeToken, + new AssignStatement(s.RangeToken, new List() { lhsExtract }, new List() { new ExprRhs(resolver.VarDotMethod(s.Tok, temp, "Extract")) } )); diff --git a/Source/DafnyCore/Resolver/PreType/PreTypeToType.cs b/Source/DafnyCore/Resolver/PreType/PreTypeToType.cs index 1d094c78df7..4a66773e93e 100644 --- a/Source/DafnyCore/Resolver/PreType/PreTypeToType.cs +++ b/Source/DafnyCore/Resolver/PreType/PreTypeToType.cs @@ -212,7 +212,7 @@ protected override bool VisitOneStatement(Statement stmt, IASTVisitorContext con protected override void PostVisitOneStatement(Statement stmt, IASTVisitorContext context) { if (stmt is VarDeclPattern varDeclPattern) { VisitPattern(varDeclPattern.LHS, context); - } else if (stmt is AssignStmt { Rhs: TypeRhs tRhs }) { + } else if (stmt is SingleAssignStmt { Rhs: TypeRhs tRhs }) { Type rhsType; // convert the type of the RHS, which we expect to be a reference type, and then create the non-null version of it var udtConvertedFromPretype = (UserDefinedType)PreType2TypeUtil.PreType2FixedType(tRhs.PreType); diff --git a/Source/DafnyCore/Resolver/PreType/TypeRefinementVisitor.cs b/Source/DafnyCore/Resolver/PreType/TypeRefinementVisitor.cs index 6cf6bfa9917..6405aacb7f2 100644 --- a/Source/DafnyCore/Resolver/PreType/TypeRefinementVisitor.cs +++ b/Source/DafnyCore/Resolver/PreType/TypeRefinementVisitor.cs @@ -328,7 +328,7 @@ Func GetPatternArgumentType(int argumentIndex) { protected override void PostVisitOneStatement(Statement stmt, IASTVisitorContext context) { if (stmt is VarDeclPattern varDeclPattern) { VisitPattern(varDeclPattern.LHS, () => TypeRefinementWrapper.NormalizeSansBottom(varDeclPattern.RHS), context); - } else if (stmt is AssignStmt { Lhs: IdentifierExpr lhsIdentifierExpr } assignStmt) { + } else if (stmt is SingleAssignStmt { Lhs: IdentifierExpr lhsIdentifierExpr } assignStmt) { if (assignStmt is { Rhs: ExprRhs exprRhs }) { flows.Add(new FlowIntoVariable(lhsIdentifierExpr.Var, exprRhs.Expr, assignStmt.tok, ":=")); } else if (assignStmt is { Rhs: TypeRhs tRhs }) { diff --git a/Source/DafnyCore/Resolver/TailRecursion.cs b/Source/DafnyCore/Resolver/TailRecursion.cs index 2fab9f2bf1f..b9ed5f9c242 100644 --- a/Source/DafnyCore/Resolver/TailRecursion.cs +++ b/Source/DafnyCore/Resolver/TailRecursion.cs @@ -104,8 +104,8 @@ TailRecursionStatus CheckTailRecursive(Statement stmt, Method enclosingMethod, r if (s.HiddenUpdate != null) { return CheckTailRecursive(s.HiddenUpdate, enclosingMethod, ref tailCall, reportErrors); } - } else if (stmt is AssignStmt) { - var s = (AssignStmt)stmt; + } else if (stmt is SingleAssignStmt) { + var s = (SingleAssignStmt)stmt; if (s.Rhs is TypeRhs tRhs && tRhs.InitCall != null && tRhs.InitCall.Method == enclosingMethod) { // It's a recursive call. However, it is not a tail call, because after the "new" allocation // and init call have taken place, the newly allocated object has yet to be assigned to @@ -250,8 +250,8 @@ TailRecursionStatus CheckTailRecursive(Statement stmt, Method enclosingMethod, r // TODO this should be the conservative choice, but probably we can consider this to be tail-recursive // under some conditions? However, how does this interact with compiling to exceptions? return TailRecursionStatus.NotTailRecursive; - } else if (stmt is UpdateStmt) { - var s = (UpdateStmt)stmt; + } else if (stmt is AssignStatement) { + var s = (AssignStatement)stmt; var ss = s.ResolvedStatements; if (ss.Count == 1) { return CheckTailRecursive(ss, enclosingMethod, ref tailCall, reportErrors); @@ -262,8 +262,8 @@ TailRecursionStatus CheckTailRecursive(Statement stmt, Method enclosingMethod, r } } else if (stmt is VarDeclStmt) { var s = (VarDeclStmt)stmt; - if (s.Update != null) { - return CheckTailRecursive(s.Update, enclosingMethod, ref tailCall, reportErrors); + if (s.Assign != null) { + return CheckTailRecursive(s.Assign, enclosingMethod, ref tailCall, reportErrors); } } else if (stmt is VarDeclPattern) { } else if (stmt is ExpectStmt) { diff --git a/Source/DafnyCore/Rewriters/AutoContractsRewriter.cs b/Source/DafnyCore/Rewriters/AutoContractsRewriter.cs index 9840002b685..743e5ac87ff 100644 --- a/Source/DafnyCore/Rewriters/AutoContractsRewriter.cs +++ b/Source/DafnyCore/Rewriters/AutoContractsRewriter.cs @@ -284,7 +284,7 @@ void ProcessClassPostResolve(ClassLikeDecl cl) { // Repr := {this}; var e = new SetDisplayExpr(tok, true, new List() { self }); e.Type = systemModuleManager.ObjectSetType(); - Statement s = new AssignStmt(member.RangeToken, Repr, new ExprRhs(e)); + Statement s = new SingleAssignStmt(member.RangeToken, Repr, new ExprRhs(e)); s.IsGhost = true; sbs.AppendStmt(s); } @@ -375,7 +375,7 @@ void AddSubobjectReprs(IToken tok, IToken endCurlyTok, List e.Resolved is IdentifierExpr); } else if (s is CallStmt) { return false; diff --git a/Source/DafnyCore/Rewriters/ExpectContracts.cs b/Source/DafnyCore/Rewriters/ExpectContracts.cs index 7d66ed412b2..0fd37f1fa3d 100644 --- a/Source/DafnyCore/Rewriters/ExpectContracts.cs +++ b/Source/DafnyCore/Rewriters/ExpectContracts.cs @@ -129,13 +129,13 @@ private MemberDecl GenerateFunctionWrapper(TopLevelDeclWithMembers parent, Membe var lhss = new List { localExpr }; var rhss = new List { callRhs }; - var assignStmt = new AssignStmt(decl.RangeToken, localExpr, callRhs); + var assignStmt = new SingleAssignStmt(decl.RangeToken, localExpr, callRhs); Statement callStmt; if (origFunc.Result?.Name is null) { var local = new LocalVariable(decl.RangeToken, localName, newFunc.ResultType, false); local.type = newFunc.ResultType; var locs = new List { local }; - var varDeclStmt = new VarDeclStmt(decl.RangeToken, locs, new UpdateStmt(decl.RangeToken, lhss, rhss) { + var varDeclStmt = new VarDeclStmt(decl.RangeToken, locs, new AssignStatement(decl.RangeToken, lhss, rhss) { ResolvedStatements = new List() { assignStmt } }); localExpr.Var = local; diff --git a/Source/DafnyCore/Rewriters/ForallStmtRewriter.cs b/Source/DafnyCore/Rewriters/ForallStmtRewriter.cs index 40c9a482885..78bc809eeae 100644 --- a/Source/DafnyCore/Rewriters/ForallStmtRewriter.cs +++ b/Source/DafnyCore/Rewriters/ForallStmtRewriter.cs @@ -89,7 +89,7 @@ private void VisitAssign(ForallStmt stmt) { return; } - var s0 = (AssignStmt)stmt.S0; + var s0 = (SingleAssignStmt)stmt.S0; if (s0.Rhs is not ExprRhs exprRhs) { return; } diff --git a/Source/DafnyCore/Rewriters/RefinementTransformer.cs b/Source/DafnyCore/Rewriters/RefinementTransformer.cs index 8342c9a3909..dfbce39d164 100644 --- a/Source/DafnyCore/Rewriters/RefinementTransformer.cs +++ b/Source/DafnyCore/Rewriters/RefinementTransformer.cs @@ -1191,16 +1191,16 @@ List MergeStmtList(List skeleton, List oldStmt, if (oldS is VarDeclStmt) { var cOld = (VarDeclStmt)oldS; if (LocalVarsAgree(cOld.Locals, cNew.Locals)) { - var update = cNew.Update as UpdateStmt; + var update = cNew.Assign as AssignStatement; if (update != null && update.Rhss.TrueForAll(rhs => !rhs.CanAffectPreviouslyKnownExpressions)) { // Note, we allow switching between ghost and non-ghost, since that seems unproblematic. - if (cOld.Update == null) { + if (cOld.Assign == null) { doMerge = true; - } else if (cOld.Update is AssignSuchThatStmt) { + } else if (cOld.Assign is AssignSuchThatStmt) { doMerge = true; - addedAssert = refinementCloner.CloneExpr(((AssignSuchThatStmt)cOld.Update).Expr); + addedAssert = refinementCloner.CloneExpr(((AssignSuchThatStmt)cOld.Assign).Expr); } else { - var updateOld = (UpdateStmt)cOld.Update; // if cast fails, there are more ConcreteUpdateStatement subclasses than expected + var updateOld = (AssignStatement)cOld.Assign; // if cast fails, there are more ConcreteUpdateStatement subclasses than expected doMerge = true; foreach (var rhs in updateOld.Rhss) { if (!(rhs is HavocRhs)) { @@ -1224,13 +1224,13 @@ List MergeStmtList(List skeleton, List oldStmt, i++; } - } else if (cur is AssignStmt) { - var cNew = (AssignStmt)cur; - var cOld = oldS as AssignStmt; - if (cOld == null && oldS is UpdateStmt) { - var us = (UpdateStmt)oldS; + } else if (cur is SingleAssignStmt) { + var cNew = (SingleAssignStmt)cur; + var cOld = oldS as SingleAssignStmt; + if (cOld == null && oldS is AssignStatement) { + var us = (AssignStatement)oldS; if (us.ResolvedStatements.Count == 1) { - cOld = us.ResolvedStatements[0] as AssignStmt; + cOld = us.ResolvedStatements[0] as SingleAssignStmt; } } bool doMerge = false; @@ -1252,12 +1252,12 @@ List MergeStmtList(List skeleton, List oldStmt, i++; } - } else if (cur is UpdateStmt) { - var nw = (UpdateStmt)cur; + } else if (cur is AssignStatement) { + var nw = (AssignStatement)cur; List stmtGenerated = new List(); bool doMerge = false; - if (oldS is UpdateStmt) { - var s = (UpdateStmt)oldS; + if (oldS is AssignStatement) { + var s = (AssignStatement)oldS; if (LeftHandSidesAgree(s.Lhss, nw.Lhss)) { doMerge = true; stmtGenerated.Add(nw); @@ -1419,8 +1419,8 @@ bool PotentialMatch(Statement nxt, Statement other) { } else if (other is BlockStmt && ((BlockStmt)other).Labels == null) { return true; // both are unlabeled } - } else if (nxt is UpdateStmt) { - var up = (UpdateStmt)nxt; + } else if (nxt is AssignStatement) { + var up = (AssignStatement)nxt; if (other is AssignSuchThatStmt) { var oth = other as AssignSuchThatStmt; return oth != null && LeftHandSidesAgree(oth.Lhss, up.Lhss); @@ -1528,15 +1528,15 @@ void CheckIsOkayNewStatement(Statement s, Stack labels, int loopLevels) if (b.TargetLabel != null ? !labels.Contains(b.TargetLabel.val) : loopLevels < b.BreakAndContinueCount) { Error(ErrorId.ref_invalid_break_in_skeleton, s, $"{b.Kind} statement in skeleton is not allowed to break outside the skeleton fragment"); } - } else if (s is AssignStmt) { + } else if (s is SingleAssignStmt) { // TODO: To be a refinement automatically (that is, without any further verification), only variables and fields defined // in this module are allowed. This needs to be checked. If the LHS refers to an l-value that was not declared within // this module, then either an error should be reported or the Translator needs to know to translate new proof obligations. - var a = (AssignStmt)s; + var a = (SingleAssignStmt)s; Error(ErrorId.ref_misplaced_assignment, a.Tok, "cannot have assignment statement"); - } else if (s is ConcreteUpdateStatement) { + } else if (s is ConcreteAssignStatement) { postTasks.Enqueue(() => { - CheckIsOkayUpdateStmt((ConcreteUpdateStatement)s, moduleUnderConstruction); + CheckIsOkayUpdateStmt((ConcreteAssignStatement)s, moduleUnderConstruction); }); } else if (s is CallStmt) { Error(ErrorId.ref_misplaced_call, s.Tok, "cannot have call statement"); @@ -1555,7 +1555,7 @@ void CheckIsOkayNewStatement(Statement s, Stack labels, int loopLevels) } // Checks that statement stmt, defined in the constructed module m, is a refinement of skip in the parent module - void CheckIsOkayUpdateStmt(ConcreteUpdateStatement stmt, ModuleDefinition m) { + void CheckIsOkayUpdateStmt(ConcreteAssignStatement stmt, ModuleDefinition m) { foreach (var lhs in stmt.Lhss) { var l = lhs.Resolved; if (l is IdentifierExpr) { @@ -1575,8 +1575,8 @@ void CheckIsOkayUpdateStmt(ConcreteUpdateStatement stmt, ModuleDefinition m) { Error(ErrorId.ref_invalid_new_assignments, l.tok, "new assignments in a refinement method can only assign to state that the module defines (which never includes array elements)"); } } - if (stmt is UpdateStmt) { - var s = (UpdateStmt)stmt; + if (stmt is AssignStatement) { + var s = (AssignStatement)stmt; foreach (var rhs in s.Rhss) { if (rhs.CanAffectPreviouslyKnownExpressions) { Error(ErrorId.ref_invalid_assignment_rhs, rhs.Tok, "assignment RHS in refinement method is not allowed to affect previously defined state"); diff --git a/Source/DafnyCore/Rewriters/RunAllTestsMainMethod.cs b/Source/DafnyCore/Rewriters/RunAllTestsMainMethod.cs index d158261d849..6da318076d5 100644 --- a/Source/DafnyCore/Rewriters/RunAllTestsMainMethod.cs +++ b/Source/DafnyCore/Rewriters/RunAllTestsMainMethod.cs @@ -246,7 +246,7 @@ private BlockStmt PrintTestFailureStatement(IToken tok, Expression successVarExp failureValueExpr, Expression.CreateStringLiteral(tok, "\n")); var failSuiteStmt = - new AssignStmt(tok.ToRange(), successVarExpr, new ExprRhs(Expression.CreateBoolLiteral(tok, false))); + new SingleAssignStmt(tok.ToRange(), successVarExpr, new ExprRhs(Expression.CreateBoolLiteral(tok, false))); return new BlockStmt(tok.ToRange(), Util.List(failedPrintStmt, failSuiteStmt)); } } \ No newline at end of file diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs index d0b3ed230ff..f7c74cc5099 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs @@ -90,7 +90,7 @@ public void RemoveDefiniteAssignmentTrackers(List ss, int prevDefAssT Contract.Requires(ss != null); foreach (var s in ss) { if (s is VarDeclStmt vdecl) { - if (vdecl.Update is AssignOrReturnStmt ars) { + if (vdecl.Assign is AssignOrReturnStmt ars) { foreach (var sx in ars.ResolvedStatements) { if (sx is VarDeclStmt vdecl2) { vdecl2.Locals.ForEach(RemoveDefiniteAssignmentTracker); diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs index 17613aea422..3548e74157c 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs @@ -21,7 +21,7 @@ private void TrForallStmt(BoogieStmtListBuilder builder, List locals, if (forallStmt.BoundVars.Count == 0) { TrStmt(forallStmt.Body, builder, locals, etran); } else { - var s0 = (AssignStmt)forallStmt.S0; + var s0 = (SingleAssignStmt)forallStmt.S0; var definedness = new BoogieStmtListBuilder(this, options, builder.Context); var updater = new BoogieStmtListBuilder(this, options, builder.Context); DefineFuelConstant(forallStmt.Tok, forallStmt.Attributes, definedness, etran); @@ -209,7 +209,7 @@ void TrForallStmtCall(IToken tok, List boundVars, List bo } } - void TrForallAssign(ForallStmt s, AssignStmt s0, + void TrForallAssign(ForallStmt s, SingleAssignStmt s0, BoogieStmtListBuilder definedness, BoogieStmtListBuilder updater, List locals, ExpressionTranslator etran) { // The statement: // forall (x,y | Range(x,y)) { diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs index 8feffecb48c..2a5ad694261 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs @@ -410,7 +410,7 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, var subStmts = TransitiveSubstatements(loop); var modifiedVars = subStmts - .OfType() + .OfType() .Select(s => s.Lhs) .OfType(); foreach (var ie in modifiedVars) { diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs index 5d422ce00d8..d4cf1bc81a2 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs @@ -232,16 +232,16 @@ public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, builder.Add(TrAssumeCmdWithDependencies(etran, s.Tok, s.Expr, "assign-such-that constraint")); builder.AddCaptureState(s); // just do one capture state--here, at the very end (that is, don't do one before the assume) - } else if (stmt is UpdateStmt statement) { + } else if (stmt is AssignStatement statement) { TrUpdateStmt(builder, locals, etran, statement); } else if (stmt is AssignOrReturnStmt) { AddComment(builder, stmt, "assign-or-return statement (:-)"); AssignOrReturnStmt s = (AssignOrReturnStmt)stmt; TrStmtList(s.ResolvedStatements, builder, locals, etran); - } else if (stmt is AssignStmt) { + } else if (stmt is SingleAssignStmt) { AddComment(builder, stmt, "assignment statement"); - AssignStmt s = (AssignStmt)stmt; + SingleAssignStmt s = (SingleAssignStmt)stmt; TrAssignment(stmt, s.Lhs.Resolved, s.Rhs, builder, locals, etran); } else if (stmt is CallStmt) { @@ -412,7 +412,7 @@ void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { } } - private void TrUpdateStmt(BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, UpdateStmt statement) { + private void TrUpdateStmt(BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, AssignStatement statement) { // This UpdateStmt can be single-target assignment, a multi-assignment, a call statement, or // an array-range update. Handle the multi-assignment here and handle the others as for .ResolvedStatements. var resolved = statement.ResolvedStatements; @@ -420,7 +420,7 @@ private void TrUpdateStmt(BoogieStmtListBuilder builder, List locals, TrStmt(resolved[0], builder, locals, etran); } else { AddComment(builder, statement, "update statement"); - var assignStmts = resolved.Cast().ToList(); + var assignStmts = resolved.Cast().ToList(); var lhss = assignStmts.Select(a => a.Lhs).ToList(); var rhss = assignStmts.Select(a => a.Rhs).ToList(); // note: because we have more than one expression, we always must assign to Boogie locals in a two @@ -455,10 +455,10 @@ void TrVarDeclStmt(VarDeclStmt varDeclStmt, BoogieStmtListBuilder builder, List< new Bpl.IdentifierExpr(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator), varType), local.Type, etran, isAllocContext.Var(varDeclStmt.IsGhost, local)); // if needed, register definite-assignment tracking for this local - var needDefiniteAssignmentTracking = varDeclStmt.Update == null || varDeclStmt.Update is AssignSuchThatStmt; - if (varDeclStmt.Update is UpdateStmt) { + var needDefiniteAssignmentTracking = varDeclStmt.Assign == null || varDeclStmt.Assign is AssignSuchThatStmt; + if (varDeclStmt.Assign is AssignStatement) { // there is an initial assignment, but we need to look out for "*" being that assignment - var us = (UpdateStmt)varDeclStmt.Update; + var us = (AssignStatement)varDeclStmt.Assign; if (i < us.Rhss.Count && us.Rhss[i] is HavocRhs) { needDefiniteAssignmentTracking = true; } @@ -483,7 +483,7 @@ void TrVarDeclStmt(VarDeclStmt varDeclStmt, BoogieStmtListBuilder builder, List< locals.Add(var); i++; } - if (varDeclStmt.Update == null) { + if (varDeclStmt.Assign == null) { // it is necessary to do a havoc here in order to give the variable the correct allocation state builder.Add(new HavocCmd(varDeclStmt.Tok, newLocalIds)); } @@ -494,8 +494,8 @@ void TrVarDeclStmt(VarDeclStmt varDeclStmt, BoogieStmtListBuilder builder, List< 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 (varDeclStmt.Update != null) { - TrStmt(varDeclStmt.Update, builder, locals, etran); + if (varDeclStmt.Assign != null) { + TrStmt(varDeclStmt.Assign, builder, locals, etran); } } diff --git a/Source/DafnyLanguageServer/Language/GhostStateDiagnosticCollector.cs b/Source/DafnyLanguageServer/Language/GhostStateDiagnosticCollector.cs index dd590d4e4cf..61669bad221 100644 --- a/Source/DafnyLanguageServer/Language/GhostStateDiagnosticCollector.cs +++ b/Source/DafnyLanguageServer/Language/GhostStateDiagnosticCollector.cs @@ -84,12 +84,12 @@ private bool IsGhostStatementToMark(Statement statement) { private static Range GetRange(Statement statement) { return statement switch { - UpdateStmt updateStatement => GetRange(updateStatement), + AssignStatement updateStatement => GetRange(updateStatement), _ => CreateRange(statement.RangeToken.StartToken, statement.RangeToken.EndToken) }; } - private static Range GetRange(UpdateStmt updateStatement) { + private static Range GetRange(AssignStatement updateStatement) { IToken startToken; if (updateStatement.Lhss.Count > 0) { startToken = updateStatement.Lhss[0].tok; diff --git a/Source/DafnyLanguageServer/Language/ImplicitFailingAssertionCodeActionProvider.cs b/Source/DafnyLanguageServer/Language/ImplicitFailingAssertionCodeActionProvider.cs index f44928fedea..70c9006fe21 100644 --- a/Source/DafnyLanguageServer/Language/ImplicitFailingAssertionCodeActionProvider.cs +++ b/Source/DafnyLanguageServer/Language/ImplicitFailingAssertionCodeActionProvider.cs @@ -72,7 +72,7 @@ public override IEnumerable GetEdits() { var node = nodesTillFailure[i]; var nextNode = i < nodesTillFailure.Count - 1 ? nodesTillFailure[i + 1] : null; if (node is Statement or LetExpr && - node is not UpdateStmt && nextNode is not VarDeclStmt && nextNode is not AssignSuchThatStmt) { + node is not AssignStatement && nextNode is not VarDeclStmt && nextNode is not AssignSuchThatStmt) { insertionNode = node; break; } diff --git a/Source/DafnyLanguageServer/Language/SyntaxTreeVisitor.cs b/Source/DafnyLanguageServer/Language/SyntaxTreeVisitor.cs index 38000562ddb..7e6f1170b96 100644 --- a/Source/DafnyLanguageServer/Language/SyntaxTreeVisitor.cs +++ b/Source/DafnyLanguageServer/Language/SyntaxTreeVisitor.cs @@ -185,7 +185,7 @@ public virtual void Visit(Statement statement) { case VarDeclStmt variableDeclarationStatement: Visit(variableDeclarationStatement); break; - case UpdateStmt updateStatement: + case AssignStatement updateStatement: Visit(updateStatement); break; case AssertStmt assertStatement: @@ -329,12 +329,12 @@ public virtual void Visit(VarDeclStmt variableDeclarationStatement) { foreach (var localVariable in variableDeclarationStatement.Locals) { Visit(localVariable); } - if (variableDeclarationStatement.Update != null) { - Visit(variableDeclarationStatement.Update); + if (variableDeclarationStatement.Assign != null) { + Visit(variableDeclarationStatement.Assign); } } - public virtual void Visit(UpdateStmt updateStatement) { + public virtual void Visit(AssignStatement updateStatement) { VisitNullableAttributes(updateStatement.Attributes); foreach (var leftHandSide in updateStatement.Lhss) { Visit(leftHandSide); diff --git a/Source/DafnyServer/SuperLegacySymbolTable.cs b/Source/DafnyServer/SuperLegacySymbolTable.cs index 7f5ce9ab86b..193949462dd 100644 --- a/Source/DafnyServer/SuperLegacySymbolTable.cs +++ b/Source/DafnyServer/SuperLegacySymbolTable.cs @@ -138,7 +138,7 @@ private static IEnumerable ResolveLocalDefinitions(IEnumerabl var declarations = (VarDeclStmt)statement; { Type type = null; - var rightSide = declarations.Update as UpdateStmt; + var rightSide = declarations.Assign as AssignStatement; if (rightSide != null) { var definition = rightSide.Rhss.First(); var typeDef = definition as TypeRhs; @@ -162,8 +162,8 @@ private static IEnumerable ResolveLocalDefinitions(IEnumerabl } } } - if (statement is UpdateStmt) { - var updateStatement = statement as UpdateStmt; + if (statement is AssignStatement) { + var updateStatement = statement as AssignStatement; var lefts = updateStatement.Lhss; foreach (var expression in lefts) { if (expression is AutoGhostIdentifierExpr) { @@ -191,7 +191,7 @@ private static IEnumerable ResolveCallStatements(IEnumerable< foreach (var statement in statements) { if (statement is CallStmt) { ParseCallStatement(statement, information); - } else if (statement is UpdateStmt) { + } else if (statement is AssignStatement) { ParseUpdateStatement(statement, information); } @@ -224,7 +224,7 @@ private static void ParseCallStatement(Statement statement, List information) { - var updateStmt = (UpdateStmt)statement; + var updateStmt = (AssignStatement)statement; var leftSide = updateStmt.Lhss; var rightSide = updateStmt.Rhss; var leftSideDots = leftSide.OfType(); @@ -300,8 +300,8 @@ private static ICollection GetAllSubExpressions(Expression expressio private static IEnumerable ParseBodyForFieldReferences(IEnumerable block, string fieldName, string className, string moduleName) { var information = new List(); foreach (var statement in block) { - if (statement is UpdateStmt) { - var updateStmt = (UpdateStmt)statement; + if (statement is AssignStatement) { + var updateStmt = (AssignStatement)statement; var leftSide = updateStmt.Lhss; var rightSide = updateStmt.Rhss; var leftSideDots = leftSide.OfType(); diff --git a/Source/DafnyTestGeneration.Test/ShortCircuitRemoval.cs b/Source/DafnyTestGeneration.Test/ShortCircuitRemoval.cs index 3ad54186dbd..8362ef5e718 100644 --- a/Source/DafnyTestGeneration.Test/ShortCircuitRemoval.cs +++ b/Source/DafnyTestGeneration.Test/ShortCircuitRemoval.cs @@ -474,8 +474,8 @@ function PropagateFailure():Result {this} var blockStmt = resultingMethod.Body.Body[1] as BlockStmt; Assert.True(blockStmt.Body[0] is AssignOrReturnStmt); var assignOrReturn = blockStmt.Body[0] as AssignOrReturnStmt; // :- Fail() prior to desugaring - Assert.True(assignOrReturn.Children.ToList()[1] is UpdateStmt); - var updateStmt = assignOrReturn.Children.ToList()[1] as UpdateStmt; // := Fail(), which is part of desugaring + Assert.True(assignOrReturn.Children.ToList()[1] is AssignStatement); + var updateStmt = assignOrReturn.Children.ToList()[1] as AssignStatement; // := Fail(), which is part of desugaring Assert.Contains(updateStmt.ResolvedStatements, statement => statement is CallStmt); // Fail() is a method call } @@ -506,8 +506,8 @@ function Extract():T requires !IsFailure() {value} var blockStmt = resultingMethod.Body.Body[1] as BlockStmt; Assert.True(blockStmt.Body[0] is VarDeclStmt); var varDeclStmt = blockStmt.Body[0] as VarDeclStmt; // x :- Fail() prior to desugaring - Assert.True(varDeclStmt.Update.Children.ToList()[1] is UpdateStmt); - var updateStmt = varDeclStmt.Update.Children.ToList()[1] as UpdateStmt; // x := Fail(), which is part of desugaring + Assert.True(varDeclStmt.Assign.Children.ToList()[1] is AssignStatement); + var updateStmt = varDeclStmt.Assign.Children.ToList()[1] as AssignStatement; // x := Fail(), which is part of desugaring Assert.Contains(updateStmt.ResolvedStatements, statement => statement is CallStmt); // Fail() is a method call } diff --git a/Source/DafnyTestGeneration/Inlining/FunctionCallToMethodCallRewriter.cs b/Source/DafnyTestGeneration/Inlining/FunctionCallToMethodCallRewriter.cs index 2fdfe1c8c68..fdd55f878bb 100644 --- a/Source/DafnyTestGeneration/Inlining/FunctionCallToMethodCallRewriter.cs +++ b/Source/DafnyTestGeneration/Inlining/FunctionCallToMethodCallRewriter.cs @@ -56,14 +56,14 @@ private void Visit(Method method) { } public override Statement CloneStmt(Statement stmt, bool isReference) { - if (stmt == null || stmt is not UpdateStmt updateStmt) { + if (stmt == null || stmt is not AssignStatement updateStmt) { return base.CloneStmt(stmt, isReference); } - var clonedUpdate = (UpdateStmt)base.CloneStmt(updateStmt, isReference); + var clonedUpdate = (AssignStatement)base.CloneStmt(updateStmt, isReference); var newResolvedStmts = new List(); foreach (var resolvedStmt in clonedUpdate.ResolvedStatements) { if (!resolvedStmt.IsGhost && - resolvedStmt is AssignStmt { Rhs: ExprRhs exprRhs } && + resolvedStmt is SingleAssignStmt { Rhs: ExprRhs exprRhs } && exprRhs.Expr.Resolved is FunctionCallExpr { IsByMethodCall: true } funcCallExpr) { var memberSelectExpr = new MemberSelectExpr( funcCallExpr.tok, diff --git a/Source/DafnyTestGeneration/Inlining/RemoveShortCircuitRewriter.cs b/Source/DafnyTestGeneration/Inlining/RemoveShortCircuitRewriter.cs index 18fac9df0a9..fb6f87290dd 100644 --- a/Source/DafnyTestGeneration/Inlining/RemoveShortCircuitRewriter.cs +++ b/Source/DafnyTestGeneration/Inlining/RemoveShortCircuitRewriter.cs @@ -124,7 +124,7 @@ public override Statement CloneStmt(Statement statement, bool isReference) { switch (statement) { case IfStmt ifStatement: return CloneIfStmt(ifStatement); - case UpdateStmt updateStatement: + case AssignStatement updateStatement: return CloneUpdateStmt(updateStatement); case ReturnStmt returnStatement: return CloneReturnStmt(returnStatement); @@ -202,8 +202,8 @@ private Statement CloneIfStmt(IfStmt ifStatement) { return new IfStmt(ifStatement.RangeToken, ifStatement.IsBindingGuard, guard, thn, els, ifStatement.Attributes); } - private Statement CloneUpdateStmt(UpdateStmt updateStatement) { - return new UpdateStmt(updateStatement.RangeToken, CloneExpressionList(updateStatement.Lhss), CloneRhss(updateStatement.Rhss, true)); + private Statement CloneUpdateStmt(AssignStatement updateStatement) { + return new AssignStatement(updateStatement.RangeToken, CloneExpressionList(updateStatement.Lhss), CloneRhss(updateStatement.Rhss, true)); } private Statement CloneAssignOrReturnStmt(AssignOrReturnStmt assignOrReturnStmt) { @@ -310,16 +310,16 @@ private Expression CreateIf(string tmpVarName, Type typ, Expression initialExpr, new List { new(new RangeToken(original.StartToken, original.StartToken), tmpVarName, typ, false) }, null); newStmtStack.Last().Add(varDecl); if (initialExpr != null) { - var updateStmt = new UpdateStmt(new RangeToken(original.StartToken, original.StartToken), new List { identifierExpr }, + var updateStmt = new AssignStatement(new RangeToken(original.StartToken, original.StartToken), new List { identifierExpr }, new List { new ExprRhs(initialExpr) }); newStmtStack.Last().Add(updateStmt); } - var thenStmt = new UpdateStmt( + var thenStmt = new AssignStatement( new RangeToken(thenToken, thenToken), new List { identifierExpr }, new List { new ExprRhs(thenExpr) }); var elseStmt = elseExpr != null - ? new UpdateStmt(new RangeToken(elseToken, elseToken), new List { identifierExpr }, + ? new AssignStatement(new RangeToken(elseToken, elseToken), new List { identifierExpr }, new List { new ExprRhs(elseExpr) }) : null; var ifStmt = new IfStmt(new RangeToken(original.StartToken, original.StartToken), false, testExpr, @@ -344,7 +344,7 @@ public override Expression CloneExpr(Expression expr) { VarDeclStmt varDecl = new VarDeclStmt( new RangeToken(expr.StartToken, expr.StartToken), new List { new(new RangeToken(expr.StartToken, expr.StartToken), tmpVarName, new InferredTypeProxy(), false) }, null); - UpdateStmt updateStmt; + AssignStatement assignStatement; int i = 0; switch (expr) { @@ -363,20 +363,20 @@ public override Expression CloneExpr(Expression expr) { Expression.CreateBoolLiteral(binaryExpr.E1.EndToken, true), binaryExpr); case StmtExpr stmtExpr: newStmtStack.Last().Add(varDecl); - updateStmt = new UpdateStmt(stmtExpr.E.RangeToken, new List { identifierExpr }, + assignStatement = new AssignStatement(stmtExpr.E.RangeToken, new List { identifierExpr }, new List { new ExprRhs(stmtExpr.E) }); var stmtBlockUpdate = new BlockStmt(new RangeToken(stmtExpr.S.StartToken, stmtExpr.E.EndToken), new List()); stmtBlockUpdate.Body.Add(stmtExpr.S); - stmtBlockUpdate.Body.Add(updateStmt); + stmtBlockUpdate.Body.Add(assignStatement); newStmtStack.Last().Add(stmtBlockUpdate); return identifierExpr; case NestedMatchExpr matchExpr: newStmtStack.Last().Add(varDecl); var caseStmts = new List(); foreach (var c in matchExpr.Cases) { - updateStmt = new UpdateStmt(new RangeToken(c.Body.StartToken, c.Body.StartToken), new List { identifierExpr }, + assignStatement = new AssignStatement(new RangeToken(c.Body.StartToken, c.Body.StartToken), new List { identifierExpr }, new List { new ExprRhs(c.Body) }); - caseStmts.Add(new NestedMatchCaseStmt(new RangeToken(c.StartToken, c.StartToken), c.Pat, new List { updateStmt })); + caseStmts.Add(new NestedMatchCaseStmt(new RangeToken(c.StartToken, c.StartToken), c.Pat, new List { assignStatement })); } var matchStmt = new NestedMatchStmt(matchExpr.RangeToken, matchExpr.Source, caseStmts, false, matchExpr.Attributes); newStmtStack.Last().Add(matchStmt); @@ -388,16 +388,16 @@ public override Expression CloneExpr(Expression expr) { varDecl = new VarDeclStmt( new RangeToken(letOrFailExpr.Lhs.Var.StartToken, letOrFailExpr.Rhs.EndToken), new List { new(letOrFailExpr.Lhs.Var.RangeToken, letOrFailExpr.Lhs.Var.Name, new InferredTypeProxy(), false) }, assignOrReturn); - updateStmt = new UpdateStmt(letOrFailExpr.Body.RangeToken, new List { identifierExpr }, + assignStatement = new AssignStatement(letOrFailExpr.Body.RangeToken, new List { identifierExpr }, new List { new ExprRhs(letOrFailExpr.Body) }); - newStmtStack.Last().Add(new BlockStmt(letOrFailExpr.RangeToken, new List { varDecl, updateStmt })); + newStmtStack.Last().Add(new BlockStmt(letOrFailExpr.RangeToken, new List { varDecl, assignStatement })); return identifierExpr; case LetOrFailExpr letOrFailExpr: newStmtStack.Last().Add(varDecl); var assignOrReturnNoLhs = new AssignOrReturnStmt(letOrFailExpr.Rhs.RangeToken, new List(), new ExprRhs(letOrFailExpr.Rhs), null, new List()); - updateStmt = new UpdateStmt(letOrFailExpr.Body.RangeToken, new List { identifierExpr }, + assignStatement = new AssignStatement(letOrFailExpr.Body.RangeToken, new List { identifierExpr }, new List { new ExprRhs(letOrFailExpr.Body) }); - newStmtStack.Last().Add(new BlockStmt(letOrFailExpr.RangeToken, new List { assignOrReturnNoLhs, updateStmt })); + newStmtStack.Last().Add(new BlockStmt(letOrFailExpr.RangeToken, new List { assignOrReturnNoLhs, assignStatement })); return identifierExpr; case LetExpr letExpr: if (letExpr.Exact == false || letExpr.BoundVars.Count() != letExpr.RHSs.Count) { @@ -408,28 +408,28 @@ public override Expression CloneExpr(Expression expr) { var blockUpdate = new BlockStmt(letExpr.RangeToken, new List()); foreach (var boundVar in letExpr.BoundVars) { identifierExpr = new IdentifierExpr(letExpr.RHSs[i].StartToken, boundVar.Name); - updateStmt = new UpdateStmt(letExpr.RHSs[i].RangeToken, new List { identifierExpr }, + assignStatement = new AssignStatement(letExpr.RHSs[i].RangeToken, new List { identifierExpr }, new List { new ExprRhs(letExpr.RHSs[i]) }); varDecl = new VarDeclStmt( new RangeToken(boundVar.StartToken, letExpr.RHSs[i].EndToken), - new List { new(boundVar.RangeToken, boundVar.Name, new InferredTypeProxy(), false) }, updateStmt); + new List { new(boundVar.RangeToken, boundVar.Name, new InferredTypeProxy(), false) }, assignStatement); blockUpdate.Body.Add(varDecl); i += 1; } identifierExpr = new IdentifierExpr(letExpr.Body.StartToken, tmpVarName); - updateStmt = new UpdateStmt(letExpr.Body.RangeToken, new List { identifierExpr }, + assignStatement = new AssignStatement(letExpr.Body.RangeToken, new List { identifierExpr }, new List { new ExprRhs(letExpr.Body) }); - blockUpdate.Body.Add(updateStmt); + blockUpdate.Body.Add(assignStatement); newStmtStack.Last().Add(blockUpdate); return identifierExpr; case ApplySuffix applySuffix: if (wasProcessingRhs) { break; } - updateStmt = new UpdateStmt(applySuffix.RangeToken, new List { identifierExpr }, + assignStatement = new AssignStatement(applySuffix.RangeToken, new List { identifierExpr }, new List { new ExprRhs(applySuffix) }); newStmtStack.Last().Add(varDecl); - newStmtStack.Last().Add(updateStmt); + newStmtStack.Last().Add(assignStatement); return identifierExpr; } nextVariableId--; // the new variable was not used in the end From 6b985cf4568c9221401904167ae1c22d9a366a80 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 20 Sep 2024 17:21:54 +0200 Subject: [PATCH 14/47] Add comments --- .../DafnyCore/AST/Statements/Assignment/AssignOrReturnStmt.cs | 3 +++ Source/DafnyCore/AST/Statements/Assignment/AssignStatement.cs | 3 +++ .../DafnyCore/AST/Statements/Assignment/AssignSuchThatStmt.cs | 3 +++ 3 files changed, 9 insertions(+) diff --git a/Source/DafnyCore/AST/Statements/Assignment/AssignOrReturnStmt.cs b/Source/DafnyCore/AST/Statements/Assignment/AssignOrReturnStmt.cs index dc6b087f523..249f3fed37c 100644 --- a/Source/DafnyCore/AST/Statements/Assignment/AssignOrReturnStmt.cs +++ b/Source/DafnyCore/AST/Statements/Assignment/AssignOrReturnStmt.cs @@ -5,6 +5,9 @@ namespace Microsoft.Dafny; +/// +/// Parsed from ":-" +/// public class AssignOrReturnStmt : ConcreteAssignStatement, ICloneable, ICanResolve { public readonly ExprRhs Rhs; // this is the unresolved RHS, and thus can also be a method call public readonly List Rhss; diff --git a/Source/DafnyCore/AST/Statements/Assignment/AssignStatement.cs b/Source/DafnyCore/AST/Statements/Assignment/AssignStatement.cs index 2c05b223a27..3e0ac6073eb 100644 --- a/Source/DafnyCore/AST/Statements/Assignment/AssignStatement.cs +++ b/Source/DafnyCore/AST/Statements/Assignment/AssignStatement.cs @@ -4,6 +4,9 @@ namespace Microsoft.Dafny; +/// +/// Parsed from ":=" +/// public class AssignStatement : ConcreteAssignStatement, ICloneable, ICanResolve { public readonly List Rhss; public readonly bool CanMutateKnownState; diff --git a/Source/DafnyCore/AST/Statements/Assignment/AssignSuchThatStmt.cs b/Source/DafnyCore/AST/Statements/Assignment/AssignSuchThatStmt.cs index de8d5966704..28ed81ef49c 100644 --- a/Source/DafnyCore/AST/Statements/Assignment/AssignSuchThatStmt.cs +++ b/Source/DafnyCore/AST/Statements/Assignment/AssignSuchThatStmt.cs @@ -5,6 +5,9 @@ namespace Microsoft.Dafny; +/// +/// Parsed from ":|" +/// public class AssignSuchThatStmt : ConcreteAssignStatement, ICloneable, ICanResolveNewAndOld { public readonly Expression Expr; public readonly AttributedToken AssumeToken; From fe456d38d7f2ff0121e8fa1f1da783b5e987a800 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Sat, 21 Sep 2024 12:22:14 +0200 Subject: [PATCH 15/47] Definite assignment tracking issue --- .../AST/Grammar/Printer/Printer.Statement.cs | 11 +++++++---- Source/DafnyCore/AST/Statements/BlockByProofStmt.cs | 10 +--------- .../Verifier/Statements/BlockByProofStmtVerifier.cs | 1 + 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs b/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs index 69846e90996..5444e24d27c 100644 --- a/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs +++ b/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs @@ -30,7 +30,7 @@ public partial class Printer { /// If the statement requires several lines, subsequent lines are indented at "indent". /// No newline is printed after the statement. /// - public void PrintStatement(Statement stmt, int indent) { + public void PrintStatement(Statement stmt, int indent, bool includeSemicolon = true) { Contract.Requires(stmt != null); if (stmt.IsGhost && printMode == PrintModes.NoGhostOrIncludes) { @@ -50,7 +50,7 @@ public void PrintStatement(Statement stmt, int indent) { } if (stmt is PredicateStmt) { - PrintPredicateStmt(stmt); + PrintPredicateStmt(stmt, includeSemicolon); } else if (stmt is PrintStmt) { PrintStmt s = (PrintStmt)stmt; wr.Write("print"); @@ -330,7 +330,7 @@ public void PrintStatement(Statement stmt, int indent) { } } else if (stmt is ConcreteAssignStatement) { - PrintConcreteUpdateStatement(stmt, indent); + PrintConcreteUpdateStatement(stmt, indent, includeSemicolon); } else if (stmt is CallStmt) { // Most calls are printed from their concrete syntax given in the input. However, recursive calls to // prefix lemmas end up as CallStmt's by the end of resolution and they may need to be printed here. @@ -361,7 +361,10 @@ public void PrintStatement(Statement stmt, int indent) { wr.Write(" "); PrintUpdateRHS(s.Assign, indent); } - wr.Write(";"); + + if (includeSemicolon) { + wr.Write(";"); + } } else if (stmt is VarDeclPattern) { var s = (VarDeclPattern)stmt; if (s.tok is AutoGeneratedToken) { diff --git a/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs b/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs index 05286b81aa2..0f944559a05 100644 --- a/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs +++ b/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs @@ -50,15 +50,7 @@ internal static void ResolveByProof(INewOrOldResolver resolver, BlockStmt proof, } public void Render(TextWriter wr, Printer printer, int indent) { - if (Body is AssertStmt assertStmt) { - printer.PrintPredicateStmt(assertStmt, false); - } else if (Body is ConcreteAssignStatement updateStmt) { - printer.PrintConcreteUpdateStatement(updateStmt, indent, false); - } else if (Body is BlockStmt blockStmt) { - printer.PrintBlockStmt(blockStmt, indent); - } else { - throw new NotImplementedException(); - } + printer.PrintStatement(Body, indent, false); wr.Write(" by "); printer.PrintBlockStmt(Proof, indent); } diff --git a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs index 46801118950..2304e4d6408 100644 --- a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs +++ b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs @@ -14,6 +14,7 @@ public static void EmitBoogie(BoogieGenerator generator, BlockByProofStmt block, generator.CurrentIdGenerator.Pop(); generator.TrStmt(block.Body, proofBuilder, locals, etran); + //generator.RemoveDefiniteAssignmentTrackers(new [] { block.Body }, prevDefiniteAssignmentTrackerCount); generator.PathAsideBlock(block.Tok, proofBuilder, builder); generator.TrStmt(block.Body, builder.WithContext(builder.Context with { AssertMode = AssertMode.Assume From 5af6b22889f28e2bb68ed1f9eb49b034855f1ae1 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Sat, 21 Sep 2024 12:43:28 +0200 Subject: [PATCH 16/47] Definite assignment tracker changed so it's never cleaned up --- .../BoogieGenerator.DefiniteAssignment.cs | 31 +++---------------- .../Verifier/BoogieGenerator.Methods.cs | 2 -- Source/DafnyCore/Verifier/BoogieGenerator.cs | 2 -- .../Statements/BoogieGenerator.TrLoop.cs | 11 +++++-- .../Statements/BoogieGenerator.TrStatement.cs | 10 ------ .../Statements/OpaqueBlockVerifier.cs | 6 ++-- 6 files changed, 15 insertions(+), 47 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs index f7c74cc5099..596d4bedd65 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs @@ -47,7 +47,11 @@ bool NeedsDefiniteAssignmentTracker(bool isGhost, Type type, bool isField) { return null; } - Bpl.Variable tracker; + if (DefiniteAssignmentTrackers.TryGetValue(p.UniqueName, out var result)) { + return result; + } + + Variable tracker; if (isOutParam) { tracker = new Bpl.Formal(p.Tok, new Bpl.TypedIdent(p.Tok, DefassPrefix + p.UniqueName, Bpl.Type.Bool), false); } else { @@ -86,31 +90,6 @@ void AddDefiniteAssignmentTrackerSurrogate(Field field, TopLevelDeclWithMembers DefiniteAssignmentTrackers.Add(nm, ie); } - public void RemoveDefiniteAssignmentTrackers(List ss, int prevDefAssTrackerCount) { - Contract.Requires(ss != null); - foreach (var s in ss) { - if (s is VarDeclStmt vdecl) { - if (vdecl.Assign is AssignOrReturnStmt ars) { - foreach (var sx in ars.ResolvedStatements) { - if (sx is VarDeclStmt vdecl2) { - vdecl2.Locals.ForEach(RemoveDefiniteAssignmentTracker); - } - } - } - - vdecl.Locals.ForEach(RemoveDefiniteAssignmentTracker); - } else if (s is AssignOrReturnStmt ars) { - foreach (var sx in ars.ResolvedStatements) { - if (sx is VarDeclStmt vdecl2) { - vdecl2.Locals.ForEach(RemoveDefiniteAssignmentTracker); - } - } - } - } - - Contract.Assert(prevDefAssTrackerCount == DefiniteAssignmentTrackers.Count); - } - void RemoveDefiniteAssignmentTracker(IVariable p) { Contract.Requires(p != null); DefiniteAssignmentTrackers.Remove(p.UniqueName); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index e9adfe3fa2e..22a391548f4 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -798,7 +798,6 @@ private StmtList TrMethodBody(Method m, BoogieStmtListBuilder builder, List AddExistingDefiniteAssignmentTracker(p, m.IsGhost)); // translate the body TrStmt(m.Body, builder, localVariables, etran); @@ -810,7 +809,6 @@ private StmtList TrMethodBody(Method m, BoogieStmtListBuilder builder, List>(); - foreach (var dat in DefiniteAssignmentTrackers.Values) { // TODO: the order is non-deterministic and may change between invocations of Dafny + var existingLocals = locals.ToList(); + foreach (var local in existingLocals) { + if (!DefiniteAssignmentTrackers.TryGetValue(local.Name, out var dat)) { + continue; + } var preLoopDat = new Bpl.LocalVariable(dat.tok, new Bpl.TypedIdent(dat.tok, "preLoop$" + suffix + "$" + dat.Name, dat.Type)); locals.Add(preLoopDat); var ie = new Bpl.IdentifierExpr(s.Tok, preLoopDat); daTrackersMonotonicity.Add(new Tuple(ie, dat)); - builder.Add(Bpl.Cmd.SimpleAssign(s.Tok, ie, dat)); + builder.Add(Cmd.SimpleAssign(s.Tok, ie, dat)); } - List initDecr = null; + List initDecr = null; if (!Contract.Exists(theDecreases, e => e is WildcardExpr)) { initDecr = RecordDecreasesValue(theDecreases, builder, locals, etran, "$decr_init$" + suffix); } diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs index d4cf1bc81a2..957cb31ec3d 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs @@ -251,7 +251,6 @@ public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, } else if (stmt is DividedBlockStmt) { var s = (DividedBlockStmt)stmt; AddComment(builder, stmt, "divided block before new;"); - var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; var tok = s.SeparatorTok ?? s.Tok; // a DividedBlockStmt occurs only inside a Constructor body of a class var cl = (ClassDecl)((Constructor)codeContext).EnclosingClass; @@ -286,16 +285,12 @@ public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, AddComment(builder, stmt, "divided block after new;"); TrStmtList(s.BodyProper, builder, locals, etran); - RemoveDefiniteAssignmentTrackers(s.Body, prevDefiniteAssignmentTrackerCount); - } else if (stmt is OpaqueBlock opaqueBlock) { OpaqueBlockVerifier.EmitBoogie(this, opaqueBlock, builder, locals, etran, (IMethodCodeContext)codeContext); } else if (stmt is BlockByProofStmt blockByProof) { BlockByProofStmtVerifier.EmitBoogie(this, blockByProof, builder, locals, etran, codeContext); } else if (stmt is BlockStmt blockStmt) { - var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; TrStmtList(blockStmt.Body, builder, locals, etran, blockStmt.RangeToken); - RemoveDefiniteAssignmentTrackers(blockStmt.Body, prevDefiniteAssignmentTrackerCount); } else if (stmt is IfStmt ifStmt) { TrIfStmt(ifStmt, builder, locals, etran); @@ -677,10 +672,7 @@ private void TrMatchStmt(MatchStmt stmt, BoogieStmtListBuilder builder, List alternatives, IToken elseToken, Act } else { b.Add(TrAssumeCmdWithDependencies(etran, alternative.Guard.tok, alternative.Guard, "alternative guard")); } - var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; TrStmtList(alternative.Body, b, locals, etran, alternative.RangeToken); - RemoveDefiniteAssignmentTrackers(alternative.Body, prevDefiniteAssignmentTrackerCount); Bpl.StmtList thn = b.Collect(alternative.Tok); elsIf = new Bpl.IfCmd(alternative.Tok, null, thn, elsIf, els); els = null; diff --git a/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs b/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs index 56fce24c17a..b68ffe6c586 100644 --- a/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs +++ b/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs @@ -19,9 +19,7 @@ public static void EmitBoogie(BoogieGenerator generator, OpaqueBlock block, Boog var blockBuilder = new BoogieStmtListBuilder(generator, builder.Options, builder.Context); var bodyTranslator = GetBodyTranslator(generator, block, locals, etran, hasModifiesClause, blockBuilder); - var prevDefiniteAssignmentTrackerCount = generator.DefiniteAssignmentTrackers.Count; generator.TrStmtList(block.Body, blockBuilder, locals, bodyTranslator, block.RangeToken); - generator.RemoveDefiniteAssignmentTrackers(block.Body, prevDefiniteAssignmentTrackerCount); var assignedVariables = block.DescendantsAndSelf. SelectMany(s => s.GetAssignedLocals()).Select(ie => ie.Var) @@ -30,8 +28,8 @@ public static void EmitBoogie(BoogieGenerator generator, OpaqueBlock block, Boog var variablesUsedInEnsures = block.Ensures.SelectMany(ae => ae.E.DescendantsAndSelf). OfType().DistinctBy(ie => ie.Var); - var implicitAssignedIdentifiers = variablesUsedInEnsures.Where( - v => assignedVariables.Contains(v.Var) && generator.DefiniteAssignmentTrackers.ContainsKey(v.Var.UniqueName)); + var implicitAssignedIdentifiers = + variablesUsedInEnsures.Where(v => assignedVariables.Contains(v.Var)); foreach (var v in implicitAssignedIdentifiers) { var expression = new AttributedExpression(Expression.CreateAssigned(v.Tok, v)); totalEnsures.Add(expression); From 6e6d73dc61a629156523af220d10422b7607cce0 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Sat, 21 Sep 2024 12:43:40 +0200 Subject: [PATCH 17/47] Add substituter implementation --- Source/DafnyCore/AST/Substituter.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/DafnyCore/AST/Substituter.cs b/Source/DafnyCore/AST/Substituter.cs index f0999d9a4f4..c4508ea6151 100644 --- a/Source/DafnyCore/AST/Substituter.cs +++ b/Source/DafnyCore/AST/Substituter.cs @@ -879,6 +879,12 @@ protected virtual Statement SubstStmt(Statement stmt) { rr.ResolvedStatements.AddRange(revealStmt.ResolvedStatements.ConvertAll(SubstStmt)); rr.OffsetMembers = revealStmt.OffsetMembers.ToList(); r = rr; + } else if (stmt is BlockByProofStmt blockByProofStmt) { + // Move this code into the class + var rr = new BlockByProofStmt(blockByProofStmt.RangeToken, + (BlockStmt)SubstStmt(blockByProofStmt.Proof), + SubstStmt(blockByProofStmt.Body)); + r = rr; } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement } From 212cfbec8fba215baed296a4826db356efdcb6ae Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 23 Sep 2024 10:48:35 +0200 Subject: [PATCH 18/47] Prepare for using ordered dictionary for locals --- .../DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs index 2304e4d6408..8bb908024ef 100644 --- a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs +++ b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using Microsoft.Boogie; using Microsoft.Dafny; @@ -14,10 +15,11 @@ public static void EmitBoogie(BoogieGenerator generator, BlockByProofStmt block, generator.CurrentIdGenerator.Pop(); generator.TrStmt(block.Body, proofBuilder, locals, etran); - //generator.RemoveDefiniteAssignmentTrackers(new [] { block.Body }, prevDefiniteAssignmentTrackerCount); + generator.PathAsideBlock(block.Tok, proofBuilder, builder); generator.TrStmt(block.Body, builder.WithContext(builder.Context with { AssertMode = AssertMode.Assume }), locals, etran); + } } \ No newline at end of file From da8787e415bc7d27df1941061e489bb37fae78af Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 23 Sep 2024 11:13:06 +0200 Subject: [PATCH 19/47] Introduce Variables class to track Boogie variables using an ordered dictionary --- .../AST/Grammar/Printer/Printer.Statement.cs | 15 +- Source/DafnyCore/AST/Members/ConstantField.cs | 2 +- Source/DafnyCore/AST/Members/ICodeContext.cs | 4 +- Source/DafnyCore/AST/Substituter.cs | 4 +- .../AST/TypeDeclarations/DatatypeDecl.cs | 2 +- .../DafnyCore/Generic/ReporterExtensions.cs | 2 +- Source/DafnyCore/Pipeline/Compilation.cs | 2 +- Source/DafnyCore/ProofDependencyWarnings.cs | 2 - .../Resolver/GhostInterestVisitor.cs | 4 +- .../Verifier/BoogieGenerator.BoogieFactory.cs | 2 +- .../Verifier/BoogieGenerator.Decreases.cs | 4 +- .../BoogieGenerator.DefiniteAssignment.cs | 10 +- .../BoogieGenerator.ExpressionWellformed.cs | 154 +++++++++--------- .../Verifier/BoogieGenerator.Fields.cs | 4 +- ...oogieGenerator.Functions.Wellformedness.cs | 16 +- .../Verifier/BoogieGenerator.Iterators.cs | 13 +- .../Verifier/BoogieGenerator.Methods.cs | 45 ++--- .../Verifier/BoogieGenerator.Types.cs | 40 ++--- Source/DafnyCore/Verifier/BoogieGenerator.cs | 86 +++++----- .../Datatypes/BoogieGenerator.DataTypes.cs | 2 +- Source/DafnyCore/Verifier/ProofDependency.cs | 8 +- .../Verifier/ProofObligationDescription.cs | 2 +- .../Statements/BlockByProofStmtVerifier.cs | 44 ++++- .../BoogieGenerator.TrAssignment.cs | 30 ++-- .../Statements/BoogieGenerator.TrCall.cs | 21 ++- .../BoogieGenerator.TrForallStmt.cs | 14 +- .../Statements/BoogieGenerator.TrLoop.cs | 26 +-- .../BoogieGenerator.TrPredicateStatement.cs | 12 +- .../Statements/BoogieGenerator.TrStatement.cs | 51 +++--- .../Statements/OpaqueBlockVerifier.cs | 13 +- .../Handlers/DafnyHoverHandler.cs | 4 - ...licitFailingAssertionCodeActionProvider.cs | 2 +- .../ImplicitAssertionTest.cs | 3 - Source/DafnyPipeline.Test/TranslatorTest.cs | 8 +- .../LitTests/LitTest/dafny0/CallBy.dfy | 2 +- 35 files changed, 331 insertions(+), 322 deletions(-) diff --git a/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs b/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs index 5444e24d27c..c0c0e0b0ff0 100644 --- a/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs +++ b/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs @@ -22,7 +22,7 @@ namespace Microsoft.Dafny { interface ICanPrint { void Render(TextWriter wr, Printer printer, int indent); } - + public partial class Printer { /// @@ -36,14 +36,14 @@ public void PrintStatement(Statement stmt, int indent, bool includeSemicolon = t if (stmt.IsGhost && printMode == PrintModes.NoGhostOrIncludes) { return; } - + for (LList void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, - List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, + Variables locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, AddResultCommands addResultCommands) { var origOptions = wfOptions; if (wfOptions.LValueContext) { @@ -349,7 +349,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, builder.Add(TrAssumeCmd(selectExpr.tok, correctConstructor)); } else { builder.Add(Assert(GetToken(expr), correctConstructor, - new PODesc.DestructorValid(dtor, e.Obj, dtor.EnclosingCtors), builder.Context)); + new DestructorValid(dtor, e.Obj, dtor.EnclosingCtors), builder.Context)); } CheckNotGhostVariant(e, "destructor", dtor.EnclosingCtors, builder, etran); } else if (e.Member is DatatypeDiscriminator discriminator) { @@ -359,13 +359,13 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, if (e.Member is TwoStateFunction) { Bpl.Expr wh = GetWhereClause(selectExpr.tok, etran.TrExpr(e.Obj), e.Obj.Type, etran.OldAt(e.AtLabel), ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("receiver argument", "in the two-state function's previous state", e.Obj, e.AtLabel); + var desc = new IsAllocated("receiver argument", "in the two-state function's previous state", e.Obj, e.AtLabel); builder.Add(Assert(GetToken(expr), wh, desc, builder.Context)); } } else if (etran.UsesOldHeap) { Bpl.Expr wh = GetWhereClause(selectExpr.tok, etran.TrExpr(e.Obj), e.Obj.Type, etran, ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("receiver", + var desc = new IsAllocated("receiver", $"in the state in which its {(e.Member is Field ? "fields" : "members")} are accessed", e.Obj, e.AtLabel); builder.Add(Assert(GetToken(expr), wh, desc, builder.Context)); } @@ -373,7 +373,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, } if (!origOptions.LValueContext && wfOptions.DoReadsChecks && e.Member is Field { IsMutable: true } f) { var requiredFrame = new FrameExpression(Token.NoToken, e.Obj, f.Name); - var desc = new PODesc.ReadFrameSubset("read field", requiredFrame, readFrames, selectExpr, etran.scope); + var desc = new ReadFrameSubset("read field", requiredFrame, readFrames, selectExpr, etran.scope); wfOptions.AssertSink(this, builder)(selectExpr.tok, Bpl.Expr.SelectTok(selectExpr.tok, etran.ReadsFrame(selectExpr.tok), etran.TrExpr(e.Obj), GetField(e)), desc, wfOptions.AssertKv); } @@ -388,10 +388,10 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Bpl.Expr seq = etran.TrExpr(e.Seq); if (eSeqType.IsArrayType) { builder.Add(Assert(GetToken(e.Seq), Bpl.Expr.Neq(seq, predef.Null), - new PODesc.NonNull("array", e.Seq), builder.Context)); + new NonNull("array", e.Seq), builder.Context)); if (etran.UsesOldHeap) { builder.Add(Assert(GetToken(e.Seq), MkIsAlloc(seq, eSeqType, etran.HeapExpr), - new PODesc.IsAllocated("array", null, e.Seq), builder.Context)); + new IsAllocated("array", null, e.Seq), builder.Context)); } } Bpl.Expr e0 = null; @@ -403,7 +403,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Bpl.Expr inDomain = FunctionCall(selectExpr.tok, f, finite ? predef.MapType : predef.IMapType, seq); inDomain = Bpl.Expr.Select(inDomain, BoxIfNecessary(e.tok, e0, e.E0.Type)); builder.Add(Assert(GetToken(expr), inDomain, - new PODesc.ElementInDomain(e.Seq, e.E0), builder.Context, wfOptions.AssertKv)); + new ElementInDomain(e.Seq, e.E0), builder.Context, wfOptions.AssertKv)); } else if (eSeqType is MultiSetType) { // cool @@ -411,7 +411,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, if (e.E0 != null) { e0 = etran.TrExpr(e.E0); CheckWellformed(e.E0, wfOptions, locals, builder, etran); - var desc = new PODesc.InRange(e.Seq, e.E0, e.SelectOne, e.SelectOne ? "index" : "lower bound"); + var desc = new InRange(e.Seq, e.E0, e.SelectOne, e.SelectOne ? "index" : "lower bound"); builder.Add(Assert(GetToken(expr), InSeqRange(selectExpr.tok, e0, e.E0.Type, seq, isSequence, null, !e.SelectOne), desc, builder.Context, wfOptions.AssertKv)); @@ -426,7 +426,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, } builder.Add(Assert(GetToken(expr), InSeqRange(selectExpr.tok, etran.TrExpr(e.E1), e.E1.Type, seq, isSequence, lowerBound, true), - new PODesc.SequenceSelectRangeValid(e.Seq, e.E0, e.E1, isSequence ? "sequence" : "array"), + new SequenceSelectRangeValid(e.Seq, e.E0, e.E1, isSequence ? "sequence" : "array"), builder.Context, wfOptions.AssertKv)); } } @@ -437,7 +437,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, i = ConvertExpression(selectExpr.tok, i, e.E0.Type, Type.Int); Bpl.Expr fieldName = FunctionCall(selectExpr.tok, BuiltinFunction.IndexField, null, i); var requiredFrame = new FrameExpression(Token.NoToken, e.Seq, null); - var desc = new PODesc.ReadFrameSubset("read array element", requiredFrame, readFrames, e, etran.scope); + var desc = new ReadFrameSubset("read array element", requiredFrame, readFrames, e, etran.scope); wfOptions.AssertSink(this, builder)(selectExpr.tok, Bpl.Expr.SelectTok(selectExpr.tok, etran.ReadsFrame(selectExpr.tok), seq, fieldName), desc, wfOptions.AssertKv); } else { @@ -453,7 +453,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, var trigger = BplTrigger(allowedToRead); // Note, the assertion we're about to produce only seems useful in the check-only mode (that is, with subsumption 0), but if it were to be assumed, we'll use this entire RHS as the trigger var qq = new Bpl.ForallExpr(e.tok, new List { iVar }, trigger, BplImp(range, allowedToRead)); var requiredFrame = new FrameExpression(Token.NoToken, e.Seq, null); - var desc = new PODesc.ReadFrameSubset("read the indicated range of array elements", requiredFrame, readFrames, e, etran.scope); + var desc = new ReadFrameSubset("read the indicated range of array elements", requiredFrame, readFrames, e, etran.scope); wfOptions.AssertSink(this, builder)(selectExpr.tok, qq, desc, wfOptions.AssertKv); } } @@ -468,10 +468,10 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, CheckWellformed(e.Array, wfOptions, locals, builder, etran); Bpl.Expr array = etran.TrExpr(e.Array); builder.Add(Assert(GetToken(e.Array), Bpl.Expr.Neq(array, predef.Null), - new PODesc.NonNull("array", e.Array), builder.Context)); + new NonNull("array", e.Array), builder.Context)); if (etran.UsesOldHeap) { builder.Add(Assert(GetToken(e.Array), MkIsAlloc(array, e.Array.Type, etran.HeapExpr), - new PODesc.IsAllocated("array", null, e.Array), builder.Context)); + new IsAllocated("array", null, e.Array), builder.Context)); } for (int idxId = 0; idxId < e.Indices.Count; idxId++) { var idx = e.Indices[idxId]; @@ -484,13 +484,13 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, var upper = Bpl.Expr.Lt(index, length); var tok = idx is IdentifierExpr ? e.tok : idx.tok; // TODO: Reusing the token of an identifier expression would underline its definition. but this is still not perfect. - var desc = new PODesc.InRange(e.Array, e.Indices[idxId], true, $"index {idxId}", idxId); + var desc = new InRange(e.Array, e.Indices[idxId], true, $"index {idxId}", idxId); builder.Add(Assert(tok, BplAnd(lower, upper), desc, builder.Context, wfOptions.AssertKv)); } if (wfOptions.DoReadsChecks) { Bpl.Expr fieldName = etran.GetArrayIndexFieldName(e.tok, e.Indices); var requiredFrame = new FrameExpression(Token.NoToken, e.Array, null); - var desc = new PODesc.ReadFrameSubset("read array element", requiredFrame, readFrames, selectExpr, etran.scope); + var desc = new ReadFrameSubset("read array element", requiredFrame, readFrames, selectExpr, etran.scope); wfOptions.AssertSink(this, builder)(selectExpr.tok, Bpl.Expr.SelectTok(selectExpr.tok, etran.ReadsFrame(selectExpr.tok), array, fieldName), desc, wfOptions.AssertKv); } @@ -507,7 +507,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, // validate index CheckWellformed(e.Index, wfOptions, locals, builder, etran); if (collectionType is SeqType) { - var desc = new PODesc.InRange(e.Seq, e.Index, true, "index"); + var desc = new InRange(e.Seq, e.Index, true, "index"); builder.Add(Assert(GetToken(e.Index), InSeqRange(updateExpr.tok, index, e.Index.Type, seq, true, null, false), desc, builder.Context, wfOptions.AssertKv)); @@ -521,7 +521,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, } else if (collectionType is MapType mapType) { CheckSubrange(e.Value.tok, value, e.Value.Type, mapType.Range, e.Value, builder); } else if (collectionType is MultiSetType) { - var desc = new PODesc.NonNegative("new number of occurrences", e.Value); + var desc = new NonNegative("new number of occurrences", e.Value); builder.Add(Assert(GetToken(e.Value), Bpl.Expr.Le(Bpl.Expr.Literal(0), value), desc, builder.Context, wfOptions.AssertKv)); } else { @@ -552,14 +552,14 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, if (etran.UsesOldHeap) { Bpl.Expr wh = GetWhereClause(e.Function.tok, etran.TrExpr(e.Function), e.Function.Type, etran, ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("function", "in the state in which the function is invoked", e.Function); + var desc = new IsAllocated("function", "in the state in which the function is invoked", e.Function); builder.Add(Assert(GetToken(e.Function), wh, desc, builder.Context)); } for (int i = 0; i < e.Args.Count; i++) { Expression ee = e.Args[i]; wh = GetWhereClause(ee.tok, etran.TrExpr(ee), ee.Type, etran, ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("argument", "in the state in which the function is invoked", ee); + var desc = new IsAllocated("argument", "in the state in which the function is invoked", ee); builder.Add(Assert(GetToken(ee), wh, desc, builder.Context)); } } @@ -621,7 +621,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, // check precond var bPrecond = FunctionCall(e.tok, Requires(arity), Bpl.Type.Bool, args); builder.Add(Assert(GetToken(expr), bPrecond, - new PODesc.PreconditionSatisfied(dPrecond, null, null), builder.Context)); + new PreconditionSatisfied(dPrecond, null, null), builder.Context)); } if (wfOptions.DoReadsChecks && !fnCoreType.IsArrowTypeWithoutReadEffects) { @@ -640,7 +640,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, ); readsCall.Type = objset; var requiredFrame = new FrameExpression(Token.NoToken, readsCall, null); - var desc = new PODesc.ReadFrameSubset("invoke function", requiredFrame, readFrames); + var desc = new ReadFrameSubset("invoke function", requiredFrame, readFrames); CheckFrameSubset(applyExpr.tok, new List { wrappedReads }, null, null, etran, etran.ReadsFrame(applyExpr.tok), wfOptions.AssertSink(this, builder), desc, wfOptions.AssertKv); @@ -729,7 +729,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, if (!e.Function.IsStatic) { Bpl.Expr wh = GetWhereClause(e.Receiver.tok, etran.TrExpr(e.Receiver), e.Receiver.Type, etran, ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("receiver argument", "in the state in which the function is invoked", e.Receiver, e.AtLabel); + var desc = new IsAllocated("receiver argument", "in the state in which the function is invoked", e.Receiver, e.AtLabel); builder.Add(Assert(GetToken(e.Receiver), wh, desc, builder.Context)); } } @@ -737,7 +737,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, 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); + var desc = new IsAllocated("argument", "in the state in which the function is invoked", ee, e.AtLabel); builder.Add(Assert(GetToken(ee), wh, desc, builder.Context)); } } @@ -745,7 +745,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, if (!e.Function.IsStatic) { Bpl.Expr wh = GetWhereClause(e.Receiver.tok, etran.TrExpr(e.Receiver), e.Receiver.Type, etran.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); + var desc = new IsAllocated("receiver argument", "in the two-state function's previous state", e.Receiver, e.AtLabel); builder.Add(Assert(GetToken(e.Receiver), wh, desc, builder.Context)); } } @@ -757,9 +757,9 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Bpl.Expr wh = GetWhereClause(ee.tok, etran.TrExpr(ee), ee.Type, etran.OldAt(e.AtLabel), ISALLOC, true); if (wh != null) { var pIdx = e.Args.Count == 1 ? "" : " at index " + i; - var desc = new PODesc.IsAllocated( + var desc = new IsAllocated( $"argument{pIdx} for parameter '{formal.Name}'", - "in the two-state function's previous state" + PODesc.IsAllocated.HelperFormal(formal), + "in the two-state function's previous state" + IsAllocated.HelperFormal(formal), ee, e.AtLabel ); @@ -774,7 +774,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, if (e.Function.Name == "reads" && !e.Receiver.Type.IsArrowTypeWithoutReadEffects) { var arguments = etran.FunctionInvocationArguments(e, null, null); var precondition = FunctionCall(e.tok, Requires(e.Args.Count), Bpl.Type.Bool, arguments); - builder.Add(Assert(GetToken(expr), precondition, new PODesc.PreconditionSatisfied(null, null, null), builder.Context)); + builder.Add(Assert(GetToken(expr), precondition, new PreconditionSatisfied(null, null, null), builder.Context)); if (wfOptions.DoReadsChecks) { // check that the callee reads only what the caller is already allowed to read @@ -806,7 +806,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, requiredFrames = new() { new FrameExpression(Token.NoToken, readsCall, null) }; break; } - var desc = new PODesc.ReadFrameSubset("invoke function", requiredFrames, readFrames); + var desc = new ReadFrameSubset("invoke function", requiredFrames, readFrames); CheckFrameSubset(expr.tok, new List { reads }, null, null, etran, etran.ReadsFrame(expr.tok), wfOptions.AssertSink(this, builder), desc, wfOptions.AssertKv); } @@ -824,7 +824,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, foreach (var ss in TrSplitExpr(builder.Context, precond, etran, true, out _)) { if (ss.IsChecked) { var tok = new NestedToken(GetToken(expr), ss.Tok); - var desc = new PODesc.PreconditionSatisfied(directPrecond, errorMessage, successMessage); + var desc = new PreconditionSatisfied(directPrecond, errorMessage, successMessage); if (wfOptions.AssertKv != null) { // use the given assert attribute only builder.Add(Assert(tok, ss.E, desc, builder.Context, wfOptions.AssertKv)); @@ -843,7 +843,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, // substitute actual args for parameters in description expression frames... var requiredFrames = e.Function.Reads.Expressions.ConvertAll(directSub.SubstFrameExpr); - var desc = new PODesc.ReadFrameSubset("invoke function", requiredFrames, readFrames); + var desc = new ReadFrameSubset("invoke function", requiredFrames, readFrames); // ... but that substitution isn't needed for frames passed to CheckFrameSubset var readsSubst = new Substituter(null, new Dictionary(), e.GetTypeArgumentSubstitutions()); @@ -859,7 +859,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Contract.Assert(calleeSCCLookup != null); if (ModuleDefinition.InSameSCC(calleeSCCLookup, codeContext)) { if (wfOptions.DoOnlyCoarseGrainedTerminationChecks) { - builder.Add(Assert(GetToken(expr), Bpl.Expr.False, new PODesc.IsNonRecursive(), builder.Context)); + builder.Add(Assert(GetToken(expr), Bpl.Expr.False, new IsNonRecursive(), builder.Context)); } else { List contextDecreases = codeContext.Decreases.Expressions; List calleeDecreases = e.Function.Decreases.Expressions; @@ -925,7 +925,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, case SeqConstructionExpr constructionExpr: { var e = constructionExpr; CheckWellformed(e.N, wfOptions, locals, builder, etran); - var desc = new PODesc.NonNegative("sequence size", e.N); + var desc = new NonNegative("sequence size", e.N); builder.Add(Assert(GetToken(e.N), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(e.N)), desc, builder.Context)); CheckWellformed(e.Initializer, wfOptions, locals, builder, etran); @@ -961,18 +961,18 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Contract.Assert(ty.IsRefType); nonNull = Bpl.Expr.Neq(r, predef.Null); builder.Add(Assert(GetToken(fe.E), BplImp(ante, nonNull), - new PODesc.NonNull(description, fe.E, description != "object"), builder.Context)); + new NonNull(description, fe.E, description != "object"), builder.Context)); } // check that "r" was allocated in the "e.AtLabel" state Bpl.Expr wh = GetWhereClause(fe.E.tok, r, ty, etran.OldAt(e.AtLabel), ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated(description, "in the old-state of the 'unchanged' predicate", + var desc = new IsAllocated(description, "in the old-state of the 'unchanged' predicate", fe.E, e.AtLabel, description != "object"); builder.Add(Assert(GetToken(fe.E), BplImp(BplAnd(ante, nonNull), wh), desc, builder.Context)); } // check that the 'unchanged' argument reads only what the context is allowed to read if (wfOptions.DoReadsChecks) { - var desc = new PODesc.ReadFrameSubset($"read state of 'unchanged' {description}", fe, contextReadsFrames); + var desc = new ReadFrameSubset($"read state of 'unchanged' {description}", fe, contextReadsFrames); CheckFrameSubset(fe.E.tok, new List() { fe }, null, new Dictionary(), etran, etran.ReadsFrame(fe.E.tok), wfOptions.AssertSink(this, builder), @@ -1021,11 +1021,11 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, if (e.ResolvedOp == BinaryExpr.ResolvedOpcode.Sub && e.E0.Type.IsBigOrdinalType) { var rhsIsNat = FunctionCall(binaryExpr.tok, "ORD#IsNat", Bpl.Type.Bool, etran.TrExpr(e.E1)); builder.Add(Assert(GetToken(expr), rhsIsNat, - new PODesc.OrdinalSubtractionIsNatural(e.E1), builder.Context)); + new OrdinalSubtractionIsNatural(e.E1), builder.Context)); var offset0 = FunctionCall(binaryExpr.tok, "ORD#Offset", Bpl.Type.Int, etran.TrExpr(e.E0)); var offset1 = FunctionCall(binaryExpr.tok, "ORD#Offset", Bpl.Type.Int, etran.TrExpr(e.E1)); builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(offset1, offset0), - new PODesc.OrdinalSubtractionUnderflow(e.E0, e.E1), builder.Context)); + new OrdinalSubtractionUnderflow(e.E0, e.E1), builder.Context)); } else if (e.Type.NormalizeToAncestorType().IsCharType) { var e0 = FunctionCall(binaryExpr.tok, "char#ToInt", Bpl.Type.Int, etran.TrExpr(e.E0)); var e1 = FunctionCall(binaryExpr.tok, "char#ToInt", Bpl.Type.Int, etran.TrExpr(e.E1)); @@ -1033,13 +1033,13 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, builder.Add(Assert(GetToken(expr), FunctionCall(Token.NoToken, BuiltinFunction.IsChar, null, Bpl.Expr.Binary(BinaryOperator.Opcode.Add, e0, e1)), - new PODesc.CharOverflow(e.E0, e.E1), builder.Context)); + new CharOverflow(e.E0, e.E1), builder.Context)); } else { Contract.Assert(e.ResolvedOp == BinaryExpr.ResolvedOpcode.Sub); // .Mul is not supported for char builder.Add(Assert(GetToken(expr), FunctionCall(Token.NoToken, BuiltinFunction.IsChar, null, Bpl.Expr.Binary(BinaryOperator.Opcode.Sub, e0, e1)), - new PODesc.CharUnderflow(e.E0, e.E1), builder.Context)); + new CharUnderflow(e.E0, e.E1), builder.Context)); } } break; @@ -1055,14 +1055,14 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, } CheckWellformed(e.E1, wfOptions, locals, builder, etran); builder.Add(Assert(GetToken(expr), Bpl.Expr.Neq(etran.TrExpr(e.E1), zero), - new PODesc.DivisorNonZero(e.E1), builder.Context, wfOptions.AssertKv)); + new DivisorNonZero(e.E1), builder.Context, wfOptions.AssertKv)); } break; case BinaryExpr.ResolvedOpcode.LeftShift: case BinaryExpr.ResolvedOpcode.RightShift: { CheckWellformed(e.E1, wfOptions, locals, builder, etran); var w = e.Type.NormalizeToAncestorType().AsBitVectorType.Width; - var upperDesc = new PODesc.ShiftUpperBound(w, true, e.E1); + var upperDesc = new ShiftUpperBound(w, true, e.E1); if (e.E1.Type.NormalizeToAncestorType().AsBitVectorType is { } bitvectorType) { // Known to be non-negative, so we don't need to check lower bound. // Check upper bound, that is, check "E1 <= w" @@ -1084,7 +1084,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, // already holds, so there is no reason to check it. } } else { - var positiveDesc = new PODesc.ShiftLowerBound(true, e.E1); + var positiveDesc = new ShiftLowerBound(true, e.E1); builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(e.E1)), positiveDesc, builder.Context, wfOptions.AssertKv)); builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(etran.TrExpr(e.E1), Bpl.Expr.Literal(w)), @@ -1116,7 +1116,7 @@ void CheckOperand(Expression operand) { var notGhostCtor = BplAnd(ghostConstructors.ConvertAll( ctor => Bpl.Expr.Not(FunctionCall(expr.tok, ctor.QueryField.FullSanitizedName, Bpl.Type.Bool, value)))); builder.Add(Assert(GetToken(expr), notGhostCtor, - new PODesc.NotGhostVariant("equality", operand, ghostConstructors), builder.Context)); + new NotGhostVariant("equality", operand, ghostConstructors), builder.Context)); } CheckOperand(e.E0); @@ -1142,7 +1142,7 @@ void CheckOperand(Expression operand) { case TernaryExpr.Opcode.PrefixNeqOp: if (e.E0.Type.IsNumericBased(Type.NumericPersuasion.Int)) { builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(e.E0)), - new PODesc.PrefixEqualityLimit(e.E0), builder.Context, wfOptions.AssertKv)); + new PrefixEqualityLimit(e.E0), builder.Context, wfOptions.AssertKv)); } break; default: @@ -1255,7 +1255,7 @@ void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { 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), builder.Context)); + new ComprehensionNoAlias(mc.BoundVars, mc.Range, mc.TermLeft, mc.Term), builder.Context)); }); } }); @@ -1325,7 +1325,7 @@ void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { // Every constructor has this destructor; no need to check anything } else { builder.Add(Assert(GetToken(expr), correctConstructor, - new PODesc.ValidConstructorNames(updateExpr.Root, e.LegalSourceConstructors), builder.Context)); + new ValidConstructorNames(updateExpr.Root, e.LegalSourceConstructors), builder.Context)); } CheckNotGhostVariant(e.InCompiledContext, updateExpr, e.Root, "update of", e.Members, @@ -1380,7 +1380,7 @@ public void CheckSubsetType(ExpressionTranslator etran, Expression expr, Bpl.Exp builder.Add(TrAssumeCmd(expr.tok, MkIs(selfCall, resultType))); } - private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, List locals, + private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, Variables locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, AddResultCommands addResultCommands) { FillMissingCases(me); @@ -1393,13 +1393,13 @@ private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, List local foreach (var missingCtor in me.MissingCases) { // havoc all bound variables var b = new BoogieStmtListBuilder(this, options, builder.Context); - List newLocals = new List(); + Variables newLocals = new(); Bpl.Expr r = CtorInvocation(me.tok, missingCtor, etran, newLocals, b); - locals.AddRange(newLocals); + locals.AddRange(newLocals.Values); if (newLocals.Count != 0) { List havocIds = new List(); - foreach (Variable local in newLocals) { + foreach (Variable local in newLocals.Values) { havocIds.Add(new Bpl.IdentifierExpr(local.tok, local)); } @@ -1408,7 +1408,7 @@ private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, List local String missingStr = me.Context.FillHole(new IdCtx(missingCtor)).AbstractAllHoles().ToString(); b.Add(Assert(GetToken(me), Bpl.Expr.False, - new PODesc.MatchIsComplete("expression", missingStr), builder.Context)); + new MatchIsComplete("expression", missingStr), builder.Context)); Bpl.Expr guard = Bpl.Expr.Eq(src, r); ifCmd = new Bpl.IfCmd(me.tok, guard, b.Collect(me.tok), ifCmd, els); @@ -1429,7 +1429,7 @@ private void TrMatchExpr(MatchExpr me, WFOptions wfOptions, List local builder.Add(ifCmd); } - private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions wfOptions, List locals, + private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions wfOptions, Variables locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, AddResultCommands addResultCommands) { var statements = new List() { stmtExpr.S }; @@ -1466,7 +1466,7 @@ private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions wfOptions, Lis /// end of the code generated. (Any other exit would cause control flow to miss /// BuildWithHeapAs's assignment that restores the value of $Heap.) /// - void BuildWithHeapAs(IToken token, Bpl.Expr temporaryHeap, string heapVarSuffix, List locals, + void BuildWithHeapAs(IToken token, Bpl.Expr temporaryHeap, string heapVarSuffix, Variables locals, BoogieStmtListBuilder builder, System.Action build) { var suffix = CurrentIdGenerator.FreshId(heapVarSuffix); var tmpHeapVar = new Bpl.LocalVariable(token, new Bpl.TypedIdent(token, "Heap$" + suffix, predef.HeapType)); @@ -1502,7 +1502,7 @@ private void CheckNotGhostVariant(bool inCompiledContext, Expression exprUsedFor var notGhostCtor = BplAnd(enclosingGhostConstructors.ConvertAll( ctor => Bpl.Expr.Not(FunctionCall(exprUsedForToken.tok, ctor.QueryField.FullSanitizedName, Bpl.Type.Bool, obj)))); builder.Add(Assert(GetToken(exprUsedForToken), notGhostCtor, - new PODesc.NotGhostVariant(whatKind, + new NotGhostVariant(whatKind, Util.PrintableNameList(members.ConvertAll(member => member.Name), "and"), datatypeValue, enclosingGhostConstructors), builder.Context)); @@ -1511,7 +1511,7 @@ private void CheckNotGhostVariant(bool inCompiledContext, Expression exprUsedFor } void CheckWellformedSpecialFunction(FunctionCallExpr expr, WFOptions options, Bpl.Expr result, Type resultType, - List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { + Variables locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { Contract.Requires(expr.Function is SpecialFunction); CheckWellformed(expr.Receiver, options, locals, builder, etran); @@ -1522,15 +1522,15 @@ void CheckWellformedSpecialFunction(FunctionCallExpr expr, WFOptions options, Bp var w = expr.Type.AsBitVectorType.Width; var arg = expr.Args[0]; builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(arg)), - new PODesc.ShiftLowerBound(false, arg), builder.Context, options.AssertKv)); + new ShiftLowerBound(false, arg), builder.Context, options.AssertKv)); builder.Add(Assert(GetToken(expr), Bpl.Expr.Le(etran.TrExpr(arg), Bpl.Expr.Literal(w)), - new PODesc.ShiftUpperBound(w, false, arg), builder.Context, options.AssertKv)); + new ShiftUpperBound(w, false, arg), builder.Context, options.AssertKv)); } } delegate void AddResultCommands(BoogieStmtListBuilder builder, Expression result); - void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, List locals, + void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, Variables locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, bool checkRhs, AddResultCommands addResultCommands) { if (e.Exact) { var substMap = SetupBoundVarsAsLocals(e.BoundVars.ToList(), builder, locals, etran, "#Z"); @@ -1599,7 +1599,7 @@ void CheckPostconditionForRhs(BoogieStmtListBuilder innerBuilder, Expression bod 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()), builder.Context)); + new LetSuchThatUnique(e.RHSs[0], e.BoundVars.ToList()), builder.Context)); } // assume $let$canCall(g); etran.LetDesugaring(e); // call LetDesugaring to prepare the desugaring and populate letSuchThatExprInfo with something for e @@ -1610,7 +1610,7 @@ void CheckPostconditionForRhs(BoogieStmtListBuilder innerBuilder, Expression bod } } - void CheckFrameWellFormed(WFOptions wfo, List fes, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { + void CheckFrameWellFormed(WFOptions wfo, List fes, Variables locals, BoogieStmtListBuilder builder, ExpressionTranslator etran) { Contract.Requires(fes != null); Contract.Requires(locals != null); Contract.Requires(builder != null); @@ -1618,7 +1618,7 @@ void CheckFrameWellFormed(WFOptions wfo, List fes, List dims, // check precond var pre = FunctionCall(tok, Requires(dims.Count), Bpl.Type.Bool, args); var q = new Bpl.ForallExpr(tok, bvs, BplImp(ante, pre)); - var indicesDesc = new PODesc.IndicesInDomain(forArray ? "array" : "sequence", dims, init); + var indicesDesc = new IndicesInDomain(forArray ? "array" : "sequence", dims, init); builder.Add(AssertAndForget(builder.Context, tok, q, indicesDesc)); if (!forArray && options.DoReadsChecks) { // unwrap renamed local lambdas @@ -1681,12 +1681,12 @@ private void CheckElementInit(IToken tok, bool forArray, List dims, FunctionCall(tok, Reads(1), TrType(objset), args), objset); var reads = new FrameExpression(tok, wrap, null); - Action maker = (t, e, d, qk) => { + Action maker = (t, e, d, qk) => { var qe = new Bpl.ForallExpr(t, bvs, BplImp(ante, e)); options.AssertSink(this, builder)(t, qe, d, qk); }; - PODesc.Utils.MakeQuantifierVarsForDims(dims, out var indexVars, out var indexVarExprs, out var indicesRange); + Utils.MakeQuantifierVarsForDims(dims, out var indexVars, out var indexVarExprs, out var indicesRange); var readsCall = new ApplyExpr( Token.NoToken, new ExprDotName(Token.NoToken, unwrappedFunc, "reads", null), @@ -1700,10 +1700,10 @@ private void CheckElementInit(IToken tok, bool forArray, List dims, RangeToken.NoToken, indexVars, indicesRange, - PODesc.Utils.MakeDafnyFrameCheck(contextReads, readsCall, null), + Utils.MakeDafnyFrameCheck(contextReads, readsCall, null), null ); - var readsDesc = new PODesc.ReadFrameSubset("invoke the function passed as an argument to the sequence constructor", readsDescExpr); + var readsDesc = new ReadFrameSubset("invoke the function passed as an argument to the sequence constructor", readsDescExpr); CheckFrameSubset(tok, new List { reads }, null, null, etran, etran.ReadsFrame(tok), maker, readsDesc, options.AssertKv); } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Fields.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Fields.cs index 85eaa55d109..cfcea6d36c4 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Fields.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Fields.cs @@ -205,9 +205,9 @@ void AddWellformednessCheck(ConstantField decl) { sink.AddTopLevelDeclaration(proc); var implInParams = Bpl.Formal.StripWhereClauses(inParams); - var locals = new List(); + var locals = new Variables(); var builder = new BoogieStmtListBuilder(this, options, new BodyTranslationContext(false)); - builder.Add(new CommentCmd(string.Format("AddWellformednessCheck for {0} {1}", decl.WhatKind, decl))); + builder.Add(new CommentCmd($"AddWellformednessCheck for {decl.WhatKind} {decl}")); builder.AddCaptureState(decl.tok, false, "initial state"); isAllocContext = new IsAllocContext(options, true); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs index e4401d21a61..6dd5b67cb3c 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs @@ -60,7 +60,7 @@ public void Check(Function f) { var (olderParameterCount, olderCondition) = generator.OlderCondition(f, selfCall, procedureParameters); if (olderParameterCount != 0) { generator.AddEnsures(ens, new Ensures(f.tok, false, olderCondition, null) { - Description = new PODesc.IsOlderProofObligation(olderParameterCount, f.Ins.Count + (f.IsStatic ? 0 : 1)) + Description = new IsOlderProofObligation(olderParameterCount, f.Ins.Count + (f.IsStatic ? 0 : 1)) }); } @@ -78,7 +78,7 @@ public void Check(Function f) { Contract.Assert(proc.InParams.Count == typeInParams.Count + heapParameters.Count + procedureParameters.Count); // Changed the next line to strip from inParams instead of proc.InParams // They should be the same, but hence the added contract - var locals = new List(); + var locals = new Variables(); var builder = new BoogieStmtListBuilder(generator, generator.options, context); var builderInitializationArea = new BoogieStmtListBuilder(generator, generator.options, context); if (f is TwoStateFunction) { @@ -107,7 +107,7 @@ public void Check(Function f) { if (formal.IsOld) { Expr wh = generator.GetWhereClause(e.tok, etran.TrExpr(e), e.Type, etran.Old, ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("default value", "in the two-state function's previous state", e); + var desc = new IsAllocated("default value", "in the two-state function's previous state", e); builder.Add(generator.Assert(generator.GetToken(e), wh, desc, builder.Context)); } } @@ -173,12 +173,12 @@ public void Check(Function f) { private void ConcurrentAttributeCheck(Function f, ExpressionTranslator etran, BoogieStmtListBuilder builder) { // If the function is marked as {:concurrent}, check that the reads clause is empty. if (Attributes.Contains(f.Attributes, Attributes.ConcurrentAttributeName)) { - var desc = new PODesc.ConcurrentFrameEmpty(f, "reads"); + var desc = new ConcurrentFrameEmpty(f, "reads"); generator.CheckFrameEmpty(f.tok, etran, etran.ReadsFrame(f.tok), builder, desc, null); } } - private void CheckBodyAndEnsuresClauseWellformedness(Function f, ExpressionTranslator etran, List locals, List inParams, + private void CheckBodyAndEnsuresClauseWellformedness(Function f, ExpressionTranslator etran, Variables locals, List inParams, BoogieStmtListBuilder builderInitializationArea, BoogieStmtListBuilder builder) { builder.Add(new CommentCmd("Check body and ensures clauses")); // Generate: @@ -201,7 +201,7 @@ private void CheckBodyAndEnsuresClauseWellformedness(Function f, ExpressionTrans private BoogieStmtListBuilder GetBodyCheckBuilder(Function f, ExpressionTranslator etran, List parameters, - List locals, BoogieStmtListBuilder builderInitializationArea) { + Variables locals, BoogieStmtListBuilder builderInitializationArea) { var selfCall = GetSelfCall(f, etran, parameters); var context = new BodyTranslationContext(f.ContainsHide); var bodyCheckBuilder = new BoogieStmtListBuilder(generator, generator.options, context); @@ -230,7 +230,7 @@ void CheckPostcondition(BoogieStmtListBuilder innerBuilder, Expression innerBody generator.CheckWellformedWithResult(f.Body, wfo, locals, bodyCheckBuilder, etran, CheckPostcondition); // var b$reads_guards#0 : bool ... - locals.AddRange(wfo.Locals); + locals.AddRange(wfo.Locals.Values); // b$reads_guards#0 := true ... foreach (var cmd in wfo.AssignLocals) { builderInitializationArea.Add(cmd); @@ -272,7 +272,7 @@ private Expr GetSelfCall(Function f, ExpressionTranslator etran, List return funcAppl; } - private BoogieStmtListBuilder GetPostCheckBuilder(Function f, ExpressionTranslator etran, List locals) { + private BoogieStmtListBuilder GetPostCheckBuilder(Function f, ExpressionTranslator etran, Variables locals) { var context = new BodyTranslationContext(f.ContainsHide); var postCheckBuilder = new BoogieStmtListBuilder(generator, generator.options, context); postCheckBuilder.Add(new CommentCmd("Check well-formedness of postcondition and assume false")); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs index dd161356a05..1598d48c30d 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs @@ -72,8 +72,8 @@ Bpl.Procedure AddIteratorProc(IteratorDecl iter, MethodTranslationKind kind) { var etran = new ExpressionTranslator(this, predef, iter.tok, iter); var inParams = new List(); - List outParams; - GenerateMethodParametersChoose(iter.tok, iter, kind, true, true, false, etran, inParams, out outParams); + GenerateMethodParametersChoose(iter.tok, iter, kind, + true, true, false, etran, inParams, out var outParams); var req = new List(); var mod = new List(); @@ -122,7 +122,8 @@ Bpl.Procedure AddIteratorProc(IteratorDecl iter, MethodTranslationKind kind) { } var name = MethodName(iter, kind); - var proc = new Bpl.Procedure(iter.tok, name, new List(), inParams, outParams, false, req, mod, ens, etran.TrAttributes(iter.Attributes, null)); + var proc = new Bpl.Procedure(iter.tok, name, new List(), + inParams, outParams.Values.ToList(), false, req, mod, ens, etran.TrAttributes(iter.Attributes, null)); AddVerboseNameAttribute(proc, iter.FullDafnyName, kind); currentModule = null; @@ -150,7 +151,7 @@ void AddIteratorWellformednessCheck(IteratorDecl iter, Procedure proc) { // Don't do reads checks since iterator reads clauses mean something else. // See comment inside GenerateIteratorImplPrelude(). etran = etran.WithReadsFrame(null); - var localVariables = new List(); + var localVariables = new Variables(); GenerateIteratorImplPrelude(iter, inParams, new List(), builder, localVariables, etran); // check well-formedness of any default-value expressions (before assuming preconditions) @@ -284,7 +285,7 @@ void AddIteratorImpl(IteratorDecl iter, Bpl.Procedure proc) { // Don't do reads checks since iterator reads clauses mean something else. // See comment inside GenerateIteratorImplPrelude(). etran = etran.WithReadsFrame(null); - var localVariables = new List(); + var localVariables = new Variables(); GenerateIteratorImplPrelude(iter, inParams, new List(), builder, localVariables, etran); // add locals for the yield-history variables and the extra variables @@ -349,7 +350,7 @@ Bpl.Expr YieldCountAssumption(IteratorDecl iter, ExpressionTranslator etran) { } void GenerateIteratorImplPrelude(IteratorDecl iter, List inParams, List outParams, - BoogieStmtListBuilder builder, List localVariables, ExpressionTranslator etran) { + BoogieStmtListBuilder builder, Variables localVariables, ExpressionTranslator etran) { Contract.Requires(iter != null); Contract.Requires(inParams != null); Contract.Requires(outParams != null); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 22a391548f4..83745d558de 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -552,7 +552,7 @@ private void AddMethodImpl(Method m, Bpl.Procedure proc, bool wellformednessProc etran = etran.WithReadsFrame(null); } InitializeFuelConstant(m.tok, builder, etran); - var localVariables = new List(); + var localVariables = new Variables(); GenerateImplPrelude(m, wellformednessProc, inParams, outParams, builder, localVariables, etran); if (UseOptimizationInZ3) { @@ -584,7 +584,7 @@ private void AddMethodImpl(Method m, Bpl.Procedure proc, bool wellformednessProc Reset(); } - private StmtList TrMethodContractWellformednessCheck(Method m, ExpressionTranslator etran, List localVariables, + private StmtList TrMethodContractWellformednessCheck(Method m, ExpressionTranslator etran, Variables localVariables, BoogieStmtListBuilder builder, List outParams) { var builderInitializationArea = new BoogieStmtListBuilder(this, options, builder.Context); StmtList stmts; @@ -601,7 +601,7 @@ private StmtList TrMethodContractWellformednessCheck(Method m, ExpressionTransla if (formal.IsOld) { Boogie.Expr wh = GetWhereClause(e.tok, etran.TrExpr(e), e.Type, etran.Old, ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("default value", "in the two-state lemma's previous state", e); + var desc = new IsAllocated("default value", "in the two-state lemma's previous state", e); builder.Add(Assert(e.RangeToken, wh, desc, builder.Context)); } } @@ -623,7 +623,7 @@ private StmtList TrMethodContractWellformednessCheck(Method m, ExpressionTransla // on the method, and {:assume_concurrent} is NOT present on the reads clause. if (Attributes.Contains(m.Attributes, Attributes.ConcurrentAttributeName) && !Attributes.Contains(m.Reads.Attributes, Attributes.AssumeConcurrentAttributeName)) { - var desc = new PODesc.ConcurrentFrameEmpty(m, "reads"); + var desc = new ConcurrentFrameEmpty(m, "reads"); if (etran.readsFrame != null) { CheckFrameEmpty(m.tok, etran, etran.ReadsFrame(m.tok), builder, desc, null); } else { @@ -641,7 +641,7 @@ private StmtList TrMethodContractWellformednessCheck(Method m, ExpressionTransla // and {:assume_concurrent} is NOT present on the modifies clause. if (Attributes.Contains(m.Attributes, Attributes.ConcurrentAttributeName) && !Attributes.Contains(m.Mod.Attributes, Attributes.AssumeConcurrentAttributeName)) { - var desc = new PODesc.ConcurrentFrameEmpty(m, "modifies"); + var desc = new ConcurrentFrameEmpty(m, "modifies"); CheckFrameEmpty(m.tok, etran, etran.ModifiesFrame(m.tok), builder, desc, null); } @@ -696,7 +696,7 @@ public void ApplyModifiesEffect(INode node, ExpressionTranslator etran, BoogieSt } } - private StmtList TrMethodBody(Method m, BoogieStmtListBuilder builder, List localVariables, + private StmtList TrMethodBody(Method m, BoogieStmtListBuilder builder, Variables localVariables, ExpressionTranslator etran) { var inductionVars = ApplyInduction(m.Ins, m.Attributes); if (inductionVars.Count != 0) { @@ -833,7 +833,7 @@ private void AddMethodOverrideCheckImpl(Method m, Boogie.Procedure proc) { var builder = new BoogieStmtListBuilder(this, options, new BodyTranslationContext(false)); var etran = new ExpressionTranslator(this, predef, m.tok, m); - var localVariables = new List(); + var localVariables = new Variables(); InitializeFuelConstant(m.tok, builder, etran); // assume traitTypeParameter == G(overrideTypeParameters); @@ -898,7 +898,7 @@ private void AddMethodOverrideCheckImpl(Method m, Boogie.Procedure proc) { Reset(); } - private void HavocMethodFrameLocations(Method m, BoogieStmtListBuilder builder, ExpressionTranslator etran, List localVariables) { + private void HavocMethodFrameLocations(Method m, BoogieStmtListBuilder builder, ExpressionTranslator etran, Variables localVariables) { Contract.Requires(m != null); Contract.Requires(m.EnclosingClass != null && m.EnclosingClass is ClassLikeDecl); @@ -1014,7 +1014,7 @@ private void AddFunctionOverrideCheckImpl(Function f) { //List outParams = Bpl.Formal.StripWhereClauses(proc.OutParams); BoogieStmtListBuilder builder = new BoogieStmtListBuilder(this, options, new BodyTranslationContext(false)); - List localVariables = new List(); + var localVariables = new Variables(); InitializeFuelConstant(f.tok, builder, etran); @@ -1132,12 +1132,12 @@ private void AddFunctionOverrideEnsChk(Function f, BoogieStmtListBuilder builder var constraint = allOverrideEns == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Imp, allOverrideEns, subEn); - builder.Add(Assert(f.tok, s.E, new PODesc.FunctionContractOverride(true, constraint), builder.Context)); + builder.Add(Assert(f.tok, s.E, new FunctionContractOverride(true, constraint), builder.Context)); } } } - private void AddOverrideCheckTypeArgumentInstantiations(MemberDecl member, BoogieStmtListBuilder builder, List localVariables) { + private void AddOverrideCheckTypeArgumentInstantiations(MemberDecl member, BoogieStmtListBuilder builder, Variables localVariables) { Contract.Requires(member is Function || member is Method); Contract.Requires(member.EnclosingClass is TopLevelDeclWithMembers); Contract.Requires(builder != null); @@ -1164,7 +1164,7 @@ private void AddOverrideCheckTypeArgumentInstantiations(MemberDecl member, Boogi } - private void AddFunctionOverrideSubsetChk(Function func, BoogieStmtListBuilder builder, ExpressionTranslator etran, List localVariables, + private void AddFunctionOverrideSubsetChk(Function func, BoogieStmtListBuilder builder, ExpressionTranslator etran, Variables localVariables, Dictionary substMap, Dictionary typeMap) { //getting framePrime @@ -1204,7 +1204,7 @@ private void AddFunctionOverrideSubsetChk(Function func, BoogieStmtListBuilder b Bpl.Expr consequent2 = InRWClause(tok, o, f, traitFrameExps, etran, null, null); Bpl.Expr q = new Bpl.ForallExpr(tok, new List(), new List { oVar, fVar }, BplImp(BplAnd(ante, oInCallee), consequent2)); - var description = new PODesc.TraitFrame(func.WhatKind, false, func.Reads.Expressions, traitFrameExps); + var description = new TraitFrame(func.WhatKind, false, func.Reads.Expressions, traitFrameExps); builder.Add(Assert(tok, q, description, builder.Context, kv)); } @@ -1232,7 +1232,7 @@ private void AddFunctionOverrideReqsChk(Function f, BoogieStmtListBuilder builde var constraint = allTraitReqs == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Imp, allTraitReqs, req.E); - builder.Add(Assert(f.tok, s.E, new PODesc.FunctionContractOverride(false, constraint), builder.Context)); + builder.Add(Assert(f.tok, s.E, new FunctionContractOverride(false, constraint), builder.Context)); } } } @@ -1480,7 +1480,7 @@ private void AddMethodOverrideEnsChk(Method m, BoogieStmtListBuilder builder, Ex var constraint = allOverrideEns == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Imp, allOverrideEns, subEn); - builder.Add(Assert(m.RangeToken, s.E, new PODesc.EnsuresStronger(constraint), builder.Context)); + builder.Add(Assert(m.RangeToken, s.E, new EnsuresStronger(constraint), builder.Context)); } } } @@ -1509,7 +1509,7 @@ private void AddMethodOverrideReqsChk(Method m, BoogieStmtListBuilder builder, E var constraint = allTraitReqs == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Imp, allTraitReqs, req.E); - builder.Add(Assert(m.RangeToken, s.E, new PODesc.RequiresWeaker(constraint), builder.Context)); + builder.Add(Assert(m.RangeToken, s.E, new RequiresWeaker(constraint), builder.Context)); } } } @@ -1592,11 +1592,11 @@ private void AddOverrideTerminationChk(ICallable original, ICallable overryd, Bo contextDecreases.Select(sub.Substitute).ToList(), calleeDecreases, true); - var desc = new PODesc.TraitDecreases(original.WhatKind, assertedExpr); + var desc = new TraitDecreases(original.WhatKind, assertedExpr); builder.Add(Assert(original.RangeToken, decrChk, desc, builder.Context)); } - private void AddMethodOverrideFrameSubsetChk(Method m, bool isModifies, BoogieStmtListBuilder builder, ExpressionTranslator etran, List localVariables, + private void AddMethodOverrideFrameSubsetChk(Method m, bool isModifies, BoogieStmtListBuilder builder, ExpressionTranslator etran, Variables localVariables, Dictionary substMap, Dictionary typeMap) { @@ -1638,7 +1638,7 @@ private void AddMethodOverrideFrameSubsetChk(Method m, bool isModifies, BoogieSt var consequent2 = InRWClause(tok, o, f, traitFrameExps, etran, null, null); var q = new Boogie.ForallExpr(tok, new List(), new List { oVar, fVar }, BplImp(BplAnd(ante, oInCallee), consequent2)); - var description = new PODesc.TraitFrame(m.WhatKind, isModifies, classFrameExps, traitFrameExps); + var description = new TraitFrame(m.WhatKind, isModifies, classFrameExps, traitFrameExps); builder.Add(Assert(m.RangeToken, q, description, builder.Context, kv)); } @@ -1745,8 +1745,8 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { if (formal.IsOld) { var dafnyFormalIdExpr = new IdentifierExpr(formal.tok, formal); var pIdx = m.Ins.Count == 1 ? "" : " at index " + index; - var desc = new PODesc.IsAllocated($"argument{pIdx} for parameter '{formal.Name}'", - "in the two-state lemma's previous state" + PODesc.IsAllocated.HelperFormal(formal), + var desc = new IsAllocated($"argument{pIdx} for parameter '{formal.Name}'", + "in the two-state lemma's previous state" + IsAllocated.HelperFormal(formal), dafnyFormalIdExpr ); var require = Requires(formal.tok, false, null, MkIsAlloc(etran.TrExpr(dafnyFormalIdExpr), formal.Type, prevHeap), @@ -1793,7 +1793,8 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { var req = GetRequires(); var mod = new List { ordinaryEtran.HeapCastToIdentifierExpr }; var ens = GetEnsures(); - var proc = new Bpl.Procedure(m.tok, name, new List(), inParams, outParams, false, req, mod, ens, etran.TrAttributes(m.Attributes, null)); + var proc = new Bpl.Procedure(m.tok, name, new List(), + inParams, outParams.Values.ToList(), false, req, mod, ens, etran.TrAttributes(m.Attributes, null)); AddVerboseNameAttribute(proc, m.FullDafnyName, kind); if (InsertChecksums) { diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs index e527192ac5e..a4f13182086 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs @@ -652,7 +652,7 @@ private void GenerateAndCheckGuesses(IToken tok, List bvars, List>, Expression>> GeneratePartialGuesses(List bvars, Expression expression) { @@ -1194,7 +1194,7 @@ private Bpl.Expr IntToBV(IToken tok, Bpl.Expr r, Type toType) { /// /// Emit checks that "expr" (which may or may not be a value of type "expr.Type"!) is a value of type "toType". /// - void CheckResultToBeInType(IToken tok, Expression expr, Type toType, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, string errorMsgPrefix = "") { + void CheckResultToBeInType(IToken tok, Expression expr, Type toType, Variables locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, string errorMsgPrefix = "") { Contract.Requires(tok != null); Contract.Requires(expr != null); Contract.Requires(toType != null); @@ -1242,13 +1242,13 @@ void PutSourceIntoLocal() { Bpl.Expr from = FunctionCall(tok, BuiltinFunction.RealToInt, null, o); Bpl.Expr e = FunctionCall(tok, BuiltinFunction.IntToReal, null, from); e = Bpl.Expr.Binary(tok, Bpl.BinaryOperator.Opcode.Eq, e, o); - builder.Add(Assert(tok, e, new PODesc.IsInteger(expr, errorMsgPrefix), builder.Context)); + builder.Add(Assert(tok, e, new IsInteger(expr, errorMsgPrefix), builder.Context)); } if (fromType.IsBigOrdinalType && !toType.IsBigOrdinalType) { PutSourceIntoLocal(); Bpl.Expr boundsCheck = FunctionCall(tok, "ORD#IsNat", Bpl.Type.Bool, o); - builder.Add(Assert(tok, boundsCheck, new PODesc.ConversionIsNatural(errorMsgPrefix, expr), builder.Context)); + builder.Add(Assert(tok, boundsCheck, new ConversionIsNatural(errorMsgPrefix, expr), builder.Context)); } if (toTypeFamily.IsBitVectorType) { @@ -1300,22 +1300,22 @@ void PutSourceIntoLocal() { } if (boundsCheck != null) { - builder.Add(Assert(tok, boundsCheck, new PODesc.ConversionFit("value", toType, dafnyBoundsCheck, errorMsgPrefix), builder.Context)); + builder.Add(Assert(tok, boundsCheck, new ConversionFit("value", toType, dafnyBoundsCheck, errorMsgPrefix), builder.Context)); } } else if (toType.IsCharType) { if (fromType.IsNumericBased(Type.NumericPersuasion.Int)) { PutSourceIntoLocal(); var boundsCheck = FunctionCall(Token.NoToken, BuiltinFunction.IsChar, null, o); - var dafnyBoundsCheck = PODesc.Utils.MakeCharBoundsCheck(options, expr); - builder.Add(Assert(tok, boundsCheck, new PODesc.ConversionFit("value", toType, dafnyBoundsCheck, errorMsgPrefix), builder.Context)); + var dafnyBoundsCheck = Utils.MakeCharBoundsCheck(options, expr); + builder.Add(Assert(tok, boundsCheck, new ConversionFit("value", toType, dafnyBoundsCheck, errorMsgPrefix), builder.Context)); } else if (fromType.IsNumericBased(Type.NumericPersuasion.Real)) { PutSourceIntoLocal(); var oi = FunctionCall(tok, BuiltinFunction.RealToInt, null, o); var boundsCheck = FunctionCall(Token.NoToken, BuiltinFunction.IsChar, null, oi); Expression intExpr = new ExprDotName(expr.tok, expr, "Floor", null); - var dafnyBoundsCheck = PODesc.Utils.MakeCharBoundsCheck(options, intExpr); - builder.Add(Assert(tok, boundsCheck, new PODesc.ConversionFit("real value", toType, dafnyBoundsCheck, errorMsgPrefix), builder.Context)); + var dafnyBoundsCheck = Utils.MakeCharBoundsCheck(options, intExpr); + builder.Add(Assert(tok, boundsCheck, new ConversionFit("real value", toType, dafnyBoundsCheck, errorMsgPrefix), builder.Context)); } else if (fromType.IsBitVectorType) { PutSourceIntoLocal(); var fromWidth = fromType.AsBitVectorType.Width; @@ -1328,7 +1328,7 @@ void PutSourceIntoLocal() { var boundsCheck = FunctionCall(expr.tok, "lt_bv" + fromWidth, Bpl.Type.Bool, o, bound); var dafnyBound = new BinaryExpr(expr.tok, BinaryExpr.Opcode.LeftShift, Expression.CreateIntLiteral(expr.tok, 1), Expression.CreateIntLiteral(expr.tok, toWidth)); var dafnyBoundsCheck = new BinaryExpr(expr.tok, BinaryExpr.Opcode.Lt, expr, dafnyBound); - builder.Add(Assert(tok, boundsCheck, new PODesc.ConversionFit("bit-vector value", toType, dafnyBoundsCheck, errorMsgPrefix), builder.Context)); + builder.Add(Assert(tok, boundsCheck, new ConversionFit("bit-vector value", toType, dafnyBoundsCheck, errorMsgPrefix), builder.Context)); } } else if (fromType.IsBigOrdinalType) { PutSourceIntoLocal(); @@ -1340,14 +1340,14 @@ void PutSourceIntoLocal() { var dafnyBound = new BinaryExpr(expr.tok, BinaryExpr.Opcode.LeftShift, Expression.CreateIntLiteral(expr.tok, 1), Expression.CreateIntLiteral(expr.tok, toWidth)); var offset = new ExprDotName(expr.tok, expr, "Offset", null); var dafnyBoundsCheck = new BinaryExpr(expr.tok, BinaryExpr.Opcode.Lt, offset, dafnyBound); - builder.Add(Assert(tok, boundsCheck, new PODesc.ConversionFit("ORDINAL value", toType, dafnyBoundsCheck, errorMsgPrefix), builder.Context)); + builder.Add(Assert(tok, boundsCheck, new ConversionFit("ORDINAL value", toType, dafnyBoundsCheck, errorMsgPrefix), builder.Context)); } } else if (toType.IsBigOrdinalType) { if (fromType.IsNumericBased(Type.NumericPersuasion.Int)) { PutSourceIntoLocal(); Bpl.Expr boundsCheck = Bpl.Expr.Le(Bpl.Expr.Literal(0), o); - var desc = new PODesc.ConversionPositive("integer", toType, expr, errorMsgPrefix); + var desc = new ConversionPositive("integer", toType, expr, errorMsgPrefix); builder.Add(Assert(tok, boundsCheck, desc, builder.Context)); } if (fromType.IsNumericBased(Type.NumericPersuasion.Real)) { @@ -1355,7 +1355,7 @@ void PutSourceIntoLocal() { var oi = FunctionCall(tok, BuiltinFunction.RealToInt, null, o); Bpl.Expr boundsCheck = Bpl.Expr.Le(Bpl.Expr.Literal(0), oi); var intExpr = new ExprDotName(expr.tok, expr, "Floor", null); - var desc = new PODesc.ConversionPositive("real", toType, intExpr, errorMsgPrefix); + var desc = new ConversionPositive("real", toType, intExpr, errorMsgPrefix); builder.Add(Assert(tok, boundsCheck, desc, builder.Context)); } @@ -1415,7 +1415,7 @@ void CheckResultToBeInType_Aux(IToken tok, Expression boogieExpr, Expression ori var typeMap = TypeParameter.SubstitutionMap(rdt.TypeArgs, udt.TypeArgs); var dafnyConstraint = Substitute(rdt.Constraint, null, new() { { rdt.Var, origExpr } }, typeMap); var boogieConstraint = etran.TrExpr(Substitute(rdt.Constraint, null, new() { { rdt.Var, boogieExpr } }, typeMap)); - builder.Add(Assert(tok, boogieConstraint, new PODesc.ConversionSatisfiesConstraints(errorMsgPrefix, kind, rdt.Name, dafnyConstraint), builder.Context)); + builder.Add(Assert(tok, boogieConstraint, new ConversionSatisfiesConstraints(errorMsgPrefix, kind, rdt.Name, dafnyConstraint), builder.Context)); } } @@ -1473,7 +1473,7 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { // 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 locals = new List(); + var locals = new Variables(); var context = new BodyTranslationContext(false); var builder = new BoogieStmtListBuilder(this, options, context); builder.Add(new CommentCmd(string.Format("AddWellformednessCheck for {0} {1}", decl.WhatKind, decl))); @@ -1522,7 +1522,7 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { witnessExpr = decl.Constraint != null ? Substitute(decl.Constraint, decl.Var, decl.Witness) : null; if (witnessExpr != null) { witnessExpr.tok = result.Tok; - var desc = new PODesc.WitnessCheck(witnessString, witnessExpr); + var desc = new WitnessCheck(witnessString, witnessExpr); SplitAndAssertExpression(returnBuilder, witnessExpr, etran, context, desc); } }); @@ -1531,7 +1531,7 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { var witness = Zero(decl.tok, baseType); if (witness == null) { witnessString = ""; - witnessCheckBuilder.Add(Assert(decl.tok, Bpl.Expr.False, new PODesc.WitnessCheck(witnessString), builder.Context)); + witnessCheckBuilder.Add(Assert(decl.tok, Bpl.Expr.False, new WitnessCheck(witnessString), builder.Context)); } else { // before trying 0 as a witness, check that 0 can be assigned to baseType witnessString = Printer.ExprToString(options, witness); @@ -1539,7 +1539,7 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { witnessExpr = decl.Constraint != null ? Substitute(decl.Constraint, decl.Var, witness) : null; if (witnessExpr != null) { witnessExpr.tok = decl.tok; - var desc = new PODesc.WitnessCheck(witnessString, witnessExpr); + var desc = new WitnessCheck(witnessString, witnessExpr); SplitAndAssertExpression(witnessCheckBuilder, witnessExpr, etran, context, desc); } } @@ -1566,7 +1566,7 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { } private void SplitAndAssertExpression(BoogieStmtListBuilder witnessCheckBuilder, Expression witnessExpr, - ExpressionTranslator etran, BodyTranslationContext context, PODesc.WitnessCheck desc) { + ExpressionTranslator etran, BodyTranslationContext context, WitnessCheck desc) { witnessCheckBuilder.Add(new Bpl.AssumeCmd(witnessExpr.tok, etran.CanCallAssumption(witnessExpr))); var ss = TrSplitExpr(context, witnessExpr, etran, true, out var splitHappened); @@ -1583,7 +1583,7 @@ private void SplitAndAssertExpression(BoogieStmtListBuilder witnessCheckBuilder, } private BoogieStmtListBuilder CheckConstraintWellformedness(RedirectingTypeDecl decl, BodyTranslationContext context, - Bpl.Expr whereClause, ExpressionTranslator etran, List locals, BoogieStmtListBuilder builder) { + Bpl.Expr whereClause, ExpressionTranslator etran, Variables locals, BoogieStmtListBuilder builder) { var constraintCheckBuilder = new BoogieStmtListBuilder(this, options, context); var builderInitializationArea = new BoogieStmtListBuilder(this, options, context); if (decl.Constraint == null) { diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index 6b84d3400a3..e0c65c03cd5 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -1402,10 +1402,10 @@ void AddEnsures(List list, Bpl.Ensures ens) { } private Implementation AddImplementationWithAttributes(IToken tok, Procedure proc, List inParams, - List outParams, List localVariables, StmtList stmts, QKeyValue kv) { + List outParams, Variables localVariables, StmtList stmts, QKeyValue kv) { Bpl.Implementation impl = new Bpl.Implementation(tok, proc.Name, new List(), inParams, outParams, - localVariables, stmts, kv); + localVariables.Values.ToList(), stmts, kv); AddVerboseNameAttribute(impl, proc.VerboseName); if (options.IsUsingZ3()) { if (DisableNonLinearArithmetic) { @@ -1781,7 +1781,7 @@ public VerificationIdGenerator CurrentIdGenerator { public int assertionCount = 0; - Bpl.IdentifierExpr GetTmpVar_IdExpr(Bpl.IToken tok, string name, Bpl.Type ty, List locals) // local variable that's shared between statements that need it + Bpl.IdentifierExpr GetTmpVar_IdExpr(Bpl.IToken tok, string name, Bpl.Type ty, Variables locals) // local variable that's shared between statements that need it { Contract.Requires(tok != null); Contract.Requires(name != null); @@ -1802,7 +1802,7 @@ Bpl.IdentifierExpr GetTmpVar_IdExpr(Bpl.IToken tok, string name, Bpl.Type ty, Li return ie; } - Bpl.IdentifierExpr GetPrevHeapVar_IdExpr(IToken tok, List locals) // local variable that's shared between statements that need it + Bpl.IdentifierExpr GetPrevHeapVar_IdExpr(IToken tok, Variables locals) // local variable that's shared between statements that need it { Contract.Requires(tok != null); Contract.Requires(locals != null); Contract.Requires(predef != null); @@ -1811,7 +1811,7 @@ Bpl.IdentifierExpr GetPrevHeapVar_IdExpr(IToken tok, List locals) // return GetTmpVar_IdExpr(tok, "$prevHeap", predef.HeapType, locals); } - Bpl.IdentifierExpr GetNewVar_IdExpr(IToken tok, List locals) // local variable that's shared between statements that need it + Bpl.IdentifierExpr GetNewVar_IdExpr(IToken tok, Variables locals) // local variable that's shared between statements that need it { Contract.Requires(tok != null); Contract.Requires(locals != null); @@ -1829,7 +1829,7 @@ Bpl.IdentifierExpr GetNewVar_IdExpr(IToken tok, List locals) // local /// have the same type "ty" and that these variables can be shared. /// As an optimization, if "otherExprsCanAffectPreviouslyKnownExpressions" is "false", then "expr" itself is returned. /// - Bpl.Expr SaveInTemp(Bpl.Expr expr, bool otherExprsCanAffectPreviouslyKnownExpressions, string name, Bpl.Type ty, BoogieStmtListBuilder builder, List locals) { + Bpl.Expr SaveInTemp(Bpl.Expr expr, bool otherExprsCanAffectPreviouslyKnownExpressions, string name, Bpl.Type ty, BoogieStmtListBuilder builder, Variables locals) { Contract.Requires(expr != null); Contract.Requires(name != null); Contract.Requires(ty != null); @@ -2020,7 +2020,7 @@ public void InsertUniqueIdForImplementation(Bpl.Declaration decl) { } void GenerateImplPrelude(Method m, bool wellformednessProc, List inParams, List outParams, - BoogieStmtListBuilder builder, List localVariables, ExpressionTranslator etran) { + BoogieStmtListBuilder builder, Variables localVariables, ExpressionTranslator etran) { Contract.Requires(m != null); Contract.Requires(inParams != null); Contract.Requires(outParams != null); @@ -2050,13 +2050,12 @@ void GenerateImplPrelude(Method m, bool wellformednessProc, List inPar } public void DefineFrame(IToken/*!*/ tok, Boogie.IdentifierExpr frameIdentifier, List/*!*/ frameClause, - BoogieStmtListBuilder/*!*/ builder, List/*!*/ localVariables, string name, ExpressionTranslator/*?*/ etran = null) { + BoogieStmtListBuilder/*!*/ builder, Variables localVariables, string name, ExpressionTranslator/*?*/ etran = null) { Contract.Requires(tok != null); Contract.Requires(frameIdentifier != null); Contract.Requires(frameIdentifier.Type != null); Contract.Requires(cce.NonNullElements(frameClause)); Contract.Requires(builder != null); - Contract.Requires(cce.NonNullElements(localVariables)); Contract.Requires(predef != null); if (etran == null) { @@ -2087,7 +2086,7 @@ public void CheckFrameSubset(IToken tok, List calleeFrame, Expression receiverReplacement, Dictionary substMap, ExpressionTranslator /*!*/ etran, Boogie.IdentifierExpr /*!*/ enclosingFrame, BoogieStmtListBuilder /*!*/ builder, - PODesc.ProofObligationDescription desc, + ProofObligationDescription desc, Bpl.QKeyValue kv) { CheckFrameSubset(tok, calleeFrame, receiverReplacement, substMap, etran, enclosingFrame, (t, e, d, q) => builder.Add(Assert(t, e, d, builder.Context, q)), desc, kv); @@ -2096,8 +2095,8 @@ public void CheckFrameSubset(IToken tok, List calleeFrame, void CheckFrameSubset(IToken tok, List calleeFrame, Expression receiverReplacement, Dictionary substMap, ExpressionTranslator/*!*/ etran, Boogie.IdentifierExpr /*!*/ enclosingFrame, - Action makeAssert, - PODesc.ProofObligationDescription desc, + Action makeAssert, + ProofObligationDescription desc, Bpl.QKeyValue kv) { Contract.Requires(tok != null); Contract.Requires(calleeFrame != null); @@ -2125,7 +2124,7 @@ void CheckFrameSubset(IToken tok, List calleeFrame, void CheckFrameEmpty(IToken tok, ExpressionTranslator/*!*/ etran, Boogie.IdentifierExpr /*!*/ frame, - BoogieStmtListBuilder/*!*/ builder, PODesc.ProofObligationDescription desc, + BoogieStmtListBuilder/*!*/ builder, ProofObligationDescription desc, Bpl.QKeyValue kv) { Contract.Requires(tok != null); Contract.Requires(etran != null); @@ -2300,7 +2299,7 @@ Bpl.Expr InRWClause_Aux(IToken tok, Bpl.Expr o, Bpl.Expr boxO, Bpl.Expr f, List< /// If "declareLocals" is "false", then the locals are added only if they are new, that is, if /// they don't already exist in "locals". /// - Bpl.Expr CtorInvocation(MatchCase mc, Type sourceType, ExpressionTranslator etran, List locals, BoogieStmtListBuilder localTypeAssumptions, IsAllocType isAlloc, bool declareLocals = true) { + Bpl.Expr CtorInvocation(MatchCase mc, Type sourceType, ExpressionTranslator etran, Variables locals, BoogieStmtListBuilder localTypeAssumptions, IsAllocType isAlloc, bool declareLocals = true) { Contract.Requires(mc != null); Contract.Requires(sourceType != null); Contract.Requires(etran != null); @@ -2320,7 +2319,7 @@ Bpl.Expr CtorInvocation(MatchCase mc, Type sourceType, ExpressionTranslator etra for (int i = 0; i < mc.Arguments.Count; i++) { BoundVar p = mc.Arguments[i]; var nm = p.AssignUniqueName(currentDeclaration.IdGenerator); - Bpl.Variable local = declareLocals ? null : locals.FirstOrDefault(v => v.Name == nm); // find previous local + Bpl.Variable local = declareLocals ? null : locals.GetValueOrDefault(nm); // find previous local if (local == null) { local = new Bpl.LocalVariable(p.tok, new Bpl.TypedIdent(p.tok, nm, TrType(p.Type))); locals.Add(local); @@ -2340,7 +2339,7 @@ Bpl.Expr CtorInvocation(MatchCase mc, Type sourceType, ExpressionTranslator etra return new Bpl.NAryExpr(mc.tok, new Bpl.FunctionCall(id), args); } - Bpl.Expr CtorInvocation(IToken tok, DatatypeCtor ctor, ExpressionTranslator etran, List locals, BoogieStmtListBuilder localTypeAssumptions) { + Bpl.Expr CtorInvocation(IToken tok, DatatypeCtor ctor, ExpressionTranslator etran, Variables locals, BoogieStmtListBuilder localTypeAssumptions) { Contract.Requires(tok != null); Contract.Requires(ctor != null); Contract.Requires(etran != null); @@ -2441,7 +2440,7 @@ void CheckCasePatternShape(CasePattern pat, Expression dRhs, Bpl.Expr rh // There is only one constructor, so the value must have been constructed by it; might as well assume that here. builder.Add(TrAssumeCmd(pat.tok, correctConstructor)); } else { - builder.Add(Assert(pat.tok, correctConstructor, new PODesc.PatternShapeIsValid(dRhs, ctor.Name), builder.Context)); + builder.Add(Assert(pat.tok, correctConstructor, new PatternShapeIsValid(dRhs, ctor.Name), builder.Context)); } for (int i = 0; i < pat.Arguments.Count; i++) { var arg = pat.Arguments[i]; @@ -2470,14 +2469,14 @@ void CheckNonNull(IToken tok, Expression e, BoogieStmtListBuilder builder, Expre // also ok } else { builder.Add(Assert(tok, Bpl.Expr.Neq(etran.TrExpr(e), predef.Null), - new PODesc.NonNull("target object", e), builder.Context, kv)); + new NonNull("target object", e), builder.Context, kv)); } } void CheckFunctionSelectWF(string what, BoogieStmtListBuilder builder, ExpressionTranslator etran, Expression e, string hint) { if (e is MemberSelectExpr sel && sel.Member is Function fn) { Bpl.Expr assertion = !InVerificationScope(fn) ? Bpl.Expr.True : Bpl.Expr.Not(etran.HeightContext(fn)); - builder.Add(Assert(GetToken(e), assertion, new PODesc.ValidInRecursion(what, hint), builder.Context)); + builder.Add(Assert(GetToken(e), assertion, new ValidInRecursion(what, hint), builder.Context)); } } @@ -2574,7 +2573,7 @@ private Expr NewOneHeapExpr(IToken tok) { /// -- "obj" as an expression "e[i]", where "i" is a new identifier, and /// -- "antecedent" as "0 <= i < |e|". /// - void EachReferenceInFrameExpression(Expression e, List locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, + void EachReferenceInFrameExpression(Expression e, Variables locals, BoogieStmtListBuilder builder, ExpressionTranslator etran, out string description, out Type type, out Bpl.Expr obj, out Bpl.Expr antecedent) { Contract.Requires(e != null); Contract.Requires(locals != null); @@ -2859,13 +2858,14 @@ private static CallCmd Call(BodyTranslationContext context, IToken tok, string m return call; } - private void GenerateMethodParameters(IToken tok, Method m, MethodTranslationKind kind, ExpressionTranslator etran, List inParams, out List outParams) { + private void GenerateMethodParameters(IToken tok, Method m, MethodTranslationKind kind, ExpressionTranslator etran, + List inParams, out Variables outParams) { GenerateMethodParametersChoose(tok, m, kind, !m.IsStatic, true, true, etran, inParams, out outParams); } private void GenerateMethodParametersChoose(IToken tok, IMethodCodeContext m, MethodTranslationKind kind, bool includeReceiver, bool includeInParams, bool includeOutParams, - ExpressionTranslator etran, List inParams, out List outParams) { - outParams = new List(); + ExpressionTranslator etran, List inParams, out Variables outParams) { + outParams = new Variables(); // Add type parameters first, always! inParams.AddRange(MkTyParamFormals(GetTypeParams(m), true)); if (includeReceiver) { @@ -3335,12 +3335,12 @@ public override IToken WithVal(string newVal) { } } - public Bpl.PredicateCmd Assert(IToken tok, Bpl.Expr condition, PODesc.ProofObligationDescription description, + public Bpl.PredicateCmd Assert(IToken tok, Bpl.Expr condition, ProofObligationDescription description, BodyTranslationContext context, Bpl.QKeyValue kv = null) { return Assert(tok, condition, description, tok, context, kv); } - private PredicateCmd Assert(IToken tok, Expr condition, PODesc.ProofObligationDescription description, + private PredicateCmd Assert(IToken tok, Expr condition, ProofObligationDescription description, IToken refinesToken, BodyTranslationContext context, QKeyValue kv = null) { Contract.Requires(tok != null); Contract.Requires(condition != null); @@ -3360,11 +3360,11 @@ private PredicateCmd Assert(IToken tok, Expr condition, PODesc.ProofObligationDe return cmd; } - PredicateCmd AssertAndForget(BodyTranslationContext context, IToken tok, Bpl.Expr condition, PODesc.ProofObligationDescription desc) { + PredicateCmd AssertAndForget(BodyTranslationContext context, IToken tok, Bpl.Expr condition, ProofObligationDescription desc) { return AssertAndForget(context, tok, condition, desc, tok, null); } - PredicateCmd AssertAndForget(BodyTranslationContext context, IToken tok, Bpl.Expr condition, PODesc.ProofObligationDescription desc, IToken refinesTok, Bpl.QKeyValue kv) { + PredicateCmd AssertAndForget(BodyTranslationContext context, IToken tok, Bpl.Expr condition, ProofObligationDescription desc, IToken refinesTok, Bpl.QKeyValue kv) { Contract.Requires(tok != null); Contract.Requires(desc != null); Contract.Requires(condition != null); @@ -3404,7 +3404,7 @@ Bpl.Ensures Ensures(IToken tok, bool free, Expression dafnyCondition, Bpl.Expr c var unwrappedToken = ForceCheckToken.Unwrap(tok); Bpl.Ensures ens = new Bpl.Ensures(unwrappedToken, free, condition, comment); - var description = new PODesc.EnsuresDescription(dafnyCondition, errorMessage, successMessage); + var description = new EnsuresDescription(dafnyCondition, errorMessage, successMessage); ens.Description = description; if (!free) { ReportAssertion(unwrappedToken, description); @@ -3427,12 +3427,12 @@ Bpl.Requires Requires(IToken tok, bool free, Expression dafnyCondition, Bpl.Expr Contract.Requires(bCondition != null); Contract.Ensures(Contract.Result() != null); Bpl.Requires req = new Bpl.Requires(ForceCheckToken.Unwrap(tok), free, bCondition, comment); - req.Description = new PODesc.RequiresDescription(dafnyCondition, errorMessage, successMessage); + req.Description = new RequiresDescription(dafnyCondition, errorMessage, successMessage); return req; } Bpl.StmtList TrStmt2StmtList(BoogieStmtListBuilder builder, - Statement block, List locals, ExpressionTranslator etran, bool introduceScope = false) { + Statement block, Variables locals, ExpressionTranslator etran, bool introduceScope = false) { Contract.Requires(builder != null); Contract.Requires(block != null); Contract.Requires(locals != null); @@ -3501,14 +3501,14 @@ Bpl.AssertCmd TrAssertCmd(IToken tok, Bpl.Expr expr, Bpl.QKeyValue attributes = return attributes == null ? new Bpl.AssertCmd(tok, expr) : new Bpl.AssertCmd(tok, expr, attributes); } - Bpl.AssertCmd TrAssertCmdDesc(IToken tok, Bpl.Expr expr, - PODesc.ProofObligationDescription description, Bpl.QKeyValue attributes = null) { + Bpl.AssertCmd TrAssertCmdDesc(IToken tok, Bpl.Expr expr, + ProofObligationDescription description, Bpl.QKeyValue attributes = null) { ReportAssertion(tok, description); return new Bpl.AssertCmd(tok, expr, description, attributes); } private ISet<(Uri, int)> reportedAssertions = new HashSet<(Uri, int)>(); - private void ReportAssertion(IToken tok, PODesc.ProofObligationDescription description) { + private void ReportAssertion(IToken tok, ProofObligationDescription description) { if (!reportedAssertions.Add((tok.Uri, tok.pos))) { return; } @@ -3531,7 +3531,7 @@ private void ReportAssertion(IToken tok, PODesc.ProofObligationDescription descr } Dictionary SetupBoundVarsAsLocals(List boundVars, out Bpl.Expr typeAntecedent, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, string nameSuffix = null) { Contract.Requires(boundVars != null); Contract.Requires(builder != null); @@ -3560,7 +3560,7 @@ Dictionary SetupBoundVarsAsLocals(List boundVar } Dictionary SetupBoundVarsAsLocals(List boundVars, BoogieStmtListBuilder builder, - List locals, ExpressionTranslator etran, + Variables locals, ExpressionTranslator etran, string nameSuffix = null) { Contract.Requires(boundVars != null); Contract.Requires(builder != null); @@ -3580,7 +3580,7 @@ Dictionary SetupBoundVarsAsLocals(List boundVar /// is NOT emitted; rather, it is returned by this method. /// Bpl.Expr SetupVariableAsLocal(IVariable v, Dictionary substMap, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { Contract.Requires(v != null); Contract.Requires(substMap != null); Contract.Requires(builder != null); @@ -3601,7 +3601,7 @@ Bpl.Expr SetupVariableAsLocal(IVariable v, Dictionary sub return wh ?? Bpl.Expr.True; } - List RecordDecreasesValue(List decreases, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, string varPrefix) { + List RecordDecreasesValue(List decreases, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, string varPrefix) { Contract.Requires(locals != null); Contract.Requires(etran != null); Contract.Requires(varPrefix != null); @@ -3945,7 +3945,7 @@ Bpl.Expr GetSubrangeCheck( // allow null for checked expressions that cannot necessarily be named without side effects, such as method out-params [CanBeNull] Expression source, [CanBeNull] SubrangeCheckContext context, - out PODesc.ProofObligationDescription desc, string errorMessagePrefix = "") { + out ProofObligationDescription desc, string errorMessagePrefix = "") { Contract.Requires(bSource != null); Contract.Requires(sourceType != null); Contract.Requires(targetType != null); @@ -3985,19 +3985,19 @@ Bpl.Expr GetSubrangeCheck( certain = udt.ResolvedClass.TypeArgs.Count == 0; cause = "it may be null"; } - desc = new PODesc.SubrangeCheck(errorMessagePrefix, sourceType.ToString(), targetType.ToString(), false, certain, cause, dafnyCheck); + desc = new SubrangeCheck(errorMessagePrefix, sourceType.ToString(), targetType.ToString(), false, certain, cause, dafnyCheck); } else if (udt != null && ArrowType.IsTotalArrowTypeName(udt.Name)) { - desc = new PODesc.SubrangeCheck(errorMessagePrefix, sourceType.ToString(), targetType.ToString(), true, false, + desc = new SubrangeCheck(errorMessagePrefix, sourceType.ToString(), targetType.ToString(), true, false, "it may be partial or have read effects", canTestFunctionTypes ? dafnyCheck : null ); } else if (udt != null && ArrowType.IsPartialArrowTypeName(udt.Name)) { - desc = new PODesc.SubrangeCheck(errorMessagePrefix, sourceType.ToString(), targetType.ToString(), true, false, + desc = new SubrangeCheck(errorMessagePrefix, sourceType.ToString(), targetType.ToString(), true, false, "it may have read effects", canTestFunctionTypes ? dafnyCheck : null ); } else { - desc = new PODesc.SubrangeCheck(errorMessagePrefix, sourceType.ToString(), targetType.ToString(), + desc = new SubrangeCheck(errorMessagePrefix, sourceType.ToString(), targetType.ToString(), targetType.NormalizeExpandKeepConstraints() is UserDefinedType { ResolvedClass: SubsetTypeDecl or NewtypeDecl { Var: { } } }, @@ -4033,7 +4033,7 @@ void Check_NewRestrictions(IToken tok, Expression dObj, Bpl.Expr obj, Field f, B var fId = new Bpl.IdentifierExpr(tok, GetField(f)); var subset = FunctionCall(tok, BuiltinFunction.SetSubset, null, rhs, ApplyUnbox(tok, ReadHeap(tok, etran.HeapExpr, obj, fId), predef.SetType)); - builder.Add(Assert(tok, subset, new PODesc.AssignmentShrinks(dObj, f.Name), builder.Context)); + builder.Add(Assert(tok, subset, new AssignmentShrinks(dObj, f.Name), builder.Context)); } } diff --git a/Source/DafnyCore/Verifier/Datatypes/BoogieGenerator.DataTypes.cs b/Source/DafnyCore/Verifier/Datatypes/BoogieGenerator.DataTypes.cs index 91de9daa3ea..396815f117c 100644 --- a/Source/DafnyCore/Verifier/Datatypes/BoogieGenerator.DataTypes.cs +++ b/Source/DafnyCore/Verifier/Datatypes/BoogieGenerator.DataTypes.cs @@ -838,7 +838,7 @@ void AddWellformednessCheck(DatatypeCtor ctor) { sink.AddTopLevelDeclaration(proc); var implInParams = Bpl.Formal.StripWhereClauses(inParams); - var locals = new List(); + var locals = new Variables(); var builder = new BoogieStmtListBuilder(this, options, new BodyTranslationContext(false)); builder.Add(new CommentCmd($"AddWellformednessCheck for datatype constructor {ctor}")); builder.AddCaptureState(ctor.tok, false, "initial state"); diff --git a/Source/DafnyCore/Verifier/ProofDependency.cs b/Source/DafnyCore/Verifier/ProofDependency.cs index e7ab874bfad..78f556603bb 100644 --- a/Source/DafnyCore/Verifier/ProofDependency.cs +++ b/Source/DafnyCore/Verifier/ProofDependency.cs @@ -55,12 +55,12 @@ public string OriginalString() { public class ProofObligationDependency : ProofDependency { public override RangeToken Range { get; } - public PODesc.ProofObligationDescription ProofObligation { get; } + public ProofObligationDescription ProofObligation { get; } public override string Description => $"{ProofObligation.SuccessDescription}"; - public ProofObligationDependency(Microsoft.Boogie.IToken tok, PODesc.ProofObligationDescription proofObligation) { + public ProofObligationDependency(Microsoft.Boogie.IToken tok, ProofObligationDescription proofObligation) { Range = BoogieGenerator.ToDafnyToken(true, tok).ToRange(); ProofObligation = proofObligation; } @@ -69,12 +69,12 @@ public ProofObligationDependency(Microsoft.Boogie.IToken tok, PODesc.ProofObliga public class AssumedProofObligationDependency : ProofDependency { public override RangeToken Range { get; } - public PODesc.ProofObligationDescription ProofObligation { get; } + public ProofObligationDescription ProofObligation { get; } public override string Description => $"assumption that {ProofObligation.SuccessDescription}"; - public AssumedProofObligationDependency(IToken tok, PODesc.ProofObligationDescription proofObligation) { + public AssumedProofObligationDependency(IToken tok, ProofObligationDescription proofObligation) { Range = tok as RangeToken ?? new RangeToken(tok, tok); ProofObligation = proofObligation; } diff --git a/Source/DafnyCore/Verifier/ProofObligationDescription.cs b/Source/DafnyCore/Verifier/ProofObligationDescription.cs index a83a9be9c58..e3e647d9a23 100644 --- a/Source/DafnyCore/Verifier/ProofObligationDescription.cs +++ b/Source/DafnyCore/Verifier/ProofObligationDescription.cs @@ -8,7 +8,7 @@ using JetBrains.Annotations; using Microsoft.Boogie; -namespace Microsoft.Dafny.ProofObligationDescription; +namespace Microsoft.Dafny; public abstract class ProofObligationDescription : Boogie.ProofObligationDescription { public virtual bool IsImplicit => true; diff --git a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs index 8bb908024ef..50c4e6482e4 100644 --- a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs +++ b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs @@ -1,13 +1,14 @@ +using System; using System.Collections.Generic; using System.Linq; using Microsoft.Boogie; using Microsoft.Dafny; -namespace DafnyCore.Verifier.Statements; +namespace Microsoft.Dafny; public class BlockByProofStmtVerifier { public static void EmitBoogie(BoogieGenerator generator, BlockByProofStmt block, BoogieStmtListBuilder builder, - List locals, BoogieGenerator.ExpressionTranslator etran, ICodeContext codeContext) { + Variables locals, BoogieGenerator.ExpressionTranslator etran, ICodeContext codeContext) { var proofBuilder = new BoogieStmtListBuilder(generator, builder.Options, builder.Context); generator.CurrentIdGenerator.Push(); @@ -15,11 +16,46 @@ public static void EmitBoogie(BoogieGenerator generator, BlockByProofStmt block, generator.CurrentIdGenerator.Pop(); generator.TrStmt(block.Body, proofBuilder, locals, etran); - + generator.PathAsideBlock(block.Tok, proofBuilder, builder); generator.TrStmt(block.Body, builder.WithContext(builder.Context with { AssertMode = AssertMode.Assume }), locals, etran); - + + } +} + +public class Variables : OrderedDictionary { + public Variables() : base(v => v.Name) { + } + +} + +public class OrderedDictionary { + private readonly Dictionary keyToValue = new(); + private readonly List keyOrder = new(); + private readonly Func getKey; + + public OrderedDictionary(Func getKey) { + this.getKey = getKey; } + public IEnumerable Values => keyOrder.Select(key => keyToValue[key]); + + public void AddRange(IEnumerable values) { + foreach (var value in values) { + Add(value); + } + } + + public void Add(TValue value) { + var key = getKey(value); + keyOrder.Add(key); + keyToValue[key] = value; + } + + public TValue GetValueOrDefault(TKey key) { + return keyToValue.GetValueOrDefault(key); + } + + public int Count => keyOrder.Count; } \ No newline at end of file diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs index 1d216b7d55f..c6dd53d7ae7 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs @@ -14,14 +14,13 @@ public partial class BoogieGenerator { /// "lhs" is expected to be a resolved form of an expression, i.e., not a concrete-syntax expression. /// void TrAssignment(Statement stmt, Expression lhs, AssignmentRhs rhs, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { Contract.Requires(stmt != null); Contract.Requires(lhs != null); Contract.Requires(!(lhs is ConcreteSyntaxExpression)); Contract.Requires(!(lhs is SeqSelectExpr && !((SeqSelectExpr)lhs).SelectOne)); // these were once allowed, but their functionality is now provided by 'forall' statements Contract.Requires(rhs != null); Contract.Requires(builder != null); - Contract.Requires(cce.NonNullElements(locals)); Contract.Requires(etran != null); Contract.Requires(predef != null); @@ -37,13 +36,12 @@ void TrAssignment(Statement stmt, Expression lhs, AssignmentRhs rhs, void ProcessRhss(List lhsBuilder, List bLhss, List lhss, List rhss, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, Statement stmt) { + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, Statement stmt) { Contract.Requires(lhsBuilder != null); Contract.Requires(bLhss != null); Contract.Requires(cce.NonNullElements(lhss)); Contract.Requires(cce.NonNullElements(rhss)); Contract.Requires(builder != null); - Contract.Requires(cce.NonNullElements(locals)); Contract.Requires(etran != null); Contract.Requires(predef != null); @@ -90,12 +88,11 @@ void ProcessRhss(List lhsBuilder, List ProcessUpdateAssignRhss(List lhss, List rhss, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, Statement stmt) { Contract.Requires(cce.NonNullElements(lhss)); Contract.Requires(cce.NonNullElements(rhss)); Contract.Requires(builder != null); - Contract.Requires(cce.NonNullElements(locals)); Contract.Requires(etran != null); Contract.Requires(predef != null); Contract.Ensures(Contract.ForAll(Contract.Result>(), i => i != null)); @@ -171,7 +168,7 @@ private void CheckLhssDistinctness(List rhs, List rhsOr void AssertDistinctness(Expression lhsa, Expression lhsb, BoogieStmtListBuilder builder, ExpressionTranslator etran) { CheckDistinctness(lhsa, lhsb, etran, out var dExpr, out var bExpr); if (bExpr != null) { - builder.Add(Assert(GetToken(lhsa), bExpr, new PODesc.DistinctLHS(Printer.ExprToString(options, lhsa), + builder.Add(Assert(GetToken(lhsa), bExpr, new DistinctLHS(Printer.ExprToString(options, lhsa), Printer.ExprToString(options, lhsb), bExpr != Bpl.Expr.False, false, dExpr), builder.Context)); } } @@ -180,7 +177,7 @@ void AssertDistinctness(Expression lhsa, Expression lhsb, Bpl.Expr rhsa, Bpl.Exp CheckDistinctness(lhsa, lhsb, etran, out var dExpr, out var bExpr); if (bExpr != null) { bExpr = BplOr(bExpr, Bpl.Expr.Eq(rhsa, rhsb)); - builder.Add(Assert(GetToken(lhsa), bExpr, new PODesc.DistinctLHS(Printer.ExprToString(options, lhsa), + builder.Add(Assert(GetToken(lhsa), bExpr, new DistinctLHS(Printer.ExprToString(options, lhsa), Printer.ExprToString(options, lhsb), false, true, dExpr), builder.Context)); } } @@ -192,13 +189,12 @@ void AssertDistinctness(Expression lhsa, Expression lhsb, Bpl.Expr rhsa, Bpl.Exp /// Checks that they denote different locations iff checkDistinctness is true. /// void ProcessLhss(List lhss, bool rhsCanAffectPreviouslyKnownExpressions, bool checkDistinctness, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, Statement stmt, + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, Statement stmt, out List lhsBuilders, out List bLhss, out Bpl.Expr[] prevObj, out Bpl.Expr[] prevIndex, out string[] prevNames, Expression originalInitialLhs = null) { Contract.Requires(cce.NonNullElements(lhss)); Contract.Requires(builder != null); - Contract.Requires(cce.NonNullElements(locals)); Contract.Requires(etran != null); Contract.Requires(predef != null); Contract.Ensures(Contract.ValueAtReturn(out lhsBuilders).Count == lhss.Count); @@ -267,7 +263,7 @@ void ProcessLhss(List lhss, bool rhsCanAffectPreviouslyKnownExpressi prevObj[i] = obj; if (!useSurrogateLocal) { // check that the enclosing modifies clause allows this object to be written: assert $_ModifiesFrame[obj]); - var desc = new PODesc.Modifiable("an object", contextModFrames, fse.Obj, field); + var desc = new Modifiable("an object", contextModFrames, fse.Obj, field); builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.ModifiesFrame(tok), obj, GetField(fse)), desc, builder.Context)); } @@ -317,7 +313,7 @@ void ProcessLhss(List lhss, bool rhsCanAffectPreviouslyKnownExpressi prevObj[i] = obj; prevIndex[i] = fieldName; // check that the enclosing modifies clause allows this object to be written: assert $_Frame[obj,index]); - var desc = new PODesc.Modifiable("an array element", contextModFrames, sel.Seq, null); + var desc = new Modifiable("an array element", contextModFrames, sel.Seq, null); builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.ModifiesFrame(tok), obj, fieldName), desc, builder.Context)); bLhss.Add(null); @@ -342,7 +338,7 @@ void ProcessLhss(List lhss, bool rhsCanAffectPreviouslyKnownExpressi "$index" + i, predef.FieldName(mse.tok), builder, locals); prevObj[i] = obj; prevIndex[i] = fieldName; - var desc = new PODesc.Modifiable("an array element", contextModFrames, mse.Array, null); + var desc = new Modifiable("an array element", contextModFrames, mse.Array, null); builder.Add(Assert(tok, Bpl.Expr.SelectTok(tok, etran.ModifiesFrame(tok), obj, fieldName), desc, builder.Context)); bLhss.Add(null); @@ -382,7 +378,7 @@ void ProcessLhss(List lhss, bool rhsCanAffectPreviouslyKnownExpressi /// Bpl.Expr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bGivenLhs, IVariable lhsVar, Type lhsType, AssignmentRhs rhs, Type rhsTypeConstraint, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, Statement stmt) { Contract.Requires(tok != null); Contract.Requires(rhs != null); @@ -467,7 +463,7 @@ Bpl.Expr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bGivenLhs, IVariable lhs int i = 0; foreach (Expression dim in tRhs.ArrayDimensions) { CheckWellformed(dim, new WFOptions(), locals, builder, etran); - var desc = new PODesc.NonNegative(tRhs.ArrayDimensions.Count == 1 + var desc = new NonNegative(tRhs.ArrayDimensions.Count == 1 ? "array size" : $"array size (dimension {i})", dim); builder.Add(Assert(GetToken(dim), Bpl.Expr.Le(Bpl.Expr.Literal(0), etran.TrExpr(dim)), desc, builder.Context)); i++; @@ -476,7 +472,7 @@ Bpl.Expr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bGivenLhs, IVariable lhs CheckWellformed(tRhs.ElementInit, new WFOptions(), locals, builder, etran); } else if (tRhs.InitDisplay != null) { var dim = tRhs.ArrayDimensions[0]; - var desc = new PODesc.ArrayInitSizeValid(tRhs, dim); + var desc = new ArrayInitSizeValid(tRhs, dim); builder.Add(Assert(GetToken(dim), Bpl.Expr.Eq(etran.TrExpr(dim), Bpl.Expr.Literal(tRhs.InitDisplay.Count)), desc, builder.Context)); foreach (var v in tRhs.InitDisplay) { CheckWellformed(v, new WFOptions(), locals, builder, etran); @@ -491,7 +487,7 @@ Bpl.Expr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bGivenLhs, IVariable lhs foreach (Expression dim in tRhs.ArrayDimensions) { zeroSize = BplOr(zeroSize, Bpl.Expr.Eq(Bpl.Expr.Literal(0), etran.TrExpr(dim))); } - var desc = new PODesc.ArrayInitEmpty(tRhs.EType.ToString(), tRhs.ArrayDimensions); + var desc = new ArrayInitEmpty(tRhs.EType.ToString(), tRhs.ArrayDimensions); builder.Add(Assert(tRhs.Tok, zeroSize, desc, builder.Context)); } } diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs index 241597595ab..c386cd8b1f9 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs @@ -3,7 +3,6 @@ using System.Diagnostics.Contracts; using System.Linq; using DafnyCore.Verifier; -using DafnyCore.Verifier.Statements; using Microsoft.Boogie; using Bpl = Microsoft.Boogie; using BplParser = Microsoft.Boogie.Parser; @@ -15,7 +14,7 @@ namespace Microsoft.Dafny; public partial class BoogieGenerator { - void TrCallStmt(CallStmt s, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, Bpl.IdentifierExpr actualReceiver) { + void TrCallStmt(CallStmt s, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, Bpl.IdentifierExpr actualReceiver) { Contract.Requires(s != null); Contract.Requires(builder != null); Contract.Requires(locals != null); @@ -99,7 +98,7 @@ void TrCallStmt(CallStmt s, BoogieStmtListBuilder builder, List locals void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.Expr bReceiver, List Lhss, List LhsTypes, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { Contract.Requires(cs != null); Contract.Requires(Lhss != null); @@ -238,7 +237,7 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E if (!method.IsStatic && !(method is Constructor)) { Bpl.Expr wh = GetWhereClause(receiver.tok, etran.TrExpr(receiver), receiver.Type, etran, ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("receiver argument", "in the state in which the method is invoked", receiver); + var desc = new IsAllocated("receiver argument", "in the state in which the method is invoked", receiver); builder.Add(Assert(receiver.tok, wh, desc, builder.Context)); } } @@ -246,7 +245,7 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E Expression ee = Args[i]; Bpl.Expr wh = GetWhereClause(ee.tok, etran.TrExpr(ee), ee.Type, etran, ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("argument", "in the state in which the method is invoked", ee); + var desc = new IsAllocated("argument", "in the state in which the method is invoked", ee); builder.Add(Assert(ee.tok, wh, desc, builder.Context)); } } @@ -254,7 +253,7 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E if (!method.IsStatic) { Bpl.Expr wh = GetWhereClause(receiver.tok, etran.TrExpr(receiver), receiver.Type, etran.OldAt(atLabel), ISALLOC, true); if (wh != null) { - var desc = new PODesc.IsAllocated("receiver argument", "in the two-state lemma's previous state", receiver, atLabel); + var desc = new IsAllocated("receiver argument", "in the two-state lemma's previous state", receiver, atLabel); builder.Add(Assert(receiver.tok, wh, desc, builder.Context)); } } @@ -266,9 +265,9 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E Bpl.Expr wh = GetWhereClause(ee.tok, etran.TrExpr(ee), ee.Type, etran.OldAt(atLabel), ISALLOC, true); if (wh != null) { var pIdx = Args.Count == 1 ? "" : " at index " + i; - var desc = new PODesc.IsAllocated( + var desc = new IsAllocated( $"argument{pIdx} for parameter '{formal.Name}'", - "in the two-state lemma's previous state" + PODesc.IsAllocated.HelperFormal(formal), + "in the two-state lemma's previous state" + IsAllocated.HelperFormal(formal), ee, atLabel ); @@ -285,7 +284,7 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E if (etran.readsFrame != null) { // substitute actual args for parameters in description expression frames... var requiredFrames = callee.Reads.Expressions.ConvertAll(directSub.SubstFrameExpr); - var desc = new PODesc.ReadFrameSubset("call", requiredFrames, GetContextReadsFrames()); + var desc = new ReadFrameSubset("call", requiredFrames, GetContextReadsFrames()); // ... but that substitution isn't needed for frames passed to CheckFrameSubset var readsSubst = new Substituter(null, new Dictionary(), tySubst); @@ -298,7 +297,7 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E // Check that the modifies clause of a subcall is a subset of the current modifies frame, // but only if we're in a context that defines a modifies frame. if (codeContext is IMethodCodeContext methodCodeContext) { - var desc = new PODesc.ModifyFrameSubset( + var desc = new ModifyFrameSubset( "call", frameExpressions, methodCodeContext.Modifies.Expressions @@ -314,7 +313,7 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E if (isRecursiveCall) { Contract.Assert(codeContext != null); if (codeContext is DatatypeDecl) { - builder.Add(Assert(tok, Bpl.Expr.False, new PODesc.IsNonRecursive(), builder.Context)); + builder.Add(Assert(tok, Bpl.Expr.False, new IsNonRecursive(), builder.Context)); } else { List contextDecreases = codeContext.Decreases.Expressions; List calleeDecreases = callee.Decreases.Expressions; diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs index 3548e74157c..ab7e6ae4a58 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs @@ -10,7 +10,7 @@ namespace Microsoft.Dafny; public partial class BoogieGenerator { - private void TrForallStmt(BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + private void TrForallStmt(BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, ForallStmt forallStmt) { this.fuelContext = FuelSetting.ExpandFuelContext(forallStmt.Attributes, forallStmt.Tok, this.fuelContext, this.reporter); @@ -72,7 +72,7 @@ private void TrForallStmt(BoogieStmtListBuilder builder, List locals, void TrForallStmtCall(IToken tok, List boundVars, List bounds, Expression range, ExpressionConverter additionalRange, List forallExpressions, CallStmt s0, - BoogieStmtListBuilder definedness, BoogieStmtListBuilder exporter, List locals, ExpressionTranslator etran) { + BoogieStmtListBuilder definedness, BoogieStmtListBuilder exporter, Variables locals, ExpressionTranslator etran) { Contract.Requires(tok != null); Contract.Requires(boundVars != null); Contract.Requires(bounds != null); @@ -210,7 +210,7 @@ void TrForallStmtCall(IToken tok, List boundVars, List bo } void TrForallAssign(ForallStmt s, SingleAssignStmt s0, - BoogieStmtListBuilder definedness, BoogieStmtListBuilder updater, List locals, ExpressionTranslator etran) { + BoogieStmtListBuilder definedness, BoogieStmtListBuilder updater, Variables locals, ExpressionTranslator etran) { // The statement: // forall (x,y | Range(x,y)) { // (a) E(x,y) . f := G(x,y); @@ -294,7 +294,7 @@ void TrForallAssign(ForallStmt s, SingleAssignStmt s0, MultiSelectExpr e => (e.Array, null), _ => throw new cce.UnreachableException() }; - var desc = new PODesc.Modifiable(description, GetContextModifiesFrames(), lhsObj, lhsField); + var desc = new Modifiable(description, GetContextModifiesFrames(), lhsObj, lhsField); definedness.Add(Assert(lhs.tok, Bpl.Expr.SelectTok(lhs.tok, etran.ModifiesFrame(lhs.tok), obj, F), desc, definedness.Context)); if (s0.Rhs is ExprRhs) { @@ -349,7 +349,7 @@ void TrForallAssign(ForallStmt s, SingleAssignStmt s0, BplOr( BplOr(Bpl.Expr.Neq(obj, objPrime), Bpl.Expr.Neq(F, FPrime)), Bpl.Expr.Eq(rhs, rhsPrime)), - new PODesc.ForallLHSUnique(s.BoundVars, s.Range, lhsComponents, Rhs), definedness.Context)); + new ForallLHSUnique(s.BoundVars, s.Range, lhsComponents, Rhs), definedness.Context)); } definedness.Add(TrAssumeCmd(s.Tok, Bpl.Expr.False)); @@ -457,7 +457,7 @@ IEnumerable TransitiveSubstatements(Statement s) { } void TrForallProof(ForallStmt forallStmt, BoogieStmtListBuilder definedness, BoogieStmtListBuilder exporter, - List locals, ExpressionTranslator etran) { + Variables locals, ExpressionTranslator etran) { // Translate: // forall (x,y | Range(x,y)) // ensures Post(x,y); @@ -511,7 +511,7 @@ void TrForallProof(ForallStmt forallStmt, BoogieStmtListBuilder definedness, Boo foreach (var ens in forallStmt.Ens) { foreach (var split in TrSplitExpr(definedness.Context, ens.E, etran, true, out var splitHappened)) { if (split.IsChecked) { - definedness.Add(Assert(split.Tok, split.E, new PODesc.ForallPostcondition(ens.E), definedness.Context)); + definedness.Add(Assert(split.Tok, split.E, new ForallPostcondition(ens.E), definedness.Context)); } } } diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs index 216fa0fc504..dae8647f040 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs @@ -11,7 +11,7 @@ namespace Microsoft.Dafny; public partial class BoogieGenerator { - private void TrAlternativeLoopStmt(AlternativeLoopStmt stmt, BoogieStmtListBuilder builder, List locals, + private void TrAlternativeLoopStmt(AlternativeLoopStmt stmt, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { AddComment(builder, stmt, "alternative loop statement"); var tru = Expression.CreateBoolLiteral(stmt.Tok, true); @@ -30,7 +30,7 @@ private void TrAlternativeLoopStmt(AlternativeLoopStmt stmt, BoogieStmtListBuild builder, locals, etran); } - private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { Contract.Requires(stmt != null); Contract.Requires(builder != null); Contract.Requires(locals != null); @@ -72,7 +72,7 @@ private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, List Is(x, typ) { @@ -110,7 +110,7 @@ private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, List { indexVar }, dafnyRange, new TypeTestExpr(indexVar.tok, dIndex, indexVar.Type), null); - builder.Add(Assert(tok, cre, new PODesc.ForRangeAssignable(desc, dafnyAssertion), builder.Context)); + builder.Add(Assert(tok, cre, new ForRangeAssignable(desc, dafnyAssertion), builder.Context)); } } @@ -147,7 +147,7 @@ private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + private void TrWhileStmt(WhileStmt stmt, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { Contract.Requires(stmt != null); Contract.Requires(builder != null); Contract.Requires(locals != null); @@ -170,7 +170,7 @@ private void TrWhileStmt(WhileStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, Bpl.Expr freeInvariant = null, bool includeTerminationCheck = true) { Contract.Requires(s != null); Contract.Requires(builder != null); @@ -197,15 +197,15 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, if (s.Mod.Expressions != null) { // check well-formedness and that the modifies is a subset CheckFrameWellFormed(new WFOptions(), s.Mod.Expressions, locals, builder, etran); - var desc = new PODesc.ModifyFrameSubset("loop modifies clause", s.Mod.Expressions, GetContextModifiesFrames()); + var desc = new ModifyFrameSubset("loop modifies clause", s.Mod.Expressions, GetContextModifiesFrames()); CheckFrameSubset(s.Tok, s.Mod.Expressions, null, null, etran, etran.ModifiesFrame(s.Tok), builder, desc, null); DefineFrame(s.Tok, etran.ModifiesFrame(s.Tok), s.Mod.Expressions, builder, locals, loopFrameName); } builder.Add(Bpl.Cmd.SimpleAssign(s.Tok, preLoopHeap, etran.HeapExpr)); - + var daTrackersMonotonicity = new List>(); - var existingLocals = locals.ToList(); + var existingLocals = locals.Values.ToList(); foreach (var local in existingLocals) { if (!DefiniteAssignmentTrackers.TryGetValue(local.Name, out var dat)) { continue; @@ -244,12 +244,12 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, var ss = TrSplitExpr(builder.Context, loopInv.E, etran, false, out var splitHappened); if (!splitHappened) { var wInv = BplImp(w, etran.TrExpr(loopInv.E)); - invariants.Add(Assert(loopInv.E.tok, wInv, new PODesc.LoopInvariant(loopInv.E, errorMessage, successMessage), builder.Context)); + invariants.Add(Assert(loopInv.E.tok, wInv, new LoopInvariant(loopInv.E, errorMessage, successMessage), builder.Context)); } else { foreach (var split in ss) { var wInv = Bpl.Expr.Binary(split.E.tok, BinaryOperator.Opcode.Imp, w, split.E); if (split.IsChecked) { - invariants.Add(Assert(split.Tok, wInv, new PODesc.LoopInvariant(loopInv.E, errorMessage, successMessage), builder.Context)); // TODO: it would be fine to have this use {:subsumption 0} + invariants.Add(Assert(split.Tok, wInv, new LoopInvariant(loopInv.E, errorMessage, successMessage), builder.Context)); // TODO: it would be fine to have this use {:subsumption 0} } else { var cmd = TrAssumeCmd(split.E.tok, wInv); proofDependencies?.AddProofDependencyId(cmd, loopInv.E.tok, new InvariantDependency(loopInv.E)); @@ -277,7 +277,7 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, invariants.Add(TrAssumeCmd(s.Tok, tri.Expr)); } else { Contract.Assert(tri.ErrorMessage != null); // follows from BoilerplateTriple invariant - invariants.Add(Assert(s.Tok, tri.Expr, new PODesc.BoilerplateTriple(tri.ErrorMessage, tri.SuccessMessage, tri.Comment), builder.Context)); + invariants.Add(Assert(s.Tok, tri.Expr, new Microsoft.Dafny.BoilerplateTriple(tri.ErrorMessage, tri.SuccessMessage, tri.Comment), builder.Context)); } } // add a free invariant which says that the heap hasn't changed outside of the modifies clause. @@ -375,7 +375,7 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, Bpl.Expr decrCheck = DecreasesCheck(toks, prevGhostLocals, decrsDafny, initDecrsDafny, decrs, oldBfs, loopBodyBuilder, " at end of loop iteration", false, false); var description = new - PODesc.Terminates(s.InferredDecreases, prevGhostLocals, null, initDecrsDafny, theDecreases, false); + Terminates(s.InferredDecreases, prevGhostLocals, null, initDecrsDafny, theDecreases, false); loopBodyBuilder.Add(Assert(s.Tok, decrCheck, description, builder.Context)); } } diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs index 5fd014021b1..62abcc3c366 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs @@ -13,7 +13,7 @@ namespace Microsoft.Dafny { public partial class BoogieGenerator { private void TrPredicateStmt(PredicateStmt stmt, BoogieStmtListBuilder builder, - List locals, ExpressionTranslator etran) { + Variables locals, ExpressionTranslator etran) { Contract.Requires(stmt != null); Contract.Requires(builder != null); Contract.Requires(locals != null); @@ -30,7 +30,7 @@ private void TrPredicateStmt(PredicateStmt stmt, BoogieStmtListBuilder builder, fuelContext = FuelSetting.PopFuelContext(); } - private void TrAssumeStmt(AssumeStmt assumeStmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + private void TrAssumeStmt(AssumeStmt assumeStmt, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { AddComment(builder, assumeStmt, "assume statement"); stmtContext = StmtType.ASSUME; TrStmt_CheckWellformed(assumeStmt.Expr, builder, locals, etran, false); @@ -42,7 +42,7 @@ private void TrAssumeStmt(AssumeStmt assumeStmt, BoogieStmtListBuilder builder, } } - private void TrExpectStmt(BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, ExpectStmt expectStmt) { + private void TrExpectStmt(BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, ExpectStmt expectStmt) { AddComment(builder, expectStmt, "expect statement"); stmtContext = StmtType.ASSUME; TrStmt_CheckWellformed(expectStmt.Expr, builder, locals, etran, false); @@ -64,7 +64,7 @@ private void TrExpectStmt(BoogieStmtListBuilder builder, List locals, stmtContext = StmtType.NONE; // done with translating expect stmt. } - public void TrAssertStmt(PredicateStmt stmt, BoogieStmtListBuilder builder, List locals, + public void TrAssertStmt(PredicateStmt stmt, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { var stmtBuilder = new BoogieStmtListBuilder(this, options, builder.Context); var defineFuel = DefineFuelConstant(stmt.Tok, stmt.Attributes, stmtBuilder, etran); @@ -165,14 +165,14 @@ private bool TrAssertCondition(PredicateStmt stmt, var splits = TrSplitExpr(proofBuilder.Context, stmt.Expr, etran, true, out var splitHappened); if (!splitHappened) { var tok = enclosingToken == null ? GetToken(stmt.Expr) : new NestedToken(enclosingToken, GetToken(stmt.Expr)); - var desc = new PODesc.AssertStatementDescription(stmt, errorMessage, successMessage); + var desc = new AssertStatementDescription(stmt, errorMessage, successMessage); proofBuilder.Add(Assert(tok, etran.TrExpr(stmt.Expr), desc, stmt.Tok, proofBuilder.Context, etran.TrAttributes(stmt.Attributes, null))); } else { foreach (var split in splits) { if (split.IsChecked) { var tok = enclosingToken == null ? split.E.tok : new NestedToken(enclosingToken, split.Tok); - var desc = new PODesc.AssertStatementDescription(stmt, errorMessage, successMessage); + var desc = new AssertStatementDescription(stmt, errorMessage, successMessage); proofBuilder.Add(AssertAndForget(proofBuilder.Context, ToDafnyToken(flags.ReportRanges, tok), split.E, desc, stmt.Tok, etran.TrAttributes(stmt.Attributes, null))); // attributes go on every split } diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs index 957cb31ec3d..8828fd1cb3b 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs @@ -3,7 +3,6 @@ using System.Diagnostics.Contracts; using System.Linq; using DafnyCore.Verifier; -using DafnyCore.Verifier.Statements; using Microsoft.Boogie; using Bpl = Microsoft.Boogie; using static Microsoft.Dafny.Util; @@ -15,7 +14,7 @@ public partial class BoogieGenerator { public const string FrameVariablePrefix = "$Frame$"; public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, - List locals, ExpressionTranslator etran) { + Variables locals, ExpressionTranslator etran) { stmt.ScopeDepth = builder.Context.ScopeDepth; @@ -130,7 +129,7 @@ public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, // this postcondition was inherited into this module, so just ignore it } else if (split.IsChecked) { var yieldToken = new NestedToken(s.Tok, split.Tok); - var desc = new PODesc.YieldEnsures(fieldSub.Substitute(p.E)); + var desc = new YieldEnsures(fieldSub.Substitute(p.E)); builder.Add(AssertAndForget(builder.Context, yieldToken, split.E, desc, stmt.Tok, null)); } } @@ -297,7 +296,7 @@ public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, } else if (stmt is AlternativeStmt) { AddComment(builder, stmt, "alternative statement"); var s = (AlternativeStmt)stmt; - var elseCase = Assert(s.Tok, Bpl.Expr.False, new PODesc.AlternativeIsComplete(), builder.Context); + var elseCase = Assert(s.Tok, Bpl.Expr.False, new AlternativeIsComplete(), builder.Context); TrAlternatives(s.Alternatives, s.Tok, b => b.Add(elseCase), builder, locals, etran, stmt.IsGhost); } else if (stmt is WhileStmt whileStmt) { @@ -315,7 +314,7 @@ public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, var wfOptions = new WFOptions(); CheckFrameWellFormed(wfOptions, s.Mod.Expressions, locals, builder, etran); // check that the modifies is a subset - var desc = new PODesc.ModifyFrameSubset("modify statement", s.Mod.Expressions, GetContextModifiesFrames()); + var desc = new ModifyFrameSubset("modify statement", s.Mod.Expressions, GetContextModifiesFrames()); CheckFrameSubset(s.Tok, s.Mod.Expressions, null, null, etran, etran.ModifiesFrame(s.Tok), builder, desc, null); // cause the change of the heap according to the given frame var suffix = CurrentIdGenerator.FreshId("modify#"); @@ -407,7 +406,7 @@ void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { } } - private void TrUpdateStmt(BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, AssignStatement statement) { + private void TrUpdateStmt(BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, AssignStatement statement) { // This UpdateStmt can be single-target assignment, a multi-assignment, a call statement, or // an array-range update. Handle the multi-assignment here and handle the others as for .ResolvedStatements. var resolved = statement.ResolvedStatements; @@ -440,7 +439,7 @@ private void TrUpdateStmt(BoogieStmtListBuilder builder, List locals, } } - void TrVarDeclStmt(VarDeclStmt varDeclStmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + void TrVarDeclStmt(VarDeclStmt varDeclStmt, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { var newLocalIds = new List(); int i = 0; @@ -494,7 +493,7 @@ void TrVarDeclStmt(VarDeclStmt varDeclStmt, BoogieStmtListBuilder builder, List< } } - private void TranslateRevealStmt(BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + private void TranslateRevealStmt(BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, HideRevealStmt revealStmt) { AddComment(builder, revealStmt, "hide/reveal statement"); foreach (var la in revealStmt.LabeledAsserts) { @@ -515,7 +514,7 @@ private void TranslateRevealStmt(BoogieStmtListBuilder builder, List l TrStmtList(revealStmt.ResolvedStatements, builder, locals, etran); } - private void TrCalcStmt(CalcStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + private void TrCalcStmt(CalcStmt stmt, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { Contract.Requires(stmt != null); Contract.Requires(builder != null); Contract.Requires(locals != null); @@ -579,7 +578,7 @@ private void TrCalcStmt(CalcStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + private void TrMatchStmt(MatchStmt stmt, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { Contract.Requires(stmt != null); Contract.Requires(builder != null); Contract.Requires(locals != null); @@ -635,20 +634,20 @@ private void TrMatchStmt(MatchStmt stmt, BoogieStmtListBuilder builder, List newLocals = new List(); + var newLocals = new Variables(); Bpl.Expr r = CtorInvocation(stmt.Tok, missingCtor, etran, newLocals, b); - locals.AddRange(newLocals); + locals.AddRange(newLocals.Values); if (newLocals.Count != 0) { List havocIds = new List(); - foreach (Variable local in newLocals) { + foreach (Variable local in newLocals.Values) { havocIds.Add(new Bpl.IdentifierExpr(local.tok, local)); } builder.Add(new Bpl.HavocCmd(stmt.Tok, havocIds)); } String missingStr = stmt.Context.FillHole(new IdCtx(missingCtor)).AbstractAllHoles() .ToString(); - var desc = new PODesc.MatchIsComplete("statement", missingStr); + var desc = new MatchIsComplete("statement", missingStr); b.Add(Assert(stmt.Tok, Bpl.Expr.False, desc, builder.Context)); Bpl.Expr guard = Bpl.Expr.Eq(source, r); @@ -660,13 +659,13 @@ private void TrMatchStmt(MatchStmt stmt, BoogieStmtListBuilder builder, List newLocals = new List(); + var newLocals = new Variables(); Bpl.Expr r = CtorInvocation(mc, stmt.Source.Type, etran, newLocals, b, stmt.IsGhost ? NOALLOC : ISALLOC); - locals.AddRange(newLocals); + locals.AddRange(newLocals.Values); if (newLocals.Count != 0) { List havocIds = new List(); - foreach (Variable local in newLocals) { + foreach (Variable local in newLocals.Values) { havocIds.Add(new Bpl.IdentifierExpr(local.tok, local)); } builder.Add(new Bpl.HavocCmd(mc.tok, havocIds)); @@ -744,7 +743,7 @@ private static SubrangeCheckContext MakeNumericBoundsSubrangeCheckContext(BoundV return CheckContext; } - private void TrIfStmt(IfStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + private void TrIfStmt(IfStmt stmt, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { Contract.Requires(stmt != null); Contract.Requires(builder != null); Contract.Requires(locals != null); @@ -793,7 +792,7 @@ private void TrIfStmt(IfStmt stmt, BoogieStmtListBuilder builder, List void TrAlternatives(List alternatives, IToken elseToken, Action buildElseCase, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, bool isGhost) { + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, bool isGhost) { Contract.Requires(alternatives != null); Contract.Requires(builder != null); Contract.Requires(locals != null); @@ -844,7 +843,7 @@ void TrAlternatives(List alternatives, IToken elseToken, Act void RecordNewObjectsIn_New(IToken tok, IteratorDecl iter, Bpl.Expr initHeap, Bpl.IdentifierExpr currentHeap, - BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { + BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran) { Contract.Requires(tok != null); Contract.Requires(iter != null); Contract.Requires(initHeap != null); @@ -939,7 +938,7 @@ private void CommitAllocatedObject(IToken tok, Bpl.IdentifierExpr nw, Bpl.Cmd ex } - private void IntroduceAndAssignExistentialVars(ExistsExpr exists, BoogieStmtListBuilder builder, BoogieStmtListBuilder builderOutsideIfConstruct, List locals, ExpressionTranslator etran, bool isGhost) { + private void IntroduceAndAssignExistentialVars(ExistsExpr exists, BoogieStmtListBuilder builder, BoogieStmtListBuilder builderOutsideIfConstruct, Variables locals, ExpressionTranslator etran, bool isGhost) { Contract.Requires(exists != null); Contract.Requires(exists.Range == null); Contract.Requires(builder != null); @@ -961,7 +960,7 @@ private void IntroduceAndAssignExistentialVars(ExistsExpr exists, BoogieStmtList builder.Add(TrAssumeCmd(exists.tok, etran.TrExpr(exists.Term))); } - public void TrStmtList(List stmts, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + public void TrStmtList(List stmts, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, RangeToken scopeRange = null, bool processLabels = true) { Contract.Requires(stmts != null); Contract.Requires(builder != null); @@ -1017,7 +1016,7 @@ public void TrStmtList(List stmts, BoogieStmtListBuilder builder, Lis } } - void TrStmt_CheckWellformed(Expression expr, BoogieStmtListBuilder builder, List locals, + void TrStmt_CheckWellformed(Expression expr, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, bool subsumption, bool lValueContext = false, AddResultCommands addResultCommands = null) { Contract.Requires(expr != null); Contract.Requires(builder != null); diff --git a/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs b/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs index b68ffe6c586..8f3c2aadb6f 100644 --- a/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs +++ b/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs @@ -1,19 +1,14 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Boogie; -using Microsoft.Dafny; -using Microsoft.Dafny.ProofObligationDescription; -using Formal = Microsoft.Dafny.Formal; using DafnyIdentifierExpr = Microsoft.Dafny.IdentifierExpr; using BoogieIdentifierExpr = Microsoft.Boogie.IdentifierExpr; -using ProofObligationDescription = Microsoft.Dafny.ProofObligationDescription.ProofObligationDescription; -using Token = Microsoft.Dafny.Token; -namespace DafnyCore.Verifier; +namespace Microsoft.Dafny; public static class OpaqueBlockVerifier { public static void EmitBoogie(BoogieGenerator generator, OpaqueBlock block, BoogieStmtListBuilder builder, - List locals, BoogieGenerator.ExpressionTranslator etran, IMethodCodeContext codeContext) { + Variables locals, BoogieGenerator.ExpressionTranslator etran, IMethodCodeContext codeContext) { var hasModifiesClause = block.Modifies.Expressions.Any(); var blockBuilder = new BoogieStmtListBuilder(generator, builder.Options, builder.Context); @@ -28,7 +23,7 @@ public static void EmitBoogie(BoogieGenerator generator, OpaqueBlock block, Boog var variablesUsedInEnsures = block.Ensures.SelectMany(ae => ae.E.DescendantsAndSelf). OfType().DistinctBy(ie => ie.Var); - var implicitAssignedIdentifiers = + var implicitAssignedIdentifiers = variablesUsedInEnsures.Where(v => assignedVariables.Contains(v.Var)); foreach (var v in implicitAssignedIdentifiers) { var expression = new AttributedExpression(Expression.CreateAssigned(v.Tok, v)); @@ -72,7 +67,7 @@ public static void EmitBoogie(BoogieGenerator generator, OpaqueBlock block, Boog } } - private static BoogieGenerator.ExpressionTranslator GetBodyTranslator(BoogieGenerator generator, OpaqueBlock block, List locals, + private static BoogieGenerator.ExpressionTranslator GetBodyTranslator(BoogieGenerator generator, OpaqueBlock block, Variables locals, BoogieGenerator.ExpressionTranslator etran, bool hasModifiesClause, BoogieStmtListBuilder blockBuilder) { BoogieGenerator.ExpressionTranslator bodyTranslator; if (hasModifiesClause) { diff --git a/Source/DafnyLanguageServer/Handlers/DafnyHoverHandler.cs b/Source/DafnyLanguageServer/Handlers/DafnyHoverHandler.cs index 0fda0ac80bc..959c6a92339 100644 --- a/Source/DafnyLanguageServer/Handlers/DafnyHoverHandler.cs +++ b/Source/DafnyLanguageServer/Handlers/DafnyHoverHandler.cs @@ -11,11 +11,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Boogie; -using Microsoft.Dafny.LanguageServer.Language; -using Microsoft.Dafny.LanguageServer.Util; using Microsoft.Dafny.LanguageServer.Workspace.Notifications; -using OmniSharp.Extensions.JsonRpc.Server; -using EnsuresDescription = Microsoft.Dafny.ProofObligationDescription.EnsuresDescription; namespace Microsoft.Dafny.LanguageServer.Handlers { public class DafnyHoverHandler : HoverHandlerBase { diff --git a/Source/DafnyLanguageServer/Language/ImplicitFailingAssertionCodeActionProvider.cs b/Source/DafnyLanguageServer/Language/ImplicitFailingAssertionCodeActionProvider.cs index 70c9006fe21..1f43f7c8b68 100644 --- a/Source/DafnyLanguageServer/Language/ImplicitFailingAssertionCodeActionProvider.cs +++ b/Source/DafnyLanguageServer/Language/ImplicitFailingAssertionCodeActionProvider.cs @@ -123,7 +123,7 @@ public override IEnumerable GetEdits() { assertTree.Finished && assertTree.Range.Intersects(selection) && assertTree.StatusVerification is GutterVerificationStatus.Error or GutterVerificationStatus.Inconclusive && - assertTree.GetAssertion()?.Description is ProofObligationDescription.ProofObligationDescription description && + assertTree.GetAssertion()?.Description is ProofObligationDescription description && description.GetAssertedExpr(options) is { } assertedExpr) { failingExpressions.Add(assertedExpr); } diff --git a/Source/DafnyPipeline.Test/ImplicitAssertionTest.cs b/Source/DafnyPipeline.Test/ImplicitAssertionTest.cs index 35f46ca15ff..a28848a9e26 100644 --- a/Source/DafnyPipeline.Test/ImplicitAssertionTest.cs +++ b/Source/DafnyPipeline.Test/ImplicitAssertionTest.cs @@ -6,9 +6,6 @@ using Bpl = Microsoft.Boogie; using Xunit; using Microsoft.Dafny; -using Microsoft.Dafny.ProofObligationDescription; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; namespace DafnyPipeline.Test; diff --git a/Source/DafnyPipeline.Test/TranslatorTest.cs b/Source/DafnyPipeline.Test/TranslatorTest.cs index 6c9dab4d4ca..bb55852ab6c 100644 --- a/Source/DafnyPipeline.Test/TranslatorTest.cs +++ b/Source/DafnyPipeline.Test/TranslatorTest.cs @@ -1,13 +1,7 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; +using System.Collections.Generic; using Bpl = Microsoft.Boogie; using Xunit; using Microsoft.Dafny; -using Microsoft.Dafny.ProofObligationDescription; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; namespace DafnyPipeline.Test; diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallBy.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallBy.dfy index 6d83e1da819..3842bfeb506 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallBy.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallBy.dfy @@ -1,4 +1,4 @@ -// RUN: %testDafnyForEachResolver --expect-exit-code=4 "%s" -- --standard-libraries +// RUN: %testDafnyForEachResolver --expect-exit-code=4 "%s" -- --standard-libraries --bprint=/Users/rwillems/SourceCode/dafny/CallBy.bpl import opened Std.Wrappers From 185834a618b26aeae08c4e3e58049a00be317cea Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 23 Sep 2024 11:48:33 +0200 Subject: [PATCH 20/47] CallBy.dfy works now --- .../Verifier/BoogieGenerator.BoogieFactory.cs | 4 +-- .../BoogieGenerator.DefiniteAssignment.cs | 5 ++- .../BoogieGenerator.ExpressionWellformed.cs | 12 +++---- .../Verifier/BoogieGenerator.Iterators.cs | 10 +++--- .../Verifier/BoogieGenerator.Methods.cs | 6 ++-- .../Verifier/BoogieGenerator.Types.cs | 3 +- Source/DafnyCore/Verifier/BoogieGenerator.cs | 31 ++++++++----------- .../Statements/BlockByProofStmtVerifier.cs | 18 ++++++++++- .../BoogieGenerator.TrAssignment.cs | 3 +- .../Statements/BoogieGenerator.TrCall.cs | 8 ++--- .../Statements/BoogieGenerator.TrLoop.cs | 24 ++++++-------- .../BoogieGenerator.TrPredicateStatement.cs | 5 ++- .../Statements/BoogieGenerator.TrStatement.cs | 25 ++++++--------- .../LitTests/LitTest/dafny0/CallBy.dfy | 2 +- 14 files changed, 73 insertions(+), 83 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.BoogieFactory.cs b/Source/DafnyCore/Verifier/BoogieGenerator.BoogieFactory.cs index 4dddb438d12..0773f1ac449 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.BoogieFactory.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.BoogieFactory.cs @@ -829,8 +829,8 @@ static Bpl.Axiom BplAxiom(Bpl.Expr e) { return new Bpl.Axiom(e.tok, e); } - public static Bpl.Expr BplLocalVar(string name, Bpl.Type ty, OrderedDictionary lvars) { - lvars.Add(BplLocalVar(name, ty, out var v)); + public static Bpl.Expr BplLocalVar(string name, Bpl.Type ty, Variables lvars) { + lvars.GetOrAdd(BplLocalVar(name, ty, out var v)); return v; } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs index 9f37791196c..cfdca247fb8 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs @@ -58,7 +58,7 @@ bool NeedsDefiniteAssignmentTracker(bool isGhost, Type type, bool isField) { tracker = new Bpl.LocalVariable(p.Tok, new Bpl.TypedIdent(p.Tok, DefassPrefix + p.UniqueName, Bpl.Type.Bool)); } - localVariables.Add(tracker); + tracker = localVariables.GetOrAdd(tracker); var ie = new Bpl.IdentifierExpr(p.Tok, tracker); DefiniteAssignmentTrackers.Add(p.UniqueName, ie); return ie; @@ -84,8 +84,7 @@ void AddDefiniteAssignmentTrackerSurrogate(Field field, TopLevelDeclWithMembers } var nm = SurrogateName(field); - var tracker = new Bpl.LocalVariable(field.tok, new Bpl.TypedIdent(field.tok, DefassPrefix + nm, Bpl.Type.Bool)); - localVariables.Add(tracker); + var tracker = localVariables.GetOrAdd(new Bpl.LocalVariable(field.tok, new Bpl.TypedIdent(field.tok, DefassPrefix + nm, Bpl.Type.Bool))); var ie = new Bpl.IdentifierExpr(field.tok, tracker); DefiniteAssignmentTrackers.Add(nm, ie); } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index f3e88b7d975..d7c3aa02760 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -701,7 +701,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, }; ie.Type = ie.Var.Type; // resolve ie here substMap.Add(p, ie); - locals.Add(new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator), TrType(local.Type)))); + locals.GetOrAdd(new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator), TrType(local.Type)))); Bpl.IdentifierExpr lhs = (Bpl.IdentifierExpr)etran.TrExpr(ie); // TODO: is this cast always justified? Expression ee = e.Args[i]; directSubstMap.Add(p, ee); @@ -1173,7 +1173,7 @@ void CheckOperand(Expression operand) { var comprehensionEtran = etran; if (lam != null) { // Havoc heap - locals.Add(BplLocalVar(CurrentIdGenerator.FreshId((etran.UsesOldHeap ? "$Heap_at_" : "") + "$lambdaHeap#"), predef.HeapType, out var lambdaHeap)); + locals.GetOrAdd(BplLocalVar(CurrentIdGenerator.FreshId((etran.UsesOldHeap ? "$Heap_at_" : "") + "$lambdaHeap#"), predef.HeapType, out var lambdaHeap)); comprehensionEtran = new ExpressionTranslator(comprehensionEtran, lambdaHeap); nextBuilder.Add(new HavocCmd(e.tok, Singleton((Bpl.IdentifierExpr)comprehensionEtran.HeapExpr))); nextBuilder.Add(new AssumeCmd(e.tok, FunctionCall(e.tok, BuiltinFunction.IsGoodHeap, null, comprehensionEtran.HeapExpr))); @@ -1226,7 +1226,7 @@ void CheckOperand(Expression operand) { if (lam != null) { var resultName = CurrentIdGenerator.FreshId("lambdaResult#"); var resultVar = new Bpl.LocalVariable(body.tok, new Bpl.TypedIdent(body.tok, resultName, TrType(body.Type))); - locals.Add(resultVar); + locals.GetOrAdd(resultVar); resultIe = new Bpl.IdentifierExpr(body.tok, resultVar); rangeType = lam.Type.AsArrowType.Result; } @@ -1469,8 +1469,7 @@ private void CheckWellformedStmtExpr(StmtExpr stmtExpr, WFOptions wfOptions, Var void BuildWithHeapAs(IToken token, Bpl.Expr temporaryHeap, string heapVarSuffix, Variables locals, BoogieStmtListBuilder builder, System.Action build) { var suffix = CurrentIdGenerator.FreshId(heapVarSuffix); - var tmpHeapVar = new Bpl.LocalVariable(token, new Bpl.TypedIdent(token, "Heap$" + suffix, predef.HeapType)); - locals.Add(tmpHeapVar); + var tmpHeapVar = locals.GetOrAdd(new Bpl.LocalVariable(token, new Bpl.TypedIdent(token, "Heap$" + suffix, predef.HeapType))); var tmpHeap = new Bpl.IdentifierExpr(token, tmpHeapVar); var generalEtran = new ExpressionTranslator(this, predef, token, null); var theHeap = generalEtran.HeapCastToIdentifierExpr; @@ -1540,8 +1539,7 @@ void CheckWellformedLetExprWithResult(LetExpr e, WFOptions wfOptions, Variables var pat = e.LHSs[i]; var rhs = e.RHSs[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 r = locals.GetOrAdd(new Bpl.LocalVariable(pat.tok, new Bpl.TypedIdent(pat.tok, nm, TrType(pat.Expr.Type)))); var rIe = new Bpl.IdentifierExpr(rhs.tok, r); void CheckPostconditionForRhs(BoogieStmtListBuilder innerBuilder, Expression body) { diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs index 1598d48c30d..70fc46b6243 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs @@ -202,8 +202,7 @@ void AddIteratorWellformednessCheck(IteratorDecl iter, Procedure proc) { } // save the heap (representing the state where yield-requires holds): $_OldIterHeap := Heap; - var oldIterHeap = new Bpl.LocalVariable(iter.tok, new Bpl.TypedIdent(iter.tok, "$_OldIterHeap", predef.HeapType)); - localVariables.Add(oldIterHeap); + var oldIterHeap = localVariables.GetOrAdd(new Bpl.LocalVariable(iter.tok, new Bpl.TypedIdent(iter.tok, "$_OldIterHeap", predef.HeapType))); builder.Add(Bpl.Cmd.SimpleAssign(iter.tok, new Bpl.IdentifierExpr(iter.tok, oldIterHeap), etran.HeapExpr)); // simulate a modifies this, this._modifies, this._new; var nw = new MemberSelectExpr(iter.tok, th, iter.Member_New); @@ -303,17 +302,16 @@ void AddIteratorImpl(IteratorDecl iter, Bpl.Procedure proc) { builder.Add(TrAssumeCmdWithDependencies(etran, p.E.tok, p.E, "iterator constructor ensures clause")); } // add the _yieldCount variable, and assume its initial value to be 0 - yieldCountVariable = new Bpl.LocalVariable(iter.tok, - new Bpl.TypedIdent(iter.tok, iter.YieldCountVariable.AssignUniqueName(currentDeclaration.IdGenerator), TrType(iter.YieldCountVariable.Type))); + yieldCountVariable = (Bpl.LocalVariable)localVariables.GetOrAdd(new Bpl.LocalVariable(iter.tok, + new Bpl.TypedIdent(iter.tok, iter.YieldCountVariable.AssignUniqueName(currentDeclaration.IdGenerator), TrType(iter.YieldCountVariable.Type)))); yieldCountVariable.TypedIdent.WhereExpr = YieldCountAssumption(iter, etran); // by doing this after setting "yieldCountVariable", the variable can be used by YieldCountAssumption - localVariables.Add(yieldCountVariable); builder.Add(TrAssumeCmd(iter.tok, Bpl.Expr.Eq(new Bpl.IdentifierExpr(iter.tok, yieldCountVariable), Bpl.Expr.Literal(0)))); // add a variable $_OldIterHeap var oih = new Bpl.IdentifierExpr(iter.tok, "$_OldIterHeap", predef.HeapType); Bpl.Expr wh = BplAnd( FunctionCall(iter.tok, BuiltinFunction.IsGoodHeap, null, oih), HeapSucc(oih, etran.HeapExpr)); - localVariables.Add(new Bpl.LocalVariable(iter.tok, new Bpl.TypedIdent(iter.tok, "$_OldIterHeap", predef.HeapType, wh))); + localVariables.GetOrAdd(new Bpl.LocalVariable(iter.tok, new Bpl.TypedIdent(iter.tok, "$_OldIterHeap", predef.HeapType, wh))); // do an initial YieldHavoc YieldHavoc(iter.tok, iter, builder, etran); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 83745d558de..9b432369d64 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -1156,8 +1156,7 @@ private void AddOverrideCheckTypeArgumentInstantiations(MemberDecl member, Boogi } var typeMap = GetTypeArgumentSubstitutionMap(overriddenMember, member); foreach (var tp in Util.Concat(overriddenMember.EnclosingClass.TypeArgs, overriddenTypeParameters)) { - var local = BplLocalVar(NameTypeParam(tp), predef.Ty, out var lhs); - localVariables.Add(local); + localVariables.GetOrAdd(BplLocalVar(NameTypeParam(tp), predef.Ty, out var lhs)); var rhs = TypeToTy(typeMap[tp]); builder.Add(new Boogie.AssumeCmd(tp.tok, Boogie.Expr.Eq(lhs, rhs))); } @@ -1184,8 +1183,7 @@ private void AddFunctionOverrideSubsetChk(Function func, BoogieStmtListBuilder b Bpl.IdentifierExpr traitFrame = etran.ReadsFrame(func.OverriddenFunction.tok); // this is a throw-away expression, used only to extract the type and name of the $_ReadsFrame variable traitFrame.Name = func.EnclosingClass.Name + "_" + traitFrame.Name; Contract.Assert(traitFrame.Type != null); // follows from the postcondition of ReadsFrame - Bpl.LocalVariable frame = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, null ?? traitFrame.Name, traitFrame.Type)); - localVariables.Add(frame); + var frame = localVariables.GetOrAdd(new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, null ?? traitFrame.Name, traitFrame.Type))); // $_ReadsFrame := (lambda $o: ref, $f: Field :: $o != null && $Heap[$o,alloc] ==> ($o,$f) in Modifies/Reads-Clause); Bpl.BoundVariable oVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$o", predef.RefType)); Bpl.IdentifierExpr o = new Bpl.IdentifierExpr(tok, oVar); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs index a4f13182086..cc8fa9b19bd 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs @@ -1211,8 +1211,7 @@ void CheckResultToBeInType(IToken tok, Expression expr, Type toType, Variables l void PutSourceIntoLocal() { if (o == null) { var oType = fromType.IsCharType ? Type.Int : fromType; - var oVar = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, CurrentIdGenerator.FreshId("newtype$check#"), TrType(oType))); - locals.Add(oVar); + var oVar = locals.GetOrAdd(new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, CurrentIdGenerator.FreshId("newtype$check#"), TrType(oType)))); o = new Bpl.IdentifierExpr(tok, oVar); var rhs = etran.TrExpr(expr); if (fromType.IsCharType) { diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index e0c65c03cd5..b386bc0842f 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -1405,7 +1405,7 @@ private Implementation AddImplementationWithAttributes(IToken tok, Procedure pro List outParams, Variables localVariables, StmtList stmts, QKeyValue kv) { Bpl.Implementation impl = new Bpl.Implementation(tok, proc.Name, new List(), inParams, outParams, - localVariables.Values.ToList(), stmts, kv); + localVariables.Values.ToList(), stmts, kv); AddVerboseNameAttribute(impl, proc.VerboseName); if (options.IsUsingZ3()) { if (DisableNonLinearArithmetic) { @@ -1794,8 +1794,7 @@ Bpl.IdentifierExpr GetTmpVar_IdExpr(Bpl.IToken tok, string name, Bpl.Type ty, Va Contract.Assume(ie.Type.Equals(ty)); } else { // the "tok" and "ty" of the first request for this variable is the one we use - var v = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, name, ty)); // important for the "$nw" client: no where clause (see GetNewVar_IdExpr) - locals.Add(v); + var v = locals.GetOrAdd(new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, name, ty))); // important for the "$nw" client: no where clause (see GetNewVar_IdExpr) ie = new Bpl.IdentifierExpr(tok, v); _tmpIEs.Add(name, ie); } @@ -2065,8 +2064,7 @@ public void DefineFrame(IToken/*!*/ tok, Boogie.IdentifierExpr frameIdentifier, etran = new ExpressionTranslator(this, predef, tok, null); } // Declare a local variable $_Frame: [ref, Field]bool - Bpl.LocalVariable frame = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, name ?? frameIdentifier.Name, frameIdentifier.Type)); - localVariables.Add(frame); + var frame = localVariables.GetOrAdd(new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, name ?? frameIdentifier.Name, frameIdentifier.Type))); // $_Frame := (lambda $o: ref, $f: Field :: $o != null && $Heap[$o,alloc] ==> ($o,$f) in Modifies/Reads-Clause); // $_Frame := (lambda $o: ref, $f: Field :: $o != null ==> ($o,$f) in Modifies/Reads-Clause); Bpl.BoundVariable oVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$o", predef.RefType)); @@ -2319,10 +2317,9 @@ Bpl.Expr CtorInvocation(MatchCase mc, Type sourceType, ExpressionTranslator etra for (int i = 0; i < mc.Arguments.Count; i++) { BoundVar p = mc.Arguments[i]; var nm = p.AssignUniqueName(currentDeclaration.IdGenerator); - Bpl.Variable local = declareLocals ? null : locals.GetValueOrDefault(nm); // find previous local + var local = declareLocals ? null : locals.GetValueOrDefault(nm); // find previous local if (local == null) { - local = new Bpl.LocalVariable(p.tok, new Bpl.TypedIdent(p.tok, nm, TrType(p.Type))); - locals.Add(local); + local = locals.GetOrAdd(new Bpl.LocalVariable(p.tok, new Bpl.TypedIdent(p.tok, nm, TrType(p.Type)))); } else { Contract.Assert(Bpl.Type.Equals(local.TypedIdent.Type, TrType(p.Type))); } @@ -2354,8 +2351,7 @@ Bpl.Expr CtorInvocation(IToken tok, DatatypeCtor ctor, ExpressionTranslator etra foreach (Formal arg in ctor.Formals) { Contract.Assert(arg != null); var nm = varNameGen.FreshId(string.Format("#{0}#", args.Count)); - Bpl.Variable bv = new Bpl.LocalVariable(arg.tok, new Bpl.TypedIdent(arg.tok, nm, TrType(arg.Type))); - locals.Add(bv); + var bv = locals.GetOrAdd(new Bpl.LocalVariable(arg.tok, new Bpl.TypedIdent(arg.tok, nm, TrType(arg.Type)))); args.Add(new Bpl.IdentifierExpr(arg.tok, bv)); } @@ -2597,8 +2593,7 @@ void EachReferenceInFrameExpression(Expression e, Variables locals, BoogieStmtLi type = sType.Arg; // var $x var name = CurrentIdGenerator.FreshId("$unchanged#x"); - var xVar = new Bpl.LocalVariable(e.tok, new Bpl.TypedIdent(e.tok, name, isSetType || isMultisetType ? TrType(type) : Bpl.Type.Int)); - locals.Add(xVar); + var xVar = locals.GetOrAdd(new Bpl.LocalVariable(e.tok, new Bpl.TypedIdent(e.tok, name, isSetType || isMultisetType ? TrType(type) : Bpl.Type.Int))); var x = new Bpl.IdentifierExpr(e.tok, xVar); // havoc $x builder.Add(new Bpl.HavocCmd(e.tok, new List() { x })); @@ -2889,7 +2884,7 @@ private void GenerateMethodParametersChoose(IToken tok, IMethodCodeContext m, Me if (thVar.InComing) { inParams.Add(thVar); } else { - outParams.Add(thVar); + outParams.GetOrAdd(thVar); } } if (includeInParams) { @@ -2916,13 +2911,13 @@ private void GenerateMethodParametersChoose(IToken tok, IMethodCodeContext m, Me wh = BplImp(tracker, wh); } } - outParams.Add(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.AssignUniqueName(currentDeclaration.IdGenerator), varType, wh), false)); + outParams.GetOrAdd(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.AssignUniqueName(currentDeclaration.IdGenerator), varType, wh), false)); } // tear down definite-assignment trackers m.Outs.ForEach(RemoveDefiniteAssignmentTracker); if (kind == MethodTranslationKind.Implementation) { - outParams.Add(new Bpl.Formal(tok, new Bpl.TypedIdent(tok, "$_reverifyPost", Bpl.Type.Bool), false)); + outParams.GetOrAdd(new Bpl.Formal(tok, new Bpl.TypedIdent(tok, "$_reverifyPost", Bpl.Type.Bool), false)); } } } @@ -3548,7 +3543,7 @@ Dictionary SetupBoundVarsAsLocals(List boundVar ie.Var = local; ie.Type = ie.Var.Type; // resolve ie here substMap.Add(bv, ie); Bpl.LocalVariable bvar = new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator), TrType(local.Type))); - locals.Add(bvar); + locals.GetOrAdd(bvar); var bIe = new Bpl.IdentifierExpr(bvar.tok, bvar); builder.Add(new Bpl.HavocCmd(bv.tok, new List { bIe })); Bpl.Expr wh = GetWhereClause(bv.tok, bIe, local.Type, etran, IsAllocType.ISALLOC); @@ -3594,7 +3589,7 @@ Bpl.Expr SetupVariableAsLocal(IVariable v, Dictionary sub substMap.Add(v, ie); var bvar = new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator), TrType(local.Type))); - locals.Add(bvar); + locals.GetOrAdd(bvar); var bIe = new Bpl.IdentifierExpr(bvar.tok, bvar); builder.Add(new Bpl.HavocCmd(v.Tok, new List { bIe })); var wh = GetWhereClause(v.Tok, bIe, local.Type, etran, ISALLOC); @@ -3612,7 +3607,7 @@ Bpl.Expr SetupVariableAsLocal(IVariable v, Dictionary sub foreach (Expression e in decreases) { Contract.Assert(e != null); Bpl.LocalVariable bfVar = new Bpl.LocalVariable(e.tok, new Bpl.TypedIdent(e.tok, idGen.FreshId(varPrefix), TrType(cce.NonNull(e.Type)))); - locals.Add(bfVar); + locals.GetOrAdd(bfVar); Bpl.IdentifierExpr bf = new Bpl.IdentifierExpr(e.tok, bfVar); oldBfs.Add(bf); // record value of each decreases expression at beginning of the loop iteration diff --git a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs index 50c4e6482e4..58b65b7c079 100644 --- a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs +++ b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs @@ -47,12 +47,28 @@ public void AddRange(IEnumerable values) { } } + public TValue GetOrAdd(TValue value) { + var key = getKey(value); + return GetOrCreate(key, () => value); + } + + public TValue GetOrCreate(TKey key, Func createValue) { + if (keyToValue.TryGetValue(key, out var result)) { + return result; + } + + result = createValue(); + keyToValue[key] = result; + keyOrder.Add(key); + return result; + } + public void Add(TValue value) { var key = getKey(value); keyOrder.Add(key); keyToValue[key] = value; } - + public TValue GetValueOrDefault(TKey key) { return keyToValue.GetValueOrDefault(key); } diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs index c6dd53d7ae7..0d78e61af61 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs @@ -415,8 +415,7 @@ Bpl.Expr TrAssignmentRhs(IToken tok, Bpl.IdentifierExpr bGivenLhs, IVariable lhs // "where" wouldn't provide additional information over the assigned value. wh = null; } - var v = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, nm, ty, wh)); - locals.Add(v); + var v = locals.GetOrCreate(nm, () => new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, nm, ty, wh))); bLhs = new Bpl.IdentifierExpr(tok, v); } diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs index c386cd8b1f9..5110548c1f7 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrCall.cs @@ -40,8 +40,7 @@ void TrCallStmt(CallStmt s, BoogieStmtListBuilder builder, Variables locals, Exp string nm = CurrentIdGenerator.FreshId("$rhs##"); var formalOutType = s.Method.Outs[i].Type.Subst(tySubst); var ty = TrType(formalOutType); - Bpl.LocalVariable var = new Bpl.LocalVariable(lhs.tok, new Bpl.TypedIdent(lhs.tok, nm, ty)); - locals.Add(var); + var var = locals.GetOrCreate(nm, () => new Bpl.LocalVariable(lhs.tok, new Bpl.TypedIdent(lhs.tok, nm, ty))); bLhss[i] = new Bpl.IdentifierExpr(lhs.tok, var.Name, ty); } } @@ -185,10 +184,11 @@ void ProcessCallStmt(CallStmt cs, Dictionary tySubst, Bpl.E var formal = callee.Ins[i]; var local = new LocalVariable(formal.RangeToken, formal.Name + "#", formal.Type.Subst(tySubst), formal.IsGhost); local.type = local.SyntacticType; // resolve local here - var ie = new IdentifierExpr(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator)); + var localName = local.AssignUniqueName(currentDeclaration.IdGenerator); + var ie = new IdentifierExpr(local.Tok, localName); ie.Var = local; ie.Type = ie.Var.Type; // resolve ie here substMap.Add(formal, ie); - locals.Add(new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator), TrType(local.Type)))); + locals.GetOrCreate(localName, () => new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, localName, TrType(local.Type)))); var param = (Bpl.IdentifierExpr)etran.TrExpr(ie); // TODO: is this cast always justified? Bpl.Expr bActual; diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs index dae8647f040..18d6a2aaa06 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs @@ -41,8 +41,7 @@ private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, Variable var indexVar = stmt.LoopIndex; var indexVarName = indexVar.AssignUniqueName(currentDeclaration.IdGenerator); var dIndex = new IdentifierExpr(indexVar.tok, indexVar); - var bIndexVar = new Bpl.LocalVariable(indexVar.tok, new Bpl.TypedIdent(indexVar.Tok, indexVarName, TrType(indexVar.Type))); - locals.Add(bIndexVar); + locals.GetOrCreate(indexVarName, () => new Bpl.LocalVariable(indexVar.tok, new Bpl.TypedIdent(indexVar.Tok, indexVarName, TrType(indexVar.Type)))); var bIndex = new Bpl.IdentifierExpr(indexVar.tok, indexVarName); var lo = stmt.GoingUp ? stmt.Start : stmt.End; @@ -53,8 +52,7 @@ private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, Variable Bpl.IdentifierExpr bHi = null; if (lo != null) { var name = indexVarName + "#lo"; - var bLoVar = new Bpl.LocalVariable(lo.tok, new Bpl.TypedIdent(lo.tok, name, Bpl.Type.Int)); - locals.Add(bLoVar); + locals.GetOrCreate(name, () => new Bpl.LocalVariable(lo.tok, new Bpl.TypedIdent(lo.tok, name, Bpl.Type.Int))); bLo = new Bpl.IdentifierExpr(lo.tok, name); CheckWellformed(lo, new WFOptions(null, false), locals, builder, etran); builder.Add(Bpl.Cmd.SimpleAssign(lo.tok, bLo, etran.TrExpr(lo))); @@ -62,8 +60,7 @@ private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, Variable } if (hi != null) { var name = indexVarName + "#hi"; - var bHiVar = new Bpl.LocalVariable(hi.tok, new Bpl.TypedIdent(hi.tok, name, Bpl.Type.Int)); - locals.Add(bHiVar); + locals.GetOrCreate(name, () => new Bpl.LocalVariable(hi.tok, new Bpl.TypedIdent(hi.tok, name, Bpl.Type.Int))); bHi = new Bpl.IdentifierExpr(hi.tok, name); CheckWellformed(hi, new WFOptions(null, false), locals, builder, etran); builder.Add(Bpl.Cmd.SimpleAssign(hi.tok, bHi, etran.TrExpr(hi))); @@ -83,7 +80,6 @@ private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, Variable // assert Is(x, typ); var tok = indexVar.tok; var name = indexVarName + "#x"; - var xVar = new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, name, Bpl.Type.Int)); var x = new Bpl.IdentifierExpr(tok, name); var sourceBoundVar = new BoundVar(Token.NoToken, "x", Type.Int); @@ -94,7 +90,7 @@ private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, Variable checkContext, out var desc); if (cre != null) { - locals.Add(xVar); + locals.GetOrCreate(name, () => new Bpl.LocalVariable(tok, new Bpl.TypedIdent(tok, name, Bpl.Type.Int))); builder.Add(new Bpl.HavocCmd(tok, new List() { x })); builder.Add(new Bpl.AssumeCmd(tok, ForLoopBounds(x, bLo, bHi))); List dafnyRangeBounds = new(); @@ -183,8 +179,8 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, var theDecreases = s.Decreases.Expressions; - Bpl.LocalVariable preLoopHeapVar = new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$PreLoopHeap$" + suffix, predef.HeapType)); - locals.Add(preLoopHeapVar); + var preloopheap = "$PreLoopHeap$" + suffix; + var preLoopHeapVar = locals.GetOrCreate(preloopheap, () => new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, preloopheap, predef.HeapType))); Bpl.IdentifierExpr preLoopHeap = new Bpl.IdentifierExpr(s.Tok, preLoopHeapVar); ExpressionTranslator etranPreLoop = new ExpressionTranslator(this, predef, preLoopHeap, etran.scope); ExpressionTranslator updatedFrameEtran; @@ -210,8 +206,9 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, if (!DefiniteAssignmentTrackers.TryGetValue(local.Name, out var dat)) { continue; } - var preLoopDat = new Bpl.LocalVariable(dat.tok, new Bpl.TypedIdent(dat.tok, "preLoop$" + suffix + "$" + dat.Name, dat.Type)); - locals.Add(preLoopDat); + + var name = "preLoop$" + suffix + "$" + dat.Name; + var preLoopDat = locals.GetOrCreate(name, () => new Bpl.LocalVariable(dat.tok, new Bpl.TypedIdent(dat.tok, name, dat.Type))); var ie = new Bpl.IdentifierExpr(s.Tok, preLoopDat); daTrackersMonotonicity.Add(new Tuple(ie, dat)); builder.Add(Cmd.SimpleAssign(s.Tok, ie, dat)); @@ -224,9 +221,8 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, // The variable w is used to coordinate the definedness checking of the loop invariant. // It is also used for body-less loops to turn off invariant checking after the generated body. - Bpl.LocalVariable wVar = new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$w$" + suffix, Bpl.Type.Bool)); + var wVar = locals.GetOrAdd(new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$w$" + suffix, Bpl.Type.Bool))); Bpl.IdentifierExpr w = new Bpl.IdentifierExpr(s.Tok, wVar); - locals.Add(wVar); // havoc w; builder.Add(new Bpl.HavocCmd(s.Tok, new List { w })); diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs index 62abcc3c366..73077d52d0a 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs @@ -104,8 +104,7 @@ public void TrAssertStmt(PredicateStmt stmt, BoogieStmtListBuilder builder, Vari if (assertStmt is { Label: not null }) { // make copies of the variables used in the assertion var name = "$Heap_at_" + assertStmt.Label.AssignUniqueId(CurrentIdGenerator); - var heapAt = new Bpl.LocalVariable(stmt.Tok, new Bpl.TypedIdent(stmt.Tok, name, predef.HeapType)); - locals.Add(heapAt); + var heapAt = locals.GetOrAdd(new Bpl.LocalVariable(stmt.Tok, new Bpl.TypedIdent(stmt.Tok, name, predef.HeapType))); var heapReference = new Bpl.IdentifierExpr(stmt.Tok, heapAt); b.Add(Bpl.Cmd.SimpleAssign(stmt.Tok, heapReference, etran.HeapExpr)); var substMap = new Dictionary(); @@ -119,7 +118,7 @@ public void TrAssertStmt(PredicateStmt stmt, BoogieStmtListBuilder builder, Vari ie.Var = vcopy; ie.Type = ie.Var.Type; // resolve ie here substMap.Add(v, ie); - locals.Add(new Bpl.LocalVariable(vcopy.Tok, + locals.GetOrAdd(new Bpl.LocalVariable(vcopy.Tok, new Bpl.TypedIdent(vcopy.Tok, vcopy.AssignUniqueName(currentDeclaration.IdGenerator), TrType(vcopy.Type)))); b.Add(Bpl.Cmd.SimpleAssign(stmt.Tok, TrVar(stmt.Tok, vcopy), TrVar(stmt.Tok, v))); diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs index 8828fd1cb3b..f42545d6455 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs @@ -319,8 +319,7 @@ public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, // cause the change of the heap according to the given frame var suffix = CurrentIdGenerator.FreshId("modify#"); string modifyFrameName = FrameVariablePrefix + suffix; - var preModifyHeapVar = new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$PreModifyHeap$" + suffix, predef.HeapType)); - locals.Add(preModifyHeapVar); + var preModifyHeapVar = locals.GetOrAdd(new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$PreModifyHeap$" + suffix, predef.HeapType))); DefineFrame(s.Tok, etran.ModifiesFrame(s.Tok), s.Mod.Expressions, builder, locals, modifyFrameName); if (s.Body == null) { var preModifyHeap = new Bpl.IdentifierExpr(s.Tok, preModifyHeapVar); @@ -356,10 +355,9 @@ public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, TrVarDeclStmt(s, builder, locals, etran); } else if (stmt is VarDeclPattern varDeclPattern) { foreach (var dafnyLocal in varDeclPattern.LocalVars) { - var boogieLocal = new Bpl.LocalVariable(dafnyLocal.Tok, + var boogieLocal = locals.GetOrAdd(new Bpl.LocalVariable(dafnyLocal.Tok, new Bpl.TypedIdent(dafnyLocal.Tok, dafnyLocal.AssignUniqueName(currentDeclaration.IdGenerator), - TrType(dafnyLocal.Type))); - locals.Add(boogieLocal); + TrType(dafnyLocal.Type)))); var variableReference = new Bpl.IdentifierExpr(boogieLocal.tok, boogieLocal); builder.Add(new Bpl.HavocCmd(dafnyLocal.Tok, new List { @@ -376,8 +374,7 @@ public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, var pat = varDeclPattern.LHS; var rhs = varDeclPattern.RHS; var nm = varNameGen.FreshId("#0#"); - var boogieTupleLocal = new Bpl.LocalVariable(pat.tok, new TypedIdent(pat.tok, nm, TrType(rhs.Type))); - locals.Add(boogieTupleLocal); + var boogieTupleLocal = locals.GetOrAdd(new Bpl.LocalVariable(pat.tok, new TypedIdent(pat.tok, nm, TrType(rhs.Type)))); var boogieTupleReference = new Bpl.IdentifierExpr(rhs.tok, boogieTupleLocal); void AddResultCommands(BoogieStmtListBuilder returnBuilder, Expression result) { @@ -471,10 +468,9 @@ void TrVarDeclStmt(VarDeclStmt varDeclStmt, BoogieStmtListBuilder builder, Varia } } // create the variable itself (now that "wh" may mention the definite-assignment tracker) - Bpl.LocalVariable var = new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator), varType, wh)); + var var = locals.GetOrAdd(new Bpl.LocalVariable(local.Tok, new Bpl.TypedIdent(local.Tok, local.AssignUniqueName(currentDeclaration.IdGenerator), varType, wh))); var.Attributes = etran.TrAttributes(local.Attributes, null); newLocalIds.Add(new Bpl.IdentifierExpr(local.Tok, var)); - locals.Add(var); i++; } if (varDeclStmt.Assign == null) { @@ -852,8 +848,7 @@ void RecordNewObjectsIn_New(IToken tok, IteratorDecl iter, Bpl.Expr initHeap, Bp Contract.Requires(locals != null); Contract.Requires(etran != null); // Add all newly allocated objects to the set this._new - var updatedSet = new Bpl.LocalVariable(iter.tok, new Bpl.TypedIdent(iter.tok, CurrentIdGenerator.FreshId("$iter_newUpdate"), predef.SetType)); - locals.Add(updatedSet); + var updatedSet = locals.GetOrAdd(new Bpl.LocalVariable(iter.tok, new Bpl.TypedIdent(iter.tok, CurrentIdGenerator.FreshId("$iter_newUpdate"), predef.SetType))); var updatedSetIE = new Bpl.IdentifierExpr(iter.tok, updatedSet); // call $iter_newUpdate := $IterCollectNewObjects(initHeap, $Heap, this, _new); var th = new Bpl.IdentifierExpr(iter.tok, etran.This, predef.RefType); @@ -952,8 +947,7 @@ private void IntroduceAndAssignExistentialVars(ExistsExpr exists, BoogieStmtList Bpl.Expr wh = GetWhereClause(bv.Tok, new Bpl.IdentifierExpr(bv.Tok, bv.AssignUniqueName(currentDeclaration.IdGenerator), varType), bv.Type, etran, isAllocContext.Var(isGhost, bv)); - Bpl.Variable local = new Bpl.LocalVariable(bv.Tok, new Bpl.TypedIdent(bv.Tok, bv.AssignUniqueName(currentDeclaration.IdGenerator), varType, wh)); - locals.Add(local); + Bpl.Variable local = locals.GetOrAdd(new Bpl.LocalVariable(bv.Tok, new Bpl.TypedIdent(bv.Tok, bv.AssignUniqueName(currentDeclaration.IdGenerator), varType, wh))); iesForHavoc.Add(new Bpl.IdentifierExpr(local.tok, local)); } builderOutsideIfConstruct.Add(new Bpl.HavocCmd(exists.tok, iesForHavoc)); @@ -998,9 +992,8 @@ public void TrStmtList(List stmts, BoogieStmtListBuilder builder, Var var indexBuilder = innerBuilder.WithContext(indexContext); if (processLabels) { for (var l = ss.Labels; l != null; l = l.Next) { - var heapAt = new Bpl.LocalVariable(ss.Tok, - new Bpl.TypedIdent(ss.Tok, "$Heap_at_" + l.Data.AssignUniqueId(CurrentIdGenerator), predef.HeapType)); - locals.Add(heapAt); + var heapAt = locals.GetOrAdd(new Bpl.LocalVariable(ss.Tok, + new Bpl.TypedIdent(ss.Tok, "$Heap_at_" + l.Data.AssignUniqueId(CurrentIdGenerator), predef.HeapType))); builder.Add(Bpl.Cmd.SimpleAssign(ss.Tok, new Bpl.IdentifierExpr(ss.Tok, heapAt), etran.HeapExpr)); } } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallBy.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallBy.dfy index 3842bfeb506..6d83e1da819 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallBy.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallBy.dfy @@ -1,4 +1,4 @@ -// RUN: %testDafnyForEachResolver --expect-exit-code=4 "%s" -- --standard-libraries --bprint=/Users/rwillems/SourceCode/dafny/CallBy.bpl +// RUN: %testDafnyForEachResolver --expect-exit-code=4 "%s" -- --standard-libraries import opened Std.Wrappers From 6f2dbf3ebb03533cab55cb42edb6bf1b9424b9d9 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 23 Sep 2024 12:21:11 +0200 Subject: [PATCH 21/47] CallByHide.dfy passes --- .../DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs | 2 +- .../TestFiles/LitTests/LitTest/dafny0/CallByHide.dfy | 2 +- .../TestFiles/LitTests/LitTest/dafny0/CallByHide.dfy.expect | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs index 58b65b7c079..c32672d0f41 100644 --- a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs +++ b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs @@ -68,7 +68,7 @@ public void Add(TValue value) { keyOrder.Add(key); keyToValue[key] = value; } - + public TValue GetValueOrDefault(TKey key) { return keyToValue.GetValueOrDefault(key); } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByHide.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByHide.dfy index 31e5fe5fb51..68a4d9142c4 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByHide.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByHide.dfy @@ -1,4 +1,4 @@ -// RUN: ! %verify --type-system-refresh %s > %t +// RUN: ! %verify --type-system-refresh --isolate-assertions %s > %t // RUN: %diff "%s.expect" "%t" predicate P() { true } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByHide.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByHide.dfy.expect index ef939499206..953f23283c0 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByHide.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByHide.dfy.expect @@ -1,3 +1,3 @@ CallByHide.dfy(15,10): Error: assertion might not hold -Dafny program verifier finished with 0 verified, 1 error +Dafny program verifier finished with 2 verified, 1 error From 16f8ef77b2d3b6e8798b199282a7e762ada6ad57 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 23 Sep 2024 12:23:36 +0200 Subject: [PATCH 22/47] Convert giant if to switch statement --- .../Resolver/GhostInterestVisitor.cs | 756 ++++++++++-------- 1 file changed, 414 insertions(+), 342 deletions(-) diff --git a/Source/DafnyCore/Resolver/GhostInterestVisitor.cs b/Source/DafnyCore/Resolver/GhostInterestVisitor.cs index cf9216a1cbe..73545283fc6 100644 --- a/Source/DafnyCore/Resolver/GhostInterestVisitor.cs +++ b/Source/DafnyCore/Resolver/GhostInterestVisitor.cs @@ -74,417 +74,489 @@ public void Visit(Statement stmt, bool mustBeErasable, [CanBeNull] string proofC Contract.Assume(!codeContext.IsGhost || mustBeErasable); // (this is really a precondition) CodeContext.IsGhost ==> mustBeErasable Contract.Assume(mustBeErasable || proofContext == null); // (this is really a precondition) !mustBeErasable ==> proofContext == null - if (stmt is AssertStmt or AssumeStmt) { - stmt.IsGhost = true; - } else if (stmt is ExpectStmt expectStmt) { - expectStmt.IsGhost = false; - if (mustBeErasable) { - Error(ErrorId.r_expect_statement_is_not_ghost, expectStmt, "expect statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); - } else { - ExpressionTester.CheckIsCompilable(resolver, reporter, expectStmt.Expr, codeContext); - // If not provided, the message is populated with a default value in resolution - Contract.Assert(expectStmt.Message != null); - ExpressionTester.CheckIsCompilable(resolver, reporter, expectStmt.Message, codeContext); - } + switch (stmt) + { + case AssertStmt or AssumeStmt: + stmt.IsGhost = true; + break; + case ExpectStmt expectStmt: + { + expectStmt.IsGhost = false; + if (mustBeErasable) { + Error(ErrorId.r_expect_statement_is_not_ghost, expectStmt, "expect statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); + } else { + ExpressionTester.CheckIsCompilable(resolver, reporter, expectStmt.Expr, codeContext); + // If not provided, the message is populated with a default value in resolution + Contract.Assert(expectStmt.Message != null); + ExpressionTester.CheckIsCompilable(resolver, reporter, expectStmt.Message, codeContext); + } - } else if (stmt is PrintStmt) { - var s = (PrintStmt)stmt; - if (mustBeErasable) { - Error(ErrorId.r_print_statement_is_not_ghost, stmt, "print statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); - } else { - s.Args.ForEach(ee => ExpressionTester.CheckIsCompilable(resolver, reporter, ee, codeContext)); + break; } + case PrintStmt printStmt: + { + var s = printStmt; + if (mustBeErasable) { + Error(ErrorId.r_print_statement_is_not_ghost, printStmt, "print statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); + } else { + s.Args.ForEach(ee => ExpressionTester.CheckIsCompilable(resolver, reporter, ee, codeContext)); + } - } else if (stmt is HideRevealStmt hideRevealStmt) { - hideRevealStmt.ResolvedStatements.ForEach(ss => Visit(ss, true, $"a {hideRevealStmt.Kind} statement")); - hideRevealStmt.IsGhost = hideRevealStmt.ResolvedStatements.All(ss => ss.IsGhost); - } else if (stmt is BreakStmt) { - var s = (BreakStmt)stmt; - s.IsGhost = mustBeErasable; - if (s.IsGhost && !s.TargetStmt.IsGhost) { - var targetKind = s.TargetStmt is LoopStmt ? "loop" : "structure"; - Error(ErrorId.r_ghost_break, stmt, $"ghost-context {s.Kind} statement is not allowed to {s.Kind} out of non-ghost {targetKind}"); - } + break; + } + case HideRevealStmt hideRevealStmt: + hideRevealStmt.ResolvedStatements.ForEach(ss => Visit(ss, true, $"a {hideRevealStmt.Kind} statement")); + hideRevealStmt.IsGhost = hideRevealStmt.ResolvedStatements.All(ss => ss.IsGhost); + break; + case BreakStmt breakStmt: + { + var s = breakStmt; + s.IsGhost = mustBeErasable; + if (s.IsGhost && !s.TargetStmt.IsGhost) { + var targetKind = s.TargetStmt is LoopStmt ? "loop" : "structure"; + Error(ErrorId.r_ghost_break, breakStmt, $"ghost-context {s.Kind} statement is not allowed to {s.Kind} out of non-ghost {targetKind}"); + } - } else if (stmt is ProduceStmt) { - var s = (ProduceStmt)stmt; - var kind = stmt is YieldStmt ? "yield" : "return"; - if (mustBeErasable && !codeContext.IsGhost) { - Error(ErrorId.r_produce_statement_not_allowed_in_ghost, stmt, "{0} statement is not allowed in this context (because it is guarded by a specification-only expression)", kind); - } - if (s.HiddenUpdate != null) { - Visit(s.HiddenUpdate, mustBeErasable, proofContext); + break; } + case ProduceStmt produceStmt: + { + var s = produceStmt; + var kind = produceStmt is YieldStmt ? "yield" : "return"; + if (mustBeErasable && !codeContext.IsGhost) { + Error(ErrorId.r_produce_statement_not_allowed_in_ghost, produceStmt, "{0} statement is not allowed in this context (because it is guarded by a specification-only expression)", kind); + } + if (s.HiddenUpdate != null) { + Visit(s.HiddenUpdate, mustBeErasable, proofContext); + } - } else if (stmt is AssignSuchThatStmt) { - var s = (AssignSuchThatStmt)stmt; - s.IsGhost = mustBeErasable || s.AssumeToken != null || s.Lhss.Any(SingleAssignStmt.LhsIsToGhost); - if (mustBeErasable && !codeContext.IsGhost) { - foreach (var lhs in s.Lhss) { - var gk = SingleAssignStmt.LhsIsToGhost_Which(lhs); - if (gk != SingleAssignStmt.NonGhostKind.IsGhost) { - Error(ErrorId.r_no_assign_to_var_in_ghost, lhs, "cannot assign to {0} in a ghost context", SingleAssignStmt.NonGhostKind_To_String(gk)); + break; + } + case AssignSuchThatStmt thatStmt: + { + var s = thatStmt; + s.IsGhost = mustBeErasable || s.AssumeToken != null || s.Lhss.Any(SingleAssignStmt.LhsIsToGhost); + if (mustBeErasable && !codeContext.IsGhost) { + foreach (var lhs in s.Lhss) { + var gk = SingleAssignStmt.LhsIsToGhost_Which(lhs); + if (gk != SingleAssignStmt.NonGhostKind.IsGhost) { + Error(ErrorId.r_no_assign_to_var_in_ghost, lhs, "cannot assign to {0} in a ghost context", SingleAssignStmt.NonGhostKind_To_String(gk)); + } + } + } else if (!mustBeErasable && s.AssumeToken == null && ExpressionTester.UsesSpecFeatures(s.Expr)) { + foreach (var lhs in s.Lhss) { + var gk = SingleAssignStmt.LhsIsToGhost_Which(lhs); + if (gk != SingleAssignStmt.NonGhostKind.IsGhost) { + Error(ErrorId.r_no_assign_ghost_to_var, lhs, "{0} cannot be assigned a value that depends on a ghost", SingleAssignStmt.NonGhostKind_To_String(gk)); + } } } - } else if (!mustBeErasable && s.AssumeToken == null && ExpressionTester.UsesSpecFeatures(s.Expr)) { - foreach (var lhs in s.Lhss) { - var gk = SingleAssignStmt.LhsIsToGhost_Which(lhs); - if (gk != SingleAssignStmt.NonGhostKind.IsGhost) { - Error(ErrorId.r_no_assign_ghost_to_var, lhs, "{0} cannot be assigned a value that depends on a ghost", SingleAssignStmt.NonGhostKind_To_String(gk)); + + break; + } + case AssignStatement statement: + { + var s = statement; + s.ResolvedStatements.ForEach(ss => Visit(ss, mustBeErasable, proofContext)); + s.IsGhost = s.ResolvedStatements.All(ss => ss.IsGhost); + break; + } + case AssignOrReturnStmt returnStmt: + { + var s = returnStmt; + s.ResolvedStatements.ForEach(ss => Visit(ss, mustBeErasable, proofContext)); + s.IsGhost = s.ResolvedStatements.All(ss => ss.IsGhost); + break; + } + case VarDeclStmt declStmt: + { + var s = declStmt; + if (mustBeErasable) { + foreach (var local in s.Locals) { + // a local variable in a specification-only context might as well be ghost + local.MakeGhost(); } } - } + if (s.Assign != null) { + Visit(s.Assign, mustBeErasable, proofContext); + } + s.IsGhost = (s.Assign == null || s.Assign.IsGhost) && s.Locals.All(v => v.IsGhost); - } else if (stmt is AssignStatement) { - var s = (AssignStatement)stmt; - s.ResolvedStatements.ForEach(ss => Visit(ss, mustBeErasable, proofContext)); - s.IsGhost = s.ResolvedStatements.All(ss => ss.IsGhost); - } else if (stmt is AssignOrReturnStmt) { - var s = (AssignOrReturnStmt)stmt; - s.ResolvedStatements.ForEach(ss => Visit(ss, mustBeErasable, proofContext)); - s.IsGhost = s.ResolvedStatements.All(ss => ss.IsGhost); - - } else if (stmt is VarDeclStmt) { - var s = (VarDeclStmt)stmt; - if (mustBeErasable) { + // Check on "assumption" variables foreach (var local in s.Locals) { - // a local variable in a specification-only context might as well be ghost - local.MakeGhost(); - } - } - if (s.Assign != null) { - Visit(s.Assign, mustBeErasable, proofContext); - } - s.IsGhost = (s.Assign == null || s.Assign.IsGhost) && s.Locals.All(v => v.IsGhost); - - // Check on "assumption" variables - foreach (var local in s.Locals) { - if (Attributes.Contains(local.Attributes, "assumption")) { - if (allowAssumptionVariables) { - if (!local.Type.IsBoolType) { - Error(ErrorId.r_assumption_var_must_be_bool, local.Tok, "assumption variable must be of type 'bool'"); - } - if (!local.IsGhost) { - Error(ErrorId.r_assumption_var_must_be_ghost, local.Tok, "assumption variable must be ghost"); + if (Attributes.Contains(local.Attributes, "assumption")) { + if (allowAssumptionVariables) { + if (!local.Type.IsBoolType) { + Error(ErrorId.r_assumption_var_must_be_bool, local.Tok, "assumption variable must be of type 'bool'"); + } + if (!local.IsGhost) { + Error(ErrorId.r_assumption_var_must_be_ghost, local.Tok, "assumption variable must be ghost"); + } + } else { + Error(ErrorId.r_assumption_var_must_be_in_method, local.Tok, "assumption variable can only be declared in a method"); } - } else { - Error(ErrorId.r_assumption_var_must_be_in_method, local.Tok, "assumption variable can only be declared in a method"); } } - } - } else if (stmt is VarDeclPattern) { - var s = (VarDeclPattern)stmt; - - if (mustBeErasable) { - foreach (var local in s.LocalVars) { - local.MakeGhost(); - } + break; } - if (s.HasGhostModifier || mustBeErasable) { - s.IsGhost = s.LocalVars.All(v => v.IsGhost); - } else { - var spec = ExpressionTester.UsesSpecFeatures(s.RHS); - if (spec) { + case VarDeclPattern pattern: + { + var s = pattern; + + if (mustBeErasable) { foreach (var local in s.LocalVars) { local.MakeGhost(); } + } + if (s.HasGhostModifier || mustBeErasable) { + s.IsGhost = s.LocalVars.All(v => v.IsGhost); } else { - ExpressionTester.CheckIsCompilable(resolver, reporter, s.RHS, codeContext); + var spec = ExpressionTester.UsesSpecFeatures(s.RHS); + if (spec) { + foreach (var local in s.LocalVars) { + local.MakeGhost(); + } + } else { + ExpressionTester.CheckIsCompilable(resolver, reporter, s.RHS, codeContext); + } + s.IsGhost = spec; } - s.IsGhost = spec; - } - } else if (stmt is SingleAssignStmt) { - var s = (SingleAssignStmt)stmt; - CheckAssignStmt(s, mustBeErasable, proofContext); - - } else if (stmt is CallStmt) { - var s = (CallStmt)stmt; - var callee = s.Method; - Contract.Assert(callee != null); // follows from the invariant of CallStmt - s.IsGhost = callee.IsGhost; - if (proofContext != null && !callee.IsLemmaLike) { - Error(ErrorId.r_no_calls_in_proof, s, $"in {proofContext}, calls are allowed only to lemmas"); - } else if (mustBeErasable) { - if (!s.IsGhost) { - Error(ErrorId.r_only_ghost_calls, s, "only ghost methods can be called from this context"); - } - } else { - int j; - if (!callee.IsGhost) { - // check in-parameters - ExpressionTester.CheckIsCompilable(resolver, reporter, s.Receiver, codeContext); - j = 0; - foreach (var e in s.Args) { - Contract.Assume(j < callee.Ins.Count); // this should have already been checked by the resolver - if (!callee.Ins[j].IsGhost) { - ExpressionTester.CheckIsCompilable(resolver, reporter, e, codeContext); + break; + } + case SingleAssignStmt assignStmt: + { + var s = assignStmt; + CheckAssignStmt(s, mustBeErasable, proofContext); + break; + } + case CallStmt callStmt: + { + var s = callStmt; + var callee = s.Method; + Contract.Assert(callee != null); // follows from the invariant of CallStmt + s.IsGhost = callee.IsGhost; + if (proofContext != null && !callee.IsLemmaLike) { + Error(ErrorId.r_no_calls_in_proof, s, $"in {proofContext}, calls are allowed only to lemmas"); + } else if (mustBeErasable) { + if (!s.IsGhost) { + Error(ErrorId.r_only_ghost_calls, s, "only ghost methods can be called from this context"); + } + } else { + int j; + if (!callee.IsGhost) { + // check in-parameters + ExpressionTester.CheckIsCompilable(resolver, reporter, s.Receiver, codeContext); + j = 0; + foreach (var e in s.Args) { + Contract.Assume(j < callee.Ins.Count); // this should have already been checked by the resolver + if (!callee.Ins[j].IsGhost) { + ExpressionTester.CheckIsCompilable(resolver, reporter, e, codeContext); + } + j++; } - j++; } - } - j = 0; - foreach (var e in s.Lhs) { - var resolvedLhs = e.Resolved; - if (callee.IsGhost || callee.Outs[j].IsGhost) { - // LHS must denote a ghost - if (resolvedLhs is IdentifierExpr) { - var ll = (IdentifierExpr)resolvedLhs; - if (!ll.Var.IsGhost) { - if (ll is AutoGhostIdentifierExpr && ll.Var is LocalVariable) { - // the variable was actually declared in this statement, so auto-declare it as ghost - ((LocalVariable)ll.Var).MakeGhost(); - } else { - Error(ErrorId.r_out_parameter_must_be_ghost, s, "actual out-parameter{0} is required to be a ghost variable", s.Lhs.Count == 1 ? "" : " " + j); + j = 0; + foreach (var e in s.Lhs) { + var resolvedLhs = e.Resolved; + if (callee.IsGhost || callee.Outs[j].IsGhost) { + // LHS must denote a ghost + if (resolvedLhs is IdentifierExpr) { + var ll = (IdentifierExpr)resolvedLhs; + if (!ll.Var.IsGhost) { + if (ll is AutoGhostIdentifierExpr && ll.Var is LocalVariable) { + // the variable was actually declared in this statement, so auto-declare it as ghost + ((LocalVariable)ll.Var).MakeGhost(); + } else { + Error(ErrorId.r_out_parameter_must_be_ghost, s, "actual out-parameter{0} is required to be a ghost variable", s.Lhs.Count == 1 ? "" : " " + j); + } } + } else if (resolvedLhs is MemberSelectExpr) { + var ll = (MemberSelectExpr)resolvedLhs; + if (!ll.Member.IsGhost) { + Error(ErrorId.r_out_parameter_must_be_ghost_field, s, "actual out-parameter{0} is required to be a ghost field", s.Lhs.Count == 1 ? "" : " " + j); + } + } else { + // this is an array update, and arrays are always non-ghost + Error(ErrorId.r_out_parameter_must_be_ghost, s, "actual out-parameter{0} is required to be a ghost variable", s.Lhs.Count == 1 ? "" : " " + j); } - } else if (resolvedLhs is MemberSelectExpr) { - var ll = (MemberSelectExpr)resolvedLhs; - if (!ll.Member.IsGhost) { - Error(ErrorId.r_out_parameter_must_be_ghost_field, s, "actual out-parameter{0} is required to be a ghost field", s.Lhs.Count == 1 ? "" : " " + j); - } - } else { - // this is an array update, and arrays are always non-ghost - Error(ErrorId.r_out_parameter_must_be_ghost, s, "actual out-parameter{0} is required to be a ghost variable", s.Lhs.Count == 1 ? "" : " " + j); } + j++; } - j++; } - } - } else if (stmt is BlockStmt) { - var s = (BlockStmt)stmt; - s.IsGhost = mustBeErasable; // set .IsGhost before descending into substatements (since substatements may do a 'break' out of this block) - if (s is DividedBlockStmt ds) { - var giv = new GhostInterestVisitor(this.codeContext, this.resolver, this.reporter, true, allowAssumptionVariables); - ds.BodyInit.ForEach(ss => giv.Visit(ss, mustBeErasable, proofContext)); - ds.BodyProper.ForEach(ss => Visit(ss, mustBeErasable, proofContext)); - } else { - s.Body.ForEach(ss => Visit(ss, mustBeErasable, proofContext)); - } - s.IsGhost = s.IsGhost || s.Body.All(ss => ss.IsGhost); // mark the block statement as ghost if all its substatements are ghost - - } else if (stmt is IfStmt) { - var s = (IfStmt)stmt; - s.IsGhost = mustBeErasable || (s.Guard != null && ExpressionTester.UsesSpecFeatures(s.Guard)); - if (!mustBeErasable && s.IsGhost) { - reporter.Info(MessageSource.Resolver, s.Tok, "ghost if"); - } - Visit(s.Thn, s.IsGhost, proofContext); - if (s.Els != null) { - Visit(s.Els, s.IsGhost, proofContext); - } - // if both branches were all ghost, then we can mark the enclosing statement as ghost as well - s.IsGhost = s.IsGhost || (s.Thn.IsGhost && (s.Els == null || s.Els.IsGhost)); - if (!s.IsGhost && s.Guard != null) { - // If there were features in the guard that are treated differently in ghost and non-ghost - // contexts, make sure they get treated for non-ghost use. - ExpressionTester.CheckIsCompilable(resolver, reporter, s.Guard, codeContext); + break; } + case BlockStmt blockStmt: + { + var s = blockStmt; + s.IsGhost = mustBeErasable; // set .IsGhost before descending into substatements (since substatements may do a 'break' out of this block) + if (s is DividedBlockStmt ds) { + var giv = new GhostInterestVisitor(this.codeContext, this.resolver, this.reporter, true, allowAssumptionVariables); + ds.BodyInit.ForEach(ss => giv.Visit(ss, mustBeErasable, proofContext)); + ds.BodyProper.ForEach(ss => Visit(ss, mustBeErasable, proofContext)); + } else { + s.Body.ForEach(ss => Visit(ss, mustBeErasable, proofContext)); + } + s.IsGhost = s.IsGhost || s.Body.All(ss => ss.IsGhost); // mark the block statement as ghost if all its substatements are ghost + break; + } + case IfStmt ifStmt: + { + var s = ifStmt; + s.IsGhost = mustBeErasable || (s.Guard != null && ExpressionTester.UsesSpecFeatures(s.Guard)); + if (!mustBeErasable && s.IsGhost) { + reporter.Info(MessageSource.Resolver, s.Tok, "ghost if"); + } + Visit(s.Thn, s.IsGhost, proofContext); + if (s.Els != null) { + Visit(s.Els, s.IsGhost, proofContext); + } + // if both branches were all ghost, then we can mark the enclosing statement as ghost as well + s.IsGhost = s.IsGhost || (s.Thn.IsGhost && (s.Els == null || s.Els.IsGhost)); + if (!s.IsGhost && s.Guard != null) { + // If there were features in the guard that are treated differently in ghost and non-ghost + // contexts, make sure they get treated for non-ghost use. + ExpressionTester.CheckIsCompilable(resolver, reporter, s.Guard, codeContext); + } - } else if (stmt is AlternativeStmt) { - var s = (AlternativeStmt)stmt; - s.IsGhost = mustBeErasable || s.Alternatives.Exists(alt => ExpressionTester.UsesSpecFeatures(alt.Guard)); - if (!mustBeErasable && s.IsGhost) { - reporter.Info(MessageSource.Resolver, s.Tok, "ghost if"); + break; } - s.Alternatives.ForEach(alt => alt.Body.ForEach(ss => Visit(ss, s.IsGhost, proofContext))); - s.IsGhost = s.IsGhost || s.Alternatives.All(alt => alt.Body.All(ss => ss.IsGhost)); - if (!s.IsGhost) { - // If there were features in the guards that are treated differently in ghost and non-ghost - // contexts, make sure they get treated for non-ghost use. - foreach (var alt in s.Alternatives) { - ExpressionTester.CheckIsCompilable(resolver, reporter, alt.Guard, codeContext); + case AlternativeStmt alternativeStmt: + { + var s = alternativeStmt; + s.IsGhost = mustBeErasable || s.Alternatives.Exists(alt => ExpressionTester.UsesSpecFeatures(alt.Guard)); + if (!mustBeErasable && s.IsGhost) { + reporter.Info(MessageSource.Resolver, s.Tok, "ghost if"); + } + s.Alternatives.ForEach(alt => alt.Body.ForEach(ss => Visit(ss, s.IsGhost, proofContext))); + s.IsGhost = s.IsGhost || s.Alternatives.All(alt => alt.Body.All(ss => ss.IsGhost)); + if (!s.IsGhost) { + // If there were features in the guards that are treated differently in ghost and non-ghost + // contexts, make sure they get treated for non-ghost use. + foreach (var alt in s.Alternatives) { + ExpressionTester.CheckIsCompilable(resolver, reporter, alt.Guard, codeContext); + } } - } - } else if (stmt is WhileStmt) { - var s = (WhileStmt)stmt; - if (proofContext != null && s.Mod.Expressions != null && s.Mod.Expressions.Count != 0) { - Error(ErrorId.r_loop_may_not_use_modifies, s.Mod.Expressions[0].tok, $"a loop in {proofContext} is not allowed to use 'modifies' clauses"); + break; } + case WhileStmt whileStmt: + { + var s = whileStmt; + if (proofContext != null && s.Mod.Expressions != null && s.Mod.Expressions.Count != 0) { + Error(ErrorId.r_loop_may_not_use_modifies, s.Mod.Expressions[0].tok, $"a loop in {proofContext} is not allowed to use 'modifies' clauses"); + } - s.IsGhost = mustBeErasable || (s.Guard != null && ExpressionTester.UsesSpecFeatures(s.Guard)); - if (!mustBeErasable && s.IsGhost) { - reporter.Info(MessageSource.Resolver, s.Tok, "ghost while"); - } - if (s.IsGhost && s.Decreases.Expressions.Exists(e => e is WildcardExpr)) { - Error(ErrorId.r_decreases_forbidden_on_ghost_loops, s, "'decreases *' is not allowed on ghost loops"); - } - if (s.IsGhost && s.Mod.Expressions != null) { - s.Mod.Expressions.ForEach(resolver.DisallowNonGhostFieldSpecifiers); - } - if (s.Body != null) { - Visit(s.Body, s.IsGhost, proofContext); - if (s.Body.IsGhost && !s.Decreases.Expressions.Exists(e => e is WildcardExpr)) { - s.IsGhost = true; + s.IsGhost = mustBeErasable || (s.Guard != null && ExpressionTester.UsesSpecFeatures(s.Guard)); + if (!mustBeErasable && s.IsGhost) { + reporter.Info(MessageSource.Resolver, s.Tok, "ghost while"); + } + if (s.IsGhost && s.Decreases.Expressions.Exists(e => e is WildcardExpr)) { + Error(ErrorId.r_decreases_forbidden_on_ghost_loops, s, "'decreases *' is not allowed on ghost loops"); + } + if (s.IsGhost && s.Mod.Expressions != null) { + s.Mod.Expressions.ForEach(resolver.DisallowNonGhostFieldSpecifiers); + } + if (s.Body != null) { + Visit(s.Body, s.IsGhost, proofContext); + if (s.Body.IsGhost && !s.Decreases.Expressions.Exists(e => e is WildcardExpr)) { + s.IsGhost = true; + } + } + if (!s.IsGhost && s.Guard != null) { + // If there were features in the guard that are treated differently in ghost and non-ghost + // contexts, make sure they get treated for non-ghost use. + ExpressionTester.CheckIsCompilable(resolver, reporter, s.Guard, codeContext); } - } - if (!s.IsGhost && s.Guard != null) { - // If there were features in the guard that are treated differently in ghost and non-ghost - // contexts, make sure they get treated for non-ghost use. - ExpressionTester.CheckIsCompilable(resolver, reporter, s.Guard, codeContext); - } - } else if (stmt is AlternativeLoopStmt) { - var s = (AlternativeLoopStmt)stmt; - if (proofContext != null && s.Mod.Expressions != null && s.Mod.Expressions.Count != 0) { - Error(ErrorId.r_loop_in_proof_may_not_use_modifies, s.Mod.Expressions[0].tok, $"a loop in {proofContext} is not allowed to use 'modifies' clauses"); + break; } + case AlternativeLoopStmt loopStmt: + { + var s = loopStmt; + if (proofContext != null && s.Mod.Expressions != null && s.Mod.Expressions.Count != 0) { + Error(ErrorId.r_loop_in_proof_may_not_use_modifies, s.Mod.Expressions[0].tok, $"a loop in {proofContext} is not allowed to use 'modifies' clauses"); + } - s.IsGhost = mustBeErasable || s.Alternatives.Exists(alt => ExpressionTester.UsesSpecFeatures(alt.Guard)); - if (!mustBeErasable && s.IsGhost) { - reporter.Info(MessageSource.Resolver, s.Tok, "ghost while"); - } - if (s.IsGhost && s.Decreases.Expressions.Exists(e => e is WildcardExpr)) { - Error(ErrorId.r_decreases_forbidden_on_ghost_loops, s, "'decreases *' is not allowed on ghost loops"); - } - if (s.IsGhost && s.Mod.Expressions != null) { - s.Mod.Expressions.ForEach(resolver.DisallowNonGhostFieldSpecifiers); - } - s.Alternatives.ForEach(alt => alt.Body.ForEach(ss => Visit(ss, s.IsGhost, proofContext))); - s.IsGhost = s.IsGhost || (!s.Decreases.Expressions.Exists(e => e is WildcardExpr) && s.Alternatives.All(alt => alt.Body.All(ss => ss.IsGhost))); - if (!s.IsGhost) { - // If there were features in the guards that are treated differently in ghost and non-ghost - // contexts, make sure they get treated for non-ghost use. - foreach (var alt in s.Alternatives) { - ExpressionTester.CheckIsCompilable(resolver, reporter, alt.Guard, codeContext); + s.IsGhost = mustBeErasable || s.Alternatives.Exists(alt => ExpressionTester.UsesSpecFeatures(alt.Guard)); + if (!mustBeErasable && s.IsGhost) { + reporter.Info(MessageSource.Resolver, s.Tok, "ghost while"); + } + if (s.IsGhost && s.Decreases.Expressions.Exists(e => e is WildcardExpr)) { + Error(ErrorId.r_decreases_forbidden_on_ghost_loops, s, "'decreases *' is not allowed on ghost loops"); + } + if (s.IsGhost && s.Mod.Expressions != null) { + s.Mod.Expressions.ForEach(resolver.DisallowNonGhostFieldSpecifiers); + } + s.Alternatives.ForEach(alt => alt.Body.ForEach(ss => Visit(ss, s.IsGhost, proofContext))); + s.IsGhost = s.IsGhost || (!s.Decreases.Expressions.Exists(e => e is WildcardExpr) && s.Alternatives.All(alt => alt.Body.All(ss => ss.IsGhost))); + if (!s.IsGhost) { + // If there were features in the guards that are treated differently in ghost and non-ghost + // contexts, make sure they get treated for non-ghost use. + foreach (var alt in s.Alternatives) { + ExpressionTester.CheckIsCompilable(resolver, reporter, alt.Guard, codeContext); + } } - } - } else if (stmt is ForLoopStmt) { - var s = (ForLoopStmt)stmt; - if (proofContext != null && s.Mod.Expressions != null && s.Mod.Expressions.Count != 0) { - Error(ErrorId.r_loop_in_proof_may_not_use_modifies, s.Mod.Expressions[0].tok, $"a loop in {proofContext} is not allowed to use 'modifies' clauses"); + break; } + case ForLoopStmt loopStmt: + { + var s = loopStmt; + if (proofContext != null && s.Mod.Expressions != null && s.Mod.Expressions.Count != 0) { + Error(ErrorId.r_loop_in_proof_may_not_use_modifies, s.Mod.Expressions[0].tok, $"a loop in {proofContext} is not allowed to use 'modifies' clauses"); + } - s.IsGhost = mustBeErasable || ExpressionTester.UsesSpecFeatures(s.Start) || (s.End != null && ExpressionTester.UsesSpecFeatures(s.End)); - if (!mustBeErasable && s.IsGhost) { - reporter.Info(MessageSource.Resolver, s.Tok, "ghost for-loop"); - } - if (s.IsGhost) { - if (s.Decreases.Expressions.Exists(e => e is WildcardExpr)) { - Error(ErrorId.r_decreases_forbidden_on_ghost_loops, s, "'decreases *' is not allowed on ghost loops"); - } else if (s.End == null && s.Decreases.Expressions.Count == 0) { - Error(ErrorId.r_ghost_loop_must_terminate, s, "a ghost loop must be terminating; make the end-expression specific or add a 'decreases' clause"); + s.IsGhost = mustBeErasable || ExpressionTester.UsesSpecFeatures(s.Start) || (s.End != null && ExpressionTester.UsesSpecFeatures(s.End)); + if (!mustBeErasable && s.IsGhost) { + reporter.Info(MessageSource.Resolver, s.Tok, "ghost for-loop"); } - } - if (s.IsGhost && s.Mod.Expressions != null) { - s.Mod.Expressions.ForEach(resolver.DisallowNonGhostFieldSpecifiers); - } - if (s.Body != null) { - Visit(s.Body, s.IsGhost, proofContext); - if (s.Body.IsGhost) { - s.IsGhost = true; + if (s.IsGhost) { + if (s.Decreases.Expressions.Exists(e => e is WildcardExpr)) { + Error(ErrorId.r_decreases_forbidden_on_ghost_loops, s, "'decreases *' is not allowed on ghost loops"); + } else if (s.End == null && s.Decreases.Expressions.Count == 0) { + Error(ErrorId.r_ghost_loop_must_terminate, s, "a ghost loop must be terminating; make the end-expression specific or add a 'decreases' clause"); + } } - } - if (!s.IsGhost) { - // If there were features in the bounds that are treated differently in ghost and non-ghost - // contexts, make sure they get treated for non-ghost use. - ExpressionTester.CheckIsCompilable(resolver, reporter, s.Start, codeContext); - if (s.End != null) { - ExpressionTester.CheckIsCompilable(resolver, reporter, s.End, codeContext); + if (s.IsGhost && s.Mod.Expressions != null) { + s.Mod.Expressions.ForEach(resolver.DisallowNonGhostFieldSpecifiers); + } + if (s.Body != null) { + Visit(s.Body, s.IsGhost, proofContext); + if (s.Body.IsGhost) { + s.IsGhost = true; + } + } + if (!s.IsGhost) { + // If there were features in the bounds that are treated differently in ghost and non-ghost + // contexts, make sure they get treated for non-ghost use. + ExpressionTester.CheckIsCompilable(resolver, reporter, s.Start, codeContext); + if (s.End != null) { + ExpressionTester.CheckIsCompilable(resolver, reporter, s.End, codeContext); + } } - } - } else if (stmt is ForallStmt) { - var s = (ForallStmt)stmt; - s.IsGhost = mustBeErasable || s.Kind != ForallStmt.BodyKind.Assign || ExpressionTester.UsesSpecFeatures(s.Range); - if (proofContext != null && s.Kind == ForallStmt.BodyKind.Assign) { - Error(ErrorId.r_no_aggregate_heap_update_in_proof, s, $"{proofContext} is not allowed to perform an aggregate heap update"); - } else if (s.Body != null) { - Visit(s.Body, s.IsGhost, s.Kind == ForallStmt.BodyKind.Assign ? proofContext : "a forall statement"); + break; } - s.IsGhost = s.IsGhost || s.Body == null || s.Body.IsGhost; - - if (!s.IsGhost) { - // Since we've determined this is a non-ghost forall statement, we now check that the bound variables have compilable bounds. - var uncompilableBoundVars = s.UncompilableBoundVars(); - if (uncompilableBoundVars.Count != 0) { - foreach (var bv in uncompilableBoundVars) { - Error(ErrorId.r_unknown_bounds_for_forall, s, "forall statements in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for '{0}'", bv.Name); + case ForallStmt forallStmt: + { + var s = forallStmt; + s.IsGhost = mustBeErasable || s.Kind != ForallStmt.BodyKind.Assign || ExpressionTester.UsesSpecFeatures(s.Range); + if (proofContext != null && s.Kind == ForallStmt.BodyKind.Assign) { + Error(ErrorId.r_no_aggregate_heap_update_in_proof, s, $"{proofContext} is not allowed to perform an aggregate heap update"); + } else if (s.Body != null) { + Visit(s.Body, s.IsGhost, s.Kind == ForallStmt.BodyKind.Assign ? proofContext : "a forall statement"); + } + s.IsGhost = s.IsGhost || s.Body == null || s.Body.IsGhost; + + if (!s.IsGhost) { + // Since we've determined this is a non-ghost forall statement, we now check that the bound variables have compilable bounds. + var uncompilableBoundVars = s.UncompilableBoundVars(); + if (uncompilableBoundVars.Count != 0) { + foreach (var bv in uncompilableBoundVars) { + Error(ErrorId.r_unknown_bounds_for_forall, s, "forall statements in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for '{0}'", bv.Name); + } } + + // If there were features in the range that are treated differently in ghost and non-ghost + // contexts, make sure they get treated for non-ghost use. + ExpressionTester.CheckIsCompilable(resolver, reporter, s.Range, codeContext); } - // If there were features in the range that are treated differently in ghost and non-ghost - // contexts, make sure they get treated for non-ghost use. - ExpressionTester.CheckIsCompilable(resolver, reporter, s.Range, codeContext); + break; } + case ModifyStmt modifyStmt: + { + var s = modifyStmt; + if (proofContext != null) { + Error(ErrorId.r_modify_forbidden_in_proof, modifyStmt, $"a modify statement is not allowed in {proofContext}"); + } - } else if (stmt is ModifyStmt) { - var s = (ModifyStmt)stmt; - if (proofContext != null) { - Error(ErrorId.r_modify_forbidden_in_proof, stmt, $"a modify statement is not allowed in {proofContext}"); - } + s.IsGhost = mustBeErasable; + if (s.IsGhost) { + s.Mod.Expressions.ForEach(resolver.DisallowNonGhostFieldSpecifiers); + } + if (s.Body != null) { + Visit(s.Body, mustBeErasable, proofContext); + } - s.IsGhost = mustBeErasable; - if (s.IsGhost) { - s.Mod.Expressions.ForEach(resolver.DisallowNonGhostFieldSpecifiers); - } - if (s.Body != null) { - Visit(s.Body, mustBeErasable, proofContext); + break; } + case CalcStmt calcStmt: + { + var s = calcStmt; + s.IsGhost = true; + foreach (var h in s.Hints) { + Visit(h, true, "a hint"); + } - } else if (stmt is CalcStmt) { - var s = (CalcStmt)stmt; - s.IsGhost = true; - foreach (var h in s.Hints) { - Visit(h, true, "a hint"); + break; } + case NestedMatchStmt nestedMatchStmt: + { + var hasGhostPattern = nestedMatchStmt.Cases. + SelectMany(caze => caze.Pat.DescendantsAndSelf) + .OfType().Any(idPattern => idPattern.Ctor != null && idPattern.Ctor.IsGhost); + nestedMatchStmt.IsGhost = mustBeErasable || ExpressionTester.UsesSpecFeatures(nestedMatchStmt.Source) || hasGhostPattern; - } else if (stmt is NestedMatchStmt nestedMatchStmt) { - var hasGhostPattern = nestedMatchStmt.Cases. - SelectMany(caze => caze.Pat.DescendantsAndSelf) - .OfType().Any(idPattern => idPattern.Ctor != null && idPattern.Ctor.IsGhost); - nestedMatchStmt.IsGhost = mustBeErasable || ExpressionTester.UsesSpecFeatures(nestedMatchStmt.Source) || hasGhostPattern; + foreach (var kase in nestedMatchStmt.Cases) { + ExpressionTester.MakeGhostAsNeeded(kase.Pat, nestedMatchStmt.IsGhost); + } - foreach (var kase in nestedMatchStmt.Cases) { - ExpressionTester.MakeGhostAsNeeded(kase.Pat, nestedMatchStmt.IsGhost); - } + if (!mustBeErasable && nestedMatchStmt.IsGhost) { + reporter.Info(MessageSource.Resolver, nestedMatchStmt.Tok, "ghost match"); + } + nestedMatchStmt.Cases.ForEach(kase => kase.Body.ForEach(ss => Visit(ss, nestedMatchStmt.IsGhost, proofContext))); + nestedMatchStmt.IsGhost = nestedMatchStmt.IsGhost || nestedMatchStmt.Cases.All(kase => kase.Body.All(ss => ss.IsGhost)); + if (!nestedMatchStmt.IsGhost) { + // If there were features in the source expression that are treated differently in ghost and non-ghost + // contexts, make sure they get treated for non-ghost use. + ExpressionTester.CheckIsCompilable(resolver, reporter, nestedMatchStmt.Source, codeContext); + } - if (!mustBeErasable && nestedMatchStmt.IsGhost) { - reporter.Info(MessageSource.Resolver, nestedMatchStmt.Tok, "ghost match"); - } - nestedMatchStmt.Cases.ForEach(kase => kase.Body.ForEach(ss => Visit(ss, nestedMatchStmt.IsGhost, proofContext))); - nestedMatchStmt.IsGhost = nestedMatchStmt.IsGhost || nestedMatchStmt.Cases.All(kase => kase.Body.All(ss => ss.IsGhost)); - if (!nestedMatchStmt.IsGhost) { - // If there were features in the source expression that are treated differently in ghost and non-ghost - // contexts, make sure they get treated for non-ghost use. - ExpressionTester.CheckIsCompilable(resolver, reporter, nestedMatchStmt.Source, codeContext); - } - } else if (stmt is MatchStmt) { - var s = (MatchStmt)stmt; - s.IsGhost = mustBeErasable || ExpressionTester.UsesSpecFeatures(s.Source) || ExpressionTester.FirstCaseThatDependsOnGhostCtor(s.Cases) != null; - if (!mustBeErasable && s.IsGhost) { - reporter.Info(MessageSource.Resolver, s.Tok, "ghost match"); - } - s.Cases.ForEach(kase => kase.Body.ForEach(ss => Visit(ss, s.IsGhost, proofContext))); - s.IsGhost = s.IsGhost || s.Cases.All(kase => kase.Body.All(ss => ss.IsGhost)); - if (!s.IsGhost) { - // If there were features in the source expression that are treated differently in ghost and non-ghost - // contexts, make sure they get treated for non-ghost use. - ExpressionTester.CheckIsCompilable(resolver, reporter, s.Source, codeContext); + break; } + case MatchStmt matchStmt: + { + var s = matchStmt; + s.IsGhost = mustBeErasable || ExpressionTester.UsesSpecFeatures(s.Source) || ExpressionTester.FirstCaseThatDependsOnGhostCtor(s.Cases) != null; + if (!mustBeErasable && s.IsGhost) { + reporter.Info(MessageSource.Resolver, s.Tok, "ghost match"); + } + s.Cases.ForEach(kase => kase.Body.ForEach(ss => Visit(ss, s.IsGhost, proofContext))); + s.IsGhost = s.IsGhost || s.Cases.All(kase => kase.Body.All(ss => ss.IsGhost)); + if (!s.IsGhost) { + // If there were features in the source expression that are treated differently in ghost and non-ghost + // contexts, make sure they get treated for non-ghost use. + ExpressionTester.CheckIsCompilable(resolver, reporter, s.Source, codeContext); + } - } else if (stmt is SkeletonStatement) { - var s = (SkeletonStatement)stmt; - s.IsGhost = mustBeErasable; - if (s.S != null) { - Visit(s.S, mustBeErasable, proofContext); - s.IsGhost = s.IsGhost || s.S.IsGhost; + break; } + case SkeletonStatement statement: + { + var s = statement; + s.IsGhost = mustBeErasable; + if (s.S != null) { + Visit(s.S, mustBeErasable, proofContext); + s.IsGhost = s.IsGhost || s.S.IsGhost; + } - } else if (stmt is BlockByProofStmt blockByProofStmt) { - // TODO move to the BlockByProofStmt class + break; + } + case BlockByProofStmt blockByProofStmt: + // TODO move to the BlockByProofStmt class - blockByProofStmt.IsGhost = mustBeErasable; // set .IsGhost before descending into substatements (since substatements may do a 'break' out of this block) - Visit(blockByProofStmt.Body, mustBeErasable, proofContext); - blockByProofStmt.IsGhost = blockByProofStmt.IsGhost || blockByProofStmt.Body.IsGhost; + blockByProofStmt.IsGhost = mustBeErasable; // set .IsGhost before descending into substatements (since substatements may do a 'break' out of this block) + Visit(blockByProofStmt.Body, mustBeErasable, proofContext); + blockByProofStmt.IsGhost = blockByProofStmt.IsGhost || blockByProofStmt.Body.IsGhost; - Visit(blockByProofStmt.Proof, true, "a by body"); - } else { - Contract.Assert(false); throw new cce.UnreachableException(); + Visit(blockByProofStmt.Proof, true, "a by body"); + break; + default: + Contract.Assert(false); throw new cce.UnreachableException(); } } From 5415a97664eb21a0b2bf097668ac2b7b5a874465 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 23 Sep 2024 12:29:33 +0200 Subject: [PATCH 23/47] Fix --- Source/DafnyCore/AST/Statements/BlockByProofStmt.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs b/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs index 0f944559a05..cd87df77c3e 100644 --- a/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs +++ b/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs @@ -21,19 +21,12 @@ public BlockByProofStmt(Cloner cloner, BlockByProofStmt original) : base(cloner, public override void GenResolve(INewOrOldResolver resolver, ResolutionContext resolutionContext) { resolver.ResolveStatement(Body, resolutionContext); - ResolveByProof(resolver, Proof, resolutionContext with { - CodeContext = new CodeContextWrapper(resolutionContext.CodeContext, true) - }); + ResolveByProof(resolver, Proof, resolutionContext); base.GenResolve(resolver, resolutionContext); } // CheckLocalityUpdates - // GhostInterestVisitor - // if (s.Proof != null) { - // Visit(s.Proof, true, "a call-by body"); - // } - internal static void ResolveByProof(INewOrOldResolver resolver, BlockStmt proof, ResolutionContext resolutionContext) { if (proof == null) { return; From 10ee6698d0ef1f12a280d45d6378778558229af0 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 23 Sep 2024 16:07:53 +0200 Subject: [PATCH 24/47] Ran formatter and def assignment improvement --- .../Resolver/GhostInterestVisitor.cs | 772 +++++++++--------- .../BoogieGenerator.DefiniteAssignment.cs | 34 +- .../BoogieGenerator.ExpressionTranslator.cs | 10 +- .../Verifier/BoogieGenerator.Methods.cs | 2 - Source/DafnyCore/Verifier/BoogieGenerator.cs | 4 +- .../BoogieGenerator.TrAssignment.cs | 2 +- .../Statements/BoogieGenerator.TrLoop.cs | 2 +- .../Statements/BoogieGenerator.TrStatement.cs | 1 - 8 files changed, 393 insertions(+), 434 deletions(-) diff --git a/Source/DafnyCore/Resolver/GhostInterestVisitor.cs b/Source/DafnyCore/Resolver/GhostInterestVisitor.cs index 73545283fc6..748704e16e4 100644 --- a/Source/DafnyCore/Resolver/GhostInterestVisitor.cs +++ b/Source/DafnyCore/Resolver/GhostInterestVisitor.cs @@ -74,478 +74,454 @@ public void Visit(Statement stmt, bool mustBeErasable, [CanBeNull] string proofC Contract.Assume(!codeContext.IsGhost || mustBeErasable); // (this is really a precondition) CodeContext.IsGhost ==> mustBeErasable Contract.Assume(mustBeErasable || proofContext == null); // (this is really a precondition) !mustBeErasable ==> proofContext == null - switch (stmt) - { + switch (stmt) { case AssertStmt or AssumeStmt: stmt.IsGhost = true; break; - case ExpectStmt expectStmt: - { - expectStmt.IsGhost = false; - if (mustBeErasable) { - Error(ErrorId.r_expect_statement_is_not_ghost, expectStmt, "expect statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); - } else { - ExpressionTester.CheckIsCompilable(resolver, reporter, expectStmt.Expr, codeContext); - // If not provided, the message is populated with a default value in resolution - Contract.Assert(expectStmt.Message != null); - ExpressionTester.CheckIsCompilable(resolver, reporter, expectStmt.Message, codeContext); - } + case ExpectStmt expectStmt: { + expectStmt.IsGhost = false; + if (mustBeErasable) { + Error(ErrorId.r_expect_statement_is_not_ghost, expectStmt, "expect statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); + } else { + ExpressionTester.CheckIsCompilable(resolver, reporter, expectStmt.Expr, codeContext); + // If not provided, the message is populated with a default value in resolution + Contract.Assert(expectStmt.Message != null); + ExpressionTester.CheckIsCompilable(resolver, reporter, expectStmt.Message, codeContext); + } - break; - } - case PrintStmt printStmt: - { - var s = printStmt; - if (mustBeErasable) { - Error(ErrorId.r_print_statement_is_not_ghost, printStmt, "print statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); - } else { - s.Args.ForEach(ee => ExpressionTester.CheckIsCompilable(resolver, reporter, ee, codeContext)); + break; } + case PrintStmt printStmt: { + var s = printStmt; + if (mustBeErasable) { + Error(ErrorId.r_print_statement_is_not_ghost, printStmt, "print statement is not allowed in this context (because this is a ghost method or because the statement is guarded by a specification-only expression)"); + } else { + s.Args.ForEach(ee => ExpressionTester.CheckIsCompilable(resolver, reporter, ee, codeContext)); + } - break; - } + break; + } case HideRevealStmt hideRevealStmt: hideRevealStmt.ResolvedStatements.ForEach(ss => Visit(ss, true, $"a {hideRevealStmt.Kind} statement")); hideRevealStmt.IsGhost = hideRevealStmt.ResolvedStatements.All(ss => ss.IsGhost); break; - case BreakStmt breakStmt: - { - var s = breakStmt; - s.IsGhost = mustBeErasable; - if (s.IsGhost && !s.TargetStmt.IsGhost) { - var targetKind = s.TargetStmt is LoopStmt ? "loop" : "structure"; - Error(ErrorId.r_ghost_break, breakStmt, $"ghost-context {s.Kind} statement is not allowed to {s.Kind} out of non-ghost {targetKind}"); - } + case BreakStmt breakStmt: { + var s = breakStmt; + s.IsGhost = mustBeErasable; + if (s.IsGhost && !s.TargetStmt.IsGhost) { + var targetKind = s.TargetStmt is LoopStmt ? "loop" : "structure"; + Error(ErrorId.r_ghost_break, breakStmt, $"ghost-context {s.Kind} statement is not allowed to {s.Kind} out of non-ghost {targetKind}"); + } - break; - } - case ProduceStmt produceStmt: - { - var s = produceStmt; - var kind = produceStmt is YieldStmt ? "yield" : "return"; - if (mustBeErasable && !codeContext.IsGhost) { - Error(ErrorId.r_produce_statement_not_allowed_in_ghost, produceStmt, "{0} statement is not allowed in this context (because it is guarded by a specification-only expression)", kind); - } - if (s.HiddenUpdate != null) { - Visit(s.HiddenUpdate, mustBeErasable, proofContext); + break; } - - break; - } - case AssignSuchThatStmt thatStmt: - { - var s = thatStmt; - s.IsGhost = mustBeErasable || s.AssumeToken != null || s.Lhss.Any(SingleAssignStmt.LhsIsToGhost); - if (mustBeErasable && !codeContext.IsGhost) { - foreach (var lhs in s.Lhss) { - var gk = SingleAssignStmt.LhsIsToGhost_Which(lhs); - if (gk != SingleAssignStmt.NonGhostKind.IsGhost) { - Error(ErrorId.r_no_assign_to_var_in_ghost, lhs, "cannot assign to {0} in a ghost context", SingleAssignStmt.NonGhostKind_To_String(gk)); - } + case ProduceStmt produceStmt: { + var s = produceStmt; + var kind = produceStmt is YieldStmt ? "yield" : "return"; + if (mustBeErasable && !codeContext.IsGhost) { + Error(ErrorId.r_produce_statement_not_allowed_in_ghost, produceStmt, "{0} statement is not allowed in this context (because it is guarded by a specification-only expression)", kind); } - } else if (!mustBeErasable && s.AssumeToken == null && ExpressionTester.UsesSpecFeatures(s.Expr)) { - foreach (var lhs in s.Lhss) { - var gk = SingleAssignStmt.LhsIsToGhost_Which(lhs); - if (gk != SingleAssignStmt.NonGhostKind.IsGhost) { - Error(ErrorId.r_no_assign_ghost_to_var, lhs, "{0} cannot be assigned a value that depends on a ghost", SingleAssignStmt.NonGhostKind_To_String(gk)); - } + if (s.HiddenUpdate != null) { + Visit(s.HiddenUpdate, mustBeErasable, proofContext); } - } - break; - } - case AssignStatement statement: - { - var s = statement; - s.ResolvedStatements.ForEach(ss => Visit(ss, mustBeErasable, proofContext)); - s.IsGhost = s.ResolvedStatements.All(ss => ss.IsGhost); - break; - } - case AssignOrReturnStmt returnStmt: - { - var s = returnStmt; - s.ResolvedStatements.ForEach(ss => Visit(ss, mustBeErasable, proofContext)); - s.IsGhost = s.ResolvedStatements.All(ss => ss.IsGhost); - break; - } - case VarDeclStmt declStmt: - { - var s = declStmt; - if (mustBeErasable) { - foreach (var local in s.Locals) { - // a local variable in a specification-only context might as well be ghost - local.MakeGhost(); - } + break; } - if (s.Assign != null) { - Visit(s.Assign, mustBeErasable, proofContext); - } - s.IsGhost = (s.Assign == null || s.Assign.IsGhost) && s.Locals.All(v => v.IsGhost); - - // Check on "assumption" variables - foreach (var local in s.Locals) { - if (Attributes.Contains(local.Attributes, "assumption")) { - if (allowAssumptionVariables) { - if (!local.Type.IsBoolType) { - Error(ErrorId.r_assumption_var_must_be_bool, local.Tok, "assumption variable must be of type 'bool'"); + case AssignSuchThatStmt thatStmt: { + var s = thatStmt; + s.IsGhost = mustBeErasable || s.AssumeToken != null || s.Lhss.Any(SingleAssignStmt.LhsIsToGhost); + if (mustBeErasable && !codeContext.IsGhost) { + foreach (var lhs in s.Lhss) { + var gk = SingleAssignStmt.LhsIsToGhost_Which(lhs); + if (gk != SingleAssignStmt.NonGhostKind.IsGhost) { + Error(ErrorId.r_no_assign_to_var_in_ghost, lhs, "cannot assign to {0} in a ghost context", SingleAssignStmt.NonGhostKind_To_String(gk)); } - if (!local.IsGhost) { - Error(ErrorId.r_assumption_var_must_be_ghost, local.Tok, "assumption variable must be ghost"); + } + } else if (!mustBeErasable && s.AssumeToken == null && ExpressionTester.UsesSpecFeatures(s.Expr)) { + foreach (var lhs in s.Lhss) { + var gk = SingleAssignStmt.LhsIsToGhost_Which(lhs); + if (gk != SingleAssignStmt.NonGhostKind.IsGhost) { + Error(ErrorId.r_no_assign_ghost_to_var, lhs, "{0} cannot be assigned a value that depends on a ghost", SingleAssignStmt.NonGhostKind_To_String(gk)); } - } else { - Error(ErrorId.r_assumption_var_must_be_in_method, local.Tok, "assumption variable can only be declared in a method"); } } - } - break; - } - case VarDeclPattern pattern: - { - var s = pattern; + break; + } + case AssignStatement statement: { + var s = statement; + s.ResolvedStatements.ForEach(ss => Visit(ss, mustBeErasable, proofContext)); + s.IsGhost = s.ResolvedStatements.All(ss => ss.IsGhost); + break; + } + case AssignOrReturnStmt returnStmt: { + var s = returnStmt; + s.ResolvedStatements.ForEach(ss => Visit(ss, mustBeErasable, proofContext)); + s.IsGhost = s.ResolvedStatements.All(ss => ss.IsGhost); + break; + } + case VarDeclStmt declStmt: { + var s = declStmt; + if (mustBeErasable) { + foreach (var local in s.Locals) { + // a local variable in a specification-only context might as well be ghost + local.MakeGhost(); + } + } + if (s.Assign != null) { + Visit(s.Assign, mustBeErasable, proofContext); + } + s.IsGhost = (s.Assign == null || s.Assign.IsGhost) && s.Locals.All(v => v.IsGhost); - if (mustBeErasable) { - foreach (var local in s.LocalVars) { - local.MakeGhost(); + // Check on "assumption" variables + foreach (var local in s.Locals) { + if (Attributes.Contains(local.Attributes, "assumption")) { + if (allowAssumptionVariables) { + if (!local.Type.IsBoolType) { + Error(ErrorId.r_assumption_var_must_be_bool, local.Tok, "assumption variable must be of type 'bool'"); + } + if (!local.IsGhost) { + Error(ErrorId.r_assumption_var_must_be_ghost, local.Tok, "assumption variable must be ghost"); + } + } else { + Error(ErrorId.r_assumption_var_must_be_in_method, local.Tok, "assumption variable can only be declared in a method"); + } + } } + + break; } - if (s.HasGhostModifier || mustBeErasable) { - s.IsGhost = s.LocalVars.All(v => v.IsGhost); - } else { - var spec = ExpressionTester.UsesSpecFeatures(s.RHS); - if (spec) { + case VarDeclPattern pattern: { + var s = pattern; + + if (mustBeErasable) { foreach (var local in s.LocalVars) { local.MakeGhost(); } + } + if (s.HasGhostModifier || mustBeErasable) { + s.IsGhost = s.LocalVars.All(v => v.IsGhost); } else { - ExpressionTester.CheckIsCompilable(resolver, reporter, s.RHS, codeContext); + var spec = ExpressionTester.UsesSpecFeatures(s.RHS); + if (spec) { + foreach (var local in s.LocalVars) { + local.MakeGhost(); + } + } else { + ExpressionTester.CheckIsCompilable(resolver, reporter, s.RHS, codeContext); + } + s.IsGhost = spec; } - s.IsGhost = spec; - } - break; - } - case SingleAssignStmt assignStmt: - { - var s = assignStmt; - CheckAssignStmt(s, mustBeErasable, proofContext); - break; - } - case CallStmt callStmt: - { - var s = callStmt; - var callee = s.Method; - Contract.Assert(callee != null); // follows from the invariant of CallStmt - s.IsGhost = callee.IsGhost; - if (proofContext != null && !callee.IsLemmaLike) { - Error(ErrorId.r_no_calls_in_proof, s, $"in {proofContext}, calls are allowed only to lemmas"); - } else if (mustBeErasable) { - if (!s.IsGhost) { - Error(ErrorId.r_only_ghost_calls, s, "only ghost methods can be called from this context"); - } - } else { - int j; - if (!callee.IsGhost) { - // check in-parameters - ExpressionTester.CheckIsCompilable(resolver, reporter, s.Receiver, codeContext); - j = 0; - foreach (var e in s.Args) { - Contract.Assume(j < callee.Ins.Count); // this should have already been checked by the resolver - if (!callee.Ins[j].IsGhost) { - ExpressionTester.CheckIsCompilable(resolver, reporter, e, codeContext); + break; + } + case SingleAssignStmt assignStmt: { + var s = assignStmt; + CheckAssignStmt(s, mustBeErasable, proofContext); + break; + } + case CallStmt callStmt: { + var s = callStmt; + var callee = s.Method; + Contract.Assert(callee != null); // follows from the invariant of CallStmt + s.IsGhost = callee.IsGhost; + if (proofContext != null && !callee.IsLemmaLike) { + Error(ErrorId.r_no_calls_in_proof, s, $"in {proofContext}, calls are allowed only to lemmas"); + } else if (mustBeErasable) { + if (!s.IsGhost) { + Error(ErrorId.r_only_ghost_calls, s, "only ghost methods can be called from this context"); + } + } else { + int j; + if (!callee.IsGhost) { + // check in-parameters + ExpressionTester.CheckIsCompilable(resolver, reporter, s.Receiver, codeContext); + j = 0; + foreach (var e in s.Args) { + Contract.Assume(j < callee.Ins.Count); // this should have already been checked by the resolver + if (!callee.Ins[j].IsGhost) { + ExpressionTester.CheckIsCompilable(resolver, reporter, e, codeContext); + } + j++; } - j++; } - } - j = 0; - foreach (var e in s.Lhs) { - var resolvedLhs = e.Resolved; - if (callee.IsGhost || callee.Outs[j].IsGhost) { - // LHS must denote a ghost - if (resolvedLhs is IdentifierExpr) { - var ll = (IdentifierExpr)resolvedLhs; - if (!ll.Var.IsGhost) { - if (ll is AutoGhostIdentifierExpr && ll.Var is LocalVariable) { - // the variable was actually declared in this statement, so auto-declare it as ghost - ((LocalVariable)ll.Var).MakeGhost(); - } else { - Error(ErrorId.r_out_parameter_must_be_ghost, s, "actual out-parameter{0} is required to be a ghost variable", s.Lhs.Count == 1 ? "" : " " + j); + j = 0; + foreach (var e in s.Lhs) { + var resolvedLhs = e.Resolved; + if (callee.IsGhost || callee.Outs[j].IsGhost) { + // LHS must denote a ghost + if (resolvedLhs is IdentifierExpr) { + var ll = (IdentifierExpr)resolvedLhs; + if (!ll.Var.IsGhost) { + if (ll is AutoGhostIdentifierExpr && ll.Var is LocalVariable) { + // the variable was actually declared in this statement, so auto-declare it as ghost + ((LocalVariable)ll.Var).MakeGhost(); + } else { + Error(ErrorId.r_out_parameter_must_be_ghost, s, "actual out-parameter{0} is required to be a ghost variable", s.Lhs.Count == 1 ? "" : " " + j); + } } + } else if (resolvedLhs is MemberSelectExpr) { + var ll = (MemberSelectExpr)resolvedLhs; + if (!ll.Member.IsGhost) { + Error(ErrorId.r_out_parameter_must_be_ghost_field, s, "actual out-parameter{0} is required to be a ghost field", s.Lhs.Count == 1 ? "" : " " + j); + } + } else { + // this is an array update, and arrays are always non-ghost + Error(ErrorId.r_out_parameter_must_be_ghost, s, "actual out-parameter{0} is required to be a ghost variable", s.Lhs.Count == 1 ? "" : " " + j); } - } else if (resolvedLhs is MemberSelectExpr) { - var ll = (MemberSelectExpr)resolvedLhs; - if (!ll.Member.IsGhost) { - Error(ErrorId.r_out_parameter_must_be_ghost_field, s, "actual out-parameter{0} is required to be a ghost field", s.Lhs.Count == 1 ? "" : " " + j); - } - } else { - // this is an array update, and arrays are always non-ghost - Error(ErrorId.r_out_parameter_must_be_ghost, s, "actual out-parameter{0} is required to be a ghost variable", s.Lhs.Count == 1 ? "" : " " + j); } + j++; } - j++; } - } - break; - } - case BlockStmt blockStmt: - { - var s = blockStmt; - s.IsGhost = mustBeErasable; // set .IsGhost before descending into substatements (since substatements may do a 'break' out of this block) - if (s is DividedBlockStmt ds) { - var giv = new GhostInterestVisitor(this.codeContext, this.resolver, this.reporter, true, allowAssumptionVariables); - ds.BodyInit.ForEach(ss => giv.Visit(ss, mustBeErasable, proofContext)); - ds.BodyProper.ForEach(ss => Visit(ss, mustBeErasable, proofContext)); - } else { - s.Body.ForEach(ss => Visit(ss, mustBeErasable, proofContext)); - } - s.IsGhost = s.IsGhost || s.Body.All(ss => ss.IsGhost); // mark the block statement as ghost if all its substatements are ghost - break; - } - case IfStmt ifStmt: - { - var s = ifStmt; - s.IsGhost = mustBeErasable || (s.Guard != null && ExpressionTester.UsesSpecFeatures(s.Guard)); - if (!mustBeErasable && s.IsGhost) { - reporter.Info(MessageSource.Resolver, s.Tok, "ghost if"); - } - Visit(s.Thn, s.IsGhost, proofContext); - if (s.Els != null) { - Visit(s.Els, s.IsGhost, proofContext); - } - // if both branches were all ghost, then we can mark the enclosing statement as ghost as well - s.IsGhost = s.IsGhost || (s.Thn.IsGhost && (s.Els == null || s.Els.IsGhost)); - if (!s.IsGhost && s.Guard != null) { - // If there were features in the guard that are treated differently in ghost and non-ghost - // contexts, make sure they get treated for non-ghost use. - ExpressionTester.CheckIsCompilable(resolver, reporter, s.Guard, codeContext); + break; } + case BlockStmt blockStmt: { + var s = blockStmt; + s.IsGhost = mustBeErasable; // set .IsGhost before descending into substatements (since substatements may do a 'break' out of this block) + if (s is DividedBlockStmt ds) { + var giv = new GhostInterestVisitor(this.codeContext, this.resolver, this.reporter, true, allowAssumptionVariables); + ds.BodyInit.ForEach(ss => giv.Visit(ss, mustBeErasable, proofContext)); + ds.BodyProper.ForEach(ss => Visit(ss, mustBeErasable, proofContext)); + } else { + s.Body.ForEach(ss => Visit(ss, mustBeErasable, proofContext)); + } + s.IsGhost = s.IsGhost || s.Body.All(ss => ss.IsGhost); // mark the block statement as ghost if all its substatements are ghost + break; + } + case IfStmt ifStmt: { + var s = ifStmt; + s.IsGhost = mustBeErasable || (s.Guard != null && ExpressionTester.UsesSpecFeatures(s.Guard)); + if (!mustBeErasable && s.IsGhost) { + reporter.Info(MessageSource.Resolver, s.Tok, "ghost if"); + } + Visit(s.Thn, s.IsGhost, proofContext); + if (s.Els != null) { + Visit(s.Els, s.IsGhost, proofContext); + } + // if both branches were all ghost, then we can mark the enclosing statement as ghost as well + s.IsGhost = s.IsGhost || (s.Thn.IsGhost && (s.Els == null || s.Els.IsGhost)); + if (!s.IsGhost && s.Guard != null) { + // If there were features in the guard that are treated differently in ghost and non-ghost + // contexts, make sure they get treated for non-ghost use. + ExpressionTester.CheckIsCompilable(resolver, reporter, s.Guard, codeContext); + } - break; - } - case AlternativeStmt alternativeStmt: - { - var s = alternativeStmt; - s.IsGhost = mustBeErasable || s.Alternatives.Exists(alt => ExpressionTester.UsesSpecFeatures(alt.Guard)); - if (!mustBeErasable && s.IsGhost) { - reporter.Info(MessageSource.Resolver, s.Tok, "ghost if"); + break; } - s.Alternatives.ForEach(alt => alt.Body.ForEach(ss => Visit(ss, s.IsGhost, proofContext))); - s.IsGhost = s.IsGhost || s.Alternatives.All(alt => alt.Body.All(ss => ss.IsGhost)); - if (!s.IsGhost) { - // If there were features in the guards that are treated differently in ghost and non-ghost - // contexts, make sure they get treated for non-ghost use. - foreach (var alt in s.Alternatives) { - ExpressionTester.CheckIsCompilable(resolver, reporter, alt.Guard, codeContext); + case AlternativeStmt alternativeStmt: { + var s = alternativeStmt; + s.IsGhost = mustBeErasable || s.Alternatives.Exists(alt => ExpressionTester.UsesSpecFeatures(alt.Guard)); + if (!mustBeErasable && s.IsGhost) { + reporter.Info(MessageSource.Resolver, s.Tok, "ghost if"); + } + s.Alternatives.ForEach(alt => alt.Body.ForEach(ss => Visit(ss, s.IsGhost, proofContext))); + s.IsGhost = s.IsGhost || s.Alternatives.All(alt => alt.Body.All(ss => ss.IsGhost)); + if (!s.IsGhost) { + // If there were features in the guards that are treated differently in ghost and non-ghost + // contexts, make sure they get treated for non-ghost use. + foreach (var alt in s.Alternatives) { + ExpressionTester.CheckIsCompilable(resolver, reporter, alt.Guard, codeContext); + } } - } - break; - } - case WhileStmt whileStmt: - { - var s = whileStmt; - if (proofContext != null && s.Mod.Expressions != null && s.Mod.Expressions.Count != 0) { - Error(ErrorId.r_loop_may_not_use_modifies, s.Mod.Expressions[0].tok, $"a loop in {proofContext} is not allowed to use 'modifies' clauses"); + break; } + case WhileStmt whileStmt: { + var s = whileStmt; + if (proofContext != null && s.Mod.Expressions != null && s.Mod.Expressions.Count != 0) { + Error(ErrorId.r_loop_may_not_use_modifies, s.Mod.Expressions[0].tok, $"a loop in {proofContext} is not allowed to use 'modifies' clauses"); + } - s.IsGhost = mustBeErasable || (s.Guard != null && ExpressionTester.UsesSpecFeatures(s.Guard)); - if (!mustBeErasable && s.IsGhost) { - reporter.Info(MessageSource.Resolver, s.Tok, "ghost while"); - } - if (s.IsGhost && s.Decreases.Expressions.Exists(e => e is WildcardExpr)) { - Error(ErrorId.r_decreases_forbidden_on_ghost_loops, s, "'decreases *' is not allowed on ghost loops"); - } - if (s.IsGhost && s.Mod.Expressions != null) { - s.Mod.Expressions.ForEach(resolver.DisallowNonGhostFieldSpecifiers); - } - if (s.Body != null) { - Visit(s.Body, s.IsGhost, proofContext); - if (s.Body.IsGhost && !s.Decreases.Expressions.Exists(e => e is WildcardExpr)) { - s.IsGhost = true; + s.IsGhost = mustBeErasable || (s.Guard != null && ExpressionTester.UsesSpecFeatures(s.Guard)); + if (!mustBeErasable && s.IsGhost) { + reporter.Info(MessageSource.Resolver, s.Tok, "ghost while"); + } + if (s.IsGhost && s.Decreases.Expressions.Exists(e => e is WildcardExpr)) { + Error(ErrorId.r_decreases_forbidden_on_ghost_loops, s, "'decreases *' is not allowed on ghost loops"); + } + if (s.IsGhost && s.Mod.Expressions != null) { + s.Mod.Expressions.ForEach(resolver.DisallowNonGhostFieldSpecifiers); + } + if (s.Body != null) { + Visit(s.Body, s.IsGhost, proofContext); + if (s.Body.IsGhost && !s.Decreases.Expressions.Exists(e => e is WildcardExpr)) { + s.IsGhost = true; + } + } + if (!s.IsGhost && s.Guard != null) { + // If there were features in the guard that are treated differently in ghost and non-ghost + // contexts, make sure they get treated for non-ghost use. + ExpressionTester.CheckIsCompilable(resolver, reporter, s.Guard, codeContext); } - } - if (!s.IsGhost && s.Guard != null) { - // If there were features in the guard that are treated differently in ghost and non-ghost - // contexts, make sure they get treated for non-ghost use. - ExpressionTester.CheckIsCompilable(resolver, reporter, s.Guard, codeContext); - } - break; - } - case AlternativeLoopStmt loopStmt: - { - var s = loopStmt; - if (proofContext != null && s.Mod.Expressions != null && s.Mod.Expressions.Count != 0) { - Error(ErrorId.r_loop_in_proof_may_not_use_modifies, s.Mod.Expressions[0].tok, $"a loop in {proofContext} is not allowed to use 'modifies' clauses"); + break; } + case AlternativeLoopStmt loopStmt: { + var s = loopStmt; + if (proofContext != null && s.Mod.Expressions != null && s.Mod.Expressions.Count != 0) { + Error(ErrorId.r_loop_in_proof_may_not_use_modifies, s.Mod.Expressions[0].tok, $"a loop in {proofContext} is not allowed to use 'modifies' clauses"); + } - s.IsGhost = mustBeErasable || s.Alternatives.Exists(alt => ExpressionTester.UsesSpecFeatures(alt.Guard)); - if (!mustBeErasable && s.IsGhost) { - reporter.Info(MessageSource.Resolver, s.Tok, "ghost while"); - } - if (s.IsGhost && s.Decreases.Expressions.Exists(e => e is WildcardExpr)) { - Error(ErrorId.r_decreases_forbidden_on_ghost_loops, s, "'decreases *' is not allowed on ghost loops"); - } - if (s.IsGhost && s.Mod.Expressions != null) { - s.Mod.Expressions.ForEach(resolver.DisallowNonGhostFieldSpecifiers); - } - s.Alternatives.ForEach(alt => alt.Body.ForEach(ss => Visit(ss, s.IsGhost, proofContext))); - s.IsGhost = s.IsGhost || (!s.Decreases.Expressions.Exists(e => e is WildcardExpr) && s.Alternatives.All(alt => alt.Body.All(ss => ss.IsGhost))); - if (!s.IsGhost) { - // If there were features in the guards that are treated differently in ghost and non-ghost - // contexts, make sure they get treated for non-ghost use. - foreach (var alt in s.Alternatives) { - ExpressionTester.CheckIsCompilable(resolver, reporter, alt.Guard, codeContext); + s.IsGhost = mustBeErasable || s.Alternatives.Exists(alt => ExpressionTester.UsesSpecFeatures(alt.Guard)); + if (!mustBeErasable && s.IsGhost) { + reporter.Info(MessageSource.Resolver, s.Tok, "ghost while"); + } + if (s.IsGhost && s.Decreases.Expressions.Exists(e => e is WildcardExpr)) { + Error(ErrorId.r_decreases_forbidden_on_ghost_loops, s, "'decreases *' is not allowed on ghost loops"); + } + if (s.IsGhost && s.Mod.Expressions != null) { + s.Mod.Expressions.ForEach(resolver.DisallowNonGhostFieldSpecifiers); + } + s.Alternatives.ForEach(alt => alt.Body.ForEach(ss => Visit(ss, s.IsGhost, proofContext))); + s.IsGhost = s.IsGhost || (!s.Decreases.Expressions.Exists(e => e is WildcardExpr) && s.Alternatives.All(alt => alt.Body.All(ss => ss.IsGhost))); + if (!s.IsGhost) { + // If there were features in the guards that are treated differently in ghost and non-ghost + // contexts, make sure they get treated for non-ghost use. + foreach (var alt in s.Alternatives) { + ExpressionTester.CheckIsCompilable(resolver, reporter, alt.Guard, codeContext); + } } - } - break; - } - case ForLoopStmt loopStmt: - { - var s = loopStmt; - if (proofContext != null && s.Mod.Expressions != null && s.Mod.Expressions.Count != 0) { - Error(ErrorId.r_loop_in_proof_may_not_use_modifies, s.Mod.Expressions[0].tok, $"a loop in {proofContext} is not allowed to use 'modifies' clauses"); + break; } + case ForLoopStmt loopStmt: { + var s = loopStmt; + if (proofContext != null && s.Mod.Expressions != null && s.Mod.Expressions.Count != 0) { + Error(ErrorId.r_loop_in_proof_may_not_use_modifies, s.Mod.Expressions[0].tok, $"a loop in {proofContext} is not allowed to use 'modifies' clauses"); + } - s.IsGhost = mustBeErasable || ExpressionTester.UsesSpecFeatures(s.Start) || (s.End != null && ExpressionTester.UsesSpecFeatures(s.End)); - if (!mustBeErasable && s.IsGhost) { - reporter.Info(MessageSource.Resolver, s.Tok, "ghost for-loop"); - } - if (s.IsGhost) { - if (s.Decreases.Expressions.Exists(e => e is WildcardExpr)) { - Error(ErrorId.r_decreases_forbidden_on_ghost_loops, s, "'decreases *' is not allowed on ghost loops"); - } else if (s.End == null && s.Decreases.Expressions.Count == 0) { - Error(ErrorId.r_ghost_loop_must_terminate, s, "a ghost loop must be terminating; make the end-expression specific or add a 'decreases' clause"); + s.IsGhost = mustBeErasable || ExpressionTester.UsesSpecFeatures(s.Start) || (s.End != null && ExpressionTester.UsesSpecFeatures(s.End)); + if (!mustBeErasable && s.IsGhost) { + reporter.Info(MessageSource.Resolver, s.Tok, "ghost for-loop"); } - } - if (s.IsGhost && s.Mod.Expressions != null) { - s.Mod.Expressions.ForEach(resolver.DisallowNonGhostFieldSpecifiers); - } - if (s.Body != null) { - Visit(s.Body, s.IsGhost, proofContext); - if (s.Body.IsGhost) { - s.IsGhost = true; + if (s.IsGhost) { + if (s.Decreases.Expressions.Exists(e => e is WildcardExpr)) { + Error(ErrorId.r_decreases_forbidden_on_ghost_loops, s, "'decreases *' is not allowed on ghost loops"); + } else if (s.End == null && s.Decreases.Expressions.Count == 0) { + Error(ErrorId.r_ghost_loop_must_terminate, s, "a ghost loop must be terminating; make the end-expression specific or add a 'decreases' clause"); + } } - } - if (!s.IsGhost) { - // If there were features in the bounds that are treated differently in ghost and non-ghost - // contexts, make sure they get treated for non-ghost use. - ExpressionTester.CheckIsCompilable(resolver, reporter, s.Start, codeContext); - if (s.End != null) { - ExpressionTester.CheckIsCompilable(resolver, reporter, s.End, codeContext); + if (s.IsGhost && s.Mod.Expressions != null) { + s.Mod.Expressions.ForEach(resolver.DisallowNonGhostFieldSpecifiers); + } + if (s.Body != null) { + Visit(s.Body, s.IsGhost, proofContext); + if (s.Body.IsGhost) { + s.IsGhost = true; + } + } + if (!s.IsGhost) { + // If there were features in the bounds that are treated differently in ghost and non-ghost + // contexts, make sure they get treated for non-ghost use. + ExpressionTester.CheckIsCompilable(resolver, reporter, s.Start, codeContext); + if (s.End != null) { + ExpressionTester.CheckIsCompilable(resolver, reporter, s.End, codeContext); + } } - } - break; - } - case ForallStmt forallStmt: - { - var s = forallStmt; - s.IsGhost = mustBeErasable || s.Kind != ForallStmt.BodyKind.Assign || ExpressionTester.UsesSpecFeatures(s.Range); - if (proofContext != null && s.Kind == ForallStmt.BodyKind.Assign) { - Error(ErrorId.r_no_aggregate_heap_update_in_proof, s, $"{proofContext} is not allowed to perform an aggregate heap update"); - } else if (s.Body != null) { - Visit(s.Body, s.IsGhost, s.Kind == ForallStmt.BodyKind.Assign ? proofContext : "a forall statement"); + break; } - s.IsGhost = s.IsGhost || s.Body == null || s.Body.IsGhost; - - if (!s.IsGhost) { - // Since we've determined this is a non-ghost forall statement, we now check that the bound variables have compilable bounds. - var uncompilableBoundVars = s.UncompilableBoundVars(); - if (uncompilableBoundVars.Count != 0) { - foreach (var bv in uncompilableBoundVars) { - Error(ErrorId.r_unknown_bounds_for_forall, s, "forall statements in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for '{0}'", bv.Name); + case ForallStmt forallStmt: { + var s = forallStmt; + s.IsGhost = mustBeErasable || s.Kind != ForallStmt.BodyKind.Assign || ExpressionTester.UsesSpecFeatures(s.Range); + if (proofContext != null && s.Kind == ForallStmt.BodyKind.Assign) { + Error(ErrorId.r_no_aggregate_heap_update_in_proof, s, $"{proofContext} is not allowed to perform an aggregate heap update"); + } else if (s.Body != null) { + Visit(s.Body, s.IsGhost, s.Kind == ForallStmt.BodyKind.Assign ? proofContext : "a forall statement"); + } + s.IsGhost = s.IsGhost || s.Body == null || s.Body.IsGhost; + + if (!s.IsGhost) { + // Since we've determined this is a non-ghost forall statement, we now check that the bound variables have compilable bounds. + var uncompilableBoundVars = s.UncompilableBoundVars(); + if (uncompilableBoundVars.Count != 0) { + foreach (var bv in uncompilableBoundVars) { + Error(ErrorId.r_unknown_bounds_for_forall, s, "forall statements in non-ghost contexts must be compilable, but Dafny's heuristics can't figure out how to produce or compile a bounded set of values for '{0}'", bv.Name); + } } + + // If there were features in the range that are treated differently in ghost and non-ghost + // contexts, make sure they get treated for non-ghost use. + ExpressionTester.CheckIsCompilable(resolver, reporter, s.Range, codeContext); } - // If there were features in the range that are treated differently in ghost and non-ghost - // contexts, make sure they get treated for non-ghost use. - ExpressionTester.CheckIsCompilable(resolver, reporter, s.Range, codeContext); + break; } + case ModifyStmt modifyStmt: { + var s = modifyStmt; + if (proofContext != null) { + Error(ErrorId.r_modify_forbidden_in_proof, modifyStmt, $"a modify statement is not allowed in {proofContext}"); + } - break; - } - case ModifyStmt modifyStmt: - { - var s = modifyStmt; - if (proofContext != null) { - Error(ErrorId.r_modify_forbidden_in_proof, modifyStmt, $"a modify statement is not allowed in {proofContext}"); - } + s.IsGhost = mustBeErasable; + if (s.IsGhost) { + s.Mod.Expressions.ForEach(resolver.DisallowNonGhostFieldSpecifiers); + } + if (s.Body != null) { + Visit(s.Body, mustBeErasable, proofContext); + } - s.IsGhost = mustBeErasable; - if (s.IsGhost) { - s.Mod.Expressions.ForEach(resolver.DisallowNonGhostFieldSpecifiers); - } - if (s.Body != null) { - Visit(s.Body, mustBeErasable, proofContext); + break; } + case CalcStmt calcStmt: { + var s = calcStmt; + s.IsGhost = true; + foreach (var h in s.Hints) { + Visit(h, true, "a hint"); + } - break; - } - case CalcStmt calcStmt: - { - var s = calcStmt; - s.IsGhost = true; - foreach (var h in s.Hints) { - Visit(h, true, "a hint"); + break; } + case NestedMatchStmt nestedMatchStmt: { + var hasGhostPattern = nestedMatchStmt.Cases. + SelectMany(caze => caze.Pat.DescendantsAndSelf) + .OfType().Any(idPattern => idPattern.Ctor != null && idPattern.Ctor.IsGhost); + nestedMatchStmt.IsGhost = mustBeErasable || ExpressionTester.UsesSpecFeatures(nestedMatchStmt.Source) || hasGhostPattern; - break; - } - case NestedMatchStmt nestedMatchStmt: - { - var hasGhostPattern = nestedMatchStmt.Cases. - SelectMany(caze => caze.Pat.DescendantsAndSelf) - .OfType().Any(idPattern => idPattern.Ctor != null && idPattern.Ctor.IsGhost); - nestedMatchStmt.IsGhost = mustBeErasable || ExpressionTester.UsesSpecFeatures(nestedMatchStmt.Source) || hasGhostPattern; - - foreach (var kase in nestedMatchStmt.Cases) { - ExpressionTester.MakeGhostAsNeeded(kase.Pat, nestedMatchStmt.IsGhost); - } + foreach (var kase in nestedMatchStmt.Cases) { + ExpressionTester.MakeGhostAsNeeded(kase.Pat, nestedMatchStmt.IsGhost); + } - if (!mustBeErasable && nestedMatchStmt.IsGhost) { - reporter.Info(MessageSource.Resolver, nestedMatchStmt.Tok, "ghost match"); - } - nestedMatchStmt.Cases.ForEach(kase => kase.Body.ForEach(ss => Visit(ss, nestedMatchStmt.IsGhost, proofContext))); - nestedMatchStmt.IsGhost = nestedMatchStmt.IsGhost || nestedMatchStmt.Cases.All(kase => kase.Body.All(ss => ss.IsGhost)); - if (!nestedMatchStmt.IsGhost) { - // If there were features in the source expression that are treated differently in ghost and non-ghost - // contexts, make sure they get treated for non-ghost use. - ExpressionTester.CheckIsCompilable(resolver, reporter, nestedMatchStmt.Source, codeContext); - } + if (!mustBeErasable && nestedMatchStmt.IsGhost) { + reporter.Info(MessageSource.Resolver, nestedMatchStmt.Tok, "ghost match"); + } + nestedMatchStmt.Cases.ForEach(kase => kase.Body.ForEach(ss => Visit(ss, nestedMatchStmt.IsGhost, proofContext))); + nestedMatchStmt.IsGhost = nestedMatchStmt.IsGhost || nestedMatchStmt.Cases.All(kase => kase.Body.All(ss => ss.IsGhost)); + if (!nestedMatchStmt.IsGhost) { + // If there were features in the source expression that are treated differently in ghost and non-ghost + // contexts, make sure they get treated for non-ghost use. + ExpressionTester.CheckIsCompilable(resolver, reporter, nestedMatchStmt.Source, codeContext); + } - break; - } - case MatchStmt matchStmt: - { - var s = matchStmt; - s.IsGhost = mustBeErasable || ExpressionTester.UsesSpecFeatures(s.Source) || ExpressionTester.FirstCaseThatDependsOnGhostCtor(s.Cases) != null; - if (!mustBeErasable && s.IsGhost) { - reporter.Info(MessageSource.Resolver, s.Tok, "ghost match"); - } - s.Cases.ForEach(kase => kase.Body.ForEach(ss => Visit(ss, s.IsGhost, proofContext))); - s.IsGhost = s.IsGhost || s.Cases.All(kase => kase.Body.All(ss => ss.IsGhost)); - if (!s.IsGhost) { - // If there were features in the source expression that are treated differently in ghost and non-ghost - // contexts, make sure they get treated for non-ghost use. - ExpressionTester.CheckIsCompilable(resolver, reporter, s.Source, codeContext); + break; } + case MatchStmt matchStmt: { + var s = matchStmt; + s.IsGhost = mustBeErasable || ExpressionTester.UsesSpecFeatures(s.Source) || ExpressionTester.FirstCaseThatDependsOnGhostCtor(s.Cases) != null; + if (!mustBeErasable && s.IsGhost) { + reporter.Info(MessageSource.Resolver, s.Tok, "ghost match"); + } + s.Cases.ForEach(kase => kase.Body.ForEach(ss => Visit(ss, s.IsGhost, proofContext))); + s.IsGhost = s.IsGhost || s.Cases.All(kase => kase.Body.All(ss => ss.IsGhost)); + if (!s.IsGhost) { + // If there were features in the source expression that are treated differently in ghost and non-ghost + // contexts, make sure they get treated for non-ghost use. + ExpressionTester.CheckIsCompilable(resolver, reporter, s.Source, codeContext); + } - break; - } - case SkeletonStatement statement: - { - var s = statement; - s.IsGhost = mustBeErasable; - if (s.S != null) { - Visit(s.S, mustBeErasable, proofContext); - s.IsGhost = s.IsGhost || s.S.IsGhost; + break; } + case SkeletonStatement statement: { + var s = statement; + s.IsGhost = mustBeErasable; + if (s.S != null) { + Visit(s.S, mustBeErasable, proofContext); + s.IsGhost = s.IsGhost || s.S.IsGhost; + } - break; - } + break; + } case BlockByProofStmt blockByProofStmt: // TODO move to the BlockByProofStmt class diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs index cfdca247fb8..4a7414ac735 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs @@ -47,7 +47,7 @@ bool NeedsDefiniteAssignmentTracker(bool isGhost, Type type, bool isField) { return null; } - if (DefiniteAssignmentTrackers.TryGetValue(p.UniqueName, out var result)) { + if (DefiniteAssignmentTrackers.TryGetValue(p.Tok, out var result)) { return result; } @@ -60,7 +60,7 @@ bool NeedsDefiniteAssignmentTracker(bool isGhost, Type type, bool isField) { tracker = localVariables.GetOrAdd(tracker); var ie = new Bpl.IdentifierExpr(p.Tok, tracker); - DefiniteAssignmentTrackers.Add(p.UniqueName, ie); + DefiniteAssignmentTrackers.Add(p.Tok, ie); return ie; } @@ -69,7 +69,7 @@ void AddExistingDefiniteAssignmentTracker(IVariable p, bool forceGhostVar) { if (NeedsDefiniteAssignmentTracker(p.IsGhost || forceGhostVar, p.Type, false)) { var ie = new Bpl.IdentifierExpr(p.Tok, DefassPrefix + p.UniqueName, Bpl.Type.Bool); - DefiniteAssignmentTrackers.Add(p.UniqueName, ie); + DefiniteAssignmentTrackers.Add(p.Tok, ie); } } @@ -86,32 +86,21 @@ void AddDefiniteAssignmentTrackerSurrogate(Field field, TopLevelDeclWithMembers var nm = SurrogateName(field); var tracker = localVariables.GetOrAdd(new Bpl.LocalVariable(field.tok, new Bpl.TypedIdent(field.tok, DefassPrefix + nm, Bpl.Type.Bool))); var ie = new Bpl.IdentifierExpr(field.tok, tracker); - DefiniteAssignmentTrackers.Add(nm, ie); - } - - void RemoveDefiniteAssignmentTracker(IVariable p) { - Contract.Requires(p != null); - DefiniteAssignmentTrackers.Remove(p.UniqueName); - } - - void RemoveDefiniteAssignmentTrackerSurrogate(Field field) { - Contract.Requires(field != null); - DefiniteAssignmentTrackers.Remove(SurrogateName(field)); + DefiniteAssignmentTrackers.Add(field.tok, ie); } void MarkDefiniteAssignmentTracker(IdentifierExpr expr, BoogieStmtListBuilder builder) { Contract.Requires(expr != null); Contract.Requires(builder != null); - MarkDefiniteAssignmentTracker(expr.tok, expr.Var.UniqueName, builder); + MarkDefiniteAssignmentTracker(expr.tok, expr.Var.Tok, builder); } - void MarkDefiniteAssignmentTracker(IToken tok, string name, BoogieStmtListBuilder builder) { + void MarkDefiniteAssignmentTracker(IToken tok, Bpl.IToken variableToken, BoogieStmtListBuilder builder) { Contract.Requires(tok != null); - Contract.Requires(name != null); Contract.Requires(builder != null); Bpl.IdentifierExpr ie; - if (DefiniteAssignmentTrackers.TryGetValue(name, out ie)) { + if (DefiniteAssignmentTrackers.TryGetValue(variableToken, out ie)) { builder.Add(Bpl.Cmd.SimpleAssign(tok, ie, Bpl.Expr.True)); } } @@ -141,7 +130,7 @@ void CheckDefiniteAssignment(IdentifierExpr expr, BoogieStmtListBuilder builder) Contract.Requires(builder != null); Bpl.IdentifierExpr ie; - if (DefiniteAssignmentTrackers.TryGetValue(expr.Var.UniqueName, out ie)) { + if (DefiniteAssignmentTrackers.TryGetValue(expr.Var.Tok, out ie)) { builder.Add(Assert(GetToken(expr), ie, new DefiniteAssignment("variable", expr.Var.Name, "here"), builder.Context)); } @@ -152,7 +141,7 @@ void CheckDefiniteAssignment(IdentifierExpr expr, BoogieStmtListBuilder builder) /// Bpl.IdentifierExpr /*?*/ GetDefiniteAssignmentTracker(IVariable var) { Bpl.IdentifierExpr ie; - if (DefiniteAssignmentTrackers.TryGetValue(var.UniqueName, out ie)) { + if (DefiniteAssignmentTrackers.TryGetValue(var.Tok, out ie)) { return ie; } @@ -164,9 +153,8 @@ void CheckDefiniteAssignmentSurrogate(IToken tok, Field field, bool atNew, Boogi Contract.Requires(field != null); Contract.Requires(builder != null); - var nm = SurrogateName(field); Bpl.IdentifierExpr ie; - if (DefiniteAssignmentTrackers.TryGetValue(nm, out ie)) { + if (DefiniteAssignmentTrackers.TryGetValue(field.Tok, out ie)) { var desc = new DefiniteAssignment( "field", field.Name, atNew ? "at this point in the constructor body" : "here"); builder.Add(Assert(tok, ie, desc, builder.Context)); @@ -209,7 +197,7 @@ void CheckDefiniteAssignmentReturn(IToken tok, Formal p, BoogieStmtListBuilder b Contract.Requires(builder != null); Bpl.IdentifierExpr ie; - if (DefiniteAssignmentTrackers.TryGetValue(p.UniqueName, out ie)) { + if (DefiniteAssignmentTrackers.TryGetValue(p.Tok, out ie)) { var desc = new DefiniteAssignment("out-parameter", p.Name, "at this return point"); builder.Add(Assert(tok, ie, desc, builder.Context)); } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index 10d78a37498..aba16314e76 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -833,22 +833,22 @@ public Boogie.Expr TrExpr(Expression expr) { // by $IsAllocBox. return BoogieGenerator.MkIsAllocBox(BoxIfNecessary(e.E.tok, TrExpr(e.E), e.E.Type), e.E.Type, HeapExpr); case UnaryOpExpr.ResolvedOpcode.Assigned: - string name = null; + IToken token = null; switch (e.E.Resolved) { case IdentifierExpr ie: - name = ie.Var.UniqueName; + token = ie.Var.Tok; break; case MemberSelectExpr mse: if (BoogieGenerator.inBodyInitContext && Expression.AsThis(mse.Obj) != null) { - name = BoogieGenerator.SurrogateName(mse.Member as Field); + token = mse.Member.Tok; } break; } - if (name == null) { + if (token == null) { return Expr.True; } - BoogieGenerator.DefiniteAssignmentTrackers.TryGetValue(name, out var defass); + BoogieGenerator.DefiniteAssignmentTrackers.TryGetValue(token, out var defass); return defass; default: Contract.Assert(false); throw new cce.UnreachableException(); // unexpected unary expression diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 9b432369d64..c89fb581257 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -806,8 +806,6 @@ private StmtList TrMethodBody(Method m, BoogieStmtListBuilder builder, Variables AssumeCanCallForByMethodDecl(m, builder); } var stmts = builder.Collect(m.Body.RangeToken.StartToken); // EndToken might make more sense, but it requires updating most of the regression tests. - // tear down definite-assignment trackers - m.Outs.ForEach(RemoveDefiniteAssignmentTracker); return stmts; } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index 3d57ad1cb17..b764607cedd 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -1758,7 +1758,7 @@ Bpl.Expr InSeqRange(IToken tok, Bpl.Expr index, Type indexType, Bpl.Expr seq, bo Bpl.LocalVariable yieldCountVariable = null; // non-null when an iterator body is being translated bool inBodyInitContext = false; // true during the translation of the .BodyInit portion of a divided constructor body - public Dictionary DefiniteAssignmentTrackers { get; } = new(); + public Dictionary DefiniteAssignmentTrackers { get; } = new(); Func assertionOnlyFilter = null; // generate assume statements instead of assert statements if not targeted by {:only} public enum StmtType { NONE, ASSERT, ASSUME }; @@ -2913,8 +2913,6 @@ private void GenerateMethodParametersChoose(IToken tok, IMethodCodeContext m, Me } outParams.GetOrAdd(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.AssignUniqueName(currentDeclaration.IdGenerator), varType, wh), false)); } - // tear down definite-assignment trackers - m.Outs.ForEach(RemoveDefiniteAssignmentTracker); if (kind == MethodTranslationKind.Implementation) { outParams.GetOrAdd(new Bpl.Formal(tok, new Bpl.TypedIdent(tok, "$_reverifyPost", Bpl.Type.Bool), false)); diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs index 0d78e61af61..b76f718500b 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs @@ -279,7 +279,7 @@ void ProcessLhss(List lhss, bool rhsCanAffectPreviouslyKnownExpressi } if (!origRhsIsHavoc || field.Type.HavocCountsAsDefiniteAssignment(field.IsGhost)) { - MarkDefiniteAssignmentTracker(lhs.tok, nm, bldr); + MarkDefiniteAssignmentTracker(lhs.tok, field.Tok, bldr); } }); } else { diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs index 18d6a2aaa06..858cd711f1e 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs @@ -203,7 +203,7 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, var daTrackersMonotonicity = new List>(); var existingLocals = locals.Values.ToList(); foreach (var local in existingLocals) { - if (!DefiniteAssignmentTrackers.TryGetValue(local.Name, out var dat)) { + if (!DefiniteAssignmentTrackers.TryGetValue(local.tok, out var dat)) { continue; } diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs index f42545d6455..a142f2fb2ac 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs @@ -269,7 +269,6 @@ public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, // The "new;" translates into an allocation of "this" AddComment(builder, stmt, "new;"); fields.ForEach(f => CheckDefiniteAssignmentSurrogate(s.SeparatorTok ?? s.RangeToken.EndToken, f, true, builder)); - fields.ForEach(RemoveDefiniteAssignmentTrackerSurrogate); var th = new ThisExpr(cl); var bplThis = (Bpl.IdentifierExpr)etran.TrExpr(th); SelectAllocateObject(tok, bplThis, th.Type, false, builder, etran); From 2eea9e3df8ed27e77dd5c999d9b7fccc079a66d4 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 08:49:36 +0200 Subject: [PATCH 25/47] Move to symbols for mapping to tracking variables --- .../BoogieGenerator.DefiniteAssignment.cs | 22 ++-- .../BoogieGenerator.ExpressionTranslator.cs | 10 +- Source/DafnyCore/Verifier/BoogieGenerator.cs | 2 +- .../BoogieGenerator.TrAssignment.cs | 2 +- .../Statements/BoogieGenerator.TrLoop.cs | 108 +++++++++--------- 5 files changed, 73 insertions(+), 71 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs index 4a7414ac735..97b0bfd1e12 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs @@ -47,7 +47,7 @@ bool NeedsDefiniteAssignmentTracker(bool isGhost, Type type, bool isField) { return null; } - if (DefiniteAssignmentTrackers.TryGetValue(p.Tok, out var result)) { + if (DefiniteAssignmentTrackers.TryGetValue(p, out var result)) { return result; } @@ -60,7 +60,7 @@ bool NeedsDefiniteAssignmentTracker(bool isGhost, Type type, bool isField) { tracker = localVariables.GetOrAdd(tracker); var ie = new Bpl.IdentifierExpr(p.Tok, tracker); - DefiniteAssignmentTrackers.Add(p.Tok, ie); + DefiniteAssignmentTrackers.Add(p, ie); return ie; } @@ -69,7 +69,7 @@ void AddExistingDefiniteAssignmentTracker(IVariable p, bool forceGhostVar) { if (NeedsDefiniteAssignmentTracker(p.IsGhost || forceGhostVar, p.Type, false)) { var ie = new Bpl.IdentifierExpr(p.Tok, DefassPrefix + p.UniqueName, Bpl.Type.Bool); - DefiniteAssignmentTrackers.Add(p.Tok, ie); + DefiniteAssignmentTrackers.Add(p, ie); } } @@ -86,21 +86,21 @@ void AddDefiniteAssignmentTrackerSurrogate(Field field, TopLevelDeclWithMembers var nm = SurrogateName(field); var tracker = localVariables.GetOrAdd(new Bpl.LocalVariable(field.tok, new Bpl.TypedIdent(field.tok, DefassPrefix + nm, Bpl.Type.Bool))); var ie = new Bpl.IdentifierExpr(field.tok, tracker); - DefiniteAssignmentTrackers.Add(field.tok, ie); + DefiniteAssignmentTrackers.Add(field, ie); } void MarkDefiniteAssignmentTracker(IdentifierExpr expr, BoogieStmtListBuilder builder) { Contract.Requires(expr != null); Contract.Requires(builder != null); - MarkDefiniteAssignmentTracker(expr.tok, expr.Var.Tok, builder); + MarkDefiniteAssignmentTracker(expr.tok, expr.Var, builder); } - void MarkDefiniteAssignmentTracker(IToken tok, Bpl.IToken variableToken, BoogieStmtListBuilder builder) { + void MarkDefiniteAssignmentTracker(IToken tok, ISymbol variable, BoogieStmtListBuilder builder) { Contract.Requires(tok != null); Contract.Requires(builder != null); Bpl.IdentifierExpr ie; - if (DefiniteAssignmentTrackers.TryGetValue(variableToken, out ie)) { + if (DefiniteAssignmentTrackers.TryGetValue(variable, out ie)) { builder.Add(Bpl.Cmd.SimpleAssign(tok, ie, Bpl.Expr.True)); } } @@ -130,7 +130,7 @@ void CheckDefiniteAssignment(IdentifierExpr expr, BoogieStmtListBuilder builder) Contract.Requires(builder != null); Bpl.IdentifierExpr ie; - if (DefiniteAssignmentTrackers.TryGetValue(expr.Var.Tok, out ie)) { + if (DefiniteAssignmentTrackers.TryGetValue(expr.Var, out ie)) { builder.Add(Assert(GetToken(expr), ie, new DefiniteAssignment("variable", expr.Var.Name, "here"), builder.Context)); } @@ -141,7 +141,7 @@ void CheckDefiniteAssignment(IdentifierExpr expr, BoogieStmtListBuilder builder) /// Bpl.IdentifierExpr /*?*/ GetDefiniteAssignmentTracker(IVariable var) { Bpl.IdentifierExpr ie; - if (DefiniteAssignmentTrackers.TryGetValue(var.Tok, out ie)) { + if (DefiniteAssignmentTrackers.TryGetValue(var, out ie)) { return ie; } @@ -154,7 +154,7 @@ void CheckDefiniteAssignmentSurrogate(IToken tok, Field field, bool atNew, Boogi Contract.Requires(builder != null); Bpl.IdentifierExpr ie; - if (DefiniteAssignmentTrackers.TryGetValue(field.Tok, out ie)) { + if (DefiniteAssignmentTrackers.TryGetValue(field, out ie)) { var desc = new DefiniteAssignment( "field", field.Name, atNew ? "at this point in the constructor body" : "here"); builder.Add(Assert(tok, ie, desc, builder.Context)); @@ -197,7 +197,7 @@ void CheckDefiniteAssignmentReturn(IToken tok, Formal p, BoogieStmtListBuilder b Contract.Requires(builder != null); Bpl.IdentifierExpr ie; - if (DefiniteAssignmentTrackers.TryGetValue(p.Tok, out ie)) { + if (DefiniteAssignmentTrackers.TryGetValue(p, out ie)) { var desc = new DefiniteAssignment("out-parameter", p.Name, "at this return point"); builder.Add(Assert(tok, ie, desc, builder.Context)); } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index aba16314e76..84d5fa60f0c 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -833,22 +833,22 @@ public Boogie.Expr TrExpr(Expression expr) { // by $IsAllocBox. return BoogieGenerator.MkIsAllocBox(BoxIfNecessary(e.E.tok, TrExpr(e.E), e.E.Type), e.E.Type, HeapExpr); case UnaryOpExpr.ResolvedOpcode.Assigned: - IToken token = null; + ISymbol variable = null; switch (e.E.Resolved) { case IdentifierExpr ie: - token = ie.Var.Tok; + variable = ie.Var; break; case MemberSelectExpr mse: if (BoogieGenerator.inBodyInitContext && Expression.AsThis(mse.Obj) != null) { - token = mse.Member.Tok; + variable = mse.Member; } break; } - if (token == null) { + if (variable == null) { return Expr.True; } - BoogieGenerator.DefiniteAssignmentTrackers.TryGetValue(token, out var defass); + BoogieGenerator.DefiniteAssignmentTrackers.TryGetValue(variable, out var defass); return defass; default: Contract.Assert(false); throw new cce.UnreachableException(); // unexpected unary expression diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index b764607cedd..59b24e765cf 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -1758,7 +1758,7 @@ Bpl.Expr InSeqRange(IToken tok, Bpl.Expr index, Type indexType, Bpl.Expr seq, bo Bpl.LocalVariable yieldCountVariable = null; // non-null when an iterator body is being translated bool inBodyInitContext = false; // true during the translation of the .BodyInit portion of a divided constructor body - public Dictionary DefiniteAssignmentTrackers { get; } = new(); + public Dictionary DefiniteAssignmentTrackers { get; } = new(); Func assertionOnlyFilter = null; // generate assume statements instead of assert statements if not targeted by {:only} public enum StmtType { NONE, ASSERT, ASSUME }; diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs index b76f718500b..7b512f5119f 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs @@ -279,7 +279,7 @@ void ProcessLhss(List lhss, bool rhsCanAffectPreviouslyKnownExpressi } if (!origRhsIsHavoc || field.Type.HavocCountsAsDefiniteAssignment(field.IsGhost)) { - MarkDefiniteAssignmentTracker(lhs.tok, field.Tok, bldr); + MarkDefiniteAssignmentTracker(lhs.tok, field, bldr); } }); } else { diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs index 858cd711f1e..2a9bf8fd6d4 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs @@ -165,53 +165,55 @@ private void TrWhileStmt(WhileStmt stmt, BoogieStmtListBuilder builder, Variable this.fuelContext = FuelSetting.PopFuelContext(); } - void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, + void TrLoop(LoopStmt loop, Expression Guard, BodyTranslator/*?*/ bodyTr, BoogieStmtListBuilder builder, Variables locals, ExpressionTranslator etran, Bpl.Expr freeInvariant = null, bool includeTerminationCheck = true) { - Contract.Requires(s != null); + Contract.Requires(loop != null); Contract.Requires(builder != null); Contract.Requires(locals != null); Contract.Requires(etran != null); - s.ScopeDepth = builder.Context.ScopeDepth; + loop.ScopeDepth = builder.Context.ScopeDepth; var suffix = CurrentIdGenerator.FreshId("loop#"); - var theDecreases = s.Decreases.Expressions; + var theDecreases = loop.Decreases.Expressions; var preloopheap = "$PreLoopHeap$" + suffix; - var preLoopHeapVar = locals.GetOrCreate(preloopheap, () => new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, preloopheap, predef.HeapType))); - Bpl.IdentifierExpr preLoopHeap = new Bpl.IdentifierExpr(s.Tok, preLoopHeapVar); + var preLoopHeapVar = locals.GetOrCreate(preloopheap, () => new Bpl.LocalVariable(loop.Tok, new Bpl.TypedIdent(loop.Tok, preloopheap, predef.HeapType))); + Bpl.IdentifierExpr preLoopHeap = new Bpl.IdentifierExpr(loop.Tok, preLoopHeapVar); ExpressionTranslator etranPreLoop = new ExpressionTranslator(this, predef, preLoopHeap, etran.scope); ExpressionTranslator updatedFrameEtran; string loopFrameName = FrameVariablePrefix + suffix; - if (s.Mod.Expressions != null) { + if (loop.Mod.Expressions != null) { updatedFrameEtran = etran.WithModifiesFrame(loopFrameName); } else { updatedFrameEtran = etran; } - if (s.Mod.Expressions != null) { // check well-formedness and that the modifies is a subset - CheckFrameWellFormed(new WFOptions(), s.Mod.Expressions, locals, builder, etran); - var desc = new ModifyFrameSubset("loop modifies clause", s.Mod.Expressions, GetContextModifiesFrames()); - CheckFrameSubset(s.Tok, s.Mod.Expressions, null, null, etran, etran.ModifiesFrame(s.Tok), builder, desc, null); - DefineFrame(s.Tok, etran.ModifiesFrame(s.Tok), s.Mod.Expressions, builder, locals, loopFrameName); + if (loop.Mod.Expressions != null) { // check well-formedness and that the modifies is a subset + CheckFrameWellFormed(new WFOptions(), loop.Mod.Expressions, locals, builder, etran); + var desc = new ModifyFrameSubset("loop modifies clause", loop.Mod.Expressions, GetContextModifiesFrames()); + CheckFrameSubset(loop.Tok, loop.Mod.Expressions, null, null, etran, etran.ModifiesFrame(loop.Tok), builder, desc, null); + DefineFrame(loop.Tok, etran.ModifiesFrame(loop.Tok), loop.Mod.Expressions, builder, locals, loopFrameName); } - builder.Add(Bpl.Cmd.SimpleAssign(s.Tok, preLoopHeap, etran.HeapExpr)); + builder.Add(Bpl.Cmd.SimpleAssign(loop.Tok, preLoopHeap, etran.HeapExpr)); + var assignedVariables = loop.DescendantsAndSelf. + SelectMany(s => s.GetAssignedLocals()).Select(ie => ie.Var) + .ToHashSet(); var daTrackersMonotonicity = new List>(); - var existingLocals = locals.Values.ToList(); - foreach (var local in existingLocals) { - if (!DefiniteAssignmentTrackers.TryGetValue(local.tok, out var dat)) { + foreach (var local in assignedVariables) { + if (!DefiniteAssignmentTrackers.TryGetValue(local, out var dat)) { continue; } var name = "preLoop$" + suffix + "$" + dat.Name; var preLoopDat = locals.GetOrCreate(name, () => new Bpl.LocalVariable(dat.tok, new Bpl.TypedIdent(dat.tok, name, dat.Type))); - var ie = new Bpl.IdentifierExpr(s.Tok, preLoopDat); + var ie = new Bpl.IdentifierExpr(loop.Tok, preLoopDat); daTrackersMonotonicity.Add(new Tuple(ie, dat)); - builder.Add(Cmd.SimpleAssign(s.Tok, ie, dat)); + builder.Add(Cmd.SimpleAssign(loop.Tok, ie, dat)); } List initDecr = null; @@ -221,17 +223,17 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, // The variable w is used to coordinate the definedness checking of the loop invariant. // It is also used for body-less loops to turn off invariant checking after the generated body. - var wVar = locals.GetOrAdd(new Bpl.LocalVariable(s.Tok, new Bpl.TypedIdent(s.Tok, "$w$" + suffix, Bpl.Type.Bool))); - Bpl.IdentifierExpr w = new Bpl.IdentifierExpr(s.Tok, wVar); + var wVar = locals.GetOrAdd(new Bpl.LocalVariable(loop.Tok, new Bpl.TypedIdent(loop.Tok, "$w$" + suffix, Bpl.Type.Bool))); + Bpl.IdentifierExpr w = new Bpl.IdentifierExpr(loop.Tok, wVar); // havoc w; - builder.Add(new Bpl.HavocCmd(s.Tok, new List { w })); + builder.Add(new Bpl.HavocCmd(loop.Tok, new List { w })); List invariants = new List(); if (freeInvariant != null) { invariants.Add(new Bpl.AssumeCmd(freeInvariant.tok, freeInvariant)); } BoogieStmtListBuilder invDefinednessBuilder = new BoogieStmtListBuilder(this, options, builder.Context); - foreach (AttributedExpression loopInv in s.Invariants) { + foreach (AttributedExpression loopInv in loop.Invariants) { var (errorMessage, successMessage) = CustomErrorMessage(loopInv.Attributes); TrStmt_CheckWellformed(loopInv.E, invDefinednessBuilder, locals, etran, false); invDefinednessBuilder.Add(TrAssumeCmdWithDependencies(etran, loopInv.E.tok, loopInv.E, "loop invariant")); @@ -264,34 +266,34 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, // add "this" to the explicit modifies clause var explicitModifies = modifiesClause; modifiesClause = new List(); - modifiesClause.Add(new FrameExpression(s.Tok, new ThisExpr((IteratorDecl)codeContext), null)); + modifiesClause.Add(new FrameExpression(loop.Tok, new ThisExpr((IteratorDecl)codeContext), null)); modifiesClause.AddRange(explicitModifies); } // include boilerplate invariants - foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(s.Tok, modifiesClause, s.IsGhost, codeContext.AllowsAllocation, etranPreLoop, etran, etran.Old)) { + foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(loop.Tok, modifiesClause, loop.IsGhost, codeContext.AllowsAllocation, etranPreLoop, etran, etran.Old)) { if (tri.IsFree) { - invariants.Add(TrAssumeCmd(s.Tok, tri.Expr)); + invariants.Add(TrAssumeCmd(loop.Tok, tri.Expr)); } else { Contract.Assert(tri.ErrorMessage != null); // follows from BoilerplateTriple invariant - invariants.Add(Assert(s.Tok, tri.Expr, new Microsoft.Dafny.BoilerplateTriple(tri.ErrorMessage, tri.SuccessMessage, tri.Comment), builder.Context)); + invariants.Add(Assert(loop.Tok, tri.Expr, new Microsoft.Dafny.BoilerplateTriple(tri.ErrorMessage, tri.SuccessMessage, tri.Comment), builder.Context)); } } // add a free invariant which says that the heap hasn't changed outside of the modifies clause. - invariants.Add(TrAssumeCmd(s.Tok, FrameConditionUsingDefinedFrame(s.Tok, etranPreLoop, etran, updatedFrameEtran, updatedFrameEtran.ModifiesFrame(s.Tok)))); + invariants.Add(TrAssumeCmd(loop.Tok, FrameConditionUsingDefinedFrame(loop.Tok, etranPreLoop, etran, updatedFrameEtran, updatedFrameEtran.ModifiesFrame(loop.Tok)))); // for iterators, add "fresh(_new)" as an invariant if (codeContext is IteratorDecl iter) { var th = new ThisExpr(iter); - var thisDotNew = new MemberSelectExpr(s.Tok, th, iter.Member_New); - var fr = new FreshExpr(s.Tok, thisDotNew); + var thisDotNew = new MemberSelectExpr(loop.Tok, th, iter.Member_New); + var fr = new FreshExpr(loop.Tok, thisDotNew); fr.Type = Type.Bool; - invariants.Add(TrAssertCmd(s.Tok, etran.TrExpr(fr))); + invariants.Add(TrAssertCmd(loop.Tok, etran.TrExpr(fr))); } } // include a free invariant that says that all definite-assignment trackers have only become more "true" foreach (var pair in daTrackersMonotonicity) { Bpl.Expr monotonic = BplImp(pair.Item1, pair.Item2); - invariants.Add(TrAssumeCmd(s.Tok, monotonic)); + invariants.Add(TrAssumeCmd(loop.Tok, monotonic)); } // include a free invariant that says that all completed iterations so far have only decreased the termination metric @@ -305,21 +307,21 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, toks.Add(e.tok); decrsDafny.Add(e); decrs.Add(etran.TrExpr(e)); - var (prevVars, eInit) = TranslateToLoopEntry(s, e, "LoopEntry"); + var (prevVars, eInit) = TranslateToLoopEntry(loop, e, "LoopEntry"); prevGhostLocals.AddRange(prevVars); initDecrsDafny.Add(eInit); } Bpl.Expr decrCheck = DecreasesCheck(toks, prevGhostLocals, decrsDafny, initDecrsDafny, decrs, initDecr, null, null, true, false); - invariants.Add(TrAssumeCmd(s.Tok, decrCheck)); + invariants.Add(TrAssumeCmd(loop.Tok, decrCheck)); } var loopBodyBuilder = new BoogieStmtListBuilder(this, options, builder.Context); - loopBodyBuilder.AddCaptureState(s.Tok, true, CaptureStateExtensions.AfterLoopIterationsStateMarker); + loopBodyBuilder.AddCaptureState(loop.Tok, true, CaptureStateExtensions.AfterLoopIterationsStateMarker); // As the first thing inside the loop, generate: if (!w) { CheckWellformed(inv); assume false; } - invDefinednessBuilder.Add(TrAssumeCmd(s.Tok, Bpl.Expr.False)); - loopBodyBuilder.Add(new Bpl.IfCmd(s.Tok, Bpl.Expr.Not(w), invDefinednessBuilder.Collect(s.Tok), null, null)); + invDefinednessBuilder.Add(TrAssumeCmd(loop.Tok, Bpl.Expr.False)); + loopBodyBuilder.Add(new Bpl.IfCmd(loop.Tok, Bpl.Expr.Not(w), invDefinednessBuilder.Collect(loop.Tok), null, null)); // Generate: CheckWellformed(guard); if (!guard) { break; } // but if this is a body-less loop, put all of that inside: if (*) { ... } @@ -327,7 +329,7 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, // on entry to the loop, and then Boogie wouldn't consider this a loop at all. (See also comment // in methods GuardAlwaysHoldsOnEntry_BodyLessLoop and GuardAlwaysHoldsOnEntry_LoopWithBody in // Test/dafny0/DirtyLoops.dfy.) - var isBodyLessLoop = s is OneBodyLoopStmt { BodySurrogate: { } }; + var isBodyLessLoop = loop is OneBodyLoopStmt { BodySurrogate: { } }; var whereToBuildLoopGuard = isBodyLessLoop ? new BoogieStmtListBuilder(this, options, builder.Context) : loopBodyBuilder; Bpl.Expr guard = null; if (Guard != null) { @@ -335,10 +337,10 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, guard = Bpl.Expr.Not(etran.TrExpr(Guard)); } var guardBreak = new BoogieStmtListBuilder(this, options, builder.Context); - guardBreak.Add(new Bpl.BreakCmd(s.Tok, null)); - whereToBuildLoopGuard.Add(new Bpl.IfCmd(s.Tok, guard, guardBreak.Collect(s.Tok), null, null)); + guardBreak.Add(new Bpl.BreakCmd(loop.Tok, null)); + whereToBuildLoopGuard.Add(new Bpl.IfCmd(loop.Tok, guard, guardBreak.Collect(loop.Tok), null, null)); if (isBodyLessLoop) { - loopBodyBuilder.Add(new Bpl.IfCmd(s.Tok, null, whereToBuildLoopGuard.Collect(s.Tok), null, null)); + loopBodyBuilder.Add(new Bpl.IfCmd(loop.Tok, null, whereToBuildLoopGuard.Collect(loop.Tok), null, null)); } if (bodyTr != null) { @@ -361,42 +363,42 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, // Note: the label "LoopEntry" doesn't exist in the program, and is // useful only for explanatory purposes. decrsDafny.Add(e); - var (prevVars, eInit) = TranslateToLoopEntry(s, e, "LoopEntry"); + var (prevVars, eInit) = TranslateToLoopEntry(loop, e, "LoopEntry"); prevGhostLocals.AddRange(prevVars); initDecrsDafny.Add(eInit); decrs.Add(etran.TrExpr(e)); } if (includeTerminationCheck) { - AddComment(loopBodyBuilder, s, "loop termination check"); + AddComment(loopBodyBuilder, loop, "loop termination check"); Bpl.Expr decrCheck = DecreasesCheck(toks, prevGhostLocals, decrsDafny, initDecrsDafny, decrs, oldBfs, loopBodyBuilder, " at end of loop iteration", false, false); var description = new - Terminates(s.InferredDecreases, prevGhostLocals, null, initDecrsDafny, theDecreases, false); - loopBodyBuilder.Add(Assert(s.Tok, decrCheck, description, builder.Context)); + Terminates(loop.InferredDecreases, prevGhostLocals, null, initDecrsDafny, theDecreases, false); + loopBodyBuilder.Add(Assert(loop.Tok, decrCheck, description, builder.Context)); } } } else if (isBodyLessLoop) { - var bodySurrogate = ((OneBodyLoopStmt)s).BodySurrogate; + var bodySurrogate = ((OneBodyLoopStmt)loop).BodySurrogate; // This is a body-less loop. Havoc the targets and then set w to false, to make the loop-invariant // maintenance check vaccuous. - var bplTargets = bodySurrogate.LocalLoopTargets.ConvertAll(v => TrVar(s.Tok, v)); + var bplTargets = bodySurrogate.LocalLoopTargets.ConvertAll(v => TrVar(loop.Tok, v)); if (bodySurrogate.UsesHeap) { bplTargets.Add(etran.HeapCastToIdentifierExpr); } - loopBodyBuilder.Add(new Bpl.HavocCmd(s.Tok, bplTargets)); - loopBodyBuilder.Add(Bpl.Cmd.SimpleAssign(s.Tok, w, Bpl.Expr.False)); + loopBodyBuilder.Add(new Bpl.HavocCmd(loop.Tok, bplTargets)); + loopBodyBuilder.Add(Bpl.Cmd.SimpleAssign(loop.Tok, w, Bpl.Expr.False)); } // Finally, assume the well-formedness of the invariant (which has been checked once and for all above), so that the check // of invariant-maintenance can use the appropriate canCall predicates. Note, it is important (see Test/git-issues/git-issue-1812.dfy) // that each CanCall assumption uses the preceding invariants as antecedents--this is achieved by treating all "invariant" // declarations as one big conjunction, because then CanCallAssumption will add the needed antecedents. - if (s.Invariants.Any()) { - var allInvariants = s.Invariants.Select(inv => inv.E).Aggregate((a, b) => Expression.CreateAnd(a, b)); - loopBodyBuilder.Add(TrAssumeCmd(s.Tok, etran.CanCallAssumption(allInvariants))); + if (loop.Invariants.Any()) { + var allInvariants = loop.Invariants.Select(inv => inv.E).Aggregate((a, b) => Expression.CreateAnd(a, b)); + loopBodyBuilder.Add(TrAssumeCmd(loop.Tok, etran.CanCallAssumption(allInvariants))); } - Bpl.StmtList body = loopBodyBuilder.Collect(s.Tok); - builder.Add(new Bpl.WhileCmd(s.Tok, Bpl.Expr.True, invariants, new List(), body)); + Bpl.StmtList body = loopBodyBuilder.Collect(loop.Tok); + builder.Add(new Bpl.WhileCmd(loop.Tok, Bpl.Expr.True, invariants, new List(), body)); } // Return the version of e that holds at the beginnging of the loop, From 6560fc2496aba2506912885935f5fb0b345fc6c8 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 09:02:12 +0200 Subject: [PATCH 26/47] Fixes --- .../BoogieGenerator.DefiniteAssignment.cs | 12 ++++-- .../Verifier/BoogieGenerator.Methods.cs | 38 +++++++++---------- .../Verifier/ProofDependencyManager.cs | 6 ++- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs index 97b0bfd1e12..43cd944ebc6 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs @@ -67,10 +67,16 @@ bool NeedsDefiniteAssignmentTracker(bool isGhost, Type type, bool isField) { void AddExistingDefiniteAssignmentTracker(IVariable p, bool forceGhostVar) { Contract.Requires(p != null); - if (NeedsDefiniteAssignmentTracker(p.IsGhost || forceGhostVar, p.Type, false)) { - var ie = new Bpl.IdentifierExpr(p.Tok, DefassPrefix + p.UniqueName, Bpl.Type.Bool); - DefiniteAssignmentTrackers.Add(p, ie); + if (DefiniteAssignmentTrackers.ContainsKey(p)) { + return; } + + if (!NeedsDefiniteAssignmentTracker(p.IsGhost || forceGhostVar, p.Type, false)) { + return; + } + + var ie = new Bpl.IdentifierExpr(p.Tok, DefassPrefix + p.UniqueName, Bpl.Type.Bool); + DefiniteAssignmentTrackers.Add(p, ie); } void AddDefiniteAssignmentTrackerSurrogate(Field field, TopLevelDeclWithMembers enclosingClass, diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index c89fb581257..669cd8f3daf 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -1716,6 +1716,25 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { GenerateMethodParameters(m.tok, m, kind, etran, inParams, out var outParams); + + var name = MethodName(m, kind); + var req = GetRequires(); + var mod = new List { ordinaryEtran.HeapCastToIdentifierExpr }; + var ens = GetEnsures(); + var proc = new Bpl.Procedure(m.tok, name, new List(), + inParams, outParams.Values.ToList(), false, req, mod, ens, etran.TrAttributes(m.Attributes, null)); + AddVerboseNameAttribute(proc, m.FullDafnyName, kind); + + if (InsertChecksums) { + InsertChecksum(m, proc, true); + } + + currentModule = null; + codeContext = null; + isAllocContext = null; + + return proc; + List GetRequires() { var req = new List(); // FREE PRECONDITIONS @@ -1784,25 +1803,6 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { return req; } - - var name = MethodName(m, kind); - var req = GetRequires(); - var mod = new List { ordinaryEtran.HeapCastToIdentifierExpr }; - var ens = GetEnsures(); - var proc = new Bpl.Procedure(m.tok, name, new List(), - inParams, outParams.Values.ToList(), false, req, mod, ens, etran.TrAttributes(m.Attributes, null)); - AddVerboseNameAttribute(proc, m.FullDafnyName, kind); - - if (InsertChecksums) { - InsertChecksum(m, proc, true); - } - - currentModule = null; - codeContext = null; - isAllocContext = null; - - return proc; - List GetEnsures() { var ens = new List(); if (kind is MethodTranslationKind.SpecWellformedness or MethodTranslationKind.OverrideCheck) { diff --git a/Source/DafnyCore/Verifier/ProofDependencyManager.cs b/Source/DafnyCore/Verifier/ProofDependencyManager.cs index 52ec364eeb2..9dce0cbb56c 100644 --- a/Source/DafnyCore/Verifier/ProofDependencyManager.cs +++ b/Source/DafnyCore/Verifier/ProofDependencyManager.cs @@ -37,7 +37,11 @@ public void SetCurrentDefinition(string verificationScopeId) { } public IEnumerable GetPotentialDependenciesForDefinition(string defName) { - return idsByMemberName[defName]; + if (idsByMemberName.TryGetValue(defName, out var result)) { + return result; + } + + return Enumerable.Empty(); } public IEnumerable GetAllPotentialDependencies() { From e9db4b9fd87f83fd458aad810bd4a42617a887cd Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 11:02:23 +0200 Subject: [PATCH 27/47] More fixes --- .../DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs | 4 ++++ Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs index 43cd944ebc6..a76e7b0727f 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs @@ -84,6 +84,10 @@ void AddDefiniteAssignmentTrackerSurrogate(Field field, TopLevelDeclWithMembers Contract.Requires(field != null); Contract.Requires(localVariables != null); + if (DefiniteAssignmentTrackers.TryGetValue(field, out _)) { + return; + } + var type = field.Type.Subst(enclosingClass.ParentFormalTypeParametersToActuals); if (!NeedsDefiniteAssignmentTracker(field.IsGhost || forceGhostVar, type, true)) { return; diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 669cd8f3daf..1271b37847b 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -1830,7 +1830,7 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { } } } - if (m is Constructor) { + if (m is Constructor && kind == MethodTranslationKind.Call) { var dafnyFresh = new OldExpr(Token.NoToken, new UnaryOpExpr(Token.NoToken, UnaryOpExpr.Opcode.Not, new UnaryOpExpr(Token.NoToken, UnaryOpExpr.Opcode.Allocated, new IdentifierExpr(Token.NoToken, "this")))); From 0830162241e616e40a296eaae0c714630180e3ac Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 11:04:18 +0200 Subject: [PATCH 28/47] Fix formatter --- Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs index a76e7b0727f..fb92eb8a06b 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs @@ -87,7 +87,7 @@ void AddDefiniteAssignmentTrackerSurrogate(Field field, TopLevelDeclWithMembers if (DefiniteAssignmentTrackers.TryGetValue(field, out _)) { return; } - + var type = field.Type.Subst(enclosingClass.ParentFormalTypeParametersToActuals); if (!NeedsDefiniteAssignmentTracker(field.IsGhost || forceGhostVar, type, true)) { return; From d4ca466e9eacb7ea7adfab43ed0357ac31fafaa3 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 11:47:32 +0200 Subject: [PATCH 29/47] Fix --- .../DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs | 5 ++--- Source/DafnyCore/Verifier/BoogieGenerator.cs | 1 + Source/DafnyTestGeneration/Utils.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs index fb92eb8a06b..91b2492ed83 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs @@ -162,9 +162,8 @@ void CheckDefiniteAssignmentSurrogate(IToken tok, Field field, bool atNew, Boogi Contract.Requires(tok != null); Contract.Requires(field != null); Contract.Requires(builder != null); - - Bpl.IdentifierExpr ie; - if (DefiniteAssignmentTrackers.TryGetValue(field, out ie)) { + + if (DefiniteAssignmentTrackers.TryGetValue(field, out var ie)) { var desc = new DefiniteAssignment( "field", field.Name, atNew ? "at this point in the constructor body" : "here"); builder.Add(Assert(tok, ie, desc, builder.Context)); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index 59b24e765cf..9b3ab2a8b06 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -1433,6 +1433,7 @@ private void Reset() { CurrentIdGenerator.Reset(); _tmpIEs.Clear(); assertionCount = 0; + DefiniteAssignmentTrackers.Clear(); } public static Bpl.QKeyValue InlineAttribute(Bpl.IToken tok, Bpl.QKeyValue/*?*/ next = null) { diff --git a/Source/DafnyTestGeneration/Utils.cs b/Source/DafnyTestGeneration/Utils.cs index f244c914be9..67ffd2a8da6 100644 --- a/Source/DafnyTestGeneration/Utils.cs +++ b/Source/DafnyTestGeneration/Utils.cs @@ -113,7 +113,7 @@ public static Microsoft.Boogie.Program DeepCloneProgram(DafnyOptions options, Mi /// public static Microsoft.Boogie.Program DeepCloneResolvedProgram(Microsoft.Boogie.Program program, DafnyOptions options) { program = DeepCloneProgram(options, program); - program.Resolve(options); + var resolutionErrors = program.Resolve(options); program.Typecheck(options); return program; } From 4dfbd8b6e7392277a48e2f7446fca2c4cfc7dc00 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 12:26:12 +0200 Subject: [PATCH 30/47] Fix expect file --- .../dafny0/snapshots/Snapshots1.run.legacy.dfy.expect | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots1.run.legacy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots1.run.legacy.dfy.expect index 6c278ab5388..a2ef2d3d341 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots1.run.legacy.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots1.run.legacy.dfy.expect @@ -1,10 +1,10 @@ -Processing command (at Snapshots1.v0.dfy(4,10)) assert {:id "id2"} Lit(false); +Processing command (at Snapshots1.v0.dfy(4,10)) assert {:id "id1"} Lit(false); >>> DoNothingToAssert Dafny program verifier finished with 1 verified, 0 errors -Processing call to procedure N (call postcondition) in implementation M (correctness) (at Snapshots1.v1.dfy(3,4)): +Processing call to procedure N (call) in implementation M (correctness) (at Snapshots1.v1.dfy(3,4)): >>> added after: a##cached##0 := a##cached##0 && false; -Processing command (at Snapshots1.v1.dfy(4,10)) assert {:id "id8"} Lit(false); +Processing command (at Snapshots1.v1.dfy(4,10)) assert {:id "id6"} Lit(false); >>> DoNothingToAssert Snapshots1.v1.dfy(4,9): Error: assertion might not hold From e8c409fab48436276f1a43830d357a07f2b5b644 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 12:36:44 +0200 Subject: [PATCH 31/47] Undo Def as tracking changes --- .../BoogieGenerator.DefiniteAssignment.cs | 70 +++++++++++++------ .../BoogieGenerator.ExpressionTranslator.cs | 10 +-- .../Verifier/BoogieGenerator.Methods.cs | 4 ++ Source/DafnyCore/Verifier/BoogieGenerator.cs | 6 +- .../Statements/BlockByProofStmtVerifier.cs | 2 + .../BoogieGenerator.TrAssignment.cs | 2 +- .../Statements/BoogieGenerator.TrLoop.cs | 4 +- .../Statements/BoogieGenerator.TrStatement.cs | 11 +++ .../Statements/OpaqueBlockVerifier.cs | 2 + .../LitTests/LitTest/comp/CSharpStyling.dfy | 2 +- .../LitTests/LitTest/dafny1/ListContents.dfy | 1 - 11 files changed, 82 insertions(+), 32 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs index 91b2492ed83..df600e3e8eb 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs @@ -47,11 +47,7 @@ bool NeedsDefiniteAssignmentTracker(bool isGhost, Type type, bool isField) { return null; } - if (DefiniteAssignmentTrackers.TryGetValue(p, out var result)) { - return result; - } - - Variable tracker; + Bpl.Variable tracker; if (isOutParam) { tracker = new Bpl.Formal(p.Tok, new Bpl.TypedIdent(p.Tok, DefassPrefix + p.UniqueName, Bpl.Type.Bool), false); } else { @@ -60,14 +56,14 @@ bool NeedsDefiniteAssignmentTracker(bool isGhost, Type type, bool isField) { tracker = localVariables.GetOrAdd(tracker); var ie = new Bpl.IdentifierExpr(p.Tok, tracker); - DefiniteAssignmentTrackers.Add(p, ie); + DefiniteAssignmentTrackers.Add(p.UniqueName, ie); return ie; } void AddExistingDefiniteAssignmentTracker(IVariable p, bool forceGhostVar) { Contract.Requires(p != null); - if (DefiniteAssignmentTrackers.ContainsKey(p)) { + if (DefiniteAssignmentTrackers.ContainsKey(p.UniqueName)) { return; } @@ -76,7 +72,7 @@ void AddExistingDefiniteAssignmentTracker(IVariable p, bool forceGhostVar) { } var ie = new Bpl.IdentifierExpr(p.Tok, DefassPrefix + p.UniqueName, Bpl.Type.Bool); - DefiniteAssignmentTrackers.Add(p, ie); + DefiniteAssignmentTrackers.Add(p.UniqueName, ie); } void AddDefiniteAssignmentTrackerSurrogate(Field field, TopLevelDeclWithMembers enclosingClass, @@ -84,10 +80,6 @@ void AddDefiniteAssignmentTrackerSurrogate(Field field, TopLevelDeclWithMembers Contract.Requires(field != null); Contract.Requires(localVariables != null); - if (DefiniteAssignmentTrackers.TryGetValue(field, out _)) { - return; - } - var type = field.Type.Subst(enclosingClass.ParentFormalTypeParametersToActuals); if (!NeedsDefiniteAssignmentTracker(field.IsGhost || forceGhostVar, type, true)) { return; @@ -96,21 +88,56 @@ void AddDefiniteAssignmentTrackerSurrogate(Field field, TopLevelDeclWithMembers var nm = SurrogateName(field); var tracker = localVariables.GetOrAdd(new Bpl.LocalVariable(field.tok, new Bpl.TypedIdent(field.tok, DefassPrefix + nm, Bpl.Type.Bool))); var ie = new Bpl.IdentifierExpr(field.tok, tracker); - DefiniteAssignmentTrackers.Add(field, ie); + DefiniteAssignmentTrackers.Add(nm, ie); + } + + public void RemoveDefiniteAssignmentTrackers(List ss, int prevDefAssTrackerCount) { + Contract.Requires(ss != null); + foreach (var s in ss) { + if (s is VarDeclStmt vdecl) { + if (vdecl.Assign is AssignOrReturnStmt ars) { + foreach (var sx in ars.ResolvedStatements) { + if (sx is VarDeclStmt vdecl2) { + vdecl2.Locals.ForEach(RemoveDefiniteAssignmentTracker); + } + } + } + + vdecl.Locals.ForEach(RemoveDefiniteAssignmentTracker); + } else if (s is AssignOrReturnStmt ars) { + foreach (var sx in ars.ResolvedStatements) { + if (sx is VarDeclStmt vdecl2) { + vdecl2.Locals.ForEach(RemoveDefiniteAssignmentTracker); + } + } + } + } + + Contract.Assert(prevDefAssTrackerCount == DefiniteAssignmentTrackers.Count); + } + + void RemoveDefiniteAssignmentTracker(IVariable p) { + Contract.Requires(p != null); + DefiniteAssignmentTrackers.Remove(p.UniqueName); + } + + void RemoveDefiniteAssignmentTrackerSurrogate(Field field) { + Contract.Requires(field != null); + DefiniteAssignmentTrackers.Remove(SurrogateName(field)); } void MarkDefiniteAssignmentTracker(IdentifierExpr expr, BoogieStmtListBuilder builder) { Contract.Requires(expr != null); Contract.Requires(builder != null); - MarkDefiniteAssignmentTracker(expr.tok, expr.Var, builder); + MarkDefiniteAssignmentTracker(expr.tok, expr.Var.UniqueName, builder); } - void MarkDefiniteAssignmentTracker(IToken tok, ISymbol variable, BoogieStmtListBuilder builder) { + void MarkDefiniteAssignmentTracker(IToken tok, string name, BoogieStmtListBuilder builder) { Contract.Requires(tok != null); Contract.Requires(builder != null); Bpl.IdentifierExpr ie; - if (DefiniteAssignmentTrackers.TryGetValue(variable, out ie)) { + if (DefiniteAssignmentTrackers.TryGetValue(name, out ie)) { builder.Add(Bpl.Cmd.SimpleAssign(tok, ie, Bpl.Expr.True)); } } @@ -140,7 +167,7 @@ void CheckDefiniteAssignment(IdentifierExpr expr, BoogieStmtListBuilder builder) Contract.Requires(builder != null); Bpl.IdentifierExpr ie; - if (DefiniteAssignmentTrackers.TryGetValue(expr.Var, out ie)) { + if (DefiniteAssignmentTrackers.TryGetValue(expr.Var.UniqueName, out ie)) { builder.Add(Assert(GetToken(expr), ie, new DefiniteAssignment("variable", expr.Var.Name, "here"), builder.Context)); } @@ -151,7 +178,7 @@ void CheckDefiniteAssignment(IdentifierExpr expr, BoogieStmtListBuilder builder) /// Bpl.IdentifierExpr /*?*/ GetDefiniteAssignmentTracker(IVariable var) { Bpl.IdentifierExpr ie; - if (DefiniteAssignmentTrackers.TryGetValue(var, out ie)) { + if (DefiniteAssignmentTrackers.TryGetValue(var.UniqueName, out ie)) { return ie; } @@ -162,8 +189,9 @@ void CheckDefiniteAssignmentSurrogate(IToken tok, Field field, bool atNew, Boogi Contract.Requires(tok != null); Contract.Requires(field != null); Contract.Requires(builder != null); - - if (DefiniteAssignmentTrackers.TryGetValue(field, out var ie)) { + + var nm = SurrogateName(field); + if (DefiniteAssignmentTrackers.TryGetValue(nm, out var ie)) { var desc = new DefiniteAssignment( "field", field.Name, atNew ? "at this point in the constructor body" : "here"); builder.Add(Assert(tok, ie, desc, builder.Context)); @@ -206,7 +234,7 @@ void CheckDefiniteAssignmentReturn(IToken tok, Formal p, BoogieStmtListBuilder b Contract.Requires(builder != null); Bpl.IdentifierExpr ie; - if (DefiniteAssignmentTrackers.TryGetValue(p, out ie)) { + if (DefiniteAssignmentTrackers.TryGetValue(p.UniqueName, out ie)) { var desc = new DefiniteAssignment("out-parameter", p.Name, "at this return point"); builder.Add(Assert(tok, ie, desc, builder.Context)); } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index 84d5fa60f0c..10d78a37498 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -833,22 +833,22 @@ public Boogie.Expr TrExpr(Expression expr) { // by $IsAllocBox. return BoogieGenerator.MkIsAllocBox(BoxIfNecessary(e.E.tok, TrExpr(e.E), e.E.Type), e.E.Type, HeapExpr); case UnaryOpExpr.ResolvedOpcode.Assigned: - ISymbol variable = null; + string name = null; switch (e.E.Resolved) { case IdentifierExpr ie: - variable = ie.Var; + name = ie.Var.UniqueName; break; case MemberSelectExpr mse: if (BoogieGenerator.inBodyInitContext && Expression.AsThis(mse.Obj) != null) { - variable = mse.Member; + name = BoogieGenerator.SurrogateName(mse.Member as Field); } break; } - if (variable == null) { + if (name == null) { return Expr.True; } - BoogieGenerator.DefiniteAssignmentTrackers.TryGetValue(variable, out var defass); + BoogieGenerator.DefiniteAssignmentTrackers.TryGetValue(name, out var defass); return defass; default: Contract.Assert(false); throw new cce.UnreachableException(); // unexpected unary expression diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 1271b37847b..678c9307adb 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -798,6 +798,7 @@ private StmtList TrMethodBody(Method m, BoogieStmtListBuilder builder, Variables // $_reverifyPost := false; builder.Add(Boogie.Cmd.SimpleAssign(m.tok, new Boogie.IdentifierExpr(m.tok, "$_reverifyPost", Boogie.Type.Bool), Boogie.Expr.False)); // register output parameters with definite-assignment trackers + Contract.Assert(DefiniteAssignmentTrackers.Count == 0); m.Outs.ForEach(p => AddExistingDefiniteAssignmentTracker(p, m.IsGhost)); // translate the body TrStmt(m.Body, builder, localVariables, etran); @@ -806,7 +807,10 @@ private StmtList TrMethodBody(Method m, BoogieStmtListBuilder builder, Variables AssumeCanCallForByMethodDecl(m, builder); } var stmts = builder.Collect(m.Body.RangeToken.StartToken); // EndToken might make more sense, but it requires updating most of the regression tests. + // tear down definite-assignment trackers + m.Outs.ForEach(RemoveDefiniteAssignmentTracker); + Contract.Assert(DefiniteAssignmentTrackers.Count == 0); return stmts; } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index 9b3ab2a8b06..3b16e6a333b 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -1759,7 +1759,7 @@ Bpl.Expr InSeqRange(IToken tok, Bpl.Expr index, Type indexType, Bpl.Expr seq, bo Bpl.LocalVariable yieldCountVariable = null; // non-null when an iterator body is being translated bool inBodyInitContext = false; // true during the translation of the .BodyInit portion of a divided constructor body - public Dictionary DefiniteAssignmentTrackers { get; } = new(); + public Dictionary DefiniteAssignmentTrackers { get; } = new(); Func assertionOnlyFilter = null; // generate assume statements instead of assert statements if not targeted by {:only} public enum StmtType { NONE, ASSERT, ASSUME }; @@ -2899,6 +2899,7 @@ private void GenerateMethodParametersChoose(IToken tok, IMethodCodeContext m, Me } } if (includeOutParams) { + Contract.Assume(DefiniteAssignmentTrackers.Count == 0); foreach (Formal p in m.Outs) { Contract.Assert(VisibleInScope(p.Type)); Contract.Assert(!p.IsOld); // out-parameters are never old (perhaps we want to relax this condition in the future) @@ -2914,6 +2915,9 @@ private void GenerateMethodParametersChoose(IToken tok, IMethodCodeContext m, Me } outParams.GetOrAdd(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.AssignUniqueName(currentDeclaration.IdGenerator), varType, wh), false)); } + // tear down definite-assignment trackers + m.Outs.ForEach(RemoveDefiniteAssignmentTracker); + Contract.Assert(DefiniteAssignmentTrackers.Count == 0); if (kind == MethodTranslationKind.Implementation) { outParams.GetOrAdd(new Bpl.Formal(tok, new Bpl.TypedIdent(tok, "$_reverifyPost", Bpl.Type.Bool), false)); diff --git a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs index c32672d0f41..688300d1e9c 100644 --- a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs +++ b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs @@ -11,9 +11,11 @@ public static void EmitBoogie(BoogieGenerator generator, BlockByProofStmt block, Variables locals, BoogieGenerator.ExpressionTranslator etran, ICodeContext codeContext) { var proofBuilder = new BoogieStmtListBuilder(generator, builder.Options, builder.Context); + var count = generator.DefiniteAssignmentTrackers.Count; generator.CurrentIdGenerator.Push(); generator.TrStmtList(block.Proof.Body, proofBuilder, locals, etran); generator.CurrentIdGenerator.Pop(); + generator.RemoveDefiniteAssignmentTrackers(block.Proof.Body, count); generator.TrStmt(block.Body, proofBuilder, locals, etran); diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs index 7b512f5119f..0d78e61af61 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrAssignment.cs @@ -279,7 +279,7 @@ void ProcessLhss(List lhss, bool rhsCanAffectPreviouslyKnownExpressi } if (!origRhsIsHavoc || field.Type.HavocCountsAsDefiniteAssignment(field.IsGhost)) { - MarkDefiniteAssignmentTracker(lhs.tok, field, bldr); + MarkDefiniteAssignmentTracker(lhs.tok, nm, bldr); } }); } else { diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs index 2a9bf8fd6d4..cebd7141fd1 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs @@ -205,7 +205,7 @@ void TrLoop(LoopStmt loop, Expression Guard, BodyTranslator/*?*/ bodyTr, var daTrackersMonotonicity = new List>(); foreach (var local in assignedVariables) { - if (!DefiniteAssignmentTrackers.TryGetValue(local, out var dat)) { + if (local.UniqueName == null || !DefiniteAssignmentTrackers.TryGetValue(local.UniqueName, out var dat)) { continue; } @@ -216,7 +216,7 @@ void TrLoop(LoopStmt loop, Expression Guard, BodyTranslator/*?*/ bodyTr, builder.Add(Cmd.SimpleAssign(loop.Tok, ie, dat)); } - List initDecr = null; + List initDecr = null; if (!Contract.Exists(theDecreases, e => e is WildcardExpr)) { initDecr = RecordDecreasesValue(theDecreases, builder, locals, etran, "$decr_init$" + suffix); } diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs index a142f2fb2ac..8c795613ec5 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs @@ -250,6 +250,7 @@ public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, } else if (stmt is DividedBlockStmt) { var s = (DividedBlockStmt)stmt; AddComment(builder, stmt, "divided block before new;"); + var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; var tok = s.SeparatorTok ?? s.Tok; // a DividedBlockStmt occurs only inside a Constructor body of a class var cl = (ClassDecl)((Constructor)codeContext).EnclosingClass; @@ -269,6 +270,7 @@ public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, // The "new;" translates into an allocation of "this" AddComment(builder, stmt, "new;"); fields.ForEach(f => CheckDefiniteAssignmentSurrogate(s.SeparatorTok ?? s.RangeToken.EndToken, f, true, builder)); + fields.ForEach(RemoveDefiniteAssignmentTrackerSurrogate); var th = new ThisExpr(cl); var bplThis = (Bpl.IdentifierExpr)etran.TrExpr(th); SelectAllocateObject(tok, bplThis, th.Type, false, builder, etran); @@ -283,12 +285,16 @@ public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, AddComment(builder, stmt, "divided block after new;"); TrStmtList(s.BodyProper, builder, locals, etran); + RemoveDefiniteAssignmentTrackers(s.Body, prevDefiniteAssignmentTrackerCount); + } else if (stmt is OpaqueBlock opaqueBlock) { OpaqueBlockVerifier.EmitBoogie(this, opaqueBlock, builder, locals, etran, (IMethodCodeContext)codeContext); } else if (stmt is BlockByProofStmt blockByProof) { BlockByProofStmtVerifier.EmitBoogie(this, blockByProof, builder, locals, etran, codeContext); } else if (stmt is BlockStmt blockStmt) { + var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; TrStmtList(blockStmt.Body, builder, locals, etran, blockStmt.RangeToken); + RemoveDefiniteAssignmentTrackers(blockStmt.Body, prevDefiniteAssignmentTrackerCount); } else if (stmt is IfStmt ifStmt) { TrIfStmt(ifStmt, builder, locals, etran); @@ -666,7 +672,10 @@ private void TrMatchStmt(MatchStmt stmt, BoogieStmtListBuilder builder, Variable builder.Add(new Bpl.HavocCmd(mc.tok, havocIds)); } + // translate the body into b + var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; TrStmtList(mc.Body, b, locals, etran); + RemoveDefiniteAssignmentTrackers(mc.Body, prevDefiniteAssignmentTrackerCount); Bpl.Expr guard = Bpl.Expr.Eq(source, r); ifCmd = new Bpl.IfCmd(mc.tok, guard, b.Collect(mc.tok), ifCmd, els); @@ -826,7 +835,9 @@ void TrAlternatives(List alternatives, IToken elseToken, Act } else { b.Add(TrAssumeCmdWithDependencies(etran, alternative.Guard.tok, alternative.Guard, "alternative guard")); } + var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; TrStmtList(alternative.Body, b, locals, etran, alternative.RangeToken); + RemoveDefiniteAssignmentTrackers(alternative.Body, prevDefiniteAssignmentTrackerCount); Bpl.StmtList thn = b.Collect(alternative.Tok); elsIf = new Bpl.IfCmd(alternative.Tok, null, thn, elsIf, els); els = null; diff --git a/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs b/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs index 8f3c2aadb6f..90fcdcdeed3 100644 --- a/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs +++ b/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs @@ -14,7 +14,9 @@ public static void EmitBoogie(BoogieGenerator generator, OpaqueBlock block, Boog var blockBuilder = new BoogieStmtListBuilder(generator, builder.Options, builder.Context); var bodyTranslator = GetBodyTranslator(generator, block, locals, etran, hasModifiesClause, blockBuilder); + var prevDefiniteAssignmentTrackerCount = generator.DefiniteAssignmentTrackers.Count; generator.TrStmtList(block.Body, blockBuilder, locals, bodyTranslator, block.RangeToken); + generator.RemoveDefiniteAssignmentTrackers(block.Body, prevDefiniteAssignmentTrackerCount); var assignedVariables = block.DescendantsAndSelf. SelectMany(s => s.GetAssignedLocals()).Select(ie => ie.Var) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/comp/CSharpStyling.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/comp/CSharpStyling.dfy index a9ec4d41420..06b9677b860 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/comp/CSharpStyling.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/comp/CSharpStyling.dfy @@ -1,4 +1,4 @@ -// RUN: %run "%s" --input %S/CSharpStyling2.cs > "%t" +// RUN: %run "%s" --input %S/CSharpStyling2.cs "%t" // RUN: %diff "%s.expect" "%t" method Main() { diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/ListContents.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/ListContents.dfy index 19f1d30c985..97c144db9cb 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/ListContents.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/ListContents.dfy @@ -1,6 +1,5 @@ // RUN: %testDafnyForEachResolver "%s" - class Node { ghost var List: seq ghost var Repr: set> From a078831881e8a7fcd09188ee96dd8a5c52992717 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 12:49:03 +0200 Subject: [PATCH 32/47] Fixes --- .../Resolver/GhostInterestVisitor.cs | 2 +- Source/DafnyCore/Resolver/ModuleResolver.cs | 28 ++++++++++++------- .../Statements/OpaqueBlockVerifier.cs | 2 +- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/Source/DafnyCore/Resolver/GhostInterestVisitor.cs b/Source/DafnyCore/Resolver/GhostInterestVisitor.cs index 748704e16e4..d9d9f1a7f69 100644 --- a/Source/DafnyCore/Resolver/GhostInterestVisitor.cs +++ b/Source/DafnyCore/Resolver/GhostInterestVisitor.cs @@ -529,7 +529,7 @@ public void Visit(Statement stmt, bool mustBeErasable, [CanBeNull] string proofC Visit(blockByProofStmt.Body, mustBeErasable, proofContext); blockByProofStmt.IsGhost = blockByProofStmt.IsGhost || blockByProofStmt.Body.IsGhost; - Visit(blockByProofStmt.Proof, true, "a by body"); + Visit(blockByProofStmt.Proof, true, "a by block"); break; default: Contract.Assert(false); throw new cce.UnreachableException(); diff --git a/Source/DafnyCore/Resolver/ModuleResolver.cs b/Source/DafnyCore/Resolver/ModuleResolver.cs index f719c6375b6..6ba52667874 100644 --- a/Source/DafnyCore/Resolver/ModuleResolver.cs +++ b/Source/DafnyCore/Resolver/ModuleResolver.cs @@ -1981,20 +1981,20 @@ void CheckExpression(Statement stmt, ModuleResolver resolver, ICodeContext codeC v.Visit(stmt); } class CheckExpressionVisitor : ResolverBottomUpVisitor { - readonly ICodeContext CodeContext; + readonly ICodeContext codeContext; public CheckExpressionVisitor(ModuleResolver resolver, ICodeContext codeContext) : base(resolver) { Contract.Requires(resolver != null); Contract.Requires(codeContext != null); - CodeContext = codeContext; + this.codeContext = codeContext; } protected override void VisitOneExpr(Expression expr) { if (expr is StmtExpr) { var e = (StmtExpr)expr; - resolver.ComputeGhostInterest(e.S, true, "a statement expression", CodeContext); + resolver.ComputeGhostInterest(e.S, true, "a statement expression", codeContext); } else if (expr is LetExpr) { var e = (LetExpr)expr; - if (CodeContext.IsGhost) { + if (codeContext.IsGhost) { foreach (var bv in e.BoundVars) { bv.MakeGhost(); } @@ -2003,12 +2003,21 @@ protected override void VisitOneExpr(Expression expr) { } protected override void VisitOneStmt(Statement stmt) { - if (stmt is CalcStmt calc) { - foreach (var h in calc.Hints) { - resolver.CheckLocalityUpdates(h, new HashSet(), "a hint"); + switch (stmt) + { + case CalcStmt calc: { + foreach (var h in calc.Hints) { + resolver.CheckLocalityUpdates(h, new HashSet(), "a hint"); + } + + break; } - } else if (stmt is ForallStmt forall && forall.Body != null) { - resolver.CheckLocalityUpdates(forall.Body, new HashSet(), "a forall statement"); + case BlockByProofStmt blockByProofStmt: + resolver.CheckLocalityUpdates(blockByProofStmt.Proof, new HashSet(), "a by block"); + break; + case ForallStmt { Body: not null } forall: + resolver.CheckLocalityUpdates(forall.Body, new HashSet(), "a forall statement"); + break; } } } @@ -3220,7 +3229,6 @@ void CheckIsFunction([CanBeNull] MemberDecl memberDecl, bool allowMethod) { /// context. /// public void CheckLocalityUpdates(Statement stmt, ISet localsAllowedInUpdates, string where) { - // TODO it looks like this method has no side-effects and doesn't return anything. Contract.Requires(stmt != null); Contract.Requires(localsAllowedInUpdates != null); Contract.Requires(where != null); diff --git a/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs b/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs index 90fcdcdeed3..d1dae58c9b5 100644 --- a/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs +++ b/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs @@ -26,7 +26,7 @@ public static void EmitBoogie(BoogieGenerator generator, OpaqueBlock block, Boog var variablesUsedInEnsures = block.Ensures.SelectMany(ae => ae.E.DescendantsAndSelf). OfType().DistinctBy(ie => ie.Var); var implicitAssignedIdentifiers = - variablesUsedInEnsures.Where(v => assignedVariables.Contains(v.Var)); + variablesUsedInEnsures.Where(v => assignedVariables.Contains(v.Var) && generator.DefiniteAssignmentTrackers.ContainsKey(v.Var.UniqueName)); foreach (var v in implicitAssignedIdentifiers) { var expression = new AttributedExpression(Expression.CreateAssigned(v.Tok, v)); totalEnsures.Add(expression); From e86fed8399ec1c881744ce0280fd63a490fd681a Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 12:55:33 +0200 Subject: [PATCH 33/47] Update expect file --- Source/DafnyCore/Resolver/ModuleResolver.cs | 2 +- .../LabeledAssertsResolution.dfy.expect | 28 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Source/DafnyCore/Resolver/ModuleResolver.cs b/Source/DafnyCore/Resolver/ModuleResolver.cs index 6ba52667874..6f9d6510aeb 100644 --- a/Source/DafnyCore/Resolver/ModuleResolver.cs +++ b/Source/DafnyCore/Resolver/ModuleResolver.cs @@ -3233,7 +3233,7 @@ public void CheckLocalityUpdates(Statement stmt, ISet localsAllow Contract.Requires(localsAllowedInUpdates != null); Contract.Requires(where != null); - if (stmt is AssertStmt || stmt is ForallStmt || stmt is CalcStmt || stmt is ModifyStmt) { + if (stmt is AssertStmt or BlockByProofStmt or ForallStmt or CalcStmt or ModifyStmt) { // don't recurse, since CheckHintRestrictions will be called on that assert-by separately return; } else if (stmt is AssignSuchThatStmt) { diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/LabeledAssertsResolution.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/LabeledAssertsResolution.dfy.expect index 0c054459d6f..c9b3e299ee0 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/LabeledAssertsResolution.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/LabeledAssertsResolution.dfy.expect @@ -10,34 +10,34 @@ LabeledAssertsResolution.dfy(94,17): Error: no label 'XYZ' in scope at this time LabeledAssertsResolution.dfy(115,14): Error: unresolved identifier: X LabeledAssertsResolution.dfy(52,11): Error: assert label shadows a dominating label LabeledAssertsResolution.dfy(54,11): Error: assert label shadows a dominating label -LabeledAssertsResolution.dfy(125,6): Error: an assert-by body is not allowed to update a variable it doesn't declare +LabeledAssertsResolution.dfy(125,6): Error: a by block is not allowed to update a variable it doesn't declare LabeledAssertsResolution.dfy(139,8): Error: assignment to non-ghost variable is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression -LabeledAssertsResolution.dfy(139,6): Error: an assert-by body is not allowed to update a variable it doesn't declare -LabeledAssertsResolution.dfy(170,16): Error: in an assert-by body, calls are allowed only to lemmas -LabeledAssertsResolution.dfy(172,10): Error: an assert-by body is not allowed to make heap updates +LabeledAssertsResolution.dfy(139,6): Error: a by block is not allowed to update a variable it doesn't declare +LabeledAssertsResolution.dfy(170,16): Error: in a by block, calls are allowed only to lemmas +LabeledAssertsResolution.dfy(172,10): Error: a by block is not allowed to make heap updates LabeledAssertsResolution.dfy(173,13): Error: assignment to array element is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression LabeledAssertsResolution.dfy(174,15): Error: assignment to array element is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression -LabeledAssertsResolution.dfy(175,8): Error: a modify statement is not allowed in an assert-by body -LabeledAssertsResolution.dfy(177,8): Error: an assert-by body is not allowed to make heap updates +LabeledAssertsResolution.dfy(175,8): Error: a modify statement is not allowed in a by block +LabeledAssertsResolution.dfy(177,8): Error: a by block is not allowed to make heap updates LabeledAssertsResolution.dfy(178,11): Error: assignment to array element is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression LabeledAssertsResolution.dfy(179,13): Error: assignment to array element is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression -LabeledAssertsResolution.dfy(180,6): Error: a modify statement is not allowed in an assert-by body +LabeledAssertsResolution.dfy(180,6): Error: a modify statement is not allowed in a by block LabeledAssertsResolution.dfy(187,12): Error: a hint is not allowed to make heap updates LabeledAssertsResolution.dfy(188,15): Error: assignment to array element is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression LabeledAssertsResolution.dfy(189,17): Error: assignment to array element is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression LabeledAssertsResolution.dfy(190,10): Error: a modify statement is not allowed in a hint LabeledAssertsResolution.dfy(195,18): Error: in a hint, calls are allowed only to lemmas -LabeledAssertsResolution.dfy(169,8): Error: an assert-by body is not allowed to update a variable it doesn't declare -LabeledAssertsResolution.dfy(170,8): Error: an assert-by body is not allowed to update a variable it doesn't declare -LabeledAssertsResolution.dfy(171,8): Error: an assert-by body is not allowed to update a variable it doesn't declare +LabeledAssertsResolution.dfy(169,8): Error: a by block is not allowed to update a variable it doesn't declare +LabeledAssertsResolution.dfy(170,8): Error: a by block is not allowed to update a variable it doesn't declare +LabeledAssertsResolution.dfy(171,8): Error: a by block is not allowed to update a variable it doesn't declare LabeledAssertsResolution.dfy(203,18): Error: a hint is not allowed to update a variable it doesn't declare LabeledAssertsResolution.dfy(204,18): Error: a hint is not allowed to update a variable it doesn't declare -LabeledAssertsResolution.dfy(199,12): Error: an assert-by body is not allowed to update a variable it doesn't declare -LabeledAssertsResolution.dfy(200,12): Error: an assert-by body is not allowed to update a variable it doesn't declare +LabeledAssertsResolution.dfy(199,12): Error: a by block is not allowed to update a variable it doesn't declare +LabeledAssertsResolution.dfy(200,12): Error: a by block is not allowed to update a variable it doesn't declare LabeledAssertsResolution.dfy(185,10): Error: a hint is not allowed to update a variable it doesn't declare LabeledAssertsResolution.dfy(195,10): Error: a hint is not allowed to update a variable it doesn't declare -LabeledAssertsResolution.dfy(219,8): Error: an assert-by body is not allowed to update a variable it doesn't declare -LabeledAssertsResolution.dfy(220,8): Error: an assert-by body is not allowed to update a variable it doesn't declare +LabeledAssertsResolution.dfy(219,8): Error: a by block is not allowed to update a variable it doesn't declare +LabeledAssertsResolution.dfy(220,8): Error: a by block is not allowed to update a variable it doesn't declare LabeledAssertsResolution.dfy(222,31): Error: return statement is not allowed in this context (because it is guarded by a specification-only expression) LabeledAssertsResolution.dfy(223,43): Error: return statement is not allowed in this context (because it is guarded by a specification-only expression) LabeledAssertsResolution.dfy(232,14): Error: return statement is not allowed in this context (because it is guarded by a specification-only expression) From 67cdf7bb545ffa0c4f70bf3c4c9c363ae4e5ae49 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 13:02:16 +0200 Subject: [PATCH 34/47] Update tests --- Source/DafnyCore/AST/Expressions/StmtExpr.cs | 34 +++++++++++-------- .../Verifier/BoogieGenerator.SplitExpr.cs | 4 +-- .../LitTests/LitTest/VSI-Benchmarks/b4.dfy | 2 +- .../Snapshots2.run.legacy.dfy.expect | 22 ++++++------ .../Snapshots8.run.legacy.dfy.expect | 22 ++++++------ .../TestFiles/LitTests/LitTest/dafny4/gcd.dfy | 2 +- 6 files changed, 46 insertions(+), 40 deletions(-) diff --git a/Source/DafnyCore/AST/Expressions/StmtExpr.cs b/Source/DafnyCore/AST/Expressions/StmtExpr.cs index 0e0fcdac78e..6d2e2f2ada4 100644 --- a/Source/DafnyCore/AST/Expressions/StmtExpr.cs +++ b/Source/DafnyCore/AST/Expressions/StmtExpr.cs @@ -53,20 +53,26 @@ public override IEnumerable TerminalExpressions { /// S is executed. /// This method should be called only after successful resolution of the expression. /// - public Expression GetSConclusion() { - // this is one place where we actually investigate what kind of statement .S is - if (S is PredicateStmt) { - var s = (PredicateStmt)S; - return s.Expr; - } else if (S is CalcStmt) { - var s = (CalcStmt)S; - return s.Result; - } else if (S is HideRevealStmt) { - return CreateBoolLiteral(tok, true); // one could use the definition axiom or the referenced labeled assertions, but "true" is conservative and much simpler :) - } else if (S is AssignStatement) { - return CreateBoolLiteral(tok, true); // one could use the postcondition of the method, suitably instantiated, but "true" is conservative and much simpler :) - } else { - Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement + public Expression GetStatementConclusion() { + return GetStatementConclusion(S); + } + + private Expression GetStatementConclusion(Statement statement) { + switch (statement) + { + // this is one place where we actually investigate what kind of statement .S is + case PredicateStmt stmt: + return stmt.Expr; + case CalcStmt stmt: + return stmt.Result; + case HideRevealStmt: + return CreateBoolLiteral(tok, true); // one could use the definition axiom or the referenced labeled assertions, but "true" is conservative and much simpler :) + case AssignStatement: + return CreateBoolLiteral(tok, true); // one could use the postcondition of the method, suitably instantiated, but "true" is conservative and much simpler :) + case BlockByProofStmt stmt: + return GetStatementConclusion(stmt.Body); + default: + Contract.Assert(false); throw new cce.UnreachableException(); // unexpected statement } } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs b/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs index b029b543c58..e3041c4e1bf 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs @@ -247,7 +247,7 @@ bool TrSplitExpr(BodyTranslationContext context, Expression expr, List E". if (position) { - var conclusion = etran.TrExpr(e.GetSConclusion()); + var conclusion = etran.TrExpr(e.GetStatementConclusion()); var ss = new List(); TrSplitExpr(context, e.E, ss, position, heightLimit, applyInduction, etran); foreach (var s in ss) { @@ -256,7 +256,7 @@ bool TrSplitExpr(BodyTranslationContext context, Expression expr, List(); - TrSplitExpr(context, e.GetSConclusion(), ss, !position, heightLimit, applyInduction, etran); + TrSplitExpr(context, e.GetStatementConclusion(), ss, !position, heightLimit, applyInduction, etran); var rhs = etran.TrExpr(e.E); foreach (var s in ss) { // as the source location in the following implication, use that of the translated "s" diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/VSI-Benchmarks/b4.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/VSI-Benchmarks/b4.dfy index 60a3675fee4..82f3a11b505 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/VSI-Benchmarks/b4.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/VSI-Benchmarks/b4.dfy @@ -1,4 +1,4 @@ -// RUN: %testDafnyForEachResolver "%s" -- --bprint=/Users/rwillems/SourceCode/dafny/b4.bpl +// RUN: %testDafnyForEachResolver "%s" // Note: We are using the built-in equality to compare keys. diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots2.run.legacy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots2.run.legacy.dfy.expect index 401acbc1f66..768491421f5 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots2.run.legacy.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots2.run.legacy.dfy.expect @@ -1,31 +1,31 @@ -Processing command (at Snapshots2.v0.dfy(4,10)) assert {:id "id2"} Lit(false); +Processing command (at Snapshots2.v0.dfy(4,10)) assert {:id "id1"} Lit(false); >>> DoNothingToAssert -Processing command (at Snapshots2.v0.dfy(11,11)) assert {:id "id6"} Lit(true); +Processing command (at Snapshots2.v0.dfy(11,11)) assert {:id "id5"} Lit(true); >>> DoNothingToAssert -Processing command (at Snapshots2.v0.dfy(11,15)) assert {:id "id5"} _module.__default.P() <==> _module.__default.Q(); +Processing command (at Snapshots2.v0.dfy(11,15)) assert {:id "id4"} _module.__default.P() <==> _module.__default.Q(); >>> DoNothingToAssert -Processing command (at Snapshots2.v0.dfy(14,11)) assert {:id "id9"} Lit(true); +Processing command (at Snapshots2.v0.dfy(14,11)) assert {:id "id8"} Lit(true); >>> DoNothingToAssert -Processing command (at Snapshots2.v0.dfy(14,15)) assert {:id "id8"} _module.__default.Q() <==> Lit(_module.__default.R()); +Processing command (at Snapshots2.v0.dfy(14,15)) assert {:id "id7"} _module.__default.Q() <==> Lit(_module.__default.R()); >>> DoNothingToAssert Dafny program verifier finished with 3 verified, 0 errors -Processing call to procedure N (call postcondition) in implementation M (correctness) (at Snapshots2.v1.dfy(3,4)): +Processing call to procedure N (call) in implementation M (correctness) (at Snapshots2.v1.dfy(3,4)): >>> added after: a##cached##0 := a##cached##0 && false; Processing implementation P (well-formedness) (at Snapshots2.v1.dfy(10,11)): >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && false; Processing implementation Q (well-formedness) (at Snapshots2.v1.dfy(13,11)): >>> added after assuming the current precondition: a##cached##0 := a##cached##0 && false; -Processing command (at Snapshots2.v1.dfy(4,10)) assert {:id "id14"} Lit(false); +Processing command (at Snapshots2.v1.dfy(4,10)) assert {:id "id12"} Lit(false); >>> DoNothingToAssert Snapshots2.v1.dfy(4,9): Error: assertion might not hold -Processing command (at Snapshots2.v1.dfy(11,11)) assert {:id "id18"} Lit(true); +Processing command (at Snapshots2.v1.dfy(11,11)) assert {:id "id16"} Lit(true); >>> DoNothingToAssert -Processing command (at Snapshots2.v1.dfy(11,15)) assert {:id "id17"} _module.__default.P() <==> _module.__default.Q(); +Processing command (at Snapshots2.v1.dfy(11,15)) assert {:id "id15"} _module.__default.P() <==> _module.__default.Q(); >>> DoNothingToAssert -Processing command (at Snapshots2.v1.dfy(14,11)) assert {:id "id21"} Lit(true); +Processing command (at Snapshots2.v1.dfy(14,11)) assert {:id "id19"} Lit(true); >>> DoNothingToAssert -Processing command (at Snapshots2.v1.dfy(14,15)) assert {:id "id20"} _module.__default.Q() <==> Lit(_module.__default.R()); +Processing command (at Snapshots2.v1.dfy(14,15)) assert {:id "id18"} _module.__default.Q() <==> Lit(_module.__default.R()); >>> DoNothingToAssert Dafny program verifier finished with 2 verified, 1 error diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots8.run.legacy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots8.run.legacy.dfy.expect index 114c1a89a2e..0b3564e7be2 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots8.run.legacy.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots8.run.legacy.dfy.expect @@ -3,40 +3,40 @@ Processing command (at Snapshots8.v0.dfy(2,37)) assert {:id "id0"} x#0 < 20 || L >>> DoNothingToAssert Processing command (at Snapshots8.v0.dfy(3,12)) assert {:id "id1"} x#0 < 10; >>> DoNothingToAssert -Processing command (at Snapshots8.v0.dfy(4,8)) assert {:id "id5$id2$requires"} {:id "id2"} LitInt(0) <= call0formal#AT#y#0; +Processing command (at Snapshots8.v0.dfy(4,8)) assert {:id "id4$id2$requires"} {:id "id2"} LitInt(0) <= call0formal#AT#y#0; >>> DoNothingToAssert Snapshots8.v0.dfy(3,11): Error: assertion might not hold Snapshots8.v0.dfy(4,7): Error: a precondition for this call could not be proved Snapshots8.v0.dfy(8,13): Related location: this is the precondition that could not be proved -Processing command (at Snapshots8.v0.dfy(13,13)) assert {:id "id9"} LitInt(2) <= z#0; +Processing command (at Snapshots8.v0.dfy(13,13)) assert {:id "id8"} LitInt(2) <= z#0; >>> DoNothingToAssert Snapshots8.v0.dfy(17,9): Error: a postcondition could not be proved on this return path Snapshots8.v0.dfy(13,12): Related location: this is the postcondition that could not be proved -Processing command (at Snapshots8.v0.dfy(23,12)) assert {:id "id11"} u#0 != 53; +Processing command (at Snapshots8.v0.dfy(23,12)) assert {:id "id10"} u#0 != 53; >>> DoNothingToAssert Snapshots8.v0.dfy(23,11): Error: assertion might not hold -Processing command (at Snapshots8.v0.dfy(28,10)) assert {:id "id12"} Lit(true); +Processing command (at Snapshots8.v0.dfy(28,10)) assert {:id "id11"} Lit(true); >>> DoNothingToAssert Dafny program verifier finished with 1 verified, 4 errors -Processing command (at Snapshots8.v1.dfy(30,17)) assert {:id "id26"} u#0 != 53; +Processing command (at Snapshots8.v1.dfy(30,17)) assert {:id "id24"} u#0 != 53; >>> RecycleError Snapshots8.v1.dfy(30,16): Error: assertion might not hold -Processing command (at Snapshots8.v1.dfy(3,15)) assert {:id "id13"} x#0 < 20 || LitInt(10) <= x#0; +Processing command (at Snapshots8.v1.dfy(3,15)) assert {:id "id12"} x#0 < 20 || LitInt(10) <= x#0; >>> MarkAsFullyVerified -Processing command (at Snapshots8.v1.dfy(5,17)) assert {:id "id14"} x#0 < 10; +Processing command (at Snapshots8.v1.dfy(5,17)) assert {:id "id13"} x#0 < 10; >>> RecycleError -Processing command (at Snapshots8.v1.dfy(6,8)) assert {:id "id19$id15$requires"} {:id "id15"} LitInt(0) <= call0formal#AT#y#0; +Processing command (at Snapshots8.v1.dfy(6,8)) assert {:id "id17$id14$requires"} {:id "id14"} LitInt(0) <= call0formal#AT#y#0; >>> RecycleError -Processing command (at Snapshots8.v1.dfy(7,12)) assert {:id "id17"} x#0 == LitInt(7); +Processing command (at Snapshots8.v1.dfy(7,12)) assert {:id "id15"} x#0 == LitInt(7); >>> DoNothingToAssert Snapshots8.v1.dfy(5,16): Error: assertion might not hold Snapshots8.v1.dfy(6,7): Error: a precondition for this call could not be proved Snapshots8.v1.dfy(12,20): Related location: this is the precondition that could not be proved Snapshots8.v1.dfy(7,11): Error: assertion might not hold -Processing command (at Snapshots8.v1.dfy(23,12)) assert {:id "id25"} Lit(true); +Processing command (at Snapshots8.v1.dfy(23,12)) assert {:id "id23"} Lit(true); >>> DoNothingToAssert -Processing command (at Snapshots8.v1.dfy(19,13)) assert {:id "id23"} LitInt(2) <= z#0; +Processing command (at Snapshots8.v1.dfy(19,13)) assert {:id "id21"} LitInt(2) <= z#0; >>> DoNothingToAssert Snapshots8.v1.dfy(24,9): Error: a postcondition could not be proved on this return path Snapshots8.v1.dfy(19,12): Related location: this is the postcondition that could not be proved diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy index 1c214141942..e74a60c9ad7 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy @@ -205,7 +205,7 @@ method EuclidGcd(X: pos, Y: pos) returns (gcd: pos) // ------------------------------------------------------------------------------------------------------ // The alternative definitions that follow allow the two cases in the GCD algorithm to look more similar. -lemma GcdSubtractAlt(x: pos, y: pos) +lemma {:isolate_assertions} GcdSubtractAlt(x: pos, y: pos) requires x < y ensures Gcd(y, x) == Gcd(x, y - x) // this says Gcd(y, x) instead of Gcd(x, y) as in GcdSubtract above { From 054140bbbc3efbc7698b9228303c162dbaf32236 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 13:10:12 +0200 Subject: [PATCH 35/47] Update tests --- .../GhostAllocations-Resolution.dfy.expect | 28 +++++++++---------- .../LitTest/dafny0/SubsetTypes.dfy.expect | 2 +- .../Snapshots0.run.legacy.dfy.expect | 12 ++++---- .../Snapshots5.run.legacy.dfy.expect | 14 +++++----- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/GhostAllocations-Resolution.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/GhostAllocations-Resolution.dfy.expect index cf476f9a6da..248c5a5b270 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/GhostAllocations-Resolution.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/GhostAllocations-Resolution.dfy.expect @@ -33,9 +33,9 @@ GhostAllocations-Resolution.dfy(240,6): Error: print statement is not allowed in GhostAllocations-Resolution.dfy(248,22): Error: a hint is not allowed to use 'new' GhostAllocations-Resolution.dfy(249,22): Error: a hint is not allowed to use 'new' GhostAllocations-Resolution.dfy(249,22): Error: in a hint, calls are allowed only to lemmas -GhostAllocations-Resolution.dfy(254,18): Error: an assert-by body is not allowed to use 'new' -GhostAllocations-Resolution.dfy(255,18): Error: an assert-by body is not allowed to use 'new' -GhostAllocations-Resolution.dfy(255,18): Error: in an assert-by body, calls are allowed only to lemmas +GhostAllocations-Resolution.dfy(254,18): Error: a by block is not allowed to use 'new' +GhostAllocations-Resolution.dfy(255,18): Error: a by block is not allowed to use 'new' +GhostAllocations-Resolution.dfy(255,18): Error: in a by block, calls are allowed only to lemmas GhostAllocations-Resolution.dfy(285,11): Error: ghost variables such as b are allowed only in specification contexts. b was inferred to be ghost based on its declaration or initialization. GhostAllocations-Resolution.dfy(274,11): Error: assignment to non-ghost field is not allowed in this context, because this is a ghost method GhostAllocations-Resolution.dfy(303,13): Error: a lemma is not allowed to use 'new' @@ -49,7 +49,7 @@ GhostAllocations-Resolution.dfy(314,13): Error: a lemma is not allowed to use 'n GhostAllocations-Resolution.dfy(318,13): Error: a twostate lemma is not allowed to use 'new' GhostAllocations-Resolution.dfy(322,13): Error: a greatest lemma is not allowed to use 'new' GhostAllocations-Resolution.dfy(349,15): Error: in a statement expression, calls are allowed only to lemmas -GhostAllocations-Resolution.dfy(361,7): Error: in an assert-by body, calls are allowed only to lemmas +GhostAllocations-Resolution.dfy(361,7): Error: in a by block, calls are allowed only to lemmas GhostAllocations-Resolution.dfy(369,5): Error: in a lemma, calls are allowed only to lemmas GhostAllocations-Resolution.dfy(376,11): Error: in a hint, calls are allowed only to lemmas GhostAllocations-Resolution.dfy(384,11): Error: in a hint, calls are allowed only to lemmas @@ -60,12 +60,12 @@ GhostAllocations-Resolution.dfy(406,4): Error: a modify statement is not allowed GhostAllocations-Resolution.dfy(407,4): Error: a modify statement is not allowed in a lemma GhostAllocations-Resolution.dfy(409,13): Error: a lemma is not allowed to use 'new' GhostAllocations-Resolution.dfy(410,6): Error: a lemma is not allowed to make heap updates -GhostAllocations-Resolution.dfy(416,17): Error: a loop in an assert-by body is not allowed to use 'modifies' clauses -GhostAllocations-Resolution.dfy(421,17): Error: a loop in an assert-by body is not allowed to use 'modifies' clauses -GhostAllocations-Resolution.dfy(426,17): Error: a loop in an assert-by body is not allowed to use 'modifies' clauses -GhostAllocations-Resolution.dfy(430,6): Error: a modify statement is not allowed in an assert-by body -GhostAllocations-Resolution.dfy(431,6): Error: a modify statement is not allowed in an assert-by body -GhostAllocations-Resolution.dfy(433,15): Error: an assert-by body is not allowed to use 'new' +GhostAllocations-Resolution.dfy(416,17): Error: a loop in a by block is not allowed to use 'modifies' clauses +GhostAllocations-Resolution.dfy(421,17): Error: a loop in a by block is not allowed to use 'modifies' clauses +GhostAllocations-Resolution.dfy(426,17): Error: a loop in a by block is not allowed to use 'modifies' clauses +GhostAllocations-Resolution.dfy(430,6): Error: a modify statement is not allowed in a by block +GhostAllocations-Resolution.dfy(431,6): Error: a modify statement is not allowed in a by block +GhostAllocations-Resolution.dfy(433,15): Error: a by block is not allowed to use 'new' GhostAllocations-Resolution.dfy(485,17): Error: a loop in a forall statement is not allowed to use 'modifies' clauses GhostAllocations-Resolution.dfy(490,17): Error: a loop in a forall statement is not allowed to use 'modifies' clauses GhostAllocations-Resolution.dfy(495,17): Error: a loop in a forall statement is not allowed to use 'modifies' clauses @@ -103,11 +103,11 @@ GhostAllocations-Resolution.dfy(600,8): Error: a hint is not allowed to perform GhostAllocations-Resolution.dfy(604,13): Error: a hint is not allowed to make heap updates GhostAllocations-Resolution.dfy(605,8): Error: a hint is not allowed to perform an aggregate heap update GhostAllocations-Resolution.dfy(613,11): Error: assignment to array element is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression -GhostAllocations-Resolution.dfy(614,6): Error: an assert-by body is not allowed to perform an aggregate heap update +GhostAllocations-Resolution.dfy(614,6): Error: a by block is not allowed to perform an aggregate heap update GhostAllocations-Resolution.dfy(618,16): Error: assignment to non-ghost field is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression -GhostAllocations-Resolution.dfy(619,6): Error: an assert-by body is not allowed to perform an aggregate heap update -GhostAllocations-Resolution.dfy(623,11): Error: an assert-by body is not allowed to make heap updates -GhostAllocations-Resolution.dfy(624,6): Error: an assert-by body is not allowed to perform an aggregate heap update +GhostAllocations-Resolution.dfy(619,6): Error: a by block is not allowed to perform an aggregate heap update +GhostAllocations-Resolution.dfy(623,11): Error: a by block is not allowed to make heap updates +GhostAllocations-Resolution.dfy(624,6): Error: a by block is not allowed to perform an aggregate heap update GhostAllocations-Resolution.dfy(632,11): Error: assignment to array element is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression GhostAllocations-Resolution.dfy(633,6): Error: a forall statement is not allowed to perform an aggregate heap update GhostAllocations-Resolution.dfy(637,16): Error: assignment to non-ghost field is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect index c26e020b6a6..dc6081d50f4 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect @@ -91,5 +91,5 @@ SubsetTypes.dfy(459,15): Error: assertion might not hold SubsetTypes.dfy(464,13): Error: assertion might not hold Dafny program verifier finished with 13 verified, 91 errors -Total resources used is 775020 +Total resources used is 774990 Max resources used by VC is 101850 diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots0.run.legacy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots0.run.legacy.dfy.expect index bc7f9c7e992..e1e675464dc 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots0.run.legacy.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots0.run.legacy.dfy.expect @@ -1,13 +1,13 @@ -Processing command (at Snapshots0.v0.dfy(4,10)) assert {:id "id2"} Lit(false); +Processing command (at Snapshots0.v0.dfy(4,10)) assert {:id "id1"} Lit(false); >>> DoNothingToAssert Dafny program verifier finished with 1 verified, 0 errors -Processing call to procedure bar (call precondtion) in implementation foo (correctness) (at Snapshots0.v1.dfy(3,6)): - >>> added after: a##cached##0 := a##cached##0 && true; -Processing call to procedure bar (call postcondition) in implementation foo (correctness) (at Snapshots0.v1.dfy(3,6)): +Processing call to procedure bar (call) in implementation foo (correctness) (at Snapshots0.v1.dfy(3,6)): >>> added axiom: (forall call0old#AT#$Heap: Heap, $Heap: Heap :: {:weight 30} { ##extracted_function##1(call0old#AT#$Heap, $Heap) } ##extracted_function##1(call0old#AT#$Heap, $Heap) == (true && Lit(false) && (forall $o: ref :: { $Heap[$o] } $o != null && $Unbox(read(call0old#AT#$Heap, $o, alloc)): bool ==> $Heap[$o] == call0old#AT#$Heap[$o]) && $HeapSucc(call0old#AT#$Heap, $Heap))) - >>> added after: a##cached##1 := a##cached##1 && ##extracted_function##1(call0old#AT#$Heap, $Heap); -Processing command (at Snapshots0.v1.dfy(4,10)) assert {:id "id7"} Lit(false); + >>> added after: a##cached##0 := a##cached##0 && ##extracted_function##1(call0old#AT#$Heap, $Heap); +Processing command (at ) a##cached##0 := a##cached##0 && ##extracted_function##1(call0old#AT#$Heap, $Heap); + >>> AssumeNegationOfAssumptionVariable +Processing command (at Snapshots0.v1.dfy(4,10)) assert {:id "id5"} Lit(false); >>> MarkAsPartiallyVerified Snapshots0.v1.dfy(4,9): Error: assertion might not hold diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots5.run.legacy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots5.run.legacy.dfy.expect index 8a6d82c8b15..5566f116ad4 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots5.run.legacy.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/snapshots/Snapshots5.run.legacy.dfy.expect @@ -2,11 +2,11 @@ Snapshots5.v0.dfy(10,12): Warning: Could not find a trigger for this quantifier. Snapshots5.v0.dfy(13,10): Warning: Could not find a trigger for this quantifier. Without a trigger, the quantifier may cause brittle verification. To silence this warning, add an explicit trigger using the {:trigger} attribute. For more information, see the section quantifier instantiation rules in the reference manual. Snapshots5.v0.dfy(20,12): Warning: Could not find a trigger for this quantifier. Without a trigger, the quantifier may cause brittle verification. To silence this warning, add an explicit trigger using the {:trigger} attribute. For more information, see the section quantifier instantiation rules in the reference manual. Snapshots5.v0.dfy(26,11): Warning: Could not find a trigger for this quantifier. Without a trigger, the quantifier may cause brittle verification. To silence this warning, add an explicit trigger using the {:trigger} attribute. For more information, see the section quantifier instantiation rules in the reference manual. -Processing command (at Snapshots5.v0.dfy(10,40)) assert {:id "id2"} (forall b#1_1: bool :: b#1_1 || !b#1_1) || 0 != 0; +Processing command (at Snapshots5.v0.dfy(10,40)) assert {:id "id1"} (forall b#1_1: bool :: b#1_1 || !b#1_1) || 0 != 0; >>> DoNothingToAssert -Processing command (at Snapshots5.v0.dfy(13,38)) assert {:id "id5"} (forall b#1: bool :: b#1 || !b#1) || 3 != 3; +Processing command (at Snapshots5.v0.dfy(13,38)) assert {:id "id3"} (forall b#1: bool :: b#1 || !b#1) || 3 != 3; >>> DoNothingToAssert -Processing command (at Snapshots5.v0.dfy(20,40)) assert {:id "id6"} (forall b#3_1: bool :: b#3_1 || !b#3_1) || 1 != 1; +Processing command (at Snapshots5.v0.dfy(20,40)) assert {:id "id4"} (forall b#3_1: bool :: b#3_1 || !b#3_1) || 1 != 1; >>> DoNothingToAssert Dafny program verifier finished with 1 verified, 0 errors @@ -15,13 +15,13 @@ Snapshots5.v1.dfy(13,10): Warning: Could not find a trigger for this quantifier. Snapshots5.v1.dfy(20,12): Warning: Could not find a trigger for this quantifier. Without a trigger, the quantifier may cause brittle verification. To silence this warning, add an explicit trigger using the {:trigger} attribute. For more information, see the section quantifier instantiation rules in the reference manual. Snapshots5.v1.dfy(22,10): Warning: Could not find a trigger for this quantifier. Without a trigger, the quantifier may cause brittle verification. To silence this warning, add an explicit trigger using the {:trigger} attribute. For more information, see the section quantifier instantiation rules in the reference manual. Snapshots5.v1.dfy(27,11): Warning: Could not find a trigger for this quantifier. Without a trigger, the quantifier may cause brittle verification. To silence this warning, add an explicit trigger using the {:trigger} attribute. For more information, see the section quantifier instantiation rules in the reference manual. -Processing command (at Snapshots5.v1.dfy(10,40)) assert {:id "id13"} (forall b#1_1: bool :: b#1_1 || !b#1_1) || 0 != 0; +Processing command (at Snapshots5.v1.dfy(10,40)) assert {:id "id10"} (forall b#1_1: bool :: b#1_1 || !b#1_1) || 0 != 0; >>> MarkAsFullyVerified -Processing command (at Snapshots5.v1.dfy(13,38)) assert {:id "id16"} (forall b#1: bool :: b#1 || !b#1) || 3 != 3; +Processing command (at Snapshots5.v1.dfy(13,38)) assert {:id "id12"} (forall b#1: bool :: b#1 || !b#1) || 3 != 3; >>> MarkAsFullyVerified -Processing command (at Snapshots5.v1.dfy(20,37)) assert {:id "id17"} (exists b#3_1: bool :: Lit(true)) || 4 != 4; +Processing command (at Snapshots5.v1.dfy(20,37)) assert {:id "id13"} (exists b#3_1: bool :: Lit(true)) || 4 != 4; >>> DoNothingToAssert -Processing command (at Snapshots5.v1.dfy(22,35)) assert {:id "id18"} (exists b#3: bool :: Lit(true)) || 5 != 5; +Processing command (at Snapshots5.v1.dfy(22,35)) assert {:id "id14"} (exists b#3: bool :: Lit(true)) || 5 != 5; >>> DoNothingToAssert Dafny program verifier finished with 1 verified, 0 errors From ae8d333238d199cb30e15d46f472e388360592ed Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 13:22:10 +0200 Subject: [PATCH 36/47] Use an immediately dictionary instead of adding and removing --- .../BoogieGenerator.DefiniteAssignment.cs | 43 ++----------------- .../Verifier/BoogieGenerator.Methods.cs | 8 ++-- Source/DafnyCore/Verifier/BoogieGenerator.cs | 8 ++-- .../Statements/BlockByProofStmtVerifier.cs | 4 +- .../Statements/BoogieGenerator.TrStatement.cs | 19 ++++---- .../Statements/OpaqueBlockVerifier.cs | 4 +- .../LitTests/LitTest/comp/CSharpStyling.dfy | 2 +- 7 files changed, 26 insertions(+), 62 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs index df600e3e8eb..bee82710a59 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs @@ -56,7 +56,7 @@ bool NeedsDefiniteAssignmentTracker(bool isGhost, Type type, bool isField) { tracker = localVariables.GetOrAdd(tracker); var ie = new Bpl.IdentifierExpr(p.Tok, tracker); - DefiniteAssignmentTrackers.Add(p.UniqueName, ie); + DefiniteAssignmentTrackers = DefiniteAssignmentTrackers.Add(p.UniqueName, ie); return ie; } @@ -72,7 +72,7 @@ void AddExistingDefiniteAssignmentTracker(IVariable p, bool forceGhostVar) { } var ie = new Bpl.IdentifierExpr(p.Tok, DefassPrefix + p.UniqueName, Bpl.Type.Bool); - DefiniteAssignmentTrackers.Add(p.UniqueName, ie); + DefiniteAssignmentTrackers = DefiniteAssignmentTrackers.Add(p.UniqueName, ie); } void AddDefiniteAssignmentTrackerSurrogate(Field field, TopLevelDeclWithMembers enclosingClass, @@ -88,44 +88,9 @@ void AddDefiniteAssignmentTrackerSurrogate(Field field, TopLevelDeclWithMembers var nm = SurrogateName(field); var tracker = localVariables.GetOrAdd(new Bpl.LocalVariable(field.tok, new Bpl.TypedIdent(field.tok, DefassPrefix + nm, Bpl.Type.Bool))); var ie = new Bpl.IdentifierExpr(field.tok, tracker); - DefiniteAssignmentTrackers.Add(nm, ie); + DefiniteAssignmentTrackers = DefiniteAssignmentTrackers.Add(nm, ie); } - - public void RemoveDefiniteAssignmentTrackers(List ss, int prevDefAssTrackerCount) { - Contract.Requires(ss != null); - foreach (var s in ss) { - if (s is VarDeclStmt vdecl) { - if (vdecl.Assign is AssignOrReturnStmt ars) { - foreach (var sx in ars.ResolvedStatements) { - if (sx is VarDeclStmt vdecl2) { - vdecl2.Locals.ForEach(RemoveDefiniteAssignmentTracker); - } - } - } - - vdecl.Locals.ForEach(RemoveDefiniteAssignmentTracker); - } else if (s is AssignOrReturnStmt ars) { - foreach (var sx in ars.ResolvedStatements) { - if (sx is VarDeclStmt vdecl2) { - vdecl2.Locals.ForEach(RemoveDefiniteAssignmentTracker); - } - } - } - } - - Contract.Assert(prevDefAssTrackerCount == DefiniteAssignmentTrackers.Count); - } - - void RemoveDefiniteAssignmentTracker(IVariable p) { - Contract.Requires(p != null); - DefiniteAssignmentTrackers.Remove(p.UniqueName); - } - - void RemoveDefiniteAssignmentTrackerSurrogate(Field field) { - Contract.Requires(field != null); - DefiniteAssignmentTrackers.Remove(SurrogateName(field)); - } - + void MarkDefiniteAssignmentTracker(IdentifierExpr expr, BoogieStmtListBuilder builder) { Contract.Requires(expr != null); Contract.Requires(builder != null); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 678c9307adb..9518c130ab4 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -798,7 +798,8 @@ private StmtList TrMethodBody(Method m, BoogieStmtListBuilder builder, Variables // $_reverifyPost := false; builder.Add(Boogie.Cmd.SimpleAssign(m.tok, new Boogie.IdentifierExpr(m.tok, "$_reverifyPost", Boogie.Type.Bool), Boogie.Expr.False)); // register output parameters with definite-assignment trackers - Contract.Assert(DefiniteAssignmentTrackers.Count == 0); + + var beforeOutTrackers = DefiniteAssignmentTrackers; m.Outs.ForEach(p => AddExistingDefiniteAssignmentTracker(p, m.IsGhost)); // translate the body TrStmt(m.Body, builder, localVariables, etran); @@ -807,10 +808,7 @@ private StmtList TrMethodBody(Method m, BoogieStmtListBuilder builder, Variables AssumeCanCallForByMethodDecl(m, builder); } var stmts = builder.Collect(m.Body.RangeToken.StartToken); // EndToken might make more sense, but it requires updating most of the regression tests. - // tear down definite-assignment trackers - m.Outs.ForEach(RemoveDefiniteAssignmentTracker); - - Contract.Assert(DefiniteAssignmentTrackers.Count == 0); + DefiniteAssignmentTrackers = beforeOutTrackers; return stmts; } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index 3b16e6a333b..c91398cde5f 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -7,6 +7,7 @@ //----------------------------------------------------------------------------- using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Numerics; using System.Diagnostics.Contracts; @@ -1759,7 +1760,7 @@ Bpl.Expr InSeqRange(IToken tok, Bpl.Expr index, Type indexType, Bpl.Expr seq, bo Bpl.LocalVariable yieldCountVariable = null; // non-null when an iterator body is being translated bool inBodyInitContext = false; // true during the translation of the .BodyInit portion of a divided constructor body - public Dictionary DefiniteAssignmentTrackers { get; } = new(); + public ImmutableDictionary DefiniteAssignmentTrackers { get; set; } = ImmutableDictionary.Empty; Func assertionOnlyFilter = null; // generate assume statements instead of assert statements if not targeted by {:only} public enum StmtType { NONE, ASSERT, ASSUME }; @@ -2899,6 +2900,7 @@ private void GenerateMethodParametersChoose(IToken tok, IMethodCodeContext m, Me } } if (includeOutParams) { + var beforeTrackers = DefiniteAssignmentTrackers; Contract.Assume(DefiniteAssignmentTrackers.Count == 0); foreach (Formal p in m.Outs) { Contract.Assert(VisibleInScope(p.Type)); @@ -2914,10 +2916,8 @@ private void GenerateMethodParametersChoose(IToken tok, IMethodCodeContext m, Me } } outParams.GetOrAdd(new Bpl.Formal(p.tok, new Bpl.TypedIdent(p.tok, p.AssignUniqueName(currentDeclaration.IdGenerator), varType, wh), false)); + DefiniteAssignmentTrackers = beforeTrackers; } - // tear down definite-assignment trackers - m.Outs.ForEach(RemoveDefiniteAssignmentTracker); - Contract.Assert(DefiniteAssignmentTrackers.Count == 0); if (kind == MethodTranslationKind.Implementation) { outParams.GetOrAdd(new Bpl.Formal(tok, new Bpl.TypedIdent(tok, "$_reverifyPost", Bpl.Type.Bool), false)); diff --git a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs index 688300d1e9c..ff06686f171 100644 --- a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs +++ b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs @@ -11,11 +11,11 @@ public static void EmitBoogie(BoogieGenerator generator, BlockByProofStmt block, Variables locals, BoogieGenerator.ExpressionTranslator etran, ICodeContext codeContext) { var proofBuilder = new BoogieStmtListBuilder(generator, builder.Options, builder.Context); - var count = generator.DefiniteAssignmentTrackers.Count; + var previousTrackers = generator.DefiniteAssignmentTrackers; generator.CurrentIdGenerator.Push(); generator.TrStmtList(block.Proof.Body, proofBuilder, locals, etran); generator.CurrentIdGenerator.Pop(); - generator.RemoveDefiniteAssignmentTrackers(block.Proof.Body, count); + generator.DefiniteAssignmentTrackers = previousTrackers; generator.TrStmt(block.Body, proofBuilder, locals, etran); diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs index 8c795613ec5..1c34054edad 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrStatement.cs @@ -250,7 +250,7 @@ public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, } else if (stmt is DividedBlockStmt) { var s = (DividedBlockStmt)stmt; AddComment(builder, stmt, "divided block before new;"); - var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; + var previousTrackers = DefiniteAssignmentTrackers; var tok = s.SeparatorTok ?? s.Tok; // a DividedBlockStmt occurs only inside a Constructor body of a class var cl = (ClassDecl)((Constructor)codeContext).EnclosingClass; @@ -259,6 +259,7 @@ public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, fields.RemoveAll(f => f == null); var localSurrogates = fields.ConvertAll(f => new Bpl.LocalVariable(f.tok, new TypedIdent(f.tok, SurrogateName(f), TrType(f.Type)))); locals.AddRange(localSurrogates); + var beforeTrackers = DefiniteAssignmentTrackers; fields.ForEach(f => AddDefiniteAssignmentTrackerSurrogate(f, cl, locals, codeContext is Constructor && codeContext.IsGhost)); Contract.Assert(!inBodyInitContext); @@ -270,7 +271,7 @@ public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, // The "new;" translates into an allocation of "this" AddComment(builder, stmt, "new;"); fields.ForEach(f => CheckDefiniteAssignmentSurrogate(s.SeparatorTok ?? s.RangeToken.EndToken, f, true, builder)); - fields.ForEach(RemoveDefiniteAssignmentTrackerSurrogate); + DefiniteAssignmentTrackers = beforeTrackers; var th = new ThisExpr(cl); var bplThis = (Bpl.IdentifierExpr)etran.TrExpr(th); SelectAllocateObject(tok, bplThis, th.Type, false, builder, etran); @@ -285,16 +286,16 @@ public void TrStmt(Statement stmt, BoogieStmtListBuilder builder, AddComment(builder, stmt, "divided block after new;"); TrStmtList(s.BodyProper, builder, locals, etran); - RemoveDefiniteAssignmentTrackers(s.Body, prevDefiniteAssignmentTrackerCount); + DefiniteAssignmentTrackers = previousTrackers; } else if (stmt is OpaqueBlock opaqueBlock) { OpaqueBlockVerifier.EmitBoogie(this, opaqueBlock, builder, locals, etran, (IMethodCodeContext)codeContext); } else if (stmt is BlockByProofStmt blockByProof) { BlockByProofStmtVerifier.EmitBoogie(this, blockByProof, builder, locals, etran, codeContext); } else if (stmt is BlockStmt blockStmt) { - var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; + var previousTrackers = DefiniteAssignmentTrackers; TrStmtList(blockStmt.Body, builder, locals, etran, blockStmt.RangeToken); - RemoveDefiniteAssignmentTrackers(blockStmt.Body, prevDefiniteAssignmentTrackerCount); + DefiniteAssignmentTrackers = previousTrackers; } else if (stmt is IfStmt ifStmt) { TrIfStmt(ifStmt, builder, locals, etran); @@ -673,9 +674,9 @@ private void TrMatchStmt(MatchStmt stmt, BoogieStmtListBuilder builder, Variable } // translate the body into b - var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; + var prevDefiniteAssignmentTrackers = DefiniteAssignmentTrackers; TrStmtList(mc.Body, b, locals, etran); - RemoveDefiniteAssignmentTrackers(mc.Body, prevDefiniteAssignmentTrackerCount); + DefiniteAssignmentTrackers = prevDefiniteAssignmentTrackers; Bpl.Expr guard = Bpl.Expr.Eq(source, r); ifCmd = new Bpl.IfCmd(mc.tok, guard, b.Collect(mc.tok), ifCmd, els); @@ -835,9 +836,9 @@ void TrAlternatives(List alternatives, IToken elseToken, Act } else { b.Add(TrAssumeCmdWithDependencies(etran, alternative.Guard.tok, alternative.Guard, "alternative guard")); } - var prevDefiniteAssignmentTrackerCount = DefiniteAssignmentTrackers.Count; + var prevDefiniteAssignmentTrackers = DefiniteAssignmentTrackers; TrStmtList(alternative.Body, b, locals, etran, alternative.RangeToken); - RemoveDefiniteAssignmentTrackers(alternative.Body, prevDefiniteAssignmentTrackerCount); + DefiniteAssignmentTrackers = prevDefiniteAssignmentTrackers; Bpl.StmtList thn = b.Collect(alternative.Tok); elsIf = new Bpl.IfCmd(alternative.Tok, null, thn, elsIf, els); els = null; diff --git a/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs b/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs index d1dae58c9b5..1035b84138f 100644 --- a/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs +++ b/Source/DafnyCore/Verifier/Statements/OpaqueBlockVerifier.cs @@ -14,9 +14,9 @@ public static void EmitBoogie(BoogieGenerator generator, OpaqueBlock block, Boog var blockBuilder = new BoogieStmtListBuilder(generator, builder.Options, builder.Context); var bodyTranslator = GetBodyTranslator(generator, block, locals, etran, hasModifiesClause, blockBuilder); - var prevDefiniteAssignmentTrackerCount = generator.DefiniteAssignmentTrackers.Count; + var prevDefiniteAssignmentTrackers = generator.DefiniteAssignmentTrackers; generator.TrStmtList(block.Body, blockBuilder, locals, bodyTranslator, block.RangeToken); - generator.RemoveDefiniteAssignmentTrackers(block.Body, prevDefiniteAssignmentTrackerCount); + generator.DefiniteAssignmentTrackers = prevDefiniteAssignmentTrackers; var assignedVariables = block.DescendantsAndSelf. SelectMany(s => s.GetAssignedLocals()).Select(ie => ie.Var) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/comp/CSharpStyling.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/comp/CSharpStyling.dfy index 06b9677b860..a9ec4d41420 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/comp/CSharpStyling.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/comp/CSharpStyling.dfy @@ -1,4 +1,4 @@ -// RUN: %run "%s" --input %S/CSharpStyling2.cs "%t" +// RUN: %run "%s" --input %S/CSharpStyling2.cs > "%t" // RUN: %diff "%s.expect" "%t" method Main() { From a4796d2ddc6c877ab32309130ac301695af3806d Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 13:37:35 +0200 Subject: [PATCH 37/47] Fix test generation --- Source/DafnyCore/AST/Expressions/StmtExpr.cs | 3 +-- Source/DafnyCore/Resolver/ModuleResolver.cs | 13 ++++++------- .../Verifier/BoogieGenerator.DefiniteAssignment.cs | 2 +- .../DafnyCore/Verifier/BoogieGenerator.Methods.cs | 3 +-- Source/DafnyCore/Verifier/BoogieGenerator.cs | 5 +++-- .../Inlining/AddImplementationForCallsRewriter.cs | 3 +-- 6 files changed, 13 insertions(+), 16 deletions(-) diff --git a/Source/DafnyCore/AST/Expressions/StmtExpr.cs b/Source/DafnyCore/AST/Expressions/StmtExpr.cs index 6d2e2f2ada4..ff34de75e37 100644 --- a/Source/DafnyCore/AST/Expressions/StmtExpr.cs +++ b/Source/DafnyCore/AST/Expressions/StmtExpr.cs @@ -58,8 +58,7 @@ public Expression GetStatementConclusion() { } private Expression GetStatementConclusion(Statement statement) { - switch (statement) - { + switch (statement) { // this is one place where we actually investigate what kind of statement .S is case PredicateStmt stmt: return stmt.Expr; diff --git a/Source/DafnyCore/Resolver/ModuleResolver.cs b/Source/DafnyCore/Resolver/ModuleResolver.cs index 6f9d6510aeb..22de8d861cd 100644 --- a/Source/DafnyCore/Resolver/ModuleResolver.cs +++ b/Source/DafnyCore/Resolver/ModuleResolver.cs @@ -2003,15 +2003,14 @@ protected override void VisitOneExpr(Expression expr) { } protected override void VisitOneStmt(Statement stmt) { - switch (stmt) - { + switch (stmt) { case CalcStmt calc: { - foreach (var h in calc.Hints) { - resolver.CheckLocalityUpdates(h, new HashSet(), "a hint"); - } + foreach (var h in calc.Hints) { + resolver.CheckLocalityUpdates(h, new HashSet(), "a hint"); + } - break; - } + break; + } case BlockByProofStmt blockByProofStmt: resolver.CheckLocalityUpdates(blockByProofStmt.Proof, new HashSet(), "a by block"); break; diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs index bee82710a59..87fb7cb6cbb 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.DefiniteAssignment.cs @@ -90,7 +90,7 @@ void AddDefiniteAssignmentTrackerSurrogate(Field field, TopLevelDeclWithMembers var ie = new Bpl.IdentifierExpr(field.tok, tracker); DefiniteAssignmentTrackers = DefiniteAssignmentTrackers.Add(nm, ie); } - + void MarkDefiniteAssignmentTracker(IdentifierExpr expr, BoogieStmtListBuilder builder) { Contract.Requires(expr != null); Contract.Requires(builder != null); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 9518c130ab4..d6fc9b6adaf 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -558,8 +558,7 @@ private void AddMethodImpl(Method m, Bpl.Procedure proc, bool wellformednessProc if (UseOptimizationInZ3) { // We ask Z3 to minimize all parameters of type 'nat'. foreach (var f in m.Ins) { - var udt = f.Type.NormalizeExpandKeepConstraints() as UserDefinedType; - if (udt != null && udt.Name == "nat") { + if (f.Type.NormalizeExpandKeepConstraints() is UserDefinedType udt && udt.Name == "nat") { builder.Add(optimizeExpr(true, new IdentifierExpr(f.tok, f), f.Tok, etran)); } } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index c91398cde5f..c5ad4911a39 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -29,6 +29,7 @@ public partial class BoogieGenerator { private DafnyOptions options; public DafnyOptions Options => options; public const string NameSeparator = "$$"; + public const string CallPrefix = "Call"; private bool filterOnlyMembers; ErrorReporter reporter; @@ -2803,7 +2804,7 @@ enum MethodTranslationKind { SpecWellformedness, Call, CoCall, Implementation, O private static readonly Dictionary kindSanitizedPrefix = new() { { MethodTranslationKind.SpecWellformedness, "CheckWellFormed" }, - { MethodTranslationKind.Call, "Call" }, + { MethodTranslationKind.Call, CallPrefix }, { MethodTranslationKind.CoCall, "CoCall" }, { MethodTranslationKind.Implementation, "Impl" }, { MethodTranslationKind.OverrideCheck, "OverrideCheck" }, @@ -2815,7 +2816,7 @@ static string MethodName(ICodeContext m, MethodTranslationKind kind) { } private static readonly Dictionary kindDescription = - new Dictionary() { + new() { {MethodTranslationKind.SpecWellformedness, "well-formedness"}, {MethodTranslationKind.Call, "call"}, {MethodTranslationKind.CoCall, "co-call"}, diff --git a/Source/DafnyTestGeneration/Inlining/AddImplementationForCallsRewriter.cs b/Source/DafnyTestGeneration/Inlining/AddImplementationForCallsRewriter.cs index 9513ddb806f..f601b316ba8 100644 --- a/Source/DafnyTestGeneration/Inlining/AddImplementationForCallsRewriter.cs +++ b/Source/DafnyTestGeneration/Inlining/AddImplementationForCallsRewriter.cs @@ -21,7 +21,6 @@ namespace DafnyTestGeneration.Inlining; /// public class AddImplementationsForCallsRewriter : ReadOnlyVisitor { - private const string CallPrefix = "CallPost$$"; private readonly DafnyOptions options; private List implsToAdd = new(); @@ -32,7 +31,7 @@ public AddImplementationsForCallsRewriter(DafnyOptions options) { } public override Procedure /*?*/ VisitProcedure(Procedure /*?*/ node) { - if (node == null || !node.Name.StartsWith(CallPrefix) || + if (node == null || !node.Name.StartsWith(BoogieGenerator.CallPrefix + BoogieGenerator.NameSeparator) || node.Name.EndsWith(ProgramModifier.CtorPostfix)) { return node; } From 8ab5cb7f980d6148f8763ea2e0b1d2511342e135 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 13:44:09 +0200 Subject: [PATCH 38/47] Refactoring --- .../AST/Grammar/Printer/Printer.Statement.cs | 15 ++--- .../DafnyCore/AST/Grammar/Printer/Printer.cs | 4 +- .../AST/Grammar/TokenNewIndentCollector.cs | 6 +- Source/DafnyCore/Generic/OrderedDictionary.cs | 50 +++++++++++++++++ .../Resolver/GhostInterestVisitor.cs | 2 - Source/DafnyCore/Resolver/TailRecursion.cs | 2 - .../Statements/BlockByProofStmtVerifier.cs | 55 ------------------- Source/DafnyCore/Verifier/Variables.cs | 9 +++ 8 files changed, 73 insertions(+), 70 deletions(-) create mode 100644 Source/DafnyCore/Generic/OrderedDictionary.cs create mode 100644 Source/DafnyCore/Verifier/Variables.cs diff --git a/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs b/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs index c0c0e0b0ff0..c3cf51fd2bb 100644 --- a/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs +++ b/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs @@ -140,7 +140,9 @@ public void PrintStatement(Statement stmt, int indent, bool includeSemicolon = t PrintSpec("invariant", s.Invariants, indent + IndentAmount); PrintDecreasesSpec(s.Decreases, indent + IndentAmount); PrintFrameSpecLine("modifies", s.Mod, indent + IndentAmount); - bool hasSpecs = s.Invariants.Count != 0 || (s.Decreases.Expressions != null && s.Decreases.Expressions.Count != 0) || s.Mod.Expressions != null; + bool hasSpecs = s.Invariants.Count != 0 || + (s.Decreases.Expressions != null && s.Decreases.Expressions.Count != 0) || + s.Mod.Expressions != null; if (s.UsesOptionalBraces) { if (hasSpecs) { wr.WriteLine(); @@ -229,7 +231,7 @@ public void PrintStatement(Statement stmt, int indent, bool includeSemicolon = t } // print the operator, if any if (op != null || (options.DafnyPrintResolvedFile != null && s.Op != null)) { - Indent(indent); // this lines up with the "calc" + Indent(indent); // this lines up with the "calc" PrintCalcOp(op ?? s.Op); wr.WriteLine(); } @@ -425,18 +427,17 @@ public void PrintStatement(Statement stmt, int indent, bool includeSemicolon = t } } - public void PrintConcreteUpdateStatement(Statement stmt, int indent, bool includeSemicolon = true) { - var s = (ConcreteAssignStatement)stmt; + public void PrintConcreteUpdateStatement(ConcreteAssignStatement stmt, int indent, bool includeSemicolon = true) { string sep = ""; - foreach (var lhs in s.Lhss) { + foreach (var lhs in stmt.Lhss) { wr.Write(sep); PrintExpression(lhs, true); sep = ", "; } - if (s.Lhss.Count > 0) { + if (stmt.Lhss.Count > 0) { wr.Write(" "); } - PrintUpdateRHS(s, indent); + PrintUpdateRHS(stmt, indent); if (includeSemicolon) { wr.Write(";"); } diff --git a/Source/DafnyCore/AST/Grammar/Printer/Printer.cs b/Source/DafnyCore/AST/Grammar/Printer/Printer.cs index 5cd7c07e953..87afafd59a9 100644 --- a/Source/DafnyCore/AST/Grammar/Printer/Printer.cs +++ b/Source/DafnyCore/AST/Grammar/Printer/Printer.cs @@ -1112,7 +1112,9 @@ void PrintFormal(Formal f, bool showNewKeyword) { internal void PrintDecreasesSpec(Specification decs, int indent) { Contract.Requires(decs != null); - if (printMode == PrintModes.NoGhostOrIncludes) { return; } + if (printMode == PrintModes.NoGhostOrIncludes) { + return; + } if (decs.Expressions != null && decs.Expressions.Count != 0) { wr.WriteLine(); Indent(indent); diff --git a/Source/DafnyCore/AST/Grammar/TokenNewIndentCollector.cs b/Source/DafnyCore/AST/Grammar/TokenNewIndentCollector.cs index 81fe8b1fa38..e1e9a0ee645 100644 --- a/Source/DafnyCore/AST/Grammar/TokenNewIndentCollector.cs +++ b/Source/DafnyCore/AST/Grammar/TokenNewIndentCollector.cs @@ -559,9 +559,9 @@ public bool SetIndentAssertLikeStatement(Statement stmt, int indent) { } } - // if (stmt is AssertStmt { Proof: { StartToken: { } startToken } }) { - // SetOpeningIndentedRegion(startToken, indent); - // } + if (stmt is BlockByProofStmt { Proof: { StartToken: { } startToken } }) { + SetOpeningIndentedRegion(startToken, indent); + } return true; } diff --git a/Source/DafnyCore/Generic/OrderedDictionary.cs b/Source/DafnyCore/Generic/OrderedDictionary.cs new file mode 100644 index 00000000000..2f6d72621c5 --- /dev/null +++ b/Source/DafnyCore/Generic/OrderedDictionary.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Dafny; + +public class OrderedDictionary { + private readonly Dictionary keyToValue = new(); + private readonly List keyOrder = new(); + private readonly Func getKey; + + public OrderedDictionary(Func getKey) { + this.getKey = getKey; + } + public IEnumerable Values => keyOrder.Select(key => keyToValue[key]); + + public void AddRange(IEnumerable values) { + foreach (var value in values) { + Add(value); + } + } + + public TValue GetOrAdd(TValue value) { + var key = getKey(value); + return GetOrCreate(key, () => value); + } + + public TValue GetOrCreate(TKey key, Func createValue) { + if (keyToValue.TryGetValue(key, out var result)) { + return result; + } + + result = createValue(); + keyToValue[key] = result; + keyOrder.Add(key); + return result; + } + + public void Add(TValue value) { + var key = getKey(value); + keyOrder.Add(key); + keyToValue[key] = value; + } + + public TValue GetValueOrDefault(TKey key) { + return keyToValue.GetValueOrDefault(key); + } + + public int Count => keyOrder.Count; +} \ No newline at end of file diff --git a/Source/DafnyCore/Resolver/GhostInterestVisitor.cs b/Source/DafnyCore/Resolver/GhostInterestVisitor.cs index d9d9f1a7f69..985bc783d19 100644 --- a/Source/DafnyCore/Resolver/GhostInterestVisitor.cs +++ b/Source/DafnyCore/Resolver/GhostInterestVisitor.cs @@ -523,8 +523,6 @@ public void Visit(Statement stmt, bool mustBeErasable, [CanBeNull] string proofC break; } case BlockByProofStmt blockByProofStmt: - // TODO move to the BlockByProofStmt class - blockByProofStmt.IsGhost = mustBeErasable; // set .IsGhost before descending into substatements (since substatements may do a 'break' out of this block) Visit(blockByProofStmt.Body, mustBeErasable, proofContext); blockByProofStmt.IsGhost = blockByProofStmt.IsGhost || blockByProofStmt.Body.IsGhost; diff --git a/Source/DafnyCore/Resolver/TailRecursion.cs b/Source/DafnyCore/Resolver/TailRecursion.cs index b9ed5f9c242..4fabda02dd6 100644 --- a/Source/DafnyCore/Resolver/TailRecursion.cs +++ b/Source/DafnyCore/Resolver/TailRecursion.cs @@ -268,8 +268,6 @@ TailRecursionStatus CheckTailRecursive(Statement stmt, Method enclosingMethod, r } else if (stmt is VarDeclPattern) { } else if (stmt is ExpectStmt) { } else if (stmt is BlockByProofStmt blockByProofStmt) { - // TODO something with the by block? - // TODO Move this to BlockByProofStmt class ? return CheckTailRecursive(blockByProofStmt.Body, enclosingMethod, ref tailCall, reportErrors); } else { Contract.Assert(false); // unexpected statement type diff --git a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs index ff06686f171..c104e23608a 100644 --- a/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs +++ b/Source/DafnyCore/Verifier/Statements/BlockByProofStmtVerifier.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Boogie; using Microsoft.Dafny; namespace Microsoft.Dafny; @@ -25,55 +21,4 @@ public static void EmitBoogie(BoogieGenerator generator, BlockByProofStmt block, }), locals, etran); } -} - -public class Variables : OrderedDictionary { - public Variables() : base(v => v.Name) { - } - -} - -public class OrderedDictionary { - private readonly Dictionary keyToValue = new(); - private readonly List keyOrder = new(); - private readonly Func getKey; - - public OrderedDictionary(Func getKey) { - this.getKey = getKey; - } - public IEnumerable Values => keyOrder.Select(key => keyToValue[key]); - - public void AddRange(IEnumerable values) { - foreach (var value in values) { - Add(value); - } - } - - public TValue GetOrAdd(TValue value) { - var key = getKey(value); - return GetOrCreate(key, () => value); - } - - public TValue GetOrCreate(TKey key, Func createValue) { - if (keyToValue.TryGetValue(key, out var result)) { - return result; - } - - result = createValue(); - keyToValue[key] = result; - keyOrder.Add(key); - return result; - } - - public void Add(TValue value) { - var key = getKey(value); - keyOrder.Add(key); - keyToValue[key] = value; - } - - public TValue GetValueOrDefault(TKey key) { - return keyToValue.GetValueOrDefault(key); - } - - public int Count => keyOrder.Count; } \ No newline at end of file diff --git a/Source/DafnyCore/Verifier/Variables.cs b/Source/DafnyCore/Verifier/Variables.cs new file mode 100644 index 00000000000..bd112bd9d83 --- /dev/null +++ b/Source/DafnyCore/Verifier/Variables.cs @@ -0,0 +1,9 @@ +using Microsoft.Boogie; + +namespace Microsoft.Dafny; + +public class Variables : OrderedDictionary { + public Variables() : base(v => v.Name) { + } + +} \ No newline at end of file From bdb02ae0ac8fb9fdf00402ac3b6ba86d3375be44 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 13:50:08 +0200 Subject: [PATCH 39/47] Update test --- .../DafnyCore/AST/Grammar/Printer/Printer.Statement.cs | 4 ++-- .../TestFiles/LitTests/LitTest/dafny0/AssertBy.dfy | 6 ++++++ .../LitTests/LitTest/dafny0/AssertBy.dfy.expect | 9 ++++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs b/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs index c3cf51fd2bb..4e02df6e126 100644 --- a/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs +++ b/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs @@ -331,8 +331,8 @@ public void PrintStatement(Statement stmt, int indent, bool includeSemicolon = t wr.Write("}"); } - } else if (stmt is ConcreteAssignStatement) { - PrintConcreteUpdateStatement(stmt, indent, includeSemicolon); + } else if (stmt is ConcreteAssignStatement concreteAssignStatement) { + PrintConcreteUpdateStatement(concreteAssignStatement, indent, includeSemicolon); } else if (stmt is CallStmt) { // Most calls are printed from their concrete syntax given in the input. However, recursive calls to // prefix lemmas end up as CallStmt's by the end of resolution and they may need to be printed here. diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AssertBy.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AssertBy.dfy index c0086af0120..d7f12194342 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AssertBy.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AssertBy.dfy @@ -15,3 +15,9 @@ method M1(x: int, y: int) } assert y == 8; // error (yes, still -- the previous assumption should not be in effect here) } + +method WellFormedness(x: int) { + assert 3 / x == 1by { + assume x == 3; + } +} \ No newline at end of file diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AssertBy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AssertBy.dfy.expect index 93e5a7c7672..78047077579 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AssertBy.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AssertBy.dfy.expect @@ -14,8 +14,15 @@ method M1(x: int, y: int) } assert y == 8; } + +method WellFormedness(x: int) +{ + assert 3 / x == 1 by { + assume x == 3; + } +} AssertBy.dfy(6,11): Error: assertion might not hold AssertBy.dfy(7,11): Error: assertion might not hold AssertBy.dfy(16,11): Error: assertion might not hold -Dafny program verifier finished with 0 verified, 3 errors +Dafny program verifier finished with 1 verified, 3 errors From 2f189bb49e0c23873d65290acf56a206f01f598a Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 14:02:38 +0200 Subject: [PATCH 40/47] Add test-case for assign such that --- .../LitTest/{dafny0 => ast/statement}/AssertBy.dfy | 0 .../LitTest/{dafny0 => ast/statement}/AssertBy.dfy.expect | 0 .../LitTests/LitTest/ast/statement/assignSuchThat.dfy | 8 ++++++++ .../LitTest/ast/statement/assignSuchThat.dfy.expect | 2 ++ .../LitTest/{dafny0 => ast/statement/calls}/CallBy.dfy | 6 ++++++ .../{dafny0 => ast/statement/calls}/CallBy.dfy.expect | 2 +- .../{dafny0 => ast/statement/calls}/CallByHide.dfy | 0 .../{dafny0 => ast/statement/calls}/CallByHide.dfy.expect | 0 .../{dafny0 => ast/statement/calls}/CallByResolution0.dfy | 0 .../statement/calls}/CallByResolution0.dfy.expect | 0 .../{dafny0 => ast/statement/calls}/CallByResolution1.dfy | 0 .../statement/calls}/CallByResolution1.dfy.expect | 0 12 files changed, 17 insertions(+), 1 deletion(-) rename Source/IntegrationTests/TestFiles/LitTests/LitTest/{dafny0 => ast/statement}/AssertBy.dfy (100%) rename Source/IntegrationTests/TestFiles/LitTests/LitTest/{dafny0 => ast/statement}/AssertBy.dfy.expect (100%) create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignSuchThat.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignSuchThat.dfy.expect rename Source/IntegrationTests/TestFiles/LitTests/LitTest/{dafny0 => ast/statement/calls}/CallBy.dfy (93%) rename Source/IntegrationTests/TestFiles/LitTests/LitTest/{dafny0 => ast/statement/calls}/CallBy.dfy.expect (84%) rename Source/IntegrationTests/TestFiles/LitTests/LitTest/{dafny0 => ast/statement/calls}/CallByHide.dfy (100%) rename Source/IntegrationTests/TestFiles/LitTests/LitTest/{dafny0 => ast/statement/calls}/CallByHide.dfy.expect (100%) rename Source/IntegrationTests/TestFiles/LitTests/LitTest/{dafny0 => ast/statement/calls}/CallByResolution0.dfy (100%) rename Source/IntegrationTests/TestFiles/LitTests/LitTest/{dafny0 => ast/statement/calls}/CallByResolution0.dfy.expect (100%) rename Source/IntegrationTests/TestFiles/LitTests/LitTest/{dafny0 => ast/statement/calls}/CallByResolution1.dfy (100%) rename Source/IntegrationTests/TestFiles/LitTests/LitTest/{dafny0 => ast/statement/calls}/CallByResolution1.dfy.expect (100%) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AssertBy.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/AssertBy.dfy similarity index 100% rename from Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AssertBy.dfy rename to Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/AssertBy.dfy diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AssertBy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/AssertBy.dfy.expect similarity index 100% rename from Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AssertBy.dfy.expect rename to Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/AssertBy.dfy.expect diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignSuchThat.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignSuchThat.dfy new file mode 100644 index 00000000000..036fbb219d6 --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignSuchThat.dfy @@ -0,0 +1,8 @@ +// RUN: %verify %s &> "%t" +// RUN: %diff "%s.expect" "%t" + +method AssignToNat(b: bool) { + var r: int :| false by { + assume {:axiom} false; + } +} \ No newline at end of file diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignSuchThat.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignSuchThat.dfy.expect new file mode 100644 index 00000000000..823a60a105c --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignSuchThat.dfy.expect @@ -0,0 +1,2 @@ + +Dafny program verifier finished with 1 verified, 0 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallBy.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallBy.dfy similarity index 93% rename from Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallBy.dfy rename to Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallBy.dfy index 6d83e1da819..c35d7cea7d8 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallBy.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallBy.dfy @@ -64,4 +64,10 @@ greatest lemma F(x: int) { F(x-2) by { ProveP(); } assert P(); // should fail +} + +method ArgumentWellformedness(x: int) { + F(3 / x) by { + assume x > 0; + } } \ No newline at end of file diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallBy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallBy.dfy.expect similarity index 84% rename from Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallBy.dfy.expect rename to Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallBy.dfy.expect index b4849841fcd..8dbe5b18e58 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallBy.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallBy.dfy.expect @@ -5,4 +5,4 @@ CallBy.dfy(50,10): Error: assertion might not hold CallBy.dfy(56,10): Error: assertion might not hold CallBy.dfy(66,10): Error: assertion might not hold -Dafny program verifier finished with 3 verified, 6 errors +Dafny program verifier finished with 4 verified, 6 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByHide.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByHide.dfy similarity index 100% rename from Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByHide.dfy rename to Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByHide.dfy diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByHide.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByHide.dfy.expect similarity index 100% rename from Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByHide.dfy.expect rename to Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByHide.dfy.expect diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByResolution0.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByResolution0.dfy similarity index 100% rename from Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByResolution0.dfy rename to Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByResolution0.dfy diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByResolution0.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByResolution0.dfy.expect similarity index 100% rename from Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByResolution0.dfy.expect rename to Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByResolution0.dfy.expect diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByResolution1.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByResolution1.dfy similarity index 100% rename from Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByResolution1.dfy rename to Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByResolution1.dfy diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByResolution1.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByResolution1.dfy.expect similarity index 100% rename from Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CallByResolution1.dfy.expect rename to Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByResolution1.dfy.expect From 880fa30165ebadc146cd0b284e9a52b3ad1dc8ba Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 14:09:28 +0200 Subject: [PATCH 41/47] Add tests --- .../AST/Grammar/Printer/Printer.Statement.cs | 4 ++-- .../LitTest/ast/statement/assignOrReturn.dfy | 12 ++++++++++++ .../LitTest/ast/statement/assignOrReturn.dfy.expect | 2 ++ .../LitTest/ast/statement/assignSuchThat.dfy | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignOrReturn.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignOrReturn.dfy.expect diff --git a/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs b/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs index 4e02df6e126..faf9c3104e8 100644 --- a/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs +++ b/Source/DafnyCore/AST/Grammar/Printer/Printer.Statement.cs @@ -140,8 +140,8 @@ public void PrintStatement(Statement stmt, int indent, bool includeSemicolon = t PrintSpec("invariant", s.Invariants, indent + IndentAmount); PrintDecreasesSpec(s.Decreases, indent + IndentAmount); PrintFrameSpecLine("modifies", s.Mod, indent + IndentAmount); - bool hasSpecs = s.Invariants.Count != 0 || - (s.Decreases.Expressions != null && s.Decreases.Expressions.Count != 0) || + bool hasSpecs = s.Invariants.Count != 0 || + (s.Decreases.Expressions != null && s.Decreases.Expressions.Count != 0) || s.Mod.Expressions != null; if (s.UsesOptionalBraces) { if (hasSpecs) { diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignOrReturn.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignOrReturn.dfy new file mode 100644 index 00000000000..a6b739c14e9 --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignOrReturn.dfy @@ -0,0 +1,12 @@ +// RUN: %verify %s --standard-libraries=true &> "%t" +// RUN: %diff "%s.expect" "%t" + +import opened Std.Wrappers + +method ByWellformedness(x: int) returns (r: Option) { + var p: int :- Some(3 / x) by { + assume {:axiom} x > 0; + } + var q: int :- None; + r := Some(4); +} \ No newline at end of file diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignOrReturn.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignOrReturn.dfy.expect new file mode 100644 index 00000000000..823a60a105c --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignOrReturn.dfy.expect @@ -0,0 +1,2 @@ + +Dafny program verifier finished with 1 verified, 0 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignSuchThat.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignSuchThat.dfy index 036fbb219d6..43edfe82cb3 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignSuchThat.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignSuchThat.dfy @@ -1,7 +1,7 @@ // RUN: %verify %s &> "%t" // RUN: %diff "%s.expect" "%t" -method AssignToNat(b: bool) { +method ByClause(b: bool) { var r: int :| false by { assume {:axiom} false; } From 95a09a2eb09c7e107b3d852f83745f37b36069c9 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 14:11:52 +0200 Subject: [PATCH 42/47] Add another test --- .../LitTests/LitTest/ast/statement/assignment.dfy | 8 ++++++++ .../LitTests/LitTest/ast/statement/assignment.dfy.expect | 2 ++ 2 files changed, 10 insertions(+) create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignment.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignment.dfy.expect diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignment.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignment.dfy new file mode 100644 index 00000000000..a602eee60fe --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignment.dfy @@ -0,0 +1,8 @@ +// RUN: %verify %s &> "%t" +// RUN: %diff "%s.expect" "%t" + +method ByWellformedness(x: int) { + var p: int := 3 / x by { + assume {:axiom} x > 0; + } +} \ No newline at end of file diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignment.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignment.dfy.expect new file mode 100644 index 00000000000..823a60a105c --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/assignment.dfy.expect @@ -0,0 +1,2 @@ + +Dafny program verifier finished with 1 verified, 0 errors From 13a25b3a94e2b0ddb38e184c47cd3f3da72a88b1 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 25 Sep 2024 10:04:10 +0200 Subject: [PATCH 43/47] Updates --- .../AST/Statements/BlockByProofStmt.cs | 2 - .../Resolver/CheckLocalityVisitor.cs | 45 ++++ Source/DafnyCore/Resolver/ModuleResolver.cs | 205 ++++++++---------- .../BoogieGenerator.TrPredicateStatement.cs | 18 +- .../binaries/DafnyStandardLibraries-cs.doo | Bin 1519 -> 1519 bytes .../binaries/DafnyStandardLibraries-go.doo | Bin 1540 -> 1540 bytes .../binaries/DafnyStandardLibraries-java.doo | Bin 1510 -> 1510 bytes .../binaries/DafnyStandardLibraries-js.doo | Bin 2028 -> 2028 bytes .../DafnyStandardLibraries-notarget.doo | Bin 1499 -> 1499 bytes .../binaries/DafnyStandardLibraries-py.doo | Bin 1506 -> 1506 bytes .../binaries/DafnyStandardLibraries.doo | Bin 57214 -> 57214 bytes .../examples/JSON/JSONExamples.dfy | 25 ++- .../calls/CallByResolution0.dfy.expect | 3 +- 13 files changed, 153 insertions(+), 145 deletions(-) create mode 100644 Source/DafnyCore/Resolver/CheckLocalityVisitor.cs diff --git a/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs b/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs index cd87df77c3e..1d90222c84b 100644 --- a/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs +++ b/Source/DafnyCore/AST/Statements/BlockByProofStmt.cs @@ -25,8 +25,6 @@ public override void GenResolve(INewOrOldResolver resolver, ResolutionContext re base.GenResolve(resolver, resolutionContext); } - // CheckLocalityUpdates - internal static void ResolveByProof(INewOrOldResolver resolver, BlockStmt proof, ResolutionContext resolutionContext) { if (proof == null) { return; diff --git a/Source/DafnyCore/Resolver/CheckLocalityVisitor.cs b/Source/DafnyCore/Resolver/CheckLocalityVisitor.cs new file mode 100644 index 00000000000..e4c26870e88 --- /dev/null +++ b/Source/DafnyCore/Resolver/CheckLocalityVisitor.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; + +namespace Microsoft.Dafny; + +class CheckLocalityVisitor : ResolverBottomUpVisitor { + readonly ICodeContext codeContext; + public CheckLocalityVisitor(ModuleResolver resolver, ICodeContext codeContext) + : base(resolver) { + Contract.Requires(resolver != null); + Contract.Requires(codeContext != null); + this.codeContext = codeContext; + } + protected override void VisitOneExpr(Expression expr) { + if (expr is StmtExpr) { + var e = (StmtExpr)expr; + resolver.ComputeGhostInterest(e.S, true, "a statement expression", codeContext); + } else if (expr is LetExpr) { + var e = (LetExpr)expr; + if (codeContext.IsGhost) { + foreach (var bv in e.BoundVars) { + bv.MakeGhost(); + } + } + } + } + + protected override void VisitOneStmt(Statement stmt) { + switch (stmt) { + case CalcStmt calc: { + foreach (var h in calc.Hints) { + resolver.CheckLocalityUpdates(h, new HashSet(), "a hint"); + } + + break; + } + case BlockByProofStmt blockByProofStmt: + resolver.CheckLocalityUpdates(blockByProofStmt.Proof, new HashSet(), "a by block"); + break; + case ForallStmt { Body: not null } forall: + resolver.CheckLocalityUpdates(forall.Body, new HashSet(), "a forall statement"); + break; + } + } +} \ No newline at end of file diff --git a/Source/DafnyCore/Resolver/ModuleResolver.cs b/Source/DafnyCore/Resolver/ModuleResolver.cs index 22de8d861cd..249f82d2967 100644 --- a/Source/DafnyCore/Resolver/ModuleResolver.cs +++ b/Source/DafnyCore/Resolver/ModuleResolver.cs @@ -1221,81 +1221,8 @@ public void ResolveTopLevelDecls_Core(List declarations, } } - // Compute ghost interests, figure out native types, check agreement among datatype destructors, and determine tail calls. if (reporter.Count(ErrorLevel.Error) == prevErrorCount) { - foreach (TopLevelDecl d in declarations) { - if (d is IteratorDecl) { - var iter = (IteratorDecl)d; - iter.SubExpressions.ForEach(e => CheckExpression(e, this, iter)); - if (iter.Body != null) { - CheckExpression(iter.Body, this, iter); - } - - } else if (d is SubsetTypeDecl subsetTypeDecl) { - Contract.Assert(subsetTypeDecl.Constraint != null); - CheckExpression(subsetTypeDecl.Constraint, this, new CodeContextWrapper(subsetTypeDecl, true)); - - if (subsetTypeDecl.Witness != null) { - CheckExpression(subsetTypeDecl.Witness, this, - new CodeContextWrapper(subsetTypeDecl, subsetTypeDecl.WitnessKind == SubsetTypeDecl.WKind.Ghost)); - if (subsetTypeDecl.WitnessKind == SubsetTypeDecl.WKind.Compiled) { - var codeContext = new CodeContextWrapper(subsetTypeDecl, subsetTypeDecl.WitnessKind == SubsetTypeDecl.WKind.Ghost); - ExpressionTester.CheckIsCompilable(Options, this, subsetTypeDecl.Witness, codeContext); - } - } - - } else if (d is NewtypeDecl newtypeDecl) { - if (newtypeDecl.Var != null) { - Contract.Assert(newtypeDecl.Constraint != null); - CheckExpression(newtypeDecl.Constraint, this, new CodeContextWrapper(newtypeDecl, true)); - } - - if (newtypeDecl.Witness != null) { - CheckExpression(newtypeDecl.Witness, this, new CodeContextWrapper(newtypeDecl, newtypeDecl.WitnessKind == SubsetTypeDecl.WKind.Ghost)); - if (newtypeDecl.WitnessKind == SubsetTypeDecl.WKind.Compiled) { - var codeContext = new CodeContextWrapper(newtypeDecl, newtypeDecl.WitnessKind == SubsetTypeDecl.WKind.Ghost); - ExpressionTester.CheckIsCompilable(Options, this, newtypeDecl.Witness, codeContext); - } - } - - new NativeTypeAnalysis(reporter).FigureOutNativeType(newtypeDecl, Options); - - } else if (d is DatatypeDecl) { - var dd = (DatatypeDecl)d; - foreach (var member in GetClassMembers(dd)!.Values) { - var dtor = member as DatatypeDestructor; - if (dtor != null) { - var rolemodel = dtor.CorrespondingFormals[0]; - for (int i = 1; i < dtor.CorrespondingFormals.Count; i++) { - var other = dtor.CorrespondingFormals[i]; - if (rolemodel.IsGhost != other.IsGhost) { - reporter.Error(MessageSource.Resolver, other, - "shared destructors must agree on whether or not they are ghost, but '{0}' is {1} in constructor '{2}' and {3} in constructor '{4}'", - rolemodel.Name, - rolemodel.IsGhost ? "ghost" : "non-ghost", dtor.EnclosingCtors[0].Name, - other.IsGhost ? "ghost" : "non-ghost", dtor.EnclosingCtors[i].Name); - } - } - } - } - foreach (var ctor in dd.Ctors) { - CheckParameterDefaultValuesAreCompilable(ctor.Formals, dd); - } - } - } - - AnalyzeTypeConstraints.AssignConstraintIsCompilable(declarations, Options); - - // Now that we have filled in the .ConstraintIsCompilable field of all subset types and newtypes, we're ready to - // visit iterator bodies and members (which will make calls to CheckIsCompilable). - foreach (TopLevelDecl d in declarations) { - if (d is IteratorDecl { Body: { } iterBody } iter) { - ComputeGhostInterest(iter.Body, false, null, iter); - } - if (d is TopLevelDeclWithMembers cl) { - ResolveClassMembers_Pass1(cl); - } - } + ComputeGhostInterestAndMisc(declarations); } // ---------------------------------- Pass 2 ---------------------------------- @@ -1639,6 +1566,85 @@ public void ResolveTopLevelDecls_Core(List declarations, } } + /// + /// Compute ghost interests, figure out native types, check agreement among datatype destructors, and determine tail calls. + /// + private void ComputeGhostInterestAndMisc(List declarations) { + foreach (TopLevelDecl d in declarations) { + if (d is IteratorDecl) { + var iter = (IteratorDecl)d; + iter.SubExpressions.ForEach(e => CheckExpression(e, this, iter)); + if (iter.Body != null) { + CheckExpression(iter.Body, this, iter); + } + + } else if (d is SubsetTypeDecl subsetTypeDecl) { + Contract.Assert(subsetTypeDecl.Constraint != null); + CheckExpression(subsetTypeDecl.Constraint, this, new CodeContextWrapper(subsetTypeDecl, true)); + + if (subsetTypeDecl.Witness != null) { + CheckExpression(subsetTypeDecl.Witness, this, + new CodeContextWrapper(subsetTypeDecl, subsetTypeDecl.WitnessKind == SubsetTypeDecl.WKind.Ghost)); + if (subsetTypeDecl.WitnessKind == SubsetTypeDecl.WKind.Compiled) { + var codeContext = new CodeContextWrapper(subsetTypeDecl, subsetTypeDecl.WitnessKind == SubsetTypeDecl.WKind.Ghost); + ExpressionTester.CheckIsCompilable(Options, this, subsetTypeDecl.Witness, codeContext); + } + } + + } else if (d is NewtypeDecl newtypeDecl) { + if (newtypeDecl.Var != null) { + Contract.Assert(newtypeDecl.Constraint != null); + CheckExpression(newtypeDecl.Constraint, this, new CodeContextWrapper(newtypeDecl, true)); + } + + if (newtypeDecl.Witness != null) { + CheckExpression(newtypeDecl.Witness, this, new CodeContextWrapper(newtypeDecl, newtypeDecl.WitnessKind == SubsetTypeDecl.WKind.Ghost)); + if (newtypeDecl.WitnessKind == SubsetTypeDecl.WKind.Compiled) { + var codeContext = new CodeContextWrapper(newtypeDecl, newtypeDecl.WitnessKind == SubsetTypeDecl.WKind.Ghost); + ExpressionTester.CheckIsCompilable(Options, this, newtypeDecl.Witness, codeContext); + } + } + + new NativeTypeAnalysis(reporter).FigureOutNativeType(newtypeDecl, Options); + + } else if (d is DatatypeDecl) { + var dd = (DatatypeDecl)d; + foreach (var member in GetClassMembers(dd)!.Values) { + var dtor = member as DatatypeDestructor; + if (dtor != null) { + var rolemodel = dtor.CorrespondingFormals[0]; + for (int i = 1; i < dtor.CorrespondingFormals.Count; i++) { + var other = dtor.CorrespondingFormals[i]; + if (rolemodel.IsGhost != other.IsGhost) { + reporter.Error(MessageSource.Resolver, other, + "shared destructors must agree on whether or not they are ghost, but '{0}' is {1} in constructor '{2}' and {3} in constructor '{4}'", + rolemodel.Name, + rolemodel.IsGhost ? "ghost" : "non-ghost", dtor.EnclosingCtors[0].Name, + other.IsGhost ? "ghost" : "non-ghost", dtor.EnclosingCtors[i].Name); + } + } + } + } + foreach (var ctor in dd.Ctors) { + CheckParameterDefaultValuesAreCompilable(ctor.Formals, dd); + } + } + } + + AnalyzeTypeConstraints.AssignConstraintIsCompilable(declarations, Options); + + // Now that we have filled in the .ConstraintIsCompilable field of all subset types and newtypes, we're ready to + // visit iterator bodies and members (which will make calls to CheckIsCompilable). + foreach (TopLevelDecl d in declarations) { + if (d is IteratorDecl { Body: { } iterBody } iter) { + ComputeGhostInterest(iter.Body, false, null, iter); + } + if (d is TopLevelDeclWithMembers cl) { + ResolveClassMembers_Pass1(cl); + } + } + } + private void CheckForCyclesAmongRedirectingTypes(RedirectingTypeDecl dd, HashSet cycleErrorHasBeenReported) { var enclosingModule = dd.EnclosingModule; if (enclosingModule.CallGraph.GetSCCSize(dd) != 1) { @@ -1964,7 +1970,7 @@ void CheckExpression(Expression expr, ModuleResolver resolver, ICodeContext code Contract.Requires(expr != null); Contract.Requires(resolver != null); Contract.Requires(codeContext != null); - var v = new CheckExpressionVisitor(resolver, codeContext); + var v = new CheckLocalityVisitor(resolver, codeContext); v.Visit(expr); } /// @@ -1977,49 +1983,10 @@ void CheckExpression(Statement stmt, ModuleResolver resolver, ICodeContext codeC Contract.Requires(stmt != null); Contract.Requires(resolver != null); Contract.Requires(codeContext != null); - var v = new CheckExpressionVisitor(resolver, codeContext); + var v = new CheckLocalityVisitor(resolver, codeContext); v.Visit(stmt); } - class CheckExpressionVisitor : ResolverBottomUpVisitor { - readonly ICodeContext codeContext; - public CheckExpressionVisitor(ModuleResolver resolver, ICodeContext codeContext) - : base(resolver) { - Contract.Requires(resolver != null); - Contract.Requires(codeContext != null); - this.codeContext = codeContext; - } - protected override void VisitOneExpr(Expression expr) { - if (expr is StmtExpr) { - var e = (StmtExpr)expr; - resolver.ComputeGhostInterest(e.S, true, "a statement expression", codeContext); - } else if (expr is LetExpr) { - var e = (LetExpr)expr; - if (codeContext.IsGhost) { - foreach (var bv in e.BoundVars) { - bv.MakeGhost(); - } - } - } - } - - protected override void VisitOneStmt(Statement stmt) { - switch (stmt) { - case CalcStmt calc: { - foreach (var h in calc.Hints) { - resolver.CheckLocalityUpdates(h, new HashSet(), "a hint"); - } - break; - } - case BlockByProofStmt blockByProofStmt: - resolver.CheckLocalityUpdates(blockByProofStmt.Proof, new HashSet(), "a by block"); - break; - case ForallStmt { Body: not null } forall: - resolver.CheckLocalityUpdates(forall.Body, new HashSet(), "a forall statement"); - break; - } - } - } #endregion void ExtremePredicateChecks(Expression expr, ExtremePredicate context, CallingPosition cp) { @@ -3232,7 +3199,7 @@ public void CheckLocalityUpdates(Statement stmt, ISet localsAllow Contract.Requires(localsAllowedInUpdates != null); Contract.Requires(where != null); - if (stmt is AssertStmt or BlockByProofStmt or ForallStmt or CalcStmt or ModifyStmt) { + if (stmt is AssertStmt or ForallStmt or CalcStmt or ModifyStmt) { // don't recurse, since CheckHintRestrictions will be called on that assert-by separately return; } else if (stmt is AssignSuchThatStmt) { @@ -3256,6 +3223,12 @@ public void CheckLocalityUpdates(Statement stmt, ISet localsAllow } else if (stmt is BlockStmt) { localsAllowedInUpdates = new HashSet(localsAllowedInUpdates); // use this new set for the recursive calls + } else if (stmt is BlockByProofStmt blockByProofStmt) { + localsAllowedInUpdates = new HashSet(localsAllowedInUpdates); + // use this new set for the recursive calls + + CheckLocalityUpdates(blockByProofStmt.Body, localsAllowedInUpdates, where); + return; } foreach (var ss in stmt.SubStatements) { diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs index 73077d52d0a..39d271c2c56 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrPredicateStatement.cs @@ -76,20 +76,10 @@ public void TrAssertStmt(PredicateStmt stmt, BoogieStmtListBuilder builder, Vari var hiddenProof = false; BoogieStmtListBuilder proofBuilder = null; var assertStmt = stmt as AssertStmt; - if (assertStmt != null) { - // if (assertStmt.Proof != null) { - // hiddenProof = true; - // proofBuilder = new BoogieStmtListBuilder(this, options, builder.Context); - // AddComment(proofBuilder, stmt, "assert statement proof"); - // CurrentIdGenerator.Push(); - // TrStmt(((AssertStmt)stmt).Proof, proofBuilder, locals, etran); - // CurrentIdGenerator.Pop(); - // } else - if (assertStmt.Label != null) { - hiddenProof = true; - proofBuilder = new BoogieStmtListBuilder(this, options, builder.Context); - AddComment(proofBuilder, stmt, "assert statement proof"); - } + if (assertStmt is { Label: not null }) { + hiddenProof = true; + proofBuilder = new BoogieStmtListBuilder(this, options, builder.Context); + AddComment(proofBuilder, stmt, "assert statement proof"); } proofBuilder ??= b; diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-cs.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-cs.doo index 6af37b2b136cdc42c3bc467b2b34132c5d51c3a2..92bb678362db5e4e2e683fa7ee2436130e7b3026 100644 GIT binary patch delta 47 wcmaFQ{hpgQz?+#xgn@y9gCRG-awG2yMrI(rc?IKHW)Nd?1*-*^-ppzP043-Q^8f$< delta 47 wcmaFQ{hpgQz?+#xgn@y9gF&cOWh3tlMrI(rc?IKHW)Nd?1*-*^-ppzP029It2LJ#7 diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-go.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-go.doo index cbeb0d853eb145bbeb2c8d968972e15dcd9ba1b7..260c1dea1494bcd0cfb58331e368a93414400b5b 100644 GIT binary patch delta 47 wcmZqSY2o1w@MdNaVPIh3U?>T&+{inFkr_yDUcvZ?8N`^}%W46pPq5km00fW>dH?_b delta 47 wcmZqSY2o1w@MdNaVPIh3V32B6*~mMCkr_yDUcvZ?8N`^}%W46pPq5km0P?8|k^lez diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-java.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-java.doo index 1365b60e2d0d505ea8742e5fdbadee7c32ab49c0..f6c991f5c676f654717bb76dc3347b5824520a04 100644 GIT binary patch delta 47 wcmaFH{fwJ8z?+#xgn@y9gCRe_awG2yMrI(rc?IJUW)Nd?KC1awG2yMrI(rc?IK6W)Nd?3abT}p3iCn02`bPhX4Qo delta 47 wcmcc3eVdy%z?+#xgn@y9gF&EGWh3tlMrI(rc?IK6W)Nd?3abT}p3iCn010*rn*aa+ diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-py.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-py.doo index ef763f12bf03b6c1e8b0884c9a15cf4554756201..4ce69633313b7f6ec7bde1a5b100d349d71ddf7f 100644 GIT binary patch delta 47 wcmaFF{fL`4z?+#xgn@y9gP}aYawG2yMrI(rc?IJEW)Nd?Hme1gUdCzz03xUi+5i9m delta 47 wcmaFF{fL`4z?+#xgn@y9gF&WMWh3tlMrI(rc?IJEW)Nd?Hme1gUdCzz01$!;?f?J) diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries.doo index 7e06b30aa2b14f8a8f9608c610b9b87fc3825599..507d5a2daf55b2c49ae9ad0fea0fbe647165c1fe 100644 GIT binary patch delta 49 ycmeyjkNMv|X5IjAW)=|!1_lm>i~!4xyfYY?f%N7Tj4^jXjLGcxEx@$XeH#FIjSr*% delta 49 ycmeyjkNMv|X5IjAW)=|!1_llW-d2^3yfYY?f%N7Tj4^jXjLGcxEx@$XeH#F0XAZak diff --git a/Source/DafnyStandardLibraries/examples/JSON/JSONExamples.dfy b/Source/DafnyStandardLibraries/examples/JSON/JSONExamples.dfy index 35832b56c5b..aa07a78eee7 100644 --- a/Source/DafnyStandardLibraries/examples/JSON/JSONExamples.dfy +++ b/Source/DafnyStandardLibraries/examples/JSON/JSONExamples.dfy @@ -254,20 +254,21 @@ module {:options "-functionSyntax:4"} ConcreteSyntax { // BUG(https://github.com/dafny-lang/dafny/issues/2184) // BUG(https://github.com/dafny-lang/dafny/issues/2690) var fn' := (sf: Suffixed) requires (ghost var in_sq := sf => sf in sq; in_sq(sf)) => sf.(t := fn(sf)); - var sq' := Seq.Map(fn', sq); + Seq.Map(fn', sq) + } - assert NoTrailingSuffix(sq') by { - forall idx | 0 <= idx < |sq'| ensures sq'[idx].suffix.Empty? <==> idx == |sq'| - 1 { - calc { - sq'[idx].suffix.Empty?; - fn'(sq[idx]).suffix.Empty?; - sq[idx].suffix.Empty?; - idx == |sq| - 1; - idx == |sq'| - 1; - } + lemma MapSuffixedSequenceNoTrailingSuffix(sq: SuffixedSequence, fn: Suffixed --> D) + requires forall suffixed | suffixed in sq :: fn.requires(suffixed) + ensures NoTrailingSuffix(MapSuffixedSequence(sq, fn)) + { + var sq' := MapSuffixedSequence(sq, fn); + forall idx | 0 <= idx < |sq'| ensures sq'[idx].suffix.Empty? <==> idx == |sq'| - 1 { + calc { + sq'[idx].suffix.Empty?; + sq[idx].suffix.Empty?; + idx == |sq| - 1; + idx == |sq'| - 1; } } - - sq' } } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByResolution0.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByResolution0.dfy.expect index d6ed0c40058..a3fd6773b9d 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByResolution0.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/ast/statement/calls/CallByResolution0.dfy.expect @@ -1,3 +1,4 @@ CallByResolution0.dfy(8,6): Error: assignment to non-ghost variable is not allowed in this context, because the statement is in a ghost context; e.g., it may be guarded by a specification-only expression CallByResolution0.dfy(9,4): Error: return statement is not allowed in this context (because it is guarded by a specification-only expression) -2 resolution/type errors detected in CallByResolution0.dfy +CallByResolution0.dfy(8,4): Error: a by block is not allowed to update a variable it doesn't declare +3 resolution/type errors detected in CallByResolution0.dfy From 46dc3028fb814597d9a0ec5ea43bca5b3ef66369 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 25 Sep 2024 10:54:17 +0200 Subject: [PATCH 44/47] Fix SubsetTypes --- .../TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect index dc6081d50f4..ad13e6b5b66 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect @@ -91,5 +91,5 @@ SubsetTypes.dfy(459,15): Error: assertion might not hold SubsetTypes.dfy(464,13): Error: assertion might not hold Dafny program verifier finished with 13 verified, 91 errors -Total resources used is 774990 +Total resources used is 774980 Max resources used by VC is 101850 From f45a1a380383d56ecc1aa2ed632da75177b978d0 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Sun, 6 Oct 2024 14:14:17 +0200 Subject: [PATCH 45/47] Remove todos --- Source/DafnyCore/AST/Substituter.cs | 1 - Source/DafnyCore/Verifier/BoogieGenerator.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/Source/DafnyCore/AST/Substituter.cs b/Source/DafnyCore/AST/Substituter.cs index 2d14011a5e7..3ec9bbae469 100644 --- a/Source/DafnyCore/AST/Substituter.cs +++ b/Source/DafnyCore/AST/Substituter.cs @@ -880,7 +880,6 @@ protected virtual Statement SubstStmt(Statement stmt) { rr.OffsetMembers = revealStmt.OffsetMembers.ToList(); r = rr; } else if (stmt is BlockByProofStmt blockByProofStmt) { - // Move this code into the class var rr = new BlockByProofStmt(blockByProofStmt.RangeToken, (BlockStmt)SubstStmt(blockByProofStmt.Proof), SubstStmt(blockByProofStmt.Body)); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index c5ad4911a39..a32575bf5da 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -2798,7 +2798,6 @@ private Bpl.Function GetCanCallFunction(Function f) { /// Note that SpecWellformedness and Implementation have procedure implementations /// but no callers, and vice versa for InterModuleCall, IntraModuleCall, and CoCall. /// - /// Remy: TODO Simplify enum MethodTranslationKind { SpecWellformedness, Call, CoCall, Implementation, OverrideCheck } private static readonly Dictionary kindSanitizedPrefix = From 7cce358abe483e919452c17445c05017776bf35d Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Sun, 6 Oct 2024 14:16:59 +0200 Subject: [PATCH 46/47] Regenerated makefiles --- Source/DafnyStandardLibraries/Makefile | 2 +- .../binaries/DafnyStandardLibraries-cs.doo | Bin 1519 -> 1519 bytes .../binaries/DafnyStandardLibraries-go.doo | Bin 1540 -> 1540 bytes .../binaries/DafnyStandardLibraries-java.doo | Bin 1510 -> 1510 bytes .../binaries/DafnyStandardLibraries-js.doo | Bin 2028 -> 2028 bytes .../DafnyStandardLibraries-notarget.doo | Bin 1499 -> 1499 bytes .../binaries/DafnyStandardLibraries-py.doo | Bin 1506 -> 1506 bytes .../binaries/DafnyStandardLibraries.doo | Bin 57214 -> 57214 bytes 8 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/DafnyStandardLibraries/Makefile b/Source/DafnyStandardLibraries/Makefile index c33e85b8949..ac2a52d50d7 100644 --- a/Source/DafnyStandardLibraries/Makefile +++ b/Source/DafnyStandardLibraries/Makefile @@ -2,7 +2,7 @@ # Invoking the CLI this way just to stay platform-independent DAFNY = dotnet run --project ../Dafny --no-build --roll-forward LatestMajor -- -NO_VERIFY := false +NO_VERIFY := false # Use make update-binary NO_VERIFY=true to generate the binaries without verification DOO_FILE_SOURCE=build/DafnyStandardLibraries.doo DOO_FILE_TARGET=binaries/DafnyStandardLibraries.doo diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-cs.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-cs.doo index 92bb678362db5e4e2e683fa7ee2436130e7b3026..d46725cbc7a95aa8c4535a71af689bf0e5171943 100644 GIT binary patch delta 47 wcmaFQ{hpgQz?+#xgn@y9gMp>UZ6ogtMrI(rc?IKHW)Nd?1*-*^-ppzP0253M3jhEB delta 47 wcmaFQ{hpgQz?+#xgn@y9gCRG-awG2yMrI(rc?IKHW)Nd?1*-*^-ppzP043-Q^8f$< diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-go.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-go.doo index 260c1dea1494bcd0cfb58331e368a93414400b5b..9c149e23aa30e6fb51f9335e16356929b59678c4 100644 GIT binary patch delta 47 wcmZqSY2o1w@MdNaVPIh3VBjfo+sHeEkr_yDUcvZ?8N`^}%W46pPq5km0P-^nmH+?% delta 47 wcmZqSY2o1w@MdNaVPIh3U?>T&+{inFkr_yDUcvZ?8N`^}%W46pPq5km00fW>dH?_b diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-java.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-java.doo index f6c991f5c676f654717bb76dc3347b5824520a04..caa4d17c410c4f8ab1a8f96c31c6185c416d5d68 100644 GIT binary patch delta 47 wcmaFH{fwJ8z?+#xgn@y9gMq!sZ6ogtMrI(rc?IJUW)Nd?KC1Hq)$ delta 47 wcmaFH{fwJ8z?+#xgn@y9gCRe_awG2yMrI(rc?IJUW)Nd?KC1awG2yMrI(rc?IK6W)Nd?3abT}p3iCn02`bPhX4Qo diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-py.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-py.doo index 4ce69633313b7f6ec7bde1a5b100d349d71ddf7f..a0f0e5a8ea93ddb5d55a0efd9b598b494f301fd6 100644 GIT binary patch delta 47 wcmaFF{fL`4z?+#xgn@y9gMqKeZ6ogtMrI(rc?IJEW)Nd?Hme1gUdCzz01yld@&Et; delta 47 wcmaFF{fL`4z?+#xgn@y9gP}aYawG2yMrI(rc?IJEW)Nd?Hme1gUdCzz03xUi+5i9m diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries.doo index 507d5a2daf55b2c49ae9ad0fea0fbe647165c1fe..09e86046224fdd3791b21083cd4838df3ef943d2 100644 GIT binary patch delta 49 ycmeyjkNMv|X5IjAW)=|!1_lm>zlClad1o*(1L@5x7-Q~&7?auWTYzb&`!)cy3lgvZ delta 49 ycmeyjkNMv|X5IjAW)=|!1_lm>i~!4xyfYY?f%N7Tj4^jXjLGcxEx@$XeH#FIjSr*% From f9c2fa38bf7e40ebd592f89acdfdb9da921c0b88 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Sun, 6 Oct 2024 15:03:48 +0200 Subject: [PATCH 47/47] Update doos --- .../binaries/DafnyStandardLibraries-cs.doo | Bin 1519 -> 1518 bytes .../binaries/DafnyStandardLibraries-go.doo | Bin 1540 -> 1539 bytes .../binaries/DafnyStandardLibraries-java.doo | Bin 1510 -> 1509 bytes .../binaries/DafnyStandardLibraries-js.doo | Bin 2028 -> 2027 bytes .../DafnyStandardLibraries-notarget.doo | Bin 1499 -> 1498 bytes .../binaries/DafnyStandardLibraries-py.doo | Bin 1506 -> 1505 bytes .../binaries/DafnyStandardLibraries.doo | Bin 57214 -> 57213 bytes 7 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-cs.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-cs.doo index 2186b753453d1830bf38f8cffd848d57036a5f08..472917014fca63fcff0f941f6ff184cb89df615d 100644 GIT binary patch delta 473 zcmaFQ{f=8Wz?+#xgn@y9gCV)XEwXLp*>m|5g;ndXWb*C(B5t{rgU4-AS*OR9wkcV^ zzb`DiyEXQUl`~&j8_!Z1WmTJhj)7W#zI@KKc(b2(H^W5z?=MdOJ6@N#MWwl4WP-M< z)5|yO-%EeoP+W0OzU}UNqwwZ0x$K%#CpQ~UU1KJws2O*#Ve^9aUGMH0SFF?EIr;NY z=z;6?;rr&i_jS}RVBpldxVzy_^vh1WJ31-T4BcWL>+b43 zIlbYWze4@Sc~@VuES#hBN!QRVW964) zZ8F=ZttXb6U;p$=o4?XHa;eR?Ow%p=yG<{&R9$~+q$4qFYHsO2_jlZ=K|e{{^505k t28N^j3=Hg>w=wQ#=7h({&+6*fLl_A^@)L(uDv3 delta 450 zcmV;z0X_ci3-1dPP)h>@6aWAK2mnZOMp=V?%W`m$6D5DdO%J{F7d<5i3|gXgiAvOl zlxq9;p}g+e4sx9#2Qx#4QudJ&?*E}E6@L2tH0-v|!zMuF^F8`=`)m8$87;}+Jq6@S zkx)AO`mz!aJG&p--LQEpb2UicH;_^}Iyh0uY7-HX;uQ=y3wo%_?mdI2io%ebFI)wD z{knPtVd8)B1_v8-W`yFo>mr+3Pk0+Zjzv5gY0{aPG_M?&>*|UKLQv-uG(~lVUB)_< z(B-PM&L=pTRy3iC4peF6Z2hbXT7d&daET5Aa&M(4`s-#Pvy3s~67Qnhn0yK7`~>nJ z>sg&n5X#Xa-x6blma2=5eXr1!89ddVVfZ7!I)i_hAv-K791=P%oQM&`nVmXWBJ!)+ zs-kDEaJt-b*ks^lS4fyMIO5R!!?CV(6&gqTfffH~aQ&1=xl5Aeq=9@ba_-Hwy!^^T z-b_|0k9}GV?Za*fG@;&gc^MWA&C0eqzmfX{P)h>@6aWAK2mnZOMp=>qY_FVo%Vb%I8nS6V{h+A&u;Bi}2*6DGjZA#Yf z?+eTBZjJq7<;<7X#9*SmYh73(y3PX0U; zdf<9}xW9srP5r$G3C-tv0``=|rWn8G%x6BavBO>2*GhQuv);!=v3CU=IbPK?EnBf} z!|90SLc3z7Ui-%@0_*seI2z67&!GV?ryjf{j$^Uj!w!nL${d6y1RN$ zPH#BpuTZ~n-qn{Z3+L#3(lvC;Sh?%QjqhBsGY}1kJ)^;)1f&B1>2v=_mwVPeZk@uf6kBe=GV(@PU(7H zo6PoU>xrf2*FXKz=C3r4Tx#*2pDTHu%vMoBLhQ0QGR++Vs3ypD;r3H2?z^- JbS*202LRe1)i3}6 delta 494 zcmZqXY2gtL@MdNaVPIh3VDK+;i)^ktT~shpShfC$FaO>z;|CLJiTnUw=*BY&(Bg&Ed&s+h4`Mvwz=_)IIsYmi39( z!~m<`zcvZ+Tkn>?eRo5pcCo3)ee(%HmR1&lC$oeFCp{A7vEZ&+ar&KY^YX4k6Xxj0 zu-5$DRmGC{sQx1xyL3rf%cGsIm1al!S4cbbx!Oo&UYa?>WWCJv*RQ%boC2N;o4A&? z#vZepdg1A+E$7rNW`>z8?BWZ_e3J8b4OdhnvqwQEA5&5OQuSZ2(-b%N7(Nnuzv%Xi z8qODgSU$Yu8_xDrs|dUg8hs16IxTGZd|LG-ZUW^yYL3 z9+^4UDpKjw>RYQ8rLQa4b*Jb?%7e6Uj~T`mk1qTiudi|JWi8^_ev`z#dyx1+Ge$X;%^qzuunL%_}xBc z1_mAh28PXB7_TyO!eeCeBvvDP5D!@mJ22K@U`gX_Mh1q0qWtut#M}UHRyL3X6A%^v J={i;r4**As&)NV0 diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-java.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-java.doo index 55c6a1d0216bc0a42968d06dcaa913829814640c..4c1f17a2c990772c64119a74a66d93f1feb8c7ef 100644 GIT binary patch delta 471 zcmaFH{ghicz?+#xgn@y9gCVWLEwXLp*>m|5g;ndXWb*C(B5t{rgU4-AS*OR9wkcV^ zzb`DiyEXQUl`~&j8_!Z1WmTJhj)7W#zI@KKc(b2(H^W5z?=MdOJ6@N#MWwl4WP-M< z)5|yO-%EeoP+W0OzU}UNqwwZ0x$K%#CpQ~UU1KJws2O*#Ve^9aUGMH0SFF?EIr;NY z=z;6?;rr&i_jS}RVBpldxVzy_^vh1WJ31-T4BcWL>+b43 zIlbYWze4@Sc~@VuES#hBN!QRVW964) zZ8F=ZttXb6U;p$=o4?XHa;eR?Ow%p=yG<{&R9$~+q$4qFYHsO2_jlZ=LC@UiDzJc= zf#E1W0|Wc!ZH(KPIpHxfxs=t2DXn7iN>&-gS&R$}1x5MkMTxlq-mGlEsAFIP!U7;2 I&kEuJ0Hg}g761SM delta 471 zcmaFL{ft{Uz?+#xgn@y9gTbrFEwZ`pbWy=XVb%I0zWjT?h})`i@I);t>-4(PHYMx# z_l0}j-Qp{ntH^Hb+!pk0qR_kl3pGURe*HChv+ew0HHRmkZGRR2&i;KzQupKoTh=FD z69cS%|Jo$PZ@pXo_T3GY+Qp_G_su5+Sz1{Hp3D*!ob*VP$AY_R#p!pp&C9zEO_-w} z!&>uqR~1X*qxz3*?9wG^Esu7-R+=5@Um@+#=V~LBd1>Yhll3ywU%%?&a0+-XY~otl z8hgxY>V>DPwwzP9m>FiWu!}Dw^GVL%HC$1R%pL`ud`w08OVxk9PE*|6WB5qu{i545 zYB*o~VfiHgI&!X`z^RK$_l1&MrmgDktzCa%s-(@;d5J%i4_GzV&rr11(UcLg(woyE zcx2{Wt4O6!t8cAZl)kQD*PWspDG$=ZJ!TkNJi74b@Wyo|qEha^8%6(T6#SjyzSDcs z(@6aWAK2mok!Mp=fb&CYL;6D5DtX%4;g7df>E3`U}MiAdCj zlxq9;p?2N1UGzFb4rYc7r5qzAJpMsZD*W{GX*le^4Z8r5FZbxn{>Oe{v?PO%6p$-L zLh0 zp4I6Dp`0x8Eip!Dsk+$M_X=H^!Bg!ShF1aB8N`1K*!&=*ElJjs2J*GYr8n2|`YR84 zGg+lP_GvY=54$zcgnHNIGAtOHm2GwYNA3$yO9KQH000080BCqdS)jCx{iX!~0OcG2 s00*@6aWAK2mnoTMp=V?%W`m$6D5DdO%J{F7d<5i3|gXgiAvOl zlxq9;p}g+e4sx9#2Qx#4QudJ&?*E}E6@L2tH0-v|!zMuF^F8`=`)m8$87;}+Jq6@S zkx)AO`mz!aJG&p--LQEpb2UicH;_^}Iyh0uY7-HX;uQ=y3wo%_?mdI2io%ebFI)wD z{knPtVd8)B1_v8-W`yFo>mr+3Pk0+Zjzv5gY0{aPG_M?&>*|UKLQv-uG(~lVUB)_< z(B-PM&L=pTRy3iC4peF6Z2hbXT7d&daET5Aa&M(4`s-#Pvy3s~67Qnhn0yK7`~>nJ z>sg&n5X#Xa-x6blma2=5eXr1!89ddVVfZ7!I)i_hAv-K791=P%oQM&`nVmXWBJ!)+ zs-kDEaJt-b*ks^lS4fyMIO5R!!?CV(6&gqTfffH~aQ&1=xl5Aeq=9@ba_-Hwy!^^T z-b_|0k9}GV?Za*fG@;&gc^MWA&C0eqzmfX{P)h>@6aWAK2mnoTMp>Y=jQyqs008A2 s006VJ0lftYO>#zAgMQ0$aFcomG6GF@6aWAK2moPtMp=fb&CYL;6D5DtX%4;g7df>E3`U}MiAdCj zlxq9;p?2N1UGzFb4rYc7r5qzAJpMsZD*W{GX*le^4Z8r5FZbxn{>Oe{v?PO%6p$-L zLh0 zp4I6Dp`0x8Eip!Dsk+$M_X=H^!Bg!ShF1aB8N`1K*!&=*ElJjs2J*GYr8n2|`YR84 zGg+lP_GvY=54$zcgnHNIGAtOHm2GwYNA3$yO9KQH000080AYAWSuL*Z=#>Ki0B{Zf s00*45P)h>@6aWAK2mnQLMp=V?%W`m$6D5DdO%J{F7d<5i3|gXgiAvOl zlxq9;p}g+e4sx9#2Qx#4QudJ&?*E}E6@L2tH0-v|!zMuF^F8`=`)m8$87;}+Jq6@S zkx)AO`mz!aJG&p--LQEpb2UicH;_^}Iyh0uY7-HX;uQ=y3wo%_?mdI2io%ebFI)wD z{knPtVd8)B1_v8-W`yFo>mr+3Pk0+Zjzv5gY0{aPG_M?&>*|UKLQv-uG(~lVUB)_< z(B-PM&L=pTRy3iC4peF6Z2hbXT7d&daET5Aa&M(4`s-#Pvy3s~67Qnhn0yK7`~>nJ z>sg&n5X#Xa-x6blma2=5eXr1!89ddVVfZ7!I)i_hAv-K791=P%oQM&`nVmXWBJ!)+ zs-kDEaJt-b*ks^lS4fyMIO5R!!?CV(6&gqTfffH~aQ&1=xl5Aeq=9@ba_-Hwy!^^T z-b_|0k9}GV?Za*fG@;&gc^MWA&C0eqzmfX{P)h>@6aWAK2mnQLMp-Sc?dX*Q003|f s006VJ0jvWFMRG=2gMQ0$aFb~TG6F?%lbr<|0h^P$1sVob1poj501B$f9smFU diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-py.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-py.doo index 4b0d311e0f823c7b924eae4a7ba3d9d092e75cd8..e823114e5352bbc0bdbd57342180bf7541090020 100644 GIT binary patch delta 494 zcmaFF{g7KYz?+#xgn@y9gCV!VEwXLp*>m|5g;ndXWb*C(B5t{rgU4-AS*OR9wkcV^ zzb`DiyEXQUl`~&j8_!Z1WmTJhj)7W#zI@KKc(b2(H^W5z?=MdOJ6@N#MWwl4WP-M< z)5|yO-%EeoP+W0OzU}UNqwwZ0x$K%#CpQ~UU1KJws2O*#Ve^9aUGMH0SFF?EIr;NY z=z;6?;rr&i_jS}RVBpldxVzy_^vh1WJ31-T4BcWL>+b43 zIlbYWze4@Sc~@VuES#hBN!QRVW964) zZ8F=ZttXb6U;p$=o4?XHa;eR?Ow%p=yG<{&R9$~+q$4qFYHsO2_jlZ=LBH{>_nA4& z3=F6E85r0%Z)4oV%n6T?$%U*&_8=aLB4Dh+z>>yUj0_9~MfvGPiMavZtZX0&CLk;T J($TCS9sq&r*0KNq delta 473 zcmaFJ{fJvQz?+#xgn@y9gCVHMEwZ`pbWy=XVb%I0zWjT?h})`i@I);t>-4(PHYMx# z_l0}j-Qp{ntH^Hb+!pk0qR_kl3pGURe*HChv+ew0HHRmkZGRR2&i;KzQupKoTh=FD z69cS%|Jo$PZ@pXo_T3GY+Qp_G_su5+Sz1{Hp3D*!ob*VP$AY_R#p!pp&C9zEO_-w} z!&>uqR~1X*qxz3*?9wG^Esu7-R+=5@Um@+#=V~LBd1>Yhll3ywU%%?&a0+-XY~otl z8hgxY>V>DPwwzP9m>FiWu!}Dw^GVL%HC$1R%pL`ud`w08OVxk9PE*|6WB5qu{i545 zYB*o~VfiHgI&!X`z^RK$_l1&MrmgDktzCa%s-(@;d5J%i4_GzV&rr11(UcLg(woyE zcx2{Wt4O6!t8cAZl)kQD*PWspDG$=ZJ!TkNJi74b@Wyo|qEha^8%6(T6#SjyzSDcs z($VvAt~i2wk~-Oi=} diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries.doo index 3f171768af869d6a3409171fdcc660f9d2246366..66b99929968ffbb3534c212b61b9ffa1e30d8de9 100644 GIT binary patch delta 452 zcmV;#0XzQwz61Tf0~1h70|XQR000O8TzE!V=R|9WZ;=xwe@zd)Fj|tqM+(T5 zBB6Bl?PVh#W)9!>hhg`t%+($-$XQR#QYsidQh;Ea;)Gv-b?1D+)t$zHk-r z_3P*fgo(!+e;iEcY!Ql=?u%^8dcvCkITrC~q*-TT(!6n8uA>_w2tl1s&=l1bb{Xqb zLbt2ZI-lTdUeSyyI#8vNv-Pt%Xax=+!6kYZko#47qCajHvXwDLT;naijmejQ&QBnp zSkLNof>2Ht`IZm2au0rEzKd|9{8(cr-(QZkyo-~lJMJ~O$me*g| z#ur&8>y*bXSF53`-I{1Z#V=yP(5!9i^M8IHP)h>@6aWAK2moAoMp^6&lJPj*003ls u1po&Cv$p|F-3eTHMp@@XYlv@?4c{^XTzHc~-y8v&lV0B%2KU|o0001U>(Ouk delta 452 zcmV;#0XzQvz61Wg0~1h70|XQR000O8Lvlu0(@U%EaFG)xe`yc7^cOiL2n4JA^DykTkHaQF zUw@7sK$v*Ef5O3p&K99~>b}UftS7t*kYf>#Mw)ddCe16y=X&90CzXK=)!`MYCX=qfah_5&;a)!_0e_jczb%Si+ITIAH5YkB#V zb^I*LWS#QZaz