Version 2.12.0-262.0.dev

Merge commit '0f371f2dba0dac9179c042cc46ce7ec6b1b90ab2' into 'dev'
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index 8ad3ca9..5a0c2ea 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -920,6 +920,21 @@
     TypeTestingStubGenerator::SpecializeStubFor(thread, dst_type);
 #if defined(DEBUG)
     ASSERT(old_code.ptr() != dst_type.type_test_stub());
+    const auto& old_instructions =
+        Instructions::Handle(old_code.instructions());
+    const auto& new_code = Code::Handle(dst_type.type_test_stub());
+    const auto& new_instructions =
+        Instructions::Handle(new_code.instructions());
+    // Check if specialization produced exactly the same sequence of
+    // instructions. If it did then we have a bug in the TTS code
+    // generation code: we know that old code could not handle src_instance
+    // which means new code would not be able to handle it either
+    // (because the code is exactly the same) and will fall through into
+    // runtime again given a similar instance and again ask compiler to
+    // specialize the stub, which would produce the same code again and
+    // so on - leading to a serious performance problem if this type check
+    // is hot.
+    ASSERT(!old_instructions.Equals(new_instructions));
 #endif
     // Only create the cache when we come from a normal stub.
     should_update_cache = false;
diff --git a/runtime/vm/type_testing_stubs.cc b/runtime/vm/type_testing_stubs.cc
index 686b69f..5fe1f7c 100644
--- a/runtime/vm/type_testing_stubs.cc
+++ b/runtime/vm/type_testing_stubs.cc
@@ -275,8 +275,8 @@
   // These are handled via the TopTypeTypeTestStub!
   ASSERT(!type.IsTopTypeForSubtyping());
 
-  // Fast case for 'int'.
-  if (type.IsIntType()) {
+  // Fast case for 'int' and '_Smi' (which can appear in core libraries).
+  if (type.IsIntType() || type.IsSmiType()) {
     compiler::Label non_smi_value;
     __ BranchIfNotSmi(TypeTestABI::kInstanceReg, &non_smi_value);
     __ Ret();
@@ -309,8 +309,8 @@
         /*include_abstract=*/false,
         /*exclude_null=*/!Instance::NullIsAssignableTo(type));
 
-    const Type& int_type = Type::Handle(Type::IntType());
-    const bool smi_is_ok = int_type.IsSubtypeOf(type, Heap::kNew);
+    const Type& smi_type = Type::Handle(Type::SmiType());
+    const bool smi_is_ok = smi_type.IsSubtypeOf(type, Heap::kNew);
 
     BuildOptimizedSubtypeRangeCheck(assembler, ranges, smi_is_ok);
   } else {
@@ -477,10 +477,11 @@
     __ BranchIf(NOT_EQUAL, check_failed);
   } else {
     const Class& type_class = Class::Handle(type_arg.type_class());
-    const CidRangeVector& ranges = hi->SubtypeRangesForClass(
-        type_class,
-        /*include_abstract=*/true,
-        /*exclude_null=*/!Instance::NullIsAssignableTo(type_arg));
+    const bool null_is_assignable = Instance::NullIsAssignableTo(type_arg);
+    const CidRangeVector& ranges =
+        hi->SubtypeRangesForClass(type_class,
+                                  /*include_abstract=*/true,
+                                  /*exclude_null=*/!null_is_assignable);
 
     __ LoadField(
         TTSInternalRegs::kScratchReg,
@@ -494,6 +495,10 @@
 
     compiler::Label is_subtype;
     __ SmiUntag(TTSInternalRegs::kScratchReg);
+    if (null_is_assignable) {
+      __ CompareImmediate(TTSInternalRegs::kScratchReg, kNullCid);
+      __ BranchIf(EQUAL, &is_subtype);
+    }
     FlowGraphCompiler::GenerateCidRangesCheck(
         assembler, TTSInternalRegs::kScratchReg, ranges, &is_subtype,
         check_failed, true);
diff --git a/runtime/vm/type_testing_stubs_test.cc b/runtime/vm/type_testing_stubs_test.cc
index bf9e154..04e4012 100644
--- a/runtime/vm/type_testing_stubs_test.cc
+++ b/runtime/vm/type_testing_stubs_test.cc
@@ -160,6 +160,11 @@
                        const SubtypeTestCache& stc,
                        const Smi& abi_regs_modified,
                        const Smi& rest_regs_modified)> nonlazy) {
+  THR_Print(
+      "TTS_Test(instance=%s, dst_type=%s, instantiator_tav=%s, "
+      "function_tav=%s)\n",
+      instance.ToCString(), dst_type.ToCString(), instantiator_tav.ToCString(),
+      function_tav.ToCString());
   ASSERT(instantiator_tav.IsNull() || instantiator_tav.IsCanonical());
   ASSERT(function_tav.IsNull() || function_tav.IsCanonical());
   auto thread = Thread::Current();
@@ -401,6 +406,7 @@
       createI() => I<int, String>();
       createI2() => I2();
       createBaseInt() => Base<int>();
+      createBaseNull() => Base<Null>();
       createA() => A();
       createA1() => A1();
       createA2() => A2<int>();
@@ -423,8 +429,10 @@
 
   const auto& obj_i = Object::Handle(Invoke(root_library, "createI"));
   const auto& obj_i2 = Object::Handle(Invoke(root_library, "createI2"));
-  const auto& obj_baseint =
+  const auto& obj_base_int =
       Object::Handle(Invoke(root_library, "createBaseInt"));
+  const auto& obj_base_null =
+      Object::Handle(Invoke(root_library, "createBaseNull"));
   const auto& obj_a = Object::Handle(Invoke(root_library, "createA"));
   const auto& obj_a1 = Object::Handle(Invoke(root_library, "createA1"));
   const auto& obj_a2 = Object::Handle(Invoke(root_library, "createA2"));
@@ -457,10 +465,10 @@
   // where there are no type arguments or the type arguments are top
   // types.
   //
-  //   obj as A                  // Subclass ranges
-  //   obj as Base<Object>       // Subclass ranges with top-type tav
-  //   obj as I2                 // Subtype ranges
-  //   obj as I<Object, dynamic> // Subtype ranges with top-type tav
+  //   obj as A                   // Subclass ranges
+  //   obj as Base<Object?>       // Subclass ranges with top-type tav
+  //   obj as I2                  // Subtype ranges
+  //   obj as I<Object?, dynamic> // Subtype ranges with top-type tav
   //
 
   // <...> as A
@@ -469,7 +477,7 @@
              ExpectFailedViaTTS);
   RunTTSTest(obj_i2, type_a, tav_null, tav_null, ExpectLazilyFailedViaTTS,
              ExpectFailedViaTTS);
-  RunTTSTest(obj_baseint, type_a, tav_null, tav_null, ExpectLazilyFailedViaTTS,
+  RunTTSTest(obj_base_int, type_a, tav_null, tav_null, ExpectLazilyFailedViaTTS,
              ExpectFailedViaTTS);
   RunTTSTest(obj_a, type_a, tav_null, tav_null, ExpectLazilyHandledViaTTS,
              ExpectHandledViaTTS);
@@ -484,14 +492,16 @@
   RunTTSTest(obj_b2, type_a, tav_null, tav_null, ExpectLazilyFailedViaTTS,
              ExpectFailedViaTTS);
 
-  // <...> as Base<Object>
+  // <...> as Base<Object?>
   auto& type_base = AbstractType::Handle(Type::New(class_base, tav_object));
   FinalizeAndCanonicalize(&type_base);
   RunTTSTest(obj_i, type_base, tav_null, tav_null, ExpectLazilyFailedViaTTS,
              ExpectFailedViaTTS);
   RunTTSTest(obj_i2, type_base, tav_null, tav_null, ExpectLazilyFailedViaTTS,
              ExpectFailedViaTTS);
-  RunTTSTest(obj_baseint, type_base, tav_null, tav_null,
+  RunTTSTest(obj_base_int, type_base, tav_null, tav_null,
+             ExpectLazilyHandledViaTTS, ExpectHandledViaTTS);
+  RunTTSTest(obj_base_null, type_base, tav_null, tav_null,
              ExpectLazilyHandledViaTTS, ExpectHandledViaTTS);
   RunTTSTest(obj_a, type_base, tav_null, tav_null, ExpectLazilyHandledViaTTS,
              ExpectHandledViaTTS);
@@ -506,14 +516,29 @@
   RunTTSTest(obj_b2, type_base, tav_null, tav_null, ExpectLazilyHandledViaTTS,
              ExpectHandledViaTTS);
 
+  // Base<Null> as Base<int?>
+  // This is a regression test verifying that Null is included in
+  // class-id ranges for int?.
+  auto& type_int = Type::Handle(Type::IntType());
+  type_int = type_int.ToNullability(
+      TestCase::IsNNBD() ? Nullability::kNullable : Nullability::kLegacy,
+      Heap::kNew);
+  auto& tav_int = TypeArguments::Handle(TypeArguments::New(1));
+  tav_int.SetTypeAt(0, type_int);
+  CanonicalizeTAV(&tav_int);
+  auto& type_base_int = AbstractType::Handle(Type::New(class_base, tav_int));
+  FinalizeAndCanonicalize(&type_base_int);
+  RunTTSTest(obj_base_null, type_base_int, tav_null, tav_null,
+             ExpectLazilyHandledViaTTS, ExpectHandledViaTTS);
+
   // <...> as I2
   const auto& type_i2 = AbstractType::Handle(class_i2.RareType());
   RunTTSTest(obj_i, type_i2, tav_null, tav_null, ExpectLazilyFailedViaTTS,
              ExpectFailedViaTTS);
   RunTTSTest(obj_i2, type_i2, tav_null, tav_null, ExpectLazilyHandledViaTTS,
              ExpectHandledViaTTS);
-  RunTTSTest(obj_baseint, type_i2, tav_null, tav_null, ExpectLazilyFailedViaTTS,
-             ExpectFailedViaTTS);
+  RunTTSTest(obj_base_int, type_i2, tav_null, tav_null,
+             ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
   RunTTSTest(obj_a, type_i2, tav_null, tav_null, ExpectLazilyFailedViaTTS,
              ExpectFailedViaTTS);
   RunTTSTest(obj_a1, type_i2, tav_null, tav_null, ExpectLazilyHandledViaTTS,
@@ -535,7 +560,7 @@
              ExpectLazilyHandledViaTTS, ExpectHandledViaTTS);
   RunTTSTest(obj_i2, type_i_object_dynamic, tav_null, tav_null,
              ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
-  RunTTSTest(obj_baseint, type_i_object_dynamic, tav_null, tav_null,
+  RunTTSTest(obj_base_int, type_i_object_dynamic, tav_null, tav_null,
              ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
   RunTTSTest(obj_a, type_i_object_dynamic, tav_null, tav_null,
              ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
@@ -562,7 +587,7 @@
              ExpectLazilyHandledViaSTC, ExpectHandledViaSTC);
   RunTTSTest(obj_i2, type_dynamic_t, tav_object, tav_null,
              ExpectLazilyFailedViaSTC, ExpectFailedViaSTC);
-  RunTTSTest(obj_baseint, type_dynamic_t, tav_object, tav_null,
+  RunTTSTest(obj_base_int, type_dynamic_t, tav_object, tav_null,
              ExpectLazilyFailedViaSTC, ExpectFailedViaSTC);
   RunTTSTest(obj_a, type_dynamic_t, tav_object, tav_null,
              ExpectLazilyFailedViaSTC, ExpectFailedViaSTC);
@@ -601,7 +626,7 @@
 
   const auto& obj_i = Object::Handle(Invoke(root_library, "createI"));
   const auto& obj_i2 = Object::Handle(Invoke(root_library, "createI2"));
-  const auto& obj_baseint =
+  const auto& obj_base_int =
       Object::Handle(Invoke(root_library, "createBaseInt"));
   const auto& obj_a = Object::Handle(Invoke(root_library, "createA"));
   const auto& obj_a1 = Object::Handle(Invoke(root_library, "createA1"));
@@ -703,7 +728,7 @@
       0, TypeParameter::Handle(GetClassTypeParameter(class_base, "T")));
   auto& type_base_t = AbstractType::Handle(Type::New(class_base, tav_baset));
   FinalizeAndCanonicalize(&type_base_t);
-  RunTTSTest(obj_baseint, type_base_t, tav_int, tav_null,
+  RunTTSTest(obj_base_int, type_base_t, tav_int, tav_null,
              ExpectLazilyHandledViaTTS, ExpectHandledViaTTS);
   RunTTSTest(obj_baseistringdouble, type_base_t, tav_int, tav_null,
              ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
@@ -715,12 +740,12 @@
   auto& type_base_b = AbstractType::Handle(Type::New(class_base, tav_baseb));
   FinalizeAndCanonicalize(&type_base_b);
   // With B == int
-  RunTTSTest(obj_baseint, type_base_b, tav_null, tav_dynamic_int,
+  RunTTSTest(obj_base_int, type_base_b, tav_null, tav_dynamic_int,
              ExpectLazilyHandledViaTTS, ExpectHandledViaTTS);
   RunTTSTest(obj_baseistringdouble, type_base_b, tav_null, tav_dynamic_int,
              ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
   // With B == dynamic (null vector)
-  RunTTSTest(obj_baseint, type_base_b, tav_null, tav_null,
+  RunTTSTest(obj_base_int, type_base_b, tav_null, tav_null,
              ExpectLazilyHandledViaTTS, ExpectHandledViaTTS);
   RunTTSTest(obj_i2, type_base_b, tav_null, tav_null, ExpectLazilyFailedViaTTS,
              ExpectFailedViaTTS);
@@ -742,7 +767,7 @@
   FinalizeAndCanonicalize(&type_i_dynamic_string);
   RunTTSTest(obj_i, type_i_dynamic_string, tav_null, tav_null,
              ExpectLazilyHandledViaSTC, ExpectHandledViaSTC);
-  RunTTSTest(obj_baseint, type_i_dynamic_string, tav_null, tav_null,
+  RunTTSTest(obj_base_int, type_i_dynamic_string, tav_null, tav_null,
              ExpectLazilyFailedViaSTC, ExpectFailedViaSTC);
 
   //   <...> as Base<A2<T>>
@@ -760,7 +785,7 @@
   FinalizeAndCanonicalize(&type_base_a2_t);
   RunTTSTest(obj_basea2int, type_base_a2_t, tav_null, tav_null,
              ExpectLazilyHandledViaSTC, ExpectHandledViaSTC);
-  RunTTSTest(obj_baseint, type_base_a2_t, tav_null, tav_null,
+  RunTTSTest(obj_base_int, type_base_a2_t, tav_null, tav_null,
              ExpectLazilyFailedViaSTC, ExpectFailedViaSTC);
 
   //   <...> as Base<A2<A1>>
@@ -872,6 +897,20 @@
              ExpectLazilyFailedViaTTS, ExpectFailedViaTTS);
 }
 
+// Check that we generate correct TTS for _Smi type.
+ISOLATE_UNIT_TEST_CASE(TTS_Smi) {
+  const auto& root_library = Library::Handle(Library::CoreLibrary());
+  const auto& smi_class = Class::Handle(GetClass(root_library, "_Smi"));
+  ClassFinalizer::FinalizeTypesInClass(smi_class);
+
+  const auto& dst_type = AbstractType::Handle(smi_class.RareType());
+  const auto& tav_null = TypeArguments::Handle(TypeArguments::null());
+
+  THR_Print("\nTesting that instance of _Smi is a subtype of _Smi\n");
+  RunTTSTest(Smi::Handle(Smi::New(0)), dst_type, tav_null, tav_null,
+             ExpectLazilyHandledViaTTS, ExpectHandledViaTTS);
+}
+
 }  // namespace dart
 
 #endif  // defined(TARGET_ARCH_ARM64) ||  defined(TARGET_ARCH_ARM) ||          \
diff --git a/tools/VERSION b/tools/VERSION
index a69e0df..96e8a89 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 261
+PRERELEASE 262
 PRERELEASE_PATCH 0
\ No newline at end of file