ERR/E1.7: ban return/break/continue/try in defer & onfail bodies

A defer or onfail body runs while the block/function is already exiting, so it
has no target to transfer control to. `raise` was already rejected (E1.3); this
adds the rest of the locked set — `return` / `break` / `continue` / `try`.

In parseStmt, the return/break/continue/try parse sites now call a new
rejectInCleanup() helper, gated on in_onfail_body || in_defer_body (the existing
flags, whose doc-comments already scoped this follow-up). The ban is transitive
through nested catch bodies and loops, but parseLambda clears both flags for the
closure body — a closure is its own function boundary, so a `return` from a
closure created inside a cleanup body stays legal. The diagnostic names the
cleanup kind ("an `onfail`" / "a `defer`").

examples/237-cleanup-body-restrictions.sx covers the rejected forms (exit 1);
six inline parser tests cover each banned exit, the transitive-through-loop
case, the closure-boundary exception, and flag-restore after the defer.

Note: examples/213-canonical-map.sx is the user's uncommitted heterogeneous-
variadic-pack WIP (prints 40 vs expected 42); it fails on the committed parser
too, independent of this change, and is left unstaged.

Gates: zig build, zig build test (288 pass), bash tests/run_examples.sh (all
green except the unrelated 213 WIP).
This commit is contained in:
agra
2026-06-01 01:14:24 +03:00
parent 66740fa95b
commit f9dd965b69
4 changed files with 117 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
// Cleanup-body control-flow restrictions (ERR step E1.7 follow-up). A `defer`
// or `onfail` body runs while the block/function is already exiting, so it has
// no target to transfer control to: `raise` / `try` / `return` / `break` /
// `continue` are all rejected inside one. The ban is transitive through nested
// `catch` bodies and loops, but NOT through a nested closure (its own function
// boundary). `raise` was already banned (E1.3); this adds the other four.
// This file is expected to FAIL compilation (exit 1).
//
// Run: ./zig-out/bin/sx run examples/237-cleanup-body-restrictions.sx
#import "modules/std.sx";
E :: error { Bad }
g :: () -> !E { return; }
f :: () -> !E {
defer { return; } // ERROR: return in defer body
onfail { try g(); } // ERROR: try in onfail body
defer { for 0..1: (i) { break; } } // ERROR: break in defer body (transitive through loop)
onfail e { if e == error.Bad { continue; } } // ERROR: continue in onfail body
try g();
return;
}
main :: () -> s32 { return 0; }