Follow-up to #135 — the fix in v0.5.161 addressed break/continue in most cases (verified: parseNumberAt's inner parser loop now advances correctly), but a specific continue pattern inside a for loop still miscomputes its branch target on web.
Perry version: 0.5.161 (built from source, bf0a171).
Symptom
A for loop with multiple if (condition) continue; statements hangs when the later continues are preceded by an if / else if chain (not nested, sequential). The for-loop variable stops advancing.
Minimal repro
const EA = [0.0, 0.0, 1.0, 0.0, 0.0]; // only index 2 is "active"
const ET = [0.0, 0.0, 1.0, 2.0, 3.0];
function run(): void {
for (let i = 0; i < 5; i = i + 1) {
if (EA[i] < 0.5) continue; // A: top-of-body skip
const type = ET[i];
if (type < 1.5) { // B: multi-branch if/else
console.log("walker " + i.toString());
} else if (type < 2.5) {
console.log("flyer " + i.toString());
} else {
console.log("chaser " + i.toString());
}
if (type > 99.0) continue; // C: second continue, never actually taken (works or hangs?)
// … more code …
}
console.log("done");
}
run();
On native: prints walker 2 then done. On web 0.5.161: prints walker 2 then hangs — i stays at 2, the for's increment never runs.
Instrumented trace showed i advancing normally (0, 1, 2, 2, 2, 2, 2, …) — the first continue (A) works, but once the for-body enters the else/else-if chain path (B) at i=2, the subsequent continue (C, even when not taken) or the fall-through to the next iteration seems to branch back to the loop body's top instead of the for's update expression.
Diagnostic
In the real game code (Bloom Jump's updateEnemies), I instrumented the loop and observed:
[updateEnemies] entered
[updateEnemies] post-type-branch i=0 ← fired ONCE (a guarded log)
[updateEnemies] ABORT iters=41 i=2 trace=[0,1,2,2,2,2,2,2,2,...]
So i reached 2 once, went through the full body once, but then re-entered at i=2 repeatedly rather than advancing.
Note that the first continue in the body (at the if (EA[i] < 0.5) guard) works fine — we saw 0 → 1 → 2 in the trace. The failure happens when execution reaches the point AFTER the multi-branch if/else if/else and encounters another continue (or falls off the end of the iteration).
Workaround
Removing all continue statements inside the for body — wrapping the body in if (active) { … } instead — unblocks the game. Nine functions in Bloom Jump needed this rewrite.
Possibly related
The working fix from #135 used block_depth.saturating_sub(loop_depth.last()). Maybe the multi-branch if/else if/else at the current depth pushes blocks in a way that throws off the block_depth count at the point the later continue or loop-end emits its branch instruction. The first continue happens before any of those pushes, which would explain why it works.
Happy to extract a smaller standalone repro if useful — the above snippet should be close but I haven't confirmed against the compiler in isolation.
Follow-up to #135 — the fix in v0.5.161 addressed
break/continuein most cases (verified:parseNumberAt's inner parser loop now advances correctly), but a specificcontinuepattern inside aforloop still miscomputes its branch target on web.Perry version: 0.5.161 (built from source,
bf0a171).Symptom
A
forloop with multipleif (condition) continue;statements hangs when the latercontinues are preceded by anif / else ifchain (not nested, sequential). The for-loop variable stops advancing.Minimal repro
On native: prints
walker 2thendone. On web 0.5.161: printswalker 2then hangs —istays at 2, the for's increment never runs.Instrumented trace showed i advancing normally (0, 1, 2, 2, 2, 2, 2, …) — the first
continue(A) works, but once the for-body enters the else/else-if chain path (B) at i=2, the subsequentcontinue(C, even when not taken) or the fall-through to the next iteration seems to branch back to the loop body's top instead of the for's update expression.Diagnostic
In the real game code (Bloom Jump's
updateEnemies), I instrumented the loop and observed:So
ireached 2 once, went through the full body once, but then re-entered at i=2 repeatedly rather than advancing.Note that the first
continuein the body (at theif (EA[i] < 0.5)guard) works fine — we saw 0 → 1 → 2 in the trace. The failure happens when execution reaches the point AFTER the multi-branchif/else if/elseand encounters anothercontinue(or falls off the end of the iteration).Workaround
Removing all
continuestatements inside the for body — wrapping the body inif (active) { … }instead — unblocks the game. Nine functions in Bloom Jump needed this rewrite.Possibly related
The working fix from #135 used
block_depth.saturating_sub(loop_depth.last()). Maybe the multi-branchif/else if/elseat the current depth pushes blocks in a way that throws off the block_depth count at the point the latercontinueor loop-end emits its branch instruction. The firstcontinuehappens before any of those pushes, which would explain why it works.Happy to extract a smaller standalone repro if useful — the above snippet should be close but I haven't confirmed against the compiler in isolation.