Implement 'instantiate to bounds' feature in resynthesizer.
R=paulberry@google.com, brianwilkerson@google.com BUG= Review URL: https://codereview.chromium.org/1963593003 .
This commit is contained in:
@@ -23,6 +23,13 @@ import 'package:analyzer/src/generated/utilities_dart.dart';
|
||||
*/
|
||||
typedef FunctionTypedElement FunctionTypedElementComputer();
|
||||
|
||||
/**
|
||||
* Computer of type arguments which is used to delay computing of type
|
||||
* arguments until they are requested, instead of at the [ParameterizedType]
|
||||
* creation time.
|
||||
*/
|
||||
typedef List<DartType> TypeArgumentsComputer();
|
||||
|
||||
/**
|
||||
* A [Type] that represents the type 'bottom'.
|
||||
*/
|
||||
@@ -1117,7 +1124,13 @@ class InterfaceTypeImpl extends TypeImpl implements InterfaceType {
|
||||
/**
|
||||
* A list containing the actual types of the type arguments.
|
||||
*/
|
||||
List<DartType> typeArguments = DartType.EMPTY_LIST;
|
||||
List<DartType> _typeArguments = DartType.EMPTY_LIST;
|
||||
|
||||
/**
|
||||
* If not `null` and [_typeArguments] is `null`, the actual type arguments
|
||||
* should be computed (once) using this function.
|
||||
*/
|
||||
TypeArgumentsComputer _typeArgumentsComputer;
|
||||
|
||||
/**
|
||||
* The set of typedefs which should not be expanded when exploring this type,
|
||||
@@ -1136,10 +1149,10 @@ class InterfaceTypeImpl extends TypeImpl implements InterfaceType {
|
||||
* with the given [name] and [typeArguments].
|
||||
*/
|
||||
InterfaceTypeImpl.elementWithNameAndArgs(
|
||||
ClassElement element, String name, List<DartType> typeArguments)
|
||||
ClassElement element, String name, this._typeArgumentsComputer)
|
||||
: prunedTypedefs = null,
|
||||
super(element, name) {
|
||||
this.typeArguments = typeArguments;
|
||||
_typeArguments = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1300,6 +1313,23 @@ class InterfaceTypeImpl extends TypeImpl implements InterfaceType {
|
||||
return supertype.substitute2(typeArguments, typeParameters);
|
||||
}
|
||||
|
||||
@override
|
||||
List<DartType> get typeArguments {
|
||||
if (_typeArguments == null) {
|
||||
_typeArguments = _typeArgumentsComputer();
|
||||
_typeArgumentsComputer = null;
|
||||
}
|
||||
return _typeArguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set [typeArguments].
|
||||
*/
|
||||
void set typeArguments(List<DartType> typeArguments) {
|
||||
_typeArguments = typeArguments;
|
||||
_typeArgumentsComputer = null;
|
||||
}
|
||||
|
||||
@override
|
||||
List<TypeParameterElement> get typeParameters => element.typeParameters;
|
||||
|
||||
|
||||
@@ -582,12 +582,13 @@ class ClassElementForLink_Class extends ClassElementForLink
|
||||
DartType getTypeArgument(int i), List<int> implicitFunctionTypeIndices) {
|
||||
int numTypeParameters = _unlinkedClass.typeParameters.length;
|
||||
if (numTypeParameters != 0) {
|
||||
List<DartType> typeArguments = new List<DartType>(numTypeParameters);
|
||||
for (int i = 0; i < numTypeParameters; i++) {
|
||||
typeArguments[i] = getTypeArgument(i);
|
||||
}
|
||||
return new InterfaceTypeImpl.elementWithNameAndArgs(
|
||||
this, name, typeArguments);
|
||||
return new InterfaceTypeImpl.elementWithNameAndArgs(this, name, () {
|
||||
List<DartType> typeArguments = new List<DartType>(numTypeParameters);
|
||||
for (int i = 0; i < numTypeParameters; i++) {
|
||||
typeArguments[i] = getTypeArgument(i);
|
||||
}
|
||||
return typeArguments;
|
||||
});
|
||||
} else {
|
||||
return _type ??= new InterfaceTypeImpl(this);
|
||||
}
|
||||
@@ -2242,12 +2243,23 @@ class ExprTypeComputer {
|
||||
ConstructorElementForLink element =
|
||||
unit._resolveRef(ref.reference).asConstructor;
|
||||
if (element != null) {
|
||||
stack.add(element.enclosingClass.buildType(
|
||||
(int i) => i >= ref.typeArguments.length
|
||||
? DynamicTypeImpl.instance
|
||||
: unit._resolveTypeRef(
|
||||
ref.typeArguments[i], variable._typeParameterContext),
|
||||
const []));
|
||||
ClassElementForLink_Class enclosingClass = element.enclosingClass;
|
||||
stack.add(enclosingClass.buildType((int i) {
|
||||
// Type argument explicitly specified.
|
||||
if (i < ref.typeArguments.length) {
|
||||
return unit._resolveTypeRef(
|
||||
ref.typeArguments[i], variable._typeParameterContext);
|
||||
}
|
||||
// In strong mode, type argument defaults to bound (if any).
|
||||
if (linker.strongMode) {
|
||||
TypeParameterElement typeParameter = enclosingClass.typeParameters[i];
|
||||
if (typeParameter.bound != null) {
|
||||
return typeParameter.bound;
|
||||
}
|
||||
}
|
||||
// Otherwise type argument defaults to `dynamic`.
|
||||
return DynamicTypeImpl.instance;
|
||||
}, const []));
|
||||
} else {
|
||||
stack.add(DynamicTypeImpl.instance);
|
||||
}
|
||||
@@ -4291,6 +4303,8 @@ class TypeParameterElementForLink implements TypeParameterElementImpl {
|
||||
TypeParameterTypeImpl _type;
|
||||
ElementLocation _location;
|
||||
|
||||
DartType _bound;
|
||||
|
||||
TypeParameterElementForLink(
|
||||
this.enclosingElement, this._unlinkedTypeParam, this.nestingLevel);
|
||||
|
||||
@@ -4299,8 +4313,8 @@ class TypeParameterElementForLink implements TypeParameterElementImpl {
|
||||
if (_unlinkedTypeParam.bound == null) {
|
||||
return null;
|
||||
}
|
||||
// TODO(scheglov) implement
|
||||
throw new UnimplementedError();
|
||||
return _bound ??= enclosingElement.compilationUnit
|
||||
._resolveTypeRef(_unlinkedTypeParam.bound, enclosingElement);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -521,10 +521,13 @@ class _ConstExprBuilder {
|
||||
TypeName _buildTypeAst(DartType type) {
|
||||
List<TypeName> argumentNodes;
|
||||
if (type is ParameterizedType) {
|
||||
List<DartType> typeArguments = type.typeArguments;
|
||||
argumentNodes = typeArguments.every((a) => a.isDynamic)
|
||||
? null
|
||||
: typeArguments.map(_buildTypeAst).toList();
|
||||
if (!resynthesizer.libraryResynthesizer.typesWithImplicitTypeArguments
|
||||
.contains(type)) {
|
||||
List<DartType> typeArguments = type.typeArguments;
|
||||
argumentNodes = typeArguments.every((a) => a.isDynamic)
|
||||
? null
|
||||
: typeArguments.map(_buildTypeAst).toList();
|
||||
}
|
||||
}
|
||||
TypeName node = AstFactory.typeName4(type.name, argumentNodes);
|
||||
node.type = type;
|
||||
@@ -950,6 +953,13 @@ class _LibraryResynthesizer {
|
||||
final Map<String, Map<String, Element>> resynthesizedElements =
|
||||
<String, Map<String, Element>>{};
|
||||
|
||||
/**
|
||||
* Types with implicit type arguments, which are the same as type parameter
|
||||
* bounds (in strong mode), or `dynamic` (in spec mode).
|
||||
*/
|
||||
final Set<DartType> typesWithImplicitTypeArguments =
|
||||
new Set<DartType>.identity();
|
||||
|
||||
_LibraryResynthesizer(this.summaryResynthesizer, this.linkedLibrary,
|
||||
this.unlinkedUnits, this.librarySource) {
|
||||
isCoreLibrary = librarySource.uri.toString() == 'dart:core';
|
||||
@@ -1286,6 +1296,11 @@ class _LibraryResynthesizer {
|
||||
* (and its associated entry in [UnlinkedUnit.references], if it exists).
|
||||
*/
|
||||
class _ReferenceInfo {
|
||||
/**
|
||||
* The [_LibraryResynthesizer] which is being used to obtain summaries.
|
||||
*/
|
||||
final _LibraryResynthesizer libraryResynthesizer;
|
||||
|
||||
/**
|
||||
* The enclosing [_ReferenceInfo], or `null` for top-level elements.
|
||||
*/
|
||||
@@ -1324,12 +1339,12 @@ class _ReferenceInfo {
|
||||
* the type itself. Otherwise, pass `null` and the type will be computed
|
||||
* when appropriate.
|
||||
*/
|
||||
_ReferenceInfo(this.enclosing, this.name, this.element, DartType specialType,
|
||||
this.numTypeParameters) {
|
||||
_ReferenceInfo(this.libraryResynthesizer, this.enclosing, this.name,
|
||||
this.element, DartType specialType, this.numTypeParameters) {
|
||||
if (specialType != null) {
|
||||
type = specialType;
|
||||
} else {
|
||||
type = _buildType((_) => DynamicTypeImpl.instance, const []);
|
||||
type = _buildType(true, 0, (_) => DynamicTypeImpl.instance, const []);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1346,12 +1361,13 @@ class _ReferenceInfo {
|
||||
* If the entity referred to by this [_ReferenceInfo] is not a type, `null`
|
||||
* is returned.
|
||||
*/
|
||||
DartType buildType(
|
||||
DartType buildType(bool instantiateToBoundsAllowed, int numTypeArguments,
|
||||
DartType getTypeArgument(int i), List<int> implicitFunctionTypeIndices) {
|
||||
DartType result =
|
||||
(numTypeParameters == 0 && implicitFunctionTypeIndices.isEmpty)
|
||||
? type
|
||||
: _buildType(getTypeArgument, implicitFunctionTypeIndices);
|
||||
: _buildType(instantiateToBoundsAllowed, numTypeArguments,
|
||||
getTypeArgument, implicitFunctionTypeIndices);
|
||||
if (result == null) {
|
||||
// TODO(paulberry): figure out how to handle this case (which should
|
||||
// only occur in the event of erroneous code).
|
||||
@@ -1361,21 +1377,51 @@ class _ReferenceInfo {
|
||||
}
|
||||
|
||||
/**
|
||||
* If this reference refers to a type, build a [DartType] which instantiates
|
||||
* it with type arguments returned by [getTypeArgument]. Otherwise return
|
||||
* `null`.
|
||||
* If this reference refers to a type, build a [DartType]. Otherwise return
|
||||
* `null`. If [numTypeArguments] is the same as the [numTypeParameters],
|
||||
* the type in instantiated with type arguments returned by [getTypeArgument],
|
||||
* otherwise it is instantiated with type parameter bounds (if strong mode),
|
||||
* or with `dynamic` type arguments.
|
||||
*
|
||||
* If [implicitFunctionTypeIndices] is not null, a [DartType] should be
|
||||
* created which refers to a function type implicitly defined by one of the
|
||||
* element's parameters. [implicitFunctionTypeIndices] is interpreted as in
|
||||
* [EntityRef.implicitFunctionTypeIndices].
|
||||
*/
|
||||
DartType _buildType(
|
||||
DartType _buildType(bool instantiateToBoundsAllowed, int numTypeArguments,
|
||||
DartType getTypeArgument(int i), List<int> implicitFunctionTypeIndices) {
|
||||
ElementHandle element = this.element; // To allow type promotion
|
||||
if (element is ClassElementHandle) {
|
||||
return new InterfaceTypeImpl.elementWithNameAndArgs(element, name,
|
||||
_buildTypeArguments(numTypeParameters, getTypeArgument));
|
||||
List<DartType> typeArguments = null;
|
||||
// If type arguments are specified, use them.
|
||||
// Otherwise, delay until they are requested.
|
||||
if (numTypeParameters == 0) {
|
||||
typeArguments = const <DartType>[];
|
||||
} else if (numTypeArguments == numTypeParameters) {
|
||||
typeArguments = new List<DartType>(numTypeParameters);
|
||||
for (int i = 0; i < numTypeParameters; i++) {
|
||||
typeArguments[i] = getTypeArgument(i);
|
||||
}
|
||||
}
|
||||
InterfaceTypeImpl type =
|
||||
new InterfaceTypeImpl.elementWithNameAndArgs(element, name, () {
|
||||
if (typeArguments == null) {
|
||||
typeArguments = element.typeParameters.map((typeParameter) {
|
||||
DartType bound = typeParameter.bound;
|
||||
return libraryResynthesizer.summaryResynthesizer.strongMode &&
|
||||
instantiateToBoundsAllowed &&
|
||||
bound != null ? bound : DynamicTypeImpl.instance;
|
||||
}).toList();
|
||||
}
|
||||
return typeArguments;
|
||||
});
|
||||
// Mark the type as having implicit type arguments, so that we don't
|
||||
// attempt to request them during constant expression resynthesizing.
|
||||
if (typeArguments == null) {
|
||||
libraryResynthesizer.typesWithImplicitTypeArguments.add(type);
|
||||
}
|
||||
// Done.
|
||||
return type;
|
||||
} else if (element is FunctionTypedElement) {
|
||||
int numTypeArguments;
|
||||
FunctionTypedElementComputer computer;
|
||||
@@ -2212,7 +2258,8 @@ class _UnitResynthesizer {
|
||||
* deserialized, so handles are used to avoid having to deserialize other
|
||||
* libraries in the process.
|
||||
*/
|
||||
DartType buildType(EntityRef type, {bool defaultVoid: false}) {
|
||||
DartType buildType(EntityRef type,
|
||||
{bool defaultVoid: false, bool instantiateToBoundsAllowed: true}) {
|
||||
if (type == null) {
|
||||
if (defaultVoid) {
|
||||
return VoidTypeImpl.instance;
|
||||
@@ -2243,7 +2290,10 @@ class _UnitResynthesizer {
|
||||
}
|
||||
_ReferenceInfo referenceInfo = referenceInfos[type.reference];
|
||||
return referenceInfo.buildType(
|
||||
getTypeArgument, type.implicitFunctionTypeIndices);
|
||||
instantiateToBoundsAllowed,
|
||||
type.typeArguments.length,
|
||||
getTypeArgument,
|
||||
type.implicitFunctionTypeIndices);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2388,7 +2438,8 @@ class _UnitResynthesizer {
|
||||
void finishTypeParameter(UnlinkedTypeParam serializedTypeParameter,
|
||||
TypeParameterElementImpl typeParameterElement) {
|
||||
if (serializedTypeParameter.bound != null) {
|
||||
typeParameterElement.bound = buildType(serializedTypeParameter.bound);
|
||||
typeParameterElement.bound = buildType(serializedTypeParameter.bound,
|
||||
instantiateToBoundsAllowed: false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2540,7 +2591,7 @@ class _UnitResynthesizer {
|
||||
break;
|
||||
}
|
||||
}
|
||||
referenceInfos[i] = new _ReferenceInfo(
|
||||
referenceInfos[i] = new _ReferenceInfo(libraryResynthesizer,
|
||||
enclosingInfo, name, element, type, numTypeParameters);
|
||||
}
|
||||
}
|
||||
@@ -2627,7 +2678,7 @@ class _UnitResynthesizer {
|
||||
bool isClass = info.element is ClassElement;
|
||||
_ReferenceInfo classInfo = isClass ? info : info.enclosing;
|
||||
List<DartType> typeArguments = typeArgumentRefs.map(buildType).toList();
|
||||
return classInfo.buildType((i) {
|
||||
return classInfo.buildType(true, typeArguments.length, (i) {
|
||||
if (i < typeArguments.length) {
|
||||
return typeArguments[i];
|
||||
} else {
|
||||
|
||||
@@ -517,42 +517,6 @@ var b = a.m();
|
||||
super.test_inferenceInCyclesIsDeterministic();
|
||||
}
|
||||
|
||||
@override
|
||||
@failingTest
|
||||
void test_instantiateToBounds_generic2_hasBound_definedAfter() {
|
||||
super.test_instantiateToBounds_generic2_hasBound_definedAfter();
|
||||
}
|
||||
|
||||
@override
|
||||
@failingTest
|
||||
void test_instantiateToBounds_generic2_hasBound_definedBefore() {
|
||||
super.test_instantiateToBounds_generic2_hasBound_definedBefore();
|
||||
}
|
||||
|
||||
@override
|
||||
@failingTest
|
||||
void test_instantiateToBounds_generic2_noBound() {
|
||||
super.test_instantiateToBounds_generic2_noBound();
|
||||
}
|
||||
|
||||
@override
|
||||
@failingTest
|
||||
void test_instantiateToBounds_generic_hasBound_definedAfter() {
|
||||
super.test_instantiateToBounds_generic_hasBound_definedAfter();
|
||||
}
|
||||
|
||||
@override
|
||||
@failingTest
|
||||
void test_instantiateToBounds_generic_hasBound_definedBefore() {
|
||||
super.test_instantiateToBounds_generic_hasBound_definedBefore();
|
||||
}
|
||||
|
||||
@override
|
||||
@failingTest
|
||||
void test_instantiateToBounds_notGeneric() {
|
||||
super.test_instantiateToBounds_notGeneric();
|
||||
}
|
||||
|
||||
void test_invokeMethod_notGeneric_genericClass() {
|
||||
var unit = checkFile(r'''
|
||||
class C<T> {
|
||||
|
||||
@@ -2839,6 +2839,22 @@ A v = null;
|
||||
expect(unit.topLevelVariables[0].type.toString(), 'A<int>');
|
||||
}
|
||||
|
||||
void test_instantiateToBounds_invokeConstructor_noBound() {
|
||||
var unit = checkFile('''
|
||||
class C<T> {}
|
||||
var x = new C();
|
||||
''');
|
||||
expect(unit.topLevelVariables[0].type.toString(), 'C<dynamic>');
|
||||
}
|
||||
|
||||
void test_instantiateToBounds_invokeConstructor_typeArgsExact() {
|
||||
var unit = checkFile('''
|
||||
class C<T extends num> {}
|
||||
var x = new C<int>();
|
||||
''');
|
||||
expect(unit.topLevelVariables[0].type.toString(), 'C<int>');
|
||||
}
|
||||
|
||||
void test_instantiateToBounds_notGeneric() {
|
||||
var unit = checkFile(r'''
|
||||
class A {}
|
||||
|
||||
Reference in New Issue
Block a user