iter 22b.3.5: fix — quality-review (chained-call test, hoist class_index, drop noise comments)

This commit is contained in:
2026-05-09 20:59:43 +02:00
parent dd87b36e00
commit e8018d48b6
3 changed files with 126 additions and 7 deletions
+52
View File
@@ -390,3 +390,55 @@ fn fixpoint_dedupes_repeated_calls_at_same_type() {
}
assert_eq!(count, 1, "exactly one show#Int across the workspace");
}
/// Property: when a synthesised instance body itself calls another class
/// method (the canonical `Eq.ne x y = not (eq x y)` shape), the workspace
/// fixpoint closes over the chained call — Round 1 synthesises the outer
/// fn, Round 2 picks up the inner class-method residual on the freshly-
/// appended body and synthesises that. Without the multi-round loop, the
/// inner mono fn would be missing.
///
/// Fixture (`test_22b3_chained_calls`):
/// class B a where b_method : (a) -> Int
/// class A a where a_method : (a) -> Int default \x. b_method x
/// instance B Int { b_method = \y. y }
/// instance A Int { /* uses class default */ }
/// main : () -> Int = a_method 5
///
/// Expected post-mono workspace contents: BOTH `a_method#Int` (Round 1,
/// from the class default) AND `b_method#Int` (Round 2, from instance B's
/// body).
#[test]
fn fixpoint_handles_chained_class_method_calls() {
let entry = examples_dir().join("test_22b3_chained_calls.ail.json");
let ws = ailang_core::load_workspace(&entry).expect("load");
let diags = ailang_check::check_workspace(&ws);
assert!(diags.is_empty(), "fixture must typecheck: {:?}", diags);
let ws = ailang_check::monomorphise_workspace(&ws).expect("mono");
// Locate both synthesised fns workspace-wide.
let mut has_a_method_int = false;
let mut has_b_method_int = false;
for m in ws.modules.values() {
for d in &m.defs {
if let ailang_core::ast::Def::Fn(f) = d {
if f.name == "a_method#Int" {
has_a_method_int = true;
}
if f.name == "b_method#Int" {
has_b_method_int = true;
}
}
}
}
assert!(
has_a_method_int,
"a_method#Int must be synthesised in Round 1 from main's call site"
);
assert!(
has_b_method_int,
"b_method#Int must be synthesised in Round 2 from a_method#Int's body \
— multi-round fixpoint property"
);
}
+1 -7
View File
@@ -74,14 +74,10 @@ pub fn monomorphise_workspace(ws: &Workspace) -> Result<Workspace> {
return Ok(ws.clone());
}
// Take ownership of a mutable workspace clone; the pass
// appends synthesised defs to per-module def lists.
let mut ws_owned: Workspace = ws.clone();
let env = build_workspace_env(&ws_owned);
let class_index = build_class_index(&ws_owned);
// Dedup set: every (class, method, type-hash) we have already
// synthesised. Once a key is here, future collection rounds
// skip it.
let mut synthesised: BTreeSet<(String, String, String)> = BTreeSet::new();
// Fixpoint: keep collecting until a round adds nothing new.
@@ -117,7 +113,6 @@ pub fn monomorphise_workspace(ws: &Workspace) -> Result<Workspace> {
// truth for ClassDef + InstanceDef lookups. Both come
// from the un-modified workspace registry — synthesis
// never mutates the registry.
let class_index = build_class_index(&ws_owned);
for t in &new {
let key = mono_target_key(t);
let registry_key = (t.class.clone(), ailang_core::canonical::type_hash(&t.type_));
@@ -153,7 +148,6 @@ pub fn monomorphise_workspace(ws: &Workspace) -> Result<Workspace> {
}
}
// Task 6 will splice the call-site rewrite here.
Ok(ws_owned)
}