iter 22b.3: JOURNAL — monomorphisation pass shipped, e2e gate green
This commit is contained in:
+104
@@ -11499,3 +11499,107 @@ cost. First end-to-end exercise is a positive signal; the
|
||||
remaining open follow-ups from 2026-05-09 (skill bugs surfaced by
|
||||
real iter use) get queued as targeted SKILL.md tweaks rather than
|
||||
a wholesale revisit.
|
||||
|
||||
## 2026-05-09 — Iteration 22b.3: Monomorphisation pass
|
||||
|
||||
Mono pass slots into `crates/ail/src/main.rs::build_to` between
|
||||
`lift_letrecs` and `lower_workspace`. The pass is a `Workspace ->
|
||||
Workspace` transformation living in `crates/ailang-check/src/mono.rs`,
|
||||
mirroring `lift.rs`'s position and ownership model. For workspaces
|
||||
with no `Def::Class` / `Def::Instance`, the early-out returns the
|
||||
input clone byte-identically — pre-22b fixtures stay
|
||||
hash-stable through the full pipeline. For typeclass-bearing
|
||||
workspaces, the pass runs three phases: (1) build the full env via
|
||||
`build_workspace_env` (same shape as `check_in_workspace`), (2)
|
||||
fixpoint loop — each round walks every fn/const body, re-runs
|
||||
`synth` to recover residual class constraints, applies `subst`,
|
||||
filters fully-concrete residuals, dedupes via `(class, method,
|
||||
type-hash)` key, synthesises one `Def::Fn` per unique target via
|
||||
`synthesise_mono_fn` (instance body OR class default fallback,
|
||||
class param substituted to instance type), and appends to the
|
||||
registry's `defining_module`. Loop terminates when a round adds
|
||||
no new entries — closes on chained class-method calls (e.g. an
|
||||
instance body that itself calls a class method, exercised by
|
||||
`test_22b3_chained_calls.ail.json`). (3) Phase 3 walks every
|
||||
fn/const body in the post-fixpoint workspace and rewrites every
|
||||
class-method `Term::Var` to its mono-symbol name, qualifying with
|
||||
the defining module if cross-module. The walker is shadow-aware:
|
||||
local bindings (Let / LetRec / Lam / Match-arm) suppress the
|
||||
rewrite at locally-named Vars, mirroring synth's
|
||||
locals-take-precedence rule.
|
||||
|
||||
End-to-end gate met: `examples/test_22b3_mono_synthetic.ail.json`
|
||||
declares `class Foo a where foo : (a) -> Int` + `instance Foo Int
|
||||
where foo = lam i. i` + `main = do io/print_int (foo 5)`. `ail
|
||||
run` produces stdout `5\n`. All three bench gates green:
|
||||
`bench/check.py` 63/63, `bench/compile_check.py` 24/24,
|
||||
`bench/cross_lang.py` 25/25 — mono pass is genuinely no-op for
|
||||
class-free fixtures.
|
||||
|
||||
Mono-symbol naming format decision: spec recommended
|
||||
`<method>#<typename>` ("but the implementer chooses"). `#` turns
|
||||
out to be invalid in LLVM IR global identifiers — clang rejected
|
||||
`@ail_..._foo#Int_clos`. Switched to `<method>__<typename>`
|
||||
(double underscore separator); same legibility, valid C-ABI.
|
||||
DESIGN.md / spec amendment in the next sweep.
|
||||
|
||||
Per-task subjects:
|
||||
|
||||
- iter 22b.3.1: mono pass skeleton — identity for class-free workspaces
|
||||
- iter 22b.3.2: mono_symbol — primitive surface forms + hash for compound
|
||||
- iter 22b.3.3: collect_mono_targets — synth-replay residual gathering
|
||||
- iter 22b.3.4: synthesise_mono_fn — instance body + class default fallback
|
||||
- iter 22b.3.5: workspace fixpoint loop — dedup + synth-append (multi-round chained-call coverage)
|
||||
- iter 22b.3.6: call-site rewrite — same/cross module qualified names (shadow-aware)
|
||||
- iter 22b.3.7: synthetic fixture — class+instance compiles, runs, prints 5
|
||||
- iter 22b.3.tester: e2e for coherence (two instances), default keyword, cross-module mono
|
||||
|
||||
Each task ran through the two-stage review (spec compliance →
|
||||
code quality). Quality round caught: shadow-blind walker (Task
|
||||
6), untested cross-module branch (Task 6), untested zero-arg
|
||||
method branch (Task 4), untested multi-round chained-call case
|
||||
(Task 5), defensive `unwrap_or_default` (Task 3), speculative
|
||||
`Float` arm (Task 2), and module-doc-vs-implementation drift
|
||||
(Task 1). All caught + fixed before iteration close.
|
||||
|
||||
**Plan-text defects surfaced during execution** (collected here
|
||||
so a 22-arc audit / next-iter brainstorm can act on them):
|
||||
|
||||
- The plan's RED test for Task 5 used `Term::Seq` with `lhs:
|
||||
Str`, but Seq requires `lhs: Unit`. Implementer substituted
|
||||
`Term::Let { name: "_a", ... }`. The spec/plan template needs
|
||||
a "discard-and-continue" idiom note.
|
||||
- The plan's fixpoint loop scaffold filtered targets only across
|
||||
rounds, not within a round. Two same-type call sites in round
|
||||
1 would both append; implementer added `seen_this_round`.
|
||||
- `Float` was speculatively included in `mono_symbol`'s primitive
|
||||
table — `Float` is not in the language. Removed.
|
||||
- Pre-existing `examples/test_22b2_*` fixtures had bare-Lit
|
||||
instance bodies for arrow-typed methods. Worked under 22b.1/2
|
||||
because instance-body typecheck is deferred follow-up.
|
||||
Mono-pass synth requires Lam-shaped bodies; affected fixtures
|
||||
Lam-wrapped during this iter (`test_22b2_instance_present`,
|
||||
`test_22b2_xmod_classmod`). Instance-body typecheck remains
|
||||
deferred — that's the right place to catch this user-side.
|
||||
|
||||
**Known debt / out-of-scope deferrals:**
|
||||
|
||||
- Form-B prose projection for `Def::Class` / `Def::Instance`
|
||||
remains deferred to 22b.4. `crates/ailang-surface/tests/round_trip.rs`
|
||||
skip-list excludes `test_22b2_*` and `test_22b3_*`.
|
||||
- DESIGN.md mono-symbol naming amendment (`#` → `__`) needs a
|
||||
small edit in 22b.4 alongside the Prelude wiring.
|
||||
- Primitive-name set is hard-coded in three sites
|
||||
(`linearity.rs`, `lib.rs` x2, `mono.rs`). Reviewer flagged the
|
||||
duplication; consolidation deferred to milestone-22 audit.
|
||||
- `pub mod mono` instead of `mod mono; pub use mono::...;`
|
||||
pattern was the deliberate plan-level call (multiple pub items
|
||||
reach by integration tests). Audit may revisit if the
|
||||
convention drifts further.
|
||||
|
||||
**Next: 22b.4 — Prelude module + Form-B parser/printer arms.**
|
||||
Prelude declares `Show`, `Eq`, `Ord` over `Int`, `Bool`, `Str`
|
||||
(default-bearing where Decision 11 mandates), `print` re-routes
|
||||
through `Show.show`, Form-B class/instance prose lands, round-trip
|
||||
filter retires. End-to-end gate: any prior `print 42`-style fixture
|
||||
must keep working through `Show.show#Int`.
|
||||
|
||||
Reference in New Issue
Block a user