[fasta] Don't generate noSuchMethod forwarders in abstract classes
Bug: http://dartbug.com/33482
Change-Id: I4c9b2e9a43fdcd37238ede5814b2ad471f7b2c4d
Reviewed-on: https://dart-review.googlesource.com/60847
Commit-Queue: Dmitry Stefantsov <dmitryas@google.com>
Reviewed-by: Samir Jindel <sjindel@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
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 ac59460..930a322 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 @@
}
}
- // 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 @@
/// class was modified.
bool addNoSuchMethodForwarders(
KernelTarget target, ClassHierarchy hierarchy) {
- if (!hasUserDefinedNoSuchMethod(cls, hierarchy)) {
+ if (cls.isAbstract ||
+ !hasUserDefinedNoSuchMethod(cls, hierarchy, target.objectClass)) {
return false;
}
Set<Name> existingForwardersNames = new Set<Name>();
Set<Name> existingSetterForwardersNames = new Set<Name>();
- if (cls.superclass != null &&
- hasUserDefinedNoSuchMethod(cls.superclass, hierarchy)) {
- List<Member> 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<Member> concrete =
+ hierarchy.getDispatchTargets(leastConcreteSuperclass);
+ for (Member member
+ in hierarchy.getInterfaceMembers(leastConcreteSuperclass)) {
if (ClassHierarchy.findMemberByName(concrete, member.name) == null) {
existingForwardersNames.add(member.name);
}
}
List<Member> 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<Member> 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<Member> 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<Member> concrete = hierarchy.getDispatchTargets(cls);
- List<Member> declared = hierarchy.getDeclaredMembers(cls);
Member noSuchMethod = ClassHierarchy.findMemberByName(
hierarchy.getInterfaceMembers(cls), noSuchMethodName);
+ List<Member> concrete = hierarchy.getDispatchTargets(cls);
+ List<Member> 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 1623e1d..c77eb3f 100644
--- a/pkg/front_end/testcases/compile.status
+++ b/pkg/front_end/testcases/compile.status
@@ -125,3 +125,8 @@
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 0000000..de97201
--- /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 fcbc2e3..6a76ef8 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 0283bae..0000000
--- 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 <core::Type>[], const <dynamic>[], core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), 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 bf046d8..0000000
--- 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 <core::Type>[], const <dynamic>[], core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), 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 <core::Type>[], const <dynamic>[], core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), 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 63d5358..0000000
--- 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 <core::Type>[], const <dynamic>[], core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), 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 0283bae..0000000
--- 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 <core::Type>[], const <dynamic>[], core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), 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 bf046d8..0000000
--- 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 <core::Type>[], const <dynamic>[], core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), 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 <core::Type>[], const <dynamic>[], core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}), 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 0000000..bb14b72
--- /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 0000000..3ea89bf
--- /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 9cdafa9..603651f 100644
--- a/pkg/front_end/testcases/outline.status
+++ b/pkg/front_end/testcases/outline.status
@@ -252,3 +252,8 @@
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 b0858b9..0b285a6 100644
--- a/pkg/front_end/testcases/strong.status
+++ b/pkg/front_end/testcases/strong.status
@@ -221,3 +221,8 @@
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 278d7f4..dccaa3a 100644
--- a/tests/language_2/language_2_dartdevc.status
+++ b/tests/language_2/language_2_dartdevc.status
@@ -553,6 +553,9 @@
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 b0a22c7..6c4d31a 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/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 @@
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