From f183f1ab387331f3957faf9a581c26ab54aa775a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 2 May 2016 09:21:54 -0700 Subject: [PATCH 1/3] Quicker bail out for type analysis in nested loops --- src/compiler/checker.ts | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c434721d714..b3174766347 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7664,17 +7664,18 @@ namespace ts { const type = flow.kind === FlowKind.LoopLabel ? getTypeAtFlowNodeCached(antecedent) : getTypeAtFlowNode(antecedent); - if (type) { - // If the type at a particular antecedent path is the declared type and the - // reference is known to always be assigned (i.e. when declared and initial types - // are the same), there is no reason to process more antecedents since the only - // possible outcome is subtypes that will be removed in the final union type anyway. - if (type === declaredType && declaredType === initialType) { - return type; - } - if (!contains(antecedentTypes, type)) { - antecedentTypes.push(type); - } + if (!type) { + break; + } + // If the type at a particular antecedent path is the declared type and the + // reference is known to always be assigned (i.e. when declared and initial types + // are the same), there is no reason to process more antecedents since the only + // possible outcome is subtypes that will be removed in the final union type anyway. + if (type === declaredType && declaredType === initialType) { + return type; + } + if (!contains(antecedentTypes, type)) { + antecedentTypes.push(type); } } return antecedentTypes.length === 0 ? undefined : From 82d4f4288f6f4bd7320d60b7693c3e877965122e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 2 May 2016 09:22:04 -0700 Subject: [PATCH 2/3] Adding regression test --- .../reference/controlFlowWhileStatement.js | 46 ++++++++++++++ .../controlFlowWhileStatement.symbols | 53 ++++++++++++++++ .../reference/controlFlowWhileStatement.types | 62 +++++++++++++++++++ .../controlFlow/controlFlowWhileStatement.ts | 23 +++++++ 4 files changed, 184 insertions(+) diff --git a/tests/baselines/reference/controlFlowWhileStatement.js b/tests/baselines/reference/controlFlowWhileStatement.js index d9faf9bd0ea..c9299330082 100644 --- a/tests/baselines/reference/controlFlowWhileStatement.js +++ b/tests/baselines/reference/controlFlowWhileStatement.js @@ -105,6 +105,29 @@ function h3() { } x; // string | number } +// Repro for #8418 +function foo(x: number): number { return 1; } +function test1() { + let x: number | undefined; + while (cond) { + while (cond) { + while (cond) { + x = foo(x); + } + } + x = 1; + } +} +// Repro for #8418 +function test2() { + let x: number | undefined; + x = 1; + while (cond) { + while (cond) { + x = foo(x); + } + } +} //// [controlFlowWhileStatement.js] @@ -214,3 +237,26 @@ function h3() { } x; // string | number } +// Repro for #8418 +function foo(x) { return 1; } +function test1() { + var x; + while (cond) { + while (cond) { + while (cond) { + x = foo(x); + } + } + x = 1; + } +} +// Repro for #8418 +function test2() { + var x; + x = 1; + while (cond) { + while (cond) { + x = foo(x); + } + } +} diff --git a/tests/baselines/reference/controlFlowWhileStatement.symbols b/tests/baselines/reference/controlFlowWhileStatement.symbols index 3b4def93b45..c38908240ee 100644 --- a/tests/baselines/reference/controlFlowWhileStatement.symbols +++ b/tests/baselines/reference/controlFlowWhileStatement.symbols @@ -254,4 +254,57 @@ function h3() { x; // string | number >x : Symbol(x, Decl(controlFlowWhileStatement.ts, 98, 7)) } +// Repro for #8418 +function foo(x: number): number { return 1; } +>foo : Symbol(foo, Decl(controlFlowWhileStatement.ts, 105, 1)) +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 107, 13)) + +function test1() { +>test1 : Symbol(test1, Decl(controlFlowWhileStatement.ts, 107, 45)) + + let x: number | undefined; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 109, 7)) + + while (cond) { +>cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) + + while (cond) { +>cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) + + while (cond) { +>cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) + + x = foo(x); +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 109, 7)) +>foo : Symbol(foo, Decl(controlFlowWhileStatement.ts, 105, 1)) +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 109, 7)) + } + } + x = 1; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 109, 7)) + } +} +// Repro for #8418 +function test2() { +>test2 : Symbol(test2, Decl(controlFlowWhileStatement.ts, 118, 1)) + + let x: number | undefined; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 121, 7)) + + x = 1; +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 121, 7)) + + while (cond) { +>cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) + + while (cond) { +>cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) + + x = foo(x); +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 121, 7)) +>foo : Symbol(foo, Decl(controlFlowWhileStatement.ts, 105, 1)) +>x : Symbol(x, Decl(controlFlowWhileStatement.ts, 121, 7)) + } + } +} diff --git a/tests/baselines/reference/controlFlowWhileStatement.types b/tests/baselines/reference/controlFlowWhileStatement.types index a17c084228b..48af317e2ec 100644 --- a/tests/baselines/reference/controlFlowWhileStatement.types +++ b/tests/baselines/reference/controlFlowWhileStatement.types @@ -307,4 +307,66 @@ function h3() { x; // string | number >x : string | number } +// Repro for #8418 +function foo(x: number): number { return 1; } +>foo : (x: number) => number +>x : number +>1 : number + +function test1() { +>test1 : () => void + + let x: number | undefined; +>x : number + + while (cond) { +>cond : boolean + + while (cond) { +>cond : boolean + + while (cond) { +>cond : boolean + + x = foo(x); +>x = foo(x) : number +>x : number +>foo(x) : number +>foo : (x: number) => number +>x : number + } + } + x = 1; +>x = 1 : number +>x : number +>1 : number + } +} +// Repro for #8418 +function test2() { +>test2 : () => void + + let x: number | undefined; +>x : number + + x = 1; +>x = 1 : number +>x : number +>1 : number + + while (cond) { +>cond : boolean + + while (cond) { +>cond : boolean + + x = foo(x); +>x = foo(x) : number +>x : number +>foo(x) : number +>foo : (x: number) => number +>x : number + } + } +} diff --git a/tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts b/tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts index 7bf49dd7224..61c09094e56 100644 --- a/tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts +++ b/tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts @@ -104,3 +104,26 @@ function h3() { } x; // string | number } +// Repro for #8418 +function foo(x: number): number { return 1; } +function test1() { + let x: number | undefined; + while (cond) { + while (cond) { + while (cond) { + x = foo(x); + } + } + x = 1; + } +} +// Repro for #8418 +function test2() { + let x: number | undefined; + x = 1; + while (cond) { + while (cond) { + x = foo(x); + } + } +} From d9f0293c6cdb6d94d80f3ca801cdd268b245c344 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 2 May 2016 11:17:10 -0700 Subject: [PATCH 3/3] Fixing repro test --- .../controlFlowLoopAnalysis.errors.txt | 39 ++++++++++++ .../reference/controlFlowLoopAnalysis.js | 58 +++++++++++++++++ .../reference/controlFlowWhileStatement.js | 46 -------------- .../controlFlowWhileStatement.symbols | 53 ---------------- .../reference/controlFlowWhileStatement.types | 62 ------------------- .../cases/compiler/controlFlowLoopAnalysis.ts | 31 ++++++++++ .../controlFlow/controlFlowWhileStatement.ts | 23 ------- 7 files changed, 128 insertions(+), 184 deletions(-) create mode 100644 tests/baselines/reference/controlFlowLoopAnalysis.errors.txt create mode 100644 tests/baselines/reference/controlFlowLoopAnalysis.js create mode 100644 tests/cases/compiler/controlFlowLoopAnalysis.ts diff --git a/tests/baselines/reference/controlFlowLoopAnalysis.errors.txt b/tests/baselines/reference/controlFlowLoopAnalysis.errors.txt new file mode 100644 index 00000000000..21bf6b1c19a --- /dev/null +++ b/tests/baselines/reference/controlFlowLoopAnalysis.errors.txt @@ -0,0 +1,39 @@ +tests/cases/compiler/controlFlowLoopAnalysis.ts(13,25): error TS2345: Argument of type 'number | undefined' is not assignable to parameter of type 'number'. + Type 'undefined' is not assignable to type 'number'. + + +==== tests/cases/compiler/controlFlowLoopAnalysis.ts (1 errors) ==== + + // Repro from #8418 + + let cond: boolean; + + function foo(x: number): number { return 1; } + + function test1() { + let x: number | undefined; + while (cond) { + while (cond) { + while (cond) { + x = foo(x); + ~ +!!! error TS2345: Argument of type 'number | undefined' is not assignable to parameter of type 'number'. +!!! error TS2345: Type 'undefined' is not assignable to type 'number'. + } + } + x = 1; + } + } + + // Repro from #8418 + + function test2() { + let x: number | undefined; + x = 1; + while (cond) { + while (cond) { + x = foo(x); + } + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/controlFlowLoopAnalysis.js b/tests/baselines/reference/controlFlowLoopAnalysis.js new file mode 100644 index 00000000000..3cf2ce2caac --- /dev/null +++ b/tests/baselines/reference/controlFlowLoopAnalysis.js @@ -0,0 +1,58 @@ +//// [controlFlowLoopAnalysis.ts] + +// Repro from #8418 + +let cond: boolean; + +function foo(x: number): number { return 1; } + +function test1() { + let x: number | undefined; + while (cond) { + while (cond) { + while (cond) { + x = foo(x); + } + } + x = 1; + } +} + +// Repro from #8418 + +function test2() { + let x: number | undefined; + x = 1; + while (cond) { + while (cond) { + x = foo(x); + } + } +} + + +//// [controlFlowLoopAnalysis.js] +// Repro from #8418 +var cond; +function foo(x) { return 1; } +function test1() { + var x; + while (cond) { + while (cond) { + while (cond) { + x = foo(x); + } + } + x = 1; + } +} +// Repro from #8418 +function test2() { + var x; + x = 1; + while (cond) { + while (cond) { + x = foo(x); + } + } +} diff --git a/tests/baselines/reference/controlFlowWhileStatement.js b/tests/baselines/reference/controlFlowWhileStatement.js index c9299330082..d9faf9bd0ea 100644 --- a/tests/baselines/reference/controlFlowWhileStatement.js +++ b/tests/baselines/reference/controlFlowWhileStatement.js @@ -105,29 +105,6 @@ function h3() { } x; // string | number } -// Repro for #8418 -function foo(x: number): number { return 1; } -function test1() { - let x: number | undefined; - while (cond) { - while (cond) { - while (cond) { - x = foo(x); - } - } - x = 1; - } -} -// Repro for #8418 -function test2() { - let x: number | undefined; - x = 1; - while (cond) { - while (cond) { - x = foo(x); - } - } -} //// [controlFlowWhileStatement.js] @@ -237,26 +214,3 @@ function h3() { } x; // string | number } -// Repro for #8418 -function foo(x) { return 1; } -function test1() { - var x; - while (cond) { - while (cond) { - while (cond) { - x = foo(x); - } - } - x = 1; - } -} -// Repro for #8418 -function test2() { - var x; - x = 1; - while (cond) { - while (cond) { - x = foo(x); - } - } -} diff --git a/tests/baselines/reference/controlFlowWhileStatement.symbols b/tests/baselines/reference/controlFlowWhileStatement.symbols index c38908240ee..3b4def93b45 100644 --- a/tests/baselines/reference/controlFlowWhileStatement.symbols +++ b/tests/baselines/reference/controlFlowWhileStatement.symbols @@ -254,57 +254,4 @@ function h3() { x; // string | number >x : Symbol(x, Decl(controlFlowWhileStatement.ts, 98, 7)) } -// Repro for #8418 -function foo(x: number): number { return 1; } ->foo : Symbol(foo, Decl(controlFlowWhileStatement.ts, 105, 1)) ->x : Symbol(x, Decl(controlFlowWhileStatement.ts, 107, 13)) - -function test1() { ->test1 : Symbol(test1, Decl(controlFlowWhileStatement.ts, 107, 45)) - - let x: number | undefined; ->x : Symbol(x, Decl(controlFlowWhileStatement.ts, 109, 7)) - - while (cond) { ->cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) - - while (cond) { ->cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) - - while (cond) { ->cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) - - x = foo(x); ->x : Symbol(x, Decl(controlFlowWhileStatement.ts, 109, 7)) ->foo : Symbol(foo, Decl(controlFlowWhileStatement.ts, 105, 1)) ->x : Symbol(x, Decl(controlFlowWhileStatement.ts, 109, 7)) - } - } - x = 1; ->x : Symbol(x, Decl(controlFlowWhileStatement.ts, 109, 7)) - } -} -// Repro for #8418 -function test2() { ->test2 : Symbol(test2, Decl(controlFlowWhileStatement.ts, 118, 1)) - - let x: number | undefined; ->x : Symbol(x, Decl(controlFlowWhileStatement.ts, 121, 7)) - - x = 1; ->x : Symbol(x, Decl(controlFlowWhileStatement.ts, 121, 7)) - - while (cond) { ->cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) - - while (cond) { ->cond : Symbol(cond, Decl(controlFlowWhileStatement.ts, 0, 3)) - - x = foo(x); ->x : Symbol(x, Decl(controlFlowWhileStatement.ts, 121, 7)) ->foo : Symbol(foo, Decl(controlFlowWhileStatement.ts, 105, 1)) ->x : Symbol(x, Decl(controlFlowWhileStatement.ts, 121, 7)) - } - } -} diff --git a/tests/baselines/reference/controlFlowWhileStatement.types b/tests/baselines/reference/controlFlowWhileStatement.types index 48af317e2ec..a17c084228b 100644 --- a/tests/baselines/reference/controlFlowWhileStatement.types +++ b/tests/baselines/reference/controlFlowWhileStatement.types @@ -307,66 +307,4 @@ function h3() { x; // string | number >x : string | number } -// Repro for #8418 -function foo(x: number): number { return 1; } ->foo : (x: number) => number ->x : number ->1 : number - -function test1() { ->test1 : () => void - - let x: number | undefined; ->x : number - - while (cond) { ->cond : boolean - - while (cond) { ->cond : boolean - - while (cond) { ->cond : boolean - - x = foo(x); ->x = foo(x) : number ->x : number ->foo(x) : number ->foo : (x: number) => number ->x : number - } - } - x = 1; ->x = 1 : number ->x : number ->1 : number - } -} -// Repro for #8418 -function test2() { ->test2 : () => void - - let x: number | undefined; ->x : number - - x = 1; ->x = 1 : number ->x : number ->1 : number - - while (cond) { ->cond : boolean - - while (cond) { ->cond : boolean - - x = foo(x); ->x = foo(x) : number ->x : number ->foo(x) : number ->foo : (x: number) => number ->x : number - } - } -} diff --git a/tests/cases/compiler/controlFlowLoopAnalysis.ts b/tests/cases/compiler/controlFlowLoopAnalysis.ts new file mode 100644 index 00000000000..60cb9436006 --- /dev/null +++ b/tests/cases/compiler/controlFlowLoopAnalysis.ts @@ -0,0 +1,31 @@ +// @strictNullChecks: true + +// Repro from #8418 + +let cond: boolean; + +function foo(x: number): number { return 1; } + +function test1() { + let x: number | undefined; + while (cond) { + while (cond) { + while (cond) { + x = foo(x); + } + } + x = 1; + } +} + +// Repro from #8418 + +function test2() { + let x: number | undefined; + x = 1; + while (cond) { + while (cond) { + x = foo(x); + } + } +} diff --git a/tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts b/tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts index 61c09094e56..7bf49dd7224 100644 --- a/tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts +++ b/tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts @@ -104,26 +104,3 @@ function h3() { } x; // string | number } -// Repro for #8418 -function foo(x: number): number { return 1; } -function test1() { - let x: number | undefined; - while (cond) { - while (cond) { - while (cond) { - x = foo(x); - } - } - x = 1; - } -} -// Repro for #8418 -function test2() { - let x: number | undefined; - x = 1; - while (cond) { - while (cond) { - x = foo(x); - } - } -}