@@ -301,8 +301,11 @@ class FlowAnalysis<Statement, Expression, Variable, Type> {
301
301
_stack.add (_current);
302
302
303
303
Set <Variable > notPromoted = null ;
304
- for (var variable in _current.promoted.keys) {
305
- if (functionBody.isPotentiallyMutatedInScope (variable)) {
304
+ for (var entry in _current.promoted.entries) {
305
+ var variable = entry.key;
306
+ var promotedType = entry.value;
307
+ if (promotedType != null &&
308
+ functionBody.isPotentiallyMutatedInScope (variable)) {
306
309
notPromoted ?? = Set <Variable >.identity ();
307
310
notPromoted.add (variable);
308
311
}
@@ -644,19 +647,18 @@ class FlowModel<Variable, Type> {
644
647
/// the control flow.
645
648
final _VariableSet <Variable > notAssigned;
646
649
647
- /// For each variable whose type is promoted at this point in the control
648
- /// flow, the promoted type. Variables whose type is not promoted are not
649
- /// present in the map.
650
+ /// For each variable being tracked by flow analysis, the variable's promoted
651
+ /// type, or `null` if the variable's type is not promoted.
650
652
///
651
653
/// Flow analysis has no awareness of scope, so variables that are out of
652
- /// scope are retained in the map until such time as their promotions would
653
- /// have been lost, if their scope had extended to the entire function being
654
- /// analyzed. So, for example, if a variable is declared and then promoted
655
- /// inside the `then ` branch of an `if` statement, and the `else` branch of
656
- /// the `if` statement ends in a `return ` statement, then the promotion
657
- /// remains in the map after the `if` statement ends . This should not have
658
- /// any effect on analysis results for error-free code, because it is an error
659
- /// to refer to a variable that is no longer in scope.
654
+ /// scope are retained in the map until such time as their declaration no
655
+ /// longer dominates the control flow. So, for example, if a variable is
656
+ /// declared and then promoted inside the `then` branch of an `if` statement,
657
+ /// and the `else ` branch of the `if` statement ends in a `return` statement,
658
+ /// then the variable remains in the map after the `if ` statement ends, even
659
+ /// though the variable is not in scope anymore . This should not have any
660
+ /// effect on analysis results for error-free code, because it is an error to
661
+ /// refer to a variable that is no longer in scope.
660
662
final Map <Variable , Type > promoted;
661
663
662
664
/// Creates a state object with the given [reachable] status. All variables
@@ -680,15 +682,13 @@ class FlowModel<Variable, Type> {
680
682
/// the point of declaration.
681
683
FlowModel <Variable , Type > add (Variable variable, {bool assigned: false }) {
682
684
var newNotAssigned = assigned ? notAssigned : notAssigned.add (variable);
683
-
684
- if (identical (newNotAssigned, notAssigned)) {
685
- return this ;
686
- }
685
+ var newPromoted = Map <Variable , Type >.from (promoted);
686
+ newPromoted[variable] = null ;
687
687
688
688
return FlowModel <Variable , Type >._(
689
689
reachable,
690
690
newNotAssigned,
691
- promoted ,
691
+ newPromoted ,
692
692
);
693
693
}
694
694
@@ -819,9 +819,9 @@ class FlowModel<Variable, Type> {
819
819
var newPromoted = < Variable , Type > {};
820
820
bool promotedMatchesThis = true ;
821
821
bool promotedMatchesOther = true ;
822
- for (var variable in Set < Variable >. from ( promoted.keys)
823
- .. addAll (other.promoted.keys)) {
824
- var thisType = promoted[variable] ;
822
+ for (var entry in promoted.entries) {
823
+ var variable = entry.key;
824
+ var thisType = entry.value ;
825
825
var otherType = other.promoted[variable];
826
826
if (! unsafe.contains (variable)) {
827
827
if (otherType != null &&
@@ -844,6 +844,7 @@ class FlowModel<Variable, Type> {
844
844
promotedMatchesOther = false ;
845
845
}
846
846
} else {
847
+ newPromoted[variable] = null ;
847
848
if (promotedMatchesOther && otherType != null ) {
848
849
promotedMatchesOther = false ;
849
850
}
@@ -912,16 +913,10 @@ class FlowModel<Variable, Type> {
912
913
/// immutable.
913
914
Map <Variable , Type > _removePromoted (
914
915
Map <Variable , Type > map, Variable variable) {
915
- if (map.isEmpty) return const {};
916
-
917
- var result = < Variable , Type > {};
918
- for (var key in map.keys) {
919
- if (! identical (key, variable)) {
920
- result[key] = map[key];
921
- }
922
- }
916
+ if (map[variable] == null ) return map;
923
917
924
- if (result.isEmpty) return const {};
918
+ var result = Map <Variable , Type >.from (map);
919
+ result[variable] = null ;
925
920
return result;
926
921
}
927
922
@@ -936,11 +931,14 @@ class FlowModel<Variable, Type> {
936
931
937
932
var result = < Variable , Type > {};
938
933
var noChanges = true ;
939
- for (var key in map.keys) {
940
- if (variables.contains (key)) {
934
+ for (var entry in map.entries) {
935
+ var variable = entry.key;
936
+ var promotedType = entry.value;
937
+ if (variables.contains (variable) && promotedType != null ) {
938
+ result[variable] = null ;
941
939
noChanges = false ;
942
940
} else {
943
- result[key ] = map[key] ;
941
+ result[variable ] = promotedType ;
944
942
}
945
943
}
946
944
@@ -996,24 +994,32 @@ class FlowModel<Variable, Type> {
996
994
var result = < Variable , Type > {};
997
995
var alwaysFirst = true ;
998
996
var alwaysSecond = true ;
999
- for (var variable in first.keys) {
1000
- var firstType = first[variable];
1001
- var secondType = second[variable];
1002
- if (secondType != null ) {
997
+ for (var entry in first.entries) {
998
+ var variable = entry.key;
999
+ if (! second.containsKey (variable)) {
1000
+ alwaysFirst = false ;
1001
+ } else {
1002
+ var firstType = entry.value;
1003
+ var secondType = second[variable];
1003
1004
if (identical (firstType, secondType)) {
1004
1005
result[variable] = firstType;
1006
+ } else if (firstType == null ) {
1007
+ result[variable] = null ;
1008
+ alwaysSecond = false ;
1009
+ } else if (secondType == null ) {
1010
+ result[variable] = null ;
1011
+ alwaysFirst = false ;
1005
1012
} else if (typeOperations.isSubtypeOf (firstType, secondType)) {
1006
1013
result[variable] = secondType;
1007
1014
alwaysFirst = false ;
1008
1015
} else if (typeOperations.isSubtypeOf (secondType, firstType)) {
1009
1016
result[variable] = firstType;
1010
1017
alwaysSecond = false ;
1011
1018
} else {
1019
+ result[variable] = null ;
1012
1020
alwaysFirst = false ;
1013
1021
alwaysSecond = false ;
1014
1022
}
1015
- } else {
1016
- alwaysFirst = false ;
1017
1023
}
1018
1024
}
1019
1025
@@ -1060,7 +1066,12 @@ class FlowModel<Variable, Type> {
1060
1066
for (var entry in p1.entries) {
1061
1067
var p1Value = entry.value;
1062
1068
var p2Value = p2[entry.key];
1063
- if (! typeOperations.isSameType (p1Value, p2Value)) return false ;
1069
+ if (p1Value == null ) {
1070
+ if (p2Value != null ) return false ;
1071
+ } else {
1072
+ if (p2Value == null ) return false ;
1073
+ if (! typeOperations.isSameType (p1Value, p2Value)) return false ;
1074
+ }
1064
1075
}
1065
1076
return true ;
1066
1077
}
0 commit comments