143 lines
6.7 KiB
Markdown
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
|