[ty] Infer parameter specializations of explicitly implemented generic protocols (#18054)

Follows on from (and depends on)
https://github.com/astral-sh/ruff/pull/18021.

This updates our function specialization inference to infer type
mappings from parameters that are generic protocols.

For now, this only works when the argument _explicitly_ implements the
protocol by listing it as a base class. (We end up using exactly the
same logic as for generic classes in #18021.) For this to work with
classes that _implicitly_ implement the protocol, we will have to check
the types of the protocol members (which we are not currently doing), so
that we can infer the specialization of the protocol that the class
implements.

---------

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

View file

@ -98,12 +98,10 @@ def deeper_list(x: list[set[str]]) -> None:
reveal_type(takes_in_protocol(x)) # revealed: Unknown
def deep_explicit(x: ExplicitlyImplements[str]) -> None:
# TODO: revealed: str
reveal_type(takes_in_protocol(x)) # revealed: Unknown
reveal_type(takes_in_protocol(x)) # revealed: str
def deeper_explicit(x: ExplicitlyImplements[set[str]]) -> None:
# TODO: revealed: set[str]
reveal_type(takes_in_protocol(x)) # revealed: Unknown
reveal_type(takes_in_protocol(x)) # revealed: set[str]
def takes_in_type(x: type[T]) -> type[T]:
return x
@ -128,10 +126,8 @@ reveal_type(takes_in_protocol(GenericSub[str]())) # revealed: Unknown
class ExplicitSub(ExplicitlyImplements[int]): ...
class ExplicitGenericSub(ExplicitlyImplements[T]): ...
# TODO: revealed: int
reveal_type(takes_in_protocol(ExplicitSub())) # revealed: Unknown
# TODO: revealed: str
reveal_type(takes_in_protocol(ExplicitGenericSub[str]())) # revealed: Unknown
reveal_type(takes_in_protocol(ExplicitSub())) # revealed: int
reveal_type(takes_in_protocol(ExplicitGenericSub[str]())) # revealed: str
```
## Inferring a bound typevar

View file

@ -93,12 +93,10 @@ def deeper_list(x: list[set[str]]) -> None:
reveal_type(takes_in_protocol(x)) # revealed: Unknown
def deep_explicit(x: ExplicitlyImplements[str]) -> None:
# TODO: revealed: str
reveal_type(takes_in_protocol(x)) # revealed: Unknown
reveal_type(takes_in_protocol(x)) # revealed: str
def deeper_explicit(x: ExplicitlyImplements[set[str]]) -> None:
# TODO: revealed: set[str]
reveal_type(takes_in_protocol(x)) # revealed: Unknown
reveal_type(takes_in_protocol(x)) # revealed: set[str]
def takes_in_type[T](x: type[T]) -> type[T]:
return x
@ -123,10 +121,8 @@ reveal_type(takes_in_protocol(GenericSub[str]())) # revealed: Unknown
class ExplicitSub(ExplicitlyImplements[int]): ...
class ExplicitGenericSub[T](ExplicitlyImplements[T]): ...
# TODO: revealed: int
reveal_type(takes_in_protocol(ExplicitSub())) # revealed: Unknown
# TODO: revealed: str
reveal_type(takes_in_protocol(ExplicitGenericSub[str]())) # revealed: Unknown
reveal_type(takes_in_protocol(ExplicitSub())) # revealed: int
reveal_type(takes_in_protocol(ExplicitGenericSub[str]())) # revealed: str
```
## Inferring a bound typevar