microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.95k stars 12.47k forks source link

async code generator does not correctly handle labelled blocks #50332

Open Maxdamantus opened 2 years ago

Maxdamantus commented 2 years ago

Bug Report

When using the "ES5" target, async/await code generation does not correctly handle breaking from labelled blocks.

πŸ”Ž Search Terms

break block label async

πŸ•— Version & Regression Information

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

Compiler options: --lib ES2015,dom --target ES5

async function foo() {
    tmp: {
        for (let x = 0; x < 4; x++)
            if (await Promise.resolve(true))
                break tmp;
        console.log("Impossible");
        throw 0;
    }
    console.log("Success");
}

foo();

πŸ™ Actual behavior

"Impossible" is logged, because the break statement seems to short-circuit the inner loop

πŸ™‚ Expected behavior

"Success" is logged, because the break statement should short-circuit the outer block

Interestingly, it does seem to handle breaking from outer loops (eg adding a superfluous loop, tmp: for (let nvm = 0; nvm < 1; nvm++) ...), but not plain blocks.

Maxdamantus commented 2 years ago

I should probably have actually simplified it a bit further, since it's not dependent on any await-based condition:

async function foo() {
    tmp: {
        for (let x = 0; x < 4; x++) {
            break tmp;
            await Promise.resolve();
        }
        console.log("Impossible");
        throw 0;
    }
    console.log("Success");
}
foo();

The code above should be statically known to log Success, since the await never actually happens. The generated code instead jumps to the Impossible branch.