Files
AILang/docs/journals/2026-05-12-iter-ctt.2.md
T

143 lines
6.7 KiB
Markdown

# iter ctt.2 — `Registry.type_def_module` re-key to tuple-keyed map
**Date:** 2026-05-12
**Started from:** 548ebf8d51758ab9f156b266e0baa59c78b14034
**Status:** DONE
**Tasks completed:** 3 of 3
## Summary
`Registry.type_def_module` re-keyed from `BTreeMap<String, String>` to
`BTreeMap<(String, String), String>` keyed by `(owning_module,
bare_name)`. A new `caller_module: &str` parameter threads through
`normalize_type_for_registry` and `Registry::normalize_type_for_lookup`,
and all four `ailang-check` consumer sites pass the correct
module-context per the plan's Design Notes block. A regression test
plus a three-module fixture exercise the bare-name collision the
re-key fixes — two modules each declaring `type Foo` now register
under distinct canonical keys instead of tripping the silent-overwrite
defect. `cargo test --workspace` green (16 binary-test suites,
~600 tests total, zero failures).
## Per-task notes
- iter ctt.2.1: RED-first — three fixture JSON files
(`ctt2_collision_{cls,lib,main}.ail.json`) plus
`crates/ailang-core/tests/ctt2_registry_rekey.rs`. Test goes
RED today. Failure shape was `OrphanInstance` rather than the
plan's predicted `DuplicateInstance` (a) or missing-hash (b) —
see Concerns.
- iter ctt.2.2: GREEN — atomic re-key edit pass. Touched the field
type + doc-comment, `Registry::normalize_type_for_lookup`
signature + body, pass-1 declaration + insert,
pass-2 coherence-check lookup + canonical-form hashing call,
`normalize_type_for_registry` signature + doc-comment + body
(recursive calls thread caller_module everywhere), the inline
`ct1_5a_normalize_recurses_into_forall_constraints` test
at workspace.rs:2543, and all four `ailang-check` consumer sites
at the module-context dictated by the plan's Design Notes
(lib.rs:1640 → `env.current_module.as_str()`, mono.rs:121 →
`defining_module.as_str()`, mono.rs:624 and :1175 → `module_name`).
Clean `cargo build --workspace` at the end of the pass.
- iter ctt.2.3: workspace-wide test gate. New test goes GREEN.
Full `cargo test --workspace` green, no test in any crate
regressed. The plan's Step 2 hypothesis ("a fixture under
`examples/` might legitimately exercise a bare cross-module
type reference") did not materialise — the existing corpus
uses canonical-form (qualified) cross-module type refs
exclusively, so the tightening had no surface.
## Concerns
- Task 1 RED-failure shape diverged from the plan's predicted
shapes (a) `DuplicateInstance` and (b) missing-`contains_key`.
The plan's verbatim two-module fixture pair (lib imports main +
main imports lib + lib-side instance with qualified class
`ctt2_collision_main.MyC`) had two independent structural
defects against the loaded codebase invariants:
1. The workspace loader rejects import cycles
(`WorkspaceLoadError::Cycle`) at module-name granularity —
the plan's claim "Two-way import is supported; workspace
loader DFS handles cycles at module-name granularity" is
empirically false. Confirmed by inspection at
`workspace.rs:1410-1413` and `:1451-1454`.
2. `validate_canonical_type_names` rejects qualified
`InstanceDef.class` with `QualifiedClassName` (see test
`ct1_validator_rejects_qualified_instancedef_class` at
workspace.rs:2321) — the plan's lib-side instance with
`class: "ctt2_collision_main.MyC"` would have been rejected
even without the cycle.
Implementer-phase repair: introduced a third module
`ctt2_collision_cls.ail.json` carrying `class MyC`, broke the
cycle (both lib and main import cls only; main also imports
lib so the loader DFS reaches it), and dropped the qualifier
from both instance heads (both declare bare `class: "MyC"`
satisfying coherence via the type's module). The plan's
*core* RED-condition is preserved: two modules each declaring
`type Foo` with `instance MyC Foo` collide on the pre-ctt.2
bare-name `type_def_module` key.
The actually-observed RED shape was `OrphanInstance` rather
than `DuplicateInstance` because the pass-2 coherence check
(`workspace.rs:656-659`) uses the bare-name lookup *before*
the duplicate-uniqueness check fires — lib's `Foo` and main's
`Foo` both resolve to whichever module won the alphabetical
race (`ctt2_collision_main` beats `ctt2_collision_lib`
lexicographically), so lib's instance is no longer coherent
("type lives in main, class lives in cls, instance lives in
lib — neither matches"). The re-key fixes this consumer site
simultaneously with the duplicate-check site because both go
through the same `type_def_module` lookup, now tuple-keyed.
The deviation from the plan's verbatim fixtures is an
implementer-phase NEEDS_CONTEXT repair that the
user-no-stop-instruction permitted in-line; the plan's *intent*
(RED-first against the bare-name collision) is achieved and
the GREEN-state matches the plan's acceptance criteria
unchanged. Boss may want to back-edit the plan or amend the
spec to note the multi-module fixture shape if this kind of
collision regression returns later.
## Known debt
- The `OrphanInstance` consumer site at `workspace.rs:656-659`
is fixed by ctt.2 but the diagnostic-shape change (orphan
instead of duplicate-on-loser) is not separately tested. The
new test asserts the GREEN state directly (both entries land
under distinct keys) which transitively exercises both
consumer sites, but neither the orphan nor the duplicate
diagnostic shapes are pinned by a dedicated RED test. Adding
one is plausibly out-of-scope for ctt.2.
## Files touched
Modified (3):
- `crates/ailang-check/src/lib.rs` — single call-site update at
the NoInstance check inside `check_fn`.
- `crates/ailang-check/src/mono.rs` — three call-site updates
(monomorphise_workspace at :121, collect_mono_targets at :624,
collect_residuals_ordered at :1175).
- `crates/ailang-core/src/workspace.rs` — field re-key,
`Registry::normalize_type_for_lookup` signature,
`normalize_type_for_registry` signature + body,
pass-1 declaration + insert,
pass-2 coherence-check lookup + canonical-form hashing call,
inline test `ct1_5a_normalize_recurses_into_forall_constraints`
at :2543; doc-comments at field-decl and at both
`normalize_…` fns.
Added (4):
- `crates/ailang-core/tests/ctt2_registry_rekey.rs` — regression test.
- `examples/ctt2_collision_cls.ail.json` — class-bearing module
(not in plan; required to break the cycle in the plan's
verbatim fixture design).
- `examples/ctt2_collision_lib.ail.json``type Foo = MkLib` +
bare `instance MyC Foo`.
- `examples/ctt2_collision_main.ail.json``type Foo = MkMain` +
bare `instance MyC Foo`, plus imports of both `cls` and `lib`.
## Stats
bench/orchestrator-stats/2026-05-12-iter-ctt.2.json