From 79db146e32a13185a566f5439cd42bb2c74f7d5c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 4 May 2016 12:44:27 -0700 Subject: [PATCH] Refactor from FlowKind to FlowFlags --- src/compiler/binder.ts | 90 ++++++++++++++++++++--------------------- src/compiler/checker.ts | 47 ++++++++++----------- src/compiler/types.ts | 20 ++++----- 3 files changed, 80 insertions(+), 77 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 10e11e66eb0..9d5b1e39888 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -129,8 +129,8 @@ namespace ts { let Symbol: { new (flags: SymbolFlags, name: string): Symbol }; let classifiableNames: Map; - const unreachableFlow: FlowNode = { kind: FlowKind.Unreachable }; - const reportedUnreachableFlow: FlowNode = { kind: FlowKind.Unreachable }; + const unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable }; + const reportedUnreachableFlow: FlowNode = { flags: FlowFlags.Unreachable }; function bindSourceFile(f: SourceFile, opts: CompilerOptions) { file = f; @@ -471,7 +471,7 @@ namespace ts { savedActiveLabels = activeLabels; hasExplicitReturn = false; - currentFlow = { kind: FlowKind.Start }; + currentFlow = { flags: FlowFlags.Start }; currentBreakTarget = undefined; currentContinueTarget = undefined; activeLabels = undefined; @@ -483,7 +483,7 @@ namespace ts { bindReachableStatement(node); - if (currentFlow.kind !== FlowKind.Unreachable && isFunctionLikeKind(kind) && nodeIsPresent((node).body)) { + if (!(currentFlow.flags & FlowFlags.Unreachable) && isFunctionLikeKind(kind) && nodeIsPresent((node).body)) { flags |= NodeFlags.HasImplicitReturn; if (hasExplicitReturn) { flags |= NodeFlags.HasExplicitReturn; @@ -639,50 +639,50 @@ namespace ts { return false; } - function createFlowLabel(): FlowLabel { + function createBranchLabel(): FlowLabel { return { - kind: FlowKind.Label, + flags: FlowFlags.BranchLabel, antecedents: undefined }; } - function createFlowLoopLabel(): FlowLabel { + function createLoopLabel(): FlowLabel { return { - kind: FlowKind.LoopLabel, + flags: FlowFlags.LoopLabel, antecedents: undefined }; } function addAntecedent(label: FlowLabel, antecedent: FlowNode): void { - if (antecedent.kind !== FlowKind.Unreachable && !contains(label.antecedents, antecedent)) { + if (!(antecedent.flags & FlowFlags.Unreachable) && !contains(label.antecedents, antecedent)) { (label.antecedents || (label.antecedents = [])).push(antecedent); } } - function createFlowCondition(antecedent: FlowNode, expression: Expression, assumeTrue: boolean): FlowNode { - if (antecedent.kind === FlowKind.Unreachable) { + function createFlowCondition(flags: FlowFlags, antecedent: FlowNode, expression: Expression): FlowNode { + if (antecedent.flags & FlowFlags.Unreachable) { return antecedent; } if (!expression) { - return assumeTrue ? antecedent : unreachableFlow; + return flags & FlowFlags.TrueCondition ? antecedent : unreachableFlow; } - if (expression.kind === SyntaxKind.TrueKeyword && !assumeTrue || expression.kind === SyntaxKind.FalseKeyword && assumeTrue) { + if (expression.kind === SyntaxKind.TrueKeyword && flags & FlowFlags.FalseCondition || + expression.kind === SyntaxKind.FalseKeyword && flags & FlowFlags.TrueCondition) { return unreachableFlow; } if (!isNarrowingExpression(expression)) { return antecedent; } return { - kind: FlowKind.Condition, + flags, antecedent, expression, - assumeTrue }; } function createFlowAssignment(antecedent: FlowNode, node: Expression | VariableDeclaration | BindingElement): FlowNode { return { - kind: FlowKind.Assignment, + flags: FlowFlags.Assignment, antecedent, node }; @@ -747,8 +747,8 @@ namespace ts { currentTrueTarget = saveTrueTarget; currentFalseTarget = saveFalseTarget; if (!node || !isLogicalExpression(node)) { - addAntecedent(trueTarget, createFlowCondition(currentFlow, node, /*assumeTrue*/ true)); - addAntecedent(falseTarget, createFlowCondition(currentFlow, node, /*assumeTrue*/ false)); + addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node)); + addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node)); } } @@ -763,9 +763,9 @@ namespace ts { } function bindWhileStatement(node: WhileStatement): void { - const preWhileLabel = createFlowLoopLabel(); - const preBodyLabel = createFlowLabel(); - const postWhileLabel = createFlowLabel(); + const preWhileLabel = createLoopLabel(); + const preBodyLabel = createBranchLabel(); + const postWhileLabel = createBranchLabel(); addAntecedent(preWhileLabel, currentFlow); currentFlow = preWhileLabel; bindCondition(node.expression, preBodyLabel, postWhileLabel); @@ -776,9 +776,9 @@ namespace ts { } function bindDoStatement(node: DoStatement): void { - const preDoLabel = createFlowLoopLabel(); - const preConditionLabel = createFlowLabel(); - const postDoLabel = createFlowLabel(); + const preDoLabel = createLoopLabel(); + const preConditionLabel = createBranchLabel(); + const postDoLabel = createBranchLabel(); addAntecedent(preDoLabel, currentFlow); currentFlow = preDoLabel; bindIterativeStatement(node.statement, postDoLabel, preConditionLabel); @@ -789,9 +789,9 @@ namespace ts { } function bindForStatement(node: ForStatement): void { - const preLoopLabel = createFlowLoopLabel(); - const preBodyLabel = createFlowLabel(); - const postLoopLabel = createFlowLabel(); + const preLoopLabel = createLoopLabel(); + const preBodyLabel = createBranchLabel(); + const postLoopLabel = createBranchLabel(); bind(node.initializer); addAntecedent(preLoopLabel, currentFlow); currentFlow = preLoopLabel; @@ -804,8 +804,8 @@ namespace ts { } function bindForInOrForOfStatement(node: ForInStatement | ForOfStatement): void { - const preLoopLabel = createFlowLoopLabel(); - const postLoopLabel = createFlowLabel(); + const preLoopLabel = createLoopLabel(); + const postLoopLabel = createBranchLabel(); addAntecedent(preLoopLabel, currentFlow); currentFlow = preLoopLabel; bind(node.expression); @@ -820,9 +820,9 @@ namespace ts { } function bindIfStatement(node: IfStatement): void { - const thenLabel = createFlowLabel(); - const elseLabel = createFlowLabel(); - const postIfLabel = createFlowLabel(); + const thenLabel = createBranchLabel(); + const elseLabel = createBranchLabel(); + const postIfLabel = createBranchLabel(); bindCondition(node.expression, thenLabel, elseLabel); currentFlow = finishFlowLabel(thenLabel); bind(node.thenStatement); @@ -875,7 +875,7 @@ namespace ts { } function bindTryStatement(node: TryStatement): void { - const postFinallyLabel = createFlowLabel(); + const postFinallyLabel = createBranchLabel(); const preTryFlow = currentFlow; // TODO: Every statement in try block is potentially an exit point! bind(node.tryBlock); @@ -893,7 +893,7 @@ namespace ts { } function bindSwitchStatement(node: SwitchStatement): void { - const postSwitchLabel = createFlowLabel(); + const postSwitchLabel = createBranchLabel(); bind(node.expression); const saveBreakTarget = currentBreakTarget; const savePreSwitchCaseFlow = preSwitchCaseFlow; @@ -915,17 +915,17 @@ namespace ts { for (let i = 0; i < clauses.length; i++) { const clause = clauses[i]; if (clause.statements.length) { - if (currentFlow.kind === FlowKind.Unreachable) { + if (currentFlow.flags & FlowFlags.Unreachable) { currentFlow = preSwitchCaseFlow; } else { - const preCaseLabel = createFlowLabel(); + const preCaseLabel = createBranchLabel(); addAntecedent(preCaseLabel, preSwitchCaseFlow); addAntecedent(preCaseLabel, currentFlow); currentFlow = finishFlowLabel(preCaseLabel); } bind(clause); - if (currentFlow.kind !== FlowKind.Unreachable && i !== clauses.length - 1 && options.noFallthroughCasesInSwitch) { + if (!(currentFlow.flags & FlowFlags.Unreachable) && i !== clauses.length - 1 && options.noFallthroughCasesInSwitch) { errorOnFirstToken(clause, Diagnostics.Fallthrough_case_in_switch); } } @@ -951,8 +951,8 @@ namespace ts { } function bindLabeledStatement(node: LabeledStatement): void { - const preStatementLabel = createFlowLoopLabel(); - const postStatementLabel = createFlowLabel(); + const preStatementLabel = createLoopLabel(); + const postStatementLabel = createBranchLabel(); bind(node.label); addAntecedent(preStatementLabel, currentFlow); const activeLabel = pushActiveLabel(node.label.text, postStatementLabel, preStatementLabel); @@ -1001,7 +1001,7 @@ namespace ts { } function bindLogicalExpression(node: BinaryExpression, trueTarget: FlowLabel, falseTarget: FlowLabel) { - const preRightLabel = createFlowLabel(); + const preRightLabel = createBranchLabel(); if (node.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken) { bindCondition(node.left, preRightLabel, falseTarget); } @@ -1031,7 +1031,7 @@ namespace ts { const operator = node.operatorToken.kind; if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken) { if (isTopLevelLogicalExpression(node)) { - const postExpressionLabel = createFlowLabel(); + const postExpressionLabel = createBranchLabel(); bindLogicalExpression(node, postExpressionLabel, postExpressionLabel); currentFlow = finishFlowLabel(postExpressionLabel); } @@ -1048,9 +1048,9 @@ namespace ts { } function bindConditionalExpressionFlow(node: ConditionalExpression) { - const trueLabel = createFlowLabel(); - const falseLabel = createFlowLabel(); - const postExpressionLabel = createFlowLabel(); + const trueLabel = createBranchLabel(); + const falseLabel = createBranchLabel(); + const postExpressionLabel = createBranchLabel(); bindCondition(node.condition, trueLabel, falseLabel); currentFlow = finishFlowLabel(trueLabel); bind(node.whenTrue); @@ -2065,7 +2065,7 @@ namespace ts { } function checkUnreachable(node: Node): boolean { - if (currentFlow.kind !== FlowKind.Unreachable) { + if (!(currentFlow.flags & FlowFlags.Unreachable)) { return false; } if (currentFlow === unreachableFlow) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 504cb17def2..c067bffbf66 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7585,27 +7585,28 @@ namespace ts { function getTypeAtFlowNode(flow: FlowNode): Type { while (true) { - switch (flow.kind) { - case FlowKind.Assignment: - const type = getTypeAtFlowAssignment(flow); - if (!type) { - flow = (flow).antecedent; - continue; - } - return type; - case FlowKind.Condition: - return getTypeAtFlowCondition(flow); - case FlowKind.Label: - case FlowKind.LoopLabel: - if ((flow).antecedents.length === 1) { - flow = (flow).antecedents[0]; - continue; - } - return getTypeAtFlowLabel(flow); - case FlowKind.Unreachable: - // Unreachable code errors are reported in the binding phase. Here we - // simply return the declared type to reduce follow-on errors. - return declaredType; + if (flow.flags & FlowFlags.Assignment) { + const type = getTypeAtFlowAssignment(flow); + if (!type) { + flow = (flow).antecedent; + continue; + } + return type; + } + if (flow.flags & FlowFlags.Condition) { + return getTypeAtFlowCondition(flow); + } + if (flow.flags & FlowFlags.Label) { + if ((flow).antecedents.length === 1) { + flow = (flow).antecedents[0]; + continue; + } + return getTypeAtFlowLabel(flow); + } + if (flow.flags & FlowFlags.Unreachable) { + // Unreachable code errors are reported in the binding phase. Here we + // simply return the declared type to reduce follow-on errors. + return declaredType; } // At the top of the flow we have the initial type return initialType; @@ -7644,7 +7645,7 @@ namespace ts { function getTypeAtFlowCondition(flow: FlowCondition) { const type = getTypeAtFlowNode(flow.antecedent); - return type && narrowType(type, flow.expression, flow.assumeTrue); + return type && narrowType(type, flow.expression, (flow.flags & FlowFlags.TrueCondition) !== 0); } function getTypeAtFlowNodeCached(flow: FlowNode) { @@ -7676,7 +7677,7 @@ namespace ts { function getTypeAtFlowLabel(flow: FlowLabel) { const antecedentTypes: Type[] = []; for (const antecedent of flow.antecedents) { - const type = flow.kind === FlowKind.LoopLabel ? + const type = flow.flags & FlowFlags.LoopLabel ? getTypeAtFlowNodeCached(antecedent) : getTypeAtFlowNode(antecedent); if (!type) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d0d411a710e..02f451d1d61 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1515,17 +1515,20 @@ namespace ts { isBracketed: boolean; } - export const enum FlowKind { - Unreachable, - Start, - Label, - LoopLabel, - Assignment, - Condition + export const enum FlowFlags { + Unreachable = 1 << 0, + Start = 1 << 1, + BranchLabel = 1 << 2, + LoopLabel = 1 << 3, + Assignment = 1 << 4, + TrueCondition = 1 << 5, + FalseCondition = 1 << 6, + Label = BranchLabel | LoopLabel, + Condition = TrueCondition | FalseCondition } export interface FlowNode { - kind: FlowKind; // Node kind + flags: FlowFlags; id?: number; // Node id used by flow type cache in checker } @@ -1545,7 +1548,6 @@ namespace ts { // node's location in the control flow. export interface FlowCondition extends FlowNode { expression: Expression; - assumeTrue: boolean; antecedent: FlowNode; }