[ty] Specialize bound methods and nominal instances (#17865)

Fixes
https://github.com/astral-sh/ruff/pull/17832#issuecomment-2851224968. We
had a comment that we did not need to apply specializations to generic
aliases, or to the bound `self` of a bound method, because they were
already specialized. But they might be specialized with a type variable,
which _does_ need to be specialized, in the case of a "multi-step"
specialization, such as:

```py
class LinkedList[T]: ...

class C[U]:
    def method(self) -> LinkedList[U]:
        return LinkedList[U]()
```

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
Douglas Creager 2025-05-05 17:17:36 -04:00 committed by GitHub
parent 9a6633da0b
commit 47e3aa40b3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 106 additions and 13 deletions

View file

@ -145,7 +145,7 @@ def f(x: int) -> int:
return x**2
# TODO: Should be `_lru_cache_wrapper[int]`
reveal_type(f) # revealed: _lru_cache_wrapper[_T]
reveal_type(f) # revealed: _lru_cache_wrapper[Unknown]
# TODO: Should be `int`
reveal_type(f(1)) # revealed: Unknown

View file

@ -361,6 +361,40 @@ c: C[int] = C[int]()
reveal_type(c.method("string")) # revealed: Literal["string"]
```
## Specializations propagate
In a specialized generic alias, the specialization is applied to the attributes and methods of the
class.
```py
from typing import Generic, TypeVar
T = TypeVar("T")
U = TypeVar("U")
class LinkedList(Generic[T]): ...
class C(Generic[T, U]):
x: T
y: U
def method1(self) -> T:
return self.x
def method2(self) -> U:
return self.y
def method3(self) -> LinkedList[T]:
return LinkedList[T]()
c = C[int, str]()
reveal_type(c.x) # revealed: int
reveal_type(c.y) # revealed: str
reveal_type(c.method1()) # revealed: int
reveal_type(c.method2()) # revealed: str
reveal_type(c.method3()) # revealed: LinkedList[int]
```
## Cyclic class definitions
### F-bounded quantification

View file

@ -305,6 +305,35 @@ c: C[int] = C[int]()
reveal_type(c.method("string")) # revealed: Literal["string"]
```
## Specializations propagate
In a specialized generic alias, the specialization is applied to the attributes and methods of the
class.
```py
class LinkedList[T]: ...
class C[T, U]:
x: T
y: U
def method1(self) -> T:
return self.x
def method2(self) -> U:
return self.y
def method3(self) -> LinkedList[T]:
return LinkedList[T]()
c = C[int, str]()
reveal_type(c.x) # revealed: int
reveal_type(c.y) # revealed: str
reveal_type(c.method1()) # revealed: int
reveal_type(c.method2()) # revealed: str
reveal_type(c.method3()) # revealed: LinkedList[int]
```
## Cyclic class definitions
### F-bounded quantification