fix #34273, dartdevk should use the default bound for generic functions
When dynamically calling a generic function or method without passing
any explicit type arguments, the default bounds are used. The default
should be `dynamic` unless an explicit bound was provided for the type
parameter.
Change-Id: I82f7166303f936adbecedd0c633b518f2700def4
Reviewed-on: https://dart-review.googlesource.com/72081
Commit-Queue: Jenny Messerly <jmesserly@google.com>
Reviewed-by: Vijay Menon <vsm@google.com>
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index a5f6cec..f9ce5c8 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -2498,9 +2498,37 @@
typeParts = [addTypeFormalsAsParameters(typeParts)];
helperCall = 'gFnType(#)';
+
+ /// Whether the type parameter [t] has an explicit bound, like
+ /// `<T extends C>`, `<T extends Object>` or `<T extends dynamic>`.
+ ///
+ /// In contrast, a type parameter like `<T>` has an implicit bound.
+ /// Implicit bounds are a bit unusual, in that `Object` is used as the
+ /// bound for checking, but `dynamic` is filled in as the default value.
+ ///
+ /// Kernel represents `<T>` as `<T extends Object = dynamic>`. We can find
+ /// explicit bounds by looking for anything *except* that.
+ typeParameterHasExplicitBound(TypeParameter t) =>
+ t.bound != types.objectType || t.defaultType != const DynamicType();
+
// If any explicit bounds were passed, emit them.
- if (typeFormals.any((t) => t.bound != null)) {
- var bounds = typeFormals.map((t) => _emitType(t.bound)).toList();
+ if (typeFormals.any(typeParameterHasExplicitBound)) {
+ /// Emits the bound of the type parameter [t] for use in runtime
+ /// checking and the default value (e.g. for dynamic class).
+ ///
+ /// For most type parameters we can use [TypeParameter.bound]. However,
+ /// for *implicit* bounds such as `<T>` (represented in Kernel as
+ /// `<T extends Object = dynamic>`) we need to emit `dynamic` so we use
+ /// the correct default value at runtime.
+ ///
+ /// Because `dynamic` and `Object` are both top types, they'll behave
+ /// identically for the purposes of type checks.
+ emitTypeParameterBound(TypeParameter t) =>
+ typeParameterHasExplicitBound(t)
+ ? _emitType(t.bound)
+ : visitDynamicType(const DynamicType());
+
+ var bounds = typeFormals.map(emitTypeParameterBound).toList();
typeParts.add(addTypeFormalsAsParameters(bounds));
}
} else {
diff --git a/tests/language_2/language_2_dartdevc.status b/tests/language_2/language_2_dartdevc.status
index 0e8f40f..cb05f47 100644
--- a/tests/language_2/language_2_dartdevc.status
+++ b/tests/language_2/language_2_dartdevc.status
@@ -238,7 +238,6 @@
call_method_implicit_tear_off_implements_function_test/06: RuntimeError # Kernel is missing the implicit `call` tearoff for assignment `Function`
call_method_must_not_be_field_test/06: RuntimeError # Kernel does not distinguish `d()` from `d.call()`
call_method_must_not_be_getter_test/06: RuntimeError # Kernel does not distinguish `d()` from `d.call()`
-closure_type_arguments_test: RuntimeError # Issue 34273
compile_time_constant_c_test/02: MissingCompileTimeError
compile_time_constant_k_test/01: MissingCompileTimeError
compile_time_constant_k_test/02: MissingCompileTimeError