92e830e6c2
Five polymorphic prelude free fns (ne/lt/le/gt/ge) plus three E2E fixtures: positive composition over Int/Bool/Str, user-ADT with user-written Eq/Ord on IntBox, and Float-NoInstance with a Float-aware diagnostic addendum cross-referencing DESIGN.md §"Float semantics". Two checker-side fixes were needed alongside the prelude additions, both symmetric to the iter 23.4-prep visibility fix: - linearity::check_module_with_visible — workspace-aware callee-mode resolution so a Borrow-mode user fn forwarding into a prelude poly fn (e.g. `at_most x y = not (gt x y)`) doesn't false-fire consume-while-borrowed. - mono::collect_mono_targets — distinguish rigid forall vars from unbound metavars; rigid-bearing free-fn targets skip (they get re-collected with concrete subst when the enclosing fn itself monomorphises). Without this, a polymorphic body calling another polymorphic free fn produced a spurious __Unit specialisation whose body referenced compare at Unit. Roadmap split: shipped Eq/Ord half checked, Show + print-rewire half remains pending behind the heap-Str ABI prerequisite. DESIGN.md §"Prelude classes" amended. One pre-existing fixture (test_22b1_missing_method) renamed its class method ne → tne to avoid collision with the new prelude `ne`. Bench: check.py + cross_lang.py exit 0; compile_check.py exits 1 with one sub-ms check_ms.local_rec_capture at +26% (tolerance 25%) — prelude-growth-related noise-band fluctuation, ratifiable per spec §248-249 and journal Concerns.
83 lines
2.5 KiB
Rust
83 lines
2.5 KiB
Rust
//! Workspace-level tests for the polymorphic prelude free fns
|
|
//! `ne` / `lt` / `le` / `gt` / `ge` shipped in milestone 23.
|
|
//!
|
|
//! Property protected: each free fn is monomorphised on demand at the
|
|
//! call site's concrete type, producing a mono symbol named
|
|
//! `<fn>__<TypeName>`. If the unified mono pass (iter 23.4) stops
|
|
//! emitting the free-fn mono arm, or the prelude entry for the fn
|
|
//! gets renamed / dropped, these tests fail.
|
|
//!
|
|
//! See `crates/ail/tests/mono_unification.rs` (iter 23.4) for the
|
|
//! end-to-end runtime correctness checks; this file checks only that
|
|
//! the mono symbols are produced.
|
|
|
|
use ailang_check::{check_workspace, monomorphise_workspace};
|
|
use ailang_core::workspace::load_workspace;
|
|
use std::path::PathBuf;
|
|
|
|
fn fixture(name: &str) -> PathBuf {
|
|
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
|
.join("../../examples")
|
|
.join(name)
|
|
}
|
|
|
|
fn mono_symbol_names(workspace_name: &str) -> Vec<String> {
|
|
let ws = load_workspace(&fixture(workspace_name)).expect("load");
|
|
let diags = check_workspace(&ws);
|
|
assert!(diags.is_empty(), "typecheck failed: {:#?}", diags);
|
|
let mono = monomorphise_workspace(&ws).expect("mono");
|
|
mono.modules
|
|
.values()
|
|
.flat_map(|m| m.defs.iter())
|
|
.filter_map(|d| match d {
|
|
ailang_core::ast::Def::Fn(f) => Some(f.name.clone()),
|
|
_ => None,
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
#[test]
|
|
fn ne_at_int_produces_mono_symbol() {
|
|
let names = mono_symbol_names("ne_at_int_smoke.ail.json");
|
|
assert!(
|
|
names.iter().any(|n| n == "ne__Int"),
|
|
"expected ne__Int in workspace, found: {names:#?}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn lt_at_int_produces_mono_symbol() {
|
|
let names = mono_symbol_names("lt_at_int_smoke.ail.json");
|
|
assert!(
|
|
names.iter().any(|n| n == "lt__Int"),
|
|
"expected lt__Int in workspace, found: {names:#?}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn le_at_str_produces_mono_symbol() {
|
|
let names = mono_symbol_names("le_at_str_smoke.ail.json");
|
|
assert!(
|
|
names.iter().any(|n| n == "le__Str"),
|
|
"expected le__Str in workspace, found: {names:#?}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn gt_at_bool_produces_mono_symbol() {
|
|
let names = mono_symbol_names("gt_at_bool_smoke.ail.json");
|
|
assert!(
|
|
names.iter().any(|n| n == "gt__Bool"),
|
|
"expected gt__Bool in workspace, found: {names:#?}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn ge_at_int_produces_mono_symbol() {
|
|
let names = mono_symbol_names("ge_at_int_smoke.ail.json");
|
|
assert!(
|
|
names.iter().any(|n| n == "ge__Int"),
|
|
"expected ge__Int in workspace, found: {names:#?}"
|
|
);
|
|
}
|