Skip to content

Commit c8e4b7b

Browse files
authored
Merge pull request #23566 from ajafff/generator-cfa
binder: don't inline control flow of generator function
2 parents 8ae065e + 8e565fb commit c8e4b7b

File tree

6 files changed

+302
-23
lines changed

6 files changed

+302
-23
lines changed

src/compiler/binder.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -516,8 +516,9 @@ namespace ts {
516516
const saveReturnTarget = currentReturnTarget;
517517
const saveActiveLabels = activeLabels;
518518
const saveHasExplicitReturn = hasExplicitReturn;
519-
const isIIFE = containerFlags & ContainerFlags.IsFunctionExpression && !hasModifier(node, ModifierFlags.Async) && !!getImmediatelyInvokedFunctionExpression(node);
520-
// A non-async IIFE is considered part of the containing control flow. Return statements behave
519+
const isIIFE = containerFlags & ContainerFlags.IsFunctionExpression && !hasModifier(node, ModifierFlags.Async) &&
520+
!(<FunctionLikeDeclaration>node).asteriskToken && !!getImmediatelyInvokedFunctionExpression(node);
521+
// A non-async, non-generator IIFE is considered part of the containing control flow. Return statements behave
521522
// similarly to break statements that exit to a label just past the statement body.
522523
if (!isIIFE) {
523524
currentFlow = { flags: FlowFlags.Start };
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
tests/cases/conformance/controlFlow/controlFlowIIFE.ts(64,5): error TS2454: Variable 'v' is used before being assigned.
2+
tests/cases/conformance/controlFlow/controlFlowIIFE.ts(72,5): error TS2454: Variable 'v' is used before being assigned.
3+
4+
5+
==== tests/cases/conformance/controlFlow/controlFlowIIFE.ts (2 errors) ====
6+
declare function getStringOrNumber(): string | number;
7+
8+
function f1() {
9+
let x = getStringOrNumber();
10+
if (typeof x === "string") {
11+
let n = function() {
12+
return x.length;
13+
}();
14+
}
15+
}
16+
17+
function f2() {
18+
let x = getStringOrNumber();
19+
if (typeof x === "string") {
20+
let n = (function() {
21+
return x.length;
22+
})();
23+
}
24+
}
25+
26+
function f3() {
27+
let x = getStringOrNumber();
28+
let y: number;
29+
if (typeof x === "string") {
30+
let n = (z => x.length + y + z)(y = 1);
31+
}
32+
}
33+
34+
// Repros from #8381
35+
36+
let maybeNumber: number | undefined;
37+
(function () {
38+
maybeNumber = 1;
39+
})();
40+
maybeNumber++;
41+
if (maybeNumber !== undefined) {
42+
maybeNumber++;
43+
}
44+
45+
let test: string | undefined;
46+
if (!test) {
47+
throw new Error('Test is not defined');
48+
}
49+
(() => {
50+
test.slice(1); // No error
51+
})();
52+
53+
// Repro from #23565
54+
55+
function f4() {
56+
let v: number;
57+
(function() {
58+
v = 1;
59+
})();
60+
v;
61+
}
62+
63+
function f5() {
64+
let v: number;
65+
(function*() {
66+
yield 1;
67+
v = 1;
68+
})();
69+
v; // still undefined
70+
~
71+
!!! error TS2454: Variable 'v' is used before being assigned.
72+
}
73+
74+
function f6() {
75+
let v: number;
76+
(async function() {
77+
v = await 1;
78+
})();
79+
v; // still undefined
80+
~
81+
!!! error TS2454: Variable 'v' is used before being assigned.
82+
}

tests/baselines/reference/controlFlowIIFE.js

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,45 +44,95 @@ if (!test) {
4444
}
4545
(() => {
4646
test.slice(1); // No error
47-
})();
47+
})();
48+
49+
// Repro from #23565
50+
51+
function f4() {
52+
let v: number;
53+
(function() {
54+
v = 1;
55+
})();
56+
v;
57+
}
58+
59+
function f5() {
60+
let v: number;
61+
(function*() {
62+
yield 1;
63+
v = 1;
64+
})();
65+
v; // still undefined
66+
}
67+
68+
function f6() {
69+
let v: number;
70+
(async function() {
71+
v = await 1;
72+
})();
73+
v; // still undefined
74+
}
4875

4976
//// [controlFlowIIFE.js]
5077
function f1() {
51-
var x = getStringOrNumber();
78+
let x = getStringOrNumber();
5279
if (typeof x === "string") {
53-
var n = function () {
80+
let n = function () {
5481
return x.length;
5582
}();
5683
}
5784
}
5885
function f2() {
59-
var x = getStringOrNumber();
86+
let x = getStringOrNumber();
6087
if (typeof x === "string") {
61-
var n = (function () {
88+
let n = (function () {
6289
return x.length;
6390
})();
6491
}
6592
}
6693
function f3() {
67-
var x = getStringOrNumber();
68-
var y;
94+
let x = getStringOrNumber();
95+
let y;
6996
if (typeof x === "string") {
70-
var n = (function (z) { return x.length + y + z; })(y = 1);
97+
let n = (z => x.length + y + z)(y = 1);
7198
}
7299
}
73100
// Repros from #8381
74-
var maybeNumber;
101+
let maybeNumber;
75102
(function () {
76103
maybeNumber = 1;
77104
})();
78105
maybeNumber++;
79106
if (maybeNumber !== undefined) {
80107
maybeNumber++;
81108
}
82-
var test;
109+
let test;
83110
if (!test) {
84111
throw new Error('Test is not defined');
85112
}
86-
(function () {
113+
(() => {
87114
test.slice(1); // No error
88115
})();
116+
// Repro from #23565
117+
function f4() {
118+
let v;
119+
(function () {
120+
v = 1;
121+
})();
122+
v;
123+
}
124+
function f5() {
125+
let v;
126+
(function* () {
127+
yield 1;
128+
v = 1;
129+
})();
130+
v; // still undefined
131+
}
132+
function f6() {
133+
let v;
134+
(async function () {
135+
v = await 1;
136+
})();
137+
v; // still undefined
138+
}

tests/baselines/reference/controlFlowIIFE.symbols

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ function f1() {
1616
>n : Symbol(n, Decl(controlFlowIIFE.ts, 5, 11))
1717

1818
return x.length;
19-
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
19+
>x.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
2020
>x : Symbol(x, Decl(controlFlowIIFE.ts, 3, 7))
21-
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
21+
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
2222

2323
}();
2424
}
@@ -38,9 +38,9 @@ function f2() {
3838
>n : Symbol(n, Decl(controlFlowIIFE.ts, 14, 11))
3939

4040
return x.length;
41-
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
41+
>x.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
4242
>x : Symbol(x, Decl(controlFlowIIFE.ts, 12, 7))
43-
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
43+
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
4444

4545
})();
4646
}
@@ -62,9 +62,9 @@ function f3() {
6262
let n = (z => x.length + y + z)(y = 1);
6363
>n : Symbol(n, Decl(controlFlowIIFE.ts, 24, 11))
6464
>z : Symbol(z, Decl(controlFlowIIFE.ts, 24, 17))
65-
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
65+
>x.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
6666
>x : Symbol(x, Decl(controlFlowIIFE.ts, 21, 7))
67-
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
67+
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
6868
>y : Symbol(y, Decl(controlFlowIIFE.ts, 22, 7))
6969
>z : Symbol(z, Decl(controlFlowIIFE.ts, 24, 17))
7070
>y : Symbol(y, Decl(controlFlowIIFE.ts, 22, 7))
@@ -99,12 +99,60 @@ if (!test) {
9999
>test : Symbol(test, Decl(controlFlowIIFE.ts, 39, 3))
100100

101101
throw new Error('Test is not defined');
102-
>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
102+
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
103103
}
104104
(() => {
105105
test.slice(1); // No error
106-
>test.slice : Symbol(String.slice, Decl(lib.d.ts, --, --))
106+
>test.slice : Symbol(String.slice, Decl(lib.es5.d.ts, --, --))
107107
>test : Symbol(test, Decl(controlFlowIIFE.ts, 39, 3))
108-
>slice : Symbol(String.slice, Decl(lib.d.ts, --, --))
108+
>slice : Symbol(String.slice, Decl(lib.es5.d.ts, --, --))
109109

110110
})();
111+
112+
// Repro from #23565
113+
114+
function f4() {
115+
>f4 : Symbol(f4, Decl(controlFlowIIFE.ts, 45, 5))
116+
117+
let v: number;
118+
>v : Symbol(v, Decl(controlFlowIIFE.ts, 50, 7))
119+
120+
(function() {
121+
v = 1;
122+
>v : Symbol(v, Decl(controlFlowIIFE.ts, 50, 7))
123+
124+
})();
125+
v;
126+
>v : Symbol(v, Decl(controlFlowIIFE.ts, 50, 7))
127+
}
128+
129+
function f5() {
130+
>f5 : Symbol(f5, Decl(controlFlowIIFE.ts, 55, 1))
131+
132+
let v: number;
133+
>v : Symbol(v, Decl(controlFlowIIFE.ts, 58, 7))
134+
135+
(function*() {
136+
yield 1;
137+
v = 1;
138+
>v : Symbol(v, Decl(controlFlowIIFE.ts, 58, 7))
139+
140+
})();
141+
v; // still undefined
142+
>v : Symbol(v, Decl(controlFlowIIFE.ts, 58, 7))
143+
}
144+
145+
function f6() {
146+
>f6 : Symbol(f6, Decl(controlFlowIIFE.ts, 64, 1))
147+
148+
let v: number;
149+
>v : Symbol(v, Decl(controlFlowIIFE.ts, 67, 7))
150+
151+
(async function() {
152+
v = await 1;
153+
>v : Symbol(v, Decl(controlFlowIIFE.ts, 67, 7))
154+
155+
})();
156+
v; // still undefined
157+
>v : Symbol(v, Decl(controlFlowIIFE.ts, 67, 7))
158+
}

tests/baselines/reference/controlFlowIIFE.types

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,73 @@ if (!test) {
150150
>1 : 1
151151

152152
})();
153+
154+
// Repro from #23565
155+
156+
function f4() {
157+
>f4 : () => void
158+
159+
let v: number;
160+
>v : number
161+
162+
(function() {
163+
>(function() { v = 1; })() : void
164+
>(function() { v = 1; }) : () => void
165+
>function() { v = 1; } : () => void
166+
167+
v = 1;
168+
>v = 1 : 1
169+
>v : number
170+
>1 : 1
171+
172+
})();
173+
v;
174+
>v : number
175+
}
176+
177+
function f5() {
178+
>f5 : () => void
179+
180+
let v: number;
181+
>v : number
182+
183+
(function*() {
184+
>(function*() { yield 1; v = 1; })() : IterableIterator<number>
185+
>(function*() { yield 1; v = 1; }) : () => IterableIterator<number>
186+
>function*() { yield 1; v = 1; } : () => IterableIterator<number>
187+
188+
yield 1;
189+
>yield 1 : any
190+
>1 : 1
191+
192+
v = 1;
193+
>v = 1 : 1
194+
>v : number
195+
>1 : 1
196+
197+
})();
198+
v; // still undefined
199+
>v : number
200+
}
201+
202+
function f6() {
203+
>f6 : () => void
204+
205+
let v: number;
206+
>v : number
207+
208+
(async function() {
209+
>(async function() { v = await 1; })() : Promise<void>
210+
>(async function() { v = await 1; }) : () => Promise<void>
211+
>async function() { v = await 1; } : () => Promise<void>
212+
213+
v = await 1;
214+
>v = await 1 : 1
215+
>v : number
216+
>await 1 : 1
217+
>1 : 1
218+
219+
})();
220+
v; // still undefined
221+
>v : number
222+
}

0 commit comments

Comments
 (0)