iter 23.2.5: journal entry — Eq class + primitive instances
This commit is contained in:
+123
@@ -13944,3 +13944,126 @@ tests + the new ail-crate E2E + the new workspace-loader tests,
|
||||
all green. Cross-language and the three regression scripts not
|
||||
re-run for this iter (no codegen-shape change to the existing
|
||||
fixtures); they'll run at milestone-23 close.
|
||||
|
||||
## 2026-05-10 — Iteration 23.2: Eq class + three primitive instances
|
||||
|
||||
Second iteration of milestone 23. Ships `class Eq a where eq : (a
|
||||
borrow, a borrow) -> Bool` in the auto-loaded prelude plus three
|
||||
primitive instances (Eq Int, Eq Bool, Eq Str). A user program that
|
||||
calls `eq x y` on any of those three primitives now monomorphises
|
||||
to `eq__Int` / `eq__Bool` / `eq__Str` synthesised in the prelude
|
||||
module and codegen lowers them as:
|
||||
|
||||
- `eq__Int` → `icmp eq i64` via existing `lower_eq` Int arm.
|
||||
- `eq__Bool` → `icmp eq i1` via existing `lower_eq` Bool arm.
|
||||
- `eq__Str` → `call zeroext i1 @ail_str_eq(...)` via a new codegen
|
||||
intercept (`try_emit_primitive_instance_body` in
|
||||
`crates/ailang-codegen/src/lib.rs`).
|
||||
|
||||
The Str path is the only one needing new mechanism: a hand-rolled
|
||||
body in `emit_fn` that bypasses normal lambda lowering and calls
|
||||
the new C-runtime primitive `ail_str_eq` from `runtime/str.c`.
|
||||
`runtime/str.c` is linked unconditionally for every alloc strategy
|
||||
(Gc, Bump, Rc) — the `@ail_str_eq` IR declaration is unconditional
|
||||
so the symbol must always resolve at link time; clang -O2 dead-
|
||||
strips it when no caller exists. `==` on Str stays on `@strcmp +
|
||||
icmp eq i32 0` this iter; only the mono-synthesised `eq__Str`
|
||||
goes through `@ail_str_eq`. Routing primitive `==` through the
|
||||
class methods is the declared P2 follow-up.
|
||||
|
||||
**Two latent bugs surfaced.** Adding `class Eq` to the prelude
|
||||
flipped the `workspace_has_typeclasses` gate from false to true
|
||||
on every test workspace, exposing two long-dormant gaps:
|
||||
|
||||
1. **`mono::build_workspace_env` ctor_index was workspace-flat.**
|
||||
`check_in_workspace` clears the flat ctor_index after
|
||||
`build_check_env` and rebuilds it per-module (lib.rs:1257-1287)
|
||||
so `Pattern::Ctor` resolution at lib.rs:2486-2526 produces a
|
||||
qualified `resolved_type_name` via the imports-fallback that
|
||||
matches the scrutinee's qualified `Type::Con`. The mono entry
|
||||
points (`collect_targets_workspace_wide`, the phase-3 rewrite
|
||||
loop) consumed the flat-index env directly, so cross-module
|
||||
`Cons` patterns resolved against bare `"List"` and mismatched
|
||||
the qualified `"std_list.List"`. Fix in `84dcc46`: per-module
|
||||
ctor_index overlay helper in mono.rs, applied at both sites.
|
||||
Empirically `env.types` also had to be overlayed (the carrier's
|
||||
"asymmetry" note was wrong — kept flat, the Term::Ctor and
|
||||
Pattern::Ctor synth paths disagree on bare-vs-qualified
|
||||
names). RED test: `crates/ail/tests/mono_xmod_ctor_pattern.rs`
|
||||
(`e580f75`). Same family as commits 13b36cc / 5c5180f.
|
||||
|
||||
2. **Codegen `lower_workspace_inner` import_map missed implicit
|
||||
prelude.** Symmetric to the typechecker's implicit-prelude
|
||||
injection at `build_check_env` / `check_in_workspace` (Iter
|
||||
23.1.3). When Iter 22b.3 monomorphisation rewrites a user
|
||||
call `eq x y` to the cross-module symbol `prelude.eq__Int`,
|
||||
`lower_call`'s prefix resolution looked up `"prelude"` in the
|
||||
codegen import_map and found it missing — error
|
||||
`cross-module call 'prelude.eq__Int': prefix 'prelude' not
|
||||
in import map`. Fix in `be882c4`: mirror the typechecker's
|
||||
guard (`m.name != "prelude"`) and inject the implicit entry.
|
||||
|
||||
**Four-site lockstep now confirmed.** Iter 23.1 flagged a
|
||||
three-site lockstep for implicit-prelude visibility (Pattern::Ctor
|
||||
15a / Term::Ctor typecheck / `lookup_ctor_by_type` codegen).
|
||||
Iter 23.2 just added the fourth site:
|
||||
`lower_workspace_inner` import_map. The lockstep table is now
|
||||
ripe for the architect-iron-law extension that already covers
|
||||
`lower_app ↔ is_static_callee` and `Pattern::Lit::Float reject ↔
|
||||
pre_desugar_validation`. Adding it next time the architect role
|
||||
takes an iter.
|
||||
|
||||
**Fixture rename ripple.** Six existing test fixtures used a
|
||||
local `class Eq` (some with `class Ord extends Eq`) for typeclass-
|
||||
machinery tests. With the prelude's `class Eq.eq` injected, the
|
||||
fixture-side methods collide on the global method-name uniqueness
|
||||
check (`MethodNameCollision`). Five out of six were currently
|
||||
visible failures (two surfaced first, three more after the
|
||||
mono ctor_index bug was fixed). Renamed the colliding classes
|
||||
and methods to `TEq` / `teq` / `TOrd` / `tlt` across the 6
|
||||
JSON fixtures (the prose snapshot for one of them too) and
|
||||
updated the corresponding test assertions in workspace.rs. The
|
||||
`iter22b1_workspace_with_no_classes_has_empty_registry` and
|
||||
`iter22b1_instance_in_class_module_loads_clean` tests were
|
||||
rewritten to filter `defining_module == "prelude"` rather than
|
||||
counting raw registry entries — they now assert "no NON-prelude
|
||||
entries" / "exactly one non-prelude entry", which is the
|
||||
property they always meant.
|
||||
|
||||
Coverage at iter close:
|
||||
- Codegen unit + integration tests: `eq__Str`-intercept-shape
|
||||
test, `eq__Str` closure-adapter test, three IR-shape tests
|
||||
on the smoke fixture (`@ail_prelude_eq__Int` + `icmp eq i64`,
|
||||
Bool / i1, Str / `call zeroext i1 @ail_str_eq`).
|
||||
- E2E: `examples/eq_primitives_smoke.ail.json` →
|
||||
`eq_primitives_smoke_compiles_and_runs` (full pipeline:
|
||||
AST → typecheck → mono → codegen → clang → binary → stdout
|
||||
`"1\n0\n1\n0\n1\n0\n"`).
|
||||
- Mono RED-test: `mono_xmod_ctor_pattern.rs` (guards the
|
||||
per-module ctor_index overlay against regression).
|
||||
- Prelude-load assertion extended (`loads_workspace_auto_injects_prelude`)
|
||||
to also check `class Eq` + Eq Int/Bool/Str instances.
|
||||
|
||||
Per-task commits:
|
||||
|
||||
- `cc2d694` iter 23.2.1: runtime/str.c with ail_str_eq + unconditional link
|
||||
- `736064a` iter 23.2.2: codegen — declare @ail_str_eq + eq__Str body intercept
|
||||
- `c6168ad` iter 23.2.2-fixup: emit closure adapter for primitive-instance-bodied fns
|
||||
- `1618182` iter 23.2.2-fixup-doc: tighten try_emit_primitive_instance_body contract doc
|
||||
- `e580f75` RED test (debug): mono_xmod_ctor_pattern.rs pins flat-ctor_index bug
|
||||
- `84dcc46` fix: mono.rs per-module env.ctor_index overlay (same family as 13b36cc / 5c5180f)
|
||||
- `7289bbc` iter 23.2.3-prep: reconcile workspace tests with auto-loaded prelude class Eq
|
||||
- `65ab6c6` iter 23.2.3-prep2: rename remaining two 22b2 fixtures + their prose snapshot (orchestrator-inline)
|
||||
- `7172e7e` iter 23.2.3: prelude — class Eq a + Eq Int/Bool/Str instances
|
||||
- `be882c4` fix: codegen lower_workspace_inner implicit prelude import (4th lockstep site)
|
||||
- `559e591` iter 23.2.4: e2e smoke fixture + ir-shape integration tests for eq__T mono symbols
|
||||
- `a11cb2f` iter 23.2.4-fixup: symmetrize check_workspace error assertion across three IR-shape tests
|
||||
|
||||
Out of scope for this iter (covered by later 23.x):
|
||||
- Ord class + three Ord instances + `ail_str_compare` (23.3).
|
||||
- Free top-level utility functions `ne` / `lt` / `le` / `gt` / `ge` (23.4).
|
||||
- Float-NoInstance diagnostic + DESIGN.md amendment (23.5).
|
||||
|
||||
Workspace at iter-23.2 close: full `cargo test --workspace` green
|
||||
(416 tests, 0 failed). Cross-language and the three regression
|
||||
scripts not re-run for this iter; they run at milestone-23 close.
|
||||
|
||||
Reference in New Issue
Block a user