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.
17 lines
729 B
Plaintext
17 lines
729 B
Plaintext
// An un-narrowed `?T` does NOT implicitly unwrap to its concrete payload in a
|
|
// value position — it is a compile error pointing at the explicit forms.
|
|
//
|
|
// Regression (issue 0179): passing a `?i64` where `i32` is expected used to
|
|
// compile and unconditionally extract the payload, so a NULL optional yielded
|
|
// `0` with no diagnostic (silent miscompile across the whole `?T → concrete`
|
|
// family, incl. `?bool → bool`). The legal extractions are `!` / `??` /
|
|
// binding / `case` / a `!= null` guard (see 0919); everything else is rejected.
|
|
#import "modules/std.sx";
|
|
|
|
takes_i32 :: (x: i32) { print("got {}\n", x); }
|
|
|
|
main :: () {
|
|
n : ?i64 = null;
|
|
takes_i32(n); // error: optional does not implicitly unwrap
|
|
}
|