Skip to content

[cfe] Ability to hash and compare DartTypes without dereferencing References. #64543

Open

Description

@mkustermann

Most of the DartType class hierarchy is designed such that one can hash and compare them without dereferencing the actual References (e.g. the InterfaceTypes class reference). That allows one for example to build a canonicalization cache during kernel binary deserialization, which allows having many duplicates of the same type.

But I noticed that this doesn't work on all the DartTypes. Some of them e.g. acess a nullability getter (instead of using declaredNullability) which causes dereference of References.

Something like this could fix it:

diff --git a/pkg/kernel/lib/src/ast/types.dart b/pkg/kernel/lib/src/ast/types.dart
index 4a5397c40aa..bc0c6450d7a 100644
--- a/pkg/kernel/lib/src/ast/types.dart
+++ b/pkg/kernel/lib/src/ast/types.dart
@@ -1705,7 +1705,7 @@ class TypedefType extends DartType {
     if (identical(this, other)) {
       return true;
     } else if (other is TypedefType) {
-      if (nullability != other.nullability) return false;
+      if (declaredNullability != other.declaredNullability) return false;
       if (typedefReference != other.typedefReference ||
           typeArguments.length != other.typeArguments.length) {
         return false;
@@ -1723,11 +1723,12 @@ class TypedefType extends DartType {
 
   @override
   int get hashCode {
-    int hash = 0x3fffffff & typedefNode.hashCode;
+    int hash = 0x3fffffff & typedefReference.hashCode;
     for (int i = 0; i < typeArguments.length; ++i) {
       hash = 0x3fffffff & (hash * 31 + (hash ^ typeArguments[i].hashCode));
     }
-    int nullabilityHash = (0x33333333 >> nullability.index) ^ 0x33333333;
+    int nullabilityHash =
+        (0x33333333 >> declaredNullability.index) ^ 0x33333333;
     hash = 0x3fffffff & (hash * 31 + (hash ^ nullabilityHash));
     return hash;
   }
@@ -1966,7 +1967,7 @@ class ExtensionType extends TypeDeclarationType {
   bool equals(Object other, Assumptions? assumptions) {
     if (identical(this, other)) return true;
     if (other is ExtensionType) {
-      if (nullability != other.nullability) return false;
+      if (declaredNullability != other.declaredNullability) return false;
       if (extensionTypeDeclarationReference !=
           other.extensionTypeDeclarationReference) {
         return false;
@@ -1989,7 +1990,8 @@ class ExtensionType extends TypeDeclarationType {
     for (int i = 0; i < typeArguments.length; ++i) {
       hash = 0x3fffffff & (hash * 31 + (hash ^ typeArguments[i].hashCode));
     }
-    int nullabilityHash = (0x33333333 >> nullability.index) ^ 0x33333333;
+    int nullabilityHash =
+        (0x33333333 >> declaredNullability.index) ^ 0x33333333;
     hash = 0x3fffffff & (hash * 31 + (hash ^ nullabilityHash));
     return hash;
   }
@@ -2099,77 +2101,7 @@ class IntersectionType extends DartType {
   final TypeParameterType left;
   final DartType right;
 
-  new(this.left, this.right) {
-    // TODO(cstefantsova): Also assert that [rhs] is a subtype of [lhs.bound].
-
-    Nullability leftNullability = left.nullability;
-    Nullability rightNullability = right.nullability;
-    assert( /* very long */ );
-  }
+  new(this.left, this.right);
 
   @override
   DartType get nonTypeParameterBound {
@@ -2213,9 +2145,7 @@ class IntersectionType extends DartType {
 
   @override
   int get hashCode {
-    int nullabilityHash = (0x33333333 >> nullability.index) ^ 0x33333333;
-    int hash = nullabilityHash;
-    hash = 0x3fffffff & (hash * 31 + (hash ^ left.hashCode));
+    int hash = left.hashCode;
     hash = 0x3fffffff & (hash * 31 + (hash ^ right.hashCode));
     return hash;
   }
@@ -2264,61 +2194,6 @@ class IntersectionType extends DartType {
 
     Nullability lhsNullability = left.nullability;
     Nullability rhsNullability = right.nullability;
-    assert( /* very long */ );
 
     // Whenever there's N/A in the table, it means that the corresponding
     // combination of the LHS and RHS nullability is not possible when

though it would remove some very elaborate asserts .

(I ran into this because TFA was accidentally not erasing some extension types but wrote them via metadata to kernel binary, when deserializing it tried to dereference a Reference which hasn't been deserialized yet)

/cc @johnniwinther wdyt?

Activity

  1. added
    area-dart-modelFor issues related to conformance to the language spec in the parser, compilers or the CLI analyzer.
    on Oct 9, 2026
  2. johnniwinther commented on Oct 9, 2026

    @johnniwinther
    Member

    I think we should do this. Wrt the large assert(s) in IntersectionType, one copy should be moved to the Verifier on visitIntersectionType.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-dart-modelFor issues related to conformance to the language spec in the parser, compilers or the CLI analyzer.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions