[tfa,aot,dart2wasm] More precise intersection of a wide cone type with an empty cone type

When cone type has a large number of allocated subtypes, TFA uses
symbolic wide cone type as a faster approximation to speed up
the analysis.

Intersection of wide cone type C1+ with cone type C2+ when
there is no subtyping relation between C1 and C2 results in
approximate type C1+. As a result, analysis may fail to determine
that certain type test will never succeed.

This change improves precision of cone and wide cone intersections
and unions in case cone type has an empty specialization
(no allocated subtypes).

TEST=pkg/vm/testcases/transformations/type_flow/transformer/regress_60733.dart
Fixes https://github.com/dart-lang/sdk/issues/60733

Change-Id: I51287d38a9e5f3c0ce88dab358d0825843a0de03
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/428840
Reviewed-by: Slava Egorov <vegorov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
diff --git a/pkg/vm/lib/transformations/type_flow/types.dart b/pkg/vm/lib/transformations/type_flow/types.dart
index b29ed36..9e84d81 100644
--- a/pkg/vm/lib/transformations/type_flow/types.dart
+++ b/pkg/vm/lib/transformations/type_flow/types.dart
@@ -890,10 +890,12 @@
       if (this == other) {
         return this;
       }
-      if (other.cls.isSubtypeOf(this.cls)) {
+      if (other.hasEmptySpecialization(typeHierarchy) ||
+          other.cls.isSubtypeOf(this.cls)) {
         return this;
       }
-      if (this.cls.isSubtypeOf(other.cls)) {
+      if (this.hasEmptySpecialization(typeHierarchy) ||
+          this.cls.isSubtypeOf(other.cls)) {
         return other;
       }
     } else if (other is ConcreteType) {
@@ -916,6 +918,10 @@
       if (this == other) {
         return this;
       }
+      if (this.hasEmptySpecialization(typeHierarchy) ||
+          other.hasEmptySpecialization(typeHierarchy)) {
+        return emptyType;
+      }
       if (other.cls.isSubtypeOf(this.cls)) {
         return other;
       }
@@ -957,6 +963,9 @@
       (other is WideConeType) && identical(this.cls, other.cls);
 
   @override
+  String toString() => "_T (${cls})++";
+
+  @override
   int get order => TypeOrder.WideCone.index;
 
   @override
@@ -975,7 +984,8 @@
       return other.union(this, typeHierarchy);
     }
     if (other is ConeType) {
-      if (other.cls.isSubtypeOf(this.cls)) {
+      if (other.hasEmptySpecialization(typeHierarchy) ||
+          other.cls.isSubtypeOf(this.cls)) {
         return this;
       }
       if (this.cls.isSubtypeOf(other.cls)) {
@@ -1013,6 +1023,9 @@
       return other.intersection(this, typeHierarchy);
     }
     if (other is ConeType) {
+      if (other.hasEmptySpecialization(typeHierarchy)) {
+        return emptyType;
+      }
       if (other.cls.isSubtypeOf(this.cls)) {
         return other;
       }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/regress_60733.dart b/pkg/vm/testcases/transformations/type_flow/transformer/regress_60733.dart
new file mode 100644
index 0000000..eaca3cc
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/regress_60733.dart
@@ -0,0 +1,38 @@
+// Copyright (c) 2025, 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.
+
+// Regression test for https://github.com/dart-lang/sdk/issues/60733.
+// Verifies that intersection of a wide cone with an empty cone
+// results in empty type.
+
+// This test assumes the following low thresholds:
+// maxAllocatedTypesInSetSpecialization = 4,
+
+class Widget {}
+
+class W1 implements Widget {}
+
+class W2 implements Widget {}
+
+class W3 implements Widget {}
+
+class W4 implements Widget {}
+
+class W5 implements Widget {}
+
+class _HasCreationLocation {
+  Object? get location => 'hey';
+}
+
+Object? _getObjectCreationLocation(Object object) {
+  return object is _HasCreationLocation ? object.location : null;
+}
+
+final widgets = <Widget>[W1(), W2(), W3(), W4(), W5()];
+
+void main() {
+  for (Widget w in widgets) {
+    print(_getObjectCreationLocation(w));
+  }
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/regress_60733.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/regress_60733.dart.expect
new file mode 100644
index 0000000..89adb9c
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/regress_60733.dart.expect
@@ -0,0 +1,53 @@
+library #lib;
+import self as self;
+import "dart:core" as core;
+import "dart:_internal" as _in;
+
+abstract class Widget extends core::Object {
+}
+class W1 extends core::Object implements self::Widget {
+  synthetic constructor •() → self::W1
+    : super core::Object::•()
+    ;
+}
+class W2 extends core::Object implements self::Widget {
+  synthetic constructor •() → self::W2
+    : super core::Object::•()
+    ;
+}
+class W3 extends core::Object implements self::Widget {
+  synthetic constructor •() → self::W3
+    : super core::Object::•()
+    ;
+}
+class W4 extends core::Object implements self::Widget {
+  synthetic constructor •() → self::W4
+    : super core::Object::•()
+    ;
+}
+class W5 extends core::Object implements self::Widget {
+  synthetic constructor •() → self::W5
+    : super core::Object::•()
+    ;
+}
+
+[@vm.inferred-type.metadata=dart.core::_GrowableList<#lib::Widget>]
+static final field core::List<self::Widget> widgets = [@vm.inferred-type.metadata=dart.core::_GrowableList<#lib::Widget>] core::_GrowableList::_literal5<self::Widget>(new self::W1::•(), new self::W2::•(), new self::W3::•(), new self::W4::•(), new self::W5::•());
+
+[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)]
+static method _getObjectCreationLocation() → core::Object? {
+  return _in::unsafeCast<core::Object?>(null);
+}
+
+[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)]
+static method main() → void {
+  {
+    synthesized core::Iterator<self::Widget> :sync-for-iterator = [@vm.direct-call.metadata=dart.core::_GrowableList.iterator] [@vm.inferred-type.metadata=dart._internal::ListIterator<#lib::Widget>] [@vm.inferred-type.metadata=dart.core::_GrowableList<#lib::Widget>] self::widgets.{core::Iterable::iterator}{core::Iterator<self::Widget>};
+    for (; [@vm.direct-call.metadata=dart._internal::ListIterator.moveNext] [@vm.inferred-type.metadata=dart.core::bool (skip check)] :sync-for-iterator.{core::Iterator::moveNext}(){() → core::bool}; ) {
+      self::Widget w = [@vm.direct-call.metadata=dart._internal::ListIterator.current] [@vm.inferred-type.metadata=!] :sync-for-iterator.{core::Iterator::current}{self::Widget};
+      {
+        core::print([@vm.inferred-type.metadata=dart.core::Null? (value: null)] self::_getObjectCreationLocation());
+      }
+    }
+  }
+}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/regress_60733.dart.options b/pkg/vm/testcases/transformations/type_flow/transformer/regress_60733.dart.options
new file mode 100644
index 0000000..c1d7b57
--- /dev/null
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/regress_60733.dart.options
@@ -0,0 +1 @@
+--tfa.maxAllocatedTypesInSetSpecialization=4