wcc: continue runs post-step in 3-clause for and range form (#138)
`for (init; cond; post) { ... continue; ... }` and `for (let i .. xs)
{ ... continue; ... }` now emit a `post` (3-clause) or `rpost` (range)
label between the body and the JMP back to the cond-test. `continue`
jumps to that label, runs the post-step, then re-tests the loop
condition — mirrors C/Go/Hare semantics. Pre-fix both stages emitted
`JMP loop_top` for continue, SKIPPING the post-step → the value that
triggered continue never advanced → silent infinite loop on the first
matching iteration. Found by impl-strconv-fold2 during the fold-3
decimal.ha port: `leftshift_newdigits`'s `for (... i+=1) { ... else
if (d.digits[i]==p5[i]) continue; ... }` would infinite-loop at the
first equal digit.
BOTH stages were identically buggy → 990-997 cs==ww byte-id held →
gate-blind. Bootstrap audit (`grep -rE 'for \(let .*\.\.' lib/
selfhost/`) confirmed zero existing callers with continue in either
the 3-clause or range form; bootstrap-NEUTRAL.
Sites: cmd/w6c/cgen.c N_FOR + N_FORRANGE; selfhost/cmd/wcc/
cgenstmt.ww cgfor + cgforrange. 1-clause `for (cond)` byte-id
preserved (cont_target stays = loop_top when n.rhs == nil). Rule-11
carve-out: 3-clause and range share the lowered structure; fixing
one without the other would leave the same silent miscompile in
N_FORRANGE — one-class closure on the continue-skips-post bug, same
precedent as #133-expanded.
911_continue_run: 4 rows. for3_skip_one (lead's repro, was infinite
loop, now 4), for3_skip_two (nested continues, 30), range_skip
(Hare-range continue, was infinite loop, now 120), for1_continue_
byteid (1-clause regression assertion — bootstrap shape unchanged).
Pre-existing parser-side divergences (cstage silently drops post in
the never-used 2-clause `for (cond; post)`; wwstage doesn't support
infinite `for {}`) deferred to #139 — not in decimal.ha, no shared
class with the cgen continue-skips-post.
This commit is contained in:
@@ -22365,6 +22365,14 @@ fn cgfor(c: *cgen, n: *node) void = {
|
||||
// label so the else body sits between it and the break target.
|
||||
let naturall: str = endl;
|
||||
if (n.els != nil) { naturall = mklabel(c, "elseloop"); };
|
||||
// #138: `continue` in a 3-clause `for (init; cond; post)` must
|
||||
// run the post-step before re-testing cond. Pre-fix the continue-
|
||||
// target was `topl`, which SKIPPED the post-step → state never
|
||||
// advanced → infinite loop. Allocate a dedicated `post` label
|
||||
// only when there IS a post-step (`n.rhs != nil`); else keep
|
||||
// continue → loop-top, byte-id with 1-clause for.
|
||||
let conttgt: str = topl;
|
||||
if (n.rhs != nil) { conttgt = mklabel(c, "post"); };
|
||||
|
||||
if (n.lhs != nil) { cgstmt(c, n.lhs); };
|
||||
|
||||
@@ -22376,14 +22384,17 @@ fn cgfor(c: *cgen, n: *node) void = {
|
||||
};
|
||||
|
||||
c.loopendbuf[c.looptop] = endl;
|
||||
c.loopcontbuf[c.looptop] = topl;
|
||||
c.loopcontbuf[c.looptop] = conttgt;
|
||||
c.looptop += 1;
|
||||
|
||||
if (n.body != nil) { cgstmt(c, n.body); };
|
||||
|
||||
c.looptop -= 1;
|
||||
|
||||
if (n.rhs != nil) { cgexpr(c, n.rhs); };
|
||||
if (n.rhs != nil) {
|
||||
emitlabel(conttgt);
|
||||
cgexpr(c, n.rhs);
|
||||
};
|
||||
emitline("\tJMP\t"); emitline(topl); emitline("\n");
|
||||
if (n.els != nil) {
|
||||
emitlabel(naturall);
|
||||
@@ -22738,8 +22749,13 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
||||
let endl: str = mklabel(c, "rend");
|
||||
let naturall: str = endl;
|
||||
if (n.els != nil) { naturall = mklabel(c, "relseloop"); };
|
||||
// #138 (range form): `continue` must run the implicit `i+=1`
|
||||
// post-step before re-testing the bound. Pre-fix cont = loopl
|
||||
// (top), skipping the ADDQ $1, ioff below — infinite loop on
|
||||
// the value that triggered continue. Dedicated `rpost` label.
|
||||
let rpost: str = mklabel(c, "rpost");
|
||||
|
||||
c.loopcontbuf[c.looptop] = loopl;
|
||||
c.loopcontbuf[c.looptop] = rpost;
|
||||
c.loopendbuf[c.looptop] = endl;
|
||||
c.looptop += 1;
|
||||
|
||||
@@ -22796,6 +22812,7 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
||||
|
||||
c.looptop -= 1;
|
||||
|
||||
emitlabel(rpost);
|
||||
emitline("\tADDQ\t$1, ");
|
||||
emitoff(ioff: i64);
|
||||
emitline("(BP)\n");
|
||||
|
||||
Reference in New Issue
Block a user