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.
29 lines
998 B
Plaintext
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);
|
|
}
|