Fix bug in RTI optimization

Change-Id: Iec3026f0a79ac54dd125e15f00e4beb21e9a371c
Reviewed-on: https://dart-review.googlesource.com/58020
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/js_backend/runtime_types.dart b/pkg/compiler/lib/src/js_backend/runtime_types.dart
index 743ef5c..c94a5f2 100644
--- a/pkg/compiler/lib/src/js_backend/runtime_types.dart
+++ b/pkg/compiler/lib/src/js_backend/runtime_types.dart
@@ -789,7 +789,7 @@
   ///     }
   ///     main() => new B<int>().m(0);
   ///
-  Iterable<ClassEntity> get classTests =>
+  Iterable<ClassEntity> get classTestsForTesting =>
       _classes.values.where((n) => n.hasTest).map((n) => n.cls).toSet();
 
   /// Classes that explicitly use their type variables in is-tests.
@@ -801,7 +801,7 @@
   ///     }
   ///     main() => new A<int>().m(0);
   ///
-  Iterable<ClassEntity> get directClassTests =>
+  Iterable<ClassEntity> get directClassTestsForTesting =>
       _classes.values.where((n) => n.hasDirectTest).map((n) => n.cls).toSet();
 
   /// Methods that explicitly or implicitly use their type variables in
@@ -813,7 +813,7 @@
   ///     m2<S>(o) => m1<S>(o);
   ///     main() => m2<int>(0);
   ///
-  Iterable<Entity> get methodTests =>
+  Iterable<Entity> get methodTestsForTesting =>
       _methods.values.where((n) => n.hasTest).map((n) => n.function).toSet();
 
   /// Methods that explicitly use their type variables in is-tests.
@@ -823,7 +823,7 @@
   ///     m<T>(o) => o is T;
   ///     main() => m<int>(0);
   ///
-  Iterable<Entity> get directMethodTests => _methods.values
+  Iterable<Entity> get directMethodTestsForTesting => _methods.values
       .where((n) => n.hasDirectTest)
       .map((n) => n.function)
       .toSet();
@@ -1022,20 +1022,25 @@
 
   void _propagateTests(CommonElements commonElements,
       ElementEnvironment elementEnvironment, WorldBuilder worldBuilder) {
+    void processTypeVariableType(TypeVariableType type, {bool direct: true}) {
+      TypeVariableEntity variable = type.element;
+      if (variable.typeDeclaration is ClassEntity) {
+        _getClassNode(variable.typeDeclaration).markTest(direct: direct);
+      } else {
+        _getMethodNode(
+                elementEnvironment, worldBuilder, variable.typeDeclaration)
+            .markTest(direct: direct);
+      }
+    }
+
     void processType(DartType type, {bool direct: true}) {
-      if (type.isTypeVariable) {
-        TypeVariableType typeVariableType = type;
-        TypeVariableEntity variable = typeVariableType.element;
-        if (variable.typeDeclaration is ClassEntity) {
-          _getClassNode(variable.typeDeclaration).markTest(direct: direct);
-        } else {
-          _getMethodNode(
-                  elementEnvironment, worldBuilder, variable.typeDeclaration)
-              .markTest(direct: direct);
-        }
-      } else if (type is FutureOrType) {
+      if (type is FutureOrType) {
         _getClassNode(commonElements.futureClass).markIndirectTest();
         processType(type.typeArgument, direct: false);
+      } else {
+        type.forEachTypeVariable((TypeVariableType type) {
+          processTypeVariableType(type, direct: direct);
+        });
       }
     }
 
diff --git a/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart b/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart
index 58f7e1d..3545b57 100644
--- a/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart
+++ b/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart
@@ -692,7 +692,7 @@
               'UNEXPECTED $mode DATA for ${id.descriptor}: '
               'Object: ${actualData.objectText}\n '
               'expected: ${colorizeExpected('$expected')}\n '
-              'actual: ${colorizeActual('$actual')}');
+              'actual  : ${colorizeActual('$actual')}');
           if (filterActualData == null ||
               filterActualData(expected, actualData)) {
             hasLocalFailure = true;
diff --git a/tests/compiler/dart2js/rti/data/function_subtype_local5.dart b/tests/compiler/dart2js/rti/data/function_subtype_local5.dart
index 6fb64c6..7119670 100644
--- a/tests/compiler/dart2js/rti/data/function_subtype_local5.dart
+++ b/tests/compiler/dart2js/rti/data/function_subtype_local5.dart
@@ -14,7 +14,7 @@
 typedef int Boz<T>(T a);
 typedef int Biz<T>(T a, int b);
 
-/*class: C:explicit=[int Function(C.T),int Function(C.T,[String]),int Function(C.T,int),int Function(C.T,{,b:String})],needsArgs*/
+/*class: C:direct,explicit=[int Function(C.T),int Function(C.T,[String]),int Function(C.T,int),int Function(C.T,{,b:String})],needsArgs*/
 class C<T> {
   void test(String nameOfT, bool expectedResult) {
     // TODO(johnniwinther): Optimize local function type signature need.
diff --git a/tests/compiler/dart2js/rti/data/generic_methods_dynamic_05_strong.dart b/tests/compiler/dart2js/rti/data/generic_methods_dynamic_05_strong.dart
index 6208ec6..f438d3f 100644
--- a/tests/compiler/dart2js/rti/data/generic_methods_dynamic_05_strong.dart
+++ b/tests/compiler/dart2js/rti/data/generic_methods_dynamic_05_strong.dart
@@ -20,7 +20,7 @@
 
 class C {
   /*!strong.element: C.bar:needsArgs,selectors=[Selector(call, bar, arity=1, types=1)]*/
-  /*strong.element: C.bar:explicit=[Iterable<bar.T>],implicit=[bar.T],indirect,needsArgs,selectors=[Selector(call, bar, arity=1, types=1)]*/
+  /*strong.element: C.bar:direct,explicit=[Iterable<bar.T>],implicit=[bar.T],needsArgs,selectors=[Selector(call, bar, arity=1, types=1)]*/
   List<T> bar<T>(Iterable<T> t) => <T>[t.first];
 }
 
diff --git a/tests/compiler/dart2js/rti/data/indirect_through_static.dart b/tests/compiler/dart2js/rti/data/indirect_through_static.dart
new file mode 100644
index 0000000..6359003
--- /dev/null
+++ b/tests/compiler/dart2js/rti/data/indirect_through_static.dart
@@ -0,0 +1,50 @@
+// 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.class: A:*/
+/*!kernel.class: A:implicit=[A]*/
+abstract class A {}
+
+class B implements A {}
+
+/*kernel.class: C:
+ deps=[lookup],
+ explicit=[C<lookup.T>]
+*/
+/*strong.class: C:
+  deps=[lookup],
+  explicit=[C<lookup.T>,Map<String,C>],
+  implicit=[C],
+  needsArgs
+*/
+/*omit.class: C:
+ deps=[lookup],explicit=[C<lookup.T>],
+ needsArgs
+*/
+class C<T> {}
+
+final Map<String, C> map = {};
+
+void setup() {
+  map['x'] = new C<B>();
+}
+
+/*kernel.element: lookup:direct,explicit=[C<lookup.T>]*/
+/*!kernel.element: lookup:direct,explicit=[C<lookup.T>],needsArgs*/
+C<T> lookup<T>(String key) {
+  final value = map[key];
+  if (value != null && value is C<T>) {
+    return value;
+  }
+  throw 'Invalid C value for $key: ${value}';
+}
+
+void lookupA() {
+  lookup<A>('x');
+}
+
+main() {
+  setup();
+  lookupA();
+}
diff --git a/tests/compiler/dart2js/rti/data/local_function_map_literal_strong.dart b/tests/compiler/dart2js/rti/data/local_function_map_literal_strong.dart
index 30702fe..ebdd352 100644
--- a/tests/compiler/dart2js/rti/data/local_function_map_literal_strong.dart
+++ b/tests/compiler/dart2js/rti/data/local_function_map_literal_strong.dart
@@ -4,7 +4,7 @@
 
 import 'package:expect/expect.dart';
 
-/*strong.class: global#LinkedHashMap:deps=[Map],explicit=[LinkedHashMap<LinkedHashMap.K,LinkedHashMap.V>],implicit=[LinkedHashMap.K,LinkedHashMap.V],indirect,needsArgs*/
+/*strong.class: global#LinkedHashMap:deps=[Map],direct,explicit=[LinkedHashMap<LinkedHashMap.K,LinkedHashMap.V>],implicit=[LinkedHashMap.K,LinkedHashMap.V],needsArgs*/
 /*omit.class: global#LinkedHashMap:deps=[Map],needsArgs*/
 
 /*strong.element: method:implicit=[method.T],indirect,needsArgs*/
diff --git a/tests/compiler/dart2js/rti/data/map_literal.dart b/tests/compiler/dart2js/rti/data/map_literal.dart
index 2f04d3b..8376333 100644
--- a/tests/compiler/dart2js/rti/data/map_literal.dart
+++ b/tests/compiler/dart2js/rti/data/map_literal.dart
@@ -6,7 +6,7 @@
 /*strong.class: global#Map:explicit=[Map],indirect,needsArgs*/
 
 /*!strong.class: global#LinkedHashMap:deps=[Map]*/
-/*strong.class: global#LinkedHashMap:deps=[Map],explicit=[LinkedHashMap<LinkedHashMap.K,LinkedHashMap.V>],implicit=[LinkedHashMap.K,LinkedHashMap.V],indirect,needsArgs*/
+/*strong.class: global#LinkedHashMap:deps=[Map],direct,explicit=[LinkedHashMap<LinkedHashMap.K,LinkedHashMap.V>],implicit=[LinkedHashMap.K,LinkedHashMap.V],needsArgs*/
 
 /*!strong.class: global#JsLinkedHashMap:deps=[LinkedHashMap]*/
 /*strong.class: global#JsLinkedHashMap:deps=[LinkedHashMap],direct,explicit=[JsLinkedHashMap.K,JsLinkedHashMap.V,void Function(JsLinkedHashMap.K,JsLinkedHashMap.V)],implicit=[JsLinkedHashMap.K,JsLinkedHashMap.V],needsArgs*/
diff --git a/tests/compiler/dart2js/rti/data/map_literal_checked.dart b/tests/compiler/dart2js/rti/data/map_literal_checked.dart
index 9c6733f..d48447a 100644
--- a/tests/compiler/dart2js/rti/data/map_literal_checked.dart
+++ b/tests/compiler/dart2js/rti/data/map_literal_checked.dart
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 /*class: global#Map:explicit=[Map],indirect,needsArgs*/
-/*class: global#LinkedHashMap:deps=[Map],explicit=[LinkedHashMap<LinkedHashMap.K,LinkedHashMap.V>],implicit=[LinkedHashMap.K,LinkedHashMap.V],indirect,needsArgs*/
+/*class: global#LinkedHashMap:deps=[Map],direct,explicit=[LinkedHashMap<LinkedHashMap.K,LinkedHashMap.V>],implicit=[LinkedHashMap.K,LinkedHashMap.V],needsArgs*/
 /*class: global#JsLinkedHashMap:deps=[LinkedHashMap],direct,explicit=[Iterable<JsLinkedHashMap.K>,JsLinkedHashMap.K,JsLinkedHashMap.V,JsLinkedHashMap<JsLinkedHashMap.K,JsLinkedHashMap.V>,void Function(JsLinkedHashMap.K,JsLinkedHashMap.V)],implicit=[JsLinkedHashMap.K,JsLinkedHashMap.V],needsArgs*/
 /*class: global#double:explicit=[double],implicit=[double]*/
 /*class: global#JSDouble:*/
diff --git a/tests/compiler/dart2js/rti/data/map_literal_strong.dart b/tests/compiler/dart2js/rti/data/map_literal_strong.dart
index 4c0bf5f..92bb292 100644
--- a/tests/compiler/dart2js/rti/data/map_literal_strong.dart
+++ b/tests/compiler/dart2js/rti/data/map_literal_strong.dart
@@ -5,7 +5,7 @@
 /*strong.class: global#Map:explicit=[Map],indirect,needsArgs*/
 /*omit.class: global#Map:*/
 
-/*strong.class: global#LinkedHashMap:deps=[Map],explicit=[LinkedHashMap<LinkedHashMap.K,LinkedHashMap.V>],implicit=[LinkedHashMap.K,LinkedHashMap.V],indirect,needsArgs*/
+/*strong.class: global#LinkedHashMap:deps=[Map],direct,explicit=[LinkedHashMap<LinkedHashMap.K,LinkedHashMap.V>],implicit=[LinkedHashMap.K,LinkedHashMap.V],needsArgs*/
 /*omit.class: global#LinkedHashMap:deps=[Map]*/
 
 /*strong.class: global#JsLinkedHashMap:deps=[LinkedHashMap],direct,explicit=[JsLinkedHashMap.K,JsLinkedHashMap.V,void Function(JsLinkedHashMap.K,JsLinkedHashMap.V)],implicit=[JsLinkedHashMap.K,JsLinkedHashMap.V],needsArgs*/
diff --git a/tests/compiler/dart2js/rti/data/map_to_set.dart b/tests/compiler/dart2js/rti/data/map_to_set.dart
index 06d337a..073f358 100644
--- a/tests/compiler/dart2js/rti/data/map_to_set.dart
+++ b/tests/compiler/dart2js/rti/data/map_to_set.dart
@@ -6,7 +6,7 @@
 /*strong.class: global#Map:deps=[Class,JsLinkedHashMap,MapMixin],explicit=[Map,Map<JsLinkedHashMap.K,JsLinkedHashMap.V>,Map<MapMixin.K,MapMixin.V>],indirect,needsArgs*/
 
 /*!strong.class: global#LinkedHashMap:deps=[Map],needsArgs*/
-/*strong.class: global#LinkedHashMap:deps=[Map],explicit=[LinkedHashMap<LinkedHashMap.K,LinkedHashMap.V>],implicit=[LinkedHashMap.K,LinkedHashMap.V],indirect,needsArgs*/
+/*strong.class: global#LinkedHashMap:deps=[Map],direct,explicit=[LinkedHashMap<LinkedHashMap.K,LinkedHashMap.V>],implicit=[LinkedHashMap.K,LinkedHashMap.V],needsArgs*/
 
 /*!strong.class: global#JsLinkedHashMap:deps=[LinkedHashMap],implicit=[JsLinkedHashMap.K],needsArgs*/
 /*strong.class: global#JsLinkedHashMap:deps=[LinkedHashMap],explicit=[JsLinkedHashMap.K,JsLinkedHashMap.V,Map<JsLinkedHashMap.K,JsLinkedHashMap.V>,void Function(JsLinkedHashMap.K,JsLinkedHashMap.V)],implicit=[JsLinkedHashMap.K,JsLinkedHashMap.V],indirect,needsArgs*/
diff --git a/tests/compiler/dart2js/rti/data/map_to_set_strong.dart b/tests/compiler/dart2js/rti/data/map_to_set_strong.dart
index 641baca..89dfbbe 100644
--- a/tests/compiler/dart2js/rti/data/map_to_set_strong.dart
+++ b/tests/compiler/dart2js/rti/data/map_to_set_strong.dart
@@ -5,7 +5,7 @@
 /*strong.class: global#Map:deps=[Class,JsLinkedHashMap,MapMixin],explicit=[Map,Map<JsLinkedHashMap.K,JsLinkedHashMap.V>,Map<MapMixin.K,MapMixin.V>],indirect,needsArgs*/
 /*omit.class: global#Map:deps=[Class],needsArgs*/
 
-/*strong.class: global#LinkedHashMap:deps=[Map],explicit=[LinkedHashMap<LinkedHashMap.K,LinkedHashMap.V>],implicit=[LinkedHashMap.K,LinkedHashMap.V],indirect,needsArgs*/
+/*strong.class: global#LinkedHashMap:deps=[Map],direct,explicit=[LinkedHashMap<LinkedHashMap.K,LinkedHashMap.V>],implicit=[LinkedHashMap.K,LinkedHashMap.V],needsArgs*/
 /*omit.class: global#LinkedHashMap:deps=[Map],needsArgs*/
 
 /*strong.class: global#JsLinkedHashMap:deps=[LinkedHashMap],explicit=[JsLinkedHashMap.K,JsLinkedHashMap.V,Map<JsLinkedHashMap.K,JsLinkedHashMap.V>,void Function(JsLinkedHashMap.K,JsLinkedHashMap.V)],implicit=[JsLinkedHashMap.K,JsLinkedHashMap.V],indirect,needsArgs*/
diff --git a/tests/compiler/dart2js/rti/data/type_variable_function_type.dart b/tests/compiler/dart2js/rti/data/type_variable_function_type.dart
index 53e53f3..b4a7f70 100644
--- a/tests/compiler/dart2js/rti/data/type_variable_function_type.dart
+++ b/tests/compiler/dart2js/rti/data/type_variable_function_type.dart
@@ -8,7 +8,7 @@
 
 typedef T Func<T>();
 
-/*class: Foo:explicit=[Foo.S Function()],needsArgs*/
+/*class: Foo:direct,explicit=[Foo.S Function()],needsArgs*/
 class Foo<S> {
   m(x) => x is Func<S>;
 }
diff --git a/tests/compiler/dart2js/rti/emission/indirect_through_static.dart b/tests/compiler/dart2js/rti/emission/indirect_through_static.dart
new file mode 100644
index 0000000..a80da25
--- /dev/null
+++ b/tests/compiler/dart2js/rti/emission/indirect_through_static.dart
@@ -0,0 +1,38 @@
+// 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.class: A:*/
+/*!kernel.class: A:checkedInstance,checks=[],typeArgument*/
+abstract class A {}
+
+/*kernel.class: B:checks=[],typeArgument*/
+/*!kernel.class: B:checks=[$isA],typeArgument*/
+class B implements A {}
+
+/*kernel.class: C:checks=[],instance*/
+/*!kernel.class: C:checkedInstance,checks=[],instance,typeArgument*/
+class C<T> {}
+
+final Map<String, C> map = {};
+
+void setup() {
+  map['x'] = new C<B>();
+}
+
+C<T> lookup<T>(String key) {
+  final value = map[key];
+  if (value != null && value is C<T>) {
+    return value;
+  }
+  throw 'Invalid C value for $key: ${value}';
+}
+
+void lookupA() {
+  lookup<A>('x');
+}
+
+main() {
+  setup();
+  lookupA();
+}
diff --git a/tests/compiler/dart2js/rti/rti_need_test_helper.dart b/tests/compiler/dart2js/rti/rti_need_test_helper.dart
index d7a44f5..7386d6a 100644
--- a/tests/compiler/dart2js/rti/rti_need_test_helper.dart
+++ b/tests/compiler/dart2js/rti/rti_need_test_helper.dart
@@ -113,10 +113,10 @@
         .contains(frontendClass)) {
       features.add(Tags.typeLiteral);
     }
-    if (rtiNeedBuilder.typeVariableTestsForTesting.directClassTests
+    if (rtiNeedBuilder.typeVariableTestsForTesting.directClassTestsForTesting
         .contains(frontendClass)) {
       features.add(Tags.directTypeArgumentTest);
-    } else if (rtiNeedBuilder.typeVariableTestsForTesting.classTests
+    } else if (rtiNeedBuilder.typeVariableTestsForTesting.classTestsForTesting
         .contains(frontendClass)) {
       features.add(Tags.indirectTypeArgumentTest);
     }
@@ -143,10 +143,12 @@
 
       void addFrontendData(Entity entity) {
         findDependencies(features, entity);
-        if (rtiNeedBuilder.typeVariableTestsForTesting.directMethodTests
+        if (rtiNeedBuilder
+            .typeVariableTestsForTesting.directMethodTestsForTesting
             .contains(entity)) {
           features.add(Tags.directTypeArgumentTest);
-        } else if (rtiNeedBuilder.typeVariableTestsForTesting.methodTests
+        } else if (rtiNeedBuilder
+            .typeVariableTestsForTesting.methodTestsForTesting
             .contains(entity)) {
           features.add(Tags.indirectTypeArgumentTest);
         }