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.1 KiB
Plaintext
34 lines
1.1 KiB
Plaintext
// Bindingless `while <optional>` must test the optional's has_value flag,
|
|
// exactly like `if <optional>` — not fold the `{T,i1}` aggregate truthy.
|
|
//
|
|
// Regression (issue 0164): a bare optional loop condition emitted `br i1 true`,
|
|
// so the loop never observed `null` and either spun forever (present→drained)
|
|
// or ran when it should have been skipped (already-null).
|
|
#import "modules/std.sx";
|
|
|
|
main :: () {
|
|
// Present `?i64` drained to null: the body runs each step until the
|
|
// optional becomes null, then the loop stops.
|
|
countdown : ?i64 = 3;
|
|
runs := 0;
|
|
while countdown {
|
|
runs = runs + 1;
|
|
v := countdown!; // unwrap the present value
|
|
if v <= 1 {
|
|
countdown = null; // drain → loop must STOP next header eval
|
|
} else {
|
|
countdown = v - 1;
|
|
}
|
|
}
|
|
print("drain runs={}\n", runs); // 3
|
|
|
|
// Already-null `?i64`: the body must NOT run at all.
|
|
empty : ?i64 = null;
|
|
skipped := 0;
|
|
while empty {
|
|
skipped = skipped + 1;
|
|
empty = null;
|
|
}
|
|
print("skip runs={}\n", skipped); // 0
|
|
}
|