From 59e86b3805edd8100409664790840907d022c621 Mon Sep 17 00:00:00 2001 From: Brummel Date: Sat, 9 May 2026 22:41:18 +0200 Subject: [PATCH] test: red for monomorphise_workspace unknown type on user ADT --- crates/ail/tests/typeclass_22c.rs | 58 +++++++++++++++ examples/test_22c_user_class_e2e.ail.json | 90 +++++++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 crates/ail/tests/typeclass_22c.rs create mode 100644 examples/test_22c_user_class_e2e.ail.json diff --git a/crates/ail/tests/typeclass_22c.rs b/crates/ail/tests/typeclass_22c.rs new file mode 100644 index 0000000..63b0d74 --- /dev/null +++ b/crates/ail/tests/typeclass_22c.rs @@ -0,0 +1,58 @@ +//! Iter 22c: milestone-22 acceptance — user-defined classes with +//! instances over user-defined ADTs end-to-end (typecheck → mono → +//! codegen → binary). The 22b.3 mono pass was tested only against +//! instances over primitive types; this file pins the user-ADT path. +//! +//! Co-located with `typeclass_22b3.rs` so the typeclass-feature +//! coverage is browsable in one directory. + +use std::path::Path; +use std::process::Command; + +fn ail_bin() -> &'static str { + env!("CARGO_BIN_EXE_ail") +} + +fn build_and_run(example: &str) -> String { + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let workspace = Path::new(manifest_dir).parent().unwrap().parent().unwrap(); + let src = workspace.join("examples").join(example); + let tmp = std::env::temp_dir().join(format!( + "ailang_22c_{}_{}", + example.replace('.', "_"), + std::process::id() + )); + std::fs::create_dir_all(&tmp).unwrap(); + let out = tmp.join("bin"); + let status = Command::new(ail_bin()) + .args(["build", src.to_str().unwrap(), "-o"]) + .arg(&out) + .status() + .expect("ail build failed to run"); + assert!(status.success(), "ail build failed for {example}"); + let output = Command::new(&out).output().expect("execute binary"); + assert!( + output.status.success(), + "binary {} exited non-zero", + out.display() + ); + String::from_utf8(output.stdout).expect("stdout utf8") +} + +/// Property: a workspace declaring a user class with an instance over +/// a user-defined ADT must build and run end-to-end. The instance +/// body uses the ADT's constructor and pattern-matches against it, +/// which exercises the mono pass's `synth` replay against ctor terms +/// and ctor patterns whose owning type is locally defined. +/// +/// Pre-22c: `ail build` fails with +/// `monomorphise_workspace: unknown type: \`IntBox\`` +/// because `mono::build_workspace_env` does not seed the synth env's +/// per-module local-type and ctor-index tables; `synth`'s bare-name +/// lookup at `lib.rs:1855` (and the parallel ctor-index lookup at +/// `lib.rs:2316`) fall through. +#[test] +fn user_class_instance_over_user_adt_builds_and_runs() { + let stdout = build_and_run("test_22c_user_class_e2e.ail.json"); + assert_eq!(stdout.trim(), "42"); +} diff --git a/examples/test_22c_user_class_e2e.ail.json b/examples/test_22c_user_class_e2e.ail.json new file mode 100644 index 0000000..81a0a62 --- /dev/null +++ b/examples/test_22c_user_class_e2e.ail.json @@ -0,0 +1,90 @@ +{ + "schema": "ailang/v0", + "name": "test_22c_user_class_e2e", + "imports": [], + "defs": [ + { + "kind": "type", + "name": "IntBox", + "ctors": [ + { "name": "MkIntBox", "fields": [{ "k": "con", "name": "Int" }] } + ] + }, + { + "kind": "class", + "name": "Foo", + "param": "a", + "methods": [ + { + "name": "foo", + "type": { + "k": "fn", + "params": [{ "k": "var", "name": "a" }], + "param_modes": ["borrow"], + "ret": { "k": "con", "name": "Int" }, + "effects": [] + } + } + ] + }, + { + "kind": "instance", + "class": "Foo", + "type": { "k": "con", "name": "IntBox" }, + "methods": [ + { + "name": "foo", + "body": { + "t": "lam", + "params": ["b"], + "paramTypes": [{ "k": "var", "name": "a" }], + "retType": { "k": "con", "name": "Int" }, + "body": { + "t": "match", + "scrutinee": { "t": "var", "name": "b" }, + "arms": [ + { + "pat": { + "p": "ctor", + "ctor": "MkIntBox", + "fields": [{ "p": "var", "name": "v" }] + }, + "body": { "t": "var", "name": "v" } + } + ] + } + } + } + ] + }, + { + "kind": "fn", + "name": "main", + "type": { + "k": "fn", + "params": [], + "ret": { "k": "con", "name": "Unit" }, + "effects": ["IO"] + }, + "params": [], + "body": { + "t": "do", + "op": "io/print_int", + "args": [ + { + "t": "app", + "fn": { "t": "var", "name": "foo" }, + "args": [ + { + "t": "ctor", + "type": "IntBox", + "ctor": "MkIntBox", + "args": [{ "t": "lit", "lit": { "kind": "int", "value": 42 } }] + } + ] + } + ] + } + } + ] +}