From dbab2c43c41f1b371989c10cda9cd9b1f4183124 Mon Sep 17 00:00:00 2001 From: Douglas Creager Date: Thu, 11 Dec 2025 11:21:45 -0500 Subject: [PATCH] better predicate for skipping typevars --- crates/ty_python_semantic/src/types.rs | 14 ++++++ .../ty_python_semantic/src/types/call/bind.rs | 43 +++++++++---------- .../src/types/constraints.rs | 12 ++++++ 3 files changed, 46 insertions(+), 23 deletions(-) diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index e3813d4768..41133c3709 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -1903,6 +1903,20 @@ impl<'db> Type<'db> { self.has_relation_to(db, target, inferable, TypeRelation::Assignability) } + fn when_constraint_set_assignable_to( + self, + db: &'db dyn Db, + target: Type<'db>, + inferable: InferableTypeVars<'_, 'db>, + ) -> ConstraintSet<'db> { + self.has_relation_to( + db, + target, + inferable, + TypeRelation::ConstraintSetAssignability, + ) + } + /// Return `true` if it would be redundant to add `self` to a union that already contains `other`. /// /// See [`TypeRelation::Redundancy`] for more details. diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs index 5c7ba8db82..8bf41a4921 100644 --- a/crates/ty_python_semantic/src/types/call/bind.rs +++ b/crates/ty_python_semantic/src/types/call/bind.rs @@ -3017,7 +3017,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> { FxHashMap::default(); let parameters = self.signature.parameters(); - for (argument_index, adjusted_argument_index, argument, argument_type) in + for (argument_index, adjusted_argument_index, _, argument_type) in self.enumerate_argument_types() { for (parameter_index, variadic_argument_type) in @@ -3029,28 +3029,25 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> { }; // Below, we will possibly perform literal promotion on the types that we infer for - // each typevar. Whether we do so will depend on the variance of the typevar in the - // parameter type that each argument is mapped to. - // - // Note that we do not track the variance for synthetic `self` arguments. Those - // only exist for generic methods of a (possibly generic) class. The class instance - // is fixed for any generic method call, and shouldn't affect literal promotion. - // (This is true even for the constructors of a generic class: a `self` annotation - // on `__init__` limits which specializations the constructor overload applies to, - // but we rely on occurrences of a typevar in other parameters to determine whether - // to promote literals in the specialized generic class type.) - if !matches!(argument, Argument::Synthetic) - && parameter.form == ParameterForm::Value - && expected_type.has_typevar(self.db) - { - for bound_typevar in generic_context_variables.clone() { - let variance = expected_type.variance_of(self.db, bound_typevar); - if variance != TypeVarVariance::Bivariant { - variance_in_arguments - .entry(bound_typevar.identity(self.db)) - .and_modify(|current| *current = current.join(variance)) - .or_insert(variance); - } + // each typevar. Whether we do so will depend on which arguments affect the + // specialization of the typevar, and what variance the typevar has in the + // corresponding parameter type. + let when_assignable = argument_type.when_constraint_set_assignable_to( + self.db, + expected_type, + self.inferable_typevars, + ); + for bound_typevar in generic_context_variables.clone() { + let identity = bound_typevar.identity(self.db); + if !when_assignable.mentions_typevar(self.db, identity) { + continue; + } + let variance = expected_type.variance_of(self.db, bound_typevar); + if variance != TypeVarVariance::Bivariant { + variance_in_arguments + .entry(identity) + .and_modify(|current| *current = current.join(variance)) + .or_insert(variance); } } diff --git a/crates/ty_python_semantic/src/types/constraints.rs b/crates/ty_python_semantic/src/types/constraints.rs index 14b3c32218..ef00627f68 100644 --- a/crates/ty_python_semantic/src/types/constraints.rs +++ b/crates/ty_python_semantic/src/types/constraints.rs @@ -322,6 +322,18 @@ impl<'db> ConstraintSet<'db> { false } + pub(crate) fn mentions_typevar( + self, + db: &'db dyn Db, + bound_typevar: BoundTypeVarIdentity<'db>, + ) -> bool { + let mut found = false; + self.node.for_each_constraint(db, &mut |constraint| { + found |= constraint.typevar(db).identity(db) == bound_typevar; + }); + found + } + /// Returns the constraints under which `lhs` is a subtype of `rhs`, assuming that the /// constraints in this constraint set hold. Panics if neither of the types being compared are /// a typevar. (That case is handled by `Type::has_relation_to`.)