You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Dyno: adjust compilerError to interrupt resolution of functions (#27004)
ResolvesCray/chapel-private#7229.
Depends on #26999.
Quoting from that issue: a lot of Chapel production code (including in
library modules) looks like this:
```Chapel
proc foo(param cond) {
if cond then compilerError("This condition should not be met");
codeThatOnlyCompilesIfCondIsFalse();
}
```
These rely on the fact that `codeThatOnlyCompilesIfCondIsFalse()` will
not be resolved if an error is already emitted. This means terminating
resolution of `foo()`, much like we do with `return` 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 of `compilerError`,
which distinguishes functions that call compiler error directly and
functions that simply observe errors emitted by a call.
* __For functions that invoke `compilerError` directly__, this PR makes
use of the changes in #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 on
`ResolvedExpression` called `causedFatalError_`[^1].
* Functions that are meant as "helpers" for invoking `compilerError`
(i.e., those that are ignored using the `depth` argument) are considered
"direct" callers to `compilerError` for the purposes of this phase.
That's because they mostly just serve to augment the `compilerError` in
some way.
* __For invocations of functions that error__, this PR simply marks the
call as `ErroneousType`, without interrupting the resolution of the
current function etc. Thus, if we call `foo()`, and `foo()` issues a
`compilerError`, we print the error and mark `foo()` as `ErroneousType`,
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:
```Chapel
proc foo() {
bar();
return 42;
}
proc bar() {
compilerError("Hello");
compilerWarnning("inside bar, after call to compilerError");
}
var x = foo();
```
Here, the error "Hello" immediately terminates resolution of `bar()` (so
the "inside bar" warning is never printed). This error is issued on the
`bar();` line, but we proceed past this line, and, since the `return 42`
doesn't depend on the call to `bar()`, correctly determine that the `var
x` is an `int`.
## Testing
- [x] dyno tests
- [x] paratest
- [x] spectests still work as before
Reviewed by @benharsh -- thanks!
[^1]: 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 .
0 commit comments