blob: 2ae43fa52b9e2b5191913bab4d33fb4b70346038 [file] [log] [blame]
// Copyright (c) 2012, 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.
/// Implementation of the Dart types hierarchy in 'types.dart' specifically
/// tailored to the resolution phase of the compiler.
library resolution_types;
import 'dart:math' show min;
import '../common.dart';
import '../common/resolution.dart' show Resolution;
import '../common_elements.dart';
import '../ordered_typeset.dart' show OrderedTypeSet;
import '../util/util.dart' show equalElements;
import 'elements.dart';
import 'entities.dart';
import 'modelx.dart' show TypeDeclarationElementX;
import 'names.dart';
import 'types.dart';
enum ResolutionTypeKind {
FUNCTION,
INTERFACE,
TYPEDEF,
TYPE_VARIABLE,
MALFORMED_TYPE,
DYNAMIC,
VOID,
}
abstract class ResolutionDartType implements DartType {
String get name;
ResolutionTypeKind get kind;
const ResolutionDartType();
/**
* Returns the [Element] which declared this type.
*
* This can be [ClassElement] for classes, [TypedefElement] for typedefs,
* [TypeVariableElement] for type variables and [FunctionElement] for
* function types.
*
* Invariant: [element] must be a declaration element.
*/
Element get element;
/**
* Performs the substitution [: [arguments[i]/parameters[i]]this :].
*
* The notation is known from this lambda calculus rule:
*
* (lambda x.e0)e1 -> [e1/x]e0.
*
* See [ResolutionTypeVariableType] for a motivation for this method.
*
* Invariant: There must be the same number of [arguments] and [parameters].
*/
ResolutionDartType subst(covariant List<ResolutionDartType> arguments,
covariant List<ResolutionDartType> parameters);
/// Performs the substitution of the type arguments of [type] for their
/// corresponding type variables in this type.
ResolutionDartType substByContext(GenericType type) {
return subst(type.typeArguments, type.element.typeVariables);
}
/// Computes the unaliased type of this type.
///
/// The unaliased type of a typedef'd type is the unaliased type to which its
/// name is bound. The unaliased version of any other type is the type itself.
///
/// For example, the unaliased type of `typedef A Func<A,B>(B b)` is the
/// function type `(B) -> A` and the unaliased type of `Func<int,String>`
/// is the function type `(String) -> int`.
// TODO(johnniwinther): Maybe move this to [TypedefType].
void computeUnaliased(Resolution resolution) {}
/// Returns the unaliased type of this type.
///
/// The unaliased type of a typedef'd type is the unaliased type to which its
/// name is bound. The unaliased version of any other type is the type itself.
///
/// For example, the unaliased type of `typedef A Func<A,B>(B b)` is the
/// function type `(B) -> A` and the unaliased type of `Func<int,String>`
/// is the function type `(String) -> int`.
ResolutionDartType get unaliased => this;
/**
* If this type is malformed or a generic type created with the wrong number
* of type arguments then [userProvidedBadType] holds the bad type provided
* by the user.
*/
ResolutionDartType get userProvidedBadType => null;
/// Is [: true :] if this type has no explicit type arguments.
bool get isRaw => true;
/// Returns the raw version of this type.
ResolutionDartType asRaw() => this;
/// Is [: true :] if this type has no non-dynamic type arguments.
bool get treatAsRaw => isRaw;
/// Is [: true :] if this type should be treated as the dynamic type.
bool get treatAsDynamic => false;
/// Is [: true :] if this type is the dynamic type.
bool get isDynamic => kind == ResolutionTypeKind.DYNAMIC;
/// Is [: true :] if this type is the void type.
bool get isVoid => kind == ResolutionTypeKind.VOID;
/// Is [: true :] if this is the type of `Object` from dart:core.
bool get isObject => false;
/// Is [: true :] if this type is an interface type.
bool get isInterfaceType => kind == ResolutionTypeKind.INTERFACE;
/// Is [: true :] if this type is a typedef.
bool get isTypedef => kind == ResolutionTypeKind.TYPEDEF;
/// Is [: true :] if this type is a function type.
bool get isFunctionType => kind == ResolutionTypeKind.FUNCTION;
/// Is [: true :] if this type is a type variable.
bool get isTypeVariable => kind == ResolutionTypeKind.TYPE_VARIABLE;
@override
bool get isFunctionTypeVariable => false;
@override
bool get isFutureOr => false;
/// Is [: true :] if this type is a malformed type.
bool get isMalformed => false;
/// Is `true` if this type is declared by an enum.
bool get isEnumType => false;
/// Returns an occurrence of a type variable within this type, if any.
ResolutionTypeVariableType get typeVariableOccurrence => null;
/// Applies [f] to each occurence of a [ResolutionTypeVariableType] within
/// this type.
void forEachTypeVariable(f(ResolutionTypeVariableType variable)) {}
ResolutionTypeVariableType _findTypeVariableOccurrence(
List<ResolutionDartType> types) {
for (ResolutionDartType type in types) {
ResolutionTypeVariableType typeVariable = type.typeVariableOccurrence;
if (typeVariable != null) {
return typeVariable;
}
}
return null;
}
/// Is [: true :] if this type contains any type variables.
bool get containsTypeVariables => typeVariableOccurrence != null;
/// Returns a textual representation of this type as if it was the type
/// of a member named [name].
String getStringAsDeclared(String name) {
return new TypeDeclarationFormatter().format(this, name);
}
R accept<R, A>(covariant ResolutionDartTypeVisitor<R, A> visitor, A argument);
void visitChildren<R, A>(
ResolutionDartTypeVisitor<R, A> visitor, A argument) {}
static void visitList<R, A>(List<ResolutionDartType> types,
ResolutionDartTypeVisitor<R, A> visitor, A argument) {
for (ResolutionDartType type in types) {
type.accept(visitor, argument);
}
}
/// Returns a [ResolutionDartType] which corresponds to [this] except that
/// each contained [MethodTypeVariableType] is replaced by a
/// [ResolutionDynamicType].
/// GENERIC_METHODS: Temporary, only used with '--generic-method-syntax'.
ResolutionDartType get dynamifyMethodTypeVariableType => this;
/// Returns true iff [this] is or contains a [MethodTypeVariableType].
/// GENERIC_METHODS: Temporary, only used with '--generic-method-syntax'
bool get containsMethodTypeVariableType => false;
}
/**
* Represents a type variable, that is the type parameters of a class type.
*
* For example, in [: class Array<E> { ... } :], E is a type variable.
*
* Each class should have its own unique type variables, one for each type
* parameter. A class with type parameters is said to be parameterized or
* generic.
*
* Non-static members, constructors, and factories of generic
* class/interface can refer to type variables of the current class
* (not of supertypes).
*
* When using a generic type, also known as an application or
* instantiation of the type, the actual type arguments should be
* substituted for the type variables in the class declaration.
*
* For example, given a box, [: class Box<T> { T value; } :], the
* type of the expression [: new Box<String>().value :] is
* [: String :] because we must substitute [: String :] for the
* the type variable [: T :].
*/
class ResolutionTypeVariableType extends ResolutionDartType
implements TypeVariableType {
final TypeVariableElement element;
ResolutionTypeVariableType(this.element);
ResolutionTypeKind get kind => ResolutionTypeKind.TYPE_VARIABLE;
String get name => element.name;
ResolutionDartType subst(covariant List<ResolutionDartType> arguments,
covariant List<ResolutionDartType> parameters) {
assert(arguments.length == parameters.length);
if (parameters.isEmpty) {
// Return fast on empty substitutions.
return this;
}
for (int index = 0; index < arguments.length; index++) {
ResolutionTypeVariableType parameter = parameters[index];
ResolutionDartType argument = arguments[index];
if (parameter == this) {
return argument;
}
}
// The type variable was not substituted.
return this;
}
ResolutionTypeVariableType get typeVariableOccurrence => this;
void forEachTypeVariable(f(ResolutionTypeVariableType variable)) {
f(this);
}
R accept<R, A>(DartTypeVisitor<R, A> visitor, A argument) {
return visitor.visitTypeVariableType(this, argument);
}
int get hashCode => 17 * element.hashCode;
bool operator ==(other) {
if (other is! ResolutionTypeVariableType) return false;
return identical(other.element, element);
}
String toString() => '${element.typeDeclaration.name}.$name';
}
/// Provides a thin model of method type variables: They are treated as if
/// their value were `dynamic` when used in a type annotation, and as a
/// malformed type when used in an `as` or `is` expression.
class MethodTypeVariableType extends ResolutionTypeVariableType {
MethodTypeVariableType(TypeVariableElement element) : super(element);
@override
bool get treatAsDynamic => true;
@override
bool get isMalformed => true;
@override
ResolutionDartType get dynamifyMethodTypeVariableType =>
const ResolutionDynamicType();
@override
get containsMethodTypeVariableType => true;
}
class ResolutionVoidType extends ResolutionDartType implements VoidType {
const ResolutionVoidType();
ResolutionTypeKind get kind => ResolutionTypeKind.VOID;
String get name => 'void';
Element get element => null;
ResolutionDartType subst(covariant List<ResolutionDartType> arguments,
covariant List<ResolutionDartType> parameters) {
// Void cannot be substituted.
return this;
}
R accept<R, A>(DartTypeVisitor<R, A> visitor, A argument) {
return visitor.visitVoidType(this, argument);
}
String toString() => name;
int get hashCode => 6007;
}
class MalformedType extends ResolutionDartType {
final ErroneousElement element;
/**
* [declaredType] holds the type which the user wrote in code.
*
* For instance, for a resolved but malformed type like [: Map<String> :] the
* [declaredType] is [: Map<String> :] whereas for an unresolved type
* [userProvidedBadType] is [: null :].
*/
final ResolutionDartType userProvidedBadType;
/**
* Type arguments for the malformed typed, if these cannot fit in the
* [declaredType].
*
* This field is for instance used for [: dynamic<int> :] and [: T<int> :]
* where [: T :] is a type variable, in which case [declaredType] holds
* [: dynamic :] and [: T :], respectively, or for [: X<int> :] where [: X :]
* is not resolved or does not imply a type.
*/
final List<ResolutionDartType> typeArguments;
final int hashCode = _nextHash = (_nextHash + 1).toUnsigned(30);
static int _nextHash = 43765;
MalformedType(this.element, this.userProvidedBadType,
[this.typeArguments = null]);
ResolutionTypeKind get kind => ResolutionTypeKind.MALFORMED_TYPE;
String get name => element.name;
ResolutionDartType subst(covariant List<ResolutionDartType> arguments,
covariant List<ResolutionDartType> parameters) {
// Malformed types are not substitutable.
return this;
}
// Malformed types are treated as dynamic.
bool get treatAsDynamic => true;
@override
bool get isMalformed => true;
R accept<R, A>(
covariant ResolutionDartTypeVisitor<R, A> visitor, A argument) {
return visitor.visitMalformedType(this, argument);
}
String toString() {
var sb = new StringBuffer();
if (typeArguments != null) {
if (userProvidedBadType != null) {
sb.write(userProvidedBadType.name);
} else {
sb.write(element.name);
}
if (!typeArguments.isEmpty) {
sb.write('<');
sb.write(typeArguments.join(', '));
sb.write('>');
}
} else {
sb.write(userProvidedBadType.toString());
}
return sb.toString();
}
}
abstract class GenericType extends ResolutionDartType {
final TypeDeclarationElement element;
final List<ResolutionDartType> typeArguments;
GenericType(
TypeDeclarationElement element, List<ResolutionDartType> typeArguments,
{bool checkTypeArgumentCount: true})
: this.element = element,
this.typeArguments = typeArguments,
this.containsMethodTypeVariableType =
typeArguments.any(_typeContainsMethodTypeVariableType) {
assert(
element != null,
failedAt(
CURRENT_ELEMENT_SPANNABLE, "Missing element for generic type."));
assert(() {
if (!checkTypeArgumentCount) return true;
if (element is TypeDeclarationElementX) {
return element.thisTypeCache == null ||
typeArguments.length == element.typeVariables.length;
}
return true;
}(),
failedAt(
element,
'Invalid type argument count on ${element.thisType}. '
'Provided type arguments: $typeArguments.'));
}
/// Creates a new instance of this type using the provided type arguments.
GenericType createInstantiation(List<ResolutionDartType> newTypeArguments);
GenericType subst(covariant List<ResolutionDartType> arguments,
covariant List<ResolutionDartType> parameters) {
if (typeArguments.isEmpty) {
// Return fast on non-generic types.
return this;
}
if (parameters.isEmpty) {
assert(arguments.isEmpty);
// Return fast on empty substitutions.
return this;
}
List<ResolutionDartType> newTypeArguments =
Types.substTypes(typeArguments, arguments, parameters);
if (!identical(typeArguments, newTypeArguments)) {
// Create a new type only if necessary.
return createInstantiation(newTypeArguments);
}
return this;
}
ResolutionTypeVariableType get typeVariableOccurrence {
return _findTypeVariableOccurrence(typeArguments);
}
void forEachTypeVariable(f(ResolutionTypeVariableType variable)) {
for (ResolutionDartType type in typeArguments) {
type.forEachTypeVariable(f);
}
}
void visitChildren<R, A>(
ResolutionDartTypeVisitor<R, A> visitor, var argument) {
ResolutionDartType.visitList(typeArguments, visitor, argument);
}
String toString() {
StringBuffer sb = new StringBuffer();
sb.write(name);
if (!isRaw) {
sb.write('<');
sb.write(typeArguments.join(', '));
sb.write('>');
}
return sb.toString();
}
@override
final bool containsMethodTypeVariableType;
@override
ResolutionDartType get dynamifyMethodTypeVariableType {
if (!containsMethodTypeVariableType) return this;
List<ResolutionDartType> newTypeArguments = typeArguments
.map((ResolutionDartType type) => type.dynamifyMethodTypeVariableType)
.toList();
return createInstantiation(newTypeArguments);
}
int get hashCode {
int hash = element.hashCode;
for (ResolutionDartType argument in typeArguments) {
int argumentHash = argument != null ? argument.hashCode : 0;
hash = 17 * hash + 3 * argumentHash;
}
return hash;
}
bool operator ==(other) {
if (other is! GenericType) return false;
return kind == other.kind &&
element == other.element &&
equalElements(typeArguments, other.typeArguments);
}
/// Returns `true` if the declaration of this type has type variables.
bool get isGeneric => !typeArguments.isEmpty;
bool get isRaw => typeArguments.isEmpty || identical(this, element.rawType);
GenericType asRaw() => element.rawType;
bool get treatAsRaw {
if (isRaw) return true;
for (ResolutionDartType type in typeArguments) {
if (!type.treatAsDynamic) return false;
}
return true;
}
}
class ResolutionInterfaceType extends GenericType implements InterfaceType {
int _hashCode;
ResolutionInterfaceType(ClassElement element,
[List<ResolutionDartType> typeArguments = const <ResolutionDartType>[]])
: super(element, typeArguments) {
assert(element.isDeclaration, failedAt(element));
}
ResolutionInterfaceType.forUserProvidedBadType(ClassElement element,
[List<ResolutionDartType> typeArguments = const <ResolutionDartType>[]])
: super(element, typeArguments, checkTypeArgumentCount: false);
ClassElement get element => super.element;
ResolutionTypeKind get kind => ResolutionTypeKind.INTERFACE;
String get name => element.name;
bool get isObject => element.isObject;
bool get isEnumType => element.isEnumClass;
ResolutionInterfaceType createInstantiation(
List<ResolutionDartType> newTypeArguments) {
return new ResolutionInterfaceType(element, newTypeArguments);
}
/**
* Returns the type as an instance of class [other], if possible, null
* otherwise.
*/
ResolutionInterfaceType asInstanceOf(ClassElement other) {
other = other.declaration;
if (element == other) return this;
ResolutionInterfaceType supertype = element.asInstanceOf(other);
if (supertype != null) {
List<ResolutionDartType> arguments = Types.substTypes(
supertype.typeArguments, typeArguments, element.typeVariables);
return new ResolutionInterfaceType(supertype.element, arguments);
}
return null;
}
MemberSignature lookupInterfaceMember(Name name) {
MemberSignature member = element.lookupInterfaceMember(name);
if (member != null && isGeneric) {
return new InterfaceMember(this, member);
}
return member;
}
MemberSignature lookupClassMember(Name name) {
MemberSignature member = element.lookupClassMember(name);
if (member != null && isGeneric) {
return new InterfaceMember(this, member);
}
return member;
}
int get hashCode => _hashCode ??= super.hashCode;
ResolutionInterfaceType asRaw() => super.asRaw();
R accept<R, A>(DartTypeVisitor<R, A> visitor, A argument) {
return visitor.visitInterfaceType(this, argument);
}
/// Returns the type of the 'call' method in this interface type, or
/// `null` if the interface type has no 'call' method.
ResolutionFunctionType get callType {
ResolutionFunctionType type = element.callType;
return type != null && isGeneric ? type.substByContext(this) : type;
}
ResolutionInterfaceType subst(covariant List<ResolutionDartType> arguments,
covariant List<ResolutionDartType> parameters) {
return super.subst(arguments, parameters);
}
}
/// Special subclass of [ResolutionInterfaceType] used for generic interface
/// types created with the wrong number of type arguments.
///
/// The type uses `dynamic` for all it s type arguments.
class BadInterfaceType extends ResolutionInterfaceType {
final ResolutionInterfaceType userProvidedBadType;
BadInterfaceType(
ClassElement element, ResolutionInterfaceType this.userProvidedBadType)
: super(element, element.rawType.typeArguments);
String toString() {
return userProvidedBadType.toString();
}
}
/**
* Special subclass of [ResolutionTypedefType] used for generic typedef types
* created with the wrong number of type arguments.
*
* The type uses [:dynamic:] for all it s type arguments.
*/
class BadTypedefType extends ResolutionTypedefType {
final ResolutionTypedefType userProvidedBadType;
BadTypedefType(
TypedefElement element, ResolutionTypedefType this.userProvidedBadType)
: super(element, element.rawType.typeArguments);
String toString() {
return userProvidedBadType.toString();
}
}
class ResolutionFunctionType extends ResolutionDartType
implements FunctionType {
final FunctionTypedElement element;
final ResolutionDartType returnType;
final List<ResolutionDartType> parameterTypes;
final List<ResolutionDartType> optionalParameterTypes;
/**
* The names of the named parameters ordered lexicographically.
*/
final List<String> namedParameters;
/**
* The types of the named parameters in the order corresponding to the
* [namedParameters].
*/
final List<ResolutionDartType> namedParameterTypes;
TypedefType get typedefType => null;
factory ResolutionFunctionType(FunctionTypedElement element,
[ResolutionDartType returnType = const ResolutionDynamicType(),
List<ResolutionDartType> parameterTypes = const <ResolutionDartType>[],
List<ResolutionDartType> optionalParameterTypes =
const <ResolutionDartType>[],
List<String> namedParameters = const <String>[],
List<ResolutionDartType> namedParameterTypes =
const <ResolutionDartType>[]]) {
assert(element != null, failedAt(CURRENT_ELEMENT_SPANNABLE));
assert(element.isDeclaration, failedAt(element));
return new ResolutionFunctionType.internal(
element,
returnType,
parameterTypes,
optionalParameterTypes,
namedParameters,
namedParameterTypes);
}
factory ResolutionFunctionType.synthesized(
[ResolutionDartType returnType = const ResolutionDynamicType(),
List<ResolutionDartType> parameterTypes = const <ResolutionDartType>[],
List<ResolutionDartType> optionalParameterTypes =
const <ResolutionDartType>[],
List<String> namedParameters = const <String>[],
List<ResolutionDartType> namedParameterTypes =
const <ResolutionDartType>[]]) {
return new ResolutionFunctionType.internal(null, returnType, parameterTypes,
optionalParameterTypes, namedParameters, namedParameterTypes);
}
factory ResolutionFunctionType.generalized(
ResolutionDartType returnType,
List<ResolutionDartType> parameterTypes,
List<ResolutionDartType> optionalParameterTypes,
List<String> namedParameters,
List<ResolutionDartType> namedParameterTypes) {
return new ResolutionFunctionType.internal(null, returnType, parameterTypes,
optionalParameterTypes, namedParameters, namedParameterTypes);
}
ResolutionFunctionType.internal(FunctionTypedElement this.element,
[ResolutionDartType returnType = const ResolutionDynamicType(),
List<ResolutionDartType> parameterTypes = const <ResolutionDartType>[],
List<ResolutionDartType> optionalParameterTypes =
const <ResolutionDartType>[],
List<String> namedParameters = const <String>[],
List<ResolutionDartType> namedParameterTypes =
const <ResolutionDartType>[]])
: this.returnType = returnType,
this.parameterTypes = parameterTypes,
this.optionalParameterTypes = optionalParameterTypes,
this.namedParameters = namedParameters,
this.namedParameterTypes = namedParameterTypes,
this.containsMethodTypeVariableType = returnType
.containsMethodTypeVariableType ||
parameterTypes.any(_typeContainsMethodTypeVariableType) ||
optionalParameterTypes.any(_typeContainsMethodTypeVariableType) ||
namedParameterTypes.any(_typeContainsMethodTypeVariableType) {
assert(element == null || element.isDeclaration,
failedAt(CURRENT_ELEMENT_SPANNABLE));
// Assert that optional and named parameters are not used at the same time.
assert(optionalParameterTypes.isEmpty || namedParameterTypes.isEmpty);
assert(namedParameters.length == namedParameterTypes.length);
}
@override
final List<FunctionTypeVariable> typeVariables =
const <FunctionTypeVariable>[];
FunctionType instantiate(List<DartType> arguments) {
throw new UnsupportedError("ResolutionFunctionType.instantiate");
}
ResolutionTypeKind get kind => ResolutionTypeKind.FUNCTION;
ResolutionDartType getNamedParameterType(String name) {
for (int i = 0; i < namedParameters.length; i++) {
if (namedParameters[i] == name) {
return namedParameterTypes[i];
}
}
return null;
}
ResolutionDartType subst(covariant List<ResolutionDartType> arguments,
covariant List<ResolutionDartType> parameters) {
if (parameters.isEmpty) {
assert(arguments.isEmpty);
// Return fast on empty substitutions.
return this;
}
ResolutionDartType newReturnType = returnType.subst(arguments, parameters);
bool changed = !identical(newReturnType, returnType);
List<ResolutionDartType> newParameterTypes =
Types.substTypes(parameterTypes, arguments, parameters);
List<ResolutionDartType> newOptionalParameterTypes =
Types.substTypes(optionalParameterTypes, arguments, parameters);
List<ResolutionDartType> newNamedParameterTypes =
Types.substTypes(namedParameterTypes, arguments, parameters);
if (!changed &&
(!identical(parameterTypes, newParameterTypes) ||
!identical(optionalParameterTypes, newOptionalParameterTypes) ||
!identical(namedParameterTypes, newNamedParameterTypes))) {
changed = true;
}
if (changed) {
// Create a new type only if necessary.
return new ResolutionFunctionType.internal(
element,
newReturnType,
newParameterTypes,
newOptionalParameterTypes,
namedParameters,
newNamedParameterTypes);
}
return this;
}
ResolutionTypeVariableType get typeVariableOccurrence {
ResolutionTypeVariableType typeVariableType =
returnType.typeVariableOccurrence;
if (typeVariableType != null) return typeVariableType;
typeVariableType = _findTypeVariableOccurrence(parameterTypes);
if (typeVariableType != null) return typeVariableType;
typeVariableType = _findTypeVariableOccurrence(optionalParameterTypes);
if (typeVariableType != null) return typeVariableType;
return _findTypeVariableOccurrence(namedParameterTypes);
}
void forEachTypeVariable(f(ResolutionTypeVariableType variable)) {
returnType.forEachTypeVariable(f);
parameterTypes.forEach((ResolutionDartType type) {
type.forEachTypeVariable(f);
});
optionalParameterTypes.forEach((ResolutionDartType type) {
type.forEachTypeVariable(f);
});
namedParameterTypes.forEach((ResolutionDartType type) {
type.forEachTypeVariable(f);
});
}
R accept<R, A>(covariant DartTypeVisitor<R, A> visitor, A argument) {
return visitor.visitFunctionType(this, argument);
}
void visitChildren<R, A>(
ResolutionDartTypeVisitor<R, A> visitor, var argument) {
returnType.accept(visitor, argument);
ResolutionDartType.visitList(parameterTypes, visitor, argument);
ResolutionDartType.visitList(optionalParameterTypes, visitor, argument);
ResolutionDartType.visitList(namedParameterTypes, visitor, argument);
}
String toString() {
StringBuffer sb = new StringBuffer();
sb.write('(');
sb.write(parameterTypes.join(', '));
bool first = parameterTypes.isEmpty;
if (!optionalParameterTypes.isEmpty) {
if (!first) {
sb.write(', ');
}
sb.write('[');
sb.write(optionalParameterTypes.join(', '));
sb.write(']');
first = false;
}
if (!namedParameterTypes.isEmpty) {
if (!first) {
sb.write(', ');
}
sb.write('{');
first = true;
for (int i = 0; i < namedParameters.length; i++) {
if (!first) {
sb.write(', ');
}
sb.write(namedParameterTypes[i]);
sb.write(' ');
sb.write(namedParameters[i]);
first = false;
}
sb.write('}');
}
sb.write(') -> ${returnType}');
return sb.toString();
}
String get name => 'Function';
@override
ResolutionDartType get dynamifyMethodTypeVariableType {
if (!containsMethodTypeVariableType) return this;
ResolutionDartType eraseIt(ResolutionDartType type) =>
type.dynamifyMethodTypeVariableType;
ResolutionDartType newReturnType =
returnType.dynamifyMethodTypeVariableType;
List<ResolutionDartType> newParameterTypes =
parameterTypes.map(eraseIt).toList();
List<ResolutionDartType> newOptionalParameterTypes =
optionalParameterTypes.map(eraseIt).toList();
List<ResolutionDartType> newNamedParameterTypes =
namedParameterTypes.map(eraseIt).toList();
return new ResolutionFunctionType.internal(
element,
newReturnType,
newParameterTypes,
newOptionalParameterTypes,
namedParameters,
newNamedParameterTypes);
}
@override
final bool containsMethodTypeVariableType;
int get hashCode {
int hash = 3 * returnType.hashCode;
for (ResolutionDartType parameter in parameterTypes) {
hash = 17 * hash + 5 * parameter.hashCode;
}
for (ResolutionDartType parameter in optionalParameterTypes) {
hash = 19 * hash + 7 * parameter.hashCode;
}
for (String name in namedParameters) {
hash = 23 * hash + 11 * name.hashCode;
}
for (ResolutionDartType parameter in namedParameterTypes) {
hash = 29 * hash + 13 * parameter.hashCode;
}
return hash;
}
bool operator ==(other) {
if (other is! ResolutionFunctionType) return false;
return returnType == other.returnType &&
equalElements(parameterTypes, other.parameterTypes) &&
equalElements(optionalParameterTypes, other.optionalParameterTypes) &&
equalElements(namedParameters, other.namedParameters) &&
equalElements(namedParameterTypes, other.namedParameterTypes);
}
}
bool _typeContainsMethodTypeVariableType(ResolutionDartType type) =>
type.containsMethodTypeVariableType;
class ResolutionTypedefType extends GenericType implements TypedefType {
ResolutionDartType _unaliased;
ResolutionTypedefType(TypedefElement element,
[List<ResolutionDartType> typeArguments = const <ResolutionDartType>[]])
: super(element, typeArguments);
ResolutionTypedefType.forUserProvidedBadType(TypedefElement element,
[List<ResolutionDartType> typeArguments = const <ResolutionDartType>[]])
: super(element, typeArguments, checkTypeArgumentCount: false);
TypedefElement get element => super.element;
ResolutionTypeKind get kind => ResolutionTypeKind.TYPEDEF;
String get name => element.name;
ResolutionTypedefType createInstantiation(
List<ResolutionDartType> newTypeArguments) {
return new ResolutionTypedefType(element, newTypeArguments);
}
ResolutionTypedefType subst(covariant List<ResolutionDartType> arguments,
covariant List<ResolutionDartType> parameters) {
return super.subst(arguments, parameters);
}
void computeUnaliased(Resolution resolution) {
if (_unaliased == null) {
element.ensureResolved(resolution);
if (element.isMalformed) {
_unaliased = const ResolutionDynamicType();
return;
}
element.checkCyclicReference(resolution);
element.alias.computeUnaliased(resolution);
_unaliased = element.alias.unaliased.substByContext(this);
}
}
ResolutionDartType get unaliased {
if (_unaliased == null) {
ResolutionDartType definition = element.alias.unaliased;
_unaliased = definition.substByContext(this);
}
return _unaliased;
}
int get hashCode => super.hashCode;
ResolutionTypedefType asRaw() => super.asRaw();
R accept<R, A>(
covariant ResolutionDartTypeVisitor<R, A> visitor, A argument) {
return visitor.visitTypedefType(this, argument);
}
}
/**
* Special type for the `dynamic` type.
*/
class ResolutionDynamicType extends ResolutionDartType implements DynamicType {
const ResolutionDynamicType();
Element get element => null;
String get name => 'dynamic';
bool get treatAsDynamic => true;
ResolutionTypeKind get kind => ResolutionTypeKind.DYNAMIC;
ResolutionDartType subst(covariant List<ResolutionDartType> arguments,
covariant List<ResolutionDartType> parameters) =>
this;
R accept<R, A>(DartTypeVisitor<R, A> visitor, A argument) {
return visitor.visitDynamicType(this, argument);
}
int get hashCode => 91;
String toString() => name;
}
/**
* [InterfaceMember] encapsulates a member (method, field, property) with
* the types of the declarer and receiver in order to do substitution on the
* member type.
*
* Consider for instance these classes and the variable `B<String> b`:
*
* class A<E> {
* E field;
* }
* class B<F> extends A<F> {}
*
* In an [InterfaceMember] for `b.field` the [receiver] is the type
* `B<String>` and the declarer is the type `A<F>`, which is the supertype of
* `B<F>` from which `field` has been inherited. To compute the type of
* `b.field` we must first substitute `E` by `F` using the relation between
* `A<E>` and `A<F>`, and then `F` by `String` using the relation between
* `B<F>` and `B<String>`.
*/
class InterfaceMember implements MemberSignature {
final ResolutionInterfaceType instance;
final MemberSignature member;
InterfaceMember(this.instance, this.member);
Name get name => member.name;
ResolutionDartType get type => member.type.substByContext(instance);
ResolutionFunctionType get functionType =>
member.functionType.substByContext(instance);
bool get isGetter => member.isGetter;
bool get isSetter => member.isSetter;
bool get isMethod => member.isMethod;
Iterable<Member> get declarations => member.declarations;
}
abstract class ResolutionDartTypeVisitor<R, A> extends DartTypeVisitor<R, A> {
const ResolutionDartTypeVisitor();
R visitMalformedType(MalformedType type, A argument) => null;
R visitTypedefType(TypedefType type, A argument) => null;
}
abstract class BaseResolutionDartTypeVisitor<R, A>
extends BaseDartTypeVisitor<R, A>
implements ResolutionDartTypeVisitor<R, A> {
const BaseResolutionDartTypeVisitor();
@override
R visitMalformedType(MalformedType type, A argument) =>
visitType(type, argument);
R visitGenericType(GenericType type, A argument) => visitType(type, argument);
@override
R visitInterfaceType(covariant ResolutionInterfaceType type, A argument) =>
visitGenericType(type, argument);
@override
R visitTypedefType(covariant ResolutionTypedefType type, A argument) =>
visitGenericType(type, argument);
}
abstract class AbstractTypeRelationMixin
implements
AbstractTypeRelation<ResolutionDartType>,
ResolutionDartTypeVisitor<bool, ResolutionDartType> {
Resolution get resolution;
@override
CommonElements get commonElements => resolution.commonElements;
/// Ensures that the super hierarchy of [type] is computed.
void ensureResolved(covariant ResolutionInterfaceType type) {
// TODO(johnniwinther): Currently needed since literal types like int,
// double, bool etc. might not have been resolved yet.
type.element.ensureResolved(resolution);
}
/// Returns the unaliased version of [type].
ResolutionDartType getUnaliased(covariant ResolutionDartType type) {
type.computeUnaliased(resolution);
return type.unaliased;
}
@override
DartType getTypeVariableBound(covariant TypeVariableElement element) =>
element.bound;
@override
FunctionType getCallType(covariant ResolutionInterfaceType type) =>
type.callType;
@override
InterfaceType asInstanceOf(
covariant ResolutionInterfaceType type, ClassEntity cls) =>
type.asInstanceOf(cls);
/// Handle as dynamic for both subtype and more specific relation to avoid
/// spurious errors from malformed types.
bool visitMalformedType(MalformedType t, ResolutionDartType s) => true;
bool visitTypedefType(
covariant ResolutionTypedefType t, ResolutionDartType s) =>
visitType(t, s);
}
class ResolutionMoreSpecificVisitor
extends MoreSpecificVisitor<ResolutionDartType>
with AbstractTypeRelationMixin {
final Resolution resolution;
bool get strongMode => false;
ResolutionMoreSpecificVisitor(this.resolution);
}
class ResolutionSubtypeVisitor extends SubtypeVisitor<ResolutionDartType>
with AbstractTypeRelationMixin {
final Resolution resolution;
bool get strongMode => false;
ResolutionSubtypeVisitor(this.resolution);
}
class ResolutionPotentialSubtypeVisitor
extends PotentialSubtypeVisitor<ResolutionDartType>
with AbstractTypeRelationMixin {
final Resolution resolution;
bool get strongMode => false;
ResolutionPotentialSubtypeVisitor(this.resolution);
}
/**
* Callback used to check whether the [typeArgument] of [type] is a valid
* substitute for the bound of [typeVariable]. [bound] holds the bound against
* which [typeArgument] should be checked.
*/
typedef void CheckTypeVariableBound(GenericType type, DartType typeArgument,
TypeVariableType typeVariable, DartType bound);
class Types extends DartTypes {
final Resolution resolution;
final ResolutionMoreSpecificVisitor moreSpecificVisitor;
final ResolutionSubtypeVisitor subtypeVisitor;
final ResolutionPotentialSubtypeVisitor potentialSubtypeVisitor;
CommonElements get commonElements => resolution.commonElements;
DiagnosticReporter get reporter => resolution.reporter;
Types(Resolution resolution)
: this.resolution = resolution,
this.moreSpecificVisitor =
new ResolutionMoreSpecificVisitor(resolution),
this.subtypeVisitor = new ResolutionSubtypeVisitor(resolution),
this.potentialSubtypeVisitor =
new ResolutionPotentialSubtypeVisitor(resolution);
Types copy(Resolution resolution) {
return new Types(resolution);
}
@override
InterfaceType asInstanceOf(
covariant ResolutionInterfaceType type, ClassEntity cls) {
return type.asInstanceOf(cls);
}
@override
ResolutionDartType substByContext(covariant ResolutionDartType base,
covariant ResolutionInterfaceType context) {
return base.substByContext(context);
}
@override
InterfaceType getThisType(covariant ClassElement cls) => cls.thisType;
@override
ResolutionInterfaceType getSupertype(covariant ClassElement cls) =>
cls.supertype;
@override
Iterable<InterfaceType> getSupertypes(covariant ClassElement cls) {
assert(cls.allSupertypes != null,
failedAt(cls, 'Supertypes have not been computed for $cls.'));
return cls.allSupertypes;
}
@override
Iterable<InterfaceType> getInterfaces(covariant ClassElement cls) {
return new List<InterfaceType>.from(cls.interfaces.toList());
}
@override
FunctionType getCallType(covariant ResolutionInterfaceType type) =>
type.callType;
/// Flatten [type] by recursively removing enclosing `Future` annotations.
///
/// Defined in the language specification as:
///
/// If T = Future<S> then flatten(T) = flatten(S).
/// Otherwise if T <: Future then let S be a type such that T << Future<S>
/// and for all R, if T << Future<R> then S << R. Then flatten(T) = S.
/// In any other circumstance, flatten(T) = T.
///
/// For instance:
/// flatten(T) = T
/// flatten(Future<T>) = T
/// flatten(Future<Future<T>>) = T
///
/// This method is used in the static typing of await and type checking of
/// return.
ResolutionDartType flatten(ResolutionDartType type) {
if (type is ResolutionInterfaceType) {
if (type.element == commonElements.futureClass) {
// T = Future<S>
return flatten(type.typeArguments.first);
}
ResolutionInterfaceType futureType =
type.asInstanceOf(commonElements.futureClass);
if (futureType != null) {
// T << Future<S>
return futureType.typeArguments.single;
}
}
return type;
}
/// Returns true if [t] is more specific than [s].
bool isMoreSpecific(ResolutionDartType t, ResolutionDartType s) {
return moreSpecificVisitor.isMoreSpecific(t, s);
}
/// Returns the most specific type of [t] and [s] or `null` if neither is more
/// specific than the other.
ResolutionDartType getMostSpecific(
ResolutionDartType t, ResolutionDartType s) {
if (isMoreSpecific(t, s)) {
return t;
} else if (isMoreSpecific(s, t)) {
return s;
} else {
return null;
}
}
/** Returns true if t is a subtype of s */
bool isSubtype(
covariant ResolutionDartType t, covariant ResolutionDartType s) {
return subtypeVisitor.isSubtype(t, s);
}
bool isAssignable(
covariant ResolutionDartType r, covariant ResolutionDartType s) {
return subtypeVisitor.isAssignable(r, s);
}
bool isPotentialSubtype(
covariant ResolutionDartType t, covariant ResolutionDartType s) {
// TODO(johnniwinther): Return a set of variable points in the positive
// cases.
return potentialSubtypeVisitor.isSubtype(t, s);
}
@override
void checkTypeVariableBounds(
covariant ResolutionInterfaceType type,
void checkTypeVariableBound(InterfaceType type, DartType typeArgument,
TypeVariableType typeVariable, DartType bound)) {
void f(DartType type, DartType typeArgument, TypeVariableType typeVariable,
DartType bound) =>
checkTypeVariableBound(type, typeArgument, typeVariable, bound);
genericCheckTypeVariableBounds(type, f);
}
/**
* Checks the type arguments of [type] against the type variable bounds
* declared on [element]. Calls [checkTypeVariableBound] on each type
* argument and bound.
*/
void genericCheckTypeVariableBounds(
GenericType type, CheckTypeVariableBound checkTypeVariableBound) {
TypeDeclarationElement element = type.element;
List<ResolutionDartType> typeArguments = type.typeArguments;
List<ResolutionDartType> typeVariables = element.typeVariables;
assert(typeVariables.length == typeArguments.length);
for (int index = 0; index < typeArguments.length; index++) {
ResolutionTypeVariableType typeVariable = typeVariables[index];
ResolutionDartType bound =
typeVariable.element.bound.substByContext(type);
ResolutionDartType typeArgument = typeArguments[index];
checkTypeVariableBound(type, typeArgument, typeVariable, bound);
}
}
/**
* Helper method for performing substitution of a list of types.
*
* If no types are changed by the substitution, the [types] is returned
* instead of a newly created list.
*/
static List<ResolutionDartType> substTypes(List<ResolutionDartType> types,
List<ResolutionDartType> arguments, List<ResolutionDartType> parameters) {
bool changed = false;
List<ResolutionDartType> result =
new List<ResolutionDartType>.generate(types.length, (index) {
ResolutionDartType type = types[index];
ResolutionDartType argument = type.subst(arguments, parameters);
if (!changed && !identical(argument, type)) {
changed = true;
}
return argument;
});
// Use the new List only if necessary.
return changed ? result : types;
}
/**
* A `compareTo` function that globally orders types using
* [Elements.compareByPosition] to order types defined by a declaration.
*
* The order is:
* * void
* * dynamic
* * interface, typedef, type variables ordered by element order
* - interface and typedef of the same element are ordered by
* the order of their type arguments
* * function types, ordered by
* - return type
* - required parameter types
* - optional parameter types
* - named parameter names
* - named parameter types
* * malformed types
* * statement types
*/
static int compare(ResolutionDartType a, ResolutionDartType b) {
if (a == b) return 0;
if (a.isVoid) {
// [b] is not void => a < b.
return -1;
} else if (b.isVoid) {
// [a] is not void => a > b.
return 1;
}
if (a.isDynamic) {
// [b] is not dynamic => a < b.
return -1;
} else if (b.isDynamic) {
// [a] is not dynamic => a > b.
return 1;
}
bool isDefinedByDeclaration(ResolutionDartType type) {
return type.isInterfaceType || type.isTypedef || type.isTypeVariable;
}
if (isDefinedByDeclaration(a)) {
if (isDefinedByDeclaration(b)) {
int result = Elements.compareByPosition(a.element, b.element);
if (result != 0) return result;
if (a.isTypeVariable) {
return b.isTypeVariable
? 0
: 1; // [b] is not a type variable => a > b.
} else {
if (b.isTypeVariable) {
// [a] is not a type variable => a < b.
return -1;
} else {
return compareList((a as GenericType).typeArguments,
(b as GenericType).typeArguments);
}
}
} else {
// [b] is neither an interface, typedef, type variable, dynamic,
// nor void => a < b.
return -1;
}
} else if (isDefinedByDeclaration(b)) {
// [a] is neither an interface, typedef, type variable, dynamic,
// nor void => a > b.
return 1;
}
if (a.isFunctionType) {
if (b.isFunctionType) {
ResolutionFunctionType aFunc = a;
ResolutionFunctionType bFunc = b;
int result = compare(aFunc.returnType, bFunc.returnType);
if (result != 0) return result;
result = compareList(aFunc.parameterTypes, bFunc.parameterTypes);
if (result != 0) return result;
result = compareList(
aFunc.optionalParameterTypes, bFunc.optionalParameterTypes);
if (result != 0) return result;
// TODO(karlklose): reuse [compareList].
Iterator<String> aNames = aFunc.namedParameters.iterator;
Iterator<String> bNames = bFunc.namedParameters.iterator;
while (aNames.moveNext() && bNames.moveNext()) {
int result = aNames.current.compareTo(bNames.current);
if (result != 0) return result;
}
if (aNames.moveNext()) {
// [aNames] is longer that [bNames] => a > b.
return 1;
} else if (bNames.moveNext()) {
// [bNames] is longer that [aNames] => a < b.
return -1;
}
return compareList(
aFunc.namedParameterTypes, bFunc.namedParameterTypes);
} else {
// [b] is a malformed or statement type => a < b.
return -1;
}
} else if (b.isFunctionType) {
// [b] is a malformed or statement type => a > b.
return 1;
}
assert(a.isMalformed);
assert(b.isMalformed);
// TODO(johnniwinther): Can we do this better?
return Elements.compareByPosition(a.element, b.element);
}
static int compareList(
List<ResolutionDartType> a, List<ResolutionDartType> b) {
for (int index = 0; index < min(a.length, b.length); index++) {
int result = compare(a[index], b[index]);
if (result != 0) return result;
}
if (a.length > b.length) {
return 1;
} else if (a.length < b.length) {
return -1;
}
return 0;
}
static List<ResolutionDartType> sorted(Iterable<ResolutionDartType> types) {
return types.toList()..sort(compare);
}
/// Computes the least upper bound of two interface types [a] and [b].
ResolutionInterfaceType computeLeastUpperBoundInterfaces(
ResolutionInterfaceType a, ResolutionInterfaceType b) {
/// Returns the set of supertypes of [type] at depth [depth].
Set<ResolutionDartType> getSupertypesAtDepth(
ResolutionInterfaceType type, int depth) {
OrderedTypeSet types = type.element.allSupertypesAndSelf;
Set<ResolutionDartType> set = new Set<ResolutionDartType>();
types.forEach(depth, (_supertype) {
ResolutionInterfaceType supertype = _supertype;
set.add(supertype.substByContext(type));
});
return set;
}
ClassElement aClass = a.element;
ClassElement bClass = b.element;
int maxCommonDepth = min(aClass.hierarchyDepth, bClass.hierarchyDepth);
for (int depth = maxCommonDepth; depth >= 0; depth--) {
Set<ResolutionDartType> aTypeSet = getSupertypesAtDepth(a, depth);
Set<ResolutionDartType> bTypeSet = getSupertypesAtDepth(b, depth);
Set<ResolutionDartType> intersection = aTypeSet..retainAll(bTypeSet);
if (intersection.length == 1) {
return intersection.first;
}
}
reporter.internalError(CURRENT_ELEMENT_SPANNABLE,
'No least upper bound computed for $a and $b.');
return null;
}
/// Computes the least upper bound of the types in the longest prefix of [a]
/// and [b].
List<ResolutionDartType> computeLeastUpperBoundsTypes(
List<ResolutionDartType> a, List<ResolutionDartType> b) {
if (a.isEmpty || b.isEmpty) return const <ResolutionDartType>[];
int prefixLength = min(a.length, b.length);
List<ResolutionDartType> types = new List<ResolutionDartType>(prefixLength);
for (int index = 0; index < prefixLength; index++) {
types[index] = computeLeastUpperBound(a[index], b[index]);
}
return types;
}
/// Computes the least upper bound of two function types [a] and [b].
///
/// If the required parameter count of [a] and [b] does not match, `Function`
/// is returned.
///
/// Otherwise, a function type is returned whose return type and
/// parameter types are the least upper bound of those of [a] and [b],
/// respectively. In addition, the optional parameters are the least upper
/// bound of the longest common prefix of the optional parameters of [a] and
/// [b], and the named parameters are the least upper bound of those common to
/// [a] and [b].
ResolutionDartType computeLeastUpperBoundFunctionTypes(
ResolutionFunctionType a, ResolutionFunctionType b) {
if (a.parameterTypes.length != b.parameterTypes.length) {
ResolutionInterfaceType functionType = commonElements.functionType;
return functionType;
}
ResolutionDartType returnType =
computeLeastUpperBound(a.returnType, b.returnType);
List<ResolutionDartType> parameterTypes =
computeLeastUpperBoundsTypes(a.parameterTypes, b.parameterTypes);
List<ResolutionDartType> optionalParameterTypes =
computeLeastUpperBoundsTypes(
a.optionalParameterTypes, b.optionalParameterTypes);
List<String> namedParameters = <String>[];
List<String> aNamedParameters = a.namedParameters;
List<String> bNamedParameters = b.namedParameters;
List<ResolutionDartType> namedParameterTypes = <ResolutionDartType>[];
List<ResolutionDartType> aNamedParameterTypes = a.namedParameterTypes;
List<ResolutionDartType> bNamedParameterTypes = b.namedParameterTypes;
int aIndex = 0;
int bIndex = 0;
while (
aIndex < aNamedParameters.length && bIndex < bNamedParameters.length) {
String aNamedParameter = aNamedParameters[aIndex];
String bNamedParameter = bNamedParameters[bIndex];
int result = aNamedParameter.compareTo(bNamedParameter);
if (result == 0) {
namedParameters.add(aNamedParameter);
namedParameterTypes.add(computeLeastUpperBound(
aNamedParameterTypes[aIndex], bNamedParameterTypes[bIndex]));
}
if (result <= 0) {
aIndex++;
}
if (result >= 0) {
bIndex++;
}
}
return new ResolutionFunctionType.synthesized(returnType, parameterTypes,
optionalParameterTypes, namedParameters, namedParameterTypes);
}
/// Computes the least upper bound of two types of which at least one is a
/// type variable. The least upper bound of a type variable is defined in
/// terms of its bound, but to ensure reflexivity we need to check for common
/// bounds transitively.
ResolutionDartType computeLeastUpperBoundTypeVariableTypes(
ResolutionDartType a, ResolutionDartType b) {
Set<ResolutionDartType> typeVariableBounds = new Set<ResolutionDartType>();
while (a.isTypeVariable) {
if (a == b) return a;
typeVariableBounds.add(a);
TypeVariableElement element = a.element;
a = element.bound;
}
while (b.isTypeVariable) {
if (typeVariableBounds.contains(b)) return b;
TypeVariableElement element = b.element;
b = element.bound;
}
return computeLeastUpperBound(a, b);
}
/// Computes the least upper bound for [a] and [b].
ResolutionDartType computeLeastUpperBound(
ResolutionDartType a, ResolutionDartType b) {
if (a == b) return a;
if (a.isTypeVariable || b.isTypeVariable) {
return computeLeastUpperBoundTypeVariableTypes(a, b);
}
a.computeUnaliased(resolution);
a = a.unaliased;
b.computeUnaliased(resolution);
b = b.unaliased;
if (a.treatAsDynamic || b.treatAsDynamic)
return const ResolutionDynamicType();
if (a.isVoid || b.isVoid) return const ResolutionVoidType();
if (a.isFunctionType && b.isFunctionType) {
return computeLeastUpperBoundFunctionTypes(a, b);
}
if (a.isFunctionType) {
ResolutionInterfaceType functionType = commonElements.functionType;
a = functionType;
}
if (b.isFunctionType) {
ResolutionInterfaceType functionType = commonElements.functionType;
b = functionType;
}
if (a.isInterfaceType && b.isInterfaceType) {
return computeLeastUpperBoundInterfaces(a, b);
}
return const ResolutionDynamicType();
}
/// Computes the unaliased type of the first non type variable bound of
/// [type].
///
/// This is used to normalize malformed types, type variables and typedef
/// before use in typechecking.
///
/// Malformed types are normalized to `dynamic`. Typedefs are normalized to
/// their alias, or `dynamic` if cyclic. Type variables are normalized to the
/// normalized type of their bound, or `Object` if cyclic.
///
/// For instance for these types:
///
/// class Foo<T extends Bar, S extends T, U extends Baz> {}
/// class Bar<X extends Y, Y extends X> {}
/// typedef Baz();
///
/// the unaliased bounds types are:
///
/// unaliasedBound(Foo) = Foo
/// unaliasedBound(Bar) = Bar
/// unaliasedBound(Unresolved) = `dynamic`
/// unaliasedBound(Baz) = ()->dynamic
/// unaliasedBound(T) = Bar
/// unaliasedBound(S) = unaliasedBound(T) = Bar
/// unaliasedBound(U) = unaliasedBound(Baz) = ()->dynamic
/// unaliasedBound(X) = unaliasedBound(Y) = `Object`
///
static ResolutionDartType computeUnaliasedBound(
Resolution resolution, ResolutionDartType type) {
ResolutionDartType originalType = type;
while (type.isTypeVariable) {
ResolutionTypeVariableType variable = type;
type = variable.element.bound;
if (type == originalType) {
ResolutionInterfaceType objectType =
resolution.commonElements.objectType;
type = objectType;
}
}
if (type.isMalformed) {
return const ResolutionDynamicType();
}
type.computeUnaliased(resolution);
return type.unaliased;
}
/// Computes the interface type of [type], which is the type that defines
/// the property of [type].
///
/// For an interface type it is the type itself, for a type variable it is the
/// interface type of the bound, for function types and typedefs it is the
/// `Function` type. For other types, like `dynamic`, `void` and malformed
/// types, there is no interface type and `null` is returned.
///
/// For instance for these types:
///
/// class Foo<T extends Bar, S extends T, U extends Baz> {}
/// class Bar {}
/// typedef Baz();
///
/// the interface types are:
///
/// interfaceType(Foo) = Foo
/// interfaceType(Bar) = Bar
/// interfaceType(Baz) = interfaceType(()->dynamic) = Function
/// interfaceType(T) = interfaceType(Bar) = Bar
/// interfaceType(S) = interfaceType(T) = interfaceType(Bar) = Bar
/// interfaceType(U) = interfaceType(Baz)
/// = intefaceType(()->dynamic) = Function
///
/// When typechecking `o.foo` the interface type of the static type of `o` is
/// used to lookup the existence and type of `foo`.
///
static ResolutionInterfaceType computeInterfaceType(
Resolution resolution, ResolutionDartType type) {
type = computeUnaliasedBound(resolution, type);
if (type.treatAsDynamic) {
return null;
}
if (type.isFunctionType) {
ResolutionInterfaceType functionType =
resolution.commonElements.functionType;
type = functionType;
}
assert(type.isInterfaceType,
failedAt(NO_LOCATION_SPANNABLE, "unexpected type kind ${type.kind}."));
return type;
}
}
/// Visitor used to compute an instantiation of a generic type that is more
/// specific than a given type.
///
/// The visitor tries to compute constraints for all type variables in the
/// visited type by structurally matching it with the argument type. If the
/// constraints are too complex or the two types are too different, `false`
/// is returned. Otherwise, the [constraintMap] holds the valid constraints.
class MoreSpecificSubtypeVisitor
extends BaseResolutionDartTypeVisitor<bool, ResolutionDartType> {
final Types types;
Map<ResolutionTypeVariableType, ResolutionDartType> constraintMap;
MoreSpecificSubtypeVisitor(this.types);
/// Compute an instance of [element] which is more specific than [supertype].
/// If no instance is found, `null` is returned.
///
/// Note that this computation is a heuristic. It does not find a suggestion
/// in all possible cases.
ResolutionInterfaceType computeMoreSpecific(
ClassElement element, ResolutionInterfaceType supertype) {
ResolutionInterfaceType supertypeInstance =
element.thisType.asInstanceOf(supertype.element);
if (supertypeInstance == null) return null;
constraintMap = new Map<ResolutionTypeVariableType, ResolutionDartType>();
element.typeVariables.forEach((ResolutionDartType typeVariable) {
constraintMap[typeVariable] = const ResolutionDynamicType();
});
if (supertypeInstance.accept(this, supertype)) {
List<ResolutionDartType> variables = element.typeVariables;
List<ResolutionDartType> typeArguments =
new List<ResolutionDartType>.generate(
variables.length, (int index) => constraintMap[variables[index]]);
return element.thisType.createInstantiation(typeArguments);
}
return null;
}
bool visitType(
covariant ResolutionDartType type, ResolutionDartType argument) {
return types.isMoreSpecific(type, argument);
}
bool visitTypes(List<ResolutionDartType> a, List<ResolutionDartType> b) {
int prefixLength = min(a.length, b.length);
for (int index = 0; index < prefixLength; index++) {
if (!a[index].accept(this, b[index])) return false;
}
return prefixLength == a.length && a.length == b.length;
}
bool visitTypeVariableType(
covariant ResolutionTypeVariableType type, ResolutionDartType argument) {
ResolutionDartType constraint =
types.getMostSpecific(constraintMap[type], argument);
constraintMap[type] = constraint;
return constraint != null;
}
bool visitFunctionType(
covariant ResolutionFunctionType type, ResolutionDartType argument) {
if (argument is ResolutionFunctionType) {
if (type.parameterTypes.length != argument.parameterTypes.length) {
return false;
}
if (type.optionalParameterTypes.length !=
argument.optionalParameterTypes.length) {
return false;
}
if (type.namedParameters != argument.namedParameters) {
return false;
}
if (!type.returnType.accept(this, argument.returnType)) return false;
if (visitTypes(type.parameterTypes, argument.parameterTypes)) {
return false;
}
if (visitTypes(
type.optionalParameterTypes, argument.optionalParameterTypes)) {
return false;
}
return visitTypes(type.namedParameterTypes, argument.namedParameterTypes);
}
return false;
}
bool visitGenericType(GenericType type, ResolutionDartType argument) {
if (argument is GenericType) {
if (type.element != argument.element) return false;
return visitTypes(type.typeArguments, argument.typeArguments);
}
return false;
}
}
/// Visitor used to print type annotation like they used in the source code.
/// The visitor is especially for printing a function type like
/// `(Foo,[Bar])->Baz` as `Baz m(Foo a1, [Bar a2])`.
class TypeDeclarationFormatter
extends BaseResolutionDartTypeVisitor<dynamic, String> {
Set<String> usedNames;
StringBuffer sb;
/// Creates textual representation of [type] as if a member by the [name] were
/// declared. For instance 'String foo' for `format(String, 'foo')`.
String format(ResolutionDartType type, String name) {
sb = new StringBuffer();
usedNames = new Set<String>();
type.accept(this, name);
usedNames = null;
return sb.toString();
}
String createName(String name) {
if (name != null && !usedNames.contains(name)) {
usedNames.add(name);
return name;
}
int index = usedNames.length;
String proposal;
do {
proposal = '${name}${index++}';
} while (usedNames.contains(proposal));
usedNames.add(proposal);
return proposal;
}
void visit(covariant ResolutionDartType type, [_]) {
type.accept(this, null);
}
void visitTypes(List<ResolutionDartType> types, String prefix) {
bool needsComma = false;
for (ResolutionDartType type in types) {
if (needsComma) {
sb.write(', ');
}
type.accept(this, prefix);
needsComma = true;
}
}
void visitType(covariant ResolutionDartType type, String name) {
if (name == null) {
sb.write(type);
} else {
sb.write('$type ${createName(name)}');
}
}
void visitGenericType(GenericType type, String name) {
sb.write(type.name);
if (!type.treatAsRaw) {
sb.write('<');
visitTypes(type.typeArguments, null);
sb.write('>');
}
if (name != null) {
sb.write(' ');
sb.write(createName(name));
}
}
void visitFunctionType(covariant ResolutionFunctionType type, String name) {
visit(type.returnType);
sb.write(' ');
if (name != null) {
sb.write(name);
} else {
sb.write(createName('f'));
}
sb.write('(');
visitTypes(type.parameterTypes, 'a');
bool needsComma = !type.parameterTypes.isEmpty;
if (!type.optionalParameterTypes.isEmpty) {
if (needsComma) {
sb.write(', ');
}
sb.write('[');
visitTypes(type.optionalParameterTypes, 'a');
sb.write(']');
needsComma = true;
}
if (!type.namedParameterTypes.isEmpty) {
if (needsComma) {
sb.write(', ');
}
sb.write('{');
List<String> namedParameters = type.namedParameters;
List<ResolutionDartType> namedParameterTypes = type.namedParameterTypes;
needsComma = false;
for (int index = 0; index < namedParameters.length; index++) {
if (needsComma) {
sb.write(', ');
}
namedParameterTypes[index].accept(this, namedParameters[index]);
needsComma = true;
}
sb.write('}');
}
sb.write(')');
}
}