Skip to content

for (var k …) inside a function hangs forever when a closure in the body captures k #11052

Description

@proggeramlug

A for (var k = ...; k < n; k++) loop inside a function never terminates when any closure in the loop body captures k. The closure doesn't have to be called.

Reproduced on main f5cfbff88 (v0.5.1638), Linux x86_64, PERRY_NO_CACHE=1:

function f(n: any) { for (var k = 0; k < n; k++) { const r = () => k; } console.log(k); }
f(5);
variant perry node
closure created, never called (above) hangs (killed by timeout 5, rc=124) 5
read = () => k assigned each iteration, called after the loop hangs 5 5
same with n: number hangs 5 5
h += read() inside the loop hangs 10 5 5
var k declared at module level, same loop in a function 5 5 ✅ 5 5

So the trigger is a function-scoped var induction variable that a closure captures. The loop uses the generic boxed counter in its IR (found while working on loop-counter representation). Presumably the increment and the k < n test are not reading and writing the same storage: one goes to the closure box and the other to a local copy. That is inferred, not verified.

Found while validating an unrelated loop-counter change, which hangs identically on both arms: the bug predates that change and the change does not admit this case.

https://claude.ai/code/session_01EQdCw7BN4AAnn2hAbNXg33

Activity

  1. proggeramlug commented on Sep 22, 2026

    @proggeramlug
    ContributorAuthor

    A/B: this predates #11053, and it's a spin, not a park.

    Same fixtures built with both compilers (PERRY_NO_CACHE=1, Linux x86_64), each run for 3 s and then sampled:

    arm compiler closure never called (v3) read = () => k (v1) h += read() (v)
    A = main f5cfbff88 sha256 1f1ef6c5fee7 hang hang hang
    B = #11053 sha256 bb654007a784 hang hang hang

    Every hung process was in state R at 100% CPU: 1 thread, no children, and 100 CPU ticks in the sampled second. So it's a busy loop, not a wait: the loop condition never becomes false. The first suspect is the counter's storage (the k++ and the k < n test on different copies once k is boxed for the closure), not the event loop or a lock.

    (B's binary was built from the pre-amend commit 4d1f97c3c; git diff 4d1f97c3c e8c3b8947 touches only three campaign tooling files, no code.)

    https://claude.ai/code/session_01EQdCw7BN4AAnn2hAbNXg33

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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