Skip to content

Commit 880eb87

Browse files
Fix checking of match sequence pattern against bounded type variables (#18091)
Fixes #18089 Adds handling for bounded type variables when checking match sequence patterns. Previously, this crashed. Now, this correctly narrows the upper bound of the type variable: ```python from typing import TypeVar, Sequence T = TypeVar("T", bound=Sequence[int | str]) def accept_seq_int(x: Sequence[int]): pass def f(x: T) -> None: match x: case [1, 2]: accept_seq_int(x) # ok: upper bound narrowed to Sequence[int] case _: accept_seq_int(x) # E: Argument 1 to "accept_seq_int" has incompatible type "T"; expected "Sequence[int]" ```
1 parent fa01a07 commit 880eb87

File tree

2 files changed

+42
-6
lines changed

2 files changed

+42
-6
lines changed

mypy/checkpattern.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
TypedDictType,
4747
TypeOfAny,
4848
TypeVarTupleType,
49+
TypeVarType,
4950
UninhabitedType,
5051
UnionType,
5152
UnpackType,
@@ -343,13 +344,11 @@ def visit_sequence_pattern(self, o: SequencePattern) -> PatternType:
343344
new_inner_type = UninhabitedType()
344345
for typ in new_inner_types:
345346
new_inner_type = join_types(new_inner_type, typ)
346-
new_type = self.construct_sequence_child(current_type, new_inner_type)
347-
if is_subtype(new_type, current_type):
348-
new_type, _ = self.chk.conditional_types_with_intersection(
349-
current_type, [get_type_range(new_type)], o, default=current_type
350-
)
347+
if isinstance(current_type, TypeVarType):
348+
new_bound = self.narrow_sequence_child(current_type.upper_bound, new_inner_type, o)
349+
new_type = current_type.copy_modified(upper_bound=new_bound)
351350
else:
352-
new_type = current_type
351+
new_type = self.narrow_sequence_child(current_type, new_inner_type, o)
353352
return PatternType(new_type, rest_type, captures)
354353

355354
def get_sequence_type(self, t: Type, context: Context) -> Type | None:
@@ -448,6 +447,16 @@ def expand_starred_pattern_types(
448447

449448
return new_types
450449

450+
def narrow_sequence_child(self, outer_type: Type, inner_type: Type, ctx: Context) -> Type:
451+
new_type = self.construct_sequence_child(outer_type, inner_type)
452+
if is_subtype(new_type, outer_type):
453+
new_type, _ = self.chk.conditional_types_with_intersection(
454+
outer_type, [get_type_range(new_type)], ctx, default=outer_type
455+
)
456+
else:
457+
new_type = outer_type
458+
return new_type
459+
451460
def visit_starred_pattern(self, o: StarredPattern) -> PatternType:
452461
captures: dict[Expression, Type] = {}
453462
if o.capture is not None:

test-data/unit/check-python310.test

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2382,3 +2382,30 @@ def test(xs: Tuple[Unpack[Ts]]) -> None:
23822382
reveal_type(b3) # N: Revealed type is "builtins.list[builtins.object]"
23832383
reveal_type(c3) # N: Revealed type is "builtins.int"
23842384
[builtins fixtures/tuple.pyi]
2385+
2386+
[case testMatchSequencePatternTypeVarBoundNoCrash]
2387+
# This was crashing: https://github.com/python/mypy/issues/18089
2388+
from typing import TypeVar, Sequence, Any
2389+
2390+
T = TypeVar("T", bound=Sequence[Any])
2391+
2392+
def f(x: T) -> None:
2393+
match x:
2394+
case [_]:
2395+
pass
2396+
[builtins fixtures/tuple.pyi]
2397+
2398+
[case testMatchSequencePatternTypeVarBoundNarrows]
2399+
from typing import TypeVar, Sequence
2400+
2401+
T = TypeVar("T", bound=Sequence[int | str])
2402+
2403+
def accept_seq_int(x: Sequence[int]): ...
2404+
2405+
def f(x: T) -> None:
2406+
match x:
2407+
case [1, 2]:
2408+
accept_seq_int(x)
2409+
case _:
2410+
accept_seq_int(x) # E: Argument 1 to "accept_seq_int" has incompatible type "T"; expected "Sequence[int]"
2411+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)