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
|
remaining open follow-ups from 2026-05-09 (skill bugs surfaced by
|
||||||
real iter use) get queued as targeted SKILL.md tweaks rather than
|
real iter use) get queued as targeted SKILL.md tweaks rather than
|
||||||
a wholesale revisit.
|
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