Files
sx/issues/0173-typed-array-literal-null-element-unresolved.md
agra 5a436eddb1 fix: coerce array/vector literal elements to element type (issue 0168)
[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).
2026-06-22 22:50:20 +03:00

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 prints 7.
  • The .[...] form works fine when no element is null ((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 a null element 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.