832375f2ac
All 176 files in the four accumulating directories now use a zero-padded 4-digit counter prefix that reflects creation order (`NNNN-slug.md`). The counter is assigned per directory in strict git-log creation order; ties broken alphabetically by original name. The old `YYYY-MM-DD-` prefix on docs/specs/ and docs/plans/ files is dropped — the date is recoverable from git log and the counter carries the ordering. A file's counter is stable for the life of the file: never reassigned, never reused, never compacted. Deleted files retire their counter; subsequent files do not fill the gap. This is the property that lets cross-references stay literal — refs use the full filename including the counter (`design/contracts/0007-honesty-rule.md`) so they grep cleanly and resolve directly without a glob step. 313 cross-references updated across .md/.rs/.toml/.c/.json files (test pins, include_str! paths, design-INDEX entries, baseline notes, runtime C comments, inter-contract markdown links incl. bare basename and `../models/foo.md` forms). CLAUDE.md gets a new "File-naming convention" section spelling out the rule and rationale. skills/brainstorm/SKILL.md and skills/planner/SKILL.md updated so new spec/plan creation produces counter-prefixed names from the start. The full test suite (cargo test --workspace) passes.
409 lines
21 KiB
Markdown
409 lines
21 KiB
Markdown
# 23 — Eq / Ord Prelude — Design Spec (revised)
|
||
|
||
**Date:** 2026-05-11
|
||
**Status:** Draft — awaiting user spec review
|
||
**Authors:** Brummel (orchestrator) + Claude
|
||
|
||
**Supersedes:** `docs/specs/2026-05-10-23-eq-ord-prelude.md` (retired
|
||
2026-05-11 in iter gc.1; commit `f6d4ba3`). Iter 23.1–23.3 shipped
|
||
under the original spec and remain in effect; this revised spec
|
||
re-frames the remaining work on a corrected architecture.
|
||
|
||
## Goal
|
||
|
||
Complete milestone 23 by shipping the five free top-level utility
|
||
functions `ne`, `lt`, `le`, `gt`, `ge` in the auto-loaded prelude,
|
||
plus the E2E and DESIGN.md amendments that close the Eq/Ord
|
||
surface. Establish the partial-Eq/Ord story for `Float` as a lived
|
||
reality: `Float` has neither instance, so polymorphic `eq x y` /
|
||
`compare x y` over a Float fires `NoInstance` at typecheck — exactly
|
||
what DESIGN.md §"Float semantics" prescribes.
|
||
|
||
The re-brainstorm exists because the original spec's architecture
|
||
section silently assumed that polymorphic free fns with class
|
||
constraints would be mono-specialised the same way class methods
|
||
are. They are not — the typecheck-time mono pass
|
||
(`crates/ailang-check/src/mono.rs`, iter 22b.3) operates only on
|
||
class methods, while polymorphic free fns are specialised at codegen
|
||
time by `crates/ailang-codegen/src/lib.rs::lower_polymorphic_call`
|
||
(iter 12b). A polymorphic free fn whose body invokes class methods
|
||
falls between the two passes. Iter 23.4 BLOCKED three times on this
|
||
seam.
|
||
|
||
The corrected architecture unifies the two specialisers (see
|
||
Architecture below) and ships the remaining free fns on top of
|
||
the unified pass.
|
||
|
||
**Prep-commit status (verified against main 2026-05-11 after
|
||
iter/23.4 branch teardown):** prep1 (linearity + checker
|
||
bare-name) landed on main on 2026-05-11 via cherry-pick from the
|
||
stranded iter/23.4 branch; it stays load-bearing. prep2 and prep3
|
||
(codegen bare-name fall-throughs) were attempted on the stranded
|
||
iter/23.4 branch and *never reached main*; with iter/23.4 deleted,
|
||
they are abandoned as git history without a rollback step. Their
|
||
architectural intent — make codegen handle bare polymorphic
|
||
free-fn names directly — is superseded by the mono-unification.
|
||
The unified pass rewrites bare polymorphic call sites to mono
|
||
symbols before codegen runs, making the fall-throughs structurally
|
||
unnecessary.
|
||
|
||
`Show`, operator routing, `print`-rewire, and the heap-Str ABI
|
||
remain out of scope; each is its own milestone with substantive
|
||
reasons (see "Out of scope" — unchanged from original spec).
|
||
|
||
## Architecture
|
||
|
||
The milestone no longer consumes the 22b.3 typeclass machinery
|
||
unchanged. Three artefacts:
|
||
|
||
### 1. Mono-Pass unification (the corrected centrepiece)
|
||
|
||
`crates/ailang-check/src/mono.rs::monomorphise_workspace` is
|
||
restructured into a single specialiser for every
|
||
`Type::Forall`-quantified `Def::Fn` whose call sites supply
|
||
fully-concrete substitutions. Two thin source-body entry points:
|
||
|
||
- **Class-method entry:** look up the resolved instance body via
|
||
`Registry::entries[(class, type-hash)]`. Unchanged behaviour from
|
||
iter 22b.3.
|
||
- **Free-fn entry:** take the source body directly from the
|
||
polymorphic `Def::Fn`, apply rigid-var substitution.
|
||
|
||
Shared core mechanics — identical for both arms — are: rigid-var
|
||
substitution via `crate::substitute_rigids`, fixpoint loop (`mono.rs`
|
||
line 88) that keeps collecting targets until a round adds nothing
|
||
new, dedup cache keyed by `(name, type-hash)`, and the
|
||
call-site-rewrite walker.
|
||
|
||
Output: synthesised `Def::Fn` entries with naming
|
||
`<name>__<typesurfacename>` (extended to
|
||
`<name>__<type1>__<type2>__…` for N-ary type vars in declaration
|
||
order; concrete format is an Open commitment), appended to the
|
||
defining module. The mono pass becomes the only specialiser; codegen
|
||
sees no class machinery and no polymorphic call sites.
|
||
|
||
The current codegen-time specialiser is retired in the same iter:
|
||
`crates/ailang-codegen/src/lib.rs::lower_polymorphic_call`,
|
||
`module_polymorphic_fns` (the per-module FnDef map), and `mono_queue`
|
||
(the deduplicated emission queue) are removed. All today's call
|
||
sites of `lower_polymorphic_call` (lines 1944, 1972) become direct
|
||
calls to mono symbols after the typecheck-mono walker has rewritten
|
||
them.
|
||
|
||
### 2. Prelude AILang module
|
||
|
||
`examples/prelude.ail.json` already contains, from iters 23.1–23.3:
|
||
the `Ordering` ADT, the `Eq` class with `Eq Int`/`Bool`/`Str`
|
||
instances, the `Ord` class with `Ord Int`/`Bool`/`Str` instances.
|
||
Iter 23.5 adds the five free fns:
|
||
|
||
- `ne : forall a. Eq a => (a borrow, a borrow) -> Bool`, body
|
||
`not (eq x y)`.
|
||
- `lt`, `le`, `gt`, `ge`: `forall a. Ord a => (a borrow, a borrow) -> Bool`,
|
||
bodies pattern-matching `compare` against the appropriate
|
||
`Ordering` shapes.
|
||
|
||
### 3. C-runtime functions
|
||
|
||
`runtime/str.c` shipped its two functions in iter 23.2 and 23.3:
|
||
`ail_str_eq(a, b) -> bool` (byte-equality on constant-string layouts)
|
||
and `ail_str_compare(a, b) -> i32` (lex comparison returning
|
||
-1/0/+1). No further runtime work.
|
||
|
||
Existing primitive operators (`==`, `<`, `<=`, `>`, `>=`) remain
|
||
primitive and per-type-routed at lowering, unchanged. Routing them
|
||
through the class methods is the declared P2 follow-up; the
|
||
redundancy between primitive `==` and class `eq x y` on
|
||
Int/Bool/Str is acknowledged and ratified as transitional.
|
||
|
||
## Components (iterations)
|
||
|
||
| Iter | Status | Scope |
|
||
|------|--------|-------|
|
||
| 23.1 | shipped (2026-05-10) | `Ordering` ADT + prelude skeleton + auto-load via `check_workspace`. |
|
||
| 23.2 | shipped (2026-05-10) | `class Eq` + `Eq Int/Bool/Str` + `ail_str_eq`. |
|
||
| 23.3 | shipped (2026-05-10) | `class Ord extends Eq` + `Ord Int/Bool/Str` + `ail_str_compare`. |
|
||
| 23.4-prep | shipped to main 2026-05-11 (cherry-picked from stranded iter/23.4), **kept** | commits on main: `0caaced` (linearity registers `Def::Class` method types in globals) + `923dd8c` (check bare-name fall-through to implicit-imported free fns). Both resolve names *before* mono; independent of B1. |
|
||
| 23.4-prep2 / 23.4-prep3 | **abandoned 2026-05-11 with iter/23.4 deletion** — never reached main | Attempted-but-stranded commits were `a06159d` + `c42a0f5` (codegen bare-name fall-throughs); they were anticipatory patches for an approach (codegen-time bare-name handling) the mono-unification supersedes. No rollback step is needed because no rollback target exists on main. |
|
||
| **23.4** (revised) | upcoming | **Mono-Pass unification.** Restructure `crates/ailang-check/src/mono.rs::monomorphise_workspace` into one specialiser with two source-body entry points (class-method via `Registry::entries`, free-fn direct). Extend `collect_mono_targets` to also produce targets for polymorphic free-fn calls with fully-concrete substitutions. Remove `lower_polymorphic_call` + `module_polymorphic_fns` + `mono_queue` from codegen. Amend DESIGN.md §"Resolution and monomorphisation" to describe the pass as covering all `Type::Forall`-Defs. Tests: existing polymorphic fixtures (`poly_id`, `poly_apply`, `poly_rec_capture`) green; new fixture `cmp_max_smoke` proves cross-cutting (`cmp_max : forall a. Ord a => (a, a) -> a` at Int synthesises both `cmp_max__Int` and nested `compare__Int` in one fixpoint). |
|
||
| **23.5** (revised) | upcoming | **Prelude free fns + E2E.** Add `ne`/`lt`/`le`/`gt`/`ge` to `examples/prelude.ail.json`. E2E fixtures: (a) positive polymorphic helper at Int/Bool/Str, (b) negative — `eq f g` at Float typecheck-rejects with Float-aware diagnostic, (c) user-ADT — `IntBox` with `Eq`/`Ord` instances + polymorphic helper monomorphises to the user instance. Amend DESIGN.md §"Decision 11 / Prelude classes" — Eq/Ord shipped; the "Milestone 22 ships no built-in Prelude classes" wording stays as historical accuracy with a forward-pointing addendum. Roadmap: P1 Post-22 Prelude entry from `[~]` to `[x]` for Eq/Ord half; Show half stays. |
|
||
|
||
## Data flow
|
||
|
||
Two trajectories show the unification at work.
|
||
|
||
### Class-method call `(eq x y)` with `x : Int` (unchanged)
|
||
|
||
1. **Typecheck.** `eq` resolves to
|
||
`forall a. Eq a => (a borrow, a borrow) -> Bool`. Constraint solver
|
||
matches `Eq Int` against `instance Eq Int`; constraint
|
||
discharged.
|
||
2. **Mono (class-method entry).** `Registry::entries[(Eq, Int)]`
|
||
lookup, body from `InstanceMethod`, rigid-var subst `a → Int`,
|
||
synth `eq__Int`. Call site `eq x y` → `eq__Int x y`.
|
||
3. **Codegen.** `eq__Int` lowers to `icmp eq i64` + `zext i1 i8`.
|
||
|
||
### Polymorphic free-fn call `(ne x y)` with `x : Int` (new with B1)
|
||
|
||
1. **Typecheck.** `ne` resolves to a polymorphic `Def::Fn` with
|
||
`forall a. Eq a => (a borrow, a borrow) -> Bool`. Bare-name lookup
|
||
reaches the prelude via prep1's `env.module_globals` fall-through.
|
||
Constraint `Eq Int` discharged as above.
|
||
2. **Mono (free-fn entry).** Body taken directly from
|
||
`Def::Fn.body`, rigid-var subst `a → Int`, synth `ne__Int`. In
|
||
the same fixpoint round, the body walker detects the nested
|
||
`eq` call at concrete `Int` and schedules it as a class-method
|
||
target. The next round synthesises `eq__Int` (or cache-hits).
|
||
`ne__Int`'s body refers directly to `eq__Int`. Call site
|
||
`ne x y` → `ne__Int x y`.
|
||
3. **Codegen.** `ne__Int` lowers as any monomorphic fn would; the
|
||
nested `eq__Int` is a direct call. No `lower_polymorphic_call`
|
||
path involved — the codegen-time specialiser no longer exists.
|
||
|
||
The fixpoint loop in `monomorphise_workspace` (line 88) already
|
||
iterates until a round adds nothing new. The change is in the
|
||
target collector: it must additionally emit `MonoTarget`s for
|
||
"call site to polymorphic free-fn `Def::Fn` with fully-concrete
|
||
substitution", not only for class-constraint residuals.
|
||
|
||
## Error handling
|
||
|
||
Unchanged from original spec:
|
||
|
||
- **`NoInstance Eq Float` / `NoInstance Ord Float`** — fired at
|
||
typecheck when class resolution selects Float. Diagnostic must
|
||
cross-reference DESIGN.md §"Float semantics" so the LLM-author
|
||
immediately learns the partial-Float story.
|
||
- **`NoInstance Eq <UserType>` / `NoInstance Ord <UserType>`** —
|
||
re-uses 22's existing diagnostic path.
|
||
- **`OrphanInstance`** — Decision 11 unchanged. Prelude instances
|
||
are non-orphan.
|
||
|
||
New categories under B1:
|
||
|
||
- **Polymorphic-free-fn specialisation failures** are
|
||
`CheckError::Internal`. The mono pass runs post-typecheck, so
|
||
every substitution must have been validated by then. A failure
|
||
here is a caller-contract violation; mirrors the existing
|
||
convention in `synthesise_mono_fn` (mono.rs line 549).
|
||
- **Recursive polymorphic free fns with concrete substitutions** —
|
||
the existing fixpoint loop closes transitively, same mechanism
|
||
used for class-method bodies calling other class methods (e.g.
|
||
`default ne x y = not (eq x y)`).
|
||
- **Codegen-side errors remain not expected.** The unification
|
||
reduces codegen's responsibility (removes
|
||
`lower_polymorphic_call`), it does not expand it. Any
|
||
fall-through that survives must be
|
||
`CodegenError::Internal`-shaped per the iter-4.4 Floats precedent
|
||
— never `unimplemented!()` panic.
|
||
|
||
## Testing strategy
|
||
|
||
### 23.4 — Mono unification
|
||
|
||
- **Regression: existing polymorphic fixtures stay green.**
|
||
`examples/poly_id.ail.json`, `examples/poly_apply.ail.json`,
|
||
`examples/poly_rec_capture.ail.json`, and every 22b.3 typeclass
|
||
fixture must compile and run unchanged. These are the
|
||
load-bearing proof that codegen-time specialisation was
|
||
structurally replaceable.
|
||
- **New: composition fixture.** `examples/cmp_max_smoke.ail.json`
|
||
with `fn cmp_max : forall a. Ord a => (a, a) -> a` (body:
|
||
`match compare x y with LT -> y | _ -> x`), invoked at Int. The
|
||
test asserts that after mono, the workspace contains both
|
||
`cmp_max__Int` and `compare__Int` as `Def::Fn`, both produced
|
||
by the unified pass. This pins the cross-cutting case that
|
||
iter-23.4-prep3 unsuccessfully tried to patch.
|
||
- **Hash-stability of existing mono symbols.** `eq__Int`,
|
||
`compare__Str`, and the four other already-synthesised symbols
|
||
must produce identical bodies (and therefore identical hashes)
|
||
under the unified pass. Pin test in the style of
|
||
`crates/ailang-core/src/hash.rs::ct4_migrated_fixtures_have_canonical_form_hashes`.
|
||
- **Rollback proof.** `cargo test --workspace` green after removal
|
||
of `lower_polymorphic_call` + `module_polymorphic_fns` +
|
||
`mono_queue`. No test relies on the removed symbols.
|
||
- **Bench regression check.** `bench/check.py` /
|
||
`bench/compile_check.py` re-run after 23.4. The mono pass does
|
||
more work (two entry points, larger target sets per round); a
|
||
measurable compile-time shift is expected and ratified per audit
|
||
convention or rebaselined.
|
||
|
||
### 23.5 — Prelude free fns + E2E
|
||
|
||
- **Workspace tests per free fn:** `ne`, `lt`, `le`, `gt`, `ge`
|
||
produce correct `Bool` on Int/Bool/Str, agree with their
|
||
primitive-operator counterparts on a small smoke suite.
|
||
- **Positive E2E** `examples/eq_ord_polymorphic.ail.json`:
|
||
polymorphic helper invoked at three primitives, prints expected
|
||
output, compiles and runs end-to-end.
|
||
- **Negative E2E:** `eq f g` at Float typecheck-rejects with a
|
||
Float-aware diagnostic; gold-standard test pins the message
|
||
verbatim.
|
||
- **User-ADT integration:** `examples/eq_ord_user_adt.ail.json` —
|
||
`data IntBox = MkIntBox Int` + `instance Eq IntBox` +
|
||
`instance Ord IntBox` + polymorphic helper monomorphises to the
|
||
user instances.
|
||
- **Round-trip:** prelude module + every new fixture passes
|
||
Form-A parser + canonicaliser bit-stable.
|
||
- **Bench regression check** at milestone close: the three bench
|
||
scripts exit 0 or audit-ratified.
|
||
|
||
## Acceptance criteria
|
||
|
||
The milestone closes when:
|
||
|
||
1. **23.4 has landed:** `crates/ailang-check/src/mono.rs::monomorphise_workspace`
|
||
synthesises both class-method and polymorphic-free-fn mono Defs
|
||
in one fixpoint loop. `crates/ailang-codegen/src/lib.rs::lower_polymorphic_call` +
|
||
`module_polymorphic_fns` + `mono_queue` are removed. (prep2 +
|
||
prep3 were never on main; no rollback step in scope.)
|
||
2. **DESIGN.md §"Resolution and monomorphisation" amended:** the
|
||
pass description covers all `Type::Forall`-quantified Defs, not
|
||
only class methods.
|
||
3. **23.5 has landed:** `examples/prelude.ail.json` contains `ne`,
|
||
`lt`, `le`, `gt`, `ge` with correct constraint signatures and
|
||
bodies.
|
||
4. **DESIGN.md §"Decision 11 / Prelude classes" amended:** reflects
|
||
Eq/Ord shipping in milestone 23; the "Milestone 22 ships no
|
||
built-in Prelude classes" wording stays as historical accuracy
|
||
with a forward-pointing addendum.
|
||
5. **Tests pass:** `cargo test --workspace` green; existing
|
||
polymorphic fixtures (`poly_id`, `poly_apply`, `poly_rec_capture`)
|
||
unchanged; new composition fixture (`cmp_max_smoke`) green; E2E
|
||
fixtures from 23.5 green.
|
||
6. **Bench regression ratified:** `bench/check.py && bench/compile_check.py && bench/cross_lang.py`
|
||
exit 0 or audit-ratified with journal entry.
|
||
7. **Surface acceptance from original spec:** a polymorphic helper
|
||
`forall a. Ord a => (a, a) -> Bool` compiles, monomorphises at
|
||
three primitive types, runs, and prints the expected output for
|
||
at least Int and Str; the same helper on Float fires
|
||
`NoInstance Ord Float` at typecheck with a Float-aware
|
||
diagnostic.
|
||
8. **Roadmap update:** P1 Post-22 Prelude entry from `[~]` to `[x]`
|
||
for Eq/Ord half; Show half stays as a separate entry.
|
||
|
||
## Out of scope (deferred, with substantive rationale)
|
||
|
||
Unchanged from original spec:
|
||
|
||
- **`Show` typeclass.** Defers behind a heap-Str-ABI milestone.
|
||
`instance Show Int` needs `int_to_str` to allocate a heap-Str —
|
||
a runtime primitive AILang does not have today.
|
||
- **Operator routing through `Eq` / `Ord`.** `==`, `<`, etc. stay
|
||
primitive this milestone. Routing them through the class methods
|
||
is the declared P2 todo, gated on bench rebaseline confirmation.
|
||
- **`print`-rewire through `Show.show`.** Same heap-Str dependency.
|
||
- **`Eq` / `Ord` on Float.** No instance per DESIGN.md §"Float
|
||
semantics". A future iter exposing a partial
|
||
`compare : Float -> Float -> Maybe Ordering` is a separate
|
||
brainstorm.
|
||
- **`deriving (Eq, Ord)` for user ADTs.** No auto-derivation.
|
||
- **`Ord Ordering` instance.** Cute but no concrete demand.
|
||
|
||
New under B1 unification:
|
||
|
||
- **Higher-rank polymorphism.** Stays out of scope per DESIGN.md
|
||
§"Polymorphism via Type::Forall" (line 2138-2140). The unified
|
||
mono pass requires fully-concrete substitution at each call site;
|
||
the same constraint as today, not weakened.
|
||
- **Polymorphic constants** (`Type::Forall` on `ConstDef`). The mono
|
||
pass operates on `Def::Fn`; `const_as_pseudo_fn` (mono.rs line
|
||
261) is the only `ConstDef` bridge and stays unchanged.
|
||
- **User-customisable mono-symbol naming.** The naming format is
|
||
implementation-finalised in 23.4; no user-facing knob.
|
||
|
||
## Open commitments (23.4 / 23.5 implementer)
|
||
|
||
- **Mono-symbol naming for N-ary type vars.** Today's class-method
|
||
naming `<method>__<typesurfacename>` is single-type-var.
|
||
Polymorphic free fns can have N type vars (e.g. `poly_apply : forall a b.`).
|
||
Default proposal: `<name>__<typename1>__<typename2>__…` in
|
||
Forall-vars declaration order, hash-stable, covered by the
|
||
canonical-type-names layer. Implementer may raise an alternative
|
||
via `NEEDS_CONTEXT`.
|
||
- **Target-collector heuristic.** `collect_mono_targets` (mono.rs
|
||
line 422) today filters on residual class-constraints. For
|
||
polymorphic free fns, the collector must additionally detect
|
||
"call site to polymorphic free-fn `Def::Fn` with fully-concrete
|
||
substitution, regardless of constraints". Implementer decides
|
||
between extending the existing `synth`-residual path or adding a
|
||
separate walker; both shapes are structurally viable.
|
||
- **Original Free-Fn `Def::Fn` retention post-mono.** `Def::Class` /
|
||
`Def::Instance` are kept in the workspace after mono (mono.rs
|
||
line 40-42, for `ail describe`). Polymorphic Free-Fn `Def::Fn`s
|
||
should be kept analogously — both the source-Def and the
|
||
synthesised mono-Defs visible.
|
||
- **Float-NoInstance diagnostic wording.** Implementer drafts a
|
||
one-line Float-aware message; orchestrator reviews in
|
||
spec-compliance phase.
|
||
|
||
## Known costs
|
||
|
||
- **23.4 mono refactor:** estimated +150 / −50 LOC in
|
||
`crates/ailang-check/src/mono.rs` (target-collector extension,
|
||
source-body entry points, naming helper).
|
||
- **23.4 codegen cleanup:** estimated −200 LOC in
|
||
`crates/ailang-codegen/src/lib.rs` (`lower_polymorphic_call`,
|
||
`module_polymorphic_fns` fields, `mono_queue` machinery). Net
|
||
code reduction expected.
|
||
- **23.5 prelude additions:** `examples/prelude.ail.json` +30-40
|
||
lines (five free fns with Forall-constraint signatures and
|
||
bodies). Three new E2E `.ail.json` fixtures, ~60 lines JSON.
|
||
Diagnostic test ~20 lines Rust.
|
||
- **Compile-time shift:** mono pass does more work (additional
|
||
body walks over free fns, potentially larger fixpoint
|
||
convergence). Quantified by `bench/compile_check.py` post-23.4;
|
||
audit-ratified or rebaselined.
|
||
- **DESIGN.md amendments:** §"Resolution and monomorphisation"
|
||
(line 1513+) extends to cover polymorphic free fns; §"Decision
|
||
11 / Prelude classes" (line 1659+) reflects Eq/Ord shipping.
|
||
|
||
## Load-bearing assumptions about current behaviour
|
||
|
||
Surfaced explicitly so the Step 7.5 grounding-check agent has a
|
||
canonical list to ratify. Each assumption is something this spec
|
||
relies on as currently-true.
|
||
|
||
1. `crates/ailang-check/src/mono.rs::monomorphise_workspace`
|
||
today is the only typecheck-time specialiser, and it operates
|
||
exclusively on class-method residuals (no free-fn handling).
|
||
2. `crates/ailang-codegen/src/lib.rs::lower_polymorphic_call`
|
||
today is the only codegen-time specialiser, and it operates
|
||
exclusively on polymorphic free-fn calls (no class-method
|
||
handling).
|
||
3. The mono pass produces `Def::Fn` entries in the workspace; the
|
||
codegen-time specialiser emits LLVM symbols without
|
||
corresponding workspace Defs.
|
||
4. `examples/prelude.ail.json` today contains no free fns (only
|
||
ADT, classes, instances).
|
||
5. prep2 and prep3 (the codegen bare-name fall-through patches)
|
||
*never reached main*. They lived on the stranded iter/23.4
|
||
branch which was deleted on 2026-05-11. The current main
|
||
contains no codegen bare-name fall-through for polymorphic
|
||
free-fn calls — codegen rejects bare polymorphic free-fn calls
|
||
today, which is exactly what makes the original iter-23.4 BLOCK.
|
||
6. Commits on main `0caaced` (prep.1, linearity registers `Def::Class`
|
||
method types in globals) and `923dd8c` (prep.2, check bare-name
|
||
fall-through to implicit-imported free fns) operate at
|
||
typecheck / linearity time — before any mono pass — and are
|
||
load-bearing for any prelude free-fn resolution regardless of
|
||
mono architecture. (Originals from stranded branch: `8d39f13`
|
||
and `aef4ab8`; cherry-picked onto main 2026-05-11.)
|
||
7. `crates/ailang-codegen/src/lib.rs::lower_polymorphic_call` is
|
||
invoked from two call sites today (lines 1944, 1972), both of
|
||
which become direct mono-symbol calls after 23.4.
|
||
8. `examples/poly_id.ail.json` and `examples/poly_apply.ail.json`
|
||
today rely on the codegen-time specialiser path; under B1 they
|
||
transition to the typecheck-time mono pass without surface
|
||
behavioural change.
|
||
9. The mono pass's fixpoint loop (mono.rs line 88) iterates until a
|
||
round adds nothing new, and already supports transitive
|
||
specialisation across nested class-method calls; the same
|
||
mechanism handles a free-fn body whose specialisation triggers
|
||
a class-method specialisation.
|
||
10. The canonical-form layer (per
|
||
`docs/specs/0007-canonical-type-names.md`, ct.4 closure)
|
||
treats mono Defs as post-typecheck artefacts not in
|
||
`CheckedModule.symbols`, so renaming or extending mono symbols
|
||
does not affect surface-level hash pins.
|