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:
@@ -48,6 +48,13 @@ test "conversions: classify covers the built-in coercion ladder" {
|
||||
try std.testing.expectEqual(Plan.optional_unwrap, cr.classify(opt_i64, .i64));
|
||||
try std.testing.expectEqual(Plan.void_to_optional, cr.classify(.void, opt_i64));
|
||||
|
||||
// `?T → bool` is NOT an unwrap-then-narrow presence test (issue 0169):
|
||||
// it must reject, never silently produce `false`. But `?bool → bool`
|
||||
// is a genuine unwrap of a bool payload.
|
||||
try std.testing.expectEqual(Plan.optional_to_bool_reject, cr.classify(opt_i64, .bool));
|
||||
const opt_bool = tt.optionalOf(.bool);
|
||||
try std.testing.expectEqual(Plan.optional_unwrap, cr.classify(opt_bool, .bool));
|
||||
|
||||
// Tuple → tuple, same arity.
|
||||
const t_ss = tt.intern(.{ .tuple = .{ .fields = &[_]TypeId{ .i64, .i64 }, .names = null } });
|
||||
const t_ii = tt.intern(.{ .tuple = .{ .fields = &[_]TypeId{ .i32, .i32 }, .names = null } });
|
||||
|
||||
Reference in New Issue
Block a user