diff --git a/pkg/front_end/lib/src/fasta/type_inference/inference_visitor_base.dart b/pkg/front_end/lib/src/fasta/type_inference/inference_visitor_base.dart index b3f31131e38..298a707f8ac 100644 --- a/pkg/front_end/lib/src/fasta/type_inference/inference_visitor_base.dart +++ b/pkg/front_end/lib/src/fasta/type_inference/inference_visitor_base.dart @@ -1025,10 +1025,9 @@ abstract class InferenceVisitorBase implements InferenceVisitor { } else { for (DartType implement in extensionType.extensionTypeDeclaration.implements) { - // TODO(johnniwinther): Handle non-extension type supertypes. if (implement is ExtensionType) { - ExtensionType supertype = - hierarchyBuilder.getExtensionTypeAsInstanceOf( + ExtensionType supertype = hierarchyBuilder + .getExtensionTypeAsInstanceOfExtensionTypeDeclaration( extensionType, implement.extensionTypeDeclaration, isNonNullableByDefault: isNonNullableByDefault)!; ObjectAccessTarget? target = _findDirectExtensionTypeMember( @@ -1039,6 +1038,23 @@ abstract class InferenceVisitorBase implements InferenceVisitor { if (target != null) { return target; } + } else if (implement is InterfaceType) { + InterfaceType supertype = + hierarchyBuilder.getExtensionTypeAsInstanceOfClass( + extensionType, implement.classNode, + isNonNullableByDefault: isNonNullableByDefault)!; + Member? interfaceMember = _getInterfaceMember( + supertype.classNode, name, isSetter, fileOffset); + if (interfaceMember != null) { + return new ObjectAccessTarget.interfaceMember( + receiverType, interfaceMember, + isPotentiallyNullable: isReceiverTypePotentiallyNullable); + } + } else { + assert( + false, + "Unexpected supertype $implement extension type declaration of " + "$extensionType."); } } return null; diff --git a/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart b/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart new file mode 100644 index 00000000000..e8715b4d2cc --- /dev/null +++ b/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart @@ -0,0 +1,62 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by b +// BSD-style license that can be found in the LICENSE file. + +class A { + int field = 42; + int method() => field; + int get getter => field; + void set setter(int value) { + field = value; + } +} + +class B extends A {} + +extension type E(B it) implements B {} + +extension type F(B it) implements E {} + +main() { + B b = B(); + E e = E(b); + F f = F(b); + + expect(42, b.field); + expect(42, e.field); + expect(42, f.field); + + b.field = 87; + expect(87, b.method()); + expect(87, e.method()); + expect(87, f.method()); + + b.setter = 123; + expect(123, b.getter); + expect(123, e.getter); + expect(123, f.getter); + + e.setter = 87; + expect(87, b.field); + expect(87, e.field); + expect(87, f.field); + + e.field = 42; + expect(42, b.getter); + expect(42, e.getter); + expect(42, f.getter); + + f.field = 87; + expect(87, b.field); + expect(87, e.field); + expect(87, f.field); + + f.setter = 123; + expect(123, b.method()); + expect(123, e.method()); + expect(123, f.method()); +} + +expect(expected, actual) { + if (expected != actual) throw 'Expected $expected, actual $actual'; +} \ No newline at end of file diff --git a/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart.strong.expect b/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart.strong.expect new file mode 100644 index 00000000000..359b7646332 --- /dev/null +++ b/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart.strong.expect @@ -0,0 +1,78 @@ +library; +import self as self; +import "dart:core" as core; + +class A extends core::Object { + field core::int field = 42; + synthetic constructor •() → self::A + : super core::Object::•() + ; + method method() → core::int + return this.{self::A::field}{core::int}; + get getter() → core::int + return this.{self::A::field}{core::int}; + set setter(core::int value) → void { + this.{self::A::field} = value; + } +} +class B extends self::A { + synthetic constructor •() → self::B + : super self::A::•() + ; +} +extension type E(self::B it) implements self::B { + constructor • = self::E|; + tearoff • = self::E|_#new#tearOff; +} +extension type F(self::B it) implements self::E { + constructor • = self::F|; + tearoff • = self::F|_#new#tearOff; +} +static inline-class-member method E|(dynamic it) → self::E { + lowered final self::E #this = it; + return #this; +} +static inline-class-member method E|_#new#tearOff(dynamic it) → self::E + return self::E|(it); +static inline-class-member method F|(dynamic it) → self::F { + lowered final self::F #this = it; + return #this; +} +static inline-class-member method F|_#new#tearOff(dynamic it) → self::F + return self::F|(it); +static method main() → dynamic { + self::B b = new self::B::•(); + self::E e = self::E|(b); + self::F f = self::F|(b); + self::expect(42, b.{self::A::field}{core::int}); + self::expect(42, e.{self::A::field}{core::int}); + self::expect(42, f.{self::A::field}{core::int}); + b.{self::A::field} = 87; + self::expect(87, b.{self::A::method}(){() → core::int}); + self::expect(87, e.{self::A::method}(){() → core::int}); + self::expect(87, f.{self::A::method}(){() → core::int}); + b.{self::A::setter} = 123; + self::expect(123, b.{self::A::getter}{core::int}); + self::expect(123, e.{self::A::getter}{core::int}); + self::expect(123, f.{self::A::getter}{core::int}); + e.{self::A::setter} = 87; + self::expect(87, b.{self::A::field}{core::int}); + self::expect(87, e.{self::A::field}{core::int}); + self::expect(87, f.{self::A::field}{core::int}); + e.{self::A::field} = 42; + self::expect(42, b.{self::A::getter}{core::int}); + self::expect(42, e.{self::A::getter}{core::int}); + self::expect(42, f.{self::A::getter}{core::int}); + f.{self::A::field} = 87; + self::expect(87, b.{self::A::field}{core::int}); + self::expect(87, e.{self::A::field}{core::int}); + self::expect(87, f.{self::A::field}{core::int}); + f.{self::A::setter} = 123; + self::expect(123, b.{self::A::method}(){() → core::int}); + self::expect(123, e.{self::A::method}(){() → core::int}); + self::expect(123, f.{self::A::method}(){() → core::int}); +} +static method expect(dynamic expected, dynamic actual) → dynamic { + if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual)) + throw "Expected ${expected}, actual ${actual}"; +} diff --git a/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart.strong.transformed.expect b/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart.strong.transformed.expect new file mode 100644 index 00000000000..359b7646332 --- /dev/null +++ b/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart.strong.transformed.expect @@ -0,0 +1,78 @@ +library; +import self as self; +import "dart:core" as core; + +class A extends core::Object { + field core::int field = 42; + synthetic constructor •() → self::A + : super core::Object::•() + ; + method method() → core::int + return this.{self::A::field}{core::int}; + get getter() → core::int + return this.{self::A::field}{core::int}; + set setter(core::int value) → void { + this.{self::A::field} = value; + } +} +class B extends self::A { + synthetic constructor •() → self::B + : super self::A::•() + ; +} +extension type E(self::B it) implements self::B { + constructor • = self::E|; + tearoff • = self::E|_#new#tearOff; +} +extension type F(self::B it) implements self::E { + constructor • = self::F|; + tearoff • = self::F|_#new#tearOff; +} +static inline-class-member method E|(dynamic it) → self::E { + lowered final self::E #this = it; + return #this; +} +static inline-class-member method E|_#new#tearOff(dynamic it) → self::E + return self::E|(it); +static inline-class-member method F|(dynamic it) → self::F { + lowered final self::F #this = it; + return #this; +} +static inline-class-member method F|_#new#tearOff(dynamic it) → self::F + return self::F|(it); +static method main() → dynamic { + self::B b = new self::B::•(); + self::E e = self::E|(b); + self::F f = self::F|(b); + self::expect(42, b.{self::A::field}{core::int}); + self::expect(42, e.{self::A::field}{core::int}); + self::expect(42, f.{self::A::field}{core::int}); + b.{self::A::field} = 87; + self::expect(87, b.{self::A::method}(){() → core::int}); + self::expect(87, e.{self::A::method}(){() → core::int}); + self::expect(87, f.{self::A::method}(){() → core::int}); + b.{self::A::setter} = 123; + self::expect(123, b.{self::A::getter}{core::int}); + self::expect(123, e.{self::A::getter}{core::int}); + self::expect(123, f.{self::A::getter}{core::int}); + e.{self::A::setter} = 87; + self::expect(87, b.{self::A::field}{core::int}); + self::expect(87, e.{self::A::field}{core::int}); + self::expect(87, f.{self::A::field}{core::int}); + e.{self::A::field} = 42; + self::expect(42, b.{self::A::getter}{core::int}); + self::expect(42, e.{self::A::getter}{core::int}); + self::expect(42, f.{self::A::getter}{core::int}); + f.{self::A::field} = 87; + self::expect(87, b.{self::A::field}{core::int}); + self::expect(87, e.{self::A::field}{core::int}); + self::expect(87, f.{self::A::field}{core::int}); + f.{self::A::setter} = 123; + self::expect(123, b.{self::A::method}(){() → core::int}); + self::expect(123, e.{self::A::method}(){() → core::int}); + self::expect(123, f.{self::A::method}(){() → core::int}); +} +static method expect(dynamic expected, dynamic actual) → dynamic { + if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual)) + throw "Expected ${expected}, actual ${actual}"; +} diff --git a/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart.textual_outline.expect b/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart.textual_outline.expect new file mode 100644 index 00000000000..e06fa973dd3 --- /dev/null +++ b/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart.textual_outline.expect @@ -0,0 +1,10 @@ +class A { + int field = 42; + int method() => field; + int get getter => field; + void set setter(int value) {} +} +class B extends A {} +extension type E(B it) implements B {} extension type F(B it) implements E {} +main() {} +expect(expected, actual) {} diff --git a/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart.weak.expect b/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart.weak.expect new file mode 100644 index 00000000000..359b7646332 --- /dev/null +++ b/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart.weak.expect @@ -0,0 +1,78 @@ +library; +import self as self; +import "dart:core" as core; + +class A extends core::Object { + field core::int field = 42; + synthetic constructor •() → self::A + : super core::Object::•() + ; + method method() → core::int + return this.{self::A::field}{core::int}; + get getter() → core::int + return this.{self::A::field}{core::int}; + set setter(core::int value) → void { + this.{self::A::field} = value; + } +} +class B extends self::A { + synthetic constructor •() → self::B + : super self::A::•() + ; +} +extension type E(self::B it) implements self::B { + constructor • = self::E|; + tearoff • = self::E|_#new#tearOff; +} +extension type F(self::B it) implements self::E { + constructor • = self::F|; + tearoff • = self::F|_#new#tearOff; +} +static inline-class-member method E|(dynamic it) → self::E { + lowered final self::E #this = it; + return #this; +} +static inline-class-member method E|_#new#tearOff(dynamic it) → self::E + return self::E|(it); +static inline-class-member method F|(dynamic it) → self::F { + lowered final self::F #this = it; + return #this; +} +static inline-class-member method F|_#new#tearOff(dynamic it) → self::F + return self::F|(it); +static method main() → dynamic { + self::B b = new self::B::•(); + self::E e = self::E|(b); + self::F f = self::F|(b); + self::expect(42, b.{self::A::field}{core::int}); + self::expect(42, e.{self::A::field}{core::int}); + self::expect(42, f.{self::A::field}{core::int}); + b.{self::A::field} = 87; + self::expect(87, b.{self::A::method}(){() → core::int}); + self::expect(87, e.{self::A::method}(){() → core::int}); + self::expect(87, f.{self::A::method}(){() → core::int}); + b.{self::A::setter} = 123; + self::expect(123, b.{self::A::getter}{core::int}); + self::expect(123, e.{self::A::getter}{core::int}); + self::expect(123, f.{self::A::getter}{core::int}); + e.{self::A::setter} = 87; + self::expect(87, b.{self::A::field}{core::int}); + self::expect(87, e.{self::A::field}{core::int}); + self::expect(87, f.{self::A::field}{core::int}); + e.{self::A::field} = 42; + self::expect(42, b.{self::A::getter}{core::int}); + self::expect(42, e.{self::A::getter}{core::int}); + self::expect(42, f.{self::A::getter}{core::int}); + f.{self::A::field} = 87; + self::expect(87, b.{self::A::field}{core::int}); + self::expect(87, e.{self::A::field}{core::int}); + self::expect(87, f.{self::A::field}{core::int}); + f.{self::A::setter} = 123; + self::expect(123, b.{self::A::method}(){() → core::int}); + self::expect(123, e.{self::A::method}(){() → core::int}); + self::expect(123, f.{self::A::method}(){() → core::int}); +} +static method expect(dynamic expected, dynamic actual) → dynamic { + if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual)) + throw "Expected ${expected}, actual ${actual}"; +} diff --git a/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart.weak.modular.expect b/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart.weak.modular.expect new file mode 100644 index 00000000000..359b7646332 --- /dev/null +++ b/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart.weak.modular.expect @@ -0,0 +1,78 @@ +library; +import self as self; +import "dart:core" as core; + +class A extends core::Object { + field core::int field = 42; + synthetic constructor •() → self::A + : super core::Object::•() + ; + method method() → core::int + return this.{self::A::field}{core::int}; + get getter() → core::int + return this.{self::A::field}{core::int}; + set setter(core::int value) → void { + this.{self::A::field} = value; + } +} +class B extends self::A { + synthetic constructor •() → self::B + : super self::A::•() + ; +} +extension type E(self::B it) implements self::B { + constructor • = self::E|; + tearoff • = self::E|_#new#tearOff; +} +extension type F(self::B it) implements self::E { + constructor • = self::F|; + tearoff • = self::F|_#new#tearOff; +} +static inline-class-member method E|(dynamic it) → self::E { + lowered final self::E #this = it; + return #this; +} +static inline-class-member method E|_#new#tearOff(dynamic it) → self::E + return self::E|(it); +static inline-class-member method F|(dynamic it) → self::F { + lowered final self::F #this = it; + return #this; +} +static inline-class-member method F|_#new#tearOff(dynamic it) → self::F + return self::F|(it); +static method main() → dynamic { + self::B b = new self::B::•(); + self::E e = self::E|(b); + self::F f = self::F|(b); + self::expect(42, b.{self::A::field}{core::int}); + self::expect(42, e.{self::A::field}{core::int}); + self::expect(42, f.{self::A::field}{core::int}); + b.{self::A::field} = 87; + self::expect(87, b.{self::A::method}(){() → core::int}); + self::expect(87, e.{self::A::method}(){() → core::int}); + self::expect(87, f.{self::A::method}(){() → core::int}); + b.{self::A::setter} = 123; + self::expect(123, b.{self::A::getter}{core::int}); + self::expect(123, e.{self::A::getter}{core::int}); + self::expect(123, f.{self::A::getter}{core::int}); + e.{self::A::setter} = 87; + self::expect(87, b.{self::A::field}{core::int}); + self::expect(87, e.{self::A::field}{core::int}); + self::expect(87, f.{self::A::field}{core::int}); + e.{self::A::field} = 42; + self::expect(42, b.{self::A::getter}{core::int}); + self::expect(42, e.{self::A::getter}{core::int}); + self::expect(42, f.{self::A::getter}{core::int}); + f.{self::A::field} = 87; + self::expect(87, b.{self::A::field}{core::int}); + self::expect(87, e.{self::A::field}{core::int}); + self::expect(87, f.{self::A::field}{core::int}); + f.{self::A::setter} = 123; + self::expect(123, b.{self::A::method}(){() → core::int}); + self::expect(123, e.{self::A::method}(){() → core::int}); + self::expect(123, f.{self::A::method}(){() → core::int}); +} +static method expect(dynamic expected, dynamic actual) → dynamic { + if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual)) + throw "Expected ${expected}, actual ${actual}"; +} diff --git a/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart.weak.outline.expect b/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart.weak.outline.expect new file mode 100644 index 00000000000..d5b3e0ff33c --- /dev/null +++ b/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart.weak.outline.expect @@ -0,0 +1,39 @@ +library; +import self as self; +import "dart:core" as core; + +class A extends core::Object { + field core::int field; + synthetic constructor •() → self::A + ; + method method() → core::int + ; + get getter() → core::int + ; + set setter(core::int value) → void + ; +} +class B extends self::A { + synthetic constructor •() → self::B + ; +} +extension type E(self::B it) implements self::B { + constructor • = self::E|; + tearoff • = self::E|_#new#tearOff; +} +extension type F(self::B it) implements self::E { + constructor • = self::F|; + tearoff • = self::F|_#new#tearOff; +} +static inline-class-member method E|(dynamic it) → self::E + ; +static inline-class-member method E|_#new#tearOff(dynamic it) → self::E + return self::E|(it); +static inline-class-member method F|(dynamic it) → self::F + ; +static inline-class-member method F|_#new#tearOff(dynamic it) → self::F + return self::F|(it); +static method main() → dynamic + ; +static method expect(dynamic expected, dynamic actual) → dynamic + ; diff --git a/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart.weak.transformed.expect b/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart.weak.transformed.expect new file mode 100644 index 00000000000..359b7646332 --- /dev/null +++ b/pkg/front_end/testcases/inline_class/extension_types/access_non_extension_type_members.dart.weak.transformed.expect @@ -0,0 +1,78 @@ +library; +import self as self; +import "dart:core" as core; + +class A extends core::Object { + field core::int field = 42; + synthetic constructor •() → self::A + : super core::Object::•() + ; + method method() → core::int + return this.{self::A::field}{core::int}; + get getter() → core::int + return this.{self::A::field}{core::int}; + set setter(core::int value) → void { + this.{self::A::field} = value; + } +} +class B extends self::A { + synthetic constructor •() → self::B + : super self::A::•() + ; +} +extension type E(self::B it) implements self::B { + constructor • = self::E|; + tearoff • = self::E|_#new#tearOff; +} +extension type F(self::B it) implements self::E { + constructor • = self::F|; + tearoff • = self::F|_#new#tearOff; +} +static inline-class-member method E|(dynamic it) → self::E { + lowered final self::E #this = it; + return #this; +} +static inline-class-member method E|_#new#tearOff(dynamic it) → self::E + return self::E|(it); +static inline-class-member method F|(dynamic it) → self::F { + lowered final self::F #this = it; + return #this; +} +static inline-class-member method F|_#new#tearOff(dynamic it) → self::F + return self::F|(it); +static method main() → dynamic { + self::B b = new self::B::•(); + self::E e = self::E|(b); + self::F f = self::F|(b); + self::expect(42, b.{self::A::field}{core::int}); + self::expect(42, e.{self::A::field}{core::int}); + self::expect(42, f.{self::A::field}{core::int}); + b.{self::A::field} = 87; + self::expect(87, b.{self::A::method}(){() → core::int}); + self::expect(87, e.{self::A::method}(){() → core::int}); + self::expect(87, f.{self::A::method}(){() → core::int}); + b.{self::A::setter} = 123; + self::expect(123, b.{self::A::getter}{core::int}); + self::expect(123, e.{self::A::getter}{core::int}); + self::expect(123, f.{self::A::getter}{core::int}); + e.{self::A::setter} = 87; + self::expect(87, b.{self::A::field}{core::int}); + self::expect(87, e.{self::A::field}{core::int}); + self::expect(87, f.{self::A::field}{core::int}); + e.{self::A::field} = 42; + self::expect(42, b.{self::A::getter}{core::int}); + self::expect(42, e.{self::A::getter}{core::int}); + self::expect(42, f.{self::A::getter}{core::int}); + f.{self::A::field} = 87; + self::expect(87, b.{self::A::field}{core::int}); + self::expect(87, e.{self::A::field}{core::int}); + self::expect(87, f.{self::A::field}{core::int}); + f.{self::A::setter} = 123; + self::expect(123, b.{self::A::method}(){() → core::int}); + self::expect(123, e.{self::A::method}(){() → core::int}); + self::expect(123, f.{self::A::method}(){() → core::int}); +} +static method expect(dynamic expected, dynamic actual) → dynamic { + if(!(expected =={core::Object::==}{(core::Object) → core::bool} actual)) + throw "Expected ${expected}, actual ${actual}"; +} diff --git a/pkg/front_end/testcases/textual_outline.status b/pkg/front_end/testcases/textual_outline.status index 06ab9a6606c..929a389e8b4 100644 --- a/pkg/front_end/testcases/textual_outline.status +++ b/pkg/front_end/testcases/textual_outline.status @@ -124,6 +124,7 @@ general/type_variable_in_static_context: FormatterCrash general/var_as_type_name: FormatterCrash inline_class/inline_class_declaration: FormatterCrash inline_class/issue52667: FormatterCrash +inline_class/extension_types/access_non_extension_type_members: FormatterCrash inline_class/extension_types/annotations: FormatterCrash inline_class/extension_types/const_constructor: FormatterCrash inline_class/extension_types/constructor_access: FormatterCrash diff --git a/pkg/kernel/lib/class_hierarchy.dart b/pkg/kernel/lib/class_hierarchy.dart index 0c43b0f1bb3..be0090c8838 100644 --- a/pkg/kernel/lib/class_hierarchy.dart +++ b/pkg/kernel/lib/class_hierarchy.dart @@ -42,17 +42,23 @@ abstract class ClassHierarchyBase { List? getTypeArgumentsAsInstanceOf( InterfaceType type, Class superclass); - /// Returns the instantiation of [superclass] that is implemented by [type], - /// or `null` if [type] does not implement [superclass] at all. - ExtensionType? getExtensionTypeAsInstanceOf( - ExtensionType type, ExtensionTypeDeclaration superclass, + /// Returns the instantiation of [superDeclaration] that is implemented by + /// [type], or `null` if [type] does not implement [superDeclaration] at all. + ExtensionType? getExtensionTypeAsInstanceOfExtensionTypeDeclaration( + ExtensionType type, ExtensionTypeDeclaration superDeclaration, {required bool isNonNullableByDefault}); - /// Returns the type arguments of the instantiation of [superclass] that is - /// implemented by [type], or `null` if [type] does not implement [superclass] - /// at all. + /// Returns the instantiation of [superclass] that is implemented by [type], + /// or `null` if [type] does not implement [superclass] at all. + InterfaceType? getExtensionTypeAsInstanceOfClass( + ExtensionType type, Class superclass, + {required bool isNonNullableByDefault}); + + /// Returns the type arguments of the instantiation of [superDeclaration] that + /// is implemented by [type], or `null` if [type] does not implement + /// [superDeclaration] at all. List? getExtensionTypeArgumentsAsInstanceOf( - ExtensionType type, ExtensionTypeDeclaration superclass); + ExtensionType type, ExtensionTypeDeclaration superDeclaration); /// True if [subtype] inherits from [superclass] though zero or more /// `extends`, `with`, and `implements` relationships. @@ -78,26 +84,26 @@ abstract class ClassHierarchyBase { {required bool isNonNullableByDefault}); } -mixin ClassHierarchyExtensionTypeMixin { - CoreTypes get coreTypes; - - ExtensionType? getExtensionTypeDeclarationAsInstanceOf( - ExtensionTypeDeclaration subclass, ExtensionTypeDeclaration superclass, - {required bool isNonNullableByDefault}) { +mixin ClassHierarchyExtensionTypeMixin implements ClassHierarchyBase { + ExtensionType? + getExtensionTypeDeclarationAsInstanceOfExtensionTypeDeclaration( + ExtensionTypeDeclaration subDeclaration, + ExtensionTypeDeclaration superDeclaration, + {required bool isNonNullableByDefault}) { // TODO(johnniwinther): Improve lookup performance. - if (identical(subclass, superclass)) { + if (identical(subDeclaration, superDeclaration)) { return coreTypes.thisExtensionType( - subclass, + subDeclaration, isNonNullableByDefault ? Nullability.nonNullable : Nullability.legacy); } - for (DartType implement in subclass.implements) { - // TODO(johnniwinther): Handle non-extension type supertypes. + for (DartType implement in subDeclaration.implements) { if (implement is ExtensionType) { - ExtensionType? supertype = getExtensionTypeDeclarationAsInstanceOf( - implement.extensionTypeDeclaration, superclass, - isNonNullableByDefault: isNonNullableByDefault); + ExtensionType? supertype = + getExtensionTypeDeclarationAsInstanceOfExtensionTypeDeclaration( + implement.extensionTypeDeclaration, superDeclaration, + isNonNullableByDefault: isNonNullableByDefault); if (supertype != null) { if (implement.typeArguments.isNotEmpty) { supertype = Substitution.fromExtensionType(implement) @@ -105,17 +111,63 @@ mixin ClassHierarchyExtensionTypeMixin { } return supertype; } + } else if (implement is InterfaceType) { + // Extension type declarations cannot be implemented through classes. + } else { + assert( + false, + "Unexpected supertype $implement extension type declaration of " + "$subDeclaration."); } } return null; } - ExtensionType? getExtensionTypeAsInstanceOf( + InterfaceType? getExtensionTypeDeclarationAsInstanceOfClass( + ExtensionTypeDeclaration subDeclaration, Class superclass, + {required bool isNonNullableByDefault}) { + // TODO(johnniwinther): Improve lookup performance. + for (DartType implement in subDeclaration.implements) { + if (implement is ExtensionType) { + InterfaceType? supertype = getExtensionTypeDeclarationAsInstanceOfClass( + implement.extensionTypeDeclaration, superclass, + isNonNullableByDefault: isNonNullableByDefault); + if (supertype != null) { + if (implement.typeArguments.isNotEmpty) { + supertype = Substitution.fromExtensionType(implement) + .substituteType(supertype) as InterfaceType; + } + return supertype; + } + } else if (implement is InterfaceType) { + Supertype? supertype = + getClassAsInstanceOf(implement.classNode, superclass); + if (supertype != null) { + if (implement.typeArguments.isNotEmpty) { + supertype = Substitution.fromInterfaceType(implement) + .substituteSupertype(supertype); + } + return new InterfaceType(supertype.classNode, Nullability.nonNullable, + supertype.typeArguments); + } + } else { + assert( + false, + "Unexpected supertype $implement extension type declaration of " + "$subDeclaration."); + } + } + return null; + } + + @override + ExtensionType? getExtensionTypeAsInstanceOfExtensionTypeDeclaration( ExtensionType type, ExtensionTypeDeclaration superclass, {required bool isNonNullableByDefault}) { - ExtensionType? supertype = getExtensionTypeDeclarationAsInstanceOf( - type.extensionTypeDeclaration, superclass, - isNonNullableByDefault: isNonNullableByDefault); + ExtensionType? supertype = + getExtensionTypeDeclarationAsInstanceOfExtensionTypeDeclaration( + type.extensionTypeDeclaration, superclass, + isNonNullableByDefault: isNonNullableByDefault); if (supertype != null) { if (type.typeArguments.isNotEmpty) { supertype = Substitution.fromExtensionType(type) @@ -126,9 +178,28 @@ mixin ClassHierarchyExtensionTypeMixin { return null; } + @override + InterfaceType? getExtensionTypeAsInstanceOfClass( + ExtensionType type, Class superclass, + {required bool isNonNullableByDefault}) { + InterfaceType? supertype = getExtensionTypeDeclarationAsInstanceOfClass( + type.extensionTypeDeclaration, superclass, + isNonNullableByDefault: isNonNullableByDefault); + if (supertype != null) { + if (type.typeArguments.isNotEmpty) { + supertype = Substitution.fromExtensionType(type) + .substituteType(supertype) as InterfaceType; + } + return supertype; + } + return null; + } + + @override List? getExtensionTypeArgumentsAsInstanceOf( ExtensionType type, ExtensionTypeDeclaration superclass) { - return getExtensionTypeAsInstanceOf(type, superclass, + return getExtensionTypeAsInstanceOfExtensionTypeDeclaration( + type, superclass, isNonNullableByDefault: true) ?.typeArguments; } diff --git a/pkg/kernel/lib/src/standard_bounds.dart b/pkg/kernel/lib/src/standard_bounds.dart index a157e97ebff..0292632d013 100644 --- a/pkg/kernel/lib/src/standard_bounds.dart +++ b/pkg/kernel/lib/src/standard_bounds.dart @@ -1032,9 +1032,10 @@ mixin StandardBounds { supertypes.add(type); for (DartType implemented in type.extensionTypeDeclaration.implements) { if (implemented is ExtensionType) { - ExtensionType supertype = hierarchy.getExtensionTypeAsInstanceOf( - type, implemented.extensionTypeDeclaration, - isNonNullableByDefault: isNonNullableByDefault)!; + ExtensionType supertype = + hierarchy.getExtensionTypeAsInstanceOfExtensionTypeDeclaration( + type, implemented.extensionTypeDeclaration, + isNonNullableByDefault: isNonNullableByDefault)!; computeSuperTypes(supertype, supertypes); } }