Skip to content

Commit ade0d1b

Browse files
committed
Clean up and add comments
1 parent 066a0f3 commit ade0d1b

File tree

1 file changed

+38
-18
lines changed

1 file changed

+38
-18
lines changed

mypy/subtypes.py

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
from collections.abc import Iterable, Iterator
44
from contextlib import contextmanager
5-
from itertools import product
65
from typing import Any, Callable, Final, TypeVar, cast
76
from typing_extensions import TypeAlias as _TypeAlias
87

@@ -35,7 +34,6 @@
3534
)
3635
from mypy.options import Options
3736
from mypy.state import state
38-
from mypy.typeops import make_simplified_union
3937
from 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.
291304
def _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

Comments
 (0)