From fd2a6c68153bd1d0740e638df8fb8a7b1d7ed70d Mon Sep 17 00:00:00 2001 From: Paul Berry Date: Mon, 7 Dec 2020 16:09:57 +0000 Subject: [PATCH] Flow analysis: Track expression variables separately from promotion info. Previously, we used a single class hierarchy, ExpressionInfo, to store all the information that flow analysis needs to know about a variable, including: 1. What is known about the program state if the expression evaluates to true/false 2. Whether the expression is a `null` literal 3. Whether the expression is a reference to a variable. However, in order to address https://github.com/dart-lang/language/issues/1274 (Infer non-nullability from local boolean variables), we'll need #3 to be tracked orthogonally from #1, so that when a local boolean is referred to later, we can track information of type #1 and #3 simultaneously. However, it makes sense to keep #1 and #2 in the same data structure, because future work is planned to represent them in a more uniform way, as part of addressing https://github.com/dart-lang/language/issues/1224 (Using `if (foo?.bar == somethingNotNull)` should promote `foo`). Change-Id: I432f6e2e80543bb1d565b49403180c520eef66a5 Bug: https://github.com/dart-lang/language/issues/1274 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/175008 Reviewed-by: Johnni Winther Commit-Queue: Paul Berry --- .../lib/src/flow_analysis/flow_analysis.dart | 126 ++++++++++-------- 1 file changed, 67 insertions(+), 59 deletions(-) diff --git a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart index 9752d91ce251a..7610ce4950b18 100644 --- a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart +++ b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart @@ -2566,8 +2566,12 @@ class _EqualityOpContext extends _BranchContext { /// The type of the expression on the LHS of `==` or `!=`. final Type _leftOperandType; - _EqualityOpContext( - ExpressionInfo conditionInfo, this._leftOperandType) + /// If the LHS of `==` or `!=` is a variable reference, the variable. + /// Otherwise `null`. + final Variable _leftOperandVariable; + + _EqualityOpContext(ExpressionInfo conditionInfo, + this._leftOperandType, this._leftOperandVariable) : super(conditionInfo); @override @@ -2602,6 +2606,14 @@ class _FlowAnalysisImpl _expressionInfo; + /// The most recently visited expression which was a variable reference, or + /// `null` if no expression has been visited that was a variable reference. + Expression _expressionWithVariable; + + /// If [_expressionVariable] is not `null`, the variable corresponding to it. + /// Otherwise `null`. + Variable _expressionVariable; + int _functionNestingLevel = 0; final AssignedVariables _assignedVariables; @@ -2615,14 +2627,8 @@ class _FlowAnalysisImpl subExpressionInfo = - _getExpressionInfo(subExpression); - Variable variable; - if (subExpressionInfo is _VariableReadInfo) { - variable = subExpressionInfo._variable; - } else { - return; - } + Variable variable = _getExpressionVariable(subExpression); + if (variable == null) return; _current = _current.tryPromoteForTypeCast(typeOperations, variable, type); } @@ -2730,8 +2736,10 @@ class _FlowAnalysisImpl context = _stack.removeLast() as _EqualityOpContext; ExpressionInfo lhsInfo = context._conditionInfo; + Variable lhsVariable = context._leftOperandVariable; Type leftOperandType = context._leftOperandType; ExpressionInfo rhsInfo = _getExpressionInfo(rightOperand); + Variable rhsVariable = _getExpressionVariable(rightOperand); TypeClassification leftOperandTypeClassification = typeOperations.classifyType(leftOperandType); TypeClassification rightOperandTypeClassification = @@ -2749,18 +2757,16 @@ class _FlowAnalysisImpl && - rhsInfo is _VariableReadInfo) { + } else if (lhsInfo is _NullInfo && rhsVariable != null) { assert( leftOperandTypeClassification == TypeClassification.nullOrEquivalent); ExpressionInfo equalityInfo = - _current.tryMarkNonNullable(typeOperations, rhsInfo._variable); + _current.tryMarkNonNullable(typeOperations, rhsVariable); _storeExpressionInfo(wholeExpression, notEqual ? equalityInfo : ExpressionInfo.invert(equalityInfo)); - } else if (rhsInfo is _NullInfo && - lhsInfo is _VariableReadInfo) { + } else if (rhsInfo is _NullInfo && lhsVariable != null) { ExpressionInfo equalityInfo = - _current.tryMarkNonNullable(typeOperations, lhsInfo._variable); + _current.tryMarkNonNullable(typeOperations, lhsVariable); _storeExpressionInfo(wholeExpression, notEqual ? equalityInfo : ExpressionInfo.invert(equalityInfo)); } @@ -2769,7 +2775,9 @@ class _FlowAnalysisImpl( - _getExpressionInfo(leftOperand), leftOperandType)); + _getExpressionInfo(leftOperand), + leftOperandType, + _getExpressionVariable(leftOperand))); } @override @@ -2844,6 +2852,9 @@ class _FlowAnalysisImpl lhsInfo = _getExpressionInfo(leftHandSide); + Variable lhsVariable = _getExpressionVariable(leftHandSide); FlowModel promoted; _current = _current.split(); - if (lhsInfo is _VariableReadInfo) { + if (lhsVariable != null) { ExpressionInfo promotionInfo = - _current.tryMarkNonNullable(typeOperations, lhsInfo._variable); + _current.tryMarkNonNullable(typeOperations, lhsVariable); _current = promotionInfo.ifFalse; promoted = promotionInfo.ifTrue; } else { @@ -2959,12 +2970,10 @@ class _FlowAnalysisImpl subExpressionInfo = - _getExpressionInfo(subExpression); - if (subExpressionInfo is _VariableReadInfo) { - ExpressionInfo expressionInfo = - _current.tryPromoteForTypeCheck( - typeOperations, subExpressionInfo._variable, type); + Variable subExpressionVariable = _getExpressionVariable(subExpression); + if (subExpressionVariable != null) { + ExpressionInfo expressionInfo = _current + .tryPromoteForTypeCheck(typeOperations, subExpressionVariable, type); _storeExpressionInfo(isExpression, isNot ? ExpressionInfo.invert(expressionInfo) : expressionInfo); } @@ -3054,11 +3063,10 @@ class _FlowAnalysisImpl operandInfo = _getExpressionInfo(operand); - if (operandInfo is _VariableReadInfo) { - _current = _current - .tryMarkNonNullable(typeOperations, operandInfo._variable) - .ifTrue; + Variable operandVariable = _getExpressionVariable(operand); + if (operandVariable != null) { + _current = + _current.tryMarkNonNullable(typeOperations, operandVariable).ifTrue; } } @@ -3075,11 +3083,10 @@ class _FlowAnalysisImpl(_current)); if (target != null) { - ExpressionInfo targetInfo = _getExpressionInfo(target); - if (targetInfo is _VariableReadInfo) { - _current = _current - .tryMarkNonNullable(typeOperations, targetInfo._variable) - .ifTrue; + Variable targetVariable = _getExpressionVariable(target); + if (targetVariable != null) { + _current = + _current.tryMarkNonNullable(typeOperations, targetVariable).ifTrue; } } } @@ -3226,7 +3233,7 @@ class _FlowAnalysisImpl _join( FlowModel first, FlowModel second) => FlowModel.join(typeOperations, first, second, _current._emptyVariableMap); @@ -3319,6 +3341,14 @@ class _FlowAnalysisImpl extends _SimpleContext { 'afterBodyAndCatches: $_afterBodyAndCatches)'; } -/// [ExpressionInfo] representing an expression that reads the value of a -/// variable. -class _VariableReadInfo - implements ExpressionInfo { - @override - final FlowModel after; - - /// The variable that is being read. - final Variable _variable; - - _VariableReadInfo(this.after, this._variable); - - @override - FlowModel get ifFalse => after; - - @override - FlowModel get ifTrue => after; - - @override - String toString() => '_VariableReadInfo(after: $after, variable: $_variable)'; -} - /// [_FlowContext] representing a `while` loop (or a C-style `for` loop, which /// is functionally similar). class _WhileContext