@@ -246,6 +246,10 @@ export class Flow {
246246 thisFieldFlags : Map < Property , FieldFlags > | null = null ;
247247 /** The label we break to when encountering a return statement, when inlining. */
248248 inlineReturnLabel : string | null = null ;
249+ /** Alternative flows if a compound expression is true-ish. */
250+ trueFlows : Map < ExpressionRef , Flow > | null = null ;
251+ /** Alternative flows if a compound expression is false-ish. */
252+ falseFlows : Map < ExpressionRef , Flow > | null = null ;
249253
250254 /** Tests if this is an inline flow. */
251255 get isInline ( ) : bool {
@@ -308,21 +312,31 @@ export class Flow {
308312 }
309313
310314 /** Forks this flow to a child flow. */
311- fork ( resetBreakContext : bool = false ) : Flow {
315+ fork (
316+ /** Whether a new break context is established, e.g. by a block. */
317+ newBreakContext : bool = false ,
318+ /** Whether a new continue context is established, e.g. by a loop. */
319+ newContinueContext : bool = newBreakContext
320+ ) : Flow {
312321 let branch = new Flow ( this . targetFunction , this . inlineFunction ) ;
313322 branch . parent = this ;
323+ branch . flags = this . flags ;
314324 branch . outer = this . outer ;
315- if ( resetBreakContext ) {
316- branch . flags = this . flags & ~ (
325+ if ( newBreakContext ) {
326+ branch . flags &= ~ (
317327 FlowFlags . Breaks |
318- FlowFlags . ConditionallyBreaks |
328+ FlowFlags . ConditionallyBreaks
329+ ) ;
330+ } else {
331+ branch . breakLabel = this . breakLabel ;
332+ }
333+ if ( newContinueContext ) {
334+ branch . flags &= ~ (
319335 FlowFlags . Continues |
320336 FlowFlags . ConditionallyContinues
321337 ) ;
322338 } else {
323- branch . flags = this . flags ;
324339 branch . continueLabel = this . continueLabel ;
325- branch . breakLabel = this . breakLabel ;
326340 }
327341 branch . localFlags = this . localFlags . slice ( ) ;
328342 if ( this . sourceFunction . is ( CommonFlags . Constructor ) ) {
@@ -335,6 +349,52 @@ export class Flow {
335349 return branch ;
336350 }
337351
352+ /** Forks this flow to a child flow where `condExpr` is true-ish. */
353+ forkThen (
354+ /** Condition that turned out to be true. */
355+ condExpr : ExpressionRef ,
356+ /** Whether a new break context is established, e.g. by a block. */
357+ newBreakContext : bool = false ,
358+ /** Whether a new continue context is established, e.g. by a loop. */
359+ newContinueContext : bool = newBreakContext
360+ ) : Flow {
361+ let flow = this . fork ( newBreakContext , newContinueContext ) ;
362+ let trueFlows = this . trueFlows ;
363+ if ( trueFlows && trueFlows . has ( condExpr ) ) {
364+ flow . inherit ( changetype < Flow > ( trueFlows . get ( condExpr ) ) ) ;
365+ }
366+ flow . inheritNonnullIfTrue ( condExpr ) ;
367+ return flow ;
368+ }
369+
370+ /** Remembers the alternative flow if `condExpr` turns out `true`. */
371+ noteThen ( condExpr : ExpressionRef , trueFlow : Flow ) : void {
372+ let trueFlows = this . trueFlows ;
373+ if ( ! trueFlows ) this . trueFlows = trueFlows = new Map ( ) ;
374+ trueFlows . set ( condExpr , trueFlow ) ;
375+ }
376+
377+ /** Forks this flow to a child flow where `condExpr` is false-ish. */
378+ forkElse (
379+ /** Condition that turned out to be false. */
380+ condExpr : ExpressionRef
381+ ) : Flow {
382+ let flow = this . fork ( ) ;
383+ let falseFlows = this . falseFlows ;
384+ if ( falseFlows && falseFlows . has ( condExpr ) ) {
385+ flow . inherit ( changetype < Flow > ( falseFlows . get ( condExpr ) ) ) ;
386+ }
387+ flow . inheritNonnullIfFalse ( condExpr ) ;
388+ return flow ;
389+ }
390+
391+ /** Remembers the alternative flow if `condExpr` turns out `false`. */
392+ noteElse ( condExpr : ExpressionRef , falseFlow : Flow ) : void {
393+ let falseFlows = this . falseFlows ;
394+ if ( ! falseFlows ) this . falseFlows = falseFlows = new Map ( ) ;
395+ falseFlows . set ( condExpr , falseFlow ) ;
396+ }
397+
338398 /** Gets a free temporary local of the specified type. */
339399 getTempLocal ( type : Type ) : Local {
340400 let local = this . targetFunction . addLocal ( type ) ;
@@ -523,36 +583,27 @@ export class Flow {
523583 }
524584 }
525585
526- /** Pushes a new break label to the stack , for example when entering a loop that one can `break` from. */
527- pushBreakLabel ( ) : string {
586+ /** Pushes a new control flow label , for example when entering a loop that one can `break` from. */
587+ pushControlFlowLabel ( ) : i32 {
528588 let targetFunction = this . targetFunction ;
529589 let id = targetFunction . nextBreakId ++ ;
530590 let stack = targetFunction . breakStack ;
531591 if ( ! stack ) targetFunction . breakStack = [ id ] ;
532592 else stack . push ( id ) ;
533- let label = id . toString ( ) ;
534- targetFunction . breakLabel = label ;
535- return label ;
593+ return id ;
536594 }
537595
538- /** Pops the most recent break label from the stack . */
539- popBreakLabel ( ) : void {
596+ /** Pops the most recent control flow label and validates that it matches . */
597+ popControlFlowLabel ( expectedLabel : i32 ) : void {
540598 let targetFunction = this . targetFunction ;
541- let stack = assert ( targetFunction . breakStack ) ;
542- let length = assert ( stack . length ) ;
543- stack . pop ( ) ;
544- if ( length > 1 ) {
545- targetFunction . breakLabel = stack [ length - 2 ] . toString ( ) ;
546- } else {
547- targetFunction . breakLabel = null ;
548- targetFunction . breakStack = null ;
549- }
599+ let stack = assert ( targetFunction . breakStack ) ; // should exist
600+ assert ( stack . length ) ; // should not be empty
601+ assert ( stack . pop ( ) == expectedLabel ) ; // should match
550602 }
551603
552604 /** Inherits flags of another flow into this one, i.e. a finished inner block. */
553605 inherit ( other : Flow ) : void {
554606 assert ( other . targetFunction == this . targetFunction ) ;
555- assert ( other . parent == this ) ; // currently the case, but might change
556607 let otherFlags = other . flags ;
557608
558609 // respective inner flags are irrelevant if contexts differ
@@ -571,18 +622,10 @@ export class Flow {
571622 this . thisFieldFlags = other . thisFieldFlags ;
572623 }
573624
574- /** Inherits flags of a conditional branch joining again with this one, i.e. then without else. */
575- inheritBranch ( other : Flow , conditionKind : ConditionKind = ConditionKind . Unknown ) : void {
576- assert ( other . targetFunction == this . targetFunction ) ;
577- switch ( conditionKind ) {
578- case ConditionKind . True : this . inherit ( other ) ; // always executes
579- case ConditionKind . False : return ; // never executes
580- }
581625
582- // Note that flags in `this` flow have already happened. For instance,
583- // a return cannot be undone no matter what'd happen in subsequent branches,
584- // but an allocation, which doesn't terminate, can become conditional. Not
585- // all flags have a corresponding conditional flag that's tracked.
626+ /** Merges only the side effects of a branch, i.e. when not taken. */
627+ mergeSideEffects ( other : Flow ) : void {
628+ assert ( other . targetFunction == this . targetFunction ) ;
586629
587630 let thisFlags = this . flags ;
588631 let otherFlags = other . flags ;
@@ -653,8 +696,13 @@ export class Flow {
653696 }
654697
655698 this . flags = newFlags | ( thisFlags & ( FlowFlags . UncheckedContext | FlowFlags . CtorParamContext ) ) ;
699+ }
656700
657- // local flags
701+ /** Merges a branch joining again with this flow, i.e. then without else. */
702+ mergeBranch ( other : Flow ) : void {
703+ this . mergeSideEffects ( other ) ;
704+
705+ // Local flags matter if the branch does not terminate
658706 let thisLocalFlags = this . localFlags ;
659707 let numThisLocalFlags = thisLocalFlags . length ;
660708 let otherLocalFlags = other . localFlags ;
@@ -675,12 +723,12 @@ export class Flow {
675723 // only be set if it has been observed prior to entering the branch.
676724 }
677725
678- /** Inherits mutual flags of two alternate branches becoming this one , i.e. then with else. */
679- inheritMutual ( left : Flow , right : Flow ) : void {
726+ /** Inherits two alternate branches to become this flow , i.e. then with else. */
727+ inheritAlternatives ( left : Flow , right : Flow ) : void {
680728 assert ( left . targetFunction == right . targetFunction ) ;
681729 assert ( left . targetFunction == this . targetFunction ) ;
682- // This differs from the previous method in that no flags are guaranteed
683- // to happen unless it is the case in both flows .
730+ // Differs from `mergeBranch` in that the alternatives are intersected to
731+ // then become this branch .
684732
685733 let leftFlags = left . flags ;
686734 let rightFlags = right . flags ;
@@ -881,7 +929,7 @@ export class Flow {
881929 }
882930
883931 /** Updates local states to reflect that this branch is only taken when `expr` is true-ish. */
884- inheritNonnullIfTrue (
932+ private inheritNonnullIfTrue (
885933 /** Expression being true. */
886934 expr : ExpressionRef ,
887935 /** If specified, only set the flag if also nonnull in this flow. */
@@ -995,7 +1043,7 @@ export class Flow {
9951043 }
9961044
9971045 /** Updates local states to reflect that this branch is only taken when `expr` is false-ish. */
998- inheritNonnullIfFalse (
1046+ private inheritNonnullIfFalse (
9991047 /** Expression being false. */
10001048 expr : ExpressionRef ,
10011049 /** If specified, only set the flag if also nonnull in this flow. */
0 commit comments