source: scrub iter-code / Decision-N residue from inline comments

Follow-up to bcd4181: the remaining ~530 inline `//` and `///`
comments still carrying opaque shorthand are now reformulated to
their content phrases. The only surviving `iter-<code>` reference
in source is the literal filename
`docs/journals/2026-05-13-iter-mq.3.md` (a real journal file).

Sweep covered:
  - `// Iter X.Y: <text>` prefixes (Iter 13a / 14a / 14e / 15g-aux /
    16b.x / 16d / 16e / 18b / 18c.x / 18d.x / 18e / 18g.x / 19a /
    19a.1 / 19b / 20a / 20f / 22-floats.x / 22b.x / 22c / 23.x /
    24.1 / cli-diag-human / hs.x / str-concat / etc.) — fully
    removed; the descriptive text that followed each prefix stays.
  - `// (Decision N)` and `per Decision N` and `Decision N axis 3` —
    replaced with the content phrase plus the relevant contract
    file (`design/contracts/tail-calls.md` for Decision 8,
    `design/contracts/memory-model.md` for Decision 10,
    `design/contracts/typeclasses.md` and `design/models/typeclasses.md`
    for Decision 11, `design/contracts/authoring-surface.md` for
    Decision 6, "the transitional dual-allocator" for Decision 9,
    "Effect prose" for Decision 3).
  - `// mq.X / mq.X (Task N) / mq.X journal / mq.X invariant` ->
    "the canonical-class-form rule / Class-class repurpose /
    method-dispatch-refactor journal / canonical-class-form
    invariant".
  - `// ct.X / ct.X (canonical-type-names) / ct.1.5a + ctt.2 /
    ct.2 Task N` -> "the canonical-form rule for type references /
    the canonical-form normalisation step / canonical-type-lookup
    refactor".
  - `// eob.X` -> "heap-Str-ABI" / "the Str carve-out".
  - `// rpe.X` -> "the per-type-print-op retirement".
  - `// post-mq.X / Pre-ct.X / pre-mq.X` -> "post-canonical-class-form" /
    "Pre-canonical-type-form" etc.
  - `/// Iter X regression: / /// Iter X.Y: / /// Iter A arm-close /
    /// Iter ct.4 (...): / /// Iter rpe.1 ...` -> descriptive
    phrases.

The journal filename in `crates/ailang-core/src/workspace.rs:573`
stays verbatim because it points at an actual file under
`docs/journals/`.

Tests: full `cargo test --workspace` green (80/80 test-result blocks
clean, no FAILED line). design_index_pin 5/5 + docs_honesty_pin 5/5
gating tests pass.
This commit is contained in:
2026-05-20 09:58:04 +02:00
parent bcd41810f4
commit ac4d545570
43 changed files with 1004 additions and 1000 deletions
+29 -29
View File
@@ -33,7 +33,7 @@ impl<'a> Emitter<'a> {
/// i1 and i8 fields also occupy a full 8-byte slot — the typed
/// load/store instructions write/read only the required size.
///
/// Iter 17a: `term_ptr` is the pointer-as-usize of the lowered
/// `term_ptr` is the pointer-as-usize of the lowered
/// `Term::Ctor` node. If escape analysis flagged this site as
/// non-escaping (i.e., the value cannot live past the current fn
/// frame), allocation lowers to LLVM `alloca` instead of
@@ -52,13 +52,13 @@ impl<'a> Emitter<'a> {
"ctor `{type_name}/{ctor_name}` arity"
)));
}
// Iter 13b: for parameterised ADTs the precomputed `cref.fields`
// for parameterised ADTs the precomputed `cref.fields`
// is meaningless because field types reference rigid type vars.
// Derive a per-use-site substitution from the arg types and
// re-lower each field type. Monomorphic ADTs hit the fast path
// (no var-set, substitution is empty, ail_fields lower exactly
// like cref.fields).
// Iter 15b: for cross-module ctors, qualify any local type-cons
// for cross-module ctors, qualify any local type-cons
// in `cref.ail_fields` (symmetric to the term-ctor synth fix).
let qualified_ail_fields: Vec<Type> = if type_name.matches('.').count() == 1 {
let (prefix, _) = type_name.split_once('.').expect("checked");
@@ -108,7 +108,7 @@ impl<'a> Emitter<'a> {
// design/contracts/frozen-value-layout.md. size = 8 + n*8, tag@0, fields@8+i*8.
let size_bytes = 8 + (compiled.len() * 8) as i64;
let p = self.fresh_ssa();
// Iter 17a: pick allocator based on escape analysis. `alloca`
// pick allocator based on escape analysis. `alloca`
// for non-escaping (stack-allocated, freed on fn return);
// `@GC_malloc` for everything else.
if self.non_escape.contains(&term_ptr) {
@@ -139,7 +139,7 @@ impl<'a> Emitter<'a> {
Ok((p, "ptr".into()))
}
/// Iter 18d.2: lower `Term::ReuseAs { source, body = Term::Ctor }`
/// lower `Term::ReuseAs { source, body = Term::Ctor }`
/// under `--alloc=rc` as a runtime refcount-1 dispatch.
///
/// IR shape (Lean 4 / Roc lineage):
@@ -192,7 +192,7 @@ impl<'a> Emitter<'a> {
"reuse-as source type must be ptr, got {src_ty}"
)));
}
// Iter 18d.3: the source binder name keys into `moved_slots`
// the source binder name keys into `moved_slots`
// so the reuse arm can skip dec'ing fields that earlier
// pattern-extracted out of the source. Linearity already
// ensures `source` is `Term::Var` here; the var-resolution
@@ -323,7 +323,7 @@ impl<'a> Emitter<'a> {
// 5. Reuse arm: overwrite tag and field slots in place.
//
// Iter 18d.3: per-field dec is now safe — moved-out slots
// per-field dec is now safe — moved-out slots
// are skipped via the codegen `moved_slots` side table;
// non-moved slots are dec'd via `field_drop_call`'s null-
// guarded drop fns BEFORE the new field values overwrite
@@ -344,7 +344,7 @@ impl<'a> Emitter<'a> {
// pointer-typed fields are dec'd via `field_drop_call`,
// closing the 18d.2 leak.
self.start_block(&reuse_lbl);
// Iter 18d.3: dec non-moved pointer-typed slots before the
// dec non-moved pointer-typed slots before the
// overwrite. The dec fns are null-guarded, so an empty slot
// is a no-op. Clone the move set out of the side table so
// the loop below can mutably borrow `self` for SSA allocation.
@@ -449,7 +449,7 @@ impl<'a> Emitter<'a> {
"match on non-ADT scrutinee (got {s_ty}); MVP supports only ADTs"
)));
}
// Iter 18d.3: move tracking key — the bare-Var binder name of
// move tracking key — the bare-Var binder name of
// the scrutinee, if it has one. A complex expression scrutinee
// (e.g. the result of `(map_inc xs)` matched directly) has no
// binder we can key off; moves through such matches are
@@ -486,7 +486,7 @@ impl<'a> Emitter<'a> {
open_var = Some(name.clone());
}
Pattern::Ctor { ctor, fields } => {
// Iter 15a: lookup falls back to imported modules when
// lookup falls back to imported modules when
// a bare ctor name doesn't resolve locally.
let cref = self.lookup_ctor_in_pattern(ctor)?;
let bindings: Vec<Option<String>> = fields
@@ -530,7 +530,7 @@ impl<'a> Emitter<'a> {
for (i, (cref, arm, bindings)) in ctor_arms.iter().enumerate() {
self.start_block(&arm_labels[i]);
// Iter 13b: derive substitution for parameterised ADTs from
// derive substitution for parameterised ADTs from
// the scrutinee's concrete type-args. Map `cref.type_vars[i]`
// → `s_ail.args[i]`. For monomorphic ADTs the mapping is
// empty and substitution is a no-op. The substituted AILang
@@ -559,7 +559,7 @@ impl<'a> Emitter<'a> {
}
};
// Load fields and bind as locals.
// Iter 15b: when the scrutinee's ADT lives in another module,
// when the scrutinee's ADT lives in another module,
// `cref.ail_fields[idx]` carries the field type written in
// the owner's local namespace. Qualify it before substituting
// — symmetric to the term-ctor and pat-ctor fixes in
@@ -571,13 +571,13 @@ impl<'a> Emitter<'a> {
_ => None,
};
let mut pushed = 0usize;
// Iter 18d.3: collect the binder names this arm pushes so
// collect the binder names this arm pushes so
// we can drop their `moved_slots` entries when the arm
// body closes (these binders are themselves freshly named
// here — their move tracking starts empty and ends at arm
// body close).
//
// Iter 18d.4: the metadata is widened to (name, SSA,
// the metadata is widened to (name, SSA,
// llvm-type, AILang-type) so that the arm-close drop
// emission can route the call through the binder's
// per-type drop symbol without re-walking `self.locals`.
@@ -608,7 +608,7 @@ impl<'a> Emitter<'a> {
self.body.push_str(&format!(
" {v} = load {fty}, ptr {addr}, align 8\n"
));
// Iter 18d.3: a non-wildcard, pointer-typed slot
// a non-wildcard, pointer-typed slot
// bound here is treated as moved out of the
// scrutinee binder. The source slot is NOT
// mutated; codegen records the move statically so
@@ -632,9 +632,9 @@ impl<'a> Emitter<'a> {
pushed += 1;
}
}
// Iter 18d.4 fix + 18g.1: scrutinee ownership signal,
// hoisted here so both the pre-tail-call shallow-dec
// (Iter 18g.1, below) and the arm-close pattern-binder
// Scrutinee ownership signal, hoisted here so both the
// pre-tail-call shallow-dec (below) and the arm-close
// pattern-binder
// dec (Iter A, further below) consult the same gate.
//
// If the scrutinee resolves to a fn-param whose mode is
@@ -657,7 +657,7 @@ impl<'a> Emitter<'a> {
},
None => true,
};
// Iter 18g.1: pre-tail-call shallow-dec for moved-from
// pre-tail-call shallow-dec for moved-from
// scrutinee outer cell. Iter A (arm-close pattern-binder
// dec, below) cannot fire when `arm.body` is a tail call:
// `lower_term` emits `musttail call ... ret` and sets
@@ -739,7 +739,7 @@ impl<'a> Emitter<'a> {
}
}
let (val, vty) = self.lower_term(&arm.body)?;
// Iter 18d.4: arm-close pattern-binder dec. Symmetric to
// arm-close pattern-binder dec. Symmetric to
// 18c.3/18c.4's `Term::Let`-scope-close drop emission, but
// fired at the lexical close of a match arm. For each
// pattern-bound binder this arm pushed, emit a drop call
@@ -763,11 +763,11 @@ impl<'a> Emitter<'a> {
// through the tail slot. Now the arm itself dec's `t`,
// freeing the 4-cell tail.
//
// Iter 18d.4 fix (regression
// `alloc_rc_pattern_bind_in_implicit_fn_does_not_dec_borrowed_children`):
// Iter A is symmetric to Iter B (Own-param dec at fn
// return) and must share Iter B's mode gate. If the
// scrutinee resolves to a fn-param whose mode is
// Regression
// `alloc_rc_pattern_bind_in_implicit_fn_does_not_dec_borrowed_children`:
// arm-close pattern-binder dec is symmetric to Own-param
// dec at fn return and must share the same mode gate. If
// the scrutinee resolves to a fn-param whose mode is
// `Borrow` or `Implicit`, the caller still holds a
// reference to the heap value the pattern-binders were
// loaded from. Dec'ing those binders fragments the
@@ -823,7 +823,7 @@ impl<'a> Emitter<'a> {
" call void @{drop_call}(ptr {b_ssa})\n"
));
} else {
// Iter 18g.tidy.fu2: pattern-binder with
// pattern-binder with
// statically-recorded moved slots routes
// through the tag-conditional helper
// `partial_drop_<owner>_<T>(b, mask)`. The
@@ -852,13 +852,13 @@ impl<'a> Emitter<'a> {
for _ in 0..pushed {
self.locals.pop();
}
// Iter 18d.3: arm-bound binders go out of scope at arm
// arm-bound binders go out of scope at arm
// body close. Drop their move-tracking entries (any moves
// recorded against `h`/`t` belonged to this arm only).
for (bname, _, _, _) in &arm_pushed_binders {
self.moved_slots.remove(bname);
}
// Iter 14e: if the arm body lowered to a `musttail call` +
// if the arm body lowered to a `musttail call` +
// `ret`, the block is already terminated. Skip the
// fall-through `br` and exclude this arm from the join phi.
if !self.block_terminated {
@@ -912,7 +912,7 @@ impl<'a> Emitter<'a> {
}
// join
// Iter 14e: if every arm tail-called and terminated its own
// if every arm tail-called and terminated its own
// block, no predecessor branches into the join. Mark the whole
// match as block-terminated and emit no join body — the
// surrounding context (top-level fn body, seq rhs, etc.) checks