Repository: python/mypy
Issue category: Bug Report

Change in inference of overloads involving `Hashable` and `slice` now that `slice` is generic
**Bug Report**

Consider the following Python snippet, saved as `foo.pyi`:

```py
from typing import assert_type, overload, Hashable

class Foo: ...

class DataFrame:
    @overload
    def __getitem__(self, key: slice) -> DataFrame: ...
    @overload
    def __getitem__(self, key: Hashable) -> Foo: ...


df = DataFrame()
assert_type(df[1:], DataFrame)
assert_type(df[:2], DataFrame)
```

Prior to [the recent change to make `slice` generic in typeshed](https://github.com/python/typeshed/pull/11637), mypy used to emit no errors on this snippet. However, mypy on the `master` branch emits the following errors:

```
foo.pyi:7: error: Overloaded function signatures 1 and 2 overlap with incompatible return types  [overload-overlap]
foo.pyi:13: error: Expression is of type "Any", not "DataFrame"  [assert-type]
foo.pyi:14: error: Expression is of type "Any", not "DataFrame"  [assert-type]
```

The first error here seems reasonable to me, but it's unclear to me why mypy now infers `Any` as the result of the `df` subscriptions. This snippet is minimized from the sole new mypy_primer error reported in https://github.com/python/typeshed/pull/11637#issuecomment-2435268577.

**To Reproduce**

https://mypy-play.net/?mypy=master&python=3.12&gist=339ba7f72c9048770ce15d6dc75207a1

**Your Environment**

- Mypy version used: mypy 1.14.0+dev.2ebc690279c7e20ed8bec006787030c5ba57c40e (compiled: no)



Functions to edit:
mypy/checkexpr.py:ExpressionChecker.visit_slice_expr

Patch:
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 577576a4e5f8..11a9cffe18c3 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -5612,11 +5612,15 @@ def visit_slice_expr(self, e: SliceExpr) -> Type:
         except KeyError:
             supports_index = self.chk.named_type("builtins.int")  # thanks, fixture life
         expected = make_optional_type(supports_index)
+        type_args = []
         for index in [e.begin_index, e.end_index, e.stride]:
             if index:
                 t = self.accept(index)
                 self.chk.check_subtype(t, expected, index, message_registry.INVALID_SLICE_INDEX)
-        return self.named_type("builtins.slice")
+                type_args.append(t)
+            else:
+                type_args.append(NoneType())
+        return self.chk.named_generic_type("builtins.slice", type_args)
 
     def visit_list_comprehension(self, e: ListComprehension) -> Type:
         return self.check_generator_or_comprehension(

Test patch: diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test
index d5ddc910bcd6..cd26c9bb408a 100644
--- a/test-data/unit/check-expressions.test
+++ b/test-data/unit/check-expressions.test
@@ -1178,8 +1178,8 @@ class B: pass
 [case testSlicingWithInvalidBase]
 
 a: A
-a[1:2] # E: Invalid index type "slice" for "A"; expected type "int"
-a[:]   # E: Invalid index type "slice" for "A"; expected type "int"
+a[1:2] # E: Invalid index type "slice[int, int, None]" for "A"; expected type "int"
+a[:]   # E: Invalid index type "slice[None, None, None]" for "A"; expected type "int"
 class A:
   def __getitem__(self, n: int) -> 'A': pass
 [builtins fixtures/slice.pyi]