馃捇
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.
馃捇
How are you using Babel?
babel-loader (webpack)
Input code
REPL reproduction
Configuration file name
No response
Configuration
{ "plugins": ["@babel/plugin-transform-block-scoping"] }Current and expected behavior
Environment
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:
When
let done;is lowered tovar 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.