diff --git a/crates/ty_python_semantic/resources/mdtest/generics/legacy/classes.md b/crates/ty_python_semantic/resources/mdtest/generics/legacy/classes.md index 184a7b49a5..c520b7e883 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/legacy/classes.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/legacy/classes.md @@ -753,5 +753,29 @@ class C(C, Generic[T]): ... class D(D[int], Generic[T]): ... ``` +### Cyclic inheritance in a stub file combined with constrained type variables + +This is a regression test for ; we used to panic on +this: + +`stub.pyi`: + +```pyi +from typing import Generic, TypeVar + +class A(B): ... +class G: ... + +T = TypeVar("T", G, A) + +class C(Generic[T]): ... +class B(C[A]): ... +class D(C[G]): ... + +def func(x: D): ... + +func(G()) # error: [invalid-argument-type] +``` + [crtp]: https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern [f-bound]: https://en.wikipedia.org/wiki/Bounded_quantification#F-bounded_quantification diff --git a/crates/ty_python_semantic/resources/mdtest/generics/pep695/classes.md b/crates/ty_python_semantic/resources/mdtest/generics/pep695/classes.md index 13ed24c7f4..1f52d16d9a 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/pep695/classes.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/pep695/classes.md @@ -638,6 +638,25 @@ class C[T](C): ... class D[T](D[int]): ... ``` +### Cyclic inheritance in a stub file combined with constrained type variables + +This is a regression test for ; we used to panic on +this: + +`stub.pyi`: + +```pyi +class A(B): ... +class G: ... +class C[T: (G, A)]: ... +class B(C[A]): ... +class D(C[G]): ... + +def func(x: D): ... + +func(G()) # error: [invalid-argument-type] +``` + ## 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 diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 6976d6dc4a..be545c4e59 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -8303,7 +8303,11 @@ impl<'db> TypeVarInstance<'db> { )) } - #[salsa::tracked(cycle_fn=lazy_bound_cycle_recover, cycle_initial=lazy_bound_cycle_initial, heap_size=ruff_memory_usage::heap_size)] + #[salsa::tracked( + cycle_fn=lazy_bound_or_constraints_cycle_recover, + cycle_initial=lazy_bound_or_constraints_cycle_initial, + heap_size=ruff_memory_usage::heap_size + )] fn lazy_bound(self, db: &'db dyn Db) -> Option> { let definition = self.definition(db)?; let module = parsed_module(db, definition.file(db)).load(db); @@ -8324,7 +8328,11 @@ impl<'db> TypeVarInstance<'db> { Some(TypeVarBoundOrConstraints::UpperBound(ty)) } - #[salsa::tracked(heap_size=ruff_memory_usage::heap_size)] + #[salsa::tracked( + cycle_fn=lazy_bound_or_constraints_cycle_recover, + cycle_initial=lazy_bound_or_constraints_cycle_initial, + heap_size=ruff_memory_usage::heap_size + )] fn lazy_constraints(self, db: &'db dyn Db) -> Option> { let definition = self.definition(db)?; let module = parsed_module(db, definition.file(db)).load(db); @@ -8385,7 +8393,7 @@ impl<'db> TypeVarInstance<'db> { } #[allow(clippy::ref_option)] -fn lazy_bound_cycle_recover<'db>( +fn lazy_bound_or_constraints_cycle_recover<'db>( _db: &'db dyn Db, _value: &Option>, _count: u32, @@ -8394,7 +8402,7 @@ fn lazy_bound_cycle_recover<'db>( salsa::CycleRecoveryAction::Iterate } -fn lazy_bound_cycle_initial<'db>( +fn lazy_bound_or_constraints_cycle_initial<'db>( _db: &'db dyn Db, _self: TypeVarInstance<'db>, ) -> Option> {