iter 22b.2.7: missing-superclass-instance diagnostic
This commit is contained in:
@@ -1123,6 +1123,23 @@ fn workspace_error_to_diagnostic(
|
||||
"second_origin": second_origin,
|
||||
})),
|
||||
),
|
||||
W::MissingSuperclassInstance {
|
||||
class,
|
||||
superclass,
|
||||
type_repr,
|
||||
} => Some(
|
||||
ailang_check::Diagnostic::error(
|
||||
"missing-superclass-instance",
|
||||
format!(
|
||||
"instance `{class} {type_repr}` requires superclass instance `{superclass} {type_repr}`, but none was found"
|
||||
),
|
||||
)
|
||||
.with_ctx(serde_json::json!({
|
||||
"class": class,
|
||||
"superclass": superclass,
|
||||
"type": type_repr,
|
||||
})),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"schema": "ailang/v0",
|
||||
"name": "test_22b2_missing_superclass_instance",
|
||||
"imports": [],
|
||||
"defs": [
|
||||
{
|
||||
"kind": "class", "name": "Eq", "param": "a",
|
||||
"methods": [{
|
||||
"name": "eq",
|
||||
"type": {
|
||||
"k": "fn",
|
||||
"params": [{ "k": "var", "name": "a" }, { "k": "var", "name": "a" }],
|
||||
"ret": { "k": "con", "name": "Bool" }, "effects": []
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
"kind": "class", "name": "Ord", "param": "a",
|
||||
"superclass": { "class": "Eq", "type": "a" },
|
||||
"methods": [{
|
||||
"name": "lt",
|
||||
"type": {
|
||||
"k": "fn",
|
||||
"params": [{ "k": "var", "name": "a" }, { "k": "var", "name": "a" }],
|
||||
"ret": { "k": "con", "name": "Bool" }, "effects": []
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
"kind": "instance",
|
||||
"class": "Ord",
|
||||
"type": { "k": "con", "name": "Int" },
|
||||
"methods": [{
|
||||
"name": "lt",
|
||||
"body": { "t": "lit", "lit": { "kind": "bool", "value": true } }
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user