wcc: reject break/continue outside loop in wwstage, align to cstage (#7)
The selfhost checker's resolvewalk had no loop-nesting guard and no N_BREAK/N_CONTINUE arm, so `break`/`continue` outside any loop fell through the generic child recursion and was silently accepted -- while cstage (cmd/wcc/check.c) correctly rejects them. A cs!=ww checker divergence (rule 10); cstage is correct (break/continue outside a loop is an error in Hare/C/Go), so align wwstage DOWN, not cstage up. Mirror cstage's mechanism exactly (check.c:598/2494/2529/2611): a `loops` counter incremented around for and for-range bodies -- the for-`else` and the init/cond/post walked OUTSIDE the count, since a break there targets an enclosing loop -- rejecting break/continue when loops==0 with a byte-identical `file:line:col: error: <kw> outside loop` diagnostic. match/switch are not loop targets, matching cstage. The divergence survived because 300_check.c only exercised the in-process C checker, never w6c_ww; the fix adds 4 rows to the both-stage 989_catA_f2_reject carrier (break/continue outside loop, the for-else els-outside-count edge, and an in-loop control). make clean && make test: all 402 passed, byte-id self-compile gates 990-996 green.
This commit is contained in:
@@ -249,6 +249,50 @@ static const struct row rows[] = {
|
||||
" return r;\n"
|
||||
"};\n",
|
||||
1, 9 },
|
||||
|
||||
/* break/continue outside any enclosing for/for-range → REJECT. Pre-fix
|
||||
* wwstage had no loop-nesting guard (check.ww resolvewalk) and silently
|
||||
* accepted, emitting a JMP to an undefined loop label; cstage rejects at
|
||||
* cmd/wcc/check.c:2611-2615 (c->loops == 0). Both diagnostics match. */
|
||||
{ "break_outside_loop",
|
||||
"package main;\n"
|
||||
"export fn main() void = {\n"
|
||||
" break;\n"
|
||||
"};\n",
|
||||
0, 0, "break outside loop" },
|
||||
|
||||
{ "continue_outside_loop",
|
||||
"package main;\n"
|
||||
"export fn main() void = {\n"
|
||||
" continue;\n"
|
||||
"};\n",
|
||||
0, 0, "continue outside loop" },
|
||||
|
||||
/* break in a for's `else` block with NO enclosing loop → REJECT. The
|
||||
* else runs at normal cond-false exit (skipped by break) and targets an
|
||||
* ENCLOSING loop, so it is OUTSIDE this loop's break count — cstage
|
||||
* cmd/wcc/check.c:2546-2549 keeps els out of the count; wwstage mirrors. */
|
||||
{ "break_in_else_outside_loop",
|
||||
"package main;\n"
|
||||
"export fn main() void = {\n"
|
||||
" let i: i32 = 0;\n"
|
||||
" for (i < 1) { i = i + 1; } else { break; };\n"
|
||||
"};\n",
|
||||
0, 0, "break outside loop" },
|
||||
|
||||
/* control — break/continue INSIDE a loop are valid → run 5. */
|
||||
{ "break_continue_inloop_ok",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let i: i32 = 0;\n"
|
||||
" for (i < 10) {\n"
|
||||
" i = i + 1;\n"
|
||||
" if (i == 3) { continue; };\n"
|
||||
" if (i == 5) { break; };\n"
|
||||
" };\n"
|
||||
" return i;\n"
|
||||
"};\n",
|
||||
1, 5 },
|
||||
};
|
||||
|
||||
static int
|
||||
|
||||
Reference in New Issue
Block a user