Function declaration in a switch case is lowered to a TDZ-bound let initializer
- Dominant language
- Go
- Stars
- 40.1k
- Forks
- 1.3k
- PR merge metrics
- No merged PRs in 30d
Description
esbuild changes the initialization timing of a block-level function declaration inside a `switch` statement.
Version:
```text
0.28.2
```
Input:
```js
switch (0) {
case 0: console.log(g()); // fn
case 1: function g() { return "fn"; }
}
```
This reproduces without minification, and esbuild outputs:
```js
switch (0) {
case 0:
console.log(g2()); // ReferenceError: Cannot access 'g2' before initialization
case 1:
let g2 = function() {
return "fn";
};
var g = g2;
}
```
Playground reproduction:
https://esbuild.github.io/try/#YgAwLjI4LjIAe21pbmlmeTogZmFsc2V9AGUAZW50cnkuanMAc3dpdGNoICgwKSB7CiAgY2FzZSAwOiBjb25zb2xlLmxvZyhnKCkpOwogIGNhc2UgMTogZnVuY3Rpb24gZygpIHsgcmV0dXJuICJmbiI7IH0KfQ
Enabling `minify` still produces a incorrect transformation:
```js
{console.log(c());let c=function(){return"fn"};var g=c} // ReferenceError: Cannot access 'c' before initialization
```
Playground reproduction:
https://esbuild.github.io/try/#YgAwLjI4LjIAe21pbmlmeTogdHJ1ZX0AZQBlbnRyeS5qcwBzd2l0Y2ggKDApIHsKICBjYXNlIDA6IGNvbnNvbGUubG9nKGcoKSk7CiAgY2FzZSAxOiBmdW5jdGlvbiBnKCkgeyByZXR1cm4gImZuIjsgfQp9
Expected output:
```js
{console.log(c()); function c(){return"fn"} }
```
A function declaration and a `let` declaration with a function-expression initializer do not have the same initialization timing.
For the original function declaration, the binding is initialized with its function object by `BlockDeclarationInstantiation` when entering the entire `CaseBlock`, before any case is selected.
For the transformed declaration, the binding is initialized only when execution reaches that statement. Since `case 0` is selected, execution never reaches the initializer in `case 1`, and the binding remains in the temporal dead zone.
The transform should preserve the function declaration or otherwise initialize the function binding before case selection.
Specification references:
- https://tc39.es/ecma262/multipage/ecmascript-language-statements-and-declarations.html#sec-switch-statement-runtime-semantics-evaluation
- https://tc39.es/ecma262/multipage/ecmascript-language-statements-and-declarations.html#sec-blockdeclarationinstantiation
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with the two Playground reproductions and compare the non-minified and minified switch outputs with the referenced Switch Statement and BlockDeclarationInstantiation specification sections. Done means the function binding is initialized before case selection, so both reproductions call the function without a temporal-dead-zone error while preserving the expected output.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, javascript
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100