diff --git a/crates/ty_python_semantic/resources/mdtest/generics/legacy/variables.md b/crates/ty_python_semantic/resources/mdtest/generics/legacy/variables.md index 35cadfd399..b58507f43c 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/legacy/variables.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/legacy/variables.md @@ -172,4 +172,28 @@ T = TypeVar("T", covariant=cond()) U = TypeVar("U", contravariant=cond()) ``` +## Callability + +A typevar bound to a Callable type is callable: + +```py +from typing import Callable, TypeVar + +T = TypeVar("T", bound=Callable[[], int]) + +def bound(f: T): + reveal_type(f) # revealed: T + reveal_type(f()) # revealed: int +``` + +Same with a constrained typevar, as long as all constraints are callable: + +```py +T = TypeVar("T", Callable[[], int], Callable[[], str]) + +def constrained(f: T): + reveal_type(f) # revealed: T + reveal_type(f()) # revealed: int | str +``` + [generics]: https://typing.python.org/en/latest/spec/generics.html diff --git a/crates/ty_python_semantic/resources/mdtest/generics/pep695/variables.md b/crates/ty_python_semantic/resources/mdtest/generics/pep695/variables.md index 15a7e651e6..ecee87ec86 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/pep695/variables.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/pep695/variables.md @@ -746,4 +746,24 @@ def h[T: (P, None)](t: T) -> None: p: P = t ``` +## Callability + +A typevar bound to a Callable type is callable: + +```py +from typing import Callable + +def bound[T: Callable[[], int]](f: T): + reveal_type(f) # revealed: T + reveal_type(f()) # revealed: int +``` + +Same with a constrained typevar, as long as all constraints are callable: + +```py +def constrained[T: (Callable[[], int], Callable[[], str])](f: T): + reveal_type(f) # revealed: T + reveal_type(f()) # revealed: int | str +``` + [pep 695]: https://peps.python.org/pep-0695/ diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index f125540688..7704d133c2 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -3608,6 +3608,15 @@ impl<'db> Type<'db> { .into() } + Type::TypeVar(typevar) => match typevar.bound_or_constraints(db) { + None => CallableBinding::not_callable(self).into(), + Some(TypeVarBoundOrConstraints::UpperBound(bound)) => bound.bindings(db), + Some(TypeVarBoundOrConstraints::Constraints(constraints)) => Bindings::from_union( + self, + constraints.elements(db).iter().map(|ty| ty.bindings(db)), + ), + }, + Type::BoundMethod(bound_method) => { let signature = bound_method.function(db).signature(db); CallableBinding::from_overloads(self, signature.overloads.iter().cloned()) @@ -4422,7 +4431,6 @@ impl<'db> Type<'db> { | Type::LiteralString | Type::Tuple(_) | Type::BoundSuper(_) - | Type::TypeVar(_) | Type::ModuleLiteral(_) => CallableBinding::not_callable(self).into(), } }