Support for type formals in function LUB.
R=brianwilkerson@google.com, paulberry@google.com
Change-Id: I5576db2279810bf1e4fc4a73870f60ca50166ee7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106924
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/element/type.dart b/pkg/analyzer/lib/src/dart/element/type.dart
index 27dd52e..e1e8de8 100644
--- a/pkg/analyzer/lib/src/dart/element/type.dart
+++ b/pkg/analyzer/lib/src/dart/element/type.dart
@@ -1212,14 +1212,18 @@
variables1.add(variable1);
variables2.add(variable2);
variablesFresh.add(variableFresh);
+
DartType bound1 = p1.bound ?? DynamicTypeImpl.instance;
DartType bound2 = p2.bound ?? DynamicTypeImpl.instance;
bound1 = bound1.substitute2(variablesFresh, variables1);
bound2 = bound2.substitute2(variablesFresh, variables2);
- pFresh.bound = bound2;
if (!relation(bound2, bound1, p2, p1)) {
return null;
}
+
+ if (!bound2.isDynamic) {
+ pFresh.bound = bound2;
+ }
}
return variablesFresh;
}
diff --git a/pkg/analyzer/lib/src/generated/type_system.dart b/pkg/analyzer/lib/src/generated/type_system.dart
index 53d06e1..9a902c2 100644
--- a/pkg/analyzer/lib/src/generated/type_system.dart
+++ b/pkg/analyzer/lib/src/generated/type_system.dart
@@ -2342,8 +2342,30 @@
* Return a function type with those types.
*/
DartType _functionLeastUpperBound(FunctionType f, FunctionType g) {
- // TODO(rnystrom): Right now, this assumes f and g do not have any type
- // parameters. Revisit that in the presence of generic methods.
+ var fTypeFormals = f.typeFormals;
+ var gTypeFormals = g.typeFormals;
+
+ // If F and G differ in their number of type parameters, then the
+ // least upper bound of F and G is Function.
+ if (fTypeFormals.length != gTypeFormals.length) {
+ return typeProvider.functionType;
+ }
+
+ // If F and G differ in bounds of their of type parameters, then the
+ // least upper bound of F and G is Function.
+ var freshTypeFormalTypes =
+ FunctionTypeImpl.relateTypeFormals(f, g, (t, s, _, __) => t == s);
+ if (freshTypeFormalTypes == null) {
+ return typeProvider.functionType;
+ }
+
+ var typeFormals = freshTypeFormalTypes
+ .map<TypeParameterElement>((t) => t.element)
+ .toList();
+
+ f = f.instantiate(freshTypeFormalTypes);
+ g = g.instantiate(freshTypeFormalTypes);
+
List<DartType> fRequired = f.normalParameterTypes;
List<DartType> gRequired = g.normalParameterTypes;
@@ -2394,7 +2416,14 @@
// Calculate the LUB of the return type.
DartType returnType = getLeastUpperBound(f.returnType, g.returnType);
- return new FunctionElementImpl.synthetic(parameters, returnType).type;
+
+ if (AnalysisDriver.useSummary2) {
+ return FunctionTypeImpl.synthetic(returnType, typeFormals, parameters);
+ }
+
+ var element = FunctionElementImpl.synthetic(parameters, returnType);
+ element.typeParameters = typeFormals;
+ return element.type;
}
/**
diff --git a/pkg/analyzer/test/generated/type_system_test.dart b/pkg/analyzer/test/generated/type_system_test.dart
index 511ce30..d4792a5 100644
--- a/pkg/analyzer/test/generated/type_system_test.dart
+++ b/pkg/analyzer/test/generated/type_system_test.dart
@@ -414,17 +414,66 @@
*
* The return type defaults to `void` if omitted.
*/
- FunctionType _functionType(List<DartType> required,
- {List<DartType> optional,
- Map<String, DartType> named,
- DartType returns}) {
- if (returns == null) {
- returns = voidType;
+ FunctionType _functionType({
+ List<TypeParameterElement> typeFormals,
+ List<DartType> required,
+ List<DartType> optional,
+ Map<String, DartType> named,
+ DartType returns,
+ }) {
+ if (optional != null && named != null) {
+ throw ArgumentError(
+ 'Cannot have both optional positional and named parameters.',
+ );
}
- return ElementFactory.functionElement8(required, returns,
- optional: optional, named: named)
- .type;
+ var parameters = <ParameterElement>[];
+ if (required != null) {
+ for (var i = 0; i < required.length; ++i) {
+ parameters.add(
+ ParameterElementImpl.synthetic(
+ 'r$i',
+ required[i],
+ ParameterKind.REQUIRED,
+ ),
+ );
+ }
+ }
+ if (optional != null) {
+ for (var i = 0; i < optional.length; ++i) {
+ parameters.add(
+ ParameterElementImpl.synthetic(
+ 'p$i',
+ optional[i],
+ ParameterKind.POSITIONAL,
+ ),
+ );
+ }
+ }
+ if (named != null) {
+ for (var namedEntry in named.entries) {
+ parameters.add(
+ ParameterElementImpl.synthetic(
+ namedEntry.key,
+ namedEntry.value,
+ ParameterKind.NAMED,
+ ),
+ );
+ }
+ }
+
+ return FunctionTypeImpl.synthetic(
+ returns ?? voidType,
+ typeFormals ?? const <TypeParameterElement>[],
+ parameters,
+ );
+ }
+
+ TypeParameterElementImpl _typeParameterElement(String name,
+ {DartType bound}) {
+ var element = TypeParameterElementImpl.synthetic(name);
+ element.bound = bound ?? typeProvider.objectType;
+ return element;
}
}
@@ -1205,92 +1254,170 @@
}
void test_functionsDifferentNamedTakeUnion() {
- FunctionType type1 = _functionType([], named: {'a': intType, 'b': intType});
- FunctionType type2 =
- _functionType([], named: {'b': doubleType, 'c': stringType});
- FunctionType expected =
- _functionType([], named: {'a': intType, 'b': numType, 'c': stringType});
+ var type1 = _functionType(
+ named: {'a': intType, 'b': intType},
+ );
+ var type2 = _functionType(
+ named: {'b': doubleType, 'c': stringType},
+ );
+ var expected = _functionType(
+ named: {'a': intType, 'b': numType, 'c': stringType},
+ );
_checkGreatestLowerBound(type1, type2, expected);
}
void test_functionsDifferentOptionalArityTakeMax() {
- FunctionType type1 = _functionType([], optional: [intType]);
- FunctionType type2 =
- _functionType([], optional: [doubleType, stringType, objectType]);
- FunctionType expected =
- _functionType([], optional: [numType, stringType, objectType]);
+ var type1 = _functionType(
+ optional: [intType],
+ );
+ var type2 = _functionType(
+ required: [],
+ optional: [doubleType, stringType, objectType],
+ );
+ var expected = _functionType(
+ optional: [numType, stringType, objectType],
+ );
_checkGreatestLowerBound(type1, type2, expected);
}
void test_functionsDifferentRequiredArityBecomeOptional() {
- FunctionType type1 = _functionType([intType]);
- FunctionType type2 = _functionType([intType, intType, intType]);
- FunctionType expected =
- _functionType([intType], optional: [intType, intType]);
+ var type1 = _functionType(
+ required: [intType],
+ );
+ var type2 = _functionType(
+ required: [intType, intType, intType],
+ );
+ var expected = _functionType(
+ required: [intType],
+ optional: [intType, intType],
+ );
_checkGreatestLowerBound(type1, type2, expected);
}
void test_functionsFromDynamic() {
- FunctionType type1 = _functionType([dynamicType]);
- FunctionType type2 = _functionType([intType]);
- FunctionType expected = _functionType([dynamicType]);
+ var type1 = _functionType(required: [dynamicType]);
+ var type2 = _functionType(required: [intType]);
+ var expected = _functionType(required: [dynamicType]);
_checkGreatestLowerBound(type1, type2, expected);
}
void test_functionsGlbReturnType() {
- FunctionType type1 = _functionType([], returns: intType);
- FunctionType type2 = _functionType([], returns: numType);
- FunctionType expected = _functionType([], returns: intType);
+ var type1 = _functionType(returns: intType);
+ var type2 = _functionType(returns: numType);
+ var expected = _functionType(returns: intType);
_checkGreatestLowerBound(type1, type2, expected);
}
void test_functionsLubNamedParams() {
- FunctionType type1 =
- _functionType([], named: {'a': stringType, 'b': intType});
- FunctionType type2 = _functionType([], named: {'a': intType, 'b': numType});
- FunctionType expected =
- _functionType([], named: {'a': objectType, 'b': numType});
+ var type1 = _functionType(
+ named: {'a': stringType, 'b': intType},
+ );
+ var type2 = _functionType(
+ named: {'a': intType, 'b': numType},
+ );
+ var expected = _functionType(
+ named: {'a': objectType, 'b': numType},
+ );
_checkGreatestLowerBound(type1, type2, expected);
}
void test_functionsLubPositionalParams() {
- FunctionType type1 = _functionType([], optional: [stringType, intType]);
- FunctionType type2 = _functionType([], optional: [intType, numType]);
- FunctionType expected = _functionType([], optional: [objectType, numType]);
+ var type1 = _functionType(
+ optional: [stringType, intType],
+ );
+ var type2 = _functionType(
+ optional: [intType, numType],
+ );
+ var expected = _functionType(
+ optional: [objectType, numType],
+ );
_checkGreatestLowerBound(type1, type2, expected);
}
void test_functionsLubRequiredParams() {
- FunctionType type1 = _functionType([stringType, intType, intType]);
- FunctionType type2 = _functionType([intType, doubleType, numType]);
- FunctionType expected = _functionType([objectType, numType, numType]);
+ var type1 = _functionType(
+ required: [stringType, intType, intType],
+ );
+ var type2 = _functionType(
+ required: [intType, doubleType, numType],
+ );
+ var expected = _functionType(
+ required: [objectType, numType, numType],
+ );
_checkGreatestLowerBound(type1, type2, expected);
}
void test_functionsMixedOptionalAndRequiredBecomeOptional() {
- FunctionType type1 = _functionType([intType, intType],
- optional: [intType, intType, intType]);
- FunctionType type2 = _functionType([intType], optional: [intType, intType]);
- FunctionType expected = _functionType([intType],
- optional: [intType, intType, intType, intType]);
+ var type1 = _functionType(
+ required: [intType, intType],
+ optional: [intType, intType, intType],
+ );
+ var type2 = _functionType(
+ required: [intType],
+ optional: [intType, intType],
+ );
+ var expected = _functionType(
+ required: [intType],
+ optional: [intType, intType, intType, intType],
+ );
_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.
- FunctionType type1 = _functionType([intType], named: {'a': intType});
- FunctionType type2 = _functionType([], named: {'a': intType});
+ var type1 = _functionType(
+ required: [intType],
+ named: {'a': intType},
+ );
+ var type2 = _functionType(
+ required: [],
+ named: {'a': intType},
+ );
_checkGreatestLowerBound(type1, type2, bottomType);
}
- void test_functionsSameType() {
- FunctionType type1 = _functionType([stringType, intType, numType],
- optional: [doubleType], named: {'n': numType}, returns: intType);
- FunctionType type2 = _functionType([stringType, intType, numType],
- optional: [doubleType], named: {'n': numType}, returns: intType);
- FunctionType expected = _functionType([stringType, intType, numType],
- optional: [doubleType], named: {'n': numType}, returns: intType);
+ void test_functionsSameType_withNamed() {
+ var type1 = _functionType(
+ required: [stringType, intType, numType],
+ named: {'n': numType},
+ returns: intType,
+ );
+
+ var type2 = _functionType(
+ required: [stringType, intType, numType],
+ named: {'n': numType},
+ returns: intType,
+ );
+
+ var expected = _functionType(
+ required: [stringType, intType, numType],
+ named: {'n': numType},
+ returns: intType,
+ );
+
+ _checkGreatestLowerBound(type1, type2, expected);
+ }
+
+ void test_functionsSameType_withOptional() {
+ var type1 = _functionType(
+ required: [stringType, intType, numType],
+ optional: [doubleType],
+ returns: intType,
+ );
+
+ var type2 = _functionType(
+ required: [stringType, intType, numType],
+ optional: [doubleType],
+ returns: intType,
+ );
+
+ var expected = _functionType(
+ required: [stringType, intType, numType],
+ optional: [doubleType],
+ returns: intType,
+ );
+
_checkGreatestLowerBound(type1, type2, expected);
}
@@ -1386,8 +1513,11 @@
ElementFactory.typeParameterElement('T').type
];
for (DartType type in types) {
- _checkGreatestLowerBound(_functionType([], returns: voidType),
- _functionType([], returns: type), _functionType([], returns: type));
+ _checkGreatestLowerBound(
+ _functionType(required: [], returns: voidType),
+ _functionType(required: [], returns: type),
+ _functionType(required: [], returns: type),
+ );
}
}
}
@@ -1399,71 +1529,164 @@
typeSystem = new Dart2TypeSystem(typeProvider);
}
- void test_functionsDifferentRequiredArity() {
- FunctionType type1 = _functionType([intType, intType]);
- FunctionType type2 = _functionType([intType, intType, intType]);
+ void test_differentRequiredArity() {
+ var type1 = _functionType(required: [intType, intType]);
+ var type2 = _functionType(required: [intType, intType, intType]);
_checkLeastUpperBound(type1, type2, functionType);
}
- void test_functionsFuzzyArrows() {
- FunctionType type1 = _functionType([dynamicType]);
- FunctionType type2 = _functionType([intType]);
- FunctionType expected = _functionType([intType]);
+ void test_fuzzyArrows() {
+ var type1 = _functionType(required: [dynamicType]);
+ var type2 = _functionType(required: [intType]);
+ var expected = _functionType(required: [intType]);
_checkLeastUpperBound(type1, type2, expected);
}
- void test_functionsGlbNamedParams() {
- FunctionType type1 =
- _functionType([], named: {'a': stringType, 'b': intType});
- FunctionType type2 = _functionType([], named: {'a': intType, 'b': numType});
- FunctionType expected =
- _functionType([], named: {'a': bottomType, 'b': intType});
+ void test_glbNamedParams() {
+ var type1 = _functionType(
+ named: {'a': stringType, 'b': intType},
+ );
+ var type2 = _functionType(
+ named: {'a': intType, 'b': numType},
+ );
+ var expected = _functionType(
+ named: {'a': bottomType, 'b': intType},
+ );
_checkLeastUpperBound(type1, type2, expected);
}
- void test_functionsGlbPositionalParams() {
- FunctionType type1 = _functionType([], optional: [stringType, intType]);
- FunctionType type2 = _functionType([], optional: [intType, numType]);
- FunctionType expected = _functionType([], optional: [bottomType, intType]);
+ void test_glbPositionalParams() {
+ var type1 = _functionType(
+ optional: [stringType, intType],
+ );
+ var type2 = _functionType(
+ optional: [intType, numType],
+ );
+ var expected = _functionType(
+ optional: [bottomType, intType],
+ );
_checkLeastUpperBound(type1, type2, expected);
}
- void test_functionsGlbRequiredParams() {
- FunctionType type1 = _functionType([stringType, intType, intType]);
- FunctionType type2 = _functionType([intType, doubleType, numType]);
- FunctionType expected = _functionType([bottomType, bottomType, intType]);
+ void test_glbRequiredParams() {
+ var type1 = _functionType(
+ required: [stringType, intType, intType],
+ );
+ var type2 = _functionType(
+ required: [intType, doubleType, numType],
+ );
+ var expected = _functionType(
+ required: [bottomType, bottomType, intType],
+ );
_checkLeastUpperBound(type1, type2, expected);
}
- void test_functionsIgnoreExtraNamedParams() {
- FunctionType type1 = _functionType([], named: {'a': intType, 'b': intType});
- FunctionType type2 = _functionType([], named: {'a': intType, 'c': intType});
- FunctionType expected = _functionType([], named: {'a': intType});
+ void test_ignoreExtraNamedParams() {
+ var type1 = _functionType(
+ named: {'a': intType, 'b': intType},
+ );
+ var type2 = _functionType(
+ named: {'a': intType, 'c': intType},
+ );
+ var expected = _functionType(
+ named: {'a': intType},
+ );
_checkLeastUpperBound(type1, type2, expected);
}
- void test_functionsIgnoreExtraPositionalParams() {
- FunctionType type1 =
- _functionType([], optional: [intType, intType, stringType]);
- FunctionType type2 = _functionType([], optional: [intType]);
- FunctionType expected = _functionType([], optional: [intType]);
+ void test_ignoreExtraPositionalParams() {
+ var type1 = _functionType(
+ optional: [intType, intType, stringType],
+ );
+ var type2 = _functionType(
+ optional: [intType],
+ );
+ var expected = _functionType(
+ optional: [intType],
+ );
_checkLeastUpperBound(type1, type2, expected);
}
- void test_functionsLubReturnType() {
- FunctionType type1 = _functionType([], returns: intType);
- FunctionType type2 = _functionType([], returns: doubleType);
- FunctionType expected = _functionType([], returns: numType);
+ void test_lubReturnType() {
+ var type1 = _functionType(returns: intType);
+ var type2 = _functionType(returns: doubleType);
+ var expected = _functionType(returns: numType);
_checkLeastUpperBound(type1, type2, expected);
}
- void test_functionsSameType() {
- FunctionType type1 = _functionType([stringType, intType, numType],
- optional: [doubleType], named: {'n': numType}, returns: intType);
- FunctionType type2 = _functionType([stringType, intType, numType],
- optional: [doubleType], named: {'n': numType}, returns: intType);
- FunctionType expected = _functionType([stringType, intType, numType],
- optional: [doubleType], named: {'n': numType}, returns: intType);
+ void test_sameType_withNamed() {
+ var type1 = _functionType(
+ required: [stringType, intType, numType],
+ named: {'n': numType},
+ returns: intType,
+ );
+
+ var type2 = _functionType(
+ required: [stringType, intType, numType],
+ named: {'n': numType},
+ returns: intType,
+ );
+
+ var expected = _functionType(
+ required: [stringType, intType, numType],
+ named: {'n': numType},
+ returns: intType,
+ );
+
+ _checkLeastUpperBound(type1, type2, expected);
+ }
+
+ void test_sameType_withOptional() {
+ var type1 = _functionType(
+ required: [stringType, intType, numType],
+ optional: [doubleType],
+ returns: intType,
+ );
+
+ var type2 = _functionType(
+ required: [stringType, intType, numType],
+ optional: [doubleType],
+ returns: intType,
+ );
+
+ var expected = _functionType(
+ required: [stringType, intType, numType],
+ optional: [doubleType],
+ returns: intType,
+ );
+
+ _checkLeastUpperBound(type1, type2, expected);
+ }
+
+ void test_typeFormals_differentBounds() {
+ var T1 = _typeParameterElement('T1', bound: intType);
+ var type1 = _functionType(typeFormals: [T1], returns: T1.type);
+
+ var T2 = _typeParameterElement('T2', bound: doubleType);
+ var type2 = _functionType(typeFormals: [T2], returns: T2.type);
+
+ _checkLeastUpperBound(type1, type2, functionType);
+ }
+
+ void test_typeFormals_differentNumber() {
+ var T1 = _typeParameterElement('T1', bound: numType);
+ var type1 = _functionType(typeFormals: [T1], returns: T1.type);
+
+ var type2 = _functionType(returns: intType);
+
+ _checkLeastUpperBound(type1, type2, functionType);
+ }
+
+ void test_typeFormals_sameBounds() {
+ var T1 = _typeParameterElement('T1', bound: numType);
+ var type1 = _functionType(typeFormals: [T1], returns: T1.type);
+
+ var T2 = _typeParameterElement('T2', bound: numType);
+ var type2 = _functionType(typeFormals: [T2], returns: T2.type);
+
+ var TE = _typeParameterElement('T', bound: numType);
+ var expected = _functionType(typeFormals: [TE], returns: TE.type);
+
_checkLeastUpperBound(type1, type2, expected);
}
}
@@ -1566,32 +1789,38 @@
}
void test_nestedFunctionsLubInnerParamTypes() {
- FunctionType type1 = _functionType([
- _functionType([stringType, intType, intType])
- ]);
- FunctionType type2 = _functionType([
- _functionType([intType, doubleType, numType])
- ]);
- FunctionType expected = _functionType([
- _functionType([objectType, numType, numType])
- ]);
+ var type1 = _functionType(
+ required: [
+ _functionType(required: [stringType, intType, intType])
+ ],
+ );
+ var type2 = _functionType(
+ required: [
+ _functionType(required: [intType, doubleType, numType])
+ ],
+ );
+ var expected = _functionType(
+ required: [
+ _functionType(required: [objectType, numType, numType])
+ ],
+ );
_checkLeastUpperBound(type1, type2, expected);
}
void test_nestedNestedFunctionsGlbInnermostParamTypes() {
- FunctionType type1 = _functionType([
- _functionType([
- _functionType([stringType, intType, intType])
+ FunctionType type1 = _functionType(required: [
+ _functionType(required: [
+ _functionType(required: [stringType, intType, intType])
])
]);
- FunctionType type2 = _functionType([
- _functionType([
- _functionType([intType, doubleType, numType])
+ FunctionType type2 = _functionType(required: [
+ _functionType(required: [
+ _functionType(required: [intType, doubleType, numType])
])
]);
- FunctionType expected = _functionType([
- _functionType([
- _functionType([bottomType, bottomType, intType])
+ FunctionType expected = _functionType(required: [
+ _functionType(required: [
+ _functionType(required: [bottomType, bottomType, intType])
])
]);
_checkLeastUpperBound(type1, type2, expected);
@@ -1840,9 +2069,10 @@
];
for (DartType type in types) {
_checkLeastUpperBound(
- _functionType([], returns: voidType),
- _functionType([], returns: type),
- _functionType([], returns: voidType));
+ _functionType(returns: voidType),
+ _functionType(returns: type),
+ _functionType(returns: voidType),
+ );
}
}
}
diff --git a/pkg/analyzer/test/src/summary/linker_test.dart b/pkg/analyzer/test/src/summary/linker_test.dart
index 3a24bb1..8d7a3e5 100644
--- a/pkg/analyzer/test/src/summary/linker_test.dart
+++ b/pkg/analyzer/test/src/summary/linker_test.dart
@@ -327,7 +327,7 @@
ClassElementForLink_Class cls = library.getContainedName('C');
expect(cls.fields, hasLength(1));
var field = cls.fields[0];
- expect(field.type.toString(), 'int Function(Never)');
+ expect(field.type.toString(), 'int Function<T>(T)');
}
void test_inferredType_instanceField_dynamic() {
diff --git a/pkg/analyzer/test/src/summary/resynthesize_ast2_test.dart b/pkg/analyzer/test/src/summary/resynthesize_ast2_test.dart
index 282c385..3ab3089 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_ast2_test.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_ast2_test.dart
@@ -136,14 +136,6 @@
return elementFactory.libraryOfUri('${source.uri}');
}
- @override
- @failingTest
- test_syntheticFunctionType_genericClosure() async {
- // TODO(scheglov) Bug in TypeSystem.getLeastUpperBound().
- // LUB(<T>(T) → int, <T>(T) → int) gives `(T) → int`, note absence of `<T>`.
- await super.test_syntheticFunctionType_genericClosure();
- }
-
void _addLibraryUnits(
Source definingSource,
CompilationUnit definingUnit,
diff --git a/pkg/analyzer/test/src/summary/resynthesize_common.dart b/pkg/analyzer/test/src/summary/resynthesize_common.dart
index f4c870d..151d00f 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_common.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_common.dart
@@ -9431,7 +9431,7 @@
bool f() => true;
''');
checkElementText(library, r'''
-final int Function(Never) v;
+final int Function<T>(T) v;
bool f() {}
''');
}