EXPERIMENT: emit the wrapper unconditionally, so exit binds at runtime too

ISHIKAWA: why did exit injection still need compile-time knowledge? Because the
body-helper wrapper was only emitted when codegen already knew an exit
construct existed. The wrapper being conditional was the cause, not the wrapper
being necessary.

PREDICTIONS AND RESULTS
  P1 exit becomes runtime-bindable                    TRUE  returns 14, bound
                                                            after the build
  P2 codegen shrinks                                  TRUE  5094 -> 5044
  P3 cost 5-15% from a call frame on every fn         FALSE 0.37s -> 0.38s, ~3%
  P4 fixpoint holds                                   TRUE

Every fn now gets a body helper and a wrapper. It has to be unconditional:
early returns must route through something for an exit construct to observe
them, and codegen cannot know which fns will be bound after the binary exists.

Removed with the machinery: declare_exit, decorator_exit, cg_exit_target,
cg_exit_construct, and the injects_at_exit scanner branch.

Two controls failed and were rewritten rather than repaired --
no-exit-construct-emits-no-wrapper asserted the optimisation this removes, so
it is now inverted. The integration harness gained a seventh assertion: an exit
construct declared after the build replaces the result.

99/99 native, 7/7 integration, fixpoint gen2==gen3.
This commit is contained in:
bigmerge
2026-08-17 09:01:55 -05:00
parent 24f7fb5143
commit 285166c25c
3 changed files with 39 additions and 72 deletions
+29 -17
View File
@@ -799,25 +799,8 @@ test "undeclared-guard-emits-nothing" {
// Declared constructs: exit injection and composition
test "declared-exit-wraps-body-so-early-returns-pass-through" {
let src: String = "@decorator(\"injects_at_exit\", \"persist_now\")\nfn durable() {}\n@durable\nfn mutate(k: String) -> Int { if str_eq(k, \"bad\") { return 0 } return 1 }"
let out: String = compile_capture(src)
assert str_contains(out, "static el_val_t __el_body_mutate"), "body moves into a helper"
assert str_contains(out, "persist_now(EL_STR(\"mutate\")"), "exit injection runs"
assert str_contains(out, "return __r;"), "the wrapper returns the body's result"
}
test "declared-exit-receives-the-result" {
let src: String = "@decorator(\"injects_at_exit\", \"persist_now\")\nfn durable() {}\n@durable\nfn f() -> Int { return 1 }"
let out: String = compile_capture(src)
assert str_contains(out, "__r);"), "the exit target is handed what the fn returned"
}
test "no-exit-construct-emits-no-wrapper" {
let src: String = "@manager\nfn f() -> Int { return 1 }"
let out: String = compile_capture(src)
assert !str_contains(out, "__el_body_"), "fns without an exit construct are unwrapped, byte for byte as before"
}
// Declared constructs: wraps and prohibitions
@@ -896,3 +879,32 @@ test "seam-is-emitted-for-undecorated-fns" {
let out: String = compile_capture(src)
assert str_contains(out, "el_seam_run(EL_STR(\"plain\")"), "any fn is bindable later, decorated or not"
}
// Exit crossings resolve at runtime too
//
// The wrapper is now UNCONDITIONAL. It has to be: early returns must route
// through something for an exit construct to see them, and codegen cannot know
// which fns will be bound after the binary exists. Measured cost of always
// emitting it: 0.37s -> 0.38s across ten self-compiles.
test "every-fn-gets-a-body-helper-and-wrapper" {
let src: String = "fn plain(k: Int) -> Int { if k > 0 { return 1 } return 2 }"
let out: String = compile_capture(src)
assert str_contains(out, "static el_val_t __el_body_plain"), "the body is a helper"
assert str_contains(out, "el_val_t plain(el_val_t k) {"), "the visible fn is a wrapper"
}
test "exit-crossing-goes-through-the-seam" {
let src: String = "fn f() -> Int { return 1 }"
let out: String = compile_capture(src)
assert str_contains(out, "__r = el_seam_run(EL_STR(\"f\"), 1, __r);"), "the exit crossing is resolved at execution and may replace the result"
}
test "early-returns-route-through-the-exit-seam" {
let src: String = "fn early(k: Int) -> Int { if k > 0 { return 99 } return 1 }"
let out: String = compile_capture(src)
let helper: Int = str_index_of(out, "__el_body_early")
let seam: Int = str_index_of(out, "el_seam_run(EL_STR(\"early\"), 1")
assert helper < seam, "the early return is inside the helper, so it passes through the exit seam"
}