iter loop-recur.3: codegen — real LLVM-IR lowering + run-to-value E2E (milestone terminal)

Third and terminal iteration of the standalone loop/recur
milestone (plan eae73bf). Replaces the iter-1 lower_term
CodegenError::Internal stub for Term::Loop/Term::Recur with real
LLVM-IR lowering: loop binders as entry-block allocas (mut.3
pending_entry_allocas, reusing mut_var_allocas so the existing
Term::Var load path is byte-unchanged), a fresh loop-header block,
recur stores + back-edge br, a loop_frames stack saved/restored at
the lambda boundary. clang -O2 mem2reg promotes the allocas to
phi. Four Boss design calls implemented verbatim and journalled
(alloca-not-hand-phi; mut_var_allocas reuse; emergent loop-exit
via the if/match join; single block_terminated field + parallel
SET site). Diff confirmed surgical: codegen/lib.rs 3 hunks (field
+ init + stub->2-arms), lambda.rs 2 hunks (save+restore); zero
edits to any existing block_terminated SET/READ site, tail-app
lowering, or verify_tail_positions (Boss call 4, the spec-pinned
invariant). Three new .ail fixtures: sum_to->55,
deep-n 1e6->500000500000 (clause-2 correctness made executable),
infinite-loop build-only. Codegen-only: no schema/typecheck
change; hash pins + drift trio stay green untouched.

One DONE_WITH_CONCERNS (T3): the plan's `cargo test ... tail`
filter resolved to no tests; ran via real names + the full 619/0
which subsumes it (feedback_plan_pseudo_vs_reality class, no
behaviour change). Boss systemic fix folded in: planner SKILL.md
Step-5 gains item 8 (verification-command filter strings must
resolve) — the second planner-meta-gap this milestone surfaced.

cargo test --workspace 616 -> 619 / 0 red (Boss-reran
independently); the 3 loop/recur e2e explicitly green. All three
components shipped: the loop/recur milestone is structurally
complete. Milestone-close audit + fieldtest is the next step.
This commit is contained in:
2026-05-17 23:57:46 +02:00
parent eae73bf320
commit edd2558d35
10 changed files with 434 additions and 3 deletions
+10
View File
@@ -0,0 +1,10 @@
(module loop_forever_build
(fn main
(doc "loop-recur iter 3 — an infinite loop (no non-recur exit) must COMPILE (typechecks AND compiles; no termination claim). Build-only; by design never returns, so the binary is never executed.")
(type (fn-type (params) (ret (con Unit)) (effects IO)))
(params)
(body (app print (app spin 0))))
(fn spin
(type (fn-type (params (con Int)) (ret (con Int))))
(params n)
(body (loop (i (con Int) 0) (recur (app + i 1))))))
+14
View File
@@ -0,0 +1,14 @@
(module loop_sum_to_deep
(fn main
(doc "loop-recur iter 3 — sum 1..1000000 iteratively. A non-tail hand-recursion to this depth would overflow; loop/recur is safe by construction. Expected stdout: 500000500000.")
(type (fn-type (params) (ret (con Unit)) (effects IO)))
(params)
(body (app print (app sum_to 1000000))))
(fn sum_to
(type (fn-type (params (con Int)) (ret (con Int))))
(params n)
(body
(loop (acc (con Int) 0) (i (con Int) 1)
(if (app > i n)
acc
(recur (app + acc i) (app + i 1)))))))
+14
View File
@@ -0,0 +1,14 @@
(module loop_sum_to_run
(fn main
(doc "loop-recur iter 3 — sum 1..10 via loop/recur. Expected stdout: 55.")
(type (fn-type (params) (ret (con Unit)) (effects IO)))
(params)
(body (app print (app sum_to 10))))
(fn sum_to
(type (fn-type (params (con Int)) (ret (con Int))))
(params n)
(body
(loop (acc (con Int) 0) (i (con Int) 1)
(if (app > i n)
acc
(recur (app + acc i) (app + i 1)))))))