From f77f8e587149e466de341f06b2525fc8ccbe1548 Mon Sep 17 00:00:00 2001 From: Dmitry Stefantsov Date: Fri, 6 Aug 2021 15:19:35 +0000 Subject: [PATCH] [cfe] Add type checks on tearing off instance members Closes #46784. Bug: https://github.com/dart-lang/sdk/issues/46784 Change-Id: I7637618be0686bbffe6eb18a19049ec5da6059aa Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/208881 Reviewed-by: Johnni Winther Commit-Queue: Dmitry Stefantsov --- .../src/fasta/kernel/inference_visitor.dart | 3 ++- .../general/bug33298.dart.weak.expect | 2 +- .../bug33298.dart.weak.transformed.expect | 2 +- ..._tearoff_covariant_generic_type_check.dart | 23 +++++++++++++++++ ...ric_type_check.dart.textual_outline.expect | 6 +++++ ...check.dart.textual_outline_modelled.expect | 6 +++++ ...ariant_generic_type_check.dart.weak.expect | 25 +++++++++++++++++++ ...eneric_type_check.dart.weak.outline.expect | 14 +++++++++++ ...ic_type_check.dart.weak.transformed.expect | 25 +++++++++++++++++++ .../covariant_generic_parameter_tear_off.dart | 4 +-- ...eneric_parameter_tear_off.dart.weak.expect | 4 +-- ...eter_tear_off.dart.weak.transformed.expect | 4 +-- pkg/kernel/lib/type_algebra.dart | 8 ++++-- 13 files changed, 115 insertions(+), 11 deletions(-) create mode 100644 pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart create mode 100644 pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart.textual_outline.expect create mode 100644 pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart.textual_outline_modelled.expect create mode 100644 pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart.weak.expect create mode 100644 pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart.weak.outline.expect create mode 100644 pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart.weak.transformed.expect diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart index fab3ab7b08f..90d2f7bd7c4 100644 --- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart +++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart @@ -4790,7 +4790,8 @@ class InferenceVisitor checkReturn = TypeInferrerImpl.returnedTypeParametersOccurNonCovariantly( interfaceMember.enclosingClass!, - interfaceMember.function.returnType); + interfaceMember.function + .computeFunctionType(inferrer.library.nonNullable)); } else if (interfaceMember is Field) { checkReturn = TypeInferrerImpl.returnedTypeParametersOccurNonCovariantly( diff --git a/pkg/front_end/testcases/general/bug33298.dart.weak.expect b/pkg/front_end/testcases/general/bug33298.dart.weak.expect index 397db9f2d7a..bac28d2902c 100644 --- a/pkg/front_end/testcases/general/bug33298.dart.weak.expect +++ b/pkg/front_end/testcases/general/bug33298.dart.weak.expect @@ -65,7 +65,7 @@ static method test() → dynamic { core::List* list1 = ["a", "b", "c"].{core::Iterable::map}(a.{self::A::call}{(core::String*) →* core::String*}){((core::String*) →* core::String*) →* core::Iterable*}.{core::Iterable::toList}(){({growable: core::bool*}) →* core::List*}; core::List* list2 = ["a", "b", "c"].{core::Iterable::map}(let final self::A* #t1 = a in #t1 == null ?{(core::String*) →* core::String*} null : #t1.{self::A::call}{(core::String*) →* core::String*}){((core::String*) →* core::String*) →* core::Iterable*}.{core::Iterable::toList}(){({growable: core::bool*}) →* core::List*}; self::B* b = new self::B::•(); - core::List* list3 = ["a", "b", "c"].{core::Iterable::map}(b.{self::B::call}{(core::String*) →* core::String*}){((core::String*) →* core::String*) →* core::Iterable*}.{core::Iterable::toList}(){({growable: core::bool*}) →* core::List*}; + core::List* list3 = ["a", "b", "c"].{core::Iterable::map}(b.{self::B::call}{(core::String*) →* core::String*} as{TypeError,CovarianceCheck} (core::String*) →* core::String*){((core::String*) →* core::String*) →* core::Iterable*}.{core::Iterable::toList}(){({growable: core::bool*}) →* core::List*}; core::List* list4 = ["a", "b", "c"].{core::Iterable::map}(let final self::B* #t2 = b in #t2 == null ?{(core::String*) →* core::String*} null : #t2.{self::B::call}{(core::String*) →* core::String*}){((core::String*) →* core::String*) →* core::Iterable*}.{core::Iterable::toList}(){({growable: core::bool*}) →* core::List*}; self::C* c = new self::C::•(); core::List* list5 = ["a", "b", "c"].{core::Iterable::map}(c.{self::C::call}{(T*) →* T*}){((core::String*) →* core::String*) →* core::Iterable*}.{core::Iterable::toList}(){({growable: core::bool*}) →* core::List*}; diff --git a/pkg/front_end/testcases/general/bug33298.dart.weak.transformed.expect b/pkg/front_end/testcases/general/bug33298.dart.weak.transformed.expect index 8f7030efc38..2fa1eb28db9 100644 --- a/pkg/front_end/testcases/general/bug33298.dart.weak.transformed.expect +++ b/pkg/front_end/testcases/general/bug33298.dart.weak.transformed.expect @@ -65,7 +65,7 @@ static method test() → dynamic { core::List* list1 = core::_GrowableList::_literal3("a", "b", "c").{core::Iterable::map}(a.{self::A::call}{(core::String*) →* core::String*}){((core::String*) →* core::String*) →* core::Iterable*}.{core::Iterable::toList}(){({growable: core::bool*}) →* core::List*}; core::List* list2 = core::_GrowableList::_literal3("a", "b", "c").{core::Iterable::map}(let final self::A* #t1 = a in #t1 == null ?{(core::String*) →* core::String*} null : #t1.{self::A::call}{(core::String*) →* core::String*}){((core::String*) →* core::String*) →* core::Iterable*}.{core::Iterable::toList}(){({growable: core::bool*}) →* core::List*}; self::B* b = new self::B::•(); - core::List* list3 = core::_GrowableList::_literal3("a", "b", "c").{core::Iterable::map}(b.{self::B::call}{(core::String*) →* core::String*}){((core::String*) →* core::String*) →* core::Iterable*}.{core::Iterable::toList}(){({growable: core::bool*}) →* core::List*}; + core::List* list3 = core::_GrowableList::_literal3("a", "b", "c").{core::Iterable::map}(b.{self::B::call}{(core::String*) →* core::String*} as{TypeError,CovarianceCheck} (core::String*) →* core::String*){((core::String*) →* core::String*) →* core::Iterable*}.{core::Iterable::toList}(){({growable: core::bool*}) →* core::List*}; core::List* list4 = core::_GrowableList::_literal3("a", "b", "c").{core::Iterable::map}(let final self::B* #t2 = b in #t2 == null ?{(core::String*) →* core::String*} null : #t2.{self::B::call}{(core::String*) →* core::String*}){((core::String*) →* core::String*) →* core::Iterable*}.{core::Iterable::toList}(){({growable: core::bool*}) →* core::List*}; self::C* c = new self::C::•(); core::List* list5 = core::_GrowableList::_literal3("a", "b", "c").{core::Iterable::map}(c.{self::C::call}{(T*) →* T*}){((core::String*) →* core::String*) →* core::Iterable*}.{core::Iterable::toList}(){({growable: core::bool*}) →* core::List*}; diff --git a/pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart b/pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart new file mode 100644 index 00000000000..8d1ca89cbfd --- /dev/null +++ b/pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart @@ -0,0 +1,23 @@ +// Copyright (c) 2021, 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. + +class A { + void f(Y y) {} +} + +expectThrows(void Function() f) { + try { + f(); + } catch (e) { + return; + } + throw "Expected an exception to be thrown!"; +} + +main() { + A a = new A(); + expectThrows(() { + void Function(Y) f = a.f; + }); +} diff --git a/pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart.textual_outline.expect b/pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart.textual_outline.expect new file mode 100644 index 00000000000..cf5cf7d3009 --- /dev/null +++ b/pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart.textual_outline.expect @@ -0,0 +1,6 @@ +class A { + void f(Y y) {} +} + +expectThrows(void Function() f) {} +main() {} diff --git a/pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart.textual_outline_modelled.expect new file mode 100644 index 00000000000..cf5cf7d3009 --- /dev/null +++ b/pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart.textual_outline_modelled.expect @@ -0,0 +1,6 @@ +class A { + void f(Y y) {} +} + +expectThrows(void Function() f) {} +main() {} diff --git a/pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart.weak.expect b/pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart.weak.expect new file mode 100644 index 00000000000..b5abc6f7ce4 --- /dev/null +++ b/pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart.weak.expect @@ -0,0 +1,25 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:core" as core; + +class A extends core::Object { + synthetic constructor •() → self::A + : super core::Object::•() + ; + method f(self::A::f::Y y) → void {} +} +static method expectThrows(() → void f) → dynamic { + try { + f(){() → void}; + } + on core::Object catch(final core::Object e) { + return; + } + throw "Expected an exception to be thrown!"; +} +static method main() → dynamic { + self::A a = new self::A::•(); + self::expectThrows(() → void { + (Y) → void f = a.{self::A::f}{(Y) → void} as{TypeError,CovarianceCheck,ForNonNullableByDefault} (Y) → void; + }); +} diff --git a/pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart.weak.outline.expect b/pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart.weak.outline.expect new file mode 100644 index 00000000000..18ac6fe3795 --- /dev/null +++ b/pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart.weak.outline.expect @@ -0,0 +1,14 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:core" as core; + +class A extends core::Object { + synthetic constructor •() → self::A + ; + method f(self::A::f::Y y) → void + ; +} +static method expectThrows(() → void f) → dynamic + ; +static method main() → dynamic + ; diff --git a/pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart.weak.transformed.expect b/pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart.weak.transformed.expect new file mode 100644 index 00000000000..b5abc6f7ce4 --- /dev/null +++ b/pkg/front_end/testcases/general/method_tearoff_covariant_generic_type_check.dart.weak.transformed.expect @@ -0,0 +1,25 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:core" as core; + +class A extends core::Object { + synthetic constructor •() → self::A + : super core::Object::•() + ; + method f(self::A::f::Y y) → void {} +} +static method expectThrows(() → void f) → dynamic { + try { + f(){() → void}; + } + on core::Object catch(final core::Object e) { + return; + } + throw "Expected an exception to be thrown!"; +} +static method main() → dynamic { + self::A a = new self::A::•(); + self::expectThrows(() → void { + (Y) → void f = a.{self::A::f}{(Y) → void} as{TypeError,CovarianceCheck,ForNonNullableByDefault} (Y) → void; + }); +} diff --git a/pkg/front_end/testcases/runtime_checks/covariant_generic_parameter_tear_off.dart b/pkg/front_end/testcases/runtime_checks/covariant_generic_parameter_tear_off.dart index 0d4dd9605b6..11693a1ef24 100644 --- a/pkg/front_end/testcases/runtime_checks/covariant_generic_parameter_tear_off.dart +++ b/pkg/front_end/testcases/runtime_checks/covariant_generic_parameter_tear_off.dart @@ -15,7 +15,7 @@ class C { } F g1(C c) { - return c.f1; + return c. /*@ checkReturn=(num*) ->* void*/ f1; } void g2(C c, Object x) { @@ -24,7 +24,7 @@ void g2(C c, Object x) { } G, num> g3(C c) { - return c.f2; + return c. /*@ checkReturn=(List*) ->* num**/ f2; } void test() { diff --git a/pkg/front_end/testcases/runtime_checks/covariant_generic_parameter_tear_off.dart.weak.expect b/pkg/front_end/testcases/runtime_checks/covariant_generic_parameter_tear_off.dart.weak.expect index 05ac14b9fb1..5d38dc570e9 100644 --- a/pkg/front_end/testcases/runtime_checks/covariant_generic_parameter_tear_off.dart.weak.expect +++ b/pkg/front_end/testcases/runtime_checks/covariant_generic_parameter_tear_off.dart.weak.expect @@ -23,14 +23,14 @@ class C extends core::Object { abstract member-signature get runtimeType() → core::Type*; -> core::Object::runtimeType } static method g1(self::C* c) → (core::num*) →* void { - return c.{self::C::f1}{(core::num*) →* void}; + return c.{self::C::f1}{(core::num*) →* void} as{TypeError,CovarianceCheck} (core::num*) →* void; } static method g2(self::C* c, core::Object* x) → void { (core::Object*) →* void f = self::g1(c) as (core::Object*) →* void; f(x){(core::Object*) →* void}; } static method g3(self::C* c) → (core::List*) →* core::num* { - return c.{self::C::f2}{(core::List*) →* core::num*}; + return c.{self::C::f2}{(core::List*) →* core::num*} as{TypeError,CovarianceCheck} (core::List*) →* core::num*; } static method test() → void { (core::num*) →* void x = self::g1(new self::C::•()); diff --git a/pkg/front_end/testcases/runtime_checks/covariant_generic_parameter_tear_off.dart.weak.transformed.expect b/pkg/front_end/testcases/runtime_checks/covariant_generic_parameter_tear_off.dart.weak.transformed.expect index 05ac14b9fb1..5d38dc570e9 100644 --- a/pkg/front_end/testcases/runtime_checks/covariant_generic_parameter_tear_off.dart.weak.transformed.expect +++ b/pkg/front_end/testcases/runtime_checks/covariant_generic_parameter_tear_off.dart.weak.transformed.expect @@ -23,14 +23,14 @@ class C extends core::Object { abstract member-signature get runtimeType() → core::Type*; -> core::Object::runtimeType } static method g1(self::C* c) → (core::num*) →* void { - return c.{self::C::f1}{(core::num*) →* void}; + return c.{self::C::f1}{(core::num*) →* void} as{TypeError,CovarianceCheck} (core::num*) →* void; } static method g2(self::C* c, core::Object* x) → void { (core::Object*) →* void f = self::g1(c) as (core::Object*) →* void; f(x){(core::Object*) →* void}; } static method g3(self::C* c) → (core::List*) →* core::num* { - return c.{self::C::f2}{(core::List*) →* core::num*}; + return c.{self::C::f2}{(core::List*) →* core::num*} as{TypeError,CovarianceCheck} (core::List*) →* core::num*; } static method test() → void { (core::num*) →* void x = self::g1(new self::C::•()); diff --git a/pkg/kernel/lib/type_algebra.dart b/pkg/kernel/lib/type_algebra.dart index e598523c167..95811073efd 100644 --- a/pkg/kernel/lib/type_algebra.dart +++ b/pkg/kernel/lib/type_algebra.dart @@ -109,7 +109,9 @@ bool containsFreeTypeVariables(DartType type) { /// mapping to be used for replacing other types to use the new type parameters. FreshTypeParameters getFreshTypeParameters(List typeParameters) { List freshParameters = new List.generate( - typeParameters.length, (i) => new TypeParameter(typeParameters[i].name), + typeParameters.length, + (i) => new TypeParameter(typeParameters[i].name) + ..flags = typeParameters[i].flags, growable: true); List freshTypeArguments = new List.generate(typeParameters.length, (int i) { @@ -138,8 +140,10 @@ FreshTypeParameters getFreshTypeParameters(List typeParameters) { class FreshTypeParameters { /// The newly created type parameters. final List freshTypeParameters; + /// List of [TypeParameterType]s for [TypeParameter]. final List freshTypeArguments; + /// Substitution from the original type parameters to [freshTypeArguments]. final Substitution substitution; @@ -389,7 +393,7 @@ class _InnerTypeSubstitutor extends _TypeSubstitutor { } TypeParameter freshTypeParameter(TypeParameter node) { - TypeParameter fresh = new TypeParameter(node.name); + TypeParameter fresh = new TypeParameter(node.name)..flags = node.flags; TypeParameterType typeParameterType = substitution[node] = new TypeParameterType.forAlphaRenaming(node, fresh); fresh.bound = visit(node.bound);