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.
41 lines
1.4 KiB
Plaintext
41 lines
1.4 KiB
Plaintext
// Generic `??` preserves the optional's presence across monomorphization.
|
|
//
|
|
// Regression (issue 0180): a `??` inside a generic fn whose lhs is a
|
|
// type-param-typed optional `?T` used to drop the RHS default and return the
|
|
// zero payload — a null `?i32` arriving as the `?T` arg was silently coerced
|
|
// to the monomorphized `?i64` with the has-bit hardcoded to `true` (the
|
|
// optional→optional arg coercion unwrapped-then-rewrapped-present), so
|
|
// `x ?? d` saw a present zero and returned 0 instead of `d`.
|
|
#import "modules/std.sx";
|
|
|
|
Pt :: struct { x: i64 = 0; y: i64 = 0; }
|
|
|
|
unwrap_or :: (d: $T, x: ?T) -> T { return x ?? d; }
|
|
|
|
main :: () {
|
|
// ?i32: null -> default, present -> value.
|
|
bi : ?i32 = null;
|
|
print("a={}\n", unwrap_or(99, bi));
|
|
ci : ?i32 = 5;
|
|
print("b={}\n", unwrap_or(99, ci));
|
|
|
|
// ?i64 instantiation.
|
|
bl : ?i64 = null;
|
|
print("c={}\n", unwrap_or(7, bl));
|
|
dl : ?i64 = 42;
|
|
print("d={}\n", unwrap_or(7, dl));
|
|
|
|
// ?*i64 instantiation: null -> the &v default.
|
|
bp : ?*i64 = null;
|
|
v : i64 = 123;
|
|
print("e={}\n", unwrap_or(@v, bp).*);
|
|
|
|
// ?Struct instantiation: null -> default struct, present -> the value.
|
|
ps : ?Pt = null;
|
|
rp := unwrap_or(Pt.{ x = 1, y = 2 }, ps);
|
|
print("f={} {}\n", rp.x, rp.y);
|
|
qs : ?Pt = Pt.{ x = 8, y = 9 };
|
|
rq := unwrap_or(Pt.{ x = 1, y = 2 }, qs);
|
|
print("g={} {}\n", rq.x, rq.y);
|
|
}
|