From c60560f39d53eeee5313be18d80bcf4e9587b2e8 Mon Sep 17 00:00:00 2001 From: Douglas Creager Date: Fri, 5 Dec 2025 18:41:37 -0500 Subject: [PATCH] do this at the overloads level --- .../ty_python_semantic/src/types/generics.rs | 20 +-- .../src/types/signatures.rs | 160 +++++++++--------- 2 files changed, 90 insertions(+), 90 deletions(-) diff --git a/crates/ty_python_semantic/src/types/generics.rs b/crates/ty_python_semantic/src/types/generics.rs index db1373bfef..c39fb3872e 100644 --- a/crates/ty_python_semantic/src/types/generics.rs +++ b/crates/ty_python_semantic/src/types/generics.rs @@ -1749,17 +1749,15 @@ impl<'db> SpecializationBuilder<'db> { return Ok(()); }; - for formal_signature in &formal_callable.signatures(self.db).overloads { - for actual_callable in actual_callables.as_slice() { - for actual_signature in &actual_callable.signatures(self.db).overloads { - let when = formal_signature.when_constraint_set_assignable_to( - self.db, - actual_signature, - self.inferable, - ); - self.add_type_mappings_from_constraint_set(when, polarity, &mut f); - } - } + for actual_callable in actual_callables.as_slice() { + let when = formal_callable + .signatures(self.db) + .when_constraint_set_assignable_to( + self.db, + actual_callable.signatures(self.db), + self.inferable, + ); + self.add_type_mappings_from_constraint_set(when, polarity, &mut f); } } diff --git a/crates/ty_python_semantic/src/types/signatures.rs b/crates/ty_python_semantic/src/types/signatures.rs index bf96db71e2..0d4b7aee07 100644 --- a/crates/ty_python_semantic/src/types/signatures.rs +++ b/crates/ty_python_semantic/src/types/signatures.rs @@ -398,6 +398,31 @@ impl<'db> CallableSignature<'db> { ) } + fn signatures_is_single_paramspec( + signatures: &[Signature<'db>], + ) -> Option> { + let [signature] = signatures else { + return None; + }; + signature.parameters.as_paramspec() + } + + pub(crate) fn when_constraint_set_assignable_to( + &self, + db: &'db dyn Db, + other: &Self, + inferable: InferableTypeVars<'_, 'db>, + ) -> ConstraintSet<'db> { + self.has_relation_to_impl( + db, + other, + inferable, + TypeRelation::ConstraintSetAssignability, + &HasRelationToVisitor::default(), + &IsDisjointVisitor::default(), + ) + } + /// Implementation of subtyping and assignability between two, possible overloaded, callable /// types. fn has_relation_to_inner( @@ -409,6 +434,62 @@ impl<'db> CallableSignature<'db> { relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, ) -> ConstraintSet<'db> { + if relation.is_constraint_set_assignability() { + // TODO: Oof, maybe ParamSpec needs to live at CallableSignature, not Signature? + let self_is_single_paramspec = Self::signatures_is_single_paramspec(self_signatures); + let other_is_single_paramspec = Self::signatures_is_single_paramspec(other_signatures); + + match (self_is_single_paramspec, other_is_single_paramspec) { + (Some(self_bound_typevar), Some(other_bound_typevar)) => { + return ConstraintSet::constrain_typevar( + db, + self_bound_typevar, + Type::TypeVar(other_bound_typevar), + Type::TypeVar(other_bound_typevar), + relation, + ); + } + + (Some(self_bound_typevar), None) => { + let upper = + Type::Callable(CallableType::new( + db, + CallableSignature::from_overloads(other_signatures.iter().map( + |signature| Signature::new(signature.parameters().clone(), None), + )), + CallableTypeKind::ParamSpecValue, + )); + return ConstraintSet::constrain_typevar( + db, + self_bound_typevar, + Type::Never, + upper, + relation, + ); + } + + (None, Some(other_bound_typevar)) => { + let lower = + Type::Callable(CallableType::new( + db, + CallableSignature::from_overloads(self_signatures.iter().map( + |signature| Signature::new(signature.parameters().clone(), None), + )), + CallableTypeKind::ParamSpecValue, + )); + return ConstraintSet::constrain_typevar( + db, + other_bound_typevar, + lower, + Type::object(), + relation, + ); + } + + (None, None) => {} + } + } + match (self_signatures, other_signatures) { ([self_signature], [other_signature]) => { // Base case: both callable types contain a single signature. @@ -964,22 +1045,6 @@ impl<'db> Signature<'db> { result } - pub(crate) fn when_constraint_set_assignable_to( - &self, - db: &'db dyn Db, - other: &Signature<'db>, - inferable: InferableTypeVars<'_, 'db>, - ) -> ConstraintSet<'db> { - self.has_relation_to_impl( - db, - other, - inferable, - TypeRelation::ConstraintSetAssignability, - &HasRelationToVisitor::default(), - &IsDisjointVisitor::default(), - ) - } - /// Implementation of subtyping and assignability for signature. fn has_relation_to_impl( &self, @@ -1145,69 +1210,6 @@ impl<'db> Signature<'db> { return result; } - // If either parameter list is a ParamSpec, then we can create a constraint set that holds - // when the ParamSpec is assignable to the other parameter list. - if relation.is_constraint_set_assignability() { - match ( - self.parameters.as_paramspec(), - other.parameters.as_paramspec(), - ) { - (Some(self_bound_typevar), Some(other_bound_typevar)) => { - result.intersect( - db, - ConstraintSet::constrain_typevar( - db, - self_bound_typevar, - Type::TypeVar(other_bound_typevar), - Type::TypeVar(other_bound_typevar), - relation, - ), - ); - return result; - } - - (Some(self_bound_typevar), None) => { - let upper = Type::Callable(CallableType::new( - db, - CallableSignature::single(Signature::new(other.parameters.clone(), None)), - CallableTypeKind::ParamSpecValue, - )); - result.intersect( - db, - ConstraintSet::constrain_typevar( - db, - self_bound_typevar, - Type::Never, - upper, - relation, - ), - ); - return result; - } - - (None, Some(other_bound_typevar)) => { - let lower = Type::Callable(CallableType::new( - db, - CallableSignature::single(Signature::new(self.parameters.clone(), None)), - CallableTypeKind::ParamSpecValue, - )); - result.intersect( - db, - ConstraintSet::constrain_typevar( - db, - other_bound_typevar, - lower, - Type::object(), - relation, - ), - ); - return result; - } - - (None, None) => {} - } - } - let mut parameters = ParametersZip { current_self: None, current_other: None,