From e5e3e7cec272af7355f54d620ff435880a41bcb1 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 11 Jun 2024 13:37:13 -0700 Subject: [PATCH 001/151] Snapshot changes --- .../Dafny/Verifier/Translator.TrStatement.cs | 51 ++- Source/Dafny/Verifier/Translator.cs | 383 ++++++++++++------ Test/dafny0/FunctionSpecifications.dfy | 8 +- Test/dafny0/FunctionSpecifications.dfy.expect | 2 - Test/git-issues/git-issue-370.dfy | 36 +- Test/git-issues/git-issue-370.dfy.expect | 32 +- 6 files changed, 326 insertions(+), 186 deletions(-) diff --git a/Source/Dafny/Verifier/Translator.TrStatement.cs b/Source/Dafny/Verifier/Translator.TrStatement.cs index af59190b916..3af863d5376 100644 --- a/Source/Dafny/Verifier/Translator.TrStatement.cs +++ b/Source/Dafny/Verifier/Translator.TrStatement.cs @@ -950,6 +950,7 @@ void TrForallProof(ForallStmt s, BoogieStmtListBuilder definedness, BoogieStmtLi // check that postconditions hold foreach (var ens in s.Ens) { + definedness.Add(TrAssumeCmd(ens.E.tok, CanCallAssumption(ens.E, etran))); bool splitHappened; // we actually don't care foreach (var split in TrSplitExpr(ens.E, etran, true, out splitHappened)) { @@ -968,6 +969,7 @@ void TrForallProof(ForallStmt s, BoogieStmtListBuilder definedness, BoogieStmtLi var se = s.Body == null ? Bpl.Expr.True : TrFunctionSideEffect(s.Body, etran); var substMap = new Dictionary(); var p = Substitute(s.ForallExpressions[0], null, substMap); + exporter.Add(TrAssumeCmd(s.Tok, CanCallAssumption(p, etran))); var qq = etran.TrExpr(p); if (s.BoundVars.Count != 0) { exporter.Add(TrAssumeCmd(s.Tok, BplAnd(se, qq))); @@ -1192,8 +1194,9 @@ void TrForallAssign(ForallStmt s, AssignStmt s0, /// /// Generate: - /// assume (forall x,y :: Range(x,y)[$Heap:=oldHeap] ==> - /// $Heap[ Object(x,y)[$Heap:=oldHeap], Field(x,y)[$Heap:=oldHeap] ] == G[$Heap:=oldHeap] )); + /// assume (forall x,y :: Range#canCall AND + /// (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 @@ -1241,7 +1244,12 @@ private Bpl.Expr TrForall_NewValueAssumption(IToken tok, List boundVar tr = new Bpl.Trigger(tok, true, tt, tr); } } - return new Bpl.ForallExpr(tok, xBvars, tr, Bpl.Expr.Imp(xAnte, Bpl.Expr.Eq(xHeapOF, g))); + var canCalls = BplAnd(CanCallAssumption(lhs, prevEtran), CanCallAssumption(rhs, prevEtran)); + var canCallRange = CanCallAssumption(range, prevEtran); + var body = BplAnd(canCalls, Bpl.Expr.Eq(xHeapOF, g)); + body = BplImp(xAnte, body); + body = BplAnd(canCallRange, body); + return new Bpl.ForallExpr(tok, xBvars, tr, body); } private void TrIfStmt(IfStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { @@ -1766,7 +1774,8 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, TrStmt_CheckWellformed(loopInv.E, invDefinednessBuilder, locals, etran, false); invDefinednessBuilder.Add(TrAssumeCmd(loopInv.E.tok, etran.TrExpr(loopInv.E))); - invariants.Add(TrAssumeCmd(loopInv.E.tok, Bpl.Expr.Imp(w, CanCallAssumption(loopInv.E, etran)))); + builder.Add(TrAssumeCmd(loopInv.E.tok, BplImp(w, CanCallAssumption(loopInv.E, etran)))); + invariants.Add(TrAssumeCmd(loopInv.E.tok, BplImp(w, CanCallAssumption(loopInv.E, etran)))); bool splitHappened; var ss = TrSplitExpr(loopInv.E, etran, false, out splitHappened); if (!splitHappened) { @@ -1785,6 +1794,7 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, } // check definedness of decreases clause foreach (Expression e in theDecreases) { + builder.Add(TrAssumeCmd(e.tok, Bpl.Expr.Imp(w, CanCallAssumption(e, etran)))); TrStmt_CheckWellformed(e, invDefinednessBuilder, locals, etran, true); } if (codeContext is IMethodCodeContext) { @@ -1870,6 +1880,9 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, // omit termination checking for this loop bodyTr(loopBodyBuilder, updatedFrameEtran); } else { + foreach (Expression e in theDecreases) { + loopBodyBuilder.Add(TrAssumeCmd(e.tok, BplImp(w, CanCallAssumption(e, etran)))); + } List oldBfs = RecordDecreasesValue(theDecreases, loopBodyBuilder, locals, etran, "$decr$" + suffix); // time for the actual loop body bodyTr(loopBodyBuilder, updatedFrameEtran); @@ -1881,6 +1894,8 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, toks.Add(e.tok); types.Add(e.Type.NormalizeExpand()); decrs.Add(etran.TrExpr(e)); + // need to add can calls again because the actual loop body updates the variables + loopBodyBuilder.Add(TrAssumeCmd(e.tok, BplImp(w, CanCallAssumption(e, etran)))); } if (includeTerminationCheck) { AddComment(loopBodyBuilder, s, "loop termination check"); @@ -2402,43 +2417,53 @@ void TrForallStmtCall(IToken tok, List boundVars, 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); + Bpl.Expr anteCanCalls, ante; Bpl.Expr post = Bpl.Expr.True; Bpl.Trigger tr; if (forallExpressions != null) { - // tranlate based on the forallExpressions since the triggers are computed based on it already. + // 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))); + tr = TrTrigger(callEtran, expr.Attributes, expr.tok, bvars, substMap, s0.MethodSelect.TypeArgumentSubstitutionsWithParents()); + + var p = Substitute(expr.Range, null, substMap); + anteCanCalls = CanCallAssumption(p, initEtran); + ante = BplAnd(ante, initEtran.TrExpr(p)); 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)); + p = Substitute(expr.Term, null, substMap); + post = BplAnd(post, CanCallAssumption(p, callEtran)); + post = BplAnd(post, callEtran.TrExpr(p)); + } 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))); + + var p = Substitute(range, null, substMap); + anteCanCalls = CanCallAssumption(p, initEtran); + ante = BplAnd(ante, initEtran.TrExpr(p)); if (additionalRange != null) { + // additionalRange produces something of the form canCallAssumptions ==> TrExpr 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 + p = Substitute(ens.E, receiver, argsSubstMap, s0.MethodSelect.TypeArgumentSubstitutionsWithParents()); // substitute the call's actuals for the method's formals + post = BplAnd(post, CanCallAssumption(p, callEtran)); post = BplAnd(post, callEtran.TrExpr(p)); } tr = antitriggerBoundVarTypes; @@ -2447,7 +2472,7 @@ void TrForallStmtCall(IToken tok, List boundVars, List (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 Box, $ih#a0#0: Seq Box :: $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)) - qq = new Bpl.ForallExpr(tok, bvars, tr, Bpl.Expr.Imp(ante, post)); // TODO: Add a SMART_TRIGGER here. If we can't find one, abort the attempt to do induction automatically + var qq = new Bpl.ForallExpr(tok, bvars, tr, BplAnd(anteCanCalls, 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)); } } diff --git a/Source/Dafny/Verifier/Translator.cs b/Source/Dafny/Verifier/Translator.cs index 9b5ba84b14a..e0351d36629 100644 --- a/Source/Dafny/Verifier/Translator.cs +++ b/Source/Dafny/Verifier/Translator.cs @@ -2417,6 +2417,7 @@ Bpl.Procedure AddIteratorProc(IteratorDecl iter, MethodTranslationKind kind) { // USER-DEFINED SPECIFICATIONS var comment = "user-defined preconditions"; foreach (var p in iter.Requires) { + req.Add(Requires(p.E.tok, true, CanCallAssumption(p.E, etran), null, comment, AlwaysAssumeAttribute(p.E.tok))); string errorMessage = CustomErrorMessage(p.Attributes); if (p.Label != null && kind == MethodTranslationKind.Implementation) { // don't include this precondition here, but record it for later use @@ -2426,7 +2427,8 @@ Bpl.Procedure AddIteratorProc(IteratorDecl iter, MethodTranslationKind kind) { if (kind == MethodTranslationKind.Call && RefinementToken.IsInherited(s.E.tok, currentModule)) { // this precondition was inherited into this module, so just ignore it } else { - req.Add(Requires(s.E.tok, s.IsOnlyFree, s.E, errorMessage, comment)); + var pre = s.E; + req.Add(Requires(s.E.tok, s.IsOnlyFree, pre, errorMessage, comment)); comment = null; // the free here is not linked to the free on the original expression (this is free things generated in the splitting.) } @@ -2435,6 +2437,8 @@ Bpl.Procedure AddIteratorProc(IteratorDecl iter, MethodTranslationKind kind) { } comment = "user-defined postconditions"; foreach (var p in iter.Ensures) { + var canCalls = CanCallAssumption(p.E, etran); + AddEnsures(ens, Ensures(p.E.tok, true, canCalls, CustomErrorMessage(p.Attributes), comment, AlwaysAssumeAttribute(p.E.tok))); foreach (var s in TrSplitExprForMethodSpec(p.E, etran, kind)) { if (kind == MethodTranslationKind.Implementation && RefinementToken.IsInherited(s.E.tok, currentModule)) { // this postcondition was inherited into this module, so just ignore it @@ -2696,6 +2700,11 @@ public static Bpl.QKeyValue InlineAttribute(Bpl.IToken tok, Bpl.QKeyValue/*?*/ n return new QKeyValue(tok, "inline", new List(), next); } + public static Bpl.QKeyValue AlwaysAssumeAttribute(Bpl.IToken tok, Bpl.QKeyValue next = null) { + Contract.Requires(tok != null); + return new QKeyValue(tok, "always_assume", new List(), next); + } + class Specialization { public readonly List Formals; public readonly List ReplacementExprs; @@ -2955,29 +2964,42 @@ void AddFunctionConsequenceAxiom(Function f, List ens) { Bpl.Expr useViaContext = !InVerificationScope(f) ? Bpl.Expr.True : (Bpl.Expr)Bpl.Expr.Neq(Bpl.Expr.Literal(mod.CallGraph.GetSCCRepresentativePredecessorCount(f)), etran.FunctionContextHeight()); // useViaCanCall: f#canCall(args) - Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool); - Bpl.Expr useViaCanCall = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(canCallFuncID), Concat(tyargs, args)); + var canCallFuncID = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool); + var useViaCanCall = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(canCallFuncID), Concat(tyargs, args)); + var reqCallID = new Bpl.IdentifierExpr(f.tok, RequiresName(f), Bpl.Type.Bool); + var fuelargs = new List(); + if (f.IsFuelAware()) { + fuelargs.Add(new Bpl.IdentifierExpr(f.tok, layer)); + } + var reqCall = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(reqCallID), Concat(tyargs, Concat(fuelargs, args))); // ante := useViaCanCall || (useViaContext && typeAnte && pre) - ante = Bpl.Expr.Or(useViaCanCall, BplAnd(useViaContext, BplAnd(ante, pre))); - + // ante = Bpl.Expr.Or(useViaCanCall, BplAnd(useViaContext, BplAnd(ante, pre))); + ante = useViaCanCall; Bpl.Trigger tr = BplTriggerHeap(this, f.tok, funcAppl, - (AlwaysUseHeap || f.ReadsHeap || !readsHeap) ? null : etran.HeapExpr); + (!AlwaysUseHeap && !f.ReadsHeap && readsHeap) ? etran.HeapExpr : null); + var trReq = BplTriggerHeap(this, f.tok, reqCall, + (!AlwaysUseHeap && !f.ReadsHeap && readsHeap) ? etran.HeapExpr : null); Bpl.Expr post = Bpl.Expr.True; // substitute function return value with the function call. if (f.Result != null) { substMap.Add(f.Result, new BoogieWrapper(funcAppl, f.ResultType)); } foreach (AttributedExpression p in ens) { - Bpl.Expr q = etran.TrExpr(Substitute(p.E, null, substMap)); + var bodyWithSubst = Substitute(p.E, null, substMap); + var canCallEns = CanCallAssumption(bodyWithSubst, etran); + post = BplAnd(post, canCallEns); + var q = etran.TrExpr(bodyWithSubst); post = BplAnd(post, q); } Bpl.Expr whr = GetWhereClause(f.tok, funcAppl, f.ResultType, etran, NOALLOC); if (whr != null) { post = Bpl.Expr.And(post, whr); } Bpl.Expr ax = BplForall(f.tok, new List(), formals, null, tr, Bpl.Expr.Imp(ante, post)); + Bpl.Expr reqAx = BplForall(f.tok, new List(), formals, null, trReq, Bpl.Expr.Imp(reqCall, ante)); var activate = AxiomActivation(f, etran); string comment = "consequence axiom for " + f.FullSanitizedName; + sink.AddTopLevelDeclaration(new Bpl.Axiom(f.tok, BplImp(AxiomActivation(f, etran, true), reqAx), "#requires ==> #canCall for " + f.FullSanitizedName)); sink.AddTopLevelDeclaration(new Bpl.Axiom(f.tok, Bpl.Expr.Imp(activate, ax), comment)); if (CommonHeapUse && !readsHeap) { @@ -2993,20 +3015,23 @@ void AddFunctionConsequenceAxiom(Function f, List ens) { } } - Bpl.Expr AxiomActivation(Function f, ExpressionTranslator etran) { + Bpl.Expr AxiomActivation(Function f, ExpressionTranslator etran, bool strict = false) { Contract.Requires(f != null); Contract.Requires(etran != null); Contract.Requires(VisibleInScope(f)); var module = f.EnclosingClass.EnclosingModuleDefinition; - if (InVerificationScope(f)) { - return - Bpl.Expr.Le(Bpl.Expr.Literal(module.CallGraph.GetSCCRepresentativePredecessorCount(f)), etran.FunctionContextHeight()); + if (strict) { + return Bpl.Expr.Lt(Bpl.Expr.Literal(module.CallGraph.GetSCCRepresentativePredecessorCount(f)), etran.FunctionContextHeight()); + } else { + return Bpl.Expr.Le(Bpl.Expr.Literal(module.CallGraph.GetSCCRepresentativePredecessorCount(f)), etran.FunctionContextHeight()); + } } else { return Bpl.Expr.True; } } + /// /// The list of formals "lits" is allowed to contain an object of type ThisSurrogate, which indicates that /// the receiver parameter of the function should be included among the lit formals. @@ -3240,7 +3265,7 @@ Bpl.Axiom FunctionAxiom(Function f, Expression/*?*/ body, List/*?*/ lits Bpl.Expr useViaCanCall = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(canCallFuncID), Concat(tyargs, args)); // ante := useViaCanCall || (useViaContext && typeAnte && pre) - ante = Bpl.Expr.Or(useViaCanCall, ante); + ante = useViaCanCall; Bpl.Expr funcAppl; { @@ -3424,11 +3449,10 @@ Bpl.Axiom FunctionOverrideAxiom(Function f, Function overridingFunction) { } // Build the triggers - // { f(Succ(s), args), f'(Succ(s), args') } + // { f'(Succ(s), args') } Bpl.Trigger tr = BplTriggerHeap(this, overridingFunction.tok, - funcAppl, - readsHeap ? etran.HeapExpr : null, - overridingFuncAppl); + overridingFuncAppl, + readsHeap ? etran.HeapExpr : null); // { f(Succ(s), args), $Is(this, T') } var exprs = new List() { funcAppl, isOfSubtype }; if (readsHeap) { @@ -3440,10 +3464,17 @@ Bpl.Axiom FunctionOverrideAxiom(Function f, Function overridingFunction) { var synonyms = Bpl.Expr.Eq( funcAppl, ModeledAsBoxType(f.ResultType) ? BoxIfUnboxed(overridingFuncAppl, overridingFunction.ResultType) : overridingFuncAppl); - + // add overridingFunction#canCall ==> f#canCall to the axiom + var callName = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool); + var callArgs = f.IsFuelAware() ? argsJF.TakeLast(argsJF.Count() - 1).ToList() : argsJF; + var canCallFunc = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(callName), callArgs); + callName = new Bpl.IdentifierExpr(overridingFunction.tok, overridingFunction.FullSanitizedName + "#canCall", Bpl.Type.Bool); + callArgs = overridingFunction.IsFuelAware() ? argsCF.TakeLast(argsCF.Count() - 1).ToList() : argsCF; + var canCallOverridingFunc = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(callName), callArgs); + var canCallImp = BplImp(canCallFunc, canCallOverridingFunc); // The axiom Bpl.Expr ax = BplForall(f.tok, new List(), forallFormals, null, tr, - Bpl.Expr.Imp(Bpl.Expr.And(ReceiverNotNull(bvThisExpr), isOfSubtype), synonyms)); + Bpl.Expr.Imp(Bpl.Expr.And(ReceiverNotNull(bvThisExpr), isOfSubtype), BplAnd(canCallImp, synonyms))); var activate = AxiomActivation(f, etran); string comment = "override axiom for " + f.FullSanitizedName + " in class " + overridingFunction.EnclosingClass.FullSanitizedName; return new Bpl.Axiom(f.tok, Bpl.Expr.Imp(activate, ax), comment); @@ -4421,15 +4452,19 @@ void AddMethodImpl(Method m, Bpl.Procedure proc, bool wellformednessProc) { var decrTypes = new List(); var decrCallee = new List(); var decrCaller = new List(); + Bpl.Expr canCalls = Bpl.Expr.True; foreach (var ee in m.Decreases.Expressions) { decrToks.Add(ee.tok); decrTypes.Add(ee.Type.NormalizeExpand()); + canCalls = BplAnd(canCalls, CanCallAssumption(ee, exprTran)); decrCaller.Add(exprTran.TrExpr(ee)); Expression es = Substitute(ee, receiverSubst, substMap); es = Substitute(es, null, decrSubstMap); + canCalls = BplAnd(canCalls, CanCallAssumption(ee, exprTran)); decrCallee.Add(exprTran.TrExpr(es)); } - return DecreasesCheck(decrToks, decrTypes, decrTypes, decrCallee, decrCaller, null, null, false, true); + return BplImp(canCalls, + DecreasesCheck(decrToks, decrTypes, decrTypes, decrCallee, decrCaller, null, null, false, true)); }; #if VERIFY_CORRECTNESS_OF_TRANSLATION_FORALL_STATEMENT_RANGE @@ -4834,6 +4869,22 @@ private void AddFunctionOverrideCheckImpl(Function f) { }; var ens = new List(); + var substMap = new Dictionary(); + for (int i = 0; i < f.Formals.Count; i++) { + // get corresponsing formal in the class + var ie = new IdentifierExpr(f.Formals[i].tok, f.Formals[i].AssignUniqueName(f.IdGenerator)); + ie.Var = f.Formals[i]; ie.Type = ie.Var.Type; + substMap.Add(f.OverriddenFunction.Formals[i], ie); + } + + if (f.OverriddenFunction.Result != null) { + Contract.Assert(pOut != null); + // get corresponsing formal in the class + var ie = new IdentifierExpr(pOut.tok, pOut.AssignUniqueName(f.IdGenerator)); + ie.Var = pOut; ie.Type = ie.Var.Type; + substMap.Add(f.OverriddenFunction.Result, ie); + } + var proc = new Bpl.Procedure(f.tok, "OverrideCheck$$" + f.FullSanitizedName, new List(), Concat(Concat(typeInParams, inParams_Heap), inParams), outParams, req, mod, ens, etran.TrAttributes(f.Attributes, null)); @@ -4858,22 +4909,6 @@ private void AddFunctionOverrideCheckImpl(Function f) { etran = ordinaryEtran; // we no longer need the special heap names } - var substMap = new Dictionary(); - for (int i = 0; i < f.Formals.Count; i++) { - //get corresponsing formal in the class - var ie = new IdentifierExpr(f.Formals[i].tok, f.Formals[i].AssignUniqueName(f.IdGenerator)); - ie.Var = f.Formals[i]; ie.Type = ie.Var.Type; - substMap.Add(f.OverriddenFunction.Formals[i], ie); - } - - if (f.OverriddenFunction.Result != null) { - Contract.Assert(pOut != null); - //get corresponsing formal in the class - var ie = new IdentifierExpr(pOut.tok, pOut.AssignUniqueName(f.IdGenerator)); - ie.Var = pOut; ie.Type = ie.Var.Type; - substMap.Add(f.OverriddenFunction.Result, ie); - } - //adding assume Pre’; assert P; // this checks that Pre’ implies P AddFunctionOverrideReqsChk(f, builder, etran, substMap); @@ -4934,8 +4969,11 @@ private void AddOverrideCheckTypeArgumentInstantiations(MemberDecl member, Boogi private void AddFunctionOverrideEnsChk(Function f, BoogieStmtListBuilder builder, ExpressionTranslator etran, Dictionary substMap, List implInParams, Bpl.Variable/*?*/ resultVariable) { Contract.Requires(f.Formals.Count <= implInParams.Count); + CanCallOptions cco = new CanCallOptions(f, true); + //generating class pre-conditions //generating class post-conditions foreach (var en in f.Ens) { + builder.Add(TrAssumeCmd(f.tok, CanCallAssumption(en.E, etran, cco))); builder.Add(TrAssumeCmd(f.tok, etran.TrExpr(en.E))); } @@ -4991,11 +5029,13 @@ private void AddFunctionOverrideEnsChk(Function f, BoogieStmtListBuilder builder } //generating trait post-conditions with class variables + cco = new CanCallOptions(f.OverriddenFunction, f, true); foreach (var en in f.OverriddenFunction.Ens) { Expression postcond = Substitute(en.E, null, substMap); bool splitHappened; // we don't actually care foreach (var s in TrSplitExpr(postcond, etran, false, out splitHappened)) { if (s.IsChecked) { + builder.Add(TrAssumeCmd(f.tok, CanCallAssumption(postcond, etran, cco))); builder.Add(Assert(f.tok, s.E, "the function must provide an equal or more detailed postcondition than in its parent trait")); } } @@ -5125,15 +5165,19 @@ private void AddFunctionOverrideReqsChk(Function f, BoogieStmtListBuilder builde Contract.Requires(etran != null); Contract.Requires(substMap != null); //generating trait pre-conditions with class variables + CanCallOptions cco = new CanCallOptions(f.OverriddenFunction, f, true); foreach (var req in f.OverriddenFunction.Req) { Expression precond = Substitute(req.E, null, substMap); + builder.Add(TrAssumeCmd(f.tok, CanCallAssumption(precond, etran, cco))); builder.Add(TrAssumeCmd(f.tok, etran.TrExpr(precond))); } //generating class pre-conditions + cco = new CanCallOptions(f, true); foreach (var req in f.Req) { bool splitHappened; // we actually don't care foreach (var s in TrSplitExpr(req.E, etran, false, out splitHappened)) { if (s.IsChecked) { + builder.Add(TrAssumeCmd(f.tok, CanCallAssumption(req.E, etran, cco))); builder.Add(Assert(f.tok, s.E, "the function must provide an equal or more permissive precondition than in its parent trait")); } } @@ -5242,6 +5286,7 @@ private void AddMethodOverrideEnsChk(Method m, BoogieStmtListBuilder builder, Ex Contract.Requires(substMap != null); //generating class post-conditions foreach (var en in m.Ens) { + builder.Add(TrAssumeCmd(m.tok, CanCallAssumption(en.E, etran))); builder.Add(TrAssumeCmd(m.tok, etran.TrExpr(en.E))); } //generating trait post-conditions with class variables @@ -5250,6 +5295,7 @@ private void AddMethodOverrideEnsChk(Method m, BoogieStmtListBuilder builder, Ex bool splitHappened; // we actually don't care foreach (var s in TrSplitExpr(postcond, etran, false, out splitHappened)) { if (s.IsChecked) { + builder.Add(TrAssumeCmd(m.OverriddenMethod.tok, CanCallAssumption(postcond, etran))); builder.Add(Assert(m.tok, s.E, "the method must provide an equal or more detailed postcondition than in its parent trait")); } } @@ -5265,12 +5311,14 @@ private void AddMethodOverrideReqsChk(Method m, BoogieStmtListBuilder builder, E foreach (var req in m.OverriddenMethod.Req) { Expression precond = Substitute(req.E, null, substMap); builder.Add(TrAssumeCmd(m.tok, etran.TrExpr(precond))); + builder.Add(TrAssumeCmd(m.OverriddenMethod.tok, CanCallAssumption(precond, etran))); } //generating class pre-conditions foreach (var req in m.Req) { bool splitHappened; // we actually don't care foreach (var s in TrSplitExpr(req.E, etran, false, out splitHappened)) { if (s.IsChecked) { + builder.Add(TrAssumeCmd(m.tok, CanCallAssumption(req.E, etran))); builder.Add(Assert(m.tok, s.E, "the method must provide an equal or more permissive precondition than in its parent trait")); } } @@ -5597,16 +5645,16 @@ void CheckFrameSubset(IToken tok, List calleeFrame, Contract.Requires(predef != null); // emit: assert (forall o: ref, f: Field alpha :: o != null && $Heap[o,alloc] && (o,f) in subFrame ==> $_Frame[o,f]); - Bpl.TypeVariable alpha = new Bpl.TypeVariable(tok, "alpha"); - Bpl.BoundVariable oVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$o", predef.RefType)); - Bpl.IdentifierExpr o = new Bpl.IdentifierExpr(tok, oVar); - Bpl.BoundVariable fVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$f", predef.FieldName(tok, alpha))); - Bpl.IdentifierExpr f = new Bpl.IdentifierExpr(tok, fVar); - Bpl.Expr ante = Bpl.Expr.And(Bpl.Expr.Neq(o, predef.Null), etran.IsAlloced(tok, o)); - Bpl.Expr oInCallee = InRWClause(tok, o, f, calleeFrame, etran, receiverReplacement, substMap); + var alpha = new Bpl.TypeVariable(tok, "alpha"); + var oVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$o", predef.RefType)); + var o = new Bpl.IdentifierExpr(tok, oVar); + var fVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$f", predef.FieldName(tok, alpha))); + var f = new Bpl.IdentifierExpr(tok, fVar); + var ante = BplAnd(Bpl.Expr.Neq(o, predef.Null), etran.IsAlloced(tok, o)); + var oInCallee = InRWClause(tok, o, f, calleeFrame, etran, receiverReplacement, substMap); Bpl.Expr inEnclosingFrame = Bpl.Expr.Select(etran.TheFrame(tok), o, f); Bpl.Expr q = new Bpl.ForallExpr(tok, new List { alpha }, new List { oVar, fVar }, - Bpl.Expr.Imp(Bpl.Expr.And(ante, oInCallee), inEnclosingFrame)); + BplImp(BplAnd(ante, oInCallee), inEnclosingFrame)); MakeAssert(tok, q, errorMessage, kv); } @@ -5707,13 +5755,26 @@ void AddFrameAxiom(Function f) { Bpl.Expr formal = new Bpl.IdentifierExpr(p.tok, bv); f0args.Add(formal); f1args.Add(formal); f0argsCanCall.Add(formal); f1argsCanCall.Add(formal); Bpl.Expr wh = GetWhereClause(p.tok, formal, p.Type, etran0, useAlloc); - if (wh != null) { fwf0 = Bpl.Expr.And(fwf0, wh); } + if (wh != null) { + fwf0 = Bpl.Expr.And(fwf0, wh); + } + wh = GetWhereClause(p.tok, formal, p.Type, etran1, useAlloc); + if (wh != null) { + fwf1 = Bpl.Expr.And(fwf1, wh); + } } var canCall = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool)); - wellFormed = Bpl.Expr.And(wellFormed, Bpl.Expr.And( - Bpl.Expr.Or(new Bpl.NAryExpr(f.tok, canCall, f0argsCanCall), fwf0), - Bpl.Expr.Or(new Bpl.NAryExpr(f.tok, canCall, f1argsCanCall), fwf1))); - + var f0canCall = new Bpl.NAryExpr(f.tok, canCall, f0argsCanCall); + var f1canCall = new Bpl.NAryExpr(f.tok, canCall, f1argsCanCall); + wellFormed = Bpl.Expr.And(wellFormed, Bpl.Expr.Or( + Bpl.Expr.Or(f0canCall, fwf0), + Bpl.Expr.Or(f1canCall, fwf1))); + /* + JA: I conjecture that we don't need fwf0 or fwf1 here. But, we + will need both can calls, + i.e., + wellFormed = BplAnd(wellFormed, BplOr(f0canCall, f1canCall)) + */ /* DR: I conjecture that this should be enough, as the requires is preserved when the frame is: @@ -5725,7 +5786,8 @@ void AddFrameAxiom(Function f) { var fn = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName, TrType(f.ResultType))); var F0 = new Bpl.NAryExpr(f.tok, fn, f0args); var F1 = new Bpl.NAryExpr(f.tok, fn, f1args); - var eq = Bpl.Expr.Eq(F0, F1); + Expr eq = Bpl.Expr.Eq(F0, F1); + eq = BplAnd(eq, Bpl.Expr.Eq(f0canCall, f1canCall)); var tr = new Bpl.Trigger(f.tok, true, new List { h0IsHeapAnchor, heapSucc, F1 }); var ax = new Bpl.ForallExpr(f.tok, new List(), bvars, null, tr, @@ -5792,6 +5854,7 @@ Bpl.Expr InRWClause_Aux(IToken tok, Bpl.Expr o, Bpl.Expr boxO, Bpl.Expr f, List< } e = Resolver.FrameArrowToObjectSet(e, CurrentIdGenerator, program.BuiltIns); + var canCallE = CanCallAssumption(e, etran); Bpl.Expr disjunct; var eType = e.Type.NormalizeExpand(); @@ -5832,7 +5895,7 @@ Bpl.Expr InRWClause_Aux(IToken tok, Bpl.Expr o, Bpl.Expr boxO, Bpl.Expr f, List< } disjunct = Bpl.Expr.And(disjunct, q); } - disjunction = BplOr(disjunction, disjunct); + disjunction = BplOr(disjunction, BplAnd(canCallE, disjunct)); } return disjunction; } @@ -5915,6 +5978,8 @@ private void AddWellformednessCheck(Function f) { var splits = new List(); bool splitHappened /*we actually don't care*/ = TrSplitExpr(p.E, splits, true, functionHeight, true, true, etran); string errorMessage = CustomErrorMessage(p.Attributes); + var canCalls = CanCallAssumption(p.E, etran, new CanCallOptions(f, true)); + AddEnsures(ens, Ensures(p.E.tok, true, canCalls, errorMessage, null, AlwaysAssumeAttribute(p.E.tok))); foreach (var s in splits) { if (s.IsChecked && !RefinementToken.IsInherited(s.E.tok, currentModule)) { AddEnsures(ens, Ensures(s.E.tok, false, s.E, errorMessage, null)); @@ -5998,6 +6063,7 @@ private void AddWellformednessCheck(Function f) { // } // Here go the postconditions (termination checks included, but no reads checks) BoogieStmtListBuilder postCheckBuilder = new BoogieStmtListBuilder(this); + postCheckBuilder.Add(TrAssumeCmd(f.tok, Bpl.Expr.True, new Bpl.QKeyValue(f.tok, "split_here", new List(), null))); // Assume the type returned by the call itself respects its type (this matters if the type is "nat", for example) { var args = new List(); @@ -6470,7 +6536,23 @@ Bpl.Expr CtorInvocation(IToken tok, DatatypeCtor ctor, ExpressionTranslator etra return new Bpl.NAryExpr(tok, new Bpl.FunctionCall(id), args); } - Bpl.Expr CanCallAssumption(Expression expr, ExpressionTranslator etran) { + private Bpl.Expr MakeAllowance(FunctionCallExpr e, ExpressionTranslator etran, CanCallOptions cco = null) { + Bpl.Expr allowance = Bpl.Expr.True; + if (!e.Function.IsStatic) { + allowance = BplAnd(allowance, Bpl.Expr.Eq(etran.TrExpr(e.Receiver), new Bpl.IdentifierExpr(e.tok, etran.This))); + } + var formals = cco == null ? e.Function.Formals : cco.EnclosingFunction.Formals; + for (int i = 0; i < e.Args.Count; i++) { + Expression ee = e.Args[i]; + Formal ff = formals[i]; + allowance = BplAnd(allowance, + Bpl.Expr.Eq(etran.TrExpr(ee), + new Bpl.IdentifierExpr(e.tok, ff.AssignUniqueName(currentDeclaration.IdGenerator), TrType(ff.Type)))); + } + return allowance; + } + + Bpl.Expr CanCallAssumption(Expression expr, ExpressionTranslator etran, CanCallOptions cco = null) { Contract.Requires(expr != null); Contract.Requires(etran != null); Contract.Requires(predef != null); @@ -6480,17 +6562,17 @@ Bpl.Expr CanCallAssumption(Expression expr, ExpressionTranslator etran) { return Bpl.Expr.True; } else if (expr is DisplayExpression) { DisplayExpression e = (DisplayExpression)expr; - return CanCallAssumption(e.Elements, etran); + return CanCallAssumption(e.Elements, etran, cco); } else if (expr is MapDisplayExpr) { MapDisplayExpr e = (MapDisplayExpr)expr; List l = new List(); foreach (ExpressionPair p in e.Elements) { l.Add(p.A); l.Add(p.B); } - return CanCallAssumption(l, etran); + return CanCallAssumption(l, etran, cco); } else if (expr is MemberSelectExpr) { MemberSelectExpr e = (MemberSelectExpr)expr; - var r = CanCallAssumption(e.Obj, etran); + var r = CanCallAssumption(e.Obj, etran, cco); if (e.Member is DatatypeDestructor) { var dtor = (DatatypeDestructor)e.Member; if (dtor.EnclosingCtors.Count == dtor.EnclosingCtors[0].EnclosingDatatype.Ctors.Count) { @@ -6503,80 +6585,100 @@ Bpl.Expr CanCallAssumption(Expression expr, ExpressionTranslator etran) { return r; } else if (expr is SeqSelectExpr) { SeqSelectExpr e = (SeqSelectExpr)expr; - Bpl.Expr total = CanCallAssumption(e.Seq, etran); + Bpl.Expr total = CanCallAssumption(e.Seq, etran, cco); if (e.E0 != null) { - total = BplAnd(total, CanCallAssumption(e.E0, etran)); + total = BplAnd(total, CanCallAssumption(e.E0, etran, cco)); } if (e.E1 != null) { - total = BplAnd(total, CanCallAssumption(e.E1, etran)); + total = BplAnd(total, CanCallAssumption(e.E1, etran, cco)); } return total; } else if (expr is MultiSelectExpr) { MultiSelectExpr e = (MultiSelectExpr)expr; - Bpl.Expr total = CanCallAssumption(e.Array, etran); + Bpl.Expr total = CanCallAssumption(e.Array, etran, cco); foreach (Expression idx in e.Indices) { - total = BplAnd(total, CanCallAssumption(idx, etran)); + total = BplAnd(total, CanCallAssumption(idx, etran, cco)); } return total; } else if (expr is SeqUpdateExpr) { SeqUpdateExpr e = (SeqUpdateExpr)expr; if (e.ResolvedUpdateExpr != null) { - return CanCallAssumption(e.ResolvedUpdateExpr, etran); + return CanCallAssumption(e.ResolvedUpdateExpr, etran, cco); } - Bpl.Expr total = CanCallAssumption(e.Seq, etran); - total = BplAnd(total, CanCallAssumption(e.Index, etran)); - total = BplAnd(total, CanCallAssumption(e.Value, etran)); + Bpl.Expr total = CanCallAssumption(e.Seq, etran, cco); + total = BplAnd(total, CanCallAssumption(e.Index, etran, cco)); + total = BplAnd(total, CanCallAssumption(e.Value, etran, cco)); return total; } else if (expr is ApplyExpr) { ApplyExpr e = (ApplyExpr)expr; + + Func TrArg = arg => { + Bpl.Expr inner = etran.TrExpr(arg); + if (ModeledAsBoxType(arg.Type)) { + return inner; + } else { + return FunctionCall(arg.tok, BuiltinFunction.Box, null, inner); + } + }; + + var args = Concat( + Map(e.Function.Type.AsArrowType.TypeArgs, TypeToTy), + Cons(etran.HeapExpr, + Cons(etran.TrExpr(e.Function), + e.Args.ConvertAll(arg => TrArg(arg))))); + + var requiresk = FunctionCall(e.tok, Requires(e.Args.Count), Bpl.Type.Bool, args); return BplAnd( - Cons(CanCallAssumption(e.Function, etran), - e.Args.ConvertAll(ee => CanCallAssumption(ee, etran)))); + BplAnd( + Cons(CanCallAssumption(e.Function, etran, cco), + e.Args.ConvertAll(ee => CanCallAssumption(ee, etran, cco)))), + requiresk); } else if (expr is FunctionCallExpr) { FunctionCallExpr e = (FunctionCallExpr)expr; - Bpl.Expr r = CanCallAssumption(e.Receiver, etran); - r = BplAnd(r, CanCallAssumption(e.Args, etran)); + Bpl.Expr r = CanCallAssumption(e.Receiver, etran, cco); + r = BplAnd(r, CanCallAssumption(e.Args, etran, cco)); if (!(e.Function is SpecialFunction)) { - // get to assume canCall Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(expr.tok, e.Function.FullSanitizedName + "#canCall", Bpl.Type.Bool); List args = etran.FunctionInvocationArguments(e, null); Bpl.Expr canCallFuncAppl = new Bpl.NAryExpr(expr.tok, new Bpl.FunctionCall(canCallFuncID), args); - r = BplAnd(r, canCallFuncAppl); + bool makeAllowance = cco != null && (e.Function == cco.SelfCallsAllowance); + var add = makeAllowance ? Bpl.Expr.Or(MakeAllowance(e, etran, cco), canCallFuncAppl) : canCallFuncAppl; + r = BplAnd(r, add); } return r; } else if (expr is DatatypeValue) { DatatypeValue dtv = (DatatypeValue)expr; - return CanCallAssumption(dtv.Arguments, etran); + return CanCallAssumption(dtv.Arguments, etran, cco); } else if (expr is SeqConstructionExpr) { var e = (SeqConstructionExpr)expr; - return BplAnd(CanCallAssumption(e.N, etran), CanCallAssumption(e.Initializer, etran)); + return BplAnd(CanCallAssumption(e.N, etran, cco), CanCallAssumption(e.Initializer, etran, cco)); } else if (expr is MultiSetFormingExpr) { MultiSetFormingExpr e = (MultiSetFormingExpr)expr; - return CanCallAssumption(e.E, etran); + return CanCallAssumption(e.E, etran, cco); } else if (expr is OldExpr) { var e = (OldExpr)expr; - return CanCallAssumption(e.E, etran.OldAt(e.AtLabel)); + return CanCallAssumption(e.E, etran.OldAt(e.AtLabel), cco); } else if (expr is UnchangedExpr) { var e = (UnchangedExpr)expr; Bpl.Expr be = Bpl.Expr.True; foreach (var fe in e.Frame) { - be = BplAnd(be, CanCallAssumption(fe.E, etran)); + be = BplAnd(be, CanCallAssumption(fe.E, etran, cco)); } return be; } else if (expr is UnaryExpr) { var e = (UnaryExpr)expr; - return CanCallAssumption(e.E, etran); + return CanCallAssumption(e.E, etran, cco); } else if (expr is BinaryExpr) { // The short-circuiting boolean operators &&, ||, and ==> end up duplicating their // left argument. Therefore, we first try to re-associate the expression to make // left arguments smaller. if (ReAssociateToTheRight(ref expr)) { - return CanCallAssumption(expr, etran); + return CanCallAssumption(expr, etran, cco); } var e = (BinaryExpr)expr; - Bpl.Expr t0 = CanCallAssumption(e.E0, etran); - Bpl.Expr t1 = CanCallAssumption(e.E1, etran); + Bpl.Expr t0 = CanCallAssumption(e.E0, etran, cco); + Bpl.Expr t1 = CanCallAssumption(e.E1, etran, cco); switch (e.ResolvedOp) { case BinaryExpr.ResolvedOpcode.And: case BinaryExpr.ResolvedOpcode.Imp: @@ -6589,7 +6691,8 @@ Bpl.Expr CanCallAssumption(Expression expr, ExpressionTranslator etran) { case BinaryExpr.ResolvedOpcode.NeqCommon: { Bpl.Expr r = Bpl.Expr.True; var dt = e.E0.Type.AsDatatype; - if (dt != null) { + bool optA = cco == null || (cco != null && !cco.skipIsA); + if (dt != null && optA) { var funcID = new Bpl.FunctionCall(new Bpl.IdentifierExpr(expr.tok, "$IsA#" + dt.FullSanitizedName, Bpl.Type.Bool)); if (!(e.E0.Resolved is DatatypeValue)) { r = BplAnd(r, new Bpl.NAryExpr(expr.tok, funcID, new List { etran.TrExpr(e.E0) })); @@ -6633,7 +6736,7 @@ Bpl.Expr CanCallAssumption(Expression expr, ExpressionTranslator etran) { return BplAnd(t0, t1); } else if (expr is TernaryExpr) { var e = (TernaryExpr)expr; - return BplAnd(CanCallAssumption(e.E0, etran), BplAnd(CanCallAssumption(e.E1, etran), CanCallAssumption(e.E2, etran))); + return BplAnd(CanCallAssumption(e.E0, etran, cco), BplAnd(CanCallAssumption(e.E1, etran, cco), CanCallAssumption(e.E2, etran, cco))); } else if (expr is LetExpr) { var e = (LetExpr)expr; @@ -6654,7 +6757,7 @@ Bpl.Expr CanCallAssumption(Expression expr, ExpressionTranslator etran) { substMap.Add(bv, call); } var p = Substitute(e.Body, null, substMap); - var cc = BplAnd(canCall, CanCallAssumption(p, etran)); + var cc = BplAnd(canCall, CanCallAssumption(p, etran, cco)); return cc; } else { // CanCall[[ var b := RHS(g); Body(b,g,h) ]] = @@ -6662,10 +6765,10 @@ Bpl.Expr CanCallAssumption(Expression expr, ExpressionTranslator etran) { // (var lhs0,lhs1,... := rhs0,rhs1,...; CanCall[[ Body ]]) Bpl.Expr canCallRHS = Bpl.Expr.True; foreach (var rhs in e.RHSs) { - canCallRHS = BplAnd(canCallRHS, CanCallAssumption(rhs, etran)); + canCallRHS = BplAnd(canCallRHS, CanCallAssumption(rhs, etran, cco)); } - var bodyCanCall = CanCallAssumption(e.Body, etran); + var bodyCanCall = CanCallAssumption(e.Body, etran, cco); // We'd like to compute the free variables if "bodyCanCall". It would be nice to use the Boogie // routine Bpl.Expr.ComputeFreeVariables for this purpose. However, calling it requires the Boogie // expression to be resolved. Instead, we do the cheesy thing of computing the set of names of @@ -6709,10 +6812,10 @@ Bpl.Expr CanCallAssumption(Expression expr, ExpressionTranslator etran) { subst[bv] = new BoogieWrapper(ve, bv.Type); } - var canCall = CanCallAssumption(Substitute(e.Body, null, subst), et); + var canCall = CanCallAssumption(Substitute(e.Body, null, subst), et, cco); if (e.Range != null) { var range = Substitute(e.Range, null, subst); - canCall = BplAnd(CanCallAssumption(range, etran), BplImp(etran.TrExpr(range), canCall)); + canCall = BplAnd(CanCallAssumption(range, etran, cco), BplImp(etran.TrExpr(range), canCall)); } // It's important to add the heap last to "bvarsAndAntecedents", because the heap may occur in the antecedents of @@ -6729,16 +6832,16 @@ Bpl.Expr CanCallAssumption(Expression expr, ExpressionTranslator etran) { var e = (ComprehensionExpr)expr; var q = e as QuantifierExpr; if (q != null && q.SplitQuantifier != null) { - return CanCallAssumption(q.SplitQuantifierExpression, etran); + return CanCallAssumption(q.SplitQuantifierExpression, etran, cco); } // Determine the CanCall's for the range and term - var canCall = CanCallAssumption(e.Term, etran); + var canCall = CanCallAssumption(e.Term, etran, cco); if (e.Range != null) { - canCall = BplAnd(CanCallAssumption(e.Range, etran), BplImp(etran.TrExpr(e.Range), canCall)); + canCall = BplAnd(CanCallAssumption(e.Range, etran, cco), BplImp(etran.TrExpr(e.Range), canCall)); } if (expr is MapComprehension mc && mc.IsGeneralMapComprehension) { - canCall = BplAnd(canCall, CanCallAssumption(mc.TermLeft, etran)); + canCall = BplAnd(canCall, CanCallAssumption(mc.TermLeft, etran, cco)); // The translation of "map x,y | R(x,y) :: F(x,y) := G(x,y)" makes use of projection // functions project_x,project_y. These are functions defined here by the following axiom: @@ -6784,30 +6887,30 @@ Bpl.Expr CanCallAssumption(Expression expr, ExpressionTranslator etran) { } else if (expr is StmtExpr) { var e = (StmtExpr)expr; - return CanCallAssumption(e.E, etran); + return CanCallAssumption(e.E, etran, cco); } else if (expr is ITEExpr) { ITEExpr e = (ITEExpr)expr; - Bpl.Expr total = CanCallAssumption(e.Test, etran); + Bpl.Expr total = CanCallAssumption(e.Test, etran, cco); Bpl.Expr test = etran.TrExpr(e.Test); - total = BplAnd(total, BplImp(test, CanCallAssumption(e.Thn, etran))); - total = BplAnd(total, BplImp(Bpl.Expr.Not(test), CanCallAssumption(e.Els, etran))); + total = BplAnd(total, BplImp(test, CanCallAssumption(e.Thn, etran, cco))); + total = BplAnd(total, BplImp(Bpl.Expr.Not(test), CanCallAssumption(e.Els, etran, cco))); return total; } else if (expr is ConcreteSyntaxExpression) { var e = (ConcreteSyntaxExpression)expr; - return CanCallAssumption(e.ResolvedExpression, etran); + return CanCallAssumption(e.ResolvedExpression, etran, cco); } else if (expr is BoogieFunctionCall) { var e = (BoogieFunctionCall)expr; - return CanCallAssumption(e.Args, etran); + return CanCallAssumption(e.Args, etran, cco); } else if (expr is MatchExpr) { var e = (MatchExpr)expr; var ite = etran.DesugarMatchExpr(e); - return CanCallAssumption(ite, etran); + return CanCallAssumption(ite, etran, cco); } else if (expr is BoxingCastExpr) { var e = (BoxingCastExpr)expr; - return CanCallAssumption(e.E, etran); + return CanCallAssumption(e.E, etran, cco); } else if (expr is UnboxingCastExpr) { var e = (UnboxingCastExpr)expr; - return CanCallAssumption(e.E, etran); + return CanCallAssumption(e.E, etran, cco); } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression } @@ -6903,7 +7006,7 @@ void CheckCasePatternShape(CasePattern pat, Bpl.Expr rhs, IToken rhsTok, } } - Bpl.Expr/*!*/ CanCallAssumption(List/*!*/ exprs, ExpressionTranslator/*!*/ etran) { + Bpl.Expr/*!*/ CanCallAssumption(List/*!*/ exprs, ExpressionTranslator/*!*/ etran, CanCallOptions cco) { Contract.Requires(etran != null); Contract.Requires(exprs != null); Contract.Ensures(Contract.Result() != null); @@ -6911,7 +7014,7 @@ void CheckCasePatternShape(CasePattern pat, Bpl.Expr rhs, IToken rhsTok, Bpl.Expr total = Bpl.Expr.True; foreach (Expression e in exprs) { Contract.Assert(e != null); - total = BplAnd(total, CanCallAssumption(e, etran)); + total = BplAnd(total, CanCallAssumption(e, etran, cco)); } return total; } @@ -6937,6 +7040,27 @@ void CheckNonNull(IToken tok, Expression e, BoogieStmtListBuilder builder, Expre } } + private class CanCallOptions { + public readonly Function SelfCallsAllowance; + public readonly Function EnclosingFunction; + + public bool skipIsA; + + public CanCallOptions(Function f, bool skip = false) { + this.SelfCallsAllowance = f; + this.EnclosingFunction = f; + this.skipIsA = skip; + } + + public CanCallOptions(Function f, Function g, bool skip = false) { + Contract.Assert(f.Formals.Count() == g.Formals.Count()); + this.SelfCallsAllowance = f; + this.EnclosingFunction = g; + this.skipIsA = skip; + } + + } + /// /// Instances of WFContext are used as an argument to CheckWellformed, supplying options for the /// checks to be performed. @@ -7580,6 +7704,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions options, Bpl.Expr resu // check that the preconditions for the call hold foreach (AttributedExpression p in e.Function.Req) { Expression precond = Substitute(p.E, e.Receiver, substMap, e.GetTypeArgumentSubstitutions()); + builder.Add(TrAssumeCmd(precond.tok, CanCallAssumption(precond, etran))); bool splitHappened; // we don't actually care string errorMessage = CustomErrorMessage(p.Attributes); foreach (var ss in TrSplitExpr(precond, etran, true, out splitHappened)) { @@ -7615,21 +7740,6 @@ void CheckWellformedWithResult(Expression expr, WFOptions options, Bpl.Expr resu if (options.DoOnlyCoarseGrainedTerminationChecks) { builder.Add(Assert(expr.tok, Bpl.Expr.False, "default-value expression is not allowed to involve recursive or mutually recursive calls")); } else { - List contextDecreases = codeContext.Decreases.Expressions; - List calleeDecreases = e.Function.Decreases.Expressions; - if (e.Function == options.SelfCallsAllowance) { - allowance = Bpl.Expr.True; - if (!e.Function.IsStatic) { - allowance = BplAnd(allowance, Bpl.Expr.Eq(etran.TrExpr(e.Receiver), new Bpl.IdentifierExpr(e.tok, etran.This))); - } - for (int i = 0; i < e.Args.Count; i++) { - Expression ee = e.Args[i]; - Formal ff = e.Function.Formals[i]; - allowance = BplAnd(allowance, - Bpl.Expr.Eq(etran.TrExpr(ee), - new Bpl.IdentifierExpr(e.tok, ff.AssignUniqueName(currentDeclaration.IdGenerator), TrType(ff.Type)))); - } - } string hint; switch (e.CoCall) { case FunctionCallExpr.CoCallResolution.NoBecauseFunctionHasSideEffects: @@ -7654,9 +7764,14 @@ void CheckWellformedWithResult(Expression expr, WFOptions options, Bpl.Expr resu Contract.Assert(false); // unexpected CoCallResolution goto case FunctionCallExpr.CoCallResolution.No; // please the compiler } + if (e.Function == options.SelfCallsAllowance) { + allowance = MakeAllowance(e, etran); + } if (e.CoCallHint != null) { hint = hint == null ? e.CoCallHint : string.Format("{0}; {1}", hint, e.CoCallHint); } + List contextDecreases = codeContext.Decreases.Expressions; + List calleeDecreases = e.Function.Decreases.Expressions; CheckCallTermination(expr.tok, contextDecreases, calleeDecreases, allowance, e.Receiver, substMap, e.GetTypeArgumentSubstitutions(), etran, etran, builder, codeContext.InferredDecreases, hint); } @@ -7944,9 +8059,14 @@ void CheckWellformedWithResult(Expression expr, WFOptions options, Bpl.Expr resu guardPrime = comprehensionEtran.TrExpr(rangePrime); } BplIfIf(e.tok, guard != null, BplAnd(guard, guardPrime), newBuilder, b => { + var canCalls = CanCallAssumption(bodyLeft, comprehensionEtran); + canCalls = BplAnd(canCalls, CanCallAssumption(bodyLeftPrime, comprehensionEtran)); + canCalls = BplAnd(canCalls, CanCallAssumption(body, comprehensionEtran)); + canCalls = BplAnd(canCalls, CanCallAssumption(bodyPrime, comprehensionEtran)); var different = BplOr( Bpl.Expr.Neq(comprehensionEtran.TrExpr(bodyLeft), comprehensionEtran.TrExpr(bodyLeftPrime)), Bpl.Expr.Eq(comprehensionEtran.TrExpr(body), comprehensionEtran.TrExpr(bodyPrime))); + b.Add(new AssumeCmd(mc.TermLeft.tok, canCalls)); b.Add(Assert(mc.TermLeft.tok, different, "key expressions may be referring to the same value")); }); } @@ -8056,13 +8176,14 @@ void CheckWellformedWithResult(Expression expr, WFOptions options, Bpl.Expr resu Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression } - if (result != null) { + if (result == null && expr is ComprehensionExpr) { + builder.Add(TrAssumeCmd(expr.tok, CanCallAssumption(expr, etran))); + } else if (result != null) { Contract.Assert(resultType != null); + builder.Add(TrAssumeCmd(expr.tok, CanCallAssumption(expr, etran))); var bResult = etran.TrExpr(expr); CheckSubrange(expr.tok, bResult, expr.Type, resultType, builder); builder.Add(TrAssumeCmd(expr.tok, Bpl.Expr.Eq(result, bResult))); - builder.Add(TrAssumeCmd(expr.tok, CanCallAssumption(expr, etran))); - builder.Add(new CommentCmd("CheckWellformedWithResult: any expression")); if (AlwaysUseHeap) { builder.Add(TrAssumeCmd(expr.tok, MkIsAlloc(result, resultType, etran.HeapExpr))); } @@ -9666,6 +9787,7 @@ Bpl.Procedure AddMethod(Method m, MethodTranslationKind kind) { var comment = "user-defined preconditions"; foreach (var p in m.Req) { string errorMessage = CustomErrorMessage(p.Attributes); + req.Add(Requires(p.E.tok, true, CanCallAssumption(p.E, etran), null, comment, AlwaysAssumeAttribute(p.E.tok))); if (p.Label != null && kind == MethodTranslationKind.Implementation) { // don't include this precondition here, but record it for later use p.Label.E = (m is TwoStateLemma ? ordinaryEtran : etran.Old).TrExpr(p.E); @@ -9676,8 +9798,7 @@ Bpl.Procedure AddMethod(Method m, MethodTranslationKind kind) { } else if (s.IsOnlyFree && !bodyKind) { // don't include in split -- it would be ignored, anyhow } else { - req.Add(Requires(s.E.tok, s.IsOnlyFree, s.E, errorMessage, comment)); - comment = null; + req.Add(Requires(s.E.tok, s.IsOnlyFree, s.E, errorMessage, null)); // the free here is not linked to the free on the original expression (this is free things generated in the splitting.) } } @@ -9686,8 +9807,8 @@ Bpl.Procedure AddMethod(Method m, MethodTranslationKind kind) { comment = "user-defined postconditions"; foreach (var p in m.Ens) { string errorMessage = CustomErrorMessage(p.Attributes); - AddEnsures(ens, Ensures(p.E.tok, true, CanCallAssumption(p.E, etran), errorMessage, comment)); - comment = null; + var canCalls = CanCallAssumption(p.E, etran); + AddEnsures(ens, Ensures(p.E.tok, true, canCalls, errorMessage, comment, AlwaysAssumeAttribute(p.E.tok))); foreach (var s in TrSplitExprForMethodSpec(p.E, etran, kind)) { var post = s.E; if (kind == MethodTranslationKind.Implementation && RefinementToken.IsInherited(s.E.tok, currentModule)) { @@ -10263,23 +10384,23 @@ Bpl.PredicateCmd AssertNS(Bpl.IToken tok, Bpl.Expr condition, string errorMessag } } - Bpl.Ensures Ensures(IToken tok, bool free, Bpl.Expr condition, string errorMessage, string comment) { + Bpl.Ensures Ensures(IToken tok, bool free, Bpl.Expr condition, string errorMessage, string comment, QKeyValue kv = null) { Contract.Requires(tok != null); Contract.Requires(condition != null); Contract.Ensures(Contract.Result() != null); - Bpl.Ensures ens = new Bpl.Ensures(ForceCheckToken.Unwrap(tok), free, condition, comment); + Bpl.Ensures ens = new Bpl.Ensures(ForceCheckToken.Unwrap(tok), free, condition, comment, kv); if (errorMessage != null) { ens.ErrorData = errorMessage; } return ens; } - Bpl.Requires Requires(IToken tok, bool free, Bpl.Expr condition, string errorMessage, string comment) { + Bpl.Requires Requires(IToken tok, bool free, Bpl.Expr condition, string errorMessage, string comment, QKeyValue kv = null) { Contract.Requires(tok != null); Contract.Requires(condition != null); Contract.Ensures(Contract.Result() != null); - Bpl.Requires req = new Bpl.Requires(ForceCheckToken.Unwrap(tok), free, condition, comment); + Bpl.Requires req = new Bpl.Requires(ForceCheckToken.Unwrap(tok), free, condition, comment, kv); if (errorMessage != null) { req.ErrorData = errorMessage; } @@ -10360,7 +10481,10 @@ private void GenerateAndCheckGuesses(IToken tok, List bvars, List freeOfAlloc = null; if (FrugalHeapUseX) { @@ -10742,9 +10866,9 @@ Dictionary SetupBoundVarsAsLocals(List boundVar return substMap; } - Dictionary SetupBoundVarsAsLocals(List boundVars, BoogieStmtListBuilder builder, - List locals, ExpressionTranslator etran, Dictionary typeMap = null, - string nameSuffix = null) { + Dictionary SetupBoundVarsAsLocals(List boundVars, + BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, + Dictionary typeMap = null, string nameSuffix = null) { Contract.Requires(boundVars != null); Contract.Requires(builder != null); Contract.Requires(locals != null); @@ -13251,6 +13375,7 @@ bool TrSplitExpr(Expression expr, List/*!*/ splits, bool pos if (!position) { ihBody = Bpl.Expr.Not(ihBody); } + ihBody = BplAnd(CanCallAssumption(bodyK, etran), ihBody); ihBody = Bpl.Expr.Imp(less, ihBody); List bvars = new List(); Bpl.Expr typeAntecedent = etran.TrBoundVariables(kvars, bvars); // no need to use allocation antecedent here, because the well-founded less-than ordering assures kk are allocated diff --git a/Test/dafny0/FunctionSpecifications.dfy b/Test/dafny0/FunctionSpecifications.dfy index b9ad2d93848..dc5f2fc59c8 100644 --- a/Test/dafny0/FunctionSpecifications.dfy +++ b/Test/dafny0/FunctionSpecifications.dfy @@ -1,4 +1,4 @@ -// RUN: %dafny /compile:0 /print:"%t.print" /dprint:"%t.dprint" "%s" > "%t" +// RUN: %dafny /compile:0 /print:"%t.print" /timeLimit:10 /dprint:"%t.dprint" "%s" > "%t" // RUN: %diff "%s.expect" "%t" function Fib(n: int): int @@ -36,9 +36,9 @@ function SumBad(a: List): int } function FibWithExtraPost(n: int): int - ensures 2 <= n ==> 0 <= FibWithExtraPost(n-1); // This is fine, because the definition of the function is discovered via canCall - ensures 1 <= n ==> 0 <= FibWithExtraPost(n-1); // Error: In the current implementation of Dafny, one needs to actually call the - // function in order to benefit from canCall. This may be improved in the future. + ensures 2 <= n ==> 0 <= FibWithExtraPost(n-1); + ensures 1 <= n ==> 0 <= FibWithExtraPost(n-1); + ensures 0 <= FibWithExtraPost(n); { if n < 0 then 0 else diff --git a/Test/dafny0/FunctionSpecifications.dfy.expect b/Test/dafny0/FunctionSpecifications.dfy.expect index 9ead3ed2d79..8073f7fb042 100644 --- a/Test/dafny0/FunctionSpecifications.dfy.expect +++ b/Test/dafny0/FunctionSpecifications.dfy.expect @@ -1,7 +1,5 @@ FunctionSpecifications.dfy(29,9): Error: A postcondition might not hold on this return path. FunctionSpecifications.dfy(31,12): Related location: This is the postcondition that might not hold. -FunctionSpecifications.dfy(38,9): Error: A postcondition might not hold on this return path. -FunctionSpecifications.dfy(40,23): Related location: This is the postcondition that might not hold. FunctionSpecifications.dfy(53,10): Error: cannot prove termination; try supplying a decreases clause FunctionSpecifications.dfy(59,9): Error: A postcondition might not hold on this return path. FunctionSpecifications.dfy(60,21): Related location: This is the postcondition that might not hold. diff --git a/Test/git-issues/git-issue-370.dfy b/Test/git-issues/git-issue-370.dfy index b2c9cc5c86a..1bd8d82763d 100644 --- a/Test/git-issues/git-issue-370.dfy +++ b/Test/git-issues/git-issue-370.dfy @@ -10,38 +10,30 @@ // to be good, so this test file is meant to alert us to any changes, in // case we then want to revisit this issue in some way. -datatype T = T(x: int) datatype S = S(u: int, v: int, w: int, x: int, y: int, z: int) -predicate a(t: T) +predicate a(s: S) -predicate WellFormed(t: T) { - && a(t) -} - -function Func(t: T): S - requires WellFormed(t) // Note, there should be NO complaint about this precondition in foo() below. -{ - S(t.x, t.x, t.x, t.x, t.x, t.x) +predicate WellFormed(s: S) { + && a(s) } predicate Good(s: S) { - && s.u == 5 - && s.v == 5 - && s.w == 5 - && s.x == 5 + && s.u == 1 + && s.v == 2 + && s.w == 3 + && s.x == 4 && s.y == 5 - && s.z == 5 + && s.z == 6 } -function {:opaque} GetT(): T { - T(5) +function {:opaque} GetS(): S { + S(1, 2, 3, 4, 5, 6) } lemma foo() - ensures var t := GetT(); - && WellFormed(t) // error (1x) - && Good(Func(t)) // error (5x, but only 4 of these are reported, due to the limit of 5 errors per method) + ensures var s := GetS(); + WellFormed(s) && // error + Good(s) // error (6x, but only 4 of these are reported, due to the limit of 5 errors per method) { - reveal GetT(); -} +} \ No newline at end of file diff --git a/Test/git-issues/git-issue-370.dfy.expect b/Test/git-issues/git-issue-370.dfy.expect index 36c0142805b..cd2b036750e 100644 --- a/Test/git-issues/git-issue-370.dfy.expect +++ b/Test/git-issues/git-issue-370.dfy.expect @@ -1,17 +1,17 @@ -git-issue-370.dfy(45,0): Error: A postcondition might not hold on this return path. -git-issue-370.dfy(43,7): Related location: This is the postcondition that might not hold. -git-issue-370.dfy(19,5): Related location -git-issue-370.dfy(45,0): Error: A postcondition might not hold on this return path. -git-issue-370.dfy(44,7): Related location: This is the postcondition that might not hold. -git-issue-370.dfy(29,9): Related location -git-issue-370.dfy(45,0): Error: A postcondition might not hold on this return path. -git-issue-370.dfy(44,7): Related location: This is the postcondition that might not hold. -git-issue-370.dfy(30,9): Related location -git-issue-370.dfy(45,0): Error: A postcondition might not hold on this return path. -git-issue-370.dfy(44,7): Related location: This is the postcondition that might not hold. -git-issue-370.dfy(31,9): Related location -git-issue-370.dfy(45,0): Error: A postcondition might not hold on this return path. -git-issue-370.dfy(44,7): Related location: This is the postcondition that might not hold. -git-issue-370.dfy(32,9): Related location +git-issue-370.dfy(38,0): Error: A postcondition might not hold on this return path. +git-issue-370.dfy(36,4): Related location: This is the postcondition that might not hold. +git-issue-370.dfy(18,5): Related location +git-issue-370.dfy(38,0): Error: A postcondition might not hold on this return path. +git-issue-370.dfy(37,4): Related location: This is the postcondition that might not hold. +git-issue-370.dfy(22,9): Related location +git-issue-370.dfy(38,0): Error: A postcondition might not hold on this return path. +git-issue-370.dfy(37,4): Related location: This is the postcondition that might not hold. +git-issue-370.dfy(23,9): Related location +git-issue-370.dfy(38,0): Error: A postcondition might not hold on this return path. +git-issue-370.dfy(37,4): Related location: This is the postcondition that might not hold. +git-issue-370.dfy(24,9): Related location +git-issue-370.dfy(38,0): Error: A postcondition might not hold on this return path. +git-issue-370.dfy(37,4): Related location: This is the postcondition that might not hold. +git-issue-370.dfy(25,9): Related location -Dafny program verifier finished with 3 verified, 5 errors +Dafny program verifier finished with 1 verified, 5 errors From facdbefcb5bc30435e7e8e555202035d859bf2fa Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 11 Jun 2024 13:59:50 -0700 Subject: [PATCH 002/151] Fix parameter order to CanCallAssumption --- .../Verifier/BoogieGenerator.TrStatement.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs index b1da325eff5..7d6d9fa04c1 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs @@ -1154,7 +1154,7 @@ void TrForallProof(ForallStmt s, BoogieStmtListBuilder definedness, BoogieStmtLi // check that postconditions hold foreach (var ens in s.Ens) { - definedness.Add(TrAssumeCmd(ens.E.tok, CanCallAssumption(ens.E, etran))); + definedness.Add(TrAssumeCmd(ens.E.tok, etran.CanCallAssumption(ens.E))); foreach (var split in TrSplitExpr(ens.E, etran, true, out var splitHappened)) { if (split.IsChecked) { @@ -1172,7 +1172,7 @@ void TrForallProof(ForallStmt s, BoogieStmtListBuilder definedness, BoogieStmtLi var se = s.Body == null ? Bpl.Expr.True : TrFunctionSideEffect(s.Body, etran); var substMap = new Dictionary(); var p = Substitute(s.EffectiveEnsuresClauses[0], null, substMap); - exporter.Add(TrAssumeCmd(s.Tok, CanCallAssumption(p, etran))); + exporter.Add(TrAssumeCmd(s.Tok, etran.CanCallAssumption(p))); var qq = etran.TrExpr(p); if (s.BoundVars.Count != 0) { exporter.Add(TrAssumeCmd(s.Tok, BplAnd(se, qq))); @@ -1444,8 +1444,8 @@ private Bpl.Expr TrForall_NewValueAssumption(IToken tok, List boundVar tr = new Bpl.Trigger(tok, true, tt, tr); } } - var canCalls = BplAnd(CanCallAssumption(lhs, prevEtran), CanCallAssumption(rhs, prevEtran)); - var canCallRange = CanCallAssumption(range, prevEtran); + var canCalls = BplAnd(prevEtran.CanCallAssumption(lhs), prevEtran.CanCallAssumption(rhs)); + var canCallRange = prevEtran.CanCallAssumption(range); var body = BplAnd(canCalls, Bpl.Expr.Eq(xHeapOF, g)); body = BplImp(xAnte, body); body = BplAnd(canCallRange, body); @@ -1998,7 +1998,7 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, } // check definedness of decreases clause foreach (Expression e in theDecreases) { - builder.Add(TrAssumeCmd(e.tok, Bpl.Expr.Imp(w, CanCallAssumption(e, etran)))); + builder.Add(TrAssumeCmd(e.tok, Bpl.Expr.Imp(w, etran.CanCallAssumption(e)))); TrStmt_CheckWellformed(e, invDefinednessBuilder, locals, etran, true); } if (codeContext is IMethodCodeContext) { @@ -2091,7 +2091,7 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, bodyTr(loopBodyBuilder, updatedFrameEtran); } else { foreach (Expression e in theDecreases) { - loopBodyBuilder.Add(TrAssumeCmd(e.tok, BplImp(w, CanCallAssumption(e, etran)))); + loopBodyBuilder.Add(TrAssumeCmd(e.tok, BplImp(w, etran.CanCallAssumption(e)))); } List oldBfs = RecordDecreasesValue(theDecreases, loopBodyBuilder, locals, etran, "$decr$" + suffix); // time for the actual loop body @@ -2112,7 +2112,7 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, initDecrsDafny.Add(eInit); decrs.Add(etran.TrExpr(e)); // need to add can calls again because the actual loop body updates the variables - loopBodyBuilder.Add(TrAssumeCmd(e.tok, BplImp(w, CanCallAssumption(e, etran)))); + loopBodyBuilder.Add(TrAssumeCmd(e.tok, BplImp(w, etran.CanCallAssumption(e)))); } if (includeTerminationCheck) { AddComment(loopBodyBuilder, s, "loop termination check"); @@ -2727,13 +2727,13 @@ void TrForallStmtCall(IToken tok, List boundVars, List bo tr = TrTrigger(callEtran, expr.Attributes, expr.tok, bvars, substMap, s0.MethodSelect.TypeArgumentSubstitutionsWithParents()); var p = Substitute(expr.Range, null, substMap); - anteCanCalls = CanCallAssumption(p, initEtran); + anteCanCalls = initEtran.CanCallAssumption(p); ante = BplAnd(ante, initEtran.TrExpr(p)); if (additionalRange != null) { ante = BplAnd(ante, additionalRange(substMap, initEtran)); } p = Substitute(expr.Term, null, substMap); - post = BplAnd(post, CanCallAssumption(p, callEtran)); + post = BplAnd(post, callEtran.CanCallAssumption(p)); post = BplAnd(post, callEtran.TrExpr(p)); } else { @@ -2744,7 +2744,7 @@ void TrForallStmtCall(IToken tok, List boundVars, List bo } var p = Substitute(range, null, substMap); - anteCanCalls = CanCallAssumption(p, initEtran); + anteCanCalls = initEtran.CanCallAssumption(p); ante = BplAnd(ante, initEtran.TrExpr(p)); if (additionalRange != null) { // additionalRange produces something of the form canCallAssumptions ==> TrExpr @@ -2753,7 +2753,7 @@ void TrForallStmtCall(IToken tok, List boundVars, List bo var receiver = new BoogieWrapper(initEtran.TrExpr(Substitute(s0.Receiver, null, substMap, s0.MethodSelect.TypeArgumentSubstitutionsWithParents())), s0.Receiver.Type); foreach (var ens in s0.Method.Ens) { p = Substitute(ens.E, receiver, argsSubstMap, s0.MethodSelect.TypeArgumentSubstitutionsWithParents()); // substitute the call's actuals for the method's formals - post = BplAnd(post, CanCallAssumption(p, callEtran)); + post = BplAnd(post, callEtran.CanCallAssumption(p)); post = BplAnd(post, callEtran.TrExpr(p)); } tr = antitriggerBoundVarTypes; From 2cd6a52f91de1bdec360a30e7d23d30781db3835 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 11 Jun 2024 15:51:49 -0700 Subject: [PATCH 003/151] Manually merge in the changes from the old Translator.cs --- .../BoogieGenerator.ExpressionTranslator.cs | 175 ++++++++++++------ .../BoogieGenerator.ExpressionWellformed.cs | 32 ++-- .../Verifier/BoogieGenerator.Functions.cs | 70 ++++--- .../Verifier/BoogieGenerator.Iterators.cs | 3 + .../Verifier/BoogieGenerator.Methods.cs | 45 +++-- .../Verifier/BoogieGenerator.SplitExpr.cs | 1 + .../Verifier/BoogieGenerator.Types.cs | 3 + Source/DafnyCore/Verifier/BoogieGenerator.cs | 25 ++- 8 files changed, 238 insertions(+), 116 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index ea44fc07b4e..68140052424 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -2125,7 +2125,21 @@ public Boogie.Expr GoodRef(IToken tok, Boogie.Expr e, Type type) { return BoogieGenerator.GetWhereClause(tok, e, type, this, ISALLOC); } - public Expr CanCallAssumption(Expression expr) { + public Expression MakeAllowance(FunctionCallExpr e, CanCallOptions cco = null) { + Expression allowance = Expression.CreateBoolLiteral(e.tok, true); + if (!e.Function.IsStatic) { + allowance = Expression.CreateAnd(allowance, Expression.CreateEq(e.Receiver, new ThisExpr(e.Function), e.Receiver.Type)); + } + var formals = cco == null ? e.Function.Ins : cco.EnclosingFunction.Ins; + for (int i = 0; i < e.Args.Count; i++) { + Expression ee = e.Args[i]; + Formal ff = formals[i]; + allowance = Expression.CreateAnd(allowance, Expression.CreateEq(ee, Expression.CreateIdentExpr(ff), ff.Type)); + } + return allowance; + } + + public Expr CanCallAssumption(Expression expr, CanCallOptions cco = null) { Contract.Requires(expr != null); Contract.Requires(this != null); Contract.Requires(BoogieGenerator.predef != null); @@ -2135,17 +2149,17 @@ public Expr CanCallAssumption(Expression expr) { return Boogie.Expr.True; } else if (expr is DisplayExpression) { DisplayExpression e = (DisplayExpression)expr; - return CanCallAssumption(e.Elements); + return CanCallAssumption(e.Elements, cco); } else if (expr is MapDisplayExpr) { MapDisplayExpr e = (MapDisplayExpr)expr; List l = new List(); foreach (ExpressionPair p in e.Elements) { l.Add(p.A); l.Add(p.B); } - return CanCallAssumption(l); + return CanCallAssumption(l, cco); } else if (expr is MemberSelectExpr) { MemberSelectExpr e = (MemberSelectExpr)expr; - var r = CanCallAssumption(e.Obj); + var r = CanCallAssumption(e.Obj, cco); if (e.Member is DatatypeDestructor) { var dtor = (DatatypeDestructor)e.Member; if (dtor.EnclosingCtors.Count == dtor.EnclosingCtors[0].EnclosingDatatype.Ctors.Count) { @@ -2158,77 +2172,99 @@ public Expr CanCallAssumption(Expression expr) { return r; } else if (expr is SeqSelectExpr) { SeqSelectExpr e = (SeqSelectExpr)expr; - Boogie.Expr total = CanCallAssumption(e.Seq); + Boogie.Expr total = CanCallAssumption(e.Seq, cco); if (e.E0 != null) { - total = BplAnd(total, CanCallAssumption(e.E0)); + total = BplAnd(total, CanCallAssumption(e.E0, cco)); } if (e.E1 != null) { - total = BplAnd(total, CanCallAssumption(e.E1)); + total = BplAnd(total, CanCallAssumption(e.E1, cco)); } return total; } else if (expr is MultiSelectExpr) { MultiSelectExpr e = (MultiSelectExpr)expr; - Boogie.Expr total = CanCallAssumption(e.Array); + Boogie.Expr total = CanCallAssumption(e.Array, cco); foreach (Expression idx in e.Indices) { - total = BplAnd(total, CanCallAssumption(idx)); + total = BplAnd(total, CanCallAssumption(idx, cco)); } return total; } else if (expr is SeqUpdateExpr) { SeqUpdateExpr e = (SeqUpdateExpr)expr; - Boogie.Expr total = CanCallAssumption(e.Seq); - total = BplAnd(total, CanCallAssumption(e.Index)); - total = BplAnd(total, CanCallAssumption(e.Value)); + Boogie.Expr total = CanCallAssumption(e.Seq, cco); + total = BplAnd(total, CanCallAssumption(e.Index, cco)); + total = BplAnd(total, CanCallAssumption(e.Value, cco)); return total; + } else if (expr is ApplyExpr) { ApplyExpr e = (ApplyExpr)expr; + + Func TrArg = arg => { + Boogie.Expr inner = TrExpr(arg); + if (ModeledAsBoxType(arg.Type)) { + return inner; + } else { + return FunctionCall(arg.tok, BuiltinFunction.Box, null, inner); + } + }; + + var args = Concat( + Map(e.Function.Type.AsArrowType.TypeArgs, TypeToTy), + Cons(HeapExpr, + Cons(TrExpr(e.Function), + e.Args.ConvertAll(arg => TrArg(arg))))); + + var requiresk = FunctionCall(e.tok, Requires(e.Args.Count), Boogie.Type.Bool, args); return BplAnd( - Cons(CanCallAssumption(e.Function), - e.Args.ConvertAll(ee => CanCallAssumption(ee)))); + BplAnd( + Cons(CanCallAssumption(e.Function, cco), + e.Args.ConvertAll(ee => CanCallAssumption(ee, cco)))), + requiresk); + } else if (expr is FunctionCallExpr) { FunctionCallExpr e = (FunctionCallExpr)expr; - Boogie.Expr r = CanCallAssumption(e.Receiver); - r = BplAnd(r, CanCallAssumption(e.Args)); + Boogie.Expr r = CanCallAssumption(e.Receiver, cco); + r = BplAnd(r, CanCallAssumption(e.Args, cco)); if (!(e.Function is SpecialFunction)) { - // get to assume canCall Boogie.IdentifierExpr canCallFuncID = new Boogie.IdentifierExpr(expr.tok, e.Function.FullSanitizedName + "#canCall", Boogie.Type.Bool); List args = FunctionInvocationArguments(e, null, null); Boogie.Expr canCallFuncAppl = new Boogie.NAryExpr(BoogieGenerator.GetToken(expr), new Boogie.FunctionCall(canCallFuncID), args); - r = BplAnd(r, canCallFuncAppl); + bool makeAllowance = cco != null && (e.Function == cco.SelfCallsAllowance); + var add = makeAllowance ? Boogie.Expr.Or(MakeAllowance(e, cco), canCallFuncAppl) : canCallFuncAppl; + r = BplAnd(r, add); } return r; } else if (expr is DatatypeValue) { DatatypeValue dtv = (DatatypeValue)expr; - return CanCallAssumption(dtv.Arguments); + return CanCallAssumption(dtv.Arguments, cco); } else if (expr is SeqConstructionExpr) { var e = (SeqConstructionExpr)expr; - return BplAnd(CanCallAssumption(e.N), CanCallAssumption(e.Initializer)); + return BplAnd(CanCallAssumption(e.N, cco), CanCallAssumption(e.Initializer, cco)); } else if (expr is MultiSetFormingExpr) { MultiSetFormingExpr e = (MultiSetFormingExpr)expr; - return CanCallAssumption(e.E); + return CanCallAssumption(e.E, cco); } else if (expr is OldExpr) { var e = (OldExpr)expr; - return OldAt(e.AtLabel).CanCallAssumption(e.E); + return OldAt(e.AtLabel).CanCallAssumption(e.E, cco); } else if (expr is UnchangedExpr) { var e = (UnchangedExpr)expr; Boogie.Expr be = Boogie.Expr.True; foreach (var fe in e.Frame) { - be = BplAnd(be, CanCallAssumption(fe.E)); + be = BplAnd(be, CanCallAssumption(fe.E, cco)); } return be; } else if (expr is UnaryExpr) { var e = (UnaryExpr)expr; - return CanCallAssumption(e.E); + return CanCallAssumption(e.E, cco); } else if (expr is BinaryExpr) { // The short-circuiting boolean operators &&, ||, and ==> end up duplicating their // left argument. Therefore, we first try to re-associate the expression to make // left arguments smaller. if (BoogieGenerator.ReAssociateToTheRight(ref expr)) { - return CanCallAssumption(expr); + return CanCallAssumption(expr, cco); } var e = (BinaryExpr)expr; - Boogie.Expr t0 = CanCallAssumption(e.E0); - Boogie.Expr t1 = CanCallAssumption(e.E1); + Boogie.Expr t0 = CanCallAssumption(e.E0, cco); + Boogie.Expr t1 = CanCallAssumption(e.E1, cco); switch (e.ResolvedOp) { case BinaryExpr.ResolvedOpcode.And: case BinaryExpr.ResolvedOpcode.Imp: @@ -2240,13 +2276,15 @@ public Expr CanCallAssumption(Expression expr) { case BinaryExpr.ResolvedOpcode.EqCommon: case BinaryExpr.ResolvedOpcode.NeqCommon: { Boogie.Expr r = Boogie.Expr.True; - if (e.E0 is { Type: { AsDatatype: { } dt0 }, Resolved: not DatatypeValue }) { - var funcID = new Boogie.FunctionCall(new Boogie.IdentifierExpr(expr.tok, "$IsA#" + dt0.FullSanitizedName, Boogie.Type.Bool)); - r = BplAnd(r, new Boogie.NAryExpr(expr.tok, funcID, new List { TrExpr(e.E0) })); - } - if (e.E1 is { Type: { AsDatatype: { } dt1 }, Resolved: not DatatypeValue }) { - var funcID = new Boogie.FunctionCall(new Boogie.IdentifierExpr(expr.tok, "$IsA#" + dt1.FullSanitizedName, Boogie.Type.Bool)); - r = BplAnd(r, new Boogie.NAryExpr(expr.tok, funcID, new List { TrExpr(e.E1) })); + if (cco == null || !cco.skipIsA) { + if (e.E0 is { Type: { AsDatatype: { } dt0 }, Resolved: not DatatypeValue }) { + var funcID = new Boogie.FunctionCall(new Boogie.IdentifierExpr(expr.tok, "$IsA#" + dt0.FullSanitizedName, Boogie.Type.Bool)); + r = BplAnd(r, new Boogie.NAryExpr(expr.tok, funcID, new List { TrExpr(e.E0) })); + } + if (e.E1 is { Type: { AsDatatype: { } dt1 }, Resolved: not DatatypeValue }) { + var funcID = new Boogie.FunctionCall(new Boogie.IdentifierExpr(expr.tok, "$IsA#" + dt1.FullSanitizedName, Boogie.Type.Bool)); + r = BplAnd(r, new Boogie.NAryExpr(expr.tok, funcID, new List { TrExpr(e.E1) })); + } } return BplAnd(r, BplAnd(t0, t1)); } @@ -2283,7 +2321,7 @@ public Expr CanCallAssumption(Expression expr) { return BplAnd(t0, t1); } else if (expr is TernaryExpr) { var e = (TernaryExpr)expr; - return BplAnd(CanCallAssumption(e.E0), BplAnd(CanCallAssumption(e.E1), CanCallAssumption(e.E2))); + return BplAnd(CanCallAssumption(e.E0, cco), BplAnd(CanCallAssumption(e.E1, cco), CanCallAssumption(e.E2, cco))); } else if (expr is LetExpr) { var e = (LetExpr)expr; @@ -2304,7 +2342,7 @@ public Expr CanCallAssumption(Expression expr) { substMap.Add(bv, call); } var p = Substitute(e.Body, null, substMap); - var cc = BplAnd(canCall, CanCallAssumption(p)); + var cc = BplAnd(canCall, CanCallAssumption(p, cco)); return cc; } else { // CanCall[[ var b := RHS(g); Body(b,g,h) ]] = @@ -2312,10 +2350,10 @@ public Expr CanCallAssumption(Expression expr) { // (var lhs0,lhs1,... := rhs0,rhs1,...; CanCall[[ Body ]]) Boogie.Expr canCallRHS = Boogie.Expr.True; foreach (var rhs in e.RHSs) { - canCallRHS = BplAnd(canCallRHS, CanCallAssumption(rhs)); + canCallRHS = BplAnd(canCallRHS, CanCallAssumption(rhs, cco)); } - var bodyCanCall = CanCallAssumption(e.Body); + var bodyCanCall = CanCallAssumption(e.Body, cco); // We'd like to compute the free variables if "bodyCanCall". It would be nice to use the Boogie // routine Bpl.Expr.ComputeFreeVariables for this purpose. However, calling it requires the Boogie // expression to be resolved. Instead, we do the cheesy thing of computing the set of names of @@ -2359,10 +2397,10 @@ public Expr CanCallAssumption(Expression expr) { subst[bv] = new BoogieWrapper(ve, bv.Type); } - var canCall = et.CanCallAssumption(Substitute(e.Body, null, subst)); + var canCall = et.CanCallAssumption(Substitute(e.Body, null, subst), cco); if (e.Range != null) { var range = Substitute(e.Range, null, subst); - canCall = BplAnd(CanCallAssumption(range), BplImp(TrExpr(range), canCall)); + canCall = BplAnd(CanCallAssumption(range, cco), BplImp(TrExpr(range), canCall)); } // It's important to add the heap last to "bvarsAndAntecedents", because the heap may occur in the antecedents of @@ -2378,16 +2416,16 @@ public Expr CanCallAssumption(Expression expr) { } else if (expr is ComprehensionExpr) { var e = (ComprehensionExpr)expr; if (e is QuantifierExpr q && q.SplitQuantifier != null) { - return CanCallAssumption(q.SplitQuantifierExpression); + return CanCallAssumption(q.SplitQuantifierExpression, cco); } // Determine the CanCall's for the range and term - var canCall = CanCallAssumption(e.Term); + var canCall = CanCallAssumption(e.Term, cco); if (e.Range != null) { - canCall = BplAnd(CanCallAssumption(e.Range), BplImp(TrExpr(e.Range), canCall)); + canCall = BplAnd(CanCallAssumption(e.Range, cco), BplImp(TrExpr(e.Range), canCall)); } if (expr is MapComprehension mc && mc.IsGeneralMapComprehension) { - canCall = BplAnd(canCall, CanCallAssumption(mc.TermLeft)); + canCall = BplAnd(canCall, CanCallAssumption(mc.TermLeft, cco)); // The translation of "map x,y | R(x,y) :: F(x,y) := G(x,y)" makes use of projection // functions project_x,project_y. These are functions defined here by the following axiom: @@ -2427,42 +2465,42 @@ public Expr CanCallAssumption(Expression expr) { } else if (expr is StmtExpr) { var e = (StmtExpr)expr; - return CanCallAssumption(e.E); + return CanCallAssumption(e.E, cco); } else if (expr is ITEExpr) { ITEExpr e = (ITEExpr)expr; - Boogie.Expr total = CanCallAssumption(e.Test); + Boogie.Expr total = CanCallAssumption(e.Test, cco); Boogie.Expr test = TrExpr(e.Test); - total = BplAnd(total, BplImp(test, CanCallAssumption(e.Thn))); - total = BplAnd(total, BplImp(Boogie.Expr.Not(test), CanCallAssumption(e.Els))); + total = BplAnd(total, BplImp(test, CanCallAssumption(e.Thn, cco))); + total = BplAnd(total, BplImp(Boogie.Expr.Not(test), CanCallAssumption(e.Els, cco))); return total; } else if (expr is ConcreteSyntaxExpression) { var e = (ConcreteSyntaxExpression)expr; - return CanCallAssumption(e.ResolvedExpression); + return CanCallAssumption(e.ResolvedExpression, cco); } else if (expr is NestedMatchExpr nestedMatchExpr) { - return CanCallAssumption(nestedMatchExpr.Flattened); + return CanCallAssumption(nestedMatchExpr.Flattened, cco); } else if (expr is BoogieFunctionCall) { var e = (BoogieFunctionCall)expr; - return CanCallAssumption(e.Args); + return CanCallAssumption(e.Args, cco); } else if (expr is MatchExpr) { var e = (MatchExpr)expr; var ite = DesugarMatchExpr(e); - return CanCallAssumption(ite); + return CanCallAssumption(ite, cco); } else if (expr is BoxingCastExpr) { var e = (BoxingCastExpr)expr; - return CanCallAssumption(e.E); + return CanCallAssumption(e.E, cco); } else if (expr is UnboxingCastExpr) { var e = (UnboxingCastExpr)expr; - return CanCallAssumption(e.E); + return CanCallAssumption(e.E, cco); } else if (expr is DecreasesToExpr decreasesToExpr) { - var oldCanCall = CanCallAssumption(decreasesToExpr.OldExpressions.ToList()); - var newCanCall = CanCallAssumption(decreasesToExpr.NewExpressions.ToList()); + var oldCanCall = CanCallAssumption(decreasesToExpr.OldExpressions.ToList(), cco); + var newCanCall = CanCallAssumption(decreasesToExpr.NewExpressions.ToList(), cco); return BplAnd(oldCanCall, newCanCall); } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression } } - public Expr /*!*/ CanCallAssumption(List exprs /*!*/ /*!*/) { + public Expr CanCallAssumption(List exprs, CanCallOptions cco) { Contract.Requires(this != null); Contract.Requires(exprs != null); Contract.Ensures(Contract.Result() != null); @@ -2470,10 +2508,31 @@ public Expr CanCallAssumption(Expression expr) { Boogie.Expr total = Boogie.Expr.True; foreach (Expression e in exprs) { Contract.Assert(e != null); - total = BplAnd(total, CanCallAssumption(e)); + total = BplAnd(total, CanCallAssumption(e, cco)); } return total; } } + + public class CanCallOptions { + public readonly Function SelfCallsAllowance; + public readonly Function EnclosingFunction; + + public bool skipIsA; + + public CanCallOptions(Function f, bool skip = false) { + this.SelfCallsAllowance = f; + this.EnclosingFunction = f; + this.skipIsA = skip; + } + + public CanCallOptions(Function f, Function g, bool skip = false) { + Contract.Assert(f.Ins.Count() == g.Ins.Count()); + this.SelfCallsAllowance = f; + this.EnclosingFunction = g; + this.skipIsA = skip; + } + + } } } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 0281ed86270..fd7ae3b1896 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -810,6 +810,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Bpl.Expr re var directPrecond = directSub.Substitute(p.E); Expression precond = Substitute(p.E, e.Receiver, substMap, e.GetTypeArgumentSubstitutions()); + builder.Add(TrAssumeCmd(precond.tok, etran.CanCallAssumption(precond))); var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); foreach (var ss in TrSplitExpr(precond, etran, true, out var splitHappened)) { if (ss.IsChecked) { @@ -851,19 +852,6 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Bpl.Expr re if (wfOptions.DoOnlyCoarseGrainedTerminationChecks) { builder.Add(Assert(GetToken(expr), Bpl.Expr.False, new PODesc.IsNonRecursive())); } else { - List contextDecreases = codeContext.Decreases.Expressions; - List calleeDecreases = e.Function.Decreases.Expressions; - if (e.Function == wfOptions.SelfCallsAllowance) { - allowance = Expression.CreateBoolLiteral(e.tok, true); - if (!e.Function.IsStatic) { - allowance = Expression.CreateAnd(allowance, Expression.CreateEq(e.Receiver, new ThisExpr(e.Function), e.Receiver.Type)); - } - for (int i = 0; i < e.Args.Count; i++) { - Expression ee = e.Args[i]; - Formal ff = e.Function.Ins[i]; - allowance = Expression.CreateAnd(allowance, Expression.CreateEq(ee, Expression.CreateIdentExpr(ff), ff.Type)); - } - } string hint; switch (e.CoCall) { case FunctionCallExpr.CoCallResolution.NoBecauseFunctionHasSideEffects: @@ -888,9 +876,15 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Bpl.Expr re Contract.Assert(false); // unexpected CoCallResolution goto case FunctionCallExpr.CoCallResolution.No; // please the compiler } + + if (e.Function == options.SelfCallsAllowance) { + allowance = etran.MakeAllowance(e); + } if (e.CoCallHint != null) { hint = hint == null ? e.CoCallHint : string.Format("{0}; {1}", hint, e.CoCallHint); } + List contextDecreases = codeContext.Decreases.Expressions; + List calleeDecreases = e.Function.Decreases.Expressions; CheckCallTermination(callExpr.tok, contextDecreases, calleeDecreases, allowance, e.Receiver, substMap, directSubstMap, e.GetTypeArgumentSubstitutions(), etran, false, builder, codeContext.InferredDecreases, hint); } @@ -1225,9 +1219,14 @@ void CheckOperand(Expression operand) { guardPrime = comprehensionEtran.TrExpr(rangePrime); } BplIfIf(e.tok, guard != null, BplAnd(guard, guardPrime), newBuilder, b => { + var canCalls = comprehensionEtran.CanCallAssumption(bodyLeft); + canCalls = BplAnd(canCalls, comprehensionEtran.CanCallAssumption(bodyLeftPrime)); + canCalls = BplAnd(canCalls, comprehensionEtran.CanCallAssumption(body)); + canCalls = BplAnd(canCalls, comprehensionEtran.CanCallAssumption(bodyPrime)); var different = BplOr( Bpl.Expr.Neq(comprehensionEtran.TrExpr(bodyLeft), comprehensionEtran.TrExpr(bodyLeftPrime)), Bpl.Expr.Eq(comprehensionEtran.TrExpr(body), comprehensionEtran.TrExpr(bodyPrime))); + b.Add(new AssumeCmd(mc.TermLeft.tok, canCalls)); b.Add(Assert(GetToken(mc.TermLeft), different, new PODesc.ComprehensionNoAlias(mc.BoundVars, mc.Range, mc.TermLeft, mc.Term))); }); } @@ -1336,15 +1335,16 @@ void CheckOperand(Expression operand) { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected expression } - if (result != null) { + if (result == null && expr is ComprehensionExpr) { + builder.Add(TrAssumeCmd(expr.tok, etran.CanCallAssumption(expr))); + } else if (result != null) { Contract.Assert(resultType != null); + builder.Add(TrAssumeCmd(expr.tok, etran.CanCallAssumption(expr))); var bResult = etran.TrExpr(expr); CheckSubrange(expr.tok, bResult, expr.Type, resultType, expr, builder); builder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, expr.tok, expr, e => Bpl.Expr.Eq(result, CondApplyBox(expr.tok, e, expr.Type, resultType)), resultDescription)); - builder.Add(TrAssumeCmd(expr.tok, etran.CanCallAssumption(expr))); - builder.Add(new CommentCmd("CheckWellformedWithResult: any expression")); builder.Add(TrAssumeCmd(expr.tok, MkIs(result, resultType))); } } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs index 762593495f3..c9829498024 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs @@ -91,6 +91,8 @@ void AddWellformednessCheck(Function f) { var splits = new List(); bool splitHappened /*we actually don't care*/ = TrSplitExpr(p.E, splits, true, functionHeight, true, true, etran); var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); + var canCalls = etran.CanCallAssumption(p.E, new CanCallOptions(f, true)); + AddEnsures(ens, Ensures(p.E.tok, true, canCalls, errorMessage, null, AlwaysAssumeAttribute(p.E.tok))); foreach (var s in splits) { if (s.IsChecked && !RefinementToken.IsInherited(s.Tok, currentModule)) { AddEnsures(ens, EnsuresWithDependencies(s.Tok, false, p.E, s.E, errorMessage, successMessage, null)); @@ -517,15 +519,21 @@ void AddFunctionConsequenceAxiom(Boogie.Function boogieFunction, Function f, Lis ? Bpl.Expr.True : Bpl.Expr.Lt(Expr.Literal(forModule.CallGraph.GetSCCRepresentativePredecessorCount(f)), etran.FunctionContextHeight()); // useViaCanCall: f#canCall(args) - Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool); - Bpl.Expr useViaCanCall = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(canCallFuncID), Concat(tyargs, args)); + var canCallFuncID = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool); + var useViaCanCall = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(canCallFuncID), Concat(tyargs, args)); + var reqCallID = new Bpl.IdentifierExpr(f.tok, RequiresName(f), Bpl.Type.Bool); + var fuelargs = new List(); + if (f.IsFuelAware()) { + fuelargs.Add(new Bpl.IdentifierExpr(f.tok, layer)); + } + var reqCall = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(reqCallID), Concat(tyargs, Concat(fuelargs, args))); - // ante := useViaCanCall || (useViaContext && typeAnte && pre) - ante = BplOr(useViaCanCall, BplAnd(useViaContext, BplAnd(ante, pre))); - anteIsAlloc = BplOr(useViaCanCall, BplAnd(useViaContext, BplAnd(anteIsAlloc, pre))); + // ante := useViaCanCall + ante = useViaCanCall; + anteIsAlloc = useViaCanCall; - Bpl.Trigger tr = BplTriggerHeap(this, f.tok, funcAppl, - (f.ReadsHeap || !readsHeap) ? null : etran.HeapExpr); + var tr = BplTriggerHeap(this, f.tok, funcAppl, (f.ReadsHeap || !readsHeap) ? null : etran.HeapExpr); + var trReq = BplTriggerHeap(this, f.tok, reqCall, (f.ReadsHeap || !readsHeap) ? null : etran.HeapExpr); Bpl.Expr post = Bpl.Expr.True; // substitute function return value with the function call. var substMap = new Dictionary(); @@ -533,7 +541,10 @@ void AddFunctionConsequenceAxiom(Boogie.Function boogieFunction, Function f, Lis substMap.Add(f.Result, new BoogieWrapper(funcAppl, f.ResultType)); } foreach (AttributedExpression p in ens) { - Bpl.Expr q = etran.TrExpr(Substitute(p.E, null, substMap)); + var bodyWithSubst = Substitute(p.E, null, substMap); + var canCallEns = etran.CanCallAssumption(bodyWithSubst); + post = BplAnd(post, canCallEns); + var q = etran.TrExpr(bodyWithSubst); post = BplAnd(post, q); } var (olderParameterCount, olderCondition) = OlderCondition(f, funcAppl, olderInParams); @@ -545,12 +556,13 @@ void AddFunctionConsequenceAxiom(Boogie.Function boogieFunction, Function f, Lis Bpl.Expr axBody = BplImp(ante, post); Bpl.Expr ax = BplForall(f.tok, new List(), formals, null, tr, axBody); + Bpl.Expr reqAx = BplForall(f.tok, new List(), formals, null, trReq, Bpl.Expr.Imp(reqCall, ante)); var activate = AxiomActivation(f, etran); - string comment = "consequence axiom for " + f.FullSanitizedName; if (RemoveLit(axBody) != Bpl.Expr.True) { - var consequenceExpr = BplImp(activate, ax); - var consequenceAxiom = new Bpl.Axiom(f.tok, consequenceExpr, comment); - AddOtherDefinition(boogieFunction, consequenceAxiom); + var comment = "#requires ==> #canCall for " + f.FullSanitizedName; + AddOtherDefinition(boogieFunction, new Bpl.Axiom(f.tok, BplImp(AxiomActivation(f, etran, true), reqAx), comment)); + comment = "consequence axiom for " + f.FullSanitizedName; + AddOtherDefinition(boogieFunction, new Bpl.Axiom(f.tok, BplImp(activate, ax), comment)); } if (f.ResultType.MayInvolveReferences) { @@ -568,7 +580,7 @@ void AddFunctionConsequenceAxiom(Boogie.Function boogieFunction, Function f, Lis ax = BplForall(f.tok, new List(), formals, null, BplTrigger(whr), axBody); if (RemoveLit(axBody) != Bpl.Expr.True) { - comment = "alloc consequence axiom for " + f.FullSanitizedName; + var comment = "alloc consequence axiom for " + f.FullSanitizedName; var allocConsequenceAxiom = new Bpl.Axiom(f.tok, BplImp(activate, ax), comment); AddOtherDefinition(boogieFunction, allocConsequenceAxiom); } @@ -834,11 +846,10 @@ private Axiom GetFunctionAxiom(Function f, Expression body, List lits) { ante = BplAnd(useViaContext, BplAnd(ante, pre)); // useViaCanCall: f#canCall(args) - Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool); - Bpl.Expr useViaCanCall = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(canCallFuncID), Concat(tyargs, args)); + var canCallFuncID = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool); + var useViaCanCall = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(canCallFuncID), Concat(tyargs, args)); - // ante := useViaCanCall || (useViaContext && typeAnte && pre) - ante = BplOr(useViaCanCall, ante); + ante = useViaCanCall; Bpl.Expr funcAppl; { @@ -1252,13 +1263,26 @@ void AddFrameAxiom(Function f) { Bpl.Expr formal = new Bpl.IdentifierExpr(p.tok, bv); f0args.Add(formal); f1args.Add(formal); f0argsCanCall.Add(formal); f1argsCanCall.Add(formal); Bpl.Expr wh = GetWhereClause(p.tok, formal, p.Type, etran0, useAlloc); - if (wh != null) { fwf0 = BplAnd(fwf0, wh); } + if (wh != null) { + fwf0 = BplAnd(fwf0, wh); + } + wh = GetWhereClause(p.tok, formal, p.Type, etran1, useAlloc); + if (wh != null) { + fwf1 = BplAnd(fwf1, wh); + } } var canCall = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool)); - wellFormed = BplAnd(wellFormed, BplAnd( - BplOr(new Bpl.NAryExpr(f.tok, canCall, f0argsCanCall), fwf0), - BplOr(new Bpl.NAryExpr(f.tok, canCall, f1argsCanCall), fwf1))); - + var f0canCall = new Bpl.NAryExpr(f.tok, canCall, f0argsCanCall); + var f1canCall = new Bpl.NAryExpr(f.tok, canCall, f1argsCanCall); + wellFormed = BplAnd(wellFormed, Bpl.Expr.Or( + BplOr(f0canCall, fwf0), + BplOr(f1canCall, fwf1))); + /* + JA: I conjecture that we don't need fwf0 or fwf1 here. But, we + will need both can calls, + i.e., + wellFormed = BplAnd(wellFormed, BplOr(f0canCall, f1canCall)) + */ /* DR: I conjecture that this should be enough, as the requires is preserved when the frame is: @@ -1270,7 +1294,7 @@ void AddFrameAxiom(Function f) { var fn = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName, TrType(f.ResultType))); var F0 = new Bpl.NAryExpr(f.tok, fn, f0args); var F1 = new Bpl.NAryExpr(f.tok, fn, f1args); - var eq = Bpl.Expr.Eq(F0, F1); + var eq = BplAnd(Bpl.Expr.Eq(F0, F1), Bpl.Expr.Eq(f0canCall, f1canCall)); var tr = new Bpl.Trigger(f.tok, true, new List { h0IsHeapAnchor, heapSucc, F1 }); var ax = new Bpl.ForallExpr(f.tok, new List(), bvars, null, tr, diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs index 8d31c39e580..6d8d08aaa3d 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs @@ -89,6 +89,7 @@ Bpl.Procedure AddIteratorProc(IteratorDecl iter, MethodTranslationKind kind) { // USER-DEFINED SPECIFICATIONS var comment = "user-defined preconditions"; foreach (var p in iter.Requires) { + req.Add(Requires(p.E.tok, true, etran.CanCallAssumption(p.E), null, comment, AlwaysAssumeAttribute(p.E.tok))); var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); if (p.Label != null && kind == MethodTranslationKind.Implementation) { // don't include this precondition here, but record it for later use @@ -107,6 +108,8 @@ Bpl.Procedure AddIteratorProc(IteratorDecl iter, MethodTranslationKind kind) { } comment = "user-defined postconditions"; foreach (var p in iter.Ensures) { + var canCalls = etran.CanCallAssumption(p.E); + AddEnsures(ens, Ensures(p.E.tok, true, canCalls, CustomErrorMessage(p.Attributes), comment, AlwaysAssumeAttribute(p.E.tok))); foreach (var s in TrSplitExprForMethodSpec(p.E, etran, kind)) { if (kind == MethodTranslationKind.Implementation && RefinementToken.IsInherited(s.Tok, currentModule)) { // this postcondition was inherited into this module, so just ignore it diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 864ee698b14..fc6b1bc7dbd 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -637,18 +637,22 @@ private void AddMethodImpl(Method m, Boogie.Procedure proc, bool wellformednessP var decrCaller = new List(); var decrCalleeDafny = new List(); var decrCallerDafny = new List(); + Bpl.Expr canCalls = Bpl.Expr.True; foreach (var ee in m.Decreases.Expressions) { decrToks.Add(ee.tok); decrTypes.Add(ee.Type.NormalizeExpand()); decrCallerDafny.Add(ee); + canCalls = BplAnd(canCalls, exprTran.CanCallAssumption(ee)); decrCaller.Add(exprTran.TrExpr(ee)); Expression es = Substitute(ee, receiverSubst, substMap); es = Substitute(es, null, decrSubstMap); decrCalleeDafny.Add(es); + canCalls = BplAnd(canCalls, exprTran.CanCallAssumption(ee)); decrCallee.Add(exprTran.TrExpr(es)); } - return DecreasesCheck(decrToks, null, decrCalleeDafny, decrCallerDafny, decrCallee, decrCaller, - null, null, false, true); + return BplImp(canCalls, + DecreasesCheck(decrToks, null, decrCalleeDafny, decrCallerDafny, decrCallee, decrCaller, + null, null, false, true)); }; #if VERIFY_CORRECTNESS_OF_TRANSLATION_FORALL_STATEMENT_RANGE @@ -1060,8 +1064,10 @@ private void AddFunctionOverrideEnsChk(Function f, BoogieStmtListBuilder builder Bpl.Variable/*?*/ resultVariable) { Contract.Requires(f.Ins.Count <= implInParams.Count); + var cco = new CanCallOptions(f, true); //generating class post-conditions foreach (var en in f.Ens) { + builder.Add(TrAssumeCmd(f.tok, etran.CanCallAssumption(en.E, cco))); builder.Add(TrAssumeCmdWithDependencies(etran, f.tok, en.E, "overridden function ensures clause")); } @@ -1102,11 +1108,13 @@ private void AddFunctionOverrideEnsChk(Function f, BoogieStmtListBuilder builder .Select(e => e.E) .Aggregate((e0, e1) => new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.And, e0, e1)); //generating trait post-conditions with class variables + cco = new CanCallOptions(f.OverriddenFunction, f, true); FunctionCallSubstituter sub = null; foreach (var en in f.OverriddenFunction.Ens) { sub ??= new FunctionCallSubstituter(substMap, typeMap, (TraitDecl)f.OverriddenFunction.EnclosingClass, (TopLevelDeclWithMembers)f.EnclosingClass); var subEn = sub.Substitute(en.E); - foreach (var s in TrSplitExpr(sub.Substitute(en.E), etran, false, out _).Where(s => s.IsChecked)) { + foreach (var s in TrSplitExpr(subEn, etran, false, out _).Where(s => s.IsChecked)) { + builder.Add(TrAssumeCmd(f.tok, etran.CanCallAssumption(subEn, cco))); var constraint = allOverrideEns == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Imp, allOverrideEns, subEn); @@ -1193,19 +1201,23 @@ private void AddFunctionOverrideReqsChk(Function f, BoogieStmtListBuilder builde Contract.Requires(etran != null); Contract.Requires(substMap != null); //generating trait pre-conditions with class variables + var cco = new CanCallOptions(f.OverriddenFunction, f, true); FunctionCallSubstituter sub = null; var subReqs = new List(); foreach (var req in f.OverriddenFunction.Req) { sub ??= new FunctionCallSubstituter(substMap, typeMap, (TraitDecl)f.OverriddenFunction.EnclosingClass, (TopLevelDeclWithMembers)f.EnclosingClass); var subReq = sub.Substitute(req.E); + builder.Add(TrAssumeCmd(f.tok, etran.CanCallAssumption(subReq, cco))); builder.Add(TrAssumeCmdWithDependencies(etran, f.tok, subReq, "overridden function requires clause")); subReqs.Add(subReq); } var allTraitReqs = subReqs.Count == 0 ? null : subReqs .Aggregate((e0, e1) => new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.And, e0, e1)); //generating class pre-conditions + cco = new CanCallOptions(f, true); foreach (var req in f.Req) { foreach (var s in TrSplitExpr(req.E, etran, false, out _).Where(s => s.IsChecked)) { + builder.Add(TrAssumeCmd(f.tok, etran.CanCallAssumption(req.E, cco))); var constraint = allTraitReqs == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Imp, allTraitReqs, req.E); @@ -1367,11 +1379,10 @@ private Boogie.Axiom FunctionOverrideAxiom(Function f, Function overridingFuncti } // Build the triggers - // { f(Succ(s), args), f'(Succ(s), args') } + // { f'(Succ(s), args') } Boogie.Trigger tr = BplTriggerHeap(this, overridingFunction.tok, - funcAppl, - readsHeap ? etran.HeapExpr : null, - overridingFuncAppl); + overridingFuncAppl, + readsHeap ? etran.HeapExpr : null); // { f(Succ(s), args), $Is(this, T') } var exprs = new List() { funcAppl, isOfSubtype }; if (readsHeap) { @@ -1383,10 +1394,18 @@ private Boogie.Axiom FunctionOverrideAxiom(Function f, Function overridingFuncti var synonyms = Boogie.Expr.Eq( funcAppl, ModeledAsBoxType(f.ResultType) ? BoxIfNotNormallyBoxed(overridingFunction.tok, overridingFuncAppl, overridingFunction.ResultType) : overridingFuncAppl); + // add overridingFunction#canCall ==> f#canCall to the axiom + var callName = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool); + var callArgs = f.IsFuelAware() ? argsJF.TakeLast(argsJF.Count() - 1).ToList() : argsJF; + var canCallFunc = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(callName), callArgs); + callName = new Bpl.IdentifierExpr(overridingFunction.tok, overridingFunction.FullSanitizedName + "#canCall", Bpl.Type.Bool); + callArgs = overridingFunction.IsFuelAware() ? argsCF.TakeLast(argsCF.Count() - 1).ToList() : argsCF; + var canCallOverridingFunc = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(callName), callArgs); + var canCallImp = BplImp(canCallFunc, canCallOverridingFunc); // The axiom Boogie.Expr ax = BplForall(f.tok, new List(), forallFormals, null, tr, - BplImp(ante, synonyms)); + BplImp(ante, BplAnd(canCallImp, synonyms))); var activate = AxiomActivation(overridingFunction, etran); string comment = "override axiom for " + f.FullSanitizedName + " in class " + overridingFunction.EnclosingClass.FullSanitizedName; return new Boogie.Axiom(f.tok, BplImp(activate, ax), comment); @@ -1442,6 +1461,7 @@ private void AddMethodOverrideEnsChk(Method m, BoogieStmtListBuilder builder, Ex Contract.Requires(substMap != null); //generating class post-conditions foreach (var en in m.Ens) { + builder.Add(TrAssumeCmd(m.tok, etran.CanCallAssumption(en.E))); builder.Add(TrAssumeCmdWithDependencies(etran, m.tok, en.E, "overridden ensures clause")); } // conjunction of class post-conditions @@ -1454,6 +1474,7 @@ private void AddMethodOverrideEnsChk(Method m, BoogieStmtListBuilder builder, Ex sub ??= new FunctionCallSubstituter(substMap, typeMap, (TraitDecl)m.OverriddenMethod.EnclosingClass, (TopLevelDeclWithMembers)m.EnclosingClass); var subEn = sub.Substitute(en.E); foreach (var s in TrSplitExpr(subEn, etran, false, out _).Where(s => s.IsChecked)) { + builder.Add(TrAssumeCmd(m.OverriddenMethod.tok, etran.CanCallAssumption(subEn))); var constraint = allOverrideEns == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Imp, allOverrideEns, subEn); @@ -1475,6 +1496,7 @@ private void AddMethodOverrideReqsChk(Method m, BoogieStmtListBuilder builder, E foreach (var req in m.OverriddenMethod.Req) { sub ??= new FunctionCallSubstituter(substMap, typeMap, (TraitDecl)m.OverriddenMethod.EnclosingClass, (TopLevelDeclWithMembers)m.EnclosingClass); var subReq = sub.Substitute(req.E); + builder.Add(TrAssumeCmd(m.OverriddenMethod.tok, etran.CanCallAssumption(subReq))); builder.Add(TrAssumeCmdWithDependencies(etran, m.tok, subReq, "overridden requires clause")); subReqs.Add(subReq); } @@ -1483,6 +1505,7 @@ private void AddMethodOverrideReqsChk(Method m, BoogieStmtListBuilder builder, E //generating class pre-conditions foreach (var req in m.Req) { foreach (var s in TrSplitExpr(req.E, etran, false, out _).Where(s => s.IsChecked)) { + builder.Add(TrAssumeCmd(m.tok, etran.CanCallAssumption(req.E))); var constraint = allTraitReqs == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Imp, allTraitReqs, req.E); @@ -1738,6 +1761,7 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { var comment = "user-defined preconditions"; foreach (var p in m.Req) { var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); + req.Add(Requires(p.E.tok, true, etran.CanCallAssumption(p.E), null, comment, AlwaysAssumeAttribute(p.E.tok))); if (p.Label != null && kind == MethodTranslationKind.Implementation) { // don't include this precondition here, but record it for later use p.Label.E = (m is TwoStateLemma ? ordinaryEtran : etran.Old).TrExpr(p.E); @@ -1748,8 +1772,7 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { } else if (s.IsOnlyFree && !bodyKind) { // don't include in split -- it would be ignored, anyhow } else { - req.Add(RequiresWithDependencies(s.Tok, s.IsOnlyFree, p.E, s.E, errorMessage, successMessage, comment)); - comment = null; + req.Add(RequiresWithDependencies(s.Tok, s.IsOnlyFree, p.E, s.E, errorMessage, successMessage, null)); // the free here is not linked to the free on the original expression (this is free things generated in the splitting.) } } @@ -1758,7 +1781,7 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { comment = "user-defined postconditions"; foreach (var p in m.Ens) { var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); - AddEnsures(ens, Ensures(p.E.tok, true, p.E, etran.CanCallAssumption(p.E), errorMessage, successMessage, comment)); + AddEnsures(ens, Ensures(p.E.tok, true, p.E, etran.CanCallAssumption(p.E), errorMessage, successMessage, comment, AlwaysAssumeAttribute(p.E.tok))); comment = null; foreach (var s in TrSplitExprForMethodSpec(p.E, etran, kind)) { var post = s.E; diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs b/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs index 05b7dabd5e4..8684e172631 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.SplitExpr.cs @@ -320,6 +320,7 @@ bool TrSplitExpr(Expression expr, List/*!*/ splits, bool pos if (!position) { ihBody = Bpl.Expr.Not(ihBody); } + ihBody = BplAnd(etran.CanCallAssumption(bodyK), ihBody); ihBody = BplImp(less, ihBody); List bvars = new List(); Bpl.Expr typeAntecedent = etran.TrBoundVariables(kvars, bvars); // no need to use allocation antecedent here, because the well-founded less-than ordering assures kk are allocated diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs index 9d7ad9f8074..6aaa0e08ac1 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs @@ -642,7 +642,10 @@ private void GenerateAndCheckGuesses(IToken tok, List bvars, List freeOfAlloc = BoundedPool.HasBounds(bounds, BoundedPool.PoolVirtues.IndependentOfAlloc_or_ExplicitAlloc); var bvs = new List(); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index f89eacab520..2878488a3f8 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -1429,6 +1429,11 @@ public static Bpl.QKeyValue InlineAttribute(Bpl.IToken tok, Bpl.QKeyValue/*?*/ n return new QKeyValue(tok, "inline", new List(), next); } + public static Bpl.QKeyValue AlwaysAssumeAttribute(Bpl.IToken tok, Bpl.QKeyValue next = null) { + Contract.Requires(tok != null); + return new QKeyValue(tok, "always_assume", new List(), next); + } + class Specialization { public readonly List Formals; public readonly List ReplacementExprs; @@ -1543,13 +1548,16 @@ public Specialization(IVariable formal, MatchCase mc, Specialization prev, Boogi return (olderParameterCount, olderCondition); } - Bpl.Expr AxiomActivation(Function f, ExpressionTranslator etran) { + Bpl.Expr AxiomActivation(Function f, ExpressionTranslator etran, bool strict = false) { Contract.Requires(f != null); Contract.Requires(etran != null); Contract.Requires(VisibleInScope(f)); if (InVerificationScope(f)) { - return - Bpl.Expr.Le(Bpl.Expr.Literal(forModule.CallGraph.GetSCCRepresentativePredecessorCount(f)), etran.FunctionContextHeight()); + if (strict) { + return Bpl.Expr.Lt(Bpl.Expr.Literal(forModule.CallGraph.GetSCCRepresentativePredecessorCount(f)), etran.FunctionContextHeight()); + } else { + return Bpl.Expr.Le(Bpl.Expr.Literal(forModule.CallGraph.GetSCCRepresentativePredecessorCount(f)), etran.FunctionContextHeight()); + } } else { return Bpl.Expr.True; } @@ -2238,6 +2246,7 @@ Bpl.Expr InRWClause_Aux(IToken tok, Bpl.Expr o, Bpl.Expr boxO, Bpl.Expr f, List< if (substMap != null) { e = Substitute(e, receiverReplacement, substMap); } + var canCallE = etran.CanCallAssumption(e); Bpl.Expr disjunct; var eType = e.Type.NormalizeToAncestorType(); @@ -2278,7 +2287,7 @@ Bpl.Expr InRWClause_Aux(IToken tok, Bpl.Expr o, Bpl.Expr boxO, Bpl.Expr f, List< } disjunct = BplAnd(disjunct, q); } - disjunction = BplOr(disjunction, disjunct); + disjunction = BplOr(disjunction, BplAnd(canCallE, disjunct)); } return disjunction; } @@ -3435,13 +3444,13 @@ Bpl.Ensures EnsuresWithDependencies(IToken tok, bool free, Expression dafnyCondi return ens; } - Bpl.Ensures Ensures(IToken tok, bool free, Expression dafnyCondition, Bpl.Expr condition, string errorMessage, string successMessage, string comment) { + Bpl.Ensures Ensures(IToken tok, bool free, Expression dafnyCondition, Bpl.Expr condition, string errorMessage, string successMessage, string comment, QKeyValue kv = null) { Contract.Requires(tok != null); Contract.Requires(condition != null); Contract.Ensures(Contract.Result() != null); var unwrappedToken = ForceCheckToken.Unwrap(tok); - Bpl.Ensures ens = new Bpl.Ensures(unwrappedToken, free, condition, comment); + Bpl.Ensures ens = new Bpl.Ensures(unwrappedToken, free, condition, comment, kv); var description = new PODesc.EnsuresDescription(dafnyCondition, errorMessage, successMessage); ens.Description = description; if (!free) { @@ -3460,11 +3469,11 @@ Bpl.Requires RequiresWithDependencies(IToken tok, bool free, Expression dafnyCon return req; } - Bpl.Requires Requires(IToken tok, bool free, Expression dafnyCondition, Bpl.Expr bCondition, string errorMessage, string successMessage, string comment) { + Bpl.Requires Requires(IToken tok, bool free, Expression dafnyCondition, Bpl.Expr bCondition, string errorMessage, string successMessage, string comment, QKeyValue kv = null) { Contract.Requires(tok != null); Contract.Requires(bCondition != null); Contract.Ensures(Contract.Result() != null); - Bpl.Requires req = new Bpl.Requires(ForceCheckToken.Unwrap(tok), free, bCondition, comment); + Bpl.Requires req = new Bpl.Requires(ForceCheckToken.Unwrap(tok), free, bCondition, comment, kv); req.Description = new PODesc.RequiresDescription(dafnyCondition, errorMessage, successMessage); return req; } From 702c2224d5d3138b8586c066079f1c98dbeae745 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 11 Jun 2024 17:47:23 -0700 Subject: [PATCH 004/151] Fix merge --- .../BoogieGenerator.ExpressionTranslator.cs | 6 +- .../BoogieGenerator.ExpressionWellformed.cs | 2 +- .../Verifier/BoogieGenerator.TrStatement.cs | 461 ------------------ 3 files changed, 4 insertions(+), 465 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index 68140052424..d096b252cbd 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -2202,12 +2202,12 @@ public Expr CanCallAssumption(Expression expr, CanCallOptions cco = null) { if (ModeledAsBoxType(arg.Type)) { return inner; } else { - return FunctionCall(arg.tok, BuiltinFunction.Box, null, inner); + return BoogieGenerator.FunctionCall(arg.tok, BuiltinFunction.Box, null, inner); } }; var args = Concat( - Map(e.Function.Type.AsArrowType.TypeArgs, TypeToTy), + Map(e.Function.Type.AsArrowType.TypeArgs, BoogieGenerator.TypeToTy), Cons(HeapExpr, Cons(TrExpr(e.Function), e.Args.ConvertAll(arg => TrArg(arg))))); @@ -2228,7 +2228,7 @@ public Expr CanCallAssumption(Expression expr, CanCallOptions cco = null) { List args = FunctionInvocationArguments(e, null, null); Boogie.Expr canCallFuncAppl = new Boogie.NAryExpr(BoogieGenerator.GetToken(expr), new Boogie.FunctionCall(canCallFuncID), args); bool makeAllowance = cco != null && (e.Function == cco.SelfCallsAllowance); - var add = makeAllowance ? Boogie.Expr.Or(MakeAllowance(e, cco), canCallFuncAppl) : canCallFuncAppl; + var add = makeAllowance ? Boogie.Expr.Or(TrExpr(MakeAllowance(e, cco)), canCallFuncAppl) : canCallFuncAppl; r = BplAnd(r, add); } return r; diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index fd7ae3b1896..18984602f1f 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -877,7 +877,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Bpl.Expr re goto case FunctionCallExpr.CoCallResolution.No; // please the compiler } - if (e.Function == options.SelfCallsAllowance) { + if (e.Function == wfOptions.SelfCallsAllowance) { allowance = etran.MakeAllowance(e); } if (e.CoCallHint != null) { diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs index 7d6d9fa04c1..622703e1b50 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs @@ -1452,467 +1452,6 @@ private Bpl.Expr TrForall_NewValueAssumption(IToken tok, List boundVar return new Bpl.ForallExpr(tok, xBvars, tr, body); } -#if THINGS_THAT_WERE_IN_TYPERSNIPER_BRANCH - private void TrIfStmt(IfStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { - AddComment(builder, stmt, "if statement"); - Expression guard; - if (stmt.Guard == null) { - guard = null; - } else { - guard = stmt.IsBindingGuard ? AlphaRename((ExistsExpr)stmt.Guard, "eg$") : stmt.Guard; - TrStmt_CheckWellformed(guard, builder, locals, etran, true); - } - - BoogieStmtListBuilder b = new BoogieStmtListBuilder(this); - CurrentIdGenerator.Push(); - if (stmt.IsBindingGuard) { - var exists = (ExistsExpr)stmt.Guard; // the original (that is, not alpha-renamed) guard - IntroduceAndAssignExistentialVars(exists, b, builder, locals, etran, stmt.IsGhost); - } - - Boogie.StmtList thn = TrStmt2StmtList(b, stmt.Thn, locals, etran); - CurrentIdGenerator.Pop(); - Boogie.StmtList els; - Boogie.IfCmd elsIf = null; - b = new BoogieStmtListBuilder(this); - if (stmt.IsBindingGuard) { - b.Add(TrAssumeCmd(guard.tok, Boogie.Expr.Not(etran.TrExpr(guard)))); - } - - if (stmt.Els == null) { - els = b.Collect(stmt.Tok); - } else { - els = TrStmt2StmtList(b, stmt.Els, locals, etran); - if (els.BigBlocks.Count == 1) { - Boogie.BigBlock bb = els.BigBlocks[0]; - if (bb.LabelName == null && bb.simpleCmds.Count == 0 && bb.ec is Boogie.IfCmd) { - elsIf = (Boogie.IfCmd)bb.ec; - els = null; - } - } - } - - builder.Add(new Boogie.IfCmd(stmt.Tok, guard == null || stmt.IsBindingGuard ? null : etran.TrExpr(guard), thn, - elsIf, els)); - } - - private void TrWhileStmt(BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, WhileStmt whileStmt) { - AddComment(builder, whileStmt, "while statement"); - this.fuelContext = - FuelSetting.ExpandFuelContext(whileStmt.Attributes, whileStmt.Tok, this.fuelContext, this.reporter); - DefineFuelConstant(whileStmt.Tok, whileStmt.Attributes, builder, etran); - BodyTranslator bodyTr = null; - if (whileStmt.Body != null) { - bodyTr = delegate (BoogieStmtListBuilder bld, ExpressionTranslator e) { - CurrentIdGenerator.Push(); - TrStmt(whileStmt.Body, bld, locals, e); - CurrentIdGenerator.Pop(); - }; - } - - TrLoop(whileStmt, whileStmt.Guard, bodyTr, builder, locals, etran); - this.fuelContext = FuelSetting.PopFuelContext(); - } - - private void TrForLoop(ForLoopStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { - 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 Boogie.LocalVariable(indexVar.tok, - new Boogie.TypedIdent(indexVar.Tok, indexVarName, TrType(indexVar.Type))); - locals.Add(bIndexVar); - var bIndex = new Boogie.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; - Boogie.IdentifierExpr bLo = null; - Boogie.IdentifierExpr bHi = null; - if (lo != null) { - var name = indexVarName + "#lo"; - var bLoVar = new Boogie.LocalVariable(lo.tok, new Boogie.TypedIdent(lo.tok, name, Boogie.Type.Int)); - locals.Add(bLoVar); - bLo = new Boogie.IdentifierExpr(lo.tok, name); - CheckWellformed(lo, new WFOptions(null, false), locals, builder, etran); - builder.Add(Boogie.Cmd.SimpleAssign(lo.tok, bLo, etran.TrExpr(lo))); - dLo = new BoogieWrapper(bLo, lo.Type); - } - - if (hi != null) { - var name = indexVarName + "#hi"; - var bHiVar = new Boogie.LocalVariable(hi.tok, new Boogie.TypedIdent(hi.tok, name, Boogie.Type.Int)); - locals.Add(bHiVar); - bHi = new Boogie.IdentifierExpr(hi.tok, name); - CheckWellformed(hi, new WFOptions(null, false), locals, builder, etran); - builder.Add(Boogie.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, Boogie.Expr.Le(bLo, bHi), "lower bound must not exceed upper bound")); - } - - // 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 Boogie.LocalVariable(tok, new Boogie.TypedIdent(tok, name, Boogie.Type.Int)); - var x = new Boogie.IdentifierExpr(tok, name); - string msg; - var cre = GetSubrangeCheck(x, Type.Int, indexVar.Type, out msg); - if (cre != null) { - locals.Add(xVar); - builder.Add(new Boogie.HavocCmd(tok, new List() { x })); - builder.Add(new Boogie.AssumeCmd(tok, ForLoopBounds(x, bLo, bHi))); - builder.Add(Assert(tok, cre, "entire range must be assignable to index variable, but some " + msg)); - } - } - - // initialize the index variable - builder.Add(Boogie.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(Boogie.Cmd.SimpleAssign(stmt.Tok, bIndex, Boogie.Expr.Sub(bIndex, Boogie.Expr.Literal(1)))); - } - - TrStmt(stmt.Body, bld, locals, e); - if (stmt.GoingUp) { - bld.Add(Boogie.Cmd.SimpleAssign(stmt.Tok, bIndex, Boogie.Expr.Add(bIndex, Boogie.Expr.Literal(1)))); - } - - CurrentIdGenerator.Pop(); - }; - } - - TrLoop(stmt, guard, bodyTr, builder, locals, etran, freeInvariant, stmt.Decreases.Expressions.Count != 0); - } - - private void TrMatchStmt(MatchStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { - TrStmt_CheckWellformed(stmt.Source, builder, locals, etran, true); - Boogie.Expr source = etran.TrExpr(stmt.Source); - var b = new BoogieStmtListBuilder(this); - b.Add(TrAssumeCmd(stmt.Tok, Boogie.Expr.False)); - Boogie.StmtList els = b.Collect(stmt.Tok); - Boogie.IfCmd ifCmd = null; - foreach (var missingCtor in stmt.MissingCases) { - // havoc all bound variables - b = new BoogieStmtListBuilder(this); - List newLocals = new List(); - Boogie.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 Boogie.IdentifierExpr(local.tok, local)); - } - - builder.Add(new Boogie.HavocCmd(stmt.Tok, havocIds)); - } - - String missingStr = stmt.Context - .FillHole(new IdCtx(new KeyValuePair(missingCtor.Name, missingCtor))) - .AbstractAllHoles().ToString(); - b.Add(Assert(stmt.Tok, Boogie.Expr.False, "missing case in match statement: " + missingStr)); - - Boogie.Expr guard = Boogie.Expr.Eq(source, r); - ifCmd = new Boogie.IfCmd(stmt.Tok, guard, b.Collect(stmt.Tok), ifCmd, els); - els = null; - } - - for (int i = stmt.Cases.Count; 0 <= --i;) { - var mc = stmt.Cases[i]; - CurrentIdGenerator.Push(); - // havoc all bound variables - b = new BoogieStmtListBuilder(this); - List newLocals = new List(); - Boogie.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 Boogie.IdentifierExpr(local.tok, local)); - } - - builder.Add(new Boogie.HavocCmd(mc.tok, havocIds)); - } - - // translate the body into b - var prevDefiniteAssignmentTrackerCount = definiteAssignmentTrackers.Count; - TrStmtList(mc.Body, b, locals, etran); - RemoveDefiniteAssignmentTrackers(mc.Body, prevDefiniteAssignmentTrackerCount); - - Boogie.Expr guard = Boogie.Expr.Eq(source, r); - ifCmd = new Boogie.IfCmd(mc.tok, guard, b.Collect(mc.tok), ifCmd, els); - els = null; - CurrentIdGenerator.Pop(); - } - - Contract.Assert(ifCmd != null); // follows from the fact that stmt.Cases.Count + stmt.MissingCases.Count != 0. - builder.Add(ifCmd); - } - - private void TrCalcStmt(CalcStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { - /* 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) { - Boogie.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); - // assume wf[line]: - AddComment(b, stmt, "assume wf[lhs]"); - CurrentIdGenerator.Push(); - assertAsAssume = true; - TrStmt_CheckWellformed(CalcStmt.Lhs(stmt.Steps[i]), b, locals, etran, false); - assertAsAssume = false; - if (stmt.Steps[i] is BinaryExpr && (((BinaryExpr)stmt.Steps[i]).ResolvedOp == BinaryExpr.ResolvedOpcode.Imp)) { - // assume line: - AddComment(b, stmt, "assume lhs"); - b.Add(TrAssumeCmd(stmt.Tok, etran.TrExpr(CalcStmt.Lhs(stmt.Steps[i])))); - } - - // 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)) { - b.Add(AssertNS(index.tok, Boogie.Expr.Le(Boogie.Expr.Literal(0), etran.TrExpr(index)), - "prefix-equality limit must be at least 0")); - } - } - - TrStmt_CheckWellformed(CalcStmt.Rhs(stmt.Steps[i]), b, locals, etran, false); - bool splitHappened; - var ss = TrSplitExpr(stmt.Steps[i], etran, true, out splitHappened); - // assert step: - AddComment(b, stmt, - "assert line" + i.ToString() + " " + (stmt.StepOps[i] ?? stmt.Op).ToString() + " line" + (i + 1).ToString()); - if (!splitHappened) { - b.Add(AssertNS(stmt.Lines[i + 1].tok, etran.TrExpr(stmt.Steps[i]), - "the calculation step between the previous line and this line might not hold")); - } else { - foreach (var split in ss) { - if (split.IsChecked) { - b.Add(AssertNS(stmt.Lines[i + 1].tok, split.E, - "the calculation step between the previous line and this line might not hold")); - } - } - } - } - - b.Add(TrAssumeCmd(stmt.Tok, Boogie.Expr.False)); - ifCmd = new Boogie.IfCmd(stmt.Tok, null, b.Collect(stmt.Tok), ifCmd, null); - CurrentIdGenerator.Pop(); - } - - // check well formedness of the first line: - b = new BoogieStmtListBuilder(this); - 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, Boogie.Expr.False)); - ifCmd = new Boogie.IfCmd(stmt.Tok, null, b.Collect(stmt.Tok), ifCmd, null); - builder.Add(ifCmd); - // assume result: - if (stmt.Steps.Count > 1) { - builder.Add(TrAssumeCmd(stmt.Tok, etran.TrExpr(stmt.Result))); - } - } - - CurrentIdGenerator.Pop(); - this.fuelContext = FuelSetting.PopFuelContext(); - } - - private void TrPredicateStmt(PredicateStmt stmt, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran) { - var stmtBuilder = new BoogieStmtListBuilder(this); - string errorMessage = CustomErrorMessage(stmt.Attributes); - this.fuelContext = FuelSetting.ExpandFuelContext(stmt.Attributes, stmt.Tok, this.fuelContext, this.reporter); - var defineFuel = DefineFuelConstant(stmt.Tok, stmt.Attributes, stmtBuilder, etran); - var b = defineFuel ? stmtBuilder : builder; - if (stmt is AssertStmt || DafnyOptions.O.DisallowSoundnessCheating) { - stmtContext = StmtType.ASSERT; - AddComment(b, stmt, "assert statement"); - TrStmt_CheckWellformed(stmt.Expr, b, locals, etran, false); - IToken enclosingToken = null; - if (Attributes.Contains(stmt.Attributes, "_prependAssertToken")) { - enclosingToken = stmt.Tok; - } - - BoogieStmtListBuilder proofBuilder = null; - var assertStmt = stmt as AssertStmt; - if (assertStmt != null) { - if (assertStmt.Proof != null) { - proofBuilder = new BoogieStmtListBuilder(this); - AddComment(proofBuilder, stmt, "assert statement proof"); - TrStmt(((AssertStmt)stmt).Proof, proofBuilder, locals, etran); - } else if (assertStmt.Label != null) { - proofBuilder = new BoogieStmtListBuilder(this); - AddComment(proofBuilder, stmt, "assert statement proof"); - } - } - - var ss = TrSplitExpr(stmt.Expr, etran, true, out var splitHappened); - if (!splitHappened) { - var tok = enclosingToken == null ? stmt.Expr.tok : new NestedToken(enclosingToken, stmt.Expr.tok); - (proofBuilder ?? b).Add(Assert(tok, etran.TrExpr(stmt.Expr), errorMessage ?? "assertion violation", stmt.Tok, - etran.TrAttributes(stmt.Attributes, null))); - } else { - foreach (var split in ss) { - if (split.IsChecked) { - var tok = enclosingToken == null ? split.E.tok : new NestedToken(enclosingToken, split.E.tok); - (proofBuilder ?? b).Add(AssertNS(tok, split.E, errorMessage ?? "assertion violation", stmt.Tok, - etran.TrAttributes(stmt.Attributes, null))); // attributes go on every split - } - } - } - - if (proofBuilder != null) { - PathAsideBlock(stmt.Tok, proofBuilder, b); - } - - stmtContext = StmtType.NONE; // done with translating assert stmt - if (splitHappened || proofBuilder != null) { - if (assertStmt != null && assertStmt.Label != null) { - // make copies of the variables used in the assertion - var name = "$Heap_at_" + assertStmt.Label.AssignUniqueId(CurrentIdGenerator); - var heapAt = new Boogie.LocalVariable(stmt.Tok, new Boogie.TypedIdent(stmt.Tok, name, predef.HeapType)); - locals.Add(heapAt); - var h = new Boogie.IdentifierExpr(stmt.Tok, heapAt); - b.Add(Boogie.Cmd.SimpleAssign(stmt.Tok, h, etran.HeapExpr)); - var substMap = new Dictionary(); - foreach (var v in FreeVariablesUtil.ComputeFreeVariables(assertStmt.Expr)) { - if (v is LocalVariable) { - var vcopy = new LocalVariable(stmt.Tok, stmt.Tok, string.Format("##{0}#{1}", name, v.Name), v.Type, - v.IsGhost); - vcopy.type = vcopy.OptionalType; // resolve local here - IdentifierExpr ie = - new IdentifierExpr(vcopy.Tok, vcopy.AssignUniqueName(currentDeclaration.IdGenerator)); - ie.Var = vcopy; - ie.Type = ie.Var.Type; // resolve ie here - substMap.Add(v, ie); - locals.Add(new Boogie.LocalVariable(vcopy.Tok, - new Boogie.TypedIdent(vcopy.Tok, vcopy.AssignUniqueName(currentDeclaration.IdGenerator), - TrType(vcopy.Type)))); - b.Add(Boogie.Cmd.SimpleAssign(stmt.Tok, TrVar(stmt.Tok, vcopy), TrVar(stmt.Tok, v))); - } - } - - var exprToBeRevealed = Substitute(assertStmt.Expr, null, substMap); - var etr = new ExpressionTranslator(etran, h); - assertStmt.Label.E = etr.TrExpr(exprToBeRevealed); - } else if (!defineFuel) { - // Adding the assume stmt, resetting the stmtContext - stmtContext = StmtType.ASSUME; - adjustFuelForExists = true; - b.Add(TrAssumeCmd(stmt.Tok, etran.TrExpr(stmt.Expr))); - stmtContext = StmtType.NONE; - } - } - - if (defineFuel) { - var ifCmd = new Boogie.IfCmd(stmt.Tok, null, b.Collect(stmt.Tok), null, - null); // BUGBUG: shouldn't this first append "assume false" to "b"? (use PathAsideBlock to do this) --KRML - builder.Add(ifCmd); - // Adding the assume stmt, resetting the stmtContext - stmtContext = StmtType.ASSUME; - adjustFuelForExists = true; - builder.Add(TrAssumeCmd(stmt.Tok, etran.TrExpr(stmt.Expr))); - stmtContext = StmtType.NONE; - } - } else if (stmt is ExpectStmt) { - AddComment(builder, stmt, "expect statement"); - ExpectStmt s = (ExpectStmt)stmt; - stmtContext = StmtType.ASSUME; - TrStmt_CheckWellformed(s.Expr, builder, locals, etran, false); - - // Need to check the message is well-formed, assuming the expected expression - // does NOT hold: - // - // if Not(TrExpr[[ s.Expr ]]) { - // CheckWellformed[[ s.Message ]] - // assume false; - // } - BoogieStmtListBuilder thnBuilder = new BoogieStmtListBuilder(this); - TrStmt_CheckWellformed(s.Message, thnBuilder, locals, etran, false); - thnBuilder.Add(TrAssumeCmd(stmt.Tok, new Boogie.LiteralExpr(stmt.Tok, false), - etran.TrAttributes(stmt.Attributes, null))); - Boogie.StmtList thn = thnBuilder.Collect(s.Tok); - builder.Add(new Boogie.IfCmd(stmt.Tok, Boogie.Expr.Not(etran.TrExpr(s.Expr)), thn, null, null)); - - stmtContext = StmtType.NONE; // done with translating expect stmt. - } else if (stmt is AssumeStmt) { - AddComment(builder, stmt, "assume statement"); - AssumeStmt s = (AssumeStmt)stmt; - stmtContext = StmtType.ASSUME; - TrStmt_CheckWellformed(s.Expr, builder, locals, etran, false); - builder.Add(TrAssumeCmd(stmt.Tok, etran.TrExpr(s.Expr), etran.TrAttributes(stmt.Attributes, null))); - stmtContext = StmtType.NONE; // done with translating assume stmt. - } - - this.fuelContext = FuelSetting.PopFuelContext(); - } -#endif - void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr, BoogieStmtListBuilder builder, List locals, ExpressionTranslator etran, Bpl.Expr freeInvariant = null, bool includeTerminationCheck = true) { From 86738d65491e41f13d8bbf3f2860d1aae17c0885 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 11 Jun 2024 17:48:09 -0700 Subject: [PATCH 005/151] Add FreeRequires/FreeEnsures helper methods --- .../DafnyCore/Verifier/BoogieGenerator.Fields.cs | 2 +- .../Verifier/BoogieGenerator.Functions.cs | 6 +++--- .../Verifier/BoogieGenerator.Iterators.cs | 6 +++--- .../DafnyCore/Verifier/BoogieGenerator.Methods.cs | 14 +++++++------- Source/DafnyCore/Verifier/BoogieGenerator.Types.cs | 2 +- Source/DafnyCore/Verifier/BoogieGenerator.cs | 10 +++++++++- 6 files changed, 24 insertions(+), 16 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Fields.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Fields.cs index 5584794f6d5..eda0438d38a 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Fields.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Fields.cs @@ -187,7 +187,7 @@ void AddWellformednessCheck(ConstantField decl) { // the procedure itself var req = new List(); // free requires mh == ModuleContextHeight && fh == TypeContextHeight; - req.Add(Requires(decl.tok, true, null, etran.HeightContext(decl), null, null, null)); + req.Add(FreeRequires(decl.tok, etran.HeightContext(decl), null)); var heapVar = new Bpl.IdentifierExpr(decl.tok, "$Heap", false); var varlist = new List { heapVar }; var name = MethodName(decl, MethodTranslationKind.SpecWellformedness); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs index c9829498024..29199aaf670 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs @@ -71,13 +71,13 @@ void AddWellformednessCheck(Function f) { // the procedure itself var req = new List(); // free requires mh == ModuleContextHeight && fh == FunctionContextHeight; - req.Add(Requires(f.tok, true, null, etran.HeightContext(f), null, null, null)); + req.Add(FreeRequires(f.tok, etran.HeightContext(f), null)); if (f is TwoStateFunction) { // free requires prevHeap == Heap && HeapSucc(prevHeap, currHeap) && IsHeap(currHeap) var a0 = Bpl.Expr.Eq(prevHeap, ordinaryEtran.HeapExpr); var a1 = HeapSucc(prevHeap, currHeap); var a2 = FunctionCall(f.tok, BuiltinFunction.IsGoodHeap, null, currHeap); - req.Add(Requires(f.tok, true, null, BplAnd(a0, BplAnd(a1, a2)), null, null, null)); + req.Add(FreeRequires(f.tok, BplAnd(a0, BplAnd(a1, a2)), null)); } // modifies $Heap @@ -92,7 +92,7 @@ void AddWellformednessCheck(Function f) { bool splitHappened /*we actually don't care*/ = TrSplitExpr(p.E, splits, true, functionHeight, true, true, etran); var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); var canCalls = etran.CanCallAssumption(p.E, new CanCallOptions(f, true)); - AddEnsures(ens, Ensures(p.E.tok, true, canCalls, errorMessage, null, AlwaysAssumeAttribute(p.E.tok))); + AddEnsures(ens, FreeEnsures(p.E.tok, canCalls, null, AlwaysAssumeAttribute(p.E.tok))); foreach (var s in splits) { if (s.IsChecked && !RefinementToken.IsInherited(s.Tok, currentModule)) { AddEnsures(ens, EnsuresWithDependencies(s.Tok, false, p.E, s.E, errorMessage, successMessage, null)); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs index 6d8d08aaa3d..0ad0365fa89 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs @@ -81,7 +81,7 @@ Bpl.Procedure AddIteratorProc(IteratorDecl iter, MethodTranslationKind kind) { // FREE PRECONDITIONS if (kind == MethodTranslationKind.SpecWellformedness || kind == MethodTranslationKind.Implementation) { // the other cases have no need for a free precondition // free requires mh == ModuleContextHeight && fh = FunctionContextHeight; - req.Add(Requires(iter.tok, true, null, etran.HeightContext(iter), null, null, null)); + req.Add(FreeRequires(iter.tok, etran.HeightContext(iter), null)); } mod.Add(etran.HeapCastToIdentifierExpr); @@ -89,7 +89,7 @@ Bpl.Procedure AddIteratorProc(IteratorDecl iter, MethodTranslationKind kind) { // USER-DEFINED SPECIFICATIONS var comment = "user-defined preconditions"; foreach (var p in iter.Requires) { - req.Add(Requires(p.E.tok, true, etran.CanCallAssumption(p.E), null, comment, AlwaysAssumeAttribute(p.E.tok))); + req.Add(FreeRequires(p.E.tok, etran.CanCallAssumption(p.E), comment, AlwaysAssumeAttribute(p.E.tok))); var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); if (p.Label != null && kind == MethodTranslationKind.Implementation) { // don't include this precondition here, but record it for later use @@ -109,7 +109,7 @@ Bpl.Procedure AddIteratorProc(IteratorDecl iter, MethodTranslationKind kind) { comment = "user-defined postconditions"; foreach (var p in iter.Ensures) { var canCalls = etran.CanCallAssumption(p.E); - AddEnsures(ens, Ensures(p.E.tok, true, canCalls, CustomErrorMessage(p.Attributes), comment, AlwaysAssumeAttribute(p.E.tok))); + AddEnsures(ens, FreeEnsures(p.E.tok, canCalls, comment, AlwaysAssumeAttribute(p.E.tok))); foreach (var s in TrSplitExprForMethodSpec(p.E, etran, kind)) { if (kind == MethodTranslationKind.Implementation && RefinementToken.IsInherited(s.Tok, currentModule)) { // this postcondition was inherited into this module, so just ignore it diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index fc6b1bc7dbd..fab64fdb9db 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -968,13 +968,13 @@ private void AddFunctionOverrideCheckImpl(Function f) { // the procedure itself var req = new List(); // free requires fh == FunctionContextHeight; - req.Add(Requires(f.tok, true, null, etran.HeightContext(f), null, null, null)); + req.Add(FreeRequires(f.tok, etran.HeightContext(f), null)); if (f is TwoStateFunction) { // free requires prevHeap == Heap && HeapSucc(prevHeap, currHeap) && IsHeap(currHeap) var a0 = Boogie.Expr.Eq(prevHeap, ordinaryEtran.HeapExpr); var a1 = HeapSucc(prevHeap, currHeap); var a2 = FunctionCall(f.tok, BuiltinFunction.IsGoodHeap, null, currHeap); - req.Add(Requires(f.tok, true, null, BplAnd(a0, BplAnd(a1, a2)), null, null, null)); + req.Add(FreeRequires(f.tok, BplAnd(a0, BplAnd(a1, a2)), null)); } // modifies $Heap var mod = new List { @@ -1724,13 +1724,13 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { // FREE PRECONDITIONS if (kind == MethodTranslationKind.SpecWellformedness || kind == MethodTranslationKind.Implementation || kind == MethodTranslationKind.OverrideCheck) { // the other cases have no need for a free precondition // free requires mh == ModuleContextHeight && fh == FunctionContextHeight; - req.Add(Requires(m.tok, true, null, etran.HeightContext(m), null, null, null)); + req.Add(FreeRequires(m.tok, etran.HeightContext(m), null)); if (m is TwoStateLemma) { // free requires prevHeap == Heap && HeapSucc(prevHeap, currHeap) && IsHeap(currHeap) var a0 = Boogie.Expr.Eq(prevHeap, ordinaryEtran.HeapExpr); var a1 = HeapSucc(prevHeap, currHeap); var a2 = FunctionCall(m.tok, BuiltinFunction.IsGoodHeap, null, currHeap); - req.Add(Requires(m.tok, true, null, BplAnd(a0, BplAnd(a1, a2)), null, null, null)); + req.Add(FreeRequires(m.tok, BplAnd(a0, BplAnd(a1, a2)), null)); } } if (m is TwoStateLemma) { @@ -1761,7 +1761,7 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { var comment = "user-defined preconditions"; foreach (var p in m.Req) { var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); - req.Add(Requires(p.E.tok, true, etran.CanCallAssumption(p.E), null, comment, AlwaysAssumeAttribute(p.E.tok))); + req.Add(FreeRequires(p.E.tok, etran.CanCallAssumption(p.E), comment, AlwaysAssumeAttribute(p.E.tok))); if (p.Label != null && kind == MethodTranslationKind.Implementation) { // don't include this precondition here, but record it for later use p.Label.E = (m is TwoStateLemma ? ordinaryEtran : etran.Old).TrExpr(p.E); @@ -1781,7 +1781,7 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { comment = "user-defined postconditions"; foreach (var p in m.Ens) { var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); - AddEnsures(ens, Ensures(p.E.tok, true, p.E, etran.CanCallAssumption(p.E), errorMessage, successMessage, comment, AlwaysAssumeAttribute(p.E.tok))); + AddEnsures(ens, FreeEnsures(p.E.tok, etran.CanCallAssumption(p.E), comment, AlwaysAssumeAttribute(p.E.tok))); comment = null; foreach (var s in TrSplitExprForMethodSpec(p.E, etran, kind)) { var post = s.E; @@ -1816,7 +1816,7 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { 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)); + AddEnsures(ens, FreeEnsures(m.tok, GetRevealConstant(f), null)); } } } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs index 6aaa0e08ac1..db6e32957ed 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs @@ -1439,7 +1439,7 @@ void AddWellformednessCheck(RedirectingTypeDecl decl) { // the procedure itself var req = new List(); // free requires mh == ModuleContextHeight && fh == TypeContextHeight; - req.Add(Requires(decl.tok, true, null, etran.HeightContext(decl), null, null, null)); + req.Add(FreeRequires(decl.tok, etran.HeightContext(decl), null)); // modifies $Heap var mod = new List { etran.HeapCastToIdentifierExpr, diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index 2878488a3f8..ddebe197baf 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -2326,7 +2326,7 @@ void AddWellformednessCheck(DatatypeCtor ctor) { // 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)); + req.Add(FreeRequires(ctor.tok, etran.HeightContext(ctor.EnclosingDatatype), 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(), @@ -3444,6 +3444,10 @@ Bpl.Ensures EnsuresWithDependencies(IToken tok, bool free, Expression dafnyCondi return ens; } + Bpl.Ensures FreeEnsures(IToken tok, Bpl.Expr condition, string comment, QKeyValue kv = null) { + return Ensures(tok, true, null, condition, null, null, comment, kv); + } + Bpl.Ensures Ensures(IToken tok, bool free, Expression dafnyCondition, Bpl.Expr condition, string errorMessage, string successMessage, string comment, QKeyValue kv = null) { Contract.Requires(tok != null); Contract.Requires(condition != null); @@ -3469,6 +3473,10 @@ Bpl.Requires RequiresWithDependencies(IToken tok, bool free, Expression dafnyCon return req; } + Bpl.Requires FreeRequires(IToken tok, Bpl.Expr bCondition, string comment, QKeyValue kv = null) { + return Requires(tok, true, null, bCondition, null, null, comment, kv); + } + Bpl.Requires Requires(IToken tok, bool free, Expression dafnyCondition, Bpl.Expr bCondition, string errorMessage, string successMessage, string comment, QKeyValue kv = null) { Contract.Requires(tok != null); Contract.Requires(bCondition != null); From e712995152af42138c261f73c5874544de4f9112 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 12 Jun 2024 09:51:41 -0700 Subject: [PATCH 006/151] chore: Remove deprecated semi-colons --- .../LitTest/vstte2012/BreadthFirstSearch.dfy | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/vstte2012/BreadthFirstSearch.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/vstte2012/BreadthFirstSearch.dfy index 79489ae4c5e..b983fa44e16 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/vstte2012/BreadthFirstSearch.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/vstte2012/BreadthFirstSearch.dfy @@ -1,4 +1,4 @@ -// RUN: %testDafnyForEachResolver "%s" -- --allow-deprecation +// RUN: %testDafnyForEachResolver "%s" class BreadthFirstSearch @@ -32,18 +32,18 @@ class BreadthFirstSearch BFS(source: Vertex, dest: Vertex, ghost AllVertices: set) returns (d: int, ghost path: List) // source and dest are among AllVertices - requires source in AllVertices && dest in AllVertices; + requires source in AllVertices && dest in AllVertices // AllVertices is closed under Succ - requires IsClosed(AllVertices); + requires IsClosed(AllVertices) // This method has two basic outcomes, as indicated by the sign of "d". // More precisely, "d" is non-negative iff "source" can reach "dest". // The following postcondition says that under the "0 <= d" outcome, // "path" denotes a path of length "d" from "source" to "dest": - ensures 0 <= d ==> IsPath(source, dest, path) && length(path) == d; + ensures 0 <= d ==> IsPath(source, dest, path) && length(path) == d // Moreover, that path is as short as any path from "source" to "dest": - ensures 0 <= d ==> forall p :: IsPath(source, dest, p) ==> length(path) <= length(p); + ensures 0 <= d ==> forall p :: IsPath(source, dest, p) ==> length(path) <= length(p) // Finally, under the outcome "d < 0", there is no path from "source" to "dest": - ensures d < 0 ==> !exists p :: IsPath(source, dest, p); + ensures d < 0 ==> !exists p :: IsPath(source, dest, p) { path := Nil; // avoid indefinite assignment errors var V, C, N := {source}, {source}, {}; @@ -63,30 +63,30 @@ class BreadthFirstSearch assert {:split_here} true; while C != {} // V, Processed, C, N are all subsets of AllVertices: - invariant V <= AllVertices && Processed <= AllVertices && C <= AllVertices && N <= AllVertices; + invariant V <= AllVertices && Processed <= AllVertices && C <= AllVertices && N <= AllVertices // source is encountered: - invariant source in V; + invariant source in V // V is the disjoint union of Processed, C, and N: - invariant V == Processed + C + N; - invariant Processed !! C !! N; // Processed, C, and N are mutually disjoint + invariant V == Processed + C + N + invariant Processed !! C !! N // Processed, C, and N are mutually disjoint // "paths" records a path for every encountered vertex - invariant ValidMap(source, paths); - invariant V == paths.Keys; + invariant ValidMap(source, paths) + invariant V == paths.Keys // shortest paths for vertices in C have length d, and for vertices in N // have length d+1 - invariant forall x :: x in C ==> length(Find(source, x, paths)) == d; - invariant forall x :: x in N ==> length(Find(source, x, paths)) == d + 1; + invariant forall x :: x in C ==> length(Find(source, x, paths)) == d + invariant forall x :: x in N ==> length(Find(source, x, paths)) == d + 1 // "dest" is not reachable for any smaller value of "d": - invariant dest in R(source, d, AllVertices) ==> dest in C; - invariant d != 0 ==> dest !in R(source, d-1, AllVertices); + invariant dest in R(source, d, AllVertices) ==> dest in C + invariant d != 0 ==> dest !in R(source, d-1, AllVertices) // together, Processed and C are all the vertices reachable in at most d steps: - invariant Processed + C == R(source, d, AllVertices); + invariant Processed + C == R(source, d, AllVertices) // N are the successors of Processed that are not reachable within d steps: - invariant N == Successors(Processed, AllVertices) - R(source, d, AllVertices); + invariant N == Successors(Processed, AllVertices) - R(source, d, AllVertices) // if we have exhausted C, then we have also exhausted N: - invariant C == {} ==> N == {}; + invariant C == {} ==> N == {} // variant: - decreases AllVertices - Processed; + decreases AllVertices - Processed { // remove a vertex "v" from "C" var v :| v in C; @@ -97,7 +97,7 @@ class BreadthFirstSearch if v == dest { forall p | IsPath(source, dest, p) - ensures length(pathToV) <= length(p); + ensures length(pathToV) <= length(p) { reveal R(); Lemma_IsPath_R(source, dest, p, AllVertices); @@ -134,7 +134,7 @@ class BreadthFirstSearch // show that "dest" in not in any reachability set, no matter // how many successors one follows forall n: nat - ensures dest !in R(source, n, AllVertices); + ensures dest !in R(source, n, AllVertices) { reveal R(); if n < d { @@ -149,7 +149,7 @@ class BreadthFirstSearch forall p | IsPath(source, dest, p) // this and the previous two lines will establish the // absurdity of a "p" satisfying IsPath(source, dest, p) - ensures false; + ensures false { reveal R(); Lemma_IsPath_R(source, dest, p, AllVertices); @@ -165,8 +165,8 @@ class BreadthFirstSearch lemma Lemma_IsPath_Closure(source: Vertex, dest: Vertex, p: List, AllVertices: set) - requires IsPath(source, dest, p) && source in AllVertices && IsClosed(AllVertices); - ensures dest in AllVertices && forall v :: v in elements(p) ==> v in AllVertices; + requires IsPath(source, dest, p) && source in AllVertices && IsClosed(AllVertices) + ensures dest in AllVertices && forall v :: v in elements(p) ==> v in AllVertices { match p { case Nil => @@ -189,9 +189,9 @@ class BreadthFirstSearch } lemma RMonotonicity(source: Vertex, m: nat, n: nat, AllVertices: set) - requires m <= n; - ensures R(source, m, AllVertices) <= R(source, n, AllVertices); - decreases n - m; + requires m <= n + ensures R(source, m, AllVertices) <= R(source, n, AllVertices) + decreases n - m { reveal R(); if m < n { @@ -200,10 +200,10 @@ class BreadthFirstSearch } lemma {:isolate_assertions} IsReachFixpoint(source: Vertex, m: nat, n: nat, AllVertices: set) - requires R(source, m, AllVertices) == R(source, m+1, AllVertices); - requires m <= n; - ensures R(source, m, AllVertices) == R(source, n, AllVertices); - decreases n - m; + requires R(source, m, AllVertices) == R(source, m+1, AllVertices) + requires m <= n + ensures R(source, m, AllVertices) == R(source, n, AllVertices) + decreases n - m { reveal R(); if m < n { @@ -212,8 +212,8 @@ class BreadthFirstSearch } lemma Lemma_IsPath_R(source: Vertex, x: Vertex, p: List, AllVertices: set) - requires IsPath(source, x, p) && source in AllVertices && IsClosed(AllVertices); - ensures x in R(source, length(p), AllVertices); + requires IsPath(source, x, p) && source in AllVertices && IsClosed(AllVertices) + ensures x in R(source, length(p), AllVertices) { reveal R(); match p { @@ -232,8 +232,8 @@ class BreadthFirstSearch } ghost function Find(source: Vertex, x: Vertex, m: map>): List - requires ValidMap(source, m) && x in m; - ensures IsPath(source, x, Find(source, x, m)); + requires ValidMap(source, m) && x in m + ensures IsPath(source, x, Find(source, x, m)) { m[x] } @@ -241,13 +241,13 @@ class BreadthFirstSearch ghost method UpdatePaths(vSuccs: set, source: Vertex, paths: map>, v: Vertex, pathToV: List) returns (newPaths: map>) - requires ValidMap(source, paths); - requires vSuccs !! paths.Keys; - requires forall succ :: succ in vSuccs ==> IsPath(source, succ, Cons(v, pathToV)); - ensures ValidMap(source, newPaths) && newPaths.Keys == paths.Keys + vSuccs; + requires ValidMap(source, paths) + requires vSuccs !! paths.Keys + requires forall succ :: succ in vSuccs ==> IsPath(source, succ, Cons(v, pathToV)) + ensures ValidMap(source, newPaths) && newPaths.Keys == paths.Keys + vSuccs ensures forall x :: x in paths ==> - Find(source, x, paths) == Find(source, x, newPaths); - ensures forall x :: x in vSuccs ==> Find(source, x, newPaths) == Cons(v, pathToV); + Find(source, x, paths) == Find(source, x, newPaths) + ensures forall x :: x in vSuccs ==> Find(source, x, newPaths) == Cons(v, pathToV) { if vSuccs == {} { newPaths := paths; From bf0ec2cffcf6d227bba2a0f0c997fc3f888bcc70 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 12 Jun 2024 13:50:35 -0700 Subject: [PATCH 007/151] Restore FunctionSpecification.dfy --- .../LitTest/dafny0/FunctionSpecifications.dfy | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/FunctionSpecifications.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/FunctionSpecifications.dfy index 045b02e142f..d8da69c7837 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/FunctionSpecifications.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/FunctionSpecifications.dfy @@ -36,10 +36,10 @@ ghost function SumBad(a: List): int } ghost function FibWithExtraPost(n: int): int - ensures 2 <= n ==> 0 <= FibWithExtraPost(n-1); - ensures 1 <= n ==> 0 <= FibWithExtraPost(n-1); - - ensures 0 <= FibWithExtraPost(n); + ensures 2 <= n ==> 0 <= FibWithExtraPost(n-1) // This is fine, because the definition of the function is discovered via canCall + ensures 1 <= n ==> 0 <= FibWithExtraPost(n-1) // Error: In the current implementation of Dafny, one needs to actually call the + // function in order to benefit from canCall. This may be improved in the future. + ensures 0 <= FibWithExtraPost(n) { if n < 0 then 0 else if n < 2 then n else @@ -91,13 +91,13 @@ ghost method M() { ghost function IncB(i: nat): nat { - var n :| n>i; n + var n :| n > i; n } ghost function IncC(i: nat): int ensures IncC(i)>=0 { - var n :| n>i; n + var n :| n > i; n } From 73d0b5c052179270927340fe98f1a0fbfc472e14 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 12 Jun 2024 13:53:06 -0700 Subject: [PATCH 008/151] Move generation of req==>CanCall axiom --- .../Verifier/BoogieGenerator.Functions.cs | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs index 29199aaf670..081eaa84c20 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs @@ -521,19 +521,12 @@ void AddFunctionConsequenceAxiom(Boogie.Function boogieFunction, Function f, Lis // useViaCanCall: f#canCall(args) var canCallFuncID = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool); var useViaCanCall = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(canCallFuncID), Concat(tyargs, args)); - var reqCallID = new Bpl.IdentifierExpr(f.tok, RequiresName(f), Bpl.Type.Bool); - var fuelargs = new List(); - if (f.IsFuelAware()) { - fuelargs.Add(new Bpl.IdentifierExpr(f.tok, layer)); - } - var reqCall = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(reqCallID), Concat(tyargs, Concat(fuelargs, args))); // ante := useViaCanCall ante = useViaCanCall; anteIsAlloc = useViaCanCall; var tr = BplTriggerHeap(this, f.tok, funcAppl, (f.ReadsHeap || !readsHeap) ? null : etran.HeapExpr); - var trReq = BplTriggerHeap(this, f.tok, reqCall, (f.ReadsHeap || !readsHeap) ? null : etran.HeapExpr); Bpl.Expr post = Bpl.Expr.True; // substitute function return value with the function call. var substMap = new Dictionary(); @@ -556,13 +549,10 @@ void AddFunctionConsequenceAxiom(Boogie.Function boogieFunction, Function f, Lis Bpl.Expr axBody = BplImp(ante, post); Bpl.Expr ax = BplForall(f.tok, new List(), formals, null, tr, axBody); - Bpl.Expr reqAx = BplForall(f.tok, new List(), formals, null, trReq, Bpl.Expr.Imp(reqCall, ante)); var activate = AxiomActivation(f, etran); if (RemoveLit(axBody) != Bpl.Expr.True) { - var comment = "#requires ==> #canCall for " + f.FullSanitizedName; - AddOtherDefinition(boogieFunction, new Bpl.Axiom(f.tok, BplImp(AxiomActivation(f, etran, true), reqAx), comment)); - comment = "consequence axiom for " + f.FullSanitizedName; - AddOtherDefinition(boogieFunction, new Bpl.Axiom(f.tok, BplImp(activate, ax), comment)); + AddOtherDefinition(boogieFunction, new Bpl.Axiom(f.tok, BplImp(activate, ax), + "consequence axiom for " + f.FullSanitizedName)); } if (f.ResultType.MayInvolveReferences) { @@ -817,6 +807,9 @@ private Axiom GetFunctionAxiom(Function f, Expression body, List lits) { preReqAxiom = BplAnd(preRA, pre); } + var canCallFuncID = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool); + var useViaCanCall = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(canCallFuncID), Concat(tyargs, args)); + // Add the precondition function and its axiom (which is equivalent to the anteReqAxiom) if (body == null || (RevealedInScope(f) && lits == null)) { var precondF = new Bpl.Function(f.tok, @@ -831,6 +824,10 @@ private Axiom GetFunctionAxiom(Function f, Expression body, List lits) { AddOtherDefinition(precondF, new Axiom(f.tok, BplForall(forallFormals, trig, BplImp(anteReqAxiom, Bpl.Expr.Eq(appl, preReqAxiom))), "#requires axiom for " + f.FullSanitizedName)); + + AddOtherDefinition(precondF, new Bpl.Axiom(f.tok, + BplForall(f.tok, new List(), forallFormals, null, trig, Bpl.Expr.Imp(appl, useViaCanCall)), + "#requires ==> #canCall for " + f.FullSanitizedName)); } if (body == null || !RevealedInScope(f)) { @@ -846,9 +843,6 @@ private Axiom GetFunctionAxiom(Function f, Expression body, List lits) { ante = BplAnd(useViaContext, BplAnd(ante, pre)); // useViaCanCall: f#canCall(args) - var canCallFuncID = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool); - var useViaCanCall = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(canCallFuncID), Concat(tyargs, args)); - ante = useViaCanCall; Bpl.Expr funcAppl; From 13aa0d99e15821fd143be8eecdecee3e1a081670 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 12 Jun 2024 16:07:42 -0700 Subject: [PATCH 009/151] Replace kv parameter by alwaysAssume option in Free-Requires/Ensures --- Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs | 2 +- Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs | 4 ++-- Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs | 2 +- Source/DafnyCore/Verifier/BoogieGenerator.cs | 6 ++++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs index 081eaa84c20..6d7c0c84c48 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs @@ -92,7 +92,7 @@ void AddWellformednessCheck(Function f) { bool splitHappened /*we actually don't care*/ = TrSplitExpr(p.E, splits, true, functionHeight, true, true, etran); var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); var canCalls = etran.CanCallAssumption(p.E, new CanCallOptions(f, true)); - AddEnsures(ens, FreeEnsures(p.E.tok, canCalls, null, AlwaysAssumeAttribute(p.E.tok))); + AddEnsures(ens, FreeEnsures(p.E.tok, canCalls, null, true)); foreach (var s in splits) { if (s.IsChecked && !RefinementToken.IsInherited(s.Tok, currentModule)) { AddEnsures(ens, EnsuresWithDependencies(s.Tok, false, p.E, s.E, errorMessage, successMessage, null)); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs index 0ad0365fa89..73f8b06b119 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs @@ -89,7 +89,7 @@ Bpl.Procedure AddIteratorProc(IteratorDecl iter, MethodTranslationKind kind) { // USER-DEFINED SPECIFICATIONS var comment = "user-defined preconditions"; foreach (var p in iter.Requires) { - req.Add(FreeRequires(p.E.tok, etran.CanCallAssumption(p.E), comment, AlwaysAssumeAttribute(p.E.tok))); + req.Add(FreeRequires(p.E.tok, etran.CanCallAssumption(p.E), comment, true)); var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); if (p.Label != null && kind == MethodTranslationKind.Implementation) { // don't include this precondition here, but record it for later use @@ -109,7 +109,7 @@ Bpl.Procedure AddIteratorProc(IteratorDecl iter, MethodTranslationKind kind) { comment = "user-defined postconditions"; foreach (var p in iter.Ensures) { var canCalls = etran.CanCallAssumption(p.E); - AddEnsures(ens, FreeEnsures(p.E.tok, canCalls, comment, AlwaysAssumeAttribute(p.E.tok))); + AddEnsures(ens, FreeEnsures(p.E.tok, canCalls, comment, true)); foreach (var s in TrSplitExprForMethodSpec(p.E, etran, kind)) { if (kind == MethodTranslationKind.Implementation && RefinementToken.IsInherited(s.Tok, currentModule)) { // this postcondition was inherited into this module, so just ignore it diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index fab64fdb9db..95c4675b97d 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -1781,7 +1781,7 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { comment = "user-defined postconditions"; foreach (var p in m.Ens) { var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); - AddEnsures(ens, FreeEnsures(p.E.tok, etran.CanCallAssumption(p.E), comment, AlwaysAssumeAttribute(p.E.tok))); + AddEnsures(ens, FreeEnsures(p.E.tok, etran.CanCallAssumption(p.E), comment, true)); comment = null; foreach (var s in TrSplitExprForMethodSpec(p.E, etran, kind)) { var post = s.E; diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index ddebe197baf..202218d7728 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -3444,7 +3444,8 @@ Bpl.Ensures EnsuresWithDependencies(IToken tok, bool free, Expression dafnyCondi return ens; } - Bpl.Ensures FreeEnsures(IToken tok, Bpl.Expr condition, string comment, QKeyValue kv = null) { + Bpl.Ensures FreeEnsures(IToken tok, Bpl.Expr condition, string comment, bool alwaysAssume = false) { + var kv = alwaysAssume ? AlwaysAssumeAttribute(tok) : null; return Ensures(tok, true, null, condition, null, null, comment, kv); } @@ -3473,7 +3474,8 @@ Bpl.Requires RequiresWithDependencies(IToken tok, bool free, Expression dafnyCon return req; } - Bpl.Requires FreeRequires(IToken tok, Bpl.Expr bCondition, string comment, QKeyValue kv = null) { + Bpl.Requires FreeRequires(IToken tok, Bpl.Expr bCondition, string comment, bool alwaysAssume = false) { + var kv = alwaysAssume ? AlwaysAssumeAttribute(tok) : null; return Requires(tok, true, null, bCondition, null, null, comment, kv); } From e7e6c61939332637355a274fca8594612ee25d5b Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 12 Jun 2024 16:08:49 -0700 Subject: [PATCH 010/151] =?UTF-8?q?Don=E2=80=99t=20add=20CanCall=20=5Finsi?= =?UTF-8?q?de=5F=20frame=20condition,=20but=20as=20free=20precondition?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs | 10 +++++++++- Source/DafnyCore/Verifier/BoogieGenerator.cs | 3 +-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 95c4675b97d..912b74bc962 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -1761,7 +1761,8 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { var comment = "user-defined preconditions"; foreach (var p in m.Req) { var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); - req.Add(FreeRequires(p.E.tok, etran.CanCallAssumption(p.E), comment, AlwaysAssumeAttribute(p.E.tok))); + req.Add(FreeRequires(p.E.tok, etran.CanCallAssumption(p.E), comment, true)); + comment = null; if (p.Label != null && kind == MethodTranslationKind.Implementation) { // don't include this precondition here, but record it for later use p.Label.E = (m is TwoStateLemma ? ordinaryEtran : etran.Old).TrExpr(p.E); @@ -1778,6 +1779,13 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { } } } + // assume can-call conditions for the modifies clause + comment = "user-defined frame expressions"; + foreach (var frameExpression in m.Mod.Expressions) { + req.Add(FreeRequires(frameExpression.tok, etran.CanCallAssumption(frameExpression.E), comment, true)); + comment = null; + } + comment = "user-defined postconditions"; foreach (var p in m.Ens) { var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index 202218d7728..350a2a9f010 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -2246,7 +2246,6 @@ Bpl.Expr InRWClause_Aux(IToken tok, Bpl.Expr o, Bpl.Expr boxO, Bpl.Expr f, List< if (substMap != null) { e = Substitute(e, receiverReplacement, substMap); } - var canCallE = etran.CanCallAssumption(e); Bpl.Expr disjunct; var eType = e.Type.NormalizeToAncestorType(); @@ -2287,7 +2286,7 @@ Bpl.Expr InRWClause_Aux(IToken tok, Bpl.Expr o, Bpl.Expr boxO, Bpl.Expr f, List< } disjunct = BplAnd(disjunct, q); } - disjunction = BplOr(disjunction, BplAnd(canCallE, disjunct)); + disjunction = BplOr(disjunction, disjunct); } return disjunction; } From e1e695bd3b5a1b255be7ab2b9a7664b346693bbb Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 12 Jun 2024 16:43:11 -0700 Subject: [PATCH 011/151] Assume CanCall of callee frames --- .../BoogieGenerator.ExpressionWellformed.cs | 9 ++--- Source/DafnyCore/Verifier/BoogieGenerator.cs | 14 ++++++-- .../LitTests/LitTest/dafny0/CanCall.dfy | 36 +++++++++++++++++++ .../LitTest/dafny0/CanCall.dfy.expect | 2 ++ 4 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy.expect diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 18984602f1f..2df036f2f07 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -634,7 +634,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Bpl.Expr re var desc = new PODesc.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); + etran, etran.ReadsFrame(applyExpr.tok), wfOptions.AssertSink(this, builder), (ta, qa) => builder.Add(new Bpl.AssumeCmd(ta, qa)), desc, wfOptions.AssertKv); } break; @@ -798,7 +798,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Bpl.Expr re } var desc = new PODesc.ReadFrameSubset("invoke function", requiredFrames, readFrames); CheckFrameSubset(expr.tok, new List { reads }, null, null, - etran, etran.ReadsFrame(expr.tok), wfOptions.AssertSink(this, builder), desc, wfOptions.AssertKv); + etran, etran.ReadsFrame(expr.tok), wfOptions.AssertSink(this, builder), (ta, qa) => builder.Add(new Bpl.AssumeCmd(ta, qa)), desc, wfOptions.AssertKv); } } else { @@ -840,7 +840,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Bpl.Expr re var readsSubst = new Substituter(null, new Dictionary(), e.GetTypeArgumentSubstitutions()); CheckFrameSubset(callExpr.tok, e.Function.Reads.Expressions.ConvertAll(readsSubst.SubstFrameExpr), - e.Receiver, substMap, etran, etran.ReadsFrame(callExpr.tok), wfOptions.AssertSink(this, builder), desc, wfOptions.AssertKv); + e.Receiver, substMap, etran, etran.ReadsFrame(callExpr.tok), wfOptions.AssertSink(this, builder), (ta, qa) => builder.Add(new Bpl.AssumeCmd(ta, qa)), desc, wfOptions.AssertKv); } } Expression allowance = null; @@ -959,6 +959,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Bpl.Expr re CheckFrameSubset(fe.E.tok, new List() { fe }, null, new Dictionary(), etran, etran.ReadsFrame(fe.E.tok), wfOptions.AssertSink(this, builder), + (ta, qa) => builder.Add(new Bpl.AssumeCmd(ta, qa)), desc, wfOptions.AssertKv); } } @@ -1660,7 +1661,7 @@ private void CheckElementInit(IToken tok, bool forArray, List dims, ); var readsDesc = new PODesc.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); + etran, etran.ReadsFrame(tok), maker, (ta, qa) => builder.Add(new Bpl.AssumeCmd(ta, qa)), readsDesc, options.AssertKv); } // Check that the values coming out of the function satisfy any appropriate subset-type constraints var apply = UnboxUnlessInherentlyBoxed(FunctionCall(tok, Apply(dims.Count), TrType(elementType), args), elementType); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index 350a2a9f010..64cc2179338 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -2086,13 +2086,16 @@ 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, q)), + (t, e) => builder.Add(TrAssumeCmd(t, e)), + desc, kv); } void CheckFrameSubset(IToken tok, List calleeFrame, - Expression receiverReplacement, Dictionary substMap, - ExpressionTranslator/*!*/ etran, Boogie.IdentifierExpr /*!*/ enclosingFrame, + Expression receiverReplacement, Dictionary substMap, + ExpressionTranslator etran, Boogie.IdentifierExpr enclosingFrame, Action MakeAssert, + Action MakeAssume, PODesc.ProofObligationDescription desc, Bpl.QKeyValue kv) { Contract.Requires(tok != null); @@ -2102,6 +2105,11 @@ void CheckFrameSubset(IToken tok, List calleeFrame, Contract.Requires(MakeAssert != null); Contract.Requires(predef != null); + foreach (var frameExpression in calleeFrame) { + var e = Substitute(frameExpression.E, receiverReplacement, substMap); + MakeAssume(frameExpression.tok, etran.CanCallAssumption(e)); + } + // emit: assert (forall o: ref, f: Field :: o != null && $Heap[o,alloc] && (o,f) in subFrame ==> enclosingFrame[o,f]); var oVar = new Bpl.BoundVariable(tok, new Bpl.TypedIdent(tok, "$o", predef.RefType)); var o = new Bpl.IdentifierExpr(tok, oVar); diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy new file mode 100644 index 00000000000..5dd750450e0 --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy @@ -0,0 +1,36 @@ +// RUN: %testDafnyForEachResolver "%s" + + +module ModifiesCallee { + ghost function Wrap(S: set): set { + S + } + + method DoWrapped(S: set) + modifies Wrap(S) + { + DoOne(S); + } + + method DoOne(S: set) + modifies S + { + } +} + +module ModifiesCaller { + ghost function Wrap(S: set): set { + S + } + + method DoWrapped(S: set) + modifies Wrap(S) + { + } + + method DoOne(S: set) + modifies S + { + DoWrapped(S); + } +} diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy.expect new file mode 100644 index 00000000000..ebe2328e072 --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy.expect @@ -0,0 +1,2 @@ + +Dafny program verifier finished with 2 verified, 0 errors From 500ff5c77e8ad45364ac11c883554d09a6bc8d2f Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 12 Jun 2024 17:25:53 -0700 Subject: [PATCH 012/151] chore: Remove deprecated semi-colons --- .../LitTest/dafny4/ACL2-extractor.dfy | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/ACL2-extractor.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/ACL2-extractor.dfy index 0d3eca21e51..f7da86e8718 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/ACL2-extractor.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/ACL2-extractor.dfy @@ -1,4 +1,4 @@ -// RUN: %verify --allow-deprecation --solver-option="O:smt.qi.eager_threshold=30" "%s" > "%t" +// RUN: %verify --solver-option="O:smt.qi.eager_threshold=30" "%s" > "%t" // RUN: %diff "%s.expect" "%t" // This is the Extractor Problem from section 11.8 of the ACL2 book, @@ -25,7 +25,7 @@ ghost opaque function nth(n: int, xs: List): T } ghost function nthWorker(n: int, xs: List): T - requires 0 <= n < length(xs); + requires 0 <= n < length(xs) { if n == 0 then xs.head else nthWorker(n-1, xs.tail) } @@ -57,7 +57,7 @@ ghost function xtr(mp: List, lst: List): List } lemma ExtractorTheorem(xs: List) - ensures xtr(nats(length(xs)), xs) == rev(xs); + ensures xtr(nats(length(xs)), xs) == rev(xs) { var a, b := xtr(nats(length(xs)), xs), rev(xs); calc { @@ -73,7 +73,7 @@ lemma ExtractorTheorem(xs: List) length(b); } forall i | 0 <= i < length(xs) - ensures nth(i, a) == nth(i, b); + ensures nth(i, a) == nth(i, b) { reveal nth(); ExtractorLemma(i, xs); @@ -86,22 +86,22 @@ lemma ExtractorTheorem(xs: List) // lemmas about length lemma XtrLength(mp: List, lst: List) - ensures length(xtr(mp, lst)) == length(mp); + ensures length(xtr(mp, lst)) == length(mp) { } lemma NatsLength(n: nat) - ensures length(nats(n)) == n; + ensures length(nats(n)) == n { } lemma AppendLength(xs: List, ys: List) - ensures length(append(xs, ys)) == length(xs) + length(ys); + ensures length(append(xs, ys)) == length(xs) + length(ys) { } lemma RevLength(xs: List) - ensures length(rev(xs)) == length(xs); + ensures length(rev(xs)) == length(xs) { match xs { case Nil => @@ -142,8 +142,8 @@ lemma EqualElementsMakeEqualLists(xs: List, ys: List) // here is the theorem, but applied to the ith element lemma {:isolate_assertions} ExtractorLemma(i: int, xs: List) - requires 0 <= i < length(xs); - ensures nth(i, xtr(nats(length(xs)), xs)) == nth(i, rev(xs)); + requires 0 <= i < length(xs) + ensures nth(i, xtr(nats(length(xs)), xs)) == nth(i, rev(xs)) { calc { nth(i, xtr(nats(length(xs)), xs)); @@ -160,8 +160,8 @@ lemma {:isolate_assertions} ExtractorLemma(i: int, xs: List) // lemmas about what nth gives on certain lists lemma NthXtr(i: int, mp: List, lst: List) - requires 0 <= i < length(mp); - ensures nth(i, xtr(mp, lst)) == nth(nth(i, mp), lst); + requires 0 <= i < length(mp) + ensures nth(i, xtr(mp, lst)) == nth(nth(i, mp), lst) { reveal nth(); XtrLength(mp, lst); @@ -179,8 +179,8 @@ lemma NthXtr(i: int, mp: List, lst: List) } lemma NthNats(i: int, n: nat) - requires 0 <= i < n; - ensures nth(i, nats(n)) == n - 1 - i; + requires 0 <= i < n + ensures nth(i, nats(n)) == n - 1 - i { reveal nth(); NatsLength(n); @@ -188,14 +188,14 @@ lemma NthNats(i: int, n: nat) } lemma NthNatsWorker(i: int, n: nat) - requires 0 <= i < n && length(nats(n)) == n; - ensures nthWorker(i, nats(n)) == n - 1 - i; + requires 0 <= i < n && length(nats(n)) == n + ensures nthWorker(i, nats(n)) == n - 1 - i { } lemma NthRev(i: int, xs: List) - requires 0 <= i < length(xs) == length(rev(xs)); - ensures nthWorker(i, rev(xs)) == nthWorker(length(xs) - 1 - i, xs); + requires 0 <= i < length(xs) == length(rev(xs)) + ensures nthWorker(i, rev(xs)) == nthWorker(length(xs) - 1 - i, xs) { reveal nth(); assert xs.Cons?; @@ -236,8 +236,8 @@ lemma NthRev(i: int, xs: List) } lemma NthAppendA(i: int, xs: List, ys: List) - requires 0 <= i < length(xs); - ensures nth(i, append(xs, ys)) == nth(i, xs); + requires 0 <= i < length(xs) + ensures nth(i, append(xs, ys)) == nth(i, xs) { reveal nth(); if i == 0 { @@ -258,8 +258,8 @@ lemma NthAppendA(i: int, xs: List, ys: List) } lemma NthAppendB(i: int, xs: List, ys: List) - requires length(xs) <= i < length(xs) + length(ys); - ensures nth(i, append(xs, ys)) == nth(i - length(xs), ys); + requires length(xs) <= i < length(xs) + length(ys) + ensures nth(i, append(xs, ys)) == nth(i - length(xs), ys) { reveal nth(); AppendLength(xs, ys); From 79d3291837b3ad910c4732f35e88b131408c541d Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 14 Jun 2024 16:23:55 -0700 Subject: [PATCH 013/151] Generate CanCall assumptions for result of calc --- Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs index 622703e1b50..ff558ba8b52 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs @@ -709,6 +709,7 @@ private void TrCalcStmt(CalcStmt stmt, BoogieStmtListBuilder builder, List op line; assume false; } + assume CanCallAssumptions for line<0> and line; assume line<0> op line; */ Contract.Assert(stmt.Steps.Count == stmt.Hints.Count); // established by the resolver @@ -779,6 +780,7 @@ private void TrCalcStmt(CalcStmt stmt, BoogieStmtListBuilder builder, List 1) { + builder.Add(TrAssumeCmd(stmt.Tok, etran.CanCallAssumption(stmt.Result))); builder.Add(TrAssumeCmdWithDependencies(etran, stmt.Tok, stmt.Result, "calc statement result")); } } From ecef1e7bdcc60dee3ed240d9552ebae5bcc3832e Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Sun, 16 Jun 2024 17:10:14 -0700 Subject: [PATCH 014/151] Generate CanCall assumptions for array/seq init expression --- .../BoogieGenerator.ExpressionTranslator.cs | 41 ++++++++++++++++++- .../BoogieGenerator.ExpressionWellformed.cs | 14 +++++-- .../LitTests/LitTest/dafny0/CanCall.dfy | 27 ++++++++++++ .../LitTest/dafny0/CanCall.dfy.expect | 2 +- 4 files changed, 78 insertions(+), 6 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index d096b252cbd..4a265ce6806 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -3,9 +3,9 @@ using System.Diagnostics.Contracts; using System.Linq; using System.Numerics; -using Dafny; using Microsoft.BaseTypes; using Microsoft.Boogie; +using Bpl = Microsoft.Boogie; using static Microsoft.Dafny.Util; namespace Microsoft.Dafny { @@ -2237,7 +2237,44 @@ public Expr CanCallAssumption(Expression expr, CanCallOptions cco = null) { return CanCallAssumption(dtv.Arguments, cco); } else if (expr is SeqConstructionExpr) { var e = (SeqConstructionExpr)expr; - return BplAnd(CanCallAssumption(e.N, cco), CanCallAssumption(e.Initializer, cco)); + // CanCallAssumption[[ seq(n, init) ]] = + // CanCallAssumption[[ n ]] && + // CanCallAssumption[[ init ]] && + // var initF := init; // necessary, in order to use init(i) in trigger, since it may contain quantifiers + // (forall i: int + // { initF(i) } + // 0 <= i < n ==> + // CanCallAssumption[[ init(i) ]]) + + var varNameGen = BoogieGenerator.CurrentIdGenerator.NestedFreshIdGenerator("seqinit$"); + var indexVar = new Bpl.BoundVariable(e.tok, new Bpl.TypedIdent(e.tok, varNameGen.FreshId("#i"), Bpl.Type.Int)); + var index = new Bpl.IdentifierExpr(e.tok, indexVar); + var indexRange = BplAnd(Bpl.Expr.Le(Bpl.Expr.Literal(0), index), Bpl.Expr.Lt(index, TrExpr(e.N))); + var initFVar = new Bpl.BoundVariable(e.tok, new Bpl.TypedIdent(e.tok, varNameGen.FreshId("#f"), predef.HandleType)); + + var initF = new Bpl.IdentifierExpr(e.tok, initFVar); + + var dafnyInitApplication = new ApplyExpr(e.tok, e.Initializer, + new List() { new BoogieWrapper(index, Type.Int) }, + e.tok) { + Type = e.Initializer.Type.AsArrowType.Result + }; + var canCall = CanCallAssumption(dafnyInitApplication); + + dafnyInitApplication = new ApplyExpr(e.tok, new BoogieWrapper(initF, e.Initializer.Type), + new List() { new BoogieWrapper(index, Type.Int) }, + e.tok) { + Type = e.Initializer.Type.AsArrowType.Result + }; + var apply = TrExpr(dafnyInitApplication); + + var tr = new Bpl.Trigger(e.tok, true, new List { apply }); + var ccaInit = new Bpl.ForallExpr(e.tok, new List() { indexVar }, tr, BplImp(indexRange, canCall)); + var rhsAppliedToIndex = new Bpl.LetExpr(e.tok, new List() { initFVar }, + new List() { TrExpr(e.Initializer) }, null, ccaInit); + + return BplAnd(BplAnd(CanCallAssumption(e.N, cco), CanCallAssumption(e.Initializer, cco)), rhsAppliedToIndex); + } else if (expr is MultiSetFormingExpr) { MultiSetFormingExpr e = (MultiSetFormingExpr)expr; return CanCallAssumption(e.E, cco); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 2df036f2f07..c898a0e6cab 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -1678,12 +1678,20 @@ private void CheckElementInit(IToken tok, bool forArray, List dims, if (forArray) { // Assume that array elements have initial values according to the given initialization function. That is: // assume (forall i0,i1,i2,... :: { nw[i0,i1,i2,...] } - // 0 <= i0 < ... && ... ==> nw[i0,i1,i2,...] == init.requires(i0,i1,i2,...)); + // 0 <= i0 < ... && ... ==> + // CanCallAssumptions[[ init(i0,i1,i2,...) ]] && + // nw[i0,i1,i2,...] == init.requires(i0,i1,i2,...)); + var dafnyInitApplication = new ApplyExpr(tok, init, + bvs.ConvertAll(indexBv => (Expression)new BoogieWrapper(new Bpl.IdentifierExpr(indexBv.tok, indexBv), Type.Int)).ToList(), + tok) { + Type = sourceType.Result + }; + var canCall = etran.CanCallAssumption(dafnyInitApplication); + var ai = ReadHeap(tok, etran.HeapExpr, nw, GetArrayIndexFieldName(tok, indices)); var ai_prime = UnboxUnlessBoxType(tok, ai, elementType); var tr = new Bpl.Trigger(tok, true, new List { ai }); - q = new Bpl.ForallExpr(tok, bvs, tr, - BplImp(ante, Bpl.Expr.Eq(ai_prime, apply))); // TODO: use a more general Equality translation + q = new Bpl.ForallExpr(tok, bvs, tr, BplImp(ante, BplAnd(canCall, Bpl.Expr.Eq(ai_prime, apply)))); builder.Add(new Bpl.AssumeCmd(tok, q)); } } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy index 5dd750450e0..205181ecf4e 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy @@ -34,3 +34,30 @@ module ModifiesCaller { DoWrapped(S); } } + +module ArraySeqInit { + function F(a: int): int { + 45 + } + + method TestArrayF() { + var m := new int[3](F); + assert m[0] == 45; + } + + method TestArrayLambda() { + var m := new int[3](_ => 45); + assert m[0] == 45; + } + + method TestSeqF() { + var m := seq(3, F); + assert m[0] == 45; + } + + method TestSeqLambda() { + var m := seq(3, _ => 45); + assert m[0] == 45; + } + +} diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy.expect index ebe2328e072..83193971bf0 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy.expect @@ -1,2 +1,2 @@ -Dafny program verifier finished with 2 verified, 0 errors +Dafny program verifier finished with 6 verified, 0 errors From 8f5822f81ece5a5f0aa7f70c0658f204d4f1f377 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 17 Jun 2024 11:52:36 -0700 Subject: [PATCH 015/151] Carve pre/post-conditions up into separate conjuncts dafny4/Fstar-QuickSort.dfy --- .../DafnyCore/AST/Expressions/Expression.cs | 12 +++++++ .../BoogieGenerator.ExpressionWellformed.cs | 2 +- .../Verifier/BoogieGenerator.Functions.cs | 10 +++--- .../Verifier/BoogieGenerator.Iterators.cs | 6 ++-- .../Verifier/BoogieGenerator.Methods.cs | 24 +++++++------- .../Verifier/BoogieGenerator.TrStatement.cs | 6 ++-- Source/DafnyCore/Verifier/BoogieGenerator.cs | 11 +++++++ .../LitTests/LitTest/dafny0/CanCall.dfy | 33 +++++++++++++++++++ .../LitTest/dafny0/CanCall.dfy.expect | 2 +- 9 files changed, 81 insertions(+), 25 deletions(-) diff --git a/Source/DafnyCore/AST/Expressions/Expression.cs b/Source/DafnyCore/AST/Expressions/Expression.cs index ab1825a99db..0daa3076f2a 100644 --- a/Source/DafnyCore/AST/Expressions/Expression.cs +++ b/Source/DafnyCore/AST/Expressions/Expression.cs @@ -139,6 +139,18 @@ public virtual bool IsImplicit { get { return false; } } + public static IEnumerable ConjunctsWithLetsOnOutside(Expression expr) { + foreach (var conjunct in Conjuncts(expr)) { + if (conjunct is LetExpr { Exact: true } letExpr) { + foreach (var letBodyConjunct in ConjunctsWithLetsOnOutside(letExpr.Body)) { + yield return new LetExpr(letExpr.tok, letExpr.LHSs, letExpr.RHSs, letBodyConjunct, letExpr.Exact, letExpr.Attributes); + } + } else { + yield return conjunct; + } + } + } + public static IEnumerable Conjuncts(Expression expr) { Contract.Requires(expr != null); Contract.Requires(expr.Type.IsBoolType); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index c898a0e6cab..764f9e513d7 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -806,7 +806,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Bpl.Expr re var argSubstMap = e.Function.Ins.Zip(e.Args).ToDictionary(fa => fa.First as IVariable, fa => fa.Second); var directSub = new Substituter(e.Receiver, argSubstMap, e.GetTypeArgumentSubstitutions()); - foreach (AttributedExpression p in e.Function.Req) { + foreach (AttributedExpression p in ConjunctsOf(e.Function.Req)) { var directPrecond = directSub.Substitute(p.E); Expression precond = Substitute(p.E, e.Receiver, substMap, e.GetTypeArgumentSubstitutions()); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs index 6d7c0c84c48..507c4d7607d 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs @@ -86,7 +86,7 @@ void AddWellformednessCheck(Function f) { }; // check that postconditions hold var ens = new List(); - foreach (AttributedExpression p in f.Ens) { + foreach (AttributedExpression p in ConjunctsOf(f.Ens)) { var functionHeight = currentModule.CallGraph.GetSCCRepresentativePredecessorCount(f); var splits = new List(); bool splitHappened /*we actually don't care*/ = TrSplitExpr(p.E, splits, true, functionHeight, true, true, etran); @@ -153,7 +153,7 @@ void AddWellformednessCheck(Function f) { // assume each one of them. After all that (in particular, after assuming all // of them), do the postponed reads checks. delayer.DoWithDelayedReadsChecks(false, wfo => { - foreach (AttributedExpression p in f.Req) { + foreach (AttributedExpression p in ConjunctsOf(f.Req)) { if (p.Label != null) { p.Label.E = (f is TwoStateFunction ? ordinaryEtran : etran.Old).TrExpr(p.E); CheckWellformed(p.E, wfo, locals, builder, etran); @@ -225,7 +225,7 @@ void AddWellformednessCheck(Function f) { } } // Now for the ensures clauses - foreach (AttributedExpression p in f.Ens) { + foreach (AttributedExpression p in ConjunctsOf(f.Ens)) { // assume the postcondition for the benefit of checking the remaining postconditions CheckWellformedAndAssume(p.E, new WFOptions(f, false), locals, postCheckBuilder, etran, "ensures clause"); } @@ -511,7 +511,7 @@ void AddFunctionConsequenceAxiom(Boogie.Function boogieFunction, Function f, Lis } Bpl.Expr pre = Bpl.Expr.True; - foreach (AttributedExpression req in f.Req) { + foreach (AttributedExpression req in ConjunctsOf(f.Req)) { pre = BplAnd(pre, etran.TrExpr(req.E)); } // useViaContext: fh < FunctionContextHeight @@ -786,7 +786,7 @@ private Axiom GetFunctionAxiom(Function f, Expression body, List lits) { } Bpl.Expr pre = Bpl.Expr.True; - foreach (AttributedExpression req in f.Req) { + foreach (AttributedExpression req in ConjunctsOf(f.Req)) { pre = BplAnd(pre, etran.TrExpr(Substitute(req.E, receiverReplacement, substMap))); } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs index 73f8b06b119..24879749d04 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Iterators.cs @@ -176,7 +176,7 @@ void AddIteratorWellformednessCheck(IteratorDecl iter, Procedure proc) { } // Next, we assume about this.* whatever we said that the iterator constructor promises - foreach (var p in iter.Member_Init.Ens) { + foreach (var p in ConjunctsOf(iter.Member_Init.Ens)) { builder.Add(TrAssumeCmdWithDependencies(etran, p.E.tok, p.E, "iterator ensures clause")); } @@ -291,7 +291,7 @@ void AddIteratorImpl(IteratorDecl iter, Bpl.Procedure proc) { // add locals for the yield-history variables and the extra variables // Assume the precondition and postconditions of the iterator constructor method - foreach (var p in iter.Member_Init.Req) { + foreach (var p in ConjunctsOf(iter.Member_Init.Req)) { if (p.Label != null) { // don't include this precondition here Contract.Assert(p.Label.E != null); // it should already have been recorded @@ -299,7 +299,7 @@ void AddIteratorImpl(IteratorDecl iter, Bpl.Procedure proc) { builder.Add(TrAssumeCmdWithDependencies(etran, p.E.tok, p.E, "iterator constructor requires clause")); } } - foreach (var p in iter.Member_Init.Ens) { + foreach (var p in ConjunctsOf(iter.Member_Init.Ens)) { // these postconditions are two-state predicates, but that's okay, because we haven't changed anything yet builder.Add(TrAssumeCmdWithDependencies(etran, p.E.tok, p.E, "iterator constructor ensures clause")); } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 912b74bc962..44e6cdabba1 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -707,7 +707,7 @@ private void AddMethodImpl(Method m, Boogie.Procedure proc, bool wellformednessP // check well-formedness of the preconditions, and then assume each one of them readsCheckDelayer.DoWithDelayedReadsChecks(false, wfo => { - foreach (AttributedExpression p in m.Req) { + foreach (AttributedExpression p in ConjunctsOf(m.Req)) { CheckWellformedAndAssume(p.E, wfo, localVariables, builder, etran, "method requires clause"); } }); @@ -774,7 +774,7 @@ private void AddMethodImpl(Method m, Boogie.Procedure proc, bool wellformednessP } // check wellformedness of postconditions - foreach (AttributedExpression p in m.Ens) { + foreach (AttributedExpression p in ConjunctsOf(m.Ens)) { CheckWellformedAndAssume(p.E, new WFOptions(), localVariables, builder, etran, "method ensures clause"); } @@ -1066,7 +1066,7 @@ private void AddFunctionOverrideEnsChk(Function f, BoogieStmtListBuilder builder var cco = new CanCallOptions(f, true); //generating class post-conditions - foreach (var en in f.Ens) { + foreach (var en in ConjunctsOf(f.Ens)) { builder.Add(TrAssumeCmd(f.tok, etran.CanCallAssumption(en.E, cco))); builder.Add(TrAssumeCmdWithDependencies(etran, f.tok, en.E, "overridden function ensures clause")); } @@ -1110,7 +1110,7 @@ private void AddFunctionOverrideEnsChk(Function f, BoogieStmtListBuilder builder //generating trait post-conditions with class variables cco = new CanCallOptions(f.OverriddenFunction, f, true); FunctionCallSubstituter sub = null; - foreach (var en in f.OverriddenFunction.Ens) { + foreach (var en in ConjunctsOf(f.OverriddenFunction.Ens)) { sub ??= new FunctionCallSubstituter(substMap, typeMap, (TraitDecl)f.OverriddenFunction.EnclosingClass, (TopLevelDeclWithMembers)f.EnclosingClass); var subEn = sub.Substitute(en.E); foreach (var s in TrSplitExpr(subEn, etran, false, out _).Where(s => s.IsChecked)) { @@ -1204,7 +1204,7 @@ private void AddFunctionOverrideReqsChk(Function f, BoogieStmtListBuilder builde var cco = new CanCallOptions(f.OverriddenFunction, f, true); FunctionCallSubstituter sub = null; var subReqs = new List(); - foreach (var req in f.OverriddenFunction.Req) { + foreach (var req in ConjunctsOf(f.OverriddenFunction.Req)) { sub ??= new FunctionCallSubstituter(substMap, typeMap, (TraitDecl)f.OverriddenFunction.EnclosingClass, (TopLevelDeclWithMembers)f.EnclosingClass); var subReq = sub.Substitute(req.E); builder.Add(TrAssumeCmd(f.tok, etran.CanCallAssumption(subReq, cco))); @@ -1215,7 +1215,7 @@ private void AddFunctionOverrideReqsChk(Function f, BoogieStmtListBuilder builde .Aggregate((e0, e1) => new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.And, e0, e1)); //generating class pre-conditions cco = new CanCallOptions(f, true); - foreach (var req in f.Req) { + foreach (var req in ConjunctsOf(f.Req)) { foreach (var s in TrSplitExpr(req.E, etran, false, out _).Where(s => s.IsChecked)) { builder.Add(TrAssumeCmd(f.tok, etran.CanCallAssumption(req.E, cco))); var constraint = allTraitReqs == null @@ -1460,7 +1460,7 @@ private void AddMethodOverrideEnsChk(Method m, BoogieStmtListBuilder builder, Ex Contract.Requires(etran != null); Contract.Requires(substMap != null); //generating class post-conditions - foreach (var en in m.Ens) { + foreach (var en in ConjunctsOf(m.Ens)) { builder.Add(TrAssumeCmd(m.tok, etran.CanCallAssumption(en.E))); builder.Add(TrAssumeCmdWithDependencies(etran, m.tok, en.E, "overridden ensures clause")); } @@ -1470,7 +1470,7 @@ private void AddMethodOverrideEnsChk(Method m, BoogieStmtListBuilder builder, Ex .Aggregate((e0, e1) => new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.And, e0, e1)); //generating trait post-conditions with class variables FunctionCallSubstituter sub = null; - foreach (var en in m.OverriddenMethod.Ens) { + foreach (var en in ConjunctsOf(m.OverriddenMethod.Ens)) { sub ??= new FunctionCallSubstituter(substMap, typeMap, (TraitDecl)m.OverriddenMethod.EnclosingClass, (TopLevelDeclWithMembers)m.EnclosingClass); var subEn = sub.Substitute(en.E); foreach (var s in TrSplitExpr(subEn, etran, false, out _).Where(s => s.IsChecked)) { @@ -1493,7 +1493,7 @@ private void AddMethodOverrideReqsChk(Method m, BoogieStmtListBuilder builder, E //generating trait pre-conditions with class variables FunctionCallSubstituter sub = null; var subReqs = new List(); - foreach (var req in m.OverriddenMethod.Req) { + foreach (var req in ConjunctsOf(m.OverriddenMethod.Req)) { sub ??= new FunctionCallSubstituter(substMap, typeMap, (TraitDecl)m.OverriddenMethod.EnclosingClass, (TopLevelDeclWithMembers)m.EnclosingClass); var subReq = sub.Substitute(req.E); builder.Add(TrAssumeCmd(m.OverriddenMethod.tok, etran.CanCallAssumption(subReq))); @@ -1503,7 +1503,7 @@ private void AddMethodOverrideReqsChk(Method m, BoogieStmtListBuilder builder, E var allTraitReqs = subReqs.Count == 0 ? null : subReqs .Aggregate((e0, e1) => new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.And, e0, e1)); //generating class pre-conditions - foreach (var req in m.Req) { + foreach (var req in ConjunctsOf(m.Req)) { foreach (var s in TrSplitExpr(req.E, etran, false, out _).Where(s => s.IsChecked)) { builder.Add(TrAssumeCmd(m.tok, etran.CanCallAssumption(req.E))); var constraint = allTraitReqs == null @@ -1759,7 +1759,7 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { if (kind != MethodTranslationKind.SpecWellformedness && kind != MethodTranslationKind.OverrideCheck) { // USER-DEFINED SPECIFICATIONS var comment = "user-defined preconditions"; - foreach (var p in m.Req) { + foreach (var p in ConjunctsOf(m.Req)) { var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); req.Add(FreeRequires(p.E.tok, etran.CanCallAssumption(p.E), comment, true)); comment = null; @@ -1787,7 +1787,7 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { } comment = "user-defined postconditions"; - foreach (var p in m.Ens) { + foreach (var p in ConjunctsOf(m.Ens)) { var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); AddEnsures(ens, FreeEnsures(p.E.tok, etran.CanCallAssumption(p.E), comment, true)); comment = null; diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs index ff558ba8b52..b12143fba85 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs @@ -1145,7 +1145,7 @@ void TrForallProof(ForallStmt s, BoogieStmtListBuilder definedness, BoogieStmtLi definedness.Add(TrAssumeCmdWithDependencies(etran, s.Range.tok, s.Range, "forall statement range")); var ensuresDefinedness = new BoogieStmtListBuilder(this, options); - foreach (var ens in s.Ens) { + foreach (var ens in ConjunctsOf(s.Ens)) { TrStmt_CheckWellformed(ens.E, ensuresDefinedness, locals, etran, false); ensuresDefinedness.Add(TrAssumeCmdWithDependencies(etran, ens.E.tok, ens.E, "forall statement ensures clause")); } @@ -1155,7 +1155,7 @@ void TrForallProof(ForallStmt s, BoogieStmtListBuilder definedness, BoogieStmtLi TrStmt(s.Body, definedness, locals, etran); // check that postconditions hold - foreach (var ens in s.Ens) { + foreach (var ens in ConjunctsOf(s.Ens)) { definedness.Add(TrAssumeCmd(ens.E.tok, etran.CanCallAssumption(ens.E))); foreach (var split in TrSplitExpr(ens.E, etran, true, out var splitHappened)) { @@ -2292,7 +2292,7 @@ void TrForallStmtCall(IToken tok, List boundVars, List bo 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) { + foreach (var ens in ConjunctsOf(s0.Method.Ens)) { p = Substitute(ens.E, receiver, argsSubstMap, s0.MethodSelect.TypeArgumentSubstitutionsWithParents()); // substitute the call's actuals for the method's formals post = BplAnd(post, callEtran.CanCallAssumption(p)); post = BplAnd(post, callEtran.TrExpr(p)); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index 64cc2179338..ea8c59cc3a5 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -4582,6 +4582,17 @@ internal IsAllocType Var(bool ghostStmt, NonglobalVariable var) { } } + /// + /// Return the conjuncts of "attributedExpressions". + /// + public static IEnumerable ConjunctsOf(List attributedExpressions) { + foreach (var attrExpr in attributedExpressions) { + foreach (var conjunct in Expression.ConjunctsWithLetsOnOutside(attrExpr.E)) { + yield return new AttributedExpression(conjunct, attrExpr.Attributes); + } + } + } + List/*!*/ TrSplitExpr(Expression expr, ExpressionTranslator etran, bool apply_induction, out bool splitHappened) { Contract.Requires(expr != null); Contract.Requires(etran != null); diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy index 205181ecf4e..41f3de002d5 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy @@ -61,3 +61,36 @@ module ArraySeqInit { } } + +module QuantifierBeforeOtherConjunct { + datatype List = Nil | Cons(head: T, tail: List) + + function Length(list: List): nat { + match list + case Nil => 0 + case Cons(_, tl) => 1 + Length(tl) + } + + function Occurrences(x: int, list: List): nat + { + match list + case Nil => 0 + case Cons(y, tl) => (if x == y then 1 else 0) + Occurrences(x, tl) + } + + function Partition(x: int, l: List): (result: (List, List)) + ensures + && (forall y :: Occurrences(y, result.0) == if y <= x then Occurrences(y, l) else 0) + && Length(l) == Length(result.0) + Length(result.1) + + lemma CallPartition(x: int, l: List) returns (aaa: List, bbb: List) + requires l.Cons? && l.head <= x + ensures + && (forall y :: Occurrences(y, aaa) == if y <= x then Occurrences(y, l) else 0) + && Length(l) == Length(aaa) + Length(bbb) + { + var (lo, hi) := Partition(x, l.tail); + aaa := Cons(l.head, lo); + bbb := hi; + } +} diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy.expect index 83193971bf0..ccc01c35f48 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CanCall.dfy.expect @@ -1,2 +1,2 @@ -Dafny program verifier finished with 6 verified, 0 errors +Dafny program verifier finished with 11 verified, 0 errors From 5e697d259f89560b09b1e16b3894e6cbaf7931c8 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 20 Jun 2024 11:27:40 -0700 Subject: [PATCH 016/151] Fix null dereference --- Source/DafnyCore/Verifier/BoogieGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index ea8c59cc3a5..1603b8d4c2a 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -2106,7 +2106,7 @@ void CheckFrameSubset(IToken tok, List calleeFrame, Contract.Requires(predef != null); foreach (var frameExpression in calleeFrame) { - var e = Substitute(frameExpression.E, receiverReplacement, substMap); + var e = substMap != null ? Substitute(frameExpression.E, receiverReplacement, substMap) : frameExpression.E; MakeAssume(frameExpression.tok, etran.CanCallAssumption(e)); } From 8cc0ba6438ff6afd618b00e1fcba5407964016f3 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 20 Jun 2024 11:28:01 -0700 Subject: [PATCH 017/151] chore: Remove stale comment --- Source/DafnyCore/Verifier/BoogieGenerator.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index 1603b8d4c2a..d00f17ca7a7 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -3320,7 +3320,6 @@ public Bpl.Expr ApplyBox(Bpl.IToken tok, Bpl.Expr e) { /// /// If the expression is boxed, but the type is not boxed, this unboxes it. /// For lambda functions. - /// KRML: The name of this method is really confusing. It seems it should be named something like UnboxUnlessInherentlyBoxed. /// public Bpl.Expr UnboxUnlessInherentlyBoxed(Bpl.Expr e, Type t) { if (!ModeledAsBoxType(t)) { From 8f0b3213be540c8e40bba3e40bc019bac35dcb9c Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 15 Jul 2024 19:02:17 -0700 Subject: [PATCH 018/151] Strip any box/unbox around trigger terms --- .../CounterExampleGeneration/DafnyModel.cs | 4 +-- .../Verifier/BoogieGenerator.BoogieFactory.cs | 7 +++-- Source/DafnyCore/Verifier/BoogieGenerator.cs | 27 ++++++++++++++----- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/Source/DafnyCore/CounterExampleGeneration/DafnyModel.cs b/Source/DafnyCore/CounterExampleGeneration/DafnyModel.cs index 1ab7cfebe77..5357c288dc0 100644 --- a/Source/DafnyCore/CounterExampleGeneration/DafnyModel.cs +++ b/Source/DafnyCore/CounterExampleGeneration/DafnyModel.cs @@ -72,7 +72,7 @@ public DafnyModel(Model model, DafnyOptions options) { fMapEmpty = new ModelFuncWrapper(this, "Map#Empty", 0, 0); fIs = new ModelFuncWrapper(this, "$Is", 2, tyArgMultiplier); fIsBox = new ModelFuncWrapper(this, "$IsBox", 2, 0); - fBox = new ModelFuncWrapper(this, "$Box", 1, tyArgMultiplier); + fBox = new ModelFuncWrapper(this, BoogieGenerator.BoxFunctionName, 1, tyArgMultiplier); fDim = new ModelFuncWrapper(this, "FDim", 1, 0); fIndexField = new ModelFuncWrapper(this, "IndexField", 1, 0); fMultiIndexField = new ModelFuncWrapper(this, "MultiIndexField", 2, 0); @@ -84,7 +84,7 @@ public DafnyModel(Model model, DafnyOptions options) { fU2Int = new ModelFuncWrapper(this, "U_2_int", 1, 0); fTag = new ModelFuncWrapper(this, "Tag", 1, 0); fBv = new ModelFuncWrapper(this, "TBitvector", 1, 0); - fUnbox = new ModelFuncWrapper(this, "$Unbox", 2, 0); + fUnbox = new ModelFuncWrapper(this, BoogieGenerator.UnboxFunctionName, 2, 0); fLs = new ModelFuncWrapper(this, "$LS", 1, 0); fLz = new ModelFuncWrapper(this, "$LZ", 0, 0); InitDataTypes(); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.BoogieFactory.cs b/Source/DafnyCore/Verifier/BoogieGenerator.BoogieFactory.cs index f707a486cd8..f3dbb74110c 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.BoogieFactory.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.BoogieFactory.cs @@ -152,6 +152,9 @@ enum BuiltinFunction { AtLayer } + public const string BoxFunctionName = "$Box"; + public const string UnboxFunctionName = "$Unbox"; + Bpl.Expr Lit(Bpl.Expr expr, Bpl.Type typ) { Contract.Requires(expr != null); Contract.Requires(typ != null); @@ -494,11 +497,11 @@ Bpl.NAryExpr FunctionCall(Bpl.IToken tok, BuiltinFunction f, Bpl.Type typeInstan case BuiltinFunction.Box: Contract.Assert(args.Length == 1); Contract.Assert(typeInstantiation == null); - return FunctionCall(tok, "$Box", predef.BoxType, args); + return FunctionCall(tok, BoxFunctionName, predef.BoxType, args); case BuiltinFunction.Unbox: Contract.Assert(args.Length == 1); Contract.Assert(typeInstantiation != null); - return Bpl.Expr.CoerceType(tok, FunctionCall(tok, "$Unbox", typeInstantiation, args), typeInstantiation); + return Bpl.Expr.CoerceType(tok, FunctionCall(tok, UnboxFunctionName, typeInstantiation, args), typeInstantiation); case BuiltinFunction.RealToInt: Contract.Assume(args.Length == 1); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index d00f17ca7a7..afff5bad8b8 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -3258,7 +3258,7 @@ public Bpl.Expr CondApplyBox(Bpl.IToken tok, Bpl.Expr e, Type fromType, Type toT if (e is Bpl.NAryExpr { Fun: Bpl.TypeCoercion } coerce) { Contract.Assert(coerce.Args.Count == 1); Contract.Assert(Bpl.Type.Equals(((Bpl.TypeCoercion)coerce.Fun).Type, TrType(fromType))); - if (coerce.Args[0] is Bpl.NAryExpr { Fun: Bpl.FunctionCall { FunctionName: "$Unbox" } } call) { + if (coerce.Args[0] is Bpl.NAryExpr { Fun: Bpl.FunctionCall { FunctionName: UnboxFunctionName } } call) { Contract.Assert(call.Args.Count == 1); return call.Args[0]; } @@ -3311,7 +3311,7 @@ public Bpl.Expr BoxIfNotNormallyBoxed(Bpl.IToken tok, Bpl.Expr e, Type t) { public Bpl.Expr ApplyBox(Bpl.IToken tok, Bpl.Expr e) { Contract.Assert(tok != null); Contract.Assert(e != null); - if (e.Type == predef.BoxType || e is NAryExpr { Fun.FunctionName: "$Box" }) { + if (e.Type == predef.BoxType || e is NAryExpr { Fun.FunctionName: BoxFunctionName }) { return e; } return FunctionCall(tok, BuiltinFunction.Box, null, e); @@ -3368,6 +3368,17 @@ public static bool ModeledAsBoxType(Type t) { return res; } + /// + /// This method returns "expr" are stripping off any type coercions and box/unbox functions. + /// + public static Boogie.Expr StripBoxAdjustments(Boogie.Expr expr) { + while (expr is Boogie.NAryExpr { Fun: Boogie.FunctionCall { FunctionName: BoxFunctionName or UnboxFunctionName } or Boogie.TypeCoercion } nAryExpr) { + Contract.Assert(nAryExpr.Args.Count == 1); + expr = nAryExpr.Args[0]; + } + return expr; + } + // ----- Statement ---------------------------------------------------------------------------- /// @@ -4622,11 +4633,13 @@ Bpl.Trigger TrTrigger(ExpressionTranslator etran, Attributes attribs, IToken tok foreach (var trigger in attribs.AsEnumerable().Where(aa => aa.Name == "trigger").Select(aa => aa.Args)) { List tt = new List(); foreach (var arg in trigger) { + Bpl.Expr term; if (substMap == null) { - tt.Add(argsEtran.TrExpr(arg)); + term = argsEtran.TrExpr(arg); } else { - tt.Add(argsEtran.TrExpr(Substitute(arg, null, substMap))); + term = argsEtran.TrExpr(Substitute(arg, null, substMap)); } + tt.Add(StripBoxAdjustments(term)); } tr = new Bpl.Trigger(tok, true, tt, tr); } @@ -4661,11 +4674,13 @@ Bpl.Trigger TrTrigger(ExpressionTranslator etran, Attributes attribs, IToken tok foreach (var trigger in attribs.AsEnumerable().Where(aa => aa.Name == "trigger")) { List tt = new List(); foreach (var arg in trigger.Args) { + Bpl.Expr term; if (substMap == null) { - tt.Add(argsEtran.TrExpr(arg)); + term = argsEtran.TrExpr(arg); } else { - tt.Add(argsEtran.TrExpr(Substitute(arg, null, substMap, typeMap))); + term = argsEtran.TrExpr(Substitute(arg, null, substMap, typeMap)); } + tt.Add(StripBoxAdjustments(term)); } if (useHeapAsQuantifier) { tt.Add(FunctionCall(tok, BuiltinFunction.IsGoodHeap, null, argsEtran.HeapExpr)); From 2dc5fa4cf9ceaf087cd01c60237007f42c1a8be8 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 15 Jul 2024 19:02:33 -0700 Subject: [PATCH 019/151] Fix casts in test case --- .../TestFiles/LitTests/LitTest/dafny0/Strings.dfy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Strings.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Strings.dfy index b1b2260ec8c..cfce8478458 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Strings.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Strings.dfy @@ -143,8 +143,8 @@ method WeirdChars() returns (c: char, d: char) method AllCharsTest() { var allChars := set c: char {:trigger Identity(c)} | true :: Identity(c); - var allUTF16CodeUnits := set cp: int {:trigger Identity(cp)} | 0 <= cp < 0x1_0000 :: Identity(cp as char); - assert forall c: char {:trigger Identity(c)} :: 0 <= Identity(c as int) < 0x1_0000; + var allUTF16CodeUnits := set cp: int {:trigger Identity(cp as char)} | 0 <= cp < 0x1_0000 :: Identity(cp as char); + assert forall c: char {:trigger Identity(c)} :: 0 <= Identity(c) as int < 0x1_0000; assert forall c: char :: Identity(c) in allChars; assert allChars == allUTF16CodeUnits; From ea774a5d57d920349c910a9a0dd049cbc3e973c7 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 15 Jul 2024 21:50:16 -0700 Subject: [PATCH 020/151] Fix ConjunctsOf to not drop labels --- Source/DafnyCore/Verifier/BoogieGenerator.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index afff5bad8b8..81bc45261a2 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -4597,8 +4597,13 @@ internal IsAllocType Var(bool ghostStmt, NonglobalVariable var) { /// public static IEnumerable ConjunctsOf(List attributedExpressions) { foreach (var attrExpr in attributedExpressions) { - foreach (var conjunct in Expression.ConjunctsWithLetsOnOutside(attrExpr.E)) { - yield return new AttributedExpression(conjunct, attrExpr.Attributes); + if (attrExpr.Label != null) { + // don't mess with labeled expressions + yield return attrExpr; + } else { + foreach (var conjunct in Expression.ConjunctsWithLetsOnOutside(attrExpr.E)) { + yield return new AttributedExpression(conjunct, attrExpr.Attributes); + } } } } From 466c1b812b7b0eadbee2cc12bea041223a484636 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 15 Jul 2024 22:11:29 -0700 Subject: [PATCH 021/151] Add CanCalls to primed version of comprehension uniqueness check --- .../Verifier/BoogieGenerator.ExpressionWellformed.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 764f9e513d7..0f83b36cfb2 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -1213,14 +1213,17 @@ void CheckOperand(Expression operand) { Contract.Assert(substMapPrime != null); Contract.Assert(bodyLeftPrime != null); Contract.Assert(bodyPrime != null); + Bpl.Expr guardPrimeCanCall = null; Bpl.Expr guardPrime = null; if (guard != null) { Contract.Assert(e.Range != null); var rangePrime = Substitute(e.Range, null, substMapPrime); + guardPrimeCanCall = comprehensionEtran.CanCallAssumption(rangePrime); guardPrime = comprehensionEtran.TrExpr(rangePrime); } BplIfIf(e.tok, guard != null, BplAnd(guard, guardPrime), newBuilder, b => { - var canCalls = comprehensionEtran.CanCallAssumption(bodyLeft); + var canCalls = guardPrimeCanCall ?? Bpl.Expr.True; + canCalls = BplAnd(canCalls, comprehensionEtran.CanCallAssumption(bodyLeft)); canCalls = BplAnd(canCalls, comprehensionEtran.CanCallAssumption(bodyLeftPrime)); canCalls = BplAnd(canCalls, comprehensionEtran.CanCallAssumption(body)); canCalls = BplAnd(canCalls, comprehensionEtran.CanCallAssumption(bodyPrime)); From 171939ecdca4eda570b1fb33fbbe18e39d645531 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 16 Jul 2024 10:24:42 -0700 Subject: [PATCH 022/151] Reflect change in error output ordering --- .../TestFiles/LitTests/LitTest/dafny0/AutoContracts.dfy.expect | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AutoContracts.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AutoContracts.dfy.expect index 50ce7f167c5..5655e533d30 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AutoContracts.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AutoContracts.dfy.expect @@ -573,12 +573,12 @@ module N2 refines N1 { */ } AutoContracts.dfy(17,4): Error: a postcondition could not be proved on this return path -AutoContracts.dfy(12,20): Related location: this proposition could not be proved AutoContracts.dfy(17,4): Error: a postcondition could not be proved on this return path AutoContracts.dfy(12,20): Related location: this proposition could not be proved AutoContracts.dfy(17,4): Error: a postcondition could not be proved on this return path AutoContracts.dfy(12,20): Related location: this proposition could not be proved AutoContracts.dfy(17,4): Error: a postcondition could not be proved on this return path +AutoContracts.dfy(12,20): Related location: this proposition could not be proved AutoContracts.dfy(17,4): Error: a postcondition could not be proved on this return path AutoContracts.dfy(12,20): Related location: this proposition could not be proved AutoContracts.dfy(5,25): Related location: this proposition could not be proved From c7222b65f031139acd7554e94076fda5017ba7ca Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 16 Jul 2024 15:09:50 -0700 Subject: [PATCH 023/151] =?UTF-8?q?Fix=20Conjuncts=20all=20to=20include=20?= =?UTF-8?q?type=20of=20new=20LetExpr=E2=80=99s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/DafnyCore/AST/Expressions/Expression.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/DafnyCore/AST/Expressions/Expression.cs b/Source/DafnyCore/AST/Expressions/Expression.cs index 0daa3076f2a..8909c5bb6f3 100644 --- a/Source/DafnyCore/AST/Expressions/Expression.cs +++ b/Source/DafnyCore/AST/Expressions/Expression.cs @@ -143,7 +143,9 @@ public static IEnumerable ConjunctsWithLetsOnOutside(Expression expr foreach (var conjunct in Conjuncts(expr)) { if (conjunct is LetExpr { Exact: true } letExpr) { foreach (var letBodyConjunct in ConjunctsWithLetsOnOutside(letExpr.Body)) { - yield return new LetExpr(letExpr.tok, letExpr.LHSs, letExpr.RHSs, letBodyConjunct, letExpr.Exact, letExpr.Attributes); + yield return new LetExpr(letExpr.tok, letExpr.LHSs, letExpr.RHSs, letBodyConjunct, letExpr.Exact, letExpr.Attributes) { + Type = letExpr.Type + }; } } else { yield return conjunct; From 94a0ccca7bc5d4dd226fa49967c33fa383255ef0 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 16 Jul 2024 16:32:34 -0700 Subject: [PATCH 024/151] Add CanCall assumptions to subset-constraint checks --- Source/DafnyCore/Verifier/BoogieGenerator.Types.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs index db6e32957ed..1bb66e8d521 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs @@ -1398,8 +1398,11 @@ void CheckResultToBeInType_Aux(IToken tok, Expression boogieExpr, Expression ori // TODO: use TrSplitExpr 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))); + var boogieConstraint = Substitute(rdt.Constraint, null, new() { { rdt.Var, boogieExpr } }, typeMap); + + var canCall = etran.CanCallAssumption(boogieConstraint); + var constraint = etran.TrExpr(boogieConstraint); + builder.Add(Assert(tok, BplImp(canCall, constraint), new PODesc.ConversionSatisfiesConstraints(errorMsgPrefix, kind, rdt.Name, dafnyConstraint))); } } From f059ffdf9871935365fc36da9169d8b1c6f160b9 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 16 Jul 2024 17:19:36 -0700 Subject: [PATCH 025/151] Restructure CanCallOptions and make allowances for overriding functions --- .../BoogieGenerator.ExpressionTranslator.cs | 30 +++++++++---------- .../Verifier/BoogieGenerator.Functions.cs | 2 +- .../Verifier/BoogieGenerator.Methods.cs | 8 ++--- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index 4a265ce6806..a844edb4392 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -2227,8 +2227,7 @@ public Expr CanCallAssumption(Expression expr, CanCallOptions cco = null) { Boogie.IdentifierExpr canCallFuncID = new Boogie.IdentifierExpr(expr.tok, e.Function.FullSanitizedName + "#canCall", Boogie.Type.Bool); List args = FunctionInvocationArguments(e, null, null); Boogie.Expr canCallFuncAppl = new Boogie.NAryExpr(BoogieGenerator.GetToken(expr), new Boogie.FunctionCall(canCallFuncID), args); - bool makeAllowance = cco != null && (e.Function == cco.SelfCallsAllowance); - var add = makeAllowance ? Boogie.Expr.Or(TrExpr(MakeAllowance(e, cco)), canCallFuncAppl) : canCallFuncAppl; + var add = cco != null && cco.MakeAllowance(e.Function) ? Boogie.Expr.Or(TrExpr(MakeAllowance(e, cco)), canCallFuncAppl) : canCallFuncAppl; r = BplAnd(r, add); } return r; @@ -2313,7 +2312,7 @@ public Expr CanCallAssumption(Expression expr, CanCallOptions cco = null) { case BinaryExpr.ResolvedOpcode.EqCommon: case BinaryExpr.ResolvedOpcode.NeqCommon: { Boogie.Expr r = Boogie.Expr.True; - if (cco == null || !cco.skipIsA) { + if (cco?.SkipIsA == true) { if (e.E0 is { Type: { AsDatatype: { } dt0 }, Resolved: not DatatypeValue }) { var funcID = new Boogie.FunctionCall(new Boogie.IdentifierExpr(expr.tok, "$IsA#" + dt0.FullSanitizedName, Boogie.Type.Bool)); r = BplAnd(r, new Boogie.NAryExpr(expr.tok, funcID, new List { TrExpr(e.E0) })); @@ -2552,24 +2551,23 @@ public Expr CanCallAssumption(List exprs, CanCallOptions cco) { } public class CanCallOptions { - public readonly Function SelfCallsAllowance; - public readonly Function EnclosingFunction; + public bool SkipIsA; - public bool skipIsA; + public readonly Function EnclosingFunction; // self-call allowance is applied to the enclosing function + public readonly bool SelfCallAllowanceAlsoForOverride; - public CanCallOptions(Function f, bool skip = false) { - this.SelfCallsAllowance = f; - this.EnclosingFunction = f; - this.skipIsA = skip; + public bool MakeAllowance(Function f) { + return f == EnclosingFunction || (SelfCallAllowanceAlsoForOverride && f == EnclosingFunction.OverriddenFunction); } - public CanCallOptions(Function f, Function g, bool skip = false) { - Contract.Assert(f.Ins.Count() == g.Ins.Count()); - this.SelfCallsAllowance = f; - this.EnclosingFunction = g; - this.skipIsA = skip; + public CanCallOptions(bool skipIsA, Function enclosingFunction, bool selfCallAllowanceAlsoForOverride = false) { + Contract.Assert(!selfCallAllowanceAlsoForOverride || + (enclosingFunction.OverriddenFunction != null && + enclosingFunction.Ins.Count == enclosingFunction.OverriddenFunction.Ins.Count)); + this.SkipIsA = skipIsA; + this.EnclosingFunction = enclosingFunction; + this.SelfCallAllowanceAlsoForOverride = selfCallAllowanceAlsoForOverride; } - } } } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs index 507c4d7607d..fe7a6ca3704 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs @@ -91,7 +91,7 @@ void AddWellformednessCheck(Function f) { var splits = new List(); bool splitHappened /*we actually don't care*/ = TrSplitExpr(p.E, splits, true, functionHeight, true, true, etran); var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); - var canCalls = etran.CanCallAssumption(p.E, new CanCallOptions(f, true)); + var canCalls = etran.CanCallAssumption(p.E, new CanCallOptions(true, f)); AddEnsures(ens, FreeEnsures(p.E.tok, canCalls, null, true)); foreach (var s in splits) { if (s.IsChecked && !RefinementToken.IsInherited(s.Tok, currentModule)) { diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 44e6cdabba1..fe3bd28b132 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -1064,7 +1064,7 @@ private void AddFunctionOverrideEnsChk(Function f, BoogieStmtListBuilder builder Bpl.Variable/*?*/ resultVariable) { Contract.Requires(f.Ins.Count <= implInParams.Count); - var cco = new CanCallOptions(f, true); + var cco = new CanCallOptions(true, f); //generating class post-conditions foreach (var en in ConjunctsOf(f.Ens)) { builder.Add(TrAssumeCmd(f.tok, etran.CanCallAssumption(en.E, cco))); @@ -1108,7 +1108,7 @@ private void AddFunctionOverrideEnsChk(Function f, BoogieStmtListBuilder builder .Select(e => e.E) .Aggregate((e0, e1) => new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.And, e0, e1)); //generating trait post-conditions with class variables - cco = new CanCallOptions(f.OverriddenFunction, f, true); + cco = new CanCallOptions(true, f, true); FunctionCallSubstituter sub = null; foreach (var en in ConjunctsOf(f.OverriddenFunction.Ens)) { sub ??= new FunctionCallSubstituter(substMap, typeMap, (TraitDecl)f.OverriddenFunction.EnclosingClass, (TopLevelDeclWithMembers)f.EnclosingClass); @@ -1201,7 +1201,7 @@ private void AddFunctionOverrideReqsChk(Function f, BoogieStmtListBuilder builde Contract.Requires(etran != null); Contract.Requires(substMap != null); //generating trait pre-conditions with class variables - var cco = new CanCallOptions(f.OverriddenFunction, f, true); + var cco = new CanCallOptions(true, f, true); FunctionCallSubstituter sub = null; var subReqs = new List(); foreach (var req in ConjunctsOf(f.OverriddenFunction.Req)) { @@ -1214,7 +1214,7 @@ private void AddFunctionOverrideReqsChk(Function f, BoogieStmtListBuilder builde var allTraitReqs = subReqs.Count == 0 ? null : subReqs .Aggregate((e0, e1) => new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.And, e0, e1)); //generating class pre-conditions - cco = new CanCallOptions(f, true); + cco = new CanCallOptions(true, f); foreach (var req in ConjunctsOf(f.Req)) { foreach (var s in TrSplitExpr(req.E, etran, false, out _).Where(s => s.IsChecked)) { builder.Add(TrAssumeCmd(f.tok, etran.CanCallAssumption(req.E, cco))); From d63ff0accd28f0b99e4a375caf2fe678a4c85693 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 17 Jul 2024 09:44:37 -0700 Subject: [PATCH 026/151] =?UTF-8?q?Add=20CanCan=20antecedent=20to=20?= =?UTF-8?q?=E2=80=9Cr=E2=80=9D=20component=20of=20function=20handles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Verifier/BoogieGenerator.ExpressionTranslator.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index a844edb4392..b7190f43f25 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -1589,9 +1589,13 @@ private Expr TrLambdaExpr(LambdaExpr e) { ebody = BoogieGenerator.BoxIfNotNormallyBoxed(ebody.tok, ebody, e.Body.Type); var isBoxes = BplAnd(ves.Zip(e.BoundVars, (ve, bv) => BoogieGenerator.MkIsBox(ve, bv.Type))); - var reqbody = e.Range == null - ? isBoxes - : BplAnd(isBoxes, et.TrExpr(BoogieGenerator.Substitute(e.Range, null, subst))); + Bpl.Expr reqbody; + if (e.Range == null) { + reqbody = isBoxes; + } else { + var range = BoogieGenerator.Substitute(e.Range, null, subst); + reqbody = BplAnd(isBoxes, BplImp(et.CanCallAssumption(range), et.TrExpr(range))); + } var rdvars = new List(); var o = BplBoundVar(varNameGen.FreshId("#o#"), predef.RefType, rdvars); From f7bf313ed9939a4cc5228d3d6421d9e38d368977 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 19 Jul 2024 21:32:57 -0700 Subject: [PATCH 027/151] Remove deprecation --- .../LitTest/dafny1/Induction.legacy.dfy | 88 +++++++++---------- .../LitTest/dafny3/SimpleInduction.dfy | 23 +++-- 2 files changed, 55 insertions(+), 56 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/Induction.legacy.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/Induction.legacy.dfy index a7309c89a8f..aa2286383e4 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/Induction.legacy.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/Induction.legacy.dfy @@ -1,4 +1,4 @@ -// RUN: %exits-with 4 %dafny /compile:0 /deprecation:0 /induction:3 /dprint:"%t.dprint" "%s" > "%t" +// RUN: %exits-with 4 %dafny /compile:0 /induction:3 /dprint:"%t.dprint" "%s" > "%t" // RUN: %diff "%s.expect" "%t" class IntegerInduction { @@ -9,13 +9,13 @@ class IntegerInduction { // SumOfCubes(n) == Gauss(n) * Gauss(n) ghost function SumOfCubes(n: int): int - requires 0 <= n; + requires 0 <= n { if n == 0 then 0 else SumOfCubes(n-1) + n*n*n } ghost function Gauss(n: int): int - requires 0 <= n; + requires 0 <= n { if n == 0 then 0 else Gauss(n-1) + n } @@ -23,39 +23,39 @@ class IntegerInduction { // Here is one proof. It uses a lemma, which is proved separately. lemma Theorem0(n: int) - requires 0 <= n; - ensures SumOfCubes(n) == Gauss(n) * Gauss(n); + requires 0 <= n + ensures SumOfCubes(n) == Gauss(n) * Gauss(n) { - if (n != 0) { + if n != 0 { Theorem0(n-1); Lemma(n-1); } } lemma Lemma(n: int) - requires 0 <= n; - ensures 2 * Gauss(n) == n*(n+1); + requires 0 <= n + ensures 2 * Gauss(n) == n*(n+1) { - if (n != 0) { Lemma(n-1); } + if n != 0 { Lemma(n-1); } } // Here is another proof. It states the lemma as part of the theorem, and // thus proves the two together. lemma Theorem1(n: int) - requires 0 <= n; - ensures SumOfCubes(n) == Gauss(n) * Gauss(n); - ensures 2 * Gauss(n) == n*(n+1); + requires 0 <= n + ensures SumOfCubes(n) == Gauss(n) * Gauss(n) + ensures 2 * Gauss(n) == n*(n+1) { - if (n != 0) { + if n != 0 { Theorem1(n-1); } } lemma DoItAllInOneGo() - ensures (forall n {:split false} :: 0 <= n ==> // WISH reenable quantifier splitting here. This will only work once we generate induction hypotheses at the Dafny level. + ensures forall n {:split false} :: 0 <= n ==> // WISH reenable quantifier splitting here. This will only work once we generate induction hypotheses at the Dafny level. SumOfCubes(n) == Gauss(n) * Gauss(n) && - 2 * Gauss(n) == n*(n+1)); + 2 * Gauss(n) == n*(n+1) { } @@ -64,15 +64,15 @@ class IntegerInduction { // Dafny's ghost-method induction tactic). lemma Lemma_Auto(n: int) - requires 0 <= n; - ensures 2 * Gauss(n) == n*(n+1); + requires 0 <= n + ensures 2 * Gauss(n) == n*(n+1) { } lemma Theorem1_Auto(n: int) - requires 0 <= n; - ensures SumOfCubes(n) == Gauss(n) * Gauss(n); - ensures 2 * Gauss(n) == n*(n+1); + requires 0 <= n + ensures SumOfCubes(n) == Gauss(n) * Gauss(n) + ensures 2 * Gauss(n) == n*(n+1) { } @@ -80,37 +80,37 @@ class IntegerInduction { // prove the lemma. lemma Theorem2(n: int) - requires 0 <= n; - ensures SumOfCubes(n) == Gauss(n) * Gauss(n); + requires 0 <= n + ensures SumOfCubes(n) == Gauss(n) * Gauss(n) { - if (n != 0) { + if n != 0 { Theorem2(n-1); - assert (forall m :: 0 <= m ==> 2 * Gauss(m) == m*(m+1)); + assert forall m :: 0 <= m ==> 2 * Gauss(m) == m*(m+1); } } lemma M(n: int) - requires 0 <= n; + requires 0 <= n { - assume (forall k :: 0 <= k && k < n ==> 2 * Gauss(k) == k*(k+1)); // manually assume the induction hypothesis + assume forall k :: 0 <= k && k < n ==> 2 * Gauss(k) == k*(k+1); // manually assume the induction hypothesis assert 2 * Gauss(n) == n*(n+1); } // Another way to prove the lemma is to supply a postcondition on the Gauss function lemma Theorem3(n: int) - requires 0 <= n; - ensures SumOfCubes(n) == GaussWithPost(n) * GaussWithPost(n); + requires 0 <= n + ensures SumOfCubes(n) == GaussWithPost(n) * GaussWithPost(n) { - if (n != 0) { + if n != 0 { Theorem3(n-1); } } ghost function GaussWithPost(n: int): int - requires 0 <= n; - ensures 2 * GaussWithPost(n) == n*(n+1); + requires 0 <= n + ensures 2 * GaussWithPost(n) == n*(n+1) { if n == 0 then 0 else GaussWithPost(n-1) + n } @@ -118,19 +118,19 @@ class IntegerInduction { // Finally, with the postcondition of GaussWithPost, one can prove the entire theorem by induction lemma Theorem4() - ensures (forall n :: 0 <= n ==> - SumOfCubes(n) == GaussWithPost(n) * GaussWithPost(n)); + ensures forall n :: 0 <= n ==> + SumOfCubes(n) == GaussWithPost(n) * GaussWithPost(n) { // look ma, no hints! } lemma Theorem5(n: int) - requires 0 <= n; - ensures SumOfCubes(n) == GaussWithPost(n) * GaussWithPost(n); + requires 0 <= n + ensures SumOfCubes(n) == GaussWithPost(n) * GaussWithPost(n) { // the postcondition is a simple consequence of these quantified versions of the theorem: - if (*) { - assert (forall m :: 0 <= m ==> SumOfCubes(m) == GaussWithPost(m) * GaussWithPost(m)); + if * { + assert forall m :: 0 <= m ==> SumOfCubes(m) == GaussWithPost(m) * GaussWithPost(m); } else { Theorem4(); } @@ -149,10 +149,10 @@ class IntegerInduction { // The example uses an attribute that requests induction on just "j". However, the proof also // goes through by applying induction on both bound variables. function IsSorted(s: seq): bool //WISH remove autotriggers false - ensures IsSorted(s) ==> (forall i,j {:induction j} {:autotriggers false} :: 0 <= i < j < |s| ==> s[i] <= s[j]); - ensures (forall i,j :: 0 <= i && i < j && j < |s| ==> s[i] <= s[j]) ==> IsSorted(s); + ensures IsSorted(s) ==> forall i,j {:induction j} {:autotriggers false} :: 0 <= i < j < |s| ==> s[i] <= s[j] + ensures (forall i,j :: 0 <= i && i < j && j < |s| ==> s[i] <= s[j]) ==> IsSorted(s) { - (forall i {:nowarn} {:matchinglooprewrite false}:: 1 <= i && i < |s| ==> s[i-1] <= s[i]) + forall i {:nowarn} {:matchinglooprewrite false} :: 1 <= i && i < |s| ==> s[i-1] <= s[i] } } @@ -167,9 +167,9 @@ class DatatypeInduction { } method Theorem0(tree: Tree) - ensures 1 <= LeafCount(tree); + ensures 1 <= LeafCount(tree) { - assert (forall t: Tree :: 1 <= LeafCount(t)); + assert forall t: Tree :: 1 <= LeafCount(t); } // see also Test/dafny0/DTypes.dfy for more variations of this example @@ -182,7 +182,7 @@ class DatatypeInduction { } method RegressionTest(tree: Tree) // the translation of the following line once crashed Dafny - requires forall y :: 0 <= OccurrenceCount(tree, y); + requires forall y :: 0 <= OccurrenceCount(tree, y) { } @@ -195,7 +195,7 @@ class DatatypeInduction { datatype D = Nothing | Something(D) ghost function FooD(n: nat, d: D): int - ensures 10 <= FooD(n, d); + ensures 10 <= FooD(n, d) { match d case Nothing => diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/SimpleInduction.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/SimpleInduction.dfy index 82270e29727..0ffe9e2fbe4 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/SimpleInduction.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/SimpleInduction.dfy @@ -1,4 +1,4 @@ -// RUN: %testDafnyForEachResolver "%s" -- --allow-deprecation +// RUN: %testDafnyForEachResolver "%s" /* @@ -10,14 +10,14 @@ */ ghost function Fib(n: nat): nat - decreases n; + decreases n { if n < 2 then n else Fib(n-2) + Fib(n-1) } lemma FibLemma(n: nat) - ensures Fib(n) % 2 == 0 <==> n % 3 == 0; - decreases n; + ensures Fib(n) % 2 == 0 <==> n % 3 == 0 + decreases n { - if (n < 2) { + if n < 2 { } else { FibLemma(n-2); FibLemma(n-1); @@ -31,7 +31,7 @@ lemma FibLemma(n: nat) */ lemma FibLemma_Alternative(n: nat) - ensures Fib(n) % 2 == 0 <==> n % 3 == 0; + ensures Fib(n) % 2 == 0 <==> n % 3 == 0 { forall k | 0 <= k < n { FibLemma_Alternative(k); @@ -39,7 +39,7 @@ lemma FibLemma_Alternative(n: nat) } lemma FibLemma_All() - ensures forall n :: 0 <= n ==> (Fib(n) % 2 == 0 <==> n % 3 == 0); + ensures forall n :: 0 <= n ==> (Fib(n) % 2 == 0 <==> n % 3 == 0) { forall n | 0 <= n { FibLemma(n); @@ -59,7 +59,7 @@ lemma FibLemma_All() datatype List = Nil | Cons(head: T, tail: List) ghost function Append(xs: List, ys: List): List - decreases xs; + decreases xs { match xs case Nil => ys @@ -69,8 +69,8 @@ ghost function Append(xs: List, ys: List): List // The {:induction false} attribute disables automatic induction tactic, // so we can make the proof explicit. lemma {:induction false} AppendIsAssociative(xs: List, ys: List, zs: List) - ensures Append(Append(xs, ys), zs) == Append(xs, Append(ys, zs)); - decreases xs; + ensures Append(Append(xs, ys), zs) == Append(xs, Append(ys, zs)) + decreases xs { match (xs) { case Nil => @@ -82,7 +82,6 @@ lemma {:induction false} AppendIsAssociative(xs: List, ys: List, zs: List) // Here the proof is fully automatic - the body of the method is empty, // yet still verifies. lemma AppendIsAssociative_Auto(xs: List, ys: List, zs: List) - ensures Append(Append(xs, ys), zs) == Append(xs, Append(ys, zs)); + ensures Append(Append(xs, ys), zs) == Append(xs, Append(ys, zs)) { } - From 460e400b9d65037e7fc1660fd58679d70066ebbd Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 19 Jul 2024 21:33:39 -0700 Subject: [PATCH 028/151] Use :induction instead of /induction:3 --- .../LitTests/LitTest/dafny1/Induction.legacy.dfy | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/Induction.legacy.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/Induction.legacy.dfy index aa2286383e4..cfc80e894fa 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/Induction.legacy.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/Induction.legacy.dfy @@ -1,4 +1,4 @@ -// RUN: %exits-with 4 %dafny /compile:0 /induction:3 /dprint:"%t.dprint" "%s" > "%t" +// RUN: %exits-with 4 %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t" // RUN: %diff "%s.expect" "%t" class IntegerInduction { @@ -53,7 +53,7 @@ class IntegerInduction { } lemma DoItAllInOneGo() - ensures forall n {:split false} :: 0 <= n ==> // WISH reenable quantifier splitting here. This will only work once we generate induction hypotheses at the Dafny level. + ensures forall n {:induction} {:split false} :: 0 <= n ==> // WISH reenable quantifier splitting here. This will only work once we generate induction hypotheses at the Dafny level. SumOfCubes(n) == Gauss(n) * Gauss(n) && 2 * Gauss(n) == n*(n+1) { @@ -86,7 +86,7 @@ class IntegerInduction { if n != 0 { Theorem2(n-1); - assert forall m :: 0 <= m ==> 2 * Gauss(m) == m*(m+1); + assert forall m {:induction} :: 0 <= m ==> 2 * Gauss(m) == m*(m+1); } } @@ -118,7 +118,7 @@ class IntegerInduction { // Finally, with the postcondition of GaussWithPost, one can prove the entire theorem by induction lemma Theorem4() - ensures forall n :: 0 <= n ==> + ensures forall n {:induction} :: 0 <= n ==> SumOfCubes(n) == GaussWithPost(n) * GaussWithPost(n) { // look ma, no hints! @@ -130,7 +130,7 @@ class IntegerInduction { { // the postcondition is a simple consequence of these quantified versions of the theorem: if * { - assert forall m :: 0 <= m ==> SumOfCubes(m) == GaussWithPost(m) * GaussWithPost(m); + assert forall m {:induction} :: 0 <= m ==> SumOfCubes(m) == GaussWithPost(m) * GaussWithPost(m); } else { Theorem4(); } @@ -169,7 +169,7 @@ class DatatypeInduction { method Theorem0(tree: Tree) ensures 1 <= LeafCount(tree) { - assert forall t: Tree :: 1 <= LeafCount(t); + assert forall t: Tree {:induction} :: 1 <= LeafCount(t); } // see also Test/dafny0/DTypes.dfy for more variations of this example From 09be550a50d9ffe511b9dbc84a4280cca37e1d92 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 19 Jul 2024 21:50:29 -0700 Subject: [PATCH 029/151] Add CanCall assumptions to generated let function Fixes comp/Comprehensions.dfy --- Source/DafnyCore/Verifier/BoogieGenerator.LetExpr.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.LetExpr.cs b/Source/DafnyCore/Verifier/BoogieGenerator.LetExpr.cs index 9bcfa1a3912..ba2b5a28f7a 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.LetExpr.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.LetExpr.cs @@ -151,7 +151,7 @@ private void AddLetSuchThenCanCallAxiom(LetExpr e, LetSuchThatExprInfo info, Bpl var call = FunctionCall(e.tok, info.SkolemFunctionName(bv), TrType(bv.Type), gExprs); tr = new Bpl.Trigger(e.tok, true, new List { call }, tr); substMap.Add(bv, new BoogieWrapper(call, bv.Type)); - if (!(bv.Type.IsTypeParameter)) { + if (!bv.Type.IsTypeParameter) { Bpl.Expr wh = GetWhereClause(bv.tok, call, bv.Type, etranCC, NOALLOC); if (wh != null) { antecedent = BplAnd(antecedent, wh); @@ -176,7 +176,8 @@ private void AddLetSuchThenCanCallAxiom(LetExpr e, LetSuchThatExprInfo info, Bpl var canCall = FunctionCall(e.tok, info.CanCallFunctionName(), Bpl.Type.Bool, gExprs); var p = Substitute(e.RHSs[0], receiverReplacement, substMap); - Bpl.Expr ax = BplImp(canCall, BplAnd(antecedent, etranCC.TrExpr(p))); + var canCallBody = etranCC.CanCallAssumption(p); + Bpl.Expr ax = BplImp(canCall, BplAnd(antecedent, BplAnd(canCallBody, etranCC.TrExpr(p)))); ax = BplForall(gg, tr, ax); AddOtherDefinition(canCallFunction, new Bpl.Axiom(e.tok, ax)); } From 87b6f0f7bde4bf3f8ee9c0c1093ac2e00ba25bfc Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 19 Jul 2024 22:03:10 -0700 Subject: [PATCH 030/151] Adjust test case, which is now passing --- .../TestFiles/LitTests/LitTest/git-issues/git-issue-2500.dfy | 2 +- .../LitTests/LitTest/git-issues/git-issue-2500.dfy.expect | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-2500.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-2500.dfy index ffd3895cead..b59730c4dd2 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-2500.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-2500.dfy @@ -31,7 +31,7 @@ class Cl extends M.Tr { ensures ActuallyTrue() { true } predicate ActuallyTrue''() - // This is logically correct, but the disguised receiver in the Tr spec prevents the override check from passing. + // This is logically correct. (Before CanCall, the disguised receiver in the Tr spec had prevented the override check from passing.) ensures ActuallyTrue''() { true } predicate Other(x: nat, free: M.Tr) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-2500.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-2500.dfy.expect index 64fcebbc922..6360ffa291f 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-2500.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-2500.dfy.expect @@ -1,6 +1,5 @@ git-issue-2500.dfy(21,12): Error: the function must provide an equal or more detailed postcondition than in its parent trait git-issue-2500.dfy(24,12): Error: the function must provide an equal or more detailed postcondition than in its parent trait -git-issue-2500.dfy(33,12): Error: the function must provide an equal or more detailed postcondition than in its parent trait git-issue-2500.dfy(37,12): Error: the function must provide an equal or more detailed postcondition than in its parent trait -Dafny program verifier finished with 26 verified, 4 errors +Dafny program verifier finished with 27 verified, 3 errors From 3127da25500230a559d5d72f90709c94f90bcb88 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 22 Jul 2024 11:17:06 -0700 Subject: [PATCH 031/151] Incorporate CanCall into newtype/subset-type $Is axioms This helped many tests go through, including: * autoRevealDependencies/subset-types.dfy * git-issues/git-issue-1479.dfy * git-issues/git-issue-362.dfy --- .../Verifier/BoogieGenerator.Types.cs | 97 ++++++++++++------- 1 file changed, 60 insertions(+), 37 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs index 1bb66e8d521..1e8b0649aa3 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs @@ -954,71 +954,94 @@ void AddTypeDecl(SubsetTypeDecl dd) { this.fuelContext = oldFuelContext; } - /** - * Example: - // _System.object: subset type $Is - axiom (forall c#0: ref :: - { $Is(c#0, Tclass._System.object()) } - $Is(c#0, Tclass._System.object()) - <==> $Is(c#0, Tclass._System.object?()) && c#0 != null); - - // _System.object: subset type $IsAlloc - axiom (forall c#0: ref, $h: Heap :: - { $IsAlloc(c#0, Tclass._System.object(), $h) } - $IsAlloc(c#0, Tclass._System.object(), $h) - <==> $IsAlloc(c#0, Tclass._System.object?(), $h)); - */ - void AddRedirectingTypeDeclAxioms(bool is_alloc, T dd, string fullName) + /// + /// Generate $Is (if "!generateIsAlloc") or $IsAlloc (if "generateIsAlloc") axioms for the newtype/subset-type "dd", + /// whose printable name is "fullName". + /// + /// Given that the type "dd" is + /// + /// (new)type dd = x: Base | constraint + /// + /// the $Is axioms have the form + /// + /// axiom (forall o: dd :: + /// { $Is(o, Tclass.dd) } + /// $Is(o, Tclass.dd) ==> + /// $Is(o, Tclass.Base) && constraintCanCall && constraint); + /// axiom (forall o: dd :: + /// { $Is(o, Tclass.dd) } + /// $Is(o, Tclass.Base) && (constraintCanCall ==> constraint) ==> + /// $Is(o, Tclass.dd)); + /// + /// and the $IsAlloc axiom has the form + /// + /// axiom (forall o: dd, $h: Heap :: + /// { $IsAlloc(o, Tclass.dd, $h) } + /// $IsAlloc(o, Tclass.dd, $h) <==> $IsAlloc(o, Tclass.Base, $h)); + /// + void AddRedirectingTypeDeclAxioms(bool generateIsAlloc, T dd, string fullName) where T : TopLevelDecl, RedirectingTypeDecl { Contract.Requires(dd != null); Contract.Requires((dd.Var != null && dd.Constraint != null) || dd is NewtypeDecl); Contract.Requires(fullName != null); - List typeArgs; - var vars = MkTyParamBinders(dd.TypeArgs, out typeArgs); - var o_ty = ClassTyCon(dd, typeArgs); + var vars = MkTyParamBinders(dd.TypeArgs, out var typeArgs); + var typeTerm = ClassTyCon(dd, typeArgs); var baseType = dd.Var != null ? dd.Var.Type : ((NewtypeDecl)(object)dd).BaseType; var oBplType = TrType(baseType); var c = new BoundVar(dd.tok, CurrentIdGenerator.FreshId("c"), baseType); var o = BplBoundVar((dd.Var ?? c).AssignUniqueName((dd.IdGenerator)), oBplType, vars); - Bpl.Expr body, is_o; - string comment; - - if (is_alloc) { - comment = $"$IsAlloc axiom for {dd.WhatKind} {fullName}"; + if (generateIsAlloc) { var h = BplBoundVar("$h", predef.HeapType, vars); // $IsAlloc(o, ..) - is_o = MkIsAlloc(o, o_ty, h, ModeledAsBoxType(baseType)); + var isAlloc = MkIsAlloc(o, typeTerm, h, ModeledAsBoxType(baseType)); + Bpl.Expr body; if (baseType.IsNumericBased() || baseType.IsBitVectorType || baseType.IsBoolType || baseType.IsCharType) { - body = is_o; + body = isAlloc; } else { Bpl.Expr rhs = MkIsAlloc(o, baseType, h); - body = BplIff(is_o, rhs); + body = BplIff(isAlloc, rhs); } + + var comment = $"$IsAlloc axiom for {dd.WhatKind} {fullName}"; + var axiom = new Bpl.Axiom(dd.tok, BplForall(vars, BplTrigger(isAlloc), body), comment); + AddOtherDefinition(GetOrCreateTypeConstructor(dd), axiom); + } else { - comment = $"$Is axiom for {dd.WhatKind} {fullName}"; // $Is(o, ..) - is_o = MkIs(o, o_ty, ModeledAsBoxType(baseType)); + var isPredicate = MkIs(o, typeTerm, ModeledAsBoxType(baseType)); var etran = new ExpressionTranslator(this, predef, NewOneHeapExpr(dd.tok), null); - Bpl.Expr parentConstraint, constraint; + Bpl.Expr parentConstraint; + Expression condition; if (baseType.IsNumericBased() || baseType.IsBitVectorType || baseType.IsBoolType || baseType.IsCharType) { // optimize this to only use the numeric/bitvector constraint, not the whole $Is thing on the base type parentConstraint = Bpl.Expr.True; var udt = UserDefinedType.FromTopLevelDecl(dd.tok, dd); - var substitutee = Expression.CreateIdentExpr(dd.Var ?? c); - constraint = etran.TrExpr(ModuleResolver.GetImpliedTypeConstraint(substitutee, udt)); + var idExpr = Expression.CreateIdentExpr(dd.Var ?? c); + condition = ModuleResolver.GetImpliedTypeConstraint(idExpr, udt); } else { parentConstraint = MkIs(o, baseType); - // conjoin the constraint - constraint = etran.TrExpr(dd.Constraint ?? Expression.CreateBoolLiteral(dd.tok, true)); + condition = dd.Constraint ?? Expression.CreateBoolLiteral(dd.tok, true); } - body = BplIff(is_o, BplAnd(parentConstraint, constraint)); - } - var axiom = new Bpl.Axiom(dd.tok, BplForall(vars, BplTrigger(is_o), body), comment); - AddOtherDefinition(GetOrCreateTypeConstructor(dd), axiom); + var constraintCanCall = etran.CanCallAssumption(condition); + var canCallIsJustTrue = constraintCanCall == Bpl.Expr.True; + var constraint = etran.TrExpr(condition); + var comment = $"$Is axiom{(canCallIsJustTrue ? "" : "s")} for {dd.WhatKind} {fullName}"; + + var rhs = BplAnd(parentConstraint, BplAnd(constraintCanCall, constraint)); + var body = canCallIsJustTrue ? BplIff(isPredicate, rhs) : BplImp(isPredicate, rhs); + var axiom = new Bpl.Axiom(dd.tok, BplForall(vars, BplTrigger(isPredicate), body), comment); + AddOtherDefinition(GetOrCreateTypeConstructor(dd), axiom); + + if (!canCallIsJustTrue) { + body = BplImp(BplAnd(parentConstraint, BplImp(constraintCanCall, constraint)), isPredicate); + axiom = new Bpl.Axiom(dd.tok, BplForall(vars, BplTrigger(isPredicate), body), null); + AddOtherDefinition(GetOrCreateTypeConstructor(dd), axiom); + } + } } From 8bba9a1a8dbafe4bbc287c86e8fc725ccbbb7f34 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 22 Jul 2024 11:51:13 -0700 Subject: [PATCH 032/151] Update order of error messages in expect file --- .../LitTests/LitTest/git-issues/git-issue-2299.dfy.expect | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-2299.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-2299.dfy.expect index 802b5ada1dc..101c6a5a397 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-2299.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-2299.dfy.expect @@ -7,13 +7,13 @@ git-issue-2299.dfy(21,4): Related location: this proposition could not be proved git-issue-2299.dfy(67,13): Error: assertion might not hold git-issue-2299.dfy(21,4): Related location: this proposition could not be proved git-issue-2299.dfy(81,11): Error: assertion might not hold -git-issue-2299.dfy(27,4): Related location: this proposition could not be proved -git-issue-2299.dfy(10,11): Related location: this proposition could not be proved +git-issue-2299.dfy(27,18): Related location: this proposition could not be proved +git-issue-2299.dfy(16,4): Related location: this proposition could not be proved git-issue-2299.dfy(81,11): Error: assertion might not hold git-issue-2299.dfy(27,32): Related location: this proposition could not be proved git-issue-2299.dfy(21,4): Related location: this proposition could not be proved git-issue-2299.dfy(81,11): Error: assertion might not hold -git-issue-2299.dfy(27,18): Related location: this proposition could not be proved -git-issue-2299.dfy(16,4): Related location: this proposition could not be proved +git-issue-2299.dfy(27,4): Related location: this proposition could not be proved +git-issue-2299.dfy(10,11): Related location: this proposition could not be proved Dafny program verifier finished with 7 verified, 7 errors From 567c6670e34f6056b4667b2b45a17bee8ad93583 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 22 Jul 2024 14:04:21 -0700 Subject: [PATCH 033/151] Remove a redundant test file --- .../LitTest/git-issues/git-issue-370b.dfy | 47 ------------------- .../git-issues/git-issue-370b.dfy.expect | 17 ------- 2 files changed, 64 deletions(-) delete mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-370b.dfy delete mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-370b.dfy.expect diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-370b.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-370b.dfy deleted file mode 100644 index f6f30563061..00000000000 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-370b.dfy +++ /dev/null @@ -1,47 +0,0 @@ -// RUN: %testDafnyForEachResolver --expect-exit-code=4 "%s" - - -// Issue 370 reported a situation where there were 6 errors, but only 5 of -// them are reported, since there's a limit of 5 error messages per method. -// The confusing part was that the omitted error was the first of the 6, -// which makes it appear as if there's no problem with postcondition -// WellFormed(t) of foo(). While the ordering of error messages is not -// guaranteed to be deterministic, the current behavior for this test happens -// to be good, so this test file is meant to alert us to any changes, in -// case we then want to revisit this issue in some way. - -datatype T = T(x: int) -datatype S = S(u: int, v: int, w: int, x: int, y: int, z: int) - -predicate a(t: T) - -predicate WellFormed(t: T) { - && a(t) -} - -function Func(t: T): S - requires WellFormed(t) // Note, there should be NO complaint about this precondition in foo() below. -{ - S(t.x, t.x, t.x, t.x, t.x, t.x) -} - -predicate Good(s: S) { - && s.u == 5 - && s.v == 5 - && s.w == 5 - && s.x == 5 - && s.y == 5 - && s.z == 5 -} - -opaque function GetT(): T { - T(5) -} - -lemma foo() - ensures var t := GetT(); - && WellFormed(t) // error (1x) - && Good(Func(t)) // error (5x, but only 4 of these are reported, due to the limit of 5 errors per method) -{ - reveal GetT(); -} diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-370b.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-370b.dfy.expect deleted file mode 100644 index 15d49a042a5..00000000000 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-370b.dfy.expect +++ /dev/null @@ -1,17 +0,0 @@ -git-issue-370b.dfy(45,0): Error: a postcondition could not be proved on this return path -git-issue-370b.dfy(43,7): Related location: this is the postcondition that could not be proved -git-issue-370b.dfy(19,5): Related location: this proposition could not be proved -git-issue-370b.dfy(45,0): Error: a postcondition could not be proved on this return path -git-issue-370b.dfy(44,7): Related location: this is the postcondition that could not be proved -git-issue-370b.dfy(29,9): Related location: this proposition could not be proved -git-issue-370b.dfy(45,0): Error: a postcondition could not be proved on this return path -git-issue-370b.dfy(44,7): Related location: this is the postcondition that could not be proved -git-issue-370b.dfy(30,9): Related location: this proposition could not be proved -git-issue-370b.dfy(45,0): Error: a postcondition could not be proved on this return path -git-issue-370b.dfy(44,7): Related location: this is the postcondition that could not be proved -git-issue-370b.dfy(31,9): Related location: this proposition could not be proved -git-issue-370b.dfy(45,0): Error: a postcondition could not be proved on this return path -git-issue-370b.dfy(44,7): Related location: this is the postcondition that could not be proved -git-issue-370b.dfy(32,9): Related location: this proposition could not be proved - -Dafny program verifier finished with 1 verified, 5 errors From 7db4ffca476eb28f9d6b4b845e4906049edb626c Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 22 Jul 2024 14:04:31 -0700 Subject: [PATCH 034/151] Adjust test output --- .../LitTest/git-issues/git-issue-370.dfy | 9 ++++++--- .../git-issues/git-issue-370.dfy.expect | 20 ++++--------------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-370.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-370.dfy index 662b94a697e..f81c4baef3b 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-370.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-370.dfy @@ -10,6 +10,9 @@ // to be good, so this test file is meant to alert us to any changes, in // case we then want to revisit this issue in some way. +// UPDATE: With the full implementation of CanCall, this test gives just +// 1 error. + datatype T = T(x: int) datatype S = S(u: int, v: int, w: int, x: int, y: int, z: int) @@ -34,14 +37,14 @@ ghost predicate Good(s: S) { && s.z == 5 } -ghost function {:opaque} GetT(): T { +opaque ghost function GetT(): T { T(5) } lemma foo() ensures var t := GetT(); - && WellFormed(t) // error (1x) - && Good(Func(t)) // error (5x, but only 4 of these are reported, due to the limit of 5 errors per method) + && WellFormed(t) // error + && Good(Func(t)) { reveal GetT(); } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-370.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-370.dfy.expect index 6b5ee927f45..cac47f16163 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-370.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-370.dfy.expect @@ -1,17 +1,5 @@ -git-issue-370.dfy(45,0): Error: a postcondition could not be proved on this return path -git-issue-370.dfy(43,7): Related location: this is the postcondition that could not be proved -git-issue-370.dfy(19,5): Related location: this proposition could not be proved -git-issue-370.dfy(45,0): Error: a postcondition could not be proved on this return path -git-issue-370.dfy(44,7): Related location: this is the postcondition that could not be proved -git-issue-370.dfy(29,9): Related location: this proposition could not be proved -git-issue-370.dfy(45,0): Error: a postcondition could not be proved on this return path -git-issue-370.dfy(44,7): Related location: this is the postcondition that could not be proved -git-issue-370.dfy(30,9): Related location: this proposition could not be proved -git-issue-370.dfy(45,0): Error: a postcondition could not be proved on this return path -git-issue-370.dfy(44,7): Related location: this is the postcondition that could not be proved -git-issue-370.dfy(31,9): Related location: this proposition could not be proved -git-issue-370.dfy(45,0): Error: a postcondition could not be proved on this return path -git-issue-370.dfy(44,7): Related location: this is the postcondition that could not be proved -git-issue-370.dfy(32,9): Related location: this proposition could not be proved +git-issue-370.dfy(48,0): Error: a postcondition could not be proved on this return path +git-issue-370.dfy(46,7): Related location: this is the postcondition that could not be proved +git-issue-370.dfy(22,5): Related location: this proposition could not be proved -Dafny program verifier finished with 1 verified, 5 errors +Dafny program verifier finished with 1 verified, 1 error From e5c91dd1837f0d5f39fc5dea51cdf7d8a1678f9f Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 22 Jul 2024 16:14:08 -0700 Subject: [PATCH 035/151] Fix parameters to CanCall function in override axiom --- .../Verifier/BoogieGenerator.Methods.cs | 32 ++++++++++++------- .../LitTest/git-issues/git-issue-3995.dfy | 15 ++++++++- .../git-issues/git-issue-3995.dfy.expect | 2 +- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index fe3bd28b132..8a652282c01 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -1269,14 +1269,23 @@ private Boogie.Axiom FunctionOverrideAxiom(Function f, Function overridingFuncti // argsCF are the arguments to C.F (the overriding function) var forallFormals = new List(); var argsJF = new List(); + var argsJFCanCall = new List(); var argsCF = new List(); + var argsCFCanCall = new List(); // Add type arguments forallFormals.AddRange(MkTyParamBinders(GetTypeParams(overridingFunction), out _)); - argsJF.AddRange(GetTypeArguments(f, overridingFunction).ConvertAll(TypeToTy)); - argsCF.AddRange(GetTypeArguments(overridingFunction, null).ConvertAll(TypeToTy)); + { + var typeArguments = GetTypeArguments(f, overridingFunction).ConvertAll(TypeToTy); + argsJF.AddRange(typeArguments); + argsJFCanCall.AddRange(typeArguments); + typeArguments = GetTypeArguments(overridingFunction, null).ConvertAll(TypeToTy); + argsCF.AddRange(typeArguments); + argsCFCanCall.AddRange(typeArguments); + } - var moreArgsCF = new List(); + var moreArgsJF = new List(); // non-type-parameters, non-fuel, non-reveal arguments + var moreArgsCF = new List(); // non-type-parameters, non-fuel, non-reveal arguments Expr layer = null; Expr reveal = null; @@ -1307,14 +1316,14 @@ private Boogie.Axiom FunctionOverrideAxiom(Function f, Function overridingFuncti if (f is TwoStateFunction) { Contract.Assert(bvPrevHeap != null); forallFormals.Add(bvPrevHeap); - argsJF.Add(etran.Old.HeapExpr); + moreArgsJF.Add(etran.Old.HeapExpr); moreArgsCF.Add(etran.Old.HeapExpr); } if (f.ReadsHeap || overridingFunction.ReadsHeap) { var heap = new Boogie.BoundVariable(f.tok, new Boogie.TypedIdent(f.tok, predef.HeapVarName, predef.HeapType)); forallFormals.Add(heap); if (f.ReadsHeap) { - argsJF.Add(new Boogie.IdentifierExpr(f.tok, heap)); + moreArgsJF.Add(new Boogie.IdentifierExpr(f.tok, heap)); } if (overridingFunction.ReadsHeap) { moreArgsCF.Add(new Boogie.IdentifierExpr(overridingFunction.tok, heap)); @@ -1326,7 +1335,7 @@ private Boogie.Axiom FunctionOverrideAxiom(Function f, Function overridingFuncti var bvThis = new Boogie.BoundVariable(f.tok, new Boogie.TypedIdent(f.tok, etran.This, TrType(thisType))); forallFormals.Add(bvThis); var bvThisExpr = new Boogie.IdentifierExpr(f.tok, bvThis); - argsJF.Add(BoxifyForTraitParent(f.tok, bvThisExpr, f, thisType)); + moreArgsJF.Add(BoxifyForTraitParent(f.tok, bvThisExpr, f, thisType)); moreArgsCF.Add(bvThisExpr); // $Is(this, C) var isOfSubtype = GetWhereClause(overridingFunction.tok, bvThisExpr, thisType, f is TwoStateFunction ? etran.Old : etran, @@ -1341,7 +1350,7 @@ private Boogie.Axiom FunctionOverrideAxiom(Function f, Function overridingFuncti var bv = new Boogie.BoundVariable(p.tok, new Boogie.TypedIdent(p.tok, p.AssignUniqueName(currentDeclaration.IdGenerator), TrType(pType))); forallFormals.Add(bv); var jfArg = new Boogie.IdentifierExpr(p.tok, bv); - argsJF.Add(ModeledAsBoxType(p.Type) ? BoxIfNotNormallyBoxed(p.tok, jfArg, pType) : jfArg); + moreArgsJF.Add(ModeledAsBoxType(p.Type) ? BoxIfNotNormallyBoxed(p.tok, jfArg, pType) : jfArg); moreArgsCF.Add(new Boogie.IdentifierExpr(p.tok, bv)); } @@ -1362,7 +1371,10 @@ private Boogie.Axiom FunctionOverrideAxiom(Function f, Function overridingFuncti argsCF.Add(reveal); } + argsJF = Concat(argsJF, moreArgsJF); + argsJFCanCall = Concat(argsJFCanCall, moreArgsJF); argsCF = Concat(argsCF, moreArgsCF); + argsCFCanCall = Concat(argsCFCanCall, moreArgsCF); // ante := useViaCanCall || (useViaContext && this != null && $Is(this, C)) ante = BplOr(useViaCanCall, BplAnd(useViaContext, ante)); @@ -1396,11 +1408,9 @@ private Boogie.Axiom FunctionOverrideAxiom(Function f, Function overridingFuncti ModeledAsBoxType(f.ResultType) ? BoxIfNotNormallyBoxed(overridingFunction.tok, overridingFuncAppl, overridingFunction.ResultType) : overridingFuncAppl); // add overridingFunction#canCall ==> f#canCall to the axiom var callName = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool); - var callArgs = f.IsFuelAware() ? argsJF.TakeLast(argsJF.Count() - 1).ToList() : argsJF; - var canCallFunc = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(callName), callArgs); + var canCallFunc = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(callName), argsJFCanCall); callName = new Bpl.IdentifierExpr(overridingFunction.tok, overridingFunction.FullSanitizedName + "#canCall", Bpl.Type.Bool); - callArgs = overridingFunction.IsFuelAware() ? argsCF.TakeLast(argsCF.Count() - 1).ToList() : argsCF; - var canCallOverridingFunc = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(callName), callArgs); + var canCallOverridingFunc = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(callName), argsCFCanCall); var canCallImp = BplImp(canCallFunc, canCallOverridingFunc); // The axiom diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3995.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3995.dfy index fa2d4210fd6..6ccf8a0b99c 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3995.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3995.dfy @@ -32,4 +32,17 @@ class C extends T { predicate {:opaque} Valid(x: int) { true } method MyMethod(x: int) requires Valid(x) { } -} \ No newline at end of file +} + +// The following two types make sure that the override axiom is produced with the correct +// parameters to the CanCall function. + +trait TT { + opaque predicate Valid(x: int, y: Y) +} + +class CC extends TT { + opaque predicate Valid(x: int, z: Z) { + if 0 < x then Valid(x - 1, z) else true + } +} diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3995.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3995.dfy.expect index ccc01c35f48..023f9ca2415 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3995.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3995.dfy.expect @@ -1,2 +1,2 @@ -Dafny program verifier finished with 11 verified, 0 errors +Dafny program verifier finished with 13 verified, 0 errors From 1379380c2c40644c8498106d8bb6d17b808ea745 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 23 Jul 2024 13:17:47 -0700 Subject: [PATCH 036/151] Change override axiom to use CanCall --- .../Verifier/BoogieGenerator.Functions.cs | 8 ----- .../Verifier/BoogieGenerator.Methods.cs | 35 +++++++++---------- 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs index fe7a6ca3704..8ed471b9897 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs @@ -834,14 +834,6 @@ private Axiom GetFunctionAxiom(Function f, Expression body, List lits) { return null; } - // useViaContext: fh < FunctionContextHeight - ModuleDefinition mod = f.EnclosingClass.EnclosingModuleDefinition; - Bpl.Expr useViaContext = !InVerificationScope(f) - ? Bpl.Expr.True - : Bpl.Expr.Lt(Bpl.Expr.Literal(forModule.CallGraph.GetSCCRepresentativePredecessorCount(f)), etran.FunctionContextHeight()); - // ante := (useViaContext && typeAnte && pre) - ante = BplAnd(useViaContext, BplAnd(ante, pre)); - // useViaCanCall: f#canCall(args) ante = useViaCanCall; diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 8a652282c01..8297295048d 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -1231,8 +1231,9 @@ private void AddFunctionOverrideReqsChk(Function f, BoogieStmtListBuilder builde /// axiom (forall $heap: HeapType, typeArgs: Ty, this: ref, x#0: int, fuel: LayerType :: /// { J.F(fuel, $heap, G(typeArgs), this, x#0), C.F(fuel, $heap, typeArgs, this, x#0) } /// { J.F(fuel, $heap, G(typeArgs), this, x#0), $Is(this, C) } - /// C.F#canCall(args) || (fh < FunctionContextHeight && this != null && $Is(this, C)) + /// C.F#canCall(args) || (J.F#canCall(args) && $Is(this, C)) /// ==> + /// (J.F#canCall(args) ==> C.F#canCall(args)) && /// J.F(fuel, $heap, G(typeArgs), this, x#0) == C.F(fuel, $heap, typeArgs, this, x#0)); /// (without the other usual antecedents). Essentially, the override gives a part of the body of the /// trait's function, so we call FunctionAxiom to generate a conditional axiom (that is, we pass in the "overridingFunction" @@ -1341,8 +1342,6 @@ private Boogie.Axiom FunctionOverrideAxiom(Function f, Function overridingFuncti var isOfSubtype = GetWhereClause(overridingFunction.tok, bvThisExpr, thisType, f is TwoStateFunction ? etran.Old : etran, IsAllocType.NEVERALLOC, alwaysUseSymbolicName: true); - Bpl.Expr ante = BplAnd(ReceiverNotNull(bvThisExpr), isOfSubtype); - // Add other arguments var typeMap = GetTypeArgumentSubstitutionMap(f, overridingFunction); foreach (Formal p in f.Ins) { @@ -1354,15 +1353,6 @@ private Boogie.Axiom FunctionOverrideAxiom(Function f, Function overridingFuncti moreArgsCF.Add(new Boogie.IdentifierExpr(p.tok, bv)); } - // useViaContext: fh < FunctionContextHeight - Boogie.Expr useViaContext = !InVerificationScope(overridingFunction) - ? Boogie.Expr.True - : Boogie.Expr.Lt(Boogie.Expr.Literal(forModule.CallGraph.GetSCCRepresentativePredecessorCount(overridingFunction)), etran.FunctionContextHeight()); - - // useViaCanCall: C.F#canCall(args) - Bpl.IdentifierExpr canCallFuncID = new Bpl.IdentifierExpr(overridingFunction.tok, overridingFunction.FullSanitizedName + "#canCall", Bpl.Type.Bool); - Bpl.Expr useViaCanCall = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(canCallFuncID), Concat(argsCF, moreArgsCF)); - if (layer != null) { argsCF.Add(layer); } @@ -1376,8 +1366,19 @@ private Boogie.Axiom FunctionOverrideAxiom(Function f, Function overridingFuncti argsCF = Concat(argsCF, moreArgsCF); argsCFCanCall = Concat(argsCFCanCall, moreArgsCF); - // ante := useViaCanCall || (useViaContext && this != null && $Is(this, C)) - ante = BplOr(useViaCanCall, BplAnd(useViaContext, ante)); + Bpl.Expr canCallFunc, canCallOverridingFunc; + { + var callName = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool); + canCallFunc = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(callName), argsJFCanCall); + callName = new Bpl.IdentifierExpr(overridingFunction.tok, overridingFunction.FullSanitizedName + "#canCall", Bpl.Type.Bool); + canCallOverridingFunc = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(callName), argsCFCanCall); + } + + // useViaCanCall: C.F#canCall(args) + Bpl.Expr useViaCanCall = canCallFunc; + + // ante := C.F#canCall(args) || (J.F#canCall(args) && $Is(this, C)) + var ante = BplOr(canCallOverridingFunc, BplAnd(canCallFunc, isOfSubtype)); Boogie.Expr funcAppl; { @@ -1407,17 +1408,13 @@ private Boogie.Axiom FunctionOverrideAxiom(Function f, Function overridingFuncti funcAppl, ModeledAsBoxType(f.ResultType) ? BoxIfNotNormallyBoxed(overridingFunction.tok, overridingFuncAppl, overridingFunction.ResultType) : overridingFuncAppl); // add overridingFunction#canCall ==> f#canCall to the axiom - var callName = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool); - var canCallFunc = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(callName), argsJFCanCall); - callName = new Bpl.IdentifierExpr(overridingFunction.tok, overridingFunction.FullSanitizedName + "#canCall", Bpl.Type.Bool); - var canCallOverridingFunc = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(callName), argsCFCanCall); var canCallImp = BplImp(canCallFunc, canCallOverridingFunc); // The axiom Boogie.Expr ax = BplForall(f.tok, new List(), forallFormals, null, tr, BplImp(ante, BplAnd(canCallImp, synonyms))); var activate = AxiomActivation(overridingFunction, etran); - string comment = "override axiom for " + f.FullSanitizedName + " in class " + overridingFunction.EnclosingClass.FullSanitizedName; + var comment = $"override axiom for {f.FullSanitizedName} in {overridingFunction.EnclosingClass.WhatKind} {overridingFunction.EnclosingClass.FullSanitizedName}"; return new Boogie.Axiom(f.tok, BplImp(activate, ax), comment); } From 3a8307ea30b2b8c1b94e999b86fdf3c7fab3dc6c Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 23 Jul 2024 13:30:49 -0700 Subject: [PATCH 037/151] Adjust resource limit (down) to make test pass --- .../TestFiles/LitTests/LitTest/git-issues/github-issue-4804.dfy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/github-issue-4804.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/github-issue-4804.dfy index 35dc939d8eb..2c856483240 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/github-issue-4804.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/github-issue-4804.dfy @@ -1,4 +1,4 @@ -// RUN: %exits-with 4 %verify --allow-axioms --resource-limit 1000000 %s > %t +// RUN: %exits-with 4 %verify --allow-axioms --resource-limit 10000 %s > %t // RUN: %diff "%s.expect" "%t" module Power { From a9b195a342353ffb84cb44829a87fd139bcd5663 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 29 Jul 2024 17:42:28 -0700 Subject: [PATCH 038/151] Fix typo in comments --- .../TestFiles/LitTests/LitTest/dafny0/Datatypes.dfy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Datatypes.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Datatypes.dfy index 6d3f2849253..eed09adb30c 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Datatypes.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Datatypes.dfy @@ -399,7 +399,7 @@ module Exhaustiveness { } else if c == B { } else if c == C { } else { - assert false; // used to fails :(, but now works :) + assert false; // used to fail :(, but now works :) } } @@ -418,7 +418,7 @@ module Exhaustiveness { { var c := s[i]; if c != A && c != B && c != C { - assert false; // used to fails :(, but now works :) + assert false; // used to fail :(, but now works :) } } From afdb5c5d995319797d70ded616fff8dc25cc732f Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 29 Jul 2024 17:48:44 -0700 Subject: [PATCH 039/151] Add workaround for incorrect partial-arrow constraint --- Source/DafnyCore/Verifier/BoogieGenerator.Types.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs index 1e8b0649aa3..30de86616e5 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs @@ -1027,6 +1027,12 @@ void AddRedirectingTypeDeclAxioms(bool generateIsAlloc, T dd, string fullName } var constraintCanCall = etran.CanCallAssumption(condition); + if (ArrowType.IsPartialArrowTypeName(dd.Name)) { + // Hack for now. TODO: The resolver currently sets up the constraint of a partial arrow as being + // a total arrow such that "forall bx: Box :: f(bx) == {}". However, it ought to be + // "forall bx: Box :: f.requires(bx) ==> f(bx) == {}". When that gets fixed, the hack here is no longer needed. + constraintCanCall = Bpl.Expr.True; + } var canCallIsJustTrue = constraintCanCall == Bpl.Expr.True; var constraint = etran.TrExpr(condition); var comment = $"$Is axiom{(canCallIsJustTrue ? "" : "s")} for {dd.WhatKind} {fullName}"; From 6a1b106c4faeea38d848984b35da7cebd4e0a2a2 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 30 Sep 2024 19:21:00 -0400 Subject: [PATCH 040/151] update resource count, this is positive --- .../TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect index a51243d4611..4ed2a4d28a4 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect @@ -1,4 +1,4 @@ Dafny program verifier finished with 272 verified, 0 errors -Total resources used is 30906777 -Max resources used by VC is 2048364 +Total resources used is 28512831 +Max resources used by VC is 1155634 From 349a2eaa4b8c6fb44cbe243785c71d64e543d982 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 2 Oct 2024 17:36:34 -0700 Subject: [PATCH 041/151] Fix bad refactoring --- .../DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index 696008e4e0f..50a30add394 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -2272,7 +2272,7 @@ public Expr CanCallAssumption(Expression expr, CanCallOptions cco = null) { case BinaryExpr.ResolvedOpcode.EqCommon: case BinaryExpr.ResolvedOpcode.NeqCommon: { Boogie.Expr r = Boogie.Expr.True; - if (cco?.SkipIsA == true) { + if (cco is not { SkipIsA: true }) { if (e.E0 is { Type: { AsDatatype: { } dt0 }, Resolved: not DatatypeValue }) { var funcID = new Boogie.FunctionCall(new Boogie.IdentifierExpr(expr.tok, "$IsA#" + dt0.FullSanitizedName, Boogie.Type.Bool)); r = BplAnd(r, new Boogie.NAryExpr(expr.tok, funcID, new List { TrExpr(e.E0) })); From 5cee693f313517757c93dbbe25c6706fae5a6546 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 2 Oct 2024 17:37:09 -0700 Subject: [PATCH 042/151] Adjust test and answer --- .../LitTests/LitTest/dafny0/FunctionSpecifications.dfy | 8 ++++---- .../LitTest/dafny0/FunctionSpecifications.dfy.expect | 4 +--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/FunctionSpecifications.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/FunctionSpecifications.dfy index d8da69c7837..e661001140d 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/FunctionSpecifications.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/FunctionSpecifications.dfy @@ -36,9 +36,8 @@ ghost function SumBad(a: List): int } ghost function FibWithExtraPost(n: int): int - ensures 2 <= n ==> 0 <= FibWithExtraPost(n-1) // This is fine, because the definition of the function is discovered via canCall - ensures 1 <= n ==> 0 <= FibWithExtraPost(n-1) // Error: In the current implementation of Dafny, one needs to actually call the - // function in order to benefit from canCall. This may be improved in the future. + ensures 2 <= n ==> 0 <= FibWithExtraPost(n-1) // This tests that Dafny generates the appropriate CanCall + ensures 1 <= n ==> 0 <= FibWithExtraPost(n-1) // Same here. (Before CanCalls everywhere, this postcondition was not verified.) ensures 0 <= FibWithExtraPost(n) { if n < 0 then 0 else @@ -46,12 +45,13 @@ ghost function FibWithExtraPost(n: int): int FibWithExtraPost(n-2) + FibWithExtraPost(n-1) } + ghost function GoodPost(n: int): int requires 0 <= n ensures 1 <= n ==> GoodPost(n-1) == GoodPost(n-1) ensures GoodPost(2*n - n) == GoodPost(2*(n+5) - 10 - n) // these are legal ways to denote the result value of the function { - assert 2*n - n == 2*(n+5) - 10 - n; + assert n == 2*n - n == 2*(n+5) - 10 - n; if n < 2 then n else GoodPost(n-2) + GoodPost(n-1) } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/FunctionSpecifications.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/FunctionSpecifications.dfy.expect index b20a56e2f1c..ccbfac16cf3 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/FunctionSpecifications.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/FunctionSpecifications.dfy.expect @@ -1,7 +1,5 @@ FunctionSpecifications.dfy(35,45): Error: a postcondition could not be proved on this return path FunctionSpecifications.dfy(31,12): Related location: this is the postcondition that could not be proved -FunctionSpecifications.dfy(45,16): Error: a postcondition could not be proved on this return path -FunctionSpecifications.dfy(40,23): Related location: this is the postcondition that could not be proved FunctionSpecifications.dfy(61,10): Error: cannot prove termination; try supplying a decreases clause FunctionSpecifications.dfy(71,4): Error: a postcondition could not be proved on this return path FunctionSpecifications.dfy(69,21): Related location: this is the postcondition that could not be proved @@ -15,4 +13,4 @@ FunctionSpecifications.dfy(155,2): Error: decreases clause might not decrease FunctionSpecifications.dfy(162,2): Error: decreases clause might not decrease FunctionSpecifications.dfy(167,2): Error: cannot prove termination; try supplying a decreases clause -Dafny program verifier finished with 10 verified, 12 errors +Dafny program verifier finished with 11 verified, 11 errors From 0a458140031b8d2c3a86de267015cf3b8bdcc0de Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 2 Oct 2024 18:25:42 -0700 Subject: [PATCH 043/151] Add CanCall assumptions in short-circuit WF-result checks Test case that discovered problem: dafny0/MoForallCompilation.dfy --- .../DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 9fb971e0326..b5956faede3 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -158,6 +158,7 @@ public void CheckWellformedAndAssume(Expression expr, WFOptions wfOptions, List< CheckWellformedAndAssume(e.E0, wfOptions, locals, bAnd, etran, comment); CheckWellformedAndAssume(e.E1, wfOptions, locals, bAnd, etran, comment); var bImp = new BoogieStmtListBuilder(this, options, builder.Context); + bImp.Add(TrAssumeCmd(expr.tok, etran.CanCallAssumption(expr))); bImp.Add(TrAssumeCmdWithDependencies(etran, expr.tok, expr, comment)); builder.Add(new Bpl.IfCmd(expr.tok, null, bAnd.Collect(expr.tok), null, bImp.Collect(expr.tok))); } @@ -172,6 +173,7 @@ public void CheckWellformedAndAssume(Expression expr, WFOptions wfOptions, List< var b0 = new BoogieStmtListBuilder(this, options, builder.Context); CheckWellformedAndAssume(e.E0, wfOptions, locals, b0, etran, comment); var b1 = new BoogieStmtListBuilder(this, options, builder.Context); + b1.Add(TrAssumeCmd(expr.tok, etran.CanCallAssumption(e.E0))); b1.Add(TrAssumeCmdWithDependenciesAndExtend(etran, expr.tok, e.E0, Expr.Not, comment)); CheckWellformedAndAssume(e.E1, wfOptions, locals, b1, etran, comment); builder.Add(new Bpl.IfCmd(expr.tok, null, b0.Collect(expr.tok), null, b1.Collect(expr.tok))); @@ -193,6 +195,7 @@ public void CheckWellformedAndAssume(Expression expr, WFOptions wfOptions, List< CheckWellformedAndAssume(e.Test, wfOptions, locals, bThn, etran, comment); CheckWellformedAndAssume(e.Thn, wfOptions, locals, bThn, etran, comment); var bEls = new BoogieStmtListBuilder(this, options, builder.Context); + bEls.Add(TrAssumeCmd(expr.tok, etran.CanCallAssumption(e.Test))); bEls.Add(TrAssumeCmdWithDependenciesAndExtend(etran, expr.tok, e.Test, Expr.Not, comment)); CheckWellformedAndAssume(e.Els, wfOptions, locals, bEls, etran, comment); builder.Add(new Bpl.IfCmd(expr.tok, null, bThn.Collect(expr.tok), null, bEls.Collect(expr.tok))); From 8973a0aa751be827ea9f561158b7715180460185 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 3 Oct 2024 11:50:47 -0700 Subject: [PATCH 044/151] Fix implementations of Disjuncts and Conjuncts --- .../DafnyCore/AST/Expressions/Expression.cs | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/Source/DafnyCore/AST/Expressions/Expression.cs b/Source/DafnyCore/AST/Expressions/Expression.cs index cb52f3fee16..9357006fc13 100644 --- a/Source/DafnyCore/AST/Expressions/Expression.cs +++ b/Source/DafnyCore/AST/Expressions/Expression.cs @@ -170,15 +170,27 @@ public static IEnumerable ConjunctsWithLetsOnOutside(Expression expr } } + /// + /// Return the negation of each of the expressions in "expressions". + /// If there is just one expression in "expressions", then use the given token "tok" for the negation. + /// Otherwise, use the token from each expression. + /// + static IEnumerable NegateEach(IToken tok, IEnumerable expressions) { + var exprs = expressions.ToList(); + foreach (Expression e in exprs) { + yield return Expression.CreateNot(exprs.Count == 1 ? tok : e.tok, e); + } + } + public static IEnumerable Conjuncts(Expression expr) { Contract.Requires(expr != null); Contract.Requires(expr.Type.IsBoolType); Contract.Ensures(cce.NonNullElements(Contract.Result>())); expr = StripParens(expr); - if (expr is UnaryOpExpr unary && unary.Op == UnaryOpExpr.Opcode.Not) { - foreach (Expression e in Disjuncts(unary.E)) { - yield return Expression.CreateNot(e.tok, e); + if (expr is UnaryOpExpr { Op: UnaryOpExpr.Opcode.Not } unary) { + foreach (Expression e in NegateEach(expr.tok, Disjuncts(unary.E))) { + yield return e; } yield break; @@ -203,18 +215,18 @@ public static IEnumerable Disjuncts(Expression expr) { Contract.Ensures(cce.NonNullElements(Contract.Result>())); expr = StripParens(expr); - if (expr is UnaryOpExpr unary && unary.Op == UnaryOpExpr.Opcode.Not) { - foreach (Expression e in Conjuncts(unary.E)) { - yield return Expression.CreateNot(e.tok, e); + if (expr is UnaryOpExpr { Op: UnaryOpExpr.Opcode.Not } unary) { + foreach (Expression e in NegateEach(expr.tok, Conjuncts(unary.E))) { + yield return e; } yield break; } else if (expr is BinaryExpr bin) { if (bin.ResolvedOp == BinaryExpr.ResolvedOpcode.Or) { - foreach (Expression e in Conjuncts(bin.E0)) { + foreach (Expression e in Disjuncts(bin.E0)) { yield return e; } - foreach (Expression e in Conjuncts(bin.E1)) { + foreach (Expression e in Disjuncts(bin.E1)) { yield return e; } yield break; @@ -222,7 +234,7 @@ public static IEnumerable Disjuncts(Expression expr) { foreach (Expression e in Conjuncts(bin.E0)) { yield return Expression.CreateNot(e.tok, e); } - foreach (Expression e in Conjuncts(bin.E1)) { + foreach (Expression e in Disjuncts(bin.E1)) { yield return e; } yield break; From 9ec69b27080dde1662bc1206612768dd90f94c32 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 3 Oct 2024 14:33:28 -0700 Subject: [PATCH 045/151] =?UTF-8?q?Change=20\d=20to=20[0-9],=20since=20the?= =?UTF-8?q?=20former=20apparently=20isn=E2=80=99t=20always=20supported?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TestFiles/LitTests/LitTest/git-issues/git-issue-3855.dfy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3855.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3855.dfy index d1bc619d003..827318ba2a7 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3855.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-3855.dfy @@ -1,5 +1,5 @@ // RUN: %exits-with 4 %baredafny verify --show-snippets:false --allow-axioms --allow-deprecation --use-basename-for-filename "%s" > "%t".raw -// RUN: %sed 's/after \d+ seconds/after seconds/' %t.raw > "%t" +// RUN: %sed 's/after [0-9]+ seconds/after seconds/' %t.raw > "%t" // RUN: %diff "%s.expect" "%t" // Nearly verbatim copy of the text case given in the issue //SIMULADA From d7a27548006662a8d6836e78be06c1bbc0fa0022 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 8 Oct 2024 16:29:10 -0700 Subject: [PATCH 046/151] Adjust test and answer Without general traits, the new resolver infers the type of `fetch` to be `Ins?`, which causes a null dereference in `run()`. --- .../LitTests/LitTest/git-issues/git-issue-5331.dfy | 14 ++++++++------ .../LitTest/git-issues/git-issue-5331.dfy.expect | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-5331.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-5331.dfy index c3d29769fdd..9780cf41b4a 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-5331.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-5331.dfy @@ -1,17 +1,19 @@ -// RUN: %baredafny verify %args --type-system-refresh "%s" > "%t" +// RUN: %baredafny verify %args --type-system-refresh --general-traits=datatype "%s" > "%t" // RUN: %diff "%s.expect" "%t" trait Ins { - function step(s:State):State //requires safe?(s) + function step(s: State): State } type Code = seq datatype State = S( - clock:nat + clock: nat ) { - function fetch_():Ins + function fetch_(): Ins const fetch := fetch_() const step := fetch.step(this) - function run():State decreases clock { + function run(): State + decreases clock + { if clock == 0 then this else step.(clock := clock - 1).run() } -} \ No newline at end of file +} diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-5331.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-5331.dfy.expect index ebe2328e072..823a60a105c 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-5331.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-5331.dfy.expect @@ -1,2 +1,2 @@ -Dafny program verifier finished with 2 verified, 0 errors +Dafny program verifier finished with 1 verified, 0 errors From b8332aa3e189eccbec69c864834ba2ae700aa91e Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 8 Oct 2024 17:02:50 -0700 Subject: [PATCH 047/151] Avoid triggers with nullary functions Test case: git-issues/git-issue-405.dfy --- .../Verifier/BoogieGenerator.Functions.cs | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs index 871aa85cadd..780d771296f 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs @@ -376,9 +376,9 @@ private Axiom GetFunctionAxiom(Function f, Expression body, List lits) { f.ReadsHeap ? new Bpl.IdentifierExpr(f.tok, predef.HeapVarName, predef.HeapType) : null, new Bpl.IdentifierExpr(f.tok, bvPrevHeap), f); } else { - etran = readsHeap + etran = f.ReadsHeap ? new ExpressionTranslator(this, predef, f.tok, f) - : new ExpressionTranslator(this, predef, (Bpl.Expr)null, f); + : new ExpressionTranslator(this, predef, readsHeap ? NewOneHeapExpr(f.tok) : null, f); } // quantify over the type arguments, and add them first to the arguments @@ -421,26 +421,19 @@ private Axiom GetFunctionAxiom(Function f, Expression body, List lits) { ante = BplAnd(ante, FunctionCall(f.tok, BuiltinFunction.IsGoodHeap, null, etran.Old.HeapExpr)); } - Bpl.Expr goodHeap = null; - var bv = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, predef.HeapVarName, predef.HeapType)); if (f.ReadsHeap) { + var bv = new Bpl.BoundVariable(f.tok, new Bpl.TypedIdent(f.tok, predef.HeapVarName, predef.HeapType)); funcFormals.Add(bv); - } - - if (f.ReadsHeap) { args.Add(new Bpl.IdentifierExpr(f.tok, bv)); reqFuncArguments.Add(new Bpl.IdentifierExpr(f.tok, bv)); - } - // ante: $IsGoodHeap($Heap) && $HeapSucc($prevHeap, $Heap) && this != null && formals-have-the-expected-types && - if (readsHeap) { + // ante: $IsGoodHeap($Heap) && $HeapSucc($prevHeap, $Heap) && this != null && formals-have-the-expected-types && forallFormals.Add(bv); - goodHeap = FunctionCall(f.tok, BuiltinFunction.IsGoodHeap, null, etran.HeapExpr); + var goodHeap = FunctionCall(f.tok, BuiltinFunction.IsGoodHeap, null, etran.HeapExpr); ante = BplAnd(ante, goodHeap); - } - - if (f is TwoStateFunction && f.ReadsHeap) { - ante = BplAnd(ante, HeapSucc(etran.Old.HeapExpr, etran.HeapExpr)); + if (f is TwoStateFunction) { + ante = BplAnd(ante, HeapSucc(etran.Old.HeapExpr, etran.HeapExpr)); + } } // ante: conditions on bounded type parameters @@ -479,7 +472,7 @@ private Axiom GetFunctionAxiom(Function f, Expression body, List lits) { var substMap = new Dictionary(); foreach (Formal p in f.Ins) { var pType = p.Type.Subst(typeMap); - bv = new Bpl.BoundVariable(p.tok, + var bv = new Bpl.BoundVariable(p.tok, new Bpl.TypedIdent(p.tok, p.AssignUniqueName(currentDeclaration.IdGenerator), TrType(pType))); forallFormals.Add(bv); funcFormals.Add(bv); From 09b393088fb85611a2b80751a1bbde05faf96bb7 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 8 Oct 2024 17:27:53 -0700 Subject: [PATCH 048/151] chore: Remove old syntax --- .../LitTests/LitTest/dafny0/Fuel.legacy.dfy | 112 +++++++++--------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy index aef6047d1ea..b3a6a5de9fc 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy @@ -9,8 +9,8 @@ module TestModule1 { } method test(y:int, z:int) - requires y > 5; - requires z < 0; + requires y > 5 + requires z < 0 { assert pos(z) == 0; assert pos(-1) == 0; @@ -46,8 +46,8 @@ module TestModule2 { } method test(y:int, z:int) - requires y > 5; - requires z < 0; + requires y > 5 + requires z < 0 { assert pos1(z) == 0; assert pos1(-1) == 0; @@ -59,13 +59,13 @@ module TestModule2 { assert pos2(y) == 3 + pos2(y - 3); assert pos2(y) == 4 + pos2(y - 4); - if (*) { + if * { assert pos3(y) == 5 + pos3(y - 5); // Just enough fuel to get here } else { assert pos3(y) == 6 + pos3(y - 6); // error: Should fail even with a boost, since boost is too small } - if (*) { + if * { assert pos4(z) == 0; // error: Fuel shouldn't overcome opaque } else { reveal pos4(); @@ -86,8 +86,8 @@ module TestModule3 { } method test(y:int, z:int) - requires y > 5; - requires z < 0; + requires y > 5 + requires z < 0 { assert pos(z) == 0; // error: Opaque setting hides body assert pos(-1) == 0; // error: Lits also obey opaque now @@ -105,8 +105,8 @@ module TestModule4 { // Should pass method {:fuel pos,3} test1(y:int, z:int) - requires y > 5; - requires z < 0; + requires y > 5 + requires z < 0 { assert pos(z) == 0; assert pos(-1) == 0; @@ -114,8 +114,8 @@ module TestModule4 { } method {:fuel pos,0,0} test2(y:int, z:int) - requires y > 5; - requires z < 0; + requires y > 5 + requires z < 0 { assert pos(z) == 0; // error: Should fail due to "opaque" fuel setting assert pos(-1) == 0; // error: Should fail due to "opaque" fuel setting. Even Lits obey opaque @@ -123,12 +123,12 @@ module TestModule4 { } method test3(y:int, z:int) - requires y > 5; - requires z < 0; + requires y > 5 + requires z < 0 { assert {:fuel pos,0,0} pos(z) == 0; // error: fuel can't be decreased assert pos(-1) == 0; - if (*) { + if * { assert pos(y) == 3 + pos(y - 3); // error: Should fail without extra fuel setting assert pos(y) == 6 + pos(y - 6); // error: Should fail even with previous assert turned into assume } else { @@ -138,16 +138,16 @@ module TestModule4 { } method test4(y:int, z:int) - requires y > 5; - requires z < 0; + requires y > 5 + requires z < 0 { forall t:int {:fuel pos,3} | t > 0 - ensures true; + ensures true { assert pos(y) == 3 + pos(y - 3); // Expected to pass, due to local fuel boost } - if (*) { + if * { calc {:fuel pos,3} { pos(y); 3 + pos(y - 3); @@ -172,8 +172,8 @@ module TestModule5 { } method test(y:int, z:int) - requires y > 5; - requires z < 0; + requires y > 5 + requires z < 0 { assert pos(z) == 0; assert pos(-1) == 0; @@ -182,8 +182,8 @@ module TestModule5 { } method test(y:int, z:int) - requires y > 5; - requires z < 0; + requires y > 5 + requires z < 0 { assert TestModule5aiA.pos(z) == 0; assert TestModule5aiA.pos(-1) == 0; @@ -192,8 +192,8 @@ module TestModule5 { } method test(y:int, z:int) - requires y > 5; - requires z < 0; + requires y > 5 + requires z < 0 { assert TestModule5ai.TestModule5aiA.pos(z) == 0; assert TestModule5ai.TestModule5aiA.pos(-1) == 0; @@ -211,8 +211,8 @@ module TestModule5 { } method test(y:int, z:int) - requires y > 5; - requires z < 0; + requires y > 5 + requires z < 0 { assert pos(z) == 0; assert pos(-1) == 0; @@ -232,15 +232,15 @@ module TestModule6 { } ghost function neg(x:int) : int - decreases 1 - x; + decreases 1 - x { if x > 0 then 0 else 1 + neg(x + 1) } method test1(y:int, z:int) - requires y > 5; - requires z < 5; + requires y > 5 + requires z < 5 { assert pos(y) == 3 + pos(y - 3); // error: Should fail, due to lack of fuel @@ -248,8 +248,8 @@ module TestModule6 { } method {:fuel pos,3} {:fuel neg,4} test2(y:int, z:int) - requires y > 5; - requires z < -5; + requires y > 5 + requires z < -5 { assert pos(y) == 3 + pos(y - 3); @@ -266,23 +266,23 @@ module TestModule7 { } ghost function {:fuel 0,0} neg(x:int) : int - decreases 1 - x; + decreases 1 - x { if x > 0 then 0 else 1 + neg(x + 1) } method {:fuel neg,4} {:fuel pos,0,0} test1(y:int, z:int) - requires y > 5; - requires z < -5; + requires y > 5 + requires z < -5 { - if (*) { + if * { assert pos(y) == 3 + pos(y - 3); // error: Method fuel should override function fuel, so this should fail assert neg(z) == 3 + neg(z + 3); // Method fuel should override function fuel, so this succeeds } forall t:int {:fuel pos,3} | t > 0 - ensures true; + ensures true { assert pos(y) == 3 + pos(y - 3); // Statement fuel should override method fuel, so this should succeed } @@ -321,15 +321,15 @@ module TestModule8 { function CRequest_grammar() : G { GTaggedUnion([ GTuple([EndPoint_grammar(), GUint64, CAppMessage_grammar()]), GUint64]) } function parse_EndPoint(val:V) : EndPoint - requires ValInGrammar(val, EndPoint_grammar()); + requires ValInGrammar(val, EndPoint_grammar()) type CAppMessage function CAppMessage_grammar() : G { GTaggedUnion([GUint64, GUint64, GUint64]) } function parse_AppMessage(val:V) : CAppMessage - requires ValInGrammar(val, CAppMessage_grammar()); + requires ValInGrammar(val, CAppMessage_grammar()) function {:fuel ValInGrammar,1} parse_Request1(val:V) : CRequest - requires ValInGrammar(val, CRequest_grammar()); + requires ValInGrammar(val, CRequest_grammar()) { if val.c == 0 then var ep := parse_EndPoint(val.val.t[0]); // With default fuel, error: function precondition (6x), destructor, index @@ -339,7 +339,7 @@ module TestModule8 { } function parse_Request2(val:V) : CRequest - requires ValInGrammar(val, CRequest_grammar()); + requires ValInGrammar(val, CRequest_grammar()) { if val.c == 0 then var ep := parse_EndPoint(val.val.t[0]); // With fuel boosted to 2 this succeeds @@ -349,7 +349,7 @@ module TestModule8 { } function {:fuel ValInGrammar,3} parse_Request3(val:V) : CRequest - requires ValInGrammar(val, CRequest_grammar()); + requires ValInGrammar(val, CRequest_grammar()) { if val.c == 0 then var ep := parse_EndPoint(val.val.t[0]); @@ -360,7 +360,7 @@ module TestModule8 { // With the method, everything succeeds with one less fuel boost (i.e., 2, rather than 3, as in parse_Request3) method parse_Request4(val:V) returns (req:CRequest) - requires ValInGrammar(val, CRequest_grammar()); + requires ValInGrammar(val, CRequest_grammar()) { if val.c == 0 { var ep := parse_EndPoint(val.val.t[0]); @@ -381,8 +381,8 @@ module TestModule9 { // All should pass. method test1(y:int, z:int) - requires y > 5; - requires z < 0; + requires y > 5 + requires z < 0 { assert abs(z) == -1*z; assert abs(y) == y; @@ -391,8 +391,8 @@ module TestModule9 { // Method-level fuel override method {:fuel abs,0,0} test2(y:int, z:int) - requires y > 5; - requires z < 0; + requires y > 5 + requires z < 0 { assert abs(z) == -1*z; // error: Cannot see the body of abs assert abs(y) == y; // error: Cannot see the body of abs @@ -401,8 +401,8 @@ module TestModule9 { // Statement-level fuel override method test3(y:int, z:int) - requires y > 5; - requires z < 0; + requires y > 5 + requires z < 0 { assert {:fuel abs,0,0} abs(z) == -1*z; // error: fuel can't be decreased assert abs(y) == y; // Normal success @@ -412,8 +412,8 @@ module TestModule9 { // Giving more fuel to a non-recursive function won't help, // but it shouldn't hurt either. method {:fuel abs,5,6} test4(y:int, z:int) - requires y > 5; - requires z < 0; + requires y > 5 + requires z < 0 { assert abs(z) == -1*z; assert abs(y) == y; @@ -429,8 +429,8 @@ module TestModule10 { } method test1(y:int, z:int) - requires y > 5; - requires z < 0; + requires y > 5 + requires z < 0 { assert abs(z) == -1*z; // error: Cannot see the body of abs assert abs(y) == y; // error: Cannot see the body of abs @@ -451,8 +451,8 @@ module TestModule11 { } method test1(y:int, z:int) - requires y > 5; - requires z < 0; + requires y > 5 + requires z < 0 { assert abs'(z) == -1*z; // Annotation on abs' only applies locally, so we see the body of abs assert abs'(y) == y; // Annotation on abs' only applies locally, so we see the body of abs @@ -474,7 +474,7 @@ module TestModule12 { } method {:fuel pos3,2,3} {:fuel pos4,2,3} test (y:int) - requires y > 3; + requires y > 3 { assert pos3(y) == 3 + pos4(y - 3); } From 96cd511a1909af131d870cde482e5feb5a63b0ad Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 8 Oct 2024 17:28:09 -0700 Subject: [PATCH 049/151] Reorder error output --- .../LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect index a13a5837473..2950ba4f02a 100755 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect @@ -21,25 +21,28 @@ Fuel.legacy.dfy(324,21): Related location Fuel.legacy.dfy(313,41): Related location Fuel.legacy.dfy(335,26): Error: function precondition could not be proved Fuel.legacy.dfy(324,21): Related location -Fuel.legacy.dfy(312,43): Related location +Fuel.legacy.dfy(314,72): Related location Fuel.legacy.dfy(335,26): Error: function precondition could not be proved Fuel.legacy.dfy(324,21): Related location -Fuel.legacy.dfy(312,58): Related location +Fuel.legacy.dfy(314,93): Related location Fuel.legacy.dfy(335,26): Error: function precondition could not be proved Fuel.legacy.dfy(324,21): Related location Fuel.legacy.dfy(314,46): Related location Fuel.legacy.dfy(335,26): Error: function precondition could not be proved Fuel.legacy.dfy(324,21): Related location -Fuel.legacy.dfy(314,72): Related location +Fuel.legacy.dfy(312,43): Related location Fuel.legacy.dfy(335,26): Error: function precondition could not be proved Fuel.legacy.dfy(324,21): Related location -Fuel.legacy.dfy(314,93): Related location +Fuel.legacy.dfy(312,58): Related location Fuel.legacy.dfy(335,49): Error: destructor 't' can only be applied to datatype values constructed by 'VTuple' Fuel.legacy.dfy(335,50): Error: index out of range Fuel.legacy.dfy(336,38): Error: index out of range Fuel.legacy.dfy(336,42): Error: destructor 'u' can only be applied to datatype values constructed by 'VUint64' Fuel.legacy.dfy(336,45): Error: function precondition could not be proved Fuel.legacy.dfy(329,21): Related location +Fuel.legacy.dfy(312,58): Related location +Fuel.legacy.dfy(336,45): Error: function precondition could not be proved +Fuel.legacy.dfy(329,21): Related location Fuel.legacy.dfy(311,43): Related location Fuel.legacy.dfy(336,45): Error: function precondition could not be proved Fuel.legacy.dfy(329,21): Related location @@ -49,9 +52,6 @@ Fuel.legacy.dfy(329,21): Related location Fuel.legacy.dfy(313,41): Related location Fuel.legacy.dfy(336,45): Error: function precondition could not be proved Fuel.legacy.dfy(329,21): Related location -Fuel.legacy.dfy(312,58): Related location -Fuel.legacy.dfy(336,45): Error: function precondition could not be proved -Fuel.legacy.dfy(329,21): Related location Fuel.legacy.dfy(314,72): Related location Fuel.legacy.dfy(336,45): Error: function precondition could not be proved Fuel.legacy.dfy(329,21): Related location From 41fa18308abd26392e2266002dd127e70dcd3cef Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 8 Oct 2024 17:30:12 -0700 Subject: [PATCH 050/151] Update RUs in test outputs --- .../LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect | 4 ++-- .../TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect | 4 ++-- .../TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect | 4 ++-- .../LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect index d56b584921a..745522d72f4 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect @@ -31,5 +31,5 @@ CoinductiveProofs.dfy(208,21): Related location: this is the postcondition that CoinductiveProofs.dfy(4,23): Related location: this proposition could not be proved Dafny program verifier finished with 23 verified, 12 errors -Total resources used is 770796 -Max resources used by VC is 60436 +Total resources used is 763327 +Max resources used by VC is 81873 diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect index c26e020b6a6..9f0dac30282 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 -Max resources used by VC is 101850 +Total resources used is 959380 +Max resources used by VC is 145340 diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect index a51243d4611..817bc6042f0 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect @@ -1,4 +1,4 @@ Dafny program verifier finished with 272 verified, 0 errors -Total resources used is 30906777 -Max resources used by VC is 2048364 +Total resources used is 28513057 +Max resources used by VC is 1155634 diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect index 9cdf2735095..482faf1b646 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect @@ -1,4 +1,4 @@ Dafny program verifier finished with 276 verified, 0 errors -Total resources used is 29059580 -Max resources used by VC is 974212 +Total resources used is 29928511 +Max resources used by VC is 2247116 From 522513f9bd6eaafcfa98adbd6e796c96963fa385 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 10 Oct 2024 11:57:27 -0700 Subject: [PATCH 051/151] Separate assertions into separate methods to avoid Z3 ordering problem --- .../TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy | 8 ++++---- .../LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy index b3a6a5de9fc..91e3ea6426e 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy @@ -58,21 +58,21 @@ module TestModule2 { assert pos2(-1) == 0; assert pos2(y) == 3 + pos2(y - 3); assert pos2(y) == 4 + pos2(y - 4); - + } + method test3(y: int) requires y > 5 { if * { assert pos3(y) == 5 + pos3(y - 5); // Just enough fuel to get here } else { assert pos3(y) == 6 + pos3(y - 6); // error: Should fail even with a boost, since boost is too small } - + } + method test4(y: int, z: int) requires y > 5 requires z < 0 { if * { assert pos4(z) == 0; // error: Fuel shouldn't overcome opaque } else { reveal pos4(); assert pos4(y) == 5 + pos4(y - 5); // With reveal, everything should work as above } - - } } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect index 2950ba4f02a..b7c23352a14 100755 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect @@ -1,8 +1,8 @@ Fuel.legacy.dfy(129,8): Error: Fuel can only increase within a given scope. Fuel.legacy.dfy(407,8): Error: Fuel can only increase within a given scope. Fuel.legacy.dfy(17,22): Error: assertion might not hold -Fuel.legacy.dfy(65,27): Error: assertion might not hold -Fuel.legacy.dfy(69,27): Error: assertion might not hold +Fuel.legacy.dfy(66,27): Error: assertion might not hold +Fuel.legacy.dfy(71,27): Error: assertion might not hold Fuel.legacy.dfy(92,22): Error: assertion might not hold Fuel.legacy.dfy(93,23): Error: assertion might not hold Fuel.legacy.dfy(94,22): Error: assertion might not hold @@ -64,4 +64,4 @@ Fuel.legacy.dfy(435,22): Error: assertion might not hold Fuel.legacy.dfy(436,22): Error: assertion might not hold Fuel.legacy.dfy(437,23): Error: assertion might not hold -Dafny program verifier finished with 30 verified, 39 errors +Dafny program verifier finished with 31 verified, 39 errors From 5f3cfdc26a6ce795956833cb645dd0e08e7663d5 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 11 Oct 2024 23:21:26 -0700 Subject: [PATCH 052/151] chore: Improve trigger/induction code --- Source/DafnyCore/Resolver/ModuleResolver.cs | 2 +- Source/DafnyCore/Rewriters/IRewriter.cs | 2 +- .../DafnyCore/Rewriters/InductionRewriter.cs | 68 ++++++++++--------- .../Rewriters/TriggerGeneratingRewriter.cs | 4 +- .../Triggers/ComprehensionTriggerGenerator.cs | 2 +- .../Triggers/QuantifiersCollector.cs | 24 ++++--- .../BoogieGenerator.ExpressionTranslator.cs | 9 +-- .../Verifier/BoogieGenerator.Methods.cs | 1 + .../BoogieGenerator.TrForallStmt.cs | 18 ++--- 9 files changed, 70 insertions(+), 60 deletions(-) diff --git a/Source/DafnyCore/Resolver/ModuleResolver.cs b/Source/DafnyCore/Resolver/ModuleResolver.cs index 6df5431e8b2..fa8dca07b57 100644 --- a/Source/DafnyCore/Resolver/ModuleResolver.cs +++ b/Source/DafnyCore/Resolver/ModuleResolver.cs @@ -174,7 +174,7 @@ public void ComputeIsRecursiveBit(CompilationData compilation, ModuleDefinition } foreach (var rewriter in rewriters) { - rewriter.PostCyclicityResolve(module, Reporter); + rewriter.PostCyclicityResolve(module); } } diff --git a/Source/DafnyCore/Rewriters/IRewriter.cs b/Source/DafnyCore/Rewriters/IRewriter.cs index 0a16b6fd953..8fd56e4bfef 100644 --- a/Source/DafnyCore/Rewriters/IRewriter.cs +++ b/Source/DafnyCore/Rewriters/IRewriter.cs @@ -76,7 +76,7 @@ internal virtual void PostResolveIntermediate(ModuleDefinition moduleDefinition) /// A module definition after it /// is resolved, type-checked and SCC/Cyclicity/Recursivity have been performed /// - internal virtual void PostCyclicityResolve(ModuleDefinition moduleDefinition, ErrorReporter errorReporter) { + internal virtual void PostCyclicityResolve(ModuleDefinition moduleDefinition) { Contract.Requires(moduleDefinition != null); } diff --git a/Source/DafnyCore/Rewriters/InductionRewriter.cs b/Source/DafnyCore/Rewriters/InductionRewriter.cs index 03b8e06f0ba..faeb66ebc58 100644 --- a/Source/DafnyCore/Rewriters/InductionRewriter.cs +++ b/Source/DafnyCore/Rewriters/InductionRewriter.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Diagnostics.Contracts; +using JetBrains.Annotations; using static Microsoft.Dafny.RewriterErrors; namespace Microsoft.Dafny; @@ -40,12 +41,9 @@ internal override void PostDecreasesResolve(ModuleDefinition m) { } } - if (decl is NewtypeDecl) { - var nt = (NewtypeDecl)decl; - if (nt.Constraint != null) { - var visitor = new Induction_Visitor(this); - visitor.Visit(nt.Constraint); - } + if (decl is NewtypeDecl { Constraint: { } constraint }) { + var visitor = new InductionVisitor(this); + visitor.Visit(constraint); } } } @@ -53,7 +51,7 @@ internal override void PostDecreasesResolve(ModuleDefinition m) { void ProcessMethodExpressions(Method method) { Contract.Requires(method != null); - var visitor = new Induction_Visitor(this); + var visitor = new InductionVisitor(this); method.Req.ForEach(mfe => visitor.Visit(mfe.E)); method.Ens.ForEach(mfe => visitor.Visit(mfe.E)); if (method.Body != null) { @@ -63,7 +61,7 @@ void ProcessMethodExpressions(Method method) { void ProcessFunctionExpressions(Function function) { Contract.Requires(function != null); - var visitor = new Induction_Visitor(this); + var visitor = new InductionVisitor(this); function.Req.ForEach(visitor.Visit); function.Ens.ForEach(visitor.Visit); if (function.Body != null) { @@ -73,20 +71,29 @@ void ProcessFunctionExpressions(Function function) { void ComputeLemmaInduction(Method method) { Contract.Requires(method != null); - if (method.Body != null && method.IsGhost && method.Mod.Expressions.Count == 0 && method.Outs.Count == 0 && - !(method is ExtremeLemma)) { - var specs = new List(); - method.Req.ForEach(mfe => specs.Add(mfe.E)); - method.Ens.ForEach(mfe => specs.Add(mfe.E)); - ComputeInductionVariables(method.tok, method.Ins, specs, method, ref method.Attributes); + if (method is Lemma or PrefixLemma && method is { Body: not null, Outs: { Count: 0 } }) { + Expression pre = Expression.CreateBoolLiteral(method.tok, true); + foreach (var req in method.Req) { + pre = Expression.CreateAnd(pre, req.E); + } + Expression post = Expression.CreateBoolLiteral(method.tok, true); + foreach (var ens in method.Ens) { + post = Expression.CreateAnd(post, ens.E); + } + ComputeInductionVariables(method.tok, method.Ins, Expression.CreateImplies(pre, post), method, ref method.Attributes); } } - void ComputeInductionVariables(IToken tok, List boundVars, List searchExprs, - Method lemma, ref Attributes attributes) where VarType : class, IVariable { + /// + /// Look at the command-line options and any {:induction} attribute to determine a good list of induction + /// variables. If there are any, then record them in an attribute {:_induction ...} added to "attributes". + /// "body" is the condition that the induction would support. + /// + void ComputeInductionVariables(IToken tok, List boundVars, Expression body, + [CanBeNull] Method lemma, ref Attributes attributes) where TVarType : class, IVariable { Contract.Requires(tok != null); Contract.Requires(boundVars != null); - Contract.Requires(searchExprs != null); + Contract.Requires(body != null); Contract.Requires(Reporter.Options.Induction != 0); var args = Attributes.FindExpressions(attributes, @@ -106,9 +113,9 @@ void ComputeInductionVariables(IToken tok, List boundVars, Lis } else if (args.Count == 0) { // {:induction} is treated the same as {:induction true}, which says to automatically infer induction variables // GO INFER below (all boundVars) - } else if (args.Count == 1 && args[0] is LiteralExpr && ((LiteralExpr)args[0]).Value is bool) { + } else if (args.Count == 1 && args[0] is LiteralExpr { Value: bool and var boolValue }) { // {:induction false} or {:induction true} - if (!(bool)((LiteralExpr)args[0]).Value) { + if (!boolValue) { // we're told not to infer anything return; } @@ -117,12 +124,11 @@ void ComputeInductionVariables(IToken tok, List boundVars, Lis // Here, we're expecting the arguments to {:induction args} to be a sublist of "this;boundVars", where "this" is allowed only // if "lemma" denotes an instance lemma. var goodArguments = new List(); - var i = lemma != null && !lemma.IsStatic + var i = lemma is { IsStatic: false } ? -1 : 0; // -1 says it's okay to see "this" or any other parameter; 0 <= i says it's okay to see parameter i or higher foreach (var arg in args) { - var ie = arg.Resolved as IdentifierExpr; - if (ie != null) { + if (arg.Resolved is IdentifierExpr ie) { var j = boundVars.FindIndex(v => v == ie.Var); if (0 <= j && i <= j) { goodArguments.Add(ie); @@ -163,15 +169,15 @@ void ComputeInductionVariables(IToken tok, List boundVars, Lis // Okay, here we go, coming up with good induction setting for the given situation var inductionVariables = new List(); - if (lemma != null && !lemma.IsStatic) { - if (args != null || searchExprs.Exists(expr => FreeVariablesUtil.ContainsFreeVariable(expr, true, null))) { + if (lemma is { IsStatic: false }) { + if (args != null || FreeVariablesUtil.ContainsFreeVariable(body, true, null)) { inductionVariables.Add(new ThisExpr(lemma)); } } foreach (IVariable n in boundVars) { - if (!(n.Type.IsTypeParameter || n.Type.IsAbstractType || n.Type.IsInternalTypeSynonym) && (args != null || - searchExprs.Exists(expr => InductionHeuristic.VarOccursInArgumentToRecursiveFunction(Reporter.Options, expr, n)))) { + if (!(n.Type.IsTypeParameter || n.Type.IsAbstractType || n.Type.IsInternalTypeSynonym) && + (args != null || InductionHeuristic.VarOccursInArgumentToRecursiveFunction(Reporter.Options, body, n))) { inductionVariables.Add(new IdentifierExpr(n.Tok, n)); } } @@ -189,19 +195,17 @@ void ComputeInductionVariables(IToken tok, List boundVars, Lis } } - class Induction_Visitor : BottomUpVisitor { + class InductionVisitor : BottomUpVisitor { readonly InductionRewriter IndRewriter; - public Induction_Visitor(InductionRewriter inductionRewriter) { + public InductionVisitor(InductionRewriter inductionRewriter) { Contract.Requires(inductionRewriter != null); IndRewriter = inductionRewriter; } protected override void VisitOneExpr(Expression expr) { - var q = expr as QuantifierExpr; - if (q != null && q.SplitQuantifier == null) { - IndRewriter.ComputeInductionVariables(q.tok, q.BoundVars, new List() { q.LogicalBody() }, null, - ref q.Attributes); + if (expr is QuantifierExpr { SplitQuantifier: null } q) { + IndRewriter.ComputeInductionVariables(q.tok, q.BoundVars, q.LogicalBody(), null, ref q.Attributes); } } } diff --git a/Source/DafnyCore/Rewriters/TriggerGeneratingRewriter.cs b/Source/DafnyCore/Rewriters/TriggerGeneratingRewriter.cs index e8283ee85e0..e676be5dfcb 100644 --- a/Source/DafnyCore/Rewriters/TriggerGeneratingRewriter.cs +++ b/Source/DafnyCore/Rewriters/TriggerGeneratingRewriter.cs @@ -9,8 +9,8 @@ internal TriggerGeneratingRewriter(ErrorReporter reporter, SystemModuleManager s this.systemModuleManager = systemModuleManager; } - internal override void PostCyclicityResolve(ModuleDefinition definition, ErrorReporter reporter) { - var finder = new Triggers.QuantifierCollector(reporter); + internal override void PostCyclicityResolve(ModuleDefinition definition) { + var finder = new Triggers.QuantifierCollector(Reporter); foreach (var decl in ModuleDefinition.AllCallablesIncludingPrefixDeclarations(definition.TopLevelDecls)) { finder.Visit(decl, null); diff --git a/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs b/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs index be69c3977c4..15b9fc0edb9 100644 --- a/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs +++ b/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs @@ -122,7 +122,7 @@ bool DetectAndFilterLoopingCandidates() { // In addition, we ignore cases where the only differences between a trigger // and a trigger match are places where a variable is replaced with an // expression whose free variables do not intersect that of the quantifier - // in which that expression is found. For examples of this behavious, see + // in which that expression is found. For examples of this behavior, see // triggers/literals-do-not-cause-loops. // This ignoring logic is implemented by the CouldCauseLoops method. bool foundLoop = false; diff --git a/Source/DafnyCore/Triggers/QuantifiersCollector.cs b/Source/DafnyCore/Triggers/QuantifiersCollector.cs index 1f169e161ba..869640da766 100644 --- a/Source/DafnyCore/Triggers/QuantifiersCollector.cs +++ b/Source/DafnyCore/Triggers/QuantifiersCollector.cs @@ -7,6 +7,7 @@ using System.Text; using System.Collections.ObjectModel; using System.Diagnostics.Contracts; +using JetBrains.Annotations; namespace Microsoft.Dafny.Triggers { internal class QuantifierCollector : TopDownVisitor { @@ -20,21 +21,24 @@ public QuantifierCollector(ErrorReporter reporter) { this.reporter = reporter; } + public void AddComprehension(ComprehensionExpr comprehensionExpr, [CanBeNull] List splitQuantifier) { + quantifiers.Add(comprehensionExpr); + if (splitQuantifier != null) { + var collection = splitQuantifier.OfType(); + quantifierCollections.Add(new ComprehensionTriggerGenerator(comprehensionExpr, collection, reporter)); + quantifiers.UnionWith(splitQuantifier); + } else { + quantifierCollections.Add(new ComprehensionTriggerGenerator(comprehensionExpr, Enumerable.Repeat(comprehensionExpr, 1), reporter)); + } + } + protected override bool VisitOneExpr(Expression expr, ref OldExpr/*?*/ enclosingOldContext) { // only consider quantifiers that are not empty (Bound.Vars.Count > 0) if (expr is ComprehensionExpr e && e.BoundVars.Count > 0 && !quantifiers.Contains(e)) { if (e is SetComprehension or MapComprehension) { - quantifiers.Add(e); - quantifierCollections.Add(new ComprehensionTriggerGenerator(e, Enumerable.Repeat(e, 1), reporter)); + AddComprehension(e, null); } else if (e is QuantifierExpr quantifier) { - quantifiers.Add(quantifier); - if (quantifier.SplitQuantifier != null) { - var collection = quantifier.SplitQuantifier.Select(q => q as ComprehensionExpr).Where(q => q != null); - quantifierCollections.Add(new ComprehensionTriggerGenerator(e, collection, reporter)); - quantifiers.UnionWith(quantifier.SplitQuantifier); - } else { - quantifierCollections.Add(new ComprehensionTriggerGenerator(e, Enumerable.Repeat(quantifier, 1), reporter)); - } + AddComprehension(quantifier, quantifier.SplitQuantifier); } } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index 50a30add394..28a39f44977 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -1678,17 +1678,18 @@ public Boogie.Expr TrBoundVariables(List boundVars, List boundVars, List bvars, out Dictionary substMap, out Boogie.Trigger antitriggers) { + public Boogie.Expr TrBoundVariablesRename(List boundVars, List bvars, out Dictionary substMap) { Contract.Requires(boundVars != null); Contract.Requires(bvars != null); substMap = new Dictionary(); - antitriggers = null; Boogie.Expr typeAntecedent = Boogie.Expr.True; foreach (BoundVar bv in boundVars) { var newBoundVar = new BoundVar(bv.tok, bv.Name, bv.Type); - IdentifierExpr ie = new IdentifierExpr(newBoundVar.tok, newBoundVar.AssignUniqueName(BoogieGenerator.currentDeclaration.IdGenerator)); - ie.Var = newBoundVar; ie.Type = ie.Var.Type; // resolve ie here + IdentifierExpr ie = new IdentifierExpr(newBoundVar.tok, newBoundVar.AssignUniqueName(BoogieGenerator.currentDeclaration.IdGenerator)) { + Var = newBoundVar, + Type = newBoundVar.Type + }; substMap.Add(bv, ie); Boogie.Variable bvar = new Boogie.BoundVariable(newBoundVar.tok, new Boogie.TypedIdent(newBoundVar.tok, newBoundVar.AssignUniqueName(BoogieGenerator.currentDeclaration.IdGenerator), BoogieGenerator.TrType(newBoundVar.Type))); bvars.Add(bvar); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index ed13014fc7c..2994c3d79f2 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -763,6 +763,7 @@ private StmtList TrMethodBody(Method m, BoogieStmtListBuilder builder, List decrSubstMap, ExpressionTranslator exprTran) { var decrToks = new List(); diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs index 81c6bca90e9..3ce5073cbf9 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs @@ -158,11 +158,10 @@ void TrForallStmtCall(IToken tok, List boundVars, List bo // 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 + // using "callEtran". To accomplish this, we translate the arguments 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; 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); @@ -176,7 +175,7 @@ void TrForallStmtCall(IToken tok, List boundVars, List bo expr = (QuantifierExpr)expr.SplitQuantifierExpression; } boundVars = expr.BoundVars; - ante = initEtran.TrBoundVariablesRename(boundVars, bvars, out substMap, out antitriggerBoundVarTypes); + ante = initEtran.TrBoundVariablesRename(boundVars, bvars, out substMap); tr = TrTrigger(callEtran, expr.Attributes, expr.tok, bvars, substMap, s0.MethodSelect.TypeArgumentSubstitutionsWithParents()); var p = Substitute(expr.Range, null, substMap); @@ -190,11 +189,7 @@ void TrForallStmtCall(IToken tok, List boundVars, List bo post = BplAnd(post, callEtran.TrExpr(p)); } 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 = initEtran.TrBoundVariablesRename(boundVars, bvars, out substMap); var p = Substitute(range, null, substMap); anteCanCalls = initEtran.CanCallAssumption(p); @@ -203,13 +198,18 @@ void TrForallStmtCall(IToken tok, List boundVars, List bo // additionalRange produces something of the form canCallAssumptions ==> TrExpr ante = BplAnd(ante, additionalRange(substMap, initEtran)); } + var receiver = new BoogieWrapper(initEtran.TrExpr(Substitute(s0.Receiver, null, substMap, s0.MethodSelect.TypeArgumentSubstitutionsWithParents())), s0.Receiver.Type); + 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)); + } foreach (var ens in ConjunctsOf(s0.Method.Ens)) { p = Substitute(ens.E, receiver, argsSubstMap, s0.MethodSelect.TypeArgumentSubstitutionsWithParents()); // substitute the call's actuals for the method's formals post = BplAnd(post, callEtran.CanCallAssumption(p)); post = BplAnd(post, callEtran.TrExpr(p)); } - tr = antitriggerBoundVarTypes; + tr = null; } // 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))) From cd9d205c8d7148701499efb2783a58bb376821a0 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 11 Oct 2024 23:23:09 -0700 Subject: [PATCH 053/151] Compute triggers for automatic induction --- .../DafnyCore/Rewriters/InductionRewriter.cs | 61 +++++++++++++++++++ .../Triggers/ComprehensionTriggerGenerator.cs | 14 +++++ .../Verifier/BoogieGenerator.Methods.cs | 15 +++-- .../BoogieGenerator.TrForallStmt.cs | 20 +++++- 4 files changed, 101 insertions(+), 9 deletions(-) diff --git a/Source/DafnyCore/Rewriters/InductionRewriter.cs b/Source/DafnyCore/Rewriters/InductionRewriter.cs index faeb66ebc58..e782569bde6 100644 --- a/Source/DafnyCore/Rewriters/InductionRewriter.cs +++ b/Source/DafnyCore/Rewriters/InductionRewriter.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Diagnostics.Contracts; +using System.Linq; using JetBrains.Annotations; using static Microsoft.Dafny.RewriterErrors; @@ -183,6 +184,23 @@ void ComputeInductionVariables(IToken tok, List boundVars, E } if (inductionVariables.Count != 0) { + if (lemma != null) { + var triggers = ComputeInductionTriggers(inductionVariables, body, lemma.EnclosingClass.EnclosingModuleDefinition); + if (triggers.Count == 0) { + var msg = "omitting automatic induction because of lack of triggers"; + if (args != null) { + Reporter.Warning(MessageSource.Rewriter, GenericErrors.ErrorId.none, tok, msg); + } else { + Reporter.Info(MessageSource.Rewriter, tok, msg); + } + return; + } + + foreach (var trigger in triggers) { + attributes = new Attributes("_inductionPattern", trigger, attributes); + } + } + // We found something usable, so let's record that in an attribute attributes = new Attributes("_induction", inductionVariables, attributes); // And since we're inferring something, let's also report that in a hover text. @@ -195,6 +213,49 @@ void ComputeInductionVariables(IToken tok, List boundVars, E } } + /// + /// Obtain and return matching patterns for + /// (forall inductionVariables :: body) + /// If there aren't any, then return null. + /// + List> ComputeInductionTriggers(List inductionVariables, Expression body, ModuleDefinition moduleDefinition) { + Contract.Requires(inductionVariables.Count != 0); + + // Construct a quantifier, because that's what the trigger-generating machinery expects. + // We start by creating a new BoundVar for each ThisExpr-or-IdentifierExpr in "inductionVariables". + var boundVars = new List(); + var substMap = new Dictionary(); + var reverseSubstMap = new Dictionary(); + Expression receiverReplacement = null; + foreach (var inductionVariableExpr in inductionVariables) { + var tok = inductionVariableExpr.tok; + BoundVar boundVar; + if (inductionVariableExpr is IdentifierExpr identifierExpr) { + boundVar = new BoundVar(tok, identifierExpr.Var.Name, identifierExpr.Var.Type); + substMap.Add(identifierExpr.Var, new IdentifierExpr(tok, boundVar)); + } else { + Contract.Assert(inductionVariableExpr is ThisExpr); + boundVar = new BoundVar(tok, "this", inductionVariableExpr.Type); + receiverReplacement = new IdentifierExpr(tok, boundVar); + } + boundVars.Add(boundVar); + reverseSubstMap.Add(boundVar, inductionVariableExpr); + } + + var substituter = new Substituter(receiverReplacement, substMap, new Dictionary()); + var quantifier = new ForallExpr(body.tok, body.RangeToken, boundVars, null, substituter.Substitute(body), null) { + Type = Type.Bool + }; + + var finder = new Triggers.QuantifierCollector(Reporter); + var triggersCollector = new Triggers.TriggersCollector(finder.exprsInOldContext, Reporter.Options, moduleDefinition); + var quantifierCollection = new Triggers.ComprehensionTriggerGenerator(quantifier, Enumerable.Repeat(quantifier, 1), Reporter); + quantifierCollection.ComputeTriggers(triggersCollector); + var triggers = quantifierCollection.GetTriggers(); + var reverseSubstituter = new Substituter(null, reverseSubstMap, new Dictionary()); + return triggers.ConvertAll(trigger => trigger.ConvertAll(reverseSubstituter.Substitute)); + } + class InductionVisitor : BottomUpVisitor { readonly InductionRewriter IndRewriter; diff --git a/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs b/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs index 15b9fc0edb9..65723ba9829 100644 --- a/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs +++ b/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs @@ -230,5 +230,19 @@ internal void CommitTriggers(SystemModuleManager systemModuleManager) { triggerWriter.CommitTrigger(reporter, partWriters.Count > 1 ? index : null, systemModuleManager); } } + + public List> GetTriggers() { + var triggers = new List>(); + foreach (var triggerWriter in partWriters) { + foreach (var triggerTerms in triggerWriter.Candidates) { + var trigger = new List(); + foreach (var triggerTerm in triggerTerms.Terms) { + trigger.Add(triggerTerm.Expr); + } + triggers.Add(trigger); + } + } + return triggers; + } } } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 2994c3d79f2..32399be5560 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -789,14 +789,17 @@ private StmtList TrMethodBody(Method m, BoogieStmtListBuilder builder, List locals, } 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); + TrForallStmtCall(forallStmt.Tok, forallStmt.BoundVars, forallStmt.Bounds, forallStmt.Range, null, + forallStmt.EffectiveEnsuresClauses, null, 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); + TrForallStmtCall(forallStmt.Tok, forallStmt.BoundVars, forallStmt.Bounds, forallStmt.Range, null, + forallStmt.EffectiveEnsuresClauses, null, 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))); } @@ -71,13 +73,14 @@ private void TrForallStmt(BoogieStmtListBuilder builder, List locals, void TrForallStmtCall(IToken tok, List boundVars, List bounds, - Expression range, ExpressionConverter additionalRange, List forallExpressions, CallStmt s0, + Expression range, ExpressionConverter additionalRange, List forallExpressions, List> triggers, 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(forallExpressions == null || triggers == null || triggers.Count == 0); Contract.Requires(s0 != null); // definedness is allowed to be null Contract.Requires(exporter != null); @@ -209,7 +212,18 @@ void TrForallStmtCall(IToken tok, List boundVars, List bo post = BplAnd(post, callEtran.CanCallAssumption(p)); post = BplAnd(post, callEtran.TrExpr(p)); } + tr = null; + if (triggers != null) { + foreach (var trigger in triggers) { + Contract.Assert(trigger.Count != 0); + var terms = trigger.ConvertAll(expr => { + expr = Substitute(expr, receiver, argsSubstMap, s0.MethodSelect.TypeArgumentSubstitutionsWithParents()); + return callEtran.TrExpr(expr); + }); + tr = new Trigger(trigger[0].tok, true, terms, tr); + } + } } // 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))) From 8c4c4a6306dfd855daa81560c8e1d09625a29be7 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 11 Oct 2024 23:21:26 -0700 Subject: [PATCH 054/151] chore: Improve trigger/induction code # Conflicts: # Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs --- Source/DafnyCore/Resolver/ModuleResolver.cs | 2 +- Source/DafnyCore/Rewriters/IRewriter.cs | 2 +- .../DafnyCore/Rewriters/InductionRewriter.cs | 68 ++++++++++--------- .../Rewriters/TriggerGeneratingRewriter.cs | 4 +- .../Triggers/ComprehensionTriggerGenerator.cs | 2 +- .../Triggers/QuantifiersCollector.cs | 24 ++++--- .../BoogieGenerator.ExpressionTranslator.cs | 9 +-- .../Verifier/BoogieGenerator.Methods.cs | 1 + .../BoogieGenerator.TrForallStmt.cs | 33 +++++---- 9 files changed, 80 insertions(+), 65 deletions(-) diff --git a/Source/DafnyCore/Resolver/ModuleResolver.cs b/Source/DafnyCore/Resolver/ModuleResolver.cs index ae9de04f6b2..c012f2bd652 100644 --- a/Source/DafnyCore/Resolver/ModuleResolver.cs +++ b/Source/DafnyCore/Resolver/ModuleResolver.cs @@ -174,7 +174,7 @@ public void ComputeIsRecursiveBit(CompilationData compilation, ModuleDefinition } foreach (var rewriter in rewriters) { - rewriter.PostCyclicityResolve(module, Reporter); + rewriter.PostCyclicityResolve(module); } } diff --git a/Source/DafnyCore/Rewriters/IRewriter.cs b/Source/DafnyCore/Rewriters/IRewriter.cs index 0a16b6fd953..8fd56e4bfef 100644 --- a/Source/DafnyCore/Rewriters/IRewriter.cs +++ b/Source/DafnyCore/Rewriters/IRewriter.cs @@ -76,7 +76,7 @@ internal virtual void PostResolveIntermediate(ModuleDefinition moduleDefinition) /// A module definition after it /// is resolved, type-checked and SCC/Cyclicity/Recursivity have been performed /// - internal virtual void PostCyclicityResolve(ModuleDefinition moduleDefinition, ErrorReporter errorReporter) { + internal virtual void PostCyclicityResolve(ModuleDefinition moduleDefinition) { Contract.Requires(moduleDefinition != null); } diff --git a/Source/DafnyCore/Rewriters/InductionRewriter.cs b/Source/DafnyCore/Rewriters/InductionRewriter.cs index 03b8e06f0ba..faeb66ebc58 100644 --- a/Source/DafnyCore/Rewriters/InductionRewriter.cs +++ b/Source/DafnyCore/Rewriters/InductionRewriter.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Diagnostics.Contracts; +using JetBrains.Annotations; using static Microsoft.Dafny.RewriterErrors; namespace Microsoft.Dafny; @@ -40,12 +41,9 @@ internal override void PostDecreasesResolve(ModuleDefinition m) { } } - if (decl is NewtypeDecl) { - var nt = (NewtypeDecl)decl; - if (nt.Constraint != null) { - var visitor = new Induction_Visitor(this); - visitor.Visit(nt.Constraint); - } + if (decl is NewtypeDecl { Constraint: { } constraint }) { + var visitor = new InductionVisitor(this); + visitor.Visit(constraint); } } } @@ -53,7 +51,7 @@ internal override void PostDecreasesResolve(ModuleDefinition m) { void ProcessMethodExpressions(Method method) { Contract.Requires(method != null); - var visitor = new Induction_Visitor(this); + var visitor = new InductionVisitor(this); method.Req.ForEach(mfe => visitor.Visit(mfe.E)); method.Ens.ForEach(mfe => visitor.Visit(mfe.E)); if (method.Body != null) { @@ -63,7 +61,7 @@ void ProcessMethodExpressions(Method method) { void ProcessFunctionExpressions(Function function) { Contract.Requires(function != null); - var visitor = new Induction_Visitor(this); + var visitor = new InductionVisitor(this); function.Req.ForEach(visitor.Visit); function.Ens.ForEach(visitor.Visit); if (function.Body != null) { @@ -73,20 +71,29 @@ void ProcessFunctionExpressions(Function function) { void ComputeLemmaInduction(Method method) { Contract.Requires(method != null); - if (method.Body != null && method.IsGhost && method.Mod.Expressions.Count == 0 && method.Outs.Count == 0 && - !(method is ExtremeLemma)) { - var specs = new List(); - method.Req.ForEach(mfe => specs.Add(mfe.E)); - method.Ens.ForEach(mfe => specs.Add(mfe.E)); - ComputeInductionVariables(method.tok, method.Ins, specs, method, ref method.Attributes); + if (method is Lemma or PrefixLemma && method is { Body: not null, Outs: { Count: 0 } }) { + Expression pre = Expression.CreateBoolLiteral(method.tok, true); + foreach (var req in method.Req) { + pre = Expression.CreateAnd(pre, req.E); + } + Expression post = Expression.CreateBoolLiteral(method.tok, true); + foreach (var ens in method.Ens) { + post = Expression.CreateAnd(post, ens.E); + } + ComputeInductionVariables(method.tok, method.Ins, Expression.CreateImplies(pre, post), method, ref method.Attributes); } } - void ComputeInductionVariables(IToken tok, List boundVars, List searchExprs, - Method lemma, ref Attributes attributes) where VarType : class, IVariable { + /// + /// Look at the command-line options and any {:induction} attribute to determine a good list of induction + /// variables. If there are any, then record them in an attribute {:_induction ...} added to "attributes". + /// "body" is the condition that the induction would support. + /// + void ComputeInductionVariables(IToken tok, List boundVars, Expression body, + [CanBeNull] Method lemma, ref Attributes attributes) where TVarType : class, IVariable { Contract.Requires(tok != null); Contract.Requires(boundVars != null); - Contract.Requires(searchExprs != null); + Contract.Requires(body != null); Contract.Requires(Reporter.Options.Induction != 0); var args = Attributes.FindExpressions(attributes, @@ -106,9 +113,9 @@ void ComputeInductionVariables(IToken tok, List boundVars, Lis } else if (args.Count == 0) { // {:induction} is treated the same as {:induction true}, which says to automatically infer induction variables // GO INFER below (all boundVars) - } else if (args.Count == 1 && args[0] is LiteralExpr && ((LiteralExpr)args[0]).Value is bool) { + } else if (args.Count == 1 && args[0] is LiteralExpr { Value: bool and var boolValue }) { // {:induction false} or {:induction true} - if (!(bool)((LiteralExpr)args[0]).Value) { + if (!boolValue) { // we're told not to infer anything return; } @@ -117,12 +124,11 @@ void ComputeInductionVariables(IToken tok, List boundVars, Lis // Here, we're expecting the arguments to {:induction args} to be a sublist of "this;boundVars", where "this" is allowed only // if "lemma" denotes an instance lemma. var goodArguments = new List(); - var i = lemma != null && !lemma.IsStatic + var i = lemma is { IsStatic: false } ? -1 : 0; // -1 says it's okay to see "this" or any other parameter; 0 <= i says it's okay to see parameter i or higher foreach (var arg in args) { - var ie = arg.Resolved as IdentifierExpr; - if (ie != null) { + if (arg.Resolved is IdentifierExpr ie) { var j = boundVars.FindIndex(v => v == ie.Var); if (0 <= j && i <= j) { goodArguments.Add(ie); @@ -163,15 +169,15 @@ void ComputeInductionVariables(IToken tok, List boundVars, Lis // Okay, here we go, coming up with good induction setting for the given situation var inductionVariables = new List(); - if (lemma != null && !lemma.IsStatic) { - if (args != null || searchExprs.Exists(expr => FreeVariablesUtil.ContainsFreeVariable(expr, true, null))) { + if (lemma is { IsStatic: false }) { + if (args != null || FreeVariablesUtil.ContainsFreeVariable(body, true, null)) { inductionVariables.Add(new ThisExpr(lemma)); } } foreach (IVariable n in boundVars) { - if (!(n.Type.IsTypeParameter || n.Type.IsAbstractType || n.Type.IsInternalTypeSynonym) && (args != null || - searchExprs.Exists(expr => InductionHeuristic.VarOccursInArgumentToRecursiveFunction(Reporter.Options, expr, n)))) { + if (!(n.Type.IsTypeParameter || n.Type.IsAbstractType || n.Type.IsInternalTypeSynonym) && + (args != null || InductionHeuristic.VarOccursInArgumentToRecursiveFunction(Reporter.Options, body, n))) { inductionVariables.Add(new IdentifierExpr(n.Tok, n)); } } @@ -189,19 +195,17 @@ void ComputeInductionVariables(IToken tok, List boundVars, Lis } } - class Induction_Visitor : BottomUpVisitor { + class InductionVisitor : BottomUpVisitor { readonly InductionRewriter IndRewriter; - public Induction_Visitor(InductionRewriter inductionRewriter) { + public InductionVisitor(InductionRewriter inductionRewriter) { Contract.Requires(inductionRewriter != null); IndRewriter = inductionRewriter; } protected override void VisitOneExpr(Expression expr) { - var q = expr as QuantifierExpr; - if (q != null && q.SplitQuantifier == null) { - IndRewriter.ComputeInductionVariables(q.tok, q.BoundVars, new List() { q.LogicalBody() }, null, - ref q.Attributes); + if (expr is QuantifierExpr { SplitQuantifier: null } q) { + IndRewriter.ComputeInductionVariables(q.tok, q.BoundVars, q.LogicalBody(), null, ref q.Attributes); } } } diff --git a/Source/DafnyCore/Rewriters/TriggerGeneratingRewriter.cs b/Source/DafnyCore/Rewriters/TriggerGeneratingRewriter.cs index e8283ee85e0..e676be5dfcb 100644 --- a/Source/DafnyCore/Rewriters/TriggerGeneratingRewriter.cs +++ b/Source/DafnyCore/Rewriters/TriggerGeneratingRewriter.cs @@ -9,8 +9,8 @@ internal TriggerGeneratingRewriter(ErrorReporter reporter, SystemModuleManager s this.systemModuleManager = systemModuleManager; } - internal override void PostCyclicityResolve(ModuleDefinition definition, ErrorReporter reporter) { - var finder = new Triggers.QuantifierCollector(reporter); + internal override void PostCyclicityResolve(ModuleDefinition definition) { + var finder = new Triggers.QuantifierCollector(Reporter); foreach (var decl in ModuleDefinition.AllCallablesIncludingPrefixDeclarations(definition.TopLevelDecls)) { finder.Visit(decl, null); diff --git a/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs b/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs index be69c3977c4..15b9fc0edb9 100644 --- a/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs +++ b/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs @@ -122,7 +122,7 @@ bool DetectAndFilterLoopingCandidates() { // In addition, we ignore cases where the only differences between a trigger // and a trigger match are places where a variable is replaced with an // expression whose free variables do not intersect that of the quantifier - // in which that expression is found. For examples of this behavious, see + // in which that expression is found. For examples of this behavior, see // triggers/literals-do-not-cause-loops. // This ignoring logic is implemented by the CouldCauseLoops method. bool foundLoop = false; diff --git a/Source/DafnyCore/Triggers/QuantifiersCollector.cs b/Source/DafnyCore/Triggers/QuantifiersCollector.cs index 1f169e161ba..869640da766 100644 --- a/Source/DafnyCore/Triggers/QuantifiersCollector.cs +++ b/Source/DafnyCore/Triggers/QuantifiersCollector.cs @@ -7,6 +7,7 @@ using System.Text; using System.Collections.ObjectModel; using System.Diagnostics.Contracts; +using JetBrains.Annotations; namespace Microsoft.Dafny.Triggers { internal class QuantifierCollector : TopDownVisitor { @@ -20,21 +21,24 @@ public QuantifierCollector(ErrorReporter reporter) { this.reporter = reporter; } + public void AddComprehension(ComprehensionExpr comprehensionExpr, [CanBeNull] List splitQuantifier) { + quantifiers.Add(comprehensionExpr); + if (splitQuantifier != null) { + var collection = splitQuantifier.OfType(); + quantifierCollections.Add(new ComprehensionTriggerGenerator(comprehensionExpr, collection, reporter)); + quantifiers.UnionWith(splitQuantifier); + } else { + quantifierCollections.Add(new ComprehensionTriggerGenerator(comprehensionExpr, Enumerable.Repeat(comprehensionExpr, 1), reporter)); + } + } + protected override bool VisitOneExpr(Expression expr, ref OldExpr/*?*/ enclosingOldContext) { // only consider quantifiers that are not empty (Bound.Vars.Count > 0) if (expr is ComprehensionExpr e && e.BoundVars.Count > 0 && !quantifiers.Contains(e)) { if (e is SetComprehension or MapComprehension) { - quantifiers.Add(e); - quantifierCollections.Add(new ComprehensionTriggerGenerator(e, Enumerable.Repeat(e, 1), reporter)); + AddComprehension(e, null); } else if (e is QuantifierExpr quantifier) { - quantifiers.Add(quantifier); - if (quantifier.SplitQuantifier != null) { - var collection = quantifier.SplitQuantifier.Select(q => q as ComprehensionExpr).Where(q => q != null); - quantifierCollections.Add(new ComprehensionTriggerGenerator(e, collection, reporter)); - quantifiers.UnionWith(quantifier.SplitQuantifier); - } else { - quantifierCollections.Add(new ComprehensionTriggerGenerator(e, Enumerable.Repeat(quantifier, 1), reporter)); - } + AddComprehension(quantifier, quantifier.SplitQuantifier); } } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index 10d78a37498..4f4b707f9c9 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -1674,17 +1674,18 @@ public Boogie.Expr TrBoundVariables(List boundVars, List boundVars, List bvars, out Dictionary substMap, out Boogie.Trigger antitriggers) { + public Boogie.Expr TrBoundVariablesRename(List boundVars, List bvars, out Dictionary substMap) { Contract.Requires(boundVars != null); Contract.Requires(bvars != null); substMap = new Dictionary(); - antitriggers = null; Boogie.Expr typeAntecedent = Boogie.Expr.True; foreach (BoundVar bv in boundVars) { var newBoundVar = new BoundVar(bv.tok, bv.Name, bv.Type); - IdentifierExpr ie = new IdentifierExpr(newBoundVar.tok, newBoundVar.AssignUniqueName(BoogieGenerator.currentDeclaration.IdGenerator)); - ie.Var = newBoundVar; ie.Type = ie.Var.Type; // resolve ie here + IdentifierExpr ie = new IdentifierExpr(newBoundVar.tok, newBoundVar.AssignUniqueName(BoogieGenerator.currentDeclaration.IdGenerator)) { + Var = newBoundVar, + Type = newBoundVar.Type + }; substMap.Add(bv, ie); Boogie.Variable bvar = new Boogie.BoundVariable(newBoundVar.tok, new Boogie.TypedIdent(newBoundVar.tok, newBoundVar.AssignUniqueName(BoogieGenerator.currentDeclaration.IdGenerator), BoogieGenerator.TrType(newBoundVar.Type))); bvars.Add(bvar); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index d6fc9b6adaf..130068059fb 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -759,6 +759,7 @@ private StmtList TrMethodBody(Method m, BoogieStmtListBuilder builder, Variables foreach (var pre in m.Req) { parRange = Expression.CreateAnd(parRange, Substitute(pre.E, receiverSubst, substMap)); } + // construct an expression (generator) for: VF' << VF ExpressionConverter decrCheck = delegate (Dictionary decrSubstMap, ExpressionTranslator exprTran) { var decrToks = new List(); diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs index ab7e6ae4a58..f2c402f91bc 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs @@ -158,15 +158,14 @@ void TrForallStmtCall(IToken tok, List boundVars, List bo // 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 + // using "callEtran". To accomplish this, we translate the arguments 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 ante; Bpl.Expr post = Bpl.Expr.True; Bpl.Trigger tr; if (forallExpressions != null) { @@ -176,29 +175,35 @@ void TrForallStmtCall(IToken tok, List boundVars, List bo 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))); + ante = initEtran.TrBoundVariablesRename(boundVars, bvars, out substMap); + tr = TrTrigger(callEtran, expr.Attributes, expr.tok, bvars, substMap, s0.MethodSelect.TypeArgumentSubstitutionsWithParents()); + + var p = Substitute(expr.Range, null, substMap); + ante = BplAnd(ante, initEtran.TrExpr(p)); 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))); + ante = initEtran.TrBoundVariablesRename(boundVars, bvars, out substMap); + + var p = Substitute(range, null, substMap); + ante = BplAnd(ante, initEtran.TrExpr(p)); 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); + 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)); + } 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 + 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; + tr = null; } // 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))) @@ -532,4 +537,4 @@ void TrForallProof(ForallStmt forallStmt, BoogieStmtListBuilder definedness, Boo exporter.Add(TrAssumeCmd(forallStmt.Tok, BplAnd(se, ((Bpl.ForallExpr)qq).Body))); } } -} \ No newline at end of file +} From e62d2b61b3cec129cd33f40f3838959709a249ec Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Fri, 11 Oct 2024 23:23:09 -0700 Subject: [PATCH 055/151] Compute triggers for automatic induction # Conflicts: # Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs # Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs --- .../DafnyCore/Rewriters/InductionRewriter.cs | 61 +++++++++++++++++++ .../Triggers/ComprehensionTriggerGenerator.cs | 14 +++++ .../Verifier/BoogieGenerator.Methods.cs | 15 +++-- .../BoogieGenerator.TrForallStmt.cs | 20 +++++- 4 files changed, 101 insertions(+), 9 deletions(-) diff --git a/Source/DafnyCore/Rewriters/InductionRewriter.cs b/Source/DafnyCore/Rewriters/InductionRewriter.cs index faeb66ebc58..e782569bde6 100644 --- a/Source/DafnyCore/Rewriters/InductionRewriter.cs +++ b/Source/DafnyCore/Rewriters/InductionRewriter.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Diagnostics.Contracts; +using System.Linq; using JetBrains.Annotations; using static Microsoft.Dafny.RewriterErrors; @@ -183,6 +184,23 @@ void ComputeInductionVariables(IToken tok, List boundVars, E } if (inductionVariables.Count != 0) { + if (lemma != null) { + var triggers = ComputeInductionTriggers(inductionVariables, body, lemma.EnclosingClass.EnclosingModuleDefinition); + if (triggers.Count == 0) { + var msg = "omitting automatic induction because of lack of triggers"; + if (args != null) { + Reporter.Warning(MessageSource.Rewriter, GenericErrors.ErrorId.none, tok, msg); + } else { + Reporter.Info(MessageSource.Rewriter, tok, msg); + } + return; + } + + foreach (var trigger in triggers) { + attributes = new Attributes("_inductionPattern", trigger, attributes); + } + } + // We found something usable, so let's record that in an attribute attributes = new Attributes("_induction", inductionVariables, attributes); // And since we're inferring something, let's also report that in a hover text. @@ -195,6 +213,49 @@ void ComputeInductionVariables(IToken tok, List boundVars, E } } + /// + /// Obtain and return matching patterns for + /// (forall inductionVariables :: body) + /// If there aren't any, then return null. + /// + List> ComputeInductionTriggers(List inductionVariables, Expression body, ModuleDefinition moduleDefinition) { + Contract.Requires(inductionVariables.Count != 0); + + // Construct a quantifier, because that's what the trigger-generating machinery expects. + // We start by creating a new BoundVar for each ThisExpr-or-IdentifierExpr in "inductionVariables". + var boundVars = new List(); + var substMap = new Dictionary(); + var reverseSubstMap = new Dictionary(); + Expression receiverReplacement = null; + foreach (var inductionVariableExpr in inductionVariables) { + var tok = inductionVariableExpr.tok; + BoundVar boundVar; + if (inductionVariableExpr is IdentifierExpr identifierExpr) { + boundVar = new BoundVar(tok, identifierExpr.Var.Name, identifierExpr.Var.Type); + substMap.Add(identifierExpr.Var, new IdentifierExpr(tok, boundVar)); + } else { + Contract.Assert(inductionVariableExpr is ThisExpr); + boundVar = new BoundVar(tok, "this", inductionVariableExpr.Type); + receiverReplacement = new IdentifierExpr(tok, boundVar); + } + boundVars.Add(boundVar); + reverseSubstMap.Add(boundVar, inductionVariableExpr); + } + + var substituter = new Substituter(receiverReplacement, substMap, new Dictionary()); + var quantifier = new ForallExpr(body.tok, body.RangeToken, boundVars, null, substituter.Substitute(body), null) { + Type = Type.Bool + }; + + var finder = new Triggers.QuantifierCollector(Reporter); + var triggersCollector = new Triggers.TriggersCollector(finder.exprsInOldContext, Reporter.Options, moduleDefinition); + var quantifierCollection = new Triggers.ComprehensionTriggerGenerator(quantifier, Enumerable.Repeat(quantifier, 1), Reporter); + quantifierCollection.ComputeTriggers(triggersCollector); + var triggers = quantifierCollection.GetTriggers(); + var reverseSubstituter = new Substituter(null, reverseSubstMap, new Dictionary()); + return triggers.ConvertAll(trigger => trigger.ConvertAll(reverseSubstituter.Substitute)); + } + class InductionVisitor : BottomUpVisitor { readonly InductionRewriter IndRewriter; diff --git a/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs b/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs index 15b9fc0edb9..65723ba9829 100644 --- a/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs +++ b/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs @@ -230,5 +230,19 @@ internal void CommitTriggers(SystemModuleManager systemModuleManager) { triggerWriter.CommitTrigger(reporter, partWriters.Count > 1 ? index : null, systemModuleManager); } } + + public List> GetTriggers() { + var triggers = new List>(); + foreach (var triggerWriter in partWriters) { + foreach (var triggerTerms in triggerWriter.Candidates) { + var trigger = new List(); + foreach (var triggerTerm in triggerTerms.Terms) { + trigger.Add(triggerTerm.Expr); + } + triggers.Add(trigger); + } + } + return triggers; + } } } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 130068059fb..7a893ade472 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -782,14 +782,17 @@ private StmtList TrMethodBody(Method m, BoogieStmtListBuilder builder, Variables null, null, false, true); }; + var triggers = Attributes.FindAllExpressions(m.Attributes, "_inductionPattern"); #if VERIFY_CORRECTNESS_OF_TRANSLATION_FORALL_STATEMENT_RANGE - var definedness = new BoogieStmtListBuilder(this, options); - var exporter = new BoogieStmtListBuilder(this, options); - TrForallStmtCall(m.tok, parBoundVars, parRange, decrCheck, null, recursiveCall, definedness, exporter, localVariables, etran); - // All done, so put the two pieces together - builder.Add(new Bpl.IfCmd(m.tok, null, definedness.Collect(m.tok), null, exporter.Collect(m.tok))); + var definedness = new BoogieStmtListBuilder(this, options, builder.Context); + var exporter = new BoogieStmtListBuilder(this, options, builder.Context); + TrForallStmtCall(m.tok, parBoundVars, parBounds, parRange, decrCheck, null, triggers, recursiveCall, definedness, + exporter, localVariables, etran); + // All done, so put the two pieces together + builder.Add(new Bpl.IfCmd(m.tok, null, definedness.Collect(m.tok), null, exporter.Collect(m.tok))); #else - TrForallStmtCall(m.tok, parBoundVars, parBounds, parRange, decrCheck, null, recursiveCall, null, builder, localVariables, etran); + TrForallStmtCall(m.tok, parBoundVars, parBounds, parRange, decrCheck, null, triggers, recursiveCall, null, + builder, localVariables, etran); #endif } // translate the body of the method diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs index f2c402f91bc..20c4126c10d 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs @@ -40,12 +40,14 @@ private void TrForallStmt(BoogieStmtListBuilder builder, Variables locals, Expre } 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); + TrForallStmtCall(forallStmt.Tok, forallStmt.BoundVars, forallStmt.Bounds, forallStmt.Range, null, + forallStmt.EffectiveEnsuresClauses, null, 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); + TrForallStmtCall(forallStmt.Tok, forallStmt.BoundVars, forallStmt.Bounds, forallStmt.Range, null, + forallStmt.EffectiveEnsuresClauses, null, 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))); } @@ -71,13 +73,14 @@ private void TrForallStmt(BoogieStmtListBuilder builder, Variables locals, Expre void TrForallStmtCall(IToken tok, List boundVars, List bounds, - Expression range, ExpressionConverter additionalRange, List forallExpressions, CallStmt s0, + Expression range, ExpressionConverter additionalRange, List forallExpressions, List> triggers, CallStmt s0, BoogieStmtListBuilder definedness, BoogieStmtListBuilder exporter, Variables 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(forallExpressions == null || triggers == null || triggers.Count == 0); Contract.Requires(s0 != null); // definedness is allowed to be null Contract.Requires(exporter != null); @@ -203,7 +206,18 @@ void TrForallStmtCall(IToken tok, List boundVars, List bo 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 = null; + if (triggers != null) { + foreach (var trigger in triggers) { + Contract.Assert(trigger.Count != 0); + var terms = trigger.ConvertAll(expr => { + expr = Substitute(expr, receiver, argsSubstMap, s0.MethodSelect.TypeArgumentSubstitutionsWithParents()); + return callEtran.TrExpr(expr); + }); + tr = new Trigger(trigger[0].tok, true, terms, tr); + } + } } // 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))) From ad1c724c5b5e86459479162d3e9f7ef0f0b71218 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Sat, 12 Oct 2024 11:38:10 -0700 Subject: [PATCH 056/151] Improve induction heuristics --- .../DafnyCore/Rewriters/InductionHeuristic.cs | 22 ++++++++++++++----- .../DafnyCore/Rewriters/InductionRewriter.cs | 2 +- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Source/DafnyCore/Rewriters/InductionHeuristic.cs b/Source/DafnyCore/Rewriters/InductionHeuristic.cs index 3787ab4303b..1ceab8866f2 100644 --- a/Source/DafnyCore/Rewriters/InductionHeuristic.cs +++ b/Source/DafnyCore/Rewriters/InductionHeuristic.cs @@ -1,3 +1,4 @@ +#nullable enable using System.Diagnostics.Contracts; namespace Microsoft.Dafny; @@ -6,6 +7,8 @@ public static class InductionHeuristic { /// /// Returns 'true' iff by looking at 'expr' the Induction Heuristic determines that induction should be applied to 'n'. + /// Variable 'n' can be passed in as 'null', in which case it stands for 'this'. + /// /// More precisely: /// DafnyInductionHeuristic Return 'true' /// ----------------------- ------------- @@ -17,10 +20,15 @@ public static class InductionHeuristic { /// 5 if 'n' occurs as a prominent subexpression of any argument to a recursive function /// 6 if 'n' occurs as a prominent subexpression of any decreases-influencing argument to a recursive function /// - public static bool VarOccursInArgumentToRecursiveFunction(DafnyOptions options, Expression expr, IVariable n) { + public static bool VarOccursInArgumentToRecursiveFunction(DafnyOptions options, Expression expr, IVariable? n) { switch (options.InductionHeuristic) { case 0: return true; - case 1: return FreeVariablesUtil.ContainsFreeVariable(expr, false, n); + case 1: + if (n == null) { + return FreeVariablesUtil.ContainsFreeVariable(expr, true, null); + } else { + return FreeVariablesUtil.ContainsFreeVariable(expr, false, n); + } default: return VarOccursInArgumentToRecursiveFunction(options, expr, n, false); } } @@ -30,16 +38,18 @@ public static bool VarOccursInArgumentToRecursiveFunction(DafnyOptions options, /// not 'expr' has prominent status in its context. /// DafnyInductionHeuristic cases 0 and 1 are assumed to be handled elsewhere (i.e., a precondition of this method is DafnyInductionHeuristic is at least 2). /// - static bool VarOccursInArgumentToRecursiveFunction(DafnyOptions options, Expression expr, IVariable n, bool exprIsProminent) { + static bool VarOccursInArgumentToRecursiveFunction(DafnyOptions options, Expression expr, IVariable? n, bool exprIsProminent) { Contract.Requires(expr != null); Contract.Requires(n != null); // The following variable is what gets passed down to recursive calls if the subexpression does not itself acquire prominent status. var subExprIsProminent = options.InductionHeuristic == 2 || options.InductionHeuristic == 4 ? /*once prominent, always prominent*/exprIsProminent : /*reset the prominent status*/false; - if (expr is IdentifierExpr) { + if (n != null && expr is IdentifierExpr) { var e = (IdentifierExpr)expr; return exprIsProminent && e.Var == n; + } else if (n == null && expr is ThisExpr) { + return exprIsProminent; } else if (expr is SeqSelectExpr) { var e = (SeqSelectExpr)expr; var q = options.InductionHeuristic < 4 || subExprIsProminent; @@ -108,7 +118,7 @@ static bool VarOccursInArgumentToRecursiveFunction(DafnyOptions options, Express } } else if (expr is DatatypeValue) { var e = (DatatypeValue)expr; - var q = n.Type.IsDatatype ? exprIsProminent : subExprIsProminent; // prominent status continues, if we're looking for a variable whose type is a datatype + var q = n != null && n.Type.IsDatatype ? exprIsProminent : subExprIsProminent; // prominent status continues, if we're looking for a variable whose type is a datatype return e.Arguments.Exists(exp => VarOccursInArgumentToRecursiveFunction(options, exp, n, q)); } else if (expr is UnaryExpr) { var e = (UnaryExpr)expr; @@ -145,7 +155,7 @@ static bool VarOccursInArgumentToRecursiveFunction(DafnyOptions options, Express } else if (expr is StmtExpr) { var e = (StmtExpr)expr; // ignore the statement - return VarOccursInArgumentToRecursiveFunction(options, e.E, n); + return VarOccursInArgumentToRecursiveFunction(options, e.E, n, exprIsProminent); } else if (expr is ITEExpr) { var e = (ITEExpr)expr; diff --git a/Source/DafnyCore/Rewriters/InductionRewriter.cs b/Source/DafnyCore/Rewriters/InductionRewriter.cs index e782569bde6..c8ca384bc12 100644 --- a/Source/DafnyCore/Rewriters/InductionRewriter.cs +++ b/Source/DafnyCore/Rewriters/InductionRewriter.cs @@ -171,7 +171,7 @@ void ComputeInductionVariables(IToken tok, List boundVars, E // Okay, here we go, coming up with good induction setting for the given situation var inductionVariables = new List(); if (lemma is { IsStatic: false }) { - if (args != null || FreeVariablesUtil.ContainsFreeVariable(body, true, null)) { + if (args != null || InductionHeuristic.VarOccursInArgumentToRecursiveFunction(Reporter.Options, body, null)) { inductionVariables.Add(new ThisExpr(lemma)); } } From 29d715a778d8fe9f8d8edefb18d6ac1dfb9b5d78 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Sat, 12 Oct 2024 11:44:35 -0700 Subject: [PATCH 057/151] =?UTF-8?q?chore:=20Change=20=E2=80=9Cghost=20meth?= =?UTF-8?q?od=E2=80=9D=20to=20=E2=80=9Clemma=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TestFiles/LitTests/LitTest/dafny0/LetExpr.dfy | 4 ++-- .../TestFiles/LitTests/LitTest/dafny0/Termination.dfy | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/LetExpr.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/LetExpr.dfy index 9cdc06d5040..8b3300900aa 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/LetExpr.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/LetExpr.dfy @@ -127,11 +127,11 @@ method Theorem0(n: int) } } -ghost method Theorem1(n: int) +lemma Theorem1(n: int) requires 1 <= n; ensures 1 <= Fib(n); { - // in a ghost method, the induction tactic takes care of it + // in a lemma, the induction tactic takes care of it } ghost function Theorem2(n: int): int diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Termination.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Termination.dfy index 8ac6f9a37ae..26376f13b2a 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Termination.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Termination.dfy @@ -392,7 +392,7 @@ module MultisetTests { if n == 0 then 0 else F'(a, n-1) } - ghost method M(n: nat, b: multiset) + lemma M(n: nat, b: multiset) ensures F(b, n) == 0 // proved via automatic induction { } @@ -410,7 +410,7 @@ module MapTests { if n == 0 then 0 else F'(a, n-1) } - ghost method M(n: nat, b: map) + lemma M(n: nat, b: map) ensures F(b, n) == 0 // proved via automatic induction { } From 4cfbe2183a2137639061721e40a806e886fbe35f Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 14 Oct 2024 01:33:58 -0700 Subject: [PATCH 058/151] chore: Improve C# --- Source/DafnyCore/AST/ExtremeCloner.cs | 2 +- .../DafnyCore/AST/ExtremeLemmaBodyCloner.cs | 61 ++++++------------- .../CollectFriendlyCallsInSpec_Visitor.cs | 6 +- Source/DafnyCore/Resolver/ModuleResolver.cs | 15 ++--- .../Triggers/SplitPartTriggerWriter.cs | 4 +- 5 files changed, 28 insertions(+), 60 deletions(-) diff --git a/Source/DafnyCore/AST/ExtremeCloner.cs b/Source/DafnyCore/AST/ExtremeCloner.cs index d4d725e82e3..67cc276bf05 100644 --- a/Source/DafnyCore/AST/ExtremeCloner.cs +++ b/Source/DafnyCore/AST/ExtremeCloner.cs @@ -17,7 +17,7 @@ protected ExtremeCloner(Expression k, ErrorReporter reporter) { Contract.Requires(reporter != null); this.k = k; this.reporter = reporter; - this.suffix = string.Format("#[{0}]", Printer.ExprToString(reporter.Options, k)); + this.suffix = $"#[{Printer.ExprToString(reporter.Options, k)}]"; } protected Expression CloneCallAndAddK(ApplySuffix e) { Contract.Requires(e != null); diff --git a/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs b/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs index f71835f6ce2..66eda31ca9a 100644 --- a/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs +++ b/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs @@ -11,42 +11,31 @@ namespace Microsoft.Dafny; class ExtremeLemmaBodyCloner : ExtremeCloner { readonly ExtremeLemma context; readonly ISet focalPredicates; - public ExtremeLemmaBodyCloner(ExtremeLemma context, Expression k, ISet focalPredicates, ErrorReporter reporter) + readonly ISet focalCodatatypeEquality; + public ExtremeLemmaBodyCloner(ExtremeLemma context, Expression k, + ISet focalPredicates, ISet focalCodatatypeEquality, ErrorReporter reporter) : base(k, reporter) { Contract.Requires(context != null); Contract.Requires(k != null); Contract.Requires(reporter != null); this.context = context; this.focalPredicates = focalPredicates; + this.focalCodatatypeEquality = focalCodatatypeEquality; } public override Expression CloneExpr(Expression expr) { if (reporter.Options.RewriteFocalPredicates) { - if (expr is FunctionCallExpr) { - var e = (FunctionCallExpr)expr; -#if DEBUG_PRINT - if (e.Function.Name.EndsWith("#") && Contract.Exists(focalPredicates, p => e.Function.Name == p.Name + "#")) { - Options.Writer.WriteLine("{0}({1},{2}): DEBUG: Possible opportunity to rely on new rewrite: {3}", e.tok.filename, e.tok.line, e.tok.col, Printer.ExprToString(e)); - } -#endif + if (expr is FunctionCallExpr functionCallExpr) { // Note, we don't actually ever get here, because all calls will have been parsed as ApplySuffix. // However, if something changes in the future (for example, some rewrite that changing an ApplySuffix // to its resolved FunctionCallExpr), then we do want this code, so with the hope of preventing // some error in the future, this case is included. (Of course, it is currently completely untested!) - var f = e.Function as ExtremePredicate; - if (f != null && focalPredicates.Contains(f)) { -#if DEBUG_PRINT - var r = CloneCallAndAddK(e); - Options.Writer.WriteLine("{0}({1},{2}): DEBUG: Rewrote extreme predicate into prefix predicate: {3}", e.tok.filename, e.tok.line, e.tok.col, Printer.ExprToString(r)); - return r; -#else - return CloneCallAndAddK(e); -#endif + if (functionCallExpr.Function is ExtremePredicate f && focalPredicates.Contains(f)) { + return CloneCallAndAddK(functionCallExpr); } } else if (expr is StaticReceiverExpr ee) { return new StaticReceiverExpr(Tok(ee.tok), ee.Type, ee.IsImplicit); - } else if (expr is ApplySuffix) { - var apply = (ApplySuffix)expr; + } else if (expr is ApplySuffix apply) { if (!apply.WasResolved()) { // Since we're assuming the enclosing statement to have been resolved, this ApplySuffix must // be part of an ExprRhs that actually designates a method call. Such an ApplySuffix does @@ -54,22 +43,9 @@ public override Expression CloneExpr(Expression expr) { var mse = (MemberSelectExpr)apply.Lhs.Resolved; Contract.Assume(mse.Member is Method); } else { - var fce = apply.Resolved as FunctionCallExpr; - if (fce != null) { -#if DEBUG_PRINT - if (fce.Function.Name.EndsWith("#") && Contract.Exists(focalPredicates, p => fce.Function.Name == p.Name + "#")) { - Options.Writer.WriteLine("{0}({1},{2}): DEBUG: Possible opportunity to rely on new rewrite: {3}", fce.tok.filename, fce.tok.line, fce.tok.col, Printer.ExprToString(fce)); - } -#endif - var f = fce.Function as ExtremePredicate; - if (f != null && focalPredicates.Contains(f)) { -#if DEBUG_PRINT - var r = CloneCallAndAddK(fce); - Options.Writer.WriteLine("{0}({1},{2}): DEBUG: Rewrote extreme predicate into prefix predicate: {3}", fce.tok.filename, fce.tok.line, fce.tok.col, Printer.ExprToString(r)); - return r; -#else + if (apply.Resolved is FunctionCallExpr fce) { + if (fce.Function is ExtremePredicate f && focalPredicates.Contains(f)) { return CloneCallAndAddK(fce); -#endif } } } @@ -78,32 +54,31 @@ public override Expression CloneExpr(Expression expr) { return base.CloneExpr(expr); } public override AssignmentRhs CloneRHS(AssignmentRhs rhs) { - var r = rhs as ExprRhs; - if (r != null && r.Expr is ApplySuffix) { - var apply = (ApplySuffix)r.Expr; - var mse = apply.Lhs.Resolved as MemberSelectExpr; - if (mse != null && mse.Member is ExtremeLemma && ModuleDefinition.InSameSCC(context, (ExtremeLemma)mse.Member)) { + if (rhs is ExprRhs { Expr: ApplySuffix apply }) { + if (apply.Lhs.Resolved is MemberSelectExpr { Member: ExtremeLemma extremeLemma } && ModuleDefinition.InSameSCC(context, extremeLemma)) { // we're looking at a recursive call to an extreme lemma - Contract.Assert(apply.Lhs is NameSegment || apply.Lhs is ExprDotName); // this is the only way a call statement can have been parsed + Contract.Assert(apply.Lhs is NameSegment or ExprDotName); // this is the only way a call statement can have been parsed // clone "apply.Lhs", changing the least/greatest lemma to the prefix lemma; then clone "apply", adding in the extra argument Expression lhsClone; if (apply.Lhs is NameSegment) { var lhs = (NameSegment)apply.Lhs; - lhsClone = new NameSegment(Tok(lhs.tok), lhs.Name + "#", lhs.OptTypeArguments == null ? null : lhs.OptTypeArguments.ConvertAll(CloneType)); + lhsClone = new NameSegment(Tok(lhs.tok), lhs.Name + "#", lhs.OptTypeArguments?.ConvertAll(CloneType)); } else { var lhs = (ExprDotName)apply.Lhs; - lhsClone = new ExprDotName(Tok(lhs.tok), CloneExpr(lhs.Lhs), lhs.SuffixName + "#", lhs.OptTypeArguments == null ? null : lhs.OptTypeArguments.ConvertAll(CloneType)); + lhsClone = new ExprDotName(Tok(lhs.tok), CloneExpr(lhs.Lhs), lhs.SuffixName + "#", lhs.OptTypeArguments?.ConvertAll(CloneType)); } + var args = new List(); args.Add(new ActualBinding(null, k)); apply.Bindings.ArgumentBindings.ForEach(arg => args.Add(CloneActualBinding(arg))); var applyClone = new ApplySuffix(Tok(apply.tok), apply.AtTok == null ? null : Tok(apply.AtTok), lhsClone, args, Tok(apply.CloseParen)); var c = new ExprRhs(applyClone, CloneAttributes(rhs.Attributes)); - reporter.Info(MessageSource.Cloner, apply.Lhs.tok, mse.Member.Name + suffix); + reporter.Info(MessageSource.Cloner, apply.Lhs.tok, extremeLemma.Name + suffix); return c; } } + return base.CloneRHS(rhs); } } diff --git a/Source/DafnyCore/Resolver/CollectFriendlyCallsInSpec_Visitor.cs b/Source/DafnyCore/Resolver/CollectFriendlyCallsInSpec_Visitor.cs index 76bccae2139..02b9ba438c1 100644 --- a/Source/DafnyCore/Resolver/CollectFriendlyCallsInSpec_Visitor.cs +++ b/Source/DafnyCore/Resolver/CollectFriendlyCallsInSpec_Visitor.cs @@ -19,9 +19,8 @@ protected override bool VisitOneExpr(Expression expr, ref CallingPosition cp) { // no friendly calls in "expr" return false; // don't recurse into subexpressions } - if (expr is FunctionCallExpr) { + if (expr is FunctionCallExpr fexp) { if (cp == CallingPosition.Positive) { - var fexp = (FunctionCallExpr)expr; if (IsCoContext ? fexp.Function is GreatestPredicate : fexp.Function is LeastPredicate) { if (Context.KNat != ((ExtremePredicate)fexp.Function).KNat) { KNatMismatchError(expr.tok, Context.Name, Context.TypeOfK, ((ExtremePredicate)fexp.Function).TypeOfK); @@ -31,8 +30,7 @@ protected override bool VisitOneExpr(Expression expr, ref CallingPosition cp) { } } return false; // don't explore subexpressions any further - } else if (expr is BinaryExpr && IsCoContext) { - var bin = (BinaryExpr)expr; + } else if (expr is BinaryExpr bin && IsCoContext) { if (cp == CallingPosition.Positive && bin.ResolvedOp == BinaryExpr.ResolvedOpcode.EqCommon && bin.E0.Type.IsCoDatatype) { friendlyCalls.Add(bin); return false; // don't explore subexpressions any further diff --git a/Source/DafnyCore/Resolver/ModuleResolver.cs b/Source/DafnyCore/Resolver/ModuleResolver.cs index c012f2bd652..05f8b1ebfde 100644 --- a/Source/DafnyCore/Resolver/ModuleResolver.cs +++ b/Source/DafnyCore/Resolver/ModuleResolver.cs @@ -1696,17 +1696,14 @@ private void FillInPostConditionsAndBodiesOfPrefixLemmas(List decl var post = subst.CloneExpr(p.E); prefixLemma.Ens.Add(new AttributedExpression(post)); foreach (var e in coConclusions) { - var fce = e as FunctionCallExpr; - if (fce != null) { - // the other possibility is that "e" is a BinaryExpr + if (e is FunctionCallExpr fce) { GreatestPredicate predicate = (GreatestPredicate)fce.Function; focalPredicates.Add(predicate); // For every focal predicate P in S, add to S all greatest predicates in the same strongly connected // component (in the call graph) as P - foreach (var node in predicate.EnclosingClass.EnclosingModuleDefinition.CallGraph.GetSCC( - predicate)) { - if (node is GreatestPredicate) { - focalPredicates.Add((GreatestPredicate)node); + foreach (var node in predicate.EnclosingClass.EnclosingModuleDefinition.CallGraph.GetSCC(predicate)) { + if (node is GreatestPredicate greatestPredicate) { + focalPredicates.Add(greatestPredicate); } } } @@ -1729,8 +1726,8 @@ private void FillInPostConditionsAndBodiesOfPrefixLemmas(List decl // For every focal predicate P in S, add to S all least predicates in the same strongly connected // component (in the call graph) as P foreach (var node in predicate.EnclosingClass.EnclosingModuleDefinition.CallGraph.GetSCC(predicate)) { - if (node is LeastPredicate) { - focalPredicates.Add((LeastPredicate)node); + if (node is LeastPredicate leastPredicate) { + focalPredicates.Add(leastPredicate); } } } diff --git a/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs b/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs index 4e5eedefb82..49c27770468 100644 --- a/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs +++ b/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs @@ -84,9 +84,7 @@ public bool RewriteMatchingLoop(ErrorReporter reporter, ModuleDefinition module) var entry = substMap.Find(x => ExprExtensions.ExpressionEq(sub, x.Item1)); if (entry == null) { var newBv = new BoundVar(sub.tok, "_t#" + substMap.Count, sub.Type); - var ie = new IdentifierExpr(sub.tok, newBv.Name); - ie.Var = newBv; - ie.Type = newBv.Type; + var ie = new IdentifierExpr(sub.tok, newBv.Name) { Var = newBv, Type = newBv.Type }; substMap.Add(new Tuple(sub, ie)); } } From b17c662b2b520a83a7d08f18b4ce79c0ac63c782 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 14 Oct 2024 01:35:44 -0700 Subject: [PATCH 059/151] Also consider codatatype equality as a greatest focal predicate --- Source/DafnyCore/AST/ExtremeCloner.cs | 12 ++++++++++++ .../DafnyCore/AST/ExtremeLemmaBodyCloner.cs | 4 ++++ Source/DafnyCore/Resolver/ModuleResolver.cs | 19 ++++++++++++++----- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/Source/DafnyCore/AST/ExtremeCloner.cs b/Source/DafnyCore/AST/ExtremeCloner.cs index 67cc276bf05..96411834bd9 100644 --- a/Source/DafnyCore/AST/ExtremeCloner.cs +++ b/Source/DafnyCore/AST/ExtremeCloner.cs @@ -63,4 +63,16 @@ protected Expression CloneCallAndAddK(FunctionCallExpr e) { reporter.Info(MessageSource.Cloner, e.tok, e.Name + suffix); return fexp; } + + protected Expression CloneEqualityAndAndK(BinaryExpr binaryExpr) { + if (this.CloneResolvedFields) { + throw new NotImplementedException(); + } + + var eq = new TernaryExpr(Tok(binaryExpr.tok), + binaryExpr.ResolvedOp == BinaryExpr.ResolvedOpcode.EqCommon ? TernaryExpr.Opcode.PrefixEqOp : TernaryExpr.Opcode.PrefixNeqOp, + k, CloneExpr(binaryExpr.E0), CloneExpr(binaryExpr.E1)); + reporter.Info(MessageSource.Cloner, binaryExpr.tok, "==" + suffix); + return eq; + } } diff --git a/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs b/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs index 66eda31ca9a..c5763a08f9e 100644 --- a/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs +++ b/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs @@ -49,6 +49,10 @@ public override Expression CloneExpr(Expression expr) { } } } + } else if (expr is BinaryExpr { ResolvedOp: BinaryExpr.ResolvedOpcode.EqCommon or BinaryExpr.ResolvedOpcode.NeqCommon } binaryExpr) { + if (binaryExpr.E0.Type.AsCoDatatype is { } coDatatypeDecl && focalCodatatypeEquality.Contains(coDatatypeDecl)) { + return CloneEqualityAndAndK(binaryExpr); + } } } return base.CloneExpr(expr); diff --git a/Source/DafnyCore/Resolver/ModuleResolver.cs b/Source/DafnyCore/Resolver/ModuleResolver.cs index 05f8b1ebfde..91398372d1d 100644 --- a/Source/DafnyCore/Resolver/ModuleResolver.cs +++ b/Source/DafnyCore/Resolver/ModuleResolver.cs @@ -1685,6 +1685,7 @@ private void FillInPostConditionsAndBodiesOfPrefixLemmas(List decl var k = prefixLemma.Ins[0]; var focalPredicates = new HashSet(); + var focalCodatatypeEquality = new HashSet(); if (com is GreatestLemma) { // compute the postconditions of the prefix lemma Contract.Assume(prefixLemma.Ens.Count == 0); // these are not supposed to have been filled in before @@ -1706,6 +1707,9 @@ private void FillInPostConditionsAndBodiesOfPrefixLemmas(List decl focalPredicates.Add(greatestPredicate); } } + } else { + var binExpr = (BinaryExpr)e; // each "coConclusion" is either a FunctionCallExpr or a BinaryExpr + focalCodatatypeEquality.Add(binExpr.E0.Type.AsCoDatatype ?? binExpr.E1.Type.AsCoDatatype); } } } @@ -1734,16 +1738,21 @@ private void FillInPostConditionsAndBodiesOfPrefixLemmas(List decl } } - reporter.Info(MessageSource.Resolver, com.tok, - focalPredicates.Count == 0 - ? $"{com.PrefixLemma.Name} has no focal predicates" - : $"{com.PrefixLemma.Name} with focal predicate{Util.Plural(focalPredicates.Count)} {Util.Comma(focalPredicates, p => p.Name)}"); + var focalCount = focalPredicates.Count + focalCodatatypeEquality.Count; + if (focalCount == 0) { + reporter.Info(MessageSource.Resolver, com.tok, $"{com.PrefixLemma.Name} has no focal predicates"); + } else { + var predicates = Util.Comma(focalPredicates, p => p.Name); + var equalities = Util.Comma(focalCodatatypeEquality, decl => $"{decl.Name}.=="); + var focals = predicates + (predicates.Length != 0 && equalities.Length != 0 ? ", " : "") + equalities; + reporter.Info(MessageSource.Resolver, com.tok, $"{com.PrefixLemma.Name} with focal predicate{Util.Plural(focalCount)} {focals}"); + } // Compute the statement body of the prefix lemma Contract.Assume(prefixLemma.Body == null); // this is not supposed to have been filled in before if (com.Body != null) { var kMinusOne = new BinaryExpr(com.tok, BinaryExpr.Opcode.Sub, new IdentifierExpr(k.tok, k.Name), new LiteralExpr(com.tok, 1)); - var subst = new ExtremeLemmaBodyCloner(com, kMinusOne, focalPredicates, this.reporter); + var subst = new ExtremeLemmaBodyCloner(com, kMinusOne, focalPredicates, focalCodatatypeEquality, this.reporter); var mainBody = subst.CloneBlockStmt(com.Body); Expression kk; Statement els; From cee8d58168eb7b7c3d5af495a4c5b151008c75cb Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 14 Oct 2024 01:37:09 -0700 Subject: [PATCH 060/151] Show tooltips for any quantifier rewrite substitutions --- Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs b/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs index 49c27770468..abb809b9a54 100644 --- a/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs +++ b/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs @@ -13,6 +13,7 @@ class SplitPartTriggerWriter { public List CandidateTerms { get; set; } public List Candidates { get; set; } private List RejectedCandidates { get; } + public List> NamedExpressions { get; } private List loopingMatches; private bool AllowsLoops { @@ -28,6 +29,7 @@ private bool AllowsLoops { internal SplitPartTriggerWriter(ComprehensionExpr comprehension) { this.Comprehension = comprehension; this.RejectedCandidates = new List(); + this.NamedExpressions = new(); } internal void TrimInvalidTriggers() { @@ -96,6 +98,7 @@ public bool RewriteMatchingLoop(ErrorReporter reporter, ModuleDefinition module) if (substMap.Count > 0) { var s = new ExprSubstituter(substMap); expr = s.Substitute(Comprehension) as QuantifierExpr; + NamedExpressions.AddRange(substMap); } else { // make a copy of the expr if (expr is ForallExpr) { @@ -173,7 +176,8 @@ string InfoFirstLineEnd(int count) { messages.Add($"Part #{splitPartIndex} is '{Comprehension.Term}'"); } if (Candidates.Any()) { - messages.Add($"Selected triggers:{InfoFirstLineEnd(Candidates.Count)}{string.Join(", ", Candidates)}"); + var subst = Util.Comma("", NamedExpressions, pair => $" where {pair.Item2} := {pair.Item1}"); + messages.Add($"Selected triggers:{InfoFirstLineEnd(Candidates.Count)}{string.Join(", ", Candidates)}{subst}"); } if (RejectedCandidates.Any()) { messages.Add($"Rejected triggers:{InfoFirstLineEnd(RejectedCandidates.Count)}{string.Join("\n ", RejectedCandidates)}"); From dda37cbcb1478137aaf4b26c29e1fa8b16727a0e Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 14 Oct 2024 01:55:10 -0700 Subject: [PATCH 061/151] feat!: Auto-induction for twostate lemmas but not ghost methods --- Source/DafnyCore/Rewriters/InductionRewriter.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/DafnyCore/Rewriters/InductionRewriter.cs b/Source/DafnyCore/Rewriters/InductionRewriter.cs index c8ca384bc12..8314cc34dc5 100644 --- a/Source/DafnyCore/Rewriters/InductionRewriter.cs +++ b/Source/DafnyCore/Rewriters/InductionRewriter.cs @@ -72,15 +72,17 @@ void ProcessFunctionExpressions(Function function) { void ComputeLemmaInduction(Method method) { Contract.Requires(method != null); - if (method is Lemma or PrefixLemma && method is { Body: not null, Outs: { Count: 0 } }) { + if (method is { IsGhost: true, AllowsAllocation: false, Outs: { Count: 0 }, Body: not null } and not ExtremeLemma) { Expression pre = Expression.CreateBoolLiteral(method.tok, true); foreach (var req in method.Req) { pre = Expression.CreateAnd(pre, req.E); } + Expression post = Expression.CreateBoolLiteral(method.tok, true); foreach (var ens in method.Ens) { post = Expression.CreateAnd(post, ens.E); } + ComputeInductionVariables(method.tok, method.Ins, Expression.CreateImplies(pre, post), method, ref method.Attributes); } } From fa79ba3a8a85316586bd46ab13f9d08944fe1797 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 16 Oct 2024 07:33:22 -0700 Subject: [PATCH 062/151] =?UTF-8?q?fix:=20Don=E2=80=99t=20consider=20arrow?= =?UTF-8?q?-typed=20variables=20for=20auto=20induction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/DafnyCore/Rewriters/InductionRewriter.cs | 2 +- Source/DafnyCore/Triggers/TriggerExtensions.cs | 7 +------ .../LitTest/triggers/InductionWithoutTriggers.dfy | 13 +++++++++++++ .../triggers/InductionWithoutTriggers.dfy.expect | 3 +++ 4 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.expect diff --git a/Source/DafnyCore/Rewriters/InductionRewriter.cs b/Source/DafnyCore/Rewriters/InductionRewriter.cs index 8314cc34dc5..0cb37e9fa7c 100644 --- a/Source/DafnyCore/Rewriters/InductionRewriter.cs +++ b/Source/DafnyCore/Rewriters/InductionRewriter.cs @@ -179,7 +179,7 @@ void ComputeInductionVariables(IToken tok, List boundVars, E } foreach (IVariable n in boundVars) { - if (!(n.Type.IsTypeParameter || n.Type.IsAbstractType || n.Type.IsInternalTypeSynonym) && + if (!(n.Type.IsTypeParameter || n.Type.IsAbstractType || n.Type.IsInternalTypeSynonym || n.Type.IsArrowType) && (args != null || InductionHeuristic.VarOccursInArgumentToRecursiveFunction(Reporter.Options, body, n))) { inductionVariables.Add(new IdentifierExpr(n.Tok, n)); } diff --git a/Source/DafnyCore/Triggers/TriggerExtensions.cs b/Source/DafnyCore/Triggers/TriggerExtensions.cs index e1417a6492a..6a79257962e 100644 --- a/Source/DafnyCore/Triggers/TriggerExtensions.cs +++ b/Source/DafnyCore/Triggers/TriggerExtensions.cs @@ -268,12 +268,7 @@ private static bool ShallowEq(WildcardExpr expr1, WildcardExpr expr2) { } private static bool ShallowEq(LambdaExpr expr1, LambdaExpr expr2) { -#if THROW_UNSUPPORTED_COMPARISONS - Contract.Assume(false); // This kind of expression never appears in a trigger - throw new NotImplementedException(); -#else - return false; -#endif + return true; } private static bool ShallowEq(MapComprehension expr1, MapComprehension expr2) { diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy new file mode 100644 index 00000000000..82bbf55f80c --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy @@ -0,0 +1,13 @@ +// RUN: %testDafnyForEachResolver "%s" --expect-exit-code=4 + +ghost function Sum(n: nat, f: int -> int): int +{ + if n == 0 then 0 else f(n-1) + Sum(n-1, f) +} + +lemma TestTriggerWithLambdaExpression(n: nat, f: int -> int, g: int -> int) +{ + // Once, trigger selection would crash on the following quantifier, which uses a LambdaExpr. + assert forall n: nat, f: int -> int :: Sum(n, x => 500 + f(x)) == n + Sum(n, f); // error: this does not hold +} + diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.expect new file mode 100644 index 00000000000..dd60a022868 --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.expect @@ -0,0 +1,3 @@ +InductionWithoutTriggers.dfy(17,21): Warning: Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. Change or remove the {:induction} attribute to generate a different induction hypothesis, or add {:nowarn} to silence this warning. For more information, see the section quantifier instantiation rules in the reference manual. + +Dafny program verifier finished with 2 verified, 1 error From 0028a3ad0034ccc6b52b072e8663fc76f66ed16d Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 16 Oct 2024 07:35:15 -0700 Subject: [PATCH 063/151] feat: allow ternary expressions in triggers --- Source/DafnyCore/Triggers/TriggersCollector.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/DafnyCore/Triggers/TriggersCollector.cs b/Source/DafnyCore/Triggers/TriggersCollector.cs index a39c6ce38ed..a210052fcab 100644 --- a/Source/DafnyCore/Triggers/TriggersCollector.cs +++ b/Source/DafnyCore/Triggers/TriggersCollector.cs @@ -101,6 +101,7 @@ expr is ApplyExpr || expr is DisplayExpression || expr is MapDisplayExpr || expr is DatatypeValue || + expr is TernaryExpr || TranslateToFunctionCall(expr)) { return true; } else if (expr is BinaryExpr) { From 394c635b09294f0ba9bc20b24038817aec88ad14 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 16 Oct 2024 07:58:24 -0700 Subject: [PATCH 064/151] Compute and use triggers for induction hypotheses --- .../DafnyCore/Rewriters/InductionRewriter.cs | 55 ++++++++++++++++--- .../LitTest/dafny0/AutoContracts.dfy.expect | 4 +- .../LitTest/dafny0/PrefixTypeSubst.dfy.expect | 12 ++-- .../LitTests/LitTest/dafny2/Calculations.dfy | 8 +++ .../LitTests/LitTest/dafny3/Abstemious.dfy | 3 +- .../LitTests/LitTest/dafny4/Bug170.dfy.expect | 5 +- .../LitTests/LitTest/dafny4/GHC-MergeSort.dfy | 11 ++++ .../LitTest/dafny4/Lucas-up.legacy.dfy | 4 +- .../LitTests/LitTest/dafny4/UnionFind.dfy | 2 +- .../git-issues/git-issue-977.dfy.expect | 9 +++ .../LitTest/git-issues/github-issue-4804.dfy | 2 +- .../LitTests/LitTest/hofs/SumSum.dfy | 6 +- .../LitTests/LitTest/lambdas/MatrixAssoc.dfy | 2 +- .../triggers/InductionWithoutTriggers.dfy | 8 +++ .../InductionWithoutTriggers.dfy.expect | 1 + 15 files changed, 104 insertions(+), 28 deletions(-) diff --git a/Source/DafnyCore/Rewriters/InductionRewriter.cs b/Source/DafnyCore/Rewriters/InductionRewriter.cs index 0cb37e9fa7c..e6a8cf4ae4e 100644 --- a/Source/DafnyCore/Rewriters/InductionRewriter.cs +++ b/Source/DafnyCore/Rewriters/InductionRewriter.cs @@ -165,8 +165,23 @@ void ComputeInductionVariables(IToken tok, List boundVars, E return; } - // The argument list was legal, so let's use it for the _induction attribute + // The argument list was legal, so let's use it for the _induction attribute. + // Next, look for matching patterns for the induction hypothesis. + if (lemma != null) { + var triggers = ComputeAndReportInductionTriggers(lemma, ref attributes, goodArguments, body); + if (triggers.Count == 0) { + var suppressWarnings = Attributes.Contains(attributes, "nowarn"); + var warningLevel = suppressWarnings ? ErrorLevel.Info : ErrorLevel.Warning; + + Reporter.Message(MessageSource.Rewriter, warningLevel, null, tok, + $"Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. " + + $"Change or remove the {{:induction}} attribute to generate a different induction hypothesis, or add {{:nowarn}} to silence this warning. " + + $"For more information, see the section quantifier instantiation rules in the reference manual."); + } + } + attributes = new Attributes("_induction", goodArguments, attributes); + return; } @@ -186,8 +201,9 @@ void ComputeInductionVariables(IToken tok, List boundVars, E } if (inductionVariables.Count != 0) { + List> triggers = null; if (lemma != null) { - var triggers = ComputeInductionTriggers(inductionVariables, body, lemma.EnclosingClass.EnclosingModuleDefinition); + triggers = ComputeInductionTriggers(inductionVariables, body, lemma.EnclosingClass.EnclosingModuleDefinition); if (triggers.Count == 0) { var msg = "omitting automatic induction because of lack of triggers"; if (args != null) { @@ -197,10 +213,6 @@ void ComputeInductionVariables(IToken tok, List boundVars, E } return; } - - foreach (var trigger in triggers) { - attributes = new Attributes("_inductionPattern", trigger, attributes); - } } // We found something usable, so let's record that in an attribute @@ -210,8 +222,32 @@ void ComputeInductionVariables(IToken tok, List boundVars, E if (lemma is PrefixLemma) { s = lemma.Name + " " + s; } - Reporter.Info(MessageSource.Rewriter, tok, s); + + if (triggers != null) { + ReportInductionTriggers(lemma, ref attributes, triggers); + } + } + } + + List> ComputeAndReportInductionTriggers(Method lemma, ref Attributes attributes, List inductionVariables, + Expression body) { + var triggers = ComputeInductionTriggers(inductionVariables, body, lemma.EnclosingClass.EnclosingModuleDefinition); + ReportInductionTriggers(lemma, ref attributes, triggers); + return triggers; + } + + private void ReportInductionTriggers(Method lemma, ref Attributes attributes, List> triggers) { + foreach (var trigger in triggers) { + attributes = new Attributes("_inductionPattern", trigger, attributes); +#if DEBUG + var ss = Printer.OneAttributeToString(Reporter.Options, attributes, "inductionPattern"); + if (lemma is PrefixLemma) { + ss = lemma.Name + " " + ss; + } + + Reporter.Info(MessageSource.Rewriter, lemma.tok, ss); +#endif } } @@ -253,7 +289,10 @@ List> ComputeInductionTriggers(List inductionVariab var triggersCollector = new Triggers.TriggersCollector(finder.exprsInOldContext, Reporter.Options, moduleDefinition); var quantifierCollection = new Triggers.ComprehensionTriggerGenerator(quantifier, Enumerable.Repeat(quantifier, 1), Reporter); quantifierCollection.ComputeTriggers(triggersCollector); - var triggers = quantifierCollection.GetTriggers(); + // Get the computed triggers, but only ask for those that do not require additional bound variables. (An alternative to this + // design would be to add {:matchinglooprewrite false} to "quantifier" above. However, that would cause certain matching loops + // to be ignored, so it is safer to not include triggers that require additional bound variables.) + var triggers = quantifierCollection.GetTriggers(false); var reverseSubstituter = new Substituter(null, reverseSubstMap, new Dictionary()); return triggers.ConvertAll(trigger => trigger.ConvertAll(reverseSubstituter.Substitute)); } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AutoContracts.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AutoContracts.dfy.expect index 50ce7f167c5..934b0a287e3 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AutoContracts.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AutoContracts.dfy.expect @@ -248,13 +248,13 @@ module OneModule { data < 20 } - lemma /*{:_induction this}*/ L() + lemma L() requires Valid() ensures data < 100 { } - twostate lemma /*{:_induction this}*/ TL() + twostate lemma TL() requires old(Valid()) ensures old(data) <= data { diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/PrefixTypeSubst.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/PrefixTypeSubst.dfy.expect index 71102103951..7f1f51f43c1 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/PrefixTypeSubst.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/PrefixTypeSubst.dfy.expect @@ -231,7 +231,7 @@ greatest lemma N(o: MyClass) N(o); } /*** -lemma {:axiom} /*{:_induction _k}*/ N#[_k: ORDINAL](o: MyClass) +lemma {:axiom} /*{:_induction _k}*/ /*{:_inductionPattern o.R#[_k]()}*/ N#[_k: ORDINAL](o: MyClass) ensures o.R#[_k]() decreases _k, o { @@ -253,7 +253,7 @@ greatest lemma O() O(); } /*** -lemma {:axiom} /*{:_induction _k}*/ O#[_k: ORDINAL]() +lemma {:axiom} /*{:_induction _k}*/ /*{:_inductionPattern MyClass.S#[_k]()}*/ O#[_k: ORDINAL]() ensures MyClass.S#[_k]() decreases _k { @@ -448,7 +448,7 @@ greatest lemma RstRst7() } } /*** -lemma {:axiom} /*{:_induction _k}*/ RstRst7#[_k: ORDINAL]() +lemma {:axiom} /*{:_induction _k}*/ /*{:_inductionPattern MyClass.RST#[_k]()}*/ RstRst7#[_k: ORDINAL]() ensures MyClass.RST#[_k]() decreases _k { @@ -512,7 +512,7 @@ greatest lemma RstRst10[nat]() { } /*** -lemma {:axiom} /*{:_induction _k}*/ RstRst10#[_k: nat]() +lemma {:axiom} /*{:_induction _k}*/ /*{:_inductionPattern MyClass.RST_Nat#[_k]()}*/ RstRst10#[_k: nat]() ensures MyClass.RST_Nat#[_k]() decreases _k { @@ -588,7 +588,7 @@ class MyClass { L(u, v); } /*** - lemma {:axiom} /*{:_induction this, _k}*/ L#[_k: ORDINAL](u: U, v: V) + lemma {:axiom} /*{:_induction _k}*/ /*{:_inductionPattern P#[_k](u, v)}*/ L#[_k: ORDINAL](u: U, v: V) ensures P#[_k](u, v) decreases _k { @@ -611,7 +611,7 @@ class MyClass { assert R#[_k - 1](); } /*** - lemma {:axiom} /*{:_induction this, _k}*/ M#[_k: ORDINAL]() + lemma {:axiom} /*{:_induction _k}*/ /*{:_inductionPattern R#[_k]()}*/ M#[_k: ORDINAL]() ensures R#[_k]() decreases _k { diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny2/Calculations.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny2/Calculations.dfy index 14926010e4e..ecf9db84e66 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny2/Calculations.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny2/Calculations.dfy @@ -49,6 +49,14 @@ lemma Lemma_ConcatNil(xs : List) lemma Lemma_RevCatCommute(xs : List) ensures forall ys, zs :: revacc(xs, concat(ys, zs)) == concat(revacc(xs, ys), zs); { + forall ys, zs + ensures revacc(xs, concat(ys, zs)) == concat(revacc(xs, ys), zs) + { + match xs + case Nil => + case Cons(_, xs') => + Lemma_RevCatCommute(xs'); + } } // Here is a theorem that says "qreverse" and "reverse" calculate the same result. The proof diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/Abstemious.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/Abstemious.dfy index 9645b0cf2f3..5eb338afaa8 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/Abstemious.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/Abstemious.dfy @@ -143,9 +143,10 @@ greatest lemma OhOnesTail_Correct() { } -greatest lemma OhOnes_Correct() +lemma OhOnes_Correct() ensures OhOnes() == Cons(0, ones()) { + OhOnesTail_Correct(); } lemma OhOnes_Correct'(n: nat) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Bug170.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Bug170.dfy.expect index 9b8aeb71c83..976b1a237f1 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Bug170.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Bug170.dfy.expect @@ -14,8 +14,6 @@ Bug170.dfy(10,12): Info: B#[_k - 1] Bug170.dfy(15,12): Info: A#[_k - 1] Bug170.dfy(18,14): Info: AA# decreases _k, x Bug170.dfy(26,14): Info: BB# decreases _k, x -Bug170.dfy(18,14): Info: AA# {:induction _k, x} -Bug170.dfy(26,14): Info: BB# {:induction _k, x} Bug170.dfy(36,21): Info: _k: ORDINAL Bug170.dfy(41,21): Info: _k: ORDINAL Bug170.dfy(46,17): Info: _k: ORDINAL @@ -33,7 +31,9 @@ Bug170.dfy(43,4): Info: A#[_k - 1] Bug170.dfy(46,17): Info: AA# decreases _k, x Bug170.dfy(53,17): Info: BB# decreases _k, x Bug170.dfy(46,17): Info: AA# {:induction _k, x} +Bug170.dfy(46,17): Info: AA# {:inductionPattern A#[_k](x)} Bug170.dfy(53,17): Info: BB# {:induction _k, x} +Bug170.dfy(53,17): Info: BB# {:inductionPattern B#[_k](x)} Bug170.dfy(64,18): Info: _k: ORDINAL Bug170.dfy(69,14): Info: _k: ORDINAL Bug170.dfy(70,14): Info: A#[_k] @@ -42,7 +42,6 @@ Bug170.dfy(72,7): Info: A#[_k - 1] Bug170.dfy(73,6): Info: AA#[_k - 1] Bug170.dfy(66,12): Info: A#[_k - 1] Bug170.dfy(69,14): Info: AA# decreases _k, x -Bug170.dfy(69,14): Info: AA# {:induction _k, x} Bug170.dfy(50,11): Info: Some instances of this call are not inlined. Bug170.dfy(57,11): Info: Some instances of this call are not inlined. diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/GHC-MergeSort.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/GHC-MergeSort.dfy index a4022dc988a..bd80b089d5c 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/GHC-MergeSort.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/GHC-MergeSort.dfy @@ -472,6 +472,17 @@ lemma stable_sequences(g: G, xs: List) case Cons(a, ys) => match ys { case Nil => + calc { + flatten(sequences(xs)); + // def. sequences, since xs == Cons(a, Nil) + flatten(Cons(xs, Nil)); + // def. flatten + append(xs, flatten(Nil)); + // def. flatten + append(xs, Nil); + { append_Nil(xs); } + xs; + } case Cons(b, zs) => if !Below(a, b) { calc { diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Lucas-up.legacy.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Lucas-up.legacy.dfy index 38413c3be06..5ba9023a9a7 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Lucas-up.legacy.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Lucas-up.legacy.dfy @@ -66,7 +66,7 @@ ghost function binom(a: nat, b: nat): nat // div-2 is applied to both arguments, except in the case where // the first argument to "binom" is even and the second argument // is odd, in which case "binom" is always even. -lemma {:resource_limit "8e6"} Lucas_Binary(a: nat, b: nat) +lemma {:resource_limit "8e6"} {:induction a, b} {:nowarn} Lucas_Binary(a: nat, b: nat) ensures EVEN(binom(2*a, 2*b + 1)) ensures EVEN(binom(2*a, 2*b)) <==> EVEN(binom(a, b)) ensures EVEN(binom(2*a + 1, 2*b + 1)) <==> EVEN(binom(a, b)) @@ -83,7 +83,7 @@ lemma {:resource_limit "8e6"} Lucas_Binary(a: nat, b: nat) } // Here is an alternative way to phrase the previous lemma. -lemma {:resource_limit "200e6"} Lucas_Binary'(a: nat, b: nat) +lemma {:resource_limit "200e6"} {:induction a, b} {:nowarn} Lucas_Binary'(a: nat, b: nat) ensures binom(2*a, 2*b) % 2 == binom(a, b) % 2 ensures binom(2*a, 2*b + 1) % 2 == 0 ensures binom(2*a + 1, 2*b) % 2 == binom(a, b) % 2 diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/UnionFind.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/UnionFind.dfy index d80ac5cac0c..3831148a1cc 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/UnionFind.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/UnionFind.dfy @@ -264,7 +264,7 @@ module M3 refines M2 { } } - lemma {:autocontracts false} ReachUnaffectedByChangeFromRoot'(d: nat, e: Element, r: Element, C: CMap, td: nat, r0: Element, r1: Element, C': CMap) + lemma {:autocontracts false} {:induction d, e, r, C} {:nowarn} ReachUnaffectedByChangeFromRoot'(d: nat, e: Element, r: Element, C: CMap, td: nat, r0: Element, r1: Element, C': CMap) requires GoodCMap(C) requires e in C && Reaches(d, e, r, C) requires r0 in C && r1 in C && C[r0].Root? && C[r1].Root? && C[r0].depth == C[r1].depth && r0 != r1 diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-977.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-977.dfy.expect index a88c1419457..db3dee30b5b 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-977.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-977.dfy.expect @@ -28,8 +28,17 @@ git-issue-977.dfy(84,16): Info: decreases num git-issue-977.dfy(129,16): Info: decreases k, num git-issue-977.dfy(146,16): Info: decreases k, num git-issue-977.dfy(54,6): Info: {:induction num} +git-issue-977.dfy(54,6): Info: {:inductionPattern GreatestPredNat(num)} +git-issue-977.dfy(54,6): Info: {:inductionPattern GreatestPredOrd(num)} +git-issue-977.dfy(54,6): Info: {:inductionPattern Pred(num)} git-issue-977.dfy(61,6): Info: {:induction k, num} +git-issue-977.dfy(61,6): Info: {:inductionPattern RicochetOrd(k, num)} +git-issue-977.dfy(61,6): Info: {:inductionPattern GreatestManualOrd(k, num)} +git-issue-977.dfy(61,6): Info: {:inductionPattern GreatestPredOrd#[k](num)} git-issue-977.dfy(77,6): Info: {:induction k, num} +git-issue-977.dfy(77,6): Info: {:inductionPattern RicochetNat(k, num)} +git-issue-977.dfy(77,6): Info: {:inductionPattern GreatestManualNat(k, num)} +git-issue-977.dfy(77,6): Info: {:inductionPattern GreatestPredNat#[k](num)} git-issue-977.dfy(71,4): Info: ensures GreatestPredOrd#[m](num) git-issue-977.dfy(71,4): Info: ensures GreatestManualOrd(m, num) git-issue-977.dfy(71,4): Info: ensures RicochetOrd(m, num) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/github-issue-4804.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/github-issue-4804.dfy index 35dc939d8eb..6516b3be1a8 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/github-issue-4804.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/github-issue-4804.dfy @@ -1,4 +1,4 @@ -// RUN: %exits-with 4 %verify --allow-axioms --resource-limit 1000000 %s > %t +// RUN: %exits-with 4 %verify --allow-axioms --resource-limit 1200 %s > %t // RUN: %diff "%s.expect" "%t" module Power { diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/hofs/SumSum.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/hofs/SumSum.dfy index 4a7f8894e41..bda1eb00196 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/hofs/SumSum.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/hofs/SumSum.dfy @@ -20,6 +20,7 @@ lemma ExchangeEta(n: nat, f: int -> int, g: int -> int) requires forall i :: 0 <= i < n ==> f(i) == g(i) ensures Sum(n, x => f(x)) == Sum(n, x => g(x)) { + if n != 0 { ExchangeEta(n - 1, f, g); } } lemma NestedAlphaRenaming(n: nat, g: (int,int) -> int) @@ -37,10 +38,10 @@ lemma Distribute(n: nat, f: int -> int, g: int -> int) { } -lemma {:induction false} PrettyBasicBetaReduction(n: nat, g: (int,int) -> int, i: int) +lemma PrettyBasicBetaReduction(n: nat, g: (int,int) -> int, i: int) ensures (x => Sum(n, y => g(x,y)))(i) == Sum(n, y => g(i,y)) { - // NOTE: This proof is by induction on n (it can be done automatically) + // NOTE: This proof is by induction on n if n == 0 { calc { (x => Sum(n, y => g(x,y)))(i); @@ -62,7 +63,6 @@ lemma {:induction false} PrettyBasicBetaReduction(n: nat, g: (int,int) -> int, i lemma BetaReduction0(n: nat, g: (int,int) -> int, i: int) ensures (x => Sum(n, y => g(x,y)))(i) == Sum(n, y => g(i,y)) { - // automatic proof by induction on n } lemma BetaReduction1(n': nat, g: (int,int) -> int, i: int) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/lambdas/MatrixAssoc.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/lambdas/MatrixAssoc.dfy index 0c777579f63..9a528b39724 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/lambdas/MatrixAssoc.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/lambdas/MatrixAssoc.dfy @@ -73,7 +73,7 @@ lemma distr_mult(f: Index -> int, x: int) /** Σ_k (Σ_l f(k,l)) == Σ_l (Σ_k f(k,l)) */ /** proof by induction */ -lemma sum_assoc_n(m: Matrix, n1: nat, n2: nat) +lemma {:induction m, n1, n2} {:nowarn} sum_assoc_n(m: Matrix, n1: nat, n2: nat) requires n1 <= N && n2 <= N ensures Sum_n((k: Index) => Sum_n((l: Index) => m(k)(l), n1), n2) == diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy index 82bbf55f80c..171bc0a8612 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy @@ -11,3 +11,11 @@ lemma TestTriggerWithLambdaExpression(n: nat, f: int -> int, g: int -> int) assert forall n: nat, f: int -> int :: Sum(n, x => 500 + f(x)) == n + Sum(n, f); // error: this does not hold } +// With an explicit :induction attribute, the induction hypothesis emitted lets the proof of the lemma go through. +// However, there is no good matching pattern for the induction hypothesis, so a warning is generated. +// For a manual proof of this lemma, see lemma ExchangeEta in hofs/SumSum.dfy. +lemma {:induction n} ExchangeEtaWithInductionAttribute(n: nat, f: int -> int, g: int -> int) // warning: no trigger + requires forall i :: 0 <= i < n ==> f(i) == g(i) + ensures Sum(n, x => f(x)) == Sum(n, x => g(x)) +{ +} diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.expect index dd60a022868..815d9943bf2 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.expect @@ -1,3 +1,4 @@ InductionWithoutTriggers.dfy(17,21): Warning: Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. Change or remove the {:induction} attribute to generate a different induction hypothesis, or add {:nowarn} to silence this warning. For more information, see the section quantifier instantiation rules in the reference manual. +InductionWithoutTriggers.dfy(11,9): Error: assertion might not hold Dafny program verifier finished with 2 verified, 1 error From 659d4bada49288f2060bc7028e43be1a8671e5d5 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 16 Oct 2024 07:59:42 -0700 Subject: [PATCH 065/151] Tooltip named expressions from trigger selection --- .../Triggers/ComprehensionTriggerGenerator.cs | 15 +++++++++------ .../LitTest/triggers/let-expressions.dfy.expect | 4 ++-- .../loop-detection-is-not-too-strict.dfy.expect | 6 +++--- .../loop-detection-looks-at-ranges-too.dfy.expect | 4 ++-- ...loop-detection-messages--unit-tests.dfy.expect | 10 +++++----- .../matrix-accesses-are-triggers.dfy.expect | 2 +- ...t-look-like-the-triggers-they-match.dfy.expect | 6 +++--- ...ting-triggers-recovers-expressivity.dfy.expect | 4 ++-- ...ppressing-warnings-behaves-properly.dfy.expect | 2 +- 9 files changed, 28 insertions(+), 25 deletions(-) diff --git a/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs b/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs index 65723ba9829..91b5e565632 100644 --- a/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs +++ b/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs @@ -231,15 +231,18 @@ internal void CommitTriggers(SystemModuleManager systemModuleManager) { } } - public List> GetTriggers() { + public List> GetTriggers(bool includeTriggersThatRequireNamedExpressions) { var triggers = new List>(); foreach (var triggerWriter in partWriters) { - foreach (var triggerTerms in triggerWriter.Candidates) { - var trigger = new List(); - foreach (var triggerTerm in triggerTerms.Terms) { - trigger.Add(triggerTerm.Expr); + if (includeTriggersThatRequireNamedExpressions || triggerWriter.NamedExpressions.Count == 0) { + foreach (var triggerTerms in triggerWriter.Candidates) { + var trigger = new List(); + foreach (var triggerTerm in triggerTerms.Terms) { + trigger.Add(triggerTerm.Expr); + } + + triggers.Add(trigger); } - triggers.Add(trigger); } } return triggers; diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/let-expressions.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/let-expressions.dfy.expect index c2731da3a7c..43c6c5c9d54 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/let-expressions.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/let-expressions.dfy.expect @@ -1,6 +1,6 @@ let-expressions.dfy(6,8): Info: Selected triggers: {s[i]} let-expressions.dfy(7,8): Info: Selected triggers: {s[i]} -let-expressions.dfy(8,8): Info: Selected triggers: {s[_t#0], s[i]} -let-expressions.dfy(9,8): Info: Selected triggers: {s[_t#0], s[i]} +let-expressions.dfy(8,8): Info: Selected triggers: {s[_t#0], s[i]} where _t#0 := i + 1 +let-expressions.dfy(9,8): Info: Selected triggers: {s[_t#0], s[i]} where _t#0 := i + 1 Dafny program verifier finished with 1 verified, 0 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-is-not-too-strict.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-is-not-too-strict.dfy.expect index aef0837895c..4c9f454a9c4 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-is-not-too-strict.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-is-not-too-strict.dfy.expect @@ -2,14 +2,14 @@ loop-detection-is-not-too-strict.dfy(15,9): Info: Selected triggers: {P(y, x)}, {P(x, y)} loop-detection-is-not-too-strict.dfy(18,9): Info: Selected triggers: {P(y, x)}, {P(x, y)} -loop-detection-is-not-too-strict.dfy(21,9): Info: Selected triggers: {P(x, _t#0), P(x, y)} +loop-detection-is-not-too-strict.dfy(21,9): Info: Selected triggers: {P(x, _t#0), P(x, y)} where _t#0 := y + 1 loop-detection-is-not-too-strict.dfy(26,9): Info: Selected triggers: {Q(x)} loop-detection-is-not-too-strict.dfy(27,9): Info: Selected triggers: {Q(x)} loop-detection-is-not-too-strict.dfy(28,9): Info: Selected triggers: {P(x, z)}, {P(x, 1)} loop-detection-is-not-too-strict.dfy(33,9): Info: Selected triggers: {Q(x)} loop-detection-is-not-too-strict.dfy(34,9): Info: Selected triggers: {Q(x)} -loop-detection-is-not-too-strict.dfy(36,9): Info: Selected triggers: {Q(_t#0), Q(x)} -loop-detection-is-not-too-strict.dfy(40,9): Info: Selected triggers: {Q(_t#0), Q(x)} +loop-detection-is-not-too-strict.dfy(36,9): Info: Selected triggers: {Q(_t#0), Q(x)} where _t#0 := if z > 1 then x else 3 * z + 1 +loop-detection-is-not-too-strict.dfy(40,9): Info: Selected triggers: {Q(_t#0), Q(x)} where _t#0 := x + 1 Dafny program verifier finished with 1 verified, 0 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-looks-at-ranges-too.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-looks-at-ranges-too.dfy.expect index a6679d8c1ea..992d2e551f2 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-looks-at-ranges-too.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-looks-at-ranges-too.dfy.expect @@ -1,4 +1,4 @@ -loop-detection-looks-at-ranges-too.dfy(11,17): Info: Selected triggers: {P(_t#0), P(x)} -loop-detection-looks-at-ranges-too.dfy(13,17): Info: Selected triggers: {P(x), P(_t#0)} +loop-detection-looks-at-ranges-too.dfy(11,17): Info: Selected triggers: {P(_t#0), P(x)} where _t#0 := x + 1 +loop-detection-looks-at-ranges-too.dfy(13,17): Info: Selected triggers: {P(x), P(_t#0)} where _t#0 := x + 1 Dafny program verifier finished with 1 verified, 0 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-messages--unit-tests.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-messages--unit-tests.dfy.expect index 374f02b7f9b..3d8d9a1765e 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-messages--unit-tests.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-messages--unit-tests.dfy.expect @@ -1,21 +1,21 @@ loop-detection-messages--unit-tests.dfy(11,9): Info: Selected triggers: {f(f(i))} Rejected triggers: {f(i)} (may loop with "f(f(i))") -loop-detection-messages--unit-tests.dfy(12,9): Info: Selected triggers: {f(_t#0), f(i)} -loop-detection-messages--unit-tests.dfy(13,9): Info: Selected triggers: {f(_t#0), f(i)} +loop-detection-messages--unit-tests.dfy(12,9): Info: Selected triggers: {f(_t#0), f(i)} where _t#0 := i + 1 +loop-detection-messages--unit-tests.dfy(13,9): Info: Selected triggers: {f(_t#0), f(i)} where _t#0 := i + 1 loop-detection-messages--unit-tests.dfy(15,9): Info: Quantifier was split into 2 parts. Better verification performance and error reporting may be obtained by splitting the quantifier in source. For more information, see the section quantifier instantiation rules in the reference manual. loop-detection-messages--unit-tests.dfy(15,9): Info: Part #0 is 'false ==> f(i) == f(_t#0)' - Selected triggers: {f(_t#0), f(i)} + Selected triggers: {f(_t#0), f(i)} where _t#0 := i + 1 loop-detection-messages--unit-tests.dfy(15,9): Info: Part #1 is 'false ==> f(i) == g(i)' Selected triggers: {g(i)}, {f(i)} loop-detection-messages--unit-tests.dfy(16,9): Info: Quantifier was split into 2 parts. Better verification performance and error reporting may be obtained by splitting the quantifier in source. For more information, see the section quantifier instantiation rules in the reference manual. loop-detection-messages--unit-tests.dfy(16,9): Info: Part #0 is 'false ==> f(i) == f(_t#0)' - Selected triggers: {f(_t#0), f(i)} + Selected triggers: {f(_t#0), f(i)} where _t#0 := i + 1 loop-detection-messages--unit-tests.dfy(16,9): Info: Part #1 is 'false ==> f(i) == f(i)' Selected triggers: {f(i)} loop-detection-messages--unit-tests.dfy(17,9): Info: Quantifier was split into 2 parts. Better verification performance and error reporting may be obtained by splitting the quantifier in source. For more information, see the section quantifier instantiation rules in the reference manual. loop-detection-messages--unit-tests.dfy(17,9): Info: Part #0 is 'false ==> f(i) == f(_t#0)' - Selected triggers: {f(_t#0), f(i)} + Selected triggers: {f(_t#0), f(i)} where _t#0 := i + 1 loop-detection-messages--unit-tests.dfy(17,9): Info: Part #1 is 'false ==> f(i) == f(i)' Selected triggers: {f(i)} loop-detection-messages--unit-tests.dfy(19,9): Info: Selected triggers: {f(i)} diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/matrix-accesses-are-triggers.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/matrix-accesses-are-triggers.dfy.expect index 4dc44f54ccf..4de25509099 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/matrix-accesses-are-triggers.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/matrix-accesses-are-triggers.dfy.expect @@ -1,4 +1,4 @@ -matrix-accesses-are-triggers.dfy(7,11): Info: Selected triggers: {m[j, _t#0], m[i, j]} +matrix-accesses-are-triggers.dfy(7,11): Info: Selected triggers: {m[j, _t#0], m[i, j]} where _t#0 := i + 1 matrix-accesses-are-triggers.dfy(7,82): Error: index 0 out of range matrix-accesses-are-triggers.dfy(7,86): Error: index 1 out of range diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect index 592b2b34dcd..ccd871551da 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect @@ -1,9 +1,9 @@ -some-terms-do-not-look-like-the-triggers-they-match.dfy(12,17): Info: Selected triggers: {s[_t#0], s[x]} +some-terms-do-not-look-like-the-triggers-they-match.dfy(12,17): Info: Selected triggers: {s[_t#0], s[x]} where _t#0 := x + 1 some-terms-do-not-look-like-the-triggers-they-match.dfy(15,17): Info: Quantifier was split into 2 parts. Better verification performance and error reporting may be obtained by splitting the quantifier in source. For more information, see the section quantifier instantiation rules in the reference manual. some-terms-do-not-look-like-the-triggers-they-match.dfy(15,17): Info: Part #0 is 'x in s ==> s[_t#0] > 0' - Selected triggers: {s[_t#0], s[x]} + Selected triggers: {s[_t#0], s[x]} where _t#0 := x + 1 some-terms-do-not-look-like-the-triggers-they-match.dfy(15,17): Info: Part #1 is 'x in s ==> _t#0 !in s' - Selected triggers: {s[_t#0], s[x]} + Selected triggers: {s[_t#0], s[x]} where _t#0 := x + 2 some-terms-do-not-look-like-the-triggers-they-match.dfy(24,18): Info: Selected triggers: {x in s + t} some-terms-do-not-look-like-the-triggers-they-match.dfy(25,18): Info: Selected triggers: {x in t}, {x in s} diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/splitting-triggers-recovers-expressivity.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/splitting-triggers-recovers-expressivity.dfy.expect index e66366bd3ec..b06c081fc7a 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/splitting-triggers-recovers-expressivity.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/splitting-triggers-recovers-expressivity.dfy.expect @@ -1,7 +1,7 @@ splitting-triggers-recovers-expressivity.dfy(12,10): Info: Selected triggers: - {P(_t#0), Q(i)}, {P(_t#0), P(i)} + {P(_t#0), Q(i)}, {P(_t#0), P(i)} where _t#0 := i + 1 splitting-triggers-recovers-expressivity.dfy(17,11): Info: Selected triggers: - {P(_t#0), Q(j)}, {P(_t#0), P(j)} + {P(_t#0), Q(j)}, {P(_t#0), P(j)} where _t#0 := j + 1 splitting-triggers-recovers-expressivity.dfy(26,10): Info: Selected triggers: {Q(i)} Rejected triggers: {P(i)} (may loop with "P(i + 1)") splitting-triggers-recovers-expressivity.dfy(33,11): Info: Selected triggers: {Q(j)} diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/suppressing-warnings-behaves-properly.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/suppressing-warnings-behaves-properly.dfy.expect index 4abcebe8941..80c6a424512 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/suppressing-warnings-behaves-properly.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/suppressing-warnings-behaves-properly.dfy.expect @@ -5,7 +5,7 @@ suppressing-warnings-behaves-properly.dfy(14,9): Info: Selected triggers: {f(n)} suppressing-warnings-behaves-properly.dfy(15,9): Info: Selected triggers: {f(n)} suppressing-warnings-behaves-properly.dfy(16,9): Info: The attribute {:autotriggers false} may cause brittle verification. It's better to remove this attribute, or as a second option, manually define a trigger using {:trigger}. For more information, see the section quantifier instantiation rules in the reference manual. suppressing-warnings-behaves-properly.dfy(18,9): Info: Selected triggers: - {g(n), f(_t#0)}, {f(_t#0), f(n)} + {g(n), f(_t#0)}, {f(_t#0), f(n)} where _t#0 := n + 1 suppressing-warnings-behaves-properly.dfy(19,9): Info: Selected triggers: {f(n)} suppressing-warnings-behaves-properly.dfy(20,9): Info: The attribute {:autotriggers false} may cause brittle verification. It's better to remove this attribute, or as a second option, manually define a trigger using {:trigger}. For more information, see the section quantifier instantiation rules in the reference manual. From 6b5b207386f57a6329714bb5ad5eceb4db678b85 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 16 Oct 2024 08:00:18 -0700 Subject: [PATCH 066/151] chore: Remove deprecated semi-colons --- .../LitTest/dafny3/SimpleCoinduction.dfy | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/SimpleCoinduction.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/SimpleCoinduction.dfy index 352efe03eab..5ac106cae58 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/SimpleCoinduction.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/SimpleCoinduction.dfy @@ -1,4 +1,4 @@ -// RUN: %testDafnyForEachResolver "%s" -- --allow-deprecation +// RUN: %testDafnyForEachResolver "%s" codatatype Stream = Cons(head: T, tail: Stream) @@ -22,7 +22,7 @@ ghost function Inc(s: Stream): Stream } lemma {:induction false} UpLemma(k: nat, n: int) - ensures Inc(Up(n)) ==#[k] Up(n+1); + ensures Inc(Up(n)) ==#[k] Up(n+1) { if (k != 0) { UpLemma(k-1, n+1); @@ -30,18 +30,18 @@ lemma {:induction false} UpLemma(k: nat, n: int) } greatest lemma {:induction false} CoUpLemma(n: int) - ensures Inc(Up(n)) == Up(n+1); + ensures Inc(Up(n)) == Up(n+1) { CoUpLemma(n+1); } lemma UpLemma_Auto(k: nat, n: int, nn: int) - ensures nn == n+1 ==> Inc(Up(n)) ==#[k] Up(nn); // note: it would be nice to do an automatic rewrite (from "ensures Inc(Up(n)) ==#[k] Up(n+1)") to obtain the good trigger here + ensures nn == n+1 ==> Inc(Up(n)) ==#[k] Up(nn) // note: it would be nice to do an automatic rewrite (from "ensures Inc(Up(n)) ==#[k] Up(n+1)") to obtain the good trigger here { } greatest lemma CoUpLemma_Auto(n: int, nn: int) - ensures nn == n+1 ==> Inc(Up(n)) == Up(nn); // see comment above + ensures nn == n+1 ==> Inc(Up(n)) == Up(nn) // see comment above { } @@ -53,8 +53,9 @@ ghost function Repeat(n: int): Stream } greatest lemma RepeatLemma(n: int) - ensures Inc(Repeat(n)) == Repeat(n+1); + ensures Inc(Repeat(n)) == Repeat(n+1) { + RepeatLemma(n); } // ----------------------------------------------------------------------- @@ -65,7 +66,7 @@ greatest predicate True(s: Stream) } greatest lemma AlwaysTrue(s: Stream) - ensures True(s); + ensures True(s) { } @@ -75,7 +76,7 @@ greatest predicate AlsoTrue(s: Stream) } greatest lemma AlsoAlwaysTrue(s: Stream) - ensures AlsoTrue(s); + ensures AlsoTrue(s) { } @@ -85,7 +86,7 @@ greatest predicate TT(y: int) } greatest lemma AlwaysTT(y: int) - ensures TT(y); + ensures TT(y) { } @@ -116,11 +117,11 @@ greatest predicate AtMost(a: IList, b: IList) } greatest lemma ZerosAndOnes_Theorem0() - ensures AtMost(zeros(), ones()); + ensures AtMost(zeros(), ones()) { } greatest lemma ZerosAndOnes_Theorem1() - ensures Append(zeros(), ones()) == zeros(); + ensures Append(zeros(), ones()) == zeros() { } From 3fc56d3d4e5b13fba010b3720cd6253dae95828c Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 16 Oct 2024 08:01:01 -0700 Subject: [PATCH 067/151] Add tests for ternary expressions in triggers --- .../LitTests/LitTest/dafny0/CoPrefix.dfy | 2 +- .../LitTest/dafny0/CoPrefix.dfy.expect | 3 +-- .../LitTests/LitTest/dafny3/Abstemious.dfy | 22 +++++++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoPrefix.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoPrefix.dfy index 708f0291219..f23c066103c 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoPrefix.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoPrefix.dfy @@ -135,7 +135,7 @@ greatest lemma {:induction false} Compare(h: T) Compare(Next(h)); if { case true => - assert FF(h).tail == GG(h).tail; // error: full equality is not known here + assert FF(h).tail == GG(h).tail; // yes, this full equality is a focal predicate, so it's rewritten into ==#[_k - 1] case true => assert FF(h) ==#[_k] GG(h); // yes, this is the postcondition to be proved, and it is known to hold case true => diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoPrefix.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoPrefix.dfy.expect index dd2100b12db..d5f27399a5a 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoPrefix.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoPrefix.dfy.expect @@ -3,7 +3,6 @@ CoPrefix.dfy(76,55): Error: cannot prove termination; try supplying a decreases CoPrefix.dfy(114,0): Error: a postcondition could not be proved on this return path CoPrefix.dfy(113,10): Related location: this is the postcondition that could not be proved CoPrefix.dfy(101,16): Related location: this proposition could not be proved -CoPrefix.dfy(138,24): Error: assertion might not hold CoPrefix.dfy(142,24): Error: assertion might not hold CoPrefix.dfy(117,22): Related location: this proposition could not be proved CoPrefix.dfy(151,0): Error: a postcondition could not be proved on this return path @@ -17,4 +16,4 @@ CoPrefix.dfy(205,6): Error: the calculation step between the previous line and t CoPrefix.dfy(207,6): Error: the calculation step between the previous line and this line could not be proved CoPrefix.dfy(220,12): Error: ORDINAL subtraction might underflow a limit ordinal (that is, RHS might be too large) -Dafny program verifier finished with 13 verified, 12 errors +Dafny program verifier finished with 13 verified, 11 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/Abstemious.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/Abstemious.dfy index 5eb338afaa8..258e7be2e6f 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/Abstemious.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/Abstemious.dfy @@ -206,3 +206,25 @@ lemma Fib_Correct(n: nat) } } } + +// --------------- ternary expression is a trigger --------------- + +lemma OrdinalLemma(k: ORDINAL) + ensures OhOnes().tail ==#[k] ones() +{ + // automatic induction on k +} + +lemma NaturalLemma(k: nat) + ensures OhOnes().tail ==#[k] ones() +{ + // automatic induction on k +} + +lemma Quantifier() + // the following quantifiers use the entire body as a trigger (previously, ternary expressions + // had not been considered as trigger candidates) + requires forall k: nat :: OhOnes().tail ==#[k] ones() + requires forall k: ORDINAL :: OhOnes().tail ==#[k] ones() +{ +} From 73d2d61568350f650471d7fc2f4703fb063d2a07 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 16 Oct 2024 08:01:13 -0700 Subject: [PATCH 068/151] Adjust resource count in test --- .../LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect index d56b584921a..2730c4fe541 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect @@ -31,5 +31,5 @@ CoinductiveProofs.dfy(208,21): Related location: this is the postcondition that CoinductiveProofs.dfy(4,23): Related location: this proposition could not be proved Dafny program verifier finished with 23 verified, 12 errors -Total resources used is 770796 +Total resources used is 770576 Max resources used by VC is 60436 From f4815f45def8c8e98b276dca679ec0722c9decaa Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 16 Oct 2024 08:31:43 -0700 Subject: [PATCH 069/151] Add release notes --- docs/dev/news/5835.feat | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 docs/dev/news/5835.feat diff --git a/docs/dev/news/5835.feat b/docs/dev/news/5835.feat new file mode 100644 index 00000000000..d9fb6f80a25 --- /dev/null +++ b/docs/dev/news/5835.feat @@ -0,0 +1,5 @@ +Fill in matching patterns for the quantifiers introduced by automatic induction to represent the induction hypothesis. Suppress the generation of the induction hypothesis if no such matching patterns are found. Enhance tooltips accordingly. This feature is added to make stabilize verification, but by sometimes not generating induction hypotheses, some automatic proofs may no longer go through. + +Improve the selection of induction variables. + +Allow codatatype equality in matching patterns and as a focal predicate for extreme predicates. From e4e4ce8073ebddafd932b8ea8aabdf47274658bd Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 17 Oct 2024 02:35:02 -0700 Subject: [PATCH 070/151] Help proof --- Source/DafnyCore/Backends/Rust/Dafny-compiler-rust-proofs.dfy | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/DafnyCore/Backends/Rust/Dafny-compiler-rust-proofs.dfy b/Source/DafnyCore/Backends/Rust/Dafny-compiler-rust-proofs.dfy index 9b7e31813f0..a1110275254 100644 --- a/Source/DafnyCore/Backends/Rust/Dafny-compiler-rust-proofs.dfy +++ b/Source/DafnyCore/Backends/Rust/Dafny-compiler-rust-proofs.dfy @@ -136,6 +136,7 @@ module {:extern "DafnyToRustCompilerProofs"} {:compile false} DafnyToRustCompile if i[0] == '_' { assert |i| >= 2 && i[1] in "_qkh" && IsDafnyEncodedIdTail(i[2..]); + assert [i[0]] + replaceDots(i[1..]) == [i[0]] + [i[1]] + replaceDots(i[2..]); ReplaceDotsInvertible(i[2..]); assert ReverseReplaceDots(replaceDots(i)) == i; } else { From 3a0f1c06cf57ee7c3af64342668da771dfb4cae9 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 17 Oct 2024 02:43:53 -0700 Subject: [PATCH 071/151] Update test, which no longer exhausts resources --- .../LitTests/LitTest/verification/nonLinearArithmetic.dfy | 4 ++-- .../LitTest/verification/nonLinearArithmetic.dfy.expect | 7 ++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/verification/nonLinearArithmetic.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/verification/nonLinearArithmetic.dfy index e76d541dd1c..83a50b795a4 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/verification/nonLinearArithmetic.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/verification/nonLinearArithmetic.dfy @@ -1,5 +1,5 @@ -// RUN: ! %verify "%s" --disable-nonlinear-arithmetic --allow-axioms --resource-limit 1e6 > "%t" -// RUN: ! %verify "%s" --allow-axioms --resource-limit 1e6 >> "%t" +// RUN: %verify "%s" --disable-nonlinear-arithmetic --allow-axioms --resource-limit 1e6 > "%t" +// RUN: %verify "%s" --allow-axioms --resource-limit 1e6 >> "%t" // RUN: %diff "%s.expect" "%t" module Power0 { diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/verification/nonLinearArithmetic.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/verification/nonLinearArithmetic.dfy.expect index 0337e36f8f0..9efe4297819 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/verification/nonLinearArithmetic.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/verification/nonLinearArithmetic.dfy.expect @@ -1,7 +1,4 @@ -nonLinearArithmetic.dfy(97,8): Error: Verification out of resource (Power3.LemmaPowSubAddCancel) -Dafny program verifier finished with 15 verified, 0 errors, 1 out of resource -nonLinearArithmetic.dfy(21,8): Error: Verification out of resource (Power0.LemmaPowSubAddCancel) -nonLinearArithmetic.dfy(97,8): Error: Verification out of resource (Power3.LemmaPowSubAddCancel) +Dafny program verifier finished with 16 verified, 0 errors -Dafny program verifier finished with 14 verified, 0 errors, 2 out of resource +Dafny program verifier finished with 16 verified, 0 errors From 703c6610fa0fa2ec239fb423cca5b4755f28e560 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 17 Oct 2024 03:58:08 -0700 Subject: [PATCH 072/151] Improve proofs --- .../binaries/DafnyStandardLibraries-cs.doo | Bin 1518 -> 1518 bytes .../binaries/DafnyStandardLibraries-go.doo | Bin 1539 -> 1539 bytes .../binaries/DafnyStandardLibraries-java.doo | Bin 1509 -> 1509 bytes .../binaries/DafnyStandardLibraries-js.doo | Bin 2027 -> 2027 bytes .../DafnyStandardLibraries-notarget.doo | Bin 1498 -> 1498 bytes .../binaries/DafnyStandardLibraries-py.doo | Bin 1505 -> 1505 bytes .../binaries/DafnyStandardLibraries.doo | Bin 57213 -> 57277 bytes .../src/Std/Arithmetic/LittleEndianNat.dfy | 13 +++++++++++-- .../src/Std/Arithmetic/Mul.dfy | 17 +++++++++++++++-- .../src/Std/Arithmetic/Power.dfy | 3 +-- .../src/Std/Collections/Seq.dfy | 13 +++++++------ 11 files changed, 34 insertions(+), 12 deletions(-) diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-cs.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-cs.doo index 472917014fca63fcff0f941f6ff184cb89df615d..a945bf26f7ca8ac9f55d3996bc44afa22e25114f 100644 GIT binary patch delta 99 zcmaFI{f?VAz?+#xgn@y9gW;8I;6~o*jLblK^K!o7Fz^U4Ft7tP!N7sZ4_TD}W0Vxq delta 79 zcmZqXY3AV#@MdNaVPIh3V92g;+sHeekr_yDUe5T48N`^}!z#(RWb!grIawetGU3eP Xcl($b7mdGWGoZD delta 79 zcmaFL{gj(Gz?+#xgn@y9gCVWLZ6oh=MrI(rc{$?|W)Nd?9;+ndlF1#cao7o7o7ljh?Sd|mzkDYT%uQ!pPLil&B!Fe3^yxs<6G}DbC?+zPVqA^u*38L=_Ql*vnl}q DzIz>$ diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries.doo index 66b99929968ffbb3534c212b61b9ffa1e30d8de9..6b731da1c493885744816c5940838ed97f473671 100644 GIT binary patch delta 54022 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̪<5d6z8jW$$;eA=fH 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 T00000z>{0w90oVv00000jxzvp delta 53966 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 0 ensures x * y == MulPos(x, y) { - reveal MulPos(); - LemmaMulInductionAuto(x, u => u >= 0 ==> u * y == MulPos(u, y)); + if x == 0 { + assert MulPos(x, y) == 0 by { + reveal MulPos(); + } + } else { + calc { + MulPos(x, y); + { reveal MulPos(x, y); } + y + MulPos(x - 1, y); + { LemmaMulIsMulPos(x - 1, y); } + y + (x - 1) * y; + { LemmaMulDistributes(); } + x * y; + } + } } /* ensures that the basic properties of multiplication, including the identity and zero properties */ diff --git a/Source/DafnyStandardLibraries/src/Std/Arithmetic/Power.dfy b/Source/DafnyStandardLibraries/src/Std/Arithmetic/Power.dfy index e8c99e338a6..62419d5ea8d 100644 --- a/Source/DafnyStandardLibraries/src/Std/Arithmetic/Power.dfy +++ b/Source/DafnyStandardLibraries/src/Std/Arithmetic/Power.dfy @@ -417,9 +417,8 @@ module {:disableNonlinearArithmetic} Std.Arithmetic.Power { ensures Pow(b, e1) <= Pow(b, e2) { reveal Pow(); - LemmaPowAuto(); var f := e => 0 <= e ==> Pow(b, e1) <= Pow(b, e1 + e); - forall i {:trigger IsLe(0, i)} | IsLe(0, i) && f(i) + forall i | IsLe(0, i) && f(i) ensures f(i + 1) { calc { diff --git a/Source/DafnyStandardLibraries/src/Std/Collections/Seq.dfy b/Source/DafnyStandardLibraries/src/Std/Collections/Seq.dfy index eaddebc3a3c..7dbfe3c42c6 100644 --- a/Source/DafnyStandardLibraries/src/Std/Collections/Seq.dfy +++ b/Source/DafnyStandardLibraries/src/Std/Collections/Seq.dfy @@ -781,10 +781,10 @@ module Std.Collections.Seq { /* Filtering a sequence is distributive over concatenation. That is, concatenating two sequences and then using "Filter" is the same as using "Filter" on each sequence separately, and then concatenating the two resulting sequences. */ - lemma {:isolate_assertions} - LemmaFilterDistributesOverConcat(f: (T ~> bool), xs: seq, ys: seq) - requires forall i {:trigger xs[i]}:: 0 <= i < |xs| ==> f.requires(xs[i]) - requires forall j {:trigger ys[j]}:: 0 <= j < |ys| ==> f.requires(ys[j]) + lemma {:isolate_assertions} {:induction false} + LemmaFilterDistributesOverConcat(f: T ~> bool, xs: seq, ys: seq) + requires forall i :: 0 <= i < |xs| ==> f.requires(xs[i]) + requires forall j :: 0 <= j < |ys| ==> f.requires(ys[j]) ensures Filter(f, xs + ys) == Filter(f, xs) + Filter(f, ys) { reveal Filter(); @@ -793,10 +793,11 @@ module Std.Collections.Seq { } else { calc { Filter(f, xs + ys); - { assert {:split_here} (xs + ys)[0] == xs[0]; assert (xs + ys)[1..] == xs[1..] + ys; } + { assert (xs + ys)[0] == xs[0]; assert (xs + ys)[1..] == xs[1..] + ys; } Filter(f, [xs[0]]) + Filter(f, xs[1..] + ys); + { LemmaFilterDistributesOverConcat(f, xs[1..], ys); } Filter(f, [xs[0]]) + (Filter(f, xs[1..]) + Filter(f, ys)); - { assert {:split_here} [(xs + ys)[0]] + (xs[1..] + ys) == xs + ys; } + { assert [(xs + ys)[0]] + (xs[1..] + ys) == xs + ys; } Filter(f, xs) + Filter(f, ys); } } From 9c07f8aacfb2fbeaf0ad6f40f14c203a0153efaf Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Sat, 12 Oct 2024 11:38:10 -0700 Subject: [PATCH 073/151] Improve induction heuristics --- .../DafnyCore/Rewriters/InductionHeuristic.cs | 22 ++++++++++++++----- .../DafnyCore/Rewriters/InductionRewriter.cs | 2 +- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Source/DafnyCore/Rewriters/InductionHeuristic.cs b/Source/DafnyCore/Rewriters/InductionHeuristic.cs index 3787ab4303b..1ceab8866f2 100644 --- a/Source/DafnyCore/Rewriters/InductionHeuristic.cs +++ b/Source/DafnyCore/Rewriters/InductionHeuristic.cs @@ -1,3 +1,4 @@ +#nullable enable using System.Diagnostics.Contracts; namespace Microsoft.Dafny; @@ -6,6 +7,8 @@ public static class InductionHeuristic { /// /// Returns 'true' iff by looking at 'expr' the Induction Heuristic determines that induction should be applied to 'n'. + /// Variable 'n' can be passed in as 'null', in which case it stands for 'this'. + /// /// More precisely: /// DafnyInductionHeuristic Return 'true' /// ----------------------- ------------- @@ -17,10 +20,15 @@ public static class InductionHeuristic { /// 5 if 'n' occurs as a prominent subexpression of any argument to a recursive function /// 6 if 'n' occurs as a prominent subexpression of any decreases-influencing argument to a recursive function /// - public static bool VarOccursInArgumentToRecursiveFunction(DafnyOptions options, Expression expr, IVariable n) { + public static bool VarOccursInArgumentToRecursiveFunction(DafnyOptions options, Expression expr, IVariable? n) { switch (options.InductionHeuristic) { case 0: return true; - case 1: return FreeVariablesUtil.ContainsFreeVariable(expr, false, n); + case 1: + if (n == null) { + return FreeVariablesUtil.ContainsFreeVariable(expr, true, null); + } else { + return FreeVariablesUtil.ContainsFreeVariable(expr, false, n); + } default: return VarOccursInArgumentToRecursiveFunction(options, expr, n, false); } } @@ -30,16 +38,18 @@ public static bool VarOccursInArgumentToRecursiveFunction(DafnyOptions options, /// not 'expr' has prominent status in its context. /// DafnyInductionHeuristic cases 0 and 1 are assumed to be handled elsewhere (i.e., a precondition of this method is DafnyInductionHeuristic is at least 2). /// - static bool VarOccursInArgumentToRecursiveFunction(DafnyOptions options, Expression expr, IVariable n, bool exprIsProminent) { + static bool VarOccursInArgumentToRecursiveFunction(DafnyOptions options, Expression expr, IVariable? n, bool exprIsProminent) { Contract.Requires(expr != null); Contract.Requires(n != null); // The following variable is what gets passed down to recursive calls if the subexpression does not itself acquire prominent status. var subExprIsProminent = options.InductionHeuristic == 2 || options.InductionHeuristic == 4 ? /*once prominent, always prominent*/exprIsProminent : /*reset the prominent status*/false; - if (expr is IdentifierExpr) { + if (n != null && expr is IdentifierExpr) { var e = (IdentifierExpr)expr; return exprIsProminent && e.Var == n; + } else if (n == null && expr is ThisExpr) { + return exprIsProminent; } else if (expr is SeqSelectExpr) { var e = (SeqSelectExpr)expr; var q = options.InductionHeuristic < 4 || subExprIsProminent; @@ -108,7 +118,7 @@ static bool VarOccursInArgumentToRecursiveFunction(DafnyOptions options, Express } } else if (expr is DatatypeValue) { var e = (DatatypeValue)expr; - var q = n.Type.IsDatatype ? exprIsProminent : subExprIsProminent; // prominent status continues, if we're looking for a variable whose type is a datatype + var q = n != null && n.Type.IsDatatype ? exprIsProminent : subExprIsProminent; // prominent status continues, if we're looking for a variable whose type is a datatype return e.Arguments.Exists(exp => VarOccursInArgumentToRecursiveFunction(options, exp, n, q)); } else if (expr is UnaryExpr) { var e = (UnaryExpr)expr; @@ -145,7 +155,7 @@ static bool VarOccursInArgumentToRecursiveFunction(DafnyOptions options, Express } else if (expr is StmtExpr) { var e = (StmtExpr)expr; // ignore the statement - return VarOccursInArgumentToRecursiveFunction(options, e.E, n); + return VarOccursInArgumentToRecursiveFunction(options, e.E, n, exprIsProminent); } else if (expr is ITEExpr) { var e = (ITEExpr)expr; diff --git a/Source/DafnyCore/Rewriters/InductionRewriter.cs b/Source/DafnyCore/Rewriters/InductionRewriter.cs index e782569bde6..c8ca384bc12 100644 --- a/Source/DafnyCore/Rewriters/InductionRewriter.cs +++ b/Source/DafnyCore/Rewriters/InductionRewriter.cs @@ -171,7 +171,7 @@ void ComputeInductionVariables(IToken tok, List boundVars, E // Okay, here we go, coming up with good induction setting for the given situation var inductionVariables = new List(); if (lemma is { IsStatic: false }) { - if (args != null || FreeVariablesUtil.ContainsFreeVariable(body, true, null)) { + if (args != null || InductionHeuristic.VarOccursInArgumentToRecursiveFunction(Reporter.Options, body, null)) { inductionVariables.Add(new ThisExpr(lemma)); } } From a420bc2a241684ea7272a5b931f800e2298166eb Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Sat, 12 Oct 2024 11:44:35 -0700 Subject: [PATCH 074/151] =?UTF-8?q?chore:=20Change=20=E2=80=9Cghost=20meth?= =?UTF-8?q?od=E2=80=9D=20to=20=E2=80=9Clemma=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TestFiles/LitTests/LitTest/dafny0/LetExpr.dfy | 4 ++-- .../TestFiles/LitTests/LitTest/dafny0/Termination.dfy | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/LetExpr.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/LetExpr.dfy index 9cdc06d5040..8b3300900aa 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/LetExpr.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/LetExpr.dfy @@ -127,11 +127,11 @@ method Theorem0(n: int) } } -ghost method Theorem1(n: int) +lemma Theorem1(n: int) requires 1 <= n; ensures 1 <= Fib(n); { - // in a ghost method, the induction tactic takes care of it + // in a lemma, the induction tactic takes care of it } ghost function Theorem2(n: int): int diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Termination.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Termination.dfy index 8ac6f9a37ae..26376f13b2a 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Termination.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Termination.dfy @@ -392,7 +392,7 @@ module MultisetTests { if n == 0 then 0 else F'(a, n-1) } - ghost method M(n: nat, b: multiset) + lemma M(n: nat, b: multiset) ensures F(b, n) == 0 // proved via automatic induction { } @@ -410,7 +410,7 @@ module MapTests { if n == 0 then 0 else F'(a, n-1) } - ghost method M(n: nat, b: map) + lemma M(n: nat, b: map) ensures F(b, n) == 0 // proved via automatic induction { } From 2f9a6e14a65b62349a99911fe1833f5f0900dcdc Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 14 Oct 2024 01:33:58 -0700 Subject: [PATCH 075/151] chore: Improve C# --- Source/DafnyCore/AST/ExtremeCloner.cs | 2 +- .../DafnyCore/AST/ExtremeLemmaBodyCloner.cs | 61 ++++++------------- .../CollectFriendlyCallsInSpec_Visitor.cs | 6 +- Source/DafnyCore/Resolver/ModuleResolver.cs | 15 ++--- .../Triggers/SplitPartTriggerWriter.cs | 4 +- 5 files changed, 28 insertions(+), 60 deletions(-) diff --git a/Source/DafnyCore/AST/ExtremeCloner.cs b/Source/DafnyCore/AST/ExtremeCloner.cs index d4d725e82e3..67cc276bf05 100644 --- a/Source/DafnyCore/AST/ExtremeCloner.cs +++ b/Source/DafnyCore/AST/ExtremeCloner.cs @@ -17,7 +17,7 @@ protected ExtremeCloner(Expression k, ErrorReporter reporter) { Contract.Requires(reporter != null); this.k = k; this.reporter = reporter; - this.suffix = string.Format("#[{0}]", Printer.ExprToString(reporter.Options, k)); + this.suffix = $"#[{Printer.ExprToString(reporter.Options, k)}]"; } protected Expression CloneCallAndAddK(ApplySuffix e) { Contract.Requires(e != null); diff --git a/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs b/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs index f71835f6ce2..66eda31ca9a 100644 --- a/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs +++ b/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs @@ -11,42 +11,31 @@ namespace Microsoft.Dafny; class ExtremeLemmaBodyCloner : ExtremeCloner { readonly ExtremeLemma context; readonly ISet focalPredicates; - public ExtremeLemmaBodyCloner(ExtremeLemma context, Expression k, ISet focalPredicates, ErrorReporter reporter) + readonly ISet focalCodatatypeEquality; + public ExtremeLemmaBodyCloner(ExtremeLemma context, Expression k, + ISet focalPredicates, ISet focalCodatatypeEquality, ErrorReporter reporter) : base(k, reporter) { Contract.Requires(context != null); Contract.Requires(k != null); Contract.Requires(reporter != null); this.context = context; this.focalPredicates = focalPredicates; + this.focalCodatatypeEquality = focalCodatatypeEquality; } public override Expression CloneExpr(Expression expr) { if (reporter.Options.RewriteFocalPredicates) { - if (expr is FunctionCallExpr) { - var e = (FunctionCallExpr)expr; -#if DEBUG_PRINT - if (e.Function.Name.EndsWith("#") && Contract.Exists(focalPredicates, p => e.Function.Name == p.Name + "#")) { - Options.Writer.WriteLine("{0}({1},{2}): DEBUG: Possible opportunity to rely on new rewrite: {3}", e.tok.filename, e.tok.line, e.tok.col, Printer.ExprToString(e)); - } -#endif + if (expr is FunctionCallExpr functionCallExpr) { // Note, we don't actually ever get here, because all calls will have been parsed as ApplySuffix. // However, if something changes in the future (for example, some rewrite that changing an ApplySuffix // to its resolved FunctionCallExpr), then we do want this code, so with the hope of preventing // some error in the future, this case is included. (Of course, it is currently completely untested!) - var f = e.Function as ExtremePredicate; - if (f != null && focalPredicates.Contains(f)) { -#if DEBUG_PRINT - var r = CloneCallAndAddK(e); - Options.Writer.WriteLine("{0}({1},{2}): DEBUG: Rewrote extreme predicate into prefix predicate: {3}", e.tok.filename, e.tok.line, e.tok.col, Printer.ExprToString(r)); - return r; -#else - return CloneCallAndAddK(e); -#endif + if (functionCallExpr.Function is ExtremePredicate f && focalPredicates.Contains(f)) { + return CloneCallAndAddK(functionCallExpr); } } else if (expr is StaticReceiverExpr ee) { return new StaticReceiverExpr(Tok(ee.tok), ee.Type, ee.IsImplicit); - } else if (expr is ApplySuffix) { - var apply = (ApplySuffix)expr; + } else if (expr is ApplySuffix apply) { if (!apply.WasResolved()) { // Since we're assuming the enclosing statement to have been resolved, this ApplySuffix must // be part of an ExprRhs that actually designates a method call. Such an ApplySuffix does @@ -54,22 +43,9 @@ public override Expression CloneExpr(Expression expr) { var mse = (MemberSelectExpr)apply.Lhs.Resolved; Contract.Assume(mse.Member is Method); } else { - var fce = apply.Resolved as FunctionCallExpr; - if (fce != null) { -#if DEBUG_PRINT - if (fce.Function.Name.EndsWith("#") && Contract.Exists(focalPredicates, p => fce.Function.Name == p.Name + "#")) { - Options.Writer.WriteLine("{0}({1},{2}): DEBUG: Possible opportunity to rely on new rewrite: {3}", fce.tok.filename, fce.tok.line, fce.tok.col, Printer.ExprToString(fce)); - } -#endif - var f = fce.Function as ExtremePredicate; - if (f != null && focalPredicates.Contains(f)) { -#if DEBUG_PRINT - var r = CloneCallAndAddK(fce); - Options.Writer.WriteLine("{0}({1},{2}): DEBUG: Rewrote extreme predicate into prefix predicate: {3}", fce.tok.filename, fce.tok.line, fce.tok.col, Printer.ExprToString(r)); - return r; -#else + if (apply.Resolved is FunctionCallExpr fce) { + if (fce.Function is ExtremePredicate f && focalPredicates.Contains(f)) { return CloneCallAndAddK(fce); -#endif } } } @@ -78,32 +54,31 @@ public override Expression CloneExpr(Expression expr) { return base.CloneExpr(expr); } public override AssignmentRhs CloneRHS(AssignmentRhs rhs) { - var r = rhs as ExprRhs; - if (r != null && r.Expr is ApplySuffix) { - var apply = (ApplySuffix)r.Expr; - var mse = apply.Lhs.Resolved as MemberSelectExpr; - if (mse != null && mse.Member is ExtremeLemma && ModuleDefinition.InSameSCC(context, (ExtremeLemma)mse.Member)) { + if (rhs is ExprRhs { Expr: ApplySuffix apply }) { + if (apply.Lhs.Resolved is MemberSelectExpr { Member: ExtremeLemma extremeLemma } && ModuleDefinition.InSameSCC(context, extremeLemma)) { // we're looking at a recursive call to an extreme lemma - Contract.Assert(apply.Lhs is NameSegment || apply.Lhs is ExprDotName); // this is the only way a call statement can have been parsed + Contract.Assert(apply.Lhs is NameSegment or ExprDotName); // this is the only way a call statement can have been parsed // clone "apply.Lhs", changing the least/greatest lemma to the prefix lemma; then clone "apply", adding in the extra argument Expression lhsClone; if (apply.Lhs is NameSegment) { var lhs = (NameSegment)apply.Lhs; - lhsClone = new NameSegment(Tok(lhs.tok), lhs.Name + "#", lhs.OptTypeArguments == null ? null : lhs.OptTypeArguments.ConvertAll(CloneType)); + lhsClone = new NameSegment(Tok(lhs.tok), lhs.Name + "#", lhs.OptTypeArguments?.ConvertAll(CloneType)); } else { var lhs = (ExprDotName)apply.Lhs; - lhsClone = new ExprDotName(Tok(lhs.tok), CloneExpr(lhs.Lhs), lhs.SuffixName + "#", lhs.OptTypeArguments == null ? null : lhs.OptTypeArguments.ConvertAll(CloneType)); + lhsClone = new ExprDotName(Tok(lhs.tok), CloneExpr(lhs.Lhs), lhs.SuffixName + "#", lhs.OptTypeArguments?.ConvertAll(CloneType)); } + var args = new List(); args.Add(new ActualBinding(null, k)); apply.Bindings.ArgumentBindings.ForEach(arg => args.Add(CloneActualBinding(arg))); var applyClone = new ApplySuffix(Tok(apply.tok), apply.AtTok == null ? null : Tok(apply.AtTok), lhsClone, args, Tok(apply.CloseParen)); var c = new ExprRhs(applyClone, CloneAttributes(rhs.Attributes)); - reporter.Info(MessageSource.Cloner, apply.Lhs.tok, mse.Member.Name + suffix); + reporter.Info(MessageSource.Cloner, apply.Lhs.tok, extremeLemma.Name + suffix); return c; } } + return base.CloneRHS(rhs); } } diff --git a/Source/DafnyCore/Resolver/CollectFriendlyCallsInSpec_Visitor.cs b/Source/DafnyCore/Resolver/CollectFriendlyCallsInSpec_Visitor.cs index 76bccae2139..02b9ba438c1 100644 --- a/Source/DafnyCore/Resolver/CollectFriendlyCallsInSpec_Visitor.cs +++ b/Source/DafnyCore/Resolver/CollectFriendlyCallsInSpec_Visitor.cs @@ -19,9 +19,8 @@ protected override bool VisitOneExpr(Expression expr, ref CallingPosition cp) { // no friendly calls in "expr" return false; // don't recurse into subexpressions } - if (expr is FunctionCallExpr) { + if (expr is FunctionCallExpr fexp) { if (cp == CallingPosition.Positive) { - var fexp = (FunctionCallExpr)expr; if (IsCoContext ? fexp.Function is GreatestPredicate : fexp.Function is LeastPredicate) { if (Context.KNat != ((ExtremePredicate)fexp.Function).KNat) { KNatMismatchError(expr.tok, Context.Name, Context.TypeOfK, ((ExtremePredicate)fexp.Function).TypeOfK); @@ -31,8 +30,7 @@ protected override bool VisitOneExpr(Expression expr, ref CallingPosition cp) { } } return false; // don't explore subexpressions any further - } else if (expr is BinaryExpr && IsCoContext) { - var bin = (BinaryExpr)expr; + } else if (expr is BinaryExpr bin && IsCoContext) { if (cp == CallingPosition.Positive && bin.ResolvedOp == BinaryExpr.ResolvedOpcode.EqCommon && bin.E0.Type.IsCoDatatype) { friendlyCalls.Add(bin); return false; // don't explore subexpressions any further diff --git a/Source/DafnyCore/Resolver/ModuleResolver.cs b/Source/DafnyCore/Resolver/ModuleResolver.cs index c012f2bd652..05f8b1ebfde 100644 --- a/Source/DafnyCore/Resolver/ModuleResolver.cs +++ b/Source/DafnyCore/Resolver/ModuleResolver.cs @@ -1696,17 +1696,14 @@ private void FillInPostConditionsAndBodiesOfPrefixLemmas(List decl var post = subst.CloneExpr(p.E); prefixLemma.Ens.Add(new AttributedExpression(post)); foreach (var e in coConclusions) { - var fce = e as FunctionCallExpr; - if (fce != null) { - // the other possibility is that "e" is a BinaryExpr + if (e is FunctionCallExpr fce) { GreatestPredicate predicate = (GreatestPredicate)fce.Function; focalPredicates.Add(predicate); // For every focal predicate P in S, add to S all greatest predicates in the same strongly connected // component (in the call graph) as P - foreach (var node in predicate.EnclosingClass.EnclosingModuleDefinition.CallGraph.GetSCC( - predicate)) { - if (node is GreatestPredicate) { - focalPredicates.Add((GreatestPredicate)node); + foreach (var node in predicate.EnclosingClass.EnclosingModuleDefinition.CallGraph.GetSCC(predicate)) { + if (node is GreatestPredicate greatestPredicate) { + focalPredicates.Add(greatestPredicate); } } } @@ -1729,8 +1726,8 @@ private void FillInPostConditionsAndBodiesOfPrefixLemmas(List decl // For every focal predicate P in S, add to S all least predicates in the same strongly connected // component (in the call graph) as P foreach (var node in predicate.EnclosingClass.EnclosingModuleDefinition.CallGraph.GetSCC(predicate)) { - if (node is LeastPredicate) { - focalPredicates.Add((LeastPredicate)node); + if (node is LeastPredicate leastPredicate) { + focalPredicates.Add(leastPredicate); } } } diff --git a/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs b/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs index 4e5eedefb82..49c27770468 100644 --- a/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs +++ b/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs @@ -84,9 +84,7 @@ public bool RewriteMatchingLoop(ErrorReporter reporter, ModuleDefinition module) var entry = substMap.Find(x => ExprExtensions.ExpressionEq(sub, x.Item1)); if (entry == null) { var newBv = new BoundVar(sub.tok, "_t#" + substMap.Count, sub.Type); - var ie = new IdentifierExpr(sub.tok, newBv.Name); - ie.Var = newBv; - ie.Type = newBv.Type; + var ie = new IdentifierExpr(sub.tok, newBv.Name) { Var = newBv, Type = newBv.Type }; substMap.Add(new Tuple(sub, ie)); } } From d6bc12ec4212445dd91a20c5e06f2447c9aa9604 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 14 Oct 2024 01:35:44 -0700 Subject: [PATCH 076/151] Also consider codatatype equality as a greatest focal predicate --- Source/DafnyCore/AST/ExtremeCloner.cs | 12 ++++++++++++ .../DafnyCore/AST/ExtremeLemmaBodyCloner.cs | 4 ++++ Source/DafnyCore/Resolver/ModuleResolver.cs | 19 ++++++++++++++----- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/Source/DafnyCore/AST/ExtremeCloner.cs b/Source/DafnyCore/AST/ExtremeCloner.cs index 67cc276bf05..96411834bd9 100644 --- a/Source/DafnyCore/AST/ExtremeCloner.cs +++ b/Source/DafnyCore/AST/ExtremeCloner.cs @@ -63,4 +63,16 @@ protected Expression CloneCallAndAddK(FunctionCallExpr e) { reporter.Info(MessageSource.Cloner, e.tok, e.Name + suffix); return fexp; } + + protected Expression CloneEqualityAndAndK(BinaryExpr binaryExpr) { + if (this.CloneResolvedFields) { + throw new NotImplementedException(); + } + + var eq = new TernaryExpr(Tok(binaryExpr.tok), + binaryExpr.ResolvedOp == BinaryExpr.ResolvedOpcode.EqCommon ? TernaryExpr.Opcode.PrefixEqOp : TernaryExpr.Opcode.PrefixNeqOp, + k, CloneExpr(binaryExpr.E0), CloneExpr(binaryExpr.E1)); + reporter.Info(MessageSource.Cloner, binaryExpr.tok, "==" + suffix); + return eq; + } } diff --git a/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs b/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs index 66eda31ca9a..c5763a08f9e 100644 --- a/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs +++ b/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs @@ -49,6 +49,10 @@ public override Expression CloneExpr(Expression expr) { } } } + } else if (expr is BinaryExpr { ResolvedOp: BinaryExpr.ResolvedOpcode.EqCommon or BinaryExpr.ResolvedOpcode.NeqCommon } binaryExpr) { + if (binaryExpr.E0.Type.AsCoDatatype is { } coDatatypeDecl && focalCodatatypeEquality.Contains(coDatatypeDecl)) { + return CloneEqualityAndAndK(binaryExpr); + } } } return base.CloneExpr(expr); diff --git a/Source/DafnyCore/Resolver/ModuleResolver.cs b/Source/DafnyCore/Resolver/ModuleResolver.cs index 05f8b1ebfde..91398372d1d 100644 --- a/Source/DafnyCore/Resolver/ModuleResolver.cs +++ b/Source/DafnyCore/Resolver/ModuleResolver.cs @@ -1685,6 +1685,7 @@ private void FillInPostConditionsAndBodiesOfPrefixLemmas(List decl var k = prefixLemma.Ins[0]; var focalPredicates = new HashSet(); + var focalCodatatypeEquality = new HashSet(); if (com is GreatestLemma) { // compute the postconditions of the prefix lemma Contract.Assume(prefixLemma.Ens.Count == 0); // these are not supposed to have been filled in before @@ -1706,6 +1707,9 @@ private void FillInPostConditionsAndBodiesOfPrefixLemmas(List decl focalPredicates.Add(greatestPredicate); } } + } else { + var binExpr = (BinaryExpr)e; // each "coConclusion" is either a FunctionCallExpr or a BinaryExpr + focalCodatatypeEquality.Add(binExpr.E0.Type.AsCoDatatype ?? binExpr.E1.Type.AsCoDatatype); } } } @@ -1734,16 +1738,21 @@ private void FillInPostConditionsAndBodiesOfPrefixLemmas(List decl } } - reporter.Info(MessageSource.Resolver, com.tok, - focalPredicates.Count == 0 - ? $"{com.PrefixLemma.Name} has no focal predicates" - : $"{com.PrefixLemma.Name} with focal predicate{Util.Plural(focalPredicates.Count)} {Util.Comma(focalPredicates, p => p.Name)}"); + var focalCount = focalPredicates.Count + focalCodatatypeEquality.Count; + if (focalCount == 0) { + reporter.Info(MessageSource.Resolver, com.tok, $"{com.PrefixLemma.Name} has no focal predicates"); + } else { + var predicates = Util.Comma(focalPredicates, p => p.Name); + var equalities = Util.Comma(focalCodatatypeEquality, decl => $"{decl.Name}.=="); + var focals = predicates + (predicates.Length != 0 && equalities.Length != 0 ? ", " : "") + equalities; + reporter.Info(MessageSource.Resolver, com.tok, $"{com.PrefixLemma.Name} with focal predicate{Util.Plural(focalCount)} {focals}"); + } // Compute the statement body of the prefix lemma Contract.Assume(prefixLemma.Body == null); // this is not supposed to have been filled in before if (com.Body != null) { var kMinusOne = new BinaryExpr(com.tok, BinaryExpr.Opcode.Sub, new IdentifierExpr(k.tok, k.Name), new LiteralExpr(com.tok, 1)); - var subst = new ExtremeLemmaBodyCloner(com, kMinusOne, focalPredicates, this.reporter); + var subst = new ExtremeLemmaBodyCloner(com, kMinusOne, focalPredicates, focalCodatatypeEquality, this.reporter); var mainBody = subst.CloneBlockStmt(com.Body); Expression kk; Statement els; From cb0eebd143434bbc44d4840403d00d9956ee836a Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 14 Oct 2024 01:37:09 -0700 Subject: [PATCH 077/151] Show tooltips for any quantifier rewrite substitutions --- Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs b/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs index 49c27770468..abb809b9a54 100644 --- a/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs +++ b/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs @@ -13,6 +13,7 @@ class SplitPartTriggerWriter { public List CandidateTerms { get; set; } public List Candidates { get; set; } private List RejectedCandidates { get; } + public List> NamedExpressions { get; } private List loopingMatches; private bool AllowsLoops { @@ -28,6 +29,7 @@ private bool AllowsLoops { internal SplitPartTriggerWriter(ComprehensionExpr comprehension) { this.Comprehension = comprehension; this.RejectedCandidates = new List(); + this.NamedExpressions = new(); } internal void TrimInvalidTriggers() { @@ -96,6 +98,7 @@ public bool RewriteMatchingLoop(ErrorReporter reporter, ModuleDefinition module) if (substMap.Count > 0) { var s = new ExprSubstituter(substMap); expr = s.Substitute(Comprehension) as QuantifierExpr; + NamedExpressions.AddRange(substMap); } else { // make a copy of the expr if (expr is ForallExpr) { @@ -173,7 +176,8 @@ string InfoFirstLineEnd(int count) { messages.Add($"Part #{splitPartIndex} is '{Comprehension.Term}'"); } if (Candidates.Any()) { - messages.Add($"Selected triggers:{InfoFirstLineEnd(Candidates.Count)}{string.Join(", ", Candidates)}"); + var subst = Util.Comma("", NamedExpressions, pair => $" where {pair.Item2} := {pair.Item1}"); + messages.Add($"Selected triggers:{InfoFirstLineEnd(Candidates.Count)}{string.Join(", ", Candidates)}{subst}"); } if (RejectedCandidates.Any()) { messages.Add($"Rejected triggers:{InfoFirstLineEnd(RejectedCandidates.Count)}{string.Join("\n ", RejectedCandidates)}"); From 2c4d9bcb003c6caa5b9703f32b3896a7b7c0bb46 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 14 Oct 2024 01:55:10 -0700 Subject: [PATCH 078/151] feat!: Auto-induction for twostate lemmas but not ghost methods --- Source/DafnyCore/Rewriters/InductionRewriter.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/DafnyCore/Rewriters/InductionRewriter.cs b/Source/DafnyCore/Rewriters/InductionRewriter.cs index c8ca384bc12..8314cc34dc5 100644 --- a/Source/DafnyCore/Rewriters/InductionRewriter.cs +++ b/Source/DafnyCore/Rewriters/InductionRewriter.cs @@ -72,15 +72,17 @@ void ProcessFunctionExpressions(Function function) { void ComputeLemmaInduction(Method method) { Contract.Requires(method != null); - if (method is Lemma or PrefixLemma && method is { Body: not null, Outs: { Count: 0 } }) { + if (method is { IsGhost: true, AllowsAllocation: false, Outs: { Count: 0 }, Body: not null } and not ExtremeLemma) { Expression pre = Expression.CreateBoolLiteral(method.tok, true); foreach (var req in method.Req) { pre = Expression.CreateAnd(pre, req.E); } + Expression post = Expression.CreateBoolLiteral(method.tok, true); foreach (var ens in method.Ens) { post = Expression.CreateAnd(post, ens.E); } + ComputeInductionVariables(method.tok, method.Ins, Expression.CreateImplies(pre, post), method, ref method.Attributes); } } From 5b83dfa8e2f4aff58cefbdfc4ca14cb1a69e3a45 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 16 Oct 2024 07:33:22 -0700 Subject: [PATCH 079/151] =?UTF-8?q?fix:=20Don=E2=80=99t=20consider=20arrow?= =?UTF-8?q?-typed=20variables=20for=20auto=20induction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/DafnyCore/Rewriters/InductionRewriter.cs | 2 +- Source/DafnyCore/Triggers/TriggerExtensions.cs | 7 +------ .../LitTest/triggers/InductionWithoutTriggers.dfy | 13 +++++++++++++ .../triggers/InductionWithoutTriggers.dfy.expect | 3 +++ 4 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.expect diff --git a/Source/DafnyCore/Rewriters/InductionRewriter.cs b/Source/DafnyCore/Rewriters/InductionRewriter.cs index 8314cc34dc5..0cb37e9fa7c 100644 --- a/Source/DafnyCore/Rewriters/InductionRewriter.cs +++ b/Source/DafnyCore/Rewriters/InductionRewriter.cs @@ -179,7 +179,7 @@ void ComputeInductionVariables(IToken tok, List boundVars, E } foreach (IVariable n in boundVars) { - if (!(n.Type.IsTypeParameter || n.Type.IsAbstractType || n.Type.IsInternalTypeSynonym) && + if (!(n.Type.IsTypeParameter || n.Type.IsAbstractType || n.Type.IsInternalTypeSynonym || n.Type.IsArrowType) && (args != null || InductionHeuristic.VarOccursInArgumentToRecursiveFunction(Reporter.Options, body, n))) { inductionVariables.Add(new IdentifierExpr(n.Tok, n)); } diff --git a/Source/DafnyCore/Triggers/TriggerExtensions.cs b/Source/DafnyCore/Triggers/TriggerExtensions.cs index e1417a6492a..6a79257962e 100644 --- a/Source/DafnyCore/Triggers/TriggerExtensions.cs +++ b/Source/DafnyCore/Triggers/TriggerExtensions.cs @@ -268,12 +268,7 @@ private static bool ShallowEq(WildcardExpr expr1, WildcardExpr expr2) { } private static bool ShallowEq(LambdaExpr expr1, LambdaExpr expr2) { -#if THROW_UNSUPPORTED_COMPARISONS - Contract.Assume(false); // This kind of expression never appears in a trigger - throw new NotImplementedException(); -#else - return false; -#endif + return true; } private static bool ShallowEq(MapComprehension expr1, MapComprehension expr2) { diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy new file mode 100644 index 00000000000..82bbf55f80c --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy @@ -0,0 +1,13 @@ +// RUN: %testDafnyForEachResolver "%s" --expect-exit-code=4 + +ghost function Sum(n: nat, f: int -> int): int +{ + if n == 0 then 0 else f(n-1) + Sum(n-1, f) +} + +lemma TestTriggerWithLambdaExpression(n: nat, f: int -> int, g: int -> int) +{ + // Once, trigger selection would crash on the following quantifier, which uses a LambdaExpr. + assert forall n: nat, f: int -> int :: Sum(n, x => 500 + f(x)) == n + Sum(n, f); // error: this does not hold +} + diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.expect new file mode 100644 index 00000000000..dd60a022868 --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.expect @@ -0,0 +1,3 @@ +InductionWithoutTriggers.dfy(17,21): Warning: Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. Change or remove the {:induction} attribute to generate a different induction hypothesis, or add {:nowarn} to silence this warning. For more information, see the section quantifier instantiation rules in the reference manual. + +Dafny program verifier finished with 2 verified, 1 error From f26466b3176e917411c4e6207a246bb8e3ae7073 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 16 Oct 2024 07:35:15 -0700 Subject: [PATCH 080/151] feat: allow ternary expressions in triggers --- Source/DafnyCore/Triggers/TriggersCollector.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/DafnyCore/Triggers/TriggersCollector.cs b/Source/DafnyCore/Triggers/TriggersCollector.cs index a39c6ce38ed..a210052fcab 100644 --- a/Source/DafnyCore/Triggers/TriggersCollector.cs +++ b/Source/DafnyCore/Triggers/TriggersCollector.cs @@ -101,6 +101,7 @@ expr is ApplyExpr || expr is DisplayExpression || expr is MapDisplayExpr || expr is DatatypeValue || + expr is TernaryExpr || TranslateToFunctionCall(expr)) { return true; } else if (expr is BinaryExpr) { From 6b4ca54587022de84755d27e783ee53e41d1405b Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 16 Oct 2024 07:58:24 -0700 Subject: [PATCH 081/151] Compute and use triggers for induction hypotheses # Conflicts: # Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/github-issue-4804.dfy --- .../DafnyCore/Rewriters/InductionRewriter.cs | 55 ++++++++++++++++--- .../LitTest/dafny0/AutoContracts.dfy.expect | 4 +- .../LitTest/dafny0/PrefixTypeSubst.dfy.expect | 12 ++-- .../LitTests/LitTest/dafny2/Calculations.dfy | 8 +++ .../LitTests/LitTest/dafny3/Abstemious.dfy | 3 +- .../LitTests/LitTest/dafny4/Bug170.dfy.expect | 5 +- .../LitTests/LitTest/dafny4/GHC-MergeSort.dfy | 11 ++++ .../LitTest/dafny4/Lucas-up.legacy.dfy | 4 +- .../LitTests/LitTest/dafny4/UnionFind.dfy | 2 +- .../git-issues/git-issue-977.dfy.expect | 9 +++ .../LitTest/git-issues/github-issue-4804.dfy | 2 +- .../LitTests/LitTest/hofs/SumSum.dfy | 6 +- .../LitTests/LitTest/lambdas/MatrixAssoc.dfy | 2 +- .../triggers/InductionWithoutTriggers.dfy | 8 +++ .../InductionWithoutTriggers.dfy.expect | 1 + 15 files changed, 104 insertions(+), 28 deletions(-) diff --git a/Source/DafnyCore/Rewriters/InductionRewriter.cs b/Source/DafnyCore/Rewriters/InductionRewriter.cs index 0cb37e9fa7c..e6a8cf4ae4e 100644 --- a/Source/DafnyCore/Rewriters/InductionRewriter.cs +++ b/Source/DafnyCore/Rewriters/InductionRewriter.cs @@ -165,8 +165,23 @@ void ComputeInductionVariables(IToken tok, List boundVars, E return; } - // The argument list was legal, so let's use it for the _induction attribute + // The argument list was legal, so let's use it for the _induction attribute. + // Next, look for matching patterns for the induction hypothesis. + if (lemma != null) { + var triggers = ComputeAndReportInductionTriggers(lemma, ref attributes, goodArguments, body); + if (triggers.Count == 0) { + var suppressWarnings = Attributes.Contains(attributes, "nowarn"); + var warningLevel = suppressWarnings ? ErrorLevel.Info : ErrorLevel.Warning; + + Reporter.Message(MessageSource.Rewriter, warningLevel, null, tok, + $"Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. " + + $"Change or remove the {{:induction}} attribute to generate a different induction hypothesis, or add {{:nowarn}} to silence this warning. " + + $"For more information, see the section quantifier instantiation rules in the reference manual."); + } + } + attributes = new Attributes("_induction", goodArguments, attributes); + return; } @@ -186,8 +201,9 @@ void ComputeInductionVariables(IToken tok, List boundVars, E } if (inductionVariables.Count != 0) { + List> triggers = null; if (lemma != null) { - var triggers = ComputeInductionTriggers(inductionVariables, body, lemma.EnclosingClass.EnclosingModuleDefinition); + triggers = ComputeInductionTriggers(inductionVariables, body, lemma.EnclosingClass.EnclosingModuleDefinition); if (triggers.Count == 0) { var msg = "omitting automatic induction because of lack of triggers"; if (args != null) { @@ -197,10 +213,6 @@ void ComputeInductionVariables(IToken tok, List boundVars, E } return; } - - foreach (var trigger in triggers) { - attributes = new Attributes("_inductionPattern", trigger, attributes); - } } // We found something usable, so let's record that in an attribute @@ -210,8 +222,32 @@ void ComputeInductionVariables(IToken tok, List boundVars, E if (lemma is PrefixLemma) { s = lemma.Name + " " + s; } - Reporter.Info(MessageSource.Rewriter, tok, s); + + if (triggers != null) { + ReportInductionTriggers(lemma, ref attributes, triggers); + } + } + } + + List> ComputeAndReportInductionTriggers(Method lemma, ref Attributes attributes, List inductionVariables, + Expression body) { + var triggers = ComputeInductionTriggers(inductionVariables, body, lemma.EnclosingClass.EnclosingModuleDefinition); + ReportInductionTriggers(lemma, ref attributes, triggers); + return triggers; + } + + private void ReportInductionTriggers(Method lemma, ref Attributes attributes, List> triggers) { + foreach (var trigger in triggers) { + attributes = new Attributes("_inductionPattern", trigger, attributes); +#if DEBUG + var ss = Printer.OneAttributeToString(Reporter.Options, attributes, "inductionPattern"); + if (lemma is PrefixLemma) { + ss = lemma.Name + " " + ss; + } + + Reporter.Info(MessageSource.Rewriter, lemma.tok, ss); +#endif } } @@ -253,7 +289,10 @@ List> ComputeInductionTriggers(List inductionVariab var triggersCollector = new Triggers.TriggersCollector(finder.exprsInOldContext, Reporter.Options, moduleDefinition); var quantifierCollection = new Triggers.ComprehensionTriggerGenerator(quantifier, Enumerable.Repeat(quantifier, 1), Reporter); quantifierCollection.ComputeTriggers(triggersCollector); - var triggers = quantifierCollection.GetTriggers(); + // Get the computed triggers, but only ask for those that do not require additional bound variables. (An alternative to this + // design would be to add {:matchinglooprewrite false} to "quantifier" above. However, that would cause certain matching loops + // to be ignored, so it is safer to not include triggers that require additional bound variables.) + var triggers = quantifierCollection.GetTriggers(false); var reverseSubstituter = new Substituter(null, reverseSubstMap, new Dictionary()); return triggers.ConvertAll(trigger => trigger.ConvertAll(reverseSubstituter.Substitute)); } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AutoContracts.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AutoContracts.dfy.expect index 5655e533d30..1ea587d54cf 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AutoContracts.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/AutoContracts.dfy.expect @@ -248,13 +248,13 @@ module OneModule { data < 20 } - lemma /*{:_induction this}*/ L() + lemma L() requires Valid() ensures data < 100 { } - twostate lemma /*{:_induction this}*/ TL() + twostate lemma TL() requires old(Valid()) ensures old(data) <= data { diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/PrefixTypeSubst.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/PrefixTypeSubst.dfy.expect index 71102103951..7f1f51f43c1 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/PrefixTypeSubst.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/PrefixTypeSubst.dfy.expect @@ -231,7 +231,7 @@ greatest lemma N(o: MyClass) N(o); } /*** -lemma {:axiom} /*{:_induction _k}*/ N#[_k: ORDINAL](o: MyClass) +lemma {:axiom} /*{:_induction _k}*/ /*{:_inductionPattern o.R#[_k]()}*/ N#[_k: ORDINAL](o: MyClass) ensures o.R#[_k]() decreases _k, o { @@ -253,7 +253,7 @@ greatest lemma O() O(); } /*** -lemma {:axiom} /*{:_induction _k}*/ O#[_k: ORDINAL]() +lemma {:axiom} /*{:_induction _k}*/ /*{:_inductionPattern MyClass.S#[_k]()}*/ O#[_k: ORDINAL]() ensures MyClass.S#[_k]() decreases _k { @@ -448,7 +448,7 @@ greatest lemma RstRst7() } } /*** -lemma {:axiom} /*{:_induction _k}*/ RstRst7#[_k: ORDINAL]() +lemma {:axiom} /*{:_induction _k}*/ /*{:_inductionPattern MyClass.RST#[_k]()}*/ RstRst7#[_k: ORDINAL]() ensures MyClass.RST#[_k]() decreases _k { @@ -512,7 +512,7 @@ greatest lemma RstRst10[nat]() { } /*** -lemma {:axiom} /*{:_induction _k}*/ RstRst10#[_k: nat]() +lemma {:axiom} /*{:_induction _k}*/ /*{:_inductionPattern MyClass.RST_Nat#[_k]()}*/ RstRst10#[_k: nat]() ensures MyClass.RST_Nat#[_k]() decreases _k { @@ -588,7 +588,7 @@ class MyClass { L(u, v); } /*** - lemma {:axiom} /*{:_induction this, _k}*/ L#[_k: ORDINAL](u: U, v: V) + lemma {:axiom} /*{:_induction _k}*/ /*{:_inductionPattern P#[_k](u, v)}*/ L#[_k: ORDINAL](u: U, v: V) ensures P#[_k](u, v) decreases _k { @@ -611,7 +611,7 @@ class MyClass { assert R#[_k - 1](); } /*** - lemma {:axiom} /*{:_induction this, _k}*/ M#[_k: ORDINAL]() + lemma {:axiom} /*{:_induction _k}*/ /*{:_inductionPattern R#[_k]()}*/ M#[_k: ORDINAL]() ensures R#[_k]() decreases _k { diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny2/Calculations.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny2/Calculations.dfy index 14926010e4e..ecf9db84e66 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny2/Calculations.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny2/Calculations.dfy @@ -49,6 +49,14 @@ lemma Lemma_ConcatNil(xs : List) lemma Lemma_RevCatCommute(xs : List) ensures forall ys, zs :: revacc(xs, concat(ys, zs)) == concat(revacc(xs, ys), zs); { + forall ys, zs + ensures revacc(xs, concat(ys, zs)) == concat(revacc(xs, ys), zs) + { + match xs + case Nil => + case Cons(_, xs') => + Lemma_RevCatCommute(xs'); + } } // Here is a theorem that says "qreverse" and "reverse" calculate the same result. The proof diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/Abstemious.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/Abstemious.dfy index 9645b0cf2f3..5eb338afaa8 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/Abstemious.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/Abstemious.dfy @@ -143,9 +143,10 @@ greatest lemma OhOnesTail_Correct() { } -greatest lemma OhOnes_Correct() +lemma OhOnes_Correct() ensures OhOnes() == Cons(0, ones()) { + OhOnesTail_Correct(); } lemma OhOnes_Correct'(n: nat) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Bug170.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Bug170.dfy.expect index 9b8aeb71c83..976b1a237f1 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Bug170.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Bug170.dfy.expect @@ -14,8 +14,6 @@ Bug170.dfy(10,12): Info: B#[_k - 1] Bug170.dfy(15,12): Info: A#[_k - 1] Bug170.dfy(18,14): Info: AA# decreases _k, x Bug170.dfy(26,14): Info: BB# decreases _k, x -Bug170.dfy(18,14): Info: AA# {:induction _k, x} -Bug170.dfy(26,14): Info: BB# {:induction _k, x} Bug170.dfy(36,21): Info: _k: ORDINAL Bug170.dfy(41,21): Info: _k: ORDINAL Bug170.dfy(46,17): Info: _k: ORDINAL @@ -33,7 +31,9 @@ Bug170.dfy(43,4): Info: A#[_k - 1] Bug170.dfy(46,17): Info: AA# decreases _k, x Bug170.dfy(53,17): Info: BB# decreases _k, x Bug170.dfy(46,17): Info: AA# {:induction _k, x} +Bug170.dfy(46,17): Info: AA# {:inductionPattern A#[_k](x)} Bug170.dfy(53,17): Info: BB# {:induction _k, x} +Bug170.dfy(53,17): Info: BB# {:inductionPattern B#[_k](x)} Bug170.dfy(64,18): Info: _k: ORDINAL Bug170.dfy(69,14): Info: _k: ORDINAL Bug170.dfy(70,14): Info: A#[_k] @@ -42,7 +42,6 @@ Bug170.dfy(72,7): Info: A#[_k - 1] Bug170.dfy(73,6): Info: AA#[_k - 1] Bug170.dfy(66,12): Info: A#[_k - 1] Bug170.dfy(69,14): Info: AA# decreases _k, x -Bug170.dfy(69,14): Info: AA# {:induction _k, x} Bug170.dfy(50,11): Info: Some instances of this call are not inlined. Bug170.dfy(57,11): Info: Some instances of this call are not inlined. diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/GHC-MergeSort.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/GHC-MergeSort.dfy index a4022dc988a..bd80b089d5c 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/GHC-MergeSort.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/GHC-MergeSort.dfy @@ -472,6 +472,17 @@ lemma stable_sequences(g: G, xs: List) case Cons(a, ys) => match ys { case Nil => + calc { + flatten(sequences(xs)); + // def. sequences, since xs == Cons(a, Nil) + flatten(Cons(xs, Nil)); + // def. flatten + append(xs, flatten(Nil)); + // def. flatten + append(xs, Nil); + { append_Nil(xs); } + xs; + } case Cons(b, zs) => if !Below(a, b) { calc { diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Lucas-up.legacy.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Lucas-up.legacy.dfy index 38413c3be06..5ba9023a9a7 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Lucas-up.legacy.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Lucas-up.legacy.dfy @@ -66,7 +66,7 @@ ghost function binom(a: nat, b: nat): nat // div-2 is applied to both arguments, except in the case where // the first argument to "binom" is even and the second argument // is odd, in which case "binom" is always even. -lemma {:resource_limit "8e6"} Lucas_Binary(a: nat, b: nat) +lemma {:resource_limit "8e6"} {:induction a, b} {:nowarn} Lucas_Binary(a: nat, b: nat) ensures EVEN(binom(2*a, 2*b + 1)) ensures EVEN(binom(2*a, 2*b)) <==> EVEN(binom(a, b)) ensures EVEN(binom(2*a + 1, 2*b + 1)) <==> EVEN(binom(a, b)) @@ -83,7 +83,7 @@ lemma {:resource_limit "8e6"} Lucas_Binary(a: nat, b: nat) } // Here is an alternative way to phrase the previous lemma. -lemma {:resource_limit "200e6"} Lucas_Binary'(a: nat, b: nat) +lemma {:resource_limit "200e6"} {:induction a, b} {:nowarn} Lucas_Binary'(a: nat, b: nat) ensures binom(2*a, 2*b) % 2 == binom(a, b) % 2 ensures binom(2*a, 2*b + 1) % 2 == 0 ensures binom(2*a + 1, 2*b) % 2 == binom(a, b) % 2 diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/UnionFind.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/UnionFind.dfy index d80ac5cac0c..3831148a1cc 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/UnionFind.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/UnionFind.dfy @@ -264,7 +264,7 @@ module M3 refines M2 { } } - lemma {:autocontracts false} ReachUnaffectedByChangeFromRoot'(d: nat, e: Element, r: Element, C: CMap, td: nat, r0: Element, r1: Element, C': CMap) + lemma {:autocontracts false} {:induction d, e, r, C} {:nowarn} ReachUnaffectedByChangeFromRoot'(d: nat, e: Element, r: Element, C: CMap, td: nat, r0: Element, r1: Element, C': CMap) requires GoodCMap(C) requires e in C && Reaches(d, e, r, C) requires r0 in C && r1 in C && C[r0].Root? && C[r1].Root? && C[r0].depth == C[r1].depth && r0 != r1 diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-977.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-977.dfy.expect index a88c1419457..db3dee30b5b 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-977.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-977.dfy.expect @@ -28,8 +28,17 @@ git-issue-977.dfy(84,16): Info: decreases num git-issue-977.dfy(129,16): Info: decreases k, num git-issue-977.dfy(146,16): Info: decreases k, num git-issue-977.dfy(54,6): Info: {:induction num} +git-issue-977.dfy(54,6): Info: {:inductionPattern GreatestPredNat(num)} +git-issue-977.dfy(54,6): Info: {:inductionPattern GreatestPredOrd(num)} +git-issue-977.dfy(54,6): Info: {:inductionPattern Pred(num)} git-issue-977.dfy(61,6): Info: {:induction k, num} +git-issue-977.dfy(61,6): Info: {:inductionPattern RicochetOrd(k, num)} +git-issue-977.dfy(61,6): Info: {:inductionPattern GreatestManualOrd(k, num)} +git-issue-977.dfy(61,6): Info: {:inductionPattern GreatestPredOrd#[k](num)} git-issue-977.dfy(77,6): Info: {:induction k, num} +git-issue-977.dfy(77,6): Info: {:inductionPattern RicochetNat(k, num)} +git-issue-977.dfy(77,6): Info: {:inductionPattern GreatestManualNat(k, num)} +git-issue-977.dfy(77,6): Info: {:inductionPattern GreatestPredNat#[k](num)} git-issue-977.dfy(71,4): Info: ensures GreatestPredOrd#[m](num) git-issue-977.dfy(71,4): Info: ensures GreatestManualOrd(m, num) git-issue-977.dfy(71,4): Info: ensures RicochetOrd(m, num) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/github-issue-4804.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/github-issue-4804.dfy index 2c856483240..6516b3be1a8 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/github-issue-4804.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/github-issue-4804.dfy @@ -1,4 +1,4 @@ -// RUN: %exits-with 4 %verify --allow-axioms --resource-limit 10000 %s > %t +// RUN: %exits-with 4 %verify --allow-axioms --resource-limit 1200 %s > %t // RUN: %diff "%s.expect" "%t" module Power { diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/hofs/SumSum.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/hofs/SumSum.dfy index 4a7f8894e41..bda1eb00196 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/hofs/SumSum.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/hofs/SumSum.dfy @@ -20,6 +20,7 @@ lemma ExchangeEta(n: nat, f: int -> int, g: int -> int) requires forall i :: 0 <= i < n ==> f(i) == g(i) ensures Sum(n, x => f(x)) == Sum(n, x => g(x)) { + if n != 0 { ExchangeEta(n - 1, f, g); } } lemma NestedAlphaRenaming(n: nat, g: (int,int) -> int) @@ -37,10 +38,10 @@ lemma Distribute(n: nat, f: int -> int, g: int -> int) { } -lemma {:induction false} PrettyBasicBetaReduction(n: nat, g: (int,int) -> int, i: int) +lemma PrettyBasicBetaReduction(n: nat, g: (int,int) -> int, i: int) ensures (x => Sum(n, y => g(x,y)))(i) == Sum(n, y => g(i,y)) { - // NOTE: This proof is by induction on n (it can be done automatically) + // NOTE: This proof is by induction on n if n == 0 { calc { (x => Sum(n, y => g(x,y)))(i); @@ -62,7 +63,6 @@ lemma {:induction false} PrettyBasicBetaReduction(n: nat, g: (int,int) -> int, i lemma BetaReduction0(n: nat, g: (int,int) -> int, i: int) ensures (x => Sum(n, y => g(x,y)))(i) == Sum(n, y => g(i,y)) { - // automatic proof by induction on n } lemma BetaReduction1(n': nat, g: (int,int) -> int, i: int) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/lambdas/MatrixAssoc.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/lambdas/MatrixAssoc.dfy index 0c777579f63..9a528b39724 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/lambdas/MatrixAssoc.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/lambdas/MatrixAssoc.dfy @@ -73,7 +73,7 @@ lemma distr_mult(f: Index -> int, x: int) /** Σ_k (Σ_l f(k,l)) == Σ_l (Σ_k f(k,l)) */ /** proof by induction */ -lemma sum_assoc_n(m: Matrix, n1: nat, n2: nat) +lemma {:induction m, n1, n2} {:nowarn} sum_assoc_n(m: Matrix, n1: nat, n2: nat) requires n1 <= N && n2 <= N ensures Sum_n((k: Index) => Sum_n((l: Index) => m(k)(l), n1), n2) == diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy index 82bbf55f80c..171bc0a8612 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy @@ -11,3 +11,11 @@ lemma TestTriggerWithLambdaExpression(n: nat, f: int -> int, g: int -> int) assert forall n: nat, f: int -> int :: Sum(n, x => 500 + f(x)) == n + Sum(n, f); // error: this does not hold } +// With an explicit :induction attribute, the induction hypothesis emitted lets the proof of the lemma go through. +// However, there is no good matching pattern for the induction hypothesis, so a warning is generated. +// For a manual proof of this lemma, see lemma ExchangeEta in hofs/SumSum.dfy. +lemma {:induction n} ExchangeEtaWithInductionAttribute(n: nat, f: int -> int, g: int -> int) // warning: no trigger + requires forall i :: 0 <= i < n ==> f(i) == g(i) + ensures Sum(n, x => f(x)) == Sum(n, x => g(x)) +{ +} diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.expect index dd60a022868..815d9943bf2 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.expect @@ -1,3 +1,4 @@ InductionWithoutTriggers.dfy(17,21): Warning: Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. Change or remove the {:induction} attribute to generate a different induction hypothesis, or add {:nowarn} to silence this warning. For more information, see the section quantifier instantiation rules in the reference manual. +InductionWithoutTriggers.dfy(11,9): Error: assertion might not hold Dafny program verifier finished with 2 verified, 1 error From bc050cd93d00a270e7b3c2ab6d25a1a7095ab3e0 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 16 Oct 2024 07:59:42 -0700 Subject: [PATCH 082/151] Tooltip named expressions from trigger selection --- .../Triggers/ComprehensionTriggerGenerator.cs | 15 +++++++++------ .../LitTest/triggers/let-expressions.dfy.expect | 4 ++-- .../loop-detection-is-not-too-strict.dfy.expect | 6 +++--- .../loop-detection-looks-at-ranges-too.dfy.expect | 4 ++-- ...loop-detection-messages--unit-tests.dfy.expect | 10 +++++----- .../matrix-accesses-are-triggers.dfy.expect | 2 +- ...t-look-like-the-triggers-they-match.dfy.expect | 6 +++--- ...ting-triggers-recovers-expressivity.dfy.expect | 4 ++-- ...ppressing-warnings-behaves-properly.dfy.expect | 2 +- 9 files changed, 28 insertions(+), 25 deletions(-) diff --git a/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs b/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs index 65723ba9829..91b5e565632 100644 --- a/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs +++ b/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs @@ -231,15 +231,18 @@ internal void CommitTriggers(SystemModuleManager systemModuleManager) { } } - public List> GetTriggers() { + public List> GetTriggers(bool includeTriggersThatRequireNamedExpressions) { var triggers = new List>(); foreach (var triggerWriter in partWriters) { - foreach (var triggerTerms in triggerWriter.Candidates) { - var trigger = new List(); - foreach (var triggerTerm in triggerTerms.Terms) { - trigger.Add(triggerTerm.Expr); + if (includeTriggersThatRequireNamedExpressions || triggerWriter.NamedExpressions.Count == 0) { + foreach (var triggerTerms in triggerWriter.Candidates) { + var trigger = new List(); + foreach (var triggerTerm in triggerTerms.Terms) { + trigger.Add(triggerTerm.Expr); + } + + triggers.Add(trigger); } - triggers.Add(trigger); } } return triggers; diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/let-expressions.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/let-expressions.dfy.expect index c2731da3a7c..43c6c5c9d54 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/let-expressions.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/let-expressions.dfy.expect @@ -1,6 +1,6 @@ let-expressions.dfy(6,8): Info: Selected triggers: {s[i]} let-expressions.dfy(7,8): Info: Selected triggers: {s[i]} -let-expressions.dfy(8,8): Info: Selected triggers: {s[_t#0], s[i]} -let-expressions.dfy(9,8): Info: Selected triggers: {s[_t#0], s[i]} +let-expressions.dfy(8,8): Info: Selected triggers: {s[_t#0], s[i]} where _t#0 := i + 1 +let-expressions.dfy(9,8): Info: Selected triggers: {s[_t#0], s[i]} where _t#0 := i + 1 Dafny program verifier finished with 1 verified, 0 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-is-not-too-strict.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-is-not-too-strict.dfy.expect index aef0837895c..4c9f454a9c4 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-is-not-too-strict.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-is-not-too-strict.dfy.expect @@ -2,14 +2,14 @@ loop-detection-is-not-too-strict.dfy(15,9): Info: Selected triggers: {P(y, x)}, {P(x, y)} loop-detection-is-not-too-strict.dfy(18,9): Info: Selected triggers: {P(y, x)}, {P(x, y)} -loop-detection-is-not-too-strict.dfy(21,9): Info: Selected triggers: {P(x, _t#0), P(x, y)} +loop-detection-is-not-too-strict.dfy(21,9): Info: Selected triggers: {P(x, _t#0), P(x, y)} where _t#0 := y + 1 loop-detection-is-not-too-strict.dfy(26,9): Info: Selected triggers: {Q(x)} loop-detection-is-not-too-strict.dfy(27,9): Info: Selected triggers: {Q(x)} loop-detection-is-not-too-strict.dfy(28,9): Info: Selected triggers: {P(x, z)}, {P(x, 1)} loop-detection-is-not-too-strict.dfy(33,9): Info: Selected triggers: {Q(x)} loop-detection-is-not-too-strict.dfy(34,9): Info: Selected triggers: {Q(x)} -loop-detection-is-not-too-strict.dfy(36,9): Info: Selected triggers: {Q(_t#0), Q(x)} -loop-detection-is-not-too-strict.dfy(40,9): Info: Selected triggers: {Q(_t#0), Q(x)} +loop-detection-is-not-too-strict.dfy(36,9): Info: Selected triggers: {Q(_t#0), Q(x)} where _t#0 := if z > 1 then x else 3 * z + 1 +loop-detection-is-not-too-strict.dfy(40,9): Info: Selected triggers: {Q(_t#0), Q(x)} where _t#0 := x + 1 Dafny program verifier finished with 1 verified, 0 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-looks-at-ranges-too.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-looks-at-ranges-too.dfy.expect index a6679d8c1ea..992d2e551f2 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-looks-at-ranges-too.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-looks-at-ranges-too.dfy.expect @@ -1,4 +1,4 @@ -loop-detection-looks-at-ranges-too.dfy(11,17): Info: Selected triggers: {P(_t#0), P(x)} -loop-detection-looks-at-ranges-too.dfy(13,17): Info: Selected triggers: {P(x), P(_t#0)} +loop-detection-looks-at-ranges-too.dfy(11,17): Info: Selected triggers: {P(_t#0), P(x)} where _t#0 := x + 1 +loop-detection-looks-at-ranges-too.dfy(13,17): Info: Selected triggers: {P(x), P(_t#0)} where _t#0 := x + 1 Dafny program verifier finished with 1 verified, 0 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-messages--unit-tests.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-messages--unit-tests.dfy.expect index 374f02b7f9b..3d8d9a1765e 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-messages--unit-tests.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-messages--unit-tests.dfy.expect @@ -1,21 +1,21 @@ loop-detection-messages--unit-tests.dfy(11,9): Info: Selected triggers: {f(f(i))} Rejected triggers: {f(i)} (may loop with "f(f(i))") -loop-detection-messages--unit-tests.dfy(12,9): Info: Selected triggers: {f(_t#0), f(i)} -loop-detection-messages--unit-tests.dfy(13,9): Info: Selected triggers: {f(_t#0), f(i)} +loop-detection-messages--unit-tests.dfy(12,9): Info: Selected triggers: {f(_t#0), f(i)} where _t#0 := i + 1 +loop-detection-messages--unit-tests.dfy(13,9): Info: Selected triggers: {f(_t#0), f(i)} where _t#0 := i + 1 loop-detection-messages--unit-tests.dfy(15,9): Info: Quantifier was split into 2 parts. Better verification performance and error reporting may be obtained by splitting the quantifier in source. For more information, see the section quantifier instantiation rules in the reference manual. loop-detection-messages--unit-tests.dfy(15,9): Info: Part #0 is 'false ==> f(i) == f(_t#0)' - Selected triggers: {f(_t#0), f(i)} + Selected triggers: {f(_t#0), f(i)} where _t#0 := i + 1 loop-detection-messages--unit-tests.dfy(15,9): Info: Part #1 is 'false ==> f(i) == g(i)' Selected triggers: {g(i)}, {f(i)} loop-detection-messages--unit-tests.dfy(16,9): Info: Quantifier was split into 2 parts. Better verification performance and error reporting may be obtained by splitting the quantifier in source. For more information, see the section quantifier instantiation rules in the reference manual. loop-detection-messages--unit-tests.dfy(16,9): Info: Part #0 is 'false ==> f(i) == f(_t#0)' - Selected triggers: {f(_t#0), f(i)} + Selected triggers: {f(_t#0), f(i)} where _t#0 := i + 1 loop-detection-messages--unit-tests.dfy(16,9): Info: Part #1 is 'false ==> f(i) == f(i)' Selected triggers: {f(i)} loop-detection-messages--unit-tests.dfy(17,9): Info: Quantifier was split into 2 parts. Better verification performance and error reporting may be obtained by splitting the quantifier in source. For more information, see the section quantifier instantiation rules in the reference manual. loop-detection-messages--unit-tests.dfy(17,9): Info: Part #0 is 'false ==> f(i) == f(_t#0)' - Selected triggers: {f(_t#0), f(i)} + Selected triggers: {f(_t#0), f(i)} where _t#0 := i + 1 loop-detection-messages--unit-tests.dfy(17,9): Info: Part #1 is 'false ==> f(i) == f(i)' Selected triggers: {f(i)} loop-detection-messages--unit-tests.dfy(19,9): Info: Selected triggers: {f(i)} diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/matrix-accesses-are-triggers.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/matrix-accesses-are-triggers.dfy.expect index 4dc44f54ccf..4de25509099 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/matrix-accesses-are-triggers.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/matrix-accesses-are-triggers.dfy.expect @@ -1,4 +1,4 @@ -matrix-accesses-are-triggers.dfy(7,11): Info: Selected triggers: {m[j, _t#0], m[i, j]} +matrix-accesses-are-triggers.dfy(7,11): Info: Selected triggers: {m[j, _t#0], m[i, j]} where _t#0 := i + 1 matrix-accesses-are-triggers.dfy(7,82): Error: index 0 out of range matrix-accesses-are-triggers.dfy(7,86): Error: index 1 out of range diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect index 592b2b34dcd..ccd871551da 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect @@ -1,9 +1,9 @@ -some-terms-do-not-look-like-the-triggers-they-match.dfy(12,17): Info: Selected triggers: {s[_t#0], s[x]} +some-terms-do-not-look-like-the-triggers-they-match.dfy(12,17): Info: Selected triggers: {s[_t#0], s[x]} where _t#0 := x + 1 some-terms-do-not-look-like-the-triggers-they-match.dfy(15,17): Info: Quantifier was split into 2 parts. Better verification performance and error reporting may be obtained by splitting the quantifier in source. For more information, see the section quantifier instantiation rules in the reference manual. some-terms-do-not-look-like-the-triggers-they-match.dfy(15,17): Info: Part #0 is 'x in s ==> s[_t#0] > 0' - Selected triggers: {s[_t#0], s[x]} + Selected triggers: {s[_t#0], s[x]} where _t#0 := x + 1 some-terms-do-not-look-like-the-triggers-they-match.dfy(15,17): Info: Part #1 is 'x in s ==> _t#0 !in s' - Selected triggers: {s[_t#0], s[x]} + Selected triggers: {s[_t#0], s[x]} where _t#0 := x + 2 some-terms-do-not-look-like-the-triggers-they-match.dfy(24,18): Info: Selected triggers: {x in s + t} some-terms-do-not-look-like-the-triggers-they-match.dfy(25,18): Info: Selected triggers: {x in t}, {x in s} diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/splitting-triggers-recovers-expressivity.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/splitting-triggers-recovers-expressivity.dfy.expect index e66366bd3ec..b06c081fc7a 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/splitting-triggers-recovers-expressivity.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/splitting-triggers-recovers-expressivity.dfy.expect @@ -1,7 +1,7 @@ splitting-triggers-recovers-expressivity.dfy(12,10): Info: Selected triggers: - {P(_t#0), Q(i)}, {P(_t#0), P(i)} + {P(_t#0), Q(i)}, {P(_t#0), P(i)} where _t#0 := i + 1 splitting-triggers-recovers-expressivity.dfy(17,11): Info: Selected triggers: - {P(_t#0), Q(j)}, {P(_t#0), P(j)} + {P(_t#0), Q(j)}, {P(_t#0), P(j)} where _t#0 := j + 1 splitting-triggers-recovers-expressivity.dfy(26,10): Info: Selected triggers: {Q(i)} Rejected triggers: {P(i)} (may loop with "P(i + 1)") splitting-triggers-recovers-expressivity.dfy(33,11): Info: Selected triggers: {Q(j)} diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/suppressing-warnings-behaves-properly.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/suppressing-warnings-behaves-properly.dfy.expect index 4abcebe8941..80c6a424512 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/suppressing-warnings-behaves-properly.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/suppressing-warnings-behaves-properly.dfy.expect @@ -5,7 +5,7 @@ suppressing-warnings-behaves-properly.dfy(14,9): Info: Selected triggers: {f(n)} suppressing-warnings-behaves-properly.dfy(15,9): Info: Selected triggers: {f(n)} suppressing-warnings-behaves-properly.dfy(16,9): Info: The attribute {:autotriggers false} may cause brittle verification. It's better to remove this attribute, or as a second option, manually define a trigger using {:trigger}. For more information, see the section quantifier instantiation rules in the reference manual. suppressing-warnings-behaves-properly.dfy(18,9): Info: Selected triggers: - {g(n), f(_t#0)}, {f(_t#0), f(n)} + {g(n), f(_t#0)}, {f(_t#0), f(n)} where _t#0 := n + 1 suppressing-warnings-behaves-properly.dfy(19,9): Info: Selected triggers: {f(n)} suppressing-warnings-behaves-properly.dfy(20,9): Info: The attribute {:autotriggers false} may cause brittle verification. It's better to remove this attribute, or as a second option, manually define a trigger using {:trigger}. For more information, see the section quantifier instantiation rules in the reference manual. From b9083e7eecaed2e3810d89f68a6e9cdc9d4412b1 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 16 Oct 2024 08:00:18 -0700 Subject: [PATCH 083/151] chore: Remove deprecated semi-colons --- .../LitTest/dafny3/SimpleCoinduction.dfy | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/SimpleCoinduction.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/SimpleCoinduction.dfy index 352efe03eab..5ac106cae58 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/SimpleCoinduction.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/SimpleCoinduction.dfy @@ -1,4 +1,4 @@ -// RUN: %testDafnyForEachResolver "%s" -- --allow-deprecation +// RUN: %testDafnyForEachResolver "%s" codatatype Stream = Cons(head: T, tail: Stream) @@ -22,7 +22,7 @@ ghost function Inc(s: Stream): Stream } lemma {:induction false} UpLemma(k: nat, n: int) - ensures Inc(Up(n)) ==#[k] Up(n+1); + ensures Inc(Up(n)) ==#[k] Up(n+1) { if (k != 0) { UpLemma(k-1, n+1); @@ -30,18 +30,18 @@ lemma {:induction false} UpLemma(k: nat, n: int) } greatest lemma {:induction false} CoUpLemma(n: int) - ensures Inc(Up(n)) == Up(n+1); + ensures Inc(Up(n)) == Up(n+1) { CoUpLemma(n+1); } lemma UpLemma_Auto(k: nat, n: int, nn: int) - ensures nn == n+1 ==> Inc(Up(n)) ==#[k] Up(nn); // note: it would be nice to do an automatic rewrite (from "ensures Inc(Up(n)) ==#[k] Up(n+1)") to obtain the good trigger here + ensures nn == n+1 ==> Inc(Up(n)) ==#[k] Up(nn) // note: it would be nice to do an automatic rewrite (from "ensures Inc(Up(n)) ==#[k] Up(n+1)") to obtain the good trigger here { } greatest lemma CoUpLemma_Auto(n: int, nn: int) - ensures nn == n+1 ==> Inc(Up(n)) == Up(nn); // see comment above + ensures nn == n+1 ==> Inc(Up(n)) == Up(nn) // see comment above { } @@ -53,8 +53,9 @@ ghost function Repeat(n: int): Stream } greatest lemma RepeatLemma(n: int) - ensures Inc(Repeat(n)) == Repeat(n+1); + ensures Inc(Repeat(n)) == Repeat(n+1) { + RepeatLemma(n); } // ----------------------------------------------------------------------- @@ -65,7 +66,7 @@ greatest predicate True(s: Stream) } greatest lemma AlwaysTrue(s: Stream) - ensures True(s); + ensures True(s) { } @@ -75,7 +76,7 @@ greatest predicate AlsoTrue(s: Stream) } greatest lemma AlsoAlwaysTrue(s: Stream) - ensures AlsoTrue(s); + ensures AlsoTrue(s) { } @@ -85,7 +86,7 @@ greatest predicate TT(y: int) } greatest lemma AlwaysTT(y: int) - ensures TT(y); + ensures TT(y) { } @@ -116,11 +117,11 @@ greatest predicate AtMost(a: IList, b: IList) } greatest lemma ZerosAndOnes_Theorem0() - ensures AtMost(zeros(), ones()); + ensures AtMost(zeros(), ones()) { } greatest lemma ZerosAndOnes_Theorem1() - ensures Append(zeros(), ones()) == zeros(); + ensures Append(zeros(), ones()) == zeros() { } From a2b5a6f3bfda973009d5f55ceef2ff46f7504764 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 16 Oct 2024 08:01:01 -0700 Subject: [PATCH 084/151] Add tests for ternary expressions in triggers --- .../LitTests/LitTest/dafny0/CoPrefix.dfy | 2 +- .../LitTest/dafny0/CoPrefix.dfy.expect | 3 +-- .../LitTests/LitTest/dafny3/Abstemious.dfy | 22 +++++++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoPrefix.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoPrefix.dfy index 708f0291219..f23c066103c 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoPrefix.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoPrefix.dfy @@ -135,7 +135,7 @@ greatest lemma {:induction false} Compare(h: T) Compare(Next(h)); if { case true => - assert FF(h).tail == GG(h).tail; // error: full equality is not known here + assert FF(h).tail == GG(h).tail; // yes, this full equality is a focal predicate, so it's rewritten into ==#[_k - 1] case true => assert FF(h) ==#[_k] GG(h); // yes, this is the postcondition to be proved, and it is known to hold case true => diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoPrefix.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoPrefix.dfy.expect index dd2100b12db..d5f27399a5a 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoPrefix.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoPrefix.dfy.expect @@ -3,7 +3,6 @@ CoPrefix.dfy(76,55): Error: cannot prove termination; try supplying a decreases CoPrefix.dfy(114,0): Error: a postcondition could not be proved on this return path CoPrefix.dfy(113,10): Related location: this is the postcondition that could not be proved CoPrefix.dfy(101,16): Related location: this proposition could not be proved -CoPrefix.dfy(138,24): Error: assertion might not hold CoPrefix.dfy(142,24): Error: assertion might not hold CoPrefix.dfy(117,22): Related location: this proposition could not be proved CoPrefix.dfy(151,0): Error: a postcondition could not be proved on this return path @@ -17,4 +16,4 @@ CoPrefix.dfy(205,6): Error: the calculation step between the previous line and t CoPrefix.dfy(207,6): Error: the calculation step between the previous line and this line could not be proved CoPrefix.dfy(220,12): Error: ORDINAL subtraction might underflow a limit ordinal (that is, RHS might be too large) -Dafny program verifier finished with 13 verified, 12 errors +Dafny program verifier finished with 13 verified, 11 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/Abstemious.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/Abstemious.dfy index 5eb338afaa8..258e7be2e6f 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/Abstemious.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/Abstemious.dfy @@ -206,3 +206,25 @@ lemma Fib_Correct(n: nat) } } } + +// --------------- ternary expression is a trigger --------------- + +lemma OrdinalLemma(k: ORDINAL) + ensures OhOnes().tail ==#[k] ones() +{ + // automatic induction on k +} + +lemma NaturalLemma(k: nat) + ensures OhOnes().tail ==#[k] ones() +{ + // automatic induction on k +} + +lemma Quantifier() + // the following quantifiers use the entire body as a trigger (previously, ternary expressions + // had not been considered as trigger candidates) + requires forall k: nat :: OhOnes().tail ==#[k] ones() + requires forall k: ORDINAL :: OhOnes().tail ==#[k] ones() +{ +} From 84bd74758dd8ac8ee7a087645d39bdbc2cc1f7ca Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Sun, 20 Oct 2024 03:53:14 -0700 Subject: [PATCH 085/151] Selectively exclude CanCalls from forall stmt translation --- .../Verifier/BoogieGenerator.Functions.cs | 2 +- .../DafnyCore/Verifier/BoogieGenerator.Methods.cs | 2 +- .../Statements/BoogieGenerator.TrForallStmt.cs | 15 +++++++++++---- .../LitTest/dafny0/CoinductiveProofs.dfy.expect | 2 +- .../LitTests/LitTest/dafny1/Induction.legacy.dfy | 2 +- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs index f0e9655423a..b5386b1ef53 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs @@ -576,7 +576,7 @@ private Axiom GetFunctionAxiom(Function f, Expression body, List lits) { funcAppl = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(funcID), funcArgs); } - Bpl.Trigger tr = BplTriggerHeap(this, f.tok, funcAppl, readsHeap ? etran.HeapExpr : null); + Bpl.Trigger tr = BplTriggerHeap(this, f.tok, funcAppl, f.ReadsHeap ? etran.HeapExpr : null); Bpl.Expr tastyVegetarianOption; // a.k.a. the "meat" of the operation :) if (!RevealedInScope(f)) { tastyVegetarianOption = Bpl.Expr.True; diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 7220d7306d9..d77b4e80652 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -795,7 +795,7 @@ private StmtList TrMethodBody(Method m, BoogieStmtListBuilder builder, Variables builder.Add(new Bpl.IfCmd(m.tok, null, definedness.Collect(m.tok), null, exporter.Collect(m.tok))); #else TrForallStmtCall(m.tok, parBoundVars, parBounds, parRange, decrCheck, null, triggers, recursiveCall, null, - builder, localVariables, etran); + builder, localVariables, etran, includeCanCalls: false); #endif } // translate the body of the method diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs index f485078ec85..3bf5ee6c767 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs @@ -74,7 +74,8 @@ private void TrForallStmt(BoogieStmtListBuilder builder, Variables locals, Expre void TrForallStmtCall(IToken tok, List boundVars, List bounds, Expression range, ExpressionConverter additionalRange, List forallExpressions, List> triggers, CallStmt s0, - BoogieStmtListBuilder definedness, BoogieStmtListBuilder exporter, Variables locals, ExpressionTranslator etran) { + BoogieStmtListBuilder definedness, BoogieStmtListBuilder exporter, Variables locals, ExpressionTranslator etran, + bool includeCanCalls = true) { Contract.Requires(tok != null); Contract.Requires(boundVars != null); Contract.Requires(bounds != null); @@ -209,7 +210,9 @@ void TrForallStmtCall(IToken tok, List boundVars, List bo } foreach (var ens in ConjunctsOf(s0.Method.Ens)) { p = Substitute(ens.E, receiver, argsSubstMap, s0.MethodSelect.TypeArgumentSubstitutionsWithParents()); // substitute the call's actuals for the method's formals - post = BplAnd(post, callEtran.CanCallAssumption(p)); + if (includeCanCalls) { + post = BplAnd(post, callEtran.CanCallAssumption(p)); + } post = BplAnd(post, callEtran.TrExpr(p)); } @@ -229,7 +232,11 @@ void TrForallStmtCall(IToken tok, List boundVars, List bo // 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, BplAnd(anteCanCalls, BplImp(ante, post))); // TODO: Add a SMART_TRIGGER here. If we can't find one, abort the attempt to do induction automatically + var body = BplImp(ante, post); + if (includeCanCalls) { + body = BplAnd(anteCanCalls, body); + } + var qq = new Bpl.ForallExpr(tok, bvars, tr, body); exporter.Add(TrAssumeCmd(tok, qq)); } } @@ -566,4 +573,4 @@ void TrForallProof(ForallStmt forallStmt, BoogieStmtListBuilder definedness, Boo exporter.Add(TrAssumeCmd(forallStmt.Tok, BplAnd(se, ((Bpl.ForallExpr)qq).Body))); } } -} \ No newline at end of file +} diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect index 745522d72f4..936d99ce2d3 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect @@ -31,5 +31,5 @@ CoinductiveProofs.dfy(208,21): Related location: this is the postcondition that CoinductiveProofs.dfy(4,23): Related location: this proposition could not be proved Dafny program verifier finished with 23 verified, 12 errors -Total resources used is 763327 +Total resources used is 767044 Max resources used by VC is 81873 diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/Induction.legacy.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/Induction.legacy.dfy index cfc80e894fa..473f418e3d3 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/Induction.legacy.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/Induction.legacy.dfy @@ -1,4 +1,4 @@ -// RUN: %exits-with 4 %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t" +// RUN: %exits-with 4 %dafny /compile:0 /deprecation:0 /dprint:"%t.dprint" "%s" > "%t" // RUN: %diff "%s.expect" "%t" class IntegerInduction { From 54e9ddbba5f65f124536a2677569fee249c4f175 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Sun, 20 Oct 2024 06:49:55 -0700 Subject: [PATCH 086/151] Fix typo in method name --- Source/DafnyCore/AST/ExtremeCloner.cs | 2 +- Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/DafnyCore/AST/ExtremeCloner.cs b/Source/DafnyCore/AST/ExtremeCloner.cs index 96411834bd9..c5d24c32921 100644 --- a/Source/DafnyCore/AST/ExtremeCloner.cs +++ b/Source/DafnyCore/AST/ExtremeCloner.cs @@ -64,7 +64,7 @@ protected Expression CloneCallAndAddK(FunctionCallExpr e) { return fexp; } - protected Expression CloneEqualityAndAndK(BinaryExpr binaryExpr) { + protected Expression CloneEqualityAndAddK(BinaryExpr binaryExpr) { if (this.CloneResolvedFields) { throw new NotImplementedException(); } diff --git a/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs b/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs index c5763a08f9e..98a3033e6d6 100644 --- a/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs +++ b/Source/DafnyCore/AST/ExtremeLemmaBodyCloner.cs @@ -51,7 +51,7 @@ public override Expression CloneExpr(Expression expr) { } } else if (expr is BinaryExpr { ResolvedOp: BinaryExpr.ResolvedOpcode.EqCommon or BinaryExpr.ResolvedOpcode.NeqCommon } binaryExpr) { if (binaryExpr.E0.Type.AsCoDatatype is { } coDatatypeDecl && focalCodatatypeEquality.Contains(coDatatypeDecl)) { - return CloneEqualityAndAndK(binaryExpr); + return CloneEqualityAndAddK(binaryExpr); } } } From b46f0dc448d4c9a8de2de5d151e930486d434044 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Sun, 20 Oct 2024 06:50:23 -0700 Subject: [PATCH 087/151] Remove unnecessary assertion --- .../src/Std/Arithmetic/LittleEndianNat.dfy | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/DafnyStandardLibraries/src/Std/Arithmetic/LittleEndianNat.dfy b/Source/DafnyStandardLibraries/src/Std/Arithmetic/LittleEndianNat.dfy index a5a99c223a3..4d8a84b6aae 100644 --- a/Source/DafnyStandardLibraries/src/Std/Arithmetic/LittleEndianNat.dfy +++ b/Source/DafnyStandardLibraries/src/Std/Arithmetic/LittleEndianNat.dfy @@ -123,7 +123,6 @@ abstract module {:disableNonlinearArithmetic} Std.Arithmetic.LittleEndianNat { ensures ToNatRight(xs) == First(xs) + xs[1] * BASE() { var xs1 := DropFirst(xs); - assert DropFirst(xs1) == []; calc { ToNatRight(xs); { reveal ToNatRight(); } From 022685ca5abed6010a2f5d4153853350711dc30d Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Sun, 20 Oct 2024 06:53:04 -0700 Subject: [PATCH 088/151] =?UTF-8?q?Remove=20unnecessary=20$=E2=80=99s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/DafnyCore/Rewriters/InductionRewriter.cs | 4 ++-- .../DafnyCore/Triggers/ComprehensionTriggerGenerator.cs | 2 +- Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/DafnyCore/Rewriters/InductionRewriter.cs b/Source/DafnyCore/Rewriters/InductionRewriter.cs index e6a8cf4ae4e..fb445750150 100644 --- a/Source/DafnyCore/Rewriters/InductionRewriter.cs +++ b/Source/DafnyCore/Rewriters/InductionRewriter.cs @@ -174,9 +174,9 @@ void ComputeInductionVariables(IToken tok, List boundVars, E var warningLevel = suppressWarnings ? ErrorLevel.Info : ErrorLevel.Warning; Reporter.Message(MessageSource.Rewriter, warningLevel, null, tok, - $"Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. " + + "Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. " + $"Change or remove the {{:induction}} attribute to generate a different induction hypothesis, or add {{:nowarn}} to silence this warning. " + - $"For more information, see the section quantifier instantiation rules in the reference manual."); + "For more information, see the section quantifier instantiation rules in the reference manual."); } } diff --git a/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs b/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs index 91b5e565632..aea9252ca43 100644 --- a/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs +++ b/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs @@ -222,7 +222,7 @@ internal void CommitTriggers(SystemModuleManager systemModuleManager) { reporter.Message(MessageSource.Rewriter, ErrorLevel.Info, null, comprehension.Tok, $"Quantifier was split into {partWriters.Count} parts. " + "Better verification performance and error reporting may be obtained by splitting the quantifier in source. " + - $"For more information, see the section quantifier instantiation rules in the reference manual."); + "For more information, see the section quantifier instantiation rules in the reference manual."); } for (var index = 0; index < partWriters.Count; index++) { diff --git a/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs b/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs index abb809b9a54..ef76f651579 100644 --- a/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs +++ b/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs @@ -189,14 +189,14 @@ string InfoFirstLineEnd(int count) { if (!CandidateTerms.Any() || !Candidates.Any()) { errorReporter.Message(MessageSource.Rewriter, warningLevel, null, reportingToken, - $"Could not find a trigger for this quantifier. Without a trigger, the quantifier may cause brittle verification. " + + "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."); + "For more information, see the section quantifier instantiation rules in the reference manual."); } else if (!CouldSuppressLoops && !AllowsLoops) { errorReporter.Message(MessageSource.Rewriter, warningLevel, null, reportingToken, - $"Triggers were added to this quantifier that may introduce matching loops, which may cause brittle verification. " + + "Triggers were added to this quantifier that may introduce matching loops, which 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."); + "For more information, see the section quantifier instantiation rules in the reference manual."); } } From 4cb3f5a67c89462859103856644337f528581ba8 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Sun, 20 Oct 2024 07:00:38 -0700 Subject: [PATCH 089/151] Improve C# and comments --- Source/DafnyCore/Rewriters/InductionHeuristic.cs | 8 ++------ .../DafnyCore/Triggers/ComprehensionTriggerGenerator.cs | 6 +----- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/Source/DafnyCore/Rewriters/InductionHeuristic.cs b/Source/DafnyCore/Rewriters/InductionHeuristic.cs index 1ceab8866f2..5afadabe5aa 100644 --- a/Source/DafnyCore/Rewriters/InductionHeuristic.cs +++ b/Source/DafnyCore/Rewriters/InductionHeuristic.cs @@ -23,12 +23,7 @@ public static class InductionHeuristic { public static bool VarOccursInArgumentToRecursiveFunction(DafnyOptions options, Expression expr, IVariable? n) { switch (options.InductionHeuristic) { case 0: return true; - case 1: - if (n == null) { - return FreeVariablesUtil.ContainsFreeVariable(expr, true, null); - } else { - return FreeVariablesUtil.ContainsFreeVariable(expr, false, n); - } + case 1: return FreeVariablesUtil.ContainsFreeVariable(expr, n == null, n); default: return VarOccursInArgumentToRecursiveFunction(options, expr, n, false); } } @@ -37,6 +32,7 @@ public static bool VarOccursInArgumentToRecursiveFunction(DafnyOptions options, /// Worker routine for VarOccursInArgumentToRecursiveFunction(expr,n), where the additional parameter 'exprIsProminent' says whether or /// not 'expr' has prominent status in its context. /// DafnyInductionHeuristic cases 0 and 1 are assumed to be handled elsewhere (i.e., a precondition of this method is DafnyInductionHeuristic is at least 2). + /// Variable 'n' can be passed in as 'null', in which case it stands for 'this'. /// static bool VarOccursInArgumentToRecursiveFunction(DafnyOptions options, Expression expr, IVariable? n, bool exprIsProminent) { Contract.Requires(expr != null); diff --git a/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs b/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs index aea9252ca43..c0492e27c58 100644 --- a/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs +++ b/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs @@ -236,11 +236,7 @@ public List> GetTriggers(bool includeTriggersThatRequireNamedEx foreach (var triggerWriter in partWriters) { if (includeTriggersThatRequireNamedExpressions || triggerWriter.NamedExpressions.Count == 0) { foreach (var triggerTerms in triggerWriter.Candidates) { - var trigger = new List(); - foreach (var triggerTerm in triggerTerms.Terms) { - trigger.Add(triggerTerm.Expr); - } - + var trigger = triggerTerms.Terms.ConvertAll(t => t.Expr); triggers.Add(trigger); } } From a21328575530d768aaca210f41ebf79574d993d8 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Sun, 20 Oct 2024 07:05:40 -0700 Subject: [PATCH 090/151] Revert tooltip printing of trigger named expressions --- Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs | 3 +-- .../LitTest/triggers/let-expressions.dfy.expect | 4 ++-- .../loop-detection-is-not-too-strict.dfy.expect | 6 +++--- .../loop-detection-looks-at-ranges-too.dfy.expect | 4 ++-- .../loop-detection-messages--unit-tests.dfy.expect | 10 +++++----- .../triggers/matrix-accesses-are-triggers.dfy.expect | 2 +- ...do-not-look-like-the-triggers-they-match.dfy.expect | 6 +++--- ...splitting-triggers-recovers-expressivity.dfy.expect | 4 ++-- .../suppressing-warnings-behaves-properly.dfy.expect | 2 +- 9 files changed, 20 insertions(+), 21 deletions(-) diff --git a/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs b/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs index ef76f651579..344742a2235 100644 --- a/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs +++ b/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs @@ -176,8 +176,7 @@ string InfoFirstLineEnd(int count) { messages.Add($"Part #{splitPartIndex} is '{Comprehension.Term}'"); } if (Candidates.Any()) { - var subst = Util.Comma("", NamedExpressions, pair => $" where {pair.Item2} := {pair.Item1}"); - messages.Add($"Selected triggers:{InfoFirstLineEnd(Candidates.Count)}{string.Join(", ", Candidates)}{subst}"); + messages.Add($"Selected triggers:{InfoFirstLineEnd(Candidates.Count)}{string.Join(", ", Candidates)}"); } if (RejectedCandidates.Any()) { messages.Add($"Rejected triggers:{InfoFirstLineEnd(RejectedCandidates.Count)}{string.Join("\n ", RejectedCandidates)}"); diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/let-expressions.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/let-expressions.dfy.expect index 43c6c5c9d54..c2731da3a7c 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/let-expressions.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/let-expressions.dfy.expect @@ -1,6 +1,6 @@ let-expressions.dfy(6,8): Info: Selected triggers: {s[i]} let-expressions.dfy(7,8): Info: Selected triggers: {s[i]} -let-expressions.dfy(8,8): Info: Selected triggers: {s[_t#0], s[i]} where _t#0 := i + 1 -let-expressions.dfy(9,8): Info: Selected triggers: {s[_t#0], s[i]} where _t#0 := i + 1 +let-expressions.dfy(8,8): Info: Selected triggers: {s[_t#0], s[i]} +let-expressions.dfy(9,8): Info: Selected triggers: {s[_t#0], s[i]} Dafny program verifier finished with 1 verified, 0 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-is-not-too-strict.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-is-not-too-strict.dfy.expect index 4c9f454a9c4..aef0837895c 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-is-not-too-strict.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-is-not-too-strict.dfy.expect @@ -2,14 +2,14 @@ loop-detection-is-not-too-strict.dfy(15,9): Info: Selected triggers: {P(y, x)}, {P(x, y)} loop-detection-is-not-too-strict.dfy(18,9): Info: Selected triggers: {P(y, x)}, {P(x, y)} -loop-detection-is-not-too-strict.dfy(21,9): Info: Selected triggers: {P(x, _t#0), P(x, y)} where _t#0 := y + 1 +loop-detection-is-not-too-strict.dfy(21,9): Info: Selected triggers: {P(x, _t#0), P(x, y)} loop-detection-is-not-too-strict.dfy(26,9): Info: Selected triggers: {Q(x)} loop-detection-is-not-too-strict.dfy(27,9): Info: Selected triggers: {Q(x)} loop-detection-is-not-too-strict.dfy(28,9): Info: Selected triggers: {P(x, z)}, {P(x, 1)} loop-detection-is-not-too-strict.dfy(33,9): Info: Selected triggers: {Q(x)} loop-detection-is-not-too-strict.dfy(34,9): Info: Selected triggers: {Q(x)} -loop-detection-is-not-too-strict.dfy(36,9): Info: Selected triggers: {Q(_t#0), Q(x)} where _t#0 := if z > 1 then x else 3 * z + 1 -loop-detection-is-not-too-strict.dfy(40,9): Info: Selected triggers: {Q(_t#0), Q(x)} where _t#0 := x + 1 +loop-detection-is-not-too-strict.dfy(36,9): Info: Selected triggers: {Q(_t#0), Q(x)} +loop-detection-is-not-too-strict.dfy(40,9): Info: Selected triggers: {Q(_t#0), Q(x)} Dafny program verifier finished with 1 verified, 0 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-looks-at-ranges-too.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-looks-at-ranges-too.dfy.expect index 992d2e551f2..a6679d8c1ea 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-looks-at-ranges-too.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-looks-at-ranges-too.dfy.expect @@ -1,4 +1,4 @@ -loop-detection-looks-at-ranges-too.dfy(11,17): Info: Selected triggers: {P(_t#0), P(x)} where _t#0 := x + 1 -loop-detection-looks-at-ranges-too.dfy(13,17): Info: Selected triggers: {P(x), P(_t#0)} where _t#0 := x + 1 +loop-detection-looks-at-ranges-too.dfy(11,17): Info: Selected triggers: {P(_t#0), P(x)} +loop-detection-looks-at-ranges-too.dfy(13,17): Info: Selected triggers: {P(x), P(_t#0)} Dafny program verifier finished with 1 verified, 0 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-messages--unit-tests.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-messages--unit-tests.dfy.expect index 3d8d9a1765e..374f02b7f9b 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-messages--unit-tests.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/loop-detection-messages--unit-tests.dfy.expect @@ -1,21 +1,21 @@ loop-detection-messages--unit-tests.dfy(11,9): Info: Selected triggers: {f(f(i))} Rejected triggers: {f(i)} (may loop with "f(f(i))") -loop-detection-messages--unit-tests.dfy(12,9): Info: Selected triggers: {f(_t#0), f(i)} where _t#0 := i + 1 -loop-detection-messages--unit-tests.dfy(13,9): Info: Selected triggers: {f(_t#0), f(i)} where _t#0 := i + 1 +loop-detection-messages--unit-tests.dfy(12,9): Info: Selected triggers: {f(_t#0), f(i)} +loop-detection-messages--unit-tests.dfy(13,9): Info: Selected triggers: {f(_t#0), f(i)} loop-detection-messages--unit-tests.dfy(15,9): Info: Quantifier was split into 2 parts. Better verification performance and error reporting may be obtained by splitting the quantifier in source. For more information, see the section quantifier instantiation rules in the reference manual. loop-detection-messages--unit-tests.dfy(15,9): Info: Part #0 is 'false ==> f(i) == f(_t#0)' - Selected triggers: {f(_t#0), f(i)} where _t#0 := i + 1 + Selected triggers: {f(_t#0), f(i)} loop-detection-messages--unit-tests.dfy(15,9): Info: Part #1 is 'false ==> f(i) == g(i)' Selected triggers: {g(i)}, {f(i)} loop-detection-messages--unit-tests.dfy(16,9): Info: Quantifier was split into 2 parts. Better verification performance and error reporting may be obtained by splitting the quantifier in source. For more information, see the section quantifier instantiation rules in the reference manual. loop-detection-messages--unit-tests.dfy(16,9): Info: Part #0 is 'false ==> f(i) == f(_t#0)' - Selected triggers: {f(_t#0), f(i)} where _t#0 := i + 1 + Selected triggers: {f(_t#0), f(i)} loop-detection-messages--unit-tests.dfy(16,9): Info: Part #1 is 'false ==> f(i) == f(i)' Selected triggers: {f(i)} loop-detection-messages--unit-tests.dfy(17,9): Info: Quantifier was split into 2 parts. Better verification performance and error reporting may be obtained by splitting the quantifier in source. For more information, see the section quantifier instantiation rules in the reference manual. loop-detection-messages--unit-tests.dfy(17,9): Info: Part #0 is 'false ==> f(i) == f(_t#0)' - Selected triggers: {f(_t#0), f(i)} where _t#0 := i + 1 + Selected triggers: {f(_t#0), f(i)} loop-detection-messages--unit-tests.dfy(17,9): Info: Part #1 is 'false ==> f(i) == f(i)' Selected triggers: {f(i)} loop-detection-messages--unit-tests.dfy(19,9): Info: Selected triggers: {f(i)} diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/matrix-accesses-are-triggers.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/matrix-accesses-are-triggers.dfy.expect index 4de25509099..4dc44f54ccf 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/matrix-accesses-are-triggers.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/matrix-accesses-are-triggers.dfy.expect @@ -1,4 +1,4 @@ -matrix-accesses-are-triggers.dfy(7,11): Info: Selected triggers: {m[j, _t#0], m[i, j]} where _t#0 := i + 1 +matrix-accesses-are-triggers.dfy(7,11): Info: Selected triggers: {m[j, _t#0], m[i, j]} matrix-accesses-are-triggers.dfy(7,82): Error: index 0 out of range matrix-accesses-are-triggers.dfy(7,86): Error: index 1 out of range diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect index ccd871551da..592b2b34dcd 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/some-terms-do-not-look-like-the-triggers-they-match.dfy.expect @@ -1,9 +1,9 @@ -some-terms-do-not-look-like-the-triggers-they-match.dfy(12,17): Info: Selected triggers: {s[_t#0], s[x]} where _t#0 := x + 1 +some-terms-do-not-look-like-the-triggers-they-match.dfy(12,17): Info: Selected triggers: {s[_t#0], s[x]} some-terms-do-not-look-like-the-triggers-they-match.dfy(15,17): Info: Quantifier was split into 2 parts. Better verification performance and error reporting may be obtained by splitting the quantifier in source. For more information, see the section quantifier instantiation rules in the reference manual. some-terms-do-not-look-like-the-triggers-they-match.dfy(15,17): Info: Part #0 is 'x in s ==> s[_t#0] > 0' - Selected triggers: {s[_t#0], s[x]} where _t#0 := x + 1 + Selected triggers: {s[_t#0], s[x]} some-terms-do-not-look-like-the-triggers-they-match.dfy(15,17): Info: Part #1 is 'x in s ==> _t#0 !in s' - Selected triggers: {s[_t#0], s[x]} where _t#0 := x + 2 + Selected triggers: {s[_t#0], s[x]} some-terms-do-not-look-like-the-triggers-they-match.dfy(24,18): Info: Selected triggers: {x in s + t} some-terms-do-not-look-like-the-triggers-they-match.dfy(25,18): Info: Selected triggers: {x in t}, {x in s} diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/splitting-triggers-recovers-expressivity.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/splitting-triggers-recovers-expressivity.dfy.expect index b06c081fc7a..e66366bd3ec 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/splitting-triggers-recovers-expressivity.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/splitting-triggers-recovers-expressivity.dfy.expect @@ -1,7 +1,7 @@ splitting-triggers-recovers-expressivity.dfy(12,10): Info: Selected triggers: - {P(_t#0), Q(i)}, {P(_t#0), P(i)} where _t#0 := i + 1 + {P(_t#0), Q(i)}, {P(_t#0), P(i)} splitting-triggers-recovers-expressivity.dfy(17,11): Info: Selected triggers: - {P(_t#0), Q(j)}, {P(_t#0), P(j)} where _t#0 := j + 1 + {P(_t#0), Q(j)}, {P(_t#0), P(j)} splitting-triggers-recovers-expressivity.dfy(26,10): Info: Selected triggers: {Q(i)} Rejected triggers: {P(i)} (may loop with "P(i + 1)") splitting-triggers-recovers-expressivity.dfy(33,11): Info: Selected triggers: {Q(j)} diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/suppressing-warnings-behaves-properly.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/suppressing-warnings-behaves-properly.dfy.expect index 80c6a424512..4abcebe8941 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/suppressing-warnings-behaves-properly.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/suppressing-warnings-behaves-properly.dfy.expect @@ -5,7 +5,7 @@ suppressing-warnings-behaves-properly.dfy(14,9): Info: Selected triggers: {f(n)} suppressing-warnings-behaves-properly.dfy(15,9): Info: Selected triggers: {f(n)} suppressing-warnings-behaves-properly.dfy(16,9): Info: The attribute {:autotriggers false} may cause brittle verification. It's better to remove this attribute, or as a second option, manually define a trigger using {:trigger}. For more information, see the section quantifier instantiation rules in the reference manual. suppressing-warnings-behaves-properly.dfy(18,9): Info: Selected triggers: - {g(n), f(_t#0)}, {f(_t#0), f(n)} where _t#0 := n + 1 + {g(n), f(_t#0)}, {f(_t#0), f(n)} suppressing-warnings-behaves-properly.dfy(19,9): Info: Selected triggers: {f(n)} suppressing-warnings-behaves-properly.dfy(20,9): Info: The attribute {:autotriggers false} may cause brittle verification. It's better to remove this attribute, or as a second option, manually define a trigger using {:trigger}. For more information, see the section quantifier instantiation rules in the reference manual. From 72c15fe4877f6caa32536da4f6e60cd3a7406ad8 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Sun, 20 Oct 2024 07:53:25 -0700 Subject: [PATCH 091/151] Improve and document methods that compute/report induction triggers --- Source/DafnyCore/Rewriters/InductionRewriter.cs | 16 ++++++++-------- .../Triggers/ComprehensionTriggerGenerator.cs | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Source/DafnyCore/Rewriters/InductionRewriter.cs b/Source/DafnyCore/Rewriters/InductionRewriter.cs index fb445750150..c4cde84a0e0 100644 --- a/Source/DafnyCore/Rewriters/InductionRewriter.cs +++ b/Source/DafnyCore/Rewriters/InductionRewriter.cs @@ -168,7 +168,7 @@ void ComputeInductionVariables(IToken tok, List boundVars, E // The argument list was legal, so let's use it for the _induction attribute. // Next, look for matching patterns for the induction hypothesis. if (lemma != null) { - var triggers = ComputeAndReportInductionTriggers(lemma, ref attributes, goodArguments, body); + var triggers = ComputeInductionTriggers(goodArguments, body, lemma.EnclosingClass.EnclosingModuleDefinition); if (triggers.Count == 0) { var suppressWarnings = Attributes.Contains(attributes, "nowarn"); var warningLevel = suppressWarnings ? ErrorLevel.Info : ErrorLevel.Warning; @@ -178,6 +178,7 @@ void ComputeInductionVariables(IToken tok, List boundVars, E $"Change or remove the {{:induction}} attribute to generate a different induction hypothesis, or add {{:nowarn}} to silence this warning. " + "For more information, see the section quantifier instantiation rules in the reference manual."); } + ReportInductionTriggers(lemma, ref attributes, triggers); } attributes = new Attributes("_induction", goodArguments, attributes); @@ -203,6 +204,9 @@ void ComputeInductionVariables(IToken tok, List boundVars, E if (inductionVariables.Count != 0) { List> triggers = null; if (lemma != null) { + // Compute the induction triggers, but don't report their patterns into attributes yet. Instead, + // call ReportInductionTriggers only after the "_induction" attribute has been added. This will cause the + // tooltips to appear in a logical order (showing the induction variables first, followed by the matching patterns). triggers = ComputeInductionTriggers(inductionVariables, body, lemma.EnclosingClass.EnclosingModuleDefinition); if (triggers.Count == 0) { var msg = "omitting automatic induction because of lack of triggers"; @@ -230,13 +234,9 @@ void ComputeInductionVariables(IToken tok, List boundVars, E } } - List> ComputeAndReportInductionTriggers(Method lemma, ref Attributes attributes, List inductionVariables, - Expression body) { - var triggers = ComputeInductionTriggers(inductionVariables, body, lemma.EnclosingClass.EnclosingModuleDefinition); - ReportInductionTriggers(lemma, ref attributes, triggers); - return triggers; - } - + /// + /// Report as tooltips the matching patterns selected for the induction hypothesis. + /// private void ReportInductionTriggers(Method lemma, ref Attributes attributes, List> triggers) { foreach (var trigger in triggers) { attributes = new Attributes("_inductionPattern", trigger, attributes); diff --git a/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs b/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs index c0492e27c58..6c62a1fec9d 100644 --- a/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs +++ b/Source/DafnyCore/Triggers/ComprehensionTriggerGenerator.cs @@ -235,8 +235,8 @@ public List> GetTriggers(bool includeTriggersThatRequireNamedEx var triggers = new List>(); foreach (var triggerWriter in partWriters) { if (includeTriggersThatRequireNamedExpressions || triggerWriter.NamedExpressions.Count == 0) { - foreach (var triggerTerms in triggerWriter.Candidates) { - var trigger = triggerTerms.Terms.ConvertAll(t => t.Expr); + foreach (var triggerCandidate in triggerWriter.Candidates) { + var trigger = triggerCandidate.Terms.ConvertAll(t => t.Expr); triggers.Add(trigger); } } From 704af8cb2ff04c5cc6e7dc87a70c4248ed295974 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Sun, 20 Oct 2024 09:36:09 -0700 Subject: [PATCH 092/151] Incomplete attempt at specifying behavior --- .../DafnyCore/AST/Grammar/Printer/Printer.cs | 8 ++ .../DafnyCore/Rewriters/InductionRewriter.cs | 49 +++++---- .../triggers/InductionWithoutTriggers.dfy | 104 ++++++++++++++++++ docs/dev/news/5835.feat | 38 ++++++- 4 files changed, 177 insertions(+), 22 deletions(-) diff --git a/Source/DafnyCore/AST/Grammar/Printer/Printer.cs b/Source/DafnyCore/AST/Grammar/Printer/Printer.cs index 87afafd59a9..4d1d3c25d03 100644 --- a/Source/DafnyCore/AST/Grammar/Printer/Printer.cs +++ b/Source/DafnyCore/AST/Grammar/Printer/Printer.cs @@ -78,6 +78,14 @@ public static string ExprToString(DafnyOptions options, Expression expr, [CanBeN return wr.ToString(); } + public static string ExprListToString(DafnyOptions options, List expressions, [CanBeNull] PrintFlags printFlags = null) { + Contract.Requires(expressions != null); + using var wr = new StringWriter(); + var pr = new Printer(wr, options, printFlags: printFlags); + pr.PrintExpressionList(expressions, false); + return wr.ToString(); + } + public static string GuardToString(DafnyOptions options, bool isBindingGuard, Expression expr) { Contract.Requires(!isBindingGuard || (expr is ExistsExpr && ((ExistsExpr)expr).Range == null)); using (var wr = new System.IO.StringWriter()) { diff --git a/Source/DafnyCore/Rewriters/InductionRewriter.cs b/Source/DafnyCore/Rewriters/InductionRewriter.cs index c4cde84a0e0..d5ad7866664 100644 --- a/Source/DafnyCore/Rewriters/InductionRewriter.cs +++ b/Source/DafnyCore/Rewriters/InductionRewriter.cs @@ -168,16 +168,7 @@ void ComputeInductionVariables(IToken tok, List boundVars, E // The argument list was legal, so let's use it for the _induction attribute. // Next, look for matching patterns for the induction hypothesis. if (lemma != null) { - var triggers = ComputeInductionTriggers(goodArguments, body, lemma.EnclosingClass.EnclosingModuleDefinition); - if (triggers.Count == 0) { - var suppressWarnings = Attributes.Contains(attributes, "nowarn"); - var warningLevel = suppressWarnings ? ErrorLevel.Info : ErrorLevel.Warning; - - Reporter.Message(MessageSource.Rewriter, warningLevel, null, tok, - "Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. " + - $"Change or remove the {{:induction}} attribute to generate a different induction hypothesis, or add {{:nowarn}} to silence this warning. " + - "For more information, see the section quantifier instantiation rules in the reference manual."); - } + var triggers = ComputeInductionTriggers(goodArguments, body, lemma.EnclosingClass.EnclosingModuleDefinition, tok, attributes); ReportInductionTriggers(lemma, ref attributes, triggers); } @@ -207,14 +198,12 @@ void ComputeInductionVariables(IToken tok, List boundVars, E // Compute the induction triggers, but don't report their patterns into attributes yet. Instead, // call ReportInductionTriggers only after the "_induction" attribute has been added. This will cause the // tooltips to appear in a logical order (showing the induction variables first, followed by the matching patterns). - triggers = ComputeInductionTriggers(inductionVariables, body, lemma.EnclosingClass.EnclosingModuleDefinition); - if (triggers.Count == 0) { - var msg = "omitting automatic induction because of lack of triggers"; - if (args != null) { - Reporter.Warning(MessageSource.Rewriter, GenericErrors.ErrorId.none, tok, msg); - } else { - Reporter.Info(MessageSource.Rewriter, tok, msg); - } + triggers = ComputeInductionTriggers(inductionVariables, body, lemma.EnclosingClass.EnclosingModuleDefinition, + args != null ? tok : null, attributes); + if (triggers.Count == 0 && args == null) { + // The user didn't ask for induction. But since there were candidate induction variables, report an informational message. + var candidates = $"candidate{Util.Plural(inductionVariables.Count)} {Printer.ExprListToString(Reporter.Options, inductionVariables)}"; + Reporter.Info(MessageSource.Rewriter, tok, $"omitting automatic induction (for induction-variable {candidates}) because of lack of triggers"); return; } } @@ -254,9 +243,13 @@ private void ReportInductionTriggers(Method lemma, ref Attributes attributes, Li /// /// Obtain and return matching patterns for /// (forall inductionVariables :: body) - /// If there aren't any, then return null. + /// If there aren't any, then return an empty list. + /// + /// If "errorToken" is non-null and there are no matching patterns, then a warning/info message is emitted. + /// The selection between warning vs info is done by looking for a {:nowarn} attribute among "attributes". /// - List> ComputeInductionTriggers(List inductionVariables, Expression body, ModuleDefinition moduleDefinition) { + List> ComputeInductionTriggers(List inductionVariables, Expression body, ModuleDefinition moduleDefinition, + [CanBeNull] IToken errorToken, Attributes attributes) { Contract.Requires(inductionVariables.Count != 0); // Construct a quantifier, because that's what the trigger-generating machinery expects. @@ -294,7 +287,21 @@ List> ComputeInductionTriggers(List inductionVariab // to be ignored, so it is safer to not include triggers that require additional bound variables.) var triggers = quantifierCollection.GetTriggers(false); var reverseSubstituter = new Substituter(null, reverseSubstMap, new Dictionary()); - return triggers.ConvertAll(trigger => trigger.ConvertAll(reverseSubstituter.Substitute)); + var result = triggers.ConvertAll(trigger => trigger.ConvertAll(reverseSubstituter.Substitute)); + + if (result.Count == 0 && errorToken != null) { + // The user explicitly asked for induction (with {:induction}, {:induction true}, or {:induction }). + // Respect this choice, but generate a warning that no triggers were found. + var suppressWarnings = Attributes.Contains(attributes, "nowarn"); + var warningLevel = suppressWarnings ? ErrorLevel.Info : ErrorLevel.Warning; + + Reporter.Message(MessageSource.Rewriter, warningLevel, null, errorToken, + "Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. " + + $"Change or remove the {{:induction}} attribute to generate a different induction hypothesis, or add {{:nowarn}} to silence this warning. " + + "For more information, see the section quantifier instantiation rules in the reference manual."); + } + + return result; } class InductionVisitor : BottomUpVisitor { diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy index 171bc0a8612..5e35dcdf2ef 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy @@ -19,3 +19,107 @@ lemma {:induction n} ExchangeEtaWithInductionAttribute(n: nat, f: int -> int, g: ensures Sum(n, x => f(x)) == Sum(n, x => g(x)) { } + +// ------------------------------------------- + +type OpaqueType + +// Recursive predicates. Note that the heuristics for finding candidate induction variables pay attention +// to whether or not the predicate is recursive. + +predicate P(n: nat) { + if n <= 0 then true else P(n - 1) +} + +predicate Q(n: nat, b: bool) { + if n <= 0 then true else Q(n - 1, !b) +} + +// -------------------- + +lemma {:induction n, b} GivenListNoTrigger0(n: nat, o: OpaqueType, b: bool) // warning: no trigger + requires 0 <= n < 100 + ensures P(if b then n else n) +{ +} + +lemma {:induction n, b} GivenListNoTrigger1(n: nat, o: OpaqueType, b: bool) // warning: no trigger + requires 0 <= n < 100 + ensures P(n) +{ +} + +lemma {:induction n} GivenList(n: nat, o: OpaqueType, b: bool) // matching pattern for IH: P(n) + requires 0 <= n < 100 + ensures P(n) +{ +} + +// -------------------- + +lemma {:induction} YesToIH(n: nat, o: OpaqueType, b: bool) // induction: n, b; warning: no trigger + requires 0 <= n < 100 + ensures P(if b then n else n) +{ // cannot prove postcondition +} + +lemma {:induction} YesToIH1(n: nat, o: OpaqueType, b: bool) // induction: n, b; warning: no trigger + requires 0 <= n < 100 + ensures P(n) +{ // cannot prove postcondition +} + +lemma {:induction} YesToIH2(n: nat, o: OpaqueType) // induction: n + requires 0 <= n < 100 + ensures P(n) +{ +} + +// -------------------- + +lemma {:induction false} NoIH(n: nat, o: OpaqueType, b: bool) + requires 0 <= n < 100 + ensures P(if b then n else n) +{ // cannot prove postcondition +} + +lemma {:induction false} NoIHButManualProof(n: nat, o: OpaqueType, b: bool) + requires 0 <= n < 100 + ensures P(if b then n else n) +{ + if n != 0 { + NoIHButManualProof(n - 1, o, b); + } +} + +// -------------------- + +lemma AutomaticInduction(n: nat, o: OpaqueType, b: bool) // no induction, because no triggers (candidates: n) + requires 0 <= n < 100 + ensures P(if b then n else n) +{ // cannot prove postcondition +} + +lemma AutomaticInduction1(n: nat, o: OpaqueType, b: bool) // induction: n; trigger: P(n) + requires 0 <= n < 100 + ensures P(n) +{ +} + +lemma AutomaticInduction2(n: nat, o: OpaqueType, b: bool) // induction: n, b; trigger: Q(n, b) + requires 0 <= n < 100 + ensures Q(n, b) +{ +} + +lemma AutomaticInduction3(n: nat, o: OpaqueType, b: bool) // induction: n; trigger: Q(n, true) + requires 0 <= n < 100 + ensures Q(n, true) +{ +} + +lemma AutomaticInduction4(n: nat, o: OpaqueType, b: bool) // no induction, because no triggers (candidates: n, b) + requires 0 <= n < 100 + ensures P(n) && Q(n + 12, b) +{ // cannot prove postcondition +} diff --git a/docs/dev/news/5835.feat b/docs/dev/news/5835.feat index d9fb6f80a25..5858b08bbe0 100644 --- a/docs/dev/news/5835.feat +++ b/docs/dev/news/5835.feat @@ -1,5 +1,41 @@ -Fill in matching patterns for the quantifiers introduced by automatic induction to represent the induction hypothesis. Suppress the generation of the induction hypothesis if no such matching patterns are found. Enhance tooltips accordingly. This feature is added to make stabilize verification, but by sometimes not generating induction hypotheses, some automatic proofs may no longer go through. +Fill in matching patterns for the quantifiers introduced by automatic induction to represent the induction hypothesis. Suppress the generation of the induction hypothesis if no such matching patterns are found. Enhance tooltips accordingly. This feature is added to stabilize verification, but by sometimes not generating induction hypotheses, some automatic proofs may no longer go through. For backward compatibility, use an explicit `{:induction ...}` where `...` is the list of variables to use for the induction-hypothesis quantifier. Additionally, use a `{:nowarn}` attribute to suppress any warning about lack of matching patterns. Improve the selection of induction variables. Allow codatatype equality in matching patterns and as a focal predicate for extreme predicates. + +More specifically: + +* If a lemma bears `{:induction x, y, z}`, where `x, y, z` is a subset of the lemma's parameters (in the same order + that the lemma gives them), then an induction hypothesis (IH) is generated. The IH quantifies over the + given variables. + + For an instance-member lemma, the variables may include the implicit `this` parameter. + For an extreme lemma, the IH generated is the for corresponding prefix lemma, and the given variables may + include the implicit parameter `_k`. + + If good matching patterns are found for the quantifier, then these are indicated in tooltips. + If no patterns are found, then a warning is generated; except, if the lemma bears `{:nowarn}`, then only + an informational message is given. + +* If a lemma bears `{:induction}` or `{:induction true}`, then a list of induction variables is determined heuristically. + If the list is empty, then a warning message is generated and no IH is generated. If the list is nonempty, + an IH is generated and the list of variables is indicated in a tooltip. + + If good matching patterns are found for the quantifier, then these are indicated in tooltips. + If no patterns are found, then a warning is generated; except, if the lemma bears {:nowarn}, then only + an informational message is given. + +* If a lemma bears `{:induction false}`, then no IH is generated. + +* If a lemma bears an `:induction` attribute other than those listed above, then an error is generated. + +* If a lemma bears no `:induction` attribute, then a list of induction variables is determined heuristically. + If this list is empty, then no IH is applied and no warning/info is given. + + If the list is nonempty, then the machinery looks for matching patterns for the IH quantifier. If none are + found, then an informational message is generated, saying which candidate variables were used and saying that + no matching patterns were found. + + If patterns are found, then the list of variables and the patterns are indicated in tooltips, and the + patterns are used with the IH quantifier. From 9e9dd804d1b987e3afdbe030eee6615fc20ce76a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Pit-Claudel?= Date: Wed, 23 Oct 2024 18:26:26 +0200 Subject: [PATCH 093/151] doc: Align :induction documentation with actual behavior We don't accept {:induction X} for arbitrary Xs, and bound variables must be in order. --- docs/DafnyRef/Attributes.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/DafnyRef/Attributes.md b/docs/DafnyRef/Attributes.md index baf0bbf484b..33f90db0ccc 100644 --- a/docs/DafnyRef/Attributes.md +++ b/docs/DafnyRef/Attributes.md @@ -323,13 +323,11 @@ The two contexts are: The form of the `{:induction}` attribute is one of the following: -* `{:induction}` -- apply induction to all bound variables +* `{:induction}` or `{:induction true}` -- apply induction to all bound variables * `{:induction false}` -- suppress induction, that is, don't apply it to any bound variable -* `{:induction L}` where `L` is a list consisting entirely of bound variables +* `{:induction L}` where `L` is a sublist of the bound variables -- apply induction to the specified bound variables -* `{:induction X}` where `X` is anything else -- treat the same as -`{:induction}`, that is, apply induction to all bound variables. For this -usage conventionally `X` is `true`. +* `{:induction X}` where `X` is anything else -- raise an error. Here is an example of using it on a quantifier expression: From 8a792b1e88df1a59f84187f2ecc0c881c5acb65e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Pit-Claudel?= Date: Wed, 23 Oct 2024 19:01:59 +0200 Subject: [PATCH 094/151] Remove unnecessary $s --- Source/DafnyCore/Rewriters/InductionRewriter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/DafnyCore/Rewriters/InductionRewriter.cs b/Source/DafnyCore/Rewriters/InductionRewriter.cs index d5ad7866664..08bd94cf227 100644 --- a/Source/DafnyCore/Rewriters/InductionRewriter.cs +++ b/Source/DafnyCore/Rewriters/InductionRewriter.cs @@ -297,7 +297,7 @@ List> ComputeInductionTriggers(List inductionVariab Reporter.Message(MessageSource.Rewriter, warningLevel, null, errorToken, "Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. " + - $"Change or remove the {{:induction}} attribute to generate a different induction hypothesis, or add {{:nowarn}} to silence this warning. " + + "Change or remove the {:induction} attribute to generate a different induction hypothesis, or add {:nowarn} to silence this warning. " + "For more information, see the section quantifier instantiation rules in the reference manual."); } From 7c6ab8472ffee62b1dfbd6fb157647fd2d866037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Pit-Claudel?= Date: Wed, 23 Oct 2024 21:21:09 +0200 Subject: [PATCH 095/151] Move :inductionPattern attribute generation to ComputeInductionTriggers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … and rename it to :inductionTrigger. --- .../DafnyCore/Rewriters/InductionRewriter.cs | 37 ++++++++++--------- .../Verifier/BoogieGenerator.Methods.cs | 2 +- .../LitTest/dafny0/PrefixTypeSubst.dfy.expect | 12 +++--- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/Source/DafnyCore/Rewriters/InductionRewriter.cs b/Source/DafnyCore/Rewriters/InductionRewriter.cs index 08bd94cf227..6fdd0b2b1c5 100644 --- a/Source/DafnyCore/Rewriters/InductionRewriter.cs +++ b/Source/DafnyCore/Rewriters/InductionRewriter.cs @@ -168,8 +168,8 @@ void ComputeInductionVariables(IToken tok, List boundVars, E // The argument list was legal, so let's use it for the _induction attribute. // Next, look for matching patterns for the induction hypothesis. if (lemma != null) { - var triggers = ComputeInductionTriggers(goodArguments, body, lemma.EnclosingClass.EnclosingModuleDefinition, tok, attributes); - ReportInductionTriggers(lemma, ref attributes, triggers); + var triggers = ComputeInductionTriggers(goodArguments, body, lemma.EnclosingClass.EnclosingModuleDefinition, tok, ref attributes); + ReportInductionTriggers(lemma, attributes); } attributes = new Attributes("_induction", goodArguments, attributes); @@ -193,14 +193,13 @@ void ComputeInductionVariables(IToken tok, List boundVars, E } if (inductionVariables.Count != 0) { - List> triggers = null; if (lemma != null) { // Compute the induction triggers, but don't report their patterns into attributes yet. Instead, // call ReportInductionTriggers only after the "_induction" attribute has been added. This will cause the // tooltips to appear in a logical order (showing the induction variables first, followed by the matching patterns). - triggers = ComputeInductionTriggers(inductionVariables, body, lemma.EnclosingClass.EnclosingModuleDefinition, - args != null ? tok : null, attributes); - if (triggers.Count == 0 && args == null) { + var triggers = ComputeInductionTriggers(inductionVariables, body, lemma.EnclosingClass.EnclosingModuleDefinition, + args != null ? tok : null, ref attributes); + if (triggers == null && args == null) { // The user didn't ask for induction. But since there were candidate induction variables, report an informational message. var candidates = $"candidate{Util.Plural(inductionVariables.Count)} {Printer.ExprListToString(Reporter.Options, inductionVariables)}"; Reporter.Info(MessageSource.Rewriter, tok, $"omitting automatic induction (for induction-variable {candidates}) because of lack of triggers"); @@ -217,39 +216,38 @@ void ComputeInductionVariables(IToken tok, List boundVars, E } Reporter.Info(MessageSource.Rewriter, tok, s); - if (triggers != null) { - ReportInductionTriggers(lemma, ref attributes, triggers); - } + ReportInductionTriggers(lemma, attributes); } } /// /// Report as tooltips the matching patterns selected for the induction hypothesis. /// - private void ReportInductionTriggers(Method lemma, ref Attributes attributes, List> triggers) { - foreach (var trigger in triggers) { - attributes = new Attributes("_inductionPattern", trigger, attributes); + private void ReportInductionTriggers([CanBeNull] Method lemma, Attributes attributes) { #if DEBUG - var ss = Printer.OneAttributeToString(Reporter.Options, attributes, "inductionPattern"); + foreach (var trigger in attributes.AsEnumerable().Where(attr => attr.Name == "inductionTrigger")) { + var ss = Printer.OneAttributeToString(Reporter.Options, trigger); if (lemma is PrefixLemma) { ss = lemma.Name + " " + ss; } Reporter.Info(MessageSource.Rewriter, lemma.tok, ss); -#endif } +#endif } /// /// Obtain and return matching patterns for /// (forall inductionVariables :: body) - /// If there aren't any, then return an empty list. + /// If there aren't any, then return null. + /// This trigger may come from analyzing "body" or from any user-supplied {:inductionTrigger ...} attributes. + /// Passing {:inductionTrigger} with no arguments causes an empty list to be returned (and disables trigger generation). /// /// If "errorToken" is non-null and there are no matching patterns, then a warning/info message is emitted. /// The selection between warning vs info is done by looking for a {:nowarn} attribute among "attributes". /// List> ComputeInductionTriggers(List inductionVariables, Expression body, ModuleDefinition moduleDefinition, - [CanBeNull] IToken errorToken, Attributes attributes) { + [CanBeNull] IToken errorToken, ref Attributes attributes) { Contract.Requires(inductionVariables.Count != 0); // Construct a quantifier, because that's what the trigger-generating machinery expects. @@ -301,7 +299,10 @@ List> ComputeInductionTriggers(List inductionVariab "For more information, see the section quantifier instantiation rules in the reference manual."); } - return result; + foreach (var trigger in result) { + attributes = new Attributes("inductionTrigger", trigger, attributes); + } + return result.Count == 0 ? null : result; // Return null to indicate no results } class InductionVisitor : BottomUpVisitor { @@ -318,4 +319,4 @@ protected override void VisitOneExpr(Expression expr) { } } } -} \ No newline at end of file +} diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 7a893ade472..38d1e109ec6 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -782,7 +782,7 @@ private StmtList TrMethodBody(Method m, BoogieStmtListBuilder builder, Variables null, null, false, true); }; - var triggers = Attributes.FindAllExpressions(m.Attributes, "_inductionPattern"); + var triggers = Attributes.FindAllExpressions(m.Attributes, "inductionTrigger"); #if VERIFY_CORRECTNESS_OF_TRANSLATION_FORALL_STATEMENT_RANGE var definedness = new BoogieStmtListBuilder(this, options, builder.Context); var exporter = new BoogieStmtListBuilder(this, options, builder.Context); diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/PrefixTypeSubst.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/PrefixTypeSubst.dfy.expect index 7f1f51f43c1..ea3294a2439 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/PrefixTypeSubst.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/PrefixTypeSubst.dfy.expect @@ -231,7 +231,7 @@ greatest lemma N(o: MyClass) N(o); } /*** -lemma {:axiom} /*{:_induction _k}*/ /*{:_inductionPattern o.R#[_k]()}*/ N#[_k: ORDINAL](o: MyClass) +lemma {:axiom} /*{:_induction _k}*/ /*{:inductionTrigger o.R#[_k]()}*/ N#[_k: ORDINAL](o: MyClass) ensures o.R#[_k]() decreases _k, o { @@ -253,7 +253,7 @@ greatest lemma O() O(); } /*** -lemma {:axiom} /*{:_induction _k}*/ /*{:_inductionPattern MyClass.S#[_k]()}*/ O#[_k: ORDINAL]() +lemma {:axiom} /*{:_induction _k}*/ /*{:inductionTrigger MyClass.S#[_k]()}*/ O#[_k: ORDINAL]() ensures MyClass.S#[_k]() decreases _k { @@ -448,7 +448,7 @@ greatest lemma RstRst7() } } /*** -lemma {:axiom} /*{:_induction _k}*/ /*{:_inductionPattern MyClass.RST#[_k]()}*/ RstRst7#[_k: ORDINAL]() +lemma {:axiom} /*{:_induction _k}*/ /*{:inductionTrigger MyClass.RST#[_k]()}*/ RstRst7#[_k: ORDINAL]() ensures MyClass.RST#[_k]() decreases _k { @@ -512,7 +512,7 @@ greatest lemma RstRst10[nat]() { } /*** -lemma {:axiom} /*{:_induction _k}*/ /*{:_inductionPattern MyClass.RST_Nat#[_k]()}*/ RstRst10#[_k: nat]() +lemma {:axiom} /*{:_induction _k}*/ /*{:inductionTrigger MyClass.RST_Nat#[_k]()}*/ RstRst10#[_k: nat]() ensures MyClass.RST_Nat#[_k]() decreases _k { @@ -588,7 +588,7 @@ class MyClass { L(u, v); } /*** - lemma {:axiom} /*{:_induction _k}*/ /*{:_inductionPattern P#[_k](u, v)}*/ L#[_k: ORDINAL](u: U, v: V) + lemma {:axiom} /*{:_induction _k}*/ /*{:inductionTrigger P#[_k](u, v)}*/ L#[_k: ORDINAL](u: U, v: V) ensures P#[_k](u, v) decreases _k { @@ -611,7 +611,7 @@ class MyClass { assert R#[_k - 1](); } /*** - lemma {:axiom} /*{:_induction _k}*/ /*{:_inductionPattern R#[_k]()}*/ M#[_k: ORDINAL]() + lemma {:axiom} /*{:_induction _k}*/ /*{:inductionTrigger R#[_k]()}*/ M#[_k: ORDINAL]() ensures R#[_k]() decreases _k { From 766ac5b07b5fc170fcb2d5c644f95ea4306d365f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Pit-Claudel?= Date: Wed, 23 Oct 2024 21:22:36 +0200 Subject: [PATCH 096/151] Allow users to disable trigger generation with an empty inductionTrigger --- .../DafnyCore/Rewriters/InductionRewriter.cs | 6 ++++++ .../Triggers/SplitPartTriggerWriter.cs | 18 +++++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Source/DafnyCore/Rewriters/InductionRewriter.cs b/Source/DafnyCore/Rewriters/InductionRewriter.cs index 6fdd0b2b1c5..4370767973c 100644 --- a/Source/DafnyCore/Rewriters/InductionRewriter.cs +++ b/Source/DafnyCore/Rewriters/InductionRewriter.cs @@ -250,6 +250,12 @@ List> ComputeInductionTriggers(List inductionVariab [CanBeNull] IToken errorToken, ref Attributes attributes) { Contract.Requires(inductionVariables.Count != 0); + if (Attributes.Contains(attributes, "inductionTrigger")) { + // Empty triggers are not valid at the Boogie level, but they indicate that we don't want automatic selection + Triggers.SplitPartTriggerWriter.DisableEmptyTriggers(attributes, "inductionTrigger"); + return Attributes.FindAllExpressions(attributes, "inductionTrigger") ?? new List>(); // Never null + } + // Construct a quantifier, because that's what the trigger-generating machinery expects. // We start by creating a new BoundVar for each ThisExpr-or-IdentifierExpr in "inductionVariables". var boundVars = new List(); diff --git a/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs b/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs index 344742a2235..95a755a42c7 100644 --- a/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs +++ b/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs @@ -152,13 +152,7 @@ public void CommitTrigger(ErrorReporter errorReporter, int? splitPartIndex, Syst } if (!NeedsAutoTriggers()) { - var triggerAttribute = Attributes.Find(Comprehension.Attributes, "trigger"); - if (triggerAttribute != null && triggerAttribute.Args.Count == 0) { - // Remove an empty trigger attribute, so it does not crash Boogie, - // and effectively becomes a way to silence a Dafny warning - triggerAttribute.Name = "deleted-trigger"; - } - return; + DisableEmptyTriggers(Comprehension.Attributes, "trigger"); } AddTriggerAttribute(systemModuleManager); @@ -199,6 +193,16 @@ string InfoFirstLineEnd(int count) { } } + public static void DisableEmptyTriggers(Attributes attribs, String attrName) { + foreach (var attr in attribs.AsEnumerable()) { + if (attr.Name == attrName && attr.Args.Count == 0) { + // Remove an empty trigger attribute, so it does not crash Boogie, + // and effectively becomes a way to silence a Dafny warning + attr.Name = $"deleted-{attrName}"; + } + } + } + private void AddTriggerAttribute(SystemModuleManager systemModuleManager) { foreach (var candidate in Candidates) { Comprehension.Attributes = new Attributes("trigger", From a3702d288b7a08684847eac4e49eb7e1978a55a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Pit-Claudel?= Date: Wed, 23 Oct 2024 21:24:10 +0200 Subject: [PATCH 097/151] Update documentation - Mention --manual-lemma-induction - Mention {:inductionTrigger} --- docs/dev/news/5835.feat | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/docs/dev/news/5835.feat b/docs/dev/news/5835.feat index 5858b08bbe0..daa626ee536 100644 --- a/docs/dev/news/5835.feat +++ b/docs/dev/news/5835.feat @@ -11,6 +11,7 @@ More specifically: given variables. For an instance-member lemma, the variables may include the implicit `this` parameter. + For an extreme lemma, the IH generated is the for corresponding prefix lemma, and the given variables may include the implicit parameter `_k`. @@ -19,6 +20,7 @@ More specifically: an informational message is given. * If a lemma bears `{:induction}` or `{:induction true}`, then a list of induction variables is determined heuristically. + If the list is empty, then a warning message is generated and no IH is generated. If the list is nonempty, an IH is generated and the list of variables is indicated in a tooltip. @@ -30,12 +32,19 @@ More specifically: * If a lemma bears an `:induction` attribute other than those listed above, then an error is generated. -* If a lemma bears no `:induction` attribute, then a list of induction variables is determined heuristically. - If this list is empty, then no IH is applied and no warning/info is given. +* If a lemma bears no `:induction` attribute, and the `--manual-lemma-induction` flag is present, then no IH is generated. + +* Otherwise, a list of induction variables is determined heuristically. + + If this list is empty, then no IH is generated and no warning/info is given. If the list is nonempty, then the machinery looks for matching patterns for the IH quantifier. If none are - found, then an informational message is generated, saying which candidate variables were used and saying that - no matching patterns were found. + found, then no IH is generated. An informational message is generated, saying which candidate variables were + used and saying that no matching patterns were found. + + If patterns are found, then an IH is generated, the list of variables and the patterns are indicated in tooltips, + and the patterns are used with the IH quantifier. - If patterns are found, then the list of variables and the patterns are indicated in tooltips, and the - patterns are used with the IH quantifier. + The pattern search can be overriden by providing patterns explicitly using the `{:inductionTrigger}` attribute. + This attribute has the same syntax as the `{:trigger}` attribute. Using an empty list of triggers restores + Dafny's legacy behavior (no triggers for lemma induction hypotheses). From ffe54af1e88e1903d787767537dea80418f4f1b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Pit-Claudel?= Date: Wed, 23 Oct 2024 21:26:31 +0200 Subject: [PATCH 098/151] Add one more test for induction triggers --- .../LitTest/triggers/induction-triggers.dfy | 36 ++++++ .../triggers/induction-triggers.dfy.expect | 112 ++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy.expect diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy new file mode 100644 index 00000000000..4c9f53a8ef0 --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy @@ -0,0 +1,36 @@ +// RUN: %testDafnyForEachResolver "%s" --expect-exit-code=4 + +predicate f(n: nat) { if n == 0 then true else f(n-1) } +predicate g(n: nat) { false } + +// Default: auto-generated trigger ⇒ Proof passes. +lemma Default(n: nat) ensures f(n) {} + +// Manual list of variables ⇒ Proof passes. +lemma {:induction n} ListOfVars(n: nat) ensures f(n) {} + +// No induction ⇒ Proof fails. +lemma {:induction false} NoInduction(n: nat) ensures f(n) {} + +// No induction, with manual proof ⇒ Proof passes. +lemma {:induction false} ManualInduction(n: nat) + ensures f(n) +{ + forall ih_n: nat | (n decreases to ih_n) { + ManualInduction(ih_n); + } +} + +// No triggers, so no auto induction ⇒ Proof fails. +lemma NoTriggers(n: nat) ensures f(n + 0) {} + +// No triggers but forced induction, so warning ⇒ Proof passes. +lemma {:induction} InductionWarning(n: nat) ensures f(n + 0) {} + +// Explicit triggers, so no warning ⇒ Proof passes. +lemma {:induction} {:inductionTrigger f(n)} NoWarning2(n: nat) ensures f(n + 0) {} + +// Legacy mode: auto induction with no triggers ⇒ Proof passes. +lemma {:inductionTrigger} Legacy(n: nat) ensures f(n) {} +lemma {:inductionTrigger} Legacy1(n: nat) ensures f(n + 0) {} +lemma {:induction} {:inductionTrigger} Legacy2(n: nat) ensures f(n + 0) {} diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy.expect new file mode 100644 index 00000000000..f97403cb67c --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy.expect @@ -0,0 +1,112 @@ +induction-triggers.dfy(3,10): Info: tail recursive + | +3 | predicate f(n: nat) { if n == 0 then true else f(n-1) } + | ^ + +induction-triggers.dfy(19,2): Info: Selected triggers: {f(ih_n)} + | +19 | forall ih_n: nat | (n decreases to ih_n) { + | ^^^^^^ + +induction-triggers.dfy(3,10): Info: decreases n + | +3 | predicate f(n: nat) { if n == 0 then true else f(n-1) } + | ^ + +induction-triggers.dfy(16,25): Info: decreases n + | +16 | lemma {:induction false} ManualInduction(n: nat) + | ^^^^^^^^^^^^^^^ + +induction-triggers.dfy(7,6): Info: {:induction n} + | +7 | lemma Default(n: nat) ensures f(n) {} + | ^^^^^^^ + +induction-triggers.dfy(7,6): Info: {:inductionTrigger f(n)} + | +7 | lemma Default(n: nat) ensures f(n) {} + | ^^^^^^^ + +induction-triggers.dfy(10,21): Info: {:inductionTrigger f(n)} + | +10 | lemma {:induction n} ListOfVars(n: nat) ensures f(n) {} + | ^^^^^^^^^^ + +induction-triggers.dfy(25,6): Info: omitting automatic induction (for induction-variable candidate n) because of lack of triggers + | +25 | lemma NoTriggers(n: nat) ensures f(n + 0) {} + | ^^^^^^^^^^ + +induction-triggers.dfy(28,19): Warning: Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. Change or remove the {:induction} attribute to generate a different induction hypothesis, or add {:nowarn} to silence this warning. For more information, see the section quantifier instantiation rules in the reference manual. + | +28 | lemma {:induction} InductionWarning(n: nat) ensures f(n + 0) {} + | ^^^^^^^^^^^^^^^^ + +induction-triggers.dfy(28,19): Info: {:induction n} + | +28 | lemma {:induction} InductionWarning(n: nat) ensures f(n + 0) {} + | ^^^^^^^^^^^^^^^^ + +induction-triggers.dfy(31,44): Info: {:induction n} + | +31 | lemma {:induction} {:inductionTrigger f(n)} NoWarning2(n: nat) ensures f(n + 0) {} + | ^^^^^^^^^^ + +induction-triggers.dfy(31,44): Info: {:inductionTrigger f(n)} + | +31 | lemma {:induction} {:inductionTrigger f(n)} NoWarning2(n: nat) ensures f(n + 0) {} + | ^^^^^^^^^^ + +induction-triggers.dfy(34,26): Info: {:induction n} + | +34 | lemma {:inductionTrigger} Legacy(n: nat) ensures f(n) {} + | ^^^^^^ + +induction-triggers.dfy(35,26): Info: {:induction n} + | +35 | lemma {:inductionTrigger} Legacy1(n: nat) ensures f(n + 0) {} + | ^^^^^^^ + +induction-triggers.dfy(36,39): Info: {:induction n} + | +36 | lemma {:induction} {:inductionTrigger} Legacy2(n: nat) ensures f(n + 0) {} + | ^^^^^^^ + +induction-triggers.dfy(19,2): Info: ensures f(ih_n) + | +19 | forall ih_n: nat | (n decreases to ih_n) { + | ^^^^^^ + +induction-triggers.dfy(13,58): Error: a postcondition could not be proved on this return path + | +13 | lemma {:induction false} NoInduction(n: nat) ensures f(n) {} + | ^ + +induction-triggers.dfy(13,53): Related location: this is the postcondition that could not be proved + | +13 | lemma {:induction false} NoInduction(n: nat) ensures f(n) {} + | ^^^^ + +induction-triggers.dfy(3,47): Related location: this proposition could not be proved + | +3 | predicate f(n: nat) { if n == 0 then true else f(n-1) } + | ^^^^^^ + +induction-triggers.dfy(25,42): Error: a postcondition could not be proved on this return path + | +25 | lemma NoTriggers(n: nat) ensures f(n + 0) {} + | ^ + +induction-triggers.dfy(25,33): Related location: this is the postcondition that could not be proved + | +25 | lemma NoTriggers(n: nat) ensures f(n + 0) {} + | ^^^^^^^^ + +induction-triggers.dfy(3,47): Related location: this proposition could not be proved + | +3 | predicate f(n: nat) { if n == 0 then true else f(n-1) } + | ^^^^^^ + + +Dafny program verifier finished with 14 verified, 2 errors From 8c7b06ca9459a0bb4072937adf99e736ebcba8e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Pit-Claudel?= Date: Thu, 24 Oct 2024 11:34:10 +0200 Subject: [PATCH 099/151] Document {:inductionTrigger} --- docs/DafnyRef/Attributes.md | 39 +++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/docs/DafnyRef/Attributes.md b/docs/DafnyRef/Attributes.md index 33f90db0ccc..8a85dd6347b 100644 --- a/docs/DafnyRef/Attributes.md +++ b/docs/DafnyRef/Attributes.md @@ -350,7 +350,12 @@ lemma Correspondence() } ``` -### 11.2.11. `{:only}` {#sec-only-functions-methods} + +### 11.2.11. `{:inductionTrigger}` {#sec-induction-trigger} + +Dafny automatically generates triggers for quantified induction hypotheses. The default selection can be overridden using the `{:inductionTrigger}` attribute, which works like the usual [`{:trigger}` attribute](#sec-trigger). + +### 11.2.12. `{:only}` {#sec-only-functions-methods} `method {:only} X() {}` or `function {:only} X() {}` temporarily disables the verification of all other non-`{:only}` members, e.g. other functions and methods, in the same file, even if they contain [assertions with `{:only}`](#sec-only). @@ -377,7 +382,7 @@ method TestUnverified() { More information about the Boogie implementation of `{:opaque}` is [here](https://github.com/dafny-lang/dafny/blob/master/docs/Compilation/Boogie.md). -### 11.2.12. `{:print}` {#sec-print} +### 11.2.13. `{:print}` {#sec-print} This attribute declares that a method may have print effects, that is, it may use `print` statements and may call other methods that have print effects. The attribute can be applied to compiled @@ -387,11 +392,11 @@ allowed to use a `{:print}` attribute only if the overridden method does. Print effects are enforced only with `--track-print-effects`. -### 11.2.13. `{:priority}` +### 11.2.14. `{:priority}` `{:priority N}` assigns a positive priority 'N' to a method or function to control the order in which methods or functions are verified (default: N = 1). -### 11.2.14. `{:resource_limit}` and `{:rlimit}` {#sec-rlimit} +### 11.2.15. `{:resource_limit}` and `{:rlimit}` {#sec-rlimit} `{:resource_limit N}` limits the verifier resource usage to verify the method or function to `N`. @@ -440,14 +445,14 @@ To give orders of magnitude about resource usage, here is a list of examples ind Note that, the default solver Z3 tends to overshoot by `7K` to `8K`, so if you put `{:rlimit 20}` in the last example, the total resource usage would be `27K`. -### 11.2.15. `{:selective_checking}` +### 11.2.16. `{:selective_checking}` Turn all assertions into assumptions except for the ones reachable from after the assertions marked with the attribute `{:start_checking_here}`. Thus, `assume {:start_checking_here} something;` becomes an inverse of `assume false;`: the first one disables all verification before it, and the second one disables all verification after. -### 11.2.16. `{:tailrecursion}` +### 11.2.17. `{:tailrecursion}` This attribute is used on method or function declarations. It has a boolean argument. If specified with a `false` value, it means the user specifically @@ -537,7 +542,7 @@ Note that the function definition can be changed by computing the tail closer to where it's used or switching the order of computing `r` and `tail`, but the `by method` body can stay the same. -### 11.2.17. `{:test}` {#sec-test-attribute} +### 11.2.18. `{:test}` {#sec-test-attribute} This attribute indicates the target function or method is meant to be executed at runtime in order to test that the program is working as intended. @@ -575,10 +580,10 @@ harness that supplies input arguments but has no inputs of its own and that checks any output values, perhaps with `expect` statements. The test harness is then the method marked with `{:test}`. -### 11.2.18. `{:timeLimit N}` {#sec-time-limit} +### 11.2.19. `{:timeLimit N}` {#sec-time-limit} Set the time limit for verifying a given function or method. -### 11.2.19. `{:timeLimitMultiplier X}` +### 11.2.20. `{:timeLimitMultiplier X}` This attribute may be placed on a method or function declaration and has an integer argument. If `{:timeLimitMultiplier X}` was specified a `{:timeLimit Y}` attribute is passed on to Boogie @@ -586,11 +591,11 @@ where `Y` is `X` times either the default verification time limit for a function or method, or times the value specified by the Boogie `-timeLimit` command-line option. -### 11.2.20. `{:transparent}` {#sec-transparent} +### 11.2.21. `{:transparent}` {#sec-transparent} By default, the body of a function is transparent to its users. This can be overridden using the `--default-function-opacity` command line flag. If default function opacity is set to `opaque` or `autoRevealDependencies`, then this attribute can be used on functions to make them always non-opaque. -### 11.2.21. `{:verify false}` {#sec-verify} +### 11.2.22. `{:verify false}` {#sec-verify} Skip verification of a function or a method altogether, not even trying to verify the [well-formedness](#sec-assertion-batches) of postconditions and preconditions. @@ -599,7 +604,7 @@ which performs these minimal checks while not checking that the body satisfies t If you simply want to temporarily disable all verification except on a single function or method, use the [`{:only}`](#sec-only-functions-methods) attribute on that function or method. -### 11.2.22. `{:vcs_max_cost N}` {#sec-vcs_max_cost} +### 11.2.23. `{:vcs_max_cost N}` {#sec-vcs_max_cost} Per-method version of the command-line option `/vcsMaxCost`. The [assertion batch](#sec-assertion-batches) of a method @@ -608,7 +613,7 @@ number, defaults to 2000.0. In [keep-going mode](#sec-vcs_max_keep_going_splits), only applies to the first round. If [`{:isolate_assertions}`](#sec-isolate_assertions) is set, then this parameter is useless. -### 11.2.23. `{:vcs_max_keep_going_splits N}` {#sec-vcs_max_keep_going_splits} +### 11.2.24. `{:vcs_max_keep_going_splits N}` {#sec-vcs_max_keep_going_splits} Per-method version of the command-line option `/vcsMaxKeepGoingSplits`. If set to more than 1, activates the _keep going mode_ where, after the first round of splitting, @@ -619,7 +624,7 @@ case an error is reported for that assertion). Defaults to 1. If [`{:isolate_assertions}`](#sec-isolate_assertions) is set, then this parameter is useless. -### 11.2.24. `{:vcs_max_splits N}` {#sec-vcs_max_splits} +### 11.2.25. `{:vcs_max_splits N}` {#sec-vcs_max_splits} Per-method version of the command-line option `/vcsMaxSplits`. Maximal number of [assertion batches](#sec-assertion-batches) generated for this method. @@ -627,14 +632,14 @@ In [keep-going mode](#sec-vcs_max_keep_going_splits), only applies to the first Defaults to 1. If [`{:isolate_assertions}`](#sec-isolate_assertions) is set, then this parameter is useless. -### 11.2.25. `{:isolate_assertions}` {#sec-isolate_assertions} +### 11.2.26. `{:isolate_assertions}` {#sec-isolate_assertions} Per-method version of the command-line option `/vcsSplitOnEveryAssert` In the first and only verification round, this option will split the original [assertion batch](#sec-assertion-batches) into one assertion batch per assertion. This is mostly helpful for debugging which assertion is taking the most time to prove, e.g. to profile them. -### 11.2.26. `{:synthesize}` {#sec-synthesize-attr} +### 11.2.27. `{:synthesize}` {#sec-synthesize-attr} The `{:synthesize}` attribute must be used on methods that have no body and return one or more fresh objects. During compilation, @@ -668,7 +673,7 @@ BOUNDVARS = ID : ID | BOUNDVARS, BOUNDVARS ``` -### 11.2.27. `{:options OPT0, OPT1, ... }` {#sec-attr-options} +### 11.2.28. `{:options OPT0, OPT1, ... }` {#sec-attr-options} This attribute applies only to modules. It configures Dafny as if `OPT0`, `OPT1`, … had been passed on the command line. Outside of the module, From 769d6cd03bd4b55b1a9af2d87e92de2f3cd62f6b Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 30 Oct 2024 16:06:27 -0700 Subject: [PATCH 100/151] =?UTF-8?q?Always=20report=20=E2=80=9CinductionTri?= =?UTF-8?q?gger=E2=80=9D,=20not=20just=20in=20DEBUG=20build?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Source/DafnyCore/Rewriters/InductionRewriter.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/Source/DafnyCore/Rewriters/InductionRewriter.cs b/Source/DafnyCore/Rewriters/InductionRewriter.cs index 4370767973c..a03e51eb408 100644 --- a/Source/DafnyCore/Rewriters/InductionRewriter.cs +++ b/Source/DafnyCore/Rewriters/InductionRewriter.cs @@ -169,7 +169,7 @@ void ComputeInductionVariables(IToken tok, List boundVars, E // Next, look for matching patterns for the induction hypothesis. if (lemma != null) { var triggers = ComputeInductionTriggers(goodArguments, body, lemma.EnclosingClass.EnclosingModuleDefinition, tok, ref attributes); - ReportInductionTriggers(lemma, attributes); + ReportInductionTriggers(lemma.tok, lemma, attributes); } attributes = new Attributes("_induction", goodArguments, attributes); @@ -216,24 +216,21 @@ void ComputeInductionVariables(IToken tok, List boundVars, E } Reporter.Info(MessageSource.Rewriter, tok, s); - ReportInductionTriggers(lemma, attributes); + ReportInductionTriggers(tok, lemma, attributes); } } /// /// Report as tooltips the matching patterns selected for the induction hypothesis. /// - private void ReportInductionTriggers([CanBeNull] Method lemma, Attributes attributes) { -#if DEBUG + private void ReportInductionTriggers(IToken tok, [CanBeNull] Method lemma, Attributes attributes) { foreach (var trigger in attributes.AsEnumerable().Where(attr => attr.Name == "inductionTrigger")) { var ss = Printer.OneAttributeToString(Reporter.Options, trigger); if (lemma is PrefixLemma) { ss = lemma.Name + " " + ss; } - - Reporter.Info(MessageSource.Rewriter, lemma.tok, ss); + Reporter.Info(MessageSource.Rewriter, tok, ss); } -#endif } /// @@ -251,9 +248,9 @@ List> ComputeInductionTriggers(List inductionVariab Contract.Requires(inductionVariables.Count != 0); if (Attributes.Contains(attributes, "inductionTrigger")) { - // Empty triggers are not valid at the Boogie level, but they indicate that we don't want automatic selection - Triggers.SplitPartTriggerWriter.DisableEmptyTriggers(attributes, "inductionTrigger"); - return Attributes.FindAllExpressions(attributes, "inductionTrigger") ?? new List>(); // Never null + // Empty triggers are not valid at the Boogie level, but they indicate that we don't want automatic selection + Triggers.SplitPartTriggerWriter.DisableEmptyTriggers(attributes, "inductionTrigger"); + return Attributes.FindAllExpressions(attributes, "inductionTrigger") ?? new List>(); // Never null } // Construct a quantifier, because that's what the trigger-generating machinery expects. From 2c7fcc993b8b47673cbc2bf61bd4f47500eb928f Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 30 Oct 2024 16:22:19 -0700 Subject: [PATCH 101/151] Fix format of expected test output --- .../LitTest/triggers/induction-triggers.dfy | 6 +- .../triggers/induction-triggers.dfy.expect | 88 ------------------- 2 files changed, 3 insertions(+), 91 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy index 4c9f53a8ef0..8e8dd5be01e 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy @@ -1,4 +1,4 @@ -// RUN: %testDafnyForEachResolver "%s" --expect-exit-code=4 +// RUN: %testDafnyForEachResolver --expect-exit-code=4 "%s" -- --show-hints predicate f(n: nat) { if n == 0 then true else f(n-1) } predicate g(n: nat) { false } @@ -10,7 +10,7 @@ lemma Default(n: nat) ensures f(n) {} lemma {:induction n} ListOfVars(n: nat) ensures f(n) {} // No induction ⇒ Proof fails. -lemma {:induction false} NoInduction(n: nat) ensures f(n) {} +lemma {:induction false} NoInduction(n: nat) ensures f(n) {} // error // No induction, with manual proof ⇒ Proof passes. lemma {:induction false} ManualInduction(n: nat) @@ -22,7 +22,7 @@ lemma {:induction false} ManualInduction(n: nat) } // No triggers, so no auto induction ⇒ Proof fails. -lemma NoTriggers(n: nat) ensures f(n + 0) {} +lemma NoTriggers(n: nat) ensures f(n + 0) {} // error // No triggers but forced induction, so warning ⇒ Proof passes. lemma {:induction} InductionWarning(n: nat) ensures f(n + 0) {} diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy.expect index f97403cb67c..20f20f140ad 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy.expect @@ -1,112 +1,24 @@ induction-triggers.dfy(3,10): Info: tail recursive - | -3 | predicate f(n: nat) { if n == 0 then true else f(n-1) } - | ^ - induction-triggers.dfy(19,2): Info: Selected triggers: {f(ih_n)} - | -19 | forall ih_n: nat | (n decreases to ih_n) { - | ^^^^^^ - induction-triggers.dfy(3,10): Info: decreases n - | -3 | predicate f(n: nat) { if n == 0 then true else f(n-1) } - | ^ - induction-triggers.dfy(16,25): Info: decreases n - | -16 | lemma {:induction false} ManualInduction(n: nat) - | ^^^^^^^^^^^^^^^ - induction-triggers.dfy(7,6): Info: {:induction n} - | -7 | lemma Default(n: nat) ensures f(n) {} - | ^^^^^^^ - induction-triggers.dfy(7,6): Info: {:inductionTrigger f(n)} - | -7 | lemma Default(n: nat) ensures f(n) {} - | ^^^^^^^ - induction-triggers.dfy(10,21): Info: {:inductionTrigger f(n)} - | -10 | lemma {:induction n} ListOfVars(n: nat) ensures f(n) {} - | ^^^^^^^^^^ - induction-triggers.dfy(25,6): Info: omitting automatic induction (for induction-variable candidate n) because of lack of triggers - | -25 | lemma NoTriggers(n: nat) ensures f(n + 0) {} - | ^^^^^^^^^^ - induction-triggers.dfy(28,19): Warning: Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. Change or remove the {:induction} attribute to generate a different induction hypothesis, or add {:nowarn} to silence this warning. For more information, see the section quantifier instantiation rules in the reference manual. - | -28 | lemma {:induction} InductionWarning(n: nat) ensures f(n + 0) {} - | ^^^^^^^^^^^^^^^^ - induction-triggers.dfy(28,19): Info: {:induction n} - | -28 | lemma {:induction} InductionWarning(n: nat) ensures f(n + 0) {} - | ^^^^^^^^^^^^^^^^ - induction-triggers.dfy(31,44): Info: {:induction n} - | -31 | lemma {:induction} {:inductionTrigger f(n)} NoWarning2(n: nat) ensures f(n + 0) {} - | ^^^^^^^^^^ - induction-triggers.dfy(31,44): Info: {:inductionTrigger f(n)} - | -31 | lemma {:induction} {:inductionTrigger f(n)} NoWarning2(n: nat) ensures f(n + 0) {} - | ^^^^^^^^^^ - induction-triggers.dfy(34,26): Info: {:induction n} - | -34 | lemma {:inductionTrigger} Legacy(n: nat) ensures f(n) {} - | ^^^^^^ - induction-triggers.dfy(35,26): Info: {:induction n} - | -35 | lemma {:inductionTrigger} Legacy1(n: nat) ensures f(n + 0) {} - | ^^^^^^^ - induction-triggers.dfy(36,39): Info: {:induction n} - | -36 | lemma {:induction} {:inductionTrigger} Legacy2(n: nat) ensures f(n + 0) {} - | ^^^^^^^ - induction-triggers.dfy(19,2): Info: ensures f(ih_n) - | -19 | forall ih_n: nat | (n decreases to ih_n) { - | ^^^^^^ - induction-triggers.dfy(13,58): Error: a postcondition could not be proved on this return path - | -13 | lemma {:induction false} NoInduction(n: nat) ensures f(n) {} - | ^ - induction-triggers.dfy(13,53): Related location: this is the postcondition that could not be proved - | -13 | lemma {:induction false} NoInduction(n: nat) ensures f(n) {} - | ^^^^ - induction-triggers.dfy(3,47): Related location: this proposition could not be proved - | -3 | predicate f(n: nat) { if n == 0 then true else f(n-1) } - | ^^^^^^ - induction-triggers.dfy(25,42): Error: a postcondition could not be proved on this return path - | -25 | lemma NoTriggers(n: nat) ensures f(n + 0) {} - | ^ - induction-triggers.dfy(25,33): Related location: this is the postcondition that could not be proved - | -25 | lemma NoTriggers(n: nat) ensures f(n + 0) {} - | ^^^^^^^^ - induction-triggers.dfy(3,47): Related location: this proposition could not be proved - | -3 | predicate f(n: nat) { if n == 0 then true else f(n-1) } - | ^^^^^^ - Dafny program verifier finished with 14 verified, 2 errors From af0223b91e4aa71a3e6692e0a260bf2b8e75cf42 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 30 Oct 2024 18:10:15 -0700 Subject: [PATCH 102/151] Use underscore name for generated attributes * By using _inductionTrigger for generated triggers, the Dafny machinery for cloning things into refinement modules works correctly * Tooltips only show things not already in the program text --- Source/DafnyCore/Rewriters/InductionRewriter.cs | 6 +++--- Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs | 5 ++++- .../LitTests/LitTest/triggers/induction-triggers.dfy | 3 +++ .../LitTest/triggers/induction-triggers.dfy.expect | 7 +++++-- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Source/DafnyCore/Rewriters/InductionRewriter.cs b/Source/DafnyCore/Rewriters/InductionRewriter.cs index a03e51eb408..35ced72b5ed 100644 --- a/Source/DafnyCore/Rewriters/InductionRewriter.cs +++ b/Source/DafnyCore/Rewriters/InductionRewriter.cs @@ -224,8 +224,8 @@ void ComputeInductionVariables(IToken tok, List boundVars, E /// Report as tooltips the matching patterns selected for the induction hypothesis. /// private void ReportInductionTriggers(IToken tok, [CanBeNull] Method lemma, Attributes attributes) { - foreach (var trigger in attributes.AsEnumerable().Where(attr => attr.Name == "inductionTrigger")) { - var ss = Printer.OneAttributeToString(Reporter.Options, trigger); + foreach (var trigger in attributes.AsEnumerable().Where(attr => attr.Name == "_inductionTrigger")) { + var ss = Printer.OneAttributeToString(Reporter.Options, trigger, "inductionTrigger"); if (lemma is PrefixLemma) { ss = lemma.Name + " " + ss; } @@ -303,7 +303,7 @@ List> ComputeInductionTriggers(List inductionVariab } foreach (var trigger in result) { - attributes = new Attributes("inductionTrigger", trigger, attributes); + attributes = new Attributes("_inductionTrigger", trigger, attributes); } return result.Count == 0 ? null : result; // Return null to indicate no results } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 878348d851b..078a0c361a3 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -790,7 +790,10 @@ private StmtList TrMethodBody(Method m, BoogieStmtListBuilder builder, Variables null, null, false, true); }; - var triggers = Attributes.FindAllExpressions(m.Attributes, "inductionTrigger"); + var triggers = m.Attributes.AsEnumerable() + .Where(attr => attr.Name is "inductionTrigger" or "_inductionTrigger") + .Select(attr => attr.Args) + .ToList(); #if VERIFY_CORRECTNESS_OF_TRANSLATION_FORALL_STATEMENT_RANGE var definedness = new BoogieStmtListBuilder(this, options, builder.Context); var exporter = new BoogieStmtListBuilder(this, options, builder.Context); diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy index 8e8dd5be01e..4a928d89195 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy @@ -34,3 +34,6 @@ lemma {:induction} {:inductionTrigger f(n)} NoWarning2(n: nat) ensures f(n + 0) lemma {:inductionTrigger} Legacy(n: nat) ensures f(n) {} lemma {:inductionTrigger} Legacy1(n: nat) ensures f(n + 0) {} lemma {:induction} {:inductionTrigger} Legacy2(n: nat) ensures f(n + 0) {} + +// Poorly chosen explicit trigger, so no warning ⇒ Proof fails. +lemma {:induction} {:inductionTrigger g(n)} NoWarning3(n: nat) ensures f(n + 0) {} // error diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy.expect index 20f20f140ad..ae615f9e9f7 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy.expect @@ -9,10 +9,10 @@ induction-triggers.dfy(25,6): Info: omitting automatic induction (for induction- induction-triggers.dfy(28,19): Warning: Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. Change or remove the {:induction} attribute to generate a different induction hypothesis, or add {:nowarn} to silence this warning. For more information, see the section quantifier instantiation rules in the reference manual. induction-triggers.dfy(28,19): Info: {:induction n} induction-triggers.dfy(31,44): Info: {:induction n} -induction-triggers.dfy(31,44): Info: {:inductionTrigger f(n)} induction-triggers.dfy(34,26): Info: {:induction n} induction-triggers.dfy(35,26): Info: {:induction n} induction-triggers.dfy(36,39): Info: {:induction n} +induction-triggers.dfy(39,44): Info: {:induction n} induction-triggers.dfy(19,2): Info: ensures f(ih_n) induction-triggers.dfy(13,58): Error: a postcondition could not be proved on this return path induction-triggers.dfy(13,53): Related location: this is the postcondition that could not be proved @@ -20,5 +20,8 @@ induction-triggers.dfy(3,47): Related location: this proposition could not be pr induction-triggers.dfy(25,42): Error: a postcondition could not be proved on this return path induction-triggers.dfy(25,33): Related location: this is the postcondition that could not be proved induction-triggers.dfy(3,47): Related location: this proposition could not be proved +induction-triggers.dfy(39,80): Error: a postcondition could not be proved on this return path +induction-triggers.dfy(39,71): Related location: this is the postcondition that could not be proved +induction-triggers.dfy(3,47): Related location: this proposition could not be proved -Dafny program verifier finished with 14 verified, 2 errors +Dafny program verifier finished with 15 verified, 3 errors From 6cdaa08a060b8de37fd3b42a469853d1b7c79e61 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Wed, 30 Oct 2024 18:41:49 -0700 Subject: [PATCH 103/151] Update tests --- .../LitTest/dafny0/PrefixTypeSubst.dfy.expect | 12 ++++---- .../LitTest/dafny1/Induction.legacy.dfy | 2 +- .../git-issues/git-issue-977.dfy.expect | 18 ++++++------ .../triggers/InductionWithoutTriggers.dfy | 20 ++++++------- .../InductionWithoutTriggers.dfy.expect | 26 ++++++++++++++++- ...nductionWithoutTriggers.dfy.refresh.expect | 28 +++++++++++++++++++ .../LitTest/triggers/induction-triggers.dfy | 3 ++ .../triggers/induction-triggers.dfy.expect | 3 +- 8 files changed, 84 insertions(+), 28 deletions(-) create mode 100644 Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.refresh.expect diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/PrefixTypeSubst.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/PrefixTypeSubst.dfy.expect index ea3294a2439..88360b7fc14 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/PrefixTypeSubst.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/PrefixTypeSubst.dfy.expect @@ -231,7 +231,7 @@ greatest lemma N(o: MyClass) N(o); } /*** -lemma {:axiom} /*{:_induction _k}*/ /*{:inductionTrigger o.R#[_k]()}*/ N#[_k: ORDINAL](o: MyClass) +lemma {:axiom} /*{:_inductionTrigger o.R#[_k]()}*/ /*{:_induction _k}*/ N#[_k: ORDINAL](o: MyClass) ensures o.R#[_k]() decreases _k, o { @@ -253,7 +253,7 @@ greatest lemma O() O(); } /*** -lemma {:axiom} /*{:_induction _k}*/ /*{:inductionTrigger MyClass.S#[_k]()}*/ O#[_k: ORDINAL]() +lemma {:axiom} /*{:_inductionTrigger MyClass.S#[_k]()}*/ /*{:_induction _k}*/ O#[_k: ORDINAL]() ensures MyClass.S#[_k]() decreases _k { @@ -448,7 +448,7 @@ greatest lemma RstRst7() } } /*** -lemma {:axiom} /*{:_induction _k}*/ /*{:inductionTrigger MyClass.RST#[_k]()}*/ RstRst7#[_k: ORDINAL]() +lemma {:axiom} /*{:_inductionTrigger MyClass.RST#[_k]()}*/ /*{:_induction _k}*/ RstRst7#[_k: ORDINAL]() ensures MyClass.RST#[_k]() decreases _k { @@ -512,7 +512,7 @@ greatest lemma RstRst10[nat]() { } /*** -lemma {:axiom} /*{:_induction _k}*/ /*{:inductionTrigger MyClass.RST_Nat#[_k]()}*/ RstRst10#[_k: nat]() +lemma {:axiom} /*{:_inductionTrigger MyClass.RST_Nat#[_k]()}*/ /*{:_induction _k}*/ RstRst10#[_k: nat]() ensures MyClass.RST_Nat#[_k]() decreases _k { @@ -588,7 +588,7 @@ class MyClass { L(u, v); } /*** - lemma {:axiom} /*{:_induction _k}*/ /*{:inductionTrigger P#[_k](u, v)}*/ L#[_k: ORDINAL](u: U, v: V) + lemma {:axiom} /*{:_inductionTrigger P#[_k](u, v)}*/ /*{:_induction _k}*/ L#[_k: ORDINAL](u: U, v: V) ensures P#[_k](u, v) decreases _k { @@ -611,7 +611,7 @@ class MyClass { assert R#[_k - 1](); } /*** - lemma {:axiom} /*{:_induction _k}*/ /*{:inductionTrigger R#[_k]()}*/ M#[_k: ORDINAL]() + lemma {:axiom} /*{:_inductionTrigger R#[_k]()}*/ /*{:_induction _k}*/ M#[_k: ORDINAL]() ensures R#[_k]() decreases _k { diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/Induction.legacy.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/Induction.legacy.dfy index a7309c89a8f..bdb2854ea37 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/Induction.legacy.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/Induction.legacy.dfy @@ -152,7 +152,7 @@ class IntegerInduction { ensures IsSorted(s) ==> (forall i,j {:induction j} {:autotriggers false} :: 0 <= i < j < |s| ==> s[i] <= s[j]); ensures (forall i,j :: 0 <= i && i < j && j < |s| ==> s[i] <= s[j]) ==> IsSorted(s); { - (forall i {:nowarn} {:matchinglooprewrite false}:: 1 <= i && i < |s| ==> s[i-1] <= s[i]) + (forall i {:inductionTrigger} {:matchinglooprewrite false}:: 1 <= i && i < |s| ==> s[i-1] <= s[i]) } } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-977.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-977.dfy.expect index cc0f8f9653d..0b860da1990 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-977.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-977.dfy.expect @@ -28,17 +28,17 @@ git-issue-977.dfy(84,16): Info: decreases num git-issue-977.dfy(129,16): Info: decreases k, num git-issue-977.dfy(146,16): Info: decreases k, num git-issue-977.dfy(54,6): Info: {:induction num} -git-issue-977.dfy(54,6): Info: {:inductionPattern GreatestPredNat(num)} -git-issue-977.dfy(54,6): Info: {:inductionPattern GreatestPredOrd(num)} -git-issue-977.dfy(54,6): Info: {:inductionPattern Pred(num)} +git-issue-977.dfy(54,6): Info: {:inductionTrigger GreatestPredNat(num)} +git-issue-977.dfy(54,6): Info: {:inductionTrigger GreatestPredOrd(num)} +git-issue-977.dfy(54,6): Info: {:inductionTrigger Pred(num)} git-issue-977.dfy(61,6): Info: {:induction k, num} -git-issue-977.dfy(61,6): Info: {:inductionPattern RicochetOrd(k, num)} -git-issue-977.dfy(61,6): Info: {:inductionPattern GreatestManualOrd(k, num)} -git-issue-977.dfy(61,6): Info: {:inductionPattern GreatestPredOrd#[k](num)} +git-issue-977.dfy(61,6): Info: {:inductionTrigger RicochetOrd(k, num)} +git-issue-977.dfy(61,6): Info: {:inductionTrigger GreatestManualOrd(k, num)} +git-issue-977.dfy(61,6): Info: {:inductionTrigger GreatestPredOrd#[k](num)} git-issue-977.dfy(77,6): Info: {:induction k, num} -git-issue-977.dfy(77,6): Info: {:inductionPattern RicochetNat(k, num)} -git-issue-977.dfy(77,6): Info: {:inductionPattern GreatestManualNat(k, num)} -git-issue-977.dfy(77,6): Info: {:inductionPattern GreatestPredNat#[k](num)} +git-issue-977.dfy(77,6): Info: {:inductionTrigger RicochetNat(k, num)} +git-issue-977.dfy(77,6): Info: {:inductionTrigger GreatestManualNat(k, num)} +git-issue-977.dfy(77,6): Info: {:inductionTrigger GreatestPredNat#[k](num)} git-issue-977.dfy(71,4): Info: ensures GreatestPredOrd#[m](num) git-issue-977.dfy(71,4): Info: ensures GreatestManualOrd(m, num) git-issue-977.dfy(71,4): Info: ensures RicochetOrd(m, num) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy index 5e35dcdf2ef..bb5822ead07 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy @@ -40,13 +40,13 @@ predicate Q(n: nat, b: bool) { lemma {:induction n, b} GivenListNoTrigger0(n: nat, o: OpaqueType, b: bool) // warning: no trigger requires 0 <= n < 100 ensures P(if b then n else n) -{ +{ // error: cannot prove postcondition } lemma {:induction n, b} GivenListNoTrigger1(n: nat, o: OpaqueType, b: bool) // warning: no trigger requires 0 <= n < 100 ensures P(n) -{ +{ // error: cannot prove postcondition } lemma {:induction n} GivenList(n: nat, o: OpaqueType, b: bool) // matching pattern for IH: P(n) @@ -56,17 +56,17 @@ lemma {:induction n} GivenList(n: nat, o: OpaqueType, b: bool) // matching patte } // -------------------- - -lemma {:induction} YesToIH(n: nat, o: OpaqueType, b: bool) // induction: n, b; warning: no trigger +// Just {:inductionTrigger} says to automatically pick variables for the induction, but then omit trigger +lemma {:inductionTrigger} YesToIH(n: nat, o: OpaqueType, b: bool) // induction: n; warning: no trigger requires 0 <= n < 100 ensures P(if b then n else n) -{ // cannot prove postcondition +{ } - +// {:induction} says to pick ALL parameters (of reasonable types) for the induction lemma {:induction} YesToIH1(n: nat, o: OpaqueType, b: bool) // induction: n, b; warning: no trigger requires 0 <= n < 100 ensures P(n) -{ // cannot prove postcondition +{ // error: cannot prove postcondition } lemma {:induction} YesToIH2(n: nat, o: OpaqueType) // induction: n @@ -80,7 +80,7 @@ lemma {:induction} YesToIH2(n: nat, o: OpaqueType) // induction: n lemma {:induction false} NoIH(n: nat, o: OpaqueType, b: bool) requires 0 <= n < 100 ensures P(if b then n else n) -{ // cannot prove postcondition +{ // error: cannot prove postcondition } lemma {:induction false} NoIHButManualProof(n: nat, o: OpaqueType, b: bool) @@ -97,7 +97,7 @@ lemma {:induction false} NoIHButManualProof(n: nat, o: OpaqueType, b: bool) lemma AutomaticInduction(n: nat, o: OpaqueType, b: bool) // no induction, because no triggers (candidates: n) requires 0 <= n < 100 ensures P(if b then n else n) -{ // cannot prove postcondition +{ // error: cannot prove postcondition } lemma AutomaticInduction1(n: nat, o: OpaqueType, b: bool) // induction: n; trigger: P(n) @@ -121,5 +121,5 @@ lemma AutomaticInduction3(n: nat, o: OpaqueType, b: bool) // induction: n; trigg lemma AutomaticInduction4(n: nat, o: OpaqueType, b: bool) // no induction, because no triggers (candidates: n, b) requires 0 <= n < 100 ensures P(n) && Q(n + 12, b) -{ // cannot prove postcondition +{ // error (x2): cannot prove postconditions } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.expect index 815d9943bf2..f049bcc234c 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.expect @@ -1,4 +1,28 @@ InductionWithoutTriggers.dfy(17,21): Warning: Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. Change or remove the {:induction} attribute to generate a different induction hypothesis, or add {:nowarn} to silence this warning. For more information, see the section quantifier instantiation rules in the reference manual. +InductionWithoutTriggers.dfy(40,24): Warning: Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. Change or remove the {:induction} attribute to generate a different induction hypothesis, or add {:nowarn} to silence this warning. For more information, see the section quantifier instantiation rules in the reference manual. +InductionWithoutTriggers.dfy(46,24): Warning: Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. Change or remove the {:induction} attribute to generate a different induction hypothesis, or add {:nowarn} to silence this warning. For more information, see the section quantifier instantiation rules in the reference manual. +InductionWithoutTriggers.dfy(66,19): Warning: Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. Change or remove the {:induction} attribute to generate a different induction hypothesis, or add {:nowarn} to silence this warning. For more information, see the section quantifier instantiation rules in the reference manual. InductionWithoutTriggers.dfy(11,9): Error: assertion might not hold +InductionWithoutTriggers.dfy(43,0): Error: a postcondition could not be proved on this return path +InductionWithoutTriggers.dfy(42,10): Related location: this is the postcondition that could not be proved +InductionWithoutTriggers.dfy(31,27): Related location: this proposition could not be proved +InductionWithoutTriggers.dfy(49,0): Error: a postcondition could not be proved on this return path +InductionWithoutTriggers.dfy(48,10): Related location: this is the postcondition that could not be proved +InductionWithoutTriggers.dfy(31,27): Related location: this proposition could not be proved +InductionWithoutTriggers.dfy(69,0): Error: a postcondition could not be proved on this return path +InductionWithoutTriggers.dfy(68,10): Related location: this is the postcondition that could not be proved +InductionWithoutTriggers.dfy(31,27): Related location: this proposition could not be proved +InductionWithoutTriggers.dfy(83,0): Error: a postcondition could not be proved on this return path +InductionWithoutTriggers.dfy(82,10): Related location: this is the postcondition that could not be proved +InductionWithoutTriggers.dfy(31,27): Related location: this proposition could not be proved +InductionWithoutTriggers.dfy(100,0): Error: a postcondition could not be proved on this return path +InductionWithoutTriggers.dfy(99,10): Related location: this is the postcondition that could not be proved +InductionWithoutTriggers.dfy(31,27): Related location: this proposition could not be proved +InductionWithoutTriggers.dfy(124,0): Error: a postcondition could not be proved on this return path +InductionWithoutTriggers.dfy(123,18): Related location: this is the postcondition that could not be proved +InductionWithoutTriggers.dfy(35,27): Related location: this proposition could not be proved +InductionWithoutTriggers.dfy(124,0): Error: a postcondition could not be proved on this return path +InductionWithoutTriggers.dfy(123,10): Related location: this is the postcondition that could not be proved +InductionWithoutTriggers.dfy(31,27): Related location: this proposition could not be proved -Dafny program verifier finished with 2 verified, 1 error +Dafny program verifier finished with 17 verified, 8 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.refresh.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.refresh.expect new file mode 100644 index 00000000000..963b7041fd6 --- /dev/null +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/InductionWithoutTriggers.dfy.refresh.expect @@ -0,0 +1,28 @@ +InductionWithoutTriggers.dfy(17,21): Warning: Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. Change or remove the {:induction} attribute to generate a different induction hypothesis, or add {:nowarn} to silence this warning. For more information, see the section quantifier instantiation rules in the reference manual. +InductionWithoutTriggers.dfy(40,24): Warning: Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. Change or remove the {:induction} attribute to generate a different induction hypothesis, or add {:nowarn} to silence this warning. For more information, see the section quantifier instantiation rules in the reference manual. +InductionWithoutTriggers.dfy(46,24): Warning: Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. Change or remove the {:induction} attribute to generate a different induction hypothesis, or add {:nowarn} to silence this warning. For more information, see the section quantifier instantiation rules in the reference manual. +InductionWithoutTriggers.dfy(66,19): Warning: Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. Change or remove the {:induction} attribute to generate a different induction hypothesis, or add {:nowarn} to silence this warning. For more information, see the section quantifier instantiation rules in the reference manual. +InductionWithoutTriggers.dfy(11,9): Error: assertion might not hold +InductionWithoutTriggers.dfy(43,0): Error: a postcondition could not be proved on this return path +InductionWithoutTriggers.dfy(42,10): Related location: this is the postcondition that could not be proved +InductionWithoutTriggers.dfy(31,27): Related location: this proposition could not be proved +InductionWithoutTriggers.dfy(49,0): Error: a postcondition could not be proved on this return path +InductionWithoutTriggers.dfy(48,10): Related location: this is the postcondition that could not be proved +InductionWithoutTriggers.dfy(31,27): Related location: this proposition could not be proved +InductionWithoutTriggers.dfy(69,0): Error: a postcondition could not be proved on this return path +InductionWithoutTriggers.dfy(68,10): Related location: this is the postcondition that could not be proved +InductionWithoutTriggers.dfy(31,27): Related location: this proposition could not be proved +InductionWithoutTriggers.dfy(83,0): Error: a postcondition could not be proved on this return path +InductionWithoutTriggers.dfy(82,10): Related location: this is the postcondition that could not be proved +InductionWithoutTriggers.dfy(31,27): Related location: this proposition could not be proved +InductionWithoutTriggers.dfy(100,0): Error: a postcondition could not be proved on this return path +InductionWithoutTriggers.dfy(99,10): Related location: this is the postcondition that could not be proved +InductionWithoutTriggers.dfy(31,27): Related location: this proposition could not be proved +InductionWithoutTriggers.dfy(124,0): Error: a postcondition could not be proved on this return path +InductionWithoutTriggers.dfy(123,18): Related location: this is the postcondition that could not be proved +InductionWithoutTriggers.dfy(35,27): Related location: this proposition could not be proved +InductionWithoutTriggers.dfy(124,0): Error: a postcondition could not be proved on this return path +InductionWithoutTriggers.dfy(123,10): Related location: this is the postcondition that could not be proved +InductionWithoutTriggers.dfy(31,27): Related location: this proposition could not be proved + +Dafny program verifier finished with 12 verified, 8 errors diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy index 4a928d89195..f9bfce2f11f 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy @@ -37,3 +37,6 @@ lemma {:induction} {:inductionTrigger} Legacy2(n: nat) ensures f(n + 0) {} // Poorly chosen explicit trigger, so no warning ⇒ Proof fails. lemma {:induction} {:inductionTrigger g(n)} NoWarning3(n: nat) ensures f(n + 0) {} // error + +// No triggers but forced induction with a given variable list, so warning ⇒ Proof passes. +lemma {:induction n} InductionWarningN(n: nat) ensures f(n + 0) {} diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy.expect index ae615f9e9f7..4c0f5c82886 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/triggers/induction-triggers.dfy.expect @@ -13,6 +13,7 @@ induction-triggers.dfy(34,26): Info: {:induction n} induction-triggers.dfy(35,26): Info: {:induction n} induction-triggers.dfy(36,39): Info: {:induction n} induction-triggers.dfy(39,44): Info: {:induction n} +induction-triggers.dfy(42,21): Warning: Could not find a trigger for the induction hypothesis. Without a trigger, this may cause brittle verification. Change or remove the {:induction} attribute to generate a different induction hypothesis, or add {:nowarn} to silence this warning. For more information, see the section quantifier instantiation rules in the reference manual. induction-triggers.dfy(19,2): Info: ensures f(ih_n) induction-triggers.dfy(13,58): Error: a postcondition could not be proved on this return path induction-triggers.dfy(13,53): Related location: this is the postcondition that could not be proved @@ -24,4 +25,4 @@ induction-triggers.dfy(39,80): Error: a postcondition could not be proved on thi induction-triggers.dfy(39,71): Related location: this is the postcondition that could not be proved induction-triggers.dfy(3,47): Related location: this proposition could not be proved -Dafny program verifier finished with 15 verified, 3 errors +Dafny program verifier finished with 17 verified, 3 errors From a1149c9116af53e4db006ff10879c96aaad66cc9 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 31 Oct 2024 13:12:53 -0700 Subject: [PATCH 104/151] Update tests and expected output --- .../LitTests/LitTest/dafny1/Induction.legacy.dfy | 2 +- .../LitTests/LitTest/dafny4/Bug170.dfy.expect | 4 ++-- .../LitTest/git-issues/git-issue-977.dfy.expect | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/Induction.legacy.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/Induction.legacy.dfy index bdb2854ea37..a7309c89a8f 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/Induction.legacy.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/Induction.legacy.dfy @@ -152,7 +152,7 @@ class IntegerInduction { ensures IsSorted(s) ==> (forall i,j {:induction j} {:autotriggers false} :: 0 <= i < j < |s| ==> s[i] <= s[j]); ensures (forall i,j :: 0 <= i && i < j && j < |s| ==> s[i] <= s[j]) ==> IsSorted(s); { - (forall i {:inductionTrigger} {:matchinglooprewrite false}:: 1 <= i && i < |s| ==> s[i-1] <= s[i]) + (forall i {:nowarn} {:matchinglooprewrite false}:: 1 <= i && i < |s| ==> s[i-1] <= s[i]) } } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Bug170.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Bug170.dfy.expect index 976b1a237f1..668f5801e02 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Bug170.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/Bug170.dfy.expect @@ -31,9 +31,9 @@ Bug170.dfy(43,4): Info: A#[_k - 1] Bug170.dfy(46,17): Info: AA# decreases _k, x Bug170.dfy(53,17): Info: BB# decreases _k, x Bug170.dfy(46,17): Info: AA# {:induction _k, x} -Bug170.dfy(46,17): Info: AA# {:inductionPattern A#[_k](x)} +Bug170.dfy(46,17): Info: AA# {:inductionTrigger A#[_k](x)} Bug170.dfy(53,17): Info: BB# {:induction _k, x} -Bug170.dfy(53,17): Info: BB# {:inductionPattern B#[_k](x)} +Bug170.dfy(53,17): Info: BB# {:inductionTrigger B#[_k](x)} Bug170.dfy(64,18): Info: _k: ORDINAL Bug170.dfy(69,14): Info: _k: ORDINAL Bug170.dfy(70,14): Info: A#[_k] diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-977.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-977.dfy.expect index 0b860da1990..dd5f009d9ff 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-977.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/git-issues/git-issue-977.dfy.expect @@ -28,17 +28,17 @@ git-issue-977.dfy(84,16): Info: decreases num git-issue-977.dfy(129,16): Info: decreases k, num git-issue-977.dfy(146,16): Info: decreases k, num git-issue-977.dfy(54,6): Info: {:induction num} -git-issue-977.dfy(54,6): Info: {:inductionTrigger GreatestPredNat(num)} -git-issue-977.dfy(54,6): Info: {:inductionTrigger GreatestPredOrd(num)} git-issue-977.dfy(54,6): Info: {:inductionTrigger Pred(num)} +git-issue-977.dfy(54,6): Info: {:inductionTrigger GreatestPredOrd(num)} +git-issue-977.dfy(54,6): Info: {:inductionTrigger GreatestPredNat(num)} git-issue-977.dfy(61,6): Info: {:induction k, num} -git-issue-977.dfy(61,6): Info: {:inductionTrigger RicochetOrd(k, num)} -git-issue-977.dfy(61,6): Info: {:inductionTrigger GreatestManualOrd(k, num)} git-issue-977.dfy(61,6): Info: {:inductionTrigger GreatestPredOrd#[k](num)} +git-issue-977.dfy(61,6): Info: {:inductionTrigger GreatestManualOrd(k, num)} +git-issue-977.dfy(61,6): Info: {:inductionTrigger RicochetOrd(k, num)} git-issue-977.dfy(77,6): Info: {:induction k, num} -git-issue-977.dfy(77,6): Info: {:inductionTrigger RicochetNat(k, num)} -git-issue-977.dfy(77,6): Info: {:inductionTrigger GreatestManualNat(k, num)} git-issue-977.dfy(77,6): Info: {:inductionTrigger GreatestPredNat#[k](num)} +git-issue-977.dfy(77,6): Info: {:inductionTrigger GreatestManualNat(k, num)} +git-issue-977.dfy(77,6): Info: {:inductionTrigger RicochetNat(k, num)} git-issue-977.dfy(71,4): Info: ensures GreatestPredOrd#[m](num) git-issue-977.dfy(71,4): Info: ensures GreatestManualOrd(m, num) git-issue-977.dfy(71,4): Info: ensures RicochetOrd(m, num) From c447ffab1c0f3a40516a35808850d980db0b5377 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 31 Oct 2024 13:12:58 -0700 Subject: [PATCH 105/151] Fix previous code edit --- Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs b/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs index 95a755a42c7..941e8151477 100644 --- a/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs +++ b/Source/DafnyCore/Triggers/SplitPartTriggerWriter.cs @@ -153,6 +153,7 @@ public void CommitTrigger(ErrorReporter errorReporter, int? splitPartIndex, Syst if (!NeedsAutoTriggers()) { DisableEmptyTriggers(Comprehension.Attributes, "trigger"); + return; } AddTriggerAttribute(systemModuleManager); From 63683d1a67308cae47530610135b213ce07ac896 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 31 Oct 2024 14:29:14 -0700 Subject: [PATCH 106/151] Update standard libraries --- .../binaries/DafnyStandardLibraries-cs.doo | Bin 1540 -> 1540 bytes .../binaries/DafnyStandardLibraries-go.doo | Bin 1561 -> 1561 bytes .../binaries/DafnyStandardLibraries-java.doo | Bin 1531 -> 1531 bytes .../binaries/DafnyStandardLibraries-js.doo | Bin 2049 -> 2049 bytes .../DafnyStandardLibraries-notarget.doo | Bin 1520 -> 1520 bytes .../binaries/DafnyStandardLibraries-py.doo | Bin 1527 -> 1527 bytes .../binaries/DafnyStandardLibraries.doo | Bin 57234 -> 57291 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 31247ec268bad4f811798cb852824e6e1ab9e68e..a3c2e5be37690a8f1e2833663e79c8b94a056dd7 100644 GIT binary patch delta 79 zcmZqSY2o1w@MdNaVPIh3U}!9k-^ja`kr_yD-phEF8N`^}%PPruVDfTSIaweta+0{^ Xzm?1k3`hAH7}$ZDVBo;yN32Q!Q(qK- delta 79 zcmZqSY2o1w@MdNaVPIh3U|72~a3k+pMrI(rc`xHxW)Nd?FRLWulF7?i=uehgA-wbn;zRNyY<{zq2X<029a*Y5)KL delta 79 zcmbQqGn0omz?+#xgn@y9gJH|oz>U0X8JU6f=Dm!Mm}P;C$YmA!*{zHW3|h<#47@;P YFtB9u9#%P!(#dyOB^j4Y{?4id08?BQt^fc4 diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-java.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-java.doo index c8c50e38ade8c2fb475e92c0ba325ba2feedfb55..05a0f2e5a3a363244df27a942462fd1d6e574dbd 100644 GIT binary patch delta 79 zcmey({hOONz?+#xgn@y9gQ2B3ek1Q%MrI(rc`xG;W?3L3a#@9bb}J(TgBCLb120e+ Y3>=s|k5vw&bn<>yNyY<{Z?Y-@074)Xpa1{> delta 79 zcmey({hOONz?+#xgn@y9gJJ#Fz>U0X8JU6f=DmzZm}P;C$YmA!*{zHW3|h<#47@;P YFtB9uJXSf7(#iW-B^j4YzR9Wt0D-g>&9(REB|p9i|URADDcfT?qg* Cq#V5f delta 124 zcmZn^XcXWL@MdNaVPIh3VA!}da3k+pMrI(rc`xHB)(9XYa#@9bb}J(TgBCLb120e+ z3@m8`v2qjhGSgCvOY};_#v2qjhGSgCvOY}y-5&(5! B9E1P> delta 124 zcmeys{ehb|z?+#xgn@y9gJJd7z>U0X8JU6f=DmzNnInLV$YmA!*{zHW3|h<#47@;P zFtDT%#L7*~%S=lxF3~H=&&>(&W@Hj!hMN_syYB6asX%Q7d<+ciFnvIJ$>ejaN&xM9 B9n=5- diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-py.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-py.doo index f44d53da924afb4d68d8eb9585a91bb74f8c4b0a..64e74f0e62c8ef07c200a96f49f44102c61a53de 100644 GIT binary patch delta 59 zcmey){hgaPz?+#xgn@y9gQ2@Pek1Q%MrI(rc`xGuW)Nd?1FIzCfyuL2<-ok%VBS?$ FB>)r$66*i} delta 59 zcmey){hgaPz?+#xgn@y9gJIj&z>U0X8JU6f=Dmyum_dxm4Xl!kOD4}^l>_s3gLzk3 Fl>j|g6fXb( diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries.doo index 44716b7ba7425da6a1a7b6dd5bdc2f176467038f..0454aac79de9152ab7ba2668ca656be9a54afeb4 100644 GIT binary patch delta 57248 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 V00000006*~aNisTMc)7b005&(1YrOG delta 57171 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 O)0I^#v;F_P%u5LWY^ZktcCdug}uH& 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 From 3652ff3b06e731fe697ce398ee222656ee01b2a9 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 31 Oct 2024 15:55:51 -0700 Subject: [PATCH 107/151] Update standard library --- .../binaries/DafnyStandardLibraries-cs.doo | Bin 1546 -> 1540 bytes .../binaries/DafnyStandardLibraries-go.doo | Bin 1568 -> 1561 bytes .../binaries/DafnyStandardLibraries-java.doo | Bin 1537 -> 1531 bytes .../binaries/DafnyStandardLibraries-js.doo | Bin 2056 -> 2049 bytes .../DafnyStandardLibraries-notarget.doo | Bin 1524 -> 1520 bytes .../binaries/DafnyStandardLibraries-py.doo | Bin 1533 -> 1527 bytes .../binaries/DafnyStandardLibraries.doo | Bin 56977 -> 57316 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 beb76a262d62e300f5ab51d58c58cc8e6db015af..505805da24ae80e38c5a8da3234a968fa535c9b2 100644 GIT binary patch delta 1489 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 J%{1^g^*1>P!<_&C diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-go.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-go.doo index 4f078eb34a079ed3a37992ee8127fc68c88d0410..52fa4cbdfd54239d0dd6ed9f51536497e4eb66a5 100644 GIT binary patch delta 1524 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 diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-java.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-java.doo index 4ace84e1b5ac95f085b9274d3c303e33f7bb7b5e..43fa92bf7e5ee4e1fcfb48a54e83f23b58221489 100644 GIT binary patch delta 1495 zcmZ9Mdo&XY0LC|C9(fhz{g@Pa%xhO+UR_dX7%}9PTqLjMmDdc@Ri2Tn86ld4Yn~$- zB6lRDH6%hFV^|yIdUf^3J@=lzzrORG^ZoTZ--i|4JtAgr1L773004Y|fJ86XCox(< zxLmQ5m{>c@+p=Vq1i`F|ZA0+5*Xznmjhdm)GYi&Egcp;nN~G=}p}EIA6CbM{PyCyW zhCecq6#*&@$8C-=IDtM*$|+jnIqH_@8O&CCX3V4k240+fXTz@=%*X~+IMoSH?^EAg4#>8}WgNxlxh;_&0k+k2qCayHB8D{f0-Q?}}OdrRJGSXfKEK2L2IP?>BI%GF?6 zX|%&Q4Kb4P?WIkZe<+OSU*6+mORRbpXNeBbG)$B0;}@f0#mG4*U&aVBl0@2|V%l}F z#K~GYia#5=HyCZCk+xsWxFD~L#uT@Cnefvsfmm=zYn zrOcWAPy}y)gcIlK(BkuPUz7ZifC2dt?Z74Eow>fz;zGS2$42o!gL-y zxl)(vhth@xQDe8WrxXs6VE)GhgtZhc1pxqkA^-sIA*nEQs6W~#xX)!KbWB?`alEXa$|eb}qlfc#Bb|f3!~vZOU@qSu%H?JA_)adMS)Uar(tW#Ebptid)7l$1OI9;|_{Acr5lKl9 z#i;rZ;*1veuzU*}HSs7nh%UA)RBv6Rl8WVm0x)aF=E9Apg{^y~)eM1yrhJqcPER8;r+pb&r9l*Q7iA zR*CL0+a}MqhgP1e9tjmd?(e8C5c`@i?8dOP;b+>++}Ttf^~$7!7>mWnWW#APjjpW5 zgc(?d(hVY9l01l``<3sl@-;IZU!AHK7ZaS7Q3Jh#+gH1GahT9OP zrD!Le5(I>v(FmyBL0$Oo;H@?vg2mCAPsG?U?k4qZlTUboGm7+?W~e%Cc2ze^q~6>+ zxC-sc#KKG%UoYvnoLMyN!}Ls8qiG_Y^o%K954DJdH{M+NNpIuI>e9Y7c$9v%UsD}R z4g`^JJAzf#6a&|b8zpB7ITPLC!^_V8Zbkh<_>W75W^EYn*7f zAya}d^hw7%BbA(lsA7tnEwH)oDY-UTPq2@0{L<5zSe_- zN_|b^o4^a~7HHXGc$(6^&ou)iZM}}W=16$CB(gx`xE^ci;}vG6EN(Xz=_kDT#A5XO z>alZ5Fr-k|86V8mu8UQKMl%nYS-7ZdytHK6}<<_06B78F->nF3Xhdl6&7ZqJKf!<9`n*{lX>cUZkK`X=p~39{ delta 1489 zcmZ9MYdF&j0LK3to2i^(?vz3%myt^kaa>YILJj3|Vk&nU+gzgXcN?aY43V0#NshbR zZ_!*r!>N=)vvMukD5hL;PUq9<^nQGw-?#Vqy<58z_MotKLc&r203d*xMx47i$9^S+ zic$sF>FH3}U$$Xr@$zj+|5qx+^>I<&XMu6M@1!}gK>ntgcdF9}Rgw-CYe>g!Z0gf8 zlvAwZ2iU5c8(-&I^ncG{`R^LIrNRhb-zqJLx?>V+Mo;OvsqdP!wg7R`lofH+ba#+A zaA@jLe0@?iW~_m#9~b1{XeT~*>0thZJxKrHiTVQ)BVmqOVGSQt19GJX%PTX%(IGkv+C!l zZ>0Lnsy_}-%Y+K>_MO7vanOxaLFH*QV(t=k`GlJVTiLMCY1&#sj#09B$(wsO0`!HS zX|z%76s;ZE-&D%ED}_TH#n93uddG7k>WXHsG;>bKRueqWm?`NK>Sxr3V9AT`5z4Tqo*dkWs^R zj!g8i_PP3!3jvV_+k3!|J5a10_~R%-2`9u$gaDuq2>>GBry`I-{Ye+`eXjV>Q9bdU zDKFy6A?hhO7aMRL!0SB$@ZK@Uaiqygb)uiCBV(;=CeJV8PICxKFEA*G%UOLhW9oI4 z0DOjnvoz1Jtc>4^bNWss*6t!6qWT9LXA@VbIkVtiHZw77NR2Rrl4Jn>cq3?=WSO$=(W3fok zs)#n^zmvHtqLS0qjJU{8))0QoV7HjYPvb6#bs1{qvikIryeWahg^RGIke7-XpBgjO z$%i!tb>YL@VxudT(FiY+t$P!4&e`6ba-=ttC;yEc66uJLr*2jbv&Jfr^iHaF$+PoK zF>NXB3xgX%70Vr(9fwZ}cr`hNGT`XoU#t%ny1}mx^q^&Qa-f?t`|^>~i%~r;+`8V0 zKvMeD{QT2q=;aWF*sd3R317QW8c|fce~0PO^~n)z+XUM^*N4 z8E`>;S2d;veal9N;(!~=nZa8vHNAvW$ZaNN>b@QB_B9$~W12-SN;dfl;B)VIkKEHb zFVeZUh6n1Mr*ygn5iHDB<(j=sTZ7@KCGSyqxmb{XLvF|2&7g-B_sN8$mS3Mb^ivkn zMoo&3CYPUK#5G2u$9C%0kW5@`wH(?Oy8CO@P9S_jx?7xJOP0be`d6`!aaJPaQ$^f# zYmc059a^_kpoeeWb1Rnu?FyI?ky#$$*2_k!U##+Wc@`Sn2BeGj&@09eIK$m9_Ll4T z2IcdC<;YXZ_jm_~lMHgFgTu@fjrYlM7VU#qqf-60TGz4seXnoCd3<>ooqlsVO+9LR z&Un;R&BRdV(w53ZXVv+Z!*??*3YF<`ajPHt%euiWXQuJA_De_yNE=W*Cz>_-@hZEn z97P;3an71tT6Vg&DBczxg3g;`kIP}t)6$mknK(GNwS2GjJDL&V^8TiGsd7fFb`gFN z?r*yiu*|_4=^CHVQK0_9?6jj4g?2ft!7n9muiCnFUna?CTtA60f$L*Ea{O9jAHx=T ze~!!qU-=S_2QWblP?Puxf?|*`Dld<8x2s-a-sW>=!86|`1uNSJ4)UpSTE$}7Hk7v; zlOIi|%$iNB)CA@jK_`O|=a6UU^2%wZ_pz9#;1rB0XZC&>iM4}3rGTG)76Jg8-?bL| guJr$dznEPD6IcI%|M>#`;~!*C!W7`wVE;D%02#ElL;wH) diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-js.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-js.doo index 4a26e6b42b74a8b6c10fa4abee2d5152eb47a77d..20b45a3f1d597f4441a7d245fde94c27d134c272 100644 GIT binary patch literal 2049 zcmZ{lcQo4z8^?b^YPDuuwA8E>qcirnDn&zxTGbY%s1bzFT!I+YiV>s6MXW|>jnEpY zEu|uETUt(B+UmFziBYSn)YbQW|GD>^_j&&Kp7T8C``7oJ@5d3r%Lf4fKmZ7e_qt5Q z=>^{g0e}`S0312EqOXSr`ujyU00Ox%=Vf(e;)XT|T~p#>5z#ko-=)cMhrRSE^i&x=etzSz+jur5Zio#_GZiod;Z&%{wCjW^vBki;#ar=IBEU?+p)YgyG4Oi987+3jhG~##imYwJd}ipV^&dA7C8b~pPY1v+oVW_f z`oiJXcg>GK*jtFP8ao=BtrE@~*A@LNNT(GbjFQHCzhfN;+bWl*|IitejC_`)Kni3M z9*e)!^}hSEARWV=n^BVdPRUvt-2Vk;4Unp*1=lXa> zULCacE^jGhlaX;{b{Gb_Y7$p?FHdCLATWB$_nr{U(OmtVKQN0-cJgW;FRv_T**eEI zG*WWkw`yp#`wipBu0?Buv{f^_MD}33oK;GMg)*e-+_v3yh!RVPnt9u=25!^bmM>zj zmIw*4T)RadM|Pn1o*ATFv~2v~csjX9m?sVHemNsZ_g4KZ)E)wPR7AY^P#yKHz}yEW z)!C2NO_R|qC^@uz_2~ZEroqc@d|n+)mL}X7foZ4Fs!cX0w3BW5dIxI@c9mn~e8ed{ z#_({m@;>yAfvtk@r=7^4QC7%9!izb-l)f>PiyCh!B5-l*?U0LgumxQmvST(0u)%*q z70bfiLHgx-L_a|-3@p#`uaP+p-AX&bCG7W!%Dt`OFY)P^NX9%}>WzJ>M1_ujX`)r5 zY7kub>liA_^b|)1E-0Z%{qW+Ff8L3l46#tuBn*++ZGg{+cyka)TXd-S#Mk zR_sCcmNq^|`S91g{ocU9!-1JOF&Ga~&UYvbLM`b;~>8?o^00D`cy+e-Im* z0MJrZm{bbamslEL0Xy}(FN&5npY8sq`(aUsS-#B-wlL!)GdpW-Fp9QJZzJ)c)mt9a zZ|XvGcS6JT)Sn8cQXL{+B)*QSYoDrl4{P5j$*TXMNv#S%Ye?vKcjt^|ZYg-yr#75V zf9tg0UzO>jtg*OmWgGE2B*u|F<4+dmcswqb)sEc*nTEI0poiTYF4C0xggP3&txQ+8 zwN|+@ubEavALn?DvK-bFCat51aoDW|ZO#hv9v`&#`E=o|cw=0@apzU?)oVS*z1B1cU2J1w`d)fiH64KmC=U2q{ zXQZCoSFE==V&PxqJafA?wCfvVX@k8#TfP5E|6Ibn$o%^TdCRmW*9ZW8iA@JI{~r|`5#U2VA3O&eexR{Gi2?i# D?BBIt delta 2024 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 delta 1488 zcmZ9MX*kpg0LK6BGtW43C8JEaRVZg7SuqVl&P>ihF*7u|R{fRxh}_ma8bi4aA$L5D z-4UT;XxvAO$N5udTsfjd+_`qs8ioA;z)V4+m1foGh}V46q~_+0V$Bi61)K?*e1 zB(k3(zsq7SwrDt~_xSMlXPw65cJ|5(L-TYW8WhF$rfN7uF_zhElqGl^l${X5rJj9brqc7UWdR4aJJYF1qg*hwy|*WNP#oWayW{2ZNpy;cz90dKLEYCt(weE zbn?{=%DpP5<%=Oj_0$XEh5M-44;o4qLYir&5;a&?D+3t~ENLD#ASDy%TZw|c9=_!( zL%$fjU)@80TtsjD!*VsdFM(^L$x;7%)jh7sbDS#+BPy7%-y6zHHK=N#Yj-a;Ceg&h zuC^_b%G`W|&$RV|YlrOCaPa@5sMXNFMqvVACTA3)m(pQ`pqTJRycV z!TYU<-Awgm1dyJrv}oE^qt!OFk(WjCK3^PX+oE|S4lzx8ASZECQLXZt0JXH|)$Ij+ zUfHp$elw;gP3mArkVkjAJ0MOv^@r%L%-pSLzi9pJO)Xw}kqJ=3^o52DDwuVM`$>ET zMY>+f5%zIs1h{%)JIN*1L)l5#0Tz_le`UO{j&aqFe{|gEGc>ZrVf;-kJ<$`ZJvd|G zIkmNn>S@*Ae(M=g)rfHChp}iCEEn98Y(?^{$%|ISL3~71YsGMkB*}I2=>3p-0^V)n z!-V)w%O27%?b|}r_#hf=m`CTM^1&xW%n_U-#nw?^P%ta4+gtQuI>GB(a;6y@qCKz@ zOd6m{+4tQ85xFYBj`q_I%YAs1e;AVgg>j~%8-FoO1)jNLW`8M1!HSh@Zo4f}`=lZid?2YEpW?)2t5sLaK;S8&S-6(^ll9 z<%CiFXW`LzQw!XiQplsM20^PrKlf#b#3G1PPnC=F)wZf<)C%eq7myFBSo15FYYlH7 zM>W)~y|I)G%9l~zJ=~dFI;adO#wgCL)=GaY6Kf3=upo}vIfH6po1u{b{I%)?F#^7A z==L;$thD5CX^d41#!Fck>))Fq8?wC6kq4e)mh`&UIg(LUu{Y zYFE6xF(jm`r%9pE!q9onHbtsDD98|=q&$3G=}nh^1eu3v66Jw3=5O&bc#b?KoeOz3 zrR{d>3t2mMTkzAIv#0iopyqLfmgy*HGirLI zM4~IN9M@u{eChJ=$0{wkzM1mFP7Z508@m|r%eArrfbtKMkNvRt=LEQ;{ZQWbaq2is afWI|d>1X!W2l$Vz&v?%z&5h>zxA_NM+P}sC diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-py.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-py.doo index 04382d31932d679e622d428302b2174bd654e553..af01fc2e6408f18652d2f89a554e4f6e5b2c0d1b 100644 GIT binary patch delta 1473 zcmZ9MYd8}M0ERbXHai;T&Lkq0TP&rh+;hJVosi-nm$5Q58KXFu$&lFOnp?RiEx8n} zT(XnPYLrmT*4h?XCr+o+`Ej1p)A#dxzJKrYeqUjqVF)i*kfb620Du56>7hY4QcPkl zT|wv)E_gbmYW9g#sK=VtT&cnNBSHLZ+#+j*U-m&ck=E%{rIzDkd_^H7z0N=>{mUBG z>iQ8)I8bK_|7n`L9v#-RGs{G=Xs?4Umw$&vN?}_PtZ3vMVMGgtfqy z*QZE>JcwF6`n^aI+FP}F{Zea2`?t0b8$x+X%Fr;FhO|u-&>!P&?n?Vbo1vpcLUG;P zDh?EmUre%-+|jfSt*E6xFg}BHUB&vJ;2;*<_EvZ~NVlL8debbVZVdr<_Ss`3x{fv+ z-sI}F&1cydQo!^{>g*IgA<)e- z8`-yzgEAc++PKCIcNq*6sp2sLS>}%&s_DEaV$Jo#B7{p9^s?M zb$M;s5rL+tm?szhBrk0Lj)d?7lg8r4tmVGcT4>sNF;_tg97`P`Cq&=-X zP%RMcQTYp96lvBrubO*MUyU6Ga3 zjtxhu!tSM9#yKO%<~cm0fmjTwer9+^VR%1>J0y5Sb=J!ZOau-yje**fOUIz?>LeEn zyCeo8X3N{}^+lU|U6Wwv`a;s!1siEQi~(*=y*h*{H2mPOiO^<^&%VCWW<6@IPFT+W zNEq$0t89=q*sI%@!J3X6@qZB9DR1xWyief@yc98GOFyJCf2y^*P^6Z!SpFh5aY=ybS?2(nX2FsIzVjMGa<5} z_fKqmgY0bi7|UDYT!t0jFwF_wbX8L&_~+^jNtL?TMpwz2tjm`Ks}CwPCuXHenoHA* z+&xn>NY#ZAbkH+BXBb~HkX^cGWE^Q{n3UT9?exJBshFH2e&tC;%|T5Wt!W=}+fei; z=7AvDYMDje9Aq(0xv9nTM&-!r$US{ z!Jbp5D|+Z-@8iXs4d~S=uIKsYLfupKgWGSPwuYB^PjpjxjT*%+Ctm;)H>+jMBHd=P z>}?~@qulQoB-D6y%!HN(Xb$ktx6 zxYN}OzA#M_dW(}g7#V~62-aZSSram8BmQG9UgKfi;_g&3Uz~??bZKQ>>o;lXBV}%> zxU?mMg++D0y%ArI7e?L7;Y-~VMriHkpd+SAj6P3-F7|7t9}ah{tlyf;>#4HvU4C== z8$VpQ%5&T9b6rbu3^Z(Br~JnLCHDfF*flRUh-nEHz=j?C8u@HOlse)DP{3fl-Qq%+ z$2G_2=Z?b#@EQ$>1!9=S*RregxJH(9 zHyPHAdC6>_(Q`llJ}x}AXZZbN;2&E3tLqwGu0RPz&`<9Q1OWDYw^{bP$^T_2lTst4 XtpCIEXXpQ4c2HhQse==x{%!sNhx4&` literal 1533 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 K01Ms#0000$dfIdV From b07075165adc6bf02ba23f4bb2b7f4abbe55be41 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 31 Oct 2024 17:24:52 -0700 Subject: [PATCH 108/151] Update standard library --- .../binaries/DafnyStandardLibraries-cs.doo | Bin 1540 -> 1540 bytes .../binaries/DafnyStandardLibraries-go.doo | Bin 1561 -> 1561 bytes .../binaries/DafnyStandardLibraries-java.doo | Bin 1531 -> 1531 bytes .../binaries/DafnyStandardLibraries-js.doo | Bin 2049 -> 2049 bytes .../DafnyStandardLibraries-notarget.doo | Bin 1520 -> 1520 bytes .../binaries/DafnyStandardLibraries-py.doo | Bin 1527 -> 1527 bytes .../binaries/DafnyStandardLibraries.doo | Bin 57316 -> 57316 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 505805da24ae80e38c5a8da3234a968fa535c9b2..c58910a372e0d32c0914bead2ac81f6774075929 100644 GIT binary patch delta 61 zcmZqSY2gtH@MdNaVPIh3VEED%A1QFx=fgxH4W<~2jW%_ROkcV-?_oU0%mI^{Jc-p9 KC^-2zt1SRTu@$}m delta 61 zcmZqSY2gtH@MdNaVPIh3U`VTrk6c!vpFL4XgGpw`Mw>cDrnI`vdl=6#bHJn~PhvF& K3Qj)GY6}4U5fZcj diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-go.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-go.doo index 52fa4cbdfd54239d0dd6ed9f51536497e4eb66a5..c8772b113cbaad058266f6f69cc7f5cdfae2d69d 100644 GIT binary patch delta 61 zcmbQqGm}Rsz?+#xgn@y9gW-2qe5Al#pAQp-G?-#6HrmuNGX3t_yod2IGY3p+@^V&V Kpy1>?thNAD78XMQ delta 61 zcmbQqGm}Rsz?+#xgn@y9gCVyrK5|)we)dEm4JMf#8*S delta 61 zcmey({hM1Tz?+#xgn@y9gCV0XK5|)we)dEm4JMf#8*ScDrmVWndl*l%a=@e}_p=)V K1t%Y2w*>(H;1a_C diff --git a/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-notarget.doo b/Source/DafnyStandardLibraries/binaries/DafnyStandardLibraries-notarget.doo index dcb667dc686f81a506119af2ca902771ae9acf4c..e1e2d34e031e3090f608d29ceb8c6510a2cd1698 100644 GIT binary patch delta 61 zcmeys{efF3z?+#xgn@y9gW*$Ge5Al#pAQp-G?-#6HrmuNGJWdWyoYfYGY3p+aviHN KP;l~QR$BmbTNY3N delta 61 zcmeys{efF3z?+#xgn@y9gCV&tK5|)we)dEm4JMf#8*S_g(Rk_a908m?)&dB+j?drk0WEeb?sQjO*`mz@#P@ N-8TjbPF{ZB76A6c8sq=~ delta 63 zcmaE|pZUptW}yIYW)=|!1_lm>#Jc#%r|Q;kCJJdVvFmNLsbyqJtlPYsas6EmnAGH= N`^G@Q$; Date: Tue, 5 Nov 2024 20:14:14 -0800 Subject: [PATCH 109/151] Fix merge --- Source/DafnyCore/Verifier/BoogieGenerator.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index 9fe267e83a3..0131299e00d 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -4483,6 +4483,22 @@ public static bool DetermineDisableNonLinearArithmetic(ModuleDefinition module, return dafnyOptions.DisableNLarith; } + /// + /// Return the conjuncts of "attributedExpressions". + /// + public static IEnumerable ConjunctsOf(List attributedExpressions) { + foreach (var attrExpr in attributedExpressions) { + if (attrExpr.Label != null) { + // don't mess with labeled expressions + yield return attrExpr; + } else { + foreach (var conjunct in Expression.ConjunctsWithLetsOnOutside(attrExpr.E)) { + yield return new AttributedExpression(conjunct, attrExpr.Attributes); + } + } + } + } + List /*!*/ TrSplitExpr(BodyTranslationContext context, Expression expr, ExpressionTranslator etran, bool applyInduction, out bool splitHappened) { Contract.Requires(expr != null); From f3e26167f580ec1e5265acf18bd83dd06f9194e9 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 7 Nov 2024 11:34:51 -0800 Subject: [PATCH 110/151] Fix proof --- .../LitTests/LitTest/dafny0/InductivePredicates.dfy | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/InductivePredicates.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/InductivePredicates.dfy index dbf275ef822..a46a18dd769 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/InductivePredicates.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/InductivePredicates.dfy @@ -263,6 +263,16 @@ module SomeCoolDisjunctionTests { requires P(x) || Q(x) ensures false // fine { + // The following proof should not be necessary. This lemma used to verify + // automatically, but after a change with CanCall, it stopped verifying. + // The problem may be due to https://github.com/Z3Prover/z3/issues/7444. + // After it is fixed, we should try removing the following lines again. + assert !_k.IsLimit; + if P#[_k](x) { + assert P#[_k](x) == Q#[_k - 1](x); + } else { + assert Q#[_k](x); + } } least predicate Pn[nat](x: int) From f94ccd21f004250d53521126a5e703fb0119293a Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Thu, 7 Nov 2024 13:28:47 -0800 Subject: [PATCH 111/151] Use $OneHeap in Apply if function is known to be heap independent Motivation: dafny3/Filter.dfy --- .../Verifier/BoogieGenerator.ExpressionTranslator.cs | 10 +++++++++- .../TestFiles/LitTests/LitTest/dafny3/Filter.dfy | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index 34f85975e14..edb8ba2494f 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -22,6 +22,14 @@ public Boogie.Expr HeapExpr { get { Statistics_HeapUses++; return _the_heap_expr; } } + public Boogie.Expr HeapExprForArrow(Type arrowType) { + if (arrowType.IsArrowTypeWithoutReadEffects) { + return BoogieGenerator.NewOneHeapExpr(arrowType.tok); + } else { + return HeapExpr; + } + } + /// /// Return HeapExpr as an IdentifierExpr. /// CAUTION: This getter should be used only if the caller "knows" that HeapExpr really is an IdentifierExpr. @@ -659,7 +667,7 @@ public Boogie.Expr TrExpr(Expression expr) { var applied = FunctionCall(GetToken(applyExpr), BoogieGenerator.Apply(arity), Predef.BoxType, Concat(Map(tt.TypeArgs, BoogieGenerator.TypeToTy), - Cons(HeapExpr, Cons(TrExpr(e.Function), e.Args.ConvertAll(arg => TrArg(arg)))))); + Cons(HeapExprForArrow(e.Function.Type), Cons(TrExpr(e.Function), e.Args.ConvertAll(arg => TrArg(arg)))))); return BoogieGenerator.UnboxUnlessInherentlyBoxed(applied, tt.Result); } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/Filter.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/Filter.dfy index d6ea16031cf..2c9725bb596 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/Filter.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny3/Filter.dfy @@ -221,7 +221,7 @@ lemma FS_Pong(s: Stream, P: Predicate, x: T, k: nat) } } else { assert fs == Filter(s.tail, P); // reminder of where we are - //FS_Pong(s.tail, h, x, k-1); + FS_Pong(s.tail, P, x, k-1); } } From 1d247d1970de651f63cdf815174ec0914fb00a9a Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 11 Nov 2024 09:09:20 -0500 Subject: [PATCH 112/151] upd rc --- .../TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect | 4 ++-- .../TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect index 8ec3ceb78f0..4d71194f01f 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 775200 -Max resources used by VC is 101900 +Total resources used is 959600 +Max resources used by VC is 145300 diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect index 06ac196ec16..72a9e8757a4 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect @@ -1,4 +1,4 @@ Dafny program verifier finished with 272 verified, 0 errors -Total resources used is 30525479 -Max resources used by VC is 2048364 +Total resources used is 27941703 +Max resources used by VC is 1155634 From e3dbc668d9906c60dd6691c43d14f242e88a3eef Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 11 Nov 2024 09:35:50 -0500 Subject: [PATCH 113/151] fuel expect upadte --- .../LitTest/dafny0/Fuel.legacy.dfy.expect | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect index 522dd947e44..da2060cc2e7 100755 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect @@ -1,8 +1,8 @@ -Fuel.legacy.dfy(129,8): Error: Fuel can only increase within a given scope. -Fuel.legacy.dfy(407,8): Error: Fuel can only increase within a given scope. +Fuel.legacy.dfy(129,8): Error: Fuel can only increase within a given scope. (ID: g_fuel_must_increase) +Fuel.legacy.dfy(407,8): Error: Fuel can only increase within a given scope. (ID: g_fuel_must_increase) Fuel.legacy.dfy(17,22): Error: assertion might not hold -Fuel.legacy.dfy(65,27): Error: assertion might not hold -Fuel.legacy.dfy(69,27): Error: assertion might not hold +Fuel.legacy.dfy(66,27): Error: assertion might not hold +Fuel.legacy.dfy(71,27): Error: assertion might not hold Fuel.legacy.dfy(92,22): Error: assertion might not hold Fuel.legacy.dfy(93,23): Error: assertion might not hold Fuel.legacy.dfy(94,22): Error: assertion might not hold @@ -18,44 +18,44 @@ Fuel.legacy.dfy(247,22): Error: assertion might not hold Fuel.legacy.dfy(280,26): Error: assertion might not hold Fuel.legacy.dfy(335,26): Error: function precondition could not be proved Fuel.legacy.dfy(324,21): Related location -Fuel.legacy.dfy(314,93): Related location +Fuel.legacy.dfy(314,72): Related location Fuel.legacy.dfy(335,26): Error: function precondition could not be proved Fuel.legacy.dfy(324,21): Related location -Fuel.legacy.dfy(312,43): Related location +Fuel.legacy.dfy(314,93): Related location Fuel.legacy.dfy(335,26): Error: function precondition could not be proved Fuel.legacy.dfy(324,21): Related location -Fuel.legacy.dfy(312,58): Related location +Fuel.legacy.dfy(314,46): Related location Fuel.legacy.dfy(335,26): Error: function precondition could not be proved Fuel.legacy.dfy(324,21): Related location Fuel.legacy.dfy(313,41): Related location Fuel.legacy.dfy(335,26): Error: function precondition could not be proved Fuel.legacy.dfy(324,21): Related location -Fuel.legacy.dfy(314,46): Related location +Fuel.legacy.dfy(312,43): Related location Fuel.legacy.dfy(335,26): Error: function precondition could not be proved Fuel.legacy.dfy(324,21): Related location -Fuel.legacy.dfy(314,72): Related location +Fuel.legacy.dfy(312,58): Related location Fuel.legacy.dfy(335,49): Error: destructor 't' can only be applied to datatype values constructed by 'VTuple' Fuel.legacy.dfy(335,50): Error: index out of range Fuel.legacy.dfy(336,38): Error: index out of range Fuel.legacy.dfy(336,42): Error: destructor 'u' can only be applied to datatype values constructed by 'VUint64' Fuel.legacy.dfy(336,45): Error: function precondition could not be proved Fuel.legacy.dfy(329,21): Related location -Fuel.legacy.dfy(311,43): Related location +Fuel.legacy.dfy(314,72): Related location Fuel.legacy.dfy(336,45): Error: function precondition could not be proved Fuel.legacy.dfy(329,21): Related location Fuel.legacy.dfy(313,41): Related location Fuel.legacy.dfy(336,45): Error: function precondition could not be proved Fuel.legacy.dfy(329,21): Related location -Fuel.legacy.dfy(314,72): Related location +Fuel.legacy.dfy(314,93): Related location Fuel.legacy.dfy(336,45): Error: function precondition could not be proved Fuel.legacy.dfy(329,21): Related location -Fuel.legacy.dfy(314,93): Related location +Fuel.legacy.dfy(312,43): Related location Fuel.legacy.dfy(336,45): Error: function precondition could not be proved Fuel.legacy.dfy(329,21): Related location Fuel.legacy.dfy(312,58): Related location Fuel.legacy.dfy(336,45): Error: function precondition could not be proved Fuel.legacy.dfy(329,21): Related location -Fuel.legacy.dfy(312,43): Related location +Fuel.legacy.dfy(311,43): Related location Fuel.legacy.dfy(336,71): Error: index out of range Fuel.legacy.dfy(397,22): Error: assertion might not hold Fuel.legacy.dfy(398,22): Error: assertion might not hold @@ -64,4 +64,4 @@ Fuel.legacy.dfy(435,22): Error: assertion might not hold Fuel.legacy.dfy(436,22): Error: assertion might not hold Fuel.legacy.dfy(437,23): Error: assertion might not hold -Dafny program verifier finished with 30 verified, 39 errors +Dafny program verifier finished with 31 verified, 39 errors From e3b27edc198784058916cc97179222112d352da8 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 11 Nov 2024 09:39:27 -0500 Subject: [PATCH 114/151] update rc --- .../LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect index a377a047f95..c0661c872bb 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect @@ -31,5 +31,5 @@ CoinductiveProofs.dfy(208,21): Related location: this is the postcondition that CoinductiveProofs.dfy(4,23): Related location: this proposition could not be proved Dafny program verifier finished with 23 verified, 12 errors -Total resources used is 777854 -Max resources used by VC is 66829 +Total resources used is 745558 +Max resources used by VC is 59810 From 756ceb6b8bcafd007c4e158300496d5b4b1d46da Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 11 Nov 2024 14:02:35 -0500 Subject: [PATCH 115/151] Update BoogieGenerator.Methods.cs Make true --- Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 189326959e7..3323daab839 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -806,7 +806,7 @@ private StmtList TrMethodBody(Method m, BoogieStmtListBuilder builder, Variables builder.Add(new Bpl.IfCmd(m.tok, null, definedness.Collect(m.tok), null, exporter.Collect(m.tok))); #else TrForallStmtCall(m.tok, parBoundVars, parBounds, parRange, decrCheck, null, triggers, recursiveCall, null, - builder, localVariables, etran, includeCanCalls: false); + builder, localVariables, etran, includeCanCalls: true); #endif } // translate the body of the method From f802e7290ff19c33f08c90f31b7da5c8a9e5674c Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 11 Nov 2024 14:41:55 -0500 Subject: [PATCH 116/151] rc --- .../LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect index 1c87811a450..1e4f86d4af0 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/CoinductiveProofs.dfy.expect @@ -31,5 +31,5 @@ CoinductiveProofs.dfy(208,21): Related location: this is the postcondition that CoinductiveProofs.dfy(4,23): Related location: this proposition could not be proved Dafny program verifier finished with 23 verified, 12 errors -Total resources used is 777634 -Max resources used by VC is 66829 +Total resources used is 746289 +Max resources used by VC is 59810 From 0409d51a20dc5b18ebae00b222fc9efacc860325 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 18 Nov 2024 11:11:32 -0500 Subject: [PATCH 117/151] expect updf --- .../TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect index da2060cc2e7..8fc90aabded 100755 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect @@ -1,5 +1,5 @@ -Fuel.legacy.dfy(129,8): Error: Fuel can only increase within a given scope. (ID: g_fuel_must_increase) -Fuel.legacy.dfy(407,8): Error: Fuel can only increase within a given scope. (ID: g_fuel_must_increase) +Fuel.legacy.dfy(129,8): Error: Fuel can only increase within a given scope. +Fuel.legacy.dfy(407,8): Error: Fuel can only increase within a given scope. Fuel.legacy.dfy(17,22): Error: assertion might not hold Fuel.legacy.dfy(66,27): Error: assertion might not hold Fuel.legacy.dfy(71,27): Error: assertion might not hold From 3dfd94c7d8f3354ef602436d7e2335b7cbe43e66 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 18 Nov 2024 13:43:20 -0500 Subject: [PATCH 118/151] fix heap picked for seq#create --- .../Verifier/BoogieGenerator.ExpressionTranslator.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index edb8ba2494f..1f14b2d92eb 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -745,7 +745,12 @@ public Boogie.Expr TrExpr(Expression expr) { case SeqConstructionExpr constructionExpr: { var e = constructionExpr; var eType = e.Type.NormalizeToAncestorType().AsSeqType.Arg.NormalizeExpand(); - return FunctionCall(GetToken(constructionExpr), "Seq#Create", Predef.SeqType, BoogieGenerator.TypeToTy(eType), HeapExpr, TrExpr(e.N), TrExpr(e.Initializer)); + var initalizerHeap = e.Initializer.Type.IsArrowType ? HeapExprForArrow(e.Initializer.Type) : HeapExpr; + return FunctionCall(GetToken(constructionExpr), "Seq#Create", Predef.SeqType, + BoogieGenerator.TypeToTy(eType), + initalizerHeap, T + rExpr(e.N), + TrExpr(e.Initializer)); } case MultiSetFormingExpr formingExpr: { MultiSetFormingExpr e = formingExpr; From 99fc5c147b4d0a9e4851f7ef2dee23f78915c379 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 18 Nov 2024 14:11:03 -0500 Subject: [PATCH 119/151] use oneheap for sequence intiailer --- .../Verifier/BoogieGenerator.ExpressionTranslator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index 1f14b2d92eb..770526d6974 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -748,8 +748,8 @@ public Boogie.Expr TrExpr(Expression expr) { var initalizerHeap = e.Initializer.Type.IsArrowType ? HeapExprForArrow(e.Initializer.Type) : HeapExpr; return FunctionCall(GetToken(constructionExpr), "Seq#Create", Predef.SeqType, BoogieGenerator.TypeToTy(eType), - initalizerHeap, T - rExpr(e.N), + initalizerHeap, + TrExpr(e.N), TrExpr(e.Initializer)); } case MultiSetFormingExpr formingExpr: { From 96bdb79cf2f98325c36de54033d1c5a899d293ad Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 18 Nov 2024 14:41:53 -0500 Subject: [PATCH 120/151] fix old the heap used inside lambda body --- .../Verifier/BoogieGenerator.ExpressionTranslator.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index 770526d6974..5b16c4ec540 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -1567,8 +1567,7 @@ private Expr TrLambdaExpr(LambdaExpr e) { return new KeyValuePair(bv, new BoogieWrapper(unboxy, bv.Type)); }).ToDictionary(x => x.Key, x => x.Value); var su = new Substituter(null, subst, new Dictionary()); - - var et = new ExpressionTranslator(this, heap); + var et = new ExpressionTranslator(this.BoogieGenerator, this.Predef, heap, this.Old.HeapExpr, this.scope); var lvars = new List(); var ly = BplBoundVar(varNameGen.FreshId("#ly#"), Predef.LayerType, lvars); et = et.WithLayer(ly); @@ -2346,7 +2345,7 @@ public Expr CanCallAssumption(Expression expr, CanCallOptions cco = null) { var varNameGen = BoogieGenerator.CurrentIdGenerator.NestedFreshIdGenerator("$l#"); Boogie.Expr heap; var hVar = BplBoundVar(varNameGen.FreshId("#heap#"), BoogieGenerator.Predef.HeapType, out heap); - var et = new ExpressionTranslator(this, heap); + var et = new ExpressionTranslator(this.BoogieGenerator, this.Predef, heap, this.Old.HeapExpr, this.scope); Dictionary subst = new Dictionary(); foreach (var bv in e.BoundVars) { From 21f43ed36236da539f99db0e42ca105c6ecf60c4 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Wed, 20 Nov 2024 12:06:56 -0500 Subject: [PATCH 121/151] help verifier --- .../TestFiles/LitTests/LitTest/dafny4/GHC-MergeSort.dfy | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/GHC-MergeSort.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/GHC-MergeSort.dfy index bd80b089d5c..4463e4a41a6 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/GHC-MergeSort.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/GHC-MergeSort.dfy @@ -382,8 +382,10 @@ lemma sorted_reverse(xs: List, ys: List) { match xs case Nil => - case Cons(x, rest) => + case Cons(x, rest) => { sorted_reverse(rest, Cons(x, ys)); + assert forall a,b :: a in multiset_of(xs) && b in multiset_of(ys) ==> Below(a, b); + } } lemma sorted_insertInMiddle(xs: List, a: G, ys: List) From 3cd8cf5366774fd5e1f95aa288a45c83af219be2 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Wed, 20 Nov 2024 13:22:43 -0500 Subject: [PATCH 122/151] fighting butterflies with butterflies --- .../LitTests/LitTest/dafny4/SoftwareFoundations-Basics.dfy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/SoftwareFoundations-Basics.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/SoftwareFoundations-Basics.dfy index 41baa0682b7..6241e717e7e 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/SoftwareFoundations-Basics.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/SoftwareFoundations-Basics.dfy @@ -240,7 +240,7 @@ method test_factorial1() var n10 := S(S(S(S(n6)))); var n12 := S(S(n10)); - assert factorial(n5)==mult(n10, n12); + assert {:split_here} factorial(n5)==mult(n10, n12); } method test_factorial1_old() From 92b21e4f47d6baba40f777d1a7c3e99ef0fc5718 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Wed, 20 Nov 2024 14:10:28 -0500 Subject: [PATCH 123/151] temp undo --- .../Verifier/BoogieGenerator.ExpressionTranslator.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index 5b16c4ec540..ebde67c148e 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -46,7 +46,7 @@ public Boogie.IdentifierExpr HeapCastToIdentifierExpr { public readonly string This; public readonly string readsFrame; // the name of the context's frame variable for reading state. // May be null to indicate the context's reads frame is * and doesn't require any reads checks. - public readonly IFrameScope scope; // lambda, function or predicate + public readonly IFrameScope scope; // lambda, function or predicate public readonly string modifiesFrame; // the name of the context's frame variable for writing state. readonly Function applyLimited_CurrentFunction; internal readonly FuelSetting layerInterCluster; @@ -128,7 +128,6 @@ public ExpressionTranslator(BoogieGenerator boogieGenerator, PredefinedDecls pre old.oldEtran = old; this.oldEtran = old; } - public ExpressionTranslator(BoogieGenerator boogieGenerator, PredefinedDecls predef, Boogie.Expr heap, IFrameScope scope, string thisVar) : this(boogieGenerator, predef, heap, thisVar, null, new FuelSetting(boogieGenerator, 1), null, scope, "$_ReadsFrame", "$_ModifiesFrame", false) { Contract.Requires(boogieGenerator != null); @@ -1567,7 +1566,9 @@ private Expr TrLambdaExpr(LambdaExpr e) { return new KeyValuePair(bv, new BoogieWrapper(unboxy, bv.Type)); }).ToDictionary(x => x.Key, x => x.Value); var su = new Substituter(null, subst, new Dictionary()); - var et = new ExpressionTranslator(this.BoogieGenerator, this.Predef, heap, this.Old.HeapExpr, this.scope); + // JATIN_UNDO: var oldHeapExpr = this.Old.HeapExpr; + // var et = new ExpressionTranslator(this.BoogieGenerator, this.Predef, heap, this.Old.HeapExpr, this.scope); + var et = new ExpressionTranslator(this, heap); var lvars = new List(); var ly = BplBoundVar(varNameGen.FreshId("#ly#"), Predef.LayerType, lvars); et = et.WithLayer(ly); @@ -2345,7 +2346,8 @@ public Expr CanCallAssumption(Expression expr, CanCallOptions cco = null) { var varNameGen = BoogieGenerator.CurrentIdGenerator.NestedFreshIdGenerator("$l#"); Boogie.Expr heap; var hVar = BplBoundVar(varNameGen.FreshId("#heap#"), BoogieGenerator.Predef.HeapType, out heap); - var et = new ExpressionTranslator(this.BoogieGenerator, this.Predef, heap, this.Old.HeapExpr, this.scope); + var et = new ExpressionTranslator(this, heap); + // JATIN_UNDO: var et = new ExpressionTranslator(this.BoogieGenerator, this.Predef, heap, this.Old.HeapExpr, this.scope); Dictionary subst = new Dictionary(); foreach (var bv in e.BoundVars) { From d1db55e41d7b4a96181821faa52307fbf1025222 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Wed, 20 Nov 2024 14:22:20 -0500 Subject: [PATCH 124/151] gcd --- .../TestFiles/LitTests/LitTest/dafny4/gcd.dfy | 52 +++++++++++++------ 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy index e74a60c9ad7..b44109c3009 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy @@ -14,8 +14,8 @@ ghost function Factors(x: pos): set { // The following lemma proves that the conjunct "p <= x" in the // definition of Factors does not reduce the elements in the set. -lemma FactorsHasAllFactors(x: pos) - ensures forall n :: n in Factors(x) <==> n in iset p: pos | IsFactor(p, x) +lemma FactorIsInFactors(p: pos, x: pos) + ensures p in Factors(x) <==> IsFactor (p, x) { } @@ -129,10 +129,22 @@ lemma GcdSubtract(x: pos, y: pos) { var p := Gcd(x, y); + assert IsFactor (p, x) by { + reveal Gcd(); + assert p in Factors(x); + FactorIsInFactors(p, x); + } + + assert IsFactor (p, y) by { + reveal Gcd(); + assert p in Factors(y); + FactorIsInFactors(p, y); + } + // By the definition of `Gcd`, we know that p is a factor of both x and y, // We now show that p is also a factor of y - x. - assert IsFactor(p, y - x) by { - reveal Gcd(); + assert IsFactor(p, y - x) by + { var a :| p * a == x; var b :| p * b == y; calc { @@ -146,15 +158,15 @@ lemma GcdSubtract(x: pos, y: pos) // Hence, p is a common factor of x and y - x var common := Factors(x) * Factors(y - x); - assert p in common by { reveal Gcd(); } + assert p in common; // It remains to show that, among the common factors of x and // y - x, p is the greatest forall q | q in common ensures q <= p { - // q is a factor of both x and y - x, so a and b exist: reveal Gcd(); + // q is a factor of both x and y - x, so a and b exist: var a :| q * a == x; var b :| q * b == y - x; assert IsFactor(q, y) by { @@ -172,7 +184,7 @@ lemma GcdSubtract(x: pos, y: pos) assert q in Factors(x) * Factors(y); // By the definition of Gcd(x, y), we then have that q <= p. } - assert Gcd(x, y) == Gcd(x, y - x) by { reveal Gcd(); } + assert Gcd(x, y) == Gcd(x, y - x) by {reveal Gcd();} } method EuclidGcd(X: pos, Y: pos) returns (gcd: pos) @@ -212,28 +224,36 @@ lemma {:isolate_assertions} GcdSubtractAlt(x: pos, y: pos) GcdSymmetric(x, y); // this is the difference from GcdSubtract above var p := Gcd(x, y); - assert IsFactor(p, y - x) by { + assert IsFactor (p, x) by { + reveal Gcd(); + assert p in Factors(x); + FactorIsInFactors(p, x); + } + + assert IsFactor (p, y) by { reveal Gcd(); + assert p in Factors(y); + FactorIsInFactors(p, y); + } + + assert IsFactor(p, y - x) by { var a :| p * a == x; var b :| p * b == y; - calc { - y - x; - == - p * b - p * a; - == - p * (b - a); + assert y - x == p * (b - a) by { + assert p * b - p * a == p * (b - a); } } var common := Factors(x) * Factors(y - x); - assert p in common by { reveal Gcd(); } + assert p in common; + forall q | q in common ensures q <= p { reveal Gcd(); var a :| q * a == x; var b :| q * b == y - x; - assert IsFactor(q, y) by { + assert IsFactor(q, y) by { calc { y; == From 165065e90fbdbc36b77ab3b71c4938baf4f239f0 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Wed, 20 Nov 2024 14:47:44 -0500 Subject: [PATCH 125/151] num ver --- .../LitTest/dafny4/SoftwareFoundations-Basics.dfy.expect | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/SoftwareFoundations-Basics.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/SoftwareFoundations-Basics.dfy.expect index e7cc92d0e90..35e54b59f6f 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/SoftwareFoundations-Basics.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/SoftwareFoundations-Basics.dfy.expect @@ -1,3 +1,3 @@ SoftwareFoundations-Basics.dfy(41,11): Error: assertion might not hold -Dafny program verifier finished with 52 verified, 1 error +Dafny program verifier finished with 53 verified, 1 error From 8330d54ed761c5a8a28da17f4ca87494f21cc996 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 25 Nov 2024 09:49:58 -0500 Subject: [PATCH 126/151] try isolate --- .../IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy index b44109c3009..5b3d3a920fc 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy @@ -123,7 +123,7 @@ lemma GcdIdempotent(x: pos) assert x in Factors(x) * Factors(x); } -lemma GcdSubtract(x: pos, y: pos) +lemma GcdSubtract {:isolate_assertions} (x: pos, y: pos) requires x < y ensures Gcd(x, y) == Gcd(x, y - x) { From f24e93106ffce84d0d7370422068d1d239714f10 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 25 Nov 2024 10:49:02 -0500 Subject: [PATCH 127/151] null based heaping --- .../BoogieGenerator.ExpressionTranslator.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index ebde67c148e..b82c5739163 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -1566,9 +1566,10 @@ private Expr TrLambdaExpr(LambdaExpr e) { return new KeyValuePair(bv, new BoogieWrapper(unboxy, bv.Type)); }).ToDictionary(x => x.Key, x => x.Value); var su = new Substituter(null, subst, new Dictionary()); - // JATIN_UNDO: var oldHeapExpr = this.Old.HeapExpr; - // var et = new ExpressionTranslator(this.BoogieGenerator, this.Predef, heap, this.Old.HeapExpr, this.scope); - var et = new ExpressionTranslator(this, heap); + // JATIN_CHECK: + var et = this.HeapExpr != null + ? new ExpressionTranslator(this.BoogieGenerator, this.Predef, heap, this.Old.HeapExpr, this.scope) + : new ExpressionTranslator(this, heap); var lvars = new List(); var ly = BplBoundVar(varNameGen.FreshId("#ly#"), Predef.LayerType, lvars); et = et.WithLayer(ly); @@ -2346,8 +2347,10 @@ public Expr CanCallAssumption(Expression expr, CanCallOptions cco = null) { var varNameGen = BoogieGenerator.CurrentIdGenerator.NestedFreshIdGenerator("$l#"); Boogie.Expr heap; var hVar = BplBoundVar(varNameGen.FreshId("#heap#"), BoogieGenerator.Predef.HeapType, out heap); - var et = new ExpressionTranslator(this, heap); - // JATIN_UNDO: var et = new ExpressionTranslator(this.BoogieGenerator, this.Predef, heap, this.Old.HeapExpr, this.scope); + // JATIN_CHECK: + var et = this.HeapExpr != null + ? new ExpressionTranslator(this.BoogieGenerator, this.Predef, heap, this.Old.HeapExpr, this.scope) + : new ExpressionTranslator(this, heap); Dictionary subst = new Dictionary(); foreach (var bv in e.BoundVars) { From 2edf581f1a543374b92ae45fa34b16d3662288e0 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 25 Nov 2024 11:23:31 -0500 Subject: [PATCH 128/151] typo --- .../IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy index 5b3d3a920fc..24e5d12a31a 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny4/gcd.dfy @@ -123,7 +123,7 @@ lemma GcdIdempotent(x: pos) assert x in Factors(x) * Factors(x); } -lemma GcdSubtract {:isolate_assertions} (x: pos, y: pos) +lemma {:isolate_assertions} GcdSubtract (x: pos, y: pos) requires x < y ensures Gcd(x, y) == Gcd(x, y - x) { From f266ad70d5b5f4480976a1347345e91193acb0aa Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 25 Nov 2024 13:31:33 -0500 Subject: [PATCH 129/151] finalize heap change for lambads --- .../DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index b82c5739163..14275402fa2 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -1566,7 +1566,6 @@ private Expr TrLambdaExpr(LambdaExpr e) { return new KeyValuePair(bv, new BoogieWrapper(unboxy, bv.Type)); }).ToDictionary(x => x.Key, x => x.Value); var su = new Substituter(null, subst, new Dictionary()); - // JATIN_CHECK: var et = this.HeapExpr != null ? new ExpressionTranslator(this.BoogieGenerator, this.Predef, heap, this.Old.HeapExpr, this.scope) : new ExpressionTranslator(this, heap); @@ -2347,7 +2346,6 @@ public Expr CanCallAssumption(Expression expr, CanCallOptions cco = null) { var varNameGen = BoogieGenerator.CurrentIdGenerator.NestedFreshIdGenerator("$l#"); Boogie.Expr heap; var hVar = BplBoundVar(varNameGen.FreshId("#heap#"), BoogieGenerator.Predef.HeapType, out heap); - // JATIN_CHECK: var et = this.HeapExpr != null ? new ExpressionTranslator(this.BoogieGenerator, this.Predef, heap, this.Old.HeapExpr, this.scope) : new ExpressionTranslator(this, heap); From 8ae7880e19765872e0cf24dfb8aa9b1eccb3da62 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 25 Nov 2024 13:32:10 -0500 Subject: [PATCH 130/151] change trigger on requires to only depend on heap mentions in the spec --- Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs index b5386b1ef53..46f54190dbc 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs @@ -536,7 +536,7 @@ private Axiom GetFunctionAxiom(Function f, Expression body, List lits) { sink.AddTopLevelDeclaration(precondF); var appl = FunctionCall(f.tok, RequiresName(f), Bpl.Type.Bool, reqFuncArguments); - Bpl.Trigger trig = BplTriggerHeap(this, f.tok, appl, readsHeap ? etran.HeapExpr : null); + Bpl.Trigger trig = BplTriggerHeap(this, f.tok, appl, f.ReadsHeap ? etran.HeapExpr : null); // axiom (forall params :: { f#requires(params) } ante ==> f#requires(params) == pre); AddOtherDefinition(precondF, new Axiom(f.tok, BplForall(forallFormals, trig, BplImp(anteReqAxiom, Bpl.Expr.Eq(appl, preReqAxiom))), From b23d1d3e4ce7e73ed0b83f797fae242802f99ce5 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 25 Nov 2024 13:33:21 -0500 Subject: [PATCH 131/151] split assertions into two methods --- .../ReadPreconditionBypass4.dfy | 11 ++++++++++- .../ReadPreconditionBypass4.dfy.expect | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/HigherOrderIntrinsicSpecification/ReadPreconditionBypass4.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/HigherOrderIntrinsicSpecification/ReadPreconditionBypass4.dfy index aed6b9af848..50422d9f020 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/HigherOrderIntrinsicSpecification/ReadPreconditionBypass4.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/HigherOrderIntrinsicSpecification/ReadPreconditionBypass4.dfy @@ -13,7 +13,7 @@ function myf(o: Ref): () () } -method Main() +method M() ensures false { var outer := new Ref(); @@ -25,6 +25,15 @@ method Main() outer.inner := inner1; var reads1 := myh.reads(outer); assert reads1 == {inner1}; // Error: assertion might not hold +} + +method M2() + ensures false +{ + var outer := new Ref(); + + var myg := myf.requires; + var myh := myg.requires; var inner2 := new Ref(); outer.inner := inner2; diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/HigherOrderIntrinsicSpecification/ReadPreconditionBypass4.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/HigherOrderIntrinsicSpecification/ReadPreconditionBypass4.dfy.expect index e984f72b06f..676b23d73cf 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/HigherOrderIntrinsicSpecification/ReadPreconditionBypass4.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/HigherOrderIntrinsicSpecification/ReadPreconditionBypass4.dfy.expect @@ -1,4 +1,4 @@ ReadPreconditionBypass4.dfy(27,16): Error: assertion might not hold -ReadPreconditionBypass4.dfy(32,16): Error: assertion might not hold +ReadPreconditionBypass4.dfy(41,16): Error: assertion might not hold Dafny program verifier finished with 1 verified, 2 errors From 6fc4bd12dd3cf424399509225c1fdc17bc237640 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 25 Nov 2024 13:33:30 -0500 Subject: [PATCH 132/151] eof --- .../ReadPreconditionBypass4.dfy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/HigherOrderIntrinsicSpecification/ReadPreconditionBypass4.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/HigherOrderIntrinsicSpecification/ReadPreconditionBypass4.dfy index 50422d9f020..cc8ce7abcff 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/HigherOrderIntrinsicSpecification/ReadPreconditionBypass4.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/HigherOrderIntrinsicSpecification/ReadPreconditionBypass4.dfy @@ -39,4 +39,4 @@ method M2() outer.inner := inner2; var reads2 := myh.reads(outer); assert reads2 == {inner2}; // Error: assertion might not hold -} \ No newline at end of file +} From 713131182d0661f0260c5bbdcc1b4ae448b8290c Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 25 Nov 2024 14:23:16 -0500 Subject: [PATCH 133/151] update resources --- .../TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect index 72a9e8757a4..0430916f99d 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect @@ -1,4 +1,4 @@ Dafny program verifier finished with 272 verified, 0 errors -Total resources used is 27941703 -Max resources used by VC is 1155634 +Total resources used is 29547101 +Max resources used by VC is 2247116 From 7040b1f6295ea075f3c1c1d99b85bdca15688808 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 25 Nov 2024 14:30:17 -0500 Subject: [PATCH 134/151] upd fuel leg --- .../LitTest/dafny0/Fuel.legacy.dfy.expect | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect index 8fc90aabded..c5a06552f28 100755 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/Fuel.legacy.dfy.expect @@ -18,6 +18,9 @@ Fuel.legacy.dfy(247,22): Error: assertion might not hold Fuel.legacy.dfy(280,26): Error: assertion might not hold Fuel.legacy.dfy(335,26): Error: function precondition could not be proved Fuel.legacy.dfy(324,21): Related location +Fuel.legacy.dfy(313,41): Related location +Fuel.legacy.dfy(335,26): Error: function precondition could not be proved +Fuel.legacy.dfy(324,21): Related location Fuel.legacy.dfy(314,72): Related location Fuel.legacy.dfy(335,26): Error: function precondition could not be proved Fuel.legacy.dfy(324,21): Related location @@ -27,9 +30,6 @@ Fuel.legacy.dfy(324,21): Related location Fuel.legacy.dfy(314,46): Related location Fuel.legacy.dfy(335,26): Error: function precondition could not be proved Fuel.legacy.dfy(324,21): Related location -Fuel.legacy.dfy(313,41): Related location -Fuel.legacy.dfy(335,26): Error: function precondition could not be proved -Fuel.legacy.dfy(324,21): Related location Fuel.legacy.dfy(312,43): Related location Fuel.legacy.dfy(335,26): Error: function precondition could not be proved Fuel.legacy.dfy(324,21): Related location @@ -40,22 +40,22 @@ Fuel.legacy.dfy(336,38): Error: index out of range Fuel.legacy.dfy(336,42): Error: destructor 'u' can only be applied to datatype values constructed by 'VUint64' Fuel.legacy.dfy(336,45): Error: function precondition could not be proved Fuel.legacy.dfy(329,21): Related location -Fuel.legacy.dfy(314,72): Related location +Fuel.legacy.dfy(311,43): Related location Fuel.legacy.dfy(336,45): Error: function precondition could not be proved Fuel.legacy.dfy(329,21): Related location -Fuel.legacy.dfy(313,41): Related location +Fuel.legacy.dfy(314,93): Related location Fuel.legacy.dfy(336,45): Error: function precondition could not be proved Fuel.legacy.dfy(329,21): Related location -Fuel.legacy.dfy(314,93): Related location +Fuel.legacy.dfy(312,58): Related location Fuel.legacy.dfy(336,45): Error: function precondition could not be proved Fuel.legacy.dfy(329,21): Related location -Fuel.legacy.dfy(312,43): Related location +Fuel.legacy.dfy(313,41): Related location Fuel.legacy.dfy(336,45): Error: function precondition could not be proved Fuel.legacy.dfy(329,21): Related location -Fuel.legacy.dfy(312,58): Related location +Fuel.legacy.dfy(314,72): Related location Fuel.legacy.dfy(336,45): Error: function precondition could not be proved Fuel.legacy.dfy(329,21): Related location -Fuel.legacy.dfy(311,43): Related location +Fuel.legacy.dfy(312,43): Related location Fuel.legacy.dfy(336,71): Error: index out of range Fuel.legacy.dfy(397,22): Error: assertion might not hold Fuel.legacy.dfy(398,22): Error: assertion might not hold From 700d2a6f06d8f6b31e606f7220ac06468100f2b7 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Tue, 26 Nov 2024 10:30:42 -0500 Subject: [PATCH 135/151] upd both --- .../TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect | 4 ++-- .../LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect index 0430916f99d..72a9e8757a4 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect @@ -1,4 +1,4 @@ Dafny program verifier finished with 272 verified, 0 errors -Total resources used is 29547101 -Max resources used by VC is 2247116 +Total resources used is 27941703 +Max resources used by VC is 1155634 diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect index 2e3c4a6c7f6..6b6e5d75bc6 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect @@ -1,4 +1,4 @@ Dafny program verifier finished with 276 verified, 0 errors -Total resources used is 28790607 -Max resources used by VC is 974212 +Total resources used is 27941703 +Max resources used by VC is 1155634 From 179d8e89b497e80fd0ae38817be5151a3f05a6e0 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Tue, 26 Nov 2024 16:28:17 -0800 Subject: [PATCH 136/151] Update proof --- .../concurrency/12-MutexLifetime-short.dfy | 54 +++++++++++++------ .../12-MutexLifetime-short.dfy.expect | 2 +- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/concurrency/12-MutexLifetime-short.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/concurrency/12-MutexLifetime-short.dfy index 0b931ef0ae4..6a534428f33 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/concurrency/12-MutexLifetime-short.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/concurrency/12-MutexLifetime-short.dfy @@ -886,7 +886,7 @@ class MutexGuardU32 extends OwnedObject { } // MutexGuardU32 - constructor {:isolate_assertions} (ghost universe: Universe, ghost running: Thread, ghost scope: Lifetime, mutex: Mutex, ghost mutexScope: Lifetime) + constructor {:isolate_assertions} {:resource_limit "1e9"} (ghost universe: Universe, ghost running: Thread, ghost scope: Lifetime, mutex: Mutex, ghost mutexScope: Lifetime) requires universe.globalInv() && { running, scope, mutex, mutexScope } <= universe.content requires scope.owner == running && mutexScope.owner == running && scope != mutexScope requires universe.outlives(mutex.lifetime, mutexScope) && universe.outlives(mutexScope, scope) && scope.unused() @@ -921,32 +921,52 @@ class MutexGuardU32 extends OwnedObject { this.mutex := mutex; this.data := mutex.data; new; - join(); + hide *; + join() by { + assert baseFieldsInv() by { + reveal baseFieldsInv(); + assert objectFields() <= universe.content by { + reveal objectFields(); + assert objectUserFields() <= universe.content by { + reveal *; + } + } + } + } lifetime.elements := lifetime.elements + { this }; // Acquire the lock this.mutex.locked := true; this.mutex.guards := { this }; // Transfer ownership of `this.mutex.data` from `this.mutex` to `this`. - this.mutex.data.owner := this; + this.mutex.data.owner := this by { + reveal *; + } this.mutex.data.nonvolatileVersion := Bump(this.mutex.data.nonvolatileVersion); // We don't need to bump this.mutex.nonvolatileVersion, because it uses volatileOwns. - universe.FrameOutlives@lci_l2(); - assert universe.outlives(mutex.lifetime, mutexScope) && universe.outlives(mutexScope, this.lifetime); // needed - assert universe.outlives(mutex.lifetime, this.lifetime); // needed - assert lifetime.alive(); // helps dafny - assert mutex.owner != null; // helps dafny - assert this.localUserInv(); - assert this.userInv(); - assert this.inv(); - assert this.mutex.inv(); - assert this.mutex.data.inv(); - universe.lci@lci_l2(running); - assert {:split_here} true; + universe.FrameOutlives@lci_l2() by { + reveal *; + } + + universe.lci@lci_l2(running) by { + reveal *; + assert universe.outlives(mutex.lifetime, mutexScope) && universe.outlives(mutexScope, this.lifetime); // needed + assert universe.outlives(mutex.lifetime, this.lifetime); // needed + assert lifetime.alive(); // helps dafny + assert mutex.owner != null; // helps dafny + assert this.localUserInv(); + assert this.userInv(); + assert this.inv(); + assert this.mutex.inv(); + assert this.mutex.data.inv(); + } label lci_l3: scope.owner := null; universe.FrameOutlives@lci_l3(); - universe.lci@lci_l3(running); - assert {:split_here} true; + universe.lci@lci_l3(running) by { + reveal *; + } + + reveal *; } } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/concurrency/12-MutexLifetime-short.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/concurrency/12-MutexLifetime-short.dfy.expect index 79537616855..7271da1866c 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/concurrency/12-MutexLifetime-short.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/concurrency/12-MutexLifetime-short.dfy.expect @@ -1,2 +1,2 @@ -Dafny program verifier finished with 434 verified, 0 errors +Dafny program verifier finished with 385 verified, 0 errors From d3021675d2f8c0a2ab5db88af0fa23f84def6495 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 2 Dec 2024 12:37:44 -0800 Subject: [PATCH 137/151] Update expected coverage output --- .../logger/ProofDependencies.dfy_verification.html.expect | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/logger/ProofDependencies.dfy_verification.html.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/logger/ProofDependencies.dfy_verification.html.expect index 17c0fdc81c1..f70731a9119 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/logger/ProofDependencies.dfy_verification.html.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/logger/ProofDependencies.dfy_verification.html.expect @@ -343,11 +343,11 @@ method {:testEntry} MultiPartContradictoryRequiresMethod(x: int, y: int) returns function {:testEntry} ContradictoryEnsuresClauseFunc(x: int): (r: int) requires x > 1 - ensures r > x && r < 0 + ensures r > x && r < 0 method {:testEntry} ContradictoryEnsuresClauseMethod(x: int) returns (r: int) requires x > 1 - ensures r > x && r < 0 + ensures r > x && r < 0 // Call function that has contradictory ensures clauses. function {:testEntry} CallContradictoryFunctionFunc(x: int): (r: int) From 28452b015429b5e0b003af1b22d7a70a96880141 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 2 Dec 2024 12:39:55 -0800 Subject: [PATCH 138/151] Update expected resource counts --- .../LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect index 6b6e5d75bc6..178c31653cc 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect @@ -1,4 +1,4 @@ Dafny program verifier finished with 276 verified, 0 errors -Total resources used is 27941703 -Max resources used by VC is 1155634 +Total resources used is 29547101 +Max resources used by VC is 2247116 From 2d3cf692b73a986da0be70b3196b7356dd6f8711 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 2 Dec 2024 19:35:29 -0800 Subject: [PATCH 139/151] Update coverage output --- .../ProofDependencies.dfy_tests_expected.html.expect | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/logger/ProofDependencies.dfy_tests_expected.html.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/logger/ProofDependencies.dfy_tests_expected.html.expect index a953198340b..c270958150a 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/logger/ProofDependencies.dfy_tests_expected.html.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/logger/ProofDependencies.dfy_tests_expected.html.expect @@ -392,8 +392,8 @@ function {:testEntry} ObviouslyUnreachableIfExpressionBranchFunc(x: int): (r:int ensures r > 0 { if x < 0 - then x - 1 // unreachable - else x + 1 + then x - 1 // unreachable + else x + 1 } method {:testEntry} ObviouslyUnreachableIfStatementBranchMethod(x: int) returns (r:int) @@ -414,8 +414,8 @@ function {:testEntry} ObviouslyUnreachableIfExpressionBranchFunc(x: int): (r:int ensures r > 1 // alt: r > 0 { match t { - case A => 1 // unreachable - case B => 2 + case A => 1 // unreachable + case B => 2 } } From 2006f9826931b230a8d9316917eaf254dcc6e456 Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 2 Dec 2024 20:14:48 -0800 Subject: [PATCH 140/151] Fix typos in test harness --- ...enerationNoInliningEnumerativeDefinitions.dfy | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/blogposts/TestGenerationNoInliningEnumerativeDefinitions.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/blogposts/TestGenerationNoInliningEnumerativeDefinitions.dfy index b97f6ac832b..c172dac73c7 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/blogposts/TestGenerationNoInliningEnumerativeDefinitions.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/blogposts/TestGenerationNoInliningEnumerativeDefinitions.dfy @@ -1,20 +1,20 @@ // Generating and Running Block-Based Tests: // RUN: %baredafny generate-tests %args Block %S/TestGenerationNoInliningEnumerativeDefinitions.dfy > %t-tests.dfy -// RUN: %baredafny test %args --target:cs "%t-tests.dfy" >> "%t" +// RUN: %baredafny test %args --target:cs "%t-tests.dfy" > "%t" // Generating and Running Path-Based Tests: // RUN: %baredafny generate-tests %args Path %S/TestGenerationNoInliningEnumerativeDefinitions.dfy > %t-tests.dfy // RUN: %baredafny test %args --target:cs "%t-tests.dfy" >> "%t" // RUN: %OutputCheck --file-to-check "%t" "%s" -// CHECK: .*Dafny program verifier finished with 2 verified, 0 errors* -// CHECK: .*Evaluating the position: checked=no, checkmate=no* -// CHECK: .*Evaluating the position: checked=yes, checkmate=yes* -// CHECK: .*Dafny program verifier finished with 3 verified, 0 errors* -// CHECK: .*Evaluating the position: checked=yes, checkmate=yes* -// CHECK: .*Evaluating the position: checked=yes, checkmate=no* -// CHECK: .*Evaluating the position: checked=no, checkmate=no* +// CHECK: .*Dafny program verifier finished with 2 verified, 0 errors.* +// CHECK: .*Evaluating the position: checked=no, checkmate=no.* +// CHECK: .*Evaluating the position: checked=yes, checkmate=yes.* +// CHECK: .*Dafny program verifier finished with 3 verified, 0 errors.* +// CHECK: .*Evaluating the position: checked=yes, checkmate=yes.* +// CHECK: .*Evaluating the position: checked=yes, checkmate=no.* +// CHECK: .*Evaluating the position: checked=no, checkmate=no.* include "Inputs/TestGenerationShared.dfy" From b0ca0297bbbf62cc96adc2e92b7878be643fc6eb Mon Sep 17 00:00:00 2001 From: Rustan Leino Date: Mon, 2 Dec 2024 20:27:19 -0800 Subject: [PATCH 141/151] Update coverage output further --- .../LitTest/logger/ProofDependencies.dfy_combined.html.expect | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/logger/ProofDependencies.dfy_combined.html.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/logger/ProofDependencies.dfy_combined.html.expect index e5b6841d23d..758d9110363 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/logger/ProofDependencies.dfy_combined.html.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/logger/ProofDependencies.dfy_combined.html.expect @@ -392,7 +392,7 @@ method {:testEntry} FalseAntecedentEnsuresClauseMethod(x: int) returns (r: int) ensures r > 0 { if x < 0 - then x - 1 // unreachable + then x - 1 // unreachable else x + 1 } From 641320fe605ae923c7bc036aab4514b060c4a47e Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Thu, 12 Dec 2024 11:25:33 -0500 Subject: [PATCH 142/151] forgot --- Source/DafnyCore/AST/Expressions/Expression.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/DafnyCore/AST/Expressions/Expression.cs b/Source/DafnyCore/AST/Expressions/Expression.cs index e4598b74201..5375e36385b 100644 --- a/Source/DafnyCore/AST/Expressions/Expression.cs +++ b/Source/DafnyCore/AST/Expressions/Expression.cs @@ -175,7 +175,7 @@ public static IEnumerable ConjunctsWithLetsOnOutside(Expression expr /// If there is just one expression in "expressions", then use the given token "tok" for the negation. /// Otherwise, use the token from each expression. /// - static IEnumerable NegateEach(IToken tok, IEnumerable expressions) { + static IEnumerable NegateEach(IOrigin tok, IEnumerable expressions) { var exprs = expressions.ToList(); foreach (Expression e in exprs) { yield return Expression.CreateNot(exprs.Count == 1 ? tok : e.tok, e); From b7f7dd1cf89f28c89ddab83c0ebd6bfb379a90ca Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Thu, 12 Dec 2024 14:22:41 -0500 Subject: [PATCH 143/151] update thsoe agains --- .../TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect | 4 ++-- .../TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect | 4 ++-- .../LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny0/SubsetTypes.dfy.expect index a80d2f3dd26..57f3ebb12af 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 740400 -Max resources used by VC is 82700 +Total resources used is 922000 +Max resources used by VC is 132800 diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect index 4511620644e..81afb14df05 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.expect @@ -1,4 +1,4 @@ Dafny program verifier finished with 272 verified, 0 errors -Total resources used is 29132185 -Max resources used by VC is 2079032 +Total resources used is 27987600 +Max resources used by VC is 1817957 diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect index 31b8bf886ee..b0d8b3b019e 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect @@ -1,4 +1,4 @@ Dafny program verifier finished with 276 verified, 0 errors -Total resources used is 34463182 -Max resources used by VC is 6990729 +Total resources used is 27987600 +Max resources used by VC is 1817957 From 719a9d3f37fe772f33b939eff0822f690389f2bc Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Thu, 12 Dec 2024 14:25:47 -0500 Subject: [PATCH 144/151] update rc --- .../LitTest/concurrency/06-ThreadOwnership.dfy | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/concurrency/06-ThreadOwnership.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/concurrency/06-ThreadOwnership.dfy index c5146ec389b..6137755bf13 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/concurrency/06-ThreadOwnership.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/concurrency/06-ThreadOwnership.dfy @@ -75,7 +75,7 @@ trait Object { // This should really be a constant, but I don't know how to do that while factoring out join below, // because traits can't have constructors. const universe: Universe - + // Base invariant: we're in the universe, and the universe satisfies its base. ghost predicate baseInv() reads * { this in universe.content && universe.globalBaseInv() } @@ -114,7 +114,7 @@ trait Object { ghost predicate inv() ensures inv() ==> localInv() reads * twostate predicate inv2() ensures inv2() ==> localInv2() reads * twostate lemma admissibility(running: Thread) requires goodPreAndLegalChanges(running) ensures inv2() && inv() - + // To prevent a class from extending both OwnedObject and NonOwnedObject ghost predicate instanceOfOwnedObject() } @@ -243,7 +243,7 @@ class EmptyType extends OwnedObject { twostate predicate userFieldsUnchanged() reads * { true } - + ghost predicate baseUserInv() reads * { && true } @@ -322,7 +322,7 @@ class AtomicCounter extends OwnedObject { //modifies running ensures objectGlobalInv() && universe.globalInv2() // The following might not always be needed - ensures this.universe == universe && this.owner == running && this.value == initialValue && this.closed == false + ensures this.universe == universe && this.owner == running && this.value == initialValue && this.closed == false //ensures running.ownedObjects == old(running.ownedObjects) + { this } ensures universe.content == old(universe.content) + { this } { @@ -359,7 +359,7 @@ class DoubleReadMethod extends OwnedObject { && old(initial_value) == initial_value && old(final_value) == final_value } - + ghost predicate baseUserInv() reads * { && counter in universe.content && counter.universe == universe } @@ -412,7 +412,7 @@ class DoubleReadMethod extends OwnedObject { universe.lci(running); } - method Run(ghost running: Thread) + method {:resource_limit "1e9"} Run(ghost running: Thread) requires this.objectGlobalInv() && running.universe == universe && running.inv() requires programCounter == 0 && closed && this.owner == running // Special requirements of Run modifies universe, universe.content, this From 2e4b4a501c3761f52e3670e2d0efbb8112629610 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 16 Dec 2024 11:25:41 -0500 Subject: [PATCH 145/151] nc to pc is okay --- .../LitTest/logger/ProofDependencies.dfy_combined.html.expect | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/logger/ProofDependencies.dfy_combined.html.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/logger/ProofDependencies.dfy_combined.html.expect index 758d9110363..00645d652d1 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/logger/ProofDependencies.dfy_combined.html.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/logger/ProofDependencies.dfy_combined.html.expect @@ -414,7 +414,7 @@ function {:testEntry} ObviouslyUnreachableMatchExpressionCaseFunction(t: T): (r: ensures r > 1 // alt: r > 0 { match t { - case A => 1 // unreachable + case A => 1 // unreachable case B => 2 } } From b88a8b233a5631e7e19ce0f3197e70811080cb74 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 16 Dec 2024 11:30:11 -0500 Subject: [PATCH 146/151] update rc --- .../LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect index b0d8b3b019e..3f5bab87c1f 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/dafny1/SchorrWaite.dfy.refresh.expect @@ -1,4 +1,4 @@ Dafny program verifier finished with 276 verified, 0 errors -Total resources used is 27987600 -Max resources used by VC is 1817957 +Total resources used is 27733327 +Max resources used by VC is 1227140 From 2ee1347bb6bd3c930addaca7bb6997f6f2165fb3 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 16 Dec 2024 11:39:39 -0500 Subject: [PATCH 147/151] merge with master wagain --- .../Verifier/BoogieGenerator.ExpressionTranslator.cs | 4 ++-- .../Verifier/BoogieGenerator.ExpressionWellformed.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index 68ccd73b329..2ef2a174346 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -2250,14 +2250,14 @@ public Expr CanCallAssumption(Expression expr, CanCallOptions cco = null) { var dafnyInitApplication = new ApplyExpr(e.tok, e.Initializer, new List() { new BoogieWrapper(index, Type.Int) }, - e.tok) { + Token.NoToken) { Type = e.Initializer.Type.AsArrowType.Result }; var canCall = CanCallAssumption(dafnyInitApplication); dafnyInitApplication = new ApplyExpr(e.tok, new BoogieWrapper(initF, e.Initializer.Type), new List() { new BoogieWrapper(index, Type.Int) }, - e.tok) { + Token.NoToken) { Type = e.Initializer.Type.AsArrowType.Result }; var apply = TrExpr(dafnyInitApplication); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 8007835aa4c..e1827167ac2 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -1684,7 +1684,7 @@ private void CheckElementInit(IOrigin tok, bool forArray, List dims, // nw[i0,i1,i2,...] == init.requires(i0,i1,i2,...)); var dafnyInitApplication = new ApplyExpr(tok, init, bvs.ConvertAll(indexBv => (Expression)new BoogieWrapper(new Bpl.IdentifierExpr(indexBv.tok, indexBv), Type.Int)).ToList(), - tok) { + Token.NoToken) { Type = sourceType.Result }; var canCall = etran.CanCallAssumption(dafnyInitApplication); From 313383c060c2b56acbde616e169d483ee124561f Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 16 Dec 2024 12:15:47 -0500 Subject: [PATCH 148/151] update limited expet --- ...roofDependencies.dfy_combined_limited.html.expect | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/logger/ProofDependencies.dfy_combined_limited.html.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/logger/ProofDependencies.dfy_combined_limited.html.expect index 53730d6858a..eb4f242deee 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/logger/ProofDependencies.dfy_combined_limited.html.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/logger/ProofDependencies.dfy_combined_limited.html.expect @@ -343,11 +343,11 @@ method {:testEntry} MultiPartContradictoryRequiresMethod(x: int, y: int) returns function {:testEntry} ContradictoryEnsuresClauseFunc(x: int): (r: int) requires x > 1 - ensures r > x && r < 0 + ensures r > x && r < 0 method {:testEntry} ContradictoryEnsuresClauseMethod(x: int) returns (r: int) requires x > 1 - ensures r > x && r < 0 + ensures r > x && r < 0 // Call function that has contradictory ensures clauses. function {:testEntry} CallContradictoryFunctionFunc(x: int): (r: int) @@ -392,8 +392,8 @@ method {:testEntry} FalseAntecedentEnsuresClauseMethod(x: int) returns (r: int) ensures r > 0 { if x < 0 - then x - 1 // unreachable - else x + 1 + then x - 1 // unreachable + else x + 1 } method {:testEntry} ObviouslyUnreachableIfStatementBranchMethod(x: int) returns (r:int) @@ -414,8 +414,8 @@ function {:testEntry} ObviouslyUnreachableMatchExpressionCaseFunction(t: T): (r: ensures r > 1 // alt: r > 0 { match t { - case A => 1 // unreachable - case B => 2 + case A => 1 // unreachable + case B => 2 } } From 6b1df08e6920014544205774d73040debc057736 Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Tue, 17 Dec 2024 10:10:03 -0500 Subject: [PATCH 149/151] read conditions update --- .../ReadPreconditionBypass4.dfy | 14 +++++++++----- .../ReadPreconditionBypass4.dfy.expect | 8 +++++--- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/HigherOrderIntrinsicSpecification/ReadPreconditionBypass4.dfy b/Source/IntegrationTests/TestFiles/LitTests/LitTest/HigherOrderIntrinsicSpecification/ReadPreconditionBypass4.dfy index cc8ce7abcff..61a5804b897 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/HigherOrderIntrinsicSpecification/ReadPreconditionBypass4.dfy +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/HigherOrderIntrinsicSpecification/ReadPreconditionBypass4.dfy @@ -3,7 +3,9 @@ class Ref { var inner: Ref - constructor() + constructor() { + inner := this; + } } function myf(o: Ref): () @@ -14,7 +16,6 @@ function myf(o: Ref): () } method M() - ensures false { var outer := new Ref(); @@ -23,12 +24,14 @@ method M() var inner1 := new Ref(); outer.inner := inner1; - var reads1 := myh.reads(outer); - assert reads1 == {inner1}; // Error: assertion might not hold + var reads1 := myg.reads(outer); + var reads2 := myh.reads(outer); + assert reads1 == reads2; + assert reads2 == {inner1}; // Error: assertion might not hold + assert false; // we don't know what the reads clause is, because the precondition of myf does not hold. } method M2() - ensures false { var outer := new Ref(); @@ -39,4 +42,5 @@ method M2() outer.inner := inner2; var reads2 := myh.reads(outer); assert reads2 == {inner2}; // Error: assertion might not hold + assert false; // we don't know what the reads clause is, because the precondition of myf does not hold. } diff --git a/Source/IntegrationTests/TestFiles/LitTests/LitTest/HigherOrderIntrinsicSpecification/ReadPreconditionBypass4.dfy.expect b/Source/IntegrationTests/TestFiles/LitTests/LitTest/HigherOrderIntrinsicSpecification/ReadPreconditionBypass4.dfy.expect index 676b23d73cf..b44a826c790 100644 --- a/Source/IntegrationTests/TestFiles/LitTests/LitTest/HigherOrderIntrinsicSpecification/ReadPreconditionBypass4.dfy.expect +++ b/Source/IntegrationTests/TestFiles/LitTests/LitTest/HigherOrderIntrinsicSpecification/ReadPreconditionBypass4.dfy.expect @@ -1,4 +1,6 @@ -ReadPreconditionBypass4.dfy(27,16): Error: assertion might not hold -ReadPreconditionBypass4.dfy(41,16): Error: assertion might not hold +ReadPreconditionBypass4.dfy(30,16): Error: assertion might not hold +ReadPreconditionBypass4.dfy(31,9): Error: assertion might not hold +ReadPreconditionBypass4.dfy(44,16): Error: assertion might not hold +ReadPreconditionBypass4.dfy(45,9): Error: assertion might not hold -Dafny program verifier finished with 1 verified, 2 errors +Dafny program verifier finished with 2 verified, 4 errors From 500216f811b060d3786cc5dccb9745a22063ac4c Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Tue, 17 Dec 2024 13:45:05 -0500 Subject: [PATCH 150/151] space changes --- Source/DafnyCore/Verifier/BoogieGenerator.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index 232837070f4..eb452e679a2 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -3398,7 +3398,6 @@ Bpl.Ensures EnsuresWithDependencies(IOrigin tok, bool free, Expression dafnyCond proofDependencies?.AddProofDependencyId(ens, tok, new EnsuresDependency(tok, dafnyCondition)); return ens; } - Bpl.Ensures FreeEnsures(IOrigin tok, Bpl.Expr condition, string comment, bool alwaysAssume = false) { var kv = alwaysAssume ? AlwaysAssumeAttribute(tok) : null; return Ensures(tok, true, null, condition, null, null, comment, kv); @@ -3429,7 +3428,6 @@ Bpl.Requires RequiresWithDependencies(IOrigin tok, bool free, Expression dafnyCo proofDependencies?.AddProofDependencyId(req, tok, new RequiresDependency(tok, dafnyCondition)); return req; } - Bpl.Requires FreeRequires(IOrigin tok, Bpl.Expr bCondition, string comment, bool alwaysAssume = false) { var kv = alwaysAssume ? AlwaysAssumeAttribute(tok) : null; return Requires(tok, true, null, bCondition, null, null, comment, kv); From 2c7d72b7a09ae2900e42be53ad9be60dfc37850b Mon Sep 17 00:00:00 2001 From: Jatin Arora Date: Mon, 23 Dec 2024 11:06:23 -0500 Subject: [PATCH 151/151] merge --- .../BoogieGenerator.ExpressionTranslator.cs | 26 +++++++++---------- .../BoogieGenerator.ExpressionWellformed.cs | 10 +++---- ...oogieGenerator.Functions.Wellformedness.cs | 2 +- .../Verifier/BoogieGenerator.Functions.cs | 12 ++++----- .../Verifier/BoogieGenerator.Methods.cs | 18 ++++++------- .../Verifier/BoogieGenerator.Types.cs | 2 +- Source/DafnyCore/Verifier/BoogieGenerator.cs | 2 +- .../BoogieGenerator.TrForallStmt.cs | 2 +- .../Statements/BoogieGenerator.TrLoop.cs | 4 +-- 9 files changed, 38 insertions(+), 40 deletions(-) diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs index bb6e4ce2fec..84d2939117a 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionTranslator.cs @@ -25,7 +25,7 @@ public Boogie.Expr HeapExpr { public Boogie.Expr HeapExprForArrow(Type arrowType) { if (arrowType.IsArrowTypeWithoutReadEffects) { - return BoogieGenerator.NewOneHeapExpr(arrowType.tok); + return BoogieGenerator.NewOneHeapExpr(arrowType.Tok); } else { return HeapExpr; } @@ -2123,7 +2123,7 @@ public Boogie.Expr GoodRef(IOrigin tok, Boogie.Expr e, Type type) { } public Expression MakeAllowance(FunctionCallExpr e, CanCallOptions cco = null) { - Expression allowance = Expression.CreateBoolLiteral(e.tok, true); + Expression allowance = Expression.CreateBoolLiteral(e.Tok, true); if (!e.Function.IsStatic) { allowance = Expression.CreateAnd(allowance, Expression.CreateEq(e.Receiver, new ThisExpr(e.Function), e.Receiver.Type)); } @@ -2199,7 +2199,7 @@ public Expr CanCallAssumption(Expression expr, CanCallOptions cco = null) { if (ModeledAsBoxType(arg.Type)) { return inner; } else { - return BoogieGenerator.FunctionCall(arg.tok, BuiltinFunction.Box, null, inner); + return BoogieGenerator.FunctionCall(arg.Tok, BuiltinFunction.Box, null, inner); } }; @@ -2209,7 +2209,7 @@ public Expr CanCallAssumption(Expression expr, CanCallOptions cco = null) { Cons(TrExpr(e.Function), e.Args.ConvertAll(arg => TrArg(arg))))); - var requiresk = FunctionCall(e.tok, Requires(e.Args.Count), Boogie.Type.Bool, args); + var requiresk = FunctionCall(e.Tok, Requires(e.Args.Count), Boogie.Type.Bool, args); return BplAnd( BplAnd( Cons(CanCallAssumption(e.Function, cco), @@ -2243,30 +2243,30 @@ public Expr CanCallAssumption(Expression expr, CanCallOptions cco = null) { // CanCallAssumption[[ init(i) ]]) var varNameGen = BoogieGenerator.CurrentIdGenerator.NestedFreshIdGenerator("seqinit$"); - var indexVar = new Bpl.BoundVariable(e.tok, new Bpl.TypedIdent(e.tok, varNameGen.FreshId("#i"), Bpl.Type.Int)); - var index = new Bpl.IdentifierExpr(e.tok, indexVar); + var indexVar = new Bpl.BoundVariable(e.Tok, new Bpl.TypedIdent(e.Tok, varNameGen.FreshId("#i"), Bpl.Type.Int)); + var index = new Bpl.IdentifierExpr(e.Tok, indexVar); var indexRange = BplAnd(Bpl.Expr.Le(Bpl.Expr.Literal(0), index), Bpl.Expr.Lt(index, TrExpr(e.N))); - var initFVar = new Bpl.BoundVariable(e.tok, new Bpl.TypedIdent(e.tok, varNameGen.FreshId("#f"), Predef.HandleType)); + var initFVar = new Bpl.BoundVariable(e.Tok, new Bpl.TypedIdent(e.Tok, varNameGen.FreshId("#f"), Predef.HandleType)); - var initF = new Bpl.IdentifierExpr(e.tok, initFVar); + var initF = new Bpl.IdentifierExpr(e.Tok, initFVar); - var dafnyInitApplication = new ApplyExpr(e.tok, e.Initializer, + var dafnyInitApplication = new ApplyExpr(e.Tok, e.Initializer, new List() { new BoogieWrapper(index, Type.Int) }, Token.NoToken) { Type = e.Initializer.Type.AsArrowType.Result }; var canCall = CanCallAssumption(dafnyInitApplication); - dafnyInitApplication = new ApplyExpr(e.tok, new BoogieWrapper(initF, e.Initializer.Type), + dafnyInitApplication = new ApplyExpr(e.Tok, new BoogieWrapper(initF, e.Initializer.Type), new List() { new BoogieWrapper(index, Type.Int) }, Token.NoToken) { Type = e.Initializer.Type.AsArrowType.Result }; var apply = TrExpr(dafnyInitApplication); - var tr = new Bpl.Trigger(e.tok, true, new List { apply }); - var ccaInit = new Bpl.ForallExpr(e.tok, new List() { indexVar }, tr, BplImp(indexRange, canCall)); - var rhsAppliedToIndex = new Bpl.LetExpr(e.tok, new List() { initFVar }, + var tr = new Bpl.Trigger(e.Tok, true, new List { apply }); + var ccaInit = new Bpl.ForallExpr(e.Tok, new List() { indexVar }, tr, BplImp(indexRange, canCall)); + var rhsAppliedToIndex = new Bpl.LetExpr(e.Tok, new List() { initFVar }, new List() { TrExpr(e.Initializer) }, null, ccaInit); return BplAnd(BplAnd(CanCallAssumption(e.N, cco), CanCallAssumption(e.Initializer, cco)), rhsAppliedToIndex); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs index 00e42cffe7a..79dbe57e16b 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.ExpressionWellformed.cs @@ -161,7 +161,7 @@ public void CheckWellformedAndAssume(Expression expr, WFOptions wfOptions, Varia var bImp = new BoogieStmtListBuilder(this, options, builder.Context); bImp.Add(TrAssumeCmd(expr.Tok, etran.CanCallAssumption(expr))); bImp.Add(TrAssumeCmdWithDependencies(etran, expr.Tok, expr, comment)); - builder.Add(new Bpl.IfCmd(expr.Tok, null, bAnd.Collect(expr.tok), null, bImp.Collect(expr.tok))); + builder.Add(new Bpl.IfCmd(expr.Tok, null, bAnd.Collect(expr.Tok), null, bImp.Collect(expr.Tok))); } return; case BinaryExpr.ResolvedOpcode.Or: { @@ -823,7 +823,7 @@ public void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, var directPrecond = directSub.Substitute(p.E); Expression precond = Substitute(p.E, e.Receiver, substMap, e.GetTypeArgumentSubstitutions()); - builder.Add(TrAssumeCmd(precond.tok, etran.CanCallAssumption(precond))); + builder.Add(TrAssumeCmd(precond.Tok, etran.CanCallAssumption(precond))); var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); foreach (var ss in TrSplitExpr(builder.Context, precond, etran, true, out _)) { if (ss.IsChecked) { @@ -865,8 +865,6 @@ public void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, if (wfOptions.DoOnlyCoarseGrainedTerminationChecks) { builder.Add(Assert(GetToken(expr), Bpl.Expr.False, new IsNonRecursive(), builder.Context)); } else { - List contextDecreases = codeContext.Decreases.Expressions; - List calleeDecreases = e.Function.Decreases.Expressions; if (e.Function == wfOptions.SelfCallsAllowance) { allowance = Expression.CreateBoolLiteral(e.Tok, true); if (!e.Function.IsStatic) { @@ -1272,7 +1270,7 @@ 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(new AssumeCmd(mc.TermLeft.tok, canCalls)); + b.Add(new AssumeCmd(mc.TermLeft.Tok, canCalls)); b.Add(Assert(GetToken(mc.TermLeft), different, new ComprehensionNoAlias(mc.BoundVars, mc.Range, mc.TermLeft, mc.Term), builder.Context)); }); @@ -1390,7 +1388,7 @@ public void CheckSubsetType(ExpressionTranslator etran, Expression expr, Bpl.Exp BoogieStmtListBuilder builder, string comment) { Contract.Assert(resultType != null); - builder.Add(TrAssumeCmd(expr.tok, etran.CanCallAssumption(expr))); + builder.Add(TrAssumeCmd(expr.Tok, etran.CanCallAssumption(expr))); var bResult = etran.TrExpr(expr); CheckSubrange(expr.Tok, bResult, expr.Type, resultType, expr, builder); builder.Add(TrAssumeCmdWithDependenciesAndExtend(etran, expr.Tok, expr, diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs index 01afe150d3b..da0aaa2a16f 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.Wellformedness.cs @@ -49,7 +49,7 @@ public void Check(Function f) { bool splitHappened /*we actually don't care*/ = generator.TrSplitExpr(context, ensures.E, splits, true, functionHeight, true, etran); var (errorMessage, successMessage) = generator.CustomErrorMessage(ensures.Attributes); var canCalls = etran.CanCallAssumption(ensures.E, new CanCallOptions(true, f)); - generator.AddEnsures(ens, generator.FreeEnsures(ensures.E.tok, canCalls, null, true)); + generator.AddEnsures(ens, generator.FreeEnsures(ensures.E.Tok, canCalls, null, true)); foreach (var s in splits) { if (s.IsChecked && !s.Tok.IsInherited(generator.currentModule)) { generator.AddEnsures(ens, generator.EnsuresWithDependencies(s.Tok, false, ensures.E, s.E, errorMessage, successMessage, null)); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs index 577c8035701..1a745c00b4d 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Functions.cs @@ -524,8 +524,8 @@ private Axiom GetFunctionAxiom(Function f, Expression body, List lits) { preReqAxiom = BplAnd(preRA, pre); } - var canCallFuncID = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool); - var useViaCanCall = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(canCallFuncID), Concat(tyargs, args)); + var canCallFuncID = new Bpl.IdentifierExpr(f.Tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool); + var useViaCanCall = new Bpl.NAryExpr(f.Tok, new Bpl.FunctionCall(canCallFuncID), Concat(tyargs, args)); // Add the precondition function and its axiom (which is equivalent to the anteReqAxiom) if (body == null || (RevealedInScope(f) && lits == null)) { @@ -542,9 +542,9 @@ private Axiom GetFunctionAxiom(Function f, Expression body, List lits) { BplForall(forallFormals, trig, BplImp(anteReqAxiom, Bpl.Expr.Eq(appl, preReqAxiom))), "#requires axiom for " + f.FullSanitizedName)); - AddOtherDefinition(precondF, new Bpl.Axiom(f.tok, - BplForall(f.tok, new List(), forallFormals, null, trig, Bpl.Expr.Imp(appl, useViaCanCall)), - "#requires ==> #canCall for " + f.FullSanitizedName)); + AddOtherDefinition(precondF, new Bpl.Axiom(f.Tok, + BplForall(f.Tok, new List(), forallFormals, null, trig, Bpl.Expr.Imp(appl, useViaCanCall)), + "#requires ==> #canCall for " + f.FullSanitizedName)) ; } if (body == null || !RevealedInScope(f)) { @@ -970,7 +970,7 @@ void AddFrameAxiom(Function f) { BplOr(new Bpl.NAryExpr(f.Tok, canCall, f0argsCanCall), fwf0)); */ - var fn = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName, TrType(f.ResultType))); + var fn = new Bpl.FunctionCall(new Bpl.IdentifierExpr(f.Tok, f.FullSanitizedName, TrType(f.ResultType))); var F0 = new Bpl.NAryExpr(f.Tok, fn, f0args); var F1 = new Bpl.NAryExpr(f.Tok, fn, f1args); var eq = BplAnd(Bpl.Expr.Eq(F0, F1), Bpl.Expr.Eq(f0canCall, f1canCall)); diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs index 97fc0887978..b4a0efb67f0 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Methods.cs @@ -1152,7 +1152,7 @@ private void AddFunctionOverrideEnsChk(Function f, BoogieStmtListBuilder builder sub ??= new FunctionCallSubstituter(substMap, typeMap, (TraitDecl)f.OverriddenFunction.EnclosingClass, (TopLevelDeclWithMembers)f.EnclosingClass); var subEn = sub.Substitute(en.E); foreach (var s in TrSplitExpr(new BodyTranslationContext(false), subEn, etran, false, out _).Where(s => s.IsChecked)) { - builder.Add(TrAssumeCmd(f.tok, etran.CanCallAssumption(subEn, cco))); + builder.Add(TrAssumeCmd(f.Tok, etran.CanCallAssumption(subEn, cco))); var constraint = allOverrideEns == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Imp, allOverrideEns, subEn); @@ -1405,10 +1405,10 @@ private Boogie.Axiom FunctionOverrideAxiom(Function f, Function overridingFuncti Bpl.Expr canCallFunc, canCallOverridingFunc; { - var callName = new Bpl.IdentifierExpr(f.tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool); - canCallFunc = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(callName), argsJFCanCall); - callName = new Bpl.IdentifierExpr(overridingFunction.tok, overridingFunction.FullSanitizedName + "#canCall", Bpl.Type.Bool); - canCallOverridingFunc = new Bpl.NAryExpr(f.tok, new Bpl.FunctionCall(callName), argsCFCanCall); + var callName = new Bpl.IdentifierExpr(f.Tok, f.FullSanitizedName + "#canCall", Bpl.Type.Bool); + canCallFunc = new Bpl.NAryExpr(f.Tok, new Bpl.FunctionCall(callName), argsJFCanCall); + callName = new Bpl.IdentifierExpr(overridingFunction.Tok, overridingFunction.FullSanitizedName + "#canCall", Bpl.Type.Bool); + canCallOverridingFunc = new Bpl.NAryExpr(f.Tok, new Bpl.FunctionCall(callName), argsCFCanCall); } // useViaCanCall: C.F#canCall(args) @@ -1518,7 +1518,7 @@ private void AddMethodOverrideEnsChk(Method m, BoogieStmtListBuilder builder, Ex sub ??= new FunctionCallSubstituter(substMap, typeMap, (TraitDecl)m.OverriddenMethod.EnclosingClass, (TopLevelDeclWithMembers)m.EnclosingClass); var subEn = sub.Substitute(en.E); foreach (var s in TrSplitExpr(new BodyTranslationContext(false), subEn, etran, false, out _).Where(s => s.IsChecked)) { - builder.Add(TrAssumeCmd(m.OverriddenMethod.tok, etran.CanCallAssumption(subEn))); + builder.Add(TrAssumeCmd(m.OverriddenMethod.Tok, etran.CanCallAssumption(subEn))); var constraint = allOverrideEns == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Imp, allOverrideEns, subEn); @@ -1549,7 +1549,7 @@ private void AddMethodOverrideReqsChk(Method m, BoogieStmtListBuilder builder, E //generating class pre-conditions foreach (var req in ConjunctsOf(m.Req)) { foreach (var s in TrSplitExpr(new BodyTranslationContext(false), req.E, etran, false, out _).Where(s => s.IsChecked)) { - builder.Add(TrAssumeCmd(m.tok, etran.CanCallAssumption(req.E))); + builder.Add(TrAssumeCmd(m.Tok, etran.CanCallAssumption(req.E))); var constraint = allTraitReqs == null ? null : new BinaryExpr(Token.NoToken, BinaryExpr.Opcode.Imp, allTraitReqs, req.E); @@ -1829,7 +1829,7 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { var comment = "user-defined preconditions"; foreach (var p in ConjunctsOf(m.Req)) { var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes); - req.Add(FreeRequires(p.E.tok, etran.CanCallAssumption(p.E), comment, true)); + req.Add(FreeRequires(p.E.Tok, etran.CanCallAssumption(p.E), comment, true)); comment = null; if (p.Label != null && kind == MethodTranslationKind.Implementation) { // don't include this precondition here, but record it for later use @@ -1852,7 +1852,7 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) { // assume can-call conditions for the modifies clause comment = "user-defined frame expressions"; foreach (var frameExpression in m.Mod.Expressions) { - req.Add(FreeRequires(frameExpression.tok, etran.CanCallAssumption(frameExpression.E), comment, true)); + req.Add(FreeRequires(frameExpression.Tok, etran.CanCallAssumption(frameExpression.E), comment, true)); comment = null; } diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs index b7439227618..29c71119c50 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.Types.cs @@ -1011,7 +1011,7 @@ void AddRedirectingTypeDeclAxioms(bool generateIsAlloc, T dd, string fullName } var comment = $"$IsAlloc axiom for {dd.WhatKind} {fullName}"; - var axiom = new Bpl.Axiom(dd.tok, BplForall(vars, BplTrigger(isAlloc), body), comment); + var axiom = new Bpl.Axiom(dd.Tok, BplForall(vars, BplTrigger(isAlloc), body), comment); AddOtherDefinition(GetOrCreateTypeConstructor(dd), axiom); } else { diff --git a/Source/DafnyCore/Verifier/BoogieGenerator.cs b/Source/DafnyCore/Verifier/BoogieGenerator.cs index 2b04b9ae00e..3c4d08f915c 100644 --- a/Source/DafnyCore/Verifier/BoogieGenerator.cs +++ b/Source/DafnyCore/Verifier/BoogieGenerator.cs @@ -2121,7 +2121,7 @@ void CheckFrameSubset(IOrigin tok, List calleeFrame, foreach (var frameExpression in calleeFrame) { var e = substMap != null ? Substitute(frameExpression.E, receiverReplacement, substMap) : frameExpression.E; - makeAssume(frameExpression.tok, etran.CanCallAssumption(e)); + makeAssume(frameExpression.Tok, etran.CanCallAssumption(e)); } // emit: assert (forall o: ref, f: Field :: o != null && $Heap[o,alloc] && (o,f) in subFrame ==> enclosingFrame[o,f]); diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs index ae6b6b13271..a9a21921f4d 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrForallStmt.cs @@ -547,7 +547,7 @@ void TrForallProof(ForallStmt forallStmt, BoogieStmtListBuilder definedness, Boo // check that postconditions hold foreach (var ens in ConjunctsOf(forallStmt.Ens)) { - definedness.Add(TrAssumeCmd(ens.E.tok, etran.CanCallAssumption(ens.E))); + definedness.Add(TrAssumeCmd(ens.E.Tok, etran.CanCallAssumption(ens.E))); foreach (var split in TrSplitExpr(definedness.Context, ens.E, etran, true, out var splitHappened)) { if (split.IsChecked) { diff --git a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs index 742441f60c1..90471ae557e 100644 --- a/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs +++ b/Source/DafnyCore/Verifier/Statements/BoogieGenerator.TrLoop.cs @@ -259,7 +259,7 @@ void TrLoop(LoopStmt loop, Expression Guard, BodyTranslator/*?*/ bodyTr, } // check definedness of decreases clause foreach (Expression e in theDecreases) { - builder.Add(TrAssumeCmd(e.tok, Bpl.Expr.Imp(w, etran.CanCallAssumption(e)))); + builder.Add(TrAssumeCmd(e.Tok, Bpl.Expr.Imp(w, etran.CanCallAssumption(e)))); TrStmt_CheckWellformed(e, invDefinednessBuilder, locals, etran, true); } if (codeContext is IMethodCodeContext) { @@ -373,7 +373,7 @@ void TrLoop(LoopStmt loop, Expression Guard, BodyTranslator/*?*/ bodyTr, initDecrsDafny.Add(eInit); decrs.Add(etran.TrExpr(e)); // need to add can calls again because the actual loop body updates the variables - loopBodyBuilder.Add(TrAssumeCmd(e.tok, BplImp(w, etran.CanCallAssumption(e)))); + loopBodyBuilder.Add(TrAssumeCmd(e.Tok, BplImp(w, etran.CanCallAssumption(e)))); } if (includeTerminationCheck) { AddComment(loopBodyBuilder, loop, "loop termination check");