fix: gate implicit optional unwrap on flow narrowing (issue 0179)
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.
This commit is contained in:
11
readme.md
11
readme.md
@@ -295,8 +295,19 @@ if v := x { // safe unwrap
|
||||
// Optional chaining
|
||||
node: ?Node = get_node();
|
||||
name := node?.name ?? "unknown";
|
||||
|
||||
// Flow-sensitive narrowing: a `!= null` guard proves the value present, so it
|
||||
// reads as its concrete payload in the guarded region.
|
||||
n: ?i32 = maybe();
|
||||
if n != null { take_i32(n); } // `n` is i32 here
|
||||
if n == null { return; }
|
||||
take_i32(n); // narrowed for the rest of the scope
|
||||
```
|
||||
|
||||
A `?T` never *implicitly* unwraps to `T` in a value position — a bare
|
||||
`take_i32(n)` without a guard, `!`, `??`, or binding is a compile error (it
|
||||
would otherwise silently yield a null optional's zero payload).
|
||||
|
||||
### Generics
|
||||
|
||||
```sx
|
||||
|
||||
Reference in New Issue
Block a user