iter 22b.2.7: missing-superclass-instance diagnostic

This commit is contained in:
2026-05-09 18:18:12 +02:00
parent b252f837af
commit a546c5851c
3 changed files with 118 additions and 0 deletions
+62
View File
@@ -269,6 +269,19 @@ pub enum WorkspaceLoadError {
first_origin: String,
second_origin: String,
},
/// Iter 22b.2: an instance `C T` was declared, but `C`'s
/// superclass `S` does not have an instance for the same type
/// `T`. Decision 11 single-superclass model requires `instance S
/// T` to exist whenever `instance C T` exists.
#[error(
"instance `{class} {type_repr}` requires superclass instance `{superclass} {type_repr}`, but none was found"
)]
MissingSuperclassInstance {
class: String,
superclass: String,
type_repr: String,
},
}
/// Hash over the canonical bytes of a complete module.
@@ -557,6 +570,29 @@ fn build_registry(
}
}
// Iter 22b.2: superclass-instance completeness. For every entry,
// walk the class's superclass chain and require an entry for each
// step at the same type-hash.
for (key, entry) in entries.iter() {
let (class_name, type_hash) = key;
let mut current = class_by_name.get(class_name.as_str()).copied();
while let Some(c) = current {
if let Some(sc) = &c.superclass {
let sc_key = (sc.class.clone(), type_hash.clone());
if !entries.contains_key(&sc_key) {
return Err(WorkspaceLoadError::MissingSuperclassInstance {
class: class_name.clone(),
superclass: sc.class.clone(),
type_repr: type_head_name(&entry.instance.type_),
});
}
current = class_by_name.get(sc.class.as_str()).copied();
} else {
break;
}
}
}
Ok(Registry { entries })
}
@@ -1134,4 +1170,30 @@ mod tests {
other => panic!("expected MethodNameCollision, got {other:?}"),
}
}
/// Iter 22b.2: an instance `C T` whose class `C` declares a
/// superclass `S` requires that `instance S T` also exist in the
/// workspace. The fixture declares `class Eq a`, `class Ord a
/// extends Eq a`, and `instance Ord Int` — but no `instance Eq
/// Int` — so registry build must fire
/// `MissingSuperclassInstance`. Decision 11 single-superclass
/// model requires `instance S T` whenever `instance C T` exists.
#[test]
fn instance_without_superclass_instance_fires() {
let entry = std::path::PathBuf::from(
"../../examples/test_22b2_missing_superclass_instance.ail.json",
);
let err = load_workspace(&entry)
.expect_err("must fire missing-superclass-instance");
match err {
WorkspaceLoadError::MissingSuperclassInstance {
class, superclass, type_repr,
} => {
assert_eq!(class, "Ord");
assert_eq!(superclass, "Eq");
assert_eq!(type_repr, "Int");
}
other => panic!("expected MissingSuperclassInstance, got {other:?}"),
}
}
}