Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1495)

Unified Diff: pkg/analyzer/lib/src/generated/element.dart

Issue 1488383004: Fixes incorrect generic function type capture (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | pkg/analyzer/lib/src/generated/resolver.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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>, &hellip;, T<sub>n</sub>) &rarr; 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,
« no previous file with comments | « no previous file | pkg/analyzer/lib/src/generated/resolver.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698