blob: 318e7488f429e86e8c8693d209a82407658f3935 [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.types;
import '../common.dart';
import '../common/resolution.dart' show Resolution;
import '../elements/resolution_types.dart';
import '../elements/elements.dart'
show
AmbiguousElement,
ClassElement,
Element,
Elements,
ErroneousElement,
PrefixElement,
TypedefElement,
TypeVariableElement;
import '../elements/modelx.dart' show ErroneousElementX;
import '../resolution/resolution.dart';
import '../tree/tree.dart';
import '../universe/feature.dart' show Feature;
import '../util/util.dart' show Link;
import 'members.dart' show lookupInScope;
import 'registry.dart' show ResolutionRegistry;
import 'resolution_common.dart' show MappingVisitor;
import 'scope.dart' show Scope;
class TypeResolver {
final Resolution resolution;
TypeResolver(this.resolution);
ResolverTask get resolver => resolution.resolver;
DiagnosticReporter get reporter => resolution.reporter;
Types get types => resolution.types;
/// Tries to resolve the type name as an element.
Element resolveTypeName(
Identifier prefixName, Identifier typeName, Scope scope,
{bool deferredIsMalformed: true}) {
Element element;
if (prefixName != null) {
Element prefixElement =
lookupInScope(reporter, prefixName, scope, prefixName.source);
if (prefixElement != null && prefixElement.isPrefix) {
// The receiver is a prefix. Lookup in the imported members.
PrefixElement prefix = prefixElement;
element = prefix.lookupLocalMember(typeName.source);
if (element != null && prefix.isDeferred && deferredIsMalformed) {
element = new ErroneousElementX(MessageKind.DEFERRED_TYPE_ANNOTATION,
{'node': typeName}, element.name, element);
}
} else {
// The caller of this method will create the ErroneousElement for
// the MalformedType.
element = null;
}
} else {
element = lookupInScope(reporter, typeName, scope, typeName.source);
}
return element;
}
ResolutionDartType resolveTypeAnnotation(
MappingVisitor visitor, TypeAnnotation node,
{bool malformedIsError: false, bool deferredIsMalformed: true}) {
ResolutionRegistry registry = visitor.registry;
Identifier typeName;
ResolutionDartType type;
ResolutionDartType checkNoTypeArguments(ResolutionDartType type) {
List<ResolutionDartType> arguments = new List<ResolutionDartType>();
bool hasTypeArgumentMismatch = resolveTypeArguments(
visitor, node, const <ResolutionDartType>[], arguments);
if (hasTypeArgumentMismatch) {
return new MalformedType(
new ErroneousElementX(MessageKind.TYPE_ARGUMENT_COUNT_MISMATCH,
{'type': node}, typeName.source, visitor.enclosingElement),
type,
arguments);
}
return type;
}
Identifier prefixName;
Send send = node.typeName.asSend();
if (send != null) {
// The type name is of the form [: prefix . identifier :].
prefixName = send.receiver.asIdentifier();
typeName = send.selector.asIdentifier();
} else {
typeName = node.typeName.asIdentifier();
if (identical(typeName.source, 'void')) {
type = const ResolutionVoidType();
checkNoTypeArguments(type);
registry.useType(node, type);
return type;
} else if (identical(typeName.source, 'dynamic')) {
type = const ResolutionDynamicType();
checkNoTypeArguments(type);
registry.useType(node, type);
return type;
}
}
Element element = resolveTypeName(prefixName, typeName, visitor.scope,
deferredIsMalformed: deferredIsMalformed);
ResolutionDartType reportFailureAndCreateType(
MessageKind messageKind, Map messageArguments,
{ResolutionDartType userProvidedBadType,
Element erroneousElement,
List<DiagnosticMessage> infos: const <DiagnosticMessage>[]}) {
if (malformedIsError) {
reporter.reportError(
reporter.createMessage(node, messageKind, messageArguments), infos);
} else {
registry.registerFeature(Feature.THROW_RUNTIME_ERROR);
reporter.reportWarning(
reporter.createMessage(node, messageKind, messageArguments), infos);
}
if (erroneousElement == null) {
registry.registerFeature(Feature.THROW_RUNTIME_ERROR);
erroneousElement = new ErroneousElementX(messageKind, messageArguments,
typeName.source, visitor.enclosingElement);
}
List<ResolutionDartType> arguments = <ResolutionDartType>[];
resolveTypeArguments(
visitor, node, const <ResolutionDartType>[], arguments);
return new MalformedType(
erroneousElement, userProvidedBadType, arguments);
}
// Try to construct the type from the element.
if (element == null) {
type = reportFailureAndCreateType(
MessageKind.CANNOT_RESOLVE_TYPE, {'typeName': node.typeName});
} else if (element.isAmbiguous) {
AmbiguousElement ambiguous = element;
type = reportFailureAndCreateType(
ambiguous.messageKind, ambiguous.messageArguments,
infos: ambiguous.computeInfos(
registry.mapping.analyzedElement, reporter));
;
} else if (element.isMalformed) {
if (element is ErroneousElement) {
type = reportFailureAndCreateType(
element.messageKind, element.messageArguments,
erroneousElement: element);
} else {
type = const ResolutionDynamicType();
}
} else if (!element.impliesType) {
type = reportFailureAndCreateType(
MessageKind.NOT_A_TYPE, {'node': node.typeName});
} else if (element.library.isPlatformLibrary &&
element.name == 'FutureOr') {
type = const ResolutionDynamicType();
registry.useType(node, type);
return type;
} else {
bool addTypeVariableBoundsCheck = false;
if (element.isClass) {
ClassElement cls = element;
// TODO(johnniwinther): [ensureClassWillBeResolvedInternal] should imply
// [computeType].
resolver.ensureClassWillBeResolvedInternal(cls);
cls.computeType(resolution);
List<ResolutionDartType> arguments = <ResolutionDartType>[];
bool hasTypeArgumentMismatch =
resolveTypeArguments(visitor, node, cls.typeVariables, arguments);
if (hasTypeArgumentMismatch) {
type = new BadInterfaceType(
cls.declaration,
new ResolutionInterfaceType.forUserProvidedBadType(
cls.declaration, arguments));
} else {
if (arguments.isEmpty) {
type = cls.rawType;
} else {
type = new ResolutionInterfaceType(
cls.declaration, arguments.toList(growable: false));
addTypeVariableBoundsCheck =
arguments.any((ResolutionDartType type) => !type.isDynamic);
}
}
} else if (element.isTypedef) {
TypedefElement typdef = element;
// TODO(johnniwinther): [ensureResolved] should imply [computeType].
typdef.ensureResolved(resolution);
typdef.computeType(resolution);
List<ResolutionDartType> arguments = <ResolutionDartType>[];
bool hasTypeArgumentMismatch = resolveTypeArguments(
visitor, node, typdef.typeVariables, arguments);
if (hasTypeArgumentMismatch) {
type = new BadTypedefType(
typdef,
new ResolutionTypedefType.forUserProvidedBadType(
typdef, arguments));
} else {
if (arguments.isEmpty) {
type = typdef.rawType;
} else {
type = new ResolutionTypedefType(
typdef, arguments.toList(growable: false));
addTypeVariableBoundsCheck =
arguments.any((ResolutionDartType type) => !type.isDynamic);
}
}
} else if (element.isTypeVariable) {
TypeVariableElement typeVariable = element;
Element outer =
visitor.enclosingElement.outermostEnclosingMemberOrTopLevel;
if (!outer.isClass &&
!outer.isTypedef &&
!Elements.hasAccessToTypeVariable(
visitor.enclosingElement, typeVariable)) {
registry.registerFeature(Feature.THROW_RUNTIME_ERROR);
type = reportFailureAndCreateType(
MessageKind.TYPE_VARIABLE_WITHIN_STATIC_MEMBER,
{'typeVariableName': node},
userProvidedBadType: typeVariable.type);
} else {
type = typeVariable.type;
}
type = checkNoTypeArguments(type);
} else {
reporter.internalError(
node, "Unexpected element kind ${element.kind}.");
}
if (addTypeVariableBoundsCheck) {
visitor.addDeferredAction(visitor.enclosingElement,
() => checkTypeVariableBounds(node, type));
}
}
registry.useType(node, type);
return type;
}
/// Checks the type arguments of [type] against the type variable bounds.
void checkTypeVariableBounds(TypeAnnotation node, GenericType type) {
void checkTypeVariableBound(_, ResolutionDartType typeArgument,
ResolutionTypeVariableType typeVariable, ResolutionDartType bound) {
if (!types.isSubtype(typeArgument, bound)) {
reporter.reportWarningMessage(
node, MessageKind.INVALID_TYPE_VARIABLE_BOUND, {
'typeVariable': typeVariable,
'bound': bound,
'typeArgument': typeArgument,
'thisType': type.element.thisType
});
}
}
types.checkTypeVariableBounds(type, checkTypeVariableBound);
}
/**
* Resolves the type arguments of [node] and adds these to [arguments].
*
* Returns [: true :] if the number of type arguments did not match the
* number of type variables.
*/
bool resolveTypeArguments(
MappingVisitor visitor,
TypeAnnotation node,
List<ResolutionDartType> typeVariables,
List<ResolutionDartType> arguments) {
if (node.typeArguments == null) {
return false;
}
int expectedVariables = typeVariables.length;
int index = 0;
bool typeArgumentCountMismatch = false;
for (Link<Node> typeArguments = node.typeArguments.nodes;
!typeArguments.isEmpty;
typeArguments = typeArguments.tail, index++) {
if (index > expectedVariables - 1) {
reporter.reportWarningMessage(
typeArguments.head, MessageKind.ADDITIONAL_TYPE_ARGUMENT);
typeArgumentCountMismatch = true;
}
ResolutionDartType argType =
resolveTypeAnnotation(visitor, typeArguments.head);
// TODO(karlklose): rewrite to not modify [arguments].
arguments.add(argType);
}
if (index < expectedVariables) {
reporter.reportWarningMessage(
node.typeArguments, MessageKind.MISSING_TYPE_ARGUMENT);
typeArgumentCountMismatch = true;
}
return typeArgumentCountMismatch;
}
}