w6c+selfhost: cgen && and || short-circuit
Both stages were eagerly evaluating RHS regardless of LHS (eager ANDQ/ORQ on the two results). Now: eval LHS into AX, CMPQ $0 + JE/JNE to a per-call-site label, eval RHS into AX, fall through. AX holds the LHS sentinel on the skipped path — typechecker already enforces bool operands. Surfaced by lib/getopt's nil-argv guard segfault. Six new rows in test/wcc/700_e2e.c, three of which segfault pre-fix. lib/getopt test comment relaxed; nested-if kept as regression marker.
This commit is contained in:
@@ -1627,6 +1627,59 @@ static const struct row rows[] = {
|
||||
"fn main() i32 = {\n"
|
||||
" return fmt.println(\"hello\", 7i64): i32;\n"
|
||||
"};", 8 },
|
||||
/* short-circuit `&&`: RHS skipped when LHS is false. Without
|
||||
* short-circuit the `p.x` deref on a nil pointer segfaults.
|
||||
* Pinned the cgen bug surfaced by lib/getopt's argv guards
|
||||
* (`argslen > 0 && !streq(argsptr[0], "--")`) on a nil argsptr. */
|
||||
{ "type point = struct { x: i32, y: i32 };\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let p: *point = nil;\n"
|
||||
" if (p != nil && p.x > 0) { return 1; };\n"
|
||||
" return 42;\n"
|
||||
"};", 42 },
|
||||
/* short-circuit `||`: RHS skipped when LHS is true. */
|
||||
{ "type point = struct { x: i32, y: i32 };\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let p: *point = nil;\n"
|
||||
" if (p == nil || p.x > 0) { return 42; };\n"
|
||||
" return 1;\n"
|
||||
"};", 42 },
|
||||
/* `&&` LHS true: RHS evaluated, expression yields its boolean. */
|
||||
{ "type point = struct { x: i32, y: i32 };\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let pt: point = point { x = 5, y = 10 };\n"
|
||||
" let p: *point = &pt;\n"
|
||||
" if (p != nil && p.x > 0) { return 42; };\n"
|
||||
" return 1;\n"
|
||||
"};", 42 },
|
||||
/* `||` LHS false: RHS evaluated, expression yields its boolean. */
|
||||
{ "type point = struct { x: i32, y: i32 };\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let pt: point = point { x = 7, y = 0 };\n"
|
||||
" let p: *point = &pt;\n"
|
||||
" if (p == nil || p.x > 0) { return 42; };\n"
|
||||
" return 1;\n"
|
||||
"};", 42 },
|
||||
/* mixed `&&` / `||` precedence — `&&` binds tighter than `||`,
|
||||
* so `(p != nil && p.x > 0) || p == nil`. With short-circuit at
|
||||
* each level: AND skips `p.x` (LHS false), OR keeps the true. */
|
||||
{ "type point = struct { x: i32, y: i32 };\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let p: *point = nil;\n"
|
||||
" if (p != nil && p.x > 0 || p == nil) { return 42; };\n"
|
||||
" return 1;\n"
|
||||
"};", 42 },
|
||||
/* short-circuit must still yield a clean boolean in the
|
||||
* expression context (not just inside `if`). `true && false`
|
||||
* stored into a bool and re-checked. */
|
||||
{ "fn main() i32 = {\n"
|
||||
" let a: bool = (1 > 0) && (2 < 1);\n"
|
||||
" let b: bool = (1 < 0) || (2 > 1);\n"
|
||||
" let n: i32 = 0;\n"
|
||||
" if (!a) { n += 10; };\n"
|
||||
" if (b) { n += 32; };\n"
|
||||
" return n;\n"
|
||||
"};", 42 },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user