implement super mixins in dartdevc and fix a few issues in Analyzer

Fixes #34167. This implements the Dart 2 mixin proposal
(https://goo.gl/KEKQyv) for DDC. When the mixin is applied, a class
is created for the application that extends the correct superclass
and has all of the instance members, so `super` works correctly.

This also fixes a few minor issues in Analyzer's (mostly complete)
implementation:
- InterfaceType.isObject now returns false for Dart 2 mixins.
- Least upper bound calculation recognizes mixins are not Object.
- Interface of the mixin now implements its superclass constraints.
- Mixin superclass constraints are checked against the superclass and
  all previously applied mixins (if any); this keeps it working with
  the subtype fix above, and also prevents a not-yet-applied mixin
  from satisfying the constraint

The language_2/mixin_declaration tests were updated with a few minor
fixes now that we can run Analyzer/dartdevc to test them.

This change implements super mixins for DDC's Kernel backend (DDK)
too. This will be enabled once Kernel adds a flag to recognize which
Class nodes are mixins (vs normal classes).


Change-Id: Ib3c4fcb12de9988345e52d92931196828d8227c3
Reviewed-on: https://dart-review.googlesource.com/74965
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Vijay Menon <vsm@google.com>
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 950e17d..4e466b6 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -626,8 +626,8 @@
       return false;
     }
     if (supertype == null) {
-      // Should never happen, since Object is the only class that has no
-      // supertype, and it should have been caught by the test above.
+      // Should never happen, since Object and mixins are the only classes that
+      // have no supertype, and they should have been caught by the test above.
       assert(false);
       return false;
     }
@@ -647,8 +647,8 @@
         }
         classesSeen.add(nearestNonMixinClass);
         if (nearestNonMixinClass.supertype == null) {
-          // Should never happen, since Object is the only class that has no
-          // supertype, and it is not a mixin application.
+          // Should never happen, since Object and mixins are the only classes that
+          // have no supertype, and they are not mixin applications.
           assert(false);
           return false;
         }
@@ -1106,9 +1106,9 @@
     // forwarded to this class.
     Iterable<ConstructorElement> constructorsToForward;
     if (supertype == null) {
-      // Shouldn't ever happen, since the only class with no supertype is
-      // Object, and it isn't a mixin application.  But for safety's sake just
-      // assume an empty list.
+      // Shouldn't ever happen, since the only classes with no supertype are
+      // Object and mixins, and they aren't a mixin application. But for
+      // safety's sake just assume an empty list.
       assert(false);
       constructorsToForward = <ConstructorElement>[];
     } else if (!supertype.element.isMixinApplication) {
diff --git a/pkg/analyzer/lib/src/dart/element/type.dart b/pkg/analyzer/lib/src/dart/element/type.dart
index e19757b..b8a110c 100644
--- a/pkg/analyzer/lib/src/dart/element/type.dart
+++ b/pkg/analyzer/lib/src/dart/element/type.dart
@@ -1339,7 +1339,7 @@
   }
 
   @override
-  bool get isObject => element.supertype == null;
+  bool get isObject => element.supertype == null && !element.isMixin;
 
   @override
   List<MethodElement> get methods {
@@ -1495,10 +1495,9 @@
     ClassElement jElement = j.element;
     InterfaceType supertype = jElement.supertype;
     //
-    // If J has no direct supertype then it is Object, and Object has no direct
-    // supertypes.
+    // If J is Object, then it has no direct supertypes.
     //
-    if (supertype == null) {
+    if (j.isObject) {
       return false;
     }
     //
@@ -2212,7 +2211,7 @@
       InterfaceType type, int depth, HashSet<ClassElement> visitedTypes) {
     ClassElement classElement = type.element;
     // Object case
-    if (classElement.supertype == null || visitedTypes.contains(classElement)) {
+    if (type.isObject || visitedTypes.contains(classElement)) {
       return depth;
     }
     int longestPath = 1;
@@ -2244,10 +2243,12 @@
       // TODO(brianwilkerson) Does this also need to add in the number of mixin
       // classes?
       InterfaceType supertype = classElement.supertype;
-      pathLength = _computeLongestInheritancePathToObject(
-          supertype, depth + 1, visitedTypes);
-      if (pathLength > longestPath) {
-        longestPath = pathLength;
+      if (supertype != null) {
+        pathLength = _computeLongestInheritancePathToObject(
+            supertype, depth + 1, visitedTypes);
+        if (pathLength > longestPath) {
+          longestPath = pathLength;
+        }
       }
     } finally {
       visitedTypes.remove(classElement);
diff --git a/pkg/analyzer/lib/src/dart/resolver/inheritance_manager.dart b/pkg/analyzer/lib/src/dart/resolver/inheritance_manager.dart
index db04d6f..db3945b 100644
--- a/pkg/analyzer/lib/src/dart/resolver/inheritance_manager.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/inheritance_manager.dart
@@ -283,7 +283,7 @@
     }
     InterfaceType supertype = classElt.supertype;
     if (supertype == null) {
-      // classElt is Object
+      // classElt is Object or a mixin
       _classLookup[classElt] = resultMap;
       return resultMap;
     }
@@ -380,9 +380,8 @@
     // functionality in InheritanceManagerTest
     chain.add(currentType);
     ClassElement classElt = currentType.element;
-    InterfaceType supertype = classElt.supertype;
     // Base case- reached Object
-    if (supertype == null) {
+    if (currentType.isObject) {
       // Looked up the chain all the way to Object, return null.
       // This should never happen.
       return;
@@ -411,8 +410,9 @@
       }
     }
     // Superclass
-    ClassElement superclassElt = supertype.element;
-    if (lookupMember(superclassElt, memberName) != null) {
+    InterfaceType supertype = classElt.supertype;
+    if (supertype != null &&
+        lookupMember(supertype.element, memberName) != null) {
       _computeInheritancePath(chain, supertype, memberName);
       return;
     }
diff --git a/pkg/analyzer/lib/src/generated/element_resolver.dart b/pkg/analyzer/lib/src/generated/element_resolver.dart
index 903ff7d..cc387c5 100644
--- a/pkg/analyzer/lib/src/generated/element_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/element_resolver.dart
@@ -719,8 +719,9 @@
       // Generate the type name.
       // The error code will never be generated via type propagation
       DartType getSuperType(DartType type) {
-        if (type is InterfaceType && !type.isObject) {
-          return type.superclass;
+        if (type is InterfaceType) {
+          InterfaceType superclass = type.superclass;
+          if (superclass != null) return superclass;
         }
         return type;
       }
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 2bfb759..1ebf375 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -1910,7 +1910,9 @@
       return false;
     }
     bool problemReported = false;
-    for (TypeName mixinName in withClause.mixinTypes) {
+    List<TypeName> mixinTypes = withClause.mixinTypes;
+    for (int i = 0; i < mixinTypes.length; i++) {
+      TypeName mixinName = mixinTypes[i];
       DartType mixinType = mixinName.type;
       if (mixinType is InterfaceType) {
         if (_checkForExtendsOrImplementsDisallowedClass(
@@ -1923,7 +1925,8 @@
             problemReported = true;
           }
           if (mixinElement.isMixin) {
-            if (_checkForMixinSuperclassConstraints(mixinName)) {
+            if (_checkForMixinSuperclassConstraints(
+                mixinName, mixinTypes.take(i))) {
               problemReported = true;
             }
             if (_checkForMixinSuperInvokedMembers(mixinName, mixinElement)) {
@@ -4256,12 +4259,14 @@
   }
 
   /// Check that superclass constrains for the mixin type of [mixinName]
-  /// are satisfied by the [_enclosingClass].
-  bool _checkForMixinSuperclassConstraints(TypeName mixinName) {
-    InterfaceType enclosingType = _enclosingClass.type;
+  /// are satisfied by the superclass and/or any previous mixin applications.
+  bool _checkForMixinSuperclassConstraints(
+      TypeName mixinName, Iterable<TypeName> previousMixins) {
+    List<InterfaceType> supertypes = [_enclosingClass.supertype];
+    supertypes.addAll(previousMixins.map((t) => t.type));
     InterfaceType mixinType = mixinName.type;
     for (var constraint in mixinType.superclassConstraints) {
-      if (!_typeSystem.isSubtypeOf(enclosingType, constraint)) {
+      if (!supertypes.any((s) => _typeSystem.isSubtypeOf(s, constraint))) {
         _errorReporter.reportErrorForNode(
             CompileTimeErrorCode.MIXIN_APPLICATION_NOT_IMPLEMENTED_INTERFACE,
             mixinName.name,
diff --git a/pkg/analyzer/lib/src/generated/type_system.dart b/pkg/analyzer/lib/src/generated/type_system.dart
index 2a7ac3e..0f80220 100644
--- a/pkg/analyzer/lib/src/generated/type_system.dart
+++ b/pkg/analyzer/lib/src/generated/type_system.dart
@@ -1582,7 +1582,9 @@
     visitedTypes ??= new HashSet<ClassElement>();
     if (!visitedTypes.add(i1Element)) return false;
 
-    if (_isInterfaceSubtypeOf(i1.superclass, i2, visitedTypes)) {
+    InterfaceType superclass = i1.superclass;
+    if (superclass != null &&
+        _isInterfaceSubtypeOf(superclass, i2, visitedTypes)) {
       return true;
     }
 
@@ -1598,6 +1600,14 @@
       }
     }
 
+    if (i1Element.isMixin) {
+      for (final parent in i1.superclassConstraints) {
+        if (_isInterfaceSubtypeOf(parent, i2, visitedTypes)) {
+          return true;
+        }
+      }
+    }
+
     return false;
   }
 
diff --git a/pkg/dev_compiler/lib/src/analyzer/code_generator.dart b/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
index ef0d000..cee5682 100644
--- a/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
+++ b/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
@@ -971,7 +971,9 @@
     _emitVirtualFieldSymbols(classElem, body);
     _emitClassSignature(classElem, className, memberMap, body);
     _initExtensionSymbols(classElem);
-    _defineExtensionMembers(className, body);
+    if (!classElem.isMixin) {
+      _defineExtensionMembers(className, body);
+    }
     _emitClassMetadata(classNode.metadata, className, body);
 
     var classDef = JS.Statement.from(body);
@@ -1233,7 +1235,7 @@
 
   @override
   JS.Statement visitMixinDeclaration(MixinDeclaration node) {
-    throw new UnimplementedError();
+    return _emitClassDeclaration(node, node.declaredElement, node.members);
   }
 
   /// Wraps a possibly generic class in its type arguments.
@@ -1275,6 +1277,70 @@
     return js.statement('# = #;', [className, classExpr]);
   }
 
+  /// Like [_emitClassStatement] but emits a Dart 2.1 mixin represented by
+  /// [classElem].
+  ///
+  /// Mixins work similar to normal classes, but their instance methods close
+  /// over the actual superclass. Given a Dart class like:
+  ///
+  ///     mixin M on C {
+  ///       foo() => super.foo() + 42;
+  ///     }
+  ///
+  /// We generate a JS class like this:
+  ///
+  ///     lib.M = class M extends core.Object {}
+  ///     lib.M[dart.mixinOn] = (C) => class M extends C {
+  ///       foo() {
+  ///         return super.foo() + 42;
+  ///       }
+  ///     };
+  ///
+  /// The special `dart.mixinOn` symbolized property is used by the runtime
+  /// helper `dart.applyMixin`. The helper calls the function with the actual
+  /// base class, and then copies the resulting members to the destination
+  /// class.
+  ///
+  /// In the long run we may be able to improve this so we do not have the
+  /// unnecessary class, but for now, this lets us get the right semantics with
+  /// minimal compiler and runtime changes.
+  void _emitMixinStatement(
+      ClassElement classElem,
+      JS.Expression className,
+      JS.Expression heritage,
+      List<JS.Method> methods,
+      List<JS.Statement> body) {
+    assert(classElem.isMixin);
+
+    var staticMethods = methods.where((m) => m.isStatic).toList();
+    var instanceMethods = methods.where((m) => !m.isStatic).toList();
+    body.add(
+        _emitClassStatement(classElem, className, heritage, staticMethods));
+
+    var superclassId = JS.TemporaryId(
+        classElem.superclassConstraints.map((t) => t.name).join('_'));
+    var classId =
+        className is JS.Identifier ? className : JS.TemporaryId(classElem.name);
+
+    var mixinMemberClass =
+        JS.ClassExpression(classId, superclassId, instanceMethods);
+
+    JS.Node arrowFnBody = mixinMemberClass;
+    var extensionInit = <JS.Statement>[];
+    _defineExtensionMembers(classId, extensionInit);
+    if (extensionInit.isNotEmpty) {
+      extensionInit.insert(0, mixinMemberClass.toStatement());
+      extensionInit.add(classId.toReturn());
+      arrowFnBody = JS.Block(extensionInit);
+    }
+
+    body.add(js.statement('#[#.mixinOn] = #', [
+      className,
+      runtimeModule,
+      JS.ArrowFun([superclassId], arrowFnBody)
+    ]));
+  }
+
   void _defineClass(
       ClassElement classElem,
       JS.Expression className,
@@ -1305,7 +1371,9 @@
           if (t.typeArguments.any(defer)) return true;
           if (t is InterfaceType) {
             var e = t.element;
-            return e.mixins.any(defer) || defer(e.supertype);
+            if (e.mixins.any(defer)) return true;
+            var supertype = e.supertype;
+            return supertype != null && defer(supertype);
           }
         }
         return false;
@@ -1328,7 +1396,7 @@
       return base;
     }
 
-    var supertype = classElem.supertype;
+    var supertype = classElem.isMixin ? types.objectType : classElem.supertype;
     var hasUnnamedSuper = _hasUnnamedConstructor(supertype.element);
 
     void emitMixinConstructors(JS.Expression className, [InterfaceType mixin]) {
@@ -1385,7 +1453,7 @@
       var classExpr = deferMixin ? getBaseClass(0) : className;
 
       mixinBody
-          .add(runtimeStatement('mixinMembers(#, #)', [classExpr, mixinClass]));
+          .add(runtimeStatement('applyMixin(#, #)', [classExpr, mixinClass]));
 
       _topLevelClass = savedTopLevel;
 
@@ -1395,8 +1463,8 @@
         //
         // We do this with the following pattern:
         //
-        //     mixinMembers(C, class C$ extends M { <methods>  });
-        mixinBody.add(runtimeStatement('mixinMembers(#, #)', [
+        //     applyMixin(C, class C$ extends M { <methods>  });
+        mixinBody.add(runtimeStatement('applyMixin(#, #)', [
           classExpr,
           JS.ClassExpression(
               JS.TemporaryId(classElem.name), mixinClass, methods)
@@ -1428,11 +1496,11 @@
       hasUnnamedSuper = hasUnnamedSuper || _hasUnnamedConstructor(m.element);
 
       if (shouldDefer(m)) {
-        deferredSupertypes.add(runtimeStatement('mixinMembers(#, #)',
+        deferredSupertypes.add(runtimeStatement('applyMixin(#, #)',
             [getBaseClass(mixinLength - i), emitDeferredType(m)]));
       } else {
         body.add(
-            runtimeStatement('mixinMembers(#, #)', [mixinId, emitClassRef(m)]));
+            runtimeStatement('applyMixin(#, #)', [mixinId, emitClassRef(m)]));
       }
 
       baseClass = mixinId;
@@ -1440,7 +1508,14 @@
 
     _topLevelClass = savedTopLevel;
 
-    body.add(_emitClassStatement(classElem, className, baseClass, methods));
+    if (classElem.isMixin) {
+      // TODO(jmesserly): we could make this more efficient, as this creates
+      // an extra unnecessary class. But it's the easiest way to handle the
+      // current system.
+      _emitMixinStatement(classElem, className, baseClass, methods, body);
+    } else {
+      body.add(_emitClassStatement(classElem, className, baseClass, methods));
+    }
 
     if (classElem.isMixinApplication) emitMixinConstructors(className);
   }
@@ -2069,9 +2144,13 @@
   /// Emit the signature on the class recording the runtime type information
   void _emitClassSignature(ClassElement classElem, JS.Expression className,
       Map<Element, Declaration> annotatedMembers, List<JS.Statement> body) {
-    if (classElem.interfaces.isNotEmpty) {
+    if (classElem.interfaces.isNotEmpty ||
+        classElem.superclassConstraints.isNotEmpty) {
+      var interfaces = classElem.interfaces.toList()
+        ..addAll(classElem.superclassConstraints);
+
       body.add(js.statement('#[#.implements] = () => [#];',
-          [className, runtimeModule, classElem.interfaces.map(_emitType)]));
+          [className, runtimeModule, interfaces.map(_emitType)]));
     }
 
     void emitSignature(String name, List<JS.Property> elements) {
@@ -2363,7 +2442,9 @@
     // Get the supertype's unnamed constructor.
     superCtor ??= element.supertype?.element?.unnamedConstructor;
     if (superCtor == null) {
-      assert(element.type.isObject || options.unsafeForceCompile);
+      assert(element.type.isObject ||
+          element.isMixin ||
+          options.unsafeForceCompile);
       return null;
     }
 
@@ -2384,6 +2465,7 @@
 
   bool _hasUnnamedSuperConstructor(ClassElement e) {
     var supertype = e.supertype;
+    // Object or mixin declaration.
     if (supertype == null) return false;
     if (_hasUnnamedConstructor(supertype.element)) return true;
     for (var mixin in e.mixins) {
diff --git a/pkg/dev_compiler/lib/src/analyzer/element_helpers.dart b/pkg/dev_compiler/lib/src/analyzer/element_helpers.dart
index 12bcbe5..9e7b9be 100644
--- a/pkg/dev_compiler/lib/src/analyzer/element_helpers.dart
+++ b/pkg/dev_compiler/lib/src/analyzer/element_helpers.dart
@@ -139,6 +139,7 @@
       if (mixin != null) result.add(mixin);
     }
     var supertype = cls.supertype;
+    // Object or mixin declaration.
     if (supertype == null) break;
 
     cls = supertype.element;
diff --git a/pkg/dev_compiler/lib/src/analyzer/extension_types.dart b/pkg/dev_compiler/lib/src/analyzer/extension_types.dart
index c3d65cb..513a3f3 100644
--- a/pkg/dev_compiler/lib/src/analyzer/extension_types.dart
+++ b/pkg/dev_compiler/lib/src/analyzer/extension_types.dart
@@ -103,7 +103,8 @@
     }
     element.interfaces.forEach(_addExtensionType);
     element.mixins.forEach(_addExtensionType);
-    _addExtensionType(element.supertype);
+    var supertype = element.supertype;
+    if (supertype != null) _addExtensionType(element.supertype);
   }
 
   void _addExtensionTypesForLibrary(String libraryUri, List<String> typeNames) {
diff --git a/pkg/dev_compiler/lib/src/analyzer/property_model.dart b/pkg/dev_compiler/lib/src/analyzer/property_model.dart
index b625554..a4c1146 100644
--- a/pkg/dev_compiler/lib/src/analyzer/property_model.dart
+++ b/pkg/dev_compiler/lib/src/analyzer/property_model.dart
@@ -264,7 +264,7 @@
     // mock methods encodes information about the number of arguments (and type
     // arguments) that D expects.
     var element = type.element;
-    if (!hasNoSuchMethod(element)) return;
+    if (element.isMixin || !hasNoSuchMethod(element)) return;
 
     // Collect all unimplemented members.
     //
@@ -409,7 +409,8 @@
     for (var i in element.interfaces) {
       _collectNativeMembers(i, members);
     }
-    if (!type.isObject) {
+    var supertype = element.supertype;
+    if (supertype != null) {
       _collectNativeMembers(element.supertype, members);
     }
     if (element.isEnum) {
diff --git a/pkg/dev_compiler/lib/src/js_ast/builder.dart b/pkg/dev_compiler/lib/src/js_ast/builder.dart
index f0e3a65..6640da0 100644
--- a/pkg/dev_compiler/lib/src/js_ast/builder.dart
+++ b/pkg/dev_compiler/lib/src/js_ast/builder.dart
@@ -621,8 +621,15 @@
   static final ARROW_TOKEN = '=>';
   static final ELLIPSIS_TOKEN = '...';
 
-  static final OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS =
-      ['typeof', 'void', 'delete', 'in', 'instanceof', 'await'].toSet();
+  static final OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS = [
+    'typeof',
+    'void',
+    'delete',
+    'in',
+    'instanceof',
+    'await',
+    'extends'
+  ].toSet();
 
   static int category(int code) {
     if (code >= CATEGORIES.length) return OTHER;
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index ff4086c..970cc7a 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -559,7 +559,9 @@
     _emitVirtualFieldSymbols(c, body);
     _emitClassSignature(c, className, body);
     _initExtensionSymbols(c);
-    _defineExtensionMembers(className, body);
+    if (!isMixinDeclaration(c)) {
+      _defineExtensionMembers(className, body);
+    }
     _emitClassMetadata(c.annotations, className, body);
 
     var classDef = JS.Statement.from(body);
@@ -619,6 +621,67 @@
     return js.statement('# = #;', [className, classExpr]);
   }
 
+  /// Like [_emitClassStatement] but emits a Dart 2.1 mixin represented by
+  /// [c].
+  ///
+  /// Mixins work similar to normal classes, but their instance methods close
+  /// over the actual superclass. Given a Dart class like:
+  ///
+  ///     mixin M on C {
+  ///       foo() => super.foo() + 42;
+  ///     }
+  ///
+  /// We generate a JS class like this:
+  ///
+  ///     lib.M = class M extends core.Object {}
+  ///     lib.M[dart.mixinOn] = (C) => class M extends C {
+  ///       foo() {
+  ///         return super.foo() + 42;
+  ///       }
+  ///     };
+  ///
+  /// The special `dart.mixinOn` symbolized property is used by the runtime
+  /// helper `dart.applyMixin`. The helper calls the function with the actual
+  /// base class, and then copies the resulting members to the destination
+  /// class.
+  ///
+  /// In the long run we may be able to improve this so we do not have the
+  /// unnecessary class, but for now, this lets us get the right semantics with
+  /// minimal compiler and runtime changes.
+  void _emitMixinStatement(
+      Class c,
+      JS.Expression className,
+      JS.Expression heritage,
+      List<JS.Method> methods,
+      List<JS.Statement> body) {
+    var staticMethods = methods.where((m) => m.isStatic).toList();
+    var instanceMethods = methods.where((m) => !m.isStatic).toList();
+
+    body.add(_emitClassStatement(c, className, heritage, staticMethods));
+    var superclassId = JS.TemporaryId(getLocalClassName(c.superclass));
+    var classId = className is JS.Identifier
+        ? className
+        : JS.TemporaryId(getLocalClassName(c));
+
+    var mixinMemberClass =
+        JS.ClassExpression(classId, superclassId, instanceMethods);
+
+    JS.Node arrowFnBody = mixinMemberClass;
+    var extensionInit = <JS.Statement>[];
+    _defineExtensionMembers(classId, extensionInit);
+    if (extensionInit.isNotEmpty) {
+      extensionInit.insert(0, mixinMemberClass.toStatement());
+      extensionInit.add(classId.toReturn());
+      arrowFnBody = JS.Block(extensionInit);
+    }
+
+    body.add(js.statement('#[#.mixinOn] = #', [
+      className,
+      runtimeModule,
+      JS.ArrowFun([superclassId], arrowFnBody)
+    ]));
+  }
+
   void _defineClass(Class c, JS.Expression className, List<JS.Method> methods,
       List<JS.Statement> body, List<JS.Statement> deferredSupertypes) {
     if (c == coreTypes.objectClass) {
@@ -746,7 +809,7 @@
       var classExpr = deferMixin ? getBaseClass(0) : className;
 
       mixinBody
-          .add(runtimeStatement('mixinMembers(#, #)', [classExpr, mixinClass]));
+          .add(runtimeStatement('applyMixin(#, #)', [classExpr, mixinClass]));
 
       if (methods.isNotEmpty) {
         // However we may need to add some methods to this class that call
@@ -754,8 +817,8 @@
         //
         // We do this with the following pattern:
         //
-        //     mixinMembers(C, class C$ extends M { <methods>  });
-        mixinBody.add(runtimeStatement('mixinMembers(#, #)', [
+        //     applyMixin(C, class C$ extends M { <methods>  });
+        mixinBody.add(runtimeStatement('applyMixin(#, #)', [
           classExpr,
           JS.ClassExpression(
               JS.TemporaryId(getLocalClassName(c)), mixinClass, methods)
@@ -790,17 +853,22 @@
       hasUnnamedSuper = hasUnnamedSuper || _hasUnnamedConstructor(m.classNode);
 
       if (shouldDefer(m)) {
-        deferredSupertypes.add(runtimeStatement('mixinMembers(#, #)',
+        deferredSupertypes.add(runtimeStatement('applyMixin(#, #)',
             [getBaseClass(mixins.length - i), emitDeferredType(m)]));
       } else {
         body.add(
-            runtimeStatement('mixinMembers(#, #)', [mixinId, emitClassRef(m)]));
+            runtimeStatement('applyMixin(#, #)', [mixinId, emitClassRef(m)]));
       }
 
       baseClass = mixinId;
     }
 
-    body.add(_emitClassStatement(c, className, baseClass, methods));
+    if (isMixinDeclaration(c)) {
+      _emitMixinStatement(c, className, baseClass, methods, body);
+    } else {
+      body.add(_emitClassStatement(c, className, baseClass, methods));
+    }
+
     _classEmittingExtends = savedTopLevelClass;
   }
 
diff --git a/pkg/dev_compiler/lib/src/kernel/kernel_helpers.dart b/pkg/dev_compiler/lib/src/kernel/kernel_helpers.dart
index 27b4e85..7d1beb2 100644
--- a/pkg/dev_compiler/lib/src/kernel/kernel_helpers.dart
+++ b/pkg/dev_compiler/lib/src/kernel/kernel_helpers.dart
@@ -234,3 +234,6 @@
   }
   return sc;
 }
+
+// TODO(jmesserly): replace with a flag on Class once Kernel supports it.
+bool isMixinDeclaration(Class c) => false;
diff --git a/pkg/dev_compiler/lib/src/kernel/target.dart b/pkg/dev_compiler/lib/src/kernel/target.dart
index ea955ac..170221c 100644
--- a/pkg/dev_compiler/lib/src/kernel/target.dart
+++ b/pkg/dev_compiler/lib/src/kernel/target.dart
@@ -12,6 +12,8 @@
 class DevCompilerTarget extends Target {
   bool get strongMode => true; // the only correct answer
 
+  bool get enableSuperMixins => true;
+
   String get name => 'dartdevc';
 
   List<String> get extraRequiredLibraries => const [
diff --git a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/classes.dart b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/classes.dart
index 0dd4e3e..c8b2e39 100644
--- a/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/classes.dart
+++ b/pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/classes.dart
@@ -13,9 +13,7 @@
 part of dart._runtime;
 
 /// Returns a new type that mixes members from base and the mixin.
-///
-/// The mixin must be non-generic; generic mixins are handled by [genericMixin].
-void mixinMembers(to, from) {
+void applyMixin(to, from) {
   JS('', '#[#] = #', to, _mixin, from);
   var toProto = JS('', '#.prototype', to);
   var fromProto = JS('', '#.prototype', from);
@@ -24,6 +22,11 @@
   _mixinSignature(to, from, _fieldSig);
   _mixinSignature(to, from, _getterSig);
   _mixinSignature(to, from, _setterSig);
+  var mixinOnFn = JS('', '#[#]', from, mixinOn);
+  if (mixinOnFn != null) {
+    var proto = JS('', '#(#.__proto__).prototype', mixinOnFn, to);
+    _copyMembers(toProto, proto);
+  }
 }
 
 void _copyMembers(to, from) {
@@ -97,16 +100,18 @@
 getMixin(clazz) => JS('', 'Object.hasOwnProperty.call(#, #) ? #[#] : null',
     clazz, _mixin, clazz, _mixin);
 
+final mixinOn = JS('', 'Symbol("mixinOn")');
+
 @JSExportName('implements')
-final _implements = JS('', 'Symbol("implements")');
+final implements_ = JS('', 'Symbol("implements")');
 
 List Function() getImplements(clazz) => JS(
     '',
     'Object.hasOwnProperty.call(#, #) ? #[#] : null',
     clazz,
-    _implements,
+    implements_,
     clazz,
-    _implements);
+    implements_);
 
 /// The Symbol for storing type arguments on a specialized generic type.
 final _typeArguments = JS('', 'Symbol("typeArguments")');
diff --git a/pkg/expect/lib/expect.dart b/pkg/expect/lib/expect.dart
index d20db53..eff873d 100644
--- a/pkg/expect/lib/expect.dart
+++ b/pkg/expect/lib/expect.dart
@@ -620,6 +620,30 @@
         "on ${Error.safeToString(object)}");
   }
 
+  /// Checks that `Sub` is a subtype of `Super` at compile time and run time.
+  static bool subtype<Sub extends Super, Super>() {
+    List<Super> list = <Sub>[];
+    _subtypeAtRuntime<Sub, Super>();
+  }
+
+  /// Checks that `Sub` is a subtype of `Super` at run time.
+  ///
+  /// This is similar to [subtype] but without the `Sub extends Super` generic
+  /// constraint, so a compiler is less likely to optimize away the `is` check
+  /// because the types appear to be unrelated.
+  static bool _subtypeAtRuntime<Sub, Super>() {
+    if (<Sub>[] is! List<Super>) {
+      fail("$Sub is not a subtype of $Super");
+    }
+  }
+
+  /// Checks that `Sub` is not a subtype of `Super` at run time.
+  static bool notSubtype<Sub, Super>() {
+    if (<Sub>[] is List<Super>) {
+      fail("$Sub is a subtype of $Super");
+    }
+  }
+
   static String _getMessage(String reason) =>
       (reason == null) ? "" : ", '$reason'";
 
diff --git a/tests/language_2/language_2.status b/tests/language_2/language_2.status
index 95f2fc6..03666e2 100644
--- a/tests/language_2/language_2.status
+++ b/tests/language_2/language_2.status
@@ -233,7 +233,6 @@
 
 [ $compiler == dartdevc || $compiler == dartdevk ]
 double_literals/*: Skip # https://github.com/dart-lang/sdk/issues/34359
-mixin_declaration/*: Skip # See https://github.com/dart-lang/language/issues/7
 
 [ $hot_reload || $hot_reload_rollback ]
 cha_deopt1_test: Crash # Requires deferred libraries
diff --git a/tests/language_2/language_2_analyzer.status b/tests/language_2/language_2_analyzer.status
index d3ddd0c..de2813b 100644
--- a/tests/language_2/language_2_analyzer.status
+++ b/tests/language_2/language_2_analyzer.status
@@ -72,10 +72,9 @@
 issue31596_tearoff_test: CompileTimeError # Issue #31596
 issue31596_test: CompileTimeError # Issue #31596
 malformed2_test: Pass, MissingCompileTimeError # Flaky: issue 31056.
-mixin_declaration/mixin_declaration_invalid_superinvocation_test/none: CompileTimeError # bug in analyzer? Exchange UnaryNum, UnaryOptionalNum, and it works.
+mixin_declaration/mixin_declaration_invalid_superinvocation_test/10: CompileTimeError # bug in analyzer? Exchange UnaryNum, UnaryOptionalNum, and it works.
 mixin_declaration/mixin_declaration_superinvocation_application_test/05: MissingCompileTimeError
 mixin_declaration/mixin_declaration_supertype_compatible_test/04: MissingCompileTimeError # no most specific signature
-mixin_declaration/mixin_declaration_syntax_test: CompileTimeError # test issue: many of them
 mixin_forwarding_constructor4_test/01: CompileTimeError # See issue #34375
 mixin_forwarding_constructor4_test/02: CompileTimeError # See issue #34375
 mixin_forwarding_constructor4_test/03: CompileTimeError # See issue #34375
diff --git a/tests/language_2/language_2_dartdevc.status b/tests/language_2/language_2_dartdevc.status
index cd32a21..0c59d4e 100644
--- a/tests/language_2/language_2_dartdevc.status
+++ b/tests/language_2/language_2_dartdevc.status
@@ -104,6 +104,9 @@
 issue32353_test: RuntimeError
 label_test: RuntimeError
 left_shift_test: RuntimeError # Ints and doubles are unified.
+mixin_declaration/mixin_declaration_invalid_superinvocation_test/10: CompileTimeError    # Analyzer chooses wrong(?) super method.
+mixin_declaration/mixin_declaration_superinvocation_application_test/05: MissingCompileTimeError
+mixin_declaration/mixin_declaration_supertype_compatible_test/04: MissingCompileTimeError
 mixin_forwarding_constructor4_test/01: CompileTimeError # See issue 15101
 mixin_forwarding_constructor4_test/02: CompileTimeError # See issue 15101
 mixin_forwarding_constructor4_test/03: CompileTimeError # See issue 15101
@@ -290,6 +293,7 @@
 map_literal3_test/01: MissingCompileTimeError
 map_literal3_test/02: MissingCompileTimeError
 map_literal3_test/03: MissingCompileTimeError
+mixin_declaration/*: Skip # need flag on Kernel Class nodes.
 mixin_illegal_super_use_test/01: MissingCompileTimeError
 mixin_illegal_super_use_test/04: MissingCompileTimeError
 mixin_illegal_super_use_test/07: MissingCompileTimeError
diff --git a/tests/language_2/language_2_kernel.status b/tests/language_2/language_2_kernel.status
index dce0b32..2c1858f 100644
--- a/tests/language_2/language_2_kernel.status
+++ b/tests/language_2/language_2_kernel.status
@@ -89,11 +89,10 @@
 mixin_declaration/mixin_declaration_invalid_application_supertype_test/04: MissingCompileTimeError
 mixin_declaration/mixin_declaration_invalid_application_supertype_test/05: MissingCompileTimeError
 mixin_declaration/mixin_declaration_invalid_override_test/none: CompileTimeError
+mixin_declaration/mixin_declaration_invalid_superinvocation_test/10: CompileTimeError
 mixin_declaration/mixin_declaration_invalid_superinvocation_test/none: CompileTimeError
 mixin_declaration/mixin_declaration_invalid_syntax_test/none: CompileTimeError
 mixin_declaration/mixin_declaration_invalid_usage_test/03: MissingCompileTimeError
-mixin_declaration/mixin_declaration_nsm_test: RuntimeError
-mixin_declaration/mixin_declaration_subtype_test: RuntimeError
 mixin_declaration/mixin_declaration_superinvocation_application_test/none: CompileTimeError
 mixin_declaration/mixin_declaration_supertype_compatible_test/none: CompileTimeError
 mixin_declaration/mixin_declaration_syntax_test: CompileTimeError
@@ -214,6 +213,7 @@
 mixin_declaration/mixin_declaration_invalid_application_supertype_test/04: MissingCompileTimeError
 mixin_declaration/mixin_declaration_invalid_application_supertype_test/05: MissingCompileTimeError
 mixin_declaration/mixin_declaration_invalid_override_test/none: CompileTimeError
+mixin_declaration/mixin_declaration_invalid_superinvocation_test/10: CompileTimeError
 mixin_declaration/mixin_declaration_invalid_superinvocation_test/none: CompileTimeError
 mixin_declaration/mixin_declaration_invalid_syntax_test/none: CompileTimeError
 mixin_declaration/mixin_declaration_invalid_usage_test/03: MissingCompileTimeError
@@ -2137,11 +2137,10 @@
 mixin_declaration/mixin_declaration_invalid_application_supertype_test/04: MissingCompileTimeError
 mixin_declaration/mixin_declaration_invalid_application_supertype_test/05: MissingCompileTimeError
 mixin_declaration/mixin_declaration_invalid_override_test/none: CompileTimeError
+mixin_declaration/mixin_declaration_invalid_superinvocation_test/10: CompileTimeError
 mixin_declaration/mixin_declaration_invalid_superinvocation_test/none: CompileTimeError
 mixin_declaration/mixin_declaration_invalid_syntax_test/none: CompileTimeError
 mixin_declaration/mixin_declaration_invalid_usage_test/03: MissingCompileTimeError
-mixin_declaration/mixin_declaration_nsm_test: RuntimeError
-mixin_declaration/mixin_declaration_subtype_test: RuntimeError
 mixin_declaration/mixin_declaration_superinvocation_application_test/none: CompileTimeError
 mixin_declaration/mixin_declaration_supertype_compatible_test/none: CompileTimeError
 mixin_declaration/mixin_declaration_syntax_test: CompileTimeError
diff --git a/tests/language_2/mixin_declaration/mixin_declaration_invalid_superinvocation_test.dart b/tests/language_2/mixin_declaration/mixin_declaration_invalid_superinvocation_test.dart
index 5b1c832..500ec14 100644
--- a/tests/language_2/mixin_declaration/mixin_declaration_invalid_superinvocation_test.dart
+++ b/tests/language_2/mixin_declaration/mixin_declaration_invalid_superinvocation_test.dart
@@ -41,7 +41,7 @@
 mixin M3 on UnaryNum, UnaryOptionalNum {
   void bar() {
     super.foo(4.2);
-    super.foo();
+    super.foo();     //# 10: ok
     super.foo(1, 2); //# 08: compile-time error
     super.foo("not num"); //# 09: compile-time error
   }
diff --git a/tests/language_2/mixin_declaration/mixin_declaration_nsm_test.dart b/tests/language_2/mixin_declaration/mixin_declaration_nsm_test.dart
index f1994cc..ccfde7b 100644
--- a/tests/language_2/mixin_declaration/mixin_declaration_nsm_test.dart
+++ b/tests/language_2/mixin_declaration/mixin_declaration_nsm_test.dart
@@ -13,11 +13,11 @@
 }
 
 mixin M implements Bar {
-  dynamic noSuchMethod(i) => "M${i.memberName == #foo ? "foo" : "bar"}";
+  dynamic noSuchMethod(i) => "M:${i.memberName == #foo ? "foo" : "bar"}";
 }
 
 abstract class C {
-  dynamic noSuchMethod(i) => "C${i.memberName == #foo ? "foo" : "bar"}";
+  dynamic noSuchMethod(i) => "C:${i.memberName == #foo ? "foo" : "bar"}";
 }
 
 abstract class D {
diff --git a/tests/language_2/mixin_declaration/mixin_declaration_subtype_test.dart b/tests/language_2/mixin_declaration/mixin_declaration_subtype_test.dart
index ecc9347..172bb23 100644
--- a/tests/language_2/mixin_declaration/mixin_declaration_subtype_test.dart
+++ b/tests/language_2/mixin_declaration/mixin_declaration_subtype_test.dart
@@ -30,34 +30,22 @@
 
 class GD<T> = GC<T> with GM<T>;
 
-bool expectSubtype<Sub, Super>() {
-  if (<Sub>[] is! List<Super>) {
-    Expect.fail("$Sub is not a subtype of $Super");
-  }
-}
-
-bool expectNotSubtype<Sub, Super>() {
-  if (<Sub>[] is List<Super>) {
-    Expect.fail("$Sub is a subtype of $Super");
-  }
-}
-
 main() {
-  expectSubtype<M, A>();
-  expectSubtype<M, B>();
-  expectSubtype<M, I>();
-  expectSubtype<M, J>();
-  expectSubtype<D, M>();
-  expectSubtype<D, C>();
-  expectNotSubtype<M, C>();
-  expectNotSubtype<C, M>();
+  Expect.subtype<M, A>();
+  Expect.subtype<M, B>();
+  Expect.subtype<M, I>();
+  Expect.subtype<M, J>();
+  Expect.subtype<D, M>();
+  Expect.subtype<D, C>();
+  Expect.notSubtype<M, C>();
+  Expect.notSubtype<C, M>();
 
-  expectSubtype<GM<int>, GA<int>>();
-  expectSubtype<GM<int>, GB<int>>();
-  expectSubtype<GM<int>, GI<int>>();
-  expectSubtype<GM<int>, GJ<int>>();
-  expectSubtype<GD<int>, GM<int>>();
-  expectSubtype<GD<int>, GC<int>>();
-  expectNotSubtype<GM<int>, GC<int>>();
-  expectNotSubtype<GC<int>, GM<int>>();
+  Expect.subtype<GM<int>, GA<int>>();
+  Expect.subtype<GM<int>, GB<List<int>>>();
+  Expect.subtype<GM<int>, GI<Iterable<int>>>();
+  Expect.subtype<GM<int>, GJ<Set<int>>>();
+  Expect.subtype<GD<int>, GM<int>>();
+  Expect.subtype<GD<int>, GC<int>>();
+  Expect.notSubtype<GM<int>, GC<int>>();
+  Expect.notSubtype<GC<int>, GM<int>>();
 }
\ No newline at end of file
diff --git a/tests/language_2/mixin_declaration/mixin_declaration_syntax_test.dart b/tests/language_2/mixin_declaration/mixin_declaration_syntax_test.dart
index 67de657..6159af9 100644
--- a/tests/language_2/mixin_declaration/mixin_declaration_syntax_test.dart
+++ b/tests/language_2/mixin_declaration/mixin_declaration_syntax_test.dart
@@ -91,6 +91,14 @@
   String methodC() => "MAiBC:${this}.C";
 }
 
+// Mixin with "implements" clause.
+mixin MBiIJ on B implements I, J {
+  String toString() => "${super.toString()}&MBiIJ";
+  String methodMOiIJ() => "MBiIJ:${this}.MBiIJ";
+  String methodI() => "MBiIJ:${this}.I";
+  String methodJ() => "MBiIJ:${this}.J";
+}
+
 
 // Mixin on more than one class.
 mixin MBC on B, C {
@@ -104,11 +112,11 @@
 
 // One with everything.
 mixin MBCiIJ on B, C implements I, J {
-  String toString() => "${super.toString()}&MBC";
+  String toString() => "${super.toString()}&MBCiIJ";
   String methodMBCiIJ() => "MBCiIJ:${this}.MBCiIJ";
-  String methodA() => "MBC:${this}.A->${super.methodA()}";
-  String methodB() => "MBC:${this}.B->${super.methodB()}";
-  String methodC() => "MBC:${this}.C->${super.methodC()}";
+  String methodA() => "MBCiIJ:${this}.A->${super.methodA()}";
+  String methodB() => "MBCiIJ:${this}.B->${super.methodB()}";
+  String methodC() => "MBCiIJ:${this}.C->${super.methodC()}";
   String methodI() => "MBCiIJ:${this}.I";
   String methodJ() => "MBCiIJ:${this}.J";
 }
@@ -116,7 +124,7 @@
 
 // Abstract mixin, doesn't implement its interface.
 mixin MiIJ implements I, J {
-  String toString = "${super.toString}&MiIJ";
+  String toString() => "${super.toString()}&MiIJ";
 }
 
 
@@ -142,9 +150,9 @@
 
 class COaMOiIJ_2 extends O with MOiIJ {}
 
-class CBaMOiIJ = B with MBiIJ;
+class CBaMBiIJ = B with MBiIJ;
 
-class CBaMOiIJ_2 extends B with MBiIJ {}
+class CBaMBiIJ_2 extends B with MBiIJ {}
 
 class CAaMA = A with MA;
 
@@ -154,13 +162,13 @@
 
 class CBaMA_2 extends B with MA {}
 
-class CAaMAiBC = A with CAaMAiBC;
+class CAaMAiBC = A with MAiBC;
 
-class CAaMAiBC_2 extends A with CAaMAiBC {}
+class CAaMAiBC_2 extends A with MAiBC {}
 
-class CBaMAiBC = B with CAaMAiBC;
+class CBaMAiBC = B with MAiBC;
 
-class CBaMAiBC_2 extends B with CAaMAiBC {}
+class CBaMAiBC_2 extends B with MAiBC {}
 
 class CBCaMBC = BC with MBC;
 
@@ -182,7 +190,17 @@
 abstract class OaMiIJ = O with MiIJ;
 
 // Concrete subclass of abstract mixin appliction
-class COaMiIJ extends OaMIJ {
+class COaMiIJ extends OaMiIJ {
+  String toString() => "${super.toString()}:$COaMiIJ";
+  String methodI() => "COaMiIJ:${this}.I";
+  String methodJ() => "COaMiIJ:${this}.J";
+}
+
+// Abstract class with mixin application and does not implement I and J.
+abstract class OaMiIJ_2 extends O with MiIJ {}
+
+// Concrete subclass of abstract mixin appliction
+class COaMiIJ_2 extends OaMiIJ_2 {
   String toString() => "${super.toString()}:$COaMiIJ";
   String methodI() => "COaMiIJ:${this}.I";
   String methodJ() => "COaMiIJ:${this}.J";
@@ -195,7 +213,7 @@
 // Test that the mixin applications behave as expected.
 void main() {
   {
-    for (var o in [COaM(), COaM_2()]) {
+    for (dynamic o in [COaM(), COaM_2()]) {
       Expect.type<O>(o);
       Expect.type<M>(o);
       Expect.equals("?&M", "$o");
@@ -207,14 +225,14 @@
     for (var o in [CBaM(), CBaM_2()]) {
       Expect.type<B>(o);
       Expect.type<M>(o);
-      Expect.equals("&M", "$o");
+      Expect.equals("?&M", "$o");
       Expect.equals("B:$o.B", o.methodB());
-      Expect.equals("M:$o.M", o.methodM());
+      Expect.equals("M:$o.M", (o as M).methodM());
     }
   }
 
   {
-    for (var o in [COaMO(), COaMO_2()]) {
+    for (dynamic o in [COaMO(), COaMO_2()]) {
       Expect.type<O>(o);
       Expect.type<MO>(o);
       Expect.equals("O&MO", "$o");
@@ -227,13 +245,13 @@
       Expect.type<B>(o);
       Expect.type<MO>(o);
       Expect.equals("B&MO", "$o");
-      Expect.equals("MO:$o.MO", o.methodMO());
+      Expect.equals("MO:$o.MO", (o as MO).methodMO());
       Expect.equals("B:$o.B", o.methodB());
     }
   }
 
   {
-    for (var o in [COaMOiIJ(), COaMOiIJ_2()]) {
+    for (dynamic o in [COaMOiIJ(), COaMOiIJ_2()]) {
       Expect.type<O>(o);
       Expect.type<I>(o);
       Expect.type<J>(o);
@@ -246,21 +264,21 @@
   }
 
   {
-    for (var o in [CBaMOiIJ(), CBaMOiIJ_2()]) {
+    for (dynamic o in [CBaMBiIJ(), CBaMBiIJ_2()]) {
       Expect.type<B>(o);
       Expect.type<I>(o);
       Expect.type<J>(o);
-      Expect.type<MOiIJ>(o);
-      Expect.equals("B&MOiIJ", "$o");
-      Expect.equals("MOiIJ:$o.MOiIJ", o.methodMOiIJ());
+      Expect.type<MBiIJ>(o);
+      Expect.equals("B&MBiIJ", "$o");
+      Expect.equals("MBiIJ:$o.MBiIJ", o.methodMOiIJ());
       Expect.equals("B:$o.B", o.methodB());
-      Expect.equals("MOiIJ:$o.I", o.methodI());
-      Expect.equals("MOiIJ:$o.J", o.methodJ());
+      Expect.equals("MBiIJ:$o.I", o.methodI());
+      Expect.equals("MBiIJ:$o.J", o.methodJ());
     }
   }
 
   {
-    for (var o in [CAaMA(), CAaMA_2()]) {
+    for (dynamic o in [CAaMA(), CAaMA_2()]) {
       Expect.type<A>(o);
       Expect.type<MA>(o);
       Expect.equals("A&MA", "$o");
@@ -273,20 +291,20 @@
     for (var o in [CBaMA(), CBaMA_2()]) {
       Expect.type<B>(o);
       Expect.type<MA>(o);
-      Expect.equals("A&MA", "$o");
-      Expect.equals("MA:$o.MA", o.methodMA());
-      Expect.equals("MA:$o.A->B:$o.A", o.methodA());
+      Expect.equals("B&MA", "$o");
+      Expect.equals("MA:$o.MA", (o as MA).methodMA());
+      Expect.equals("MA:$o.A->B:$o.A", (o as MA).methodA());
       Expect.equals("B:$o.B", o.methodB());
     }
   }
 
   {
-    for (var o in [CAaMAiBC(), CAaMAiBC_2()]) {
+    for (dynamic o in [CAaMAiBC(), CAaMAiBC_2()]) {
       Expect.type<A>(o);
       Expect.type<B>(o);
       Expect.type<C>(o);
       Expect.type<MAiBC>(o);
-      Expect.equals("A&MA", "$o");
+      Expect.equals("A&MAiBC", "$o");
       Expect.equals("MAiBC:$o.MAiBC", o.methodMAiBC());
       Expect.equals("MAiBC:$o.A->A:$o.A", o.methodA());
       Expect.equals("MAiBC:$o.B", o.methodB());
@@ -295,21 +313,21 @@
   }
 
   {
-    for (var o in [CBaMAiBC(), CBaMAiBC_2()]) {
+    for (dynamic o in [CBaMAiBC(), CBaMAiBC_2()]) {
       Expect.type<A>(o);
       Expect.type<B>(o);
       Expect.type<C>(o);
       Expect.type<MAiBC>(o);
-      Expect.equals("B&MA", "$o");
+      Expect.equals("B&MAiBC", "$o");
       Expect.equals("MAiBC:$o.MAiBC", o.methodMAiBC());
-      Expect.equals("MA:$o.A->B:$o.A", o.methodA());
+      Expect.equals("MAiBC:$o.A->B:$o.A", o.methodA());
       Expect.equals("MAiBC:$o.B", o.methodB());
       Expect.equals("MAiBC:$o.C", o.methodC());
     }
   }
 
   {
-    for (var o in [CBCaMBC(), CBCaMBC_2()]) {
+    for (dynamic o in [CBCaMBC(), CBCaMBC_2()]) {
       Expect.type<BC>(o);
       Expect.type<MBC>(o);
       Expect.equals("BC&MBC", "$o");
@@ -322,11 +340,11 @@
 
   {
     // Mixin on top of mixin application.
-    for (var o in [CAaMAiBCaMBC(), CAaMAiBCaMBC_2()]) {
+    for (dynamic o in [CAaMAiBCaMBC(), CAaMAiBCaMBC_2()]) {
       Expect.type<CAaMAiBC>(o);
       Expect.type<MBC>(o);
-      Expect.equals("CA&MAiBC&MBC", "$o");
-      Expect.equals("MBC:$o.MBC", o.methodMBC());
+      Expect.equals("A&MAiBC&MBC", "$o");
+      Expect.equals("MBC:$o.MBC", (o as MBC).methodMBC());
       Expect.equals("MAiBC:$o.MAiBC", o.methodMAiBC());
       Expect.equals("MBC:$o.A->MAiBC:$o.A->$A:$o.A", o.methodA());
       Expect.equals("MBC:$o.B->MAiBC:$o.B", o.methodB());
@@ -335,7 +353,7 @@
   }
 
   {
-    for (var o in [CBCaMBCiIJ(), CBCaMBCiIJ_2()]) {
+    for (dynamic o in [CBCaMBCiIJ(), CBCaMBCiIJ_2()]) {
       Expect.type<BC>(o);
       Expect.type<MBCiIJ>(o);
       Expect.type<I>(o);
@@ -352,14 +370,14 @@
 
   {
     // Mixin on top of mixin application.
-    for (var o in [CAaMAiBCaMBCiIJ(), CAaMAiBCaMBCiIJ_2()]) {
+    for (dynamic o in [CAaMAiBCaMBCiIJ(), CAaMAiBCaMBCiIJ_2()]) {
       Expect.type<CAaMAiBC>(o);
       Expect.type<MBCiIJ>(o);
       Expect.type<I>(o);
       Expect.type<J>(o);
-      Expect.equals("CA&MAiBC&MBCiIJ", "$o");
+      Expect.equals("A&MAiBC&MBCiIJ", "$o");
       Expect.equals("MBCiIJ:$o.MBCiIJ", o.methodMBCiIJ());
-      Expect.equals("MAiBC:$o.MAiBC", o.methodMAiBC());
+      Expect.equals("MAiBC:$o.MAiBC", (o as CAaMAiBC).methodMAiBC());
       Expect.equals("MBCiIJ:$o.A->MAiBC:$o.A->$A:$o.A", o.methodA());
       Expect.equals("MBCiIJ:$o.B->MAiBC:$o.B", o.methodB());
       Expect.equals("MBCiIJ:$o.C->MAiBC:$o.C", o.methodC());
@@ -370,13 +388,14 @@
 
   {
     // Abstract mixin application, concrete subclass.
-    for (var o in [COaMiIJ(), COaMiIJ_2()]) {
+    for (dynamic o in [COaMiIJ(), COaMiIJ_2()]) {
       Expect.type<O>(o);
       Expect.type<MiIJ>(o);
-      Expect.type<OaMiIJ>(o);
+      Expect.isTrue(o is OaMiIJ || o is OaMiIJ_2,
+          "`$o` should subtype OaMiIJ or OaMiIJ_2");
       Expect.type<I>(o);
       Expect.type<J>(o);
-      Expect.equals("O&MiIJ:COaMiIJ");
+      Expect.equals("O&MiIJ:COaMiIJ", "$o");
       Expect.equals("COaMiIJ:$o.I", o.methodI());
       Expect.equals("COaMiIJ:$o.J", o.methodJ());
     }