Skip to content

transform-block-scoping: closures share a binding when a nested for-loop head has an initializer#18200

Description

@ko-success

馃捇

  • Would you like to work on a fix?

How are you using Babel?

babel-loader (webpack)

Input code

REPL reproduction

var arr = {};

for (var i = 0; i < 2; i++)
  for (let x = (arr[i] = () => x, i); false;);

console.log(arr[0](), arr[1]()); // 0 1

Configuration file name

No response

Configuration

{
  "plugins": ["@babel/plugin-transform-block-scoping"]
}

Current and expected behavior

var arr = {};

for (var i = 0; i < 2; i++)
  for (var x = (arr[i] = () => x, i); false;);

console.log(arr[0](), arr[1]()); // 1 1

Environment

  • Babel version: 8.0.4
  • @babel/plugin-transform-block-scoping: 8.0.1
  • Node: 24.14.0
  • npm: 11.11.0
  • OS: macOS
  • Monorepo: no

Possible solution

No response

Additional context

This is related to #18088, but it exposes a separate part of lexical loop semantics that cannot be preserved by reinitializing the lowered var.

#18088 correctly handles nested loop-head declarations without an initializer. For example:

for (var i = 0; i < 2; i++) {
  for (let done; !done; done = true) {
    // ...
  }
}

When let done; is lowered to var done;, re-entering the inner loop does not perform an assignment, so done retains the value from the previous execution.
Transforming it to var done = void 0; correctly restores the required value initialization on every execution of the inner loop.

However, lexical declarations provide more than value initialization: every evaluation of the declaration also creates a new binding in a new lexical environment.
That distinction becomes observable when the binding is captured by a closure.

Activity

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions