| // Copyright (c) 2020, 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. |
| |
| 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/extensions.dart'; |
| import 'package:analyzer/src/dart/element/type.dart'; |
| import 'package:analyzer/src/dart/element/type_provider.dart'; |
| import 'package:analyzer/src/dart/element/type_schema.dart'; |
| import 'package:analyzer/src/dart/element/type_system.dart'; |
| import 'package:analyzer/src/generated/utilities_dart.dart'; |
| |
| class GreatestLowerBoundHelper { |
| final TypeSystemImpl _typeSystem; |
| |
| GreatestLowerBoundHelper(this._typeSystem); |
| |
| InterfaceTypeImpl get _nullNone => _typeSystem.nullNone; |
| |
| TypeProviderImpl get _typeProvider => _typeSystem.typeProvider; |
| |
| /// Computes the greatest lower bound of [T1] and [T2]. |
| /// |
| /// https://github.com/dart-lang/language |
| /// See `resources/type-system/upper-lower-bounds.md` |
| DartType getGreatestLowerBound(DartType T1, DartType T2) { |
| // DOWN(T, T) = T |
| if (identical(T1, T2)) { |
| return T1; |
| } |
| |
| // For any type T, DOWN(?, T) == T. |
| if (identical(T1, UnknownInferredType.instance)) { |
| return T2; |
| } |
| if (identical(T2, UnknownInferredType.instance)) { |
| return T1; |
| } |
| |
| var T1_isTop = _typeSystem.isTop(T1); |
| var T2_isTop = _typeSystem.isTop(T2); |
| |
| // DOWN(T1, T2) where TOP(T1) and TOP(T2) |
| if (T1_isTop && T2_isTop) { |
| // * T1 if MORETOP(T2, T1) |
| // * T2 otherwise |
| if (_typeSystem.isMoreTop(T2, T1)) { |
| return T1; |
| } else { |
| return T2; |
| } |
| } |
| |
| // DOWN(T1, T2) = T2 if TOP(T1) |
| if (T1_isTop) { |
| return T2; |
| } |
| |
| // DOWN(T1, T2) = T1 if TOP(T2) |
| if (T2_isTop) { |
| return T1; |
| } |
| |
| var T1_isBottom = T1.isBottom; |
| var T2_isBottom = T2.isBottom; |
| |
| // DOWN(T1, T2) where BOTTOM(T1) and BOTTOM(T2) |
| if (T1_isBottom && T2_isBottom) { |
| // * T1 if MOREBOTTOM(T1, T2) |
| // * T2 otherwise |
| if (_typeSystem.isMoreBottom(T1, T2)) { |
| return T1; |
| } else { |
| return T2; |
| } |
| } |
| |
| // DOWN(T1, T2) = T1 if BOTTOM(T1) |
| if (T1_isBottom) { |
| return T1; |
| } |
| |
| // DOWN(T1, T2) = T2 if BOTTOM(T2) |
| if (T2_isBottom) { |
| return T2; |
| } |
| |
| var T1_isNull = _typeSystem.isNull(T1); |
| var T2_isNull = _typeSystem.isNull(T2); |
| |
| // DOWN(T1, T2) where NULL(T1) and NULL(T2) |
| if (T1_isNull && T2_isNull) { |
| // * T1 if MOREBOTTOM(T1, T2) |
| // * T2 otherwise |
| if (_typeSystem.isMoreBottom(T1, T2)) { |
| return T1; |
| } else { |
| return T2; |
| } |
| } |
| |
| var T1_impl = T1 as TypeImpl; |
| var T2_impl = T2 as TypeImpl; |
| |
| var T1_nullability = T1_impl.nullabilitySuffix; |
| var T2_nullability = T2_impl.nullabilitySuffix; |
| |
| // DOWN(Null, T2) |
| if (T1_nullability == NullabilitySuffix.none && T1.isDartCoreNull) { |
| // * Null if Null <: T2 |
| // * Never otherwise |
| if (_typeSystem.isSubtypeOf(_nullNone, T2)) { |
| return _nullNone; |
| } else { |
| return NeverTypeImpl.instance; |
| } |
| } |
| |
| // DOWN(T1, Null) |
| if (T2_nullability == NullabilitySuffix.none && T2.isDartCoreNull) { |
| // * Null if Null <: T1 |
| // * Never otherwise |
| if (_typeSystem.isSubtypeOf(_nullNone, T1)) { |
| return _nullNone; |
| } else { |
| return NeverTypeImpl.instance; |
| } |
| } |
| |
| var T1_isObject = _typeSystem.isObject(T1); |
| var T2_isObject = _typeSystem.isObject(T2); |
| |
| // DOWN(T1, T2) where OBJECT(T1) and OBJECT(T2) |
| if (T1_isObject && T2_isObject) { |
| // * T1 if MORETOP(T2, T1) |
| // * T2 otherwise |
| if (_typeSystem.isMoreTop(T2, T1)) { |
| return T1; |
| } else { |
| return T2; |
| } |
| } |
| |
| // DOWN(T1, T2) where OBJECT(T1) |
| if (T1_isObject) { |
| // * T2 if T2 is non-nullable |
| if (_typeSystem.isNonNullable(T2)) { |
| return T2; |
| } |
| |
| // * NonNull(T2) if NonNull(T2) is non-nullable |
| var T2_nonNull = _typeSystem.promoteToNonNull(T2_impl); |
| if (_typeSystem.isNonNullable(T2_nonNull)) { |
| return T2_nonNull; |
| } |
| |
| // * Never otherwise |
| return NeverTypeImpl.instance; |
| } |
| |
| // DOWN(T1, T2) where OBJECT(T2) |
| if (T2_isObject) { |
| // * T1 if T1 is non-nullable |
| if (_typeSystem.isNonNullable(T1)) { |
| return T1; |
| } |
| |
| // * NonNull(T1) if NonNull(T1) is non-nullable |
| var T1_nonNull = _typeSystem.promoteToNonNull(T1_impl); |
| if (_typeSystem.isNonNullable(T1_nonNull)) { |
| return T1_nonNull; |
| } |
| |
| // * Never otherwise |
| return NeverTypeImpl.instance; |
| } |
| |
| // DOWN(T1?, T2?) = S? where S is DOWN(T1, T2) |
| // DOWN(T1?, T2) = S where S is DOWN(T1, T2) |
| // DOWN(T1, T2?) = S where S is DOWN(T1, T2) |
| if (T1_nullability != NullabilitySuffix.none || |
| T2_nullability != NullabilitySuffix.none) { |
| var T1_none = T1_impl.withNullability(NullabilitySuffix.none); |
| var T2_none = T2_impl.withNullability(NullabilitySuffix.none); |
| var S = getGreatestLowerBound(T1_none, T2_none); |
| if (T1_nullability == NullabilitySuffix.question && |
| T2_nullability == NullabilitySuffix.question) { |
| return (S as TypeImpl).withNullability(NullabilitySuffix.question); |
| } |
| return S; |
| } |
| |
| assert(T1_nullability == NullabilitySuffix.none); |
| assert(T2_nullability == NullabilitySuffix.none); |
| |
| if (T1 is FunctionTypeImpl && T2 is FunctionTypeImpl) { |
| return _functionType(T1, T2); |
| } |
| |
| if (T1 is RecordTypeImpl && T2 is RecordTypeImpl) { |
| return _recordType(T1, T2); |
| } |
| |
| // DOWN(T1, T2) = T1 if T1 <: T2 |
| if (_typeSystem.isSubtypeOf(T1, T2)) { |
| return T1; |
| } |
| |
| // DOWN(T1, T2) = T2 if T2 <: T1 |
| if (_typeSystem.isSubtypeOf(T2, T1)) { |
| return T2; |
| } |
| |
| // FutureOr<S1> |
| if (T1 is InterfaceTypeImpl && T1.isDartAsyncFutureOr) { |
| var S1 = T1.typeArguments[0]; |
| // DOWN(FutureOr<S1>, FutureOr<S2>) = FutureOr(S) |
| // S = DOWN(S1, S2) |
| if (T2 is InterfaceTypeImpl && T2.isDartAsyncFutureOr) { |
| var S2 = T2.typeArguments[0]; |
| var S = getGreatestLowerBound(S1, S2); |
| return _typeProvider.futureOrType(S); |
| } |
| // DOWN(FutureOr<S1>, Future<S2>) = Future(S) |
| // S = DOWN(S1, S2) |
| if (T2 is InterfaceTypeImpl && T2.isDartAsyncFuture) { |
| var S2 = T2.typeArguments[0]; |
| var S = getGreatestLowerBound(S1, S2); |
| return _typeProvider.futureType(S); |
| } |
| // DOWN(FutureOr<S1>, T2) = DOWN(S1, T2) |
| return getGreatestLowerBound(S1, T2); |
| } |
| |
| // FutureOr<S2> |
| if (T2 is InterfaceTypeImpl && T2.isDartAsyncFutureOr) { |
| var S2 = T2.typeArguments[0]; |
| // DOWN(Future<S1>, FutureOr<S2>) = Future<S> |
| // S = DOWN(S1, S2) |
| if (T1 is InterfaceTypeImpl && T1.isDartAsyncFuture) { |
| var S1 = T1.typeArguments[0]; |
| var S = getGreatestLowerBound(S1, S2); |
| return _typeProvider.futureType(S); |
| } |
| // DOWN(T1, FutureOr<S2>) = DOWN(T1, S2) |
| return getGreatestLowerBound(T1, S2); |
| } |
| |
| // DOWN(T1, T2) = Never otherwise |
| return NeverTypeImpl.instance; |
| } |
| |
| /// Compute the greatest lower bound of function types [f] and [g]. |
| /// |
| /// https://github.com/dart-lang/language |
| /// See `resources/type-system/upper-lower-bounds.md` |
| DartType _functionType(FunctionType f, FunctionType g) { |
| var fTypeFormals = f.typeFormals; |
| var gTypeFormals = g.typeFormals; |
| |
| // The number of type parameters must be the same. |
| // Otherwise the result is `Never`. |
| if (fTypeFormals.length != gTypeFormals.length) { |
| return NeverTypeImpl.instance; |
| } |
| |
| // The bounds of type parameters must be equal. |
| // Otherwise the result is `Never`. |
| var fresh = _typeSystem.relateTypeParameters(f.typeFormals, g.typeFormals); |
| if (fresh == null) { |
| return NeverTypeImpl.instance; |
| } |
| |
| f = f.instantiate(fresh.typeParameterTypes); |
| g = g.instantiate(fresh.typeParameterTypes); |
| |
| var fParameters = f.parameters; |
| var gParameters = g.parameters; |
| |
| var parameters = <ParameterElement>[]; |
| var fIndex = 0; |
| var gIndex = 0; |
| while (fIndex < fParameters.length && gIndex < gParameters.length) { |
| var fParameter = fParameters[fIndex]; |
| var gParameter = gParameters[gIndex]; |
| if (fParameter.isPositional) { |
| if (gParameter.isPositional) { |
| fIndex++; |
| gIndex++; |
| parameters.add( |
| fParameter.copyWith( |
| type: _typeSystem.leastUpperBound( |
| fParameter.type, |
| gParameter.type, |
| ), |
| kind: fParameter.isOptional || gParameter.isOptional |
| ? ParameterKind.POSITIONAL |
| : ParameterKind.REQUIRED, |
| ), |
| ); |
| } else { |
| return NeverTypeImpl.instance; |
| } |
| } else if (fParameter.isNamed) { |
| if (gParameter.isNamed) { |
| var compareNames = fParameter.name.compareTo(gParameter.name); |
| if (compareNames == 0) { |
| fIndex++; |
| gIndex++; |
| parameters.add( |
| fParameter.copyWith( |
| type: _typeSystem.leastUpperBound( |
| fParameter.type, |
| gParameter.type, |
| ), |
| kind: fParameter.isRequiredNamed && gParameter.isRequiredNamed |
| ? ParameterKind.NAMED_REQUIRED |
| : ParameterKind.NAMED, |
| ), |
| ); |
| } else if (compareNames < 0) { |
| fIndex++; |
| parameters.add( |
| fParameter.copyWith(kind: ParameterKind.NAMED), |
| ); |
| } else { |
| assert(compareNames > 0); |
| gIndex++; |
| parameters.add( |
| gParameter.copyWith(kind: ParameterKind.NAMED), |
| ); |
| } |
| } else { |
| return NeverTypeImpl.instance; |
| } |
| } |
| } |
| |
| while (fIndex < fParameters.length) { |
| var fParameter = fParameters[fIndex++]; |
| if (fParameter.isPositional) { |
| parameters.add( |
| fParameter.copyWith(kind: ParameterKind.POSITIONAL), |
| ); |
| } else { |
| assert(fParameter.isNamed); |
| parameters.add( |
| fParameter.copyWith(kind: ParameterKind.NAMED), |
| ); |
| } |
| } |
| |
| while (gIndex < gParameters.length) { |
| var gParameter = gParameters[gIndex++]; |
| if (gParameter.isPositional) { |
| parameters.add( |
| gParameter.copyWith(kind: ParameterKind.POSITIONAL), |
| ); |
| } else { |
| assert(gParameter.isNamed); |
| parameters.add( |
| gParameter.copyWith(kind: ParameterKind.NAMED), |
| ); |
| } |
| } |
| |
| var returnType = getGreatestLowerBound(f.returnType, g.returnType); |
| |
| return FunctionTypeImpl( |
| typeFormals: fresh.typeParameters, |
| parameters: parameters, |
| returnType: returnType, |
| nullabilitySuffix: NullabilitySuffix.none, |
| ); |
| } |
| |
| DartType _recordType(RecordTypeImpl T1, RecordTypeImpl T2) { |
| var positional1 = T1.positionalFields; |
| var positional2 = T2.positionalFields; |
| if (positional1.length != positional2.length) { |
| return _typeSystem.typeProvider.neverType; |
| } |
| |
| var named1 = T1.namedFields; |
| var named2 = T2.namedFields; |
| if (named1.length != named2.length) { |
| return _typeSystem.typeProvider.neverType; |
| } |
| |
| var positionalFields = <RecordTypePositionalFieldImpl>[]; |
| for (var i = 0; i < positional1.length; i++) { |
| var field1 = positional1[i]; |
| var field2 = positional2[i]; |
| var type = getGreatestLowerBound(field1.type, field2.type); |
| positionalFields.add( |
| RecordTypePositionalFieldImpl( |
| type: type, |
| ), |
| ); |
| } |
| |
| var namedFields = <RecordTypeNamedFieldImpl>[]; |
| for (var i = 0; i < named1.length; i++) { |
| var field1 = named1[i]; |
| var field2 = named2[i]; |
| if (field1.name != field2.name) { |
| return _typeSystem.typeProvider.neverType; |
| } |
| var type = getGreatestLowerBound(field1.type, field2.type); |
| namedFields.add( |
| RecordTypeNamedFieldImpl( |
| name: field1.name, |
| type: type, |
| ), |
| ); |
| } |
| |
| return RecordTypeImpl( |
| positionalFields: positionalFields, |
| namedFields: namedFields, |
| nullabilitySuffix: NullabilitySuffix.none, |
| ); |
| } |
| } |