Files
sx/examples/optionals/0917-optionals-alias-null-coalesce-struct-default.sx
agra 097d23d909 fix: presence-preserving optional->optional coercion (issue 0180)
The generic-?? wrong-fallback was not in lowerNullCoalesce: coercing
?A -> ?B (differing payload, e.g. the ?i32->?i64 call-arg coercion when
instantiating unwrap_or(99, ?i32)) routed through .optional_wrap, which
unconditionally unwrapped the source and re-wrapped as ALWAYS-PRESENT, so
a null became present-zero everywhere (args, returns, field init,
var-decl, ??). Add a CoercionPlan.optional_to_optional (conversions.zig)
+ a presence-preserving arm in coerceMode (coerce.zig): has_value ->
present: unwrap+coerce-child+wrap-present; absent: constNull(dst); merge
via a dst_ty block param. lowerVarDecl gains a !src_is_optional guard so
an annotated x : ?B = <?A> routes through the same arm (also makes
aggregate-payload var-decl ?[3]i64->?[]i64 / ?Concrete->?Protocol work).

Alias-optional struct-literal default already works (grouping + 0166);
a 1-tuple default ?(i32,) ?? 5 now emits a clean diagnostic instead of an
LLVM PHI abort (no implicit scalar->1-tuple coercion per spec).

Regressions: optionals/0916 (generic ??), 0917 (alias struct default),
0918 (var-decl optional->optional), diagnostics/1202 (1-tuple default) +
a conversions.test.zig unit test. Verified by 3 adversarial reviews,
suite 798/0.
2026-06-23 16:16:47 +03:00

29 lines
998 B
Plaintext

// `??` on an alias-of-optional with a struct-literal default — present + null.
//
// Regression (issue 0180): `?Opt ?? S.{...}` where `Opt :: ?S` must resolve
// the alias child to the same struct TypeId on both branches and lower the
// struct-literal default against that resolved child (issue-0166 threading),
// so the present-payload and the default share the merge TypeId. Both the
// null path (default) and the present path (the payload) are exercised.
#import "modules/std.sx";
S :: struct { a: i64 = 0; b: i64 = 0; }
Opt :: ?S;
main :: () {
// null lhs -> the struct-literal default.
o : Opt = null;
x := o ?? S.{ a = 7, b = 8 };
print("a={} {}\n", x.a, x.b);
// present lhs -> the payload, default ignored.
o2 : Opt = S.{ a = 1, b = 2 };
y := o2 ?? S.{ a = 7, b = 8 };
print("b={} {}\n", y.a, y.b);
// untyped `.{}` default against the alias child (issue-0166 path).
o3 : Opt = null;
z := o3 ?? .{};
print("c={} {}\n", z.a, z.b);
}