diff --git a/docs/JOURNAL.md b/docs/JOURNAL.md index 05b62a5..2ea4b8c 100644 --- a/docs/JOURNAL.md +++ b/docs/JOURNAL.md @@ -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.