Iter 12b: codegen monomorphisation for polymorphic defs
Polymorphic defs are now actually emitted: each unique instantiation gets its own specialised LLVM fn, mangled @ail_<m>_<def>__<descriptor>. The typechecker's Forall instantiations from Iter 12a now have a real backend. Strategy: - Pass 1 of lower_workspace splits fn-typed defs into mono and poly: module_user_fns keeps LLVM-typed FnSig only for monomorphic fns; module_polymorphic_fns holds the full FnDef for poly defs; module_def_ail_types is a unified AILang-type lookup for both. - Direct calls to poly defs (current-module or qualified cross-module) flow through lower_polymorphic_call: it derives the type substitution from the actual arg AILang types, mangles the symbol, and queues (def, subst) for specialisation. - emit_module drains the queue after the regular defs. Each entry produces a specialised FnDef with rigid vars substituted in both the type and the body (incl. Lam param/ret annotations), then emits via the existing emit_fn pipeline. The synthetic name embeds the descriptor; standard mangling produces the right symbol. Codegen-side type tracking: - locals tuple grew from (name, ssa, llvm_type) to 4-tuple with ail_type. Updated all 6 push sites: fn entry, Let, match-ctor field, match-open-arm, lambda capture (cap_meta also extended), lambda param. - CtorRef gained ail_fields parallel to fields, used by match-arm bindings to inherit AILang types. - New synth_arg_type / synth_with_extras: a small recursive walker that derives the AILang type of an expression in the current scope. Used at poly call sites for arg-type inference. The `extras` parameter shadows locals during Let recursion without &mut self. Helpers added: - derive_substitution(vars, params, arg_tys): unifies the param shapes against the actual args, binding rigid vars; reports unbound vars as an internal error. - apply_subst_to_type / apply_subst_to_term: substitute rigid vars throughout a Type or Term (the Term variant only matters for Lam, the only Term arm carrying types). - descriptor_for_subst / type_descriptor: stable identifier-safe name suffix. `Int → I`, `Bool → B`, `Unit → U`, `Str → S`, ADT `Foo → FFoo`, `Fn → Fn_<...>__r_<ret>`. So `id__I` for id at Int, `apply__I_I` for apply at (Int, Int). - builtin_ail_type / builtin_effect_op_ret: AILang types for the builtin operators and effect ops, used by the codegen-side type tracker. Examples + tests: - examples/poly_id.ail.json: id used at Int (42) and Bool (true). Output: 42 / true. Two specialised fns + adapters + static closures get emitted (visible in the IR). - examples/poly_apply.ail.json: apply(succ, 41) == 42. Exercises the harder case where one of the polymorphic params is itself a function value — the closure-pair ABI survives substitution. - crates/ail/tests/e2e.rs: polymorphic_id_at_int_and_bool and polymorphic_apply_with_fn_param. Tests: 58/58 (was 56/56). Hash invariant holds: sum.ail.json keeps db33f57cb329935e / d9a916a0ed10a3d3. Limitations (known, deferred): - Polymorphic fns can only be DIRECTLY called. Passing a poly fn as a value (let f = id in f(42)) fails — resolve_top_level_fn looks in module_user_fns which doesn't include poly defs. Adding this needs per-instantiation closure-pair globals. - No higher-rank polymorphism: a polymorphic arg passed to another polymorphic call (apply(id, 42)) trips the simple unify_for_subst which doesn't recurse into Forall. Acceptable for the MVP. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"schema": "ailang/v0",
|
||||
"name": "poly_apply",
|
||||
"imports": [],
|
||||
"defs": [
|
||||
{
|
||||
"kind": "fn",
|
||||
"name": "apply",
|
||||
"type": {
|
||||
"k": "forall",
|
||||
"vars": ["a", "b"],
|
||||
"body": {
|
||||
"k": "fn",
|
||||
"params": [
|
||||
{
|
||||
"k": "fn",
|
||||
"params": [{ "k": "var", "name": "a" }],
|
||||
"ret": { "k": "var", "name": "b" },
|
||||
"effects": []
|
||||
},
|
||||
{ "k": "var", "name": "a" }
|
||||
],
|
||||
"ret": { "k": "var", "name": "b" },
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
"params": ["f", "x"],
|
||||
"body": {
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "f" },
|
||||
"args": [{ "t": "var", "name": "x" }]
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "fn",
|
||||
"name": "succ",
|
||||
"type": {
|
||||
"k": "fn",
|
||||
"params": [{ "k": "con", "name": "Int" }],
|
||||
"ret": { "k": "con", "name": "Int" },
|
||||
"effects": []
|
||||
},
|
||||
"params": ["n"],
|
||||
"body": {
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "+" },
|
||||
"args": [
|
||||
{ "t": "var", "name": "n" },
|
||||
{ "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": "do",
|
||||
"op": "io/print_int",
|
||||
"args": [
|
||||
{
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "apply" },
|
||||
"args": [
|
||||
{ "t": "var", "name": "succ" },
|
||||
{ "t": "lit", "lit": { "kind": "int", "value": 41 } }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"schema": "ailang/v0",
|
||||
"name": "poly_id",
|
||||
"imports": [],
|
||||
"defs": [
|
||||
{
|
||||
"kind": "fn",
|
||||
"name": "id",
|
||||
"type": {
|
||||
"k": "forall",
|
||||
"vars": ["a"],
|
||||
"body": {
|
||||
"k": "fn",
|
||||
"params": [{ "k": "var", "name": "a" }],
|
||||
"ret": { "k": "var", "name": "a" },
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
"params": ["x"],
|
||||
"body": { "t": "var", "name": "x" }
|
||||
},
|
||||
{
|
||||
"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_int",
|
||||
"args": [
|
||||
{
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "id" },
|
||||
"args": [
|
||||
{ "t": "lit", "lit": { "kind": "int", "value": 42 } }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"rhs": {
|
||||
"t": "do",
|
||||
"op": "io/print_bool",
|
||||
"args": [
|
||||
{
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "id" },
|
||||
"args": [
|
||||
{ "t": "lit", "lit": { "kind": "bool", "value": true } }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user