give a construct its after-crossing face, and let constructs compose
§6 records 62 persist-after-mutate sites, 10 auth-per-route, and
index-after-append that failed at 9 of 9 — every one an obligation at a
crossing that decayed into "remember to do this afterwards." An obligation a
human must remember is not an obligation, and the 9-of-9 figure is what that
costs.
@decorator("injects_at_exit", "persist_now")
fn durable() {}
The body moves into a static helper and the visible fn becomes a wrapper, so
EARLY RETURNS pass through the exit injection. Emitting it only before the
fall-through return would have silently missed every early return — the exact
failure class this seam exists to remove. Fns with no exit construct emit
byte-identically to before.
Three independent constructs now compose on one fn, none known to the compiler:
el_val_t mutate(el_val_t k) {
{ el_val_t __g = my_auth(EL_STR("mutate"), EL_STR("authenticate")); if (__g) return __g; }
engram_boundary_beat(EL_STR("mutate"), EL_STR("manager"));
el_val_t __r = __el_body_mutate(k);
persist_now(EL_STR("mutate"), EL_STR("durable"), __r);
return __r;
}
Guard, then entry, then body, then exit. §5.2 asked whether `hold` is one
construct or two; the implementation answers one construct with two faces,
selected by declared kind rather than by two mechanisms.
Verified: existing output byte-identical, compiler self-hosts byte-identically,
early returns pass through the exit, ordering holds under composition. 98/98
native compiler tests pass.
This commit is contained in:
@@ -842,3 +842,36 @@ test "undeclared-guard-emits-nothing" {
|
||||
let out: String = compile_capture(src)
|
||||
assert !str_contains(out, "if (__g)"), "an undeclared construct guards 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"
|
||||
}
|
||||
|
||||
test "constructs-compose-guard-entry-exit" {
|
||||
let src: String = "@decorator(\"guards_at_entry\", \"my_auth\")\nfn authenticate() {}\n@decorator(\"injects_at_exit\", \"persist_now\")\nfn durable() {}\n@authenticate\n@durable\n@manager\nfn op() -> Int { return 1 }"
|
||||
let out: String = compile_capture(src)
|
||||
let g: Int = str_index_of(out, "my_auth(")
|
||||
let b: Int = str_index_of(out, "engram_boundary_beat(EL_STR(\"op\")")
|
||||
let x: Int = str_index_of(out, "persist_now(")
|
||||
assert g < b, "guard before entry injection"
|
||||
assert b < x, "entry injection before exit injection"
|
||||
assert x >= 0, "three independent constructs compose on one fn"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user