| // 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/element.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/generated/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 = _typeSystem.isBottom(T1); |
| var T2_isBottom = _typeSystem.isBottom(T2); |
| |
| // 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.isSubtypeOf2(_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.isSubtypeOf2(_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) |
| // 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) |
| // 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 resultNullability = NullabilitySuffix.question; |
| if (T1_nullability == NullabilitySuffix.none || |
| T2_nullability == NullabilitySuffix.none) { |
| resultNullability = NullabilitySuffix.none; |
| } else if (T1_nullability == NullabilitySuffix.star || |
| T2_nullability == NullabilitySuffix.star) { |
| resultNullability = NullabilitySuffix.star; |
| } |
| var T1_none = T1_impl.withNullability(NullabilitySuffix.none); |
| var T2_none = T2_impl.withNullability(NullabilitySuffix.none); |
| var S = getGreatestLowerBound(T1_none, T2_none); |
| return (S as TypeImpl).withNullability(resultNullability); |
| } |
| |
| assert(T1_nullability == NullabilitySuffix.none); |
| assert(T2_nullability == NullabilitySuffix.none); |
| |
| if (T1 is FunctionType && T2 is FunctionType) { |
| return _functionType(T1, T2); |
| } |
| |
| // DOWN(T1, T2) = T1 if T1 <: T2 |
| if (_typeSystem.isSubtypeOf2(T1, T2)) { |
| return T1; |
| } |
| |
| // DOWN(T1, T2) = T2 if T2 <: T1 |
| if (_typeSystem.isSubtypeOf2(T2, T1)) { |
| return T2; |
| } |
| |
| // FutureOr<S1> |
| if (T1 is InterfaceType && T1.isDartAsyncFutureOr) { |
| var S1 = T1.typeArguments[0]; |
| // DOWN(FutureOr<S1>, FutureOr<S2>) = FutureOr(S) |
| // S = DOWN(S1, S2) |
| if (T2 is InterfaceType && T2.isDartAsyncFutureOr) { |
| var S2 = T2.typeArguments[0]; |
| var S = getGreatestLowerBound(S1, S2); |
| return _typeProvider.futureOrType2(S); |
| } |
| // DOWN(FutureOr<S1>, Future<S2>) = Future(S) |
| // S = DOWN(S1, S2) |
| if (T2 is InterfaceType && T2.isDartAsyncFuture) { |
| var S2 = T2.typeArguments[0]; |
| var S = getGreatestLowerBound(S1, S2); |
| return _typeProvider.futureType2(S); |
| } |
| // DOWN(FutureOr<S1>, T2) = DOWN(S1, T2) |
| return getGreatestLowerBound(S1, T2); |
| } |
| |
| // FutureOr<S2> |
| if (T2 is InterfaceType && T2.isDartAsyncFutureOr) { |
| var S2 = T2.typeArguments[0]; |
| // DOWN(Future<S1>, FutureOr<S2>) = Future<S> |
| // S = DOWN(S1, S2) |
| if (T1 is InterfaceType && T1.isDartAsyncFuture) { |
| var S1 = T1.typeArguments[0]; |
| var S = getGreatestLowerBound(S1, S2); |
| return _typeProvider.futureType2(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 freshTypeFormalTypes = |
| FunctionTypeImpl.relateTypeFormals(f, g, (t, s, _, __) => t == s); |
| if (freshTypeFormalTypes == null) { |
| return NeverTypeImpl.instance; |
| } |
| |
| var typeFormals = freshTypeFormalTypes |
| .map<TypeParameterElement>((t) => t.element) |
| .toList(); |
| |
| f = f.instantiate(freshTypeFormalTypes); |
| g = g.instantiate(freshTypeFormalTypes); |
| |
| 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( |
| ParameterElementImpl.synthetic( |
| fParameter.name, |
| _typeSystem.getLeastUpperBound(fParameter.type, gParameter.type), |
| 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( |
| ParameterElementImpl.synthetic( |
| fParameter.name, |
| _typeSystem.getLeastUpperBound( |
| fParameter.type, gParameter.type), |
| fParameter.isRequiredNamed && gParameter.isRequiredNamed |
| ? ParameterKind.NAMED_REQUIRED |
| : ParameterKind.NAMED, |
| ), |
| ); |
| } else if (compareNames < 0) { |
| fIndex++; |
| parameters.add( |
| ParameterElementImpl.synthetic( |
| fParameter.name, |
| fParameter.type, |
| ParameterKind.NAMED, |
| ), |
| ); |
| } else { |
| assert(compareNames > 0); |
| gIndex++; |
| parameters.add( |
| ParameterElementImpl.synthetic( |
| gParameter.name, |
| gParameter.type, |
| ParameterKind.NAMED, |
| ), |
| ); |
| } |
| } else { |
| return NeverTypeImpl.instance; |
| } |
| } |
| } |
| |
| while (fIndex < fParameters.length) { |
| var fParameter = fParameters[fIndex++]; |
| if (fParameter.isPositional) { |
| parameters.add( |
| ParameterElementImpl.synthetic( |
| fParameter.name, |
| fParameter.type, |
| ParameterKind.POSITIONAL, |
| ), |
| ); |
| } else { |
| assert(fParameter.isNamed); |
| parameters.add( |
| ParameterElementImpl.synthetic( |
| fParameter.name, |
| fParameter.type, |
| ParameterKind.NAMED, |
| ), |
| ); |
| } |
| } |
| |
| while (gIndex < gParameters.length) { |
| var gParameter = gParameters[gIndex++]; |
| if (gParameter.isPositional) { |
| parameters.add( |
| ParameterElementImpl.synthetic( |
| gParameter.name, |
| gParameter.type, |
| ParameterKind.POSITIONAL, |
| ), |
| ); |
| } else { |
| assert(gParameter.isNamed); |
| parameters.add( |
| ParameterElementImpl.synthetic( |
| gParameter.name, |
| gParameter.type, |
| ParameterKind.NAMED, |
| ), |
| ); |
| } |
| } |
| |
| var returnType = getGreatestLowerBound(f.returnType, g.returnType); |
| |
| return FunctionTypeImpl( |
| typeFormals: typeFormals, |
| parameters: parameters, |
| returnType: returnType, |
| nullabilitySuffix: NullabilitySuffix.none, |
| ); |
| } |
| } |