From 75109f171da4c9e8b252758d13b016f886bfad2d Mon Sep 17 00:00:00 2001 From: Brummel Date: Sat, 30 May 2026 17:13:40 +0200 Subject: [PATCH] fix(check): param-not-in-restricted-set names the allowed set (closes #48) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ParamNotInRestrictedSet diagnostic named the offending type, the type-variable and the home type, but not the allowed set {Int, Float, Bool} that design/models/0007 §4 promises it names. A downstream author was told what is wrong but not what is right, and the kernel source that carries the set is not available to a downstream consumer. The allowed set already travels on the error struct (it flows into the JSON `details.allowed`); only the human-readable `#[error]` render dropped it. Append it to the Display string. JSON details unchanged. Surfaced by the raw-buf fieldtest (finding B3, docs/specs/0058). RED-first: param_in_reject_message_names_allowed_set pins the message content before the one-line render fix. --- crates/ailang-check/src/lib.rs | 47 +++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/crates/ailang-check/src/lib.rs b/crates/ailang-check/src/lib.rs index be37adc..0e83cad 100644 --- a/crates/ailang-check/src/lib.rs +++ b/crates/ailang-check/src/lib.rs @@ -747,7 +747,7 @@ pub enum CheckError { /// (`allowed`, deterministically alphabetical from the BTreeSet /// source). Code: `param-not-in-restricted-set`. #[error( - "type-arg `{found}` is not in the restricted set for type-variable `{var}` of `{type_name}`" + "type-arg `{found}` is not in the restricted set for type-variable `{var}` of `{type_name}` (allowed: one of {allowed:?})" )] ParamNotInRestrictedSet { type_name: String, @@ -8221,6 +8221,51 @@ mod tests { ); } + /// fieldtest B3 (raw-buf, #7): the rendered `param-not-in-restricted-set` + /// message must name the *allowed set*, not only the offending type. + /// Property per design/models/0007-kernel-extensions.md §4 + /// ("ParamNotInRestrictedSet names the offending type AND the allowed + /// set"): a downstream author rejected for `(con StubT (con Str))` + /// under `param-in = {"a": {"Int", "Float"}}` must be told `Int` and + /// `Float` are the permitted choices, so the message is actionable. + #[test] + fn param_in_reject_message_names_allowed_set() { + let m: Module = serde_json::from_value(serde_json::json!({ + "schema": SCHEMA, + "name": "k", + "defs": [ + {"kind": "type", "name": "StubT", "vars": ["a"], + "ctors": [{"name": "Stub", "fields": [{"k": "var", "name": "a"}]}], + "param-in": {"a": ["Int", "Float"]}}, + {"kind": "fn", "name": "bad", + "type": {"k": "fn", "params": [], + "ret": {"k": "con", "name": "StubT", + "args": [{"k": "con", "name": "Str"}]}, + "effects": []}, + "params": [], + "body": {"t": "lit", "lit": {"kind": "unit"}}} + ] + })).unwrap(); + let mut modules = BTreeMap::new(); + modules.insert("k".to_string(), m); + let ws = Workspace { + entry: "k".into(), + modules, + root_dir: std::path::PathBuf::from("."), + registry: ailang_core::workspace::Registry::default(), + }; + let diags = check_workspace(&ws); + let d = diags + .iter() + .find(|d| d.code == "param-not-in-restricted-set") + .unwrap_or_else(|| panic!("expected param-not-in-restricted-set, got {diags:?}")); + assert!( + d.message.contains("Int") && d.message.contains("Float"), + "message must name the allowed set {{Int, Float}}, got: {:?}", + d.message + ); + } + /// prep.3 (kernel-extension-mechanics): `param-in` enforcement /// accepts a type-arg inside the allowed set. Same TypeDef as /// the RED test, but `(con StubT (con Int))` — Int is in