| // 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; |
| |
| /// 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; |
| |
| ResolutionMoreSpecificVisitor(this.resolution); |
| } |
| |
| class ResolutionSubtypeVisitor extends SubtypeVisitor<ResolutionDartType> |
| with AbstractTypeRelationMixin { |
| final Resolution resolution; |
| |
| ResolutionSubtypeVisitor(this.resolution); |
| } |
| |
| class ResolutionPotentialSubtypeVisitor |
| extends PotentialSubtypeVisitor<ResolutionDartType> |
| with AbstractTypeRelationMixin { |
| final Resolution resolution; |
| |
| 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(')'); |
| } |
| } |