Пример #1
0
 def visit_instance(self, t: Instance) -> Type:
     if isinstance(self.s, Instance):
         nominal = join_instances(t, self.s)
         structural = None  # type: Optional[Instance]
         if t.type.is_protocol and is_protocol_implementation(self.s, t):
             structural = t
         elif self.s.type.is_protocol and is_protocol_implementation(t, self.s):
             structural = self.s
         # Structural join is preferred in the case where we have found both
         # structural and nominal and they have same MRO length (see two comments
         # in join_instances_via_supertype). Otherwise, just return the nominal join.
         if not structural or is_better(nominal, structural):
             return nominal
         return structural
     elif isinstance(self.s, FunctionLike):
         if t.type.is_protocol:
             call = unpack_callback_protocol(t)
             if call:
                 return join_types(call, self.s)
         return join_types(t, self.s.fallback)
     elif isinstance(self.s, TypeType):
         return join_types(t, self.s)
     elif isinstance(self.s, TypedDictType):
         return join_types(t, self.s)
     elif isinstance(self.s, LiteralType):
         return join_types(t, self.s)
     else:
         return self.default(self.s)
Пример #2
0
 def visit_instance(self, t: Instance) -> ProperType:
     if isinstance(self.s, Instance):
         if self.instance_joiner is None:
             self.instance_joiner = InstanceJoiner()
         nominal = self.instance_joiner.join_instances(t, self.s)
         structural: Optional[Instance] = None
         if t.type.is_protocol and is_protocol_implementation(self.s, t):
             structural = t
         elif self.s.type.is_protocol and is_protocol_implementation(
                 t, self.s):
             structural = self.s
         # Structural join is preferred in the case where we have found both
         # structural and nominal and they have same MRO length (see two comments
         # in join_instances_via_supertype). Otherwise, just return the nominal join.
         if not structural or is_better(nominal, structural):
             return nominal
         return structural
     elif isinstance(self.s, FunctionLike):
         if t.type.is_protocol:
             call = unpack_callback_protocol(t)
             if call:
                 return join_types(call, self.s)
         return join_types(t, self.s.fallback)
     elif isinstance(self.s, TypeType):
         return join_types(t, self.s)
     elif isinstance(self.s, TypedDictType):
         return join_types(t, self.s)
     elif isinstance(self.s, TupleType):
         return join_types(t, self.s)
     elif isinstance(self.s, LiteralType):
         return join_types(t, self.s)
     else:
         return self.default(self.s)
Пример #3
0
 def visit_instance(self, t: Instance) -> Type:
     if isinstance(self.s, Instance):
         nominal = join_instances(t, self.s)
         structural = None  # type: Optional[Instance]
         if t.type.is_protocol and is_protocol_implementation(self.s, t):
             structural = t
         elif self.s.type.is_protocol and is_protocol_implementation(t, self.s):
             structural = self.s
         # Structural join is preferred in the case where we have found both
         # structural and nominal and they have same MRO length (see two comments
         # in join_instances_via_supertype). Otherwise, just return the nominal join.
         if not structural or is_better(nominal, structural):
             return nominal
         return structural
     elif isinstance(self.s, FunctionLike):
         return join_types(t, self.s.fallback)
     elif isinstance(self.s, TypeType):
         return join_types(t, self.s)
     elif isinstance(self.s, TypedDictType):
         return join_types(t, self.s)
     else:
         return self.default(self.s)
Пример #4
0
def is_overlapping_types(t: Type,
                         s: Type,
                         use_promotions: bool = False) -> bool:
    """Can a value of type t be a value of type s, or vice versa?

    Note that this effectively checks against erased types, since type
    variables are erased at runtime and the overlapping check is based
    on runtime behavior. The exception is protocol types, it is not safe,
    but convenient and is an opt-in behavior.

    If use_promotions is True, also consider type promotions (int and
    float would only be overlapping if it's True).

    This does not consider multiple inheritance. For example, A and B in
    the following example are not considered overlapping, even though
    via C they can be overlapping:

        class A: ...
        class B: ...
        class C(A, B): ...

    The rationale is that this case is usually very unlikely as multiple
    inheritance is rare. Also, we can't reliably determine whether
    multiple inheritance actually occurs somewhere in a program, due to
    stub files hiding implementation details, dynamic loading etc.

    TODO: Don't consider tuples always overlapping.
    TODO: Don't consider callables always overlapping.
    TODO: Don't consider type variables with values always overlapping.
    """
    # Any overlaps with everything
    if isinstance(t, AnyType) or isinstance(s, AnyType):
        return True

    # Since we are effectively working with the erased types, we only
    # need to handle occurrences of TypeVarType at the top level.
    if isinstance(t, TypeVarType):
        t = t.erase_to_union_or_bound()
    if isinstance(s, TypeVarType):
        s = s.erase_to_union_or_bound()
    if isinstance(t, TypedDictType):
        t = t.as_anonymous().fallback
    if isinstance(s, TypedDictType):
        s = s.as_anonymous().fallback
    if isinstance(t, Instance):
        if isinstance(s, Instance):
            # Consider two classes non-disjoint if one is included in the mro
            # of another.
            if use_promotions:
                # Consider cases like int vs float to be overlapping where
                # there is only a type promotion relationship but not proper
                # subclassing.
                if t.type._promote and is_overlapping_types(
                        t.type._promote, s):
                    return True
                if s.type._promote and is_overlapping_types(
                        s.type._promote, t):
                    return True
            if t.type in s.type.mro or s.type in t.type.mro:
                return True
            if t.type.is_protocol and is_protocol_implementation(s, t):
                return True
            if s.type.is_protocol and is_protocol_implementation(t, s):
                return True
            return False
    if isinstance(t, UnionType):
        return any(
            is_overlapping_types(item, s) for item in t.relevant_items())
    if isinstance(s, UnionType):
        return any(
            is_overlapping_types(t, item) for item in s.relevant_items())
    if isinstance(t, TypeType) and isinstance(s, TypeType):
        # If both types are TypeType, compare their inner types.
        return is_overlapping_types(t.item, s.item, use_promotions)
    elif isinstance(t, TypeType) or isinstance(s, TypeType):
        # If exactly only one of t or s is a TypeType, check if one of them
        # is an `object` or a `type` and otherwise assume no overlap.
        one = t if isinstance(t, TypeType) else s
        other = s if isinstance(t, TypeType) else t
        if isinstance(other, Instance):
            return other.type.fullname() in {
                'builtins.object', 'builtins.type'
            }
        else:
            return isinstance(other, CallableType) and is_subtype(other, one)
    if experiments.STRICT_OPTIONAL:
        if isinstance(t, NoneTyp) != isinstance(s, NoneTyp):
            # NoneTyp does not overlap with other non-Union types under strict Optional checking
            return False
    # We conservatively assume that non-instance, non-union, and non-TypeType types can overlap
    # any other types.
    return True
Пример #5
0
def is_overlapping_types(left: Type,
                         right: Type,
                         ignore_promotions: bool = False,
                         prohibit_none_typevar_overlap: bool = False) -> bool:
    """Can a value of type 'left' also be of type 'right' or vice-versa?

    If 'ignore_promotions' is True, we ignore promotions while checking for overlaps.
    If 'prohibit_none_typevar_overlap' is True, we disallow None from overlapping with
    TypeVars (in both strict-optional and non-strict-optional mode).
    """

    def _is_overlapping_types(left: Type, right: Type) -> bool:
        '''Encode the kind of overlapping check to perform.

        This function mostly exists so we don't have to repeat keyword arguments everywhere.'''
        return is_overlapping_types(
            left, right,
            ignore_promotions=ignore_promotions,
            prohibit_none_typevar_overlap=prohibit_none_typevar_overlap)

    # We should never encounter this type.
    if isinstance(left, PartialType) or isinstance(right, PartialType):
        assert False, "Unexpectedly encountered partial type"

    # We should also never encounter these types, but it's possible a few
    # have snuck through due to unrelated bugs. For now, we handle these
    # in the same way we handle 'Any'.
    #
    # TODO: Replace these with an 'assert False' once we are more confident.
    illegal_types = (UnboundType, ErasedType, DeletedType)
    if isinstance(left, illegal_types) or isinstance(right, illegal_types):
        return True

    # 'Any' may or may not be overlapping with the other type
    if isinstance(left, AnyType) or isinstance(right, AnyType):
        return True

    # When running under non-strict optional mode, simplify away types of
    # the form 'Union[A, B, C, None]' into just 'Union[A, B, C]'.

    if not state.strict_optional:
        if isinstance(left, UnionType):
            left = UnionType.make_union(left.relevant_items())
        if isinstance(right, UnionType):
            right = UnionType.make_union(right.relevant_items())

    # We check for complete overlaps next as a general-purpose failsafe.
    # If this check fails, we start checking to see if there exists a
    # *partial* overlap between types.
    #
    # These checks will also handle the NoneTyp and UninhabitedType cases for us.

    if (is_proper_subtype(left, right, ignore_promotions=ignore_promotions)
            or is_proper_subtype(right, left, ignore_promotions=ignore_promotions)):
        return True

    # See the docstring for 'get_possible_variants' for more info on what the
    # following lines are doing.

    left_possible = get_possible_variants(left)
    right_possible = get_possible_variants(right)

    # We start by checking multi-variant types like Unions first. We also perform
    # the same logic if either type happens to be a TypeVar.
    #
    # Handling the TypeVars now lets us simulate having them bind to the corresponding
    # type -- if we deferred these checks, the "return-early" logic of the other
    # checks will prevent us from detecting certain overlaps.
    #
    # If both types are singleton variants (and are not TypeVars), we've hit the base case:
    # we skip these checks to avoid infinitely recursing.

    def is_none_typevar_overlap(t1: Type, t2: Type) -> bool:
        return isinstance(t1, NoneTyp) and isinstance(t2, TypeVarType)

    if prohibit_none_typevar_overlap:
        if is_none_typevar_overlap(left, right) or is_none_typevar_overlap(right, left):
            return False

    if (len(left_possible) > 1 or len(right_possible) > 1
            or isinstance(left, TypeVarType) or isinstance(right, TypeVarType)):
        for l in left_possible:
            for r in right_possible:
                if _is_overlapping_types(l, r):
                    return True
        return False

    # Now that we've finished handling TypeVars, we're free to end early
    # if one one of the types is None and we're running in strict-optional mode.
    # (None only overlaps with None in strict-optional mode).
    #
    # We must perform this check after the TypeVar checks because
    # a TypeVar could be bound to None, for example.

    if state.strict_optional and isinstance(left, NoneTyp) != isinstance(right, NoneTyp):
        return False

    # Next, we handle single-variant types that may be inherently partially overlapping:
    #
    # - TypedDicts
    # - Tuples
    #
    # If we cannot identify a partial overlap and end early, we degrade these two types
    # into their 'Instance' fallbacks.

    if isinstance(left, TypedDictType) and isinstance(right, TypedDictType):
        return are_typed_dicts_overlapping(left, right, ignore_promotions=ignore_promotions)
    elif isinstance(left, TypedDictType):
        left = left.fallback
    elif isinstance(right, TypedDictType):
        right = right.fallback

    if is_tuple(left) and is_tuple(right):
        return are_tuples_overlapping(left, right, ignore_promotions=ignore_promotions)
    elif isinstance(left, TupleType):
        left = left.fallback
    elif isinstance(right, TupleType):
        right = right.fallback

    # Next, we handle single-variant types that cannot be inherently partially overlapping,
    # but do require custom logic to inspect.
    #
    # As before, we degrade into 'Instance' whenever possible.

    if isinstance(left, TypeType) and isinstance(right, TypeType):
        # TODO: Can Callable[[...], T] and Type[T] be partially overlapping?
        return _is_overlapping_types(left.item, right.item)

    if isinstance(left, CallableType) and isinstance(right, CallableType):
        return is_callable_compatible(left, right,
                                      is_compat=_is_overlapping_types,
                                      ignore_pos_arg_names=True,
                                      allow_partial_overlap=True)
    elif isinstance(left, CallableType):
        left = left.fallback
    elif isinstance(right, CallableType):
        right = right.fallback

    if isinstance(left, LiteralType) and isinstance(right, LiteralType):
        return left == right
    elif isinstance(left, LiteralType):
        left = left.fallback
    elif isinstance(right, LiteralType):
        right = right.fallback

    # Finally, we handle the case where left and right are instances.

    if isinstance(left, Instance) and isinstance(right, Instance):
        if left.type.is_protocol and is_protocol_implementation(right, left):
            return True
        if right.type.is_protocol and is_protocol_implementation(left, right):
            return True

        # Two unrelated types cannot be partially overlapping: they're disjoint.
        # We don't need to handle promotions because they've already been handled
        # by the calls to `is_subtype(...)` up above (and promotable types never
        # have any generic arguments we need to recurse on).
        if left.type.has_base(right.type.fullname()):
            left = map_instance_to_supertype(left, right.type)
        elif right.type.has_base(left.type.fullname()):
            right = map_instance_to_supertype(right, left.type)
        else:
            return False

        if len(left.args) == len(right.args):
            # Note: we don't really care about variance here, since the overlapping check
            # is symmetric and since we want to return 'True' even for partial overlaps.
            #
            # For example, suppose we have two types Wrapper[Parent] and Wrapper[Child].
            # It doesn't matter whether Wrapper is covariant or contravariant since
            # either way, one of the two types will overlap with the other.
            #
            # Similarly, if Wrapper was invariant, the two types could still be partially
            # overlapping -- what if Wrapper[Parent] happened to contain only instances of
            # specifically Child?
            #
            # Or, to use a more concrete example, List[Union[A, B]] and List[Union[B, C]]
            # would be considered partially overlapping since it's possible for both lists
            # to contain only instances of B at runtime.
            for left_arg, right_arg in zip(left.args, right.args):
                if _is_overlapping_types(left_arg, right_arg):
                    return True

        return False

    # We ought to have handled every case by now: we conclude the
    # two types are not overlapping, either completely or partially.
    #
    # Note: it's unclear however, whether returning False is the right thing
    # to do when inferring reachability -- see  https://github.com/python/mypy/issues/5529

    assert type(left) != type(right)
    return False
Пример #6
0
def is_overlapping_types(left: Type,
                         right: Type,
                         ignore_promotions: bool = False,
                         prohibit_none_typevar_overlap: bool = False) -> bool:
    """Can a value of type 'left' also be of type 'right' or vice-versa?

    If 'ignore_promotions' is True, we ignore promotions while checking for overlaps.
    If 'prohibit_none_typevar_overlap' is True, we disallow None from overlapping with
    TypeVars (in both strict-optional and non-strict-optional mode).
    """
    def _is_overlapping_types(left: Type, right: Type) -> bool:
        '''Encode the kind of overlapping check to perform.

        This function mostly exists so we don't have to repeat keyword arguments everywhere.'''
        return is_overlapping_types(
            left,
            right,
            ignore_promotions=ignore_promotions,
            prohibit_none_typevar_overlap=prohibit_none_typevar_overlap)

    # We should never encounter this type.
    if isinstance(left, PartialType) or isinstance(right, PartialType):
        assert False, "Unexpectedly encountered partial type"

    # We should also never encounter these types, but it's possible a few
    # have snuck through due to unrelated bugs. For now, we handle these
    # in the same way we handle 'Any'.
    #
    # TODO: Replace these with an 'assert False' once we are more confident.
    illegal_types = (UnboundType, ErasedType, DeletedType)
    if isinstance(left, illegal_types) or isinstance(right, illegal_types):
        return True

    # 'Any' may or may not be overlapping with the other type
    if isinstance(left, AnyType) or isinstance(right, AnyType):
        return True

    # When running under non-strict optional mode, simplify away types of
    # the form 'Union[A, B, C, None]' into just 'Union[A, B, C]'.

    if not state.strict_optional:
        if isinstance(left, UnionType):
            left = UnionType.make_union(left.relevant_items())
        if isinstance(right, UnionType):
            right = UnionType.make_union(right.relevant_items())

    # We check for complete overlaps next as a general-purpose failsafe.
    # If this check fails, we start checking to see if there exists a
    # *partial* overlap between types.
    #
    # These checks will also handle the NoneTyp and UninhabitedType cases for us.

    if (is_proper_subtype(left, right, ignore_promotions=ignore_promotions)
            or is_proper_subtype(
                right, left, ignore_promotions=ignore_promotions)):
        return True

    # See the docstring for 'get_possible_variants' for more info on what the
    # following lines are doing.

    left_possible = get_possible_variants(left)
    right_possible = get_possible_variants(right)

    # We start by checking multi-variant types like Unions first. We also perform
    # the same logic if either type happens to be a TypeVar.
    #
    # Handling the TypeVars now lets us simulate having them bind to the corresponding
    # type -- if we deferred these checks, the "return-early" logic of the other
    # checks will prevent us from detecting certain overlaps.
    #
    # If both types are singleton variants (and are not TypeVars), we've hit the base case:
    # we skip these checks to avoid infinitely recursing.

    def is_none_typevar_overlap(t1: Type, t2: Type) -> bool:
        return isinstance(t1, NoneTyp) and isinstance(t2, TypeVarType)

    if prohibit_none_typevar_overlap:
        if is_none_typevar_overlap(left, right) or is_none_typevar_overlap(
                right, left):
            return False

    if (len(left_possible) > 1 or len(right_possible) > 1
            or isinstance(left, TypeVarType)
            or isinstance(right, TypeVarType)):
        for l in left_possible:
            for r in right_possible:
                if _is_overlapping_types(l, r):
                    return True
        return False

    # Now that we've finished handling TypeVars, we're free to end early
    # if one one of the types is None and we're running in strict-optional mode.
    # (None only overlaps with None in strict-optional mode).
    #
    # We must perform this check after the TypeVar checks because
    # a TypeVar could be bound to None, for example.

    if state.strict_optional and isinstance(left, NoneTyp) != isinstance(
            right, NoneTyp):
        return False

    # Next, we handle single-variant types that may be inherently partially overlapping:
    #
    # - TypedDicts
    # - Tuples
    #
    # If we cannot identify a partial overlap and end early, we degrade these two types
    # into their 'Instance' fallbacks.

    if isinstance(left, TypedDictType) and isinstance(right, TypedDictType):
        return are_typed_dicts_overlapping(left,
                                           right,
                                           ignore_promotions=ignore_promotions)
    elif isinstance(left, TypedDictType):
        left = left.fallback
    elif isinstance(right, TypedDictType):
        right = right.fallback

    if is_tuple(left) and is_tuple(right):
        return are_tuples_overlapping(left,
                                      right,
                                      ignore_promotions=ignore_promotions)
    elif isinstance(left, TupleType):
        left = left.fallback
    elif isinstance(right, TupleType):
        right = right.fallback

    # Next, we handle single-variant types that cannot be inherently partially overlapping,
    # but do require custom logic to inspect.
    #
    # As before, we degrade into 'Instance' whenever possible.

    if isinstance(left, TypeType) and isinstance(right, TypeType):
        return _is_overlapping_types(left.item, right.item)

    def _type_object_overlap(left: Type, right: Type) -> bool:
        """Special cases for type object types overlaps."""
        # TODO: these checks are a bit in gray area, adjust if they cause problems.
        # 1. Type[C] vs Callable[..., C], where the latter is class object.
        if isinstance(left, TypeType) and isinstance(
                right, CallableType) and right.is_type_obj():
            return _is_overlapping_types(left.item, right.ret_type)
        # 2. Type[C] vs Meta, where Meta is a metaclass for C.
        if (isinstance(left, TypeType) and isinstance(left.item, Instance)
                and isinstance(right, Instance)):
            left_meta = left.item.type.metaclass_type
            if left_meta is not None:
                return _is_overlapping_types(left_meta, right)
            # builtins.type (default metaclass) overlaps with all metaclasses
            return right.type.has_base('builtins.type')
        # 3. Callable[..., C] vs Meta is considered below, when we switch to fallbacks.
        return False

    if isinstance(left, TypeType) or isinstance(right, TypeType):
        return _type_object_overlap(left, right) or _type_object_overlap(
            right, left)

    if isinstance(left, CallableType) and isinstance(right, CallableType):
        return is_callable_compatible(left,
                                      right,
                                      is_compat=_is_overlapping_types,
                                      ignore_pos_arg_names=True,
                                      allow_partial_overlap=True)
    elif isinstance(left, CallableType):
        left = left.fallback
    elif isinstance(right, CallableType):
        right = right.fallback

    if isinstance(left, LiteralType) and isinstance(right, LiteralType):
        return left == right
    elif isinstance(left, LiteralType):
        left = left.fallback
    elif isinstance(right, LiteralType):
        right = right.fallback

    # Finally, we handle the case where left and right are instances.

    if isinstance(left, Instance) and isinstance(right, Instance):
        if left.type.is_protocol and is_protocol_implementation(right, left):
            return True
        if right.type.is_protocol and is_protocol_implementation(left, right):
            return True

        # Two unrelated types cannot be partially overlapping: they're disjoint.
        # We don't need to handle promotions because they've already been handled
        # by the calls to `is_subtype(...)` up above (and promotable types never
        # have any generic arguments we need to recurse on).
        if left.type.has_base(right.type.fullname()):
            left = map_instance_to_supertype(left, right.type)
        elif right.type.has_base(left.type.fullname()):
            right = map_instance_to_supertype(right, left.type)
        else:
            return False

        if len(left.args) == len(right.args):
            # Note: we don't really care about variance here, since the overlapping check
            # is symmetric and since we want to return 'True' even for partial overlaps.
            #
            # For example, suppose we have two types Wrapper[Parent] and Wrapper[Child].
            # It doesn't matter whether Wrapper is covariant or contravariant since
            # either way, one of the two types will overlap with the other.
            #
            # Similarly, if Wrapper was invariant, the two types could still be partially
            # overlapping -- what if Wrapper[Parent] happened to contain only instances of
            # specifically Child?
            #
            # Or, to use a more concrete example, List[Union[A, B]] and List[Union[B, C]]
            # would be considered partially overlapping since it's possible for both lists
            # to contain only instances of B at runtime.
            for left_arg, right_arg in zip(left.args, right.args):
                if _is_overlapping_types(left_arg, right_arg):
                    return True

        return False

    # We ought to have handled every case by now: we conclude the
    # two types are not overlapping, either completely or partially.
    #
    # Note: it's unclear however, whether returning False is the right thing
    # to do when inferring reachability -- see  https://github.com/python/mypy/issues/5529

    assert type(left) != type(right)
    return False
Пример #7
0
def is_overlapping_types(t: Type, s: Type, use_promotions: bool = False) -> bool:
    """Can a value of type t be a value of type s, or vice versa?

    Note that this effectively checks against erased types, since type
    variables are erased at runtime and the overlapping check is based
    on runtime behavior. The exception is protocol types, it is not safe,
    but convenient and is an opt-in behavior.

    If use_promotions is True, also consider type promotions (int and
    float would only be overlapping if it's True).

    This does not consider multiple inheritance. For example, A and B in
    the following example are not considered overlapping, even though
    via C they can be overlapping:

        class A: ...
        class B: ...
        class C(A, B): ...

    The rationale is that this case is usually very unlikely as multiple
    inheritance is rare. Also, we can't reliably determine whether
    multiple inheritance actually occurs somewhere in a program, due to
    stub files hiding implementation details, dynamic loading etc.

    TODO: Don't consider callables always overlapping.
    TODO: Don't consider type variables with values always overlapping.
    """
    # Any overlaps with everything
    if isinstance(t, AnyType) or isinstance(s, AnyType):
        return True
    # object overlaps with everything
    if (isinstance(t, Instance) and t.type.fullname() == 'builtins.object' or
            isinstance(s, Instance) and s.type.fullname() == 'builtins.object'):
        return True

    # Since we are effectively working with the erased types, we only
    # need to handle occurrences of TypeVarType at the top level.
    if isinstance(t, TypeVarType):
        t = t.erase_to_union_or_bound()
    if isinstance(s, TypeVarType):
        s = s.erase_to_union_or_bound()
    if isinstance(t, TypedDictType):
        t = t.as_anonymous().fallback
    if isinstance(s, TypedDictType):
        s = s.as_anonymous().fallback

    if isinstance(t, UnionType):
        return any(is_overlapping_types(item, s)
                   for item in t.relevant_items())
    if isinstance(s, UnionType):
        return any(is_overlapping_types(t, item)
                   for item in s.relevant_items())

    # We must check for TupleTypes before Instances, since Tuple[A, ...]
    # is an Instance
    tup_overlap = is_overlapping_tuples(t, s, use_promotions)
    if tup_overlap is not None:
        return tup_overlap

    if isinstance(t, Instance):
        if isinstance(s, Instance):
            # Consider two classes non-disjoint if one is included in the mro
            # of another.
            if use_promotions:
                # Consider cases like int vs float to be overlapping where
                # there is only a type promotion relationship but not proper
                # subclassing.
                if t.type._promote and is_overlapping_types(t.type._promote, s):
                    return True
                if s.type._promote and is_overlapping_types(s.type._promote, t):
                    return True
            if t.type in s.type.mro or s.type in t.type.mro:
                return True
            if t.type.is_protocol and is_protocol_implementation(s, t):
                return True
            if s.type.is_protocol and is_protocol_implementation(t, s):
                return True
            return False
    if isinstance(t, TypeType) and isinstance(s, TypeType):
        # If both types are TypeType, compare their inner types.
        return is_overlapping_types(t.item, s.item, use_promotions)
    elif isinstance(t, TypeType) or isinstance(s, TypeType):
        # If exactly only one of t or s is a TypeType, check if one of them
        # is an `object` or a `type` and otherwise assume no overlap.
        one = t if isinstance(t, TypeType) else s
        other = s if isinstance(t, TypeType) else t
        if isinstance(other, Instance):
            return other.type.fullname() in {'builtins.object', 'builtins.type'}
        else:
            return isinstance(other, CallableType) and is_subtype(other, one)
    if experiments.STRICT_OPTIONAL:
        if isinstance(t, NoneTyp) != isinstance(s, NoneTyp):
            # NoneTyp does not overlap with other non-Union types under strict Optional checking
            return False
    # We conservatively assume that non-instance, non-union, non-TupleType and non-TypeType types
    # can overlap any other types.
    return True