diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart index ac59460f0d9..930a3227e2a 100644 --- a/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart +++ b/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart @@ -385,13 +385,10 @@ abstract class KernelClassBuilder } } - // TODO(dmitryas): Find a better place for this routine. - static bool hasUserDefinedNoSuchMethod( - Class klass, ClassHierarchy hierarchy) { + bool hasUserDefinedNoSuchMethod( + Class klass, ClassHierarchy hierarchy, Class objectClass) { Member noSuchMethod = hierarchy.getDispatchTarget(klass, noSuchMethodName); - // `Object` doesn't have a superclass reference. - return noSuchMethod != null && - noSuchMethod.enclosingClass.superclass != null; + return noSuchMethod != null && noSuchMethod.enclosingClass != objectClass; } void transformProcedureToNoSuchMethodForwarder( @@ -439,57 +436,47 @@ abstract class KernelClassBuilder /// class was modified. bool addNoSuchMethodForwarders( KernelTarget target, ClassHierarchy hierarchy) { - if (!hasUserDefinedNoSuchMethod(cls, hierarchy)) { + if (cls.isAbstract || + !hasUserDefinedNoSuchMethod(cls, hierarchy, target.objectClass)) { return false; } Set existingForwardersNames = new Set(); Set existingSetterForwardersNames = new Set(); - if (cls.superclass != null && - hasUserDefinedNoSuchMethod(cls.superclass, hierarchy)) { - List concrete = hierarchy.getDispatchTargets(cls.superclass); - for (Member member in hierarchy.getInterfaceMembers(cls.superclass)) { + Class leastConcreteSuperclass = cls.superclass; + while ( + leastConcreteSuperclass != null && leastConcreteSuperclass.isAbstract) { + leastConcreteSuperclass = leastConcreteSuperclass.superclass; + } + if (leastConcreteSuperclass != null && + hasUserDefinedNoSuchMethod( + leastConcreteSuperclass, hierarchy, target.objectClass)) { + List concrete = + hierarchy.getDispatchTargets(leastConcreteSuperclass); + for (Member member + in hierarchy.getInterfaceMembers(leastConcreteSuperclass)) { if (ClassHierarchy.findMemberByName(concrete, member.name) == null) { existingForwardersNames.add(member.name); } } List concreteSetters = - hierarchy.getDispatchTargets(cls.superclass, setters: true); - for (Member member - in hierarchy.getInterfaceMembers(cls.superclass, setters: true)) { + hierarchy.getDispatchTargets(leastConcreteSuperclass, setters: true); + for (Member member in hierarchy + .getInterfaceMembers(leastConcreteSuperclass, setters: true)) { if (ClassHierarchy.findMemberByName(concreteSetters, member.name) == null) { existingSetterForwardersNames.add(member.name); } } } - if (cls.mixedInClass != null && - hasUserDefinedNoSuchMethod(cls.mixedInClass, hierarchy)) { - List concrete = hierarchy.getDispatchTargets(cls.mixedInClass); - for (Member member in hierarchy.getInterfaceMembers(cls.mixedInClass)) { - if (ClassHierarchy.findMemberByName(concrete, member.name) == null) { - existingForwardersNames.add(member.name); - } - } - - List concreteSetters = - hierarchy.getDispatchTargets(cls.superclass, setters: true); - for (Member member - in hierarchy.getInterfaceMembers(cls.superclass, setters: true)) { - if (ClassHierarchy.findMemberByName(concreteSetters, member.name) == - null) { - existingSetterForwardersNames.add(member.name); - } - } - } - - List concrete = hierarchy.getDispatchTargets(cls); - List declared = hierarchy.getDeclaredMembers(cls); Member noSuchMethod = ClassHierarchy.findMemberByName( hierarchy.getInterfaceMembers(cls), noSuchMethodName); + List concrete = hierarchy.getDispatchTargets(cls); + List declared = hierarchy.getDeclaredMembers(cls); + bool changed = false; for (Member member in hierarchy.getInterfaceMembers(cls)) { if (member is Procedure && diff --git a/pkg/front_end/testcases/compile.status b/pkg/front_end/testcases/compile.status index 1623e1dec0b..c77eb3f66d4 100644 --- a/pkg/front_end/testcases/compile.status +++ b/pkg/front_end/testcases/compile.status @@ -125,3 +125,8 @@ incomplete_field_formal_parameter: Fail # Fasta doesn't recover well co19_language_metadata_syntax_t04: RuntimeError # Fasta doesn't recover well external_import: RuntimeError # Expected -- test uses import which doesn't exist. + +no_such_method_forwarders/forwarders_not_assumed_from_mixin: Fail +no_such_method_forwarders/no_forwarders_for_abstract_classes: Fail +no_such_method_forwarders/no_forwarders_for_abstract_classes_chain: Fail +no_such_method_forwarders/concrete_method_over_forwarder_in_mixin_application: Fail diff --git a/pkg/front_end/testcases/no_such_method_forwarders/concrete_method_over_forwarder_in_mixin_application.dart b/pkg/front_end/testcases/no_such_method_forwarders/concrete_method_over_forwarder_in_mixin_application.dart new file mode 100644 index 00000000000..de97201f78e --- /dev/null +++ b/pkg/front_end/testcases/no_such_method_forwarders/concrete_method_over_forwarder_in_mixin_application.dart @@ -0,0 +1,23 @@ +// Copyright (c) 2018, 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. + +// This test checks that when both LHS and RHS in a mixin application have a +// concrete implementation of a method and a noSuchMethod forwarder for it, the +// concrete implementation stays. + +abstract class I { + foo(); +} + +class A { + foo() {} +} + +class B implements I { + noSuchMethod(_) => null; +} + +class C extends A with B {} + +main() {} diff --git a/pkg/front_end/testcases/no_such_method_forwarders/no_dup_from_mixin.dart b/pkg/front_end/testcases/no_such_method_forwarders/forwarders_not_assumed_from_mixin.dart similarity index 66% rename from pkg/front_end/testcases/no_such_method_forwarders/no_dup_from_mixin.dart rename to pkg/front_end/testcases/no_such_method_forwarders/forwarders_not_assumed_from_mixin.dart index fcbc2e3c7d5..6a76ef8761a 100644 --- a/pkg/front_end/testcases/no_such_method_forwarders/no_dup_from_mixin.dart +++ b/pkg/front_end/testcases/no_such_method_forwarders/forwarders_not_assumed_from_mixin.dart @@ -2,8 +2,9 @@ // 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. -// This test checks that the noSuchMethod forwarder is not duplicated in classes -// that mix in the classes that already have the forwarder. +// This test checks that the noSuchMethod forwarder is not assumed to be mixed +// in and is generated for classes that mix in a user-defined noSuchMethod and +// have an abstract method. abstract class I { void foo(); diff --git a/pkg/front_end/testcases/no_such_method_forwarders/no_dup_from_mixin.dart.direct.expect b/pkg/front_end/testcases/no_such_method_forwarders/no_dup_from_mixin.dart.direct.expect deleted file mode 100644 index 0283bae33dd..00000000000 --- a/pkg/front_end/testcases/no_such_method_forwarders/no_dup_from_mixin.dart.direct.expect +++ /dev/null @@ -1,27 +0,0 @@ -library; -import self as self; -import "dart:core" as core; - -abstract class I extends core::Object { - synthetic constructor •() → void - : super core::Object::•() - ; - abstract method foo() → void; -} -class A extends core::Object implements self::I { - synthetic constructor •() → void - : super core::Object::•() - ; - method noSuchMethod(core::Invocation i) → dynamic - return null; - no-such-method-forwarder method foo() → void - return this.{self::A::noSuchMethod}(new core::_InvocationMirror::_withoutType("foo", const [], const [], core::Map::unmodifiable(const {}), false)); -} -abstract class _B&Object&A = core::Object with self::A { -} -class B extends self::_B&Object&A { - synthetic constructor •() → void - : super core::Object::•() - ; -} -static method main() → dynamic {} diff --git a/pkg/front_end/testcases/no_such_method_forwarders/no_dup_from_mixin.dart.direct.transformed.expect b/pkg/front_end/testcases/no_such_method_forwarders/no_dup_from_mixin.dart.direct.transformed.expect deleted file mode 100644 index bf046d85321..00000000000 --- a/pkg/front_end/testcases/no_such_method_forwarders/no_dup_from_mixin.dart.direct.transformed.expect +++ /dev/null @@ -1,34 +0,0 @@ -library; -import self as self; -import "dart:core" as core; - -abstract class I extends core::Object { - synthetic constructor •() → void - : super core::Object::•() - ; - abstract method foo() → void; -} -class A extends core::Object implements self::I { - synthetic constructor •() → void - : super core::Object::•() - ; - method noSuchMethod(core::Invocation i) → dynamic - return null; - no-such-method-forwarder method foo() → void - return this.{self::A::noSuchMethod}(new core::_InvocationMirror::_withoutType("foo", const [], const [], core::Map::unmodifiable(const {}), false)); -} -abstract class _B&Object&A extends core::Object implements self::A { - synthetic constructor •() → void - : super core::Object::•() - ; - method noSuchMethod(core::Invocation i) → dynamic - return null; - no-such-method-forwarder method foo() → void - return this.{self::A::noSuchMethod}(new core::_InvocationMirror::_withoutType("foo", const [], const [], core::Map::unmodifiable(const {}), false)); -} -class B extends self::_B&Object&A { - synthetic constructor •() → void - : super core::Object::•() - ; -} -static method main() → dynamic {} diff --git a/pkg/front_end/testcases/no_such_method_forwarders/no_dup_from_mixin.dart.outline.expect b/pkg/front_end/testcases/no_such_method_forwarders/no_dup_from_mixin.dart.outline.expect deleted file mode 100644 index 63d5358c2f5..00000000000 --- a/pkg/front_end/testcases/no_such_method_forwarders/no_dup_from_mixin.dart.outline.expect +++ /dev/null @@ -1,25 +0,0 @@ -library; -import self as self; -import "dart:core" as core; - -abstract class I extends core::Object { - synthetic constructor •() → void - ; - abstract method foo() → void; -} -class A extends core::Object implements self::I { - synthetic constructor •() → void - ; - method noSuchMethod(core::Invocation i) → dynamic - ; - no-such-method-forwarder method foo() → void - return this.{self::A::noSuchMethod}(new core::_InvocationMirror::_withoutType("foo", const [], const [], core::Map::unmodifiable(const {}), false)); -} -abstract class _B&Object&A = core::Object with self::A { -} -class B extends self::_B&Object&A { - synthetic constructor •() → void - ; -} -static method main() → dynamic - ; diff --git a/pkg/front_end/testcases/no_such_method_forwarders/no_dup_from_mixin.dart.strong.expect b/pkg/front_end/testcases/no_such_method_forwarders/no_dup_from_mixin.dart.strong.expect deleted file mode 100644 index 0283bae33dd..00000000000 --- a/pkg/front_end/testcases/no_such_method_forwarders/no_dup_from_mixin.dart.strong.expect +++ /dev/null @@ -1,27 +0,0 @@ -library; -import self as self; -import "dart:core" as core; - -abstract class I extends core::Object { - synthetic constructor •() → void - : super core::Object::•() - ; - abstract method foo() → void; -} -class A extends core::Object implements self::I { - synthetic constructor •() → void - : super core::Object::•() - ; - method noSuchMethod(core::Invocation i) → dynamic - return null; - no-such-method-forwarder method foo() → void - return this.{self::A::noSuchMethod}(new core::_InvocationMirror::_withoutType("foo", const [], const [], core::Map::unmodifiable(const {}), false)); -} -abstract class _B&Object&A = core::Object with self::A { -} -class B extends self::_B&Object&A { - synthetic constructor •() → void - : super core::Object::•() - ; -} -static method main() → dynamic {} diff --git a/pkg/front_end/testcases/no_such_method_forwarders/no_dup_from_mixin.dart.strong.transformed.expect b/pkg/front_end/testcases/no_such_method_forwarders/no_dup_from_mixin.dart.strong.transformed.expect deleted file mode 100644 index bf046d85321..00000000000 --- a/pkg/front_end/testcases/no_such_method_forwarders/no_dup_from_mixin.dart.strong.transformed.expect +++ /dev/null @@ -1,34 +0,0 @@ -library; -import self as self; -import "dart:core" as core; - -abstract class I extends core::Object { - synthetic constructor •() → void - : super core::Object::•() - ; - abstract method foo() → void; -} -class A extends core::Object implements self::I { - synthetic constructor •() → void - : super core::Object::•() - ; - method noSuchMethod(core::Invocation i) → dynamic - return null; - no-such-method-forwarder method foo() → void - return this.{self::A::noSuchMethod}(new core::_InvocationMirror::_withoutType("foo", const [], const [], core::Map::unmodifiable(const {}), false)); -} -abstract class _B&Object&A extends core::Object implements self::A { - synthetic constructor •() → void - : super core::Object::•() - ; - method noSuchMethod(core::Invocation i) → dynamic - return null; - no-such-method-forwarder method foo() → void - return this.{self::A::noSuchMethod}(new core::_InvocationMirror::_withoutType("foo", const [], const [], core::Map::unmodifiable(const {}), false)); -} -class B extends self::_B&Object&A { - synthetic constructor •() → void - : super core::Object::•() - ; -} -static method main() → dynamic {} diff --git a/pkg/front_end/testcases/no_such_method_forwarders/no_forwarders_for_abstract_classes.dart b/pkg/front_end/testcases/no_such_method_forwarders/no_forwarders_for_abstract_classes.dart new file mode 100644 index 00000000000..bb14b72fa11 --- /dev/null +++ b/pkg/front_end/testcases/no_such_method_forwarders/no_forwarders_for_abstract_classes.dart @@ -0,0 +1,20 @@ +// Copyright (c) 2018, 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. + +// This test checks that the noSuchMethod forwarders aren't generated for the +// abstract classes that have user-defined noSuchMethod, but rather in their +// concrete descendants. + +abstract class A { + noSuchMethod(i) => null; + + // The forwarder for [foo] shouldn't be generated here. + void foo(); +} + +class B extends A { + // The forwarder for [foo] should be generated here. +} + +main() {} diff --git a/pkg/front_end/testcases/no_such_method_forwarders/no_forwarders_for_abstract_classes_chain.dart b/pkg/front_end/testcases/no_such_method_forwarders/no_forwarders_for_abstract_classes_chain.dart new file mode 100644 index 00000000000..3ea89bf0c55 --- /dev/null +++ b/pkg/front_end/testcases/no_such_method_forwarders/no_forwarders_for_abstract_classes_chain.dart @@ -0,0 +1,29 @@ +// Copyright (c) 2018, 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. + +// This test checks that the noSuchMethod forwarders aren't generated for the +// abstract classes that have user-defined noSuchMethod, but rather in their +// top-most concrete descendants. The immediate abstract children should not +// receive the forwarders. + +abstract class A { + noSuchMethod(i) => null; + + // The forwarder for [foo] shouldn't be generated here. + void foo(); +} + +abstract class B extends A { + // [B] shouldn't receive a forwarder. +} + +class C extends B { + // The forwarder for [foo] should be generated here. +} + +class D extends C { + // [D] shouldn't receiver a forwarder. +} + +main() {} diff --git a/pkg/front_end/testcases/outline.status b/pkg/front_end/testcases/outline.status index 9cdafa9c817..603651fc545 100644 --- a/pkg/front_end/testcases/outline.status +++ b/pkg/front_end/testcases/outline.status @@ -252,3 +252,8 @@ rasta/issue_000046: Fail rasta/issue_000047: Fail rasta/native_is_illegal: Pass # Issue 29763 rasta/type_with_parse_error: Fail + +no_such_method_forwarders/forwarders_not_assumed_from_mixin: Fail +no_such_method_forwarders/no_forwarders_for_abstract_classes: Fail +no_such_method_forwarders/no_forwarders_for_abstract_classes_chain: Fail +no_such_method_forwarders/concrete_method_over_forwarder_in_mixin_application: Fail diff --git a/pkg/front_end/testcases/strong.status b/pkg/front_end/testcases/strong.status index b0858b9f4bb..0b285a6b159 100644 --- a/pkg/front_end/testcases/strong.status +++ b/pkg/front_end/testcases/strong.status @@ -221,3 +221,8 @@ incomplete_field_formal_parameter: Fail # Fasta doesn't recover well co19_language_metadata_syntax_t04: RuntimeError # Fasta doesn't recover well external_import: RuntimeError # The native extension to import doesn't exist. This is ok. + +no_such_method_forwarders/forwarders_not_assumed_from_mixin: Fail +no_such_method_forwarders/no_forwarders_for_abstract_classes: Fail +no_such_method_forwarders/no_forwarders_for_abstract_classes_chain: Fail +no_such_method_forwarders/concrete_method_over_forwarder_in_mixin_application: Fail diff --git a/tests/language_2/language_2_dartdevc.status b/tests/language_2/language_2_dartdevc.status index 278d7f4d72e..dccaa3a7575 100644 --- a/tests/language_2/language_2_dartdevc.status +++ b/tests/language_2/language_2_dartdevc.status @@ -553,6 +553,9 @@ string_split_test: CompileTimeError # Issue 31616 string_supertype_checked_test: CompileTimeError # Issue 31616 super_bound_closure_test/none: CompileTimeError # Issue 31533 super_call4_test/01: MissingCompileTimeError +super_operator_index6_test: CompileTimeError # Issue 33498 +super_operator_index7_test: CompileTimeError # Issue 33498 +super_operator_index8_test: CompileTimeError # Issue 33498 switch_bad_case_test/01: MissingCompileTimeError switch_bad_case_test/02: MissingCompileTimeError switch_case_test/00: MissingCompileTimeError diff --git a/tests/language_2/language_2_kernel.status b/tests/language_2/language_2_kernel.status index b0a22c7c6dc..6c4d31a30b5 100644 --- a/tests/language_2/language_2_kernel.status +++ b/tests/language_2/language_2_kernel.status @@ -655,6 +655,9 @@ vm/type_vm_test/35: MissingRuntimeError vm/type_vm_test/36: MissingRuntimeError [ $compiler == dartk && $runtime == vm ] +super_operator_index6_test: CompileTimeError # Issue 33498 +super_operator_index7_test: CompileTimeError # Issue 33498 +super_operator_index8_test: CompileTimeError # Issue 33498 type_alias_equality_test/02: RuntimeError # Issue 31359 type_alias_equality_test/03: RuntimeError # Issue 31359 type_alias_equality_test/04: RuntimeError # Issue 31359 @@ -1387,6 +1390,9 @@ setter_override_test/03: MissingCompileTimeError string_split_test: CompileTimeError string_supertype_checked_test: CompileTimeError super_bound_closure_test/none: CompileTimeError +super_operator_index6_test: CompileTimeError # Issue 33498 +super_operator_index7_test: CompileTimeError # Issue 33498 +super_operator_index8_test: CompileTimeError # Issue 33498 try_catch_test/01: MissingCompileTimeError type_promotion_functions_test/02: CompileTimeError type_promotion_functions_test/03: CompileTimeError