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:
@@ -34,6 +34,7 @@ pub const CoercionResolver = struct {
|
||||
closure_to_fn_reject, // closure value → bare fn-ptr (diagnostic, returns operand)
|
||||
tuple_elementwise, // (A,B) → (C,D), same arity
|
||||
optional_unwrap, // ?T → concrete (narrowing)
|
||||
optional_to_bool_reject, // ?T → bool (no presence-test coercion; diagnostic)
|
||||
void_to_optional, // void (null literal) → ?T
|
||||
optional_wrap, // concrete → ?T
|
||||
erase_protocol, // concrete → protocol value
|
||||
@@ -99,6 +100,17 @@ pub const CoercionResolver = struct {
|
||||
const src_info = self.l.module.types.get(src_ty);
|
||||
if (src_info == .optional) {
|
||||
const child_ty = src_info.optional.child;
|
||||
// `?T → bool` is NOT a presence test. The unwrap-then-narrow
|
||||
// ladder below would extract the payload and narrow it to `i1`,
|
||||
// which silently yields `false` for every optional (issue 0169).
|
||||
// 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. Reject loudly unless
|
||||
// the payload is itself a bool (`?bool → bool` is a genuine
|
||||
// unwrap of a bool payload, handled by the same arm below).
|
||||
if (dst_ty == .bool and child_ty != .bool) {
|
||||
return .optional_to_bool_reject;
|
||||
}
|
||||
if (child_ty == dst_ty or (dst_ty.isBuiltin() and child_ty.isBuiltin())) {
|
||||
return .optional_unwrap;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user