Skip to content

target web #135 follow-up: continue after if-else chain in for loop still pins iterator #137

@proggeramlug

Description

@proggeramlug

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 hangsi 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions