issue 0151 RESOLVED: infer generic $T through generic-struct / pointer / UFCS-pack params
The generic-inference engine could not bind a $T from a generic-struct argument head. Four gaps, all on the inference + UFCS dispatch path: - extractTypeParam / matchTypeParam(Static) gained a parameterized_type_expr arm: recover the arg instance's recorded per-param bindings (struct_instance_bindings + the template's ordered type_params via struct_instance_author) and recurse positionally, so $T binds from Box($T) <=> Box(i64) like it does from []$T <=> []i64. This also fixes the pointer case — *Box($T) recurses into its Box($T) pointee. - The pointer_type_expr arm now falls through to match the pointee against a non-pointer arg (auto-address-of: a *Box($T) param accepts a by-value Box($T), e.g. the UFCS receiver b.m()). - ExprTyper.inferType gained a .lambda arm building the closure type from the lambda's annotations, so the UFCS binder (which types args from the raw AST before they are lowered) can bind a Closure(..) -> $R from the worker's declared return type. - A pack UFCS target (worker: Closure(..) -> $R, ..$args) now routes through the same lowerPackFnCall the direct call uses, with the receiver spliced in as args[0] (lowerPackFnCall reads only call_node.args, never the callee). Regression tests: examples/0214 (direct + UFCS closure-return pack) and examples/0215 (by-value / pointer / multi-param / nested / UFCS-auto-ref generic-struct-head inference). Suite green 728/0.
This commit is contained in:
@@ -1,5 +1,47 @@
|
||||
# 0151 — generic type-var not inferred through a pointer / via UFCS (LLVM SIGTRAP / "cannot infer")
|
||||
|
||||
## ✅ RESOLVED (2026-06-21)
|
||||
|
||||
**Root cause** — the generic-inference engine had no path to bind a `$T`
|
||||
from a generic-struct argument head. Three gaps, all in
|
||||
`src/ir/lower/generic.zig` + the UFCS dispatch:
|
||||
|
||||
1. `extractTypeParam` / `matchTypeParam` / `matchTypeParamStatic` lacked a
|
||||
`.parameterized_type_expr` arm — so `Box($T)` (and, recursively, the
|
||||
pointee of `*Box($T)`) never matched a type-param. Added an arm that
|
||||
recovers the arg instance's recorded per-param bindings
|
||||
(`struct_instance_bindings` + the template's ordered `type_params` via
|
||||
`struct_instance_author`) and recurses positionally.
|
||||
2. The `pointer_type_expr` arm bailed when the arg wasn't itself a pointer.
|
||||
A UFCS receiver (`b.m()`) / a value passed to a `*T` param is auto-
|
||||
address-of'd, so the arg type is the *value* `Box($T)`. Added a fall-
|
||||
through that matches the pointee against the non-pointer arg.
|
||||
3. `ExprTyper.inferType` had no `.lambda` arm (returned `.unresolved`), so
|
||||
the UFCS binder — which types args from the raw AST *before* they're
|
||||
lowered — couldn't read a lambda's declared return type to bind a
|
||||
`Closure(..) -> $R`. Added an arm that builds the closure type from the
|
||||
lambda's annotations.
|
||||
4. A pack UFCS target (`worker: Closure(..) -> $R, ..$args`) was dispatched
|
||||
through the non-pack generic path, which can't expand the pack. Routed
|
||||
it through the SAME `lowerPackFnCall` the direct call uses, with the
|
||||
receiver spliced in as `args[0]` (a synthetic call — `lowerPackFnCall`
|
||||
reads only `call_node.args`, never the callee).
|
||||
|
||||
**Fix verified** — the repro prints `value=42` (both spellings). Regression
|
||||
tests: `examples/0214-generics-ufcs-closure-return-pack.sx` (direct + UFCS
|
||||
closure-return pack) and `examples/0215-generics-infer-through-pointer.sx`
|
||||
(by-value / pointer / multi-param / nested / UFCS-auto-ref struct-head
|
||||
inference). Full suite green (726/0).
|
||||
|
||||
**Downstream (NOT this bug):** with `await`/`cancel` now callable, the
|
||||
B1.2 async examples surface a SEPARATE codegen bug — `Atomic(bool)` emits a
|
||||
sub-byte (i1) atomic load/store that fails LLVM verification (filed as a new
|
||||
issue). The `Future.canceled: Atomic(bool)` field hits it, so `1805`/`1806`
|
||||
stay blocked on that, not on 0151.
|
||||
|
||||
---
|
||||
|
||||
|
||||
## WIDENED (adversarial review of B1.2, 2026-06-21)
|
||||
The UFCS-closure-return-pack case below is one symptom of a BROADER generic-inference
|
||||
gap: **sx cannot infer a generic `$T` from a POINTER-wrapped argument.** Minimal repro,
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
// Repro for issue 0151 — UFCS dot-call where `$R` is inferred from a
|
||||
// worker closure's RETURN type through a variadic `..$args` pack leaves
|
||||
// `$R` unresolved (SIGTRAP at LLVM emission). The DIRECT spelling
|
||||
// `mymk(bx, worker, 40, 2)` resolves `$R = i64` and works; the UFCS
|
||||
// spelling `bx.mymk(worker, 40, 2)` does not. Depends on no project
|
||||
// symbols beyond modules/std.sx.
|
||||
#import "modules/std.sx";
|
||||
|
||||
Box :: struct { n: i64; }
|
||||
Wrap :: struct ($R: Type) { value: R; }
|
||||
|
||||
mymk :: ufcs (b: Box, worker: Closure(..$args) -> $R, ..$args) -> Wrap($R) {
|
||||
f : Wrap($R) = ---;
|
||||
f.value = worker(..args);
|
||||
return f;
|
||||
}
|
||||
|
||||
main :: () -> i32 {
|
||||
bx : Box = .{ n = 1 };
|
||||
g := bx.mymk((a: i64, b: i64) -> i64 => a + b, 40, 2);
|
||||
print("value={}\n", g.value);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user