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:
@@ -128,6 +128,29 @@ fn polymorphic_apply_with_fn_param() {
|
||||
assert_eq!(stdout.trim(), "42");
|
||||
}
|
||||
|
||||
/// Iter 13b: round-trip a primitive through a parameterised ADT and a
|
||||
/// polymorphic-fn boundary. `type Box[a] = MkBox(a)` plus
|
||||
/// `unbox : forall a. (Box<a>) -> a` plus `print_int(unbox(MkBox(42)))`.
|
||||
/// Guards: ctor lower with substituted LLVM field types,
|
||||
/// match-arm field bindings carrying the substituted AILang type
|
||||
/// down into a polymorphic fn body.
|
||||
#[test]
|
||||
fn parameterised_box_round_trip() {
|
||||
let stdout = build_and_run("box.ail.json");
|
||||
assert_eq!(stdout.trim(), "42");
|
||||
}
|
||||
|
||||
/// Iter 13b: pattern-match on `Maybe<Int>`. `or_else(Some(7), 99) == 7`
|
||||
/// then `or_else(None, 99) == 99`. Guards: match-arm field
|
||||
/// substitution at a parameterised ctor (the `Some(x)` arm must bind
|
||||
/// `x : Int`, not `x : a`).
|
||||
#[test]
|
||||
fn parameterised_maybe_match() {
|
||||
let stdout = build_and_run("maybe_int.ail.json");
|
||||
let lines: Vec<&str> = stdout.lines().collect();
|
||||
assert_eq!(lines, vec!["7", "99"]);
|
||||
}
|
||||
|
||||
/// Guards `ail diff`: a modified body changes the hash of `sum`, while
|
||||
/// `main` stays unchanged. Expects exit code 1, `changed` contains exactly
|
||||
/// `sum`, `unchanged` contains `main`, `added`/`removed` empty.
|
||||
|
||||
Reference in New Issue
Block a user