| // 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. |
| |
| library dart_types; |
| |
| import 'dart:math' show min; |
| |
| import 'common/resolution.dart' show Resolution; |
| import 'common.dart'; |
| import 'core_types.dart'; |
| import 'elements/elements.dart'; |
| import 'elements/modelx.dart' show TypeDeclarationElementX; |
| import 'ordered_typeset.dart' show OrderedTypeSet; |
| import 'util/util.dart' show equalElements; |
| |
| enum TypeKind { |
| FUNCTION, |
| INTERFACE, |
| STATEMENT, |
| TYPEDEF, |
| TYPE_VARIABLE, |
| MALFORMED_TYPE, |
| DYNAMIC, |
| VOID, |
| } |
| |
| abstract class DartType { |
| String get name; |
| |
| TypeKind get kind; |
| |
| const DartType(); |
| |
| /** |
| * 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 [TypeVariableType] for a motivation for this method. |
| * |
| * Invariant: There must be the same number of [arguments] and [parameters]. |
| */ |
| DartType subst(List<DartType> arguments, List<DartType> parameters); |
| |
| /// Performs the substitution of the type arguments of [type] for their |
| /// corresponding type variables in this type. |
| DartType 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`. |
| DartType 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. |
| */ |
| DartType get userProvidedBadType => null; |
| |
| /// Is [: true :] if this type has no explict type arguments. |
| bool get isRaw => true; |
| |
| /// Returns the raw version of this type. |
| DartType 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 == TypeKind.DYNAMIC; |
| |
| /// Is [: true :] if this type is the void type. |
| bool get isVoid => kind == TypeKind.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 == TypeKind.INTERFACE; |
| |
| /// Is [: true :] if this type is a typedef. |
| bool get isTypedef => kind == TypeKind.TYPEDEF; |
| |
| /// Is [: true :] if this type is a function type. |
| bool get isFunctionType => kind == TypeKind.FUNCTION; |
| |
| /// Is [: true :] if this type is a type variable. |
| bool get isTypeVariable => kind == TypeKind.TYPE_VARIABLE; |
| |
| /// 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. |
| TypeVariableType get typeVariableOccurrence => null; |
| |
| /// Applies [f] to each occurence of a [TypeVariableType] within this type. |
| void forEachTypeVariable(f(TypeVariableType variable)) {} |
| |
| TypeVariableType _findTypeVariableOccurrence(List<DartType> types) { |
| for (DartType type in types) { |
| TypeVariableType 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); |
| } |
| |
| accept(DartTypeVisitor visitor, var argument); |
| |
| void visitChildren(DartTypeVisitor visitor, var argument) {} |
| |
| static void visitList( |
| List<DartType> types, DartTypeVisitor visitor, var argument) { |
| for (DartType type in types) { |
| type.accept(visitor, argument); |
| } |
| } |
| |
| /// Returns a [DartType] which corresponds to [this] except that each |
| /// contained [MethodTypeVariableType] is replaced by a [DynamicType]. |
| /// GENERIC_METHODS: Temporary, only used with '--generic-method-syntax'. |
| DartType 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 TypeVariableType extends DartType { |
| final TypeVariableElement element; |
| |
| TypeVariableType(this.element); |
| |
| TypeKind get kind => TypeKind.TYPE_VARIABLE; |
| |
| String get name => element.name; |
| |
| DartType subst(List<DartType> arguments, List<DartType> parameters) { |
| assert(arguments.length == parameters.length); |
| if (parameters.isEmpty) { |
| // Return fast on empty substitutions. |
| return this; |
| } |
| for (int index = 0; index < arguments.length; index++) { |
| TypeVariableType parameter = parameters[index]; |
| DartType argument = arguments[index]; |
| if (parameter == this) { |
| return argument; |
| } |
| } |
| // The type variable was not substituted. |
| return this; |
| } |
| |
| TypeVariableType get typeVariableOccurrence => this; |
| |
| void forEachTypeVariable(f(TypeVariableType variable)) { |
| f(this); |
| } |
| |
| accept(DartTypeVisitor visitor, var argument) { |
| return visitor.visitTypeVariableType(this, argument); |
| } |
| |
| int get hashCode => 17 * element.hashCode; |
| |
| bool operator ==(other) { |
| if (other is! TypeVariableType) return false; |
| return identical(other.element, element); |
| } |
| |
| String toString() => 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 TypeVariableType { |
| MethodTypeVariableType(TypeVariableElement element) : super(element); |
| |
| @override |
| bool get treatAsDynamic => true; |
| |
| @override |
| bool get isMalformed => true; |
| |
| @override |
| DartType get dynamifyMethodTypeVariableType => const DynamicType(); |
| |
| @override |
| get containsMethodTypeVariableType => true; |
| } |
| |
| /// Internal type representing the result of analyzing a statement. |
| class StatementType extends DartType { |
| Element get element => null; |
| |
| TypeKind get kind => TypeKind.STATEMENT; |
| |
| String get name => 'statement'; |
| |
| const StatementType(); |
| |
| DartType subst(List<DartType> arguments, List<DartType> parameters) => this; |
| |
| accept(DartTypeVisitor visitor, var argument) { |
| return visitor.visitStatementType(this, argument); |
| } |
| } |
| |
| class VoidType extends DartType { |
| const VoidType(); |
| |
| TypeKind get kind => TypeKind.VOID; |
| |
| String get name => 'void'; |
| |
| Element get element => null; |
| |
| DartType subst(List<DartType> arguments, List<DartType> parameters) { |
| // Void cannot be substituted. |
| return this; |
| } |
| |
| accept(DartTypeVisitor visitor, var argument) { |
| return visitor.visitVoidType(this, argument); |
| } |
| |
| String toString() => name; |
| |
| int get hashCode => 6007; |
| } |
| |
| class MalformedType extends DartType { |
| 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 DartType 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<DartType> typeArguments; |
| |
| final int hashCode = _nextHash = (_nextHash + 1).toUnsigned(30); |
| static int _nextHash = 43765; |
| |
| MalformedType(this.element, this.userProvidedBadType, |
| [this.typeArguments = null]); |
| |
| TypeKind get kind => TypeKind.MALFORMED_TYPE; |
| |
| String get name => element.name; |
| |
| DartType subst(List<DartType> arguments, List<DartType> parameters) { |
| // Malformed types are not substitutable. |
| return this; |
| } |
| |
| // Malformed types are treated as dynamic. |
| bool get treatAsDynamic => true; |
| |
| @override |
| bool get isMalformed => true; |
| |
| accept(DartTypeVisitor visitor, var 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 DartType { |
| final TypeDeclarationElement element; |
| final List<DartType> typeArguments; |
| |
| GenericType(TypeDeclarationElement element, List<DartType> typeArguments, |
| {bool checkTypeArgumentCount: true}) |
| : this.element = element, |
| this.typeArguments = typeArguments, |
| this.containsMethodTypeVariableType = |
| typeArguments.any(_typeContainsMethodTypeVariableType) { |
| assert(invariant(CURRENT_ELEMENT_SPANNABLE, element != null, |
| message: "Missing element for generic type.")); |
| assert(invariant(element, () { |
| if (!checkTypeArgumentCount) return true; |
| if (element is TypeDeclarationElementX) { |
| return element.thisTypeCache == null || |
| typeArguments.length == element.typeVariables.length; |
| } |
| return true; |
| }, |
| message: () => '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<DartType> newTypeArguments); |
| |
| DartType subst(List<DartType> arguments, List<DartType> 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<DartType> newTypeArguments = |
| Types.substTypes(typeArguments, arguments, parameters); |
| if (!identical(typeArguments, newTypeArguments)) { |
| // Create a new type only if necessary. |
| return createInstantiation(newTypeArguments); |
| } |
| return this; |
| } |
| |
| TypeVariableType get typeVariableOccurrence { |
| return _findTypeVariableOccurrence(typeArguments); |
| } |
| |
| void forEachTypeVariable(f(TypeVariableType variable)) { |
| for (DartType type in typeArguments) { |
| type.forEachTypeVariable(f); |
| } |
| } |
| |
| void visitChildren(DartTypeVisitor visitor, var argument) { |
| DartType.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 |
| DartType get dynamifyMethodTypeVariableType { |
| if (!containsMethodTypeVariableType) return this; |
| List<DartType> newTypeArguments = typeArguments |
| .map((DartType type) => type.dynamifyMethodTypeVariableType) |
| .toList(); |
| return createInstantiation(newTypeArguments); |
| } |
| |
| int get hashCode { |
| int hash = element.hashCode; |
| for (DartType 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 (DartType type in typeArguments) { |
| if (!type.treatAsDynamic) return false; |
| } |
| return true; |
| } |
| } |
| |
| class InterfaceType extends GenericType { |
| int _hashCode; |
| |
| InterfaceType(ClassElement element, |
| [List<DartType> typeArguments = const <DartType>[]]) |
| : super(element, typeArguments) { |
| assert(invariant(element, element.isDeclaration)); |
| } |
| |
| InterfaceType.forUserProvidedBadType(ClassElement element, |
| [List<DartType> typeArguments = const <DartType>[]]) |
| : super(element, typeArguments, checkTypeArgumentCount: false); |
| |
| ClassElement get element => super.element; |
| |
| TypeKind get kind => TypeKind.INTERFACE; |
| |
| String get name => element.name; |
| |
| bool get isObject => element.isObject; |
| |
| bool get isEnumType => element.isEnumClass; |
| |
| InterfaceType createInstantiation(List<DartType> newTypeArguments) { |
| return new InterfaceType(element, newTypeArguments); |
| } |
| |
| /** |
| * Returns the type as an instance of class [other], if possible, null |
| * otherwise. |
| */ |
| InterfaceType asInstanceOf(ClassElement other) { |
| other = other.declaration; |
| if (element == other) return this; |
| InterfaceType supertype = element.asInstanceOf(other); |
| if (supertype != null) { |
| List<DartType> arguments = Types.substTypes( |
| supertype.typeArguments, typeArguments, element.typeVariables); |
| return new InterfaceType(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; |
| |
| InterfaceType asRaw() => super.asRaw(); |
| |
| accept(DartTypeVisitor visitor, var 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. |
| FunctionType get callType { |
| FunctionType type = element.callType; |
| return type != null && isGeneric ? type.substByContext(this) : type; |
| } |
| } |
| |
| /** |
| * Special subclass of [InterfaceType] 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 InterfaceType { |
| final InterfaceType userProvidedBadType; |
| |
| BadInterfaceType(ClassElement element, InterfaceType this.userProvidedBadType) |
| : super(element, element.rawType.typeArguments); |
| |
| String toString() { |
| return userProvidedBadType.toString(); |
| } |
| } |
| |
| /** |
| * Special subclass of [TypedefType] 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 TypedefType { |
| final TypedefType userProvidedBadType; |
| |
| BadTypedefType(TypedefElement element, TypedefType this.userProvidedBadType) |
| : super(element, element.rawType.typeArguments); |
| |
| String toString() { |
| return userProvidedBadType.toString(); |
| } |
| } |
| |
| class FunctionType extends DartType { |
| final FunctionTypedElement element; |
| final DartType returnType; |
| final List<DartType> parameterTypes; |
| final List<DartType> 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<DartType> namedParameterTypes; |
| |
| factory FunctionType(FunctionTypedElement element, |
| [DartType returnType = const DynamicType(), |
| List<DartType> parameterTypes = const <DartType>[], |
| List<DartType> optionalParameterTypes = const <DartType>[], |
| List<String> namedParameters = const <String>[], |
| List<DartType> namedParameterTypes = const <DartType>[]]) { |
| assert(invariant(CURRENT_ELEMENT_SPANNABLE, element != null)); |
| assert(invariant(element, element.isDeclaration)); |
| return new FunctionType.internal(element, returnType, parameterTypes, |
| optionalParameterTypes, namedParameters, namedParameterTypes); |
| } |
| |
| factory FunctionType.synthesized( |
| [DartType returnType = const DynamicType(), |
| List<DartType> parameterTypes = const <DartType>[], |
| List<DartType> optionalParameterTypes = const <DartType>[], |
| List<String> namedParameters = const <String>[], |
| List<DartType> namedParameterTypes = const <DartType>[]]) { |
| return new FunctionType.internal(null, returnType, parameterTypes, |
| optionalParameterTypes, namedParameters, namedParameterTypes); |
| } |
| |
| FunctionType.internal(FunctionTypedElement this.element, |
| [DartType returnType = const DynamicType(), |
| List<DartType> parameterTypes = const <DartType>[], |
| List<DartType> optionalParameterTypes = const <DartType>[], |
| List<String> namedParameters = const <String>[], |
| List<DartType> namedParameterTypes = const <DartType>[]]) |
| : 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(invariant( |
| CURRENT_ELEMENT_SPANNABLE, element == null || element.isDeclaration)); |
| // Assert that optional and named parameters are not used at the same time. |
| assert(optionalParameterTypes.isEmpty || namedParameterTypes.isEmpty); |
| assert(namedParameters.length == namedParameterTypes.length); |
| } |
| |
| TypeKind get kind => TypeKind.FUNCTION; |
| |
| DartType getNamedParameterType(String name) { |
| for (int i = 0; i < namedParameters.length; i++) { |
| if (namedParameters[i] == name) { |
| return namedParameterTypes[i]; |
| } |
| } |
| return null; |
| } |
| |
| DartType subst(List<DartType> arguments, List<DartType> parameters) { |
| if (parameters.isEmpty) { |
| assert(arguments.isEmpty); |
| // Return fast on empty substitutions. |
| return this; |
| } |
| DartType newReturnType = returnType.subst(arguments, parameters); |
| bool changed = !identical(newReturnType, returnType); |
| List<DartType> newParameterTypes = |
| Types.substTypes(parameterTypes, arguments, parameters); |
| List<DartType> newOptionalParameterTypes = |
| Types.substTypes(optionalParameterTypes, arguments, parameters); |
| List<DartType> 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 FunctionType.internal( |
| element, |
| newReturnType, |
| newParameterTypes, |
| newOptionalParameterTypes, |
| namedParameters, |
| newNamedParameterTypes); |
| } |
| return this; |
| } |
| |
| TypeVariableType get typeVariableOccurrence { |
| TypeVariableType 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(TypeVariableType variable)) { |
| returnType.forEachTypeVariable(f); |
| parameterTypes.forEach((DartType type) { |
| type.forEachTypeVariable(f); |
| }); |
| optionalParameterTypes.forEach((DartType type) { |
| type.forEachTypeVariable(f); |
| }); |
| namedParameterTypes.forEach((DartType type) { |
| type.forEachTypeVariable(f); |
| }); |
| } |
| |
| accept(DartTypeVisitor visitor, var argument) { |
| return visitor.visitFunctionType(this, argument); |
| } |
| |
| void visitChildren(DartTypeVisitor visitor, var argument) { |
| returnType.accept(visitor, argument); |
| DartType.visitList(parameterTypes, visitor, argument); |
| DartType.visitList(optionalParameterTypes, visitor, argument); |
| DartType.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'; |
| |
| int computeArity() => parameterTypes.length; |
| |
| @override |
| DartType get dynamifyMethodTypeVariableType { |
| if (!containsMethodTypeVariableType) return this; |
| DartType eraseIt(DartType type) => type.dynamifyMethodTypeVariableType; |
| DartType newReturnType = returnType.dynamifyMethodTypeVariableType; |
| List<DartType> newParameterTypes = parameterTypes.map(eraseIt).toList(); |
| List<DartType> newOptionalParameterTypes = |
| optionalParameterTypes.map(eraseIt).toList(); |
| List<DartType> newNamedParameterTypes = |
| namedParameterTypes.map(eraseIt).toList(); |
| return new FunctionType.internal(element, newReturnType, newParameterTypes, |
| newOptionalParameterTypes, namedParameters, newNamedParameterTypes); |
| } |
| |
| @override |
| final bool containsMethodTypeVariableType; |
| |
| int get hashCode { |
| int hash = 3 * returnType.hashCode; |
| for (DartType parameter in parameterTypes) { |
| hash = 17 * hash + 5 * parameter.hashCode; |
| } |
| for (DartType parameter in optionalParameterTypes) { |
| hash = 19 * hash + 7 * parameter.hashCode; |
| } |
| for (String name in namedParameters) { |
| hash = 23 * hash + 11 * name.hashCode; |
| } |
| for (DartType parameter in namedParameterTypes) { |
| hash = 29 * hash + 13 * parameter.hashCode; |
| } |
| return hash; |
| } |
| |
| bool operator ==(other) { |
| if (other is! FunctionType) return false; |
| return returnType == other.returnType && |
| equalElements(parameterTypes, other.parameterTypes) && |
| equalElements(optionalParameterTypes, other.optionalParameterTypes) && |
| equalElements(namedParameters, other.namedParameters) && |
| equalElements(namedParameterTypes, other.namedParameterTypes); |
| } |
| } |
| |
| bool _typeContainsMethodTypeVariableType(DartType type) => |
| type.containsMethodTypeVariableType; |
| |
| class TypedefType extends GenericType { |
| DartType _unaliased; |
| |
| TypedefType(TypedefElement element, |
| [List<DartType> typeArguments = const <DartType>[]]) |
| : super(element, typeArguments); |
| |
| TypedefType.forUserProvidedBadType(TypedefElement element, |
| [List<DartType> typeArguments = const <DartType>[]]) |
| : super(element, typeArguments, checkTypeArgumentCount: false); |
| |
| TypedefElement get element => super.element; |
| |
| TypeKind get kind => TypeKind.TYPEDEF; |
| |
| String get name => element.name; |
| |
| TypedefType createInstantiation(List<DartType> newTypeArguments) { |
| return new TypedefType(element, newTypeArguments); |
| } |
| |
| void computeUnaliased(Resolution resolution) { |
| if (_unaliased == null) { |
| element.ensureResolved(resolution); |
| if (element.isMalformed) { |
| _unaliased = const DynamicType(); |
| return; |
| } |
| element.checkCyclicReference(resolution); |
| element.alias.computeUnaliased(resolution); |
| _unaliased = element.alias.unaliased.substByContext(this); |
| } |
| } |
| |
| DartType get unaliased { |
| if (_unaliased == null) { |
| DartType definition = element.alias.unaliased; |
| _unaliased = definition.substByContext(this); |
| } |
| return _unaliased; |
| } |
| |
| int get hashCode => super.hashCode; |
| |
| TypedefType asRaw() => super.asRaw(); |
| |
| accept(DartTypeVisitor visitor, var argument) { |
| return visitor.visitTypedefType(this, argument); |
| } |
| } |
| |
| /** |
| * Special type for the `dynamic` type. |
| */ |
| class DynamicType extends DartType { |
| const DynamicType(); |
| |
| Element get element => null; |
| |
| String get name => 'dynamic'; |
| |
| bool get treatAsDynamic => true; |
| |
| TypeKind get kind => TypeKind.DYNAMIC; |
| |
| DartType subst(List<DartType> arguments, List<DartType> parameters) => this; |
| |
| accept(DartTypeVisitor visitor, var 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 InterfaceType instance; |
| final MemberSignature member; |
| |
| InterfaceMember(this.instance, this.member); |
| |
| Name get name => member.name; |
| |
| DartType get type => member.type.substByContext(instance); |
| |
| FunctionType 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 DartTypeVisitor<R, A> { |
| const DartTypeVisitor(); |
| |
| R visit(DartType type, A argument) => type.accept(this, argument); |
| |
| R visitVoidType(VoidType type, A argument) => null; |
| |
| R visitTypeVariableType(TypeVariableType type, A argument) => null; |
| |
| R visitFunctionType(FunctionType type, A argument) => null; |
| |
| R visitMalformedType(MalformedType type, A argument) => null; |
| |
| R visitStatementType(StatementType type, A argument) => null; |
| |
| R visitInterfaceType(InterfaceType type, A argument) => null; |
| |
| R visitTypedefType(TypedefType type, A argument) => null; |
| |
| R visitDynamicType(DynamicType type, A argument) => null; |
| } |
| |
| abstract class BaseDartTypeVisitor<R, A> extends DartTypeVisitor<R, A> { |
| const BaseDartTypeVisitor(); |
| |
| R visitType(DartType type, A argument); |
| |
| @override |
| R visitVoidType(VoidType type, A argument) => visitType(type, argument); |
| |
| @override |
| R visitTypeVariableType(TypeVariableType type, A argument) => |
| visitType(type, argument); |
| |
| @override |
| R visitFunctionType(FunctionType type, A argument) => |
| visitType(type, argument); |
| |
| @override |
| R visitMalformedType(MalformedType type, A argument) => |
| visitType(type, argument); |
| |
| @override |
| R visitStatementType(StatementType type, A argument) => |
| visitType(type, argument); |
| |
| R visitGenericType(GenericType type, A argument) => visitType(type, argument); |
| |
| @override |
| R visitInterfaceType(InterfaceType type, A argument) => |
| visitGenericType(type, argument); |
| |
| @override |
| R visitTypedefType(TypedefType type, A argument) => |
| visitGenericType(type, argument); |
| |
| @override |
| R visitDynamicType(DynamicType type, A argument) => visitType(type, argument); |
| } |
| |
| /** |
| * Abstract visitor for determining relations between types. |
| */ |
| abstract class AbstractTypeRelation |
| extends BaseDartTypeVisitor<bool, DartType> { |
| final Resolution resolution; |
| |
| AbstractTypeRelation(this.resolution); |
| |
| CoreTypes get coreTypes => resolution.coreTypes; |
| |
| bool visitType(DartType t, DartType s) { |
| throw 'internal error: unknown type kind ${t.kind}'; |
| } |
| |
| bool visitVoidType(VoidType t, DartType s) { |
| assert(s is! VoidType); |
| return false; |
| } |
| |
| bool invalidTypeArguments(DartType t, DartType s); |
| |
| bool invalidFunctionReturnTypes(DartType t, DartType s); |
| |
| bool invalidFunctionParameterTypes(DartType t, DartType s); |
| |
| bool invalidTypeVariableBounds(DartType bound, DartType s); |
| |
| bool invalidCallableType(DartType callType, DartType s); |
| |
| /// Handle as dynamic for both subtype and more specific relation to avoid |
| /// spurious errors from malformed types. |
| bool visitMalformedType(MalformedType t, DartType s) => true; |
| |
| bool visitInterfaceType(InterfaceType t, DartType s) { |
| // TODO(johnniwinther): Currently needed since literal types like int, |
| // double, bool etc. might not have been resolved yet. |
| t.element.ensureResolved(resolution); |
| |
| bool checkTypeArguments(InterfaceType instance, InterfaceType other) { |
| List<DartType> tTypeArgs = instance.typeArguments; |
| List<DartType> sTypeArgs = other.typeArguments; |
| assert(tTypeArgs.length == sTypeArgs.length); |
| for (int i = 0; i < tTypeArgs.length; i++) { |
| if (invalidTypeArguments(tTypeArgs[i], sTypeArgs[i])) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| if (s is InterfaceType) { |
| InterfaceType instance = t.asInstanceOf(s.element); |
| if (instance != null && checkTypeArguments(instance, s)) { |
| return true; |
| } |
| } |
| |
| if (s == coreTypes.functionType && t.element.callType != null) { |
| return true; |
| } else if (s is FunctionType) { |
| FunctionType callType = t.callType; |
| return callType != null && !invalidCallableType(callType, s); |
| } |
| |
| return false; |
| } |
| |
| bool visitFunctionType(FunctionType t, DartType s) { |
| if (s == coreTypes.functionType) { |
| return true; |
| } |
| if (s is! FunctionType) return false; |
| FunctionType tf = t; |
| FunctionType sf = s; |
| if (invalidFunctionReturnTypes(tf.returnType, sf.returnType)) { |
| return false; |
| } |
| |
| // TODO(johnniwinther): Rewrite the function subtyping to be more readable |
| // but still as efficient. |
| |
| // For the comments we use the following abbreviations: |
| // x.p : parameterTypes on [:x:], |
| // x.o : optionalParameterTypes on [:x:], and |
| // len(xs) : length of list [:xs:]. |
| |
| Iterator<DartType> tps = tf.parameterTypes.iterator; |
| Iterator<DartType> sps = sf.parameterTypes.iterator; |
| bool sNotEmpty = sps.moveNext(); |
| bool tNotEmpty = tps.moveNext(); |
| tNext() => (tNotEmpty = tps.moveNext()); |
| sNext() => (sNotEmpty = sps.moveNext()); |
| |
| bool incompatibleParameters() { |
| while (tNotEmpty && sNotEmpty) { |
| if (invalidFunctionParameterTypes(tps.current, sps.current)) { |
| return true; |
| } |
| tNext(); |
| sNext(); |
| } |
| return false; |
| } |
| |
| if (incompatibleParameters()) return false; |
| if (tNotEmpty) { |
| // We must have [: len(t.p) <= len(s.p) :]. |
| return false; |
| } |
| if (!sf.namedParameters.isEmpty) { |
| // We must have [: len(t.p) == len(s.p) :]. |
| if (sNotEmpty) { |
| return false; |
| } |
| // Since named parameters are globally ordered we can determine the |
| // subset relation with a linear search for [:sf.namedParameters:] |
| // within [:tf.namedParameters:]. |
| List<String> tNames = tf.namedParameters; |
| List<DartType> tTypes = tf.namedParameterTypes; |
| List<String> sNames = sf.namedParameters; |
| List<DartType> sTypes = sf.namedParameterTypes; |
| int tIndex = 0; |
| int sIndex = 0; |
| while (tIndex < tNames.length && sIndex < sNames.length) { |
| if (tNames[tIndex] == sNames[sIndex]) { |
| if (invalidFunctionParameterTypes(tTypes[tIndex], sTypes[sIndex])) { |
| return false; |
| } |
| sIndex++; |
| } |
| tIndex++; |
| } |
| if (sIndex < sNames.length) { |
| // We didn't find all names. |
| return false; |
| } |
| } else { |
| // Check the remaining [: s.p :] against [: t.o :]. |
| tps = tf.optionalParameterTypes.iterator; |
| tNext(); |
| if (incompatibleParameters()) return false; |
| if (sNotEmpty) { |
| // We must have [: len(t.p) + len(t.o) >= len(s.p) :]. |
| return false; |
| } |
| if (!sf.optionalParameterTypes.isEmpty) { |
| // Check the remaining [: s.o :] against the remaining [: t.o :]. |
| sps = sf.optionalParameterTypes.iterator; |
| sNext(); |
| if (incompatibleParameters()) return false; |
| if (sNotEmpty) { |
| // We didn't find enough parameters: |
| // We must have [: len(t.p) + len(t.o) <= len(s.p) + len(s.o) :]. |
| return false; |
| } |
| } else { |
| if (sNotEmpty) { |
| // We must have [: len(t.p) + len(t.o) >= len(s.p) :]. |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| bool visitTypeVariableType(TypeVariableType t, DartType s) { |
| // Identity check is handled in [isSubtype]. |
| DartType bound = t.element.bound; |
| if (bound.isTypeVariable) { |
| // The bound is potentially cyclic so we need to be extra careful. |
| Set<TypeVariableElement> seenTypeVariables = |
| new Set<TypeVariableElement>(); |
| seenTypeVariables.add(t.element); |
| while (bound.isTypeVariable) { |
| TypeVariableElement element = bound.element; |
| if (identical(bound.element, s.element)) { |
| // [t] extends [s]. |
| return true; |
| } |
| if (seenTypeVariables.contains(element)) { |
| // We have a cycle and have already checked all bounds in the cycle |
| // against [s] and can therefore conclude that [t] is not a subtype |
| // of [s]. |
| return false; |
| } |
| seenTypeVariables.add(element); |
| bound = element.bound; |
| } |
| } |
| if (invalidTypeVariableBounds(bound, s)) return false; |
| return true; |
| } |
| } |
| |
| class MoreSpecificVisitor extends AbstractTypeRelation { |
| MoreSpecificVisitor(Resolution resolution) : super(resolution); |
| |
| bool isMoreSpecific(DartType t, DartType s) { |
| if (identical(t, s) || s.treatAsDynamic || t == coreTypes.nullType) { |
| return true; |
| } |
| if (t.isVoid || s.isVoid) { |
| return false; |
| } |
| if (t.treatAsDynamic) { |
| return false; |
| } |
| if (s == coreTypes.objectType) { |
| return true; |
| } |
| t.computeUnaliased(resolution); |
| t = t.unaliased; |
| s.computeUnaliased(resolution); |
| s = s.unaliased; |
| |
| return t.accept(this, s); |
| } |
| |
| bool invalidTypeArguments(DartType t, DartType s) { |
| return !isMoreSpecific(t, s); |
| } |
| |
| bool invalidFunctionReturnTypes(DartType t, DartType s) { |
| if (s.treatAsDynamic && t.isVoid) return true; |
| return !s.isVoid && !isMoreSpecific(t, s); |
| } |
| |
| bool invalidFunctionParameterTypes(DartType t, DartType s) { |
| return !isMoreSpecific(t, s); |
| } |
| |
| bool invalidTypeVariableBounds(DartType bound, DartType s) { |
| return !isMoreSpecific(bound, s); |
| } |
| |
| bool invalidCallableType(DartType callType, DartType s) { |
| return !isMoreSpecific(callType, s); |
| } |
| } |
| |
| /** |
| * Type visitor that determines the subtype relation two types. |
| */ |
| class SubtypeVisitor extends MoreSpecificVisitor { |
| SubtypeVisitor(Resolution resolution) : super(resolution); |
| |
| bool isSubtype(DartType t, DartType s) { |
| return t.treatAsDynamic || isMoreSpecific(t, s); |
| } |
| |
| bool isAssignable(DartType t, DartType s) { |
| return isSubtype(t, s) || isSubtype(s, t); |
| } |
| |
| bool invalidTypeArguments(DartType t, DartType s) { |
| return !isSubtype(t, s); |
| } |
| |
| bool invalidFunctionReturnTypes(DartType t, DartType s) { |
| return !s.isVoid && !isAssignable(t, s); |
| } |
| |
| bool invalidFunctionParameterTypes(DartType t, DartType s) { |
| return !isAssignable(t, s); |
| } |
| |
| bool invalidTypeVariableBounds(DartType bound, DartType s) { |
| return !isSubtype(bound, s); |
| } |
| |
| bool invalidCallableType(DartType callType, DartType s) { |
| return !isSubtype(callType, s); |
| } |
| } |
| |
| /** |
| * 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); |
| |
| /// Basic interface for the Dart type system. |
| abstract class DartTypes { |
| /// The types defined in 'dart:core'. |
| CoreTypes get coreTypes; |
| |
| /// Returns `true` if [t] is a subtype of [s]. |
| bool isSubtype(DartType t, DartType s); |
| |
| /// Returns `true` if [t] might be a subtype of [s] for some values of |
| /// type variables in [s] and [t]. |
| bool isPotentialSubtype(DartType t, DartType s); |
| } |
| |
| class Types implements DartTypes { |
| final Resolution resolution; |
| final MoreSpecificVisitor moreSpecificVisitor; |
| final SubtypeVisitor subtypeVisitor; |
| final PotentialSubtypeVisitor potentialSubtypeVisitor; |
| |
| CoreTypes get coreTypes => resolution.coreTypes; |
| |
| DiagnosticReporter get reporter => resolution.reporter; |
| |
| Types(Resolution resolution) |
| : this.resolution = resolution, |
| this.moreSpecificVisitor = new MoreSpecificVisitor(resolution), |
| this.subtypeVisitor = new SubtypeVisitor(resolution), |
| this.potentialSubtypeVisitor = new PotentialSubtypeVisitor(resolution); |
| |
| Types copy(Resolution resolution) { |
| return new Types(resolution); |
| } |
| |
| /// 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. |
| DartType flatten(DartType type) { |
| if (type is InterfaceType) { |
| if (type.element == coreTypes.futureClass) { |
| // T = Future<S> |
| return flatten(type.typeArguments.first); |
| } |
| InterfaceType futureType = type.asInstanceOf(coreTypes.futureClass); |
| if (futureType != null) { |
| // T << Future<S> |
| return futureType.typeArguments.single; |
| } |
| } |
| return type; |
| } |
| |
| /// Returns true if [t] is more specific than [s]. |
| bool isMoreSpecific(DartType t, DartType 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. |
| DartType getMostSpecific(DartType t, DartType 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(DartType t, DartType s) { |
| return subtypeVisitor.isSubtype(t, s); |
| } |
| |
| bool isAssignable(DartType r, DartType s) { |
| return subtypeVisitor.isAssignable(r, s); |
| } |
| |
| static const int IS_SUBTYPE = 1; |
| static const int MAYBE_SUBTYPE = 0; |
| static const int NOT_SUBTYPE = -1; |
| |
| int computeSubtypeRelation(DartType t, DartType s) { |
| // TODO(johnniwinther): Compute this directly in [isPotentialSubtype]. |
| if (isSubtype(t, s)) return IS_SUBTYPE; |
| return isPotentialSubtype(t, s) ? MAYBE_SUBTYPE : NOT_SUBTYPE; |
| } |
| |
| bool isPotentialSubtype(DartType t, DartType s) { |
| // TODO(johnniwinther): Return a set of variable points in the positive |
| // cases. |
| return potentialSubtypeVisitor.isSubtype(t, s); |
| } |
| |
| /** |
| * Checks the type arguments of [type] against the type variable bounds |
| * declared on [element]. Calls [checkTypeVariableBound] on each type |
| * argument and bound. |
| */ |
| void checkTypeVariableBounds( |
| GenericType type, CheckTypeVariableBound checkTypeVariableBound) { |
| TypeDeclarationElement element = type.element; |
| List<DartType> typeArguments = type.typeArguments; |
| List<DartType> typeVariables = element.typeVariables; |
| assert(typeVariables.length == typeArguments.length); |
| for (int index = 0; index < typeArguments.length; index++) { |
| TypeVariableType typeVariable = typeVariables[index]; |
| DartType bound = typeVariable.element.bound.substByContext(type); |
| DartType 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<DartType> substTypes(List<DartType> types, |
| List<DartType> arguments, List<DartType> parameters) { |
| bool changed = false; |
| List<DartType> result = new List<DartType>.generate(types.length, (index) { |
| DartType type = types[index]; |
| DartType 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; |
| } |
| |
| /** |
| * Returns the [ClassElement] which declares the type variables occurring in |
| * [type], or [:null:] if [type] does not contain type variables. |
| */ |
| static ClassElement getClassContext(DartType type) { |
| ClassElement contextClass; |
| type.forEachTypeVariable((TypeVariableType typeVariable) { |
| if (typeVariable.element.typeDeclaration is! ClassElement) return; |
| contextClass = typeVariable.element.typeDeclaration; |
| }); |
| // GENERIC_METHODS: When generic method support is complete enough to |
| // include a runtime value for method type variables this must be updated. |
| // For full support the global assumption that all type variables are |
| // declared by the same enclosing class will not hold: Both an enclosing |
| // method and an enclosing class may define type variables, so the return |
| // type cannot be [ClassElement] and the caller must be prepared to look in |
| // two locations, not one. Currently we ignore method type variables by |
| // returning in the next statement. |
| return contextClass; |
| } |
| |
| /** |
| * 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(DartType a, DartType 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(DartType 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) { |
| FunctionType aFunc = a; |
| FunctionType 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; |
| } |
| if (a.kind == TypeKind.STATEMENT) { |
| if (b.kind == TypeKind.STATEMENT) { |
| return 0; |
| } else { |
| // [b] is a malformed type => a > b. |
| return 1; |
| } |
| } else if (b.kind == TypeKind.STATEMENT) { |
| // [a] is a malformed 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<DartType> a, List<DartType> 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<DartType> sorted(Iterable<DartType> types) { |
| return types.toList()..sort(compare); |
| } |
| |
| /// Computes the least upper bound of two interface types [a] and [b]. |
| InterfaceType computeLeastUpperBoundInterfaces( |
| InterfaceType a, InterfaceType b) { |
| /// Returns the set of supertypes of [type] at depth [depth]. |
| Set<DartType> getSupertypesAtDepth(InterfaceType type, int depth) { |
| OrderedTypeSet types = type.element.allSupertypesAndSelf; |
| Set<DartType> set = new Set<DartType>(); |
| types.forEach(depth, (DartType 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<DartType> aTypeSet = getSupertypesAtDepth(a, depth); |
| Set<DartType> bTypeSet = getSupertypesAtDepth(b, depth); |
| Set<DartType> 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<DartType> computeLeastUpperBoundsTypes( |
| List<DartType> a, List<DartType> b) { |
| if (a.isEmpty || b.isEmpty) return const <DartType>[]; |
| int prefixLength = min(a.length, b.length); |
| List<DartType> types = new List<DartType>(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]. |
| DartType computeLeastUpperBoundFunctionTypes(FunctionType a, FunctionType b) { |
| if (a.parameterTypes.length != b.parameterTypes.length) { |
| return coreTypes.functionType; |
| } |
| DartType returnType = computeLeastUpperBound(a.returnType, b.returnType); |
| List<DartType> parameterTypes = |
| computeLeastUpperBoundsTypes(a.parameterTypes, b.parameterTypes); |
| List<DartType> optionalParameterTypes = computeLeastUpperBoundsTypes( |
| a.optionalParameterTypes, b.optionalParameterTypes); |
| List<String> namedParameters = <String>[]; |
| List<String> aNamedParameters = a.namedParameters; |
| List<String> bNamedParameters = b.namedParameters; |
| List<DartType> namedParameterTypes = <DartType>[]; |
| List<DartType> aNamedParameterTypes = a.namedParameterTypes; |
| List<DartType> 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 FunctionType.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. |
| DartType computeLeastUpperBoundTypeVariableTypes(DartType a, DartType b) { |
| Set<DartType> typeVariableBounds = new Set<DartType>(); |
| 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]. |
| DartType computeLeastUpperBound(DartType a, DartType 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 DynamicType(); |
| if (a.isVoid || b.isVoid) return const VoidType(); |
| |
| if (a.isFunctionType && b.isFunctionType) { |
| return computeLeastUpperBoundFunctionTypes(a, b); |
| } |
| |
| if (a.isFunctionType) { |
| a = coreTypes.functionType; |
| } |
| if (b.isFunctionType) { |
| b = coreTypes.functionType; |
| } |
| |
| if (a.isInterfaceType && b.isInterfaceType) { |
| return computeLeastUpperBoundInterfaces(a, b); |
| } |
| return const DynamicType(); |
| } |
| |
| /// 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 DartType computeUnaliasedBound(Resolution resolution, DartType type) { |
| DartType originalType = type; |
| while (type.isTypeVariable) { |
| TypeVariableType variable = type; |
| type = variable.element.bound; |
| if (type == originalType) { |
| type = resolution.coreTypes.objectType; |
| } |
| } |
| if (type.isMalformed) { |
| return const DynamicType(); |
| } |
| 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 InterfaceType computeInterfaceType( |
| Resolution resolution, DartType type) { |
| type = computeUnaliasedBound(resolution, type); |
| if (type.treatAsDynamic) { |
| return null; |
| } |
| if (type.isFunctionType) { |
| type = resolution.coreTypes.functionType; |
| } |
| assert(invariant(NO_LOCATION_SPANNABLE, type.isInterfaceType, |
| message: "unexpected type kind ${type.kind}.")); |
| return type; |
| } |
| } |
| |
| /** |
| * Type visitor that determines one type could a subtype of another given the |
| * right type variable substitution. The computation is approximate and returns |
| * [:false:] only if we are sure no such substitution exists. |
| */ |
| class PotentialSubtypeVisitor extends SubtypeVisitor { |
| PotentialSubtypeVisitor(Resolution resolution) : super(resolution); |
| |
| bool isSubtype(DartType t, DartType s) { |
| if (t is TypeVariableType || s is TypeVariableType) { |
| return true; |
| } |
| return super.isSubtype(t, s); |
| } |
| } |
| |
| /// 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 BaseDartTypeVisitor<bool, DartType> { |
| final Types types; |
| Map<TypeVariableType, DartType> 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. |
| InterfaceType computeMoreSpecific( |
| ClassElement element, InterfaceType supertype) { |
| InterfaceType supertypeInstance = |
| element.thisType.asInstanceOf(supertype.element); |
| if (supertypeInstance == null) return null; |
| |
| constraintMap = new Map<TypeVariableType, DartType>(); |
| element.typeVariables.forEach((TypeVariableType typeVariable) { |
| constraintMap[typeVariable] = const DynamicType(); |
| }); |
| if (supertypeInstance.accept(this, supertype)) { |
| List<DartType> variables = element.typeVariables; |
| List<DartType> typeArguments = new List<DartType>.generate( |
| variables.length, (int index) => constraintMap[variables[index]]); |
| return element.thisType.createInstantiation(typeArguments); |
| } |
| return null; |
| } |
| |
| bool visitType(DartType type, DartType argument) { |
| return types.isMoreSpecific(type, argument); |
| } |
| |
| bool visitTypes(List<DartType> a, List<DartType> 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(TypeVariableType type, DartType argument) { |
| DartType constraint = types.getMostSpecific(constraintMap[type], argument); |
| constraintMap[type] = constraint; |
| return constraint != null; |
| } |
| |
| bool visitFunctionType(FunctionType type, DartType argument) { |
| if (argument is FunctionType) { |
| 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, DartType 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 BaseDartTypeVisitor<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(DartType 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(DartType type, [_]) { |
| type.accept(this, null); |
| } |
| |
| void visitTypes(List<DartType> types, String prefix) { |
| bool needsComma = false; |
| for (DartType type in types) { |
| if (needsComma) { |
| sb.write(', '); |
| } |
| type.accept(this, prefix); |
| needsComma = true; |
| } |
| } |
| |
| void visitType(DartType 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(FunctionType 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<DartType> 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(')'); |
| } |
| } |