Skip to content

Commit 746b3b7

Browse files
committed
Use proper flows when compiling switch statements
1 parent 8ae086d commit 746b3b7

File tree

1 file changed

+20
-19
lines changed

1 file changed

+20
-19
lines changed

src/compiler.ts

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2825,15 +2825,16 @@ export class Compiler extends DiagnosticEmitter {
28252825

28262826
// nest blocks in order
28272827
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;
28302830
for (let i = 0; i < numCases; ++i) {
28312831
let case_ = cases[i];
28322832
let statements = case_.statements;
28332833
let numStatements = statements.length;
28342834

2835-
// Each switch case initiates a new branch
2835+
// Can get here by matching the case or by fall-through
28362836
let innerFlow = outerFlow.fork();
2837+
if (fallThroughFlow) innerFlow.inheritBranch(fallThroughFlow);
28372838
this.currentFlow = innerFlow;
28382839
let breakLabel = `break|${context}`;
28392840
innerFlow.breakLabel = breakLabel;
@@ -2843,38 +2844,38 @@ export class Compiler extends DiagnosticEmitter {
28432844
let stmts = new Array<ExpressionRef>(1 + numStatements);
28442845
stmts[0] = currentBlock;
28452846
let count = 1;
2846-
let terminates = false;
2847+
let possiblyFallsThrough = true;
28472848
for (let j = 0; j < numStatements; ++j) {
28482849
let stmt = this.compileStatement(statements[j]);
28492850
if (getExpressionId(stmt) != ExpressionId.Nop) {
28502851
stmts[count++] = stmt;
28512852
}
28522853
if (innerFlow.isAny(FlowFlags.Terminates | FlowFlags.Breaks)) {
2853-
if (innerFlow.is(FlowFlags.Terminates)) terminates = true;
2854+
possiblyFallsThrough = false;
28542855
break;
28552856
}
28562857
}
28572858
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;
28602865
}
2861-
2862-
commonConditional |= innerFlow.deriveConditionalFlags();
2863-
2864-
// Switch back to the parent flow
2865-
innerFlow.unset(
2866-
FlowFlags.Breaks |
2867-
FlowFlags.ConditionallyBreaks
2868-
);
28692866
this.currentFlow = outerFlow;
28702867
currentBlock = module.block(nextLabel, stmts, TypeRef.None); // must be a labeled block
28712868
}
28722869
outerFlow.popBreakLabel();
28732870

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+
}
28782879
return currentBlock;
28792880
}
28802881

0 commit comments

Comments
 (0)