Files
sx/issues/0126-array-arg-slice-generic-param-unbound.md
agra 309f48e1b5 fix(0126): array args bind []$T generic params; uninferrable type params diagnose at the call
extractTypeParam's slice arm only extracted from slice-typed args, so
first(a) with a : [3]s64 at first :: (xs: []$T) -> T left T unbound
and the mono body reached LLVM emission carrying the .unresolved
sentinel (panic). The arm now also extracts from array args via the
array's element type — mirroring the array→slice promotion concrete
slice params already perform; the existing arg coercion handles the
rest.

lowerGenericCall additionally diagnoses any still-uninferrable TYPE
param at the call site instead of monomorphizing unbound — the
deliberate string-at-[]$T gap used to hit the same sentinel panic and
now errors with a source-located message. Comptime value params
($N: u32) and ..$Ts packs bind through their own dispatch and stay
exempt.

Regressions: examples/0212-generics-array-arg-slice-param.sx (scalar /
u8 / struct elements + the slice spelling) and
examples/1168-diagnostics-generic-param-uninferrable.sx (string arg
diagnostic) — both failed pre-fix.
2026-06-12 08:31:45 +03:00

3.6 KiB

RESOLVED — 0126: array arg at a []$T param leaves T unbound → LLVM emission panic

RESOLVED (2026-06-12). Root cause: extractTypeParam's .slice_type_expr arm (src/ir/lower/generic.zig) only extracted from .slice-typed args, so an array arg left T unbound and monomorphizeFunction stamped .unresolved through the body — no diagnostic before the emitter's sentinel panic. Fix: (1) the arm also extracts from .array args via the array's element type, mirroring the array→slice promotion concrete slice params perform (the existing coercion then handles the lowered arg); (2) lowerGenericCall (src/ir/lower/call.zig) diagnoses any still-uninferrable TYPE param at the call site ("cannot infer generic type parameter ...") instead of monomorphizing unbound — covers the deliberate string-at-[]$T gap, which used to hit the same panic. Comptime value params and ..$Ts packs stay exempt. Regression tests: examples/0212-generics-array-arg-slice-param.sx (scalar/u8/struct elements + slice spelling; panicked or mis-typed pre-fix) and examples/1168-diagnostics-generic-param-uninferrable.sx (string arg; panicked pre-fix). Gates: zig build test 426/426, suite 594/594, distribution repo 14/14.

Symptom

Calling a generic function whose param is a slice of the type param — first :: (xs: []$T) -> T — with an ARRAY argument panics the compiler during emission instead of compiling (or diagnosing).

  • Observed: panic: unresolved type reached LLVM emission — a type resolution failure was not diagnosed/aborted (src/backend/llvm/types.zig:175, via emitIndexGet in the monomorphized body).
  • Expected: the array coerces to a slice at the []T param — the same promotion a CONCRETE []s64 param (and a []s64-annotated local) already performs — so T binds from the array's element type and the call compiles.

Passing an actual slice works (s : []s64 = a; first(s) prints the element); only the direct array spelling breaks, and only for generic slice params.

Reproduction

#import "modules/std.sx";

first :: (xs: []$T) -> T {
    return xs[0];
}

main :: () -> s32 {
    a : [3]s64 = ---;
    a[0] = 7; a[1] = 8; a[2] = 9;
    v := first(a);
    print("{}\n", v);
    return 0;
}

Observed at master 837b5d3: the panic above. With s : []s64 = a; first(s) it prints 7.

Investigation prompt

Root cause: extractTypeParam (src/ir/lower/generic.zig, the .slice_type_expr arm) only extracts when the ARG type is itself a .slice — an .array arg returns null, so buildTypeBindings leaves T unbound, monomorphizeFunction stamps the body with T → .unresolved, and nothing diagnoses before the emitter's sentinel tripwire fires (the declaration-time gate from ca5bd52 doesn't apply: this T IS bindable — the call-site inference is what failed).

Fix: mirror the existing array→slice param coercion in the binding extractor — in the .slice_type_expr arm, when the arg type is an .array, recurse on the array's ELEMENT type exactly as the .slice case does. Verify the lowered call then coerces the array arg to the mono's now-concrete []T param (the same array_to_slice the concrete path uses) — if not, the generic dispatch arg path needs the same promotion.

Out of scope, known gap (CHECKPOINT-MEM): string deliberately does not bind []$T — that case diagnoses "unknown type 'T'" and its story is deferred to the mem-stream phases.

Verification: the repro prints 7; the slice spelling still works; zig build && zig build test, bash tests/run_examples.sh green; pin the repro as a generics example.