From 47bec74b26b7e9c39ac6d5ecc0f927b74339fba1 Mon Sep 17 00:00:00 2001 From: Brummel Date: Sat, 9 May 2026 19:13:32 +0200 Subject: [PATCH] =?UTF-8?q?iter=2022b.2.10:=20fix=20=E2=80=94=20positive?= =?UTF-8?q?=20green-path=20test,=20drop=20dead=20let=20=5F=20=3D=20ws?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the missing positive counterpart to the no-instance test: `show 42` with `instance Show Int` PRESENT must typecheck green. This guards against registry-keying or threading bugs that would otherwise sneak past the negative-only test (an empty registry at typecheck time, or a key shape mismatch, would silently fire no-instance even when the instance exists). Also drops the dead `let _ = ws;` and its stale comment in `check_in_workspace` — the line above already consumes `ws.registry`, so the explicit drop is redundant. The remaining comment is folded into the registry-threading comment so the "registry is the only thing threaded" rationale is preserved. --- crates/ail/tests/typeclass_22b2.rs | 23 +++++++++++ crates/ailang-check/src/lib.rs | 9 ++--- examples/test_22b2_instance_present.ail.json | 40 ++++++++++++++++++++ 3 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 examples/test_22b2_instance_present.ail.json diff --git a/crates/ail/tests/typeclass_22b2.rs b/crates/ail/tests/typeclass_22b2.rs index a283fcd..94a3459 100644 --- a/crates/ail/tests/typeclass_22b2.rs +++ b/crates/ail/tests/typeclass_22b2.rs @@ -235,3 +235,26 @@ fn fn_calling_class_method_at_type_without_instance_fires_no_instance() { diagnostics.iter().map(|d| &d.code).collect::>() ); } + +/// Property protected: the positive counterpart to `no-instance` — when a +/// monomorphic fn calls a class method at a fully-concrete type AND a +/// matching `instance C T` exists in the workspace, the call typechecks +/// green. The concrete-residual path must look up `(class, type_hash(T))` +/// in `env.workspace_registry` and find a hit, so no diagnostic fires. +/// This guards against registry-keying or threading bugs that would +/// otherwise sneak past the negative-only test: a registry that is empty +/// at typecheck time, or keyed under the wrong shape, would silently fire +/// `no-instance` even when the instance is present. Fixture: `main` calls +/// `show 42` with `instance Show Int` declared in the same workspace. +#[test] +fn fn_calling_class_method_at_type_with_instance_typechecks_green() { + let entry = examples_dir().join("test_22b2_instance_present.ail.json"); + let ws = ailang_core::workspace::load_workspace(&entry) + .expect("workspace loads"); + let diagnostics = ailang_check::check_workspace(&ws); + assert!( + diagnostics.is_empty(), + "expected no diagnostics — instance Show Int is present, registry lookup must hit; got: {:?}", + diagnostics + ); +} diff --git a/crates/ailang-check/src/lib.rs b/crates/ailang-check/src/lib.rs index 6280d3a..ce5c3a3 100644 --- a/crates/ailang-check/src/lib.rs +++ b/crates/ailang-check/src/lib.rs @@ -1177,12 +1177,11 @@ fn check_in_workspace( env.class_superclasses = class_superclasses; // Iter 22b.2 (Task 10): thread the workspace-load-time instance // registry into the env so `check_fn`'s concrete-residual path can - // look up `(class, type_hash)` keys for the no-instance check. + // look up `(class, type_hash)` keys for the no-instance check. The + // registry is the only field of `ws` consumed at typecheck time; + // cross-module name resolution uses `module_globals` / `module_types` + // built earlier in this function. env.workspace_registry = ws.registry.clone(); - // Workspace isn't otherwise needed in the env; cross-module lookup - // uses only `module_globals` / `module_types`. The ws reference is - // preserved in case cross-module ADTs are added later. - let _ = ws; for def in &m.defs { if let Err(e) = check_def(def, &env) { diff --git a/examples/test_22b2_instance_present.ail.json b/examples/test_22b2_instance_present.ail.json new file mode 100644 index 0000000..9090b86 --- /dev/null +++ b/examples/test_22b2_instance_present.ail.json @@ -0,0 +1,40 @@ +{ + "schema": "ailang/v0", + "name": "test_22b2_instance_present", + "imports": [], + "defs": [ + { + "kind": "class", "name": "Show", "param": "a", + "methods": [{ + "name": "show", + "type": { + "k": "fn", "params": [{ "k": "var", "name": "a" }], + "ret": { "k": "con", "name": "Str" }, "effects": [] + } + }] + }, + { + "kind": "instance", + "class": "Show", + "type": { "k": "con", "name": "Int" }, + "methods": [{ + "name": "show", + "body": { "t": "lit", "lit": { "kind": "str", "value": "n" } } + }] + }, + { + "kind": "fn", + "name": "main", + "type": { + "k": "fn", "params": [], "ret": { "k": "con", "name": "Str" }, + "effects": [] + }, + "params": [], + "body": { + "t": "app", + "fn": { "t": "var", "name": "show" }, + "args": [{ "t": "lit", "lit": { "kind": "int", "value": 42 } }] + } + } + ] +}