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 b379ebd913810fd0c5c276cd4a04b07fcbddde58..8102be6e6b3ed39b067858cdbfeafd9828ab4643 100644 |
--- a/pkg/analyzer/lib/src/generated/element.dart |
+++ b/pkg/analyzer/lib/src/generated/element.dart |
@@ -4577,6 +4577,21 @@ abstract class FunctionType implements ParameterizedType { |
DartType get returnType; |
/** |
+ * 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 the type resulting from instantiating the given [argumentTypes] for |
Brian Wilkerson
2015/12/03 15:03:17
"instantiating" --> "instantiating (replacing)"?
Jennifer Messerly
2015/12/03 17:35:04
Done. Yeah I wondered if there was a better name f
Brian Wilkerson
2015/12/03 17:42:16
Me too. I think of "instantiate" as "create an ins
Leaf
2015/12/03 18:46:35
Yeah, instantiate is the most common term that I k
|
+ * 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); |
/** |
* Initialize a newly created function type to be declared by the given |
@@ -4882,29 +4908,49 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType { |
*/ |
FunctionTypeImpl.forTypedef(FunctionTypeAliasElement element, |
[List<FunctionTypeAliasElement> prunedTypedefs]) |
- : this._(element, element?.name, prunedTypedefs, null); |
+ : this._(element, element?.name, prunedTypedefs); |
/** |
* Private constructor. |
*/ |
- FunctionTypeImpl._(Element element, String name, this.prunedTypedefs, |
- List<DartType> typeArguments) |
+ FunctionTypeImpl._( |
+ TypeParameterizedElement element, String name, this.prunedTypedefs, |
+ [List<DartType> typeArguments, |
Brian Wilkerson
2015/12/03 15:03:17
It might be better to leave these as required argu
Jennifer Messerly
2015/12/03 17:35:04
Done. Yeah I can just pass the 3 null's explicitly
|
+ 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 +4978,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 +5212,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 +5220,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 +5248,53 @@ 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<String> namesToAvoid = new HashSet<String>(); |
+ for (DartType arg in typeArguments) { |
+ 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 +5304,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 +5321,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 +5350,35 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType { |
} |
@override |
+ FunctionTypeImpl instantiate(List<DartType> argumentTypes) { |
+ List<TypeParameterType> parameterTypes = |
+ TypeParameterTypeImpl.getTypes(boundTypeParameters); |
+ if (argumentTypes.length != parameterTypes.length) { |
+ throw new IllegalArgumentException( |
+ "argumentTypes.length (${argumentTypes.length}) != " |
+ "boundTypeParameters.length (${boundTypeParameters.length})"); |
+ } |
+ if (argumentTypes.isEmpty) { |
Brian Wilkerson
2015/12/03 15:03:17
How often will argumentTypes be empty? If it's oft
Jennifer Messerly
2015/12/03 17:35:04
Good call. Done! In fact I don't even need paramet
|
+ 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 +5627,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,7 +5654,8 @@ 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 |
@@ -6356,6 +6483,7 @@ abstract class InterfaceType implements ParameterizedType { |
InterfaceType substitute2( |
List<DartType> argumentTypes, List<DartType> parameterTypes); |
+ // TODO(jmesserly): rename this to instantiate? |
Brian Wilkerson
2015/12/03 15:03:17
I like the idea of having a consistent nomenclatur
Jennifer Messerly
2015/12/03 17:35:04
+1 yeah ... I'll expand the comment, seems like th
Brian Wilkerson
2015/12/03 17:42:16
It seems consistent to me. I'm fine with postponin
Leaf
2015/12/03 18:46:35
Agreed, I think instantiate would be a good choice
|
/** |
* Return the type resulting from substituting the given arguments for this |
* type's parameters. This is fully equivalent to `substitute2(argumentTypes, |