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());
+}