iteration-1: the compiler stops adjudicating #164
@@ -3214,22 +3214,6 @@ fn cg_entry_seam(stmt: Map<String, Any>, fn_name: String) -> Void {
|
||||
|
||||
// cg_exit_target / cg_exit_construct — the first construct on this fn that
|
||||
// injects at exit, or "" if none.
|
||||
fn cg_exit_target(stmt: Map<String, Any>) -> String {
|
||||
let xdl = stmt["decorators"]
|
||||
let n_xdl: Int = native_list_len(xdl)
|
||||
let xi = 0
|
||||
let found: String = ""
|
||||
while xi < n_xdl {
|
||||
if str_eq(found, "") {
|
||||
let xd = native_list_get(xdl, xi)
|
||||
let xdn: String = xd["name"]
|
||||
let xt: String = decorator_exit(xdn)
|
||||
if !str_eq(xt, "") { let found = xt }
|
||||
}
|
||||
let xi = xi + 1
|
||||
}
|
||||
found
|
||||
}
|
||||
|
||||
fn cg_wrap_target(stmt: Map<String, Any>) -> String {
|
||||
let wdl = stmt["decorators"]
|
||||
@@ -3309,22 +3293,6 @@ fn params_to_env_args(params: [Any]) -> String {
|
||||
out
|
||||
}
|
||||
|
||||
fn cg_exit_construct(stmt: Map<String, Any>) -> String {
|
||||
let xdl = stmt["decorators"]
|
||||
let n_xdl: Int = native_list_len(xdl)
|
||||
let xi = 0
|
||||
let found: String = ""
|
||||
while xi < n_xdl {
|
||||
if str_eq(found, "") {
|
||||
let xd = native_list_get(xdl, xi)
|
||||
let xdn: String = xd["name"]
|
||||
let xt: String = decorator_exit(xdn)
|
||||
if !str_eq(xt, "") { let found = xdn }
|
||||
}
|
||||
let xi = xi + 1
|
||||
}
|
||||
found
|
||||
}
|
||||
|
||||
// params_to_call_args — "a, b, c" from the param list, for the wrapper's call
|
||||
// into the body helper.
|
||||
@@ -3390,12 +3358,10 @@ fn cg_fn(stmt: Map<String, Any>) -> Void {
|
||||
// silently miss every early return — which is precisely the class of
|
||||
// failure this seam exists to remove. Fns with no exit construct emit
|
||||
// exactly as before, byte for byte.
|
||||
let exit_target: String = cg_exit_target(stmt)
|
||||
let exit_construct: String = cg_exit_construct(stmt)
|
||||
let wrap_target: String = cg_wrap_target(stmt)
|
||||
let wrap_construct: String = cg_wrap_construct(stmt)
|
||||
let has_wrap: Bool = !str_eq(wrap_target, "")
|
||||
let has_exit: Bool = !str_eq(exit_target, "") || has_wrap
|
||||
let has_exit: Bool = true
|
||||
if has_exit {
|
||||
emit_line("static el_val_t __el_body_" + fn_name + "(" + params_c + ") {")
|
||||
} else {
|
||||
@@ -3448,9 +3414,7 @@ fn cg_fn(stmt: Map<String, Any>) -> Void {
|
||||
} else {
|
||||
emit_line(" el_val_t __r = __el_body_" + fn_name + "(" + params_to_call_args(params) + ");")
|
||||
}
|
||||
if !str_eq(exit_target, "") {
|
||||
emit_line(" " + exit_target + "(EL_STR(" + c_str_lit(fn_name) + "), EL_STR(" + c_str_lit(exit_construct) + "), __r);")
|
||||
}
|
||||
emit_line(" __r = el_seam_run(EL_STR(" + c_str_lit(fn_name) + "), 1, __r);")
|
||||
emit_line(" return __r;")
|
||||
emit_line("}")
|
||||
}
|
||||
@@ -4312,13 +4276,7 @@ fn program_has_routes(recs: [Map<String, Any>]) -> Bool {
|
||||
// persist-after-mutate sites and 9-of-9 failed index-after-append sites — every
|
||||
// one an obligation that decayed into "remember to do this after you mutate."
|
||||
// An obligation a human must remember is not an obligation.
|
||||
fn declare_exit(name: String, exits: String) -> Void {
|
||||
state_set("__dec_exit_" + name, exits)
|
||||
}
|
||||
|
||||
fn decorator_exit(name: String) -> String {
|
||||
state_get("__dec_exit_" + name)
|
||||
}
|
||||
|
||||
// A WRAP construct receives the body as a CLOSURE and decides how, whether, and
|
||||
// how many times to invoke it.
|
||||
@@ -4382,8 +4340,6 @@ fn prohibiting_constructs() -> String {
|
||||
fn scan_declared_decorators(tokens: [Any]) -> Void {
|
||||
declare_prohibition("manager", "dharma_emit,dharma_field")
|
||||
let total: Int = native_list_len(tokens) / 2
|
||||
let has_pending_x: Bool = false
|
||||
let pending_exit: String = ""
|
||||
let has_pending_w: Bool = false
|
||||
let pending_wrap: String = ""
|
||||
let has_pending_p: Bool = false
|
||||
@@ -4426,10 +4382,6 @@ fn scan_declared_decorators(tokens: [Any]) -> Void {
|
||||
if str_eq(dname, "decorator") {
|
||||
if native_list_len(args) >= 2 {
|
||||
let dkind: String = native_list_get(args, 0)
|
||||
if str_eq(dkind, "injects_at_exit") {
|
||||
let has_pending_x = true
|
||||
let pending_exit = native_list_get(args, 1)
|
||||
}
|
||||
if str_eq(dkind, "wraps_body") {
|
||||
let has_pending_w = true
|
||||
let pending_wrap = native_list_get(args, 1)
|
||||
@@ -4444,10 +4396,6 @@ fn scan_declared_decorators(tokens: [Any]) -> Void {
|
||||
} else {
|
||||
if str_eq(k, "Fn") {
|
||||
let fname: String = tok_value(tokens, pos + 1)
|
||||
if has_pending_x {
|
||||
declare_exit(fname, pending_exit)
|
||||
let has_pending_x = false
|
||||
}
|
||||
if has_pending_w {
|
||||
declare_wrap(fname, pending_wrap)
|
||||
let has_pending_w = false
|
||||
|
||||
@@ -34,6 +34,9 @@ el_val_t observe(el_val_t fn, el_val_t con, el_val_t r){
|
||||
printf("SEEN %s/%s\n", (const char*)(intptr_t)fn, (const char*)(intptr_t)con);
|
||||
return r; /* zero = do not refuse */
|
||||
}
|
||||
el_val_t double_result(el_val_t fn, el_val_t con, el_val_t r){
|
||||
(void)fn; (void)con; return r * 2; /* exit: replace the result */
|
||||
}
|
||||
el_val_t refuse(el_val_t fn, el_val_t con, el_val_t r){
|
||||
(void)fn; (void)con; (void)r; return 42; /* non-zero = short-circuit */
|
||||
}
|
||||
@@ -80,6 +83,10 @@ check "two constructs compose on one crossing" \
|
||||
SEEN work/b
|
||||
7" "$(cd "$WORK" && EL_CONSTRUCTS=two.txt ./prog 2>&1)"
|
||||
|
||||
printf 'work doubler exit double_result\n' > "$WORK/exit.txt"
|
||||
check "an EXIT construct declared after the build replaces the result" \
|
||||
"14" "$(cd "$WORK" && EL_CONSTRUCTS=exit.txt ./prog 2>&1)"
|
||||
|
||||
echo
|
||||
echo " 6 assertions, $((6-FAILS)) passed, $FAILS failed"
|
||||
echo " 7 assertions, $((6-FAILS)) passed, $FAILS failed"
|
||||
exit $FAILS
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user