Skip to content

Commit dc60e68

Browse files
committed
Merge pull request microsoft#8286 from Microsoft/controlFlowCacheFix
Control flow cache fix
2 parents 0f18cf8 + ff108c0 commit dc60e68

File tree

7 files changed

+163
-9
lines changed

7 files changed

+163
-9
lines changed

src/compiler/binder.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,13 @@ namespace ts {
645645
};
646646
}
647647

648+
function createFlowLoopLabel(): FlowLabel {
649+
return {
650+
kind: FlowKind.LoopLabel,
651+
antecedents: undefined
652+
};
653+
}
654+
648655
function addAntecedent(label: FlowLabel, antecedent: FlowNode): void {
649656
if (antecedent.kind !== FlowKind.Unreachable && !contains(label.antecedents, antecedent)) {
650657
(label.antecedents || (label.antecedents = [])).push(antecedent);
@@ -755,7 +762,7 @@ namespace ts {
755762
}
756763

757764
function bindWhileStatement(node: WhileStatement): void {
758-
const preWhileLabel = createFlowLabel();
765+
const preWhileLabel = createFlowLoopLabel();
759766
const preBodyLabel = createFlowLabel();
760767
const postWhileLabel = createFlowLabel();
761768
addAntecedent(preWhileLabel, currentFlow);
@@ -768,7 +775,7 @@ namespace ts {
768775
}
769776

770777
function bindDoStatement(node: DoStatement): void {
771-
const preDoLabel = createFlowLabel();
778+
const preDoLabel = createFlowLoopLabel();
772779
const preConditionLabel = createFlowLabel();
773780
const postDoLabel = createFlowLabel();
774781
addAntecedent(preDoLabel, currentFlow);
@@ -781,7 +788,7 @@ namespace ts {
781788
}
782789

783790
function bindForStatement(node: ForStatement): void {
784-
const preLoopLabel = createFlowLabel();
791+
const preLoopLabel = createFlowLoopLabel();
785792
const preBodyLabel = createFlowLabel();
786793
const postLoopLabel = createFlowLabel();
787794
bind(node.initializer);
@@ -796,7 +803,7 @@ namespace ts {
796803
}
797804

798805
function bindForInOrForOfStatement(node: ForInStatement | ForOfStatement): void {
799-
const preLoopLabel = createFlowLabel();
806+
const preLoopLabel = createFlowLoopLabel();
800807
const postLoopLabel = createFlowLabel();
801808
addAntecedent(preLoopLabel, currentFlow);
802809
currentFlow = preLoopLabel;
@@ -943,7 +950,7 @@ namespace ts {
943950
}
944951

945952
function bindLabeledStatement(node: LabeledStatement): void {
946-
const preStatementLabel = createFlowLabel();
953+
const preStatementLabel = createFlowLoopLabel();
947954
const postStatementLabel = createFlowLabel();
948955
bind(node.label);
949956
addAntecedent(preStatementLabel, currentFlow);

src/compiler/checker.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7592,6 +7592,7 @@ namespace ts {
75927592
case FlowKind.Condition:
75937593
return getTypeAtFlowCondition(<FlowCondition>flow);
75947594
case FlowKind.Label:
7595+
case FlowKind.LoopLabel:
75957596
if ((<FlowLabel>flow).antecedents.length === 1) {
75967597
flow = (<FlowLabel>flow).antecedents[0];
75977598
continue;
@@ -7639,7 +7640,8 @@ namespace ts {
76397640
}
76407641

76417642
function getTypeAtFlowCondition(flow: FlowCondition) {
7642-
return narrowType(getTypeAtFlowNode(flow.antecedent), flow.expression, flow.assumeTrue);
7643+
const type = getTypeAtFlowNode(flow.antecedent);
7644+
return type && narrowType(type, flow.expression, flow.assumeTrue);
76437645
}
76447646

76457647
function getTypeAtFlowNodeCached(flow: FlowNode) {
@@ -7665,13 +7667,15 @@ namespace ts {
76657667
flowStackCount--;
76667668
// Record the result only if the cache is still empty. If checkExpressionCached was called
76677669
// during processing it is possible we've already recorded a result.
7668-
return cache[key] || (cache[key] = type);
7670+
return cache[key] || type && (cache[key] = type);
76697671
}
76707672

76717673
function getTypeAtFlowLabel(flow: FlowLabel) {
76727674
const antecedentTypes: Type[] = [];
76737675
for (const antecedent of flow.antecedents) {
7674-
const type = getTypeAtFlowNodeCached(antecedent);
7676+
const type = flow.kind === FlowKind.LoopLabel ?
7677+
getTypeAtFlowNodeCached(antecedent) :
7678+
getTypeAtFlowNode(antecedent);
76757679
if (type) {
76767680
// If the type at a particular antecedent path is the declared type and the
76777681
// reference is known to always be assigned (i.e. when declared and initial types
@@ -7685,7 +7689,7 @@ namespace ts {
76857689
}
76867690
}
76877691
}
7688-
return antecedentTypes.length === 0 ? declaredType :
7692+
return antecedentTypes.length === 0 ? undefined :
76897693
antecedentTypes.length === 1 ? antecedentTypes[0] :
76907694
getUnionType(antecedentTypes);
76917695
}

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1519,6 +1519,7 @@ namespace ts {
15191519
Unreachable,
15201520
Start,
15211521
Label,
1522+
LoopLabel,
15221523
Assignment,
15231524
Condition
15241525
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//// [controlFlowIteration.ts]
2+
3+
let cond: boolean;
4+
5+
function ff() {
6+
let x: string | undefined;
7+
while (true) {
8+
if (cond) {
9+
x = "";
10+
}
11+
else {
12+
if (x) {
13+
x.length;
14+
}
15+
if (x) {
16+
x.length;
17+
}
18+
}
19+
}
20+
}
21+
22+
23+
//// [controlFlowIteration.js]
24+
var cond;
25+
function ff() {
26+
var x;
27+
while (true) {
28+
if (cond) {
29+
x = "";
30+
}
31+
else {
32+
if (x) {
33+
x.length;
34+
}
35+
if (x) {
36+
x.length;
37+
}
38+
}
39+
}
40+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
=== tests/cases/conformance/controlFlow/controlFlowIteration.ts ===
2+
3+
let cond: boolean;
4+
>cond : Symbol(cond, Decl(controlFlowIteration.ts, 1, 3))
5+
6+
function ff() {
7+
>ff : Symbol(ff, Decl(controlFlowIteration.ts, 1, 18))
8+
9+
let x: string | undefined;
10+
>x : Symbol(x, Decl(controlFlowIteration.ts, 4, 7))
11+
12+
while (true) {
13+
if (cond) {
14+
>cond : Symbol(cond, Decl(controlFlowIteration.ts, 1, 3))
15+
16+
x = "";
17+
>x : Symbol(x, Decl(controlFlowIteration.ts, 4, 7))
18+
}
19+
else {
20+
if (x) {
21+
>x : Symbol(x, Decl(controlFlowIteration.ts, 4, 7))
22+
23+
x.length;
24+
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
25+
>x : Symbol(x, Decl(controlFlowIteration.ts, 4, 7))
26+
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
27+
}
28+
if (x) {
29+
>x : Symbol(x, Decl(controlFlowIteration.ts, 4, 7))
30+
31+
x.length;
32+
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
33+
>x : Symbol(x, Decl(controlFlowIteration.ts, 4, 7))
34+
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
35+
}
36+
}
37+
}
38+
}
39+
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
=== tests/cases/conformance/controlFlow/controlFlowIteration.ts ===
2+
3+
let cond: boolean;
4+
>cond : boolean
5+
6+
function ff() {
7+
>ff : () => void
8+
9+
let x: string | undefined;
10+
>x : string | undefined
11+
12+
while (true) {
13+
>true : boolean
14+
15+
if (cond) {
16+
>cond : boolean
17+
18+
x = "";
19+
>x = "" : string
20+
>x : string | undefined
21+
>"" : string
22+
}
23+
else {
24+
if (x) {
25+
>x : string | undefined
26+
27+
x.length;
28+
>x.length : number
29+
>x : string
30+
>length : number
31+
}
32+
if (x) {
33+
>x : string | undefined
34+
35+
x.length;
36+
>x.length : number
37+
>x : string
38+
>length : number
39+
}
40+
}
41+
}
42+
}
43+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// @strictNullChecks: true
2+
3+
let cond: boolean;
4+
5+
function ff() {
6+
let x: string | undefined;
7+
while (true) {
8+
if (cond) {
9+
x = "";
10+
}
11+
else {
12+
if (x) {
13+
x.length;
14+
}
15+
if (x) {
16+
x.length;
17+
}
18+
}
19+
}
20+
}

0 commit comments

Comments
 (0)