Use promotedBound for TypeParameterType promotion.

Change-Id: I217a8b9140f60d4f6a9ffc052564388c24c1a7ff
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/136100
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2020-02-18 17:11:44 +00:00 committed by commit-bot@chromium.org
parent 999eeea5a3
commit 21e944f1bc
12 changed files with 577 additions and 770 deletions

View file

@ -10,7 +10,6 @@ import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/display_string_builder.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/nullability_eliminator.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_algebra.dart';
import 'package:analyzer/src/generated/engine.dart' show AnalysisContext;
import 'package:analyzer/src/generated/java_engine.dart';
@ -1016,82 +1015,6 @@ class TopLevelVariableMember extends VariableMember
}
}
/**
* A type parameter defined inside of another parameterized type, where the
* values of the enclosing type parameters are known.
*
* For example:
*
* class C<T> {
* S m<S extends T>(S s);
* }
*
* If we have `C<num>.m` and we ask for the type parameter "S", we should get
* `<S extends num>` instead of `<S extends T>`. This is how the parameter
* and return types work, see: [FunctionType.parameters],
* [FunctionType.returnType], and [ParameterMember].
*/
class TypeParameterMember extends Member implements TypeParameterElement {
DartType _bound;
DartType _type;
TypeParameterMember(TypeParameterElement declaration,
MapSubstitution substitution, this._bound)
: super(declaration, substitution, false) {
_type = TypeParameterTypeImpl(
element: this,
nullabilitySuffix: NullabilitySuffix.star,
);
}
@deprecated
@override
TypeParameterElement get baseElement => declaration;
@override
DartType get bound => _bound;
@override
TypeParameterElement get declaration =>
super.declaration as TypeParameterElement;
@override
Element get enclosingElement => declaration.enclosingElement;
@override
int get hashCode => declaration.hashCode;
@override
TypeParameterType get type => _type;
@override
bool operator ==(Object other) {
if (other is TypeParameterMember) {
return declaration == other.declaration;
}
return declaration == other;
}
@override
T accept<T>(ElementVisitor<T> visitor) =>
visitor.visitTypeParameterElement(this);
@override
void appendTo(ElementDisplayStringBuilder builder) {
builder.writeTypeParameter(this);
}
@override
TypeParameterType instantiate({
@required NullabilitySuffix nullabilitySuffix,
}) {
return TypeParameterTypeImpl(
element: this,
nullabilitySuffix: nullabilitySuffix,
);
}
}
/**
* A variable element defined in a parameterized type where the values of the
* type parameters are known.

View file

@ -6,7 +6,6 @@ import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/member.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_algebra.dart';
import 'package:analyzer/src/dart/element/type_provider.dart';
@ -234,11 +233,18 @@ class NormalizeHelper {
return (S as TypeImpl).withNullability(NullabilitySuffix.star);
}
/// NORM(X extends T)
/// NORM(X & T)
DartType _typeParameterType(TypeParameterType T) {
/// NORM(X extends T)
DartType _typeParameterType(TypeParameterTypeImpl T) {
var element = T.element;
// NORM(X & T)
if (T.promotedBound != null) {
// let S be NORM(T)
var S = _normalize(T.promotedBound);
return _typeParameterType_promoted(element, S);
}
var bound = element.bound;
if (bound == null) {
return T;
@ -258,18 +264,18 @@ class NormalizeHelper {
return NeverTypeImpl.instance;
}
if (element is TypeParameterMember) {
return _typeParameterType_promoted(element, S);
} else {
// NORM(X extends T)
// * else X
return T;
}
// else X extends T
return T;
}
/// NORM(X & T)
/// * let S be NORM(T)
DartType _typeParameterType_promoted(TypeParameterMember X, DartType S) {
DartType _typeParameterType_promoted(TypeParameterElement X, DartType S) {
// * if S is Never then Never
if (identical(S, NeverTypeImpl.instance)) {
return NeverTypeImpl.instance;
}
// * if S is a top type then X
if (typeSystem.isTop(S)) {
return X.declaration.instantiate(
@ -301,13 +307,10 @@ class NormalizeHelper {
}
// * else X & S
var promoted = TypeParameterMember(
X.declaration,
Substitution.empty,
S,
);
return promoted.instantiate(
return TypeParameterTypeImpl(
element: X.declaration,
nullabilitySuffix: NullabilitySuffix.none,
promotedBound: S,
);
}
}

View file

@ -2040,6 +2040,12 @@ class TypeParameterTypeImpl extends TypeImpl implements TypeParameterType {
@override
final NullabilitySuffix nullabilitySuffix;
/// An optional promoted bound on the type parameter.
///
/// 'null' indicates that the type parameter's bound has not been promoted and
/// is therefore the same as the bound of [element].
final DartType promotedBound;
/**
* Initialize a newly created type parameter type to be declared by the given
* [element] and to have the given name.
@ -2047,10 +2053,12 @@ class TypeParameterTypeImpl extends TypeImpl implements TypeParameterType {
TypeParameterTypeImpl({
@required TypeParameterElement element,
@required this.nullabilitySuffix,
this.promotedBound,
}) : super(element);
@override
DartType get bound => element.bound ?? DynamicTypeImpl.instance;
DartType get bound =>
promotedBound ?? element.bound ?? DynamicTypeImpl.instance;
@override
ElementLocation get definition => element.location;
@ -2077,22 +2085,7 @@ class TypeParameterTypeImpl extends TypeImpl implements TypeParameterType {
return false;
}
}
// If the same declaration, or the same promoted element.
if (identical(other.element, element)) {
return true;
}
// If the same declaration, but one or both are promoted.
if (identical(other.element.declaration, element.declaration)) {
return other.bound == bound;
}
// The rare case when we have equal, but not the same declarations.
// This happens when we create fresh elements, when new library context.
// Type promotion works only locally, where we have same declarations.
// So, there is no need to check bounds.
return true;
return other.promotedBound == promotedBound;
}
return false;
@ -2111,6 +2104,10 @@ class TypeParameterTypeImpl extends TypeImpl implements TypeParameterType {
@override
DartType resolveToBound(DartType objectType) {
if (promotedBound != null) {
return promotedBound;
}
if (element.bound == null) {
return objectType;
}
@ -2184,6 +2181,7 @@ class TypeParameterTypeImpl extends TypeImpl implements TypeParameterType {
return TypeParameterTypeImpl(
element: element,
nullabilitySuffix: nullabilitySuffix,
promotedBound: promotedBound,
);
}

View file

@ -16,7 +16,6 @@ import 'package:analyzer/dart/element/type_system.dart' as public;
import 'package:analyzer/error/listener.dart' show ErrorReporter;
import 'package:analyzer/src/dart/element/display_string_builder.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/member.dart' show TypeParameterMember;
import 'package:analyzer/src/dart/element/normalize.dart';
import 'package:analyzer/src/dart/element/nullability_eliminator.dart';
import 'package:analyzer/src/dart/element/runtime_type_equality.dart';
@ -973,9 +972,16 @@ class Dart2TypeSystem extends TypeSystem {
// BOTTOM(X&T) is true iff BOTTOM(T)
// BOTTOM(X extends T) is true iff BOTTOM(T)
if (type is TypeParameterType) {
var T = type.element.bound;
return isBottom(T);
if (type is TypeParameterTypeImpl) {
var T = type.promotedBound;
if (T != null) {
return isBottom(T);
}
T = type.element.bound;
if (T != null) {
return isBottom(T);
}
}
// BOTTOM(T) is false otherwise
@ -1049,28 +1055,31 @@ class Dart2TypeSystem extends TypeSystem {
}
// Type parameters.
if (T is TypeParameterType && S is TypeParameterType) {
if (T is TypeParameterTypeImpl && S is TypeParameterTypeImpl) {
// We have eliminated the possibility that T_nullability or S_nullability
// is anything except none by this point.
assert(T_nullability == NullabilitySuffix.none);
assert(S_nullability == NullabilitySuffix.none);
var T_element = T.element;
var S_element = S.element;
// MOREBOTTOM(X&T, Y&S) = MOREBOTTOM(T, S)
if (T_element is TypeParameterMember &&
S_element is TypeParameterMember) {
var T_bound = T_element.bound;
var S_bound = S_element.bound;
return isMoreBottom(T_bound, S_bound);
var T_promotedBound = T.promotedBound;
var S_promotedBound = S.promotedBound;
if (T_promotedBound != null && S_promotedBound != null) {
return isMoreBottom(T_promotedBound, S_promotedBound);
}
// MOREBOTTOM(X&T, S) = true
if (T_element is TypeParameterMember) {
if (T_promotedBound != null) {
return true;
}
// MOREBOTTOM(T, Y&S) = false
if (S_element is TypeParameterMember) {
if (S_promotedBound != null) {
return false;
}
// MOREBOTTOM(X extends T, Y extends S) = MOREBOTTOM(T, S)
var T_bound = T_element.bound;
var S_bound = S_element.bound;
@ -1282,11 +1291,17 @@ class Dart2TypeSystem extends TypeSystem {
// * if `T0` is an unpromoted type variable with bound `B`,
// then `T0 <: T1` iff `B <: Object`.
// * if `T0` is a promoted type variable `X & S`,
// then `T0 <: T1`iff `S <: Object`.
// then `T0 <: T1` iff `S <: Object`.
if (T0_nullability == NullabilitySuffix.none &&
T0 is TypeParameterTypeImpl) {
var bound = T0.element.bound ?? objectQuestion;
return isSubtypeOf2(bound, objectNone);
var S = T0.promotedBound;
var B = T0.element.bound;
if (S == null && B != null) {
return isSubtypeOf2(B, objectNone);
}
if (S != null) {
return isSubtypeOf2(S, objectNone);
}
}
// * if `T0` is `FutureOr<S>` for some `S`,
// then `T0 <: T1` iff `S <: Object`
@ -1376,21 +1391,24 @@ class Dart2TypeSystem extends TypeSystem {
return isSubtypeOf2(S0, T1) && isSubtypeOf2(nullNone, T1);
}
// Type Variable Reflexivity 1: if T0 is a type variable X0 or a promoted
// type variables X0 & S0 and T1 is X0 then:
// * T0 <: T1
if (T0 is TypeParameterTypeImpl &&
T1 is TypeParameterTypeImpl &&
T1.promotedBound == null &&
T0.element == T1.element) {
return true;
}
// Right Promoted Variable: if `T1` is a promoted type variable `X1 & S1`:
// * `T0 <: T1` iff `T0 <: X1` and `T0 <: S1`
if (T0 is TypeParameterTypeImpl) {
if (T1 is TypeParameterTypeImpl && T0.definition == T1.definition) {
var S0 = T0.element.bound ?? objectQuestion;
var S1 = T1.element.bound ?? objectQuestion;
if (isSubtypeOf2(S0, S1)) {
return true;
}
}
var T0_element = T0.element;
if (T0_element is TypeParameterMember) {
return isSubtypeOf2(T0_element.bound, T1);
}
if (T1 is TypeParameterTypeImpl && T1.promotedBound != null) {
var X1 = TypeParameterTypeImpl(
element: T1.element,
nullabilitySuffix: T1.nullabilitySuffix,
);
return isSubtypeOf2(T0, X1) && isSubtypeOf2(T0, T1.promotedBound);
}
// Right FutureOr: if `T1` is `FutureOr<S1>` then:
@ -1414,8 +1432,12 @@ class Dart2TypeSystem extends TypeSystem {
// * or `T0` is `X0` and `X0` has bound `S0` and `S0 <: T1`
// * or `T0` is `X0 & S0` and `S0 <: T1`
if (T0 is TypeParameterTypeImpl) {
var S0 = T0.element.bound ?? objectQuestion;
if (isSubtypeOf2(S0, T1)) {
var S0 = T0.promotedBound;
if (S0 != null && isSubtypeOf2(S0, T1)) {
return true;
}
var B0 = T0.element.bound;
if (B0 != null && isSubtypeOf2(B0, T1)) {
return true;
}
}
@ -1438,8 +1460,14 @@ class Dart2TypeSystem extends TypeSystem {
// or `T0` is `X0` and `X0` has bound `S0` and `S0 <: T1`
// or `T0` is `X0 & S0` and `S0 <: T1`
if (T0 is TypeParameterTypeImpl) {
var S0 = T0.element.bound ?? objectQuestion;
return isSubtypeOf2(S0, T1);
var S0 = T0.promotedBound;
if (S0 != null && isSubtypeOf2(S0, T1)) {
return true;
}
var B0 = T0.element.bound;
if (B0 != null && isSubtypeOf2(B0, T1)) {
return true;
}
}
// iff
return false;
@ -1457,8 +1485,13 @@ class Dart2TypeSystem extends TypeSystem {
// Left Type Variable Bound: `T0` is a type variable `X0` with bound `B0`
// * and `B0 <: T1`
if (T0 is TypeParameterTypeImpl) {
var S0 = T0.element.bound ?? objectQuestion;
if (isSubtypeOf2(S0, T1)) {
var S0 = T0.promotedBound;
if (S0 != null && isSubtypeOf2(S0, T1)) {
return true;
}
var B0 = T0.element.bound;
if (B0 != null && isSubtypeOf2(B0, T1)) {
return true;
}
}
@ -1604,9 +1637,10 @@ class Dart2TypeSystem extends TypeSystem {
if (from is TypeParameterType) {
if (isSubtypeOf2(to, from.bound ?? DynamicTypeImpl.instance)) {
var declaration = from.element.declaration;
var newElement = TypeParameterMember(declaration, null, to);
return newElement.instantiate(
return TypeParameterTypeImpl(
element: declaration,
nullabilitySuffix: from.nullabilitySuffix,
promotedBound: to,
);
}
}
@ -3564,32 +3598,29 @@ abstract class TypeSystem implements public.TypeSystem {
if (type is TypeParameterTypeImpl) {
var element = type.element;
var promotedBound =
promoteToNonNull(element.bound ?? typeProvider.objectType);
var identicalBound = identical(promotedBound, element.bound);
if (identicalBound) {
if (type.nullabilitySuffix == NullabilitySuffix.none) {
return type;
} else {
return TypeParameterTypeImpl(
element: element,
nullabilitySuffix: NullabilitySuffix.none,
);
}
} else {
// Note: we need to use `element.declaration` because `element` might
// itself be a TypeParameterMember (due to a previous promotion), and
// you can't create a TypeParameterMember wrapping a
// TypeParameterMember.
// NonNull(X & T) = X & NonNull(T)
if (type.promotedBound != null) {
var promotedBound = promoteToNonNull(type.promotedBound);
return TypeParameterTypeImpl(
element: TypeParameterMember(
element.declaration,
null,
promotedBound,
),
element: element,
nullabilitySuffix: NullabilitySuffix.none,
promotedBound: promotedBound,
);
}
// NonNull(X) = X & NonNull(B), where B is the bound of X
var promotedBound = element.bound != null
? promoteToNonNull(element.bound)
: typeProvider.objectType;
if (identical(promotedBound, element.bound)) {
promotedBound = null;
}
return TypeParameterTypeImpl(
element: element,
nullabilitySuffix: NullabilitySuffix.none,
promotedBound: promotedBound,
);
}
return (type as TypeImpl).withNullability(NullabilitySuffix.none);

View file

@ -7,7 +7,6 @@ import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/member.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/resolver/variance.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
@ -473,12 +472,49 @@ mixin ElementsTypesMixin {
return parameter;
}
TypeParameterMember promoteTypeParameter(
TypeParameterTypeImpl promotedTypeParameterType({
@required TypeParameterElement element,
@required NullabilitySuffix nullabilitySuffix,
@required DartType promotedBound,
}) {
return TypeParameterTypeImpl(
element: element,
nullabilitySuffix: nullabilitySuffix,
promotedBound: promotedBound,
);
}
TypeParameterTypeImpl promotedTypeParameterTypeNone(
TypeParameterElement element,
DartType bound,
DartType promotedBound,
) {
assert(element is! TypeParameterMember);
return TypeParameterMember(element, null, bound);
return promotedTypeParameterType(
element: element,
nullabilitySuffix: NullabilitySuffix.none,
promotedBound: promotedBound,
);
}
TypeParameterTypeImpl promotedTypeParameterTypeQuestion(
TypeParameterElement element,
DartType promotedBound,
) {
return promotedTypeParameterType(
element: element,
nullabilitySuffix: NullabilitySuffix.question,
promotedBound: promotedBound,
);
}
TypeParameterTypeImpl promotedTypeParameterTypeStar(
TypeParameterElement element,
DartType promotedBound,
) {
return promotedTypeParameterType(
element: element,
nullabilitySuffix: NullabilitySuffix.star,
promotedBound: promotedBound,
);
}
ParameterElement requiredParameter({

View file

@ -13,7 +13,6 @@ import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/analysis/session.dart';
import 'package:analyzer/src/dart/ast/token.dart' show KeywordToken;
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/member.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/resolver/variance.dart';
import 'package:analyzer/src/error/codes.dart';
@ -3326,13 +3325,12 @@ class TryPromoteToTest extends AbstractTypeSystemTest {
test_typeParameter() {
void check(
TypeParameterType type,
TypeParameterElement expectedDeclaration,
TypeParameterTypeImpl type,
TypeParameterElement expectedElement,
DartType expectedBound,
) {
var actualElement = type.element as TypeParameterMember;
expect(actualElement.declaration, expectedDeclaration);
expect(actualElement.bound, expectedBound);
expect(type.element, expectedElement);
expect(type.promotedBound, expectedBound);
}
var T = typeParameter('T');

View file

@ -11,7 +11,6 @@ import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/null_safety_understanding_flag.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/analysis/testing_data.dart';
import 'package:analyzer/src/dart/element/member.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/util/ast_data_extractor.dart';
@ -73,10 +72,11 @@ class _TypePromotionDataInterpreter implements DataInterpreter<DartType> {
@override
String getText(DartType actualData) {
if (actualData is TypeParameterType) {
if (actualData is TypeParameterTypeImpl) {
var element = actualData.element;
if (element is TypeParameterMember) {
return '${element.name} & ${_typeToString(element.bound)}';
var promotedBound = actualData.promotedBound;
if (promotedBound != null) {
return '${element.name} & ${_typeToString(promotedBound)}';
}
}
return _typeToString(actualData);

View file

@ -7,7 +7,6 @@ import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/element/member.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_visitor.dart';
import 'package:analyzer/src/generated/resolver.dart' show TypeSystemImpl;
@ -415,51 +414,45 @@ class NormalizeTypeTest with ElementsTypesMixin {
// * if S is Never then Never
_check(
typeParameterTypeNone(
promoteTypeParameter(T, neverNone),
),
promotedTypeParameterTypeNone(T, neverNone),
neverNone,
);
// * if S is a top type then X
_check(
typeParameterTypeNone(
promoteTypeParameter(T, objectQuestion),
),
promotedTypeParameterTypeNone(T, objectQuestion),
typeParameterTypeNone(T),
);
_check(
typeParameterTypeNone(
promoteTypeParameter(T, futureOrQuestion(objectNone)),
),
promotedTypeParameterTypeNone(T, futureOrQuestion(objectNone)),
typeParameterTypeNone(T),
);
// * if S is X then X
_check(
typeParameterTypeNone(
promoteTypeParameter(T, typeParameterTypeNone(T)),
),
promotedTypeParameterTypeNone(T, typeParameterTypeNone(T)),
typeParameterTypeNone(T),
);
// * if S is Object and NORM(B) is Object where B is the bound of X then X
T = typeParameter('T', bound: objectNone);
_check(
typeParameterTypeNone(
promoteTypeParameter(T, futureOrNone(objectNone)),
),
promotedTypeParameterTypeNone(T, futureOrNone(objectNone)),
typeParameterTypeNone(T),
);
// else X & S
T = typeParameter('T');
_check(
typeParameterTypeNone(
promoteTypeParameter(T, futureOrNone(neverNone)),
promotedTypeParameterType(
element: T,
nullabilitySuffix: NullabilitySuffix.none,
promotedBound: futureOrNone(neverNone),
),
typeParameterTypeNone(
promoteTypeParameter(T, futureNone(neverNone)),
promotedTypeParameterType(
element: T,
nullabilitySuffix: NullabilitySuffix.none,
promotedBound: futureNone(neverNone),
),
);
}
@ -517,24 +510,7 @@ T2: ${_typeString(T2 as TypeImpl)}
var typeParameterCollector = _TypeParameterCollector();
DartTypeVisitor.visit(type, typeParameterCollector);
for (var typeParameter in typeParameterCollector.typeParameters) {
if (typeParameter is TypeParameterMember) {
var base = typeParameter.declaration;
var baseBound = base.bound;
if (baseBound != null) {
var baseBoundStr = baseBound.getDisplayString(withNullability: true);
typeStr += ', ${typeParameter.name} extends ' + baseBoundStr;
}
var bound = typeParameter.bound;
var boundStr = bound.getDisplayString(withNullability: true);
typeStr += ', ${typeParameter.name} & ' + boundStr;
} else {
var bound = typeParameter.bound;
if (bound != null) {
var boundStr = bound.getDisplayString(withNullability: true);
typeStr += ', ${typeParameter.name} extends ' + boundStr;
}
}
typeStr += ', $typeParameter';
}
return typeStr;
}
@ -547,7 +523,7 @@ T2: ${_typeString(T2 as TypeImpl)}
}
class _TypeParameterCollector extends DartTypeVisitor<void> {
final Set<TypeParameterElement> typeParameters = {};
final Set<String> typeParameters = {};
/// We don't need to print bounds for these type parameters, because
/// they are already included into the function type itself, and cannot
@ -590,7 +566,31 @@ class _TypeParameterCollector extends DartTypeVisitor<void> {
@override
void visitTypeParameterType(TypeParameterType type) {
if (!functionTypeParameters.contains(type.element)) {
typeParameters.add(type.element);
var bound = type.element.bound;
var promotedBound = (type as TypeParameterTypeImpl).promotedBound;
if (bound == null && promotedBound == null) {
return;
}
var str = '';
if (bound != null) {
var boundStr = bound.getDisplayString(withNullability: true);
str += '${type.element.name} extends ' + boundStr;
}
if (promotedBound != null) {
var promotedBoundStr = promotedBound.getDisplayString(
withNullability: true,
);
if (str.isNotEmpty) {
str += ', ';
}
str += '${type.element.name} & ' + promotedBoundStr;
}
typeParameters.add(str);
}
}

View file

@ -7,7 +7,7 @@ import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/element/member.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/generated/resolver.dart' show TypeSystemImpl;
import 'package:meta/meta.dart';
import 'package:test/test.dart';
@ -572,58 +572,137 @@ class PromoteToNonNullTest extends _NullableBase {
_check(nullStar, neverNone);
}
test_typeParameter_noneBound() {
test_typeParameter_bound_dynamic() {
var element = typeParameter('T', bound: dynamicNone);
var result = typeSystem.promoteToNonNull(
typeParameterTypeNone(element),
) as TypeParameterTypeImpl;
expect(result.element, same(element));
expect(result.promotedBound, isNull);
expect(result.nullabilitySuffix, NullabilitySuffix.none);
}
test_typeParameter_bound_none() {
var element = typeParameter('T', bound: intNone);
_checkTypeParameter(
typeParameterTypeNone(element),
baseElement: element,
element: element,
promotedBound: null,
);
_checkTypeParameter(
typeParameterTypeQuestion(element),
baseElement: element,
element: element,
promotedBound: null,
);
}
test_typeParameter_nullBound() {
test_typeParameter_bound_null() {
var element = typeParameter('T', bound: null);
_checkTypeParameter(
typeParameterTypeNone(element),
baseElement: element,
expectedBound: objectNone,
element: element,
promotedBound: objectNone,
);
}
test_typeParameter_questionBound() {
test_typeParameter_bound_question() {
var element = typeParameter('T', bound: intQuestion);
_checkTypeParameter(
typeParameterTypeNone(element),
baseElement: element,
expectedBound: intNone,
element: element,
promotedBound: intNone,
);
_checkTypeParameter(
typeParameterTypeQuestion(element),
baseElement: element,
expectedBound: intNone,
element: element,
promotedBound: intNone,
);
_checkTypeParameter(
typeParameterTypeStar(element),
baseElement: element,
expectedBound: intNone,
element: element,
promotedBound: intNone,
);
}
test_typeParameter_starBound() {
test_typeParameter_bound_star() {
var element = typeParameter('T', bound: intStar);
_checkTypeParameter(
typeParameterTypeNone(element),
baseElement: element,
expectedBound: intNone,
element: element,
promotedBound: intNone,
);
}
test_typeParameter_promotedBound_none() {
var element = typeParameter('T', bound: numQuestion);
_checkTypeParameter(
promotedTypeParameterTypeNone(element, intNone),
element: element,
promotedBound: intNone,
);
_checkTypeParameter(
promotedTypeParameterTypeQuestion(element, intNone),
element: element,
promotedBound: intNone,
);
_checkTypeParameter(
promotedTypeParameterTypeStar(element, intNone),
element: element,
promotedBound: intNone,
);
}
test_typeParameter_promotedBound_question() {
var element = typeParameter('T', bound: numQuestion);
_checkTypeParameter(
promotedTypeParameterTypeNone(element, intQuestion),
element: element,
promotedBound: intNone,
);
_checkTypeParameter(
promotedTypeParameterTypeQuestion(element, intQuestion),
element: element,
promotedBound: intNone,
);
_checkTypeParameter(
promotedTypeParameterTypeStar(element, intQuestion),
element: element,
promotedBound: intNone,
);
}
test_typeParameter_promotedBound_star() {
var element = typeParameter('T', bound: numQuestion);
_checkTypeParameter(
promotedTypeParameterTypeNone(element, intStar),
element: element,
promotedBound: intNone,
);
_checkTypeParameter(
promotedTypeParameterTypeQuestion(element, intStar),
element: element,
promotedBound: intNone,
);
_checkTypeParameter(
promotedTypeParameterTypeStar(element, intStar),
element: element,
promotedBound: intNone,
);
}
@ -636,22 +715,14 @@ class PromoteToNonNullTest extends _NullableBase {
expect(result, expected);
}
/// If [expectedBound] is `null`, the element of the result must be the same
/// as the [baseElement]. Otherwise the element of the result must be a
/// [TypeParameterMember] with the [baseElement] and the [expectedBound].
void _checkTypeParameter(
TypeParameterType type, {
@required TypeParameterElement baseElement,
DartType expectedBound,
@required TypeParameterElement element,
@required DartType promotedBound,
}) {
var actual = typeSystem.promoteToNonNull(type);
if (expectedBound != null) {
var actualMember = actual.element as TypeParameterMember;
expect(actualMember.declaration, same(baseElement));
expect(actualMember.bound, expectedBound);
} else {
expect(actual.element, same(baseElement));
}
var actual = typeSystem.promoteToNonNull(type) as TypeParameterTypeImpl;
expect(actual.element, same(element));
expect(actual.promotedBound, promotedBound);
expect(actual.nullabilitySuffix, NullabilitySuffix.none);
}
}

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,6 @@
import 'package:analyzer/dart/element/null_safety_understanding_flag.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/member.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@ -48,95 +47,6 @@ class TypeParameterElementTest extends _TypeParameterElementBase {
expect(T1 == T2, isFalse);
expect(T2 == T1, isFalse);
}
test_equal_elementMember_sameBase_differentBounds() {
var T = typeParameter('T');
_setEnclosingElement(T);
var M = TypeParameterMember(T, null, typeProvider.intType);
expect(_equal(T, M), isTrue);
expect(_equal(M, T), isTrue);
}
test_equal_elementMember_sameBase_equalBounds() {
var T = typeParameter('T', bound: typeProvider.intType);
_setEnclosingElement(T);
var M = TypeParameterMember(T, null, typeProvider.intType);
expect(_equal(T, M), isTrue);
expect(_equal(M, T), isTrue);
}
test_equal_memberMember2_differentBase() {
var T1 = typeParameter('T');
var T2 = typeParameter('T');
var M1 = TypeParameterMember(T1, null, typeProvider.numType);
var M2 = TypeParameterMember(T2, null, typeProvider.numType);
expect(M1 == M2, isFalse);
}
test_equal_memberMember2_sameBase_differentBounds() {
var T = typeParameter('T');
var M1 = TypeParameterMember(T, null, typeProvider.intType);
var M2 = TypeParameterMember(T, null, typeProvider.doubleType);
expect(M1 == M2, isTrue);
}
test_equal_memberMember2_sameBase_equalBounds() {
var T = typeParameter('T');
var M1 = TypeParameterMember(T, null, typeProvider.numType);
var M2 = TypeParameterMember(T, null, typeProvider.numType);
expect(M1 == M2, isTrue);
expect(M2 == M1, isTrue);
}
test_equal_memberMember_differentBase() {
var T1 = typeParameter('T1');
var T2 = typeParameter('T2');
_setEnclosingElement(T1);
_setEnclosingElement(T2);
var M1 = TypeParameterMember(T1, null, typeProvider.numType);
var M2 = TypeParameterMember(T2, null, typeProvider.numType);
expect(M1 == M2, isFalse);
}
test_equal_memberMember_sameBase_differentBounds() {
var T = typeParameter('T');
_setEnclosingElement(T);
var M1 = TypeParameterMember(T, null, typeProvider.intType);
var M2 = TypeParameterMember(T, null, typeProvider.doubleType);
expect(M1 == M2, isTrue);
}
test_equal_memberMember_sameBase_equalBounds() {
var T = typeParameter('T');
_setEnclosingElement(T);
var M1 = TypeParameterMember(T, null, typeProvider.numType);
var M2 = TypeParameterMember(T, null, typeProvider.numType);
expect(M1 == M2, isTrue);
expect(M2 == M1, isTrue);
}
/// We use this method to work around the lint for using `==` for values
/// that are not of the same type.
static bool _equal(a, b) {
return a == b;
}
}
@reflectiveTest
@ -178,20 +88,33 @@ class TypeParameterTypeTest extends _TypeParameterElementBase {
_assertEqual(typeParameterTypeStar(T1), typeParameterTypeNone(T2), isFalse);
}
test_equal_sameElement_differentBounds() {
test_equal_sameElement_promotedBounds() {
var T = typeParameter('T');
_setEnclosingElement(T);
var T1 = TypeParameterMember(T, null, typeProvider.intType);
var T2 = TypeParameterMember(T, null, typeProvider.doubleType);
_assertEqual(
promotedTypeParameterTypeNone(T, intNone),
promotedTypeParameterTypeNone(T, intNone),
isTrue,
);
_assertEqual(typeParameterTypeNone(T1), typeParameterTypeNone(T1), isTrue);
_assertEqual(
promotedTypeParameterTypeNone(T, intNone),
promotedTypeParameterTypeNone(T, doubleNone),
isFalse,
);
_assertEqual(typeParameterTypeNone(T1), typeParameterTypeNone(T2), isFalse);
_assertEqual(typeParameterTypeNone(T2), typeParameterTypeNone(T1), isFalse);
_assertEqual(
promotedTypeParameterTypeNone(T, intNone),
typeParameterTypeNone(T),
isFalse,
);
_assertEqual(typeParameterTypeNone(T1), typeParameterTypeNone(T), isFalse);
_assertEqual(typeParameterTypeNone(T), typeParameterTypeNone(T1), isFalse);
_assertEqual(
typeParameterTypeNone(T),
promotedTypeParameterTypeNone(T, intNone),
isFalse,
);
}
test_equal_sameElements() {

View file

@ -7,7 +7,6 @@ import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/element/member.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_visitor.dart';
import 'package:analyzer/src/generated/resolver.dart' show TypeSystemImpl;
@ -112,7 +111,6 @@ class BoundsHelperPredicatesTest extends _BoundsTestBase {
test_isBottom() {
TypeParameterElement T;
TypeParameterMember T2;
// BOTTOM(Never) is true
isBottom(neverNone);
@ -122,15 +120,13 @@ class BoundsHelperPredicatesTest extends _BoundsTestBase {
// BOTTOM(X&T) is true iff BOTTOM(T)
T = typeParameter('T', bound: objectQuestion);
T2 = promoteTypeParameter(T, neverNone);
isBottom(typeParameterTypeNone(T2));
isBottom(typeParameterTypeQuestion(T2));
isBottom(typeParameterTypeStar(T2));
isBottom(promotedTypeParameterTypeNone(T, neverNone));
isBottom(promotedTypeParameterTypeQuestion(T, neverNone));
isBottom(promotedTypeParameterTypeStar(T, neverNone));
T2 = promoteTypeParameter(T, neverQuestion);
isNotBottom(typeParameterTypeNone(T2));
isNotBottom(typeParameterTypeQuestion(T2));
isNotBottom(typeParameterTypeStar(T2));
isNotBottom(promotedTypeParameterTypeNone(T, neverQuestion));
isNotBottom(promotedTypeParameterTypeQuestion(T, neverQuestion));
isNotBottom(promotedTypeParameterTypeStar(T, neverQuestion));
// BOTTOM(X extends T) is true iff BOTTOM(T)
T = typeParameter('T', bound: neverNone);
@ -165,10 +161,9 @@ class BoundsHelperPredicatesTest extends _BoundsTestBase {
isNotBottom(typeParameterTypeQuestion(T));
isNotBottom(typeParameterTypeStar(T));
T2 = promoteTypeParameter(typeParameter('T'), intNone);
isNotBottom(typeParameterTypeNone(T2));
isNotBottom(typeParameterTypeQuestion(T2));
isNotBottom(typeParameterTypeStar(T2));
isNotBottom(promotedTypeParameterTypeNone(T, intNone));
isNotBottom(promotedTypeParameterTypeQuestion(T, intNone));
isNotBottom(promotedTypeParameterTypeStar(T, intNone));
}
test_isMoreBottom() {
@ -238,27 +233,21 @@ class BoundsHelperPredicatesTest extends _BoundsTestBase {
// MOREBOTTOM(X&T, Y&S) = MOREBOTTOM(T, S)
isMoreBottom(
typeParameterTypeNone(
promoteTypeParameter(
typeParameter('T', bound: objectQuestion),
neverNone,
),
promotedTypeParameterTypeNone(
typeParameter('T', bound: objectQuestion),
neverNone,
),
typeParameterTypeQuestion(
promoteTypeParameter(
typeParameter('S', bound: objectQuestion),
neverNone,
),
promotedTypeParameterTypeQuestion(
typeParameter('S', bound: objectQuestion),
neverNone,
),
);
// MOREBOTTOM(X&T, S) = true
isMoreBottom(
typeParameterTypeNone(
promoteTypeParameter(
typeParameter('T', bound: objectQuestion),
neverNone,
),
promotedTypeParameterTypeNone(
typeParameter('T', bound: objectQuestion),
neverNone,
),
typeParameterTypeNone(
typeParameter('S', bound: neverNone),
@ -270,11 +259,9 @@ class BoundsHelperPredicatesTest extends _BoundsTestBase {
typeParameterTypeNone(
typeParameter('T', bound: neverNone),
),
typeParameterTypeNone(
promoteTypeParameter(
typeParameter('S', bound: objectQuestion),
neverNone,
),
promotedTypeParameterTypeNone(
typeParameter('S', bound: objectQuestion),
neverNone,
),
);
@ -573,11 +560,9 @@ class LowerBoundTest extends _BoundsTestBase {
}
{
var T = typeParameterTypeNone(
promoteTypeParameter(
typeParameter('T', bound: objectQuestion),
neverNone,
),
var T = promotedTypeParameterTypeNone(
typeParameter('T', bound: objectQuestion),
neverNone,
);
check(T, intNone);
check(T, intQuestion);
@ -601,11 +586,9 @@ class LowerBoundTest extends _BoundsTestBase {
check(
neverNone,
typeParameterTypeNone(
promoteTypeParameter(
typeParameter('T', bound: objectQuestion),
neverNone,
),
promotedTypeParameterTypeNone(
typeParameter('T', bound: objectQuestion),
neverNone,
),
);
}
@ -1101,10 +1084,18 @@ class LowerBoundTest extends _BoundsTestBase {
{
var T = typeParameter('T', bound: objectQuestion);
var T2 = promoteTypeParameter(T, objectNone);
check(typeParameterTypeNone(T), typeParameterTypeNone(T2));
check(typeParameterTypeQuestion(T), typeParameterTypeNone(T2));
check(typeParameterTypeStar(T), typeParameterTypeNone(T2));
check(
typeParameterTypeNone(T),
promotedTypeParameterTypeNone(T, objectNone),
);
check(
typeParameterTypeQuestion(T),
promotedTypeParameterTypeNone(T, objectNone),
);
check(
typeParameterTypeStar(T),
promotedTypeParameterTypeNone(T, objectNone),
);
}
{
@ -1300,8 +1291,8 @@ actual: $resultStr
// Check that the result is a lower bound.
if (checkSubtype) {
expect(typeSystem.isSubtypeOf(result, T1), true);
expect(typeSystem.isSubtypeOf(result, T2), true);
expect(typeSystem.isSubtypeOf2(result, T1), true);
expect(typeSystem.isSubtypeOf2(result, T2), true);
}
// Check for symmetry.
@ -1349,11 +1340,9 @@ class UpperBoundTest extends _BoundsTestBase {
}
{
var T = typeParameterTypeNone(
promoteTypeParameter(
typeParameter('T', bound: objectQuestion),
neverNone,
),
var T = promotedTypeParameterTypeNone(
typeParameter('T', bound: objectQuestion),
neverNone,
);
check(T, intNone);
check(T, intQuestion);
@ -1377,11 +1366,9 @@ class UpperBoundTest extends _BoundsTestBase {
check(
neverNone,
typeParameterTypeNone(
promoteTypeParameter(
typeParameter('T', bound: objectQuestion),
neverNone,
),
promotedTypeParameterTypeNone(
typeParameter('T', bound: objectQuestion),
neverNone,
),
);
}
@ -2063,8 +2050,8 @@ actual: $resultStr
''');
// Check that the result is an upper bound.
expect(typeSystem.isSubtypeOf(T1, result), true);
expect(typeSystem.isSubtypeOf(T2, result), true);
expect(typeSystem.isSubtypeOf2(T1, result), true);
expect(typeSystem.isSubtypeOf2(T2, result), true);
// Check for symmetry.
result = typeSystem.getLeastUpperBound(T2, T1);
@ -2150,24 +2137,7 @@ class _BoundsTestBase with ElementsTypesMixin {
var typeParameterCollector = _TypeParameterCollector();
DartTypeVisitor.visit(type, typeParameterCollector);
for (var typeParameter in typeParameterCollector.typeParameters) {
if (typeParameter is TypeParameterMember) {
var base = typeParameter.declaration;
var baseBound = base.bound;
if (baseBound != null) {
var baseBoundStr = baseBound.getDisplayString(withNullability: true);
typeStr += ', ${typeParameter.name} extends ' + baseBoundStr;
}
var bound = typeParameter.bound;
var boundStr = bound.getDisplayString(withNullability: true);
typeStr += ', ${typeParameter.name} & ' + boundStr;
} else {
var bound = typeParameter.bound;
if (bound != null) {
var boundStr = bound.getDisplayString(withNullability: true);
typeStr += ', ${typeParameter.name} extends ' + boundStr;
}
}
typeStr += ', $typeParameter';
}
return typeStr;
}
@ -2180,7 +2150,7 @@ class _BoundsTestBase with ElementsTypesMixin {
}
class _TypeParameterCollector extends DartTypeVisitor<void> {
final Set<TypeParameterElement> typeParameters = {};
final Set<String> typeParameters = {};
/// We don't need to print bounds for these type parameters, because
/// they are already included into the function type itself, and cannot
@ -2223,7 +2193,31 @@ class _TypeParameterCollector extends DartTypeVisitor<void> {
@override
void visitTypeParameterType(TypeParameterType type) {
if (!functionTypeParameters.contains(type.element)) {
typeParameters.add(type.element);
var bound = type.element.bound;
var promotedBound = (type as TypeParameterTypeImpl).promotedBound;
if (bound == null && promotedBound == null) {
return;
}
var str = '';
if (bound != null) {
var boundStr = bound.getDisplayString(withNullability: true);
str += '${type.element.name} extends ' + boundStr;
}
if (promotedBound != null) {
var promotedBoundStr = promotedBound.getDisplayString(
withNullability: true,
);
if (str.isNotEmpty) {
str += ', ';
}
str += '${type.element.name} & ' + promotedBoundStr;
}
typeParameters.add(str);
}
}