@@ -210,8 +210,10 @@ abstract class FlowAnalysis<Node extends Object, Statement extends Node,
210
210
/// Call this method after visiting an "as" expression.
211
211
///
212
212
/// [subExpression] should be the expression to which the "as" check was
213
- /// applied. [type] should be the type being checked.
214
- void asExpression_end (Expression subExpression, Type type);
213
+ /// applied, and [subExpressionType] should be its static type. [castType]
214
+ /// should be the type being cast to.
215
+ void asExpression_end (Expression subExpression,
216
+ {required Type subExpressionType, required Type castType});
215
217
216
218
/// Call this method after visiting the condition part of an assert statement
217
219
/// (or assert initializer).
@@ -659,11 +661,13 @@ abstract class FlowAnalysis<Node extends Object, Statement extends Node,
659
661
/// Call this method after visiting the LHS of an "is" expression.
660
662
///
661
663
/// [isExpression] should be the complete expression. [subExpression] should
662
- /// be the expression to which the "is" check was applied. [isNot] should be
663
- /// a boolean indicating whether this is an "is" or an "is!" expression.
664
- /// [type] should be the type being checked.
664
+ /// be the expression to which the "is" check was applied, and
665
+ /// [subExpressionType] should be its static type. [isNot] should be a
666
+ /// boolean indicating whether this is an "is" or an "is!" expression.
667
+ /// [checkedType] should be the type being checked.
665
668
void isExpression_end (
666
- Expression isExpression, Expression subExpression, bool isNot, Type type);
669
+ Expression isExpression, Expression subExpression, bool isNot,
670
+ {required Type subExpressionType, required Type checkedType});
667
671
668
672
/// Return whether the [variable] is definitely unassigned in the current
669
673
/// state.
@@ -1214,9 +1218,13 @@ class FlowAnalysisDebug<Node extends Object, Statement extends Node,
1214
1218
FlowAnalysisOperations <Variable , Type > get operations => _wrapped.operations;
1215
1219
1216
1220
@override
1217
- void asExpression_end (Expression subExpression, Type type) {
1218
- _wrap ('asExpression_end($subExpression , $type )' ,
1219
- () => _wrapped.asExpression_end (subExpression, type));
1221
+ void asExpression_end (Expression subExpression,
1222
+ {required Type subExpressionType, required Type castType}) {
1223
+ _wrap (
1224
+ 'asExpression_end($subExpression , subExpressionType: '
1225
+ '$subExpressionType , castType: $castType )' ,
1226
+ () => _wrapped.asExpression_end (subExpression,
1227
+ subExpressionType: subExpressionType, castType: castType));
1220
1228
}
1221
1229
1222
1230
@override
@@ -1573,12 +1581,14 @@ class FlowAnalysisDebug<Node extends Object, Statement extends Node,
1573
1581
}
1574
1582
1575
1583
@override
1576
- void isExpression_end (Expression isExpression, Expression subExpression,
1577
- bool isNot, Type type) {
1584
+ void isExpression_end (
1585
+ Expression isExpression, Expression subExpression, bool isNot,
1586
+ {required Type subExpressionType, required Type checkedType}) {
1578
1587
_wrap (
1579
- 'isExpression_end($isExpression , $subExpression , $isNot , $type )' ,
1580
- () => _wrapped.isExpression_end (
1581
- isExpression, subExpression, isNot, type));
1588
+ 'isExpression_end($isExpression , $subExpression , $isNot , '
1589
+ 'subExpressionType: $subExpressionType , checkedType: $checkedType )' ,
1590
+ () => _wrapped.isExpression_end (isExpression, subExpression, isNot,
1591
+ subExpressionType: subExpressionType, checkedType: checkedType));
1582
1592
}
1583
1593
1584
1594
@override
@@ -4290,7 +4300,7 @@ class _FlowAnalysisImpl<Node extends Object, Statement extends Node,
4290
4300
implements
4291
4301
FlowAnalysis <Node , Statement , Expression , Variable , Type >,
4292
4302
_PropertyTargetHelper <Expression , Type > {
4293
- /// Options affecting the behavior of flow analysis.
4303
+ /// Language features enables affecting the behavior of flow analysis.
4294
4304
final TypeAnalyzerOptions typeAnalyzerOptions;
4295
4305
4296
4306
/// The [FlowAnalysisOperations] , used to access types, check subtyping, and
@@ -4413,10 +4423,18 @@ class _FlowAnalysisImpl<Node extends Object, Statement extends Node,
4413
4423
FlowAnalysisTypeOperations <Type > get typeOperations => operations;
4414
4424
4415
4425
@override
4416
- void asExpression_end (Expression subExpression, Type type) {
4426
+ void asExpression_end (Expression subExpression,
4427
+ {required Type subExpressionType, required Type castType}) {
4428
+ // Depending on types, flow analysis may be able to prove that the `as`
4429
+ // expression is guaranteed to fail.
4430
+ if (_isTypeCheckGuaranteedToFailWithSoundNullSafety (
4431
+ staticType: subExpressionType, checkedType: castType)) {
4432
+ _current = _current.setUnreachable ();
4433
+ }
4434
+
4417
4435
_Reference <Type >? reference = _getExpressionReference (subExpression);
4418
4436
if (reference == null ) return ;
4419
- _current = _current.tryPromoteForTypeCast (this , reference, type );
4437
+ _current = _current.tryPromoteForTypeCast (this , reference, castType );
4420
4438
}
4421
4439
4422
4440
@override
@@ -4950,16 +4968,19 @@ class _FlowAnalysisImpl<Node extends Object, Statement extends Node,
4950
4968
}
4951
4969
4952
4970
@override
4953
- void isExpression_end (Expression isExpression, Expression subExpression,
4954
- bool isNot, Type type) {
4955
- if (operations.isBottomType (type)) {
4971
+ void isExpression_end (
4972
+ Expression isExpression, Expression subExpression, bool isNot,
4973
+ {required Type subExpressionType, required Type checkedType}) {
4974
+ if (operations.isBottomType (checkedType) ||
4975
+ _isTypeCheckGuaranteedToFailWithSoundNullSafety (
4976
+ staticType: subExpressionType, checkedType: checkedType)) {
4956
4977
booleanLiteral (isExpression, isNot);
4957
4978
} else {
4958
4979
_Reference <Type >? subExpressionReference =
4959
4980
_getExpressionReference (subExpression);
4960
4981
if (subExpressionReference != null ) {
4961
- ExpressionInfo <Type > expressionInfo =
4962
- _current. tryPromoteForTypeCheck ( this , subExpressionReference, type );
4982
+ ExpressionInfo <Type > expressionInfo = _current. tryPromoteForTypeCheck (
4983
+ this , subExpressionReference, checkedType );
4963
4984
_storeExpressionInfo (
4964
4985
isExpression, isNot ? expressionInfo._invert () : expressionInfo);
4965
4986
}
@@ -6063,6 +6084,32 @@ class _FlowAnalysisImpl<Node extends Object, Statement extends Node,
6063
6084
}
6064
6085
}
6065
6086
6087
+ /// Determines whether an expression having the given [staticType] is
6088
+ /// guaranteed to fail an `is` or `as` check using [checkedType] due to sound
6089
+ /// null safety.
6090
+ ///
6091
+ /// If [TypeAnalyzerOptions.soundFlowAnalysisEnabled] is `false` , this method
6092
+ /// will return `false` regardless of its input. This reflects the fact that
6093
+ /// in language versions prior to the introduction of sound flow analysis,
6094
+ /// flow analysis assumed that the program might be executing in unsound null
6095
+ /// safety mode.
6096
+ bool _isTypeCheckGuaranteedToFailWithSoundNullSafety (
6097
+ {required Type staticType, required Type checkedType}) {
6098
+ if (! typeAnalyzerOptions.soundFlowAnalysisEnabled) return false ;
6099
+ switch (typeOperations.classifyType (staticType)) {
6100
+ case TypeClassification .nonNullable
6101
+ when typeOperations.classifyType (checkedType) ==
6102
+ TypeClassification .nullOrEquivalent:
6103
+ case TypeClassification .nullOrEquivalent
6104
+ when typeOperations.classifyType (checkedType) ==
6105
+ TypeClassification .nonNullable:
6106
+ // Guaranteed to fail due to nullability mismatch.
6107
+ return true ;
6108
+ default :
6109
+ return false ;
6110
+ }
6111
+ }
6112
+
6066
6113
FlowModel <Type > _join (FlowModel <Type >? first, FlowModel <Type >? second) =>
6067
6114
FlowModel .join (this , first, second);
6068
6115
0 commit comments