Skip to content

Commit 4827728

Browse files
committed
binder: don't inline generator function's control flow
Fixes: microsoft#23565
1 parent 9a9d3ab commit 4827728

File tree

6 files changed

+340
-4
lines changed

6 files changed

+340
-4
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: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
error TS2318: Cannot find global type 'IterableIterator'.
2+
error TS2468: Cannot find global value 'Promise'.
3+
tests/cases/conformance/controlFlow/controlFlowIIFE.ts(64,5): error TS2454: Variable 'v' is used before being assigned.
4+
tests/cases/conformance/controlFlow/controlFlowIIFE.ts(69,6): error TS2705: An async function or method in ES5/ES3 requires the 'Promise' constructor. Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your `--lib` option.
5+
tests/cases/conformance/controlFlow/controlFlowIIFE.ts(72,5): error TS2454: Variable 'v' is used before being assigned.
6+
7+
8+
!!! error TS2318: Cannot find global type 'IterableIterator'.
9+
!!! error TS2468: Cannot find global value 'Promise'.
10+
==== tests/cases/conformance/controlFlow/controlFlowIIFE.ts (3 errors) ====
11+
declare function getStringOrNumber(): string | number;
12+
13+
function f1() {
14+
let x = getStringOrNumber();
15+
if (typeof x === "string") {
16+
let n = function() {
17+
return x.length;
18+
}();
19+
}
20+
}
21+
22+
function f2() {
23+
let x = getStringOrNumber();
24+
if (typeof x === "string") {
25+
let n = (function() {
26+
return x.length;
27+
})();
28+
}
29+
}
30+
31+
function f3() {
32+
let x = getStringOrNumber();
33+
let y: number;
34+
if (typeof x === "string") {
35+
let n = (z => x.length + y + z)(y = 1);
36+
}
37+
}
38+
39+
// Repros from #8381
40+
41+
let maybeNumber: number | undefined;
42+
(function () {
43+
maybeNumber = 1;
44+
})();
45+
maybeNumber++;
46+
if (maybeNumber !== undefined) {
47+
maybeNumber++;
48+
}
49+
50+
let test: string | undefined;
51+
if (!test) {
52+
throw new Error('Test is not defined');
53+
}
54+
(() => {
55+
test.slice(1); // No error
56+
})();
57+
58+
// Repro from #23565
59+
60+
function f4() {
61+
let v: number;
62+
(function() {
63+
v = 1;
64+
})();
65+
v;
66+
}
67+
68+
function f5() {
69+
let v: number;
70+
(function*() {
71+
yield 1;
72+
v = 1;
73+
})();
74+
v; // still undefined
75+
~
76+
!!! error TS2454: Variable 'v' is used before being assigned.
77+
}
78+
79+
function f6() {
80+
let v: number;
81+
(async function() {
82+
~~~~~
83+
!!! error TS2705: An async function or method in ES5/ES3 requires the 'Promise' constructor. Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your `--lib` option.
84+
v = await 1;
85+
})();
86+
v; // still undefined
87+
~
88+
!!! error TS2454: Variable 'v' is used before being assigned.
89+
}

tests/baselines/reference/controlFlowIIFE.js

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,71 @@ 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]
77+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
78+
return new (P || (P = Promise))(function (resolve, reject) {
79+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
80+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
81+
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
82+
step((generator = generator.apply(thisArg, _arguments || [])).next());
83+
});
84+
};
85+
var __generator = (this && this.__generator) || function (thisArg, body) {
86+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
87+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
88+
function verb(n) { return function (v) { return step([n, v]); }; }
89+
function step(op) {
90+
if (f) throw new TypeError("Generator is already executing.");
91+
while (_) try {
92+
if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
93+
if (y = 0, t) op = [0, t.value];
94+
switch (op[0]) {
95+
case 0: case 1: t = op; break;
96+
case 4: _.label++; return { value: op[1], done: false };
97+
case 5: _.label++; y = op[1]; op = [0]; continue;
98+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
99+
default:
100+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
101+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
102+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
103+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
104+
if (t[2]) _.ops.pop();
105+
_.trys.pop(); continue;
106+
}
107+
op = body.call(thisArg, _);
108+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
109+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
110+
}
111+
};
50112
function f1() {
51113
var x = getStringOrNumber();
52114
if (typeof x === "string") {
@@ -86,3 +148,42 @@ if (!test) {
86148
(function () {
87149
test.slice(1); // No error
88150
})();
151+
// Repro from #23565
152+
function f4() {
153+
var v;
154+
(function () {
155+
v = 1;
156+
})();
157+
v;
158+
}
159+
function f5() {
160+
var v;
161+
(function () {
162+
return __generator(this, function (_a) {
163+
switch (_a.label) {
164+
case 0: return [4 /*yield*/, 1];
165+
case 1:
166+
_a.sent();
167+
v = 1;
168+
return [2 /*return*/];
169+
}
170+
});
171+
})();
172+
v; // still undefined
173+
}
174+
function f6() {
175+
var v;
176+
(function () {
177+
return __awaiter(this, void 0, void 0, function () {
178+
return __generator(this, function (_a) {
179+
switch (_a.label) {
180+
case 0: return [4 /*yield*/, 1];
181+
case 1:
182+
v = _a.sent();
183+
return [2 /*return*/];
184+
}
185+
});
186+
});
187+
})();
188+
v; // still undefined
189+
}

tests/baselines/reference/controlFlowIIFE.symbols

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,51 @@ if (!test) {
108108
>slice : Symbol(String.slice, Decl(lib.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; })() : {}
185+
>(function*() { yield 1; v = 1; }) : () => {}
186+
>function*() { yield 1; v = 1; } : () => {}
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+
}

tests/cases/conformance/controlFlow/controlFlowIIFE.ts

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

0 commit comments

Comments
 (0)