diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 971caa3991ae..63f5137ef8ae 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -794,15 +794,18 @@ def visit_tuple_type(self, left: TupleType) -> bool: return False if any(not self._is_subtype(l, r) for l, r in zip(left.items, right.items)): return False - rfallback = mypy.typeops.tuple_fallback(right) - if is_named_instance(rfallback, "builtins.tuple"): + if is_named_instance(right.partial_fallback, "builtins.tuple"): # No need to verify fallback. This is useful since the calculated fallback # may be inconsistent due to how we calculate joins between unions vs. # non-unions. For example, join(int, str) == object, whereas # join(Union[int, C], Union[str, C]) == Union[int, str, C]. return True - lfallback = mypy.typeops.tuple_fallback(left) - return self._is_subtype(lfallback, rfallback) + if is_named_instance(left.partial_fallback, "builtins.tuple"): + # Again, no need to verify. At this point we know the right fallback + # is a subclass of tuple, so if left is plain tuple, it cannot be a subtype. + return False + # At this point we know both fallbacks are non-tuple. + return self._is_subtype(left.partial_fallback, right.partial_fallback) else: return False diff --git a/test-data/unit/check-recursive-types.test b/test-data/unit/check-recursive-types.test index b67818e169b1..33cb9ccad9af 100644 --- a/test-data/unit/check-recursive-types.test +++ b/test-data/unit/check-recursive-types.test @@ -958,3 +958,39 @@ z: Z x: X reveal_type(z) # N: Revealed type is "Union[__main__.A[...], __main__.B[Union[..., None]]]" reveal_type(x) # N: Revealed type is "Union[__main__.A[Union[..., None]], __main__.B[Union[..., None]]]" + +[case testRecursiveTupleFallback1] +from typing import NewType, Tuple, Union + +T1 = NewType("T1", str) +T2 = Tuple[T1, "T4", "T4"] +T3 = Tuple[str, "T4", "T4"] +T4 = Union[T2, T3] +[builtins fixtures/tuple.pyi] + +[case testRecursiveTupleFallback2] +from typing import NewType, Tuple, Union + +T1 = NewType("T1", str) +class T2(Tuple[T1, "T4", "T4"]): ... +T3 = Tuple[str, "T4", "T4"] +T4 = Union[T2, T3] +[builtins fixtures/tuple.pyi] + +[case testRecursiveTupleFallback3] +from typing import NewType, Tuple, Union + +T1 = NewType("T1", str) +T2 = Tuple[T1, "T4", "T4"] +class T3(Tuple[str, "T4", "T4"]): ... +T4 = Union[T2, T3] +[builtins fixtures/tuple.pyi] + +[case testRecursiveTupleFallback4] +from typing import NewType, Tuple, Union + +T1 = NewType("T1", str) +class T2(Tuple[T1, "T4", "T4"]): ... +class T3(Tuple[str, "T4", "T4"]): ... +T4 = Union[T2, T3] +[builtins fixtures/tuple.pyi]