iter 23.4: mono-pass unification — class methods + polymorphic free fns in one fixpoint
Restructure ailang-check/src/mono.rs::monomorphise_workspace into a single specialiser over every Type::Forall-quantified Def::Fn, covering both class-method residuals AND polymorphic free-fn call sites in one fixpoint pass. Remove the codegen-time specialiser (lower_polymorphic_call, module_polymorphic_fns, mono_queue, mono_emitted, emit_specialised_fn, plus the now-dead helpers apply_subst_to_term, descriptor_for_subst, type_descriptor) entirely. Codegen sees only monomorphic Def::Fns after this iter — no polymorphic call sites, no codegen-time queue. Architecture changes: - MonoTarget: struct → enum with ClassMethod / FreeFn variants; dedup keys are guaranteed-disjoint via 'class'/'free' first component. - collect_mono_targets: new FreeFnCall channel through synth (cleaner than the plan's separate-walker proposal — one less data flow, substitution tracking comes 'for free' via fresh metavars + post-synth subst.apply). Plan Step 4.4 (polymorphic_free_fns env field) consequently obsolete. - synthesise_mono_fn_for_free_fn: new free-fn entry point; substitutes rigid vars in BOTH the type AND the body (body substitution required because free-fn bodies carry inner Lams whose param_tys reference outer Forall vars — adapted from plan's 'body unchanged' sketch). - mono_symbol_n: N-ary extension of mono_symbol; bit-stable for the single-type-var case (hash-stability pin Task 1 fires zero times through all eight refactor tasks). - rewrite_class_method_calls → rewrite_mono_calls; interleaved-slot collector preserves the positional-cursor invariant across both channels. - workspace_has_typeclasses → workspace_has_specialisable_targets (principled correctness; old predicate happened to already be true for every user workspace because the prelude is auto-injected). Codegen cleanup: - Two call sites of lower_polymorphic_call deleted (codegen/src/lib.rs lines ~1939-1945, ~1965-1973). - lower_polymorphic_call body, module_polymorphic_fns field + population + Emitter wiring, mono_queue / mono_emitted, drain loop, emit_specialised_fn — all removed. - is_static_callee collapsed to module_user_fns-only. - subst.rs apply_subst_to_term + descriptor_for_subst removed; synth.rs type_descriptor removed (all dead post-removal). - ail emit-ir command pipeline gains lift_letrecs + monomorphise_workspace pre-passes (pre-iter-23.4 the codegen-time poly path was masking this dependency — emit-ir is now consistent with build). - Net codegen reduction: -285 lines lib.rs, -93 lines subst.rs. Tests: - mono_hash_stability.rs (new): regression pin for six primitive Eq/Ord mono-symbol body hashes; stayed bit-stable through all eight refactor tasks. - mono_unification.rs (new): six tests covering variant-key disjointness, free-fn target emission, free-fn synthesis with rigid substitution, rewrite walker on free fns, identity for poly-free-fn-only workspaces, end-to-end cmp_max_smoke runtime correctness. - typeclass_22b3.rs: three test sites updated to MonoTarget::ClassMethod enum form. - 73 e2e + 18 typeclass + 81 codegen tests all green workspace-wide. DESIGN.md §'Monomorphisation (post-typecheck, pre-codegen)' amended: two-arm fixpoint described, disjoint-keyed dedup, cursor-aligned walker, removed codegen-time path. Bench check (all exit 0, 112 metrics stable): bench/check.py + compile_check.py + cross_lang.py. Plan parent: docs/plans/2026-05-11-iter-23.4.md (fab1685). Spec parent: docs/specs/2026-05-11-23-eq-ord-prelude.md (841d65d). Per-iter journal documents two adapted deviations from the plan (synth-channel vs separate walker; body substitution; emit-ir fix); no spec defects.
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"schema": "ailang/v0",
|
||||
"name": "cmp_max_smoke",
|
||||
"imports": [],
|
||||
"defs": [
|
||||
{
|
||||
"kind": "fn",
|
||||
"name": "cmp_max",
|
||||
"type": {
|
||||
"k": "forall",
|
||||
"vars": ["a"],
|
||||
"constraints": [{ "class": "Ord", "type": { "k": "var", "name": "a" } }],
|
||||
"body": {
|
||||
"k": "fn",
|
||||
"params": [{ "k": "var", "name": "a" }, { "k": "var", "name": "a" }],
|
||||
"ret": { "k": "var", "name": "a" },
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
"params": ["x", "y"],
|
||||
"body": {
|
||||
"t": "match",
|
||||
"scrutinee": {
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "compare" },
|
||||
"args": [{ "t": "var", "name": "x" }, { "t": "var", "name": "y" }]
|
||||
},
|
||||
"arms": [
|
||||
{ "pat": { "p": "ctor", "type": "prelude.Ordering", "ctor": "LT", "fields": [] }, "body": { "t": "var", "name": "y" } },
|
||||
{ "pat": { "p": "wild" }, "body": { "t": "var", "name": "x" } }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"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": "cmp_max" },
|
||||
"args": [
|
||||
{ "t": "lit", "lit": { "kind": "int", "value": 3 } },
|
||||
{ "t": "lit", "lit": { "kind": "int", "value": 7 } }
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
{
|
||||
"schema": "ailang/v0",
|
||||
"name": "mono_hash_pin_smoke",
|
||||
"imports": [],
|
||||
"defs": [
|
||||
{
|
||||
"kind": "fn",
|
||||
"name": "ord_to_int",
|
||||
"type": {
|
||||
"k": "fn",
|
||||
"params": [{ "k": "con", "name": "prelude.Ordering" }],
|
||||
"ret": { "k": "con", "name": "Int" },
|
||||
"effects": []
|
||||
},
|
||||
"params": ["o"],
|
||||
"body": {
|
||||
"t": "match",
|
||||
"scrutinee": { "t": "var", "name": "o" },
|
||||
"arms": [
|
||||
{ "pat": { "p": "ctor", "type": "prelude.Ordering", "ctor": "LT", "fields": [] }, "body": { "t": "lit", "lit": { "kind": "int", "value": -1 } } },
|
||||
{ "pat": { "p": "ctor", "type": "prelude.Ordering", "ctor": "EQ", "fields": [] }, "body": { "t": "lit", "lit": { "kind": "int", "value": 0 } } },
|
||||
{ "pat": { "p": "ctor", "type": "prelude.Ordering", "ctor": "GT", "fields": [] }, "body": { "t": "lit", "lit": { "kind": "int", "value": 1 } } }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "fn",
|
||||
"name": "main",
|
||||
"type": {
|
||||
"k": "fn", "params": [], "ret": { "k": "con", "name": "Unit" },
|
||||
"effects": ["IO"]
|
||||
},
|
||||
"params": [],
|
||||
"body": {
|
||||
"t": "seq",
|
||||
"lhs": {
|
||||
"t": "do", "op": "io/print_bool",
|
||||
"args": [{
|
||||
"t": "app", "fn": { "t": "var", "name": "eq" },
|
||||
"args": [
|
||||
{ "t": "lit", "lit": { "kind": "int", "value": 1 } },
|
||||
{ "t": "lit", "lit": { "kind": "int", "value": 1 } }
|
||||
]
|
||||
}]
|
||||
},
|
||||
"rhs": {
|
||||
"t": "seq",
|
||||
"lhs": {
|
||||
"t": "do", "op": "io/print_bool",
|
||||
"args": [{
|
||||
"t": "app", "fn": { "t": "var", "name": "eq" },
|
||||
"args": [
|
||||
{ "t": "lit", "lit": { "kind": "bool", "value": true } },
|
||||
{ "t": "lit", "lit": { "kind": "bool", "value": true } }
|
||||
]
|
||||
}]
|
||||
},
|
||||
"rhs": {
|
||||
"t": "seq",
|
||||
"lhs": {
|
||||
"t": "do", "op": "io/print_bool",
|
||||
"args": [{
|
||||
"t": "app", "fn": { "t": "var", "name": "eq" },
|
||||
"args": [
|
||||
{ "t": "lit", "lit": { "kind": "str", "value": "a" } },
|
||||
{ "t": "lit", "lit": { "kind": "str", "value": "a" } }
|
||||
]
|
||||
}]
|
||||
},
|
||||
"rhs": {
|
||||
"t": "seq",
|
||||
"lhs": {
|
||||
"t": "do", "op": "io/print_int",
|
||||
"args": [{
|
||||
"t": "app", "fn": { "t": "var", "name": "ord_to_int" },
|
||||
"args": [{
|
||||
"t": "app", "fn": { "t": "var", "name": "compare" },
|
||||
"args": [
|
||||
{ "t": "lit", "lit": { "kind": "int", "value": 1 } },
|
||||
{ "t": "lit", "lit": { "kind": "int", "value": 2 } }
|
||||
]
|
||||
}]
|
||||
}]
|
||||
},
|
||||
"rhs": {
|
||||
"t": "seq",
|
||||
"lhs": {
|
||||
"t": "do", "op": "io/print_int",
|
||||
"args": [{
|
||||
"t": "app", "fn": { "t": "var", "name": "ord_to_int" },
|
||||
"args": [{
|
||||
"t": "app", "fn": { "t": "var", "name": "compare" },
|
||||
"args": [
|
||||
{ "t": "lit", "lit": { "kind": "bool", "value": false } },
|
||||
{ "t": "lit", "lit": { "kind": "bool", "value": true } }
|
||||
]
|
||||
}]
|
||||
}]
|
||||
},
|
||||
"rhs": {
|
||||
"t": "do", "op": "io/print_int",
|
||||
"args": [{
|
||||
"t": "app", "fn": { "t": "var", "name": "ord_to_int" },
|
||||
"args": [{
|
||||
"t": "app", "fn": { "t": "var", "name": "compare" },
|
||||
"args": [
|
||||
{ "t": "lit", "lit": { "kind": "str", "value": "a" } },
|
||||
{ "t": "lit", "lit": { "kind": "str", "value": "b" } }
|
||||
]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user