iter 22b.1.4: JOURNAL entry — 22b.1 closed, 22b.2 next

Records the iter close: AST schema floor (Def::Class/Instance) +
workspace registry with three coherence checks (Orphan, Duplicate,
MissingMethod), seven new tests (288 → 295), all three bench gates
green at close. JOURNAL queue updated: 22b.1 closed, 22b.2 next
(typecheck arms + FnDef.type.constraints + class-schema validation).
This commit is contained in:
2026-05-09 12:42:23 +02:00
parent 057784934a
commit 4abb195fc8
+186
View File
@@ -10995,3 +10995,189 @@ Not re-run. No runtime, codegen, or check-time path is touched.
- **21'h, latency methodology, FnDef::synthetic, Boehm full
retirement, closure-pair slab/pool, deferred richer integration
paths.** Unchanged from 21'g.
## 2026-05-09 — Iter 22b.1: typeclass schema floor + workspace registry
Trigger: 22b is the typeclass implementer arc queued by 22a. The
arc is sliced into four sub-iters (22b.1 schema floor + registry,
22b.2 typecheck arms, 22b.3 monomorphisation, 22b.4 prelude +
prose-projection arms); 22b.1 is the schema floor. Per the 22a
queue, the spec is Decision 11 §"Form-A schema" + §"Resolution
and monomorphisation" (the three coherence checks).
### What 22b.1 shipped
**AST.** `Def::Class(ClassDef)` and `Def::Instance(InstanceDef)`
landed as additive variants of `Def`. The struct shapes follow
Decision 11 §"Form-A schema": single-string `param` (multi-param
classes rejected by shape), optional `superclass: Option<Superclass
Ref>`, methods carry full FnSig signatures and an
`Option<Term>` `default` body, instances carry the concrete
instance type (`Type` variant, not `String`) plus their method
bodies. Every optional field uses `skip_serializing_if` per the
13a/19b additive-schema pattern, so canonical-JSON bytes — and
therefore `def_hash` — of every pre-22b fixture stay bit-identical.
`iter22b1_schema_extension_preserves_pre_22b_hashes` re-asserts the
two pinned hashes (`sum.ail.json`/sum → `db33f57cb329935e`,
`list.ail.json`/IntList → `b082192bd0c99202`); the
same-shape-different-paths test
(`iter22b1_classdef_empty_optionals_hash_stable`) asserts that a
`ClassDef` parsed from JSON without the optional keys hashes
identically to one constructed with `superclass: None` / `doc:
None`.
**Downstream `match Def::*` sites.** Every match was extended with
explicit `Class`/`Instance` arms in `ailang-core` (desugar,
pretty), `ailang-check` (lib, lift, linearity, uniqueness),
`ailang-codegen` (lib), `ailang-prose` (lib), `ailang-surface`
(print), and `crates/ail/src/main.rs` (collect_refs, def_summary).
Behaviour for 22b.1 is placeholder: skip in typecheck/codegen,
one-line summary in pretty/manifest, placeholder marker in
prose/print. Each arm carries a TODO comment naming the
deferred sub-iter (22b.2 typecheck, 22b.3 codegen, 22b.4
prose-projection).
**Workspace registry.** `Workspace` gains a `registry: Registry`
field, populated at the end of `load_workspace` after the import
DFS. The registry is keyed by `(class-name, type-hash)` where
`type-hash` is a new `canonical::type_hash(t: &Type) -> String`
(16-hex prefix, parallel in shape to `def_hash` and `module_hash`).
`build_registry` enforces three coherence checks per Decision 11
§"Resolution and monomorphisation":
- **Coherence (orphan-freedom).** Every `instance C T` lives in
the module of `C` or in the module of `T`. Otherwise →
`WorkspaceLoadError::OrphanInstance` with the class, type,
defining module, and the modules where the class and type
actually live.
- **Uniqueness.** No two entries share a key. Otherwise →
`DuplicateInstance` with the two colliding modules.
- **Method completeness.** Each instance specifies a body for
every required (non-default) method of its class. Otherwise →
`MissingMethod` with the missing method name.
The CLI's `workspace_error_to_diagnostic` carries three new error
codes (`orphan-instance`, `duplicate-instance`, `missing-method`)
into the JSON-mode diagnostic stream of `ail check`.
**Test fixtures.** Seven fixtures under `examples/test_22b1_*`:
- `test_22b1_orphan_class.ail.json` — class + instance in same
module (positive; one registry entry).
- `test_22b1_orphan_third_classmod.ail.json` +
`test_22b1_orphan_third.ail.json` — class in module A, instance
in third module declaring `instance Show Int` (Int is primitive,
so neither leg of coherence is satisfied → OrphanInstance).
- `test_22b1_dup_a.ail.json` + `test_22b1_dup_b.ail.json` +
`test_22b1_dup_entry.ail.json` — module A defines class Show
+ instance Show MyInt (legal, A is class's module); module B
defines type MyInt + instance Show MyInt (legal, B is type's
module). Entry imports both → DuplicateInstance with two distinct
defining modules.
- `test_22b1_missing_method.ail.json` — class Eq with two
non-default methods (eq, ne); instance Eq Int specifies only
ne. Fires MissingMethod { method: "eq" }.
The duplicate-instance setup was the trickiest: A→B import
("class A needs to know type B") works; B does not need to import
A because the registry build happens over the whole workspace,
not per-module — B just declares the instance with a string class
name. No import cycle.
**Deviations from the plan.** The plan in
`docs/superpowers/plans/2026-05-09-22b.1-typeclass-schema-floor.md`
prescribed eight commits (one per task). Per the project's
iter-cadence convention (CLAUDE.md §"Iter cycle"), 22b.1 ships as
three commits: AST + downstream match arms (22b.1.1), workspace
registry skeleton + coherence checks + hash-stability tests
(22b.1.2), fixtures + workspace tests (22b.1.3). The JOURNAL
update is this commit (22b.1.4).
### Surface round-trip gate
The Form-B parser does not yet know `class` / `instance` head
keywords (parser arms are deferred to 22b.4 alongside the prose
projection). The round-trip test
(`crates/ailang-surface/tests/round_trip.rs`) iterates every
`examples/*.ail.json` and re-parses the printed text; without
a filter, `test_22b1_*` fixtures would block the gate on a
property the schema floor does not promise. The fixture filter
now excludes `test_22b1_*` until 22b.4 ratifies prose round-trip
for the new variants.
### Test state
288 → 295 (+7). New tests:
- `iter22b1_schema_extension_preserves_pre_22b_hashes` (hash.rs)
- `iter22b1_classdef_empty_optionals_hash_stable` (hash.rs)
- `iter22b1_workspace_with_no_classes_has_empty_registry`
- `iter22b1_instance_in_class_module_loads_clean` (positive)
- `iter22b1_orphan_instance_fires_diagnostic`
- `iter22b1_duplicate_instance_fires_diagnostic`
- `iter22b1_missing_method_fires_diagnostic`
All workspace tests in `ailang-check/tests/workspace.rs` and the
e2e suite remain green; the registry threads through unchanged
(legacy callers default-construct an empty registry, codepaths
that hold a real workspace clone the field through).
### Bench gates
All three green at iter close:
- `bench/check.py` — 63 metrics, 0 regressed, 0 improved, 63 stable.
- `bench/compile_check.py` — 24 metrics, 0 regressed, 0 improved,
24 stable.
- `bench/cross_lang.py` — 25 metrics, 0 regressed, 0 improved,
25 stable.
22b.1 touches workspace-load only; no hot path is exercised. The
registry-build pass adds two passes over all loaded modules (one
to collect class/type defining-module maps, one to register
instances) but only when class/instance defs are present — pre-22b
fixtures never enter the second pass at all. No measurable impact.
### What 22b.1 does NOT ship
Explicitly deferred to 22b.2:
- `FnDef.type.constraints` field (constraint annotations on
regular fns).
- Class-schema validation diagnostics: `KindMismatch`,
`InvalidSuperclassParam`,
`ConstraintReferencesUnboundTypeVar`.
- Typecheck arms: `MissingConstraint`, `NoInstance`.
- `OverridingNonExistentMethod`, `MethodNameCollision`.
Deferred to 22b.3:
- Monomorphisation pass (the only mechanism for class-method
calls; replaces resolved class-method calls with synthesised
monomorphic FnDefs).
Deferred to 22b.4:
- Prelude module (`Show`/`Eq`/`Ord` + four primitive instances).
- `print` rewiring through `Show.show`.
- Form-B (prose) projection arms for `ClassDef`/`InstanceDef`
and the matching parser arms (the round-trip-filter compensation
retires when 22b.4 lands).
### JOURNAL queue (updated)
- **22b.2 — typeclass typecheck arms.** `FnDef.type.constraints`
schema extension; class-schema validation; `MissingConstraint`
+ `NoInstance` per call site; `OverridingNonExistentMethod` +
`MethodNameCollision`.
- **22b.3 — monomorphisation pass.** Synthesise monomorphic
FnDefs from `(method, type-hash)` pairs; rewrite class-method
calls; cache by `(method, type-hash)`. Determines the textual
monomorphised-symbol naming (open in 22a).
- **22b.4 — Prelude + prose round-trip.** Prelude module shipping
`Show`/`Eq`/`Ord` and the four primitive instances; `print`
rewiring through `Show.show`; Form-B parser/printer arms for
ClassDef/InstanceDef; remove the `test_22b1_*` filter from the
round-trip gate.
- **22c — typeclass corpus expansion (deferred).** Unchanged from
the 22a queue.
- **Operator routing through Eq/Ord (deferred, no commitment).**
Unchanged from the 22a queue.
- **21'h, latency methodology, FnDef::synthetic, Boehm full
retirement, closure-pair slab/pool, deferred richer integration
paths.** Unchanged from 21'g.