Flow analysis: do not treat null vars as equivalent to `null`.
When analyzing code like this with the
`allowLocalBooleanVarsToPromote` flag enabled, don't treat a variable
containing `null` as equivalent to a literal `null`. For example:
f(int? x) {
var y = null;
if (x != y) {
print(x + 1); // ERROR: x must be null checked.
}
}
The rationale is that flow analysis doesn't promote variables that are
known to be `null` to the `Null` type, so it seems like it would be
inconsistent if we used the fact that they're `null` to promote other
variables.
Bug: https://github.com/dart-lang/language/issues/1274
Change-Id: Icfa14104936a26732353921e3a29a840c53d4d52
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/176560
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
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 818914f..2ef2bf1 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
@@ -298,7 +298,7 @@
ExpressionInfo<Variable, Type> invert() =>
new ExpressionInfo<Variable, Type>(after, ifFalse, ifTrue);
- ExpressionInfo<Variable, Type> rebaseForward(
+ ExpressionInfo<Variable, Type>? rebaseForward(
TypeOperations<Variable, Type> typeOperations,
FlowModel<Variable, Type> base) =>
new ExpressionInfo(base, ifTrue.rebaseForward(typeOperations, base),
@@ -3757,11 +3757,11 @@
_storeExpressionVariable(expression, variable);
VariableModel<Variable, Type> variableModel = _current.infoFor(variable);
if (allowLocalBooleanVarsToPromote) {
- ExpressionInfo<Variable, Type>? expressionInfo =
- variableModel.ssaNode?.expressionInfo;
+ ExpressionInfo<Variable, Type>? expressionInfo = variableModel
+ .ssaNode?.expressionInfo
+ ?.rebaseForward(typeOperations, _current);
if (expressionInfo != null) {
- _storeExpressionInfo(
- expression, expressionInfo.rebaseForward(typeOperations, _current));
+ _storeExpressionInfo(expression, expressionInfo);
}
}
return variableModel.promotedTypes?.last;
@@ -3951,10 +3951,10 @@
}
@override
- ExpressionInfo<Variable, Type> rebaseForward(
+ ExpressionInfo<Variable, Type>? rebaseForward(
TypeOperations<Variable, Type> typeOperations,
FlowModel<Variable, Type> base) =>
- new _NullInfo(base);
+ null;
}
/// [_FlowContext] representing a language construct for which flow analysis
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
index e9740e2..f8c1de7 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
@@ -2586,18 +2586,11 @@
]);
});
- test('variableRead() restores the notion of whether a value is null', () {
- // This is not a necessary part of the design of
- // https://github.com/dart-lang/language/issues/1274; it's more of a happy
- // accident of how it was implemented: since we store an ExpressionInfo
- // object in the SSA node to keep track of the consequences of the
- // variable's value on flow analysis, and since the _NullInfo type is a
- // subtype of ExpressionInfo, that means that when a literal `null` is
- // written to a variable, and we read it later, we recognize the value as
- // being `null` for purposes of comparison to other other variables. Even
- // though this feature is a happy accident of the implementation strategy,
- // it's important to test it to make sure it doesn't regress, since users
- // might come to rely on it.
+ test("variableRead() doesn't restore the notion of whether a value is null",
+ () {
+ // Note: we have the available infrastructure to do this if we want, but
+ // we think it will give an inconsistent feel because comparisons like
+ // `if (i == null)` *don't* promote.
var h = Harness(allowLocalBooleanVarsToPromote: true);
var x = Var('x', 'int?');
var y = Var('y', 'int?');
@@ -2611,7 +2604,9 @@
checkNotPromoted(x),
checkNotPromoted(y),
], [
- checkPromoted(x, 'int'),
+ // Even though x != y and y is known to contain the value `null`, we
+ // don't promote x.
+ checkNotPromoted(x),
checkNotPromoted(y),
]),
]);