Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Better format in hover messages #3687

Merged
merged 46 commits into from
Jun 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
38568ae
Feat: Better format in hover messages
MikaelMayer Mar 3, 2023
c3c5516
Support for {:error} on hover also on postconditions
MikaelMayer Mar 4, 2023
32cb31d
Review comments
MikaelMayer Mar 6, 2023
05227f6
Review comments: Got rid of patches by integrating better with Proof …
MikaelMayer Mar 6, 2023
60ec8d9
Merge branch 'master' into feat-better-error-messages
MikaelMayer Mar 6, 2023
0aac9fe
Merge branch 'master' into feat-better-error-messages
MikaelMayer Mar 7, 2023
b136d48
Merge branch 'master' into feat-better-error-messages
MikaelMayer Mar 7, 2023
8d2b241
Merge branch 'master' into feat-better-error-messages
MikaelMayer Mar 8, 2023
b831daa
Fixed CI
MikaelMayer Mar 8, 2023
d7a32d3
Ensured EnsuresDescription and PostconditionDescription share relevan…
MikaelMayer Mar 8, 2023
be50afe
Ensure test is deterministic
MikaelMayer Mar 8, 2023
8876a81
Merge branch 'master' into feat-better-error-messages
MikaelMayer Mar 9, 2023
0cd8767
Merge branch 'master' into feat-better-error-messages
MikaelMayer Mar 10, 2023
89daeb4
Merge branch 'master' into feat-better-error-messages
MikaelMayer Mar 10, 2023
9d8d505
Merge branch 'master' into feat-better-error-messages
MikaelMayer Mar 10, 2023
ab390c5
Merge branch 'master' into feat-better-error-messages
MikaelMayer Mar 13, 2023
62abf54
Last review comment
MikaelMayer Mar 13, 2023
fea8f29
Merge remote-tracking branch 'origin/feat-better-error-messages' into…
MikaelMayer Mar 13, 2023
dcbd72f
Fixed latest merge.
MikaelMayer Mar 13, 2023
a0b2b8d
Merge branch 'master' into feat-better-error-messages
MikaelMayer Mar 15, 2023
5d53a3a
Merge branch 'master' into feat-better-error-messages
MikaelMayer Mar 16, 2023
cfc1a1d
Fixed
MikaelMayer Mar 16, 2023
154fdd3
Merge branch 'master' into feat-better-error-messages
MikaelMayer Mar 17, 2023
50e3f26
Fixed the merge
MikaelMayer Mar 17, 2023
38e14f7
Reverted premature rewording
MikaelMayer Mar 17, 2023
616490f
Fixed another one
MikaelMayer Mar 17, 2023
e05ee83
Merge branch 'master' into feat-better-error-messages
MikaelMayer Jun 12, 2023
d15cb17
Fixed merge commit
MikaelMayer Jun 12, 2023
5cef283
Fixed documentation by running "make numbers"
MikaelMayer Jun 12, 2023
15cefb6
Fixed CI tests
MikaelMayer Jun 12, 2023
df6077a
Merge branch 'master' into feat-better-error-messages
MikaelMayer Jun 12, 2023
dea8cb3
Fixed CI calculation step
MikaelMayer Jun 13, 2023
039ba35
Fixed some test cases
MikaelMayer Jun 13, 2023
2be2ff1
Fixed CI
MikaelMayer Jun 13, 2023
84e0e85
Fix error message
MikaelMayer Jun 13, 2023
518194d
Fixed CI
MikaelMayer Jun 13, 2023
ba4099a
Fixed doc
MikaelMayer Jun 13, 2023
138d8eb
Merge branch 'master' into feat-better-error-messages
MikaelMayer Jun 13, 2023
f4af82e
Fixed CI tests again
MikaelMayer Jun 13, 2023
b582fb9
Fixed CI again
MikaelMayer Jun 13, 2023
985d4c8
Fixed last CI error
MikaelMayer Jun 14, 2023
938a17d
Merge branch 'master' into feat-better-error-messages
MikaelMayer Jun 14, 2023
5e2f51e
Self-review
MikaelMayer Jun 14, 2023
cdb2f77
Merge branch 'master' into feat-better-error-messages
keyboardDrummer Jun 20, 2023
815d80c
Merge branch 'master' into feat-better-error-messages
MikaelMayer Jun 20, 2023
76756c1
Merge branch 'master' into feat-better-error-messages
keyboardDrummer Jun 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Source/DafnyCore/AST/Substituter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.Diagnostics.Contracts;
using System.Linq;
using JetBrains.Annotations;
using Microsoft.Boogie.Clustering;

namespace Microsoft.Dafny {
/// <summary>
Expand Down
110 changes: 76 additions & 34 deletions Source/DafnyCore/Verifier/ProofObligationDescription.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using System.Diagnostics.Contracts;
using System.Linq;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.Boogie;

namespace Microsoft.Dafny.ProofObligationDescription;

Expand Down Expand Up @@ -294,57 +294,99 @@ public IsOlderProofObligation(int olderParameterCount, int allParameterCount) {

//// Contract constraints

public class PreconditionSatisfied : ProofObligationDescriptionWithNoExpr {
public abstract class ProofObligationDescriptionCustomMessages : ProofObligationDescriptionWithNoExpr {
protected readonly string customErrMsg;
private readonly string customSuccessMsg;

public override string SuccessDescription =>
customErrMsg is null
? "function precondition satisfied"
: $"error is impossible: {customErrMsg}";
customSuccessMsg ?? DefaultSuccessDescription;

public abstract string DefaultSuccessDescription { get; }
public override string FailureDescription =>
customErrMsg ?? "function precondition might not hold";
customErrMsg ?? DefaultFailureDescription;
public abstract string DefaultFailureDescription { get; }
public ProofObligationDescriptionCustomMessages([CanBeNull] string customErrMsg, [CanBeNull] string customSuccessMsg) {
this.customErrMsg = customErrMsg;
this.customSuccessMsg = customSuccessMsg;
}
}

public override string ShortDescription => "precondition";
public class PreconditionSatisfied : ProofObligationDescriptionCustomMessages {
public override string DefaultSuccessDescription =>
"function precondition satisfied";

private readonly string customErrMsg;
public override string DefaultFailureDescription =>
"function precondition could not be proved";

public PreconditionSatisfied([CanBeNull] string customErrMsg) {
this.customErrMsg = customErrMsg;
public override string ShortDescription => "precondition";

public PreconditionSatisfied([CanBeNull] string customErrMsg, [CanBeNull] string customSuccessMsg)
: base(customErrMsg, customSuccessMsg) {
}
}

public class AssertStatement : ProofObligationDescriptionWithNoExpr {
public override string SuccessDescription =>
customErrMsg is null
? "assertion always holds"
: $"error is impossible: {customErrMsg}";
public class AssertStatement : ProofObligationDescriptionCustomMessages {
public override string DefaultSuccessDescription =>
"assertion always holds";

public override string FailureDescription =>
customErrMsg ?? "assertion might not hold";
public override string DefaultFailureDescription =>
"assertion might not hold";

public override string ShortDescription => "assert statement";

private readonly string customErrMsg;
public AssertStatement([CanBeNull] string customErrMsg, [CanBeNull] string customSuccessMsg)
: base(customErrMsg, customSuccessMsg) {
}
}

public AssertStatement([CanBeNull] string customErrMsg) {
this.customErrMsg = customErrMsg;
// The Boogie version does not support custom error messages yet
public class RequiresDescription : ProofObligationDescriptionCustomMessages {
public override string DefaultSuccessDescription =>
"the precondition always holds";

public override string DefaultFailureDescription =>
"this is the precondition that could not be proved";

public override string ShortDescription => "requires";

public RequiresDescription([CanBeNull] string customErrMsg, [CanBeNull] string customSuccessMsg)
: base(customErrMsg, customSuccessMsg) {
}
}

public class LoopInvariant : ProofObligationDescriptionWithNoExpr {
public override string SuccessDescription =>
customErrMsg is null
? "loop invariant always holds"
: $"error is impossible: {customErrMsg}";
// The Boogie version does not support custom error messages yet
public class EnsuresDescription : ProofObligationDescriptionCustomMessages {
public override string DefaultSuccessDescription =>
"this postcondition holds";

public override string FailureDescription =>
customErrMsg ?? "loop invariant violation";
public override string DefaultFailureDescription =>
"this is the postcondition that could not be proved";

public override string ShortDescription => "loop invariant";
// Same as FailureDescription but used not as a "related" error, but as an error by itself
public string FailureDescriptionSingle =>
customErrMsg ?? "this postcondition could not be proved on a return path";

private readonly string customErrMsg;
public string FailureAtPathDescription =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find the concept of having an at path and a regular failure description not immediately intuitive.

Conceptually I view the verification failure as a stack trace. For example in:

predicate P(x : int) {
  true
}

predicate Q(x : int) {
  x == 2 // Stack(0)
}
method Foo() return (x: int)
  ensures O(x) && Q(x) // Stack(1)
{
  x := 1;
  assert Foo.ensures() // Stack(2)
  return;
}

I think in a previous discussion about squiggly lines, you explained that any part of the stack trace that falls outside of the method definition should not be taken into account, so for failing postconditions that only leaves the bottom 2 stack elements as relevant, and then I see it makes sense to have a failure description for both of those.

Feel free to ignore this comment, just me doing rubber duck analysis.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for rubber duck analysis :-)
Your metaphor with stack trace is quite interesting, worth exploring from UX's perspective. And I think this PR is exploring it that way. Let me explain you why in the next paragraph.

The existence of "nested expressions" here is due only to the condition splitting at the Dafny level, that is, if we want to prove "A && B", it will prove "A" on one side and "A ==> B" (or just "B" where A is assumed actually) on the other side. If we want to prove P(x), we will prove each conjunct of P(x) -- that's also why sometimes it's worth making predicate opaque, because if P(x) has 100 conjuncts, proving P(y) when x == y can still be very costly -
This is why I chose to replace "Could not prove" (which was technically correct, If you can't prove A, you can't prove A && B) by "Inside", to better match the metaphor of the stack.

I think in a previous discussion about squiggly lines, you explained that any part of the stack trace that falls outside of the method definition should not be taken into account,

My point was that they should not be underlined permanently as errors because they are relevant only in the context of the failed assertion.

customErrMsg ?? new PostconditionDescription().FailureDescription;

public LoopInvariant([CanBeNull] string customErrMsg) {
this.customErrMsg = customErrMsg;
public override string ShortDescription => "ensures";

public EnsuresDescription([CanBeNull] string customErrMsg, [CanBeNull] string customSuccessMsg)
: base(customErrMsg, customSuccessMsg) {
}
}

public class LoopInvariant : ProofObligationDescriptionCustomMessages {
public override string DefaultSuccessDescription =>
"loop invariant always holds";

public override string DefaultFailureDescription =>
"loop invariant violation";

public override string ShortDescription => "loop invariant";

public LoopInvariant([CanBeNull] string customErrMsg, [CanBeNull] string customSuccessMsg)
: base(customErrMsg, customSuccessMsg) {
}
}

Expand All @@ -353,7 +395,7 @@ public class CalculationStep : ProofObligationDescriptionWithNoExpr {
"the calculation step between the previous line and this line always holds";

public override string FailureDescription =>
"the calculation step between the previous line and this line might not hold";
"the calculation step between the previous line and this line could not be proved";

public override string ShortDescription => "calc step";
}
Expand Down Expand Up @@ -999,8 +1041,8 @@ public override Expression GetAssertedExpr(DafnyOptions options) {
var bvarsExprs = bvars.Select(bvar => new IdentifierExpr(bvar.tok, bvar)).ToList();
var bvarprimes = bvars.Select(bvar => new BoundVar(bvar.tok, bvar.Name + "'", bvar.Type)).ToList();
var bvarprimesExprs = bvarprimes.Select(bvar => new IdentifierExpr(bvar.tok, bvar) as Expression).ToList();
var subContract = new Substituter(null,
bvars.Zip(bvarprimesExprs).ToDictionary<(BoundVar, Expression), IVariable, Expression>(
var subContract = new Substituter(null, Enumerable.Zip(
bvars, bvarprimesExprs).ToDictionary<(BoundVar, Expression), IVariable, Expression>(
item => item.Item1, item => item.Item2),
new Dictionary<TypeParameter, Type>()
);
Expand Down
32 changes: 16 additions & 16 deletions Source/DafnyCore/Verifier/Translator.ClassMembers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -889,13 +889,13 @@ private void AddFunctionOverrideCheckImpl(Function f) {
// the procedure itself
var req = new List<Boogie.Requires>();
// free requires fh == FunctionContextHeight;
req.Add(Requires(f.tok, true, etran.HeightContext(f), null, null));
req.Add(Requires(f.tok, true, etran.HeightContext(f), null, null, 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, BplAnd(a0, BplAnd(a1, a2)), null, null));
req.Add(Requires(f.tok, true, BplAnd(a0, BplAnd(a1, a2)), null, null, null));
}
// modifies $Heap
var mod = new List<Boogie.IdentifierExpr> {
Expand Down Expand Up @@ -1439,13 +1439,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, etran.HeightContext(m), null, null));
req.Add(Requires(m.tok, true, etran.HeightContext(m), null, null, 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, BplAnd(a0, BplAnd(a1, a2)), null, null));
req.Add(Requires(m.tok, true, BplAnd(a0, BplAnd(a1, a2)), null, null, null));
}
}
if (m is TwoStateLemma) {
Expand All @@ -1457,7 +1457,7 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) {
var pIdx = m.Ins.Count == 1 ? "" : " at index " + index;
var desc = new PODesc.IsAllocated($"parameter{pIdx} ('{formal.Name}')", "in the two-state lemma's previous state");
var require = Requires(formal.tok, false, MkIsAlloc(etran.TrExpr(dafnyFormalIdExpr), formal.Type, prevHeap),
desc.FailureDescription, null);
desc.FailureDescription, desc.SuccessDescription, null);
require.Description = desc;
req.Add(require);
}
Expand All @@ -1472,7 +1472,7 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) {
// USER-DEFINED SPECIFICATIONS
var comment = "user-defined preconditions";
foreach (var p in m.Req) {
string errorMessage = CustomErrorMessage(p.Attributes);
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
p.Label.E = (m is TwoStateLemma ? ordinaryEtran : etran.Old).TrExpr(p.E);
Expand All @@ -1483,7 +1483,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(Requires(s.Tok, s.IsOnlyFree, s.E, errorMessage, comment));
req.Add(Requires(s.Tok, s.IsOnlyFree, s.E, errorMessage, successMessage, comment));
comment = null;
// the free here is not linked to the free on the original expression (this is free things generated in the splitting.)
}
Expand All @@ -1492,8 +1492,8 @@ private Boogie.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));
var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes);
AddEnsures(ens, Ensures(p.E.tok, true, CanCallAssumption(p.E, etran), errorMessage, successMessage, comment));
comment = null;
foreach (var s in TrSplitExprForMethodSpec(p.E, etran, kind)) {
var post = s.E;
Expand All @@ -1506,16 +1506,16 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) {
} else if (s.IsOnlyChecked && !bodyKind) {
// don't include in split
} else {
AddEnsures(ens, Ensures(s.Tok, s.IsOnlyFree || this.assertionOnlyFilter != null, post, errorMessage, null));
AddEnsures(ens, Ensures(s.Tok, s.IsOnlyFree || this.assertionOnlyFilter != null, post, errorMessage, successMessage, null));
}
}
}
if (m is Constructor && kind == MethodTranslationKind.Call) {
var fresh = Boogie.Expr.Not(etran.Old.IsAlloced(m.tok, new Boogie.IdentifierExpr(m.tok, "this", TrReceiverType(m))));
AddEnsures(ens, Ensures(m.tok, false || this.assertionOnlyFilter != null, fresh, null, "constructor allocates the object"));
AddEnsures(ens, Ensures(m.tok, false || this.assertionOnlyFilter != null, fresh, null, null, "constructor allocates the object"));
}
foreach (BoilerplateTriple tri in GetTwoStateBoilerplate(m.tok, m.Mod.Expressions, m.IsGhost, m.AllowsAllocation, ordinaryEtran.Old, ordinaryEtran, ordinaryEtran.Old)) {
AddEnsures(ens, Ensures(tri.tok, tri.IsFree || this.assertionOnlyFilter != null, tri.Expr, tri.ErrorMessage, tri.Comment));
AddEnsures(ens, Ensures(tri.tok, tri.IsFree || this.assertionOnlyFilter != null, tri.Expr, tri.ErrorMessage, tri.SuccessMessage, tri.Comment));
}

// add the fuel assumption for the reveal method of a opaque method
Expand All @@ -1533,11 +1533,11 @@ private Boogie.Procedure AddMethod(Method m, MethodTranslationKind kind) {
Boogie.Expr layer = etran.layerInterCluster.LayerN(1, moreFuel_expr);
Boogie.Expr layerAssert = etran.layerInterCluster.LayerN(2, moreFuel_expr);

AddEnsures(ens, Ensures(m.tok, true, Boogie.Expr.Eq(startFuel, layer), null, null));
AddEnsures(ens, Ensures(m.tok, true, Boogie.Expr.Eq(startFuelAssert, layerAssert), null, null));
AddEnsures(ens, Ensures(m.tok, true, GetRevealConstant(f), null, null));
AddEnsures(ens, Ensures(m.tok, true, Boogie.Expr.Eq(startFuel, layer), null, null, null));
AddEnsures(ens, Ensures(m.tok, true, Boogie.Expr.Eq(startFuelAssert, layerAssert), null, null, null));
AddEnsures(ens, Ensures(m.tok, true, GetRevealConstant(f), null, null, null));

AddEnsures(ens, Ensures(m.tok, true, Boogie.Expr.Eq(FunctionCall(f.tok, BuiltinFunction.AsFuelBottom, null, moreFuel_expr), moreFuel_expr), null, "Shortcut to LZ"));
AddEnsures(ens, Ensures(m.tok, true, Boogie.Expr.Eq(FunctionCall(f.tok, BuiltinFunction.AsFuelBottom, null, moreFuel_expr), moreFuel_expr), null, null, "Shortcut to LZ"));
}
}
}
Expand Down
10 changes: 5 additions & 5 deletions Source/DafnyCore/Verifier/Translator.ExpressionWellformed.cs
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Bpl.Expr re
if (!fnCoreType.IsArrowTypeWithoutPreconditions) {
// check precond
var precond = FunctionCall(e.tok, Requires(arity), Bpl.Type.Bool, args);
builder.Add(Assert(GetToken(expr), precond, new PODesc.PreconditionSatisfied(null)));
builder.Add(Assert(GetToken(expr), precond, new PODesc.PreconditionSatisfied(null, null)));
}

if (wfOptions.DoReadsChecks && !fnCoreType.IsArrowTypeWithoutReadEffects) {
Expand Down Expand Up @@ -698,16 +698,16 @@ void CheckWellformedWithResult(Expression expr, WFOptions wfOptions, Bpl.Expr re
// 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());
string errorMessage = CustomErrorMessage(p.Attributes);
var (errorMessage, successMessage) = CustomErrorMessage(p.Attributes);
foreach (var ss in TrSplitExpr(precond, etran, true, out var splitHappened)) {
if (ss.IsChecked) {
var tok = new NestedToken(GetToken(expr), ss.Tok);
var desc = new PODesc.PreconditionSatisfied(errorMessage);
var desc = new PODesc.PreconditionSatisfied(errorMessage, successMessage);
if (wfOptions.AssertKv != null) {
// use the given assert attribute only
builder.Add(Assert(tok, ss.E, new PODesc.PreconditionSatisfied(errorMessage), wfOptions.AssertKv));
builder.Add(Assert(tok, ss.E, new PODesc.PreconditionSatisfied(errorMessage, successMessage), wfOptions.AssertKv));
} else {
builder.Add(AssertNS(tok, ss.E, new PODesc.PreconditionSatisfied(errorMessage)));
builder.Add(AssertNS(tok, ss.E, new PODesc.PreconditionSatisfied(errorMessage, successMessage)));
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions Source/DafnyCore/Verifier/Translator.TrStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ private void TrPredicateStmt(PredicateStmt stmt, BoogieStmtListBuilder builder,
Contract.Requires(etran != null);

var stmtBuilder = new BoogieStmtListBuilder(this, options);
string errorMessage = CustomErrorMessage(stmt.Attributes);
var (errorMessage, successMessage) = 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;
Expand Down Expand Up @@ -548,14 +548,14 @@ private void TrPredicateStmt(PredicateStmt stmt, BoogieStmtListBuilder builder,
var ss = TrSplitExpr(stmt.Expr, etran, true, out var splitHappened);
if (!splitHappened) {
var tok = enclosingToken == null ? GetToken(stmt.Expr) : new NestedToken(enclosingToken, GetToken(stmt.Expr));
var desc = new PODesc.AssertStatement(errorMessage);
var desc = new PODesc.AssertStatement(errorMessage, successMessage);
(proofBuilder ?? b).Add(Assert(tok, etran.TrExpr(stmt.Expr), desc, 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.Tok);
var desc = new PODesc.AssertStatement(errorMessage);
var desc = new PODesc.AssertStatement(errorMessage, successMessage);
(proofBuilder ?? b).Add(AssertNS(ToDafnyToken(tok), split.E, desc, stmt.Tok,
etran.TrAttributes(stmt.Attributes, null))); // attributes go on every split
}
Expand Down Expand Up @@ -1405,20 +1405,20 @@ void TrLoop(LoopStmt s, Expression Guard, BodyTranslator/*?*/ bodyTr,
}
BoogieStmtListBuilder invDefinednessBuilder = new BoogieStmtListBuilder(this, options);
foreach (AttributedExpression loopInv in s.Invariants) {
string errorMessage = CustomErrorMessage(loopInv.Attributes);
var (errorMessage, successMessage) = CustomErrorMessage(loopInv.Attributes);
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))));
var ss = TrSplitExpr(loopInv.E, etran, false, out var splitHappened);
if (!splitHappened) {
var wInv = Bpl.Expr.Imp(w, etran.TrExpr(loopInv.E));
invariants.Add(Assert(loopInv.E.tok, wInv, new PODesc.LoopInvariant(errorMessage)));
invariants.Add(Assert(loopInv.E.tok, wInv, new PODesc.LoopInvariant(errorMessage, successMessage)));
} else {
foreach (var split in ss) {
var wInv = Bpl.Expr.Binary(split.E.tok, BinaryOperator.Opcode.Imp, w, split.E);
if (split.IsChecked) {
invariants.Add(Assert(split.Tok, wInv, new PODesc.LoopInvariant(errorMessage))); // TODO: it would be fine to have this use {:subsumption 0}
invariants.Add(Assert(split.Tok, wInv, new PODesc.LoopInvariant(errorMessage, successMessage))); // TODO: it would be fine to have this use {:subsumption 0}
} else {
invariants.Add(TrAssumeCmd(split.E.tok, wInv));
}
Expand Down
Loading