-
Notifications
You must be signed in to change notification settings - Fork 262
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
Implement decreases to
expressions
#5367
Conversation
Things remaining: * Better syntax * Fill in error messages * Ensure correct parameters to DecreasesCheck * Figure out why most proof obligation descriptions aren't really useful
This has shortcomings, and in particular is only allowed in `assert` statements at the moment. Making it fully general may require some more parser refactoring.
Having looked only at the test, I like the design so far. Could you also include something like ambiguous parsing tests, such as tuples |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a couple of instances where you can remove whitespace, but I'm mostly happy with the rest. Unfortunately, I'm not of much help when it comes to the parser.
docs/DafnyRef/Expressions.md
Outdated
When proving that a loop or recursive callable terminates, Dafny | ||
automatically generates a proof obligation that the sequence of | ||
expressions listed in a `decreases` clause gets smaller (in the | ||
termination ordering) with each iteration or recursive call. Normally, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add that it's a lexicographical ordering?
DecreasesTo2.dfy(5,24): Error: invalid UnaryExpression | ||
DecreasesTo2.dfy(9,21): Error: ident expected | ||
DecreasesTo2.dfy(9,12): Error: expected 'to' | ||
DecreasesTo2.dfy(13,10): Error: invalid ParensExpression |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would love to see more helpful error messages, but they're just as good as the ones for other operators, so that's what we probably have to settle for.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I agree, on both sides.
|
||
/* | ||
method BadTypes () { | ||
assert (true decreases to 0); | ||
} | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/* | |
method BadTypes () { | |
assert (true decreases to 0); | |
} | |
*/ |
} | ||
|
||
method BadInstance5(i: int, b: bool) { | ||
assert (i decreases to b); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't the resolver catch this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I originally thought so, too, but I think there's a good reason for it not to. When Dafny compares two tuples of expressions for termination ordering, it requires only that a prefix of those expression even be comparable. So something like x, 0 decreases to x-1, false
should succeed (and does, in the current implementation -- maybe I should add a test showing that). To make this general, the comparable prefix can be empty, in which case the decreases to
expression will always be false.
@@ -1,4 +1,6 @@ | |||
namespace Microsoft.Dafny.LanguageServer.Language { | |||
using Microsoft.Boogie; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using Microsoft.Boogie; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this one is actually used.
@@ -1165,6 +1166,13 @@ public partial class ModuleResolver { | |||
|
|||
} else if (expr is NestedMatchExpr nestedMatchExpr) { | |||
ResolveNestedMatchExpr(nestedMatchExpr, resolutionContext); | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} else if (expr is DecreasesToExpr decreasesToExpr) { | ||
var comma = false; | ||
foreach (var oldExpr in decreasesToExpr.OldExpressions) { | ||
if (comma) { | ||
wr.Write(", "); | ||
} | ||
PrintExpression(oldExpr, false); | ||
comma = true; | ||
} | ||
if (decreasesToExpr.AllowNoChange) { | ||
wr.Write(" nonincreases to "); | ||
} else { | ||
wr.Write(" decreases to "); | ||
} | ||
comma = false; | ||
foreach (var newExpr in decreasesToExpr.NewExpressions) { | ||
if (comma) { | ||
wr.Write(", "); | ||
} | ||
PrintExpression(newExpr, false); | ||
comma = true; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we test this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep. See the Asserted expression:
lines in the added tests.
@@ -144,6 +144,7 @@ TOKENS | |||
by = "by". | |||
in = "in". | |||
decreases = "decreases". | |||
nonincreases = "nonincreases". |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that it's there, we might as well keep it, but was there a strong reason to add it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sometimes the check that Dafny needs to perform is, roughly speaking, <= instead of <. To be able to express those conditions, it seemed most natural to have a form of the expression that encoded <= instead of <. It would be possible, alternatively, to have (x decreases to y) || x == y
, but it seems a little more awkward, and I think would be more difficult to implement, as well (though I'm no longer sure of that).
Source/DafnyCore/Dafny.atg
Outdated
@@ -2919,7 +2920,7 @@ AssertStmt<out Statement/*!*/ s> | |||
BlockStmt<out proof, out proofStart, out proofEnd> | |||
| ";" | |||
| (. | |||
SemErr(ErrorId.p_assert_needs_by_or_semicolon, t, "expected either 'by' or a semicolon following the assert expression"); | |||
SemErr(ErrorId.p_assert_needs_by_or_semicolon, t, $"expected either 'by' or a semicolon following the assert expression"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SemErr(ErrorId.p_assert_needs_by_or_semicolon, t, $"expected either 'by' or a semicolon following the assert expression"); | |
SemErr(ErrorId.p_assert_needs_by_or_semicolon, t, "expected either 'by' or a semicolon following the assert expression"); |
Source/DafnyCore/Dafny.atg
Outdated
Expression<out Expression e, bool allowLemma, bool allowLambda, bool allowBitwiseOps = true> | ||
= | ||
NotDecreasesToExpression<out e, allowLemma, allowLambda, allowBitwiseOps> | ||
. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no idea how to improve on that, but making this the top level seems like a big change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, and now that you mention it this change isn't necessary anymore. I made it to help with switching certain things back and forth during debugging, but in the final version I don't think it's necessary. I'll try to remove it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me!
Description
Implement the
decreases to
andnonincreases to
expression forms. This allows you to state that the left-hand sequence of expressions decreases (or at least doesn't increase) to the right-hand sequence.One caveat: for now,
decreases to
is allowed only inside parentheses, to avoid parsing ambiguities. This is a strict subset of what we'd ideally like to support. I think it should be possible to allow it to appear elsewhere as part of a separate, backward-compatible PR.Fixes #5252
How has this been tested?
Added
dafny0/DecreasesTo{1,2,3,4,5}.dfy
By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.