Track RTI dependency through instantiations

Closes #33093

Change-Id: Ia7d9cd1235cd4fe60858f37533689f5eeb600540
Reviewed-on: https://dart-review.googlesource.com/59400
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Stephen Adams <sra@google.com>
diff --git a/pkg/compiler/lib/src/common/codegen.dart b/pkg/compiler/lib/src/common/codegen.dart
index 208d40f..a0c99af 100644
--- a/pkg/compiler/lib/src/common/codegen.dart
+++ b/pkg/compiler/lib/src/common/codegen.dart
@@ -7,6 +7,7 @@
 import '../common_elements.dart';
 import '../elements/entities.dart';
 import '../elements/types.dart' show DartType, InterfaceType;
+import '../universe/feature.dart';
 import '../universe/use.dart' show ConstantUse, DynamicUse, StaticUse, TypeUse;
 import '../universe/world_impact.dart'
     show WorldImpact, WorldImpactBuilderImpl, WorldImpactVisitor;
@@ -29,7 +30,10 @@
 
   bool get usesInterceptor => false;
 
-  Iterable<AsyncMarker> get asyncMarkers => <AsyncMarker>[];
+  Iterable<AsyncMarker> get asyncMarkers => const <AsyncMarker>[];
+
+  Iterable<GenericInstantiation> get genericInstantiations =>
+      const <GenericInstantiation>[];
 }
 
 class _CodegenImpact extends WorldImpactBuilderImpl implements CodegenImpact {
@@ -38,6 +42,7 @@
   List<Set<ClassEntity>> _specializedGetInterceptors;
   bool _usesInterceptor = false;
   EnumSet<AsyncMarker> _asyncMarkers;
+  Set<GenericInstantiation> _genericInstantiations;
 
   _CodegenImpact();
 
@@ -49,9 +54,7 @@
 
   void registerTypeVariableBoundsSubtypeCheck(
       DartType subtype, DartType supertype) {
-    if (_typeVariableBoundsSubtypeChecks == null) {
-      _typeVariableBoundsSubtypeChecks = new Setlet<Pair<DartType, DartType>>();
-    }
+    _typeVariableBoundsSubtypeChecks ??= new Setlet<Pair<DartType, DartType>>();
     _typeVariableBoundsSubtypeChecks
         .add(new Pair<DartType, DartType>(subtype, supertype));
   }
@@ -63,9 +66,7 @@
   }
 
   void registerConstSymbol(String name) {
-    if (_constSymbols == null) {
-      _constSymbols = new Setlet<String>();
-    }
+    _constSymbols ??= new Setlet<String>();
     _constSymbols.add(name);
   }
 
@@ -74,9 +75,7 @@
   }
 
   void registerSpecializedGetInterceptor(Set<ClassEntity> classes) {
-    if (_specializedGetInterceptors == null) {
-      _specializedGetInterceptors = <Set<ClassEntity>>[];
-    }
+    _specializedGetInterceptors ??= <Set<ClassEntity>>[];
     _specializedGetInterceptors.add(classes);
   }
 
@@ -93,9 +92,7 @@
   bool get usesInterceptor => _usesInterceptor;
 
   void registerAsyncMarker(AsyncMarker asyncMarker) {
-    if (_asyncMarkers == null) {
-      _asyncMarkers = new EnumSet<AsyncMarker>();
-    }
+    _asyncMarkers ??= new EnumSet<AsyncMarker>();
     _asyncMarkers.add(asyncMarker);
   }
 
@@ -104,6 +101,16 @@
         ? _asyncMarkers.iterable(AsyncMarker.values)
         : const <AsyncMarker>[];
   }
+
+  void registerGenericInstantiation(GenericInstantiation instantiation) {
+    _genericInstantiations ??= new Set<GenericInstantiation>();
+    _genericInstantiations.add(instantiation);
+  }
+
+  @override
+  Iterable<GenericInstantiation> get genericInstantiations {
+    return _genericInstantiations ?? const <GenericInstantiation>[];
+  }
 }
 
 // TODO(johnniwinther): Split this class into interface and implementation.
@@ -169,6 +176,10 @@
   void registerAsyncMarker(AsyncMarker asyncMarker) {
     worldImpact.registerAsyncMarker(asyncMarker);
   }
+
+  void registerGenericInstantiation(GenericInstantiation instantiation) {
+    worldImpact.registerGenericInstantiation(instantiation);
+  }
 }
 
 /// [WorkItem] used exclusively by the [CodegenEnqueuer].
diff --git a/pkg/compiler/lib/src/common/resolution.dart b/pkg/compiler/lib/src/common/resolution.dart
index b0eb1ac..cf2ed2c 100644
--- a/pkg/compiler/lib/src/common/resolution.dart
+++ b/pkg/compiler/lib/src/common/resolution.dart
@@ -21,4 +21,7 @@
   Iterable<ClassEntity> get seenClasses => const <ClassEntity>[];
 
   Iterable<dynamic> get nativeData => const <dynamic>[];
+
+  Iterable<GenericInstantiation> get genericInstantiations =>
+      const <GenericInstantiation>[];
 }
diff --git a/pkg/compiler/lib/src/js_backend/backend_usage.dart b/pkg/compiler/lib/src/js_backend/backend_usage.dart
index 8af1da3..7e2065e 100644
--- a/pkg/compiler/lib/src/js_backend/backend_usage.dart
+++ b/pkg/compiler/lib/src/js_backend/backend_usage.dart
@@ -43,9 +43,6 @@
 
   /// `true` if `noSuchMethod` is used.
   bool get isNoSuchMethodUsed;
-
-  /// `true` if generic instantiation is used.
-  bool get isGenericInstantiationUsed;
 }
 
 abstract class BackendUsageBuilder {
@@ -83,9 +80,6 @@
   /// `true` if `noSuchMethod` is used.
   bool isNoSuchMethodUsed;
 
-  /// `true` if generic instantiation is used.
-  bool isGenericInstantiationUsed;
-
   BackendUsage close();
 }
 
@@ -122,9 +116,6 @@
   /// `true` if `noSuchMethod` is used.
   bool isNoSuchMethodUsed = false;
 
-  /// `true` if generic instantiation is used.
-  bool isGenericInstantiationUsed = false;
-
   BackendUsageBuilderImpl(this._commonElements);
 
   @override
@@ -280,8 +271,7 @@
         isRuntimeTypeUsed: isRuntimeTypeUsed,
         isFunctionApplyUsed: isFunctionApplyUsed,
         isMirrorsUsed: isMirrorsUsed,
-        isNoSuchMethodUsed: isNoSuchMethodUsed,
-        isGenericInstantiationUsed: isGenericInstantiationUsed);
+        isNoSuchMethodUsed: isNoSuchMethodUsed);
   }
 }
 
@@ -317,9 +307,6 @@
   /// `true` if `noSuchMethod` is used.
   final bool isNoSuchMethodUsed;
 
-  /// `true` if generic instantiation is used.
-  final bool isGenericInstantiationUsed;
-
   BackendUsageImpl(
       {Set<FunctionEntity> globalFunctionDependencies,
       Set<ClassEntity> globalClassDependencies,
@@ -332,8 +319,7 @@
       this.isRuntimeTypeUsed,
       this.isFunctionApplyUsed,
       this.isMirrorsUsed,
-      this.isNoSuchMethodUsed,
-      this.isGenericInstantiationUsed})
+      this.isNoSuchMethodUsed})
       : this._globalFunctionDependencies = globalFunctionDependencies,
         this._globalClassDependencies = globalClassDependencies,
         this._helperFunctionsUsed = helperFunctionsUsed,
diff --git a/pkg/compiler/lib/src/js_backend/impact_transformer.dart b/pkg/compiler/lib/src/js_backend/impact_transformer.dart
index 70e8d1a..8d6506d 100644
--- a/pkg/compiler/lib/src/js_backend/impact_transformer.dart
+++ b/pkg/compiler/lib/src/js_backend/impact_transformer.dart
@@ -104,10 +104,6 @@
               new TypeUse.instantiation(_commonElements.nullType));
           registerImpact(_impacts.nullLiteral);
           break;
-        case Feature.GENERIC_INSTANTIATION:
-          registerImpact(_impacts.genericInstantiation);
-          _backendUsageBuilder.isGenericInstantiationUsed = true;
-          break;
         case Feature.LAZY_FIELD:
           registerImpact(_impacts.lazyField);
           break;
@@ -292,6 +288,14 @@
       _classHierarchyBuilder.registerClass(classEntity);
     }
 
+    if (worldImpact.genericInstantiations.isNotEmpty) {
+      registerImpact(_impacts.genericInstantiation);
+      for (GenericInstantiation instantiation
+          in worldImpact.genericInstantiations) {
+        _rtiNeedBuilder.registerGenericInstantiation(instantiation);
+      }
+    }
+
     return transformed;
   }
 
@@ -460,6 +464,10 @@
       }
     }
 
+    for (GenericInstantiation instantiation in impact.genericInstantiations) {
+      _rtiChecksBuilder.registerGenericInstantiation(instantiation);
+    }
+
     // TODO(johnniwinther): Remove eager registration.
     return transformed;
   }
diff --git a/pkg/compiler/lib/src/js_backend/runtime_types.dart b/pkg/compiler/lib/src/js_backend/runtime_types.dart
index d637cb1..332926b 100644
--- a/pkg/compiler/lib/src/js_backend/runtime_types.dart
+++ b/pkg/compiler/lib/src/js_backend/runtime_types.dart
@@ -13,6 +13,7 @@
 import '../js/js.dart' show js;
 import '../js_emitter/js_emitter.dart' show Emitter;
 import '../options.dart';
+import '../universe/feature.dart';
 import '../universe/selector.dart';
 import '../universe/world_builder.dart';
 import '../world.dart' show JClosedWorld, KClosedWorld;
@@ -95,6 +96,13 @@
   /// Returns `true` if a dynamic call of [selector] needs to pass type
   /// arguments.
   bool selectorNeedsTypeArguments(Selector selector);
+
+  /// Returns `true` if a generic instantiation on an expression of type
+  /// [functionType] with the given [typeArgumentCount] needs to pass type
+  /// arguments.
+  // TODO(johnniwinther): Use [functionType].
+  bool instantiationNeedsTypeArguments(
+      DartType functionType, int typeArgumentCount);
 }
 
 class TrivialRuntimeTypesNeed implements RuntimeTypesNeed {
@@ -117,6 +125,12 @@
 
   @override
   bool selectorNeedsTypeArguments(Selector selector) => true;
+
+  @override
+  bool instantiationNeedsTypeArguments(
+      DartType functionType, int typeArgumentCount) {
+    return true;
+  }
 }
 
 /// Interface for computing classes and methods that need runtime types.
@@ -131,6 +145,9 @@
   /// literal.
   void registerLocalFunctionUsingTypeVariableLiteral(Local localFunction);
 
+  /// Registers that a generic [instantiation] is used.
+  void registerGenericInstantiation(GenericInstantiation instantiation);
+
   /// Computes the [RuntimeTypesNeed] for the data registered with this builder.
   RuntimeTypesNeed computeRuntimeTypesNeed(
       ResolutionWorldBuilder resolutionWorldBuilder,
@@ -151,6 +168,9 @@
   void registerLocalFunctionUsingTypeVariableLiteral(Local localFunction) {}
 
   @override
+  void registerGenericInstantiation(GenericInstantiation instantiation) {}
+
+  @override
   RuntimeTypesNeed computeRuntimeTypesNeed(
       ResolutionWorldBuilder resolutionWorldBuilder,
       KClosedWorld closedWorld,
@@ -194,6 +214,9 @@
   void registerTypeVariableBoundsSubtypeCheck(
       DartType typeArgument, DartType bound);
 
+  /// Registers that a generic [instantiation] is used.
+  void registerGenericInstantiation(GenericInstantiation instantiation);
+
   /// Computes the [RuntimeTypesChecks] for the data in this builder.
   RuntimeTypesChecks computeRequiredChecks(
       CodegenWorldBuilder codegenWorldBuilder, CompilerOptions options);
@@ -215,6 +238,9 @@
       DartType typeArgument, DartType bound) {}
 
   @override
+  void registerGenericInstantiation(GenericInstantiation instantiation) {}
+
+  @override
   RuntimeTypesChecks computeRequiredChecks(
       CodegenWorldBuilder codegenWorldBuilder, CompilerOptions options) {
     rtiChecksBuilderClosed = true;
@@ -700,6 +726,7 @@
   final Set<Local> localFunctionsNeedingSignature;
   final Set<Local> localFunctionsNeedingTypeArguments;
   final Set<Selector> selectorsNeedingTypeArguments;
+  final Set<int> instantiationsNeedingTypeArguments;
 
   RuntimeTypesNeedImpl(
       this._elementEnvironment,
@@ -709,7 +736,8 @@
       this.methodsNeedingTypeArguments,
       this.localFunctionsNeedingSignature,
       this.localFunctionsNeedingTypeArguments,
-      this.selectorsNeedingTypeArguments);
+      this.selectorsNeedingTypeArguments,
+      this.instantiationsNeedingTypeArguments);
 
   bool checkClass(covariant ClassEntity cls) => true;
 
@@ -749,6 +777,12 @@
     if (_backendUsage.isRuntimeTypeUsed) return true;
     return selectorsNeedingTypeArguments.contains(selector);
   }
+
+  @override
+  bool instantiationNeedsTypeArguments(
+      DartType functionType, int typeArgumentCount) {
+    return instantiationsNeedingTypeArguments.contains(typeArgumentCount);
+  }
 }
 
 class TypeVariableTests {
@@ -756,6 +790,7 @@
   Map<ClassEntity, ClassNode> _classes = <ClassEntity, ClassNode>{};
   Map<Entity, MethodNode> _methods = <Entity, MethodNode>{};
   Map<Selector, Set<Entity>> _appliedSelectorMap;
+  Map<GenericInstantiation, Set<Entity>> _instantiationMap;
 
   /// All explicit is-tests.
   final Set<DartType> explicitIsChecks;
@@ -763,11 +798,17 @@
   /// All implicit is-tests.
   final Set<DartType> implicitIsChecks = new Set<DartType>();
 
-  TypeVariableTests(ElementEnvironment elementEnvironment,
-      CommonElements commonElements, DartTypes types, WorldBuilder worldBuilder,
+  TypeVariableTests(
+      ElementEnvironment elementEnvironment,
+      CommonElements commonElements,
+      DartTypes types,
+      WorldBuilder worldBuilder,
+      Set<GenericInstantiation> genericInstantiations,
       {bool forRtiNeeds: true})
       : explicitIsChecks = new Set<DartType>.from(worldBuilder.isChecks) {
-    _setupDependencies(elementEnvironment, commonElements, worldBuilder);
+    _setupDependencies(
+        elementEnvironment, commonElements, worldBuilder, genericInstantiations,
+        forRtiNeeds: forRtiNeeds);
     _propagateTests(commonElements, elementEnvironment, worldBuilder);
     if (forRtiNeeds) {
       _propagateLiterals(elementEnvironment, worldBuilder);
@@ -867,6 +908,13 @@
     _appliedSelectorMap.forEach(f);
   }
 
+  /// Calls [f] for each generic instantiation that applies to generic
+  /// closurized [targets].
+  void forEachGenericInstantiation(
+      void f(GenericInstantiation instantiation, Set<Entity> targets)) {
+    _instantiationMap?.forEach(f);
+  }
+
   ClassNode _getClassNode(ClassEntity cls) {
     return _classes.putIfAbsent(cls, () {
       ClassNode node = new ClassNode(cls);
@@ -905,8 +953,12 @@
     });
   }
 
-  void _setupDependencies(ElementEnvironment elementEnvironment,
-      CommonElements commonElements, WorldBuilder worldBuilder) {
+  void _setupDependencies(
+      ElementEnvironment elementEnvironment,
+      CommonElements commonElements,
+      WorldBuilder worldBuilder,
+      Set<GenericInstantiation> genericInstantiations,
+      {bool forRtiNeeds: true}) {
     /// Register that if `node.entity` needs type arguments then so do entities
     /// whose type variables occur in [type].
     ///
@@ -1018,6 +1070,31 @@
       worldBuilder.closurizedStatics.forEach(processEntity);
       worldBuilder.userNoSuchMethods.forEach(processEntity);
     });
+
+    for (GenericInstantiation instantiation in genericInstantiations) {
+      void processEntity(Entity entity) {
+        MethodNode node =
+            _getMethodNode(elementEnvironment, worldBuilder, entity);
+        if (node.parameterStructure.typeParameters ==
+            instantiation.typeArguments.length) {
+          if (forRtiNeeds) {
+            _instantiationMap ??= <GenericInstantiation, Set<Entity>>{};
+            _instantiationMap
+                .putIfAbsent(instantiation, () => new Set<Entity>())
+                .add(entity);
+          }
+          for (DartType type in instantiation.typeArguments) {
+            // Register that if `node.entity` needs type arguments then so do
+            // the entities that declare type variables occurring in [type].
+            registerDependencies(node, type);
+          }
+        }
+      }
+
+      worldBuilder.closurizedMembers.forEach(processEntity);
+      worldBuilder.closurizedStatics.forEach(processEntity);
+      worldBuilder.genericLocalFunctions.forEach(processEntity);
+    }
   }
 
   void _propagateTests(CommonElements commonElements,
@@ -1339,6 +1416,12 @@
 
   Map<Selector, Set<Entity>> selectorsNeedingTypeArgumentsForTesting;
 
+  Map<GenericInstantiation, Set<Entity>>
+      instantiationsNeedingTypeArgumentsForTesting;
+
+  final Set<GenericInstantiation> _genericInstantiations =
+      new Set<GenericInstantiation>();
+
   TypeVariableTests typeVariableTestsForTesting;
 
   RuntimeTypesNeedBuilderImpl(this._elementEnvironment, DartTypes types)
@@ -1360,6 +1443,11 @@
   }
 
   @override
+  void registerGenericInstantiation(GenericInstantiation instantiation) {
+    _genericInstantiations.add(instantiation);
+  }
+
+  @override
   RuntimeTypesNeed computeRuntimeTypesNeed(
       ResolutionWorldBuilder resolutionWorldBuilder,
       KClosedWorld closedWorld,
@@ -1368,7 +1456,8 @@
         closedWorld.elementEnvironment,
         closedWorld.commonElements,
         closedWorld.dartTypes,
-        resolutionWorldBuilder);
+        resolutionWorldBuilder,
+        _genericInstantiations);
     Set<ClassEntity> classesNeedingTypeArguments = new Set<ClassEntity>();
     Set<FunctionEntity> methodsNeedingSignature = new Set<FunctionEntity>();
     Set<FunctionEntity> methodsNeedingTypeArguments = new Set<FunctionEntity>();
@@ -1440,8 +1529,9 @@
           if (potentialSubtypeOf == null ||
               closedWorld.dartTypes.isPotentialSubtype(
                   functionType, potentialSubtypeOf,
-                  assumeInstantiations:
-                      closedWorld.backendUsage.isGenericInstantiationUsed)) {
+                  // TODO(johnniwinther): Use register generic instantiations
+                  // instead.
+                  assumeInstantiations: _genericInstantiations.isNotEmpty)) {
             functionType.forEachTypeVariable((TypeVariableType typeVariable) {
               Entity typeDeclaration = typeVariable.element.typeDeclaration;
               if (!processedEntities.contains(typeDeclaration)) {
@@ -1571,6 +1661,28 @@
         }
       }
     });
+    Set<int> instantiationsNeedingTypeArguments = new Set<int>();
+    typeVariableTests.forEachGenericInstantiation(
+        (GenericInstantiation instantiation, Set<Entity> targets) {
+      for (Entity target in targets) {
+        if (methodsNeedingTypeArguments.contains(target) ||
+            localFunctionsNeedingTypeArguments.contains(target)) {
+          // TODO(johnniwinther): Use the static type of the instantiated
+          // expression.
+          instantiationsNeedingTypeArguments
+              .add(instantiation.typeArguments.length);
+          if (cacheRtiDataForTesting) {
+            instantiationsNeedingTypeArgumentsForTesting ??=
+                <GenericInstantiation, Set<Entity>>{};
+            instantiationsNeedingTypeArgumentsForTesting
+                .putIfAbsent(instantiation, () => new Set<Entity>())
+                .add(target);
+          } else {
+            return;
+          }
+        }
+      }
+    });
 
     if (cacheRtiDataForTesting) {
       typeVariableTestsForTesting = typeVariableTests;
@@ -1594,9 +1706,11 @@
     localFunctionsNeedingTypeArguments.forEach((e) => print('  $e'));
     print('------------------------------------------------------------------');
     print('selectorsNeedingTypeArguments:');
-    selectorsNeedingTypeArguments.forEach((e) => print('  $e'));*/
+    selectorsNeedingTypeArguments.forEach((e) => print('  $e'));
+    print('instantiationsNeedingTypeArguments: '
+        '$instantiationsNeedingTypeArguments');*/
 
-    return _createRuntimeTypesNeed(
+    return new RuntimeTypesNeedImpl(
         _elementEnvironment,
         closedWorld.backendUsage,
         classesNeedingTypeArguments,
@@ -1604,27 +1718,8 @@
         methodsNeedingTypeArguments,
         localFunctionsNeedingSignature,
         localFunctionsNeedingTypeArguments,
-        selectorsNeedingTypeArguments);
-  }
-
-  RuntimeTypesNeed _createRuntimeTypesNeed(
-      ElementEnvironment elementEnvironment,
-      BackendUsage backendUsage,
-      Set<ClassEntity> classesNeedingTypeArguments,
-      Set<FunctionEntity> methodsNeedingSignature,
-      Set<FunctionEntity> methodsNeedingTypeArguments,
-      Set<Local> localFunctionsNeedingSignature,
-      Set<Local> localFunctionsNeedingTypeArguments,
-      Set<Selector> selectorsNeedingTypeArguments) {
-    return new RuntimeTypesNeedImpl(
-        _elementEnvironment,
-        backendUsage,
-        classesNeedingTypeArguments,
-        methodsNeedingSignature,
-        methodsNeedingTypeArguments,
-        localFunctionsNeedingSignature,
-        localFunctionsNeedingTypeArguments,
-        selectorsNeedingTypeArguments);
+        selectorsNeedingTypeArguments,
+        instantiationsNeedingTypeArguments);
   }
 }
 
@@ -1672,6 +1767,9 @@
 
   Map<ClassEntity, ClassUse> classUseMapForTesting;
 
+  final Set<GenericInstantiation> _genericInstantiations =
+      new Set<GenericInstantiation>();
+
   @override
   void registerTypeVariableBoundsSubtypeCheck(
       DartType typeArgument, DartType bound) {
@@ -1679,10 +1777,19 @@
     checkedBounds.add(bound);
   }
 
+  @override
+  void registerGenericInstantiation(GenericInstantiation instantiation) {
+    _genericInstantiations.add(instantiation);
+  }
+
   RuntimeTypesChecks computeRequiredChecks(
       CodegenWorldBuilder codegenWorldBuilder, CompilerOptions options) {
     TypeVariableTests typeVariableTests = new TypeVariableTests(
-        _elementEnvironment, _commonElements, _types, codegenWorldBuilder,
+        _elementEnvironment,
+        _commonElements,
+        _types,
+        codegenWorldBuilder,
+        _genericInstantiations,
         forRtiNeeds: false);
     Set<DartType> explicitIsChecks = typeVariableTests.explicitIsChecks;
     Set<DartType> implicitIsChecks = typeVariableTests.implicitIsChecks;
diff --git a/pkg/compiler/lib/src/js_model/closure.dart b/pkg/compiler/lib/src/js_model/closure.dart
index b1c32aa..e6cd967 100644
--- a/pkg/compiler/lib/src/js_model/closure.dart
+++ b/pkg/compiler/lib/src/js_model/closure.dart
@@ -199,6 +199,13 @@
               return true;
             }
             break;
+          case VariableUseKind.instantiationTypeArgument:
+            // TODO(johnniwinther): Use the static type of the expression.
+            if (rtiNeed.instantiationNeedsTypeArguments(
+                null, usage.instantiation.typeArguments.length)) {
+              return true;
+            }
+            break;
         }
       }
       return false;
@@ -428,6 +435,9 @@
 
   /// A type variable in a field type.
   fieldType,
+
+  /// A type argument of an generic instantiation.
+  instantiationTypeArgument,
 }
 
 class VariableUse {
@@ -436,21 +446,25 @@
   final ir.TreeNode /*ir.FunctionDeclaration|ir.FunctionExpression*/
       localFunction;
   final ir.MethodInvocation invocation;
+  final ir.Instantiation instantiation;
 
   const VariableUse._simple(this.kind)
       : this.member = null,
         this.localFunction = null,
-        this.invocation = null;
+        this.invocation = null,
+        this.instantiation = null;
 
   VariableUse.memberParameter(this.member)
       : this.kind = VariableUseKind.memberParameter,
         this.localFunction = null,
-        this.invocation = null;
+        this.invocation = null,
+        this.instantiation = null;
 
   VariableUse.localParameter(this.localFunction)
       : this.kind = VariableUseKind.localParameter,
         this.member = null,
-        this.invocation = null {
+        this.invocation = null,
+        this.instantiation = null {
     assert(localFunction is ir.FunctionDeclaration ||
         localFunction is ir.FunctionExpression);
   }
@@ -458,12 +472,14 @@
   VariableUse.memberReturnType(this.member)
       : this.kind = VariableUseKind.memberReturnType,
         this.localFunction = null,
-        this.invocation = null;
+        this.invocation = null,
+        this.instantiation = null;
 
   VariableUse.localReturnType(this.localFunction)
       : this.kind = VariableUseKind.localReturnType,
         this.member = null,
-        this.invocation = null {
+        this.invocation = null,
+        this.instantiation = null {
     assert(localFunction is ir.FunctionDeclaration ||
         localFunction is ir.FunctionExpression);
   }
@@ -471,25 +487,35 @@
   VariableUse.constructorTypeArgument(this.member)
       : this.kind = VariableUseKind.constructorTypeArgument,
         this.localFunction = null,
-        this.invocation = null;
+        this.invocation = null,
+        this.instantiation = null;
 
   VariableUse.staticTypeArgument(this.member)
       : this.kind = VariableUseKind.staticTypeArgument,
         this.localFunction = null,
-        this.invocation = null;
+        this.invocation = null,
+        this.instantiation = null;
 
   VariableUse.instanceTypeArgument(this.invocation)
       : this.kind = VariableUseKind.instanceTypeArgument,
         this.member = null,
-        this.localFunction = null;
+        this.localFunction = null,
+        this.instantiation = null;
 
   VariableUse.localTypeArgument(this.localFunction, this.invocation)
       : this.kind = VariableUseKind.localTypeArgument,
-        this.member = null {
+        this.member = null,
+        this.instantiation = null {
     assert(localFunction is ir.FunctionDeclaration ||
         localFunction is ir.FunctionExpression);
   }
 
+  VariableUse.instantiationTypeArgument(this.instantiation)
+      : this.kind = VariableUseKind.instantiationTypeArgument,
+        this.member = null,
+        this.localFunction = null,
+        this.invocation = null;
+
   static const VariableUse explicit =
       const VariableUse._simple(VariableUseKind.explicit);
 
@@ -512,7 +538,8 @@
       kind.hashCode * 11 +
       member.hashCode * 13 +
       localFunction.hashCode * 17 +
-      invocation.hashCode * 19;
+      invocation.hashCode * 19 +
+      instantiation.hashCode * 23;
 
   bool operator ==(other) {
     if (identical(this, other)) return true;
@@ -520,11 +547,13 @@
     return kind == other.kind &&
         member == other.member &&
         localFunction == other.localFunction &&
-        invocation == other.invocation;
+        invocation == other.invocation &&
+        instantiation == other.instantiation;
   }
 
   String toString() => 'VariableUse(kind=$kind,member=$member,'
-      'localFunction=$localFunction,invocation=$invocation)';
+      'localFunction=$localFunction,invocation=$invocation,'
+      'instantiation=$instantiation)';
 }
 
 class KernelScopeInfo {
@@ -1150,4 +1179,7 @@
   bool localFunctionNeedsSignature(ir.Node node);
 
   bool selectorNeedsTypeArguments(Selector selector);
+
+  bool instantiationNeedsTypeArguments(
+      DartType functionType, int typeArgumentCount);
 }
diff --git a/pkg/compiler/lib/src/js_model/closure_visitors.dart b/pkg/compiler/lib/src/js_model/closure_visitors.dart
index 5868aea..9c1dfbb 100644
--- a/pkg/compiler/lib/src/js_model/closure_visitors.dart
+++ b/pkg/compiler/lib/src/js_model/closure_visitors.dart
@@ -631,7 +631,8 @@
 
   @override
   visitInstantiation(ir.Instantiation node) {
-    visitChildrenInContext(node, VariableUse.explicit);
+    visitChildrenInContext(
+        node, new VariableUse.instantiationTypeArgument(node));
   }
 
   /// Returns true if the node is a field, or a constructor (factory or
diff --git a/pkg/compiler/lib/src/js_model/js_strategy.dart b/pkg/compiler/lib/src/js_model/js_strategy.dart
index d8a72c6..e1779e6 100644
--- a/pkg/compiler/lib/src/js_model/js_strategy.dart
+++ b/pkg/compiler/lib/src/js_model/js_strategy.dart
@@ -416,7 +416,6 @@
         map.toBackendFunctionSet(backendUsage.helperFunctionsUsed);
     Set<ClassEntity> helperClassesUsed =
         map.toBackendClassSet(backendUsage.helperClassesUsed);
-
     return new BackendUsageImpl(
         globalFunctionDependencies: globalFunctionDependencies,
         globalClassDependencies: globalClassDependencies,
@@ -572,7 +571,8 @@
         methodsNeedingTypeArguments,
         null,
         null,
-        selectorsNeedingTypeArguments);
+        selectorsNeedingTypeArguments,
+        rtiNeed.instantiationsNeedingTypeArguments);
   }
 
   /// Construct a closure class and set up the necessary class inference
@@ -829,12 +829,28 @@
 class TrivialClosureRtiNeed implements ClosureRtiNeed {
   const TrivialClosureRtiNeed();
 
+  @override
   bool localFunctionNeedsSignature(ir.Node node) => true;
+
+  @override
   bool classNeedsTypeArguments(ClassEntity cls) => true;
+
+  @override
   bool methodNeedsTypeArguments(FunctionEntity method) => true;
+
+  @override
   bool localFunctionNeedsTypeArguments(ir.Node node) => true;
+
+  @override
   bool selectorNeedsTypeArguments(Selector selector) => true;
+
+  @override
   bool methodNeedsSignature(MemberEntity method) => true;
+
+  @override
+  bool instantiationNeedsTypeArguments(
+          DartType functionType, int typeArgumentCount) =>
+      true;
 }
 
 class JsClosureRtiNeed implements ClosureRtiNeed {
@@ -849,6 +865,7 @@
       this.localFunctionsNodesNeedingTypeArguments,
       this.localFunctionsNodesNeedingSignature);
 
+  @override
   bool localFunctionNeedsSignature(ir.Node node) {
     assert(node is ir.FunctionDeclaration || node is ir.FunctionExpression);
     return backendUsage.isRuntimeTypeUsed
@@ -856,12 +873,15 @@
         : localFunctionsNodesNeedingSignature.contains(node);
   }
 
+  @override
   bool classNeedsTypeArguments(ClassEntity cls) =>
       rtiNeed.classNeedsTypeArguments(cls);
 
+  @override
   bool methodNeedsTypeArguments(FunctionEntity method) =>
       rtiNeed.methodNeedsTypeArguments(method);
 
+  @override
   bool localFunctionNeedsTypeArguments(ir.Node node) {
     assert(node is ir.FunctionDeclaration || node is ir.FunctionExpression);
     return backendUsage.isRuntimeTypeUsed
@@ -869,9 +889,16 @@
         : localFunctionsNodesNeedingTypeArguments.contains(node);
   }
 
+  @override
   bool selectorNeedsTypeArguments(Selector selector) =>
       rtiNeed.selectorNeedsTypeArguments(selector);
 
+  @override
   bool methodNeedsSignature(MemberEntity method) =>
       rtiNeed.methodNeedsSignature(method);
+
+  @override
+  bool instantiationNeedsTypeArguments(
+          DartType functionType, int typeArgumentCount) =>
+      rtiNeed.instantiationNeedsTypeArguments(functionType, typeArgumentCount);
 }
diff --git a/pkg/compiler/lib/src/resolution/registry.dart b/pkg/compiler/lib/src/resolution/registry.dart
index 2d713f1..ecdf526 100644
--- a/pkg/compiler/lib/src/resolution/registry.dart
+++ b/pkg/compiler/lib/src/resolution/registry.dart
@@ -22,6 +22,7 @@
   Setlet<ConstantExpression> _constantLiterals;
   Setlet<dynamic> _nativeData;
   Setlet<ClassEntity> _seenClasses;
+  Set<GenericInstantiation> _genericInstantiations;
 
   ResolutionWorldImpactBuilder(this.name);
 
@@ -30,9 +31,7 @@
 
   void registerMapLiteral(MapLiteralUse mapLiteralUse) {
     assert(mapLiteralUse != null);
-    if (_mapLiterals == null) {
-      _mapLiterals = new Setlet<MapLiteralUse>();
-    }
+    _mapLiterals ??= new Setlet<MapLiteralUse>();
     _mapLiterals.add(mapLiteralUse);
   }
 
@@ -43,9 +42,7 @@
 
   void registerListLiteral(ListLiteralUse listLiteralUse) {
     assert(listLiteralUse != null);
-    if (_listLiterals == null) {
-      _listLiterals = new Setlet<ListLiteralUse>();
-    }
+    _listLiterals ??= new Setlet<ListLiteralUse>();
     _listLiterals.add(listLiteralUse);
   }
 
@@ -55,9 +52,7 @@
   }
 
   void registerConstSymbolName(String name) {
-    if (_constSymbolNames == null) {
-      _constSymbolNames = new Setlet<String>();
-    }
+    _constSymbolNames ??= new Setlet<String>();
     _constSymbolNames.add(name);
   }
 
@@ -67,9 +62,7 @@
   }
 
   void registerFeature(Feature feature) {
-    if (_features == null) {
-      _features = new EnumSet<Feature>();
-    }
+    _features ??= new EnumSet<Feature>();
     _features.add(feature);
   }
 
@@ -81,9 +74,7 @@
   }
 
   void registerConstantLiteral(ConstantExpression constant) {
-    if (_constantLiterals == null) {
-      _constantLiterals = new Setlet<ConstantExpression>();
-    }
+    _constantLiterals ??= new Setlet<ConstantExpression>();
     _constantLiterals.add(constant);
   }
 
@@ -95,9 +86,7 @@
 
   void registerNativeData(dynamic nativeData) {
     assert(nativeData != null);
-    if (_nativeData == null) {
-      _nativeData = new Setlet<dynamic>();
-    }
+    _nativeData ??= new Setlet<dynamic>();
     _nativeData.add(nativeData);
   }
 
@@ -107,9 +96,7 @@
   }
 
   void registerSeenClass(ClassEntity seenClass) {
-    if (_seenClasses == null) {
-      _seenClasses = new Setlet<ClassEntity>();
-    }
+    _seenClasses ??= new Setlet<ClassEntity>();
     _seenClasses.add(seenClass);
   }
 
@@ -118,6 +105,16 @@
     return _seenClasses ?? const <ClassEntity>[];
   }
 
+  void registerInstantiation(GenericInstantiation instantiation) {
+    _genericInstantiations ??= new Set<GenericInstantiation>();
+    _genericInstantiations.add(instantiation);
+  }
+
+  @override
+  Iterable<GenericInstantiation> get genericInstantiations {
+    return _genericInstantiations ?? const <GenericInstantiation>[];
+  }
+
   String toString() {
     StringBuffer sb = new StringBuffer();
     sb.write('_ResolutionWorldImpact($name)');
@@ -155,6 +152,12 @@
         sb.write('\n  $data');
       }
     }
+    if (_genericInstantiations != null) {
+      sb.write('\n instantiations:');
+      for (var data in _genericInstantiations) {
+        sb.write('\n  $data');
+      }
+    }
     return sb.toString();
   }
 }
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 079f4c0..aa5f212 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -35,6 +35,7 @@
 import '../types/abstract_value_domain.dart';
 import '../types/types.dart';
 import '../universe/call_structure.dart';
+import '../universe/feature.dart';
 import '../universe/selector.dart';
 import '../universe/side_effects.dart' show SideEffects;
 import '../universe/target_checks.dart' show TargetChecks;
@@ -4234,9 +4235,20 @@
     var arguments = <HInstruction>[];
     node.expression.accept(this);
     arguments.add(pop());
-    for (ir.DartType type in node.typeArguments) {
-      HInstruction instruction = typeBuilder.analyzeTypeArgument(
-          _elementMap.getDartType(type), sourceElement);
+    // TODO(johnniwinther): Use the static type of the expression.
+    bool typeArgumentsNeeded = rtiNeed.instantiationNeedsTypeArguments(
+        null, node.typeArguments.length);
+    List<DartType> typeArguments = node.typeArguments
+        .map((type) => typeArgumentsNeeded
+            ? _elementMap.getDartType(type)
+            : _commonElements.dynamicType)
+        .toList();
+    registry.registerGenericInstantiation(
+        new GenericInstantiation(null, typeArguments));
+    // TODO(johnniwinther): Can we avoid creating the instantiation object?
+    for (DartType type in typeArguments) {
+      HInstruction instruction =
+          typeBuilder.analyzeTypeArgument(type, sourceElement);
       arguments.add(instruction);
     }
     int typeArgumentCount = node.typeArguments.length;
diff --git a/pkg/compiler/lib/src/ssa/kernel_impact.dart b/pkg/compiler/lib/src/ssa/kernel_impact.dart
index 2352143..0c3846e 100644
--- a/pkg/compiler/lib/src/ssa/kernel_impact.dart
+++ b/pkg/compiler/lib/src/ssa/kernel_impact.dart
@@ -680,7 +680,9 @@
   @override
   void visitInstantiation(ir.Instantiation node) {
     // TODO(johnniwinther): Track which arities are used in instantiation.
-    impactBuilder.registerFeature(Feature.GENERIC_INSTANTIATION);
+    impactBuilder.registerInstantiation(new GenericInstantiation(
+        elementMap.getStaticType(node.expression),
+        node.typeArguments.map(elementMap.getDartType).toList()));
     node.visitChildren(this);
   }
 
diff --git a/pkg/compiler/lib/src/universe/feature.dart b/pkg/compiler/lib/src/universe/feature.dart
index d63fa34..3d4f018 100644
--- a/pkg/compiler/lib/src/universe/feature.dart
+++ b/pkg/compiler/lib/src/universe/feature.dart
@@ -8,7 +8,8 @@
 /// compilation pipeline, for example during resolution.
 library compiler.universe.feature;
 
-import '../elements/types.dart' show InterfaceType;
+import '../elements/types.dart' show DartType, InterfaceType;
+import '../util/util.dart';
 
 /// A language feature that may be seen in the program.
 // TODO(johnniwinther): Should mirror usage be part of this?
@@ -43,9 +44,6 @@
   /// A field without an initializer.
   FIELD_WITHOUT_INITIALIZER,
 
-  /// A generic instantiation (application of type parameters).
-  GENERIC_INSTANTIATION,
-
   /// A field whose initialization is not a constant.
   LAZY_FIELD,
 
@@ -149,3 +147,33 @@
     return 'ListLiteralUse($type,isConstant:$isConstant,isEmpty:$isEmpty)';
   }
 }
+
+/// A generic instantiation of an expression of type [functionType] with the
+/// given [typeArguments].
+class GenericInstantiation {
+  /// The static type of the instantiated expression.
+  final DartType functionType;
+
+  /// The type arguments of the instantiation.
+  final List<DartType> typeArguments;
+
+  GenericInstantiation(this.functionType, this.typeArguments);
+
+  /// Short textual representation use for testing.
+  String get shortText => '<${typeArguments.join(',')}>';
+
+  int get hashCode =>
+      Hashing.listHash(typeArguments, Hashing.objectHash(functionType));
+
+  bool operator ==(other) {
+    if (identical(this, other)) return true;
+    if (other is! GenericInstantiation) return false;
+    return functionType == other.functionType &&
+        equalElements(typeArguments, other.typeArguments);
+  }
+
+  String toString() {
+    return 'GenericInstantiation(functionType:$functionType,'
+        'typeArguments:$typeArguments)';
+  }
+}
diff --git a/pkg/compiler/lib/src/universe/world_impact.dart b/pkg/compiler/lib/src/universe/world_impact.dart
index 3620b85..af5eaae 100644
--- a/pkg/compiler/lib/src/universe/world_impact.dart
+++ b/pkg/compiler/lib/src/universe/world_impact.dart
@@ -99,9 +99,7 @@
 
   void registerDynamicUse(DynamicUse dynamicUse) {
     assert(dynamicUse != null);
-    if (_dynamicUses == null) {
-      _dynamicUses = new Setlet<DynamicUse>();
-    }
+    _dynamicUses ??= new Setlet<DynamicUse>();
     _dynamicUses.add(dynamicUse);
   }
 
@@ -111,9 +109,7 @@
 
   void registerTypeUse(TypeUse typeUse) {
     assert(typeUse != null);
-    if (_typeUses == null) {
-      _typeUses = new Setlet<TypeUse>();
-    }
+    _typeUses ??= new Setlet<TypeUse>();
     _typeUses.add(typeUse);
   }
 
@@ -123,9 +119,7 @@
 
   void registerStaticUse(StaticUse staticUse) {
     assert(staticUse != null);
-    if (_staticUses == null) {
-      _staticUses = new Setlet<StaticUse>();
-    }
+    _staticUses ??= new Setlet<StaticUse>();
     _staticUses.add(staticUse);
   }
 
@@ -135,9 +129,7 @@
 
   void registerConstantUse(ConstantUse constantUse) {
     assert(constantUse != null);
-    if (_constantUses == null) {
-      _constantUses = new Setlet<ConstantUse>();
-    }
+    _constantUses ??= new Setlet<ConstantUse>();
     _constantUses.add(constantUse);
   }
 
diff --git a/tests/compiler/dart2js/closure/closure_test.dart b/tests/compiler/dart2js/closure/closure_test.dart
index dfc8631..819890e 100644
--- a/tests/compiler/dart2js/closure/closure_test.dart
+++ b/tests/compiler/dart2js/closure/closure_test.dart
@@ -25,7 +25,7 @@
   asyncTest(() async {
     Directory dataDir = new Directory.fromUri(Platform.script.resolve('data'));
     await checkTests(dataDir, computeKernelClosureData,
-        skipForKernel: skipForKernel, args: args);
+        skipForKernel: skipForKernel, args: args, testOmit: true);
   });
 }
 
diff --git a/tests/compiler/dart2js/closure/data/generic_strong.dart b/tests/compiler/dart2js/closure/data/generic_strong.dart
index 1fbe9ca..8823cb6 100644
--- a/tests/compiler/dart2js/closure/data/generic_strong.dart
+++ b/tests/compiler/dart2js/closure/data/generic_strong.dart
@@ -55,8 +55,14 @@
       return /*fields=[S,U],free=[S,U],hasThis*/ () => '$S$U';
     }
 
-    var local2 = /*fields=[S,this],free=[S,this],hasThis*/ (o) {
-      return /*fields=[S,this],free=[S,this],hasThis*/ () => new Map<T, S>();
+    var local2 =
+        /*strong.fields=[S,this],free=[S,this],hasThis*/
+        /*omit.hasThis*/
+        (o) {
+      return
+          /*strong.fields=[S,this],free=[S,this],hasThis*/
+          /*omit.hasThis*/
+          () => new Map<T, S>();
     };
     return local2(local<double>());
   }
diff --git a/tests/compiler/dart2js/closure/data/instantiation1.dart b/tests/compiler/dart2js/closure/data/instantiation1.dart
new file mode 100644
index 0000000..9e86e11
--- /dev/null
+++ b/tests/compiler/dart2js/closure/data/instantiation1.dart
@@ -0,0 +1,26 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+int f<T>(T a) => null;
+
+typedef int F<R>(R a);
+
+/*element: B.:hasThis*/
+class B<S> {
+  /*element: B.method:hasThis*/
+  method() {
+    return
+        /*kernel.hasThis*/
+        /*strong.fields=[this],free=[this],hasThis*/
+        /*omit.hasThis*/
+        () {
+      F<S> c = f;
+      return c;
+    };
+  }
+}
+
+main() {
+  new B().method();
+}
diff --git a/tests/compiler/dart2js/closure/data/instantiation2.dart b/tests/compiler/dart2js/closure/data/instantiation2.dart
new file mode 100644
index 0000000..4fd81d1
--- /dev/null
+++ b/tests/compiler/dart2js/closure/data/instantiation2.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+bool f<T>(T a) => a is T;
+
+typedef bool F<R>(R a);
+
+/*element: B.:hasThis*/
+class B<S> {
+  /*element: B.method:hasThis*/
+  method() {
+    return
+        /*kernel.hasThis*/
+        /*!kernel.fields=[this],free=[this],hasThis*/
+        () {
+      F<S> c = f;
+      return c;
+    };
+  }
+}
+
+main() {
+  new B().method();
+}
diff --git a/tests/compiler/dart2js/closure/data/instantiation3.dart b/tests/compiler/dart2js/closure/data/instantiation3.dart
new file mode 100644
index 0000000..aeeee92
--- /dev/null
+++ b/tests/compiler/dart2js/closure/data/instantiation3.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+int f<T>(T a) => null;
+
+typedef int F<R>(R a);
+
+method<S>() {
+  return
+      /*strong.fields=[S],free=[S]*/
+      /*omit.*/
+      () {
+    F<S> c = f;
+    return c;
+  };
+}
+
+main() {
+  method();
+}
diff --git a/tests/compiler/dart2js/closure/data/instantiation4.dart b/tests/compiler/dart2js/closure/data/instantiation4.dart
new file mode 100644
index 0000000..957eca0
--- /dev/null
+++ b/tests/compiler/dart2js/closure/data/instantiation4.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+bool f<T>(T a) => a is T;
+
+typedef bool F<R>(R a);
+
+method<S>() {
+  return
+      /*strong.fields=[S],free=[S]*/
+      /*omit.fields=[S],free=[S]*/
+      () {
+    F<S> c = f;
+    return c;
+  };
+}
+
+main() {
+  method();
+}
diff --git a/tests/compiler/dart2js/closure/data/instantiation_strong.dart b/tests/compiler/dart2js/closure/data/instantiation_strong.dart
index 10b70e9..8f95b60 100644
--- a/tests/compiler/dart2js/closure/data/instantiation_strong.dart
+++ b/tests/compiler/dart2js/closure/data/instantiation_strong.dart
@@ -5,7 +5,8 @@
 T id<T>(T t) => t;
 
 method<S>(S s) {
-  /*fields=[S],free=[S]*/
+  /*strong.fields=[S],free=[S]*/
+  /*omit.*/
   S Function(S) getId() => id;
   return getId();
 }
diff --git a/tests/compiler/dart2js/closure/data/list_literal_untested_strong_trust.dart b/tests/compiler/dart2js/closure/data/list_literal_untested_strong_trust.dart
index 55e709a..52788fc 100644
--- a/tests/compiler/dart2js/closure/data/list_literal_untested_strong_trust.dart
+++ b/tests/compiler/dart2js/closure/data/list_literal_untested_strong_trust.dart
@@ -6,7 +6,9 @@
 
 @NoInline()
 method<T>() {
-  /**/ dynamic local() => <T>[];
+  /*!strong.*/
+  /*strong.fields=[T],free=[T]*/
+  dynamic local() => <T>[];
   return local;
 }
 
diff --git a/tests/compiler/dart2js/closure/data/list_literal_untested_trust.dart b/tests/compiler/dart2js/closure/data/list_literal_untested_trust.dart
index 92b057e..89fe422 100644
--- a/tests/compiler/dart2js/closure/data/list_literal_untested_trust.dart
+++ b/tests/compiler/dart2js/closure/data/list_literal_untested_trust.dart
@@ -9,7 +9,9 @@
   /*element: A.method:hasThis*/
   @NoInline()
   method() {
-    /*hasThis*/ dynamic local() => <T>[];
+    /*!strong.hasThis*/
+    /*strong.fields=[this],free=[this],hasThis*/
+    dynamic local() => <T>[];
     return local;
   }
 }
diff --git a/tests/compiler/dart2js/closure/data/map_literal_untested_strong_trust.dart b/tests/compiler/dart2js/closure/data/map_literal_untested_strong_trust.dart
index 8793b90..6cfb9e9 100644
--- a/tests/compiler/dart2js/closure/data/map_literal_untested_strong_trust.dart
+++ b/tests/compiler/dart2js/closure/data/map_literal_untested_strong_trust.dart
@@ -6,7 +6,9 @@
 
 @NoInline()
 method<T>() {
-  /**/ dynamic local() => <T, int>{};
+  /*!strong.*/
+  /*strong.fields=[T],free=[T]*/
+  dynamic local() => <T, int>{};
   return local;
 }
 
diff --git a/tests/compiler/dart2js/closure/data/map_literal_untested_trust.dart b/tests/compiler/dart2js/closure/data/map_literal_untested_trust.dart
index 958da83..146de88 100644
--- a/tests/compiler/dart2js/closure/data/map_literal_untested_trust.dart
+++ b/tests/compiler/dart2js/closure/data/map_literal_untested_trust.dart
@@ -9,7 +9,9 @@
   /*element: A.method:hasThis*/
   @NoInline()
   method() {
-    /*hasThis*/ dynamic local() => <T, int>{};
+    /*!strong.hasThis*/
+    /*strong.fields=[this],free=[this],hasThis*/
+    dynamic local() => <T, int>{};
     return local;
   }
 }
diff --git a/tests/compiler/dart2js/closure/data/test_type.dart b/tests/compiler/dart2js/closure/data/test_type.dart
index aaadba2..1cd8aef 100644
--- a/tests/compiler/dart2js/closure/data/test_type.dart
+++ b/tests/compiler/dart2js/closure/data/test_type.dart
@@ -38,7 +38,7 @@
 class Class3<T> {
   /*element: Class3.method3:hasThis*/
   method3(dynamic o) {
-    /*kernel.fields=[o],free=[o],hasThis*/
+    /*!strong.fields=[o],free=[o],hasThis*/
     /*strong.fields=[o,this],free=[o,this],hasThis*/
     T local() => o;
     return local;
diff --git a/tests/compiler/dart2js/closure/data/test_type_strong.dart b/tests/compiler/dart2js/closure/data/test_type_strong.dart
index 93dcec0..cb8f88d 100644
--- a/tests/compiler/dart2js/closure/data/test_type_strong.dart
+++ b/tests/compiler/dart2js/closure/data/test_type_strong.dart
@@ -30,7 +30,8 @@
 
 /*element: method3:*/
 method3<T>(dynamic o) {
-  /*fields=[T,o],free=[T,o]*/
+  /*strong.fields=[T,o],free=[T,o]*/
+  /*omit.fields=[o],free=[o]*/
   T local() => o;
   return local;
 }
diff --git a/tests/compiler/dart2js/closure/data/test_type_strong_trust.dart b/tests/compiler/dart2js/closure/data/test_type_strong_trust.dart
deleted file mode 100644
index abb909b..0000000
--- a/tests/compiler/dart2js/closure/data/test_type_strong_trust.dart
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-////////////////////////////////////////////////////////////////////////////////
-/// Explicit is-test is required even with --omit-implicit-checks.
-////////////////////////////////////////////////////////////////////////////////
-
-/*element: method1:*/
-method1<T>(dynamic o) {
-  /*fields=[T,o],free=[T,o]*/
-  dynamic local() => o is T;
-  return local;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-/// Explicit as-cast is required even with --omit-implicit-checks.
-////////////////////////////////////////////////////////////////////////////////
-
-/*element: method2:*/
-method2<T>(dynamic o) {
-  /*fields=[T,o],free=[T,o]*/
-  dynamic local() => o as T;
-  return local;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-/// Implicit as-cast is not required with --omit-implicit-checks.
-////////////////////////////////////////////////////////////////////////////////
-
-/*element: method3:*/
-method3<T>(dynamic o) {
-  /*fields=[o],free=[o]*/
-  T local() => o;
-  return local;
-}
-
-main() {
-  method1<int>(0).call();
-  method2<int>(0).call();
-  method3<int>(0).call();
-}
diff --git a/tests/compiler/dart2js/closure/data/test_type_trust.dart b/tests/compiler/dart2js/closure/data/test_type_trust.dart
deleted file mode 100644
index 7ed8dc1..0000000
--- a/tests/compiler/dart2js/closure/data/test_type_trust.dart
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-////////////////////////////////////////////////////////////////////////////////
-/// Explicit is-test is required even with --omit-implicit-checks.
-////////////////////////////////////////////////////////////////////////////////
-
-/*element: Class1.:hasThis*/
-class Class1<T> {
-  /*element: Class1.method1:hasThis*/
-  method1(dynamic o) {
-    /*fields=[o,this],free=[o,this],hasThis*/
-    dynamic local() => o is T;
-    return local;
-  }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-/// Explicit as-cast is required even with --omit-implicit-checks.
-////////////////////////////////////////////////////////////////////////////////
-
-/*element: Class2.:hasThis*/
-class Class2<T> {
-  /*element: Class2.method2:hasThis*/
-  method2(dynamic o) {
-    /*fields=[o,this],free=[o,this],hasThis*/
-    dynamic local() => o as T;
-    return local;
-  }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-/// Implicit as-cast is not required with --omit-implicit-checks.
-////////////////////////////////////////////////////////////////////////////////
-
-/*element: Class3.:hasThis*/
-class Class3<T> {
-  /*element: Class3.method3:hasThis*/
-  method3(dynamic o) {
-    /*fields=[o],free=[o],hasThis*/
-    T local() => o;
-    return local;
-  }
-}
-
-main() {
-  new Class1<int>().method1(0).call();
-  new Class2<int>().method2(0).call();
-  new Class3<int>().method3(0).call();
-}
diff --git a/tests/compiler/dart2js/closure/data/type_annotations.dart b/tests/compiler/dart2js/closure/data/type_annotations.dart
index 112565e..a891048 100644
--- a/tests/compiler/dart2js/closure/data/type_annotations.dart
+++ b/tests/compiler/dart2js/closure/data/type_annotations.dart
@@ -48,7 +48,7 @@
 class Class2<T> {
   /*element: Class2.method2:hasThis*/
   method2() {
-    /*kernel.hasThis*/
+    /*!strong.hasThis*/
     /*strong.fields=[this],free=[this],hasThis*/
     dynamic local(T t) => t;
     return local;
@@ -63,7 +63,7 @@
 class Class3<T> {
   /*element: Class3.method3:hasThis*/
   method3(dynamic o) {
-    /*kernel.fields=[o],free=[o],hasThis*/
+    /*!strong.fields=[o],free=[o],hasThis*/
     /*strong.fields=[o,this],free=[o,this],hasThis*/
     T local() => o;
     return local;
@@ -106,7 +106,7 @@
 class Class6<T> {
   /*element: Class6.method6:hasThis*/
   method6() {
-    /*kernel.hasThis*/
+    /*!strong.hasThis*/
     /*strong.fields=[this],free=[this],hasThis*/
     dynamic local(T t) {
       /*fields=[t],free=[t],hasThis*/
@@ -126,7 +126,7 @@
 class Class7<T> {
   /*element: Class7.method7:hasThis*/
   method7(dynamic o) {
-    /*kernel.fields=[o],free=[o],hasThis*/
+    /*!strong.fields=[o],free=[o],hasThis*/
     /*strong.fields=[o,this],free=[o,this],hasThis*/
     T local() {
       /*fields=[o],free=[o],hasThis*/
diff --git a/tests/compiler/dart2js/closure/data/type_annotations_strong.dart b/tests/compiler/dart2js/closure/data/type_annotations_strong.dart
index 3b4c2a6..878e3bd 100644
--- a/tests/compiler/dart2js/closure/data/type_annotations_strong.dart
+++ b/tests/compiler/dart2js/closure/data/type_annotations_strong.dart
@@ -21,7 +21,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 method2<T>() {
-  /*fields=[T],free=[T]*/
+  /*strong.fields=[T],free=[T]*/
+  /*omit.*/
   dynamic local(T t) => t;
   return local;
 }
@@ -31,7 +32,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 method3<T>(dynamic o) {
-  /*fields=[T,o],free=[T,o]*/
+  /*strong.fields=[T,o],free=[T,o]*/
+  /*omit.fields=[o],free=[o]*/
   T local() => o;
   return local;
 }
@@ -61,7 +63,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 method6<T>() {
-  /*fields=[T],free=[T]*/
+  /*strong.fields=[T],free=[T]*/
+  /*omit.*/
   dynamic local(T t) {
     /*fields=[t],free=[t]*/
     dynamic inner() => t;
@@ -76,7 +79,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 method7<T>(dynamic o) {
-  /*fields=[T,o],free=[T,o]*/
+  /*strong.fields=[T,o],free=[T,o]*/
+  /*omit.fields=[o],free=[o]*/
   T local() {
     /*fields=[o],free=[o]*/
     dynamic inner() => o;
diff --git a/tests/compiler/dart2js/closure/data/type_annotations_strong_trust.dart b/tests/compiler/dart2js/closure/data/type_annotations_strong_trust.dart
deleted file mode 100644
index 1ec0bf1..0000000
--- a/tests/compiler/dart2js/closure/data/type_annotations_strong_trust.dart
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-////////////////////////////////////////////////////////////////////////////////
-/// A sound assignment to a local variable doesn't capture the type variable.
-////////////////////////////////////////////////////////////////////////////////
-
-method1<T>(T o) {
-  /*fields=[o],free=[o]*/
-  dynamic local() {
-    T t = o;
-    return t;
-  }
-
-  return local;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-/// A local function parameter type is with --omit-type-checks.
-////////////////////////////////////////////////////////////////////////////////
-
-method2<T>() {
-  /**/
-  dynamic local(T t) => t;
-  return local;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-/// A local function return type is not captured with --omit-type-checks.
-////////////////////////////////////////////////////////////////////////////////
-
-method3<T>(dynamic o) {
-  /*fields=[o],free=[o]*/
-  T local() => o;
-  return local;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-/// A member parameter type is not captured.
-////////////////////////////////////////////////////////////////////////////////
-
-method4<T>(T o) {
-  /*fields=[o],free=[o]*/
-  dynamic local() => o;
-  return local;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-/// A member return type is not captured.
-////////////////////////////////////////////////////////////////////////////////
-
-T method5<T>(dynamic o) {
-  /*fields=[o],free=[o]*/
-  dynamic local() => o;
-  return local();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-/// A local function parameter type is not captured by an inner local function.
-////////////////////////////////////////////////////////////////////////////////
-
-method6<T>() {
-  /**/
-  dynamic local(T t) {
-    /*fields=[t],free=[t]*/
-    dynamic inner() => t;
-    return inner;
-  }
-
-  return local;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-/// A local function return type is not captured by an inner local function.
-////////////////////////////////////////////////////////////////////////////////
-
-method7<T>(dynamic o) {
-  /*fields=[o],free=[o]*/
-  T local() {
-    /*fields=[o],free=[o]*/
-    dynamic inner() => o;
-    return inner();
-  }
-
-  return local;
-}
-
-main() {
-  method1<int>(0).call();
-  method2<int>().call(0);
-  method3<int>(0).call();
-  method4<int>(0).call();
-  method5<int>(0);
-  method6<int>().call(0).call();
-  method7<int>(0).call().call();
-}
diff --git a/tests/compiler/dart2js/closure/data/type_annotations_trust.dart b/tests/compiler/dart2js/closure/data/type_annotations_trust.dart
deleted file mode 100644
index ff0ee55..0000000
--- a/tests/compiler/dart2js/closure/data/type_annotations_trust.dart
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-////////////////////////////////////////////////////////////////////////////////
-/// A sound assignment to a local variable doesn't capture the type variable.
-////////////////////////////////////////////////////////////////////////////////
-
-/*element: Class1.:hasThis*/
-class Class1<T> {
-  /*element: Class1.method1:hasThis*/
-  method1(T o) {
-    /*fields=[o],free=[o],hasThis*/
-    dynamic local() {
-      T t = o;
-      return t;
-    }
-
-    return local;
-  }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-/// A local function parameter type is with --omit-type-checks.
-////////////////////////////////////////////////////////////////////////////////
-
-/*element: Class2.:hasThis*/
-class Class2<T> {
-  /*element: Class2.method2:hasThis*/
-  method2() {
-    /*hasThis*/
-    dynamic local(T t) => t;
-    return local;
-  }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-/// A local function return type is not captured with --omit-type-checks.
-////////////////////////////////////////////////////////////////////////////////
-
-/*element: Class3.:hasThis*/
-class Class3<T> {
-  /*element: Class3.method3:hasThis*/
-  method3(dynamic o) {
-    /*fields=[o],free=[o],hasThis*/
-    T local() => o;
-    return local;
-  }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-/// A member parameter type is not captured.
-////////////////////////////////////////////////////////////////////////////////
-
-/*element: Class4.:hasThis*/
-class Class4<T> {
-  /*element: Class4.method4:hasThis*/
-  method4(T o) {
-    /*fields=[o],free=[o],hasThis*/
-    dynamic local() => o;
-    return local;
-  }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-/// A member return type is not captured.
-////////////////////////////////////////////////////////////////////////////////
-
-/*element: Class5.:hasThis*/
-class Class5<T> {
-  /*element: Class5.method5:hasThis*/
-  T method5(dynamic o) {
-    /*fields=[o],free=[o],hasThis*/
-    dynamic local() => o;
-    return local();
-  }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-/// A local function parameter type is not captured by an inner local function.
-////////////////////////////////////////////////////////////////////////////////
-
-/*element: Class6.:hasThis*/
-class Class6<T> {
-  /*element: Class6.method6:hasThis*/
-  method6() {
-    /*hasThis*/
-    dynamic local(T t) {
-      /*fields=[t],free=[t],hasThis*/
-      dynamic inner() => t;
-      return inner;
-    }
-
-    return local;
-  }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-/// A local function return type is not captured by an inner local function.
-////////////////////////////////////////////////////////////////////////////////
-
-/*element: Class7.:hasThis*/
-class Class7<T> {
-  /*element: Class7.method7:hasThis*/
-  method7(dynamic o) {
-    /*fields=[o],free=[o],hasThis*/
-    T local() {
-      /*fields=[o],free=[o],hasThis*/
-      dynamic inner() => o;
-      return inner();
-    }
-
-    return local;
-  }
-}
-
-main() {
-  new Class1<int>().method1(0).call();
-  new Class2<int>().method2().call(0);
-  new Class3<int>().method3(0).call();
-  new Class4<int>().method4(0).call();
-  new Class5<int>().method5(0);
-  new Class6<int>().method6().call(0).call();
-  new Class7<int>().method7(0).call().call();
-}
diff --git a/tests/compiler/dart2js/inference/data/field_type.dart b/tests/compiler/dart2js/inference/data/field_type.dart
index 51b672d..44168f3 100644
--- a/tests/compiler/dart2js/inference/data/field_type.dart
+++ b/tests/compiler/dart2js/inference/data/field_type.dart
@@ -202,7 +202,8 @@
 
   /*element: A9.:[exact=A9]*/
   A9(/*[exact=JSBool]*/ x) {
-    if (x) {} else {
+    if (x) {
+    } else {
       /*update: [exact=A9]*/ f9 = "1";
     }
   }
@@ -727,7 +728,8 @@
   /*element: A29.:[exact=A29]*/
   A29(/*[exact=JSUInt31]*/ x) {
     this. /*update: [exact=A29]*/ f29a = x;
-    if (x /*invoke: [exact=JSUInt31]*/ == 0) {} else {
+    if (x /*invoke: [exact=JSUInt31]*/ == 0) {
+    } else {
       return;
     }
     this. /*update: [exact=A29]*/ f29b = x;
diff --git a/tests/compiler/dart2js/inference/data/general.dart b/tests/compiler/dart2js/inference/data/general.dart
index ea317d5..1b2d806 100644
--- a/tests/compiler/dart2js/inference/data/general.dart
+++ b/tests/compiler/dart2js/inference/data/general.dart
@@ -306,7 +306,8 @@
 
 /*element: testIsCheck26:[subclass=JSInt]*/
 testIsCheck26(/*[null|subclass=Object]*/ a) {
-  if (a is int) {} else {
+  if (a is int) {
+  } else {
     throw 42;
   }
   return a;
@@ -314,7 +315,8 @@
 
 /*element: testIsCheck27:[subclass=JSInt]*/
 testIsCheck27(/*[null|subclass=Object]*/ a) {
-  if (a is int) {} else {
+  if (a is int) {
+  } else {
     return 42;
   }
   return a;
@@ -322,7 +324,8 @@
 
 /*element: testIsCheck28:[null|subclass=Object]*/
 testIsCheck28(/*[null|subclass=Object]*/ a) {
-  if (a is int) {} else {}
+  if (a is int) {
+  } else {}
   return a;
 }
 
@@ -344,7 +347,8 @@
 /*element: testIf2:[null|exact=JSUInt31]*/
 testIf2(/*[null|subclass=Object]*/ a) {
   var c = null;
-  if (a) {} else {
+  if (a) {
+  } else {
     c = 10;
   }
   return c;
diff --git a/tests/compiler/dart2js/rti/data/instantiation1.dart b/tests/compiler/dart2js/rti/data/instantiation1.dart
new file mode 100644
index 0000000..1b8e7cd
--- /dev/null
+++ b/tests/compiler/dart2js/rti/data/instantiation1.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/*strong.element: f:deps=[B],direct,explicit=[f.T],needsArgs,needsInst=[<B.S>]*/
+/*omit.element: f:deps=[B]*/
+int f<T>(T a) => null;
+
+typedef int F<R>(R a);
+
+/*strong.class: B:explicit=[int Function(B.S)],indirect,needsArgs*/
+class B<S> {
+  F<S> c;
+
+  B() : c = f;
+}
+
+main() {
+  new B();
+}
diff --git a/tests/compiler/dart2js/rti/data/instantiation2.dart b/tests/compiler/dart2js/rti/data/instantiation2.dart
new file mode 100644
index 0000000..fd7022c
--- /dev/null
+++ b/tests/compiler/dart2js/rti/data/instantiation2.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/*kernel.element: f:direct,explicit=[f.T]*/
+/*!kernel.element: f:deps=[B],direct,explicit=[f.T],needsArgs,needsInst=[<B.S>]*/
+bool f<T>(T a) => a is T;
+
+typedef bool F<R>(R a);
+
+/*strong.class: B:explicit=[bool Function(B.S)],indirect,needsArgs*/
+/*omit.class: B:indirect,needsArgs*/
+class B<S> {
+  F<S> c;
+
+  B() : c = f;
+}
+
+main() {
+  new B();
+}
diff --git a/tests/compiler/dart2js/rti/data/instantiation3.dart b/tests/compiler/dart2js/rti/data/instantiation3.dart
new file mode 100644
index 0000000..7d3df34
--- /dev/null
+++ b/tests/compiler/dart2js/rti/data/instantiation3.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/*strong.element: f:deps=[B],direct,explicit=[f.T],needsArgs,needsInst=[<B.S>]*/
+/*omit.element: f:deps=[B]*/
+int f<T>(T a) => null;
+
+typedef int F<R>(R a);
+
+/*strong.class: B:direct,explicit=[int Function(B.S)],needsArgs*/
+class B<S> {
+  F<S> c;
+
+  method() {
+    return () {
+      c = f;
+    };
+  }
+}
+
+main() {
+  new B().method();
+}
diff --git a/tests/compiler/dart2js/rti/data/instantiation4.dart b/tests/compiler/dart2js/rti/data/instantiation4.dart
new file mode 100644
index 0000000..b9f1a78
--- /dev/null
+++ b/tests/compiler/dart2js/rti/data/instantiation4.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/*kernel.element: f:direct,explicit=[f.T]*/
+/*!kernel.element: f:deps=[B],direct,explicit=[f.T],needsArgs,needsInst=[<B.S>]*/
+bool f<T>(T a) => a is T;
+
+typedef bool F<R>(R a);
+
+/*strong.class: B:direct,explicit=[bool Function(B.S)],needsArgs*/
+/*omit.class: B:indirect,needsArgs*/
+class B<S> {
+  F<S> c;
+
+  method() {
+    return () {
+      c = f;
+    };
+  }
+}
+
+main() {
+  new B().method();
+}
diff --git a/tests/compiler/dart2js/rti/data/instantiation5.dart b/tests/compiler/dart2js/rti/data/instantiation5.dart
new file mode 100644
index 0000000..c69f333
--- /dev/null
+++ b/tests/compiler/dart2js/rti/data/instantiation5.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/*strong.element: f:deps=[method],direct,explicit=[f.T],needsArgs,needsInst=[<method.S>]*/
+/*omit.element: f:deps=[method]*/
+int f<T>(T a) => null;
+
+typedef int F<R>(R a);
+
+/*strong.element: method:indirect,needsArgs*/
+method<S>() {
+  F<S> c;
+
+  return () {
+    c = f;
+    return c;
+  };
+}
+
+main() {
+  method();
+}
diff --git a/tests/compiler/dart2js/rti/data/instantiation6.dart b/tests/compiler/dart2js/rti/data/instantiation6.dart
new file mode 100644
index 0000000..feb9e52
--- /dev/null
+++ b/tests/compiler/dart2js/rti/data/instantiation6.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/*kernel.element: f:direct,explicit=[f.T]*/
+/*!kernel.element: f:deps=[method],direct,explicit=[f.T],needsArgs,needsInst=[<method.S>]*/
+bool f<T>(T a) => a is T;
+
+typedef bool F<R>(R a);
+
+/*!kernel.element: method:indirect,needsArgs*/
+method<S>() {
+  F<S> c;
+
+  return () {
+    c = f;
+    return c;
+  };
+}
+
+main() {
+  method();
+}
diff --git a/tests/compiler/dart2js/rti/data/instantiation7.dart b/tests/compiler/dart2js/rti/data/instantiation7.dart
new file mode 100644
index 0000000..d8af322
--- /dev/null
+++ b/tests/compiler/dart2js/rti/data/instantiation7.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/*strong.element: f1:deps=[method],direct,explicit=[f1.T],needsArgs,needsInst=[<method.X>]*/
+/*omit.element: f1:deps=[method]*/
+int f1<T>(T a, T b, T c) => null;
+
+/*strong.element: f2:deps=[method],direct,explicit=[f2.S,f2.T],needsArgs,needsInst=[<method.X,method.Y>]*/
+/*omit.element: f2:deps=[method]*/
+int f2<T, S>(T a, S b, S c) => null;
+
+/*strong.element: f3:deps=[method],direct,explicit=[f3.S,f3.T,f3.U],needsArgs,needsInst=[<method.X,method.Y,method.Z>]*/
+/*omit.element: f3:deps=[method]*/
+int f3<T, S, U>(T a, S b, U c) => null;
+
+typedef int F1<R>(R a, R b, R c);
+typedef int F2<R, P>(R a, P b, P c);
+typedef int F3<R, P, Q>(R a, P b, Q c);
+
+/*strong.element: method:indirect,needsArgs*/
+method<X, Y, Z>() {
+  F1<X> c1;
+  F2<X, Y> c2;
+  F3<X, Y, Z> c3;
+
+  return () {
+    c1 = f1;
+    c2 = f2;
+    c3 = f3;
+    return c1 ?? c2 ?? c3;
+  };
+}
+
+main() {
+  method();
+}
diff --git a/tests/compiler/dart2js/rti/data/local_function_signatures_strong.dart b/tests/compiler/dart2js/rti/data/local_function_signatures_strong.dart
index ce07bec..8d4dd9b 100644
--- a/tests/compiler/dart2js/rti/data/local_function_signatures_strong.dart
+++ b/tests/compiler/dart2js/rti/data/local_function_signatures_strong.dart
@@ -77,7 +77,7 @@
 }
 
 method10() {
-  /*strong.direct,explicit=[local.T],needsArgs,needsSignature*/
+  /*strong.direct,explicit=[local.T],needsArgs,needsInst=[<dynamic>,<num>,<num>],needsSignature*/
   /*omit.needsSignature*/
   num local<T>(T n) => null;
   return local;
@@ -90,7 +90,7 @@
 }
 
 method12() {
-  /*strong.direct,explicit=[local.T],needsArgs*/
+  /*strong.direct,explicit=[local.T],needsArgs,needsInst=[<dynamic>,<num>,<num>]*/
   /*omit.*/
   num local<T>(num n, T t) => null;
   return local;
@@ -103,7 +103,7 @@
 }
 
 num Function(num) method14() {
-  /*strong.direct,explicit=[local.T],needsArgs,needsSignature*/
+  /*strong.direct,explicit=[local.T],needsArgs,needsInst=[<dynamic>,<num>,<num>],needsSignature*/
   /*omit.needsSignature*/
   num local<T>(T n) => null;
   return local;
diff --git a/tests/compiler/dart2js/rti/data/method_signatures_strong.dart b/tests/compiler/dart2js/rti/data/method_signatures_strong.dart
index 03650b8..afca40f5 100644
--- a/tests/compiler/dart2js/rti/data/method_signatures_strong.dart
+++ b/tests/compiler/dart2js/rti/data/method_signatures_strong.dart
@@ -16,7 +16,7 @@
 }
 
 class Class2 {
-  /*strong.element: Class2.method4:direct,explicit=[method4.T],needsArgs*/
+  /*strong.element: Class2.method4:direct,explicit=[method4.T],needsArgs,needsInst=[<num>,<num>]*/
   /*omit.element: Class2.method4:*/
   num method4<T>(T n) => null;
 }
@@ -27,19 +27,19 @@
 }
 
 class Class4 {
-  /*strong.element: Class4.method6:direct,explicit=[method6.T],needsArgs*/
+  /*strong.element: Class4.method6:direct,explicit=[method6.T],needsArgs,needsInst=[<num>,<num>]*/
   /*omit.element: Class4.method6:*/
   num method6<T>(num n, T t) => null;
 }
 
-/*strong.element: method7:direct,explicit=[method7.T],needsArgs*/
+/*strong.element: method7:direct,explicit=[method7.T],needsArgs,needsInst=[<num>,<num>]*/
 /*omit.element: method7:*/
 num method7<T>(T n) => null;
 
 /*element: method8:*/
 T method8<T>(num n) => null;
 
-/*strong.element: method9:direct,explicit=[method9.T],needsArgs*/
+/*strong.element: method9:direct,explicit=[method9.T],needsArgs,needsInst=[<num>,<num>]*/
 /*omit.element: method9:*/
 num method9<T>(num n, T t) => null;
 
diff --git a/tests/compiler/dart2js/rti/rti_need_test_helper.dart b/tests/compiler/dart2js/rti/rti_need_test_helper.dart
index 7386d6a..3e809b1 100644
--- a/tests/compiler/dart2js/rti/rti_need_test_helper.dart
+++ b/tests/compiler/dart2js/rti/rti_need_test_helper.dart
@@ -15,6 +15,7 @@
 import 'package:compiler/src/kernel/element_map.dart';
 import 'package:compiler/src/kernel/kernel_backend_strategy.dart';
 import 'package:compiler/src/kernel/kernel_strategy.dart';
+import 'package:compiler/src/universe/feature.dart';
 import 'package:compiler/src/universe/selector.dart';
 import 'package:compiler/src/universe/world_builder.dart';
 import 'package:kernel/ast.dart' as ir;
@@ -56,6 +57,7 @@
   static const String indirectTypeArgumentTest = 'indirect';
   static const String typeLiteral = 'exp';
   static const String selectors = 'selectors';
+  static const String instantiationsNeedTypeArguments = 'needsInst';
 }
 
 abstract class ComputeValueMixin<T> {
@@ -162,6 +164,13 @@
             features.addElement(Tags.selectors, selector);
           }
         });
+        rtiNeedBuilder.instantiationsNeedingTypeArgumentsForTesting?.forEach(
+            (GenericInstantiation instantiation, Set<Entity> targets) {
+          if (targets.contains(entity)) {
+            features.addElement(
+                Tags.instantiationsNeedTypeArguments, instantiation.shortText);
+          }
+        });
       }
 
       if (frontendClosure != null) {
diff --git a/tests/compiler/dart2js_extra/generic_instantiation1_test.dart b/tests/compiler/dart2js_extra/generic_instantiation1_test.dart
new file mode 100644
index 0000000..fcfb79f
--- /dev/null
+++ b/tests/compiler/dart2js_extra/generic_instantiation1_test.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// dart2jsOptions=--strong
+
+int f<T>(T a) => null;
+
+typedef int F<R>(R a);
+
+class B<S> {
+  F<S> c;
+
+  B() : c = f;
+}
+
+main() {
+  new B<int>().c(0);
+}
diff --git a/tests/compiler/dart2js_extra/generic_instantiation2_test.dart b/tests/compiler/dart2js_extra/generic_instantiation2_test.dart
new file mode 100644
index 0000000..047d221
--- /dev/null
+++ b/tests/compiler/dart2js_extra/generic_instantiation2_test.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// dart2jsOptions=--strong --omit-implicit-checks
+
+int f<T>(T a) => null;
+
+typedef int F<R>(R a);
+
+class B<S> {
+  F<S> c;
+
+  B() : c = f;
+}
+
+main() {
+  new B<int>().c(0);
+}
diff --git a/tests/compiler/dart2js_extra/generic_instantiation3_test.dart b/tests/compiler/dart2js_extra/generic_instantiation3_test.dart
new file mode 100644
index 0000000..3152565
--- /dev/null
+++ b/tests/compiler/dart2js_extra/generic_instantiation3_test.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// dart2jsOptions=--strong
+
+import 'package:expect/expect.dart';
+
+bool f<T, S>(T a, S b) => a is S;
+
+typedef bool F<P, Q>(P a, Q b);
+
+class B<X, Y> {
+  F<X, Y> c;
+
+  B() : c = f;
+}
+
+main() {
+  Expect.isTrue(new B<int, int>().c(0, 0));
+  Expect.isFalse(new B<int, String>().c(0, ''));
+}
diff --git a/tests/compiler/dart2js_extra/generic_instantiation4_test.dart b/tests/compiler/dart2js_extra/generic_instantiation4_test.dart
new file mode 100644
index 0000000..d5f8eb9
--- /dev/null
+++ b/tests/compiler/dart2js_extra/generic_instantiation4_test.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// dart2jsOptions=--strong --omit-implicit-checks
+
+import 'package:expect/expect.dart';
+
+bool f<T, S>(T a, S b) => a is S;
+
+typedef bool F<P, Q>(P a, Q b);
+
+class B<X, Y> {
+  F<X, Y> c;
+
+  B() : c = f;
+}
+
+main() {
+  Expect.isTrue(new B<int, int>().c(0, 0));
+  Expect.isFalse(new B<int, String>().c(0, ''));
+}