[N]?T arrays were corrupted: a positional literal .{ null, 7 } stored
bare T/null elements into {T,i1} optional slots because array elements
were never coerced (getStructFields is empty for an array, so the
i<struct_fields.len field-coercion gate never fired). A present element
then read back as absent and direct indexing segfaulted.
lowerStructLiteral's positional branch now computes array_elem_ty for
array/vector targets and coerces each element to it; lowerArrayLiteral
generalizes its slice-only coercion to coerce every element via
coerceToType (layout-aware: scalar->{T,i1}, pointer-sentinel->one-word,
array->slice, concrete->protocol). Verified by 3 adversarial reviews,
suite 780/0.
Regression: examples/optionals/0913-optionals-array-of-optionals.sx.
Filed adjacent pre-existing bugs: 0173 (typed .[null,..] element), 0174
(tuple positional-element coercion), 0175 (positional struct literal
variable element zeroed).
3.2 KiB
0173 — (T).[ ... ] typed array-literal with a null element panics (unresolved type at LLVM emission)
Symptom
An explicit-type array literal of the (T).[ elems ] form (the .[...]
array_literal AST node, NOT the .{ ... } positional struct literal) whose
element type is an optional and one of whose elements is the bare null
literal reaches LLVM emission with an .unresolved type and panics:
thread … panic: unresolved type reached LLVM emission — a type resolution
failure was not diagnosed/aborted
src/backend/llvm/types.zig:196 toLLVMTypeInfo (.unresolved arm)
src/backend/llvm/ops.zig:120 emitConstNull (instruction.ty == .unresolved)
Observed: hard panic (exit 134).
Expected: the null element lowers to a null ?i64, the literal builds a
[2]?i64, and arr[1] ?? -1 prints 7.
Reproduction
#import "modules/std.sx";
main :: () {
arr := ([2]?i64).[ null, 7 ]; // typed `.[...]` form with a null element
print("{}\n", arr[1] ?? -1); // expected: 7
}
Notes:
- The equivalent positional form works (fixed under issue 0168):
arr : [2]?i64 = .{ null, 7 };correctly prints7. - The
.[...]form works fine when no element isnull((i64).[10,20],([2]?i64).[ 1, 7 ]), and nested slices via.[...](rows : [][]i64 = .[ .[1,2], .[3,4,5] ]) also work. The panic is specific to anullelement under the typed.[...]array-literal path. - Pre-existing: reproduces on clean HEAD (commit
2ea25e84, before the 0168 fix). Independent of issue 0168.
Investigation prompt
lowerArrayLiteral (src/ir/lower/expr.zig, the .[...] array_literal
node, NOT lowerStructLiteral) resolves the literal's element type from
al.type_expr via resolveArrayLiteralType. For ([2]?i64).[ ... ] the
element type should resolve to ?i64, and each element is lowered with
target_type = elem_ty. A bare null element lowers via the null_literal
path, which produces a const_null whose type is .unresolved — the
target_type (?i64) is apparently not being consulted when lowering the bare
null element inside the .[...] form (whereas it IS in the .{ ... }
positional / scalar-assignment paths, which wrap correctly).
Likely fix area: the null_literal lowering in src/ir/lower/expr.zig (search
for .null_literal / constNull) — it must honor self.target_type (the
optional element/dest type) so null becomes const_null(?i64) rather than
const_null(.unresolved). Alternatively, the per-element coercion in
lowerArrayLiteral (around the elem_ty != .unresolved coercion added for
0168) could coerce the .unresolved-typed null to elem_ty — but the cleaner
fix is for the bare null to resolve its type from target_type at lowering
time (matching how x : ?i64 = null already works).
Per the no-silent-fallback rule: do NOT default the unresolved null to a
guessed type; resolve it from the contextual target_type, and if that is
itself unresolved, emit a diagnostic rather than letting .unresolved reach
LLVM emission.
Verify: the repro above prints 7. Also check ([3]?i64).[ null, null, 5 ]
and a struct-payload ([2]?Pt).[ null, .{x=1,y=2} ]. Add an
examples/optionals/09xx-typed-array-literal-null.sx regression.