Iter 13b: codegen for parameterised ADTs
Closes the gap between Iter 13a (parameterised ADTs in the checker) and end-to-end execution. ADT ctor code stays inlined at every use site — no mono-queue for types, no new symbols — but LLVM field types are now derived per use site via substitution. `CtorRef` gains `type_vars: Vec<String>` so use sites can identify which fields reference rigid vars. `cref.fields` (precomputed LLVM strings) is documented as monomorphic-only and read past for parameterised ADTs. `lower_ctor`: derives a `BTreeMap<String, Type>` substitution from `synth_arg_type` of each arg via `unify_for_subst`, then maps every `ail_field` through `apply_subst_to_type` + `llvm_type` for the per-store types. Monomorphic ADTs hit the original fast path. `lower_match`: builds `arm_subst` from `s_ail.args` ↔ `cref.type_vars`, substitutes through each `cref.ail_fields[idx]`, and uses the substituted type both for the LLVM `load` AND as the AILang slot of the local — so a downstream `unbox(b)` sees `b: Int`, not `b: a`. `synth_arg_type` for `Term::Ctor`: returns concrete type-args derived from the ctor's term-args. Vars left unpinned (e.g. `None` for `Maybe a`) fall back to `Type::unit()`. `llvm_type(Type::Var)` now hard-errors. Earlier this silently fell through to `ptr` (the ADT-via-ptr fallback), producing garbage IR. Failing loudly here surfaces missed substitutions in the test suite. Two e2e tests (`box.ail.json`, `maybe_int.ail.json`) cover ctor lower with substituted field types and match-arm field substitution. 64 tests green; clippy clean (two pre-existing warnings untouched). Hash invariant holds. Out of scope per agent assignment: poly-fn-as-value, higher-rank polymorphism, DESIGN/JOURNAL updates (deferred to 13c).
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"schema": "ailang/v0",
|
||||
"name": "box",
|
||||
"imports": [],
|
||||
"defs": [
|
||||
{
|
||||
"kind": "type",
|
||||
"name": "Box",
|
||||
"vars": ["a"],
|
||||
"doc": "A unary box around a polymorphic value.",
|
||||
"ctors": [
|
||||
{
|
||||
"name": "MkBox",
|
||||
"fields": [{ "k": "var", "name": "a" }]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "fn",
|
||||
"name": "unbox",
|
||||
"type": {
|
||||
"k": "forall",
|
||||
"vars": ["a"],
|
||||
"body": {
|
||||
"k": "fn",
|
||||
"params": [
|
||||
{
|
||||
"k": "con",
|
||||
"name": "Box",
|
||||
"args": [{ "k": "var", "name": "a" }]
|
||||
}
|
||||
],
|
||||
"ret": { "k": "var", "name": "a" },
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
"params": ["b"],
|
||||
"doc": "Polymorphic projection out of a Box.",
|
||||
"body": {
|
||||
"t": "match",
|
||||
"scrutinee": { "t": "var", "name": "b" },
|
||||
"arms": [
|
||||
{
|
||||
"pat": {
|
||||
"p": "ctor",
|
||||
"ctor": "MkBox",
|
||||
"fields": [{ "p": "var", "name": "x" }]
|
||||
},
|
||||
"body": { "t": "var", "name": "x" }
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "fn",
|
||||
"name": "main",
|
||||
"type": {
|
||||
"k": "fn",
|
||||
"params": [],
|
||||
"ret": { "k": "con", "name": "Unit" },
|
||||
"effects": ["IO"]
|
||||
},
|
||||
"params": [],
|
||||
"doc": "Round-trip 42 through MkBox + unbox.",
|
||||
"body": {
|
||||
"t": "do",
|
||||
"op": "io/print_int",
|
||||
"args": [
|
||||
{
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "unbox" },
|
||||
"args": [
|
||||
{
|
||||
"t": "ctor",
|
||||
"type": "Box",
|
||||
"ctor": "MkBox",
|
||||
"args": [
|
||||
{ "t": "lit", "lit": { "kind": "int", "value": 42 } }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
{
|
||||
"schema": "ailang/v0",
|
||||
"name": "maybe_int",
|
||||
"imports": [],
|
||||
"defs": [
|
||||
{
|
||||
"kind": "type",
|
||||
"name": "Maybe",
|
||||
"vars": ["a"],
|
||||
"doc": "Optional value with a polymorphic payload.",
|
||||
"ctors": [
|
||||
{ "name": "None", "fields": [] },
|
||||
{
|
||||
"name": "Some",
|
||||
"fields": [{ "k": "var", "name": "a" }]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "fn",
|
||||
"name": "or_else",
|
||||
"type": {
|
||||
"k": "fn",
|
||||
"params": [
|
||||
{
|
||||
"k": "con",
|
||||
"name": "Maybe",
|
||||
"args": [{ "k": "con", "name": "Int" }]
|
||||
},
|
||||
{ "k": "con", "name": "Int" }
|
||||
],
|
||||
"ret": { "k": "con", "name": "Int" },
|
||||
"effects": []
|
||||
},
|
||||
"params": ["m", "d"],
|
||||
"doc": "Returns the wrapped Int for Some(x) and the default d for None.",
|
||||
"body": {
|
||||
"t": "match",
|
||||
"scrutinee": { "t": "var", "name": "m" },
|
||||
"arms": [
|
||||
{
|
||||
"pat": { "p": "ctor", "ctor": "None", "fields": [] },
|
||||
"body": { "t": "var", "name": "d" }
|
||||
},
|
||||
{
|
||||
"pat": {
|
||||
"p": "ctor",
|
||||
"ctor": "Some",
|
||||
"fields": [{ "p": "var", "name": "x" }]
|
||||
},
|
||||
"body": { "t": "var", "name": "x" }
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "fn",
|
||||
"name": "main",
|
||||
"type": {
|
||||
"k": "fn",
|
||||
"params": [],
|
||||
"ret": { "k": "con", "name": "Unit" },
|
||||
"effects": ["IO"]
|
||||
},
|
||||
"params": [],
|
||||
"doc": "Print or_else for both arms; expected 7 then 99.",
|
||||
"body": {
|
||||
"t": "seq",
|
||||
"lhs": {
|
||||
"t": "do",
|
||||
"op": "io/print_int",
|
||||
"args": [
|
||||
{
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "or_else" },
|
||||
"args": [
|
||||
{
|
||||
"t": "ctor",
|
||||
"type": "Maybe",
|
||||
"ctor": "Some",
|
||||
"args": [
|
||||
{ "t": "lit", "lit": { "kind": "int", "value": 7 } }
|
||||
]
|
||||
},
|
||||
{ "t": "lit", "lit": { "kind": "int", "value": 99 } }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"rhs": {
|
||||
"t": "do",
|
||||
"op": "io/print_int",
|
||||
"args": [
|
||||
{
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "or_else" },
|
||||
"args": [
|
||||
{
|
||||
"t": "ctor",
|
||||
"type": "Maybe",
|
||||
"ctor": "None",
|
||||
"args": []
|
||||
},
|
||||
{ "t": "lit", "lit": { "kind": "int", "value": 99 } }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user