-
Notifications
You must be signed in to change notification settings - Fork 38
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
[Re-opening] Improve error recoverability in ToIrListener
#1228
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.
I have a few questions and minor suggestions.
The only blocking change request in my mind is the exception handling I noted.
// another, so it's better not to hide this completely. We should turn this | ||
// back into `assert`s after we feel more confident about it. | ||
console.log( | ||
'ATTENTION: There is some component(s) left on the stack(s) after parsing a module, please report a bug' |
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.
If we are asking for a bug report, I think this should just be an error-level message? WDYT?
quint/src/parsing/ToIrListener.ts
Outdated
if (def.kind !== 'def') { | ||
// only `QuintDef` is allowed in `nondet` expressions | ||
return | ||
} |
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.
Given the comment, should this be an error?
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.
This is already ensured by the parser, so we must already have gathered an error for this that will be reported later.
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.
Sorry if I'm being a bit dense, but if this is ensured by the parser, then should this not be just an assert? Or are you saying that this is not ensured by the parser, but an erroneous node here will already have an error report from the parser?
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.
In general, the latter. So, if we have an assert, the assertion will throw and omit the properly described error.
For this particular case, I'm not sure if/how this case could be reached, since the grammar requires 'nondet' for this rule. But, if somehow this is possible, the reported error still is much better than an assertion - and there must be a reported error.
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.
But, iiuc, if we get here and this is not a def
them something must be broken with the parser? So assert or exceptions seems justified. Otherwise, e.g., what is something changes upstream that removes generation of an error when this is not a def? Then we would proceed silently and without marking any errer, no?
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.
Yes, you are right. We talked about this at the meeting yesterday and @konnov suggested spitting some log lines when these branches are hit. It's not ideal, as it might generate noise on top of the proper error messages for the users, but it would help us spot problems while debugging.
Since we don't have a proper logging facility, I'm just prefixing the logs with [DEBUG]
for now.
Do you think this is a reasonable solution?
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, that sounds good. Thanks.
quint/src/parsing/ToIrListener.ts
Outdated
// if the definition has parameters, introduce a lambda | ||
let body = expr ?? this.undefinedDef(ctx) | ||
const id = this.getId(ctx) | ||
let body = expr ?? this.undefinedExpr(ctx)() |
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.
Perhaps best to put the default where expr
is bound, so that any future changes to this handle that refers to expr
will also get the default value. This is also more consistent with the preceding code.
quint/src/parsing/ToIrListener.ts
Outdated
private undefinedExpr(ctx: any): () => QuintEx { | ||
return () => { | ||
const id = this.getId(ctx) | ||
return { id, kind: 'bool', value: 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.
Should we introduce an UndefinedExpr
node in the IR so we can be very clear about where holes are showing up? I'm worried we could end up with weird bugs where parts of the AST are replaced with true
and it will be hard for maintainers to find out why that would be happening.
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.
Hmm that might be a good idea for the next step. For now, the IR with undefined components should only be manipulated in name resolution. After that, we halt the process and report all errors gathered so far. This is not something guaranteed, but I'm pretty confident that we are ensuring that this is the case.
For the next steps, I want the type checker and effect checker to run over this IR as well, and my original plan was to have a check like: if there is an error for the id of this component, don't try to infer type/effect for the component.
Perhaps introducing undefined components to the IR, as you suggest, is a better alternative than relying on the errors. This would be a bigger change tho, as it requires another case in many IR-related functions and tests.
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 see. Maybe something to consider for a followup, then. But I'm worried this will be confusing and can lead to lost time debugging. Could this be marked by making it named value or something? Like
val __undefinedExprGenerated = true; 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.
I like that idea!
quint/src/parsing/ToIrListener.ts
Outdated
private undefinedDeclaration(ctx: any): () => QuintDeclaration { | ||
return () => { | ||
const id = this.getId(ctx) | ||
return { id, kind: 'assume', name: '_', assumption: this.undefinedExpr(ctx)() } |
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.
Can we name it something like
return { id, kind: 'assume', name: '_', assumption: this.undefinedExpr(ctx)() } | |
return { id, kind: 'assume', name: `_undefinedDecl${id}`, assumption: this.undefinedExpr(ctx)() } |
quint/src/parsing/ToIrListener.ts
Outdated
private undefinedType(ctx: any): () => QuintType { | ||
return () => { | ||
const id = this.getId(ctx) | ||
return { id, kind: 'bool' } | ||
} | ||
} | ||
|
||
private undefinedVariant(ctx: any): () => RowField { | ||
return () => ({ fieldName: '_', fieldType: this.undefinedType(ctx)() }) | ||
} |
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.
Same question about making undefined forms clearer applies here.
if (errors.length === 0) { | ||
throw e | ||
} | ||
// ignore the exception, we already have errors to report |
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.
But what if the exception is not obviously explained by the recorded errors? Then we would end up swallowing unknown exceptions, potentially hiding errors.
Instead, I suggest this catch only apply to errors which are from a known family of errors that we are sure will be reflected in the errors
, and anything else is left to bubble up. In my experience, a catch-all that recovers without a record of the exception inevitably comes back to bite you :D
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.
But what if the exception is not obviously explained by the recorded errors?
It is not, what is happening is that the generated code breaks internally when the AST is malformed (because of parser errors). The idea is: there is something being reported, you should fix that problem first and then try again. If an exception is thrown after that (which should be impossible), then you can worry about the exception.
It should always be the case that the exception is a direct consequence of one of the errors, and that fixing the errors will make the exception go away.
Instead, I suggest this catch only apply to errors which are from a known family of errors that we are sure will be reflected in the
errors
That I cannot do, because I'm introducing this try catch to handle unpredictable (or hard to predict) errors that are internal to the generated code from antlr4ts. Took me some manual testing to find the first case where some error is thrown, and I don't think I could ever find all possible errors that can be thrown.
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'm not following, maybe we can talk about this a bit tomorrow.
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.
Thanks for talking thru the reasoning here. I am still a bit worried this may hide problems at some point, but less so and not enough to block here.
Perhaps the comment could be expanded to explain the reasoning?
new PR for #1223 (which I messed up with a local merge that ruined it forever) - please see that PR's description to understand the changes on this PR.
Also added some further improvements on diagnostics and error reporting, as I experienced some crashing behavior on manual tests last week.