Skip to content

Commit d55453c

Browse files
Fix crash in CFA when for-in/for-of expression throws (#63912)
1 parent 6556d51 commit d55453c

15 files changed

Lines changed: 245 additions & 1 deletion

tsc/internal/binder/binder.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1907,9 +1907,19 @@ func (b *Binder) bindForStatement(node *ast.Node) {
19071907

19081908
func (b *Binder) bindForInOrForOfStatement(node *ast.Node) {
19091909
stmt := node.AsForInOrOfStatement()
1910+
b.bind(stmt.Expression)
1911+
if b.currentFlow == b.unreachableFlow {
1912+
// Like the for-loop initializer, the for-in/for-of expression is bound before the loop's
1913+
// flow graph is constructed. If it makes flow unreachable (e.g. a throwing IIFE), addAntecedent
1914+
// will filter out the unreachable entry to preLoopLabel, leaving only the back-edge from the
1915+
// loop body. This creates a cycle with no exit that crashes isReachableFlowNodeWorker.
1916+
// Bail out early and just bind the remaining children with unreachable flow.
1917+
b.bind(stmt.Initializer)
1918+
b.bind(stmt.Statement)
1919+
return
1920+
}
19101921
preLoopLabel := b.setContinueTarget(node, b.createLoopLabel())
19111922
postLoopLabel := b.createBranchLabel()
1912-
b.bind(stmt.Expression)
19131923
b.addAntecedent(preLoopLabel, b.currentFlow)
19141924
b.currentFlow = preLoopLabel
19151925
if node.Kind == ast.KindForOfStatement {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
unreachableFlowAfterThrowingForInOfHead1.ts(2,21): error TS2488: Type 'never' must have a '[Symbol.iterator]()' method that returns an iterator.
2+
unreachableFlowAfterThrowingForInOfHead1.ts(3,9): error TS7027: Unreachable code detected.
3+
4+
5+
==== unreachableFlowAfterThrowingForInOfHead1.ts (2 errors) ====
6+
try {
7+
for (const x of (function () { throw "1"; })()) {
8+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9+
!!! error TS2488: Type 'never' must have a '[Symbol.iterator]()' method that returns an iterator.
10+
!!! related TS2773 unreachableFlowAfterThrowingForInOfHead1.ts:2:21: Did you forget to use 'await'?
11+
console.log("1");
12+
~~~~~~~~~~~~~~~~~
13+
!!! error TS7027: Unreachable code detected.
14+
}
15+
}
16+
catch (e) { }
17+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//// [tests/cases/compiler/unreachableFlowAfterThrowingForInOfHead1.ts] ////
2+
3+
=== unreachableFlowAfterThrowingForInOfHead1.ts ===
4+
try {
5+
for (const x of (function () { throw "1"; })()) {
6+
>x : Symbol(x, Decl(unreachableFlowAfterThrowingForInOfHead1.ts, 1, 14))
7+
8+
console.log("1");
9+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
10+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
11+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
12+
}
13+
}
14+
catch (e) { }
15+
>e : Symbol(e, Decl(unreachableFlowAfterThrowingForInOfHead1.ts, 5, 7))
16+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//// [tests/cases/compiler/unreachableFlowAfterThrowingForInOfHead1.ts] ////
2+
3+
=== unreachableFlowAfterThrowingForInOfHead1.ts ===
4+
try {
5+
for (const x of (function () { throw "1"; })()) {
6+
>x : any
7+
>(function () { throw "1"; })() : never
8+
>(function () { throw "1"; }) : () => never
9+
>function () { throw "1"; } : () => never
10+
>"1" : "1"
11+
12+
console.log("1");
13+
>console.log("1") : void
14+
>console.log : (...data: any[]) => void
15+
>console : Console
16+
>log : (...data: any[]) => void
17+
>"1" : "1"
18+
}
19+
}
20+
catch (e) { }
21+
>e : unknown
22+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
unreachableFlowAfterThrowingForInOfHead1.ts(2,21): error TS2488: Type 'never' must have a '[Symbol.iterator]()' method that returns an iterator.
2+
3+
4+
==== unreachableFlowAfterThrowingForInOfHead1.ts (1 errors) ====
5+
try {
6+
for (const x of (function () { throw "1"; })()) {
7+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8+
!!! error TS2488: Type 'never' must have a '[Symbol.iterator]()' method that returns an iterator.
9+
!!! related TS2773 unreachableFlowAfterThrowingForInOfHead1.ts:2:21: Did you forget to use 'await'?
10+
console.log("1");
11+
}
12+
}
13+
catch (e) { }
14+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//// [tests/cases/compiler/unreachableFlowAfterThrowingForInOfHead1.ts] ////
2+
3+
=== unreachableFlowAfterThrowingForInOfHead1.ts ===
4+
try {
5+
for (const x of (function () { throw "1"; })()) {
6+
>x : Symbol(x, Decl(unreachableFlowAfterThrowingForInOfHead1.ts, 1, 14))
7+
8+
console.log("1");
9+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
10+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
11+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
12+
}
13+
}
14+
catch (e) { }
15+
>e : Symbol(e, Decl(unreachableFlowAfterThrowingForInOfHead1.ts, 5, 7))
16+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//// [tests/cases/compiler/unreachableFlowAfterThrowingForInOfHead1.ts] ////
2+
3+
=== unreachableFlowAfterThrowingForInOfHead1.ts ===
4+
try {
5+
for (const x of (function () { throw "1"; })()) {
6+
>x : any
7+
>(function () { throw "1"; })() : never
8+
>(function () { throw "1"; }) : () => never
9+
>function () { throw "1"; } : () => never
10+
>"1" : "1"
11+
12+
console.log("1");
13+
>console.log("1") : void
14+
>console.log : (...data: any[]) => void
15+
>console : Console
16+
>log : (...data: any[]) => void
17+
>"1" : "1"
18+
}
19+
}
20+
catch (e) { }
21+
>e : unknown
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
unreachableFlowAfterThrowingForInOfHead2.ts(2,21): error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type 'never'.
2+
unreachableFlowAfterThrowingForInOfHead2.ts(3,9): error TS7027: Unreachable code detected.
3+
4+
5+
==== unreachableFlowAfterThrowingForInOfHead2.ts (2 errors) ====
6+
try {
7+
for (const x in (function () { throw "1"; })()) {
8+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9+
!!! error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type 'never'.
10+
console.log("1");
11+
~~~~~~~~~~~~~~~~~
12+
!!! error TS7027: Unreachable code detected.
13+
}
14+
}
15+
catch (e) { }
16+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//// [tests/cases/compiler/unreachableFlowAfterThrowingForInOfHead2.ts] ////
2+
3+
=== unreachableFlowAfterThrowingForInOfHead2.ts ===
4+
try {
5+
for (const x in (function () { throw "1"; })()) {
6+
>x : Symbol(x, Decl(unreachableFlowAfterThrowingForInOfHead2.ts, 1, 14))
7+
8+
console.log("1");
9+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
10+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
11+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
12+
}
13+
}
14+
catch (e) { }
15+
>e : Symbol(e, Decl(unreachableFlowAfterThrowingForInOfHead2.ts, 5, 7))
16+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//// [tests/cases/compiler/unreachableFlowAfterThrowingForInOfHead2.ts] ////
2+
3+
=== unreachableFlowAfterThrowingForInOfHead2.ts ===
4+
try {
5+
for (const x in (function () { throw "1"; })()) {
6+
>x : string
7+
>(function () { throw "1"; })() : never
8+
>(function () { throw "1"; }) : () => never
9+
>function () { throw "1"; } : () => never
10+
>"1" : "1"
11+
12+
console.log("1");
13+
>console.log("1") : void
14+
>console.log : (...data: any[]) => void
15+
>console : Console
16+
>log : (...data: any[]) => void
17+
>"1" : "1"
18+
}
19+
}
20+
catch (e) { }
21+
>e : unknown
22+

0 commit comments

Comments
 (0)