[vm] Make Class::RareType() the instantiated to bounds type.

Previously, Class::RareType() returned a Type where the type arguments
were null (e.g., all dynamic). However, this is an invalid type for
classes that have at least one type parameter with a bound that is not a
top type, and could mean that comparisons (such as subtyping) against
the "rare" type could return incorrect answers.

Instead, use TypeParameters::defaults() to get the canonicalized
instantiated to bounds type argument vector and use that instead.

TEST=ci

Cq-Include-Trybots: luci.dart.try:vm-reload-linux-debug-x64-try,vm-reload-rollback-linux-debug-x64-try,vm-linux-debug-x64-try,vm-linux-release-x64-try
Change-Id: Iea591e93102f53713265b481476f9670cfb81c93
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/312261
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Tess Strickland <sstrickl@google.com>
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 5fcbd6a..365503e 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -324,11 +324,9 @@
   // arguments.
   //
   // This is e.g. true for "String" but also for "List<dynamic>".  (A type for
-  // which the type arguments vector is filled with "dynamic" is known as a rare
-  // type)
+  // which the type arguments vector is instantiated to bounds is known as a
+  // rare type.)
   if (type_class.IsGeneric()) {
-    // TODO(kustermann): We might want to consider extending this when the type
-    // arguments are not "dynamic" but instantiated-to-bounds.
     const Type& rare_type = Type::Handle(zone, type_class.RareType());
     if (!rare_type.IsSubtypeOf(type, Heap::kNew)) {
       ASSERT(Type::Cast(type).arguments() != TypeArguments::null());
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index a29f44a..c970791 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -3068,12 +3068,17 @@
 }
 
 TypePtr Class::RareType() const {
-  if (!IsGeneric() && !IsClosureClass()) {
+  if (!IsGeneric()) {
     return DeclarationType();
   }
   ASSERT(is_declaration_loaded());
-  Type& type = Type::Handle(Type::New(*this, Object::null_type_arguments(),
-                                      Nullability::kNonNullable));
+  Thread* const thread = Thread::Current();
+  Zone* const zone = thread->zone();
+  const auto& inst_to_bounds =
+      TypeArguments::Handle(zone, InstantiateToBounds(thread));
+  ASSERT(inst_to_bounds.ptr() != Object::empty_type_arguments().ptr());
+  auto& type = Type::Handle(
+      zone, Type::New(*this, inst_to_bounds, Nullability::kNonNullable));
   type ^= ClassFinalizer::FinalizeType(type);
   return type.ptr();
 }
@@ -22768,12 +22773,14 @@
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
   const Class& cls = Class::Handle(zone, type_class());
+  const TypeParameters& params =
+      TypeParameters::Handle(zone, cls.type_parameters());
   printer->AddString(cls.NameCString(name_visibility));
   const TypeArguments& args = TypeArguments::Handle(zone, arguments());
   intptr_t num_type_params = 0;
   if (cls.is_declaration_loaded()) {
     num_type_params = cls.NumTypeParameters(thread);
-  } else if (!args.IsNull()) {
+  } else if (!args.IsNull() || args.ptr() != params.defaults()) {
     num_type_params = args.Length();
   }
   if (num_type_params == 0) {
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index e1414d4..724e58a 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -1198,7 +1198,7 @@
 
   int32_t SourceFingerprint() const;
 
-  // Return the Type with type arguments filled in with dynamic.
+  // Return the Type with type arguments instantiated to bounds.
   TypePtr RareType() const;
 
   // Return the non-nullable Type whose arguments are the type parameters
@@ -8315,6 +8315,7 @@
   friend class FunctionType;
   friend class Object;
   friend class Precompiler;
+  friend class Type;  // To determine whether to print type arguments.
 };
 
 // A TypeArguments is an array of AbstractType.