| // Copyright (c) 2015, 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/analysis/features.dart'; |
| import 'package:analyzer/dart/ast/standard_ast_factory.dart' show astFactory; |
| import 'package:analyzer/dart/ast/token.dart' show Keyword; |
| 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/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'; |
| import 'package:analyzer/src/generated/engine.dart'; |
| import 'package:analyzer/src/generated/resolver.dart'; |
| import 'package:analyzer/src/generated/source.dart' |
| show NonExistingSource, Source, UriKind; |
| import 'package:path/path.dart' show toUri; |
| import 'package:test/test.dart'; |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; |
| |
| import 'elements_types_mixin.dart'; |
| import 'test_analysis_context.dart'; |
| |
| main() { |
| defineReflectiveSuite(() { |
| defineReflectiveTests(AssignabilityTest); |
| defineReflectiveTests(ConstraintMatchingTest); |
| defineReflectiveTests(GenericFunctionInferenceTest); |
| defineReflectiveTests(GreatestLowerBoundTest); |
| defineReflectiveTests(LeastUpperBoundFunctionsTest); |
| defineReflectiveTests(LeastUpperBoundTest); |
| defineReflectiveTests(TryPromoteToTest); |
| }); |
| } |
| |
| abstract class AbstractTypeSystemTest with ElementsTypesMixin { |
| TestAnalysisContext analysisContext; |
| |
| @override |
| TypeProvider typeProvider; |
| |
| TypeSystemImpl typeSystem; |
| |
| FeatureSet get testFeatureSet { |
| return FeatureSet.forTesting(); |
| } |
| |
| void setUp() { |
| analysisContext = TestAnalysisContext( |
| featureSet: testFeatureSet, |
| ); |
| typeProvider = analysisContext.typeProviderLegacy; |
| typeSystem = analysisContext.typeSystemLegacy; |
| } |
| |
| String _typeString(TypeImpl type) { |
| return type.getDisplayString(withNullability: true); |
| } |
| } |
| |
| @reflectiveTest |
| class AssignabilityTest extends AbstractTypeSystemTest { |
| void test_isAssignableTo_bottom_isBottom() { |
| var A = class_(name: 'A'); |
| List<DartType> interassignable = <DartType>[ |
| dynamicType, |
| objectStar, |
| intStar, |
| doubleStar, |
| numStar, |
| stringStar, |
| interfaceTypeStar(A), |
| neverStar, |
| ]; |
| |
| _checkGroups(neverStar, interassignable: interassignable); |
| } |
| |
| void test_isAssignableTo_call_method() { |
| var B = class_( |
| name: 'B', |
| methods: [ |
| method('call', objectStar, parameters: [ |
| requiredParameter(name: '_', type: intStar), |
| ]), |
| ], |
| ); |
| |
| var testLibrary = _testLibrary(); |
| B.enclosingElement = testLibrary.definingCompilationUnit; |
| |
| _checkIsStrictAssignableTo( |
| interfaceTypeStar(B), |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| ], |
| returnType: objectStar, |
| ), |
| ); |
| } |
| |
| void test_isAssignableTo_classes() { |
| var classTop = class_(name: 'A'); |
| var classLeft = class_(name: 'B', superType: interfaceTypeStar(classTop)); |
| var classRight = class_(name: 'C', superType: interfaceTypeStar(classTop)); |
| var classBottom = class_( |
| name: 'D', |
| superType: interfaceTypeStar(classLeft), |
| interfaces: [interfaceTypeStar(classRight)], |
| ); |
| var top = interfaceTypeStar(classTop); |
| var left = interfaceTypeStar(classLeft); |
| var right = interfaceTypeStar(classRight); |
| var bottom = interfaceTypeStar(classBottom); |
| |
| _checkLattice(top, left, right, bottom); |
| } |
| |
| void test_isAssignableTo_double() { |
| var A = class_(name: 'A'); |
| List<DartType> interassignable = <DartType>[ |
| dynamicType, |
| objectStar, |
| doubleStar, |
| numStar, |
| neverStar, |
| ]; |
| List<DartType> unrelated = <DartType>[ |
| intStar, |
| stringStar, |
| interfaceTypeStar(A), |
| ]; |
| |
| _checkGroups(doubleStar, |
| interassignable: interassignable, unrelated: unrelated); |
| } |
| |
| void test_isAssignableTo_dynamic_isTop() { |
| var A = class_(name: 'A'); |
| List<DartType> interassignable = <DartType>[ |
| dynamicType, |
| objectStar, |
| intStar, |
| doubleStar, |
| numStar, |
| stringStar, |
| interfaceTypeStar(A), |
| neverStar, |
| ]; |
| _checkGroups(dynamicType, interassignable: interassignable); |
| } |
| |
| void test_isAssignableTo_generics() { |
| var LT = typeParameter('T'); |
| var L = class_(name: 'L', typeParameters: [LT]); |
| |
| var MT = typeParameter('T'); |
| var M = class_( |
| name: 'M', |
| typeParameters: [MT], |
| interfaces: [ |
| interfaceTypeStar( |
| L, |
| typeArguments: [ |
| typeParameterTypeStar(MT), |
| ], |
| ), |
| ], |
| ); |
| |
| var top = interfaceTypeStar(L, typeArguments: [dynamicType]); |
| var left = interfaceTypeStar(M, typeArguments: [dynamicType]); |
| var right = interfaceTypeStar(L, typeArguments: [intStar]); |
| var bottom = interfaceTypeStar(M, typeArguments: [intStar]); |
| |
| _checkCrossLattice(top, left, right, bottom); |
| } |
| |
| void test_isAssignableTo_int() { |
| var A = class_(name: 'A'); |
| List<DartType> interassignable = <DartType>[ |
| dynamicType, |
| objectStar, |
| intStar, |
| numStar, |
| neverStar, |
| ]; |
| List<DartType> unrelated = <DartType>[ |
| doubleStar, |
| stringStar, |
| interfaceTypeStar(A), |
| ]; |
| |
| _checkGroups(intStar, |
| interassignable: interassignable, unrelated: unrelated); |
| } |
| |
| void test_isAssignableTo_named_optional() { |
| var r = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| ], |
| returnType: intStar, |
| ); |
| var o = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: intStar), |
| ], |
| returnType: intStar, |
| ); |
| var n = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'x', type: intStar), |
| ], |
| returnType: intStar, |
| ); |
| |
| var rr = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| requiredParameter(type: intStar), |
| ], |
| returnType: intStar, |
| ); |
| var ro = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| positionalParameter(type: intStar), |
| ], |
| returnType: intStar, |
| ); |
| var rn = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| namedParameter(name: 'x', type: intStar), |
| ], |
| returnType: intStar, |
| ); |
| var oo = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: intStar), |
| positionalParameter(type: intStar), |
| ], |
| returnType: intStar, |
| ); |
| var nn = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'x', type: intStar), |
| namedParameter(name: 'y', type: intStar), |
| ], |
| returnType: intStar, |
| ); |
| var nnn = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'x', type: intStar), |
| namedParameter(name: 'y', type: intStar), |
| namedParameter(name: 'z', type: intStar), |
| ], |
| returnType: intStar, |
| ); |
| |
| _checkGroups(r, |
| interassignable: [r, o, ro, rn, oo], unrelated: [n, rr, nn, nnn]); |
| _checkGroups(o, |
| interassignable: [o, oo], unrelated: [n, rr, ro, rn, nn, nnn]); |
| _checkGroups(n, |
| interassignable: [n, nn, nnn], unrelated: [r, o, rr, ro, rn, oo]); |
| _checkGroups(rr, |
| interassignable: [rr, ro, oo], unrelated: [r, o, n, rn, nn, nnn]); |
| _checkGroups(ro, interassignable: [ro, oo], unrelated: [o, n, rn, nn, nnn]); |
| _checkGroups(rn, |
| interassignable: [rn], unrelated: [o, n, rr, ro, oo, nn, nnn]); |
| _checkGroups(oo, interassignable: [oo], unrelated: [n, rn, nn, nnn]); |
| _checkGroups(nn, |
| interassignable: [nn, nnn], unrelated: [r, o, rr, ro, rn, oo]); |
| _checkGroups(nnn, |
| interassignable: [nnn], unrelated: [r, o, rr, ro, rn, oo]); |
| } |
| |
| void test_isAssignableTo_num() { |
| var A = class_(name: 'A'); |
| List<DartType> interassignable = <DartType>[ |
| dynamicType, |
| objectStar, |
| numStar, |
| intStar, |
| doubleStar, |
| neverStar, |
| ]; |
| List<DartType> unrelated = <DartType>[ |
| stringStar, |
| interfaceTypeStar(A), |
| ]; |
| |
| _checkGroups(numStar, |
| interassignable: interassignable, unrelated: unrelated); |
| } |
| |
| void test_isAssignableTo_simple_function() { |
| var top = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| ], |
| returnType: objectStar, |
| ); |
| |
| var left = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| ], |
| returnType: intStar, |
| ); |
| |
| var right = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: objectStar), |
| ], |
| returnType: objectStar, |
| ); |
| |
| var bottom = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: objectStar), |
| ], |
| returnType: intStar, |
| ); |
| |
| _checkCrossLattice(top, left, right, bottom); |
| } |
| |
| void test_isAssignableTo_void_functions() { |
| var top = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| |
| var bottom = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: objectStar), |
| ], |
| returnType: intStar, |
| ); |
| |
| _checkEquivalent(bottom, top); |
| } |
| |
| void _checkCrossLattice( |
| DartType top, DartType left, DartType right, DartType bottom) { |
| _checkGroups(top, interassignable: [top, left, right, bottom]); |
| _checkGroups(left, |
| interassignable: [top, left, bottom], unrelated: [right]); |
| _checkGroups(right, |
| interassignable: [top, right, bottom], unrelated: [left]); |
| _checkGroups(bottom, interassignable: [top, left, right, bottom]); |
| } |
| |
| void _checkEquivalent(DartType type1, DartType type2) { |
| _checkIsAssignableTo(type1, type2); |
| _checkIsAssignableTo(type2, type1); |
| } |
| |
| void _checkGroups(DartType t1, |
| {List<DartType> interassignable, List<DartType> unrelated}) { |
| if (interassignable != null) { |
| for (DartType t2 in interassignable) { |
| _checkEquivalent(t1, t2); |
| } |
| } |
| if (unrelated != null) { |
| for (DartType t2 in unrelated) { |
| _checkUnrelated(t1, t2); |
| } |
| } |
| } |
| |
| void _checkIsAssignableTo(DartType type1, DartType type2) { |
| expect(typeSystem.isAssignableTo(type1, type2), true); |
| } |
| |
| void _checkIsNotAssignableTo(DartType type1, DartType type2) { |
| expect(typeSystem.isAssignableTo(type1, type2), false); |
| } |
| |
| void _checkIsStrictAssignableTo(DartType type1, DartType type2) { |
| _checkIsAssignableTo(type1, type2); |
| _checkIsNotAssignableTo(type2, type1); |
| } |
| |
| void _checkLattice( |
| DartType top, DartType left, DartType right, DartType bottom) { |
| _checkGroups(top, interassignable: <DartType>[top, left, right, bottom]); |
| _checkGroups(left, |
| interassignable: <DartType>[top, left, bottom], |
| unrelated: <DartType>[right]); |
| _checkGroups(right, |
| interassignable: <DartType>[top, right, bottom], |
| unrelated: <DartType>[left]); |
| _checkGroups(bottom, interassignable: <DartType>[top, left, right, bottom]); |
| } |
| |
| void _checkUnrelated(DartType type1, DartType type2) { |
| _checkIsNotAssignableTo(type1, type2); |
| _checkIsNotAssignableTo(type2, type1); |
| } |
| |
| /// Return a test library, in `/test.dart` file. |
| LibraryElementImpl _testLibrary() { |
| var source = _MockSource(toUri('/test.dart')); |
| |
| var definingUnit = CompilationUnitElementImpl(); |
| definingUnit.source = definingUnit.librarySource = source; |
| |
| var testLibrary = LibraryElementImpl( |
| analysisContext, AnalysisSessionImpl(null), '', -1, 0, false); |
| testLibrary.definingCompilationUnit = definingUnit; |
| return testLibrary; |
| } |
| } |
| |
| /** |
| * Base class for testing LUB and GLB in spec and strong mode. |
| */ |
| abstract class BoundTestBase extends AbstractTypeSystemTest { |
| void _checkGreatestLowerBound( |
| DartType type1, DartType type2, DartType expectedResult) { |
| var glb = typeSystem.getGreatestLowerBound(type1, type2); |
| expect(glb, expectedResult); |
| |
| // Check that the result is a lower bound. |
| expect(typeSystem.isSubtypeOf(glb, type1), true); |
| expect(typeSystem.isSubtypeOf(glb, type2), true); |
| |
| // Check for symmetry while we're at it. |
| glb = typeSystem.getGreatestLowerBound(type2, type1); |
| expect(glb, expectedResult); |
| } |
| |
| void _checkLeastUpperBound(DartType T1, DartType T2, DartType expected) { |
| var expectedStr = _typeString(expected); |
| |
| var result = typeSystem.getLeastUpperBound(T1, T2); |
| var resultStr = _typeString(result); |
| expect(result, expected, reason: ''' |
| expected: $expectedStr |
| actual: $resultStr |
| '''); |
| |
| // Check that the result is an upper bound. |
| expect(typeSystem.isSubtypeOf(T1, result), true); |
| expect(typeSystem.isSubtypeOf(T2, result), true); |
| |
| // Check for symmetry. |
| result = typeSystem.getLeastUpperBound(T2, T1); |
| resultStr = _typeString(result); |
| expect(result, expected, reason: ''' |
| expected: $expectedStr |
| actual: $resultStr |
| '''); |
| } |
| } |
| |
| @reflectiveTest |
| class ConstraintMatchingTest extends AbstractTypeSystemTest { |
| TypeParameterType T; |
| |
| @override |
| void setUp() { |
| super.setUp(); |
| T = typeParameterTypeStar( |
| typeParameter('T'), |
| ); |
| } |
| |
| void test_function_coreFunction() { |
| _checkOrdinarySubtypeMatch( |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| ], |
| returnType: stringStar, |
| ), |
| typeProvider.functionType, |
| [T], |
| covariant: true, |
| ); |
| } |
| |
| void test_function_parameter_types() { |
| _checkIsSubtypeMatchOf( |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: T), |
| ], |
| returnType: intStar, |
| ), |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringStar), |
| ], |
| returnType: intStar, |
| ), |
| [T], |
| ['String <: T'], |
| covariant: true, |
| ); |
| } |
| |
| void test_function_return_types() { |
| _checkIsSubtypeMatchOf( |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| ], |
| returnType: T, |
| ), |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| ], |
| returnType: stringStar, |
| ), |
| [T], |
| ['T <: String'], |
| covariant: true, |
| ); |
| } |
| |
| void test_futureOr_futureOr() { |
| _checkIsSubtypeMatchOf( |
| futureOrStar(T), futureOrStar(stringStar), [T], ['T <: String'], |
| covariant: true); |
| } |
| |
| void test_futureOr_x_fail_future_branch() { |
| // FutureOr<List<T>> <: List<String> can't be satisfied because |
| // Future<List<T>> <: List<String> can't be satisfied |
| _checkIsNotSubtypeMatchOf( |
| futureOrStar(listStar(T)), listStar(stringStar), [T], |
| covariant: true); |
| } |
| |
| void test_futureOr_x_fail_nonFuture_branch() { |
| // FutureOr<List<T>> <: Future<List<String>> can't be satisfied because |
| // List<T> <: Future<List<String>> can't be satisfied |
| _checkIsNotSubtypeMatchOf( |
| futureOrStar(listStar(T)), futureStar(listStar(stringStar)), [T], |
| covariant: true); |
| } |
| |
| void test_futureOr_x_success() { |
| // FutureOr<T> <: Future<T> can be satisfied by T=Null. At this point in |
| // the type inference algorithm all we figure out is that T must be a |
| // subtype of both String and Future<String>. |
| _checkIsSubtypeMatchOf(futureOrStar(T), futureStar(stringStar), [T], |
| ['T <: String', 'T <: Future<String>'], |
| covariant: true); |
| } |
| |
| void test_lhs_null() { |
| // Null <: T is trivially satisfied by the constraint Null <: T. |
| _checkIsSubtypeMatchOf(nullStar, T, [T], ['Null <: T'], covariant: false); |
| // For any other type X, Null <: X is satisfied without the need for any |
| // constraints. |
| _checkOrdinarySubtypeMatch(nullStar, listStar(T), [T], covariant: false); |
| _checkOrdinarySubtypeMatch(nullStar, stringStar, [T], covariant: false); |
| _checkOrdinarySubtypeMatch(nullStar, voidNone, [T], covariant: false); |
| _checkOrdinarySubtypeMatch(nullStar, dynamicType, [T], covariant: false); |
| _checkOrdinarySubtypeMatch(nullStar, objectStar, [T], covariant: false); |
| _checkOrdinarySubtypeMatch(nullStar, nullStar, [T], covariant: false); |
| _checkOrdinarySubtypeMatch( |
| nullStar, |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| ], |
| returnType: stringStar, |
| ), |
| [T], |
| covariant: false, |
| ); |
| } |
| |
| void test_param_on_lhs_contravariant_direct() { |
| // When doing a contravariant match, the type parameters we're trying to |
| // find types for are on the right hand side. Is a type parameter also |
| // appears on the left hand side, there is a condition in which the |
| // constraint can be satisfied without consulting the bound of the LHS type |
| // parameter: the condition where both parameters appear at corresponding |
| // locations in the type tree. |
| // |
| // In other words, List<S> <: List<T> is satisfied provided that |
| // S <: T. |
| var S = typeParameterTypeStar(typeParameter('S')); |
| _checkIsSubtypeMatchOf(listStar(S), listStar(T), [T], ['S <: T'], |
| covariant: false); |
| } |
| |
| void test_param_on_lhs_contravariant_via_bound() { |
| // When doing a contravariant match, the type parameters we're trying to |
| // find types for are on the right hand side. Is a type parameter also |
| // appears on the left hand side, we may have to constrain the RHS type |
| // parameter using the bounds of the LHS type parameter. |
| // |
| // In other words, S <: List<T> is satisfied provided that |
| // bound(S) <: List<T>. |
| var S = typeParameterTypeStar(typeParameter( |
| 'S', |
| bound: listStar(stringStar), |
| )); |
| _checkIsSubtypeMatchOf(S, listStar(T), [T], ['String <: T'], |
| covariant: false); |
| } |
| |
| void test_param_on_lhs_covariant() { |
| // When doing a covariant match, the type parameters we're trying to find |
| // types for are on the left hand side. |
| _checkIsSubtypeMatchOf(T, stringStar, [T], ['T <: String'], |
| covariant: true); |
| } |
| |
| void test_param_on_rhs_contravariant() { |
| // When doing a contravariant match, the type parameters we're trying to |
| // find types for are on the right hand side. |
| _checkIsSubtypeMatchOf(stringStar, T, [T], ['String <: T'], |
| covariant: false); |
| } |
| |
| void test_param_on_rhs_covariant_match() { |
| // When doing a covariant match, the type parameters we're trying to find |
| // types for are on the left hand side. If a type parameter appears on the |
| // right hand side, there is a condition in which the constraint can be |
| // satisfied: where both parameters appear at corresponding locations in the |
| // type tree. |
| // |
| // In other words, T <: S can be satisfied trivially by the constraint |
| // T <: S. |
| var S = typeParameterTypeStar(typeParameter('S')); |
| _checkIsSubtypeMatchOf(T, S, [T], ['T <: S'], covariant: true); |
| } |
| |
| void test_param_on_rhs_covariant_no_match() { |
| // When doing a covariant match, the type parameters we're trying to find |
| // types for are on the left hand side. If a type parameter appears on the |
| // right hand side, it's probable that the constraint can't be satisfied, |
| // because there is no possible type for the LHS (other than bottom) |
| // that's guaranteed to satisfy the relation for all possible assignments of |
| // the RHS type parameter. |
| // |
| // In other words, no match can be found for List<T> <: S because regardless |
| // of T, we can't guarantee that List<T> <: S for all S. |
| var S = typeParameterTypeStar(typeParameter('S')); |
| _checkIsNotSubtypeMatchOf(listStar(T), S, [T], covariant: true); |
| } |
| |
| void test_related_interface_types_failure() { |
| _checkIsNotSubtypeMatchOf(iterableStar(T), listStar(stringStar), [T], |
| covariant: true); |
| } |
| |
| void test_related_interface_types_success() { |
| _checkIsSubtypeMatchOf( |
| listStar(T), iterableStar(stringStar), [T], ['T <: String'], |
| covariant: true); |
| } |
| |
| void test_rhs_dynamic() { |
| // T <: dynamic is trivially satisfied by the constraint T <: dynamic. |
| _checkIsSubtypeMatchOf(T, dynamicType, [T], ['T <: dynamic'], |
| covariant: true); |
| // For any other type X, X <: dynamic is satisfied without the need for any |
| // constraints. |
| _checkOrdinarySubtypeMatch(listStar(T), dynamicType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(stringStar, dynamicType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(voidNone, dynamicType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(dynamicType, dynamicType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(objectStar, dynamicType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(nullStar, dynamicType, [T], covariant: true); |
| _checkOrdinarySubtypeMatch( |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| ], |
| returnType: stringStar, |
| ), |
| dynamicType, |
| [T], |
| covariant: true, |
| ); |
| } |
| |
| void test_rhs_object() { |
| // T <: Object is trivially satisfied by the constraint T <: Object. |
| _checkIsSubtypeMatchOf(T, objectStar, [T], ['T <: Object'], |
| covariant: true); |
| // For any other type X, X <: Object is satisfied without the need for any |
| // constraints. |
| _checkOrdinarySubtypeMatch(listStar(T), objectStar, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(stringStar, objectStar, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(voidNone, objectStar, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(dynamicType, objectStar, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(objectStar, objectStar, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(nullStar, objectStar, [T], covariant: true); |
| _checkOrdinarySubtypeMatch( |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| ], |
| returnType: stringStar, |
| ), |
| objectStar, |
| [T], |
| covariant: true, |
| ); |
| } |
| |
| void test_rhs_void() { |
| // T <: void is trivially satisfied by the constraint T <: void. |
| _checkIsSubtypeMatchOf(T, voidNone, [T], ['T <: void'], covariant: true); |
| // For any other type X, X <: void is satisfied without the need for any |
| // constraints. |
| _checkOrdinarySubtypeMatch(listStar(T), voidNone, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(stringStar, voidNone, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(voidNone, voidNone, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(dynamicType, voidNone, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(objectStar, voidNone, [T], covariant: true); |
| _checkOrdinarySubtypeMatch(nullStar, voidNone, [T], covariant: true); |
| _checkOrdinarySubtypeMatch( |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| ], |
| returnType: stringStar, |
| ), |
| voidNone, |
| [T], |
| covariant: true, |
| ); |
| } |
| |
| void test_same_interface_types() { |
| _checkIsSubtypeMatchOf( |
| listStar(T), listStar(stringStar), [T], ['T <: String'], |
| covariant: true); |
| } |
| |
| void test_variance_contravariant() { |
| // class A<in T> |
| var tContravariant = typeParameter('T', variance: Variance.contravariant); |
| var tType = typeParameterType(tContravariant); |
| var A = class_(name: 'A', typeParameters: [tContravariant]); |
| |
| // A<num> |
| // A<T> |
| var aNum = interfaceType(A, |
| typeArguments: [numStar], nullabilitySuffix: NullabilitySuffix.none); |
| var aT = interfaceType(A, |
| typeArguments: [tType], nullabilitySuffix: NullabilitySuffix.none); |
| |
| _checkIsSubtypeMatchOf(aT, aNum, [tType], ['num <: in T'], covariant: true); |
| } |
| |
| void test_variance_covariant() { |
| // class A<out T> |
| var tCovariant = typeParameter('T', variance: Variance.covariant); |
| var tType = typeParameterType(tCovariant); |
| var A = class_(name: 'A', typeParameters: [tCovariant]); |
| |
| // A<num> |
| // A<T> |
| var aNum = interfaceType(A, |
| typeArguments: [numStar], nullabilitySuffix: NullabilitySuffix.none); |
| var aT = interfaceType(A, |
| typeArguments: [tType], nullabilitySuffix: NullabilitySuffix.none); |
| |
| _checkIsSubtypeMatchOf(aT, aNum, [tType], ['out T <: num'], |
| covariant: true); |
| } |
| |
| void test_variance_invariant() { |
| // class A<inout T> |
| var tInvariant = typeParameter('T', variance: Variance.invariant); |
| var tType = typeParameterType(tInvariant); |
| var A = class_(name: 'A', typeParameters: [tInvariant]); |
| |
| // A<num> |
| // A<T> |
| var aNum = interfaceType(A, |
| typeArguments: [numStar], nullabilitySuffix: NullabilitySuffix.none); |
| var aT = interfaceType(A, |
| typeArguments: [tType], nullabilitySuffix: NullabilitySuffix.none); |
| |
| _checkIsSubtypeMatchOf( |
| aT, aNum, [tType], ['inout T <: num', 'num <: inout T'], |
| covariant: true); |
| } |
| |
| void test_x_futureOr_fail_both_branches() { |
| // List<T> <: FutureOr<String> can't be satisfied because neither |
| // List<T> <: Future<String> nor List<T> <: int can be satisfied |
| _checkIsNotSubtypeMatchOf(listStar(T), futureOrStar(stringStar), [T], |
| covariant: true); |
| } |
| |
| void test_x_futureOr_pass_both_branches_constraints_from_both_branches() { |
| // Future<String> <: FutureOr<T> can be satisfied because both |
| // Future<String> <: Future<T> and Future<String> <: T can be satisfied. |
| // Trying to match Future<String> <: Future<T> generates the constraint |
| // String <: T, whereas trying to match Future<String> <: T generates the |
| // constraint Future<String> <: T. We keep the constraint based on trying |
| // to match Future<String> <: Future<T>, so String <: T. |
| _checkIsSubtypeMatchOf( |
| futureStar(stringStar), futureOrStar(T), [T], ['String <: T'], |
| covariant: false); |
| } |
| |
| void test_x_futureOr_pass_both_branches_constraints_from_future_branch() { |
| // Future<T> <: FutureOr<Object> can be satisfied because both |
| // Future<T> <: Future<Object> and Future<T> <: Object can be satisfied. |
| // Trying to match Future<T> <: Future<Object> generates the constraint |
| // T <: Object, whereas trying to match Future<T> <: Object generates no |
| // constraints, so we keep the constraint T <: Object. |
| _checkIsSubtypeMatchOf( |
| futureStar(T), futureOrStar(objectStar), [T], ['T <: Object'], |
| covariant: true); |
| } |
| |
| void test_x_futureOr_pass_both_branches_constraints_from_nonFuture_branch() { |
| // Null <: FutureOr<T> can be satisfied because both |
| // Null <: Future<T> and Null <: T can be satisfied. |
| // Trying to match Null <: FutureOr<T> generates no constraints, whereas |
| // trying to match Null <: T generates the constraint Null <: T, |
| // so we keep the constraint Null <: T. |
| _checkIsSubtypeMatchOf(nullStar, futureOrStar(T), [T], ['Null <: T'], |
| covariant: false); |
| } |
| |
| void test_x_futureOr_pass_both_branches_no_constraints() { |
| // Future<String> <: FutureOr<Object> is satisfied because both |
| // Future<String> <: Future<Object> and Future<String> <: Object. |
| // No constraints are recorded. |
| _checkIsSubtypeMatchOf( |
| futureStar(stringStar), futureOrStar(objectStar), [T], [], |
| covariant: true); |
| } |
| |
| void test_x_futureOr_pass_future_branch() { |
| // Future<T> <: FutureOr<String> can be satisfied because |
| // Future<T> <: Future<String> can be satisfied |
| _checkIsSubtypeMatchOf( |
| futureStar(T), futureOrStar(stringStar), [T], ['T <: String'], |
| covariant: true); |
| } |
| |
| void test_x_futureOr_pass_nonFuture_branch() { |
| // List<T> <: FutureOr<List<String>> can be satisfied because |
| // List<T> <: List<String> can be satisfied |
| _checkIsSubtypeMatchOf( |
| listStar(T), futureOrStar(listStar(stringStar)), [T], ['T <: String'], |
| covariant: true); |
| } |
| |
| void _checkIsNotSubtypeMatchOf( |
| DartType t1, DartType t2, Iterable<TypeParameterType> typeFormals, |
| {bool covariant}) { |
| var inferrer = GenericInferrer( |
| typeSystem, |
| typeFormals.map((t) => t.element), |
| ); |
| var success = |
| inferrer.tryMatchSubtypeOf(t1, t2, null, covariant: covariant); |
| expect(success, isFalse); |
| inferrer.constraints.forEach((typeParameter, constraintsForTypeParameter) { |
| expect(constraintsForTypeParameter, isEmpty); |
| }); |
| } |
| |
| void _checkIsSubtypeMatchOf( |
| DartType t1, |
| DartType t2, |
| Iterable<TypeParameterType> typeFormals, |
| Iterable<String> expectedConstraints, |
| {bool covariant}) { |
| var inferrer = GenericInferrer( |
| typeSystem, |
| typeFormals.map((t) => t.element), |
| ); |
| var success = |
| inferrer.tryMatchSubtypeOf(t1, t2, null, covariant: covariant); |
| expect(success, isTrue); |
| var formattedConstraints = <String>[]; |
| inferrer.constraints.forEach((typeParameter, constraintsForTypeParameter) { |
| for (var constraint in constraintsForTypeParameter) { |
| formattedConstraints.add( |
| constraint.format( |
| typeParameter.getDisplayString( |
| withNullability: typeSystem.isNonNullableByDefault, |
| ), |
| withNullability: false, |
| ), |
| ); |
| } |
| }); |
| expect(formattedConstraints, unorderedEquals(expectedConstraints)); |
| } |
| |
| void _checkOrdinarySubtypeMatch( |
| DartType t1, DartType t2, Iterable<TypeParameterType> typeFormals, |
| {bool covariant}) { |
| bool expectSuccess = typeSystem.isSubtypeOf(t1, t2); |
| if (expectSuccess) { |
| _checkIsSubtypeMatchOf(t1, t2, typeFormals, [], covariant: covariant); |
| } else { |
| _checkIsNotSubtypeMatchOf(t1, t2, typeFormals); |
| } |
| } |
| } |
| |
| @reflectiveTest |
| class GenericFunctionInferenceTest extends AbstractTypeSystemTest { |
| void test_boundedByAnotherTypeParameter() { |
| // <TFrom, TTo extends Iterable<TFrom>>(TFrom) -> TTo |
| var tFrom = typeParameter('TFrom'); |
| var tTo = |
| typeParameter('TTo', bound: iterableStar(typeParameterTypeStar(tFrom))); |
| var cast = functionTypeStar( |
| typeFormals: [tFrom, tTo], |
| parameters: [ |
| requiredParameter( |
| type: typeParameterTypeStar(tFrom), |
| ), |
| ], |
| returnType: typeParameterTypeStar(tTo), |
| ); |
| expect(_inferCall(cast, [stringStar]), |
| [stringStar, (iterableStar(stringStar))]); |
| } |
| |
| void test_boundedByOuterClass() { |
| // Regression test for https://github.com/dart-lang/sdk/issues/25740. |
| |
| // class A {} |
| var A = class_(name: 'A', superType: objectStar); |
| var typeA = interfaceTypeStar(A); |
| |
| // class B extends A {} |
| var B = class_(name: 'B', superType: typeA); |
| var typeB = interfaceTypeStar(B); |
| |
| // class C<T extends A> { |
| var CT = typeParameter('T', bound: typeA); |
| var C = class_( |
| name: 'C', |
| superType: objectStar, |
| typeParameters: [CT], |
| ); |
| // S m<S extends T>(S); |
| var S = typeParameter('S', bound: typeParameterTypeStar(CT)); |
| var m = method( |
| 'm', |
| typeParameterTypeStar(S), |
| typeFormals: [S], |
| parameters: [ |
| requiredParameter( |
| name: '_', |
| type: typeParameterTypeStar(S), |
| ), |
| ], |
| ); |
| C.methods = [m]; |
| // } |
| |
| // C<Object> cOfObject; |
| var cOfObject = interfaceTypeStar(C, typeArguments: [objectStar]); |
| // C<A> cOfA; |
| var cOfA = interfaceTypeStar(C, typeArguments: [typeA]); |
| // C<B> cOfB; |
| var cOfB = interfaceTypeStar(C, typeArguments: [typeB]); |
| // B b; |
| // cOfB.m(b); // infer <B> |
| _assertType( |
| _inferCall2(cOfB.getMethod('m').type, [typeB]), 'B Function(B)'); |
| // cOfA.m(b); // infer <B> |
| _assertType( |
| _inferCall2(cOfA.getMethod('m').type, [typeB]), 'B Function(B)'); |
| // cOfObject.m(b); // infer <B> |
| _assertType( |
| _inferCall2(cOfObject.getMethod('m').type, [typeB]), 'B Function(B)'); |
| } |
| |
| void test_boundedByOuterClassSubstituted() { |
| // Regression test for https://github.com/dart-lang/sdk/issues/25740. |
| |
| // class A {} |
| var A = class_(name: 'A', superType: objectStar); |
| var typeA = interfaceTypeStar(A); |
| |
| // class B extends A {} |
| var B = class_(name: 'B', superType: typeA); |
| var typeB = interfaceTypeStar(B); |
| |
| // class C<T extends A> { |
| var CT = typeParameter('T', bound: typeA); |
| var C = class_( |
| name: 'C', |
| superType: objectStar, |
| typeParameters: [CT], |
| ); |
| // S m<S extends Iterable<T>>(S); |
| var iterableOfT = iterableStar(typeParameterTypeStar(CT)); |
| var S = typeParameter('S', bound: iterableOfT); |
| var m = method( |
| 'm', |
| typeParameterTypeStar(S), |
| typeFormals: [S], |
| parameters: [ |
| requiredParameter( |
| name: '_', |
| type: typeParameterTypeStar(S), |
| ), |
| ], |
| ); |
| C.methods = [m]; |
| // } |
| |
| // C<Object> cOfObject; |
| var cOfObject = interfaceTypeStar(C, typeArguments: [objectStar]); |
| // C<A> cOfA; |
| var cOfA = interfaceTypeStar(C, typeArguments: [typeA]); |
| // C<B> cOfB; |
| var cOfB = interfaceTypeStar(C, typeArguments: [typeB]); |
| // List<B> b; |
| var listOfB = listStar(typeB); |
| // cOfB.m(b); // infer <B> |
| _assertType(_inferCall2(cOfB.getMethod('m').type, [listOfB]), |
| 'List<B> Function(List<B>)'); |
| // cOfA.m(b); // infer <B> |
| _assertType(_inferCall2(cOfA.getMethod('m').type, [listOfB]), |
| 'List<B> Function(List<B>)'); |
| // cOfObject.m(b); // infer <B> |
| _assertType(_inferCall2(cOfObject.getMethod('m').type, [listOfB]), |
| 'List<B> Function(List<B>)'); |
| } |
| |
| void test_boundedRecursively() { |
| // class A<T extends A<T>> |
| var T = typeParameter('T'); |
| var A = class_( |
| name: 'Cloneable', |
| superType: objectStar, |
| typeParameters: [T], |
| ); |
| T.bound = interfaceTypeStar( |
| A, |
| typeArguments: [typeParameterTypeStar(T)], |
| ); |
| |
| // class B extends A<B> {} |
| var B = class_(name: 'B', superType: null); |
| B.supertype = interfaceTypeStar(A, typeArguments: [interfaceTypeStar(B)]); |
| var typeB = interfaceTypeStar(B); |
| |
| // <S extends A<S>> |
| var S = typeParameter('S'); |
| var typeS = typeParameterTypeStar(S); |
| S.bound = interfaceTypeStar(A, typeArguments: [typeS]); |
| |
| // (S, S) -> S |
| var clone = functionTypeStar( |
| typeFormals: [S], |
| parameters: [ |
| requiredParameter(type: typeS), |
| requiredParameter(type: typeS), |
| ], |
| returnType: typeS, |
| ); |
| expect(_inferCall(clone, [typeB, typeB]), [typeB]); |
| |
| // Something invalid... |
| expect( |
| _inferCall(clone, [stringStar, numStar], expectError: true), |
| [objectStar], |
| ); |
| } |
| |
| void test_genericCastFunction() { |
| // <TFrom, TTo>(TFrom) -> TTo |
| var tFrom = typeParameter('TFrom'); |
| var tTo = typeParameter('TTo'); |
| var cast = functionTypeStar( |
| typeFormals: [tFrom, tTo], |
| parameters: [ |
| requiredParameter( |
| type: typeParameterTypeStar(tFrom), |
| ), |
| ], |
| returnType: typeParameterTypeStar(tTo), |
| ); |
| expect(_inferCall(cast, [intStar]), [intStar, dynamicType]); |
| } |
| |
| void test_genericCastFunctionWithUpperBound() { |
| // <TFrom, TTo extends TFrom>(TFrom) -> TTo |
| var tFrom = typeParameter('TFrom'); |
| var tTo = typeParameter( |
| 'TTo', |
| bound: typeParameterTypeStar(tFrom), |
| ); |
| var cast = functionTypeStar( |
| typeFormals: [tFrom, tTo], |
| parameters: [ |
| requiredParameter( |
| type: typeParameterTypeStar(tFrom), |
| ), |
| ], |
| returnType: typeParameterTypeStar(tTo), |
| ); |
| expect(_inferCall(cast, [intStar]), [intStar, intStar]); |
| } |
| |
| void test_parameter_contravariantUseUpperBound() { |
| // <T>(T x, void Function(T) y) -> T |
| // Generates constraints int <: T <: num. |
| // Since T is contravariant, choose num. |
| var T = typeParameter('T', variance: Variance.contravariant); |
| var tFunction = functionTypeStar( |
| parameters: [requiredParameter(type: typeParameterTypeStar(T))], |
| returnType: voidNone); |
| var numFunction = functionTypeStar( |
| parameters: [requiredParameter(type: numStar)], returnType: voidNone); |
| var function = functionTypeStar( |
| typeFormals: [T], |
| parameters: [ |
| requiredParameter(type: typeParameterTypeStar(T)), |
| requiredParameter(type: tFunction) |
| ], |
| returnType: typeParameterTypeStar(T), |
| ); |
| |
| expect(_inferCall(function, [intStar, numFunction]), [numStar]); |
| } |
| |
| void test_parameter_covariantUseLowerBound() { |
| // <T>(T x, void Function(T) y) -> T |
| // Generates constraints int <: T <: num. |
| // Since T is covariant, choose int. |
| var T = typeParameter('T', variance: Variance.covariant); |
| var tFunction = functionTypeStar( |
| parameters: [requiredParameter(type: typeParameterTypeStar(T))], |
| returnType: voidNone); |
| var numFunction = functionTypeStar( |
| parameters: [requiredParameter(type: numStar)], returnType: voidNone); |
| var function = functionTypeStar( |
| typeFormals: [T], |
| parameters: [ |
| requiredParameter(type: typeParameterTypeStar(T)), |
| requiredParameter(type: tFunction) |
| ], |
| returnType: typeParameterTypeStar(T), |
| ); |
| |
| expect(_inferCall(function, [intStar, numFunction]), [intStar]); |
| } |
| |
| void test_parametersToFunctionParam() { |
| // <T>(f(T t)) -> T |
| var T = typeParameter('T'); |
| var cast = functionTypeStar( |
| typeFormals: [T], |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: typeParameterTypeStar(T), |
| ), |
| ], |
| returnType: dynamicType, |
| ), |
| ), |
| ], |
| returnType: typeParameterTypeStar(T), |
| ); |
| expect( |
| _inferCall(cast, [ |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: numStar), |
| ], |
| returnType: dynamicType, |
| ) |
| ]), |
| [numStar], |
| ); |
| } |
| |
| void test_parametersUseLeastUpperBound() { |
| // <T>(T x, T y) -> T |
| var T = typeParameter('T'); |
| var cast = functionTypeStar( |
| typeFormals: [T], |
| parameters: [ |
| requiredParameter(type: typeParameterTypeStar(T)), |
| requiredParameter(type: typeParameterTypeStar(T)), |
| ], |
| returnType: typeParameterTypeStar(T), |
| ); |
| expect(_inferCall(cast, [intStar, doubleStar]), [numStar]); |
| } |
| |
| void test_parameterTypeUsesUpperBound() { |
| // <T extends num>(T) -> dynamic |
| var T = typeParameter('T', bound: numStar); |
| var f = functionTypeStar( |
| typeFormals: [T], |
| parameters: [ |
| requiredParameter(type: typeParameterTypeStar(T)), |
| ], |
| returnType: dynamicType, |
| ); |
| expect(_inferCall(f, [intStar]), [intStar]); |
| } |
| |
| void test_returnFunctionWithGenericParameter() { |
| // <T>(T -> T) -> (T -> void) |
| var T = typeParameter('T'); |
| var f = functionTypeStar( |
| typeFormals: [T], |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: typeParameterTypeStar(T)), |
| ], |
| returnType: typeParameterTypeStar(T), |
| ), |
| ), |
| ], |
| returnType: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: typeParameterTypeStar(T)), |
| ], |
| returnType: voidNone, |
| ), |
| ); |
| expect( |
| _inferCall(f, [ |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: numStar), |
| ], |
| returnType: intStar, |
| ), |
| ]), |
| [intStar], |
| ); |
| } |
| |
| void test_returnFunctionWithGenericParameterAndContext() { |
| // <T>(T -> T) -> (T -> Null) |
| var T = typeParameter('T'); |
| var f = functionTypeStar( |
| typeFormals: [T], |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: typeParameterTypeStar(T)), |
| ], |
| returnType: typeParameterTypeStar(T), |
| ), |
| ), |
| ], |
| returnType: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: typeParameterTypeStar(T)), |
| ], |
| returnType: nullStar, |
| ), |
| ); |
| expect( |
| _inferCall( |
| f, |
| [], |
| returnType: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: numStar), |
| ], |
| returnType: intStar, |
| ), |
| ), |
| [numStar], |
| ); |
| } |
| |
| void test_returnFunctionWithGenericParameterAndReturn() { |
| // <T>(T -> T) -> (T -> T) |
| var T = typeParameter('T'); |
| var f = functionTypeStar( |
| typeFormals: [T], |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: typeParameterTypeStar(T)), |
| ], |
| returnType: typeParameterTypeStar(T), |
| ), |
| ), |
| ], |
| returnType: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: typeParameterTypeStar(T)), |
| ], |
| returnType: typeParameterTypeStar(T), |
| ), |
| ); |
| expect( |
| _inferCall(f, [ |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: numStar), |
| ], |
| returnType: intStar, |
| ) |
| ]), |
| [intStar], |
| ); |
| } |
| |
| void test_returnFunctionWithGenericReturn() { |
| // <T>(T -> T) -> (() -> T) |
| var T = typeParameter('T'); |
| var f = functionTypeStar( |
| typeFormals: [T], |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: typeParameterTypeStar(T)), |
| ], |
| returnType: typeParameterTypeStar(T), |
| ), |
| ), |
| ], |
| returnType: functionTypeStar( |
| returnType: typeParameterTypeStar(T), |
| ), |
| ); |
| expect( |
| _inferCall(f, [ |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: numStar), |
| ], |
| returnType: intStar, |
| ) |
| ]), |
| [intStar], |
| ); |
| } |
| |
| void test_returnTypeFromContext() { |
| // <T>() -> T |
| var T = typeParameter('T'); |
| var f = functionTypeStar( |
| typeFormals: [T], |
| returnType: typeParameterTypeStar(T), |
| ); |
| expect(_inferCall(f, [], returnType: stringStar), [stringStar]); |
| } |
| |
| void test_returnTypeWithBoundFromContext() { |
| // <T extends num>() -> T |
| var T = typeParameter('T', bound: numStar); |
| var f = functionTypeStar( |
| typeFormals: [T], |
| returnType: typeParameterTypeStar(T), |
| ); |
| expect(_inferCall(f, [], returnType: doubleStar), [doubleStar]); |
| } |
| |
| void test_returnTypeWithBoundFromInvalidContext() { |
| // <T extends num>() -> T |
| var T = typeParameter('T', bound: numStar); |
| var f = functionTypeStar( |
| typeFormals: [T], |
| returnType: typeParameterTypeStar(T), |
| ); |
| expect(_inferCall(f, [], returnType: stringStar), [nullStar]); |
| } |
| |
| void test_unifyParametersToFunctionParam() { |
| // <T>(f(T t), g(T t)) -> T |
| var T = typeParameter('T'); |
| var cast = functionTypeStar( |
| typeFormals: [T], |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: typeParameterTypeStar(T), |
| ), |
| ], |
| returnType: dynamicType, |
| ), |
| ), |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: typeParameterTypeStar(T), |
| ), |
| ], |
| returnType: dynamicType, |
| ), |
| ), |
| ], |
| returnType: typeParameterTypeStar(T), |
| ); |
| expect( |
| _inferCall(cast, [ |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| ], |
| returnType: dynamicType, |
| ), |
| functionTypeStar( |
| parameters: [ |
| requiredParameter(type: doubleStar), |
| ], |
| returnType: dynamicType, |
| ) |
| ]), |
| [nullStar], |
| ); |
| } |
| |
| void test_unusedReturnTypeIsDynamic() { |
| // <T>() -> T |
| var T = typeParameter('T'); |
| var f = functionTypeStar( |
| typeFormals: [T], |
| returnType: typeParameterTypeStar(T), |
| ); |
| expect(_inferCall(f, []), [dynamicType]); |
| } |
| |
| void test_unusedReturnTypeWithUpperBound() { |
| // <T extends num>() -> T |
| var T = typeParameter('T', bound: numStar); |
| var f = functionTypeStar( |
| typeFormals: [T], |
| returnType: typeParameterTypeStar(T), |
| ); |
| expect(_inferCall(f, []), [numStar]); |
| } |
| |
| void _assertType(DartType type, String expected) { |
| var typeStr = type.getDisplayString(withNullability: false); |
| expect(typeStr, expected); |
| } |
| |
| List<DartType> _inferCall(FunctionTypeImpl ft, List<DartType> arguments, |
| {DartType returnType, bool expectError = false}) { |
| var listener = RecordingErrorListener(); |
| |
| var reporter = ErrorReporter( |
| listener, |
| NonExistingSource('/test.dart', toUri('/test.dart'), UriKind.FILE_URI), |
| isNonNullableByDefault: false, |
| ); |
| |
| var typeArguments = typeSystem.inferGenericFunctionOrType( |
| typeParameters: ft.typeFormals, |
| parameters: ft.parameters, |
| declaredReturnType: ft.returnType, |
| argumentTypes: arguments, |
| contextReturnType: returnType, |
| errorReporter: reporter, |
| errorNode: astFactory.nullLiteral(KeywordToken(Keyword.NULL, 0)), |
| ); |
| |
| if (expectError) { |
| expect(listener.errors.map((e) => e.errorCode).toList(), |
| [StrongModeCode.COULD_NOT_INFER], |
| reason: 'expected exactly 1 could not infer error.'); |
| } else { |
| expect(listener.errors, isEmpty, reason: 'did not expect any errors.'); |
| } |
| return typeArguments; |
| } |
| |
| FunctionType _inferCall2(FunctionTypeImpl ft, List<DartType> arguments, |
| {DartType returnType, bool expectError = false}) { |
| var typeArguments = _inferCall( |
| ft, |
| arguments, |
| returnType: returnType, |
| expectError: expectError, |
| ); |
| return ft.instantiate(typeArguments); |
| } |
| } |
| |
| @reflectiveTest |
| class GreatestLowerBoundTest extends BoundTestBase { |
| void test_bottom_function() { |
| _checkGreatestLowerBound( |
| neverStar, functionTypeStar(returnType: voidNone), neverStar); |
| } |
| |
| void test_bottom_interface() { |
| var A = class_(name: 'A'); |
| _checkGreatestLowerBound(neverStar, interfaceTypeStar(A), neverStar); |
| } |
| |
| void test_bottom_typeParam() { |
| var T = typeParameter('T'); |
| _checkGreatestLowerBound(neverStar, typeParameterTypeStar(T), neverStar); |
| } |
| |
| void test_bounds_of_top_types_complete() { |
| // Test every combination of a subset of Tops programatically. |
| var futureOrDynamicType = futureOrStar(dynamicType); |
| var futureOrObjectType = futureOrStar(objectStar); |
| var futureOrVoidType = futureOrStar(voidNone); |
| final futureOrFutureOrDynamicType = futureOrStar(futureOrDynamicType); |
| final futureOrFutureOrObjectType = futureOrStar(futureOrObjectType); |
| final futureOrFutureOrVoidType = futureOrStar(futureOrVoidType); |
| |
| var orderedTops = [ |
| // Lower index, so lower Top |
| voidNone, |
| dynamicType, |
| objectStar, |
| futureOrVoidType, |
| futureOrDynamicType, |
| futureOrObjectType, |
| futureOrFutureOrVoidType, |
| futureOrFutureOrDynamicType, |
| futureOrFutureOrObjectType, |
| // Higher index, higher Top |
| ]; |
| |
| // We could sort and check the sort result is correct in O(n log n), but a |
| // good sorting algorithm would only run n tests here (that each value is |
| // correct relative to its nearest neighbors). But O(n^2) for n=6 is stupid |
| // fast, in this case, so just do the brute force check because we can. |
| for (var i = 0; i < orderedTops.length; ++i) { |
| for (var lower = 0; lower <= i; ++lower) { |
| _checkGreatestLowerBound( |
| orderedTops[i], orderedTops[lower], orderedTops[i]); |
| _checkLeastUpperBound( |
| orderedTops[i], orderedTops[lower], orderedTops[lower]); |
| } |
| for (var greater = i; greater < orderedTops.length; ++greater) { |
| _checkGreatestLowerBound( |
| orderedTops[i], orderedTops[greater], orderedTops[greater]); |
| _checkLeastUpperBound( |
| orderedTops[i], orderedTops[greater], orderedTops[i]); |
| } |
| } |
| } |
| |
| void test_bounds_of_top_types_sanity() { |
| var futureOrDynamicType = futureOrStar(dynamicType); |
| final futureOrFutureOrDynamicType = futureOrStar(futureOrDynamicType); |
| |
| // Sanity check specific cases of top for GLB/LUB. |
| _checkLeastUpperBound(objectStar, dynamicType, dynamicType); |
| _checkGreatestLowerBound(objectStar, dynamicType, objectStar); |
| _checkLeastUpperBound(objectStar, voidNone, voidNone); |
| _checkLeastUpperBound(futureOrDynamicType, dynamicType, dynamicType); |
| _checkGreatestLowerBound( |
| futureOrDynamicType, objectStar, futureOrDynamicType); |
| _checkGreatestLowerBound(futureOrDynamicType, futureOrFutureOrDynamicType, |
| futureOrFutureOrDynamicType); |
| } |
| |
| void test_classAndSuperclass() { |
| // class A |
| // class B extends A |
| // class C extends B |
| var A = class_(name: 'A'); |
| var B = class_(name: 'B', superType: interfaceTypeStar(A)); |
| var C = class_(name: 'C', superType: interfaceTypeStar(B)); |
| _checkGreatestLowerBound( |
| interfaceTypeStar(A), |
| interfaceTypeStar(C), |
| interfaceTypeStar(C), |
| ); |
| } |
| |
| void test_classAndSuperinterface() { |
| // class A |
| // class B implements A |
| // class C implements B |
| var A = class_(name: 'A'); |
| var B = class_(name: 'B', interfaces: [interfaceTypeStar(A)]); |
| var C = class_(name: 'C', interfaces: [interfaceTypeStar(B)]); |
| _checkGreatestLowerBound( |
| interfaceTypeStar(A), |
| interfaceTypeStar(C), |
| interfaceTypeStar(C), |
| ); |
| } |
| |
| void test_dynamic_bottom() { |
| _checkGreatestLowerBound(dynamicType, neverStar, neverStar); |
| } |
| |
| void test_dynamic_function() { |
| _checkGreatestLowerBound( |
| dynamicType, |
| functionTypeStar(returnType: voidNone), |
| functionTypeStar(returnType: voidNone)); |
| } |
| |
| void test_dynamic_interface() { |
| var A = class_(name: 'A'); |
| var typeA = interfaceTypeStar(A); |
| _checkGreatestLowerBound(dynamicType, typeA, typeA); |
| } |
| |
| void test_dynamic_typeParam() { |
| var T = typeParameter('T'); |
| var typeT = typeParameterTypeStar(T); |
| _checkGreatestLowerBound(dynamicType, typeT, typeT); |
| } |
| |
| void test_dynamic_void() { |
| // Note: _checkGreatestLowerBound tests `GLB(x, y)` as well as `GLB(y, x)` |
| _checkGreatestLowerBound(dynamicType, voidNone, dynamicType); |
| } |
| |
| void test_functionsDifferentNamedTakeUnion() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: intStar), |
| namedParameter(name: 'b', type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'b', type: doubleStar), |
| namedParameter(name: 'c', type: stringStar), |
| ], |
| returnType: voidNone, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: intStar), |
| namedParameter(name: 'b', type: numStar), |
| namedParameter(name: 'c', type: stringStar), |
| ], |
| returnType: voidNone, |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsDifferentOptionalArityTakeMax() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: doubleStar), |
| positionalParameter(type: stringStar), |
| positionalParameter(type: objectStar), |
| ], |
| returnType: voidNone, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: numStar), |
| positionalParameter(type: stringStar), |
| positionalParameter(type: objectStar), |
| ], |
| returnType: voidNone, |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsDifferentRequiredArityBecomeOptional() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| requiredParameter(type: intStar), |
| requiredParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| positionalParameter(type: intStar), |
| positionalParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsFromDynamic() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: dynamicType), |
| ], |
| returnType: voidNone, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: dynamicType), |
| ], |
| returnType: voidNone, |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsGlbReturnType() { |
| var type1 = functionTypeStar( |
| returnType: intStar, |
| ); |
| var type2 = functionTypeStar( |
| returnType: numStar, |
| ); |
| var expected = functionTypeStar( |
| returnType: intStar, |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsLubNamedParams() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: stringStar), |
| namedParameter(name: 'b', type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: intStar), |
| namedParameter(name: 'b', type: numStar), |
| ], |
| returnType: voidNone, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: objectStar), |
| namedParameter(name: 'b', type: numStar), |
| ], |
| returnType: voidNone, |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsLubPositionalParams() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: stringStar), |
| positionalParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: intStar), |
| positionalParameter(type: numStar), |
| ], |
| returnType: voidNone, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: objectStar), |
| positionalParameter(type: numStar), |
| ], |
| returnType: voidNone, |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsLubRequiredParams() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringStar), |
| requiredParameter(type: intStar), |
| requiredParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| requiredParameter(type: doubleStar), |
| requiredParameter(type: numStar), |
| ], |
| returnType: voidNone, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: objectStar), |
| requiredParameter(type: numStar), |
| requiredParameter(type: numStar), |
| ], |
| returnType: voidNone, |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsMixedOptionalAndRequiredBecomeOptional() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| requiredParameter(type: intStar), |
| positionalParameter(type: intStar), |
| positionalParameter(type: intStar), |
| positionalParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| positionalParameter(type: intStar), |
| positionalParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| positionalParameter(type: intStar), |
| positionalParameter(type: intStar), |
| positionalParameter(type: intStar), |
| positionalParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsReturnBottomIfMixOptionalAndNamed() { |
| // Dart doesn't allow a function to have both optional and named parameters, |
| // so if we would have synthethized that, pick bottom instead. |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| namedParameter(name: 'a', type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| _checkGreatestLowerBound(type1, type2, neverStar); |
| } |
| |
| void test_functionsSameType_withNamed() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringStar), |
| requiredParameter(type: intStar), |
| requiredParameter(type: numStar), |
| namedParameter(name: 'n', type: numStar), |
| ], |
| returnType: intStar, |
| ); |
| |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringStar), |
| requiredParameter(type: intStar), |
| requiredParameter(type: numStar), |
| namedParameter(name: 'n', type: numStar), |
| ], |
| returnType: intStar, |
| ); |
| |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringStar), |
| requiredParameter(type: intStar), |
| requiredParameter(type: numStar), |
| namedParameter(name: 'n', type: numStar), |
| ], |
| returnType: intStar, |
| ); |
| |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_functionsSameType_withOptional() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringStar), |
| requiredParameter(type: intStar), |
| requiredParameter(type: numStar), |
| positionalParameter(type: doubleStar), |
| ], |
| returnType: intStar, |
| ); |
| |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringStar), |
| requiredParameter(type: intStar), |
| requiredParameter(type: numStar), |
| positionalParameter(type: doubleStar), |
| ], |
| returnType: intStar, |
| ); |
| |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringStar), |
| requiredParameter(type: intStar), |
| requiredParameter(type: numStar), |
| positionalParameter(type: doubleStar), |
| ], |
| returnType: intStar, |
| ); |
| |
| _checkGreatestLowerBound(type1, type2, expected); |
| } |
| |
| void test_interface_function() { |
| var A = class_(name: 'A'); |
| var typeA = interfaceTypeStar(A); |
| _checkGreatestLowerBound( |
| typeA, |
| functionTypeStar(returnType: voidNone), |
| neverStar, |
| ); |
| } |
| |
| void test_mixin() { |
| // class A |
| // class B |
| // class C |
| // class D extends A with B, C |
| var A = class_(name: 'A'); |
| var typeA = interfaceTypeStar(A); |
| |
| var B = class_(name: 'B'); |
| var typeB = interfaceTypeStar(B); |
| |
| var C = class_(name: 'C'); |
| var typeC = interfaceTypeStar(C); |
| |
| var D = class_( |
| name: 'D', |
| superType: interfaceTypeStar(A), |
| mixins: [typeB, typeC], |
| ); |
| var typeD = interfaceTypeStar(D); |
| |
| _checkGreatestLowerBound(typeA, typeD, typeD); |
| _checkGreatestLowerBound(typeB, typeD, typeD); |
| _checkGreatestLowerBound(typeC, typeD, typeD); |
| } |
| |
| void test_self() { |
| var T = typeParameter('T'); |
| var A = class_(name: 'A'); |
| |
| List<DartType> types = [ |
| dynamicType, |
| voidNone, |
| neverStar, |
| typeParameterTypeStar(T), |
| interfaceTypeStar(A), |
| functionTypeStar(returnType: voidNone), |
| ]; |
| |
| for (DartType type in types) { |
| _checkGreatestLowerBound(type, type, type); |
| } |
| } |
| |
| void test_typeParam_function_noBound() { |
| var T = typeParameter('T'); |
| _checkGreatestLowerBound( |
| typeParameterTypeStar(T), |
| functionTypeStar(returnType: voidNone), |
| neverStar, |
| ); |
| } |
| |
| void test_typeParam_interface_bounded() { |
| var A = class_(name: 'A'); |
| var typeA = interfaceTypeStar(A); |
| |
| var B = class_(name: 'B', superType: typeA); |
| var typeB = interfaceTypeStar(B); |
| |
| var C = class_(name: 'C', superType: typeB); |
| var typeC = interfaceTypeStar(C); |
| |
| var T = typeParameter('T', bound: typeB); |
| _checkGreatestLowerBound(typeParameterTypeStar(T), typeC, neverStar); |
| } |
| |
| void test_typeParam_interface_noBound() { |
| // GLB(T, A) = ⊥ |
| var T = typeParameter('T'); |
| var A = class_(name: 'A'); |
| _checkGreatestLowerBound( |
| typeParameterTypeStar(T), |
| interfaceTypeStar(A), |
| neverStar, |
| ); |
| } |
| |
| void test_typeParameters_different() { |
| // GLB(List<int>, List<double>) = ⊥ |
| var listOfIntType = listStar(intStar); |
| var listOfDoubleType = listStar(doubleStar); |
| // TODO(rnystrom): Can we do something better here? |
| _checkGreatestLowerBound(listOfIntType, listOfDoubleType, neverStar); |
| } |
| |
| void test_typeParameters_same() { |
| // GLB(List<int>, List<int>) = List<int> |
| var listOfIntType = listStar(intStar); |
| _checkGreatestLowerBound(listOfIntType, listOfIntType, listOfIntType); |
| } |
| |
| void test_unrelatedClasses() { |
| // class A |
| // class B |
| // class C |
| var A = class_(name: 'A'); |
| var B = class_(name: 'B'); |
| _checkGreatestLowerBound( |
| interfaceTypeStar(A), interfaceTypeStar(B), neverStar); |
| } |
| |
| void test_void() { |
| var A = class_(name: 'A'); |
| var T = typeParameter('T'); |
| List<DartType> types = [ |
| neverStar, |
| functionTypeStar(returnType: voidNone), |
| interfaceTypeStar(A), |
| typeParameterTypeStar(T), |
| ]; |
| for (DartType type in types) { |
| _checkGreatestLowerBound( |
| functionTypeStar(returnType: voidNone), |
| functionTypeStar(returnType: type), |
| functionTypeStar(returnType: type), |
| ); |
| } |
| } |
| } |
| |
| @reflectiveTest |
| class LeastUpperBoundFunctionsTest extends BoundTestBase { |
| void test_differentRequiredArity() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| requiredParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| requiredParameter(type: intStar), |
| requiredParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| _checkLeastUpperBound(type1, type2, typeProvider.functionType); |
| } |
| |
| void test_fuzzyArrows() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: dynamicType), |
| ], |
| returnType: voidNone, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_glbNamedParams() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: stringStar), |
| namedParameter(name: 'b', type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: intStar), |
| namedParameter(name: 'b', type: numStar), |
| ], |
| returnType: voidNone, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: neverStar), |
| namedParameter(name: 'b', type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_glbPositionalParams() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: stringStar), |
| positionalParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: intStar), |
| positionalParameter(type: numStar), |
| ], |
| returnType: voidNone, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: neverStar), |
| positionalParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_glbRequiredParams() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringStar), |
| requiredParameter(type: intStar), |
| requiredParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| requiredParameter(type: doubleStar), |
| requiredParameter(type: numStar), |
| ], |
| returnType: voidNone, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: neverStar), |
| requiredParameter(type: neverStar), |
| requiredParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_ignoreExtraNamedParams() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: intStar), |
| namedParameter(name: 'b', type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: intStar), |
| namedParameter(name: 'c', type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| namedParameter(name: 'a', type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_ignoreExtraPositionalParams() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: intStar), |
| positionalParameter(type: intStar), |
| positionalParameter(type: stringStar), |
| ], |
| returnType: voidNone, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| positionalParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_lubReturnType() { |
| var type1 = functionTypeStar(returnType: intStar); |
| var type2 = functionTypeStar(returnType: doubleStar); |
| var expected = functionTypeStar(returnType: numStar); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_sameType_withNamed() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringStar), |
| requiredParameter(type: intStar), |
| requiredParameter(type: numStar), |
| namedParameter(name: 'n', type: numStar), |
| ], |
| returnType: intStar, |
| ); |
| |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringStar), |
| requiredParameter(type: intStar), |
| requiredParameter(type: numStar), |
| namedParameter(name: 'n', type: numStar), |
| ], |
| returnType: intStar, |
| ); |
| |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringStar), |
| requiredParameter(type: intStar), |
| requiredParameter(type: numStar), |
| namedParameter(name: 'n', type: numStar), |
| ], |
| returnType: intStar, |
| ); |
| |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_sameType_withOptional() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringStar), |
| requiredParameter(type: intStar), |
| requiredParameter(type: numStar), |
| positionalParameter(type: doubleStar), |
| ], |
| returnType: intStar, |
| ); |
| |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringStar), |
| requiredParameter(type: intStar), |
| requiredParameter(type: numStar), |
| positionalParameter(type: doubleStar), |
| ], |
| returnType: intStar, |
| ); |
| |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringStar), |
| requiredParameter(type: intStar), |
| requiredParameter(type: numStar), |
| positionalParameter(type: doubleStar), |
| ], |
| returnType: intStar, |
| ); |
| |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_typeFormals_differentBounds() { |
| var T1 = typeParameter('T1', bound: intStar); |
| var type1 = functionTypeStar( |
| typeFormals: [T1], |
| returnType: typeParameterTypeStar(T1), |
| ); |
| |
| var T2 = typeParameter('T2', bound: doubleStar); |
| var type2 = functionTypeStar( |
| typeFormals: [T2], |
| returnType: typeParameterTypeStar(T2), |
| ); |
| |
| _checkLeastUpperBound(type1, type2, typeProvider.functionType); |
| } |
| |
| void test_typeFormals_differentNumber() { |
| var T1 = typeParameter('T1', bound: numStar); |
| var type1 = functionTypeStar( |
| typeFormals: [T1], |
| returnType: typeParameterTypeStar(T1), |
| ); |
| |
| var type2 = functionTypeStar(returnType: intStar); |
| |
| _checkLeastUpperBound(type1, type2, typeProvider.functionType); |
| } |
| |
| void test_typeFormals_sameBounds() { |
| var T1 = typeParameter('T1', bound: numStar); |
| var type1 = functionTypeStar( |
| typeFormals: [T1], |
| returnType: typeParameterTypeStar(T1), |
| ); |
| |
| var T2 = typeParameter('T2', bound: numStar); |
| var type2 = functionTypeStar( |
| typeFormals: [T2], |
| returnType: typeParameterTypeStar(T2), |
| ); |
| |
| var TE = typeParameter('T', bound: numStar); |
| var expected = functionTypeStar( |
| typeFormals: [TE], |
| returnType: typeParameterTypeStar(TE), |
| ); |
| |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| } |
| |
| @reflectiveTest |
| class LeastUpperBoundTest extends BoundTestBase { |
| void test_bottom_function() { |
| _checkLeastUpperBound(neverStar, functionTypeStar(returnType: voidNone), |
| functionTypeStar(returnType: voidNone)); |
| } |
| |
| void test_bottom_interface() { |
| var A = class_(name: 'A'); |
| var typeA = interfaceTypeStar(A); |
| _checkLeastUpperBound(neverStar, typeA, typeA); |
| } |
| |
| void test_bottom_typeParam() { |
| var T = typeParameter('T'); |
| var typeT = typeParameterTypeStar(T); |
| _checkLeastUpperBound(neverStar, typeT, typeT); |
| } |
| |
| void test_directInterfaceCase() { |
| // class A |
| // class B implements A |
| // class C implements B |
| |
| var A = class_(name: 'A'); |
| var typeA = interfaceTypeStar(A); |
| |
| var B = class_(name: 'B', interfaces: [typeA]); |
| var typeB = interfaceTypeStar(B); |
| |
| var C = class_(name: 'C', interfaces: [typeB]); |
| var typeC = interfaceTypeStar(C); |
| |
| _checkLeastUpperBound(typeB, typeC, typeB); |
| } |
| |
| void test_directSubclassCase() { |
| // class A |
| // class B extends A |
| // class C extends B |
| |
| var A = class_(name: 'A'); |
| var typeA = interfaceTypeStar(A); |
| |
| var B = class_(name: 'B', superType: typeA); |
| var typeB = interfaceTypeStar(B); |
| |
| var C = class_(name: 'C', superType: typeB); |
| var typeC = interfaceTypeStar(C); |
| |
| _checkLeastUpperBound(typeB, typeC, typeB); |
| } |
| |
| void test_directSuperclass_nullability() { |
| var aElement = class_(name: 'A'); |
| var aQuestion = interfaceTypeQuestion(aElement); |
| var aStar = interfaceTypeStar(aElement); |
| var aNone = interfaceTypeNone(aElement); |
| |
| var bElementStar = class_(name: 'B', superType: aStar); |
| var bElementNone = class_(name: 'B', superType: aNone); |
| |
| InterfaceTypeImpl _bTypeStarElement(NullabilitySuffix nullability) { |
| return interfaceType( |
| bElementStar, |
| nullabilitySuffix: nullability, |
| ); |
| } |
| |
| InterfaceTypeImpl _bTypeNoneElement(NullabilitySuffix nullability) { |
| return interfaceType( |
| bElementNone, |
| nullabilitySuffix: nullability, |
| ); |
| } |
| |
| var bStarQuestion = _bTypeStarElement(NullabilitySuffix.question); |
| var bStarStar = _bTypeStarElement(NullabilitySuffix.star); |
| var bStarNone = _bTypeStarElement(NullabilitySuffix.none); |
| |
| var bNoneQuestion = _bTypeNoneElement(NullabilitySuffix.question); |
| var bNoneStar = _bTypeNoneElement(NullabilitySuffix.star); |
| var bNoneNone = _bTypeNoneElement(NullabilitySuffix.none); |
| |
| void assertLUB(DartType type1, DartType type2, DartType expected) { |
| expect(typeSystem.getLeastUpperBound(type1, type2), expected); |
| expect(typeSystem.getLeastUpperBound(type2, type1), expected); |
| } |
| |
| assertLUB(bStarQuestion, aQuestion, aQuestion); |
| assertLUB(bStarQuestion, aStar, aQuestion); |
| assertLUB(bStarQuestion, aNone, aQuestion); |
| |
| assertLUB(bStarStar, aQuestion, aQuestion); |
| assertLUB(bStarStar, aStar, aStar); |
| assertLUB(bStarStar, aNone, aStar); |
| |
| assertLUB(bStarNone, aQuestion, aQuestion); |
| assertLUB(bStarNone, aStar, aStar); |
| assertLUB(bStarNone, aNone, aNone); |
| |
| assertLUB(bNoneQuestion, aQuestion, aQuestion); |
| assertLUB(bNoneQuestion, aStar, aQuestion); |
| assertLUB(bNoneQuestion, aNone, aQuestion); |
| |
| assertLUB(bNoneStar, aQuestion, aQuestion); |
| assertLUB(bNoneStar, aStar, aStar); |
| assertLUB(bNoneStar, aNone, aStar); |
| |
| assertLUB(bNoneNone, aQuestion, aQuestion); |
| assertLUB(bNoneNone, aStar, aStar); |
| assertLUB(bNoneNone, aNone, aNone); |
| } |
| |
| void test_dynamic_bottom() { |
| _checkLeastUpperBound(dynamicType, neverStar, dynamicType); |
| } |
| |
| void test_dynamic_function() { |
| _checkLeastUpperBound( |
| dynamicType, functionTypeStar(returnType: voidNone), dynamicType); |
| } |
| |
| void test_dynamic_interface() { |
| var A = class_(name: 'A'); |
| _checkLeastUpperBound(dynamicType, interfaceTypeStar(A), dynamicType); |
| } |
| |
| void test_dynamic_typeParam() { |
| var T = typeParameter('T'); |
| _checkLeastUpperBound(dynamicType, typeParameterTypeStar(T), dynamicType); |
| } |
| |
| void test_dynamic_void() { |
| // Note: _checkLeastUpperBound tests `LUB(x, y)` as well as `LUB(y, x)` |
| _checkLeastUpperBound(dynamicType, voidNone, voidNone); |
| } |
| |
| void test_interface_function() { |
| var A = class_(name: 'A'); |
| _checkLeastUpperBound(interfaceTypeStar(A), |
| functionTypeStar(returnType: voidNone), objectStar); |
| } |
| |
| void test_interface_sameElement_nullability() { |
| var aElement = class_(name: 'A'); |
| |
| var aQuestion = interfaceTypeQuestion(aElement); |
| var aStar = interfaceTypeStar(aElement); |
| var aNone = interfaceTypeNone(aElement); |
| |
| void assertLUB(DartType type1, DartType type2, DartType expected) { |
| expect(typeSystem.getLeastUpperBound(type1, type2), expected); |
| expect(typeSystem.getLeastUpperBound(type2, type1), expected); |
| } |
| |
| assertLUB(aQuestion, aQuestion, aQuestion); |
| assertLUB(aQuestion, aStar, aQuestion); |
| assertLUB(aQuestion, aNone, aQuestion); |
| |
| assertLUB(aStar, aQuestion, aQuestion); |
| assertLUB(aStar, aStar, aStar); |
| assertLUB(aStar, aNone, aStar); |
| |
| assertLUB(aNone, aQuestion, aQuestion); |
| assertLUB(aNone, aStar, aStar); |
| assertLUB(aNone, aNone, aNone); |
| } |
| |
| void test_mixinAndClass_constraintAndInterface() { |
| var classA = class_(name: 'A'); |
| var instA = InstantiatedClass(classA, []); |
| |
| var classB = class_( |
| name: 'B', |
| interfaces: [instA.withNullabilitySuffixNone], |
| ); |
| |
| var mixinM = mixin_( |
| name: 'M', |
| constraints: [instA.withNullabilitySuffixNone], |
| ); |
| |
| _checkLeastUpperBound( |
| interfaceTypeStar(classB), |
| interfaceTypeStar(mixinM), |
| instA.withNullability(NullabilitySuffix.star), |
| ); |
| } |
| |
| void test_mixinAndClass_object() { |
| var classA = class_(name: 'A'); |
| var mixinM = mixin_(name: 'M'); |
| |
| _checkLeastUpperBound( |
| interfaceTypeStar(classA), |
| interfaceTypeStar(mixinM), |
| objectStar, |
| ); |
| } |
| |
| void test_mixinAndClass_sharedInterface() { |
| var classA = class_(name: 'A'); |
| var instA = InstantiatedClass(classA, []); |
| |
| var classB = class_( |
| name: 'B', |
| interfaces: [instA.withNullabilitySuffixNone], |
| ); |
| |
| var mixinM = mixin_( |
| name: 'M', |
| interfaces: [instA.withNullabilitySuffixNone], |
| ); |
| |
| _checkLeastUpperBound( |
| interfaceTypeStar(classB), |
| interfaceTypeStar(mixinM), |
| instA.withNullability(NullabilitySuffix.star), |
| ); |
| } |
| |
| void test_mixinCase() { |
| // class A |
| // class B extends A |
| // class C extends A |
| // class D extends B with M, N, O, P |
| |
| var A = class_(name: 'A'); |
| var typeA = interfaceTypeStar(A); |
| |
| var B = class_(name: 'B', superType: typeA); |
| var typeB = interfaceTypeStar(B); |
| |
| var C = class_(name: 'C', superType: typeA); |
| var typeC = interfaceTypeStar(C); |
| |
| var D = class_( |
| name: 'D', |
| superType: typeB, |
| mixins: [ |
| interfaceTypeStar(class_(name: 'M')), |
| interfaceTypeStar(class_(name: 'N')), |
| interfaceTypeStar(class_(name: 'O')), |
| interfaceTypeStar(class_(name: 'P')), |
| ], |
| ); |
| var typeD = interfaceTypeStar(D); |
| |
| _checkLeastUpperBound(typeD, typeC, typeA); |
| } |
| |
| void test_nestedFunctionsLubInnerParamTypes() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringStar), |
| requiredParameter(type: intStar), |
| requiredParameter(type: intStar), |
| ], |
| returnType: voidNone, |
| ), |
| ), |
| ], |
| returnType: voidNone, |
| ); |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| requiredParameter(type: doubleStar), |
| requiredParameter(type: numStar), |
| ], |
| returnType: voidNone, |
| ), |
| ), |
| ], |
| returnType: voidNone, |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: objectStar), |
| requiredParameter(type: numStar), |
| requiredParameter(type: numStar), |
| ], |
| returnType: voidNone, |
| ), |
| ), |
| ], |
| returnType: voidNone, |
| ); |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_nestedNestedFunctionsGlbInnermostParamTypes() { |
| var type1 = functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: stringStar), |
| requiredParameter(type: intStar), |
| requiredParameter(type: intStar) |
| ], |
| returnType: voidNone, |
| ), |
| ), |
| ], |
| returnType: voidNone, |
| ), |
| ), |
| ], |
| returnType: voidNone, |
| ); |
| expect( |
| _typeString(type1), |
| 'void Function(void Function(void Function(String*, int*, int*)*)*)*', |
| ); |
| |
| var type2 = functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: intStar), |
| requiredParameter(type: doubleStar), |
| requiredParameter(type: numStar) |
| ], |
| returnType: voidNone, |
| ), |
| ), |
| ], |
| returnType: voidNone, |
| ), |
| ), |
| ], |
| returnType: voidNone, |
| ); |
| expect( |
| _typeString(type2), |
| 'void Function(void Function(void Function(int*, double*, num*)*)*)*', |
| ); |
| var expected = functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter( |
| type: functionTypeStar( |
| parameters: [ |
| requiredParameter(type: neverStar), |
| requiredParameter(type: neverStar), |
| requiredParameter(type: intStar) |
| ], |
| returnType: voidNone, |
| ), |
| ), |
| ], |
| returnType: voidNone, |
| ), |
| ), |
| ], |
| returnType: voidNone, |
| ); |
| expect( |
| _typeString(expected), |
| 'void Function(void Function(void Function(Never*, Never*, int*)*)*)*', |
| ); |
| |
| _checkLeastUpperBound(type1, type2, expected); |
| } |
| |
| void test_object() { |
| var A = class_(name: 'A'); |
| var B = class_(name: 'B'); |
| var typeA = interfaceTypeStar(A); |
| var typeB = interfaceTypeStar(B); |
| var typeObject = typeA.element.supertype; |
| // assert that object does not have a super type |
| expect(typeObject.element.supertype, isNull); |
| // assert that both A and B have the same super type of Object |
| expect(typeB.element.supertype, typeObject); |
| // finally, assert that the only least upper bound of A and B is Object |
| _checkLeastUpperBound(typeA, typeB, typeObject); |
| } |
| |
| void test_self() { |
| var T = typeParameter('T'); |
| var A = class_(name: 'A'); |
| |
| List<DartType> types = [ |
| dynamicType, |
| voidNone, |
| neverStar, |
| typeParameterTypeStar(T), |
| interfaceTypeStar(A), |
| functionTypeStar(returnType: voidNone) |
| ]; |
| |
| for (DartType type in types) { |
| _checkLeastUpperBound(type, type, type); |
| } |
| } |
| |
| void test_sharedSuperclass1() { |
| var A = class_(name: 'A'); |
| var typeA = interfaceTypeStar(A); |
| |
| var B = class_(name: 'B', superType: typeA); |
| var typeB = interfaceTypeStar(B); |
| |
| var C = class_(name: 'C', superType: typeA); |
| var typeC = interfaceTypeStar(C); |
| |
| _checkLeastUpperBound(typeB, typeC, typeA); |
| } |
| |
| void test_sharedSuperclass1_nullability() { |
| var aElement = class_(name: 'A'); |
| var aQuestion = interfaceTypeQuestion(aElement); |
| var aStar = interfaceTypeStar(aElement); |
| var aNone = interfaceTypeNone(aElement); |
| |
| var bElementNone = class_(name: 'B', superType: aNone); |
| var bElementStar = class_(name: 'B', superType: aStar); |
| |
| var cElementNone = class_(name: 'C', superType: aNone); |
| var cElementStar = class_(name: 'C', superType: aStar); |
| |
| InterfaceTypeImpl bTypeElementNone(NullabilitySuffix nullability) { |
| return interfaceType( |
| bElementNone, |
| nullabilitySuffix: nullability, |
| ); |
| } |
| |
| InterfaceTypeImpl bTypeElementStar(NullabilitySuffix nullability) { |
| return interfaceType( |
| bElementStar, |
| nullabilitySuffix: nullability, |
| ); |
| } |
| |
| var bNoneQuestion = bTypeElementNone(NullabilitySuffix.question); |
| var bNoneStar = bTypeElementNone(NullabilitySuffix.star); |
| var bNoneNone = bTypeElementNone(NullabilitySuffix.none); |
| |
| var bStarQuestion = bTypeElementStar(NullabilitySuffix.question); |
| var bStarStar = bTypeElementStar(NullabilitySuffix.star); |
| var bStarNone = bTypeElementStar(NullabilitySuffix.none); |
| |
| InterfaceTypeImpl cTypeElementNone(NullabilitySuffix nullability) { |
| return interfaceType( |
| cElementNone, |
| nullabilitySuffix: nullability, |
| ); |
| } |
| |
| InterfaceTypeImpl cTypeElementStar(NullabilitySuffix nullability) { |
| return interfaceType( |
| cElementStar, |
| nullabilitySuffix: nullability, |
| ); |
| } |
| |
| var cNoneQuestion = cTypeElementNone(NullabilitySuffix.question); |
| var cNoneStar = cTypeElementNone(NullabilitySuffix.star); |
| var cNoneNone = cTypeElementNone(NullabilitySuffix.none); |
| |
| var cStarQuestion = cTypeElementStar(NullabilitySuffix.question); |
| var cStarStar = cTypeElementStar(NullabilitySuffix.star); |
| var cStarNone = cTypeElementStar(NullabilitySuffix.none); |
| |
| void assertLUB(DartType type1, DartType type2, DartType expected) { |
| expect(typeSystem.getLeastUpperBound(type1, type2), expected); |
| expect(typeSystem.getLeastUpperBound(type2, type1), expected); |
| } |
| |
| assertLUB(bNoneQuestion, cNoneQuestion, aQuestion); |
| assertLUB(bNoneQuestion, cNoneStar, aQuestion); |
| assertLUB(bNoneQuestion, cNoneNone, aQuestion); |
| assertLUB(bNoneQuestion, cStarQuestion, aQuestion); |
| assertLUB(bNoneQuestion, cStarStar, aQuestion); |
| assertLUB(bNoneQuestion, cStarNone, aQuestion); |
| |
| assertLUB(bNoneStar, cNoneQuestion, aQuestion); |
| assertLUB(bNoneStar, cNoneStar, aStar); |
| assertLUB(bNoneStar, cNoneNone, aStar); |
| assertLUB(bNoneStar, cStarQuestion, aQuestion); |
| assertLUB(bNoneStar, cStarStar, aStar); |
| assertLUB(bNoneStar, cStarNone, aStar); |
| |
| assertLUB(bNoneNone, cNoneQuestion, aQuestion); |
| assertLUB(bNoneNone, cNoneStar, aStar); |
| assertLUB(bNoneNone, cNoneNone, aNone); |
| assertLUB(bNoneNone, cStarQuestion, aQuestion); |
| assertLUB(bNoneNone, cStarStar, aStar); |
| assertLUB(bNoneNone, cStarNone, aNone); |
| |
| assertLUB(bStarQuestion, cNoneQuestion, aQuestion); |
| assertLUB(bStarQuestion, cNoneStar, aQuestion); |
| assertLUB(bStarQuestion, cNoneNone, aQuestion); |
| assertLUB(bStarQuestion, cStarQuestion, aQuestion); |
| assertLUB(bStarQuestion, cStarStar, aQuestion); |
| assertLUB(bStarQuestion, cStarNone, aQuestion); |
| |
| assertLUB(bStarStar, cNoneQuestion, aQuestion); |
| assertLUB(bStarStar, cNoneStar, aStar); |
| assertLUB(bStarStar, cNoneNone, aStar); |
| assertLUB(bStarStar, cStarQuestion, aQuestion); |
| assertLUB(bStarStar, cStarStar, aStar); |
| assertLUB(bStarStar, cStarNone, aStar); |
| |
| assertLUB(bStarNone, cNoneQuestion, aQuestion); |
| assertLUB(bStarNone, cNoneStar, aStar); |
| assertLUB(bStarNone, cNoneNone, aNone); |
| assertLUB(bStarNone, cStarQuestion, aQuestion); |
| assertLUB(bStarNone, cStarStar, aStar); |
| assertLUB(bStarNone, cStarNone, aNone); |
| } |
| |
| void test_sharedSuperclass2() { |
| var A = class_(name: 'A'); |
| var typeA = interfaceTypeStar(A); |
| |
| var B = class_(name: 'B', superType: typeA); |
| var typeB = interfaceTypeStar(B); |
| |
| var C = class_(name: 'C', superType: typeA); |
| var typeC = interfaceTypeStar(C); |
| |
| var D = class_(name: 'D', superType: typeC); |
| var typeD = interfaceTypeStar(D); |
| |
| _checkLeastUpperBound(typeB, typeD, typeA); |
| } |
| |
| void test_sharedSuperclass3() { |
| var A = class_(name: 'A'); |
| var typeA = interfaceTypeStar(A); |
| |
| var B = class_(name: 'B', superType: typeA); |
| var typeB = interfaceTypeStar(B); |
| |
| var C = class_(name: 'C', superType: typeB); |
| var typeC = interfaceTypeStar(C); |
| |
| var D = class_(name: 'D', superType: typeB); |
| var typeD = interfaceTypeStar(D); |
| |
| _checkLeastUpperBound(typeC, typeD, typeB); |
| } |
| |
| void test_sharedSuperclass4() { |
| var A = class_(name: 'A'); |
| var typeA = interfaceTypeStar(A); |
| |
| var A2 = class_(name: 'A2'); |
| var typeA2 = interfaceTypeStar(A2); |
| |
| var A3 = class_(name: 'A3'); |
| var typeA3 = interfaceTypeStar(A3); |
| |
| var B = class_(name: 'B', superType: typeA, interfaces: [typeA2]); |
| var typeB = interfaceTypeStar(B); |
| |
| var C = class_(name: 'C', superType: typeA, interfaces: [typeA3]); |
| var typeC = interfaceTypeStar(C); |
| |
| _checkLeastUpperBound(typeB, typeC, typeA); |
| } |
| |
| void test_sharedSuperinterface1() { |
| var A = class_(name: 'A'); |
| var typeA = interfaceTypeStar(A); |
| |
| var B = class_(name: 'B', interfaces: [typeA]); |
| var typeB = interfaceTypeStar(B); |
| |
| var C = class_(name: 'C', interfaces: [typeA]); |
| var typeC = interfaceTypeStar(C); |
| |
| _checkLeastUpperBound(typeB, typeC, typeA); |
| } |
| |
| void test_sharedSuperinterface2() { |
| var A = class_(name: 'A'); |
| var typeA = interfaceTypeStar(A); |
| |
| var B = class_(name: 'B', interfaces: [typeA]); |
| var typeB = interfaceTypeStar(B); |
| |
| var C = class_(name: 'C', interfaces: [typeA]); |
| var typeC = interfaceTypeStar(C); |
| |
| var D = class_(name: 'D', interfaces: [typeC]); |
| var typeD = interfaceTypeStar(D); |
| |
| _checkLeastUpperBound(typeB, typeD, typeA); |
| } |
| |
| void test_sharedSuperinterface3() { |
| var A = class_(name: 'A'); |
| var typeA = interfaceTypeStar(A); |
| |
| var B = class_(name: 'B', interfaces: [typeA]); |
| var typeB = interfaceTypeStar(B); |
| |
| var C = class_(name: 'C', interfaces: [typeB]); |
| var typeC = interfaceTypeStar(C); |
| |
| var D = class_(name: 'D', interfaces: [typeB]); |
| var typeD = interfaceTypeStar(D); |
| |
| _checkLeastUpperBound(typeC, typeD, typeB); |
| } |
| |
| void test_sharedSuperinterface4() { |
| var A = class_(name: 'A'); |
| var typeA = interfaceTypeStar(A); |
| |
| var A2 = class_(name: 'A2'); |
| var typeA2 = interfaceTypeStar(A2); |
| |
| var A3 = class_(name: 'A3'); |
| var typeA3 = interfaceTypeStar(A3); |
| |
| var B = class_(name: 'B', interfaces: [typeA, typeA2]); |
| var typeB = interfaceTypeStar(B); |
| |
| var C = class_(name: 'C', interfaces: [typeA, typeA3]); |
| var typeC = interfaceTypeStar(C); |
| |
| _checkLeastUpperBound(typeB, typeC, typeA); |
| } |
| |
| void test_twoComparables() { |
| _checkLeastUpperBound(stringStar, numStar, objectStar); |
| } |
| |
| void test_typeParam_boundedByParam() { |
| var S = typeParameter('S'); |
| var typeS = typeParameterTypeStar(S); |
| |
| var T = typeParameter('T', bound: typeS); |
| var typeT = typeParameterTypeStar(T); |
| |
| _checkLeastUpperBound(typeT, typeS, typeS); |
| } |
| |
| void test_typeParam_class_implements_Function_ignored() { |
| var A = class_(name: 'A', superType: typeProvider.functionType); |
| var T = typeParameter('T', bound: interfaceTypeStar(A)); |
| _checkLeastUpperBound(typeParameterTypeStar(T), |
| functionTypeStar(returnType: voidNone), objectStar); |
| } |
| |
| void test_typeParam_fBounded() { |
| var T = typeParameter('Q'); |
| var A = class_(name: 'A', typeParameters: [T]); |
| |
| var S = typeParameter('S'); |
| var typeS = typeParameterTypeStar(S); |
| S.bound = interfaceTypeStar(A, typeArguments: [typeS]); |
| |
| var U = typeParameter('U'); |
| var typeU = typeParameterTypeStar(U); |
| U.bound = interfaceTypeStar(A, typeArguments: [typeU]); |
| |
| _checkLeastUpperBound( |
| typeS, |
| typeParameterTypeStar(U), |
| interfaceTypeStar(A, typeArguments: [objectStar]), |
| ); |
| } |
| |
| void test_typeParam_function_bounded() { |
| var T = typeParameter('T', bound: typeProvider.functionType); |
| _checkLeastUpperBound( |
| typeParameterTypeStar(T), |
| functionTypeStar(returnType: voidNone), |
| typeProvider.functionType, |
| ); |
| } |
| |
| void test_typeParam_function_noBound() { |
| var T = typeParameter('T'); |
| _checkLeastUpperBound( |
| typeParameterTypeStar(T), |
| functionTypeStar(returnType: voidNone), |
| objectStar, |
| ); |
| } |
| |
| void test_typeParam_interface_bounded() { |
| var A = class_(name: 'A'); |
| var typeA = interfaceTypeStar(A); |
| |
| var B = class_(name: 'B', superType: typeA); |
| var typeB = interfaceTypeStar(B); |
| |
| var C = class_(name: 'C', superType: typeA); |
| var typeC = interfaceTypeStar(C); |
| |
| var T = typeParameter('T', bound: typeB); |
| var typeT = typeParameterTypeStar(T); |
| |
| _checkLeastUpperBound(typeT, typeC, typeA); |
| } |
| |
| void test_typeParam_interface_noBound() { |
| var T = typeParameter('T'); |
| var A = class_(name: 'A'); |
| _checkLeastUpperBound( |
| typeParameterTypeStar(T), |
| interfaceTypeStar(A), |
| objectStar, |
| ); |
| } |
| |
| void test_typeParameters_contravariant_different() { |
| // class A<in T> |
| var T = typeParameter('T', variance: Variance.contravariant); |
| var A = class_(name: 'A', typeParameters: [T]); |
| |
| // A<num> |
| // A<int> |
| var aNum = interfaceTypeStar(A, typeArguments: [numStar]); |
| var aInt = interfaceTypeStar(A, typeArguments: [intStar]); |
| |
| _checkLeastUpperBound(aInt, aNum, aInt); |
| } |
| |
| void test_typeParameters_contravariant_same() { |
| // class A<in T> |
| var T = typeParameter('T', variance: Variance.contravariant); |
| var A = class_(name: 'A', typeParameters: [T]); |
| |
| // A<num> |
| var aNum = interfaceTypeStar(A, typeArguments: [numStar]); |
| |
| _checkLeastUpperBound(aNum, aNum, aNum); |
| } |
| |
| void test_typeParameters_covariant_different() { |
| // class A<out T> |
| var T = typeParameter('T', variance: Variance.covariant); |
| var A = class_(name: 'A', typeParameters: [T]); |
| |
| // A<num> |
| // A<int> |
| var aNum = interfaceTypeStar(A, typeArguments: [numStar]); |
| var aInt = interfaceTypeStar(A, typeArguments: [intStar]); |
| |
| _checkLeastUpperBound(aInt, aNum, aNum); |
| } |
| |
| void test_typeParameters_covariant_same() { |
| // class A<out T> |
| var T = typeParameter('T', variance: Variance.covariant); |
| var A = class_(name: 'A', typeParameters: [T]); |
| |
| // A<num> |
| var aNum = interfaceTypeStar(A, typeArguments: [numStar]); |
| |
| _checkLeastUpperBound(aNum, aNum, aNum); |
| } |
| |
| /// Check least upper bound of the same class with different type parameters. |
| void test_typeParameters_different() { |
| // class List<int> |
| // class List<double> |
| var listOfIntType = listStar(intStar); |
| var listOfDoubleType = listStar(doubleStar); |
| var listOfNum = listStar(numStar); |
| _checkLeastUpperBound(listOfIntType, listOfDoubleType, listOfNum); |
| } |
| |
| void test_typeParameters_invariant_object() { |
| // class A<inout T> |
| var T = typeParameter('T', variance: Variance.invariant); |
| var A = class_(name: 'A', typeParameters: [T]); |
| |
| // A<num> |
| // A<int> |
| var aNum = interfaceTypeStar(A, typeArguments: [numStar]); |
| var aInt = interfaceTypeStar(A, typeArguments: [intStar]); |
| |
| _checkLeastUpperBound(aNum, aInt, objectStar); |
| } |
| |
| void test_typeParameters_invariant_same() { |
| // class A<inout T> |
| var T = typeParameter('T', variance: Variance.invariant); |
| var A = class_(name: 'A', typeParameters: [T]); |
| |
| // A<num> |
| var aNum = interfaceTypeStar(A, typeArguments: [numStar]); |
| |
| _checkLeastUpperBound(aNum, aNum, aNum); |
| } |
| |
| void test_typeParameters_multi_basic() { |
| // class Multi<out T, inout U, in V> |
| var T = typeParameter('T', variance: Variance.covariant); |
| var U = typeParameter('T', variance: Variance.invariant); |
| var V = typeParameter('T', variance: Variance.contravariant); |
| var Multi = class_(name: 'A', typeParameters: [T, U, V]); |
| |
| // Multi<num, num, num> |
| // Multi<int, num, int> |
| var multiNumNumNum = |
| interfaceTypeStar(Multi, typeArguments: [numStar, numStar, numStar]); |
| var multiIntNumInt = |
| interfaceTypeStar(Multi, typeArguments: [intStar, numStar, intStar]); |
| |
| // We expect Multi<num, num, int> |
| var multiNumNumInt = |
| interfaceTypeStar(Multi, typeArguments: [numStar, numStar, intStar]); |
| |
| _checkLeastUpperBound(multiNumNumNum, multiIntNumInt, multiNumNumInt); |
| } |
| |
| void test_typeParameters_multi_objectInterface() { |
| // class Multi<out T, inout U, in V> |
| var T = typeParameter('T', variance: Variance.covariant); |
| var U = typeParameter('T', variance: Variance.invariant); |
| var V = typeParameter('T', variance: Variance.contravariant); |
| var Multi = class_(name: 'A', typeParameters: [T, U, V]); |
| |
| // Multi<num, String, num> |
| // Multi<int, num, int> |
| var multiNumStringNum = |
| interfaceTypeStar(Multi, typeArguments: [numStar, stringStar, numStar]); |
| var multiIntNumInt = |
| interfaceTypeStar(Multi, typeArguments: [intStar, numStar, intStar]); |
| |
| _checkLeastUpperBound(multiNumStringNum, multiIntNumInt, objectStar); |
| } |
| |
| void test_typeParameters_multi_objectType() { |
| // class Multi<out T, inout U, in V> |
| var T = typeParameter('T', variance: Variance.covariant); |
| var U = typeParameter('T', variance: Variance.invariant); |
| var V = typeParameter('T', variance: Variance.contravariant); |
| var Multi = class_(name: 'A', typeParameters: [T, U, V]); |
| |
| // Multi<String, num, num> |
| // Multi<int, num, int> |
| var multiStringNumNum = |
| interfaceTypeStar(Multi, typeArguments: [stringStar, numStar, numStar]); |
| var multiIntNumInt = |
| interfaceTypeStar(Multi, typeArguments: [intStar, numStar, intStar]); |
| |
| // We expect Multi<Object, num, int> |
| var multiObjectNumInt = |
| interfaceTypeStar(Multi, typeArguments: [objectStar, numStar, intStar]); |
| |
| _checkLeastUpperBound(multiStringNumNum, multiIntNumInt, multiObjectNumInt); |
| } |
| |
| void test_typeParameters_same() { |
| // List<int> |
| // List<int> |
| var listOfIntType = listStar(intStar); |
| _checkLeastUpperBound(listOfIntType, listOfIntType, listOfIntType); |
| } |
| |
| /// Check least upper bound of two related classes with different |
| /// type parameters. |
| void test_typeParametersAndClass_different() { |
| // class List<int> |
| // class Iterable<double> |
| var listOfIntType = listStar(intStar); |
| var iterableOfDoubleType = iterableStar(doubleStar); |
| // TODO(leafp): this should be iterableOfNumType |
| _checkLeastUpperBound(listOfIntType, iterableOfDoubleType, objectStar); |
| } |
| |
| void test_void() { |
| var T = typeParameter('T'); |
| var A = class_(name: 'A'); |
| List<DartType> types = [ |
| neverStar, |
| functionTypeStar(returnType: voidNone), |
| interfaceTypeStar(A), |
| typeParameterTypeStar(T), |
| ]; |
| for (DartType type in types) { |
| _checkLeastUpperBound( |
| functionTypeStar(returnType: voidNone), |
| functionTypeStar(returnType: type), |
| functionTypeStar(returnType: voidNone), |
| ); |
| } |
| } |
| } |
| |
| @reflectiveTest |
| class TryPromoteToTest extends AbstractTypeSystemTest { |
| @override |
| FeatureSet get testFeatureSet { |
| return FeatureSet.forTesting( |
| additionalFeatures: [Feature.non_nullable], |
| ); |
| } |
| |
| void notPromotes(DartType from, DartType to) { |
| var result = typeSystem.tryPromoteToType(to, from); |
| expect(result, isNull); |
| } |
| |
| void promotes(DartType from, DartType to) { |
| var result = typeSystem.tryPromoteToType(to, from); |
| expect(result, to); |
| } |
| |
| test_interface() { |
| promotes(intNone, intNone); |
| promotes(intQuestion, intNone); |
| promotes(intStar, intNone); |
| |
| promotes(numNone, intNone); |
| promotes(numQuestion, intNone); |
| promotes(numStar, intNone); |
| |
| notPromotes(intNone, doubleNone); |
| notPromotes(intNone, intQuestion); |
| } |
| |
| test_typeParameter() { |
| void check( |
| TypeParameterType type, |
| TypeParameterElement expectedDeclaration, |
| DartType expectedBound, |
| ) { |
| var actualElement = type.element as TypeParameterMember; |
| expect(actualElement.declaration, expectedDeclaration); |
| expect(actualElement.bound, expectedBound); |
| } |
| |
| var T = typeParameter('T'); |
| var T0 = typeParameterTypeNone(T); |
| |
| var T1 = typeSystem.tryPromoteToType(numNone, T0); |
| check(T1, T, numNone); |
| |
| var T2 = typeSystem.tryPromoteToType(intNone, T1); |
| check(T2, T, intNone); |
| } |
| } |
| |
| class _MockSource implements Source { |
| @override |
| final Uri uri; |
| |
| _MockSource(this.uri); |
| |
| @override |
| String get encoding => '$uri'; |
| |
| @override |
| noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); |
| } |