mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 10:10:22 +00:00
Resolve x ?? y as LUB(X!, Y) instead of LUB(X, Y).
Change-Id: I081d8ec153916fb37e2a949d9fa85a6ea778438e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/92390 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Mike Fairhurst <mfairhurst@google.com> Auto-Submit: Mike Fairhurst <mfairhurst@google.com>
This commit is contained in:
parent
78e50a5f9b
commit
a8b6e18b62
4 changed files with 129 additions and 13 deletions
|
@ -71,7 +71,10 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<void> {
|
|||
*/
|
||||
TypePromotionManager _promoteManager;
|
||||
|
||||
bool nonNullableEnabled;
|
||||
/**
|
||||
* Whether NNBD is enabled for this compilation unit.
|
||||
*/
|
||||
bool _nonNullableEnabled;
|
||||
|
||||
/**
|
||||
* Initialize a newly created type analyzer.
|
||||
|
@ -86,7 +89,7 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<void> {
|
|||
AnalysisOptionsImpl analysisOptions =
|
||||
_resolver.definingLibrary.context.analysisOptions;
|
||||
_strictInference = analysisOptions.strictInference;
|
||||
nonNullableEnabled = featureSet.isEnabled(Feature.non_nullable);
|
||||
_nonNullableEnabled = featureSet.isEnabled(Feature.non_nullable);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -288,10 +291,22 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<void> {
|
|||
DartType staticType = _getStaticType(rightHandSide);
|
||||
_recordStaticType(node, staticType);
|
||||
} else if (operator == TokenType.QUESTION_QUESTION_EQ) {
|
||||
// The static type of a compound assignment using ??= is the least upper
|
||||
// bound of the static types of the LHS and RHS.
|
||||
_analyzeLeastUpperBound(node, node.leftHandSide, node.rightHandSide,
|
||||
read: true);
|
||||
if (_nonNullableEnabled) {
|
||||
// The static type of a compound assignment using ??= with NNBD is the
|
||||
// least upper bound of the static types of the LHS and RHS after
|
||||
// promoting the LHS/ to non-null (as we know its value will not be used
|
||||
// if null)
|
||||
_analyzeLeastUpperBoundTypes(
|
||||
node,
|
||||
_typeSystem.promoteToNonNull(
|
||||
_getExpressionType(node.leftHandSide, read: true)),
|
||||
_getExpressionType(node.rightHandSide, read: true));
|
||||
} else {
|
||||
// The static type of a compound assignment using ??= before NNBD is the
|
||||
// least upper bound of the static types of the LHS and RHS.
|
||||
_analyzeLeastUpperBound(node, node.leftHandSide, node.rightHandSide,
|
||||
read: true);
|
||||
}
|
||||
return;
|
||||
} else if (operator == TokenType.AMPERSAND_AMPERSAND_EQ ||
|
||||
operator == TokenType.BAR_BAR_EQ) {
|
||||
|
@ -370,11 +385,23 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<void> {
|
|||
@override
|
||||
void visitBinaryExpression(BinaryExpression node) {
|
||||
if (node.operator.type == TokenType.QUESTION_QUESTION) {
|
||||
// Evaluation of an if-null expression e of the form e1 ?? e2 is
|
||||
// equivalent to the evaluation of the expression
|
||||
// ((x) => x == null ? e2 : x)(e1). The static type of e is the least
|
||||
// upper bound of the static type of e1 and the static type of e2.
|
||||
_analyzeLeastUpperBound(node, node.leftOperand, node.rightOperand);
|
||||
if (_nonNullableEnabled) {
|
||||
// The static type of a compound assignment using ??= with NNBD is the
|
||||
// least upper bound of the static types of the LHS and RHS after
|
||||
// promoting the LHS/ to non-null (as we know its value will not be used
|
||||
// if null)
|
||||
_analyzeLeastUpperBoundTypes(
|
||||
node,
|
||||
_typeSystem.promoteToNonNull(
|
||||
_getExpressionType(node.leftOperand, read: true)),
|
||||
_getExpressionType(node.rightOperand, read: true));
|
||||
} else {
|
||||
// Without NNBD, evaluation of an if-null expression e of the form
|
||||
// e1 ?? e2 is equivalent to the evaluation of the expression
|
||||
// ((x) => x == null ? e2 : x)(e1). The static type of e is the least
|
||||
// upper bound of the static type of e1 and the static type of e2.
|
||||
_analyzeLeastUpperBound(node, node.leftOperand, node.rightOperand);
|
||||
}
|
||||
return;
|
||||
}
|
||||
DartType staticType = node.staticInvokeType?.returnType ?? _dynamicType;
|
||||
|
@ -874,7 +901,8 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<void> {
|
|||
// TODO(brianwilkerson) Report this internal error.
|
||||
}
|
||||
|
||||
if (node.operator.type == TokenType.QUESTION_PERIOD && nonNullableEnabled) {
|
||||
if (node.operator.type == TokenType.QUESTION_PERIOD &&
|
||||
_nonNullableEnabled) {
|
||||
staticType =
|
||||
(staticType as TypeImpl).withNullability(NullabilitySuffix.question);
|
||||
}
|
||||
|
@ -1087,10 +1115,21 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<void> {
|
|||
{bool read: false}) {
|
||||
DartType staticType1 = _getExpressionType(expr1, read: read);
|
||||
DartType staticType2 = _getExpressionType(expr2, read: read);
|
||||
|
||||
_analyzeLeastUpperBoundTypes(node, staticType1, staticType2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the static type of [node] to be the least upper bound of the static
|
||||
* types [staticType1] and [staticType2].
|
||||
*/
|
||||
void _analyzeLeastUpperBoundTypes(
|
||||
Expression node, DartType staticType1, DartType staticType2) {
|
||||
if (staticType1 == null) {
|
||||
// TODO(brianwilkerson) Determine whether this can still happen.
|
||||
staticType1 = _dynamicType;
|
||||
}
|
||||
|
||||
if (staticType2 == null) {
|
||||
// TODO(brianwilkerson) Determine whether this can still happen.
|
||||
staticType2 = _dynamicType;
|
||||
|
@ -1186,7 +1225,7 @@ class StaticTypeAnalyzer extends SimpleAstVisitor<void> {
|
|||
returnType = type.returnType;
|
||||
}
|
||||
|
||||
if (isNullableInvoke && nonNullableEnabled) {
|
||||
if (isNullableInvoke && _nonNullableEnabled) {
|
||||
returnType = returnType?.withNullability(NullabilitySuffix.question);
|
||||
}
|
||||
|
||||
|
|
|
@ -2082,6 +2082,11 @@ abstract class TypeSystem implements public.TypeSystem {
|
|||
return null;
|
||||
}
|
||||
|
||||
DartType promoteToNonNull(TypeImpl type) {
|
||||
// TODO(mfairhurst): handle type parameter types
|
||||
return type.withNullability(NullabilitySuffix.none);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to make a better guess for the type of a binary with the given
|
||||
* [operator], given that resolution has so far produced the [currentType].
|
||||
|
|
|
@ -55,6 +55,56 @@ m() {
|
|||
assertType(findNode.methodInvocation('c?.x()'), 'bool?');
|
||||
}
|
||||
|
||||
test_local_nullCoalesce_nullableInt_int() async {
|
||||
await addTestFile(r'''
|
||||
m() {
|
||||
int? x;
|
||||
int y;
|
||||
x ?? y;
|
||||
}
|
||||
''');
|
||||
await resolveTestFile();
|
||||
assertNoTestErrors();
|
||||
assertType(findNode.binary('x ?? y'), 'int');
|
||||
}
|
||||
|
||||
test_local_nullCoalesce_nullableInt_nullableInt() async {
|
||||
await addTestFile(r'''
|
||||
m() {
|
||||
int? x;
|
||||
x ?? x;
|
||||
}
|
||||
''');
|
||||
await resolveTestFile();
|
||||
assertNoTestErrors();
|
||||
assertType(findNode.binary('x ?? x'), 'int?');
|
||||
}
|
||||
|
||||
test_local_nullCoalesceAssign_nullableInt_int() async {
|
||||
await addTestFile(r'''
|
||||
m() {
|
||||
int? x;
|
||||
int y;
|
||||
x ??= y;
|
||||
}
|
||||
''');
|
||||
await resolveTestFile();
|
||||
assertNoTestErrors();
|
||||
assertType(findNode.assignment('x ??= y'), 'int');
|
||||
}
|
||||
|
||||
test_local_nullCoalesceAssign_nullableInt_nullableInt() async {
|
||||
await addTestFile(r'''
|
||||
m() {
|
||||
int? x;
|
||||
x ??= x;
|
||||
}
|
||||
''');
|
||||
await resolveTestFile();
|
||||
assertNoTestErrors();
|
||||
assertType(findNode.assignment('x ??= x'), 'int?');
|
||||
}
|
||||
|
||||
test_local_parameter_interfaceType() async {
|
||||
addTestFile('''
|
||||
main() {
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// SharedOptions=--enable-experiment=non-nullable
|
||||
|
||||
// Test that `x ?? y` results in type LUB(x!, y)
|
||||
void main() {
|
||||
f1(null, 2);
|
||||
}
|
||||
|
||||
void f1(
|
||||
int? nullableInt,
|
||||
int nonNullInt,
|
||||
) {
|
||||
(nullableInt ?? nonNullInt) + 1; //# 00: ok
|
||||
(nullableInt ?? nullableInt) + 1; //# 01: compile-time error
|
||||
(nonNullInt ?? nullableInt) + 1; //# 02: compile-time error
|
||||
(nonNullInt ?? nonNullInt) + 1; //# 03: ok
|
||||
}
|
||||
|
||||
// TODO(mfairhurst) add cases with type parameter types
|
Loading…
Reference in a new issue