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).
34 lines
1.4 KiB
Plaintext
34 lines
1.4 KiB
Plaintext
// `and` / `or` over bare optional operands reduce each operand to its
|
|
// has_value flag (presence-as-truth), exactly like `if <optional>`. A bare
|
|
// optional reaching the short-circuit merge as a `{T,i1}` aggregate used to
|
|
// fold truthy (issue 0164); it must instead test presence.
|
|
//
|
|
// Truth table below: present `?i64` is "true", absent (null) is "false".
|
|
#import "modules/std.sx";
|
|
|
|
yn :: (b: bool) -> string => if b then "T" else "F";
|
|
|
|
main :: () {
|
|
p : ?i64 = 7; // present → truthy
|
|
q : ?i64 = null; // absent → falsy
|
|
|
|
// and: true only when BOTH present.
|
|
print("PP and = {}\n", yn(p and p)); // T
|
|
print("PQ and = {}\n", yn(p and q)); // F (rhs absent)
|
|
print("QP and = {}\n", yn(q and p)); // F (lhs absent, short-circuits)
|
|
print("QQ and = {}\n", yn(q and q)); // F
|
|
|
|
// or: false only when BOTH absent.
|
|
print("PP or = {}\n", yn(p or p)); // T
|
|
print("PQ or = {}\n", yn(p or q)); // T (lhs present, short-circuits)
|
|
print("QP or = {}\n", yn(q or p)); // T (rhs present)
|
|
print("QQ or = {}\n", yn(q or q)); // F
|
|
|
|
// Mixed optional-and-bool: optional operand reduced to has_value,
|
|
// bool operand used as-is.
|
|
flag := true;
|
|
print("Pb and = {}\n", yn(p and flag)); // T
|
|
print("Qb and = {}\n", yn(q and flag)); // F (lhs absent)
|
|
print("bQ or = {}\n", yn(flag or q)); // T (lhs true, short-circuits)
|
|
}
|