Optional (?T) operands were implicitly unwrapped without proof of
presence, silently miscompiling a NULL ?T to garbage. Unwraps in
binary ops and other expression positions are now gated on flow
narrowing: a ?T value is only auto-unwrapped where control flow has
established it is non-null (the narrowed_refs set). Outside a narrowed
region, an implicit unwrap is rejected rather than producing garbage.
Touches the lowering pipeline (lower.zig + lower/{call,closure,coerce,
comptime,control_flow,expr,ffi,generic,pack,stmt}.zig). Adds optionals
examples 0919-0923 and closures example 0312 covering flow narrowing,
binop narrowing, no-implicit-unwrap rejection, and no closure leak of
narrowed state. Updates specs.md and readme.md.
34 lines
1.4 KiB
Plaintext
34 lines
1.4 KiB
Plaintext
// Calling a closure VALUE whose parameter is `?T` coerces the argument to the
|
|
// param type, just like a call to a top-level function does: a concrete arg
|
|
// wraps to a present optional, and `null` becomes an absent optional.
|
|
//
|
|
// Regression (issue 0186): the closure-value call path lowered args without
|
|
// coercing to the closure's declared param types, so a concrete `7` arrived as
|
|
// a bare payload (read ABSENT) and `null` reached a `{T,i1}` slot as a bare
|
|
// pointer (LLVM verifier failure). Fixed by typing args against the closure's
|
|
// params (`resolveCallParamTypes`) AND coercing them at the call site
|
|
// (`coerceClosureCallArgs`).
|
|
#import "modules/std.sx";
|
|
|
|
main :: () {
|
|
pick := (p: ?i64) -> i64 => {
|
|
if p == null { return -1; }
|
|
return p; // narrowed inside the lambda body
|
|
};
|
|
print("pick 7: {}\n", pick(7)); // 7 (concrete arg wraps present)
|
|
print("pick null: {}\n", pick(null)); // -1 (null arg → absent)
|
|
|
|
// also via a closure stored in a struct field
|
|
Holder :: struct { f: Closure(?i64) -> i64; }
|
|
h := Holder.{ f = pick };
|
|
print("h 5: {}\n", h.f(5)); // 5
|
|
print("h null: {}\n", h.f(null)); // -1
|
|
|
|
// and via a plain function-pointer VALUE (same coercion contract)
|
|
fp : (?i64) -> i64 = target;
|
|
print("fp 8: {}\n", fp(8)); // 8
|
|
print("fp null: {}\n", fp(null)); // -1
|
|
}
|
|
|
|
target :: (p: ?i64) -> i64 { if p == null { return -1; } return p; }
|