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:
Konstantin Shcheglov
2016-05-10 09:58:06 -07:00
parent e7b51f7bd1
commit 038478ea77
5 changed files with 148 additions and 73 deletions
+33 -3
View File
@@ -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;
+28 -14
View File
@@ -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
+71 -20
View File
@@ -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 {}