From 4b84e781d4b992a3781ad2446fc815c94bc769d6 Mon Sep 17 00:00:00 2001 From: Brummel Date: Sat, 9 May 2026 17:50:29 +0200 Subject: [PATCH] iter 22b.2.3: invalid-superclass-param class-schema diagnostic --- crates/ail/src/main.rs | 19 +++++++ crates/ailang-core/src/workspace.rs | 50 +++++++++++++++++++ ...est_22b2_invalid_superclass_param.ail.json | 42 ++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 examples/test_22b2_invalid_superclass_param.ail.json diff --git a/crates/ail/src/main.rs b/crates/ail/src/main.rs index 4d44f12..12ed1a1 100644 --- a/crates/ail/src/main.rs +++ b/crates/ail/src/main.rs @@ -1049,6 +1049,25 @@ fn workspace_error_to_diagnostic( "defining_module": defining_module, })), ), + W::InvalidSuperclassParam { + class, + superclass, + expected_param, + got_type, + } => Some( + ailang_check::Diagnostic::error( + "invalid-superclass-param", + format!( + "class `{class}` declares superclass `{superclass} {got_type}`, but its own parameter is `{expected_param}` — superclass `type` must equal class `param`" + ), + ) + .with_ctx(serde_json::json!({ + "class": class, + "superclass": superclass, + "expected_param": expected_param, + "got_type": got_type, + })), + ), } } diff --git a/crates/ailang-core/src/workspace.rs b/crates/ailang-core/src/workspace.rs index 98cd5dc..0d5c245 100644 --- a/crates/ailang-core/src/workspace.rs +++ b/crates/ailang-core/src/workspace.rs @@ -215,6 +215,21 @@ pub enum WorkspaceLoadError { method: String, defining_module: String, }, + + /// Iter 22b.2: class-schema validation. A class's `superclass.type` + /// does not equal its own `param`. Decision 11 single-superclass + /// model requires the superclass to be applied to the same param + /// (e.g. `class Ord a extends Eq a`, not `extends Eq b`). + #[error( + "class `{class}` declares superclass `{superclass} {got_type}`, but its own parameter is `{expected_param}` \ + — superclass `type` must equal class `param`" + )] + InvalidSuperclassParam { + class: String, + superclass: String, + expected_param: String, + got_type: String, + }, } /// Hash over the canonical bytes of a complete module. @@ -425,6 +440,16 @@ fn validate_classdefs( defining_module: mod_name.clone(), })?; } + if let Some(sc) = &c.superclass { + if sc.type_ != c.param { + return Err(WorkspaceLoadError::InvalidSuperclassParam { + class: c.name.clone(), + superclass: sc.class.clone(), + expected_param: c.param.clone(), + got_type: sc.type_.clone(), + }); + } + } } } } @@ -806,4 +831,29 @@ mod tests { other => panic!("expected KindMismatch, got {other:?}"), } } + + /// Iter 22b.2: a class whose `superclass.type` differs from its + /// own `param` (e.g. `class Ord a extends Eq b`) must fire + /// `InvalidSuperclassParam`. Decision 11 axis 1 ("single + /// superclass, applied to the same param") makes the only legal + /// shape `extends Eq a` when the parent class has `param: "a"`. + #[test] + fn superclass_with_wrong_param_fires_invalid_superclass_param() { + let entry = std::path::PathBuf::from( + "../../examples/test_22b2_invalid_superclass_param.ail.json", + ); + let err = load_workspace(&entry) + .expect_err("must fire invalid-superclass-param"); + match err { + WorkspaceLoadError::InvalidSuperclassParam { + class, superclass, expected_param, got_type, + } => { + assert_eq!(class, "Ord"); + assert_eq!(superclass, "Eq"); + assert_eq!(expected_param, "a"); + assert_eq!(got_type, "b"); + } + other => panic!("expected InvalidSuperclassParam, got {other:?}"), + } + } } diff --git a/examples/test_22b2_invalid_superclass_param.ail.json b/examples/test_22b2_invalid_superclass_param.ail.json new file mode 100644 index 0000000..9aacdb5 --- /dev/null +++ b/examples/test_22b2_invalid_superclass_param.ail.json @@ -0,0 +1,42 @@ +{ + "schema": "ailang/v0", + "name": "test_22b2_invalid_superclass_param", + "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": "b" }, + "methods": [ + { + "name": "lt", + "type": { + "k": "fn", + "params": [ + { "k": "var", "name": "a" }, + { "k": "var", "name": "a" } + ], + "ret": { "k": "con", "name": "Bool" }, + "effects": [] + } + } + ] + } + ] +}