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:
@@ -82,6 +82,43 @@ fn sum_1_to_10_is_55() {
|
||||
assert_eq!(stdout.trim(), "55");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_recur_sum_to_runs_to_value() {
|
||||
let stdout = build_and_run("loop_sum_to_run.ail");
|
||||
assert_eq!(stdout.trim(), "55");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_recur_deep_n_safe_by_construction() {
|
||||
// 1..1_000_000 summed via the loop back-edge (no stack growth);
|
||||
// a hand-written non-tail self-recursion to this depth would
|
||||
// overflow. sum 1..1_000_000 = 500000500000 (fits i64).
|
||||
let stdout = build_and_run("loop_sum_to_deep.ail");
|
||||
assert_eq!(stdout.trim(), "500000500000");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_recur_infinite_loop_compiles() {
|
||||
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
||||
let workspace = Path::new(manifest_dir).parent().unwrap().parent().unwrap();
|
||||
let src = workspace.join("examples").join("loop_forever_build.ail");
|
||||
let tmp = std::env::temp_dir().join(format!(
|
||||
"ailang_e2e_loopforever_{}",
|
||||
std::process::id()
|
||||
));
|
||||
std::fs::create_dir_all(&tmp).unwrap();
|
||||
let out = tmp.join("bin");
|
||||
let status = Command::new(ail_bin())
|
||||
.args(["build", src.to_str().unwrap(), "-o"])
|
||||
.arg(&out)
|
||||
.status()
|
||||
.expect("ail build failed to run");
|
||||
assert!(
|
||||
status.success(),
|
||||
"ail build must succeed on an infinite loop (typechecks AND compiles, no termination claim)"
|
||||
);
|
||||
}
|
||||
|
||||
/// Guards block tracking in codegen: max3 has nested `if`s, and wrong
|
||||
/// phi block tracking would produce a wrong result here.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user