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>
This commit is contained in:
Jenny Messerly 2018-09-18 03:21:48 +00:00
parent 0471d7e3a1
commit 0496569f09
24 changed files with 362 additions and 143 deletions

View file

@ -626,8 +626,8 @@ class ClassElementImpl extends AbstractClassElementImpl
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 @@ class ClassElementImpl extends AbstractClassElementImpl
}
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 @@ class ClassElementImpl extends AbstractClassElementImpl
// 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) {

View file

@ -1339,7 +1339,7 @@ class InterfaceTypeImpl extends TypeImpl implements InterfaceType {
}
@override
bool get isObject => element.supertype == null;
bool get isObject => element.supertype == null && !element.isMixin;
@override
List<MethodElement> get methods {
@ -1495,10 +1495,9 @@ class InterfaceTypeImpl extends TypeImpl implements InterfaceType {
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 @@ class InterfaceTypeImpl extends TypeImpl implements InterfaceType {
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 @@ class InterfaceTypeImpl extends TypeImpl implements InterfaceType {
// 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);

View file

@ -283,7 +283,7 @@ class InheritanceManager {
}
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 @@ class InheritanceManager {
// 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 @@ class InheritanceManager {
}
}
// 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;
}

View file

@ -719,8 +719,9 @@ class ElementResolver extends SimpleAstVisitor<Object> {
// 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;
}

View file

@ -1910,7 +1910,9 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
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 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
problemReported = true;
}
if (mixinElement.isMixin) {
if (_checkForMixinSuperclassConstraints(mixinName)) {
if (_checkForMixinSuperclassConstraints(
mixinName, mixinTypes.take(i))) {
problemReported = true;
}
if (_checkForMixinSuperInvokedMembers(mixinName, mixinElement)) {
@ -4256,12 +4259,14 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
}
/// 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,

View file

@ -1582,7 +1582,9 @@ class StrongTypeSystemImpl extends TypeSystem {
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 @@ class StrongTypeSystemImpl extends TypeSystem {
}
}
if (i1Element.isMixin) {
for (final parent in i1.superclassConstraints) {
if (_isInterfaceSubtypeOf(parent, i2, visitedTypes)) {
return true;
}
}
}
return false;
}

View file

@ -971,7 +971,9 @@ class CodeGenerator extends Object
_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 @@ class CodeGenerator extends Object
@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 @@ class CodeGenerator extends Object
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 @@ class CodeGenerator extends Object
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 @@ class CodeGenerator extends Object
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 @@ class CodeGenerator extends Object
var classExpr = deferMixin ? getBaseClass(0) : className;
mixinBody
.add(runtimeStatement('mixinMembers(#, #)', [classExpr, mixinClass]));
.add(runtimeStatement('applyMixin(#, #)', [classExpr, mixinClass]));
_topLevelClass = savedTopLevel;
@ -1395,8 +1463,8 @@ class CodeGenerator extends Object
//
// 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 @@ class CodeGenerator extends Object
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 @@ class CodeGenerator extends Object
_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 @@ class CodeGenerator extends Object
/// 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 @@ class CodeGenerator extends Object
// 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 @@ class CodeGenerator extends Object
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) {

View file

@ -139,6 +139,7 @@ List<ClassElement> getSuperclasses(ClassElement cls) {
if (mixin != null) result.add(mixin);
}
var supertype = cls.supertype;
// Object or mixin declaration.
if (supertype == null) break;
cls = supertype.element;

View file

@ -103,7 +103,8 @@ class ExtensionTypeSet {
}
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) {

View file

@ -264,7 +264,7 @@ class ClassPropertyModel {
// 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 @@ class ClassPropertyModel {
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) {

View file

@ -621,8 +621,15 @@ class MiniJsParser {
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;

View file

@ -559,7 +559,9 @@ class ProgramCompiler extends Object
_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 @@ class ProgramCompiler extends Object
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 @@ class ProgramCompiler extends Object
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 @@ class ProgramCompiler extends Object
//
// 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 @@ class ProgramCompiler extends Object
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;
}

View file

@ -234,3 +234,6 @@ Class getSuperclassAndMixins(Class c, List<Class> mixins) {
}
return sc;
}
// TODO(jmesserly): replace with a flag on Class once Kernel supports it.
bool isMixinDeclaration(Class c) => false;

View file

@ -12,6 +12,8 @@ import 'package:kernel/target/targets.dart';
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 [

View file

@ -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 @@ void mixinMembers(to, from) {
_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 @@ final _mixin = JS('', 'Symbol("mixin")');
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")');

View file

@ -620,6 +620,30 @@ class Expect {
"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'";

View file

@ -233,7 +233,6 @@ void/*: Skip # https://github.com/dart-lang/sdk/issues/34013
[ $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

View file

@ -72,10 +72,9 @@ issue31596_super_test/none: CompileTimeError # Issue #31596
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

View file

@ -104,6 +104,9 @@ issue31596_test: CompileTimeError
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 @@ issue31596_super_test/05: RuntimeError
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

View file

@ -89,11 +89,10 @@ mixin_declaration/mixin_declaration_invalid_application_supertype_test/03: Missi
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/03: Missi
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/03: Missi
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

View file

@ -41,7 +41,7 @@ mixin M2 on UnaryNum, UnaryInt {
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
}

View file

@ -13,11 +13,11 @@ abstract class Foo {
}
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 {

View file

@ -30,34 +30,22 @@ class GC<T> implements GA<T>, GB<List<T>> {}
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>>();
}

View file

@ -91,6 +91,14 @@ mixin MAiBC on A implements B, C {
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 @@ mixin MBC on B, C {
// 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 @@ mixin MBCiIJ on B, C implements I, J {
// 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 = O with MOiIJ;
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 = B with MA;
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 @@ class CAaMAiBCaMBCiIJ_2 extends CAaMAiBC with MBCiIJ {}
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 @@ class CeOwithM extends Object with M {}
// 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 @@ void main() {
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 @@ void main() {
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 @@ void main() {
}
{
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 @@ void main() {
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 @@ void main() {
}
{
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 @@ void main() {
{
// 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 @@ void main() {
}
{
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 @@ void main() {
{
// 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 @@ void main() {
{
// 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());
}