Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tuple slice should not propagate fallback #16154

Merged
merged 6 commits into from
Oct 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -4271,7 +4271,7 @@ def visit_tuple_slice_helper(self, left_type: TupleType, slic: SliceExpr) -> Typ

items: list[Type] = []
for b, e, s in itertools.product(begin, end, stride):
item = left_type.slice(b, e, s)
item = left_type.slice(b, e, s, fallback=self.named_type("builtins.tuple"))
if item is None:
self.chk.fail(message_registry.AMBIGUOUS_SLICE_OF_VARIADIC_TUPLE, slic)
return AnyType(TypeOfAny.from_error)
Expand Down
9 changes: 7 additions & 2 deletions mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2416,7 +2416,12 @@ def copy_modified(
items = self.items
return TupleType(items, fallback, self.line, self.column)

def slice(self, begin: int | None, end: int | None, stride: int | None) -> TupleType | None:
def slice(
self, begin: int | None, end: int | None, stride: int | None, *, fallback: Instance | None
Copy link
Contributor Author

@graingert graingert Sep 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fallback should always be named_type("builtins.tuple") but I'm not sure how to make one without access to a TypeChecker instance

) -> TupleType | None:
if fallback is None:
fallback = self.partial_fallback

if any(isinstance(t, UnpackType) for t in self.items):
total = len(self.items)
unpack_index = find_unpack_in_list(self.items)
Expand Down Expand Up @@ -2462,7 +2467,7 @@ def slice(self, begin: int | None, end: int | None, stride: int | None) -> Tuple
return None
else:
slice_items = self.items[begin:end:stride]
return TupleType(slice_items, self.partial_fallback, self.line, self.column, self.implicit)
return TupleType(slice_items, fallback, self.line, self.column, self.implicit)


class TypedDictType(ProperType):
Expand Down
9 changes: 5 additions & 4 deletions test-data/unit/check-literal.test
Original file line number Diff line number Diff line change
Expand Up @@ -1872,8 +1872,9 @@ reveal_type(tup2[idx3]) # N: Revealed type is "__main__.D"
reveal_type(tup2[idx4]) # N: Revealed type is "__main__.E"
reveal_type(tup2[idx_neg1]) # N: Revealed type is "__main__.E"
tup2[idx5] # E: Tuple index out of range
reveal_type(tup2[idx2:idx4]) # N: Revealed type is "Tuple[__main__.C, __main__.D, fallback=__main__.Tup2Class]"
reveal_type(tup2[::idx2]) # N: Revealed type is "Tuple[__main__.A, __main__.C, __main__.E, fallback=__main__.Tup2Class]"
reveal_type(tup2[idx2:idx4]) # N: Revealed type is "Tuple[__main__.C, __main__.D]"
reveal_type(tup2[::idx2]) # N: Revealed type is "Tuple[__main__.A, __main__.C, __main__.E]"
tup3: Tup2Class = tup2[:] # E: Incompatible types in assignment (expression has type "Tuple[A, B, C, D, E]", variable has type "Tup2Class")
[builtins fixtures/slice.pyi]

[case testLiteralIntelligentIndexingTypedDict]
Expand Down Expand Up @@ -1977,8 +1978,8 @@ reveal_type(tup1[0::idx1]) # N: Revealed type is "Union[Tuple[__main__.A, _
tup1[idx_bad] # E: Tuple index out of range

reveal_type(tup2[idx1]) # N: Revealed type is "Union[__main__.B, __main__.C]"
reveal_type(tup2[idx1:idx2]) # N: Revealed type is "Union[Tuple[__main__.B, __main__.C, fallback=__main__.Tup2Class], Tuple[__main__.B, __main__.C, __main__.D, fallback=__main__.Tup2Class], Tuple[__main__.C, fallback=__main__.Tup2Class], Tuple[__main__.C, __main__.D, fallback=__main__.Tup2Class]]"
reveal_type(tup2[0::idx1]) # N: Revealed type is "Union[Tuple[__main__.A, __main__.B, __main__.C, __main__.D, __main__.E, fallback=__main__.Tup2Class], Tuple[__main__.A, __main__.C, __main__.E, fallback=__main__.Tup2Class]]"
reveal_type(tup2[idx1:idx2]) # N: Revealed type is "Union[Tuple[__main__.B, __main__.C], Tuple[__main__.B, __main__.C, __main__.D], Tuple[__main__.C], Tuple[__main__.C, __main__.D]]"
reveal_type(tup2[0::idx1]) # N: Revealed type is "Union[Tuple[__main__.A, __main__.B, __main__.C, __main__.D, __main__.E], Tuple[__main__.A, __main__.C, __main__.E]]"
tup2[idx_bad] # E: Tuple index out of range
[builtins fixtures/slice.pyi]
[out]
Expand Down
10 changes: 10 additions & 0 deletions test-data/unit/check-tuples.test
Original file line number Diff line number Diff line change
Expand Up @@ -1681,3 +1681,13 @@ def g(t: Tuple):
reveal_type(zip(*t)) # N: Revealed type is "typing.Iterator[builtins.tuple[Any, ...]]"
reveal_type(zip(t)) # N: Revealed type is "typing.Iterator[Tuple[Any]]"
[builtins fixtures/tuple.pyi]

[case testTupleSubclassSlice]
from typing import Tuple

class A: ...

class tuple_aa_subclass(Tuple[A, A]): ...

inst_tuple_aa_subclass: tuple_aa_subclass = tuple_aa_subclass((A(), A()))[:] # E: Incompatible types in assignment (expression has type "Tuple[A, A]", variable has type "tuple_aa_subclass")
[builtins fixtures/tuple.pyi]