iter loop-recur.tidy GREEN: reject lambda-captures-loop-binder at check + milestone-close audit resolution
GREEN side of the RED audit-trail commit 39380d3. The loop/recur
milestone-close audit found a [high] correctness defect — a lambda
capturing a loop binder passed `ail check` then panicked
unreachable!() at codegen (crates/ailang-codegen/src/lambda.rs:102)
on type-correct input. Fixed symmetrically to mut.4-tidy: new
CheckError::LoopBinderCapturedByLambda (code
loop-binder-captured-by-lambda, bracket-free F2 Display), the
Term::Lam escape guard gained a parallel loop_stack pass. Minimal
mechanism: loop_stack element Vec<Type> -> Vec<(String,Type)> so
the already-threaded per-loop frame carries binder names; recur
still reads .1 by position (Boss-call-2 positional invariant
preserved verbatim — the name is a second field for the escape
guard only, a consumer iter-2 did not anticipate). Codegen
byte-unchanged (typecheck-only fix). carve_out 17->18 + ct1-F2 +
DESIGN.md note lockstep.
Folded in (Boss-side, audit resolution): the two [medium]
doc-honesty edits the audit surfaced (mut_var_allocas rustdoc now
states its mut-var+loop-binder dual use; Term::Loop doc-comment
now describes the real shipped state, not iter-1's stale
"per-binder phi" forward-look) + a [low] P2 roadmap todo
(plan-recon undercount countermeasure, pairs with planner Step-5
items 7+8). Bench gate: all 3 scripts exit 0, 25 metrics 0
regressed — pristine, carry-on (no baseline/ratify).
cargo test --workspace 619 -> 622 / 0 red (Boss-reran
independently, hash_pin 11/0). The loop/recur milestone is
audit-clean; fieldtest is the only remaining step before close.
This commit is contained in:
@@ -714,14 +714,20 @@ struct Emitter<'a> {
|
||||
/// dec'ing, because Implicit and Borrow do not carry the "caller
|
||||
/// handed off ownership" signal that makes the dec safe.
|
||||
current_param_modes: BTreeMap<String, ParamMode>,
|
||||
/// Iter mut.3: per-fn map of mut-var name → (alloca SSA name,
|
||||
/// AIL element type). Populated when entering a `Term::Mut`
|
||||
/// block; entries removed (or restored to their prior binding)
|
||||
/// when leaving the block. Consulted by the `Term::Var` arm of
|
||||
/// `lower_term` before `self.locals`, matching the typecheck-
|
||||
/// side mut-scope-stack precedence (`synth` in `ailang-check`
|
||||
/// walks `mut_scope_stack` innermost-first before falling
|
||||
/// through to locals/globals).
|
||||
/// Per-fn map of name → (alloca SSA name, AIL element type) for
|
||||
/// the two alloca-resident binding classes. Populated on entry
|
||||
/// to a `Term::Mut` block (iter mut.3 — mut-vars) AND on entry
|
||||
/// to a `Term::Loop` (iter loop-recur.3 — loop binders ride the
|
||||
/// same alloca representation; the `Term::Loop` arm inserts each
|
||||
/// binder here and `recur` stores into the slot). Entries are
|
||||
/// removed (or restored to their prior binding) on block exit.
|
||||
/// Consulted by the `Term::Var` arm of `lower_term` before
|
||||
/// `self.locals` — the single load path serving both classes.
|
||||
/// Note: only the mut-var half mirrors a typecheck-side
|
||||
/// `mut_scope_stack`; loop binders are tracked typecheck-side in
|
||||
/// the ordinary `locals` plus `loop_stack` (positional), not in
|
||||
/// `mut_scope_stack` — the shared codegen map is a
|
||||
/// representation detail, not a scope-precedence claim.
|
||||
mut_var_allocas: BTreeMap<String, (String, Type)>,
|
||||
/// loop-recur iter 3: stack of enclosing `Term::Loop` codegen
|
||||
/// frames. Each frame is `(header_block_label, binder_slots)`
|
||||
|
||||
Reference in New Issue
Block a user