Files
sx/examples/optionals/0907-optionals-accessor-through-chain.sx
agra 1b0c857b91 fix: struct-literal → optional coercion + #get through optional chain (issue 0160)
Two fixes for optional interactions surfaced by the #set/#get review. The
original issue 0160 mis-diagnosed (A) as an optional-chain bug; the chain works
fine for real fields. The actual bugs:

(A) A bare struct literal `.{ ... }` against an optional target `?T` was built
into the optional's {payload, has_value} layout instead of the inner T, then
re-wrapped — corrupting the value (a multi-field payload's first field clobbered
by the has_value flag, or a `?T` arg silently null) or failing LLVM
verification. lowerStructLiteral now builds the inner T, materializes it, and
wraps via coerceToType; lowerVarDecl's previously-UNCONDITIONAL optional wrap is
guarded so an already-`?T` value isn't double-wrapped. Fixed across var-decl,
arg, return, nested field, reassignment, and array-element contexts.

(B) `#get` accessors are now reachable through an optional chain (`obj?.getter`):
lowerOptionalChain dispatches the getter via a synthetic receiver, and
expr_typer types `obj?.getter` through a shared getterReturnTypeOnDeref helper
(handles `?T` and `?*T`, value and pointer optionals, and generic-instance
getters like List.len). The `#set` write side through `?.` is intentionally left
matching real-field behavior (optional-chain assignment unsupported).

Regression tests: examples/optionals/0906 (struct-literal → optional) and 0907
(accessor through chain). issues/0160 marked RESOLVED with the corrected root
cause.
2026-06-22 18:28:57 +03:00

40 lines
1.3 KiB
Plaintext

// A `#get` property accessor is reachable through an optional chain
// (`obj?.getter`): the some-branch dispatches the getter and the result is
// re-wrapped as `?R`; a null receiver short-circuits to null. Works for a value
// optional (`?T`), a pointer optional (`?*T`), and a generic-instance getter
// (`List.len`), and types correctly without an explicit annotation. Real fields
// through `?.` keep working unchanged.
// Regression (issue 0160).
#import "modules/std.sx";
Temp :: struct {
raw: i64 = 0;
doubled :: (self: *Temp) -> i64 #get => self.raw * 2;
}
main :: () -> i64 {
t : Temp = .{ raw = 4 };
// value optional ?T — getter through chain
ot : ?Temp = t;
print("?T getter: {}\n", ot?.doubled ?? -1); // 8
// pointer optional ?*T — getter through chain
pt : ?*Temp = @t;
print("?*T getter: {}\n", pt?.doubled ?? -1); // 8
// null receiver short-circuits
nope : ?Temp = null;
print("null getter: {}\n", nope?.doubled ?? -1); // -1
// real field through chain still works
print("?T field: {}\n", ot?.raw ?? -1); // 4
// generic-instance getter (List.len) through chain
xs : List(i64) = .{};
xs.append(10); xs.append(20); xs.append(30);
pxs : ?*List(i64) = @xs;
print("?*List len: {}\n", pxs?.len ?? -1); // 3
return 0;
}