2
2
3
3
from collections .abc import Iterable , Iterator
4
4
from contextlib import contextmanager
5
- from itertools import product
6
5
from typing import Any , Callable , Final , TypeVar , cast
7
6
from typing_extensions import TypeAlias as _TypeAlias
8
7
35
34
)
36
35
from mypy .options import Options
37
36
from mypy .state import state
38
- from mypy .typeops import make_simplified_union
39
37
from mypy .types import (
40
38
MYPYC_NATIVE_INT_NAMES ,
41
39
TUPLE_LIKE_INSTANCE_NAMES ,
@@ -286,6 +284,21 @@ def is_same_type(
286
284
)
287
285
288
286
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
+
289
302
# This is a common entry point for subtyping checks (both proper and non-proper).
290
303
# Never call this private function directly, use the public versions.
291
304
def _is_subtype (
@@ -308,22 +321,29 @@ def _is_subtype(
308
321
# ErasedType as we do for non-proper subtyping.
309
322
return True
310
323
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 )
327
347
if isinstance (right , UnionType ) and not isinstance (left , UnionType ):
328
348
# Normally, when 'left' is not itself a union, the only way
329
349
# 'left' can be a subtype of the union 'right' is if it is a
0 commit comments