From 0280949000ee448f17a1c2b5d802d9ad569301ac Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Wed, 3 Dec 2025 19:01:42 +0000 Subject: [PATCH] [ty] fix panic when attempting to infer the variance of a PEP-695 class that depends on a recursive type aliases and also somehow protocols (#21778) Fixes https://github.com/astral-sh/ty/issues/1716. ## Test plan I added a corpus snippet that causes us to panic on `main` (I tested by running `cargo run -p ty_python_semantic --test=corpus` without the fix applied). --- .../resources/corpus/cyclic_pep695_variance.py | 14 ++++++++++++++ crates/ty_python_semantic/src/types/class.rs | 11 ++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 crates/ty_python_semantic/resources/corpus/cyclic_pep695_variance.py diff --git a/crates/ty_python_semantic/resources/corpus/cyclic_pep695_variance.py b/crates/ty_python_semantic/resources/corpus/cyclic_pep695_variance.py new file mode 100644 index 0000000000..70f354337d --- /dev/null +++ b/crates/ty_python_semantic/resources/corpus/cyclic_pep695_variance.py @@ -0,0 +1,14 @@ +from typing import Protocol + +class A(Protocol): + @property + def f(self): ... + +type Recursive = int | tuple[Recursive, ...] + +class B[T: A]: ... + +class C[T: A](A): + x: tuple[Recursive, ...] + +class D(B[C]): ... diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs index 0abb33c54f..5ebf92fb06 100644 --- a/crates/ty_python_semantic/src/types/class.rs +++ b/crates/ty_python_semantic/src/types/class.rs @@ -340,9 +340,18 @@ impl<'db> From> for Type<'db> { } } +fn variance_of_cycle_initial<'db>( + _db: &'db dyn Db, + _id: salsa::Id, + _self: GenericAlias<'db>, + _typevar: BoundTypeVarInstance<'db>, +) -> TypeVarVariance { + TypeVarVariance::Bivariant +} + #[salsa::tracked] impl<'db> VarianceInferable<'db> for GenericAlias<'db> { - #[salsa::tracked(heap_size=ruff_memory_usage::heap_size)] + #[salsa::tracked(heap_size=ruff_memory_usage::heap_size, cycle_initial=variance_of_cycle_initial)] fn variance_of(self, db: &'db dyn Db, typevar: BoundTypeVarInstance<'db>) -> TypeVarVariance { let origin = self.origin(db);