[fasta] Fix checks of abstract methods when noSuchMethod is defined
If an abstract method overrides a concrete method, and the abstract one
is more specific, it's an error, unless there's a user-defined
noSuchMethod, effectively making the abstract method concrete.
Change-Id: I06ebe2de257ef6627529e6e97ca11b768c9647b9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106084
Commit-Queue: Dmitry Stefantsov <dmitryas@google.com>
Reviewed-by: Aske Simon Christensen <askesc@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart b/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart
index d61cbca..0960fca 100644
--- a/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/class_hierarchy_builder.dart
@@ -1323,6 +1323,7 @@
superclasses,
interfaces,
maxInheritancePath,
+ hasNoSuchMethod,
);
}
@@ -1705,6 +1706,8 @@
int get depth => superclasses.length;
+ final bool hasNoSuchMethod;
+
ClassHierarchyNode(
this.cls,
this.classMembers,
@@ -1713,7 +1716,8 @@
this.interfaceSetters,
this.superclasses,
this.interfaces,
- this.maxInheritancePath);
+ this.maxInheritancePath,
+ this.hasNoSuchMethod);
/// Returns a list of all supertypes of [cls], including this node.
List<ClassHierarchyNode> computeAllSuperNodes(
@@ -2400,7 +2404,7 @@
Declaration get abstractMember => declarations[1];
Member check(ClassHierarchyBuilder hierarchy) {
- if (!parent.isAbstract) {
+ if (!parent.isAbstract && !hierarchy.nodes[parent.cls].hasNoSuchMethod) {
new DelayedOverrideCheck(parent, concreteImplementation, abstractMember)
.check(hierarchy);
}
diff --git a/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart
new file mode 100644
index 0000000..7bba23c
--- /dev/null
+++ b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2019, 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.
+
+abstract class A {
+ A foo() => null;
+}
+
+abstract class B extends A {
+ B foo();
+}
+
+class C {
+ noSuchMethod(_) => null;
+}
+
+class D extends C implements B {}
+
+main() {}
diff --git a/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.hierarchy.expect b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.hierarchy.expect
new file mode 100644
index 0000000..344af9a
--- /dev/null
+++ b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.hierarchy.expect
@@ -0,0 +1,105 @@
+Object:
+ superclasses:
+ interfaces:
+ classMembers:
+ Object._haveSameRuntimeType
+ Object.toString
+ Object.runtimeType
+ Object._toString
+ Object._simpleInstanceOf
+ Object._hashCodeRnd
+ Object._instanceOf
+ Object.noSuchMethod
+ Object._objectHashCode
+ Object._identityHashCode
+ Object.hashCode
+ Object._simpleInstanceOfFalse
+ Object._simpleInstanceOfTrue
+ Object.==
+ classSetters:
+
+A:
+ superclasses:
+ Object
+ interfaces:
+ classMembers:
+ Object.toString
+ Object.runtimeType
+ Object._simpleInstanceOf
+ Object._instanceOf
+ A.foo
+ Object.noSuchMethod
+ Object._identityHashCode
+ Object.hashCode
+ Object._simpleInstanceOfFalse
+ Object._simpleInstanceOfTrue
+ Object.==
+ classSetters:
+
+B:
+ superclasses:
+ Object
+ -> A
+ interfaces:
+ classMembers:
+ Object.toString
+ Object.runtimeType
+ Object._simpleInstanceOf
+ Object._instanceOf
+ B.A.foo%B.foo
+ Object.noSuchMethod
+ Object._identityHashCode
+ Object.hashCode
+ Object._simpleInstanceOfFalse
+ Object._simpleInstanceOfTrue
+ Object.==
+ classSetters:
+
+C:
+ superclasses:
+ Object
+ interfaces:
+ classMembers:
+ Object.toString
+ Object.runtimeType
+ Object._simpleInstanceOf
+ Object._instanceOf
+ C.noSuchMethod
+ Object._identityHashCode
+ Object.hashCode
+ Object._simpleInstanceOfFalse
+ Object._simpleInstanceOfTrue
+ Object.==
+ classSetters:
+
+D:
+ Longest path to Object: 3
+ superclasses:
+ Object
+ -> C
+ interfaces: B, A
+ classMembers:
+ Object.toString
+ Object.runtimeType
+ Object._simpleInstanceOf
+ Object._instanceOf
+ C.noSuchMethod
+ Object._identityHashCode
+ Object.hashCode
+ Object._simpleInstanceOfFalse
+ Object._simpleInstanceOfTrue
+ Object.==
+ classSetters:
+ interfaceMembers:
+ Object.toString
+ Object.runtimeType
+ Object._simpleInstanceOf
+ Object._instanceOf
+ B.A.foo%B.foo
+ C.noSuchMethod
+ Object._identityHashCode
+ Object.hashCode
+ Object._simpleInstanceOfFalse
+ Object._simpleInstanceOfTrue
+ Object.==
+ interfaceSetters:
diff --git a/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.legacy.expect b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.legacy.expect
new file mode 100644
index 0000000..f45f7c3
--- /dev/null
+++ b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.legacy.expect
@@ -0,0 +1,32 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+abstract class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ method foo() → self::A
+ return null;
+}
+abstract class B extends self::A {
+ synthetic constructor •() → self::B
+ : super self::A::•()
+ ;
+ abstract method foo() → self::B;
+}
+class C extends core::Object {
+ synthetic constructor •() → self::C
+ : super core::Object::•()
+ ;
+ method noSuchMethod(core::Invocation _) → dynamic
+ return null;
+}
+class D extends self::C implements self::B {
+ synthetic constructor •() → self::D
+ : super self::C::•()
+ ;
+ no-such-method-forwarder method foo() → self::B
+ return this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withType(#foo, 0, const <core::Type>[], const <dynamic>[], core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))) as{TypeError} self::B;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.legacy.transformed.expect b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.legacy.transformed.expect
new file mode 100644
index 0000000..f45f7c3
--- /dev/null
+++ b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.legacy.transformed.expect
@@ -0,0 +1,32 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+abstract class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ method foo() → self::A
+ return null;
+}
+abstract class B extends self::A {
+ synthetic constructor •() → self::B
+ : super self::A::•()
+ ;
+ abstract method foo() → self::B;
+}
+class C extends core::Object {
+ synthetic constructor •() → self::C
+ : super core::Object::•()
+ ;
+ method noSuchMethod(core::Invocation _) → dynamic
+ return null;
+}
+class D extends self::C implements self::B {
+ synthetic constructor •() → self::D
+ : super self::C::•()
+ ;
+ no-such-method-forwarder method foo() → self::B
+ return this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withType(#foo, 0, const <core::Type>[], const <dynamic>[], core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))) as{TypeError} self::B;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.outline.expect b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.outline.expect
new file mode 100644
index 0000000..5430c79
--- /dev/null
+++ b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.outline.expect
@@ -0,0 +1,29 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+abstract class A extends core::Object {
+ synthetic constructor •() → self::A
+ ;
+ method foo() → self::A
+ ;
+}
+abstract class B extends self::A {
+ synthetic constructor •() → self::B
+ ;
+ abstract method foo() → self::B;
+}
+class C extends core::Object {
+ synthetic constructor •() → self::C
+ ;
+ method noSuchMethod(core::Invocation _) → dynamic
+ ;
+}
+class D extends self::C implements self::B {
+ synthetic constructor •() → self::D
+ ;
+ no-such-method-forwarder method foo() → self::B
+ return this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withType(#foo, 0, const <core::Type>[], const <dynamic>[], core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))) as{TypeError} self::B;
+}
+static method main() → dynamic
+ ;
diff --git a/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.strong.expect b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.strong.expect
new file mode 100644
index 0000000..f45f7c3
--- /dev/null
+++ b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.strong.expect
@@ -0,0 +1,32 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+abstract class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ method foo() → self::A
+ return null;
+}
+abstract class B extends self::A {
+ synthetic constructor •() → self::B
+ : super self::A::•()
+ ;
+ abstract method foo() → self::B;
+}
+class C extends core::Object {
+ synthetic constructor •() → self::C
+ : super core::Object::•()
+ ;
+ method noSuchMethod(core::Invocation _) → dynamic
+ return null;
+}
+class D extends self::C implements self::B {
+ synthetic constructor •() → self::D
+ : super self::C::•()
+ ;
+ no-such-method-forwarder method foo() → self::B
+ return this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withType(#foo, 0, const <core::Type>[], const <dynamic>[], core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))) as{TypeError} self::B;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.strong.transformed.expect b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.strong.transformed.expect
new file mode 100644
index 0000000..f45f7c3
--- /dev/null
+++ b/pkg/front_end/testcases/abstract_overrides_concrete_with_no_such_method.dart.strong.transformed.expect
@@ -0,0 +1,32 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+abstract class A extends core::Object {
+ synthetic constructor •() → self::A
+ : super core::Object::•()
+ ;
+ method foo() → self::A
+ return null;
+}
+abstract class B extends self::A {
+ synthetic constructor •() → self::B
+ : super self::A::•()
+ ;
+ abstract method foo() → self::B;
+}
+class C extends core::Object {
+ synthetic constructor •() → self::C
+ : super core::Object::•()
+ ;
+ method noSuchMethod(core::Invocation _) → dynamic
+ return null;
+}
+class D extends self::C implements self::B {
+ synthetic constructor •() → self::D
+ : super self::C::•()
+ ;
+ no-such-method-forwarder method foo() → self::B
+ return this.{self::C::noSuchMethod}(new core::_InvocationMirror::_withType(#foo, 0, const <core::Type>[], const <dynamic>[], core::Map::unmodifiable<core::Symbol, dynamic>(const <core::Symbol, dynamic>{}))) as{TypeError} self::B;
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status
index 32535ad..1fd5eae 100644
--- a/pkg/front_end/testcases/text_serialization.status
+++ b/pkg/front_end/testcases/text_serialization.status
@@ -8,6 +8,7 @@
DeltaBlue: TextSerializationFailure # Was: Pass
abstract_members: TypeCheckError
+abstract_overrides_concrete_with_no_such_method: TextSerializationFailure
accessors: TextSerializationFailure # Was: RuntimeError
ambiguous_exports: TextSerializationFailure # Was: RuntimeError # Expected, this file exports two main methods.
annotation_eof: TextSerializationFailure # Was: Pass