docs: file issue 0190 (void-failable fall-through leaves error slot uninitialized)
This commit is contained in:
@@ -0,0 +1,71 @@
|
|||||||
|
# 0190 — void failable (`-> !`) implicit fall-through leaves the error slot uninitialized
|
||||||
|
|
||||||
|
**Status:** OPEN
|
||||||
|
|
||||||
|
## Symptom
|
||||||
|
|
||||||
|
A `-> !` (void failable) function that exits by **implicit fall-through**
|
||||||
|
(no explicit `return;`) does not initialize its error-channel slot, so a
|
||||||
|
caller (or `main`) reads a non-zero garbage tag and reports a phantom
|
||||||
|
unhandled error.
|
||||||
|
|
||||||
|
- Observed: `main :: () -> ! { print("ok\n"); }` prints `ok` then
|
||||||
|
`error: unhandled error reached main: error.` and exits **1**.
|
||||||
|
- Expected: exit **0** (specs.md §11: "the exit code is `0` for void /
|
||||||
|
`-> !` success"). Adding an explicit trailing `return;` makes it exit 0.
|
||||||
|
|
||||||
|
This is the silent-uninitialized-slot failure mode: the success path
|
||||||
|
should write "no error" into the channel just like an explicit `return;`
|
||||||
|
does, but the fall-through path skips it.
|
||||||
|
|
||||||
|
## Reproduction
|
||||||
|
|
||||||
|
```sx
|
||||||
|
#import "modules/std.sx";
|
||||||
|
|
||||||
|
main :: () -> ! {
|
||||||
|
print("ok\n");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Run: `./zig-out/bin/sx run repro.sx` → prints `ok`, then
|
||||||
|
`error: unhandled error reached main: error.`, exit 1 (should be 0).
|
||||||
|
|
||||||
|
A non-`main` void failable shows the same uninitialized slot downstream:
|
||||||
|
|
||||||
|
```sx
|
||||||
|
#import "modules/std.sx";
|
||||||
|
|
||||||
|
noop :: () -> ! { } // falls through, no `return;`
|
||||||
|
main :: () {
|
||||||
|
noop() catch (e) { print("phantom: {}\n", e); } // fires spuriously
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Workaround (confirms root cause): an explicit `return;` at the end of the
|
||||||
|
`-> !` body initializes the slot and the phantom error disappears.
|
||||||
|
|
||||||
|
## Investigation prompt
|
||||||
|
|
||||||
|
The error channel for a `-> !` function is the last slot of the return
|
||||||
|
aggregate (specs.md §12 ABI). An explicit `return;` lowers to a write of
|
||||||
|
the "no error" sentinel into that slot; the **implicit fall-through** exit
|
||||||
|
path (end of body with no `return`) apparently omits that write, leaving
|
||||||
|
the slot whatever was on the stack.
|
||||||
|
|
||||||
|
Likely area: the function-epilogue / failable-return lowering in
|
||||||
|
`src/ir/lower/` (the path that synthesizes the implicit return for a
|
||||||
|
body that falls off the end — search for where a void/`-> !` function's
|
||||||
|
trailing fall-through is lowered, and where the error slot's "no error"
|
||||||
|
sentinel is written on the explicit-`return;` path). The fix: the
|
||||||
|
implicit fall-through of a failable function must initialize the error
|
||||||
|
slot to "no error" exactly like `return;` does.
|
||||||
|
|
||||||
|
Verification: the two repros above must exit 0 / not fire the catch;
|
||||||
|
`examples/errors/1026-errors-failable-main.sx` (which currently passes
|
||||||
|
only because it ends in `return;`) must keep passing. Add a regression
|
||||||
|
example: a `-> !` function (and a `main :: () -> !`) that succeeds by
|
||||||
|
fall-through with no explicit `return;`.
|
||||||
|
|
||||||
|
(Found by adversarial review during the tuple-syntax-cutover docs pass,
|
||||||
|
commit `989e18b7`. Pre-existing — independent of the tuple change.)
|
||||||
Reference in New Issue
Block a user