Files
sx/examples/optionals/0916-optionals-generic-null-coalesce-fallback.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

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);
}