Merge pull request #23566 from ajafff/generator-cfa

binder: don't inline control flow of generator function
This commit is contained in:
Mohamed Hegazy 2018-04-20 09:11:50 -07:00 committed by GitHub
commit c8e4b7ba50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 302 additions and 23 deletions

View File

@ -516,8 +516,9 @@ namespace ts {
const saveReturnTarget = currentReturnTarget;
const saveActiveLabels = activeLabels;
const saveHasExplicitReturn = hasExplicitReturn;
const isIIFE = containerFlags & ContainerFlags.IsFunctionExpression && !hasModifier(node, ModifierFlags.Async) && !!getImmediatelyInvokedFunctionExpression(node);
// A non-async IIFE is considered part of the containing control flow. Return statements behave
const isIIFE = containerFlags & ContainerFlags.IsFunctionExpression && !hasModifier(node, ModifierFlags.Async) &&
!(<FunctionLikeDeclaration>node).asteriskToken && !!getImmediatelyInvokedFunctionExpression(node);
// A non-async, non-generator IIFE is considered part of the containing control flow. Return statements behave
// similarly to break statements that exit to a label just past the statement body.
if (!isIIFE) {
currentFlow = { flags: FlowFlags.Start };

View File

@ -0,0 +1,82 @@
tests/cases/conformance/controlFlow/controlFlowIIFE.ts(64,5): error TS2454: Variable 'v' is used before being assigned.
tests/cases/conformance/controlFlow/controlFlowIIFE.ts(72,5): error TS2454: Variable 'v' is used before being assigned.
==== tests/cases/conformance/controlFlow/controlFlowIIFE.ts (2 errors) ====
declare function getStringOrNumber(): string | number;
function f1() {
let x = getStringOrNumber();
if (typeof x === "string") {
let n = function() {
return x.length;
}();
}
}
function f2() {
let x = getStringOrNumber();
if (typeof x === "string") {
let n = (function() {
return x.length;
})();
}
}
function f3() {
let x = getStringOrNumber();
let y: number;
if (typeof x === "string") {
let n = (z => x.length + y + z)(y = 1);
}
}
// Repros from #8381
let maybeNumber: number | undefined;
(function () {
maybeNumber = 1;
})();
maybeNumber++;
if (maybeNumber !== undefined) {
maybeNumber++;
}
let test: string | undefined;
if (!test) {
throw new Error('Test is not defined');
}
(() => {
test.slice(1); // No error
})();
// Repro from #23565
function f4() {
let v: number;
(function() {
v = 1;
})();
v;
}
function f5() {
let v: number;
(function*() {
yield 1;
v = 1;
})();
v; // still undefined
~
!!! error TS2454: Variable 'v' is used before being assigned.
}
function f6() {
let v: number;
(async function() {
v = await 1;
})();
v; // still undefined
~
!!! error TS2454: Variable 'v' is used before being assigned.
}

View File

@ -44,34 +44,61 @@ if (!test) {
}
(() => {
test.slice(1); // No error
})();
})();
// Repro from #23565
function f4() {
let v: number;
(function() {
v = 1;
})();
v;
}
function f5() {
let v: number;
(function*() {
yield 1;
v = 1;
})();
v; // still undefined
}
function f6() {
let v: number;
(async function() {
v = await 1;
})();
v; // still undefined
}
//// [controlFlowIIFE.js]
function f1() {
var x = getStringOrNumber();
let x = getStringOrNumber();
if (typeof x === "string") {
var n = function () {
let n = function () {
return x.length;
}();
}
}
function f2() {
var x = getStringOrNumber();
let x = getStringOrNumber();
if (typeof x === "string") {
var n = (function () {
let n = (function () {
return x.length;
})();
}
}
function f3() {
var x = getStringOrNumber();
var y;
let x = getStringOrNumber();
let y;
if (typeof x === "string") {
var n = (function (z) { return x.length + y + z; })(y = 1);
let n = (z => x.length + y + z)(y = 1);
}
}
// Repros from #8381
var maybeNumber;
let maybeNumber;
(function () {
maybeNumber = 1;
})();
@ -79,10 +106,33 @@ maybeNumber++;
if (maybeNumber !== undefined) {
maybeNumber++;
}
var test;
let test;
if (!test) {
throw new Error('Test is not defined');
}
(function () {
(() => {
test.slice(1); // No error
})();
// Repro from #23565
function f4() {
let v;
(function () {
v = 1;
})();
v;
}
function f5() {
let v;
(function* () {
yield 1;
v = 1;
})();
v; // still undefined
}
function f6() {
let v;
(async function () {
v = await 1;
})();
v; // still undefined
}

View File

@ -16,9 +16,9 @@ function f1() {
>n : Symbol(n, Decl(controlFlowIIFE.ts, 5, 11))
return x.length;
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowIIFE.ts, 3, 7))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
}();
}
@ -38,9 +38,9 @@ function f2() {
>n : Symbol(n, Decl(controlFlowIIFE.ts, 14, 11))
return x.length;
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowIIFE.ts, 12, 7))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
})();
}
@ -62,9 +62,9 @@ function f3() {
let n = (z => x.length + y + z)(y = 1);
>n : Symbol(n, Decl(controlFlowIIFE.ts, 24, 11))
>z : Symbol(z, Decl(controlFlowIIFE.ts, 24, 17))
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowIIFE.ts, 21, 7))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
>y : Symbol(y, Decl(controlFlowIIFE.ts, 22, 7))
>z : Symbol(z, Decl(controlFlowIIFE.ts, 24, 17))
>y : Symbol(y, Decl(controlFlowIIFE.ts, 22, 7))
@ -99,12 +99,60 @@ if (!test) {
>test : Symbol(test, Decl(controlFlowIIFE.ts, 39, 3))
throw new Error('Test is not defined');
>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
}
(() => {
test.slice(1); // No error
>test.slice : Symbol(String.slice, Decl(lib.d.ts, --, --))
>test.slice : Symbol(String.slice, Decl(lib.es5.d.ts, --, --))
>test : Symbol(test, Decl(controlFlowIIFE.ts, 39, 3))
>slice : Symbol(String.slice, Decl(lib.d.ts, --, --))
>slice : Symbol(String.slice, Decl(lib.es5.d.ts, --, --))
})();
// Repro from #23565
function f4() {
>f4 : Symbol(f4, Decl(controlFlowIIFE.ts, 45, 5))
let v: number;
>v : Symbol(v, Decl(controlFlowIIFE.ts, 50, 7))
(function() {
v = 1;
>v : Symbol(v, Decl(controlFlowIIFE.ts, 50, 7))
})();
v;
>v : Symbol(v, Decl(controlFlowIIFE.ts, 50, 7))
}
function f5() {
>f5 : Symbol(f5, Decl(controlFlowIIFE.ts, 55, 1))
let v: number;
>v : Symbol(v, Decl(controlFlowIIFE.ts, 58, 7))
(function*() {
yield 1;
v = 1;
>v : Symbol(v, Decl(controlFlowIIFE.ts, 58, 7))
})();
v; // still undefined
>v : Symbol(v, Decl(controlFlowIIFE.ts, 58, 7))
}
function f6() {
>f6 : Symbol(f6, Decl(controlFlowIIFE.ts, 64, 1))
let v: number;
>v : Symbol(v, Decl(controlFlowIIFE.ts, 67, 7))
(async function() {
v = await 1;
>v : Symbol(v, Decl(controlFlowIIFE.ts, 67, 7))
})();
v; // still undefined
>v : Symbol(v, Decl(controlFlowIIFE.ts, 67, 7))
}

View File

@ -150,3 +150,73 @@ if (!test) {
>1 : 1
})();
// Repro from #23565
function f4() {
>f4 : () => void
let v: number;
>v : number
(function() {
>(function() { v = 1; })() : void
>(function() { v = 1; }) : () => void
>function() { v = 1; } : () => void
v = 1;
>v = 1 : 1
>v : number
>1 : 1
})();
v;
>v : number
}
function f5() {
>f5 : () => void
let v: number;
>v : number
(function*() {
>(function*() { yield 1; v = 1; })() : IterableIterator<number>
>(function*() { yield 1; v = 1; }) : () => IterableIterator<number>
>function*() { yield 1; v = 1; } : () => IterableIterator<number>
yield 1;
>yield 1 : any
>1 : 1
v = 1;
>v = 1 : 1
>v : number
>1 : 1
})();
v; // still undefined
>v : number
}
function f6() {
>f6 : () => void
let v: number;
>v : number
(async function() {
>(async function() { v = await 1; })() : Promise<void>
>(async function() { v = await 1; }) : () => Promise<void>
>async function() { v = await 1; } : () => Promise<void>
v = await 1;
>v = await 1 : 1
>v : number
>await 1 : 1
>1 : 1
})();
v; // still undefined
>v : number
}

View File

@ -1,4 +1,5 @@
// @strictNullChecks: true
// @target: ES2017
declare function getStringOrNumber(): string | number;
@ -45,4 +46,31 @@ if (!test) {
}
(() => {
test.slice(1); // No error
})();
})();
// Repro from #23565
function f4() {
let v: number;
(function() {
v = 1;
})();
v;
}
function f5() {
let v: number;
(function*() {
yield 1;
v = 1;
})();
v; // still undefined
}
function f6() {
let v: number;
(async function() {
v = await 1;
})();
v; // still undefined
}