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..0f80220d 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());
}