From b88269bace621082bf7588c4002ccd330aea692b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 25 Apr 2016 11:23:56 -0700 Subject: [PATCH] Cache control flow types only at loop-back junctions --- src/compiler/binder.ts | 17 ++++++++++++----- src/compiler/checker.ts | 12 ++++++++---- src/compiler/types.ts | 1 + 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 3d6db01580a..5ad15f4d5c4 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -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); diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8491ef7de92..992fc466dd7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7592,6 +7592,7 @@ namespace ts { case FlowKind.Condition: return getTypeAtFlowCondition(flow); case FlowKind.Label: + case FlowKind.LoopLabel: if ((flow).antecedents.length === 1) { flow = (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); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 25329b7ee2b..e1d7f00ff99 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1519,6 +1519,7 @@ namespace ts { Unreachable, Start, Label, + LoopLabel, Assignment, Condition }