Files
sx/examples/optionals/0906-optionals-struct-literal-into-optional.sx
agra ff9e448f8c fix: optional-chain getter/field correctness from 0160 adversarial review
Five adversarial reviews of the issue-0160 fix surfaced three more bugs in the
touched optional-chain / optional-coercion code; all fixed here:

1. A COLD generic-instance getter through `?.` (`?*Vec(i64)` `.getter`, never
   called directly first) panicked with "unresolved type reached LLVM emission":
   a cold instance method is absent from resolveFuncByName, so the getter's
   return type resolved to .unresolved → a ?unresolved merge type. lowerOptionalChain
   and getterReturnTypeOnDeref now warm the monomorph (ensureGenericInstanceMethodLowered)
   before querying its return type. (The 0907 test passed only by luck — List(i64)
   is warmed by stdlib use; 0907 now also exercises a cold user generic.)

2. A real-field read through a `?*T` chain (`op?.field`, op: ?*T) reinterpreted
   the pointer bits as the field (silent garbage) — the some-branch real-field
   path didn't load through the pointer. It now derefs `?*T` before the field
   access. (Pre-existing — the else-branch predates 0160 — but it's the same
   function and a silent miscompile, so fixed here.)

3. `?[]T = array` skipped the array→slice promotion (corrupt .len/.ptr): the
   lowerVarDecl optional arm wrapped the raw array. It now coerces the value to
   the optional's child type (array→slice) before wrapping.

Regression examples 0906/0907 extended to cover all three. Distinct PRE-EXISTING
bugs the reviews surfaced in untouched subsystems are filed as issues 0161
(struct-literal vs scalar), 0162 (#run returning an optional aggregate), 0163
(untagged-union payload-binding match).
2026-06-22 18:55:41 +03:00

48 lines
1.9 KiB
Plaintext

// A bare struct literal `.{ ... }` against an optional target `?T` builds the
// inner `T` and wraps it once — in every caller context: var-decl, function
// argument, return value, a nested `?T` struct field, reassignment, and an
// array element. Previously this filled the optional's {payload, has_value}
// layout directly, corrupting the value (a multi-field payload's first field
// was clobbered by the has_value flag, or a `?T` arg silently read as null) or
// failing LLVM verification on a double wrap.
// Regression (issue 0160).
#import "modules/std.sx";
T :: struct { a: i64 = 0; b: i64 = 0; }
Outer :: struct { opt: ?T = null; tag: i64 = 0; }
take :: (o: ?T) -> i64 { if o != null { return o!.a * 10 + o!.b; } return -1; }
make :: () -> ?T { return .{ a = 5, b = 6 }; }
main :: () -> i64 {
// var-decl
v : ?T = .{ a = 3, b = 9 };
if v != null { print("var: {} {}\n", v!.a, v!.b); } // 3 9
// function argument
print("arg: {}\n", take(.{ a = 4, b = 7 })); // 47
// return value
r := make();
if r != null { print("ret: {} {}\n", r!.a, r!.b); } // 5 6
// nested ?T struct field
o : Outer = .{ opt = .{ a = 1, b = 2 }, tag = 8 };
if o.opt != null { print("nested: {} {} {}\n", o.opt!.a, o.opt!.b, o.tag); } // 1 2 8
// reassignment
v = .{ a = 11, b = 12 };
if v != null { print("reassign: {} {}\n", v!.a, v!.b); } // 11 12
// array element
arr : [2]?T = .[ .{ a = 20 }, .{ a = 21 } ];
if arr[0] != null { if arr[1] != null { print("arr: {} {}\n", arr[0]!.a, arr[1]!.a); } } // 20 21
// array value into an optional-of-slice `?[]T`: the array→slice promotion
// must run on the child BEFORE wrapping, or `.len`/`.ptr` are corrupted.
nums : [3]i64 = .[ 1, 2, 3 ];
os : ?[]i64 = nums;
if os != null { s := os!; print("optslice: {} {} {}\n", s.len, s[0], s[2]); } // 3 1 3
return 0;
}