fix: bindingless if/while/and/or over optional reads has_value (issue 0164)
lowerIfExpr emitted optional_has_value only for the binding form; a bare
'if opt' passed the raw {T,i1} aggregate to condBr, where emitCondBr's
catch-all struct arm silently folded it to 'i1 true' (structs always
truthy) — a silent miscompile that took the present-branch for null
optionals. while / and / or shared the same defect.
Reduce bindingless optional conditions to optional_has_value in
lowerIfExpr/lowerWhile and via a new lowerBoolCondition helper for and/or
operands. Replace the silent-true emitCondBr arm with a lowering-time
diagnostic (checkConditionType/isValidConditionType) rejecting conditions
whose type isn't bool/integer/pointer/optional; the backend @panic is now
an unreachable tripwire.
Regressions: examples/optionals/0908..0910 + diagnostics/1194 (negative).
Verified by 3+3 adversarial reviews.
Filed adjacent bugs found during review: 0168 (array-of-optionals element
load), 0169 (optional->bool coercion), 0170 (closure-optional layout).
This commit is contained in:
@@ -2375,11 +2375,19 @@ pub const Ops = struct {
|
||||
cond = c.LLVMBuildICmp(self.e.builder, c.LLVMIntNE, cond, c.LLVMConstNull(cond_ty), "tobool");
|
||||
} else if (cond_kind == c.LLVMIntegerTypeKind) {
|
||||
cond = c.LLVMBuildICmp(self.e.builder, c.LLVMIntNE, cond, c.LLVMConstInt(cond_ty, 0, 0), "tobool");
|
||||
} else if (cond_kind == c.LLVMStructTypeKind) {
|
||||
// Struct values are always truthy
|
||||
cond = c.LLVMConstInt(self.e.cached_i1, 1, 0);
|
||||
} else {
|
||||
cond = c.LLVMConstInt(self.e.cached_i1, 1, 0); // default truthy
|
||||
// UNREACHABLE backend tripwire. A condBr condition must be i1,
|
||||
// an integer, or a pointer. Anything else (a struct — e.g. an
|
||||
// optional `{T,i1}` aggregate — or a float) is now rejected at
|
||||
// lowering with a located type error: `checkConditionType` in
|
||||
// src/ir/lower/expr.zig gates every condition site (`if` /
|
||||
// `while` / `and` / `or`), and optionals are reduced to their
|
||||
// has_value i1 before reaching here (issue 0164). Folding such a
|
||||
// condition truthy was a silent miscompile (`if opt { }` always
|
||||
// took the present branch); reaching this @panic now means a NEW
|
||||
// condition site bypassed `checkConditionType` — add the check
|
||||
// there, don't fold truthy.
|
||||
@panic("emitCondBr: non-boolean condition reached condBr — should have been rejected at lowering as a type error (issue 0164; see checkConditionType in src/ir/lower/expr.zig)");
|
||||
}
|
||||
}
|
||||
_ = c.LLVMBuildCondBr(self.e.builder, cond, then_bb, else_bb);
|
||||
|
||||
Reference in New Issue
Block a user