At the moment, lexer panics on some errors.
I think panicking is bad for compiler (it certainly is bad for IDEs), and I'd like to fix it. What's the correct way of doing it?
I would think that I should still report a diagnostic with
Fatal severity, but just avoid actually raising
FatalError seems to hint that, if you produced a
Fatal diagnostics, you must panic.
Should I just produce an
Error? Should I use
FatalError always panics and we want to move away from panicing, should we add a comment saying "don't add new usages of
FatalError and try to remove existing ones"?
IIRC, there's a way to call something like exit_if_errors?
That's probably what you want after emitting just a regular error
Yeah, I think that should work, right? Or is there something I'm missing? Presumably FatalError.raise() could just call abort_if_errors (modulo global session and such, but we can thread it through I think)
That doesn't discriminate between recoverable errors and "oh noes, don't even try to run further phases of the compiler to avoid the flood of cascading errors"
FatalError.raise() could just call abort_if_errors (modulo global session and such, but we can thread it through I think)
It's the other way arround: abort_if_errors calls
I assume in this case there's no choice but to fatally abort?
hm, but you implied FatalError.raise() panics?
which I didn't think was the case
Sorry for the sloppy wording:
FatalError.raise unwinds (via resume unwind, not via panic), and I assume that we don't actually want that behavior for "something's wrong with user's code"
@centril on the contrary, in the lexer's case, we can just print an error, produce a fake token and chug along. However that might trigger cascading errors.
do we need an
::Err token to avoid cascading errors?
cc @Vadim Petrochenkov @Esteban Küber
It wouldnt help in the lexer
Or rather, you need the lexer to return an
Err for cascading errors not to happen
Level::PhaseFatal? Semantically, it is exactly what I need here, but, although this variant exists, it isn't actually used anywhere =/ There's an irony in that
Level::PhaseFatal is the only documented error variant
@Esteban Küber returning
Result is no-go for IDE use-case. IDEs should basically never stop processing. Cascading errors is not a problem if you emit squiggly underlines, and it's important to make at least some sense of the code after the error.
For example, here's how Kotlin deals with unclosed strings:
there's a bunch of errors (8), but they are not that harmful, b/c they are not printed to the terminal, and IDE does useful stuff for the following declaration
I've found returning Results to be super annoying in practice
it seems like sentinel is better overall
@nikomatsakis the approaches seem isomorphic tho? -- at least with
Result you get monad powers
In a compiler I wrote over the last weeks I experimented without sentinels and marked the spans as "already errored" and silenced further warnings on spans with such markings
Result doesn't work well for "log and continue" I think, since you end up writing equivalent of unwrap_or(sentinel) anyway essentially
The approaches may seem isomorphic but, in general, I found it much smoother to have (e.g.) an
Err variant inside of
Ty than to make
Ty always be
Result<Ty, Error> (and so forth for other types). Maybe, if you were thorough, they would both work out roughly the same.
Result is the wrong monad; http://hackage.haskell.org/package/monad-validate-220.127.116.11/docs/Control-Monad-Validate.html seems interesting; Something more
Writer-monad like might be better
@nikomatsakis Yeah I suppose it's a question of biasing towards handling or not handling the error case. Having
::Err inside might also be better for introducing (possibly named) typed holes and whatnot later