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.
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
// `??` 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);
|
||||
}
|
||||
Reference in New Issue
Block a user