fix: reject implicit ?T -> bool coercion instead of silent false (issue 0169)

The Optional->Concrete unwrap classify rule treated ?i64 -> bool as
unwrap+narrow (both builtin), silently yielding false for every optional
(present or null). specs.md defines no implicit optional->bool
conversion. Reject it: conversions.zig adds an optional_to_bool_reject
plan (dst == bool, child != bool); coerce.zig emits a located diagnostic
suggesting '!= null'. Covers arg/field-init/return via the shared
coerceMode. The if-opt presence test (issue 0164) is a separate path,
untouched.

Regression: examples/diagnostics/1199-diagnostics-optional-to-bool.sx +
conversions.test.zig unit test. Verified by 3 adversarial reviews, suite
789/0. Filed adjacent issue 0179 (whole implicit ?T->concrete unwrap
family silently miscompiles a null optional; design-touching).
This commit is contained in:
agra
2026-06-23 02:47:51 +03:00
parent 3c738695dc
commit e5b682e622
9 changed files with 143 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
// Passing an optional (`?T`) where a `bool` is expected is a hard type error,
// not a silent miscompile. Regression (issue 0169): the coercion classifier's
// "optional → concrete (unwrap+narrow)" rule used to accept `?i64 → bool`
// (both child and dst are builtin), unwrapping the payload and narrowing it to
// `i1` — which silently produced `false` for EVERY optional, present or null
// alike. There is no implicit optional→bool coercion in the language (only
// `T → ?T` wrapping and flow-sensitive narrowing); a bool position wants an
// explicit presence test. The classifier now rejects `?T → bool` (payload not
// itself a bool) at the coercion site (exit 1) with a `!= null` fix-it. The
// `if opt {}` presence test (issue 0164) is a separate path and still works.
#import "modules/std.sx";
takes_bool :: (b: bool) { if b { print("true\n"); } else { print("false\n"); } }
main :: () {
a : ?i64 = 42;
takes_bool(a); // <- '?i64' cannot be used where 'bool' is expected
}

View File

@@ -0,0 +1,5 @@
error: cannot use a value of type '?i64' where 'bool' is expected; test presence explicitly with '!= null'
--> examples/diagnostics/1199-diagnostics-optional-to-bool.sx:18:3
|
18 | takes_bool(a); // <- '?i64' cannot be used where 'bool' is expected
| ^^^^^^^^^^^^^