Fix #29014, make function type parameters invariant.
With the restricted behavior, some tests have been removed, modified,
and added based on the new invariance.
Bug: 29014
Change-Id: I086749bbe40aaa723009f9ae06f58d85554cae53
Reviewed-on: https://dart-review.googlesource.com/56029
Commit-Queue: Mike Fairhurst <mfairhurst@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
diff --git a/pkg/analyzer/lib/src/dart/element/type.dart b/pkg/analyzer/lib/src/dart/element/type.dart
index 5d91528..3ccdac9 100644
--- a/pkg/analyzer/lib/src/dart/element/type.dart
+++ b/pkg/analyzer/lib/src/dart/element/type.dart
@@ -1175,8 +1175,8 @@
/**
* Compares two function types [t] and [s] to see if their corresponding
- * parameter types match [parameterRelation] and their return types match
- * [returnRelation].
+ * parameter types match [parameterRelation], return types match
+ * [returnRelation], and type parameter bounds match [boundsRelation].
*
* Used for the various relations on function types which have the same
* structural rules for handling optional parameters and arity, but use their
@@ -1184,14 +1184,20 @@
*
* If [parameterRelation] is omitted, uses [returnRelation] for both. This
* is convenient for Dart 1 type system methods.
+ *
+ * If [boundsRelation] is omitted, uses [returnRelation]. This is for
+ * backwards compatibility, and convenience for Dart 1 type system methods.
*/
static bool relate(
FunctionType t,
DartType other,
bool returnRelation(DartType t, DartType s),
DartType instantiateToBounds(DartType t),
- {bool parameterRelation(ParameterElement t, ParameterElement s)}) {
+ {bool parameterRelation(ParameterElement t, ParameterElement s),
+ bool boundsRelation(DartType bound2, DartType bound1,
+ TypeParameterElement formal2, TypeParameterElement formal1)}) {
parameterRelation ??= (t, s) => returnRelation(t.type, s.type);
+ boundsRelation ??= (t, s, _, __) => returnRelation(t, s);
// Trivial base cases.
if (other == null) {
@@ -1208,8 +1214,7 @@
// This type cast is safe, because we checked it above.
FunctionType s = other as FunctionType;
if (t.typeFormals.isNotEmpty) {
- List<DartType> freshVariables =
- relateTypeFormals(t, s, (s, t, _, __) => returnRelation(s, t));
+ List<DartType> freshVariables = relateTypeFormals(t, s, boundsRelation);
if (freshVariables == null) {
return false;
}
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 185d6d3..91509f1 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -6748,6 +6748,9 @@
*/
void _inferFunctionExpressionParametersTypes(
Expression mayBeClosure, DartType mayByFunctionType) {
+ // TODO(mfairhurst): remove this code and callers. It's doing
+ // "propagated type" inference for the Dart 1 type system.
+ assert(!strongMode);
// prepare closure
if (mayBeClosure is! FunctionExpression) {
return;
diff --git a/pkg/analyzer/lib/src/generated/type_system.dart b/pkg/analyzer/lib/src/generated/type_system.dart
index d178f25..ab376a1 100644
--- a/pkg/analyzer/lib/src/generated/type_system.dart
+++ b/pkg/analyzer/lib/src/generated/type_system.dart
@@ -646,18 +646,14 @@
if (t1 is FunctionType && t2 is FunctionType) {
return FunctionTypeImpl.relate(
- t1,
- t2,
- (t1, t2) {
- // TODO(jmesserly): should we flip covariance when we're relating
- // type formal bounds? They're more like parameters.
- return matchSubtype(t1, t2);
- },
- _typeSystem.instantiateToBounds,
+ t1, t2, matchSubtype, _typeSystem.instantiateToBounds,
parameterRelation: (p1, p2) {
return _matchSubtypeOf(p2.type, p1.type, null, origin,
covariant: !covariant);
- });
+ },
+ // Type parameter bounds are invariant.
+ boundsRelation: (t1, t2, p1, p2) =>
+ matchSubtype(t1, t2) && matchSubtype(t2, t1));
}
if (t1 is FunctionType && t2 == typeProvider.functionType) {
@@ -1181,7 +1177,10 @@
/// - it allows opt-in covariant parameters.
bool isOverrideSubtypeOf(FunctionType f1, FunctionType f2) {
return FunctionTypeImpl.relate(f1, f2, isSubtypeOf, instantiateToBounds,
- parameterRelation: isOverrideSubtypeOfParameter);
+ parameterRelation: isOverrideSubtypeOfParameter,
+ // Type parameter bounds are invariant.
+ boundsRelation: (t1, t2, p1, p2) =>
+ isSubtypeOf(t1, t2) && isSubtypeOf(t2, t1));
}
/// Check that parameter [p2] is a subtype of [p1], given that we are
@@ -1522,7 +1521,10 @@
/// Check that [f1] is a subtype of [f2].
bool _isFunctionSubtypeOf(FunctionType f1, FunctionType f2) {
return FunctionTypeImpl.relate(f1, f2, isSubtypeOf, instantiateToBounds,
- parameterRelation: (p1, p2) => isSubtypeOf(p2.type, p1.type));
+ parameterRelation: (p1, p2) => isSubtypeOf(p2.type, p1.type),
+ // Type parameter bounds are invariant.
+ boundsRelation: (t1, t2, p1, p2) =>
+ isSubtypeOf(t1, t2) && isSubtypeOf(t2, t1));
}
bool _isInterfaceSubtypeOf(
diff --git a/pkg/analyzer/test/generated/strong_mode_kernel_test.dart b/pkg/analyzer/test/generated/strong_mode_kernel_test.dart
index fdd3388..763d120 100644
--- a/pkg/analyzer/test/generated/strong_mode_kernel_test.dart
+++ b/pkg/analyzer/test/generated/strong_mode_kernel_test.dart
@@ -357,9 +357,18 @@
@override
@failingTest
- test_genericMethod_override_invalidTypeParamBounds() async {
+ test_genericMethod_override_invalidCovariantTypeParamBounds() async {
// Expected 1 errors of type StrongModeCode.STRONG_MODE_INVALID_METHOD_OVERRIDE, found 0
- await super.test_genericMethod_override_invalidTypeParamBounds();
+ await super
+ .test_genericMethod_override_invalidContravariantTypeParamBounds();
+ }
+
+ @override
+ @failingTest
+ test_genericMethod_override_invalidContravariantTypeParamBounds() async {
+ // Expected 1 errors of type StrongModeCode.STRONG_MODE_INVALID_METHOD_OVERRIDE, found 0
+ await super
+ .test_genericMethod_override_invalidContravariantTypeParamBounds();
}
@override
diff --git a/pkg/analyzer/test/generated/strong_mode_test.dart b/pkg/analyzer/test/generated/strong_mode_test.dart
index 6a0a0b3..ab24845 100644
--- a/pkg/analyzer/test/generated/strong_mode_test.dart
+++ b/pkg/analyzer/test/generated/strong_mode_test.dart
@@ -3407,13 +3407,17 @@
test_genericMethod_override_bounds() async {
await resolveTestUnit(r'''
class A {}
-class B extends A {}
-class C {
- T f<T extends B>(T x) => null;
-}
-class D extends C {
+class B {
T f<T extends A>(T x) => null;
}
+// override with the same bound is OK
+class C extends B {
+ T f<T extends A>(T x) => null;
+}
+// override with new name and the same bound is OK
+class D extends B {
+ Q f<Q extends A>(Q x) => null;
+}
''');
}
@@ -3433,20 +3437,27 @@
verify([source]);
}
- test_genericMethod_override_invalidReturnType() async {
+ test_genericMethod_override_differentContextsSameBounds() async {
Source source = addSource(r'''
-class C {
- Iterable<T> f<T>(T x) => null;
+ class GenericMethodBounds<T> {
+ Type get t => T;
+ GenericMethodBounds<E> foo<E extends T>() => new GenericMethodBounds<E>();
+ GenericMethodBounds<E> bar<E extends void Function(T)>() =>
+ new GenericMethodBounds<E>();
}
-class D extends C {
- String f<S>(S x) => null;
-}''');
+
+class GenericMethodBoundsDerived extends GenericMethodBounds<num> {
+ GenericMethodBounds<E> foo<E extends num>() => new GenericMethodBounds<E>();
+ GenericMethodBounds<E> bar<E extends void Function(num)>() =>
+ new GenericMethodBounds<E>();
+}
+''');
await computeAnalysisResult(source);
- assertErrors(source, [StrongModeCode.INVALID_METHOD_OVERRIDE]);
+ assertNoErrors(source);
verify([source]);
}
- test_genericMethod_override_invalidTypeParamBounds() async {
+ test_genericMethod_override_invalidContravariantTypeParamBounds() async {
Source source = addSource(r'''
class A {}
class B extends A {}
@@ -3461,6 +3472,34 @@
verify([source]);
}
+ test_genericMethod_override_invalidCovariantTypeParamBounds() async {
+ Source source = addSource(r'''
+class A {}
+class B extends A {}
+class C {
+ T f<T extends B>(T x) => null;
+}
+class D extends C {
+ T f<T extends A>(T x) => null;
+}''');
+ await computeAnalysisResult(source);
+ assertErrors(source, [StrongModeCode.INVALID_METHOD_OVERRIDE]);
+ verify([source]);
+ }
+
+ test_genericMethod_override_invalidReturnType() async {
+ Source source = addSource(r'''
+class C {
+ Iterable<T> f<T>(T x) => null;
+}
+class D extends C {
+ String f<S>(S x) => null;
+}''');
+ await computeAnalysisResult(source);
+ assertErrors(source, [StrongModeCode.INVALID_METHOD_OVERRIDE]);
+ verify([source]);
+ }
+
test_genericMethod_override_invalidTypeParamCount() async {
Source source = addSource(r'''
class C {
@@ -3910,6 +3949,48 @@
''');
}
+ @failingTest
+ test_notInstantiatedBound_class_error_recursion() async {
+ String code = r'''
+class A<T extends B> {} // points to a
+class B<T extends A> {} // points to b
+class C<T extends A> {} // points to a cyclical type
+''';
+ await resolveTestUnit(code, noErrors: false);
+ assertErrors(testSource, [
+ StrongModeCode.NOT_INSTANTIATED_BOUND,
+ StrongModeCode.NOT_INSTANTIATED_BOUND,
+ StrongModeCode.NOT_INSTANTIATED_BOUND,
+ ]);
+ }
+
+ @failingTest
+ test_notInstantiatedBound_class_error_recursion_less_direct() async {
+ String code = r'''
+class A<T extends B<A>> {}
+class B<T extends A<B>> {}
+''';
+ await resolveTestUnit(code, noErrors: false);
+ assertErrors(testSource, [
+ StrongModeCode.NOT_INSTANTIATED_BOUND,
+ StrongModeCode.NOT_INSTANTIATED_BOUND,
+ ]);
+ }
+
+ test_notInstantiatedBound_class_error_recursion_typedef() async {
+ String code = r'''
+typedef F(C value);
+class C<T extends F> {}
+class D<T extends C> {}
+''';
+ await resolveTestUnit(code, noErrors: false);
+ assertErrors(testSource, [
+ StrongModeCode.NOT_INSTANTIATED_BOUND,
+ StrongModeCode.NOT_INSTANTIATED_BOUND,
+ CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF,
+ ]);
+ }
+
test_notInstantiatedBound_error_class_argument() async {
String code = r'''
class A<K, V extends List<K>> {}
@@ -3959,34 +4040,6 @@
]);
}
- @failingTest
- test_notInstantiatedBound_class_error_recursion() async {
- String code = r'''
-class A<T extends B> {} // points to a
-class B<T extends A> {} // points to b
-class C<T extends A> {} // points to a cyclical type
-''';
- await resolveTestUnit(code, noErrors: false);
- assertErrors(testSource, [
- StrongModeCode.NOT_INSTANTIATED_BOUND,
- StrongModeCode.NOT_INSTANTIATED_BOUND,
- StrongModeCode.NOT_INSTANTIATED_BOUND,
- ]);
- }
-
- @failingTest
- test_notInstantiatedBound_class_error_recursion_less_direct() async {
- String code = r'''
-class A<T extends B<A>> {}
-class B<T extends A<B>> {}
-''';
- await resolveTestUnit(code, noErrors: false);
- assertErrors(testSource, [
- StrongModeCode.NOT_INSTANTIATED_BOUND,
- StrongModeCode.NOT_INSTANTIATED_BOUND,
- ]);
- }
-
test_notInstantiatedBound_error_typedef_argument() async {
String code = r'''
class A<K, V extends List<K>> {}
@@ -4014,20 +4067,6 @@
assertErrors(testSource, [StrongModeCode.NOT_INSTANTIATED_BOUND]);
}
- test_notInstantiatedBound_class_error_recursion_typedef() async {
- String code = r'''
-typedef F(C value);
-class C<T extends F> {}
-class D<T extends C> {}
-''';
- await resolveTestUnit(code, noErrors: false);
- assertErrors(testSource, [
- StrongModeCode.NOT_INSTANTIATED_BOUND,
- StrongModeCode.NOT_INSTANTIATED_BOUND,
- CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF,
- ]);
- }
-
test_notInstantiatedBound_ok_class() async {
String code = r'''
class A<T extends int> {}
@@ -4068,6 +4107,15 @@
assertNoErrors(testSource);
}
+ test_notInstantiatedBound_ok_class_function() async {
+ String code = r'''
+class A<T extends void Function<Z>()> {}
+class B<T extends A> {}
+''';
+ await resolveTestUnit(code);
+ assertNoErrors(testSource);
+ }
+
test_notInstantiatedBound_ok_class_typedef() async {
String code = r'''
typedef void F<T extends int>();
@@ -4086,15 +4134,6 @@
assertNoErrors(testSource);
}
- test_notInstantiatedBound_ok_class_function() async {
- String code = r'''
-class A<T extends void Function<Z>()> {}
-class B<T extends A> {}
-''';
- await resolveTestUnit(code);
- assertNoErrors(testSource);
- }
-
test_objectMethodOnFunctions_Anonymous() async {
String code = r'''
void main() {
@@ -4403,7 +4442,7 @@
}
abstract class Override implements Base, BaseCopy {
- f<E>(x) => null;
+ f<E extends int>(x) => null;
}
class C extends Override implements Base {}
diff --git a/pkg/analyzer/test/generated/type_system_test.dart b/pkg/analyzer/test/generated/type_system_test.dart
index c371ec8..a7ec816 100644
--- a/pkg/analyzer/test/generated/type_system_test.dart
+++ b/pkg/analyzer/test/generated/type_system_test.dart
@@ -2145,28 +2145,6 @@
}
}
- void test_genericFunction_simple_fBounded() {
- ClassElementImpl AClass = ElementFactory.classElement2('A', ["Q"]);
- InterfaceType AType = AClass.type;
- ClassElementImpl BClass = ElementFactory.classElement2('B', ["R"]);
- BClass.supertype = AType.instantiate([BClass.typeParameters[0].type]);
- InterfaceType BType = BClass.type;
-
- DartType s = TypeBuilder.variable("S");
- (s.element as TypeParameterElementImpl).bound = AType.instantiate([s]);
- DartType t = TypeBuilder.variable("T", bound: s);
- DartType u = TypeBuilder.variable("U");
- (u.element as TypeParameterElementImpl).bound = BType.instantiate([u]);
- DartType v = TypeBuilder.variable("V", bound: u);
-
- _checkIsStrictSubtypeOf(
- TypeBuilder.function(types: [s]), TypeBuilder.function(types: [u]));
-
- _checkIsStrictSubtypeOf(
- TypeBuilder.function(types: [s, t], required: [s], result: t),
- TypeBuilder.function(types: [u, v], required: [u], result: v));
- }
-
void test_generics() {
ClassElementImpl LClass = ElementFactory.classElement2('L', ["T"]);
InterfaceType LType = LClass.type;
diff --git a/tests/language_2/language_2_analyzer.status b/tests/language_2/language_2_analyzer.status
index 74be6be..402c681 100644
--- a/tests/language_2/language_2_analyzer.status
+++ b/tests/language_2/language_2_analyzer.status
@@ -399,7 +399,6 @@
generic_methods_bounds_test/01: MissingCompileTimeError
generic_methods_dynamic_test/01: MissingCompileTimeError
generic_methods_dynamic_test/03: MissingCompileTimeError
-generic_methods_overriding_test/01: MissingCompileTimeError
generic_methods_overriding_test/03: MissingCompileTimeError
generic_methods_overriding_test/06: StaticWarning
generic_methods_recursive_bound_test/02: MissingCompileTimeError
@@ -1107,8 +1106,6 @@
field3a_negative_test: StaticWarning # Issue 28823
forwarding_stub_tearoff_test: CompileTimeError
generic_methods_generic_function_result_test/none: CompileTimeError # Issue #30207
-generic_methods_overriding_test/01: MissingCompileTimeError # Issue 29070
-generic_methods_overriding_test/03: MissingCompileTimeError # Issue 29070
generic_no_such_method_dispatcher_test: CompileTimeError
generic_tearoff_test: CompileTimeError
import_core_prefix_test: CompileTimeError # "dynamic" should be defined in core.
diff --git a/tests/language_2/language_2_dartdevc.status b/tests/language_2/language_2_dartdevc.status
index 2bfb404..11456f2 100644
--- a/tests/language_2/language_2_dartdevc.status
+++ b/tests/language_2/language_2_dartdevc.status
@@ -63,7 +63,6 @@
generic_local_functions_test: CompileTimeError
generic_methods_generic_function_parameter_test: CompileTimeError
generic_methods_generic_function_result_test/none: CompileTimeError # Issue #30208
-generic_methods_overriding_test/03: MissingCompileTimeError # Issue 29920
generic_no_such_method_dispatcher_simple_test: Skip # This test is just for kernel.
generic_no_such_method_dispatcher_test: CompileTimeError
getter_closure_execution_order_test: RuntimeError # Issue 29920
@@ -391,6 +390,7 @@
function_type_parameter_negative_test: Fail
generic_function_bounds_test: RuntimeError
generic_methods_generic_function_result_test/01: MissingCompileTimeError
+generic_methods_overriding_test/01: MissingCompileTimeError # Issue 29920
generic_methods_recursive_bound_test/02: MissingCompileTimeError
generic_no_such_method_dispatcher_simple_test: CompileTimeError # Warning: Superclass has no method named 'foo'.
generic_no_such_method_dispatcher_test: CompileTimeError # Issue 31533
@@ -685,7 +685,6 @@
generic_function_type_as_type_argument_test/02: MissingCompileTimeError # Issue 29920
generic_instanceof2_test: RuntimeError # Issue 29920; ReferenceError: FooOfK$String is not defined
generic_is_check_test: RuntimeError # Issue 29920; Expect.isTrue(false) fails.
-generic_methods_overriding_test/01: MissingCompileTimeError # Issue 29920
generic_tearoff_test: CompileTimeError
identical_closure2_test: RuntimeError # Issue 29920; Expect.isFalse(true) fails.
infinite_switch_label_test: RuntimeError # Issue 29920; NoSuchMethodError: method not found: '<Unexpected Null Value>'