# mut.3 — codegen + e2e — Implementation Plan > **Parent spec:** `docs/specs/0029-mut-local.md` > > **For agentic workers:** REQUIRED SUB-SKILL: use `skills/implement` > to run this plan. Steps use `- [ ]` checkboxes for tracking. **Goal:** Replace the iter mut.1 dispatch stubs at `crates/ailang-codegen/src/lib.rs:1683-1691` 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`. Extend the codegen-side `Term::Var` resolution ladder to consult mut-var allocas before `self.locals`. Ship two end-to-end fixtures (`examples/mut_counter.ail` + `examples/mut_sum_floats.ail`) and two e2e test cases asserting expected stdout. Append a bullet to DESIGN.md §"What is supported". **Architecture:** Mut-var allocas land in the fn's entry block via a side buffer `pending_entry_allocas: String` accumulated during body lowering and flushed once into `self.body` at a tracked byte position immediately after `start_block("entry")` emits the `entry:` label. This is a deliberate departure from the existing alloca sites (`match_lower.rs:114`, `lambda.rs:240/266`) which emit allocas at the current body position — those allocas are one-shot per fn invocation and don't suffer from being non-entry-block. Mut-var allocas in deeply-nested blocks (e.g. inside `if`/`match`/`seq`) must remain accessible across the whole fn body, so mem2reg eligibility (entry-block placement) matters. Mut-var lookup at codegen uses a per-fn `mut_var_allocas: BTreeMap` mapping name → (alloca SSA name, AIL type). Push entries on `Term::Mut`-entry, remove on exit; lexical scoping mirrors the mut.2 typecheck-side stack. **Tech Stack:** `ailang-codegen` only. No `ailang-core`, `ailang-surface`, `ailang-check`, `ailang-prose`, or `ail` source changes (one new test fn in `crates/ail/tests/e2e.rs`). Two new example fixtures + one DESIGN.md amendment. --- **Files this plan creates or modifies:** - Create: `examples/mut_counter.ail` — sum 1..10 via mut, prints 55. - Create: `examples/mut_sum_floats.ail` — Float twin (sum 1.0..10.0), prints the Float total. - Modify: `crates/ailang-codegen/src/lib.rs:716-717` — add `mut_var_allocas: BTreeMap` and `pending_entry_allocas: String` and `entry_block_end_marker: Option` fields to `Emitter`. - Modify: `crates/ailang-codegen/src/lib.rs:820-842` — initialise the three new fields in `Emitter::new`. - Modify: `crates/ailang-codegen/src/lib.rs:1008-1034` — reset the three new fields per fn-body in `emit_fn`, and capture the entry-block byte marker after `start_block("entry")`. - Modify: `crates/ailang-codegen/src/lib.rs:837-842` — extend `start_block` (or its caller) to record `entry_block_end_marker` on entry-block emission. - Modify: `crates/ailang-codegen/src/lib.rs:1292-1357` (`Term::Var` arm) — prepend a mut-var lookup that emits a `load` and returns the SSA name. - Modify: `crates/ailang-codegen/src/lib.rs:1683-1691` — replace the `Term::Mut`/`Term::Assign` `Internal` stubs with real lowering. - Modify: `crates/ailang-codegen/src/lib.rs` (end of `emit_fn`) — flush `pending_entry_allocas` into `self.body` at the marker. - Modify: `crates/ail/tests/e2e.rs` — add `mut_counter_prints_55` and `mut_sum_floats_prints_55_dot_0` test fns. - Modify: `docs/DESIGN.md` — append a bullet to the "What **is** supported" subsection around line 2680. --- ## Task 1 — `Emitter` field additions + per-fn reset **Files:** - Modify: `crates/ailang-codegen/src/lib.rs:716-717` (struct fields) - Modify: `crates/ailang-codegen/src/lib.rs:820-842` (`new`) - Modify: `crates/ailang-codegen/src/lib.rs:1008-1034` (`emit_fn` reset) - [ ] **Step 1: Add three fields to `Emitter`** Locate the `Emitter` struct around `crates/ailang-codegen/src/lib.rs:716`. After the existing field `current_param_modes: BTreeMap` (line 717), insert: ```rust /// 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 when leaving the block. Consulted by /// the `Term::Var` arm of `lower_term` before `self.locals`, /// matching the typecheck-side mut-scope-stack precedence. mut_var_allocas: BTreeMap, /// Iter mut.3: side buffer for `alloca` instructions emitted /// during body lowering but hoisted to the fn's entry block. /// Flushed once into `self.body` at `entry_block_end_marker` /// after `lower_term` completes. pending_entry_allocas: String, /// Iter mut.3: byte position in `self.body` immediately after /// the `entry:` label, captured during `start_block("entry")` /// at fn-body emission. Used to splice `pending_entry_allocas` /// into the entry block once body lowering completes. entry_block_end_marker: Option, ``` - [ ] **Step 2: Initialise in `Emitter::new`** Locate `Emitter::new` around `crates/ailang-codegen/src/lib.rs:820`. Near the existing `closure_drops: BTreeMap::new(),` line (around 831), add: ```rust mut_var_allocas: BTreeMap::new(), pending_entry_allocas: String::new(), entry_block_end_marker: None, ``` - [ ] **Step 3: Reset in `emit_fn`** Locate the per-fn reset block in `emit_fn` (around `crates/ailang-codegen/src/lib.rs:1008-1034` — where `self.locals.clear()`, `self.counter = 0`, `self.moved_slots.clear()`, `self.current_param_modes.clear()` already live). Append: ```rust self.mut_var_allocas.clear(); self.pending_entry_allocas.clear(); self.entry_block_end_marker = None; ``` - [ ] **Step 4: Verify build green** Run: `cargo build --workspace 2>&1 | tail -10` Expected: green (the three new fields are added but not yet consumed; the only consumers are added in Tasks 2-3). --- ## Task 2 — `start_block` records the entry-block marker **Files:** - Modify: `crates/ailang-codegen/src/lib.rs:837-842` (`start_block`) `start_block` is the canonical helper that emits `