isSubtypeOf method

  1. @override
bool isSubtypeOf(
  1. RuntimeType other, {
  2. Object? value,
})
override

Checks if this type is a subtype of other.

Implementation

@override
bool isSubtypeOf(RuntimeType other, {Object? value}) {
  // Any concrete type is a subtype of a type parameter (T)
  if (other is TypeParameter) return true;

  if (other is BridgedClass) {
    if (isSubtypeOfFunc != null) {
      return isSubtypeOfFunc!.call(other, value: value);
    }
    if (name == 'num') {
      // DFUB7: `num` is a subtype of `num` (and, below, of `Object`), but NOT
      // of its own subtypes `int`/`double`. The downward direction
      // (int/double <: num) is handled separately further down. Returning
      // true for int/double here made num a subtype of its subtypes.
      final isSubtype = switch (other.name) {
        'num' => true,
        _ => false,
      };
      return isSubtype;
    }

    if (nativeType == other.nativeType) return true;

    // Common Dart type hierarchy relationships
    // Object is a supertype of everything
    if (other.name == 'Object') return true;
    // List, Set implement Iterable
    if (other.name == 'Iterable' && (name == 'List' || name == 'Set')) {
      return true;
    }
    // int, double are subtypes of num
    if (other.name == 'num' && (name == 'int' || name == 'double')) {
      return true;
    }

    // GEN-075: Check native type hierarchy via isAssignable
    // When the value's native object satisfies the target class's isAssignable,
    // the native type IS a subtype (e.g., Row is a subtype of Widget).
    if (value != null && other.isAssignable != null) {
      final nativeValue = value is BridgedInstance
          ? value.nativeObject
          : value;
      if (other.isAssignable!(nativeValue)) return true;
    }

    // RC-7b: Check static supertype registry for native class hierarchy.
    // This handles cases like StatelessWidget→Widget where BridgedClass
    // objects don't have parent references.
    //
    // SCC19: consult the FULL closure. This used to check the direct
    // supertypes and one further hop and then stop — the comment here said
    // "walk the registry chain", but it walked exactly one extra link — so a
    // chain three levels deep answered false, while the member walk, reading
    // the same registry through [transitiveSupertypeNames], went all the way
    // down. A bridge could therefore resolve its inherited members correctly
    // and deny being a subtype of its own root, and every hierarchy block in
    // the stdlib was written with its closure flattened by hand to work
    // around it.
    //
    // The direct hit is kept as a short circuit: it answers most queries
    // without allocating the walk at all.
    final supertypes = _supertypeRegistry[name];
    if (supertypes == null) return false;
    if (supertypes.contains(other.name)) return true;
    return transitiveSupertypeNames(name).contains(other.name);
  }

  return false;
}