c5fa6e6ea3ca4abc531c697c7c605828fd1e311f
2 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
07f080256c |
iter remove-mut-var-assign.1: atomic removal of mut/var/assign
mut/var/assign removed from AILang entirely and atomically. Deleted: Term::Mut/Term::Assign/struct MutVar; the three Form-A keywords + parse_mut/parse_assign + grammar EBNF; the 4 mut CheckError variants; the mut_scope_stack synth threading (param dropped from synth + every internal/external/test caller); the two lower_term arms; and every exhaustive no-_ Term::Mut/Term::Assign match arm across 17 source files — cut in lockstep with DESIGN.md, fixtures, the drift trio, carve-out and roadmap so the schema is honest at every commit. No catch-all wildcard introduced (verified). loop/recur + let/if are the surviving forms. The shared codegen alloca machinery survives (loop reuses it): mut_var_allocas renamed binder_allocas (representation-only, loop codegen byte-identical) and the shared Term::Lam escape guard simplified to !loop_stack.is_empty() with the loop half (LoopBinderCapturedByLambda) byte-equivalent. Feature-acceptance applied inverted: the removed feature fails clause 2 (redundant) and clause 3 (IS the iterated-mutable-state bug class). Behaviour preservation is executable: mut_counter/mut_sum_floats still print 55 after the faithful let/if rewrite. The removal is made executable by the new mut_removed_pin.rs (4 must-fail pins). Independent verification: cargo test --workspace 605/0, zero residual mut symbols in any crate source, loop/recur non-regression all green (55 / 500000500000 / infinite-compiles / the lambda_capturing_loop_binder pin), roundtrip_cli PASS. One DONE_WITH_CONCERNS: a 4th recurrence of the recon-undercount class (in-source mod tests + a drift-pin fn + 5 orphaned mut .ail.json carve-outs + a non-enumerated E0599); all resolved within implementer remit, no behaviour change. Milestone-close audit then fieldtest remain. spec docs/specs/2026-05-18-remove-mut-var-assign.md (grounding PASS) plan docs/plans/remove-mut-var-assign.1.md |
||
|
|
03fb633d85 |
iter mut.3: codegen + e2e — mut-local milestone end-to-end closed
Replaces the iter mut.1 dispatch stubs in lower_term with real LLVM
lowering: mut-vars become alloca slots hoisted to the fn's entry
block, assign lowers to store, mut-var reads lower to load. Two
example fixtures run end-to-end printing 55 (Int via mut_counter,
Float via mut_sum_floats).
Concretely:
- Emitter struct gains three new fields: mut_var_allocas:
BTreeMap<String, (String, Type)> for per-fn name→(alloca SSA,
AIL type) tracking; pending_entry_allocas: String as a side
buffer accumulating alloca instructions during body lowering;
entry_block_end_marker: Option<usize> recording the byte position
in self.body immediately after the entry: label.
- start_block(label) captures the marker when label == 'entry'.
emit_fn resets all three new fields per fn body. At the end of
emit_fn, String::insert_str splices pending_entry_allocas into
self.body at the marker — alloca instructions land in the entry
block regardless of where in the source tree Term::Mut was
encountered (mem2reg eligibility preserved).
- Term::Mut arm: per var, fresh-name an alloca SSA, push the
alloca instruction into pending_entry_allocas, lower the init at
the current body position, emit a store to bind. The mut-var
binding is NOT visible during init (matches typecheck-side
ordering at lib.rs:3576). After all vars are bound, lower the
body in the extended scope. On block exit, restore any prior
bindings via a per-block save stack — supports nested
shadowing correctly.
- Term::Assign arm: look up the alloca + type via
mut_var_allocas.get(name); lower the value; emit store; yield
the canonical Unit SSA per the existing Term::Lit { lit:
Literal::Unit } convention.
- Term::Var arm: prepended with a mut-var lookup that emits a
load <ty>, ptr <alloca> and returns the load SSA. Innermost-wins
shadowing falls out of the BTreeMap's insert-overwrites
semantics combined with the per-block save/restore.
- examples/mut_counter.ail + examples/mut_sum_floats.ail:
recursive sum_helper accumulates 1..10 (Int) or 1.0..10.0 (Float)
inside a mut block whose single var is assigned the helper's
result. Both run end-to-end printing 55.
- crates/ail/tests/e2e.rs: mut_counter_prints_55 and
mut_sum_floats_prints_55 #[test] fns using the existing
build_and_run helper.
- DESIGN.md 'What is supported' subsection: 'Local mutable state'
bullet describes the new construct, points at the two example
fixtures, and reaffirms the seal-by-construction invariant under
Decision 10.
Beyond-plan adjustments absorbed in this iter:
- synth_with_extras's parallel Term::Var arm in
ailang-codegen/src/lib.rs needed the same mut-var lookup as
lower_term's. Plan only specified lower_term's arm.
- crates/ailang-codegen/src/lambda.rs needed save/restore of all
three new Emitter fields across the lambda-body boundary plus
in-thunk splice of pending_entry_allocas at the lambda's own
entry marker, so mut-blocks inside a closure body hoist into
the closure's entry block (not the outer fn's).
mut-local milestone end-to-end status:
- mut.1 (
|