Emit classes used in type variable replacements

Change-Id: Iad3bf28790172e364d83a96003fb7ef96f25747e
Reviewed-on: https://dart-review.googlesource.com/64322
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/deferred_load.dart b/pkg/compiler/lib/src/deferred_load.dart
index b8fc4c1..8b87744 100644
--- a/pkg/compiler/lib/src/deferred_load.dart
+++ b/pkg/compiler/lib/src/deferred_load.dart
@@ -363,6 +363,7 @@
               }
               break;
             case TypeUseKind.RTI_VALUE:
+            case TypeUseKind.TYPE_ARGUMENT:
               failedAt(element, "Unexpected type use: $typeUse.");
               break;
           }
diff --git a/pkg/compiler/lib/src/enqueue.dart b/pkg/compiler/lib/src/enqueue.dart
index a4c49e1..3ab906a 100644
--- a/pkg/compiler/lib/src/enqueue.dart
+++ b/pkg/compiler/lib/src/enqueue.dart
@@ -381,6 +381,7 @@
         }
         break;
       case TypeUseKind.RTI_VALUE:
+      case TypeUseKind.TYPE_ARGUMENT:
         failedAt(CURRENT_ELEMENT_SPANNABLE, "Unexpected type use: $typeUse.");
         break;
     }
diff --git a/pkg/compiler/lib/src/js_backend/enqueuer.dart b/pkg/compiler/lib/src/js_backend/enqueuer.dart
index 7d9d0b3..3b78c2f 100644
--- a/pkg/compiler/lib/src/js_backend/enqueuer.dart
+++ b/pkg/compiler/lib/src/js_backend/enqueuer.dart
@@ -211,6 +211,9 @@
         }
         break;
       case TypeUseKind.RTI_VALUE:
+        _worldBuilder.registerConstTypeLiteral(type);
+        break;
+      case TypeUseKind.TYPE_ARGUMENT:
         _worldBuilder.registerTypeArgument(type);
         break;
     }
diff --git a/pkg/compiler/lib/src/js_backend/impact_transformer.dart b/pkg/compiler/lib/src/js_backend/impact_transformer.dart
index a4fb279..46e3e2d4 100644
--- a/pkg/compiler/lib/src/js_backend/impact_transformer.dart
+++ b/pkg/compiler/lib/src/js_backend/impact_transformer.dart
@@ -203,6 +203,7 @@
           hasTypeLiteral = true;
           break;
         case TypeUseKind.RTI_VALUE:
+        case TypeUseKind.TYPE_ARGUMENT:
           failedAt(CURRENT_ELEMENT_SPANNABLE, "Unexpected type use: $typeUse.");
           break;
       }
diff --git a/pkg/compiler/lib/src/js_backend/runtime_types.dart b/pkg/compiler/lib/src/js_backend/runtime_types.dart
index 00dd8de..7154217 100644
--- a/pkg/compiler/lib/src/js_backend/runtime_types.dart
+++ b/pkg/compiler/lib/src/js_backend/runtime_types.dart
@@ -1998,6 +1998,9 @@
 
     codegenWorldBuilder.forEachStaticTypeArgument(processMethodTypeArguments);
     codegenWorldBuilder.forEachDynamicTypeArgument(processMethodTypeArguments);
+    codegenWorldBuilder.liveTypeArguments.forEach((DartType type) {
+      liveTypeVisitor.visitType(type, TypeVisitorState.typeArgument);
+    });
     codegenWorldBuilder.constTypeLiterals.forEach((DartType type) {
       liveTypeVisitor.visitType(type, TypeVisitorState.typeLiteral);
     });
@@ -2944,12 +2947,10 @@
 
   /// Whether the class is used in a constant type literal.
   ///
-  /// For instance `A`, `B` and `C` in:
+  /// For instance `A`:
   ///
   ///     class A {}
-  ///     class B<T> {}
-  ///     class C {}
-  ///     main() => A == B<C>;
+  ///     main() => A;
   ///
   bool typeLiteral = false;
 
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index 947432d..2b1adbc 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -3098,6 +3098,10 @@
 
   void visitTypeInfoExpression(HTypeInfoExpression node) {
     DartType type = node.dartType;
+    if (node.isTypeVariableReplacement) {
+      _registry.registerTypeUse(new TypeUse.typeArgument(type));
+    }
+
     List<js.Expression> arguments = <js.Expression>[];
     for (HInstruction input in node.inputs) {
       use(input);
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 4451c74..9488821 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -3699,8 +3699,13 @@
 class HTypeInfoExpression extends HInstruction {
   final TypeInfoExpressionKind kind;
   final DartType dartType;
+
+  /// `true` if this
+  final bool isTypeVariableReplacement;
+
   HTypeInfoExpression(this.kind, this.dartType, List<HInstruction> inputs,
-      AbstractValue instructionType)
+      AbstractValue instructionType,
+      {this.isTypeVariableReplacement: false})
       : super(inputs, instructionType) {
     setUseGvn();
   }
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index 17d701f..145497d 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -1404,7 +1404,8 @@
             TypeInfoExpressionKind.COMPLETE,
             typeArgument,
             const <HInstruction>[],
-            _abstractValueDomain.dynamicType);
+            _abstractValueDomain.dynamicType,
+            isTypeVariableReplacement: true);
         return replacement;
       }
       return node;
@@ -1436,7 +1437,8 @@
           TypeInfoExpressionKind.COMPLETE,
           type,
           arguments,
-          _abstractValueDomain.dynamicType);
+          _abstractValueDomain.dynamicType,
+          isTypeVariableReplacement: true);
       return replacement;
     }
 
diff --git a/pkg/compiler/lib/src/universe/codegen_world_builder.dart b/pkg/compiler/lib/src/universe/codegen_world_builder.dart
index cc99a9d..0e371ba 100644
--- a/pkg/compiler/lib/src/universe/codegen_world_builder.dart
+++ b/pkg/compiler/lib/src/universe/codegen_world_builder.dart
@@ -80,6 +80,9 @@
 
   /// Returns the types that are live as constant type literals.
   Iterable<DartType> get constTypeLiterals;
+
+  /// Returns the types that are live as constant type literals.
+  Iterable<DartType> get liveTypeArguments;
 }
 
 class CodegenWorldBuilderImpl extends WorldBuilderBase
@@ -165,6 +168,7 @@
   final KernelToWorldBuilder _elementMap;
   final GlobalLocalsMap _globalLocalsMap;
 
+  final Set<DartType> _constTypeLiterals = new Set<DartType>();
   final Set<DartType> _liveTypeArguments = new Set<DartType>();
 
   CodegenWorldBuilderImpl(
@@ -692,9 +696,15 @@
     });
   }
 
+  void registerConstTypeLiteral(DartType type) {
+    _constTypeLiterals.add(type);
+  }
+
+  Iterable<DartType> get constTypeLiterals => _constTypeLiterals;
+
   void registerTypeArgument(DartType type) {
     _liveTypeArguments.add(type);
   }
 
-  Iterable<DartType> get constTypeLiterals => _liveTypeArguments;
+  Iterable<DartType> get liveTypeArguments => _liveTypeArguments;
 }
diff --git a/pkg/compiler/lib/src/universe/use.dart b/pkg/compiler/lib/src/universe/use.dart
index 9972e88..f6d00aa 100644
--- a/pkg/compiler/lib/src/universe/use.dart
+++ b/pkg/compiler/lib/src/universe/use.dart
@@ -577,6 +577,7 @@
   IMPLICIT_CAST,
   PARAMETER_CHECK,
   RTI_VALUE,
+  TYPE_ARGUMENT,
 }
 
 /// Use of a [DartType].
@@ -625,6 +626,9 @@
         sb.write('param:');
         break;
       case TypeUseKind.RTI_VALUE:
+        sb.write('rti:');
+        break;
+      case TypeUseKind.TYPE_ARGUMENT:
         sb.write('typeArg:');
         break;
     }
@@ -695,6 +699,14 @@
     return new TypeUse.internal(type, TypeUseKind.RTI_VALUE);
   }
 
+  /// [type] used directly as a type argument.
+  ///
+  /// The happens during optimization where a type variable can be replaced by
+  /// an invariable type argument derived from a constant receiver.
+  factory TypeUse.typeArgument(DartType type) {
+    return new TypeUse.internal(type, TypeUseKind.TYPE_ARGUMENT);
+  }
+
   bool operator ==(other) {
     if (identical(this, other)) return true;
     if (other is! TypeUse) return false;
diff --git a/tests/compiler/dart2js/rti/emission/event_callback.dart b/tests/compiler/dart2js/rti/emission/event_callback.dart
index a0c84dd..37da4d1 100644
--- a/tests/compiler/dart2js/rti/emission/event_callback.dart
+++ b/tests/compiler/dart2js/rti/emission/event_callback.dart
@@ -7,8 +7,13 @@
 /*kernel.class: global#Event:checkedTypeArgument,checks=[$isEvent],instance,typeArgument*/
 /*strong.class: global#Event:checkedInstance,checkedTypeArgument,checks=[$isEvent],instance,typeArgument*/
 
-/*class: global#MouseEvent:checks=[],instance*/
-/*class: global#KeyboardEvent:checks=[],instance*/
+/*kernel.class: global#MouseEvent:checks=[$isMouseEvent],instance,typeArgument*/
+/*strong.class: global#MouseEvent:checks=[$isMouseEvent],instance,typeArgument*/
+/*omit.class: global#MouseEvent:checks=[],instance*/
+
+/*kernel.class: global#KeyboardEvent:checks=[$isKeyboardEvent],instance,typeArgument*/
+/*strong.class: global#KeyboardEvent:checks=[$isKeyboardEvent],instance,typeArgument*/
+/*omit.class: global#KeyboardEvent:checks=[],instance*/
 
 void main() {
   print('InputElement');
diff --git a/tests/compiler/dart2js/rti/emission/replaced_type_variable.dart b/tests/compiler/dart2js/rti/emission/replaced_type_variable.dart
new file mode 100644
index 0000000..5b22b5f
--- /dev/null
+++ b/tests/compiler/dart2js/rti/emission/replaced_type_variable.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.
+
+import 'package:expect/expect.dart';
+
+// This class is inlined away.
+/*class: Class:*/
+class Class<T> {
+  const Class();
+
+  Type get type => T;
+}
+
+/*class: A:checks=[],typeArgument*/
+class A {}
+
+@NoInline()
+test(o) => Expect.notEquals('dynamic', '$o');
+
+main() {
+  test(const Class<A>().type);
+}
diff --git a/tests/compiler/dart2js_extra/replaced_type_variable_test.dart b/tests/compiler/dart2js_extra/replaced_type_variable_test.dart
new file mode 100644
index 0000000..f530000
--- /dev/null
+++ b/tests/compiler/dart2js_extra/replaced_type_variable_test.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.
+
+import 'package:expect/expect.dart';
+
+// This class is inlined away.
+class Class<T> {
+  const Class();
+
+  Type get type => T;
+}
+
+class A {}
+
+@NoInline()
+test(o) => Expect.notEquals('dynamic', '$o');
+
+main() {
+  test(const Class<A>().type);
+}