spec: canonical type names — class-names out-of-scope rationale

This commit is contained in:
2026-05-11 00:28:39 +02:00
parent 4d614a053f
commit b840c340d4
+36 -7
View File
@@ -85,7 +85,7 @@ The milestone decomposes into four iterations.
| Iter | Scope |
|------|-------|
| **ct.1** | `.ail.json` schema validator at `load_workspace` (the rule above) + one-time migration. The migration is targeted: a workspace-walker tool (small Rust binary or test-driven script under `crates/ail/src/bin/`) reads every `.ail.json` under `examples/`, finds bare cross-module Type::Con names (i.e. bare names that don't resolve to a local type def in the same file), looks up the qualified form via the import graph, rewrites in place. Files with only intra-module refs are unchanged. Single bulk commit for the rewritten fixtures. |
| **ct.1** | `.ail.json` schema validator at `load_workspace` (the rule above) + one-time migration. The migration is targeted: a workspace-walker tool (small Rust binary or test-driven script under `crates/ail/src/bin/`) reads every `.ail.json` under `examples/`, finds bare cross-module Type::Con names (i.e. bare names that don't resolve to a local type def in the same file), looks up the qualified form via the import graph, rewrites in place. Files with only intra-module refs are unchanged. Single bulk commit for the rewritten fixtures. The validator also rejects *qualified* class-name fields (`InstanceDef.class`, `SuperclassRef.class`, `Constraint.class`) — those stay bare per the Out-of-scope note. |
| **ct.2** | Remove obsolete typechecker mechanisms that papered over bare cross-module refs: `Pattern::Ctor`'s imports-fallback (iter 15a, ~`crates/ailang-check/src/lib.rs:2486-2521`) and `Term::Ctor`'s synth imports-fallback (iter 23.1.3, ~lib.rs:1979-2025). Both become unreachable because bare-non-primitive type-name reaches them only when local (direct lookup succeeds) — never as a stale cross-module ref. **`qualify_local_types` (iter 15a, ~lib.rs:1848) STAYS** — it correctly handles the cross-boundary translation when a cross-module fn signature is pulled into the current module's typecheck context. Under the new rule it becomes more prominent (called uniformly at every cross-module fn-lookup boundary, where today it runs only at the Term::Var prefix-qualified path); this iter audits its call sites for consistency. |
| **ct.3** | Remove obsolete codegen + mono mechanisms: `lookup_ctor_by_type`'s imports-fallback (iter 23.1.4) and `apply_per_module_ctor_index_overlay` in mono (iter 23.2 fix `84dcc46`). The codegen `lower_workspace_inner` implicit-prelude import_map entry (iter 23.2.4 fix `be882c4`) STAYS — fn-name resolution for mono symbols, not Type::Con names. |
| **ct.4** | DESIGN.md amendments + JOURNAL migration entry + re-baseline of hash-pinned regression tests that touched cross-module-ref fixtures + IR snapshot refresh (where affected). Form-B prose round-trip test extended to cover the qualifier-trim-on-print behaviour for intra-module refs. |
@@ -261,12 +261,41 @@ many ambiguity cases are caught earlier by the validator.
local-to-this-file, qualified = explicit cross-module). No
imports-fallback needed in the pattern path.
- **Class names** (`class Eq`, `instance Eq Int`, `class Ord extends Eq`,
`Constraint.class`): same conceptual shape as Type::Con but separate
namespace, separate uniqueness check (`MethodNameCollision`). Whether
class names should also follow the same module-scoping rule is a
separate spec question deferred to a future milestone. This spec
touches Type::Con only.
- **Class names** (`ClassDef.name`, `InstanceDef.class`,
`SuperclassRef.class`, `Constraint.class`): NOT migrated to the
module-scoped canonical form in this milestone. The asymmetry to
Type::Con is intentional and rests on a real semantic difference:
- For types, no workspace-global uniqueness rule exists. Two modules
may independently declare `type Foo`. Bare cross-module references
are therefore genuinely ambiguous; the iter-23.3 bug is the
concrete instance of that ambiguity surfacing in unification.
- For classes, workspace-global uniqueness is enforced one level
deeper: `WorkspaceLoadError::MethodNameCollision` (iter 22b.2,
`workspace.rs:472`) rejects two classes that share any method
name. Two `class Eq`s with the conventional `eq` method cannot
coexist; `class_by_name: BTreeMap<String, &ClassDef>` (workspace-
flat) is consistent with that invariant. Empirically no
`examples/` fixture today has a bare cross-module class
reference that resolves ambiguously — the cross-module fixtures
that exist (`test_22b3_xmod_e2e_main`) call methods bare and rely
on the method→class→instance chain, never on a direct class-name
reference.
Three class-reference fields (`InstanceDef.class`,
`SuperclassRef.class`, `Constraint.class`) gain a *defensive*
validator check in ct.1: reject qualified forms (containing `'.'`)
explicitly, since the schema does not currently model qualified
class names. This prevents half-migrated files from silently
loading.
If AILang ever revisits workspace-global method-name uniqueness
(e.g. introduces module-qualified method names so two libraries
can each declare `class Eq` with its own `eq`), the symmetry to
Type::Con returns and class names will need the same module-scoped
treatment. That is a separate DESIGN-level milestone and is not
this spec's scope.
- **Surface notation in `.ailx`**: bare local names continue to be
the LLM-author convention. Prose parser/printer bridge — no