Dyno: adjust compilerError
to interrupt resolution of functions
#27004
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Resolves https://github.com/Cray/chapel-private/issues/7229.
Depends on #26999.
Quoting from that issue: a lot of Chapel production code (including in library modules) looks like this:
These rely on the fact that
codeThatOnlyCompilesIfCondIsFalse()
will not be resolved if an error is already emitted. This means terminating resolution offoo()
, much like we do withreturn
in #26955.This PR implements that.
One conceptual curiosity is that this conflicts with Dyno's existing approach of attempting to continue resolving programs to get partial information. Specifically, expressions that emitted errors are marked with
ErroneousType
, and if the rest of the types can be inferred despite the error, we infer them. This is desirable (partial information is useful in editors, for example), but counter to stopping resolution in its tracks. This PR goes for a two-phase handling ofcompilerError
, which distinguishes functions that call compiler error directly and functions that simply observe errors emitted by a call.compilerError
directly, this PR makes use of the changes in Dyno: unify return, break, throw etc. handling across Resolver, type inference, and split init etc. #26955 to stop resolution in its tracks. This requires some communication between the resolver and the other branch-sensitive passes to inform them of the fatal error, which I achieve using a new flag onResolvedExpression
calledcausedFatalError_
1.compilerError
(i.e., those that are ignored using thedepth
argument) are considered "direct" callers tocompilerError
for the purposes of this phase. That's because they mostly just serve to augment thecompilerError
in some way.ErroneousType
, without interrupting the resolution of the current function etc. Thus, if we callfoo()
, andfoo()
issues acompilerError
, we print the error and markfoo()
asErroneousType
, but (as described above) continue resolving with partial information.This way, I believe we can reconcile the expectation of
compilerError
as terminating resolution, while still allowing us to compute partial information in other contexts. See the program tested by this PR:Here, the error "Hello" immediately terminates resolution of
bar()
(so the "inside bar" warning is never printed). This error is issued on thebar();
line, but we proceed past this line, and, since thereturn 42
doesn't depend on the call tobar()
, correctly determine that thevar x
is anint
.Testing
Reviewed by @benharsh -- thanks!
Footnotes
I have checked and on my machine, this flag does not cause the
ResolvedExpression
type to increaase in size, presumably due to the way that struct is padded. So this does not cause size / memory penalties . ↩