From 038478ea775445a621c6b892ea2ee8d83bc79ace Mon Sep 17 00:00:00 2001 From: Konstantin Shcheglov Date: Tue, 10 May 2016 09:58:06 -0700 Subject: [PATCH] Implement 'instantiate to bounds' feature in resynthesizer. R=paulberry@google.com, brianwilkerson@google.com BUG= Review URL: https://codereview.chromium.org/1963593003 . --- pkg/analyzer/lib/src/dart/element/type.dart | 36 +++++++- pkg/analyzer/lib/src/summary/link.dart | 42 ++++++--- .../lib/src/summary/resynthesize.dart | 91 +++++++++++++++---- .../src/summary/resynthesize_ast_test.dart | 36 -------- .../src/task/strong/inferred_type_test.dart | 16 ++++ 5 files changed, 148 insertions(+), 73 deletions(-) diff --git a/pkg/analyzer/lib/src/dart/element/type.dart b/pkg/analyzer/lib/src/dart/element/type.dart index 7d08ca8d463..f2e9a1507d8 100644 --- a/pkg/analyzer/lib/src/dart/element/type.dart +++ b/pkg/analyzer/lib/src/dart/element/type.dart @@ -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 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 typeArguments = DartType.EMPTY_LIST; + List _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 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 get typeArguments { + if (_typeArguments == null) { + _typeArguments = _typeArgumentsComputer(); + _typeArgumentsComputer = null; + } + return _typeArguments; + } + + /** + * Set [typeArguments]. + */ + void set typeArguments(List typeArguments) { + _typeArguments = typeArguments; + _typeArgumentsComputer = null; + } + @override List get typeParameters => element.typeParameters; diff --git a/pkg/analyzer/lib/src/summary/link.dart b/pkg/analyzer/lib/src/summary/link.dart index ee222edc865..307a9f2e3d1 100644 --- a/pkg/analyzer/lib/src/summary/link.dart +++ b/pkg/analyzer/lib/src/summary/link.dart @@ -582,12 +582,13 @@ class ClassElementForLink_Class extends ClassElementForLink DartType getTypeArgument(int i), List implicitFunctionTypeIndices) { int numTypeParameters = _unlinkedClass.typeParameters.length; if (numTypeParameters != 0) { - List typeArguments = new List(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 typeArguments = new List(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 diff --git a/pkg/analyzer/lib/src/summary/resynthesize.dart b/pkg/analyzer/lib/src/summary/resynthesize.dart index e6ac50d7a5f..127197bdd5e 100644 --- a/pkg/analyzer/lib/src/summary/resynthesize.dart +++ b/pkg/analyzer/lib/src/summary/resynthesize.dart @@ -521,10 +521,13 @@ class _ConstExprBuilder { TypeName _buildTypeAst(DartType type) { List argumentNodes; if (type is ParameterizedType) { - List typeArguments = type.typeArguments; - argumentNodes = typeArguments.every((a) => a.isDynamic) - ? null - : typeArguments.map(_buildTypeAst).toList(); + if (!resynthesizer.libraryResynthesizer.typesWithImplicitTypeArguments + .contains(type)) { + List 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> resynthesizedElements = >{}; + /** + * Types with implicit type arguments, which are the same as type parameter + * bounds (in strong mode), or `dynamic` (in spec mode). + */ + final Set typesWithImplicitTypeArguments = + new Set.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 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 implicitFunctionTypeIndices) { ElementHandle element = this.element; // To allow type promotion if (element is ClassElementHandle) { - return new InterfaceTypeImpl.elementWithNameAndArgs(element, name, - _buildTypeArguments(numTypeParameters, getTypeArgument)); + List typeArguments = null; + // If type arguments are specified, use them. + // Otherwise, delay until they are requested. + if (numTypeParameters == 0) { + typeArguments = const []; + } else if (numTypeArguments == numTypeParameters) { + typeArguments = new List(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 typeArguments = typeArgumentRefs.map(buildType).toList(); - return classInfo.buildType((i) { + return classInfo.buildType(true, typeArguments.length, (i) { if (i < typeArguments.length) { return typeArguments[i]; } else { diff --git a/pkg/analyzer/test/src/summary/resynthesize_ast_test.dart b/pkg/analyzer/test/src/summary/resynthesize_ast_test.dart index d92bff19c6f..459fb40d286 100644 --- a/pkg/analyzer/test/src/summary/resynthesize_ast_test.dart +++ b/pkg/analyzer/test/src/summary/resynthesize_ast_test.dart @@ -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 { diff --git a/pkg/analyzer/test/src/task/strong/inferred_type_test.dart b/pkg/analyzer/test/src/task/strong/inferred_type_test.dart index ae7c50d43ac..3dd8e602aed 100644 --- a/pkg/analyzer/test/src/task/strong/inferred_type_test.dart +++ b/pkg/analyzer/test/src/task/strong/inferred_type_test.dart @@ -2839,6 +2839,22 @@ A v = null; expect(unit.topLevelVariables[0].type.toString(), 'A'); } + void test_instantiateToBounds_invokeConstructor_noBound() { + var unit = checkFile(''' +class C {} +var x = new C(); +'''); + expect(unit.topLevelVariables[0].type.toString(), 'C'); + } + + void test_instantiateToBounds_invokeConstructor_typeArgsExact() { + var unit = checkFile(''' +class C {} +var x = new C(); +'''); + expect(unit.topLevelVariables[0].type.toString(), 'C'); + } + void test_instantiateToBounds_notGeneric() { var unit = checkFile(r''' class A {}