mirror of https://github.com/astral-sh/ruff
[ty] Avoid stack overflow when calculating inferable typevars (#21971)
When we calculate which typevars are inferable in a generic context, the
result might include more than the typevars bound by the generic
context. The canonical example is a generic method of a generic class:
```py
class C[A]:
def method[T](self, t: T): ...
```
Here, the inferable typevar set of `method` contains `Self` and `T`, as
you'd expect. (Those are the typevars bound by the method.) But it also
contains `A@C`, since the implicit `Self` typevar is defined as `Self:
C[A]`. That means when we call `method`, we need to mark `A@C` as
inferable, so that we can determine the correct mapping for `A@C` at the
call site.
Fixes https://github.com/astral-sh/ty/issues/1874
This commit is contained in:
parent
8f530a7ab0
commit
cbfecfaf41
|
|
@ -800,6 +800,29 @@ def func(x: D): ...
|
|||
func(G()) # error: [invalid-argument-type]
|
||||
```
|
||||
|
||||
### Self-referential protocol with different specialization
|
||||
|
||||
This is a minimal reproduction for [ty#1874](https://github.com/astral-sh/ty/issues/1874).
|
||||
|
||||
```py
|
||||
from __future__ import annotations
|
||||
from typing import Protocol
|
||||
from ty_extensions import generic_context
|
||||
|
||||
class A[S, R](Protocol):
|
||||
def get(self, s: S) -> R: ...
|
||||
def set(self, s: S, r: R) -> S: ...
|
||||
def merge[R2](self, other: A[S, R2]) -> A[S, tuple[R, R2]]: ...
|
||||
|
||||
class Impl[S, R](A[S, R]):
|
||||
def foo(self, s: S) -> S:
|
||||
return self.set(s, self.get(s))
|
||||
|
||||
reveal_type(generic_context(A.get)) # revealed: ty_extensions.GenericContext[Self@get]
|
||||
reveal_type(generic_context(A.merge)) # revealed: ty_extensions.GenericContext[Self@merge, R2@merge]
|
||||
reveal_type(generic_context(Impl.foo)) # revealed: ty_extensions.GenericContext[Self@foo]
|
||||
```
|
||||
|
||||
## Tuple as a PEP-695 generic class
|
||||
|
||||
Our special handling for `tuple` does not break if `tuple` is defined as a PEP-695 generic class in
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ use crate::types::{
|
|||
KnownClass, KnownInstanceType, MaterializationKind, NormalizedVisitor, Signature, Type,
|
||||
TypeContext, TypeMapping, TypeRelation, TypeVarBoundOrConstraints, TypeVarIdentity,
|
||||
TypeVarInstance, TypeVarKind, TypeVarVariance, UnionType, declaration_type,
|
||||
walk_bound_type_var_type,
|
||||
walk_type_var_bounds,
|
||||
};
|
||||
use crate::{Db, FxOrderMap, FxOrderSet};
|
||||
|
||||
|
|
@ -290,6 +290,18 @@ impl<'db> GenericContext<'db> {
|
|||
)
|
||||
}
|
||||
|
||||
/// Returns the typevars that are inferable in this generic context. This set might include
|
||||
/// more typevars than the ones directly bound by the generic context. For instance, consider a
|
||||
/// method of a generic class:
|
||||
///
|
||||
/// ```py
|
||||
/// class C[A]:
|
||||
/// def method[T](self, t: T):
|
||||
/// ```
|
||||
///
|
||||
/// In this example, `method`'s generic context binds `Self` and `T`, but its inferable set
|
||||
/// also includes `A@C`. This is needed because at each call site, we need to infer the
|
||||
/// specialized class instance type whose method is being invoked.
|
||||
pub(crate) fn inferable_typevars(self, db: &'db dyn Db) -> InferableTypeVars<'db, 'db> {
|
||||
#[derive(Default)]
|
||||
struct CollectTypeVars<'db> {
|
||||
|
|
@ -299,7 +311,7 @@ impl<'db> GenericContext<'db> {
|
|||
|
||||
impl<'db> TypeVisitor<'db> for CollectTypeVars<'db> {
|
||||
fn should_visit_lazy_type_attributes(&self) -> bool {
|
||||
true
|
||||
false
|
||||
}
|
||||
|
||||
fn visit_bound_type_var_type(
|
||||
|
|
@ -310,7 +322,10 @@ impl<'db> GenericContext<'db> {
|
|||
self.typevars
|
||||
.borrow_mut()
|
||||
.insert(bound_typevar.identity(db));
|
||||
walk_bound_type_var_type(db, bound_typevar, self);
|
||||
let typevar = bound_typevar.typevar(db);
|
||||
if let Some(bound_or_constraints) = typevar.bound_or_constraints(db) {
|
||||
walk_type_var_bounds(db, bound_or_constraints, self);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_type(&self, db: &'db dyn Db, ty: Type<'db>) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue