mirror of
https://github.com/astral-sh/ruff
synced 2026-01-21 21:40:51 -05:00
[ty] Support type inference between protocol instances (#22120)
This commit is contained in:
@@ -80,6 +80,7 @@ class CanIndex(Protocol[T]):
|
||||
def __getitem__(self, index: int, /) -> T: ...
|
||||
|
||||
class ExplicitlyImplements(CanIndex[T]): ...
|
||||
class SubProtocol(CanIndex[T], Protocol): ...
|
||||
|
||||
def takes_in_list(x: list[T]) -> list[T]:
|
||||
return x
|
||||
@@ -103,6 +104,18 @@ def deep_explicit(x: ExplicitlyImplements[str]) -> None:
|
||||
def deeper_explicit(x: ExplicitlyImplements[set[str]]) -> None:
|
||||
reveal_type(takes_in_protocol(x)) # revealed: set[str]
|
||||
|
||||
def deep_subprotocol(x: SubProtocol[str]) -> None:
|
||||
reveal_type(takes_in_protocol(x)) # revealed: str
|
||||
|
||||
def deeper_subprotocol(x: SubProtocol[set[str]]) -> None:
|
||||
reveal_type(takes_in_protocol(x)) # revealed: set[str]
|
||||
|
||||
def itself(x: CanIndex[str]) -> None:
|
||||
reveal_type(takes_in_protocol(x)) # revealed: str
|
||||
|
||||
def deep_itself(x: CanIndex[set[str]]) -> None:
|
||||
reveal_type(takes_in_protocol(x)) # revealed: set[str]
|
||||
|
||||
def takes_in_type(x: type[T]) -> type[T]:
|
||||
return x
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ class CanIndex(Protocol[S]):
|
||||
def __getitem__(self, index: int, /) -> S: ...
|
||||
|
||||
class ExplicitlyImplements[T](CanIndex[T]): ...
|
||||
class SubProtocol[T](CanIndex[T], Protocol): ...
|
||||
|
||||
def takes_in_list[T](x: list[T]) -> list[T]:
|
||||
return x
|
||||
@@ -98,6 +99,18 @@ def deep_explicit(x: ExplicitlyImplements[str]) -> None:
|
||||
def deeper_explicit(x: ExplicitlyImplements[set[str]]) -> None:
|
||||
reveal_type(takes_in_protocol(x)) # revealed: set[str]
|
||||
|
||||
def deep_subprotocol(x: SubProtocol[str]) -> None:
|
||||
reveal_type(takes_in_protocol(x)) # revealed: str
|
||||
|
||||
def deeper_subprotocol(x: SubProtocol[set[str]]) -> None:
|
||||
reveal_type(takes_in_protocol(x)) # revealed: set[str]
|
||||
|
||||
def itself(x: CanIndex[str]) -> None:
|
||||
reveal_type(takes_in_protocol(x)) # revealed: str
|
||||
|
||||
def deep_itself(x: CanIndex[set[str]]) -> None:
|
||||
reveal_type(takes_in_protocol(x)) # revealed: set[str]
|
||||
|
||||
def takes_in_type[T](x: type[T]) -> type[T]:
|
||||
return x
|
||||
|
||||
|
||||
@@ -1866,6 +1866,22 @@ impl<'db> SpecializationBuilder<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
(formal, Type::ProtocolInstance(actual_protocol)) => {
|
||||
// TODO: This will only handle protocol classes that explicit inherit
|
||||
// from other generic protocol classes by listing it as a base class.
|
||||
// To handle classes that implicitly implement a generic protocol, we
|
||||
// will need to check the types of the protocol members to be able to
|
||||
// infer the specialization of the protocol that the class implements.
|
||||
if let Some(actual_nominal) = actual_protocol.as_nominal_type() {
|
||||
return self.infer_map_impl(
|
||||
formal,
|
||||
Type::NominalInstance(actual_nominal),
|
||||
polarity,
|
||||
f,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
(formal, Type::NominalInstance(actual_nominal)) => {
|
||||
// Special case: `formal` and `actual` are both tuples.
|
||||
if let (Some(formal_tuple), Some(actual_tuple)) = (
|
||||
|
||||
Reference in New Issue
Block a user