| Index: pkg/analyzer/lib/src/generated/element.dart
|
| diff --git a/pkg/analyzer/lib/src/generated/element.dart b/pkg/analyzer/lib/src/generated/element.dart
|
| index 292474893f94a9ba2e4ddc3505c0abec475bac8c..6f8d8d2e935bb79c8340b1d49045b7d7df1bbd65 100644
|
| --- a/pkg/analyzer/lib/src/generated/element.dart
|
| +++ b/pkg/analyzer/lib/src/generated/element.dart
|
| @@ -4540,6 +4540,15 @@ class FunctionElementImpl extends ExecutableElementImpl
|
| */
|
| abstract class FunctionType implements ParameterizedType {
|
| /**
|
| + * The type parameters of this generic function. For example `<T> T -> T`.
|
| + *
|
| + * These are distinct from the [typeParameters] list, which contains type
|
| + * parameters from surrounding contexts, and thus are free type variables from
|
| + * the perspective of this function type.
|
| + */
|
| + List<TypeParameterElement> get boundTypeParameters;
|
| +
|
| + /**
|
| * Return a map from the names of named parameters to the types of the named
|
| * parameters of this type of function. The entries in the map will be
|
| * iterated in the same order as the order in which the named parameters were
|
| @@ -4577,6 +4586,12 @@ abstract class FunctionType implements ParameterizedType {
|
| DartType get returnType;
|
|
|
| /**
|
| + * Return the type resulting from instantiating (replacing) the given
|
| + * [argumentTypes] for this function's bound type parameters.
|
| + */
|
| + FunctionType instantiate(List<DartType> argumentTypes);
|
| +
|
| + /**
|
| * Return `true` if this type is a subtype of the given [type].
|
| *
|
| * A function type <i>(T<sub>1</sub>, …, T<sub>n</sub>) → T</i> is
|
| @@ -4647,6 +4662,7 @@ abstract class FunctionType implements ParameterizedType {
|
| * this type's parameters. This is fully equivalent to
|
| * `substitute(argumentTypes, getTypeArguments())`.
|
| */
|
| + @deprecated // use instantiate
|
| FunctionType substitute3(List<DartType> argumentTypes);
|
| }
|
|
|
| @@ -4844,7 +4860,17 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
|
| /**
|
| * The list of [typeArguments].
|
| */
|
| - List<DartType> _typeArguments = DartType.EMPTY_LIST;
|
| + List<DartType> _typeArguments;
|
| +
|
| + /**
|
| + * The list of [typeParameters].
|
| + */
|
| + List<TypeParameterElement> _typeParameters;
|
| +
|
| + /**
|
| + * The list of [boundTypeParameters].
|
| + */
|
| + List<TypeParameterElement> _boundTypeParameters;
|
|
|
| /**
|
| * The set of typedefs which should not be expanded when exploring this type,
|
| @@ -4859,7 +4885,7 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
|
| */
|
| FunctionTypeImpl(ExecutableElement element,
|
| [List<FunctionTypeAliasElement> prunedTypedefs])
|
| - : this._(element, null, prunedTypedefs, null);
|
| + : this._(element, null, prunedTypedefs, null, null, null);
|
|
|
| /**
|
| * Initialize a newly created function type to be declared by the given
|
| @@ -4882,29 +4908,51 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
|
| */
|
| FunctionTypeImpl.forTypedef(FunctionTypeAliasElement element,
|
| [List<FunctionTypeAliasElement> prunedTypedefs])
|
| - : this._(element, element?.name, prunedTypedefs, null);
|
| + : this._(element, element?.name, prunedTypedefs, null, null, null);
|
|
|
| /**
|
| * Private constructor.
|
| */
|
| - FunctionTypeImpl._(Element element, String name, this.prunedTypedefs,
|
| - List<DartType> typeArguments)
|
| + FunctionTypeImpl._(
|
| + TypeParameterizedElement element,
|
| + String name,
|
| + this.prunedTypedefs,
|
| + List<DartType> typeArguments,
|
| + List<TypeParameterElement> typeParameters,
|
| + List<TypeParameterElement> boundTypeParameters)
|
| : super(element, name) {
|
| - if (typeArguments != null) {
|
| - _typeArguments = typeArguments;
|
| - } else {
|
| - List<TypeParameterElement> typeParameters = this.typeParameters;
|
| + _boundTypeParameters = boundTypeParameters ??
|
| + element?.typeParameters ??
|
| + TypeParameterElement.EMPTY_LIST;
|
| +
|
| + if (typeParameters == null) {
|
| + // Combine the generic type variables from all enclosing contexts, except
|
| + // for this generic function's type variables. Those variables are
|
| + // tracked in [boundTypeParameters].
|
| + typeParameters = <TypeParameterElement>[];
|
| + Element e = element?.enclosingElement;
|
| + while (e != null) {
|
| + if (e is TypeParameterizedElement) {
|
| + typeParameters.addAll((e as TypeParameterizedElement).typeParameters);
|
| + }
|
| + e = e.enclosingElement;
|
| + }
|
| + }
|
| + _typeParameters = typeParameters;
|
| +
|
| + if (typeArguments == null) {
|
| // TODO(jmesserly): reuse TypeParameterTypeImpl.getTypes once we can
|
| // make it generic, which will allow it to return List<DartType> instead
|
| // of List<TypeParameterType>.
|
| if (typeParameters.isEmpty) {
|
| - _typeArguments = DartType.EMPTY_LIST;
|
| + typeArguments = DartType.EMPTY_LIST;
|
| } else {
|
| - _typeArguments = new List<DartType>.from(
|
| + typeArguments = new List<DartType>.from(
|
| typeParameters.map((t) => t.type),
|
| growable: false);
|
| }
|
| }
|
| + _typeArguments = typeArguments;
|
| }
|
|
|
| /**
|
| @@ -4932,6 +4980,9 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
|
| }
|
|
|
| @override
|
| + List<TypeParameterElement> get boundTypeParameters => _boundTypeParameters;
|
| +
|
| + @override
|
| String get displayName {
|
| String name = this.name;
|
| if (name == null || name.length == 0) {
|
| @@ -5163,18 +5214,7 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
|
| List<DartType> get typeArguments => _typeArguments;
|
|
|
| @override
|
| - List<TypeParameterElement> get typeParameters {
|
| - // Combine the generic type arguments from all enclosing contexts.
|
| - // For example, this could be a generic method in a class, or a local
|
| - // function within another function.
|
| - List<TypeParameterElement> typeParams = <TypeParameterElement>[];
|
| - for (Element e = element; e != null; e = e.enclosingElement) {
|
| - if (e is TypeParameterizedElement) {
|
| - typeParams.addAll(e.typeParameters);
|
| - }
|
| - }
|
| - return typeParams;
|
| - }
|
| + List<TypeParameterElement> get typeParameters => _typeParameters;
|
|
|
| @override
|
| bool operator ==(Object object) {
|
| @@ -5182,6 +5222,24 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
|
| return false;
|
| }
|
| FunctionTypeImpl otherType = object as FunctionTypeImpl;
|
| + if (boundTypeParameters.length != otherType.boundTypeParameters.length) {
|
| + return false;
|
| + }
|
| + // `<T>T -> T` should be equal to `<U>U -> U`
|
| + // To test this, we instantiate both types with the same (unique) type
|
| + // variables, and see if the result is equal.
|
| + if (boundTypeParameters.isNotEmpty) {
|
| + List<DartType> instantiateTypeArgs = new List<DartType>();
|
| + for (TypeParameterElement e in boundTypeParameters) {
|
| + instantiateTypeArgs.add(new TypeParameterTypeImpl(
|
| + new TypeParameterElementImpl(e.name, -1)));
|
| + }
|
| + // After instantiation, they will no longer have boundTypeParameters,
|
| + // so we will continue below.
|
| + return this.instantiate(instantiateTypeArgs) ==
|
| + otherType.instantiate(instantiateTypeArgs);
|
| + }
|
| +
|
| return returnType == otherType.returnType &&
|
| TypeImpl.equalArrays(
|
| normalParameterTypes, otherType.normalParameterTypes) &&
|
| @@ -5192,13 +5250,56 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
|
|
|
| @override
|
| void appendTo(StringBuffer buffer) {
|
| + if (boundTypeParameters.isNotEmpty) {
|
| + // To print a type with type variables, first make sure we have unique
|
| + // variable names to print.
|
| + Set<TypeParameterType> freeVariables = new HashSet<TypeParameterType>();
|
| + _freeVariablesInFunctionType(this, freeVariables);
|
| +
|
| + Set<String> namesToAvoid = new HashSet<String>();
|
| + for (DartType arg in freeVariables) {
|
| + if (arg is TypeParameterType) {
|
| + namesToAvoid.add(arg.displayName);
|
| + }
|
| + }
|
| +
|
| + List<DartType> instantiateTypeArgs = new List<DartType>();
|
| + buffer.write("<");
|
| + for (TypeParameterElement e in boundTypeParameters) {
|
| + if (e != boundTypeParameters[0]) {
|
| + buffer.write(",");
|
| + }
|
| + String name = e.name;
|
| + int counter = 0;
|
| + while (!namesToAvoid.add(name)) {
|
| + // Unicode subscript-zero is U+2080, zero is U+0030. Other digits
|
| + // are sequential from there. Thus +0x2050 will get us the subscript.
|
| + String subscript = new String.fromCharCodes(
|
| + counter.toString().codeUnits.map((n) => n + 0x2050));
|
| +
|
| + name = e.name + subscript;
|
| + counter++;
|
| + }
|
| + TypeParameterTypeImpl t =
|
| + new TypeParameterTypeImpl(new TypeParameterElementImpl(name, -1));
|
| + t.appendTo(buffer);
|
| + instantiateTypeArgs.add(t);
|
| + }
|
| + buffer.write(">");
|
| +
|
| + // Instantiate it and print the resulting type. After instantiation, it
|
| + // will no longer have boundTypeParameters, so we will continue below.
|
| + this.instantiate(instantiateTypeArgs).appendTo(buffer);
|
| + return;
|
| + }
|
| +
|
| List<DartType> normalParameterTypes = this.normalParameterTypes;
|
| List<DartType> optionalParameterTypes = this.optionalParameterTypes;
|
| Map<String, DartType> namedParameterTypes = this.namedParameterTypes;
|
| DartType returnType = this.returnType;
|
| buffer.write("(");
|
| bool needsComma = false;
|
| - if (normalParameterTypes.length > 0) {
|
| + if (normalParameterTypes.isNotEmpty) {
|
| for (DartType type in normalParameterTypes) {
|
| if (needsComma) {
|
| buffer.write(", ");
|
| @@ -5208,7 +5309,7 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
|
| (type as TypeImpl).appendTo(buffer);
|
| }
|
| }
|
| - if (optionalParameterTypes.length > 0) {
|
| + if (optionalParameterTypes.isNotEmpty) {
|
| if (needsComma) {
|
| buffer.write(", ");
|
| needsComma = false;
|
| @@ -5225,7 +5326,7 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
|
| buffer.write("]");
|
| needsComma = true;
|
| }
|
| - if (namedParameterTypes.length > 0) {
|
| + if (namedParameterTypes.isNotEmpty) {
|
| if (needsComma) {
|
| buffer.write(", ");
|
| needsComma = false;
|
| @@ -5254,6 +5355,33 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
|
| }
|
|
|
| @override
|
| + FunctionTypeImpl instantiate(List<DartType> argumentTypes) {
|
| + if (argumentTypes.length != boundTypeParameters.length) {
|
| + throw new IllegalArgumentException(
|
| + "argumentTypes.length (${argumentTypes.length}) != "
|
| + "boundTypeParameters.length (${boundTypeParameters.length})");
|
| + }
|
| + if (argumentTypes.isEmpty) {
|
| + return this;
|
| + }
|
| +
|
| + // Given:
|
| + // {U/T} <S> T -> S
|
| + // Where {U/T} represents the typeArguments (U) and typeParameters (T) list,
|
| + // and <S> represents the boundTypeParameters.
|
| + //
|
| + // Now instantiate([V]), and the result should be:
|
| + // {U/T, V/S} T -> S.
|
| + List<TypeParameterElement> newTypeParams = typeParameters.toList();
|
| + List<DartType> newTypeArgs = typeArguments.toList();
|
| + newTypeParams.addAll(boundTypeParameters);
|
| + newTypeArgs.addAll(argumentTypes);
|
| +
|
| + return new FunctionTypeImpl._(element, name, prunedTypedefs, newTypeArgs,
|
| + newTypeParams, TypeParameterElement.EMPTY_LIST);
|
| + }
|
| +
|
| + @override
|
| bool isAssignableTo(DartType type) {
|
| // A function type T may be assigned to a function type S, written T <=> S,
|
| // iff T <: S.
|
| @@ -5502,7 +5630,8 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
|
| List<DartType> typeArgs = typeArguments
|
| .map((TypeImpl t) => t.pruned(prune))
|
| .toList(growable: false);
|
| - return new FunctionTypeImpl._(element, name, prune, typeArgs);
|
| + return new FunctionTypeImpl._(element, name, prune, typeArgs,
|
| + _typeParameters, _boundTypeParameters);
|
| }
|
| }
|
|
|
| @@ -5528,13 +5657,51 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
|
| }
|
| List<DartType> typeArgs =
|
| TypeImpl.substitute(typeArguments, argumentTypes, parameterTypes);
|
| - return new FunctionTypeImpl._(element, name, prune, typeArgs);
|
| + return new FunctionTypeImpl._(
|
| + element, name, prune, typeArgs, _typeParameters, _boundTypeParameters);
|
| }
|
|
|
| @override
|
| FunctionTypeImpl substitute3(List<DartType> argumentTypes) =>
|
| substitute2(argumentTypes, typeArguments);
|
|
|
| + void _freeVariablesInFunctionType(
|
| + FunctionType type, Set<TypeParameterType> free) {
|
| + // Make some fresh variables to avoid capture.
|
| + List<DartType> typeArgs = DartType.EMPTY_LIST;
|
| + if (type.boundTypeParameters.isNotEmpty) {
|
| + typeArgs = new List<DartType>.from(type.boundTypeParameters.map((e) =>
|
| + new TypeParameterTypeImpl(new TypeParameterElementImpl(e.name, -1))));
|
| +
|
| + type = type.instantiate(typeArgs);
|
| + }
|
| +
|
| + for (ParameterElement p in type.parameters) {
|
| + _freeVariablesInType(p.type, free);
|
| + }
|
| + _freeVariablesInType(type.returnType, free);
|
| +
|
| + // Remove all of our bound variables.
|
| + free.removeAll(typeArgs);
|
| + }
|
| +
|
| + void _freeVariablesInInterfaceType(
|
| + InterfaceType type, Set<TypeParameterType> free) {
|
| + for (DartType typeArg in type.typeArguments) {
|
| + _freeVariablesInType(typeArg, free);
|
| + }
|
| + }
|
| +
|
| + void _freeVariablesInType(DartType type, Set<TypeParameterType> free) {
|
| + if (type is TypeParameterType) {
|
| + free.add(type);
|
| + } else if (type is FunctionType) {
|
| + _freeVariablesInFunctionType(type, free);
|
| + } else if (type is InterfaceType) {
|
| + _freeVariablesInInterfaceType(type, free);
|
| + }
|
| + }
|
| +
|
| /**
|
| * Compute the least upper bound of types [f] and [g], both of which are
|
| * known to be function types.
|
| @@ -6356,6 +6523,12 @@ abstract class InterfaceType implements ParameterizedType {
|
| InterfaceType substitute2(
|
| List<DartType> argumentTypes, List<DartType> parameterTypes);
|
|
|
| + // TODO(jmesserly): introduce a new "instantiate" and deprecate this.
|
| + // The new "instantiate" should work similar to FunctionType.instantiate,
|
| + // which uses [boundTypeParameters] to model type parameters that haven't been
|
| + // filled in yet. Those are kept separate from already-substituted type
|
| + // parameters or free variables from the enclosing scopes, which allows nested
|
| + // generics to work, such as a generic method in a generic class.
|
| /**
|
| * Return the type resulting from substituting the given arguments for this
|
| * type's parameters. This is fully equivalent to `substitute2(argumentTypes,
|
|
|