0164 if <optional> no-binding folds has_value to true (silent miscompile)
0165 parenthesized nested optional ?(?T) malformed double-wrap (crash)
0166 ?? .{ } struct-literal default unresolved type (crash)
0167 comptime regToValue array-in-aggregate gap + unclean recovery
1.7 KiB
0166 — ?? .{ ... } struct-literal default panics with "unresolved type reached LLVM emission"
Symptom
Using a struct literal as the default of a ?? (null-coalesce) operator panics:
panic: unresolved type reached LLVM emission
in emitStructInit (exit 134 / SIGABRT). The coalesce result type is inferred
correctly (the optional's child T), but that target type is NOT threaded into
the RHS struct-literal lowering, so the struct_init instruction's .ty stays
.unresolved and reaches codegen.
Reproduction
#import "modules/std.sx";
T :: struct { a: i64 = 0; }
mk :: () -> ?T { return null; }
main :: () { t := mk() ?? .{ a = 9 }; print("{}\n", t.a); }
Expected: 9 (null lhs → take the struct-literal default, typed as T).
Observed: panic: unresolved type reached LLVM emission, exit 134.
Investigation prompt
The result type IS inferred correctly — src/ir/expr_typer.zig (~lines 413–425)
returns the optional's child T for the coalesce expression. The gap is in
lowering: the ?? RHS struct literal is lowered without a target type. Suspected
area: src/ir/lower/expr.zig lowerNullCoalesce (dispatched ~expr.zig:2417)
must set the lowering target type to the lhs optional's child (T) before
lowering nc.rhs, so an untyped .{ ... } literal on the RHS resolves to T
the same way an assignment/return target would. Mirror however other contexts
push an expected type into an untyped struct-literal lowering.
Verify: the repro prints 9; also test a present lhs (mk returns a value →
prints the value's field, default not taken) and a nested-field struct literal
default. Add an examples/optionals/09xx-null-coalesce-struct-literal.sx
regression.