@@ -129,15 +129,16 @@ namespace ts {
129129 let Symbol : { new ( flags : SymbolFlags , name : string ) : Symbol } ;
130130 let classifiableNames : Map < string > ;
131131
132- const unreachableFlow : FlowNode = { kind : FlowKind . Unreachable } ;
133- const reportedUnreachableFlow : FlowNode = { kind : FlowKind . Unreachable } ;
132+ const unreachableFlow : FlowNode = { flags : FlowFlags . Unreachable } ;
133+ const reportedUnreachableFlow : FlowNode = { flags : FlowFlags . Unreachable } ;
134134
135135 function bindSourceFile ( f : SourceFile , opts : CompilerOptions ) {
136136 file = f ;
137137 options = opts ;
138138 languageVersion = getEmitScriptTarget ( options ) ;
139139 inStrictMode = ! ! file . externalModuleIndicator ;
140140 classifiableNames = { } ;
141+ symbolCount = 0 ;
141142
142143 Symbol = objectAllocator . getSymbolConstructor ( ) ;
143144
@@ -470,7 +471,7 @@ namespace ts {
470471 savedActiveLabels = activeLabels ;
471472
472473 hasExplicitReturn = false ;
473- currentFlow = { kind : FlowKind . Start } ;
474+ currentFlow = { flags : FlowFlags . Start } ;
474475 currentBreakTarget = undefined ;
475476 currentContinueTarget = undefined ;
476477 activeLabels = undefined ;
@@ -482,7 +483,7 @@ namespace ts {
482483
483484 bindReachableStatement ( node ) ;
484485
485- if ( currentFlow . kind !== FlowKind . Unreachable && isFunctionLikeKind ( kind ) && nodeIsPresent ( ( < FunctionLikeDeclaration > node ) . body ) ) {
486+ if ( ! ( currentFlow . flags & FlowFlags . Unreachable ) && isFunctionLikeKind ( kind ) && nodeIsPresent ( ( < FunctionLikeDeclaration > node ) . body ) ) {
486487 flags |= NodeFlags . HasImplicitReturn ;
487488 if ( hasExplicitReturn ) {
488489 flags |= NodeFlags . HasExplicitReturn ;
@@ -638,55 +639,80 @@ namespace ts {
638639 return false ;
639640 }
640641
641- function createFlowLabel ( ) : FlowLabel {
642+ function createBranchLabel ( ) : FlowLabel {
642643 return {
643- kind : FlowKind . Label ,
644+ flags : FlowFlags . BranchLabel ,
644645 antecedents : undefined
645646 } ;
646647 }
647648
648- function createFlowLoopLabel ( ) : FlowLabel {
649+ function createLoopLabel ( ) : FlowLabel {
649650 return {
650- kind : FlowKind . LoopLabel ,
651+ flags : FlowFlags . LoopLabel ,
651652 antecedents : undefined
652653 } ;
653654 }
654655
656+ function setFlowNodeReferenced ( flow : FlowNode ) {
657+ // On first reference we set the Referenced flag, thereafter we set the Shared flag
658+ flow . flags |= flow . flags & FlowFlags . Referenced ? FlowFlags . Shared : FlowFlags . Referenced ;
659+ }
660+
655661 function addAntecedent ( label : FlowLabel , antecedent : FlowNode ) : void {
656- if ( antecedent . kind !== FlowKind . Unreachable && ! contains ( label . antecedents , antecedent ) ) {
662+ if ( ! ( antecedent . flags & FlowFlags . Unreachable ) && ! contains ( label . antecedents , antecedent ) ) {
657663 ( label . antecedents || ( label . antecedents = [ ] ) ) . push ( antecedent ) ;
664+ setFlowNodeReferenced ( antecedent ) ;
658665 }
659666 }
660667
661- function createFlowCondition ( antecedent : FlowNode , expression : Expression , assumeTrue : boolean ) : FlowNode {
662- if ( antecedent . kind === FlowKind . Unreachable ) {
668+ function createFlowCondition ( flags : FlowFlags , antecedent : FlowNode , expression : Expression ) : FlowNode {
669+ if ( antecedent . flags & FlowFlags . Unreachable ) {
663670 return antecedent ;
664671 }
665672 if ( ! expression ) {
666- return assumeTrue ? antecedent : unreachableFlow ;
673+ return flags & FlowFlags . TrueCondition ? antecedent : unreachableFlow ;
667674 }
668- if ( expression . kind === SyntaxKind . TrueKeyword && ! assumeTrue || expression . kind === SyntaxKind . FalseKeyword && assumeTrue ) {
675+ if ( expression . kind === SyntaxKind . TrueKeyword && flags & FlowFlags . FalseCondition ||
676+ expression . kind === SyntaxKind . FalseKeyword && flags & FlowFlags . TrueCondition ) {
669677 return unreachableFlow ;
670678 }
671679 if ( ! isNarrowingExpression ( expression ) ) {
672680 return antecedent ;
673681 }
682+ setFlowNodeReferenced ( antecedent ) ;
674683 return < FlowCondition > {
675- kind : FlowKind . Condition ,
684+ flags ,
676685 antecedent,
677686 expression,
678- assumeTrue
679687 } ;
680688 }
681689
682690 function createFlowAssignment ( antecedent : FlowNode , node : Expression | VariableDeclaration | BindingElement ) : FlowNode {
691+ setFlowNodeReferenced ( antecedent ) ;
683692 return < FlowAssignment > {
684- kind : FlowKind . Assignment ,
693+ flags : FlowFlags . Assignment ,
685694 antecedent,
686695 node
687696 } ;
688697 }
689698
699+ function skipSimpleConditionalFlow ( flow : FlowNode ) {
700+ // We skip over simple conditional flows of the form 'x ? aaa : bbb', where 'aaa' and 'bbb' contain
701+ // no constructs that affect control flow type analysis. Such simple flows have no effect on the
702+ // code paths that follow and ignoring them means we'll do less work.
703+ if ( flow . flags & FlowFlags . BranchLabel && ( < FlowLabel > flow ) . antecedents . length === 2 ) {
704+ const a = ( < FlowLabel > flow ) . antecedents [ 0 ] ;
705+ const b = ( < FlowLabel > flow ) . antecedents [ 1 ] ;
706+ if ( ( a . flags & FlowFlags . TrueCondition && b . flags & FlowFlags . FalseCondition ||
707+ a . flags & FlowFlags . FalseCondition && b . flags & FlowFlags . TrueCondition ) &&
708+ ( < FlowCondition > a ) . antecedent === ( < FlowCondition > b ) . antecedent &&
709+ ( < FlowCondition > a ) . expression === ( < FlowCondition > b ) . expression ) {
710+ return ( < FlowCondition > a ) . antecedent ;
711+ }
712+ }
713+ return flow ;
714+ }
715+
690716 function finishFlowLabel ( flow : FlowLabel ) : FlowNode {
691717 const antecedents = flow . antecedents ;
692718 if ( ! antecedents ) {
@@ -695,7 +721,7 @@ namespace ts {
695721 if ( antecedents . length === 1 ) {
696722 return antecedents [ 0 ] ;
697723 }
698- return flow ;
724+ return skipSimpleConditionalFlow ( flow ) ;
699725 }
700726
701727 function isStatementCondition ( node : Node ) {
@@ -746,8 +772,8 @@ namespace ts {
746772 currentTrueTarget = saveTrueTarget ;
747773 currentFalseTarget = saveFalseTarget ;
748774 if ( ! node || ! isLogicalExpression ( node ) ) {
749- addAntecedent ( trueTarget , createFlowCondition ( currentFlow , node , /*assumeTrue*/ true ) ) ;
750- addAntecedent ( falseTarget , createFlowCondition ( currentFlow , node , /*assumeTrue*/ false ) ) ;
775+ addAntecedent ( trueTarget , createFlowCondition ( FlowFlags . TrueCondition , currentFlow , node ) ) ;
776+ addAntecedent ( falseTarget , createFlowCondition ( FlowFlags . FalseCondition , currentFlow , node ) ) ;
751777 }
752778 }
753779
@@ -762,9 +788,9 @@ namespace ts {
762788 }
763789
764790 function bindWhileStatement ( node : WhileStatement ) : void {
765- const preWhileLabel = createFlowLoopLabel ( ) ;
766- const preBodyLabel = createFlowLabel ( ) ;
767- const postWhileLabel = createFlowLabel ( ) ;
791+ const preWhileLabel = createLoopLabel ( ) ;
792+ const preBodyLabel = createBranchLabel ( ) ;
793+ const postWhileLabel = createBranchLabel ( ) ;
768794 addAntecedent ( preWhileLabel , currentFlow ) ;
769795 currentFlow = preWhileLabel ;
770796 bindCondition ( node . expression , preBodyLabel , postWhileLabel ) ;
@@ -775,9 +801,9 @@ namespace ts {
775801 }
776802
777803 function bindDoStatement ( node : DoStatement ) : void {
778- const preDoLabel = createFlowLoopLabel ( ) ;
779- const preConditionLabel = createFlowLabel ( ) ;
780- const postDoLabel = createFlowLabel ( ) ;
804+ const preDoLabel = createLoopLabel ( ) ;
805+ const preConditionLabel = createBranchLabel ( ) ;
806+ const postDoLabel = createBranchLabel ( ) ;
781807 addAntecedent ( preDoLabel , currentFlow ) ;
782808 currentFlow = preDoLabel ;
783809 bindIterativeStatement ( node . statement , postDoLabel , preConditionLabel ) ;
@@ -788,9 +814,9 @@ namespace ts {
788814 }
789815
790816 function bindForStatement ( node : ForStatement ) : void {
791- const preLoopLabel = createFlowLoopLabel ( ) ;
792- const preBodyLabel = createFlowLabel ( ) ;
793- const postLoopLabel = createFlowLabel ( ) ;
817+ const preLoopLabel = createLoopLabel ( ) ;
818+ const preBodyLabel = createBranchLabel ( ) ;
819+ const postLoopLabel = createBranchLabel ( ) ;
794820 bind ( node . initializer ) ;
795821 addAntecedent ( preLoopLabel , currentFlow ) ;
796822 currentFlow = preLoopLabel ;
@@ -803,8 +829,8 @@ namespace ts {
803829 }
804830
805831 function bindForInOrForOfStatement ( node : ForInStatement | ForOfStatement ) : void {
806- const preLoopLabel = createFlowLoopLabel ( ) ;
807- const postLoopLabel = createFlowLabel ( ) ;
832+ const preLoopLabel = createLoopLabel ( ) ;
833+ const postLoopLabel = createBranchLabel ( ) ;
808834 addAntecedent ( preLoopLabel , currentFlow ) ;
809835 currentFlow = preLoopLabel ;
810836 bind ( node . expression ) ;
@@ -819,9 +845,9 @@ namespace ts {
819845 }
820846
821847 function bindIfStatement ( node : IfStatement ) : void {
822- const thenLabel = createFlowLabel ( ) ;
823- const elseLabel = createFlowLabel ( ) ;
824- const postIfLabel = createFlowLabel ( ) ;
848+ const thenLabel = createBranchLabel ( ) ;
849+ const elseLabel = createBranchLabel ( ) ;
850+ const postIfLabel = createBranchLabel ( ) ;
825851 bindCondition ( node . expression , thenLabel , elseLabel ) ;
826852 currentFlow = finishFlowLabel ( thenLabel ) ;
827853 bind ( node . thenStatement ) ;
@@ -874,7 +900,7 @@ namespace ts {
874900 }
875901
876902 function bindTryStatement ( node : TryStatement ) : void {
877- const postFinallyLabel = createFlowLabel ( ) ;
903+ const postFinallyLabel = createBranchLabel ( ) ;
878904 const preTryFlow = currentFlow ;
879905 // TODO: Every statement in try block is potentially an exit point!
880906 bind ( node . tryBlock ) ;
@@ -892,7 +918,7 @@ namespace ts {
892918 }
893919
894920 function bindSwitchStatement ( node : SwitchStatement ) : void {
895- const postSwitchLabel = createFlowLabel ( ) ;
921+ const postSwitchLabel = createBranchLabel ( ) ;
896922 bind ( node . expression ) ;
897923 const saveBreakTarget = currentBreakTarget ;
898924 const savePreSwitchCaseFlow = preSwitchCaseFlow ;
@@ -914,17 +940,17 @@ namespace ts {
914940 for ( let i = 0 ; i < clauses . length ; i ++ ) {
915941 const clause = clauses [ i ] ;
916942 if ( clause . statements . length ) {
917- if ( currentFlow . kind === FlowKind . Unreachable ) {
943+ if ( currentFlow . flags & FlowFlags . Unreachable ) {
918944 currentFlow = preSwitchCaseFlow ;
919945 }
920946 else {
921- const preCaseLabel = createFlowLabel ( ) ;
947+ const preCaseLabel = createBranchLabel ( ) ;
922948 addAntecedent ( preCaseLabel , preSwitchCaseFlow ) ;
923949 addAntecedent ( preCaseLabel , currentFlow ) ;
924950 currentFlow = finishFlowLabel ( preCaseLabel ) ;
925951 }
926952 bind ( clause ) ;
927- if ( currentFlow . kind !== FlowKind . Unreachable && i !== clauses . length - 1 && options . noFallthroughCasesInSwitch ) {
953+ if ( ! ( currentFlow . flags & FlowFlags . Unreachable ) && i !== clauses . length - 1 && options . noFallthroughCasesInSwitch ) {
928954 errorOnFirstToken ( clause , Diagnostics . Fallthrough_case_in_switch ) ;
929955 }
930956 }
@@ -950,8 +976,8 @@ namespace ts {
950976 }
951977
952978 function bindLabeledStatement ( node : LabeledStatement ) : void {
953- const preStatementLabel = createFlowLoopLabel ( ) ;
954- const postStatementLabel = createFlowLabel ( ) ;
979+ const preStatementLabel = createLoopLabel ( ) ;
980+ const postStatementLabel = createBranchLabel ( ) ;
955981 bind ( node . label ) ;
956982 addAntecedent ( preStatementLabel , currentFlow ) ;
957983 const activeLabel = pushActiveLabel ( node . label . text , postStatementLabel , preStatementLabel ) ;
@@ -1000,7 +1026,7 @@ namespace ts {
10001026 }
10011027
10021028 function bindLogicalExpression ( node : BinaryExpression , trueTarget : FlowLabel , falseTarget : FlowLabel ) {
1003- const preRightLabel = createFlowLabel ( ) ;
1029+ const preRightLabel = createBranchLabel ( ) ;
10041030 if ( node . operatorToken . kind === SyntaxKind . AmpersandAmpersandToken ) {
10051031 bindCondition ( node . left , preRightLabel , falseTarget ) ;
10061032 }
@@ -1030,7 +1056,7 @@ namespace ts {
10301056 const operator = node . operatorToken . kind ;
10311057 if ( operator === SyntaxKind . AmpersandAmpersandToken || operator === SyntaxKind . BarBarToken ) {
10321058 if ( isTopLevelLogicalExpression ( node ) ) {
1033- const postExpressionLabel = createFlowLabel ( ) ;
1059+ const postExpressionLabel = createBranchLabel ( ) ;
10341060 bindLogicalExpression ( node , postExpressionLabel , postExpressionLabel ) ;
10351061 currentFlow = finishFlowLabel ( postExpressionLabel ) ;
10361062 }
@@ -1047,9 +1073,9 @@ namespace ts {
10471073 }
10481074
10491075 function bindConditionalExpressionFlow ( node : ConditionalExpression ) {
1050- const trueLabel = createFlowLabel ( ) ;
1051- const falseLabel = createFlowLabel ( ) ;
1052- const postExpressionLabel = createFlowLabel ( ) ;
1076+ const trueLabel = createBranchLabel ( ) ;
1077+ const falseLabel = createBranchLabel ( ) ;
1078+ const postExpressionLabel = createBranchLabel ( ) ;
10531079 bindCondition ( node . condition , trueLabel , falseLabel ) ;
10541080 currentFlow = finishFlowLabel ( trueLabel ) ;
10551081 bind ( node . whenTrue ) ;
@@ -2064,7 +2090,7 @@ namespace ts {
20642090 }
20652091
20662092 function checkUnreachable ( node : Node ) : boolean {
2067- if ( currentFlow . kind !== FlowKind . Unreachable ) {
2093+ if ( ! ( currentFlow . flags & FlowFlags . Unreachable ) ) {
20682094 return false ;
20692095 }
20702096 if ( currentFlow === unreachableFlow ) {
0 commit comments