Support multiple superclasses in the mixin `on` clause.
Change-Id: I23c613170be8fdf3c4a7ace560d71161b3e7320f
Reviewed-on: https://dart-review.googlesource.com/75881
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Stephen Adams <sra@google.com>
diff --git a/pkg/compiler/lib/src/kernel/element_map_impl.dart b/pkg/compiler/lib/src/kernel/element_map_impl.dart
index a6fb4fb..dea93c5 100644
--- a/pkg/compiler/lib/src/kernel/element_map_impl.dart
+++ b/pkg/compiler/lib/src/kernel/element_map_impl.dart
@@ -9,6 +9,7 @@
import 'package:kernel/ast.dart' as ir;
import 'package:kernel/class_hierarchy.dart' as ir;
import 'package:kernel/core_types.dart' as ir;
+import 'package:kernel/type_algebra.dart' as ir;
import 'package:kernel/type_environment.dart' as ir;
import '../common.dart';
@@ -284,7 +285,47 @@
return supertype;
}
- InterfaceType supertype = processSupertype(node.supertype);
+ InterfaceType supertype;
+ LinkBuilder<InterfaceType> linkBuilder =
+ new LinkBuilder<InterfaceType>();
+ if (node.isMixinDeclaration) {
+ // A mixin declaration
+ //
+ // mixin M on A, B, C {}
+ //
+ // is encoded by CFE as
+ //
+ // abstract class M extends A with B, C {}
+ //
+ // but we encode it as
+ //
+ // abstract class M extends Object implements A, B, C {}
+ //
+ // so we need to collect the non-Object superclasses and add them
+ // to the interfaces of M.
+
+ ir.Class superclass = node.superclass;
+ while (superclass != null) {
+ if (superclass.isAnonymousMixin) {
+ // Add second to last mixed in superclasses. `B` and `C` in the
+ // example above.
+ ir.DartType mixinType = typeEnvironment.hierarchy
+ .getTypeAsInstanceOf(node.thisType, superclass.mixedInClass);
+ linkBuilder.addLast(getDartType(mixinType));
+ } else {
+ // Add first mixed in superclass. `A` in the example above.
+ ir.DartType mixinType = typeEnvironment.hierarchy
+ .getTypeAsInstanceOf(node.thisType, superclass);
+ linkBuilder.addLast(getDartType(mixinType));
+ break;
+ }
+ superclass = superclass.superclass;
+ }
+ // Set superclass to `Object`.
+ supertype = _commonElements.objectType;
+ } else {
+ supertype = processSupertype(node.supertype);
+ }
if (supertype == _commonElements.objectType) {
ClassEntity defaultSuperclass =
_commonElements.getDefaultSuperclass(cls, nativeBasicData);
@@ -292,8 +333,6 @@
} else {
data.supertype = supertype;
}
- LinkBuilder<InterfaceType> linkBuilder =
- new LinkBuilder<InterfaceType>();
if (node.mixedInType != null) {
data.isMixinApplication = true;
linkBuilder
@@ -709,20 +748,24 @@
return data.imports[node];
}
- DartType getStaticType(ir.Expression node) {
+ ir.TypeEnvironment get typeEnvironment {
if (_typeEnvironment == null) {
_typeEnvironment ??= new ir.TypeEnvironment(
new ir.CoreTypes(env.mainComponent),
new ir.ClassHierarchy(env.mainComponent));
}
+ return _typeEnvironment;
+ }
+
+ DartType getStaticType(ir.Expression node) {
ir.TreeNode enclosingClass = node;
while (enclosingClass != null && enclosingClass is! ir.Class) {
enclosingClass = enclosingClass.parent;
}
try {
- _typeEnvironment.thisType =
+ typeEnvironment.thisType =
enclosingClass is ir.Class ? enclosingClass.thisType : null;
- return getDartType(node.getStaticType(_typeEnvironment));
+ return getDartType(node.getStaticType(typeEnvironment));
} catch (e) {
// The static type computation crashes on type errors. Use `dynamic`
// as static type.
diff --git a/tests/compiler/dart2js_extra/supermixin/supermixin11_test.dart b/tests/compiler/dart2js_extra/supermixin/supermixin11_test.dart
new file mode 100644
index 0000000..efd86c0
--- /dev/null
+++ b/tests/compiler/dart2js_extra/supermixin/supermixin11_test.dart
@@ -0,0 +1,39 @@
+// 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.
+
+import 'package:expect/expect.dart';
+
+class SuperA1 {
+ method1(a) => 'A1.m1$a';
+}
+
+class SuperA2 {
+ method2(a) => 'A2.m2$a';
+}
+
+class SuperA {
+ method1(a) => 'A.m1$a';
+ method2(a) => 'A.m2$a';
+}
+
+class SuperB1 extends SuperA implements SuperA1 {
+ method1(a) => 'B1.m1$a';
+}
+
+class SuperB2 extends SuperB1 implements SuperA2 {
+ method2(a) => 'B2.m2$a';
+}
+
+mixin Mixin on SuperA1, SuperA2 {
+ method1(a) => super.method1('M$a');
+ method2(a) => super.method2('M$a');
+}
+
+class Class extends SuperB2 with Mixin {}
+
+main() {
+ var c = new Class();
+ Expect.equals("B1.m1MC", c.method1('C'));
+ Expect.equals("B2.m2MC", c.method2('C'));
+}
diff --git a/tests/compiler/dart2js_extra/supermixin/supermixin12_test.dart b/tests/compiler/dart2js_extra/supermixin/supermixin12_test.dart
new file mode 100644
index 0000000..81fa7f29
--- /dev/null
+++ b/tests/compiler/dart2js_extra/supermixin/supermixin12_test.dart
@@ -0,0 +1,24 @@
+// 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.
+
+import 'package:expect/expect.dart';
+
+class SuperA {
+ method(a, [b = 'foo']) => 'A$a$b';
+}
+
+class SuperB extends SuperA {
+ method(a, [b = 'foo']) => 'B$a$b';
+}
+
+mixin Mixin on SuperA {
+ method2(a) => super.method('M$a');
+}
+
+class Class extends SuperB with Mixin {}
+
+main() {
+ var c = new Class();
+ Expect.equals("BMCfoo", c.method2('C'));
+}
diff --git a/tests/compiler/dart2js_extra/supermixin/supermixin13_test.dart b/tests/compiler/dart2js_extra/supermixin/supermixin13_test.dart
new file mode 100644
index 0000000..142e893
--- /dev/null
+++ b/tests/compiler/dart2js_extra/supermixin/supermixin13_test.dart
@@ -0,0 +1,24 @@
+// 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.
+
+import 'package:expect/expect.dart';
+
+class SuperA {
+ method(a) => 'A$a';
+}
+
+class SuperB extends SuperA {
+ method(a, [b = 'foo']) => 'B$a$b';
+}
+
+mixin Mixin on SuperA {
+ method2(a) => super.method('M$a');
+}
+
+class Class extends SuperB with Mixin {}
+
+main() {
+ var c = new Class();
+ Expect.equals("BMCfoo", c.method2('C'));
+}
diff --git a/tests/compiler/dart2js_extra/supermixin/supermixin14_test.dart b/tests/compiler/dart2js_extra/supermixin/supermixin14_test.dart
new file mode 100644
index 0000000..224baa4
--- /dev/null
+++ b/tests/compiler/dart2js_extra/supermixin/supermixin14_test.dart
@@ -0,0 +1,24 @@
+// 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.
+
+import 'package:expect/expect.dart';
+
+class SuperA {
+ method(a) => 'A$a';
+}
+
+class SuperB implements SuperA {
+ method(a) => 'B$a';
+}
+
+mixin Mixin on SuperA {
+ method(a) => super.method('M$a');
+}
+
+class Class extends SuperB with Mixin {}
+
+main() {
+ var c = new Class();
+ Expect.equals("BMC", c.method('C'));
+}
\ No newline at end of file
diff --git a/tests/compiler/dart2js_extra/supermixin/supermixin15_test.dart b/tests/compiler/dart2js_extra/supermixin/supermixin15_test.dart
new file mode 100644
index 0000000..a2df040
--- /dev/null
+++ b/tests/compiler/dart2js_extra/supermixin/supermixin15_test.dart
@@ -0,0 +1,24 @@
+// 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.
+
+import 'package:expect/expect.dart';
+
+class SuperA {
+ method(a) => 'A$a';
+}
+
+class SuperB extends SuperA {
+ method(a) => 'B$a';
+}
+
+mixin Mixin on SuperA {
+ method(a) => super.method('M$a');
+}
+
+class Class extends SuperB with Mixin {}
+
+main() {
+ var c = new Class();
+ Expect.equals("BMC", c.method('C'));
+}
\ No newline at end of file
diff --git a/tests/compiler/dart2js_extra/supermixin/supermixin16_test.dart b/tests/compiler/dart2js_extra/supermixin/supermixin16_test.dart
new file mode 100644
index 0000000..09e0267
--- /dev/null
+++ b/tests/compiler/dart2js_extra/supermixin/supermixin16_test.dart
@@ -0,0 +1,42 @@
+// 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.
+
+import 'package:expect/expect.dart';
+
+class SuperA1 {}
+
+class SuperA2 {}
+
+class InterfaceA {}
+
+mixin MixinA on SuperA1, SuperA2 implements InterfaceA {}
+
+class SuperB1<T> {}
+
+class SuperB2<T> {}
+
+class SuperB3<T> {}
+
+class InterfaceB<T> {}
+
+mixin MixinB<T> on SuperB1<List<T>>, SuperB2<int>, SuperB3<T>
+ implements InterfaceB<Map<int, T>> {}
+
+class C extends SuperA1 with SuperA2 {}
+
+main() {
+ var listA = <MixinA>[];
+ Expect.isTrue(listA is List<MixinA>);
+ Expect.isTrue(listA is List<SuperA1>);
+ Expect.isTrue(listA is List<SuperA2>);
+ Expect.isTrue(listA is List<InterfaceA>);
+
+ var listB = <MixinB<String>>[];
+ Expect.isTrue(listB is List<MixinB<String>>);
+ Expect.isTrue(listB is List<SuperB1<List<String>>>);
+ Expect.isTrue(listB is List<SuperB2<int>>);
+ Expect.isTrue(listB is List<SuperB3<String>>);
+ Expect.isTrue(listB is List<InterfaceB<Map<int, String>>>);
+
+}
\ No newline at end of file
diff --git a/tests/compiler/dart2js_extra/supermixin/supermixin17_test.dart b/tests/compiler/dart2js_extra/supermixin/supermixin17_test.dart
new file mode 100644
index 0000000..7752bc7
--- /dev/null
+++ b/tests/compiler/dart2js_extra/supermixin/supermixin17_test.dart
@@ -0,0 +1,36 @@
+// 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.
+
+import 'package:expect/expect.dart';
+
+class S {}
+class M {}
+
+class SuperC = S with M;
+
+class SuperA {
+}
+
+class SuperB extends SuperA {
+}
+
+mixin Mixin on SuperC, SuperA {
+}
+
+class Class extends SuperB with Mixin {}
+
+@AssumeDynamic()
+@NoInline()
+test(c) {
+ Expect.isTrue(c is Mixin, "Unexpected result for $c is Mixin");
+ Expect.isTrue(c is SuperC, "Unexpected result for $c is SuperC");
+ Expect.isTrue(c is SuperA, "Unexpected result for $c is SuperA");
+ Expect.isTrue(c is S, "Unexpected result for $c is S");
+ Expect.isTrue(c is M, "Unexpected result for $c is M");
+}
+
+main() {
+ new SuperC();
+ test(new Class());
+}
diff --git a/tests/compiler/dart2js_extra/supermixin/supermixin18_test.dart b/tests/compiler/dart2js_extra/supermixin/supermixin18_test.dart
new file mode 100644
index 0000000..8153da1
--- /dev/null
+++ b/tests/compiler/dart2js_extra/supermixin/supermixin18_test.dart
@@ -0,0 +1,36 @@
+// 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.
+
+import 'package:expect/expect.dart';
+
+class S {}
+class M {}
+
+class SuperC = S with M;
+
+class SuperA {
+}
+
+class SuperB extends SuperA {
+}
+
+mixin Mixin on SuperA, SuperC {
+}
+
+class Class extends SuperB with Mixin {}
+
+@AssumeDynamic()
+@NoInline()
+test(c) {
+ Expect.isTrue(c is Mixin, "Unexpected result for $c is Mixin");
+ Expect.isTrue(c is SuperC, "Unexpected result for $c is SuperC");
+ Expect.isTrue(c is SuperA, "Unexpected result for $c is SuperA");
+ Expect.isTrue(c is S, "Unexpected result for $c is S");
+ Expect.isTrue(c is M, "Unexpected result for $c is M");
+}
+
+main() {
+ new SuperC();
+ test(new Class());
+}