iter mq.1: class-ref canonical-form extension + workspace-internal class-name qualification
Three schema fields move bare → canonical (bare for same-module,
<module>.<Class> for cross-module): InstanceDef.class,
Constraint.class, SuperclassRef.class. Symmetric to ct.1's
Type::Con.name rule. ClassDef.name stays bare.
validate_canonical_type_names gains three field-walks via new
check_class_ref helper + two sibling diagnostics
BareCrossModuleClassRef / BadCrossModuleClassRef.
check_class_name_fields narrowed to ClassDef.name-only.
Workspace-internal class-name keys all qualified:
class_def_module, class_by_name, registry entries.0,
ClassMethodEntry.class_name, class_superclasses, mono's
class_index, Origin::Class.class_name. New qualify_class_ref
helper at workspace.rs scope plus sibling
qualify_class_ref_in_check in ailang-check.
Two new positive on-disk fixtures (mq1_xmod_constraint_class{,_dep})
exercise the qualified Constraint.class shape.
Recon claim that all existing fixtures' class-refs are intra-module
was empirically wrong — 10 fixtures required minimal canonical-form
migration. Duplicate-instance test architecturally restructured:
post-mq.1 orphan-freedom makes two-modules-on-same-(class,type)
structurally impossible; new test_22b1_dup_same_module fixture
lands both instances in the class's module.
Plan defects fixed inline: Origin::Class.class_name single
construction site (plan recon off-by-one); build_class_index in
mono.rs needed qualifying; build_module_globals needed Def::Instance
carve-out; check_fn declared-constraint matching needed inline
canonical-form lifting; NoInstance Float-aware message arm needed
prelude.Eq/prelude.Ord match; coherence type-leg needed qualified-
head split-and-lookup. Tasks 3-6 ran as one coherent fix.
7/7 tasks, 520 tests green. bench/compile_check.py + cross_lang.py
exit 0; bench/check.py exit 1 (4 improvements-beyond-tolerance,
audit-ratifiable, not a regression).
MethodNameCollision + dispatch path unchanged; iters mq.2 + mq.3
land them.
This commit is contained in:
@@ -258,7 +258,13 @@ pub struct Suppress {
|
||||
/// pre-22b hashes.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ClassDef {
|
||||
/// Class name (capitalised by convention).
|
||||
/// Class name (capitalised by convention). Bare — symmetric to
|
||||
/// `TypeDef.name`, the field is the defining-site context, not
|
||||
/// a reference. Cross-module class references live in
|
||||
/// `InstanceDef.class`, `Constraint.class`, and
|
||||
/// `SuperclassRef.class`; those fields carry the canonical form
|
||||
/// (bare for same-module, `<module>.<Class>` for cross-module)
|
||||
/// per mq.1.
|
||||
pub name: String,
|
||||
/// Single type-parameter name.
|
||||
pub param: String,
|
||||
@@ -275,7 +281,9 @@ pub struct ClassDef {
|
||||
/// Iter 22b.1: reference to a superclass relation in [`ClassDef`].
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SuperclassRef {
|
||||
/// Superclass name.
|
||||
/// Superclass name in canonical form (mq.1): bare for a
|
||||
/// same-module class, `<module>.<Class>` for a cross-module
|
||||
/// class. Symmetric to ct.1's `Type::Con.name` rule.
|
||||
pub class: String,
|
||||
/// Type the superclass is applied to. MUST equal the parent
|
||||
/// `ClassDef.param` (validated in 22b.2 — schema does not enforce).
|
||||
@@ -310,7 +318,9 @@ pub struct ClassMethod {
|
||||
/// overrides of default-bearing methods.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct InstanceDef {
|
||||
/// Class being instantiated.
|
||||
/// Class being instantiated, in canonical form (mq.1): bare for
|
||||
/// a same-module class, `<module>.<Class>` for a cross-module
|
||||
/// class. Symmetric to ct.1's `Type::Con.name` rule.
|
||||
pub class: String,
|
||||
/// Concrete type the class is applied to.
|
||||
#[serde(rename = "type")]
|
||||
@@ -341,7 +351,9 @@ pub struct InstanceMethod {
|
||||
/// matching registry entry exists.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Constraint {
|
||||
/// Class name.
|
||||
/// Class name in canonical form (mq.1): bare for a same-module
|
||||
/// class, `<module>.<Class>` for a cross-module class.
|
||||
/// Symmetric to ct.1's `Type::Con.name` rule.
|
||||
pub class: String,
|
||||
/// Type the class is applied to.
|
||||
#[serde(rename = "type")]
|
||||
|
||||
@@ -293,20 +293,34 @@ mod tests {
|
||||
let dup_a_src = std::fs::read(examples.join("test_22b1_dup_a.ail.json"))
|
||||
.expect("examples/test_22b1_dup_a.ail.json present");
|
||||
let dup_a_mod: crate::ast::Module = serde_json::from_slice(&dup_a_src).unwrap();
|
||||
// test_22b1_dup_a has two defs: the Show class and an instance
|
||||
// of Show for test_22b1_dup_b.MyInt. Def::name() returns the
|
||||
// class name for both, so pin each by index. The instance
|
||||
// carries the migrated cross-module qualifier in its `type`.
|
||||
assert_eq!(dup_a_mod.defs.len(), 2, "test_22b1_dup_a expected to have exactly 2 defs (class + instance)");
|
||||
// mq.1: test_22b1_dup_a has one def post-migration — the instance
|
||||
// of `test_22b1_dup_classmod.Show` for `test_22b1_dup_b.MyInt`.
|
||||
// The `Show` class itself moved to `test_22b1_dup_classmod` to
|
||||
// break the dup_a ↔ dup_b cycle that the pre-mq.1 bare-class
|
||||
// shape required for cross-module duplicate-instance coverage.
|
||||
assert_eq!(dup_a_mod.defs.len(), 1, "test_22b1_dup_a expected to have exactly 1 def (the instance)");
|
||||
// Hash captured post-mq.1 migration; the instance carries the
|
||||
// canonical qualified `class` field. Computed by running this
|
||||
// test once with a length-only assertion and recording the
|
||||
// observed value. A future schema-touching change that shifts
|
||||
// this hash without intent triggers this pin.
|
||||
assert_eq!(
|
||||
def_hash(&dup_a_mod.defs[0]),
|
||||
"1c2573661ffd3da3",
|
||||
"test_22b1_dup_a class Show canonical hash must match captured post-migration value"
|
||||
"bdefb4ec75e046e4",
|
||||
"test_22b1_dup_a instance hash drifted; expected post-mq.1 captured value"
|
||||
);
|
||||
|
||||
// Also pin the new test_22b1_dup_classmod fixture's `Show` class
|
||||
// hash: the class moved here from dup_a verbatim (same method
|
||||
// signature), so the hash is independent of the migration.
|
||||
let dup_classmod_src = std::fs::read(examples.join("test_22b1_dup_classmod.ail.json"))
|
||||
.expect("examples/test_22b1_dup_classmod.ail.json present");
|
||||
let dup_classmod_mod: crate::ast::Module = serde_json::from_slice(&dup_classmod_src).unwrap();
|
||||
assert_eq!(dup_classmod_mod.defs.len(), 1, "test_22b1_dup_classmod expected to have exactly 1 def (class Show)");
|
||||
assert_eq!(
|
||||
def_hash(&dup_a_mod.defs[1]),
|
||||
"cc8685f92f246e40",
|
||||
"test_22b1_dup_a instance Show for test_22b1_dup_b.MyInt canonical hash must match captured post-migration value"
|
||||
def_hash(&dup_classmod_mod.defs[0]),
|
||||
"1c2573661ffd3da3",
|
||||
"test_22b1_dup_classmod class Show canonical hash must equal the pre-mq.1 dup_a class Show hash (same bytes)"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+632
-159
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user