Merge pull request #8286 from Microsoft/controlFlowCacheFix

Control flow cache fix
This commit is contained in:
Anders Hejlsberg
2016-04-25 12:26:30 -07:00
7 changed files with 163 additions and 9 deletions

View File

@@ -645,6 +645,13 @@ namespace ts {
};
}
function createFlowLoopLabel(): FlowLabel {
return {
kind: FlowKind.LoopLabel,
antecedents: undefined
};
}
function addAntecedent(label: FlowLabel, antecedent: FlowNode): void {
if (antecedent.kind !== FlowKind.Unreachable && !contains(label.antecedents, antecedent)) {
(label.antecedents || (label.antecedents = [])).push(antecedent);
@@ -755,7 +762,7 @@ namespace ts {
}
function bindWhileStatement(node: WhileStatement): void {
const preWhileLabel = createFlowLabel();
const preWhileLabel = createFlowLoopLabel();
const preBodyLabel = createFlowLabel();
const postWhileLabel = createFlowLabel();
addAntecedent(preWhileLabel, currentFlow);
@@ -768,7 +775,7 @@ namespace ts {
}
function bindDoStatement(node: DoStatement): void {
const preDoLabel = createFlowLabel();
const preDoLabel = createFlowLoopLabel();
const preConditionLabel = createFlowLabel();
const postDoLabel = createFlowLabel();
addAntecedent(preDoLabel, currentFlow);
@@ -781,7 +788,7 @@ namespace ts {
}
function bindForStatement(node: ForStatement): void {
const preLoopLabel = createFlowLabel();
const preLoopLabel = createFlowLoopLabel();
const preBodyLabel = createFlowLabel();
const postLoopLabel = createFlowLabel();
bind(node.initializer);
@@ -796,7 +803,7 @@ namespace ts {
}
function bindForInOrForOfStatement(node: ForInStatement | ForOfStatement): void {
const preLoopLabel = createFlowLabel();
const preLoopLabel = createFlowLoopLabel();
const postLoopLabel = createFlowLabel();
addAntecedent(preLoopLabel, currentFlow);
currentFlow = preLoopLabel;
@@ -943,7 +950,7 @@ namespace ts {
}
function bindLabeledStatement(node: LabeledStatement): void {
const preStatementLabel = createFlowLabel();
const preStatementLabel = createFlowLoopLabel();
const postStatementLabel = createFlowLabel();
bind(node.label);
addAntecedent(preStatementLabel, currentFlow);

View File

@@ -7592,6 +7592,7 @@ namespace ts {
case FlowKind.Condition:
return getTypeAtFlowCondition(<FlowCondition>flow);
case FlowKind.Label:
case FlowKind.LoopLabel:
if ((<FlowLabel>flow).antecedents.length === 1) {
flow = (<FlowLabel>flow).antecedents[0];
continue;
@@ -7639,7 +7640,8 @@ namespace ts {
}
function getTypeAtFlowCondition(flow: FlowCondition) {
return narrowType(getTypeAtFlowNode(flow.antecedent), flow.expression, flow.assumeTrue);
const type = getTypeAtFlowNode(flow.antecedent);
return type && narrowType(type, flow.expression, flow.assumeTrue);
}
function getTypeAtFlowNodeCached(flow: FlowNode) {
@@ -7665,13 +7667,15 @@ namespace ts {
flowStackCount--;
// Record the result only if the cache is still empty. If checkExpressionCached was called
// during processing it is possible we've already recorded a result.
return cache[key] || (cache[key] = type);
return cache[key] || type && (cache[key] = type);
}
function getTypeAtFlowLabel(flow: FlowLabel) {
const antecedentTypes: Type[] = [];
for (const antecedent of flow.antecedents) {
const type = getTypeAtFlowNodeCached(antecedent);
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
@@ -7685,7 +7689,7 @@ namespace ts {
}
}
}
return antecedentTypes.length === 0 ? declaredType :
return antecedentTypes.length === 0 ? undefined :
antecedentTypes.length === 1 ? antecedentTypes[0] :
getUnionType(antecedentTypes);
}

View File

@@ -1519,6 +1519,7 @@ namespace ts {
Unreachable,
Start,
Label,
LoopLabel,
Assignment,
Condition
}

View File

@@ -0,0 +1,40 @@
//// [controlFlowIteration.ts]
let cond: boolean;
function ff() {
let x: string | undefined;
while (true) {
if (cond) {
x = "";
}
else {
if (x) {
x.length;
}
if (x) {
x.length;
}
}
}
}
//// [controlFlowIteration.js]
var cond;
function ff() {
var x;
while (true) {
if (cond) {
x = "";
}
else {
if (x) {
x.length;
}
if (x) {
x.length;
}
}
}
}

View File

@@ -0,0 +1,39 @@
=== tests/cases/conformance/controlFlow/controlFlowIteration.ts ===
let cond: boolean;
>cond : Symbol(cond, Decl(controlFlowIteration.ts, 1, 3))
function ff() {
>ff : Symbol(ff, Decl(controlFlowIteration.ts, 1, 18))
let x: string | undefined;
>x : Symbol(x, Decl(controlFlowIteration.ts, 4, 7))
while (true) {
if (cond) {
>cond : Symbol(cond, Decl(controlFlowIteration.ts, 1, 3))
x = "";
>x : Symbol(x, Decl(controlFlowIteration.ts, 4, 7))
}
else {
if (x) {
>x : Symbol(x, Decl(controlFlowIteration.ts, 4, 7))
x.length;
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowIteration.ts, 4, 7))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
}
if (x) {
>x : Symbol(x, Decl(controlFlowIteration.ts, 4, 7))
x.length;
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowIteration.ts, 4, 7))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
}
}
}
}

View File

@@ -0,0 +1,43 @@
=== tests/cases/conformance/controlFlow/controlFlowIteration.ts ===
let cond: boolean;
>cond : boolean
function ff() {
>ff : () => void
let x: string | undefined;
>x : string | undefined
while (true) {
>true : boolean
if (cond) {
>cond : boolean
x = "";
>x = "" : string
>x : string | undefined
>"" : string
}
else {
if (x) {
>x : string | undefined
x.length;
>x.length : number
>x : string
>length : number
}
if (x) {
>x : string | undefined
x.length;
>x.length : number
>x : string
>length : number
}
}
}
}

View File

@@ -0,0 +1,20 @@
// @strictNullChecks: true
let cond: boolean;
function ff() {
let x: string | undefined;
while (true) {
if (cond) {
x = "";
}
else {
if (x) {
x.length;
}
if (x) {
x.length;
}
}
}
}