@@ -400,44 +400,6 @@ class FlowAnalysis<Statement, Expression, Variable, Type> {
400400 }
401401 }
402402
403- @visibleForTesting
404- Map <Variable , Type > joinPromoted (
405- Map <Variable , Type > first,
406- Map <Variable , Type > second,
407- ) {
408- if (identical (first, second)) return first;
409- if (first.isEmpty || second.isEmpty) return const {};
410-
411- var result = < Variable , Type > {};
412- var alwaysFirst = true ;
413- var alwaysSecond = true ;
414- for (var variable in first.keys) {
415- var firstType = first[variable];
416- var secondType = second[variable];
417- if (secondType != null ) {
418- if (identical (firstType, secondType)) {
419- result[variable] = firstType;
420- } else if (typeOperations.isSubtypeOf (firstType, secondType)) {
421- result[variable] = secondType;
422- alwaysFirst = false ;
423- } else if (typeOperations.isSubtypeOf (secondType, firstType)) {
424- result[variable] = firstType;
425- alwaysSecond = false ;
426- } else {
427- alwaysFirst = false ;
428- alwaysSecond = false ;
429- }
430- } else {
431- alwaysFirst = false ;
432- }
433- }
434-
435- if (alwaysFirst) return first;
436- if (alwaysSecond && result.length == second.length) return second;
437- if (result.isEmpty) return const {};
438- return result;
439- }
440-
441403 void logicalAnd_end (Expression andExpression, Expression rightOperand) {
442404 _conditionalEnd (rightOperand);
443405 // Tail of the stack: falseLeft, trueLeft, falseRight, trueRight
@@ -643,27 +605,8 @@ class FlowAnalysis<Statement, Expression, Variable, Type> {
643605 }
644606
645607 State <Variable , Type > _join (
646- State <Variable , Type > first,
647- State <Variable , Type > second,
648- ) {
649- if (first == null ) return second;
650- if (second == null ) return first;
651-
652- if (first.reachable && ! second.reachable) return first;
653- if (! first.reachable && second.reachable) return second;
654-
655- var newReachable = first.reachable || second.reachable;
656- var newNotAssigned = first.notAssigned.union (second.notAssigned);
657- var newPromoted = joinPromoted (first.promoted, second.promoted);
658-
659- return State ._identicalOrNew (
660- first,
661- second,
662- newReachable,
663- newNotAssigned,
664- newPromoted,
665- );
666- }
608+ State <Variable , Type > first, State <Variable , Type > second) =>
609+ State .join (typeOperations, first, second);
667610
668611 /// If assertions are enabled, records that the given variable has been
669612 /// referenced. The [finish] method will verify that all referenced variables
@@ -1019,6 +962,80 @@ class State<Variable, Type> {
1019962 return result;
1020963 }
1021964
965+ /// Forms a new state to reflect a control flow path that might have come from
966+ /// either `this` or the [other] state.
967+ ///
968+ /// The control flow path is considered reachable if either of the input
969+ /// states is reachable. Variables are considered definitely assigned if they
970+ /// were definitely assigned in both of the input states. Variable promotions
971+ /// are kept only if they are common to both input states; if a variable is
972+ /// promoted to one type in one state and a subtype in the other state, the
973+ /// less specific type promotion is kept.
974+ static State <Variable , Type > join <Variable , Type >(
975+ TypeOperations <Variable , Type > typeOperations,
976+ State <Variable , Type > first,
977+ State <Variable , Type > second,
978+ ) {
979+ if (first == null ) return second;
980+ if (second == null ) return first;
981+
982+ if (first.reachable && ! second.reachable) return first;
983+ if (! first.reachable && second.reachable) return second;
984+
985+ var newReachable = first.reachable || second.reachable;
986+ var newNotAssigned = first.notAssigned.union (second.notAssigned);
987+ var newPromoted =
988+ State .joinPromoted (typeOperations, first.promoted, second.promoted);
989+
990+ return State ._identicalOrNew (
991+ first,
992+ second,
993+ newReachable,
994+ newNotAssigned,
995+ newPromoted,
996+ );
997+ }
998+
999+ /// Joins two "promoted" maps. See [join] for details.
1000+ @visibleForTesting
1001+ static Map <Variable , Type > joinPromoted <Variable , Type >(
1002+ TypeOperations <Variable , Type > typeOperations,
1003+ Map <Variable , Type > first,
1004+ Map <Variable , Type > second,
1005+ ) {
1006+ if (identical (first, second)) return first;
1007+ if (first.isEmpty || second.isEmpty) return const {};
1008+
1009+ var result = < Variable , Type > {};
1010+ var alwaysFirst = true ;
1011+ var alwaysSecond = true ;
1012+ for (var variable in first.keys) {
1013+ var firstType = first[variable];
1014+ var secondType = second[variable];
1015+ if (secondType != null ) {
1016+ if (identical (firstType, secondType)) {
1017+ result[variable] = firstType;
1018+ } else if (typeOperations.isSubtypeOf (firstType, secondType)) {
1019+ result[variable] = secondType;
1020+ alwaysFirst = false ;
1021+ } else if (typeOperations.isSubtypeOf (secondType, firstType)) {
1022+ result[variable] = firstType;
1023+ alwaysSecond = false ;
1024+ } else {
1025+ alwaysFirst = false ;
1026+ alwaysSecond = false ;
1027+ }
1028+ } else {
1029+ alwaysFirst = false ;
1030+ }
1031+ }
1032+
1033+ if (alwaysFirst) return first;
1034+ if (alwaysSecond && result.length == second.length) return second;
1035+ if (result.isEmpty) return const {};
1036+ return result;
1037+ }
1038+
10221039 /// Creates a new [State] object, unless it is equivalent to either [first] or
10231040 /// [second] , in which case one of those objects is re-used.
10241041 static State <Variable , Type > _identicalOrNew <Variable , Type >(
0 commit comments