-
Notifications
You must be signed in to change notification settings - Fork 285
Support multiple canVerifies having the same origin #6263
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
Support multiple canVerifies having the same origin #6263
Conversation
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.
Hugely appreciate the help completing the solution.
On testing, yes I think we should have at LEAST one binary file that triggers the issues we were hitting, which I think is easier than creating the program programmatically (and provides more binary format coverage to boot). It's probably easiest to whittle down the temp.dbin I gave you. I tried to build one up from a small example and manually changing locations but it wasn't immediately reproducing the problems.
Beyond that (not blocking this PR going in), we could also consider mutating a subset of the integration test suite to change their source locations and ensure the verification behavior is the same. Even if the current suite passes with these changes, I still worry there are other cases where verification doesn't work correctly on the binary format.
|
|
||
| return result.GroupBy(t => ((IOrigin)t.ScopeToken).GetFilePosition()).ToDictionary( | ||
| return result.GroupBy(t => { | ||
| var dafnyToken = (CanVerifyOrigin)t.ScopeToken; |
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 think overall this is a good approach, because although I think we could easily have edge cases where we could have a verification task with a ScopeToken that isn't attached to a ICanVerify somehow, that will be a hard casting exception here so it won't be a silent failure.
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 think the more likely bad scenario is that the ScopeToken is attached to an ICanVerify, but not one that Dafny's front-end found, like a by-method or something nested inside an iterator, but I don't see a way to detect that scenario.
You want to at some point ask: did I assign every created verification task to an ICanVerify? But I don't think we have a good time to ask that question.
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.
Agreed, I'm still not resting easy that there isn't still a bad edge case in here. But this PR is definitely a big step in the right direction.
| tasksForModule.GetValueOrDefault(canVerify) ?? | ||
| new List<IVerificationTask>(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.
I think these existing fallbacks to an empty list of verification tasks here (either because it's not in the dictionary or mapped to null) is the most dangerous part remaining. That's where we get silently failing to verify verifiable things. Does everything work if we take this out and just crash if the canVerify isn't mapped to anything?
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.
An ICanVerify that produces no tasks, like a method without any assertions, seems like a valid happy case, so I don't think we can take this out.
|
|
||
| namespace Microsoft.Dafny { | ||
|
|
||
| class CanVerifyOrigin : OriginWrapper { |
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.
Worth a comment why this exists.
| return false; | ||
| var result = false; | ||
| foreach (var canVerify in canVerifies) { | ||
| result |= await VerifyCanVerify(canVerify, taskFilter ?? (_ => true), randomSeed, onlyPrepareVerificationForGutterTests); |
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 don't think this return value makes sense. Before a boolean made sense because it was saying "is there something verifiable at this location", and CliCompilation.VerifyAllLazily was using that to determine whether to track the canVerify for later. Now that caller is using VerifyCanVerify instead, and the only caller left that actually looks at the result value is VerificationHandler.Handle(VerificationParams), and that method's boolean result is meant to mean "has this request been handled", which should be true when we get here no matter what.
TL;DR I think the return type of VerifyLocation should be Task now instead of Task<bool>.
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.
It will still return false if this canVerify is currently already being verified or has already been verified, or if options specify that it should not be verified.
and that method's boolean result is meant to mean "has this request been handled"
I don't think so. I think it means to tell the client whether this thing started verifying or not.
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.
Okay I'll trust your greater familiarity on this. I was only wary because I tried exactly this version myself and it didn't function correctly, but I also didn't replace VerifyLocation with VerifyCanVerify as you did.
I'd rather not have a binary file in the tests since I want to be able to change the format without breaking tests. Maybe we can have a tests that parses a .dfy file, resets all the source locations, and then continues. |
…afny into fixMultipleSameOrigins
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.
Not fully satisfied we're completely out of danger, but this is definitely a big improvement at least! No comments are blocking, just good for the next related PR.
| public ClassDecl(IOrigin origin, Name nameNode, Attributes? attributes, | ||
| List<TypeParameter> typeArgs, ModuleDefinition enclosingModuleDefinition, | ||
| [Captured] List<MemberDecl> members, List<Type> traits, bool isRefining) | ||
| [Captured] List<MemberDecl> members, List<Type>? traits, bool isRefining) |
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.
Not blocking, but seems like this shouldn't be nullable.
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.
Down the line there's this code Traits = traits ?? [], but I'll remove it in a follow-up since it's a bit ugly.
| new StreamWriter(file2.FullName)), | ||
| inputArgument, outputArgument); | ||
|
|
||
| var deleteSourcesOption = new Option<bool>("--delete-sources"); |
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.
Option is worth a docstring even just to understand better when reading the source here.
Name makes me think we deleting source code though, perhaps --delete-source-locations?
| } | ||
|
|
||
| if (current is NodeWithOrigin withOrigin && withOrigin.Origin != Token.NoToken) { | ||
| var token = new Token(-1, -1) { Uri = withOrigin.Origin.Uri }; |
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.
Good bang for the buck coverage with this approach. For the record I'm still a bit worried the hanging I observed when locations were different but overlapped isn't covered.
| return 1 + 2; | ||
| } | ||
|
|
||
| // opaque predicate P() { 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.
Worth a comment explaining why these are here (I think it's "these are good to test too but they aren't working correctly yet"?)
Or would it be possible to uncomment them anyway? You made a bunch of nullability declaration changes and updated the schema, but those new cases for the binary format aren't being tested otherwise AFAICT.
|
|
||
| return result.GroupBy(t => ((IOrigin)t.ScopeToken).GetFilePosition()).ToDictionary( | ||
| return result.GroupBy(t => { | ||
| var dafnyToken = (CanVerifyOrigin)t.ScopeToken; |
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.
Agreed, I'm still not resting easy that there isn't still a bad edge case in here. But this PR is definitely a big step in the right direction.
| return false; | ||
| var result = false; | ||
| foreach (var canVerify in canVerifies) { | ||
| result |= await VerifyCanVerify(canVerify, taskFilter ?? (_ => true), randomSeed, onlyPrepareVerificationForGutterTests); |
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.
Okay I'll trust your greater familiarity on this. I was only wary because I tried exactly this version myself and it didn't function correctly, but I also didn't replace VerifyLocation with VerifyCanVerify as you did.
What was changed?
[SyntaxConstructor], deserialization is still incorrect because it forgets to update the system module. Because I don't want to fix that last issue, and actually the test is fine without those constructs, I commented out those constructs in the test.SyntaxDeserializerGenerator.csrelated to[SyntaxBaseType]How has this been tested?
inputFormatDeleteSources.dfy, which deletes sources after parsing, then serializes, then deserializes and verifiesBy submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.