Skip to content

Commit d833f2f

Browse files
stereotype441commit-bot@chromium.org
authored andcommitted
Flow analysis changes to fix mixed-mode unsoundness loophole.
This is the flow analysis portion of the fix to dart-lang/language#1143. Follow-up changes will be needed in the CFE and/or backends to ensure that exceptions are thrown under appropriate circumstances. This CL also makes some improvements to flow analysis's reachability analysis so that it accounts for nullability of the target when analyzing the reachability of `??=` and `?.`. Hopefully these improvements should make the fix to dart-lang/language#1143 clearer and more consistent. Change-Id: I5fa5c070f13fd57ac4c2fb87f2d67588861594b0 Bug: dart-lang/language#1143 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/160440 Reviewed-by: Konstantin Shcheglov <[email protected]> Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Paul Berry <[email protected]>
1 parent 33e4a6b commit d833f2f

File tree

18 files changed

+918
-88
lines changed

18 files changed

+918
-88
lines changed

pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart

Lines changed: 89 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,16 @@ abstract class FlowAnalysis<Node, Statement extends Node, Expression, Variable,
362362
void doStatement_end(Expression condition);
363363

364364
/// Call this method just after visiting a binary `==` or `!=` expression.
365-
void equalityOp_end(Expression wholeExpression, Expression rightOperand,
365+
///
366+
/// Return value indicates whether flow analysis believes that a successful
367+
/// equality check is reachable. If `false` is returned, the client should
368+
/// ensure that the `==` test behaves like `x == y && throw ...`.
369+
///
370+
/// Note that if `notEqual` is `true`, then the return value describes the
371+
/// behavior of the underlying `==` test. So if `notEqual` is `true` and
372+
/// `false` is returned, the client should ensure that the `!=` test behaves
373+
/// like `!(x == y && throw ...)`.
374+
bool equalityOp_end(Expression wholeExpression, Expression rightOperand,
366375
Type rightOperandType,
367376
{bool notEqual = false});
368377

@@ -475,7 +484,13 @@ abstract class FlowAnalysis<Node, Statement extends Node, Expression, Variable,
475484

476485
/// Call this method after visiting the LHS of an if-null expression ("??")
477486
/// or if-null assignment ("??=").
478-
void ifNullExpression_rightBegin(Expression leftHandSide);
487+
///
488+
/// Return value indicates whether flow analysis believes that the right hand
489+
/// side is reachable. If `false` is returned, the client should ensure that
490+
/// `x ?? y` behaves like `x ?? throw ...` (or, correspondingly, that
491+
/// `x ??= y` behaves like `x ??= throw ...`).
492+
bool ifNullExpression_rightBegin(
493+
Expression leftHandSide, Type leftHandSideType);
479494

480495
/// Call this method after visiting the "then" part of an if statement, and
481496
/// before visiting the "else" part.
@@ -511,7 +526,16 @@ abstract class FlowAnalysis<Node, Statement extends Node, Expression, Variable,
511526
/// be the expression to which the "is" check was applied. [isNot] should be
512527
/// a boolean indicating whether this is an "is" or an "is!" expression.
513528
/// [type] should be the type being checked.
514-
void isExpression_end(
529+
///
530+
/// Return value indicates whether flow analysis believes that a failure of
531+
/// the `is` test is reachable. If `false` is returned, the client should
532+
/// ensure that the `is` test behaves like `x is T || throw ...`.
533+
///
534+
/// Note that if `isNot` is `true`, then the return value describes the
535+
/// behavior of the underlying `if` test. So if `isNot` is `true` and `false`
536+
/// is returned, the client should ensure that the `is!` test behaves like
537+
/// `!(x is T || throw ...)`.
538+
bool isExpression_end(
515539
Expression isExpression, Expression subExpression, bool isNot, Type type);
516540

517541
/// Return whether the [variable] is definitely unassigned in the current
@@ -558,12 +582,22 @@ abstract class FlowAnalysis<Node, Statement extends Node, Expression, Variable,
558582
/// [target] should be the expression just before the null-aware operator, or
559583
/// `null` if the null-aware access starts a cascade section.
560584
///
585+
/// [targetType] should be the type of the expression just before the
586+
/// null-aware operator, and should be non-null even if the null-aware access
587+
/// starts a cascade section.
588+
///
561589
/// Note that [nullAwareAccess_end] should be called after the conclusion
562590
/// of any null-shorting that is caused by the `?.`. So, for example, if the
563591
/// code being analyzed is `x?.y?.z(x)`, [nullAwareAccess_rightBegin] should
564592
/// be called once upon reaching each `?.`, but [nullAwareAccess_end] should
565593
/// not be called until after processing the method call to `z(x)`.
566-
void nullAwareAccess_rightBegin(Expression target);
594+
///
595+
/// Return value indicates whether flow analysis believes that a null target
596+
/// is reachable. If `false` is returned, the client should ensure that
597+
/// `x?.y` behaves like `x!.y`. (Note that this is necessary even if `y`
598+
/// exists on `Object`--see
599+
/// https://github.com/dart-lang/language/issues/1143#issuecomment-682096575.)
600+
bool nullAwareAccess_rightBegin(Expression target, Type targetType);
567601

568602
/// Call this method when encountering an expression that is a `null` literal.
569603
void nullLiteral(Expression expression);
@@ -811,15 +845,17 @@ class FlowAnalysisDebug<Node, Statement extends Node, Expression, Variable,
811845
}
812846

813847
@override
814-
void equalityOp_end(Expression wholeExpression, Expression rightOperand,
848+
bool equalityOp_end(Expression wholeExpression, Expression rightOperand,
815849
Type rightOperandType,
816850
{bool notEqual = false}) {
817-
_wrap(
851+
return _wrap(
818852
'equalityOp_end($wholeExpression, $rightOperand, $rightOperandType, '
819853
'notEqual: $notEqual)',
820854
() => _wrapped.equalityOp_end(
821855
wholeExpression, rightOperand, rightOperandType,
822-
notEqual: notEqual));
856+
notEqual: notEqual),
857+
isQuery: true,
858+
isPure: false);
823859
}
824860

825861
@override
@@ -902,9 +938,14 @@ class FlowAnalysisDebug<Node, Statement extends Node, Expression, Variable,
902938
}
903939

904940
@override
905-
void ifNullExpression_rightBegin(Expression leftHandSide) {
906-
return _wrap('ifNullExpression_rightBegin($leftHandSide)',
907-
() => _wrapped.ifNullExpression_rightBegin(leftHandSide));
941+
bool ifNullExpression_rightBegin(
942+
Expression leftHandSide, Type leftHandSideType) {
943+
return _wrap(
944+
'ifNullExpression_rightBegin($leftHandSide, $leftHandSideType)',
945+
() => _wrapped.ifNullExpression_rightBegin(
946+
leftHandSide, leftHandSideType),
947+
isQuery: true,
948+
isPure: false);
908949
}
909950

910951
@override
@@ -931,12 +972,14 @@ class FlowAnalysisDebug<Node, Statement extends Node, Expression, Variable,
931972
}
932973

933974
@override
934-
void isExpression_end(Expression isExpression, Expression subExpression,
975+
bool isExpression_end(Expression isExpression, Expression subExpression,
935976
bool isNot, Type type) {
936-
_wrap(
977+
return _wrap(
937978
'isExpression_end($isExpression, $subExpression, $isNot, $type)',
938-
() => _wrapped.isExpression_end(
939-
isExpression, subExpression, isNot, type));
979+
() =>
980+
_wrapped.isExpression_end(isExpression, subExpression, isNot, type),
981+
isQuery: true,
982+
isPure: false);
940983
}
941984

942985
@override
@@ -991,9 +1034,10 @@ class FlowAnalysisDebug<Node, Statement extends Node, Expression, Variable,
9911034
}
9921035

9931036
@override
994-
void nullAwareAccess_rightBegin(Expression target) {
995-
_wrap('nullAwareAccess_rightBegin($target)',
996-
() => _wrapped.nullAwareAccess_rightBegin(target));
1037+
bool nullAwareAccess_rightBegin(Expression target, Type targetType) {
1038+
return _wrap('nullAwareAccess_rightBegin($target, $targetType)',
1039+
() => _wrapped.nullAwareAccess_rightBegin(target, targetType),
1040+
isQuery: true, isPure: false);
9971041
}
9981042

9991043
@override
@@ -2456,7 +2500,7 @@ class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
24562500
}
24572501

24582502
@override
2459-
void equalityOp_end(Expression wholeExpression, Expression rightOperand,
2503+
bool equalityOp_end(Expression wholeExpression, Expression rightOperand,
24602504
Type rightOperandType,
24612505
{bool notEqual = false}) {
24622506
_EqualityOpContext<Variable, Type> context =
@@ -2471,14 +2515,16 @@ class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
24712515
typeOperations.classifyType(rightOperandType);
24722516
if (leftOperandTypeClassification == TypeClassification.nullOrEquivalent &&
24732517
rightOperandTypeClassification == TypeClassification.nullOrEquivalent) {
2474-
return booleanLiteral(wholeExpression, !notEqual);
2518+
booleanLiteral(wholeExpression, !notEqual);
2519+
return true;
24752520
} else if ((leftOperandTypeClassification ==
24762521
TypeClassification.nullOrEquivalent &&
24772522
rightOperandTypeClassification == TypeClassification.nonNullable) ||
24782523
(rightOperandTypeClassification ==
24792524
TypeClassification.nullOrEquivalent &&
24802525
leftOperandTypeClassification == TypeClassification.nonNullable)) {
2481-
return booleanLiteral(wholeExpression, notEqual);
2526+
booleanLiteral(wholeExpression, notEqual);
2527+
return false;
24822528
} else if (lhsInfo is _NullInfo<Variable, Type> &&
24832529
rhsInfo is _VariableReadInfo<Variable, Type>) {
24842530
assert(
@@ -2490,10 +2536,11 @@ class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
24902536
equalityInfo =
24912537
_current.tryMarkNonNullable(typeOperations, lhsInfo._variable);
24922538
} else {
2493-
return;
2539+
return true;
24942540
}
24952541
_storeExpressionInfo(wholeExpression,
24962542
notEqual ? equalityInfo : ExpressionInfo.invert(equalityInfo));
2543+
return equalityInfo.ifFalse.reachable;
24972544
}
24982545

24992546
@override
@@ -2619,7 +2666,8 @@ class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
26192666
}
26202667

26212668
@override
2622-
void ifNullExpression_rightBegin(Expression leftHandSide) {
2669+
bool ifNullExpression_rightBegin(
2670+
Expression leftHandSide, Type leftHandSideType) {
26232671
ExpressionInfo<Variable, Type> lhsInfo = _getExpressionInfo(leftHandSide);
26242672
FlowModel<Variable, Type> promoted;
26252673
if (lhsInfo is _VariableReadInfo<Variable, Type>) {
@@ -2630,7 +2678,12 @@ class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
26302678
} else {
26312679
promoted = _current;
26322680
}
2681+
if (typeOperations.classifyType(leftHandSideType) ==
2682+
TypeClassification.nonNullable) {
2683+
_current = _current.setReachable(false);
2684+
}
26332685
_stack.add(new _SimpleContext<Variable, Type>(promoted));
2686+
return _current.reachable;
26342687
}
26352688

26362689
@override
@@ -2670,20 +2723,21 @@ class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
26702723
}
26712724

26722725
@override
2673-
void isExpression_end(Expression isExpression, Expression subExpression,
2726+
bool isExpression_end(Expression isExpression, Expression subExpression,
26742727
bool isNot, Type type) {
26752728
ExpressionInfo<Variable, Type> subExpressionInfo =
26762729
_getExpressionInfo(subExpression);
26772730
Variable variable;
26782731
if (subExpressionInfo is _VariableReadInfo<Variable, Type>) {
26792732
variable = subExpressionInfo._variable;
26802733
} else {
2681-
return;
2734+
return true;
26822735
}
26832736
ExpressionInfo<Variable, Type> expressionInfo =
26842737
_current.tryPromoteForTypeCheck(typeOperations, variable, type);
26852738
_storeExpressionInfo(isExpression,
26862739
isNot ? ExpressionInfo.invert(expressionInfo) : expressionInfo);
2740+
return expressionInfo.ifFalse.reachable;
26872741
}
26882742

26892743
@override
@@ -2760,8 +2814,16 @@ class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
27602814
}
27612815

27622816
@override
2763-
void nullAwareAccess_rightBegin(Expression target) {
2764-
_stack.add(new _SimpleContext<Variable, Type>(_current));
2817+
bool nullAwareAccess_rightBegin(Expression target, Type targetType) {
2818+
assert(targetType != null);
2819+
bool shortingIsReachable = true;
2820+
FlowModel<Variable, Type> shortingModel = _current;
2821+
if (typeOperations.classifyType(targetType) ==
2822+
TypeClassification.nonNullable) {
2823+
shortingModel = shortingModel.setReachable(false);
2824+
shortingIsReachable = false;
2825+
}
2826+
_stack.add(new _SimpleContext<Variable, Type>(shortingModel));
27652827
if (target != null) {
27662828
ExpressionInfo<Variable, Type> targetInfo = _getExpressionInfo(target);
27672829
if (targetInfo is _VariableReadInfo<Variable, Type>) {
@@ -2770,6 +2832,7 @@ class _FlowAnalysisImpl<Node, Statement extends Node, Expression, Variable,
27702832
.ifTrue;
27712833
}
27722834
}
2835+
return shortingIsReachable;
27732836
}
27742837

27752838
@override

0 commit comments

Comments
 (0)