@@ -2825,15 +2825,16 @@ export class Compiler extends DiagnosticEmitter {
2825
2825
2826
2826
// nest blocks in order
2827
2827
let currentBlock = module . block ( `case0|${ context } ` , breaks , TypeRef . None ) ;
2828
- let commonCategorical = FlowFlags . AnyCategorical ;
2829
- let commonConditional = 0 ;
2828
+ let fallThroughFlow : Flow | null = null ;
2829
+ let commonBreakingFlow : Flow | null = null ;
2830
2830
for ( let i = 0 ; i < numCases ; ++ i ) {
2831
2831
let case_ = cases [ i ] ;
2832
2832
let statements = case_ . statements ;
2833
2833
let numStatements = statements . length ;
2834
2834
2835
- // Each switch case initiates a new branch
2835
+ // Can get here by matching the case or by fall-through
2836
2836
let innerFlow = outerFlow . fork ( ) ;
2837
+ if ( fallThroughFlow ) innerFlow . inheritBranch ( fallThroughFlow ) ;
2837
2838
this . currentFlow = innerFlow ;
2838
2839
let breakLabel = `break|${ context } ` ;
2839
2840
innerFlow . breakLabel = breakLabel ;
@@ -2843,38 +2844,38 @@ export class Compiler extends DiagnosticEmitter {
2843
2844
let stmts = new Array < ExpressionRef > ( 1 + numStatements ) ;
2844
2845
stmts [ 0 ] = currentBlock ;
2845
2846
let count = 1 ;
2846
- let terminates = false ;
2847
+ let possiblyFallsThrough = true ;
2847
2848
for ( let j = 0 ; j < numStatements ; ++ j ) {
2848
2849
let stmt = this . compileStatement ( statements [ j ] ) ;
2849
2850
if ( getExpressionId ( stmt ) != ExpressionId . Nop ) {
2850
2851
stmts [ count ++ ] = stmt ;
2851
2852
}
2852
2853
if ( innerFlow . isAny ( FlowFlags . Terminates | FlowFlags . Breaks ) ) {
2853
- if ( innerFlow . is ( FlowFlags . Terminates ) ) terminates = true ;
2854
+ possiblyFallsThrough = false ;
2854
2855
break ;
2855
2856
}
2856
2857
}
2857
2858
stmts . length = count ;
2858
- if ( terminates || isLast || innerFlow . isAny ( FlowFlags . Breaks | FlowFlags . ConditionallyBreaks ) ) {
2859
- commonCategorical &= innerFlow . flags ;
2859
+ fallThroughFlow = possiblyFallsThrough ? innerFlow : null ;
2860
+ let possiblyBreaks = innerFlow . isAny ( FlowFlags . Breaks | FlowFlags . ConditionallyBreaks ) ;
2861
+ innerFlow . unset ( FlowFlags . Breaks | FlowFlags . ConditionallyBreaks ) ; // clear
2862
+ if ( possiblyBreaks || ( isLast && possiblyFallsThrough ) ) {
2863
+ if ( commonBreakingFlow ) commonBreakingFlow . inheritBranch ( innerFlow ) ;
2864
+ else commonBreakingFlow = innerFlow ;
2860
2865
}
2861
-
2862
- commonConditional |= innerFlow . deriveConditionalFlags ( ) ;
2863
-
2864
- // Switch back to the parent flow
2865
- innerFlow . unset (
2866
- FlowFlags . Breaks |
2867
- FlowFlags . ConditionallyBreaks
2868
- ) ;
2869
2866
this . currentFlow = outerFlow ;
2870
2867
currentBlock = module . block ( nextLabel , stmts , TypeRef . None ) ; // must be a labeled block
2871
2868
}
2872
2869
outerFlow . popBreakLabel ( ) ;
2873
2870
2874
- // If the switch has a default (guaranteed to handle any value), propagate common flags
2875
- if ( defaultIndex >= 0 ) outerFlow . flags |= commonCategorical & ~ FlowFlags . Breaks ;
2876
- outerFlow . flags |= commonConditional & ~ FlowFlags . ConditionallyBreaks ;
2877
- // TODO: what about local states?
2871
+ // If the switch has a default, we only get past through a breaking flow
2872
+ if ( defaultIndex >= 0 ) {
2873
+ if ( commonBreakingFlow ) outerFlow . inherit ( commonBreakingFlow ) ;
2874
+ else outerFlow . set ( FlowFlags . Terminates ) ;
2875
+ // Otherwise either skipping or any breaking flow can get past
2876
+ } else if ( commonBreakingFlow ) {
2877
+ outerFlow . inheritBranch ( commonBreakingFlow ) ;
2878
+ }
2878
2879
return currentBlock ;
2879
2880
}
2880
2881
0 commit comments