@@ -246,6 +246,10 @@ export class Flow {
246
246
thisFieldFlags : Map < Property , FieldFlags > | null = null ;
247
247
/** The label we break to when encountering a return statement, when inlining. */
248
248
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 ;
249
253
250
254
/** Tests if this is an inline flow. */
251
255
get isInline ( ) : bool {
@@ -308,21 +312,31 @@ export class Flow {
308
312
}
309
313
310
314
/** 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 {
312
321
let branch = new Flow ( this . targetFunction , this . inlineFunction ) ;
313
322
branch . parent = this ;
323
+ branch . flags = this . flags ;
314
324
branch . outer = this . outer ;
315
- if ( resetBreakContext ) {
316
- branch . flags = this . flags & ~ (
325
+ if ( newBreakContext ) {
326
+ branch . flags &= ~ (
317
327
FlowFlags . Breaks |
318
- FlowFlags . ConditionallyBreaks |
328
+ FlowFlags . ConditionallyBreaks
329
+ ) ;
330
+ } else {
331
+ branch . breakLabel = this . breakLabel ;
332
+ }
333
+ if ( newContinueContext ) {
334
+ branch . flags &= ~ (
319
335
FlowFlags . Continues |
320
336
FlowFlags . ConditionallyContinues
321
337
) ;
322
338
} else {
323
- branch . flags = this . flags ;
324
339
branch . continueLabel = this . continueLabel ;
325
- branch . breakLabel = this . breakLabel ;
326
340
}
327
341
branch . localFlags = this . localFlags . slice ( ) ;
328
342
if ( this . sourceFunction . is ( CommonFlags . Constructor ) ) {
@@ -335,6 +349,52 @@ export class Flow {
335
349
return branch ;
336
350
}
337
351
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
+
338
398
/** Gets a free temporary local of the specified type. */
339
399
getTempLocal ( type : Type ) : Local {
340
400
let local = this . targetFunction . addLocal ( type ) ;
@@ -523,36 +583,27 @@ export class Flow {
523
583
}
524
584
}
525
585
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 {
528
588
let targetFunction = this . targetFunction ;
529
589
let id = targetFunction . nextBreakId ++ ;
530
590
let stack = targetFunction . breakStack ;
531
591
if ( ! stack ) targetFunction . breakStack = [ id ] ;
532
592
else stack . push ( id ) ;
533
- let label = id . toString ( ) ;
534
- targetFunction . breakLabel = label ;
535
- return label ;
593
+ return id ;
536
594
}
537
595
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 {
540
598
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
550
602
}
551
603
552
604
/** Inherits flags of another flow into this one, i.e. a finished inner block. */
553
605
inherit ( other : Flow ) : void {
554
606
assert ( other . targetFunction == this . targetFunction ) ;
555
- assert ( other . parent == this ) ; // currently the case, but might change
556
607
let otherFlags = other . flags ;
557
608
558
609
// respective inner flags are irrelevant if contexts differ
@@ -571,18 +622,10 @@ export class Flow {
571
622
this . thisFieldFlags = other . thisFieldFlags ;
572
623
}
573
624
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
- }
581
625
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 ) ;
586
629
587
630
let thisFlags = this . flags ;
588
631
let otherFlags = other . flags ;
@@ -653,8 +696,13 @@ export class Flow {
653
696
}
654
697
655
698
this . flags = newFlags | ( thisFlags & ( FlowFlags . UncheckedContext | FlowFlags . CtorParamContext ) ) ;
699
+ }
656
700
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
658
706
let thisLocalFlags = this . localFlags ;
659
707
let numThisLocalFlags = thisLocalFlags . length ;
660
708
let otherLocalFlags = other . localFlags ;
@@ -675,12 +723,12 @@ export class Flow {
675
723
// only be set if it has been observed prior to entering the branch.
676
724
}
677
725
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 {
680
728
assert ( left . targetFunction == right . targetFunction ) ;
681
729
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 .
684
732
685
733
let leftFlags = left . flags ;
686
734
let rightFlags = right . flags ;
@@ -881,7 +929,7 @@ export class Flow {
881
929
}
882
930
883
931
/** Updates local states to reflect that this branch is only taken when `expr` is true-ish. */
884
- inheritNonnullIfTrue (
932
+ private inheritNonnullIfTrue (
885
933
/** Expression being true. */
886
934
expr : ExpressionRef ,
887
935
/** If specified, only set the flag if also nonnull in this flow. */
@@ -995,7 +1043,7 @@ export class Flow {
995
1043
}
996
1044
997
1045
/** Updates local states to reflect that this branch is only taken when `expr` is false-ish. */
998
- inheritNonnullIfFalse (
1046
+ private inheritNonnullIfFalse (
999
1047
/** Expression being false. */
1000
1048
expr : ExpressionRef ,
1001
1049
/** If specified, only set the flag if also nonnull in this flow. */
0 commit comments