22
33from collections .abc import Iterable , Iterator
44from contextlib import contextmanager
5- from itertools import product
65from typing import Any , Callable , Final , TypeVar , cast
76from typing_extensions import TypeAlias as _TypeAlias
87
3534)
3635from mypy .options import Options
3736from mypy .state import state
38- from mypy .typeops import make_simplified_union
3937from mypy .types import (
4038 MYPYC_NATIVE_INT_NAMES ,
4139 TUPLE_LIKE_INSTANCE_NAMES ,
@@ -286,6 +284,21 @@ def is_same_type(
286284 )
287285
288286
287+ # This is a helper function used to check for recursive type of distributed tuple
288+ def structurally_recursive (typ : Type , seen : set [Type ] | None = None ) -> bool :
289+ if seen is None :
290+ seen = set ()
291+ typ = get_proper_type (typ )
292+ if typ in seen :
293+ return True
294+ seen .add (typ )
295+ if isinstance (typ , UnionType ):
296+ return any (structurally_recursive (item , seen .copy ()) for item in typ .items )
297+ if isinstance (typ , TupleType ):
298+ return any (structurally_recursive (item , seen .copy ()) for item in typ .items )
299+ return False
300+
301+
289302# This is a common entry point for subtyping checks (both proper and non-proper).
290303# Never call this private function directly, use the public versions.
291304def _is_subtype (
@@ -308,22 +321,29 @@ def _is_subtype(
308321 # ErasedType as we do for non-proper subtyping.
309322 return True
310323 if isinstance (left , TupleType ) and isinstance (right , UnionType ):
311- items = [get_proper_type (item ) for item in left .items ]
312- if any (isinstance (item , UnionType ) for item in items ):
313- expanded = []
314- for item in items :
315- if isinstance (item , UnionType ):
316- expanded .append (item .items )
317- else :
318- expanded .append ([item ])
319- distributed = []
320- for combo in product (* expanded ):
321- fb = left .partial_fallback
322- if hasattr (left , "fallback" ) and left .fallback is not None :
323- fb = left .fallback
324- distributed .append (TupleType (list (combo ), fallback = fb ))
325- simplified = make_simplified_union (distributed )
326- return _is_subtype (simplified , right , subtype_context , proper_subtype = False )
324+ # check only if not recursive type because if recursive type,
325+ # test run into maximum recursive depth reached
326+ if not structurally_recursive (left ) and not structurally_recursive (right ):
327+ fallback = left .partial_fallback
328+ tuple_items = left .items
329+ if hasattr (left , "fallback" ) and left .fallback is not None :
330+ fallback = left .fallback
331+ for i in range (len (tuple_items )):
332+ uitems = tuple_items [i ]
333+ uitems_type = get_proper_type (uitems )
334+ if isinstance (uitems_type , UnionType ):
335+ new_tuples = [
336+ TupleType (
337+ tuple_items [:i ] + [uitem ] + tuple_items [i + 1 :], fallback = fallback
338+ )
339+ for uitem in uitems_type .items
340+ ]
341+ result = [
342+ _is_subtype (t , right , subtype_context , proper_subtype = False )
343+ for t in new_tuples
344+ ]
345+ inverted_list = [not item for item in result ]
346+ return not any (inverted_list )
327347 if isinstance (right , UnionType ) and not isinstance (left , UnionType ):
328348 # Normally, when 'left' is not itself a union, the only way
329349 # 'left' can be a subtype of the union 'right' is if it is a
0 commit comments