[ty] add support for cyclic legacy generic protocols (#20125)

## Summary

Just add the necessary Salsa cycle handling.

## Test Plan

Added mdtest.
This commit is contained in:
Carl Meyer 2025-08-28 09:58:01 -07:00 committed by GitHub
parent f4362b95d7
commit 9363eeca26
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 41 additions and 1 deletions

View file

@ -2339,6 +2339,28 @@ class Bar(Protocol[S]):
z: S | Bar[S] z: S | Bar[S]
``` ```
### Recursive legacy generic protocol
```py
from typing import Generic, TypeVar, Protocol
T = TypeVar("T")
class P(Protocol[T]):
attr: "P[T] | T"
class A(Generic[T]):
attr: T
class B(A[P[int]]):
pass
def f(b: B):
reveal_type(b) # revealed: B
reveal_type(b.attr) # revealed: P[int]
reveal_type(b.attr.attr) # revealed: P[int] | int
```
### Recursive generic protocols with property members ### Recursive generic protocols with property members
An early version of <https://github.com/astral-sh/ruff/pull/19936> caused stack overflows on this An early version of <https://github.com/astral-sh/ruff/pull/19936> caused stack overflows on this

View file

@ -5964,7 +5964,7 @@ impl<'db> Type<'db> {
/// Note that this does not specialize generic classes, functions, or type aliases! That is a /// Note that this does not specialize generic classes, functions, or type aliases! That is a
/// different operation that is performed explicitly (via a subscript operation), or implicitly /// different operation that is performed explicitly (via a subscript operation), or implicitly
/// via a call to the generic object. /// via a call to the generic object.
#[salsa::tracked(heap_size=ruff_memory_usage::heap_size)] #[salsa::tracked(heap_size=ruff_memory_usage::heap_size, cycle_fn=apply_specialization_cycle_recover, cycle_initial=apply_specialization_cycle_initial)]
pub(crate) fn apply_specialization( pub(crate) fn apply_specialization(
self, self,
db: &'db dyn Db, db: &'db dyn Db,
@ -6610,6 +6610,24 @@ impl<'db> VarianceInferable<'db> for Type<'db> {
} }
} }
fn apply_specialization_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &Type<'db>,
_count: u32,
_self: Type<'db>,
_specialization: Specialization<'db>,
) -> salsa::CycleRecoveryAction<Type<'db>> {
salsa::CycleRecoveryAction::Iterate
}
fn apply_specialization_cycle_initial<'db>(
_db: &'db dyn Db,
_self: Type<'db>,
_specialization: Specialization<'db>,
) -> Type<'db> {
Type::Never
}
/// A mapping that can be applied to a type, producing another type. This is applied inductively to /// A mapping that can be applied to a type, producing another type. This is applied inductively to
/// the components of complex types. /// the components of complex types.
/// ///