diff --git a/crates/ty_python_semantic/resources/mdtest/generics/pep695/paramspec.md b/crates/ty_python_semantic/resources/mdtest/generics/pep695/paramspec.md index 2ace9d8336..3a91454e12 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/pep695/paramspec.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/pep695/paramspec.md @@ -711,6 +711,9 @@ class Child2(Parent): ### One `ParamSpec` not inferable +Here, `P` is in a non-inferable position while `Q` is inferable. So, they are not considered +assignable. + ```py from typing import Callable diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 0026a4b4f7..ca06e8ca79 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -2174,23 +2174,6 @@ impl<'db> Type<'db> { ConstraintSet::from(true) } - // Two ParamSpec attributes (P.args/P.kwargs and Q.args/Q.kwargs) are assignable to - // each other if they have the same attribute kind and both underlying ParamSpecs are - // inferable. - (Type::TypeVar(lhs_bound_typevar), Type::TypeVar(rhs_bound_typevar)) - if lhs_bound_typevar.paramspec_attr(db).is_some() - && lhs_bound_typevar.paramspec_attr(db) - == rhs_bound_typevar.paramspec_attr(db) - && lhs_bound_typevar - .without_paramspec_attr(db) - .is_inferable(db, inferable) - && rhs_bound_typevar - .without_paramspec_attr(db) - .is_inferable(db, inferable) => - { - ConstraintSet::from(true) - } - // `type[T]` is a subtype of the class object `A` if every instance of `T` is a subtype of an instance // of `A`, and vice versa. (Type::SubclassOf(subclass_of), _) diff --git a/crates/ty_python_semantic/src/types/signatures.rs b/crates/ty_python_semantic/src/types/signatures.rs index 45c3f81de2..76fe3a35d4 100644 --- a/crates/ty_python_semantic/src/types/signatures.rs +++ b/crates/ty_python_semantic/src/types/signatures.rs @@ -1072,6 +1072,29 @@ impl<'db> Signature<'db> { let mut check_types = |type1: Option>, type2: Option>| { let type1 = type1.unwrap_or(Type::unknown()); let type2 = type2.unwrap_or(Type::unknown()); + + match (type1, type2) { + // This is a special case where the _same_ components of two different `ParamSpec` + // type variables are assignable to each other when they're both in an inferable + // position. + // + // `ParamSpec` type variables can only occur in parameter lists so this special case + // is present here instead of in `Type::has_relation_to_impl`. + (Type::TypeVar(typevar1), Type::TypeVar(typevar2)) + if typevar1.paramspec_attr(db).is_some() + && typevar1.paramspec_attr(db) == typevar2.paramspec_attr(db) + && typevar1 + .without_paramspec_attr(db) + .is_inferable(db, inferable) + && typevar2 + .without_paramspec_attr(db) + .is_inferable(db, inferable) => + { + return true; + } + _ => {} + } + !result .intersect( db,