blob: 7dbba649bd077d450041883cfcf5418e0f8a39dc [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library dart2js.resolution.class_hierarchy;
import '../common.dart';
import '../common/resolution.dart' show Resolution;
import '../common_elements.dart' show CommonElements;
import '../elements/resolution_types.dart';
import '../elements/elements.dart';
import '../elements/modelx.dart'
show
BaseClassElementX,
ErroneousElementX,
MixinApplicationElementX,
SynthesizedConstructorElementX,
TypeVariableElementX,
UnnamedMixinApplicationElementX;
import '../ordered_typeset.dart' show OrderedTypeSet, OrderedTypeSetBuilder;
import '../tree/tree.dart';
import '../universe/call_structure.dart' show CallStructure;
import '../universe/feature.dart' show Feature;
import '../util/util.dart' show Link, Setlet;
import 'enum_creator.dart';
import 'members.dart' show lookupInScope;
import 'registry.dart' show ResolutionRegistry;
import 'resolution_common.dart' show CommonResolverVisitor, MappingVisitor;
import 'scope.dart' show Scope, TypeDeclarationScope;
class TypeDefinitionVisitor extends MappingVisitor<ResolutionDartType> {
Scope scope;
final TypeDeclarationElement enclosingElement;
TypeDeclarationElement get element => enclosingElement;
TypeDefinitionVisitor(Resolution resolution, TypeDeclarationElement element,
ResolutionRegistry registry)
: this.enclosingElement = element,
scope = Scope.buildEnclosingScope(element),
super(resolution, registry);
CommonElements get commonElements => resolution.commonElements;
ResolutionInterfaceType get objectType => commonElements.objectType;
void resolveTypeVariableBounds(NodeList node) {
if (node == null) return;
Setlet<String> nameSet = new Setlet<String>();
// Resolve the bounds of type variables.
Iterator<ResolutionDartType> types = element.typeVariables.iterator;
Link<Node> nodeLink = node.nodes;
while (!nodeLink.isEmpty) {
types.moveNext();
ResolutionTypeVariableType typeVariable = types.current;
String typeName = typeVariable.name;
TypeVariable typeNode = nodeLink.head;
registry.useType(typeNode, typeVariable);
if (nameSet.contains(typeName)) {
reporter.reportErrorMessage(
typeNode,
MessageKind.DUPLICATE_TYPE_VARIABLE_NAME,
{'typeVariableName': typeName});
}
nameSet.add(typeName);
TypeVariableElementX variableElement = typeVariable.element;
if (typeNode.bound != null) {
ResolutionDartType boundType = typeResolver
.resolveNominalTypeAnnotation(this, typeNode.bound, const []);
variableElement.boundCache = boundType;
void checkTypeVariableBound() {
Link<TypeVariableElement> seenTypeVariables =
const Link<TypeVariableElement>();
seenTypeVariables = seenTypeVariables.prepend(variableElement);
ResolutionDartType bound = boundType;
while (bound.isTypeVariable) {
TypeVariableElement element = bound.element;
if (seenTypeVariables.contains(element)) {
if (identical(element, variableElement)) {
// Only report an error on the checked type variable to avoid
// generating multiple errors for the same cyclicity.
reporter.reportWarningMessage(
typeNode.name,
MessageKind.CYCLIC_TYPE_VARIABLE,
{'typeVariableName': variableElement.name});
}
break;
}
seenTypeVariables = seenTypeVariables.prepend(element);
bound = element.bound;
}
}
addDeferredAction(element, checkTypeVariableBound);
} else {
variableElement.boundCache = objectType;
}
nodeLink = nodeLink.tail;
}
assert(!types.moveNext());
}
}
/**
* The implementation of [ResolverTask.resolveClass].
*
* This visitor has to be extra careful as it is building the basic
* element information, and cannot safely look at other elements as
* this may lead to cycles.
*
* This visitor can assume that the supertypes have already been
* resolved, but it cannot call [ResolverTask.resolveClass] directly
* or indirectly (through [ClassElement.ensureResolved]) for any other
* types.
*/
class ClassResolverVisitor extends TypeDefinitionVisitor {
BaseClassElementX get element => enclosingElement;
ClassResolverVisitor(Resolution resolution, ClassElement classElement,
ResolutionRegistry registry)
: super(resolution, classElement, registry);
ResolutionDartType visitClassNode(ClassNode node) {
if (element == null) {
throw reporter.internalError(node, 'element is null');
}
if (element.resolutionState != STATE_STARTED) {
throw reporter.internalError(
element, 'cyclic resolution of class $element');
}
element.computeType(resolution);
scope = new TypeDeclarationScope(scope, element);
// TODO(ahe): It is not safe to call resolveTypeVariableBounds yet.
// As a side-effect, this may get us back here trying to
// resolve this class again.
resolveTypeVariableBounds(node.typeParameters);
// Setup the supertype for the element (if there is a cycle in the
// class hierarchy, it has already been set to Object).
if (element.supertype == null && node.superclass != null) {
MixinApplication superMixin = node.superclass.asMixinApplication();
if (superMixin != null) {
ResolutionDartType supertype =
resolveSupertype(element, superMixin.superclass);
Link<Node> link = superMixin.mixins.nodes;
while (!link.isEmpty) {
supertype =
applyMixin(supertype, checkMixinType(link.head), link.head);
link = link.tail;
}
element.supertype = supertype;
} else {
element.supertype = resolveSupertype(element, node.superclass);
}
}
// If the super type isn't specified, we provide a default. The language
// specifies [Object] but the backend can pick a specific 'implementation'
// of Object - the JavaScript backend chooses between Object and
// Interceptor.
if (element.supertype == null) {
ClassElement superElement = registry.defaultSuperclass(element);
// Avoid making the superclass (usually Object) extend itself.
if (element != superElement) {
if (superElement == null) {
reporter.internalError(
node, "Cannot resolve default superclass for $element.");
} else {
superElement.ensureResolved(resolution);
}
element.supertype = superElement.computeType(resolution);
}
}
if (element.interfaces == null) {
element.interfaces = resolveInterfaces(node.interfaces, node.superclass);
} else {
assert(invariant(element, element.hasIncompleteHierarchy));
}
calculateAllSupertypes(element);
if (!element.hasConstructor) {
Element superMember = element.superclass.localLookup('');
if (superMember == null) {
MessageKind kind = MessageKind.CANNOT_FIND_UNNAMED_CONSTRUCTOR;
Map arguments = {'className': element.superclass.name};
// TODO(ahe): Why is this a compile-time error? Or if it is an error,
// why do we bother to registerThrowNoSuchMethod below?
reporter.reportErrorMessage(node, kind, arguments);
superMember = new ErroneousElementX(kind, arguments, '', element);
registry.registerFeature(Feature.THROW_NO_SUCH_METHOD);
} else if (!superMember.isGenerativeConstructor) {
MessageKind kind = MessageKind.SUPER_CALL_TO_FACTORY;
Map arguments = {'className': element.superclass.name};
// TODO(ahe): Why is this a compile-time error? Or if it is an error,
// why do we bother to registerThrowNoSuchMethod below?
reporter.reportErrorMessage(node, kind, arguments);
superMember = new ErroneousElementX(kind, arguments, '', element);
registry.registerFeature(Feature.THROW_NO_SUCH_METHOD);
} else {
ConstructorElement superConstructor = superMember;
superConstructor.computeType(resolution);
if (!CallStructure.NO_ARGS.signatureApplies(superConstructor.type)) {
MessageKind kind = MessageKind.NO_MATCHING_CONSTRUCTOR_FOR_IMPLICIT;
reporter.reportErrorMessage(node, kind);
superMember = new ErroneousElementX(kind, {}, '', element);
}
}
FunctionElement constructor =
new SynthesizedConstructorElementX.forDefault(superMember, element);
if (superMember.isMalformed) {
ErroneousElement erroneousElement = superMember;
resolution.registerCompileTimeError(
constructor,
reporter.createMessage(node, erroneousElement.messageKind,
erroneousElement.messageArguments));
}
element.setDefaultConstructor(constructor, reporter);
}
return element.computeType(resolution);
}
@override
ResolutionDartType visitEnum(Enum node) {
if (element == null) {
throw reporter.internalError(node, 'element is null');
}
if (element.resolutionState != STATE_STARTED) {
throw reporter.internalError(
element, 'cyclic resolution of class $element');
}
ResolutionInterfaceType enumType = element.computeType(resolution);
element.supertype = objectType;
element.interfaces = const Link<ResolutionDartType>();
calculateAllSupertypes(element);
if (node.names.nodes.isEmpty) {
reporter.reportErrorMessage(
node, MessageKind.EMPTY_ENUM_DECLARATION, {'enumName': element.name});
}
EnumCreator creator =
new EnumCreator(reporter, resolution.commonElements, element);
creator.createMembers();
return enumType;
}
/// Resolves the mixed type for [mixinNode] and checks that the mixin type
/// is a valid, non-blacklisted interface type. The mixin type is returned.
ResolutionDartType checkMixinType(NominalTypeAnnotation mixinNode) {
ResolutionDartType mixinType = resolveNominalType(mixinNode);
if (isBlackListed(mixinType)) {
reporter.reportErrorMessage(
mixinNode, MessageKind.CANNOT_MIXIN, {'type': mixinType});
} else if (mixinType.isTypeVariable) {
reporter.reportErrorMessage(mixinNode, MessageKind.CLASS_NAME_EXPECTED);
} else if (mixinType.isMalformed) {
reporter.reportErrorMessage(mixinNode, MessageKind.CANNOT_MIXIN_MALFORMED,
{'className': element.name, 'malformedType': mixinType});
} else if (mixinType.isEnumType) {
reporter.reportErrorMessage(mixinNode, MessageKind.CANNOT_MIXIN_ENUM,
{'className': element.name, 'enumType': mixinType});
}
return mixinType;
}
ResolutionDartType visitNamedMixinApplication(NamedMixinApplication node) {
if (element == null) {
throw reporter.internalError(node, 'element is null');
}
if (element.resolutionState != STATE_STARTED) {
throw reporter.internalError(
element, 'cyclic resolution of class $element');
}
element.computeType(resolution);
scope = new TypeDeclarationScope(scope, element);
resolveTypeVariableBounds(node.typeParameters);
// Generate anonymous mixin application elements for the
// intermediate mixin applications (excluding the last).
ResolutionDartType supertype = resolveSupertype(element, node.superclass);
Link<Node> link = node.mixins.nodes;
while (!link.tail.isEmpty) {
supertype = applyMixin(supertype, checkMixinType(link.head), link.head);
link = link.tail;
}
doApplyMixinTo(element, supertype, checkMixinType(link.head));
return element.computeType(resolution);
}
ResolutionDartType applyMixin(
ResolutionDartType supertype, ResolutionDartType mixinType, Node node) {
String superName = supertype.name;
String mixinName = mixinType.name;
MixinApplicationElementX mixinApplication =
new UnnamedMixinApplicationElementX("${superName}+${mixinName}",
element, resolution.idGenerator.getNextFreeId(), node);
// Create synthetic type variables for the mixin application.
List<ResolutionDartType> typeVariables = <ResolutionDartType>[];
int index = 0;
for (ResolutionTypeVariableType type in element.typeVariables) {
TypeVariableElementX typeVariableElement = new TypeVariableElementX(
type.name, mixinApplication, index, type.element.node);
ResolutionTypeVariableType typeVariable =
new ResolutionTypeVariableType(typeVariableElement);
typeVariables.add(typeVariable);
index++;
}
// Setup bounds on the synthetic type variables.
for (ResolutionTypeVariableType type in element.typeVariables) {
ResolutionTypeVariableType typeVariable =
typeVariables[type.element.index];
TypeVariableElementX typeVariableElement = typeVariable.element;
typeVariableElement.typeCache = typeVariable;
typeVariableElement.boundCache =
type.element.bound.subst(typeVariables, element.typeVariables);
}
// Setup this and raw type for the mixin application.
mixinApplication.computeThisAndRawType(resolution, typeVariables);
// Substitute in synthetic type variables in super and mixin types.
supertype = supertype.subst(typeVariables, element.typeVariables);
mixinType = mixinType.subst(typeVariables, element.typeVariables);
doApplyMixinTo(mixinApplication, supertype, mixinType);
mixinApplication.resolutionState = STATE_DONE;
mixinApplication.supertypeLoadState = STATE_DONE;
// Replace the synthetic type variables by the original type variables in
// the returned type (which should be the type actually extended).
ResolutionInterfaceType mixinThisType = mixinApplication.thisType;
return mixinThisType.subst(
element.typeVariables, mixinThisType.typeArguments);
}
bool isDefaultConstructor(FunctionElement constructor) {
if (constructor.name != '') return false;
constructor.computeType(resolution);
return constructor.functionSignature.parameterCount == 0;
}
FunctionElement createForwardingConstructor(
ConstructorElement target, ClassElement enclosing) {
FunctionElement constructor =
new SynthesizedConstructorElementX.notForDefault(
target.name, target, enclosing);
constructor.computeType(resolution);
return constructor;
}
void doApplyMixinTo(MixinApplicationElementX mixinApplication,
ResolutionDartType supertype, ResolutionDartType mixinType) {
Node node = mixinApplication.parseNode(resolution.parsingContext);
if (mixinApplication.supertype != null) {
// [supertype] is not null if there was a cycle.
assert(invariant(node, reporter.hasReportedError));
supertype = mixinApplication.supertype;
assert(invariant(node, supertype.isObject));
} else {
mixinApplication.supertype = supertype;
}
// Named mixin application may have an 'implements' clause.
NamedMixinApplication namedMixinApplication =
node.asNamedMixinApplication();
Link<ResolutionDartType> interfaces = (namedMixinApplication != null)
? resolveInterfaces(
namedMixinApplication.interfaces, namedMixinApplication.superclass)
: const Link<ResolutionDartType>();
// The class that is the result of a mixin application implements
// the interface of the class that was mixed in so always prepend
// that to the interface list.
if (mixinApplication.interfaces == null) {
if (mixinType.isInterfaceType) {
// Avoid malformed types in the interfaces.
interfaces = interfaces.prepend(mixinType);
}
mixinApplication.interfaces = interfaces;
} else {
assert(
invariant(mixinApplication, mixinApplication.hasIncompleteHierarchy));
}
ClassElement superclass = supertype.element;
if (mixinType.kind != ResolutionTypeKind.INTERFACE) {
mixinApplication.hasIncompleteHierarchy = true;
mixinApplication.allSupertypesAndSelf = superclass.allSupertypesAndSelf;
return;
}
assert(mixinApplication.mixinType == null);
mixinApplication.mixinType = resolveMixinFor(mixinApplication, mixinType);
// Create forwarding constructors for constructor defined in the superclass
// because they are now hidden by the mixin application.
superclass.forEachLocalMember((Element member) {
if (!member.isGenerativeConstructor) return;
FunctionElement forwarder =
createForwardingConstructor(member, mixinApplication);
if (Name.isPrivateName(member.name) &&
mixinApplication.library != superclass.library) {
// Do not create a forwarder to the super constructor, because the mixin
// application is in a different library than the constructor in the
// super class and it is not possible to call that constructor from the
// library using the mixin application.
return;
}
mixinApplication.addConstructor(forwarder);
});
calculateAllSupertypes(mixinApplication);
}
ResolutionInterfaceType resolveMixinFor(
MixinApplicationElement mixinApplication, ResolutionDartType mixinType) {
ClassElement mixin = mixinType.element;
mixin.ensureResolved(resolution);
// Check for cycles in the mixin chain.
ClassElement previous = mixinApplication; // For better error messages.
ClassElement current = mixin;
while (current != null && current.isMixinApplication) {
MixinApplicationElement currentMixinApplication = current;
if (currentMixinApplication == mixinApplication) {
reporter.reportErrorMessage(
mixinApplication,
MessageKind.ILLEGAL_MIXIN_CYCLE,
{'mixinName1': current.name, 'mixinName2': previous.name});
// We have found a cycle in the mixin chain. Return null as
// the mixin for this application to avoid getting into
// infinite recursion when traversing members.
return null;
}
previous = current;
current = currentMixinApplication.mixin;
}
return mixinType;
}
ResolutionDartType resolveNominalType(NominalTypeAnnotation node) {
return typeResolver.resolveNominalTypeAnnotation(this, node, const []);
}
ResolutionDartType resolveSupertype(
ClassElement cls, NominalTypeAnnotation superclass) {
ResolutionDartType supertype = resolveNominalType(superclass);
if (supertype != null) {
if (supertype.isMalformed) {
reporter.reportErrorMessage(
superclass,
MessageKind.CANNOT_EXTEND_MALFORMED,
{'className': element.name, 'malformedType': supertype});
return objectType;
} else if (supertype.isEnumType) {
reporter.reportErrorMessage(superclass, MessageKind.CANNOT_EXTEND_ENUM,
{'className': element.name, 'enumType': supertype});
return objectType;
} else if (!supertype.isInterfaceType) {
reporter.reportErrorMessage(
superclass.typeName, MessageKind.CLASS_NAME_EXPECTED);
return objectType;
} else if (isBlackListed(supertype)) {
reporter.reportErrorMessage(
superclass, MessageKind.CANNOT_EXTEND, {'type': supertype});
return objectType;
}
}
return supertype;
}
Link<ResolutionDartType> resolveInterfaces(
NodeList interfaces, Node superclass) {
Link<ResolutionDartType> result = const Link<ResolutionDartType>();
if (interfaces == null) return result;
for (Link<Node> link = interfaces.nodes; !link.isEmpty; link = link.tail) {
ResolutionDartType interfaceType = resolveNominalType(link.head);
if (interfaceType != null) {
if (interfaceType.isMalformed) {
reporter.reportErrorMessage(
link.head,
MessageKind.CANNOT_IMPLEMENT_MALFORMED,
{'className': element.name, 'malformedType': interfaceType});
} else if (interfaceType.isEnumType) {
reporter.reportErrorMessage(
link.head,
MessageKind.CANNOT_IMPLEMENT_ENUM,
{'className': element.name, 'enumType': interfaceType});
} else if (!interfaceType.isInterfaceType) {
// TODO(johnniwinther): Handle dynamic.
NominalTypeAnnotation typeAnnotation = link.head;
reporter.reportErrorMessage(
typeAnnotation.typeName, MessageKind.CLASS_NAME_EXPECTED);
} else {
if (interfaceType == element.supertype) {
reporter.reportErrorMessage(
superclass,
MessageKind.DUPLICATE_EXTENDS_IMPLEMENTS,
{'type': interfaceType});
reporter.reportErrorMessage(
link.head,
MessageKind.DUPLICATE_EXTENDS_IMPLEMENTS,
{'type': interfaceType});
}
if (result.contains(interfaceType)) {
reporter.reportErrorMessage(link.head,
MessageKind.DUPLICATE_IMPLEMENTS, {'type': interfaceType});
}
result = result.prepend(interfaceType);
if (isBlackListed(interfaceType)) {
reporter.reportErrorMessage(link.head, MessageKind.CANNOT_IMPLEMENT,
{'type': interfaceType});
}
}
}
}
return result;
}
/**
* Compute the list of all supertypes.
*
* The elements of this list are ordered as follows: first the supertype that
* the class extends, then the implemented interfaces, and then the supertypes
* of these. The class [Object] appears only once, at the end of the list.
*
* For example, for a class `class C extends S implements I1, I2`, we compute
* supertypes(C) = [S, I1, I2] ++ supertypes(S) ++ supertypes(I1)
* ++ supertypes(I2),
* where ++ stands for list concatenation.
*
* This order makes sure that if a class implements an interface twice with
* different type arguments, the type used in the most specific class comes
* first.
*/
void calculateAllSupertypes(BaseClassElementX cls) {
if (cls.allSupertypesAndSelf != null) return;
final ResolutionDartType supertype = cls.supertype;
if (supertype != null) {
cls.allSupertypesAndSelf = new OrderedTypeSetBuilder(cls,
reporter: reporter, objectType: commonElements.objectType)
.createOrderedTypeSet(supertype, cls.interfaces);
} else {
assert(cls == resolution.commonElements.objectClass);
cls.allSupertypesAndSelf =
new OrderedTypeSet.singleton(cls.computeType(resolution));
}
}
isBlackListed(ResolutionDartType type) {
LibraryElement lib = element.library;
return !identical(lib, resolution.commonElements.coreLibrary) &&
!resolution.target.isTargetSpecificLibrary(lib) &&
(type.isDynamic ||
type == commonElements.boolType ||
type == commonElements.numType ||
type == commonElements.intType ||
type == commonElements.doubleType ||
type == commonElements.stringType ||
type == commonElements.nullType);
}
}
class ClassSupertypeResolver extends CommonResolverVisitor {
Scope context;
ClassElement classElement;
ClassSupertypeResolver(Resolution resolution, ClassElement cls)
: context = Scope.buildEnclosingScope(cls),
this.classElement = cls,
super(resolution);
CommonElements get commonElements => resolution.commonElements;
void loadSupertype(ClassElement element, Node from) {
if (!element.isResolved) {
resolution.resolver.loadSupertypes(element, from);
element.ensureResolved(resolution);
}
}
void visitNodeList(NodeList node) {
if (node != null) {
for (Link<Node> link = node.nodes; !link.isEmpty; link = link.tail) {
link.head.accept(this);
}
}
}
void visitClassNode(ClassNode node) {
if (node.superclass == null) {
if (classElement != commonElements.objectClass) {
loadSupertype(commonElements.objectClass, node);
}
} else {
node.superclass.accept(this);
}
visitNodeList(node.interfaces);
}
void visitEnum(Enum node) {
loadSupertype(commonElements.objectClass, node);
}
void visitMixinApplication(MixinApplication node) {
node.superclass.accept(this);
visitNodeList(node.mixins);
}
void visitNamedMixinApplication(NamedMixinApplication node) {
node.superclass.accept(this);
visitNodeList(node.mixins);
visitNodeList(node.interfaces);
}
void visitNominalTypeAnnotation(NominalTypeAnnotation node) {
node.typeName.accept(this);
}
void visitIdentifier(Identifier node) {
Element element = lookupInScope(reporter, node, context, node.source);
if (element != null && element.isClass) {
loadSupertype(element, node);
}
}
void visitSend(Send node) {
Identifier prefix = node.receiver.asIdentifier();
if (prefix == null) {
reporter.reportErrorMessage(
node.receiver, MessageKind.NOT_A_PREFIX, {'node': node.receiver});
return;
}
Element element = lookupInScope(reporter, prefix, context, prefix.source);
if (element == null || !identical(element.kind, ElementKind.PREFIX)) {
reporter.reportErrorMessage(
node.receiver, MessageKind.NOT_A_PREFIX, {'node': node.receiver});
return;
}
PrefixElement prefixElement = element;
Identifier selector = node.selector.asIdentifier();
var e = prefixElement.lookupLocalMember(selector.source);
if (e == null || !e.impliesType) {
reporter.reportErrorMessage(node.selector,
MessageKind.CANNOT_RESOLVE_TYPE, {'typeName': node.selector});
return;
}
loadSupertype(e, node);
}
}