Files
sx/issues/0174-tuple-positional-literal-element-not-coerced.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

39 lines
1.8 KiB
Markdown

# 0174 — positional literal for a TUPLE target does not coerce elements (same corruption class as 0168)
## Symptom
A positional literal `.{ a, b }` whose target is a TUPLE does not coerce its
elements to the tuple's field types. When a field type is an optional (or any
type the element doesn't already match), the raw element is stored into the
field slot with the wrong shape — e.g. a bare `i64` into a `{i64,i1}` optional
slot — so the value reads back wrong (a present optional reads as absent). Silent
miscompile. This is the tuple analogue of issue 0168 (which fixed the
array/vector case).
## Reproduction
```sx
#import "modules/std.sx";
main :: () {
t : (?i64, f64) = .{ 7, 3.0 };
print("{}\n", t.0 ?? -1); // prints "-1" — WRONG, expected 7
}
```
Expected: `7` (field 0 is a present `?i64`). Observed: `-1` (read as absent).
## Investigation prompt
`src/ir/lower/expr.zig` `lowerStructLiteral` positional branch. Issue 0168 added
element coercion for array/vector targets via `array_elem_ty`, but a TUPLE target
is neither a struct (so `getStructFields` returns empty → the `i < struct_fields.len`
field-coercion path doesn't fire) nor array/vector (so `array_elem_ty` is
`.unresolved`). Extend the positional branch to recognize a `.tuple` target:
fetch the tuple's per-field types (`TupleInfo.fields`) and coerce element `i` to
`fields[i]` (mirroring the struct-field path, which uses `struct_fields[i].ty`).
Set `target_type` per element so a nested untyped literal element resolves too.
Follow the no-silent-fallback rule. Verify: the repro prints `7`; a tuple with
mixed element coercions (int→float, concrete→protocol, array→slice) initializes
correctly; named tuples `(x: ?i64, y: f64)` too. Add an
`examples/types/01xx-tuple-positional-optional-element.sx` regression.