From 8e60f03a5c8e49c765e6d8192178a5761935e015 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 29 May 2026 00:56:48 +0100 Subject: [PATCH] Fix crash on unhandled meet variadic tuple vs indtance --- mypy/meet.py | 23 ++++++++++++++++++++++- test-data/unit/check-typeis.test | 3 +-- test-data/unit/check-typevar-tuple.test | 12 ++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/mypy/meet.py b/mypy/meet.py index 18b2732c55932..6c05d449abe5c 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -1090,7 +1090,28 @@ def visit_tuple_type(self, t: TupleType) -> ProperType: elif isinstance(self.s, Instance): # meet(Tuple[t1, t2, <...>], Tuple[s, ...]) == Tuple[meet(t1, s), meet(t2, s), <...>]. if self.s.type.fullname in TUPLE_LIKE_INSTANCE_NAMES and self.s.args: - return t.copy_modified(items=[meet_types(it, self.s.args[0]) for it in t.items]) + arg = self.s.args[0] + new_items: list[Type] = [] + for it in t.items: + # Unpack items need to be handled by the caller. + if isinstance(it, UnpackType): + unpacked = get_proper_type(it.type) + if isinstance(unpacked, TypeVarTupleType): + # We can't infer anything in this case. + new_arg = UninhabitedType() + instance = unpacked.tuple_fallback + else: + assert ( + isinstance(unpacked, Instance) + and unpacked.type.fullname == "builtins.tuple" + ) + new_arg = meet_types(unpacked.args[0], arg) + instance = unpacked + new_items.append(UnpackType(instance.copy_modified(args=[new_arg]))) + else: + # All other items can be processed in a regular way. + new_items.append(meet_types(it, arg)) + return t.copy_modified(items=new_items) elif is_proper_subtype(t, self.s): # A named tuple that inherits from a normal class return t diff --git a/test-data/unit/check-typeis.test b/test-data/unit/check-typeis.test index b6c6b5ce0011e..65ee837452f5b 100644 --- a/test-data/unit/check-typeis.test +++ b/test-data/unit/check-typeis.test @@ -213,8 +213,7 @@ def test5(t: tuple[A | B, ...]) -> None: def test6(t: tuple[B, Unpack[tuple[A | B, ...]], B]) -> None: if is_tuple_of_B(t): - # Should this be tuple[B, *tuple[B, ...], B] - reveal_type(t) # N: Revealed type is "tuple[__main__.B, Never, __main__.B]" + reveal_type(t) # N: Revealed type is "tuple[__main__.B, Unpack[builtins.tuple[__main__.B, ...]], __main__.B]" else: reveal_type(t) # N: Revealed type is "tuple[__main__.B, Unpack[builtins.tuple[__main__.A | __main__.B, ...]], __main__.B]" [builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index 0119728e5834e..3f0765ba5c770 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -2873,3 +2873,15 @@ reveal_type(x) # N: Revealed type is "builtins.list[tuple[()]]" reveal_type(y) # N: Revealed type is "builtins.list[tuple[()]]" reveal_type(z) # N: Revealed type is "builtins.list[tuple[()]]" [builtins fixtures/tuple.pyi] + +[case testMeetTupleWithUnpackVsInstanceWorks_no_verbose_reveal] +from typing import TypeVar, Unpack, Union + +K = TypeVar("K") +Prefix = tuple[Union[K, list[int]], ...] + +def g(x: K) -> None: + d: Union[Prefix[K], tuple[Unpack[Prefix[int]], int], None] = None + assert d is not None + reveal_type(d) # N: Revealed type is "tuple[K | list[int], ...] | tuple[*tuple[int | list[int], ...], int]" +[builtins fixtures/tuple.pyi]