diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 777a2f2ee8..3f8702c858 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -53,8 +53,8 @@ use crate::types::function::{ DataclassTransformerParams, FunctionSpans, FunctionType, KnownFunction, }; use crate::types::generics::{ - GenericContext, PartialSpecialization, Specialization, bind_typevar, typing_self, - walk_generic_context, + GenericContext, InferableTypeVars, PartialSpecialization, Specialization, bind_typevar, + typing_self, walk_generic_context, }; use crate::types::infer::infer_unpack_types; use crate::types::mro::{Mro, MroError, MroIterator}; @@ -571,21 +571,27 @@ impl<'db> PropertyInstanceType<'db> { } } - fn when_equivalent_to(self, db: &'db dyn Db, other: Self) -> ConstraintSet<'db> { - self.is_equivalent_to_impl(db, other, &IsEquivalentVisitor::default()) + fn when_equivalent_to( + self, + db: &'db dyn Db, + other: Self, + inferable: InferableTypeVars<'_, 'db>, + ) -> ConstraintSet<'db> { + self.is_equivalent_to_impl(db, other, inferable, &IsEquivalentVisitor::default()) } fn is_equivalent_to_impl( self, db: &'db dyn Db, other: Self, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { let getter_equivalence = if let Some(getter) = self.getter(db) { let Some(other_getter) = other.getter(db) else { return ConstraintSet::from(false); }; - getter.is_equivalent_to_impl(db, other_getter, visitor) + getter.is_equivalent_to_impl(db, other_getter, inferable, visitor) } else { if other.getter(db).is_some() { return ConstraintSet::from(false); @@ -598,7 +604,7 @@ impl<'db> PropertyInstanceType<'db> { let Some(other_setter) = other.setter(db) else { return ConstraintSet::from(false); }; - setter.is_equivalent_to_impl(db, other_setter, visitor) + setter.is_equivalent_to_impl(db, other_setter, inferable, visitor) } else { if other.setter(db).is_some() { return ConstraintSet::from(false); @@ -1197,9 +1203,18 @@ impl<'db> Type<'db> { } /// Remove the union elements that are not related to `target`. - pub(crate) fn filter_disjoint_elements(self, db: &'db dyn Db, target: Type<'db>) -> Type<'db> { + pub(crate) fn filter_disjoint_elements( + self, + db: &'db dyn Db, + target: Type<'db>, + inferable: InferableTypeVars<'_, 'db>, + ) -> Type<'db> { if let Type::Union(union) = self { - union.filter(db, |elem| !elem.is_disjoint_from(db, target)) + union.filter(db, |elem| { + !elem + .when_disjoint_from(db, target, inferable) + .is_always_satisfied() + }) } else { self } @@ -1495,29 +1510,41 @@ impl<'db> Type<'db> { /// /// See [`TypeRelation::Subtyping`] for more details. pub(crate) fn is_subtype_of(self, db: &'db dyn Db, target: Type<'db>) -> bool { - self.when_subtype_of(db, target).is_always_satisfied() + self.when_subtype_of(db, target, InferableTypeVars::None) + .is_always_satisfied() } - fn when_subtype_of(self, db: &'db dyn Db, target: Type<'db>) -> ConstraintSet<'db> { - self.has_relation_to(db, target, TypeRelation::Subtyping) + fn when_subtype_of( + self, + db: &'db dyn Db, + target: Type<'db>, + inferable: InferableTypeVars<'_, 'db>, + ) -> ConstraintSet<'db> { + self.has_relation_to(db, target, inferable, TypeRelation::Subtyping) } /// Return true if this type is assignable to type `target`. /// /// See [`TypeRelation::Assignability`] for more details. pub(crate) fn is_assignable_to(self, db: &'db dyn Db, target: Type<'db>) -> bool { - self.when_assignable_to(db, target).is_always_satisfied() + self.when_assignable_to(db, target, InferableTypeVars::None) + .is_always_satisfied() } - fn when_assignable_to(self, db: &'db dyn Db, target: Type<'db>) -> ConstraintSet<'db> { - self.has_relation_to(db, target, TypeRelation::Assignability) + fn when_assignable_to( + self, + db: &'db dyn Db, + target: Type<'db>, + inferable: InferableTypeVars<'_, 'db>, + ) -> ConstraintSet<'db> { + self.has_relation_to(db, target, inferable, TypeRelation::Assignability) } /// Return `true` if it would be redundant to add `self` to a union that already contains `other`. /// /// See [`TypeRelation::Redundancy`] for more details. pub(crate) fn is_redundant_with(self, db: &'db dyn Db, other: Type<'db>) -> bool { - self.has_relation_to(db, other, TypeRelation::Redundancy) + self.has_relation_to(db, other, InferableTypeVars::None, TypeRelation::Redundancy) .is_always_satisfied() } @@ -1525,11 +1552,13 @@ impl<'db> Type<'db> { self, db: &'db dyn Db, target: Type<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation, ) -> ConstraintSet<'db> { self.has_relation_to_impl( db, target, + inferable, relation, &HasRelationToVisitor::default(), &IsDisjointVisitor::default(), @@ -1540,6 +1569,7 @@ impl<'db> Type<'db> { self, db: &'db dyn Db, target: Type<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -1579,6 +1609,7 @@ impl<'db> Type<'db> { self_alias.value_type(db).has_relation_to_impl( db, target, + inferable, relation, relation_visitor, disjointness_visitor, @@ -1591,6 +1622,7 @@ impl<'db> Type<'db> { self.has_relation_to_impl( db, target_alias.value_type(db), + inferable, relation, relation_visitor, disjointness_visitor, @@ -1607,6 +1639,7 @@ impl<'db> Type<'db> { field.default_type(db).has_relation_to_impl( db, right, + inferable, relation, relation_visitor, disjointness_visitor, @@ -1689,6 +1722,7 @@ impl<'db> Type<'db> { .has_relation_to_impl( db, target, + inferable, relation, relation_visitor, disjointness_visitor, @@ -1698,6 +1732,7 @@ impl<'db> Type<'db> { constraint.has_relation_to_impl( db, target, + inferable, relation, relation_visitor, disjointness_visitor, @@ -1719,6 +1754,7 @@ impl<'db> Type<'db> { self.has_relation_to_impl( db, *constraint, + inferable, relation, relation_visitor, disjointness_visitor, @@ -1739,6 +1775,7 @@ impl<'db> Type<'db> { self.has_relation_to_impl( db, *constraint, + inferable, relation, relation_visitor, disjointness_visitor, @@ -1763,6 +1800,7 @@ impl<'db> Type<'db> { elem_ty.has_relation_to_impl( db, target, + inferable, relation, relation_visitor, disjointness_visitor, @@ -1773,6 +1811,7 @@ impl<'db> Type<'db> { self.has_relation_to_impl( db, elem_ty, + inferable, relation, relation_visitor, disjointness_visitor, @@ -1789,6 +1828,7 @@ impl<'db> Type<'db> { self.has_relation_to_impl( db, pos_ty, + inferable, relation, relation_visitor, disjointness_visitor, @@ -1820,6 +1860,7 @@ impl<'db> Type<'db> { self_ty.is_disjoint_from_impl( db, neg_ty, + inferable, disjointness_visitor, relation_visitor, ) @@ -1831,6 +1872,7 @@ impl<'db> Type<'db> { elem_ty.has_relation_to_impl( db, target, + inferable, relation, relation_visitor, disjointness_visitor, @@ -1852,6 +1894,7 @@ impl<'db> Type<'db> { .has_relation_to_impl( db, bound, + inferable, relation, relation_visitor, disjointness_visitor, @@ -1865,6 +1908,7 @@ impl<'db> Type<'db> { self.has_relation_to_impl( db, bound, + inferable, relation, relation_visitor, disjointness_visitor, @@ -1886,7 +1930,7 @@ impl<'db> Type<'db> { (left, Type::AlwaysTruthy) => ConstraintSet::from(left.bool(db).is_always_true()), // Currently, the only supertype of `AlwaysFalsy` and `AlwaysTruthy` is the universal set (object instance). (Type::AlwaysFalsy | Type::AlwaysTruthy, _) => { - target.when_equivalent_to(db, Type::object()) + target.when_equivalent_to(db, Type::object(), inferable) } // These clauses handle type variants that include function literals. A function @@ -1895,18 +1939,33 @@ impl<'db> Type<'db> { // applied to the signature. Different specializations of the same function literal are // only subtypes of each other if they result in the same signature. (Type::FunctionLiteral(self_function), Type::FunctionLiteral(target_function)) => { - self_function.has_relation_to_impl(db, target_function, relation, relation_visitor) + self_function.has_relation_to_impl( + db, + target_function, + inferable, + relation, + relation_visitor, + disjointness_visitor, + ) } (Type::BoundMethod(self_method), Type::BoundMethod(target_method)) => self_method .has_relation_to_impl( db, target_method, + inferable, relation, relation_visitor, disjointness_visitor, ), (Type::KnownBoundMethod(self_method), Type::KnownBoundMethod(target_method)) => { - self_method.has_relation_to_impl(db, target_method, relation, relation_visitor) + self_method.has_relation_to_impl( + db, + target_method, + inferable, + relation, + relation_visitor, + disjointness_visitor, + ) } // No literal type is a subtype of any other literal type, unless they are the same @@ -1935,6 +1994,7 @@ impl<'db> Type<'db> { self_callable.has_relation_to_impl( db, other_callable, + inferable, relation, relation_visitor, disjointness_visitor, @@ -1946,6 +2006,7 @@ impl<'db> Type<'db> { callable.has_relation_to_impl( db, target, + inferable, relation, relation_visitor, disjointness_visitor, @@ -1958,6 +2019,7 @@ impl<'db> Type<'db> { self.satisfies_protocol( db, protocol, + inferable, relation, relation_visitor, disjointness_visitor, @@ -2001,6 +2063,7 @@ impl<'db> Type<'db> { instance.has_relation_to_impl( db, target, + inferable, relation, relation_visitor, disjointness_visitor, @@ -2008,16 +2071,36 @@ impl<'db> Type<'db> { }), // The same reasoning applies for these special callable types: - (Type::BoundMethod(_), _) => KnownClass::MethodType - .to_instance(db) - .has_relation_to_impl(db, target, relation, relation_visitor, disjointness_visitor), - (Type::KnownBoundMethod(method), _) => method - .class() - .to_instance(db) - .has_relation_to_impl(db, target, relation, relation_visitor, disjointness_visitor), + (Type::BoundMethod(_), _) => { + KnownClass::MethodType.to_instance(db).has_relation_to_impl( + db, + target, + inferable, + relation, + relation_visitor, + disjointness_visitor, + ) + } + (Type::KnownBoundMethod(method), _) => { + method.class().to_instance(db).has_relation_to_impl( + db, + target, + inferable, + relation, + relation_visitor, + disjointness_visitor, + ) + } (Type::WrapperDescriptor(_), _) => KnownClass::WrapperDescriptorType .to_instance(db) - .has_relation_to_impl(db, target, relation, relation_visitor, disjointness_visitor), + .has_relation_to_impl( + db, + target, + inferable, + relation, + relation_visitor, + disjointness_visitor, + ), (Type::DataclassDecorator(_) | Type::DataclassTransformer(_), _) => { // TODO: Implement subtyping using an equivalent `Callable` type. @@ -2030,6 +2113,7 @@ impl<'db> Type<'db> { .has_relation_to_impl( db, right.return_type(db), + inferable, relation, relation_visitor, disjointness_visitor, @@ -2038,6 +2122,7 @@ impl<'db> Type<'db> { right.return_type(db).has_relation_to_impl( db, left.return_type(db), + inferable, relation, relation_visitor, disjointness_visitor, @@ -2048,6 +2133,7 @@ impl<'db> Type<'db> { (Type::TypeIs(_), _) => KnownClass::Bool.to_instance(db).has_relation_to_impl( db, target, + inferable, relation, relation_visitor, disjointness_visitor, @@ -2060,6 +2146,7 @@ impl<'db> Type<'db> { .has_relation_to_impl( db, target, + inferable, relation, relation_visitor, disjointness_visitor, @@ -2068,10 +2155,13 @@ impl<'db> Type<'db> { (Type::Callable(_), _) => ConstraintSet::from(false), - (Type::BoundSuper(_), Type::BoundSuper(_)) => self.when_equivalent_to(db, target), + (Type::BoundSuper(_), Type::BoundSuper(_)) => { + self.when_equivalent_to(db, target, inferable) + } (Type::BoundSuper(_), _) => KnownClass::Super.to_instance(db).has_relation_to_impl( db, target, + inferable, relation, relation_visitor, disjointness_visitor, @@ -2086,6 +2176,7 @@ impl<'db> Type<'db> { ClassType::NonGeneric(class).has_relation_to_impl( db, subclass_of_class, + inferable, relation, relation_visitor, disjointness_visitor, @@ -2099,6 +2190,7 @@ impl<'db> Type<'db> { ClassType::Generic(alias).has_relation_to_impl( db, subclass_of_class, + inferable, relation, relation_visitor, disjointness_visitor, @@ -2111,6 +2203,7 @@ impl<'db> Type<'db> { self_subclass_ty.has_relation_to_impl( db, target_subclass_ty, + inferable, relation, relation_visitor, disjointness_visitor, @@ -2120,12 +2213,26 @@ impl<'db> Type<'db> { // `Literal[str]` is a subtype of `type` because the `str` class object is an instance of its metaclass `type`. // `Literal[abc.ABC]` is a subtype of `abc.ABCMeta` because the `abc.ABC` class object // is an instance of its metaclass `abc.ABCMeta`. - (Type::ClassLiteral(class), _) => class - .metaclass_instance_type(db) - .has_relation_to_impl(db, target, relation, relation_visitor, disjointness_visitor), + (Type::ClassLiteral(class), _) => { + class.metaclass_instance_type(db).has_relation_to_impl( + db, + target, + inferable, + relation, + relation_visitor, + disjointness_visitor, + ) + } (Type::GenericAlias(alias), _) => ClassType::from(alias) .metaclass_instance_type(db) - .has_relation_to_impl(db, target, relation, relation_visitor, disjointness_visitor), + .has_relation_to_impl( + db, + target, + inferable, + relation, + relation_visitor, + disjointness_visitor, + ), // `type[Any]` is a subtype of `type[object]`, and is assignable to any `type[...]` (Type::SubclassOf(subclass_of_ty), other) if subclass_of_ty.is_dynamic() => { @@ -2134,6 +2241,7 @@ impl<'db> Type<'db> { .has_relation_to_impl( db, other, + inferable, relation, relation_visitor, disjointness_visitor, @@ -2143,6 +2251,7 @@ impl<'db> Type<'db> { other.has_relation_to_impl( db, KnownClass::Type.to_instance(db), + inferable, relation, relation_visitor, disjointness_visitor, @@ -2158,6 +2267,7 @@ impl<'db> Type<'db> { other.has_relation_to_impl( db, KnownClass::Type.to_instance(db), + inferable, relation, relation_visitor, disjointness_visitor, @@ -2176,7 +2286,14 @@ impl<'db> Type<'db> { .into_class() .map(|class| class.metaclass_instance_type(db)) .unwrap_or_else(|| KnownClass::Type.to_instance(db)) - .has_relation_to_impl(db, target, relation, relation_visitor, disjointness_visitor), + .has_relation_to_impl( + db, + target, + inferable, + relation, + relation_visitor, + disjointness_visitor, + ), // For example: `Type::SpecialForm(SpecialFormType::Type)` is a subtype of `Type::NominalInstance(_SpecialForm)`, // because `Type::SpecialForm(SpecialFormType::Type)` is a set with exactly one runtime value in it @@ -2184,6 +2301,7 @@ impl<'db> Type<'db> { (Type::SpecialForm(left), right) => left.instance_fallback(db).has_relation_to_impl( db, right, + inferable, relation, relation_visitor, disjointness_visitor, @@ -2192,6 +2310,7 @@ impl<'db> Type<'db> { (Type::KnownInstance(left), right) => left.instance_fallback(db).has_relation_to_impl( db, right, + inferable, relation, relation_visitor, disjointness_visitor, @@ -2204,6 +2323,7 @@ impl<'db> Type<'db> { self_instance.has_relation_to_impl( db, target_instance, + inferable, relation, relation_visitor, disjointness_visitor, @@ -2211,12 +2331,20 @@ impl<'db> Type<'db> { }) } - (Type::PropertyInstance(_), _) => KnownClass::Property - .to_instance(db) - .has_relation_to_impl(db, target, relation, relation_visitor, disjointness_visitor), + (Type::PropertyInstance(_), _) => { + KnownClass::Property.to_instance(db).has_relation_to_impl( + db, + target, + inferable, + relation, + relation_visitor, + disjointness_visitor, + ) + } (_, Type::PropertyInstance(_)) => self.has_relation_to_impl( db, KnownClass::Property.to_instance(db), + inferable, relation, relation_visitor, disjointness_visitor, @@ -2243,17 +2371,24 @@ impl<'db> Type<'db> { /// /// [equivalent to]: https://typing.python.org/en/latest/spec/glossary.html#term-equivalent pub(crate) fn is_equivalent_to(self, db: &'db dyn Db, other: Type<'db>) -> bool { - self.when_equivalent_to(db, other).is_always_satisfied() + self.when_equivalent_to(db, other, InferableTypeVars::None) + .is_always_satisfied() } - fn when_equivalent_to(self, db: &'db dyn Db, other: Type<'db>) -> ConstraintSet<'db> { - self.is_equivalent_to_impl(db, other, &IsEquivalentVisitor::default()) + fn when_equivalent_to( + self, + db: &'db dyn Db, + other: Type<'db>, + inferable: InferableTypeVars<'_, 'db>, + ) -> ConstraintSet<'db> { + self.is_equivalent_to_impl(db, other, inferable, &IsEquivalentVisitor::default()) } pub(crate) fn is_equivalent_to_impl( self, db: &'db dyn Db, other: Type<'db>, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { if self == other { @@ -2282,44 +2417,44 @@ impl<'db> Type<'db> { (Type::TypeAlias(self_alias), _) => { let self_alias_ty = self_alias.value_type(db).normalized(db); visitor.visit((self_alias_ty, other), || { - self_alias_ty.is_equivalent_to_impl(db, other, visitor) + self_alias_ty.is_equivalent_to_impl(db, other, inferable, visitor) }) } (_, Type::TypeAlias(other_alias)) => { let other_alias_ty = other_alias.value_type(db).normalized(db); visitor.visit((self, other_alias_ty), || { - self.is_equivalent_to_impl(db, other_alias_ty, visitor) + self.is_equivalent_to_impl(db, other_alias_ty, inferable, visitor) }) } (Type::NominalInstance(first), Type::NominalInstance(second)) => { - first.is_equivalent_to_impl(db, second, visitor) + first.is_equivalent_to_impl(db, second, inferable, visitor) } (Type::Union(first), Type::Union(second)) => { - first.is_equivalent_to_impl(db, second, visitor) + first.is_equivalent_to_impl(db, second, inferable, visitor) } (Type::Intersection(first), Type::Intersection(second)) => { - first.is_equivalent_to_impl(db, second, visitor) + first.is_equivalent_to_impl(db, second, inferable, visitor) } (Type::FunctionLiteral(self_function), Type::FunctionLiteral(target_function)) => { - self_function.is_equivalent_to_impl(db, target_function, visitor) + self_function.is_equivalent_to_impl(db, target_function, inferable, visitor) } (Type::BoundMethod(self_method), Type::BoundMethod(target_method)) => { - self_method.is_equivalent_to_impl(db, target_method, visitor) + self_method.is_equivalent_to_impl(db, target_method, inferable, visitor) } (Type::KnownBoundMethod(self_method), Type::KnownBoundMethod(target_method)) => { - self_method.is_equivalent_to_impl(db, target_method, visitor) + self_method.is_equivalent_to_impl(db, target_method, inferable, visitor) } (Type::Callable(first), Type::Callable(second)) => { - first.is_equivalent_to_impl(db, second, visitor) + first.is_equivalent_to_impl(db, second, inferable, visitor) } (Type::ProtocolInstance(first), Type::ProtocolInstance(second)) => { - first.is_equivalent_to_impl(db, second, visitor) + first.is_equivalent_to_impl(db, second, inferable, visitor) } (Type::ProtocolInstance(protocol), nominal @ Type::NominalInstance(n)) | (nominal @ Type::NominalInstance(n), Type::ProtocolInstance(protocol)) => { @@ -2336,7 +2471,7 @@ impl<'db> Type<'db> { } (Type::PropertyInstance(left), Type::PropertyInstance(right)) => { - left.is_equivalent_to_impl(db, right, visitor) + left.is_equivalent_to_impl(db, right, inferable, visitor) } _ => ConstraintSet::from(false), @@ -2359,13 +2494,20 @@ impl<'db> Type<'db> { /// This function aims to have no false positives, but might return wrong /// `false` answers in some cases. pub(crate) fn is_disjoint_from(self, db: &'db dyn Db, other: Type<'db>) -> bool { - self.when_disjoint_from(db, other).is_always_satisfied() + self.when_disjoint_from(db, other, InferableTypeVars::None) + .is_always_satisfied() } - fn when_disjoint_from(self, db: &'db dyn Db, other: Type<'db>) -> ConstraintSet<'db> { + fn when_disjoint_from( + self, + db: &'db dyn Db, + other: Type<'db>, + inferable: InferableTypeVars<'_, 'db>, + ) -> ConstraintSet<'db> { self.is_disjoint_from_impl( db, other, + inferable, &IsDisjointVisitor::default(), &HasRelationToVisitor::default(), ) @@ -2375,6 +2517,7 @@ impl<'db> Type<'db> { self, db: &'db dyn Db, other: Type<'db>, + inferable: InferableTypeVars<'_, 'db>, disjointness_visitor: &IsDisjointVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>, ) -> ConstraintSet<'db> { @@ -2382,6 +2525,7 @@ impl<'db> Type<'db> { db: &'db dyn Db, protocol: ProtocolInstanceType<'db>, other: Type<'db>, + inferable: InferableTypeVars<'_, 'db>, disjointness_visitor: &IsDisjointVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>, ) -> ConstraintSet<'db> { @@ -2394,6 +2538,7 @@ impl<'db> Type<'db> { member.has_disjoint_type_from( db, attribute_type, + inferable, disjointness_visitor, relation_visitor, ) @@ -2412,6 +2557,7 @@ impl<'db> Type<'db> { self_alias_ty.is_disjoint_from_impl( db, other, + inferable, disjointness_visitor, relation_visitor, ) @@ -2424,6 +2570,7 @@ impl<'db> Type<'db> { self.is_disjoint_from_impl( db, other_alias_ty, + inferable, disjointness_visitor, relation_visitor, ) @@ -2462,12 +2609,19 @@ impl<'db> Type<'db> { match bound_typevar.typevar(db).bound_or_constraints(db) { None => ConstraintSet::from(false), Some(TypeVarBoundOrConstraints::UpperBound(bound)) => bound - .is_disjoint_from_impl(db, other, disjointness_visitor, relation_visitor), + .is_disjoint_from_impl( + db, + other, + inferable, + disjointness_visitor, + relation_visitor, + ), Some(TypeVarBoundOrConstraints::Constraints(constraints)) => { constraints.elements(db).iter().when_all(db, |constraint| { constraint.is_disjoint_from_impl( db, other, + inferable, disjointness_visitor, relation_visitor, ) @@ -2481,7 +2635,13 @@ impl<'db> Type<'db> { (Type::Union(union), other) | (other, Type::Union(union)) => { union.elements(db).iter().when_all(db, |e| { - e.is_disjoint_from_impl(db, other, disjointness_visitor, relation_visitor) + e.is_disjoint_from_impl( + db, + other, + inferable, + disjointness_visitor, + relation_visitor, + ) }) } @@ -2497,6 +2657,7 @@ impl<'db> Type<'db> { p.is_disjoint_from_impl( db, other, + inferable, disjointness_visitor, relation_visitor, ) @@ -2506,6 +2667,7 @@ impl<'db> Type<'db> { p.is_disjoint_from_impl( db, self, + inferable, disjointness_visitor, relation_visitor, ) @@ -2524,6 +2686,7 @@ impl<'db> Type<'db> { p.is_disjoint_from_impl( db, non_intersection, + inferable, disjointness_visitor, relation_visitor, ) @@ -2534,6 +2697,7 @@ impl<'db> Type<'db> { non_intersection.has_relation_to_impl( db, neg_ty, + inferable, TypeRelation::Subtyping, relation_visitor, disjointness_visitor, @@ -2617,7 +2781,7 @@ impl<'db> Type<'db> { (Type::ProtocolInstance(left), Type::ProtocolInstance(right)) => disjointness_visitor .visit((self, other), || { - left.is_disjoint_from_impl(db, right, disjointness_visitor) + left.is_disjoint_from_impl(db, right, inferable, disjointness_visitor) }), (Type::ProtocolInstance(protocol), Type::SpecialForm(special_form)) @@ -2627,6 +2791,7 @@ impl<'db> Type<'db> { db, protocol, special_form.instance_fallback(db), + inferable, disjointness_visitor, relation_visitor, ) @@ -2640,6 +2805,7 @@ impl<'db> Type<'db> { db, protocol, known_instance.instance_fallback(db), + inferable, disjointness_visitor, relation_visitor, ) @@ -2703,6 +2869,7 @@ impl<'db> Type<'db> { db, protocol, ty, + inferable, disjointness_visitor, relation_visitor, ) @@ -2720,6 +2887,7 @@ impl<'db> Type<'db> { db, protocol, nominal, + inferable, disjointness_visitor, relation_visitor, ) @@ -2734,6 +2902,7 @@ impl<'db> Type<'db> { Place::Type(attribute_type, _) => member.has_disjoint_type_from( db, attribute_type, + inferable, disjointness_visitor, relation_visitor, ), @@ -2758,25 +2927,37 @@ impl<'db> Type<'db> { match subclass_of_ty.subclass_of() { SubclassOfInner::Dynamic(_) => ConstraintSet::from(false), SubclassOfInner::Class(class_a) => ClassType::from(alias_b) - .when_subclass_of(db, class_a) + .when_subclass_of(db, class_a, inferable) .negate(db), } } (Type::SubclassOf(left), Type::SubclassOf(right)) => { - left.is_disjoint_from_impl(db, right, disjointness_visitor) + left.is_disjoint_from_impl(db, right, inferable, disjointness_visitor) } // for `type[Any]`/`type[Unknown]`/`type[Todo]`, we know the type cannot be any larger than `type`, // so although the type is dynamic we can still determine disjointedness in some situations (Type::SubclassOf(subclass_of_ty), other) | (other, Type::SubclassOf(subclass_of_ty)) => match subclass_of_ty.subclass_of() { - SubclassOfInner::Dynamic(_) => KnownClass::Type - .to_instance(db) - .is_disjoint_from_impl(db, other, disjointness_visitor, relation_visitor), - SubclassOfInner::Class(class) => class - .metaclass_instance_type(db) - .is_disjoint_from_impl(db, other, disjointness_visitor, relation_visitor), + SubclassOfInner::Dynamic(_) => { + KnownClass::Type.to_instance(db).is_disjoint_from_impl( + db, + other, + inferable, + disjointness_visitor, + relation_visitor, + ) + } + SubclassOfInner::Class(class) => { + class.metaclass_instance_type(db).is_disjoint_from_impl( + db, + other, + inferable, + disjointness_visitor, + relation_visitor, + ) + } }, (Type::SpecialForm(special_form), Type::NominalInstance(instance)) @@ -2843,6 +3024,7 @@ impl<'db> Type<'db> { .has_relation_to_impl( db, instance, + inferable, TypeRelation::Subtyping, relation_visitor, disjointness_visitor, @@ -2857,7 +3039,7 @@ impl<'db> Type<'db> { (Type::ClassLiteral(class), instance @ Type::NominalInstance(_)) | (instance @ Type::NominalInstance(_), Type::ClassLiteral(class)) => class .metaclass_instance_type(db) - .when_subtype_of(db, instance) + .when_subtype_of(db, instance, inferable) .negate(db), (Type::GenericAlias(alias), instance @ Type::NominalInstance(_)) | (instance @ Type::NominalInstance(_), Type::GenericAlias(alias)) => { @@ -2866,6 +3048,7 @@ impl<'db> Type<'db> { .has_relation_to_impl( db, instance, + inferable, TypeRelation::Subtyping, relation_visitor, disjointness_visitor, @@ -2884,12 +3067,19 @@ impl<'db> Type<'db> { (Type::BoundMethod(_), other) | (other, Type::BoundMethod(_)) => KnownClass::MethodType .to_instance(db) - .is_disjoint_from_impl(db, other, disjointness_visitor, relation_visitor), + .is_disjoint_from_impl( + db, + other, + inferable, + disjointness_visitor, + relation_visitor, + ), (Type::KnownBoundMethod(method), other) | (other, Type::KnownBoundMethod(method)) => { method.class().to_instance(db).is_disjoint_from_impl( db, other, + inferable, disjointness_visitor, relation_visitor, ) @@ -2898,7 +3088,13 @@ impl<'db> Type<'db> { (Type::WrapperDescriptor(_), other) | (other, Type::WrapperDescriptor(_)) => { KnownClass::WrapperDescriptorType .to_instance(db) - .is_disjoint_from_impl(db, other, disjointness_visitor, relation_visitor) + .is_disjoint_from_impl( + db, + other, + inferable, + disjointness_visitor, + relation_visitor, + ) } (Type::Callable(_) | Type::FunctionLiteral(_), Type::Callable(_)) @@ -2946,6 +3142,7 @@ impl<'db> Type<'db> { .has_relation_to_impl( db, CallableType::unknown(db), + inferable, TypeRelation::Assignability, relation_visitor, disjointness_visitor, @@ -2971,6 +3168,7 @@ impl<'db> Type<'db> { other.is_disjoint_from_impl( db, KnownClass::ModuleType.to_instance(db), + inferable, disjointness_visitor, relation_visitor, ) @@ -2978,24 +3176,37 @@ impl<'db> Type<'db> { (Type::NominalInstance(left), Type::NominalInstance(right)) => disjointness_visitor .visit((self, other), || { - left.is_disjoint_from_impl(db, right, disjointness_visitor, relation_visitor) + left.is_disjoint_from_impl( + db, + right, + inferable, + disjointness_visitor, + relation_visitor, + ) }), (Type::PropertyInstance(_), other) | (other, Type::PropertyInstance(_)) => { KnownClass::Property.to_instance(db).is_disjoint_from_impl( db, other, + inferable, disjointness_visitor, relation_visitor, ) } (Type::BoundSuper(_), Type::BoundSuper(_)) => { - self.when_equivalent_to(db, other).negate(db) + self.when_equivalent_to(db, other, inferable).negate(db) + } + (Type::BoundSuper(_), other) | (other, Type::BoundSuper(_)) => { + KnownClass::Super.to_instance(db).is_disjoint_from_impl( + db, + other, + inferable, + disjointness_visitor, + relation_visitor, + ) } - (Type::BoundSuper(_), other) | (other, Type::BoundSuper(_)) => KnownClass::Super - .to_instance(db) - .is_disjoint_from_impl(db, other, disjointness_visitor, relation_visitor), } } @@ -9819,6 +10030,7 @@ impl<'db> BoundMethodType<'db> { self, db: &'db dyn Db, other: Self, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -9828,11 +10040,19 @@ impl<'db> BoundMethodType<'db> { // differently), and of the bound self parameter (taking care that parameters, including a // bound self parameter, are contravariant.) self.function(db) - .has_relation_to_impl(db, other.function(db), relation, relation_visitor) + .has_relation_to_impl( + db, + other.function(db), + inferable, + relation, + relation_visitor, + disjointness_visitor, + ) .and(db, || { other.self_instance(db).has_relation_to_impl( db, self.self_instance(db), + inferable, relation, relation_visitor, disjointness_visitor, @@ -9844,14 +10064,18 @@ impl<'db> BoundMethodType<'db> { self, db: &'db dyn Db, other: Self, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { self.function(db) - .is_equivalent_to_impl(db, other.function(db), visitor) + .is_equivalent_to_impl(db, other.function(db), inferable, visitor) .and(db, || { - other - .self_instance(db) - .is_equivalent_to_impl(db, self.self_instance(db), visitor) + other.self_instance(db).is_equivalent_to_impl( + db, + self.self_instance(db), + inferable, + visitor, + ) }) } } @@ -9973,6 +10197,7 @@ impl<'db> CallableType<'db> { self, db: &'db dyn Db, other: Self, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -9983,6 +10208,7 @@ impl<'db> CallableType<'db> { self.signatures(db).has_relation_to_impl( db, other.signatures(db), + inferable, relation, relation_visitor, disjointness_visitor, @@ -9996,6 +10222,7 @@ impl<'db> CallableType<'db> { self, db: &'db dyn Db, other: Self, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { if self == other { @@ -10004,7 +10231,7 @@ impl<'db> CallableType<'db> { ConstraintSet::from(self.is_function_like(db) == other.is_function_like(db)).and(db, || { self.signatures(db) - .is_equivalent_to_impl(db, other.signatures(db), visitor) + .is_equivalent_to_impl(db, other.signatures(db), inferable, visitor) }) } } @@ -10065,19 +10292,35 @@ impl<'db> KnownBoundMethodType<'db> { self, db: &'db dyn Db, other: Self, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation, - visitor: &HasRelationToVisitor<'db>, + relation_visitor: &HasRelationToVisitor<'db>, + disjointness_visitor: &IsDisjointVisitor<'db>, ) -> ConstraintSet<'db> { match (self, other) { ( KnownBoundMethodType::FunctionTypeDunderGet(self_function), KnownBoundMethodType::FunctionTypeDunderGet(other_function), - ) => self_function.has_relation_to_impl(db, other_function, relation, visitor), + ) => self_function.has_relation_to_impl( + db, + other_function, + inferable, + relation, + relation_visitor, + disjointness_visitor, + ), ( KnownBoundMethodType::FunctionTypeDunderCall(self_function), KnownBoundMethodType::FunctionTypeDunderCall(other_function), - ) => self_function.has_relation_to_impl(db, other_function, relation, visitor), + ) => self_function.has_relation_to_impl( + db, + other_function, + inferable, + relation, + relation_visitor, + disjointness_visitor, + ), ( KnownBoundMethodType::PropertyDunderGet(self_property), @@ -10086,7 +10329,7 @@ impl<'db> KnownBoundMethodType<'db> { | ( KnownBoundMethodType::PropertyDunderSet(self_property), KnownBoundMethodType::PropertyDunderSet(other_property), - ) => self_property.when_equivalent_to(db, other_property), + ) => self_property.when_equivalent_to(db, other_property, inferable), (KnownBoundMethodType::StrStartswith(_), KnownBoundMethodType::StrStartswith(_)) => { ConstraintSet::from(self == other) @@ -10117,18 +10360,19 @@ impl<'db> KnownBoundMethodType<'db> { self, db: &'db dyn Db, other: Self, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { match (self, other) { ( KnownBoundMethodType::FunctionTypeDunderGet(self_function), KnownBoundMethodType::FunctionTypeDunderGet(other_function), - ) => self_function.is_equivalent_to_impl(db, other_function, visitor), + ) => self_function.is_equivalent_to_impl(db, other_function, inferable, visitor), ( KnownBoundMethodType::FunctionTypeDunderCall(self_function), KnownBoundMethodType::FunctionTypeDunderCall(other_function), - ) => self_function.is_equivalent_to_impl(db, other_function, visitor), + ) => self_function.is_equivalent_to_impl(db, other_function, inferable, visitor), ( KnownBoundMethodType::PropertyDunderGet(self_property), @@ -10137,7 +10381,7 @@ impl<'db> KnownBoundMethodType<'db> { | ( KnownBoundMethodType::PropertyDunderSet(self_property), KnownBoundMethodType::PropertyDunderSet(other_property), - ) => self_property.is_equivalent_to_impl(db, other_property, visitor), + ) => self_property.is_equivalent_to_impl(db, other_property, inferable, visitor), (KnownBoundMethodType::StrStartswith(_), KnownBoundMethodType::StrStartswith(_)) => { ConstraintSet::from(self == other) @@ -10982,6 +11226,7 @@ impl<'db> UnionType<'db> { self, db: &'db dyn Db, other: Self, + _inferable: InferableTypeVars<'_, 'db>, _visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { if self == other { @@ -11083,6 +11328,7 @@ impl<'db> IntersectionType<'db> { self, db: &'db dyn Db, other: Self, + _inferable: InferableTypeVars<'_, 'db>, _visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { if self == other { diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs index b1b1b074aa..41a392a88b 100644 --- a/crates/ty_python_semantic/src/types/call/bind.rs +++ b/crates/ty_python_semantic/src/types/call/bind.rs @@ -26,7 +26,9 @@ use crate::types::enums::is_enum_class; use crate::types::function::{ DataclassTransformerParams, FunctionDecorators, FunctionType, KnownFunction, OverloadLiteral, }; -use crate::types::generics::{Specialization, SpecializationBuilder, SpecializationError}; +use crate::types::generics::{ + InferableTypeVars, Specialization, SpecializationBuilder, SpecializationError, +}; use crate::types::signatures::{Parameter, ParameterForm, ParameterKind, Parameters}; use crate::types::tuple::{TupleLength, TupleType}; use crate::types::{ @@ -597,7 +599,8 @@ impl<'db> Bindings<'db> { Type::FunctionLiteral(function_type) => match function_type.known(db) { Some(KnownFunction::IsEquivalentTo) => { if let [Some(ty_a), Some(ty_b)] = overload.parameter_types() { - let constraints = ty_a.when_equivalent_to(db, *ty_b); + let constraints = + ty_a.when_equivalent_to(db, *ty_b, InferableTypeVars::None); let tracked = TrackedConstraintSet::new(db, constraints); overload.set_return_type(Type::KnownInstance( KnownInstanceType::ConstraintSet(tracked), @@ -607,7 +610,8 @@ impl<'db> Bindings<'db> { Some(KnownFunction::IsSubtypeOf) => { if let [Some(ty_a), Some(ty_b)] = overload.parameter_types() { - let constraints = ty_a.when_subtype_of(db, *ty_b); + let constraints = + ty_a.when_subtype_of(db, *ty_b, InferableTypeVars::None); let tracked = TrackedConstraintSet::new(db, constraints); overload.set_return_type(Type::KnownInstance( KnownInstanceType::ConstraintSet(tracked), @@ -617,7 +621,8 @@ impl<'db> Bindings<'db> { Some(KnownFunction::IsAssignableTo) => { if let [Some(ty_a), Some(ty_b)] = overload.parameter_types() { - let constraints = ty_a.when_assignable_to(db, *ty_b); + let constraints = + ty_a.when_assignable_to(db, *ty_b, InferableTypeVars::None); let tracked = TrackedConstraintSet::new(db, constraints); overload.set_return_type(Type::KnownInstance( KnownInstanceType::ConstraintSet(tracked), @@ -627,7 +632,8 @@ impl<'db> Bindings<'db> { Some(KnownFunction::IsDisjointFrom) => { if let [Some(ty_a), Some(ty_b)] = overload.parameter_types() { - let constraints = ty_a.when_disjoint_from(db, *ty_b); + let constraints = + ty_a.when_disjoint_from(db, *ty_b, InferableTypeVars::None); let tracked = TrackedConstraintSet::new(db, constraints); overload.set_return_type(Type::KnownInstance( KnownInstanceType::ConstraintSet(tracked), @@ -1407,7 +1413,10 @@ impl<'db> CallableBinding<'db> { let parameter_type = overload.signature.parameters()[*parameter_index] .annotated_type() .unwrap_or(Type::unknown()); - if argument_type.is_assignable_to(db, parameter_type) { + if argument_type + .when_assignable_to(db, parameter_type, overload.inferable_typevars) + .is_always_satisfied() + { is_argument_assignable_to_any_overload = true; break 'overload; } @@ -1633,7 +1642,14 @@ impl<'db> CallableBinding<'db> { .unwrap_or(Type::unknown()); let first_parameter_type = &mut first_parameter_types[parameter_index]; if let Some(first_parameter_type) = first_parameter_type { - if !first_parameter_type.is_equivalent_to(db, current_parameter_type) { + if !first_parameter_type + .when_equivalent_to( + db, + current_parameter_type, + overload.inferable_typevars, + ) + .is_always_satisfied() + { participating_parameter_indexes.insert(parameter_index); } } else { @@ -1750,7 +1766,12 @@ impl<'db> CallableBinding<'db> { matching_overloads.all(|(_, overload)| { overload .return_type() - .is_equivalent_to(db, first_overload_return_type) + .when_equivalent_to( + db, + first_overload_return_type, + overload.inferable_typevars, + ) + .is_always_satisfied() }) } else { // No matching overload @@ -2461,6 +2482,7 @@ struct ArgumentTypeChecker<'a, 'db> { call_expression_tcx: &'a TypeContext<'db>, errors: &'a mut Vec>, + inferable_typevars: InferableTypeVars<'db, 'db>, specialization: Option>, } @@ -2482,6 +2504,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> { parameter_tys, call_expression_tcx, errors, + inferable_typevars: InferableTypeVars::None, specialization: None, } } @@ -2514,11 +2537,12 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> { } fn infer_specialization(&mut self) { - if self.signature.generic_context.is_none() { + let Some(generic_context) = self.signature.generic_context else { return; - } + }; - let mut builder = SpecializationBuilder::new(self.db); + // TODO: Use the list of inferable typevars from the generic context of the callable. + let mut builder = SpecializationBuilder::new(self.db, self.inferable_typevars); // Note that we infer the annotated type _before_ the arguments if this call is part of // an annotated assignment, to closer match the order of any unions written in the type @@ -2563,10 +2587,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> { } } - self.specialization = self - .signature - .generic_context - .map(|gc| builder.build(gc, *self.call_expression_tcx)); + self.specialization = Some(builder.build(generic_context, *self.call_expression_tcx)); } fn check_argument_type( @@ -2590,7 +2611,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> { // constraint set that we get from this assignability check, instead of inferring and // building them in an earlier separate step. if argument_type - .when_assignable_to(self.db, expected_ty) + .when_assignable_to(self.db, expected_ty, self.inferable_typevars) .is_never_satisfied() { let positional = matches!(argument, Argument::Positional | Argument::Synthetic) @@ -2719,7 +2740,14 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> { return; }; - if !key_type.is_assignable_to(self.db, KnownClass::Str.to_instance(self.db)) { + if !key_type + .when_assignable_to( + self.db, + KnownClass::Str.to_instance(self.db), + self.inferable_typevars, + ) + .is_always_satisfied() + { self.errors.push(BindingError::InvalidKeyType { argument_index: adjusted_argument_index, provided_ty: key_type, @@ -2754,8 +2782,8 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> { } } - fn finish(self) -> Option> { - self.specialization + fn finish(self) -> (InferableTypeVars<'db, 'db>, Option>) { + (self.inferable_typevars, self.specialization) } } @@ -2819,6 +2847,9 @@ pub(crate) struct Binding<'db> { /// Return type of the call. return_ty: Type<'db>, + /// The inferable typevars in this signature. + inferable_typevars: InferableTypeVars<'db, 'db>, + /// The specialization that was inferred from the argument types, if the callable is generic. specialization: Option>, @@ -2845,6 +2876,7 @@ impl<'db> Binding<'db> { callable_type: signature_type, signature_type, return_ty: Type::unknown(), + inferable_typevars: InferableTypeVars::None, specialization: None, argument_matches: Box::from([]), variadic_argument_matched_to_variadic_parameter: false, @@ -2916,7 +2948,7 @@ impl<'db> Binding<'db> { checker.infer_specialization(); checker.check_argument_types(); - self.specialization = checker.finish(); + (self.inferable_typevars, self.specialization) = checker.finish(); if let Some(specialization) = self.specialization { self.return_ty = self.return_ty.apply_specialization(db, specialization); } @@ -3010,6 +3042,7 @@ impl<'db> Binding<'db> { fn snapshot(&self) -> BindingSnapshot<'db> { BindingSnapshot { return_ty: self.return_ty, + inferable_typevars: self.inferable_typevars, specialization: self.specialization, argument_matches: self.argument_matches.clone(), parameter_tys: self.parameter_tys.clone(), @@ -3020,6 +3053,7 @@ impl<'db> Binding<'db> { fn restore(&mut self, snapshot: BindingSnapshot<'db>) { let BindingSnapshot { return_ty, + inferable_typevars, specialization, argument_matches, parameter_tys, @@ -3027,6 +3061,7 @@ impl<'db> Binding<'db> { } = snapshot; self.return_ty = return_ty; + self.inferable_typevars = inferable_typevars; self.specialization = specialization; self.argument_matches = argument_matches; self.parameter_tys = parameter_tys; @@ -3046,6 +3081,7 @@ impl<'db> Binding<'db> { /// Resets the state of this binding to its initial state. fn reset(&mut self) { self.return_ty = Type::unknown(); + self.inferable_typevars = InferableTypeVars::None; self.specialization = None; self.argument_matches = Box::from([]); self.parameter_tys = Box::from([]); @@ -3056,6 +3092,7 @@ impl<'db> Binding<'db> { #[derive(Clone, Debug)] struct BindingSnapshot<'db> { return_ty: Type<'db>, + inferable_typevars: InferableTypeVars<'db, 'db>, specialization: Option>, argument_matches: Box<[MatchedArgument<'db>]>, parameter_tys: Box<[Option>]>, @@ -3095,6 +3132,7 @@ impl<'db> CallableBindingSnapshot<'db> { // ... and update the snapshot with the current state of the binding. snapshot.return_ty = binding.return_ty; + snapshot.inferable_typevars = binding.inferable_typevars; snapshot.specialization = binding.specialization; snapshot .argument_matches diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs index b9b63a50a1..876f6d3930 100644 --- a/crates/ty_python_semantic/src/types/class.rs +++ b/crates/ty_python_semantic/src/types/class.rs @@ -22,7 +22,7 @@ use crate::types::diagnostic::INVALID_TYPE_ALIAS_TYPE; use crate::types::enums::enum_metadata; use crate::types::function::{DataclassTransformerParams, KnownFunction}; use crate::types::generics::{ - GenericContext, Specialization, walk_generic_context, walk_specialization, + GenericContext, InferableTypeVars, Specialization, walk_generic_context, walk_specialization, }; use crate::types::infer::nearest_enclosing_class; use crate::types::member::{Member, class_member}; @@ -540,17 +540,20 @@ impl<'db> ClassType<'db> { /// Return `true` if `other` is present in this class's MRO. pub(super) fn is_subclass_of(self, db: &'db dyn Db, other: ClassType<'db>) -> bool { - self.when_subclass_of(db, other).is_always_satisfied() + self.when_subclass_of(db, other, InferableTypeVars::None) + .is_always_satisfied() } pub(super) fn when_subclass_of( self, db: &'db dyn Db, other: ClassType<'db>, + inferable: InferableTypeVars<'_, 'db>, ) -> ConstraintSet<'db> { self.has_relation_to_impl( db, other, + inferable, TypeRelation::Subtyping, &HasRelationToVisitor::default(), &IsDisjointVisitor::default(), @@ -561,6 +564,7 @@ impl<'db> ClassType<'db> { self, db: &'db dyn Db, other: Self, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -586,6 +590,7 @@ impl<'db> ClassType<'db> { base.specialization(db).has_relation_to_impl( db, other.specialization(db), + inferable, relation, relation_visitor, disjointness_visitor, @@ -610,6 +615,7 @@ impl<'db> ClassType<'db> { self, db: &'db dyn Db, other: ClassType<'db>, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { if self == other { @@ -628,6 +634,7 @@ impl<'db> ClassType<'db> { this.specialization(db).is_equivalent_to_impl( db, other.specialization(db), + inferable, visitor, ) }) diff --git a/crates/ty_python_semantic/src/types/function.rs b/crates/ty_python_semantic/src/types/function.rs index b6867b84ea..a94c222a37 100644 --- a/crates/ty_python_semantic/src/types/function.rs +++ b/crates/ty_python_semantic/src/types/function.rs @@ -73,7 +73,7 @@ use crate::types::diagnostic::{ report_runtime_check_against_non_runtime_checkable_protocol, }; use crate::types::display::DisplaySettings; -use crate::types::generics::GenericContext; +use crate::types::generics::{GenericContext, InferableTypeVars}; use crate::types::ide_support::all_members; use crate::types::narrow::ClassInfoConstraintFunction; use crate::types::signatures::{CallableSignature, Signature}; @@ -81,9 +81,9 @@ use crate::types::visitor::any_over_type; use crate::types::{ ApplyTypeMappingVisitor, BoundMethodType, BoundTypeVarInstance, CallableType, ClassBase, ClassLiteral, ClassType, DeprecatedInstance, DynamicType, FindLegacyTypeVarsVisitor, - HasRelationToVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType, NormalizedVisitor, - SpecialFormType, TrackedConstraintSet, Truthiness, Type, TypeContext, TypeMapping, - TypeRelation, UnionBuilder, binding_type, todo_type, walk_signature, + HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType, + NormalizedVisitor, SpecialFormType, TrackedConstraintSet, Truthiness, Type, TypeContext, + TypeMapping, TypeRelation, UnionBuilder, binding_type, todo_type, walk_signature, }; use crate::{Db, FxOrderSet, ModuleName, resolve_module}; @@ -959,46 +959,42 @@ impl<'db> FunctionType<'db> { self, db: &'db dyn Db, other: Self, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation, - _visitor: &HasRelationToVisitor<'db>, + relation_visitor: &HasRelationToVisitor<'db>, + disjointness_visitor: &IsDisjointVisitor<'db>, ) -> ConstraintSet<'db> { - match relation { - TypeRelation::Subtyping | TypeRelation::Redundancy => { - ConstraintSet::from(self.is_subtype_of(db, other)) - } - TypeRelation::Assignability => ConstraintSet::from(self.is_assignable_to(db, other)), - } - } - - pub(crate) fn is_subtype_of(self, db: &'db dyn Db, other: Self) -> bool { // A function type is the subtype of itself, and not of any other function type. However, // our representation of a function type includes any specialization that should be applied // to the signature. Different specializations of the same function type are only subtypes // of each other if they result in subtype signatures. - if self.normalized(db) == other.normalized(db) { - return true; + if matches!(relation, TypeRelation::Subtyping | TypeRelation::Redundancy) + && self.normalized(db) == other.normalized(db) + { + return ConstraintSet::from(true); } + if self.literal(db) != other.literal(db) { - return false; + return ConstraintSet::from(false); } + let self_signature = self.signature(db); let other_signature = other.signature(db); - self_signature.is_subtype_of(db, other_signature) - } - - pub(crate) fn is_assignable_to(self, db: &'db dyn Db, other: Self) -> bool { - // A function type is assignable to itself, and not to any other function type. However, - // our representation of a function type includes any specialization that should be applied - // to the signature. Different specializations of the same function type are only - // assignable to each other if they result in assignable signatures. - self.literal(db) == other.literal(db) - && self.signature(db).is_assignable_to(db, other.signature(db)) + self_signature.has_relation_to_impl( + db, + other_signature, + inferable, + relation, + relation_visitor, + disjointness_visitor, + ) } pub(crate) fn is_equivalent_to_impl( self, db: &'db dyn Db, other: Self, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { if self.normalized(db) == other.normalized(db) { @@ -1009,7 +1005,7 @@ impl<'db> FunctionType<'db> { } let self_signature = self.signature(db); let other_signature = other.signature(db); - self_signature.is_equivalent_to_impl(db, other_signature, visitor) + self_signature.is_equivalent_to_impl(db, other_signature, inferable, visitor) } pub(crate) fn find_legacy_typevars_impl( diff --git a/crates/ty_python_semantic/src/types/generics.rs b/crates/ty_python_semantic/src/types/generics.rs index 68b4d2a95c..20e1ab127c 100644 --- a/crates/ty_python_semantic/src/types/generics.rs +++ b/crates/ty_python_semantic/src/types/generics.rs @@ -1,4 +1,4 @@ -use crate::types::constraints::ConstraintSet; +use std::marker::PhantomData; use itertools::Itertools; use ruff_python_ast as ast; @@ -9,6 +9,7 @@ use crate::semantic_index::scope::{FileScopeId, NodeWithScopeKind, ScopeId}; use crate::semantic_index::{SemanticIndex, semantic_index}; use crate::types::class::ClassType; use crate::types::class_base::ClassBase; +use crate::types::constraints::ConstraintSet; use crate::types::infer::infer_definition_types; use crate::types::instance::{Protocol, ProtocolInstanceType}; use crate::types::signatures::{Parameter, Parameters, Signature}; @@ -140,6 +141,16 @@ pub(crate) fn typing_self<'db>( .map(typevar_to_type) } +#[derive(Clone, Copy, Debug)] +pub(crate) enum InferableTypeVars<'a, 'db> { + None, + // TODO: This variant isn't used, and only exists so that we can include the 'a and 'db in the + // type definition. They will be used soon when we start creating real InferableTypeVars + // instances. + #[expect(unused)] + Unused(PhantomData<&'a &'db ()>), +} + #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)] pub struct GenericContextTypeVar<'db> { bound_typevar: BoundTypeVarInstance<'db>, @@ -593,12 +604,14 @@ pub(super) fn walk_specialization<'db, V: super::visitor::TypeVisitor<'db> + ?Si } } +#[expect(clippy::too_many_arguments)] fn is_subtype_in_invariant_position<'db>( db: &'db dyn Db, derived_type: &Type<'db>, derived_materialization: MaterializationKind, base_type: &Type<'db>, base_materialization: MaterializationKind, + inferable: InferableTypeVars<'_, 'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, ) -> ConstraintSet<'db> { @@ -623,6 +636,7 @@ fn is_subtype_in_invariant_position<'db>( derived.has_relation_to_impl( db, base, + inferable, TypeRelation::Subtyping, relation_visitor, disjointness_visitor, @@ -675,6 +689,7 @@ fn has_relation_in_invariant_position<'db>( derived_materialization: Option, base_type: &Type<'db>, base_materialization: Option, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -688,6 +703,7 @@ fn has_relation_in_invariant_position<'db>( derived_mat, base_type, base_mat, + inferable, relation_visitor, disjointness_visitor, ), @@ -707,6 +723,7 @@ fn has_relation_in_invariant_position<'db>( .has_relation_to_impl( db, *base_type, + inferable, relation, relation_visitor, disjointness_visitor, @@ -715,6 +732,7 @@ fn has_relation_in_invariant_position<'db>( base_type.has_relation_to_impl( db, *derived_type, + inferable, relation, relation_visitor, disjointness_visitor, @@ -728,6 +746,7 @@ fn has_relation_in_invariant_position<'db>( MaterializationKind::Top, base_type, base_mat, + inferable, relation_visitor, disjointness_visitor, ) @@ -739,6 +758,7 @@ fn has_relation_in_invariant_position<'db>( derived_mat, base_type, MaterializationKind::Bottom, + inferable, relation_visitor, disjointness_visitor, ) @@ -750,6 +770,7 @@ fn has_relation_in_invariant_position<'db>( MaterializationKind::Bottom, base_type, base_mat, + inferable, relation_visitor, disjointness_visitor, ), @@ -759,6 +780,7 @@ fn has_relation_in_invariant_position<'db>( derived_mat, base_type, MaterializationKind::Top, + inferable, relation_visitor, disjointness_visitor, ), @@ -1002,6 +1024,7 @@ impl<'db> Specialization<'db> { self, db: &'db dyn Db, other: Self, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -1016,6 +1039,7 @@ impl<'db> Specialization<'db> { return self_tuple.has_relation_to_impl( db, other_tuple, + inferable, relation, relation_visitor, disjointness_visitor, @@ -1043,6 +1067,7 @@ impl<'db> Specialization<'db> { self_materialization_kind, other_type, other_materialization_kind, + inferable, relation, relation_visitor, disjointness_visitor, @@ -1050,6 +1075,7 @@ impl<'db> Specialization<'db> { TypeVarVariance::Covariant => self_type.has_relation_to_impl( db, *other_type, + inferable, relation, relation_visitor, disjointness_visitor, @@ -1057,6 +1083,7 @@ impl<'db> Specialization<'db> { TypeVarVariance::Contravariant => other_type.has_relation_to_impl( db, *self_type, + inferable, relation, relation_visitor, disjointness_visitor, @@ -1075,6 +1102,7 @@ impl<'db> Specialization<'db> { self, db: &'db dyn Db, other: Specialization<'db>, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { if self.materialization_kind(db) != other.materialization_kind(db) { @@ -1100,7 +1128,7 @@ impl<'db> Specialization<'db> { TypeVarVariance::Invariant | TypeVarVariance::Covariant | TypeVarVariance::Contravariant => { - self_type.is_equivalent_to_impl(db, *other_type, visitor) + self_type.is_equivalent_to_impl(db, *other_type, inferable, visitor) } TypeVarVariance::Bivariant => ConstraintSet::from(true), }; @@ -1113,7 +1141,8 @@ impl<'db> Specialization<'db> { (Some(_), None) | (None, Some(_)) => return ConstraintSet::from(false), (None, None) => {} (Some(self_tuple), Some(other_tuple)) => { - let compatible = self_tuple.is_equivalent_to_impl(db, other_tuple, visitor); + let compatible = + self_tuple.is_equivalent_to_impl(db, other_tuple, inferable, visitor); if result.intersect(db, compatible).is_never_satisfied() { return result; } @@ -1168,13 +1197,15 @@ impl<'db> PartialSpecialization<'_, 'db> { /// specialization of a generic function. pub(crate) struct SpecializationBuilder<'db> { db: &'db dyn Db, + inferable: InferableTypeVars<'db, 'db>, types: FxHashMap, Type<'db>>, } impl<'db> SpecializationBuilder<'db> { - pub(crate) fn new(db: &'db dyn Db) -> Self { + pub(crate) fn new(db: &'db dyn Db, inferable: InferableTypeVars<'db, 'db>) -> Self { Self { db, + inferable, types: FxHashMap::default(), } } @@ -1244,14 +1275,16 @@ impl<'db> SpecializationBuilder<'db> { // without specializing `T` to `None`. if !matches!(formal, Type::ProtocolInstance(_)) && !actual.is_never() - && actual.is_subtype_of(self.db, formal) + && actual + .when_subtype_of(self.db, formal, self.inferable) + .is_always_satisfied() { return Ok(()); } // For example, if `formal` is `list[T]` and `actual` is `list[int] | None`, we want to specialize `T` to `int`. // So, here we remove the union elements that are not related to `formal`. - actual = actual.filter_disjoint_elements(self.db, formal); + actual = actual.filter_disjoint_elements(self.db, formal, self.inferable); match (formal, actual) { // TODO: We haven't implemented a full unification solver yet. If typevars appear in @@ -1324,7 +1357,10 @@ impl<'db> SpecializationBuilder<'db> { (Type::TypeVar(bound_typevar), ty) | (ty, Type::TypeVar(bound_typevar)) => { match bound_typevar.typevar(self.db).bound_or_constraints(self.db) { Some(TypeVarBoundOrConstraints::UpperBound(bound)) => { - if !ty.is_assignable_to(self.db, bound) { + if !ty + .when_assignable_to(self.db, bound, self.inferable) + .is_always_satisfied() + { return Err(SpecializationError::MismatchedBound { bound_typevar, argument: ty, @@ -1334,7 +1370,10 @@ impl<'db> SpecializationBuilder<'db> { } Some(TypeVarBoundOrConstraints::Constraints(constraints)) => { for constraint in constraints.elements(self.db) { - if ty.is_assignable_to(self.db, *constraint) { + if ty + .when_assignable_to(self.db, *constraint, self.inferable) + .is_always_satisfied() + { self.add_type_mapping(bound_typevar, *constraint); return Ok(()); } diff --git a/crates/ty_python_semantic/src/types/infer/builder.rs b/crates/ty_python_semantic/src/types/infer/builder.rs index 4b672f6e10..16b669e65a 100644 --- a/crates/ty_python_semantic/src/types/infer/builder.rs +++ b/crates/ty_python_semantic/src/types/infer/builder.rs @@ -75,8 +75,10 @@ use crate::types::diagnostic::{ use crate::types::function::{ FunctionDecorators, FunctionLiteral, FunctionType, KnownFunction, OverloadLiteral, }; -use crate::types::generics::{GenericContext, bind_typevar, enclosing_generic_contexts}; -use crate::types::generics::{LegacyGenericBase, SpecializationBuilder}; +use crate::types::generics::{ + GenericContext, InferableTypeVars, LegacyGenericBase, SpecializationBuilder, bind_typevar, + enclosing_generic_contexts, +}; use crate::types::infer::nearest_enclosing_function; use crate::types::instance::SliceLiteral; use crate::types::mro::MroErrorKind; @@ -5964,11 +5966,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { return None; }; + // TODO: Use the list of inferable typevars from the generic context of the collection + // class. + let inferable = InferableTypeVars::None; let tcx = tcx.map_annotation(|annotation| { // Remove any union elements of `annotation` that are not related to `collection_ty`. // e.g. `annotation: list[int] | None => list[int]` if `collection_ty: list` let collection_ty = collection_class.to_instance(self.db()); - annotation.filter_disjoint_elements(self.db(), collection_ty) + annotation.filter_disjoint_elements(self.db(), collection_ty, inferable) }); // Extract the annotated type of `T`, if provided. @@ -5977,7 +5982,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { .map(|specialization| specialization.types(self.db())); // Create a set of constraints to infer a precise type for `T`. - let mut builder = SpecializationBuilder::new(self.db()); + let mut builder = SpecializationBuilder::new(self.db(), inferable); match annotated_elt_tys { // The annotated type acts as a constraint for `T`. diff --git a/crates/ty_python_semantic/src/types/instance.rs b/crates/ty_python_semantic/src/types/instance.rs index 9079536435..ac081ca02a 100644 --- a/crates/ty_python_semantic/src/types/instance.rs +++ b/crates/ty_python_semantic/src/types/instance.rs @@ -9,7 +9,7 @@ use crate::place::PlaceAndQualifiers; use crate::semantic_index::definition::Definition; use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension}; use crate::types::enums::is_single_member_enum; -use crate::types::generics::walk_specialization; +use crate::types::generics::{InferableTypeVars, walk_specialization}; use crate::types::protocol_class::walk_protocol_interface; use crate::types::tuple::{TupleSpec, TupleType}; use crate::types::{ @@ -121,6 +121,7 @@ impl<'db> Type<'db> { self, db: &'db dyn Db, protocol: ProtocolInstanceType<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -129,6 +130,7 @@ impl<'db> Type<'db> { self_protocol.interface(db).has_relation_to_impl( db, protocol.interface(db), + inferable, relation, relation_visitor, disjointness_visitor, @@ -142,6 +144,7 @@ impl<'db> Type<'db> { member.is_satisfied_by( db, self, + inferable, relation, relation_visitor, disjointness_visitor, @@ -173,6 +176,7 @@ impl<'db> Type<'db> { type_to_test.has_relation_to_impl( db, Type::NominalInstance(nominal_instance), + inferable, relation, relation_visitor, disjointness_visitor, @@ -360,6 +364,7 @@ impl<'db> NominalInstanceType<'db> { self, db: &'db dyn Db, other: Self, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -372,6 +377,7 @@ impl<'db> NominalInstanceType<'db> { ) => tuple1.has_relation_to_impl( db, tuple2, + inferable, relation, relation_visitor, disjointness_visitor, @@ -379,6 +385,7 @@ impl<'db> NominalInstanceType<'db> { _ => self.class(db).has_relation_to_impl( db, other.class(db), + inferable, relation, relation_visitor, disjointness_visitor, @@ -390,18 +397,19 @@ impl<'db> NominalInstanceType<'db> { self, db: &'db dyn Db, other: Self, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { match (self.0, other.0) { ( NominalInstanceInner::ExactTuple(tuple1), NominalInstanceInner::ExactTuple(tuple2), - ) => tuple1.is_equivalent_to_impl(db, tuple2, visitor), + ) => tuple1.is_equivalent_to_impl(db, tuple2, inferable, visitor), (NominalInstanceInner::Object, NominalInstanceInner::Object) => { ConstraintSet::from(true) } (NominalInstanceInner::NonTuple(class1), NominalInstanceInner::NonTuple(class2)) => { - class1.is_equivalent_to_impl(db, class2, visitor) + class1.is_equivalent_to_impl(db, class2, inferable, visitor) } _ => ConstraintSet::from(false), } @@ -411,6 +419,7 @@ impl<'db> NominalInstanceType<'db> { self, db: &'db dyn Db, other: Self, + inferable: InferableTypeVars<'_, 'db>, disjointness_visitor: &IsDisjointVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>, ) -> ConstraintSet<'db> { @@ -423,6 +432,7 @@ impl<'db> NominalInstanceType<'db> { let compatible = self_spec.is_disjoint_from_impl( db, &other_spec, + inferable, disjointness_visitor, relation_visitor, ); @@ -650,6 +660,7 @@ impl<'db> ProtocolInstanceType<'db> { .satisfies_protocol( db, protocol, + InferableTypeVars::None, TypeRelation::Subtyping, &HasRelationToVisitor::default(), &IsDisjointVisitor::default(), @@ -708,6 +719,7 @@ impl<'db> ProtocolInstanceType<'db> { self, db: &'db dyn Db, other: Self, + _inferable: InferableTypeVars<'_, 'db>, _visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { if self == other { @@ -729,6 +741,7 @@ impl<'db> ProtocolInstanceType<'db> { self, _db: &'db dyn Db, _other: Self, + _inferable: InferableTypeVars<'_, 'db>, _visitor: &IsDisjointVisitor<'db>, ) -> ConstraintSet<'db> { ConstraintSet::from(false) diff --git a/crates/ty_python_semantic/src/types/protocol_class.rs b/crates/ty_python_semantic/src/types/protocol_class.rs index 716b814aaa..fdcb693508 100644 --- a/crates/ty_python_semantic/src/types/protocol_class.rs +++ b/crates/ty_python_semantic/src/types/protocol_class.rs @@ -22,6 +22,7 @@ use crate::{ constraints::{ConstraintSet, IteratorConstraintsExtension, OptionConstraintsExtension}, context::InferContext, diagnostic::report_undeclared_protocol_member, + generics::InferableTypeVars, signatures::{Parameter, Parameters}, todo_type, }, @@ -235,6 +236,7 @@ impl<'db> ProtocolInterface<'db> { self, db: &'db dyn Db, other: Self, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -276,6 +278,7 @@ impl<'db> ProtocolInterface<'db> { our_type.has_relation_to_impl( db, Type::Callable(other_type.bind_self(db)), + inferable, relation, relation_visitor, disjointness_visitor, @@ -288,6 +291,7 @@ impl<'db> ProtocolInterface<'db> { ) => our_method.bind_self(db).has_relation_to_impl( db, other_method.bind_self(db), + inferable, relation, relation_visitor, disjointness_visitor, @@ -300,6 +304,7 @@ impl<'db> ProtocolInterface<'db> { .has_relation_to_impl( db, other_type, + inferable, relation, relation_visitor, disjointness_visitor, @@ -308,6 +313,7 @@ impl<'db> ProtocolInterface<'db> { other_type.has_relation_to_impl( db, our_type, + inferable, relation, relation_visitor, disjointness_visitor, @@ -605,6 +611,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> { &self, db: &'db dyn Db, other: Type<'db>, + inferable: InferableTypeVars<'_, 'db>, disjointness_visitor: &IsDisjointVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>, ) -> ConstraintSet<'db> { @@ -613,9 +620,13 @@ impl<'a, 'db> ProtocolMember<'a, 'db> { ProtocolMemberKind::Property(_) | ProtocolMemberKind::Method(_) => { ConstraintSet::from(false) } - ProtocolMemberKind::Other(ty) => { - ty.is_disjoint_from_impl(db, other, disjointness_visitor, relation_visitor) - } + ProtocolMemberKind::Other(ty) => ty.is_disjoint_from_impl( + db, + other, + inferable, + disjointness_visitor, + relation_visitor, + ), } } @@ -625,6 +636,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> { &self, db: &'db dyn Db, other: Type<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -664,6 +676,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> { attribute_type.has_relation_to_impl( db, Type::Callable(method.bind_self(db)), + inferable, relation, relation_visitor, disjointness_visitor, @@ -684,6 +697,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> { .has_relation_to_impl( db, attribute_type, + inferable, relation, relation_visitor, disjointness_visitor, @@ -692,6 +706,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> { attribute_type.has_relation_to_impl( db, *member_type, + inferable, relation, relation_visitor, disjointness_visitor, diff --git a/crates/ty_python_semantic/src/types/signatures.rs b/crates/ty_python_semantic/src/types/signatures.rs index ed96430b48..92fb74ad26 100644 --- a/crates/ty_python_semantic/src/types/signatures.rs +++ b/crates/ty_python_semantic/src/types/signatures.rs @@ -23,7 +23,9 @@ use super::{ use crate::semantic_index::definition::Definition; use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension}; use crate::types::function::FunctionType; -use crate::types::generics::{GenericContext, typing_self, walk_generic_context}; +use crate::types::generics::{ + GenericContext, InferableTypeVars, typing_self, walk_generic_context, +}; use crate::types::infer::nearest_enclosing_class; use crate::types::{ ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, ClassLiteral, @@ -174,41 +176,27 @@ impl<'db> CallableSignature<'db> { } } - /// Check whether this callable type is a subtype of another callable type. - /// - /// See [`Type::is_subtype_of`] for more details. - pub(crate) fn is_subtype_of(&self, db: &'db dyn Db, other: &Self) -> bool { - self.is_subtype_of_impl(db, other).is_always_satisfied() - } - - fn is_subtype_of_impl(&self, db: &'db dyn Db, other: &Self) -> ConstraintSet<'db> { + fn is_subtype_of_impl( + &self, + db: &'db dyn Db, + other: &Self, + inferable: InferableTypeVars<'_, 'db>, + ) -> ConstraintSet<'db> { self.has_relation_to_impl( db, other, + inferable, TypeRelation::Subtyping, &HasRelationToVisitor::default(), &IsDisjointVisitor::default(), ) } - /// Check whether this callable type is assignable to another callable type. - /// - /// See [`Type::is_assignable_to`] for more details. - pub(crate) fn is_assignable_to(&self, db: &'db dyn Db, other: &Self) -> bool { - self.has_relation_to_impl( - db, - other, - TypeRelation::Assignability, - &HasRelationToVisitor::default(), - &IsDisjointVisitor::default(), - ) - .is_always_satisfied() - } - pub(crate) fn has_relation_to_impl( &self, db: &'db dyn Db, other: &Self, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -217,6 +205,7 @@ impl<'db> CallableSignature<'db> { db, &self.overloads, &other.overloads, + inferable, relation, relation_visitor, disjointness_visitor, @@ -229,6 +218,7 @@ impl<'db> CallableSignature<'db> { db: &'db dyn Db, self_signatures: &[Signature<'db>], other_signatures: &[Signature<'db>], + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -239,6 +229,7 @@ impl<'db> CallableSignature<'db> { self_signature.has_relation_to_impl( db, other_signature, + inferable, relation, relation_visitor, disjointness_visitor, @@ -251,6 +242,7 @@ impl<'db> CallableSignature<'db> { db, std::slice::from_ref(self_signature), other_signatures, + inferable, relation, relation_visitor, disjointness_visitor, @@ -263,6 +255,7 @@ impl<'db> CallableSignature<'db> { db, self_signatures, std::slice::from_ref(other_signature), + inferable, relation, relation_visitor, disjointness_visitor, @@ -275,6 +268,7 @@ impl<'db> CallableSignature<'db> { db, self_signatures, std::slice::from_ref(other_signature), + inferable, relation, relation_visitor, disjointness_visitor, @@ -290,20 +284,21 @@ impl<'db> CallableSignature<'db> { &self, db: &'db dyn Db, other: &Self, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { match (self.overloads.as_slice(), other.overloads.as_slice()) { ([self_signature], [other_signature]) => { // Common case: both callable types contain a single signature, use the custom // equivalence check instead of delegating it to the subtype check. - self_signature.is_equivalent_to_impl(db, other_signature, visitor) + self_signature.is_equivalent_to_impl(db, other_signature, inferable, visitor) } (_, _) => { if self == other { return ConstraintSet::from(true); } - self.is_subtype_of_impl(db, other) - .and(db, || other.is_subtype_of_impl(db, self)) + self.is_subtype_of_impl(db, other, inferable) + .and(db, || other.is_subtype_of_impl(db, self, inferable)) } } } @@ -619,6 +614,7 @@ impl<'db> Signature<'db> { &self, db: &'db dyn Db, other: &Signature<'db>, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { let mut result = ConstraintSet::from(true); @@ -626,7 +622,10 @@ impl<'db> Signature<'db> { let self_type = self_type.unwrap_or(Type::unknown()); let other_type = other_type.unwrap_or(Type::unknown()); !result - .intersect(db, self_type.is_equivalent_to_impl(db, other_type, visitor)) + .intersect( + db, + self_type.is_equivalent_to_impl(db, other_type, inferable, visitor), + ) .is_never_satisfied() }; @@ -702,6 +701,7 @@ impl<'db> Signature<'db> { &self, db: &'db dyn Db, other: &Signature<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -777,6 +777,7 @@ impl<'db> Signature<'db> { type1.has_relation_to_impl( db, type2, + inferable, relation, relation_visitor, disjointness_visitor, diff --git a/crates/ty_python_semantic/src/types/subclass_of.rs b/crates/ty_python_semantic/src/types/subclass_of.rs index c6a16620a9..db55132c1e 100644 --- a/crates/ty_python_semantic/src/types/subclass_of.rs +++ b/crates/ty_python_semantic/src/types/subclass_of.rs @@ -1,6 +1,7 @@ use crate::place::PlaceAndQualifiers; use crate::semantic_index::definition::Definition; use crate::types::constraints::ConstraintSet; +use crate::types::generics::InferableTypeVars; use crate::types::variance::VarianceInferable; use crate::types::{ ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassType, DynamicType, @@ -135,6 +136,7 @@ impl<'db> SubclassOfType<'db> { self, db: &'db dyn Db, other: SubclassOfType<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -157,6 +159,7 @@ impl<'db> SubclassOfType<'db> { .has_relation_to_impl( db, other_class, + inferable, relation, relation_visitor, disjointness_visitor, @@ -171,6 +174,7 @@ impl<'db> SubclassOfType<'db> { self, db: &'db dyn Db, other: Self, + _inferable: InferableTypeVars<'_, 'db>, _visitor: &IsDisjointVisitor<'db>, ) -> ConstraintSet<'db> { match (self.subclass_of, other.subclass_of) { diff --git a/crates/ty_python_semantic/src/types/tuple.rs b/crates/ty_python_semantic/src/types/tuple.rs index a94a0610e7..e70c7708a2 100644 --- a/crates/ty_python_semantic/src/types/tuple.rs +++ b/crates/ty_python_semantic/src/types/tuple.rs @@ -24,6 +24,7 @@ use itertools::{Either, EitherOrBoth, Itertools}; use crate::semantic_index::definition::Definition; use crate::types::class::{ClassType, KnownClass}; use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension}; +use crate::types::generics::InferableTypeVars; use crate::types::{ ApplyTypeMappingVisitor, BoundTypeVarInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, NormalizedVisitor, Type, TypeMapping, TypeRelation, @@ -258,6 +259,7 @@ impl<'db> TupleType<'db> { self, db: &'db dyn Db, other: Self, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -265,6 +267,7 @@ impl<'db> TupleType<'db> { self.tuple(db).has_relation_to_impl( db, other.tuple(db), + inferable, relation, relation_visitor, disjointness_visitor, @@ -275,10 +278,11 @@ impl<'db> TupleType<'db> { self, db: &'db dyn Db, other: Self, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { self.tuple(db) - .is_equivalent_to_impl(db, other.tuple(db), visitor) + .is_equivalent_to_impl(db, other.tuple(db), inferable, visitor) } pub(crate) fn is_single_valued(self, db: &'db dyn Db) -> bool { @@ -442,6 +446,7 @@ impl<'db> FixedLengthTuple> { &self, db: &'db dyn Db, other: &Tuple>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -453,6 +458,7 @@ impl<'db> FixedLengthTuple> { self_ty.has_relation_to_impl( db, *other_ty, + inferable, relation, relation_visitor, disjointness_visitor, @@ -473,6 +479,7 @@ impl<'db> FixedLengthTuple> { let element_constraints = self_ty.has_relation_to_impl( db, *other_ty, + inferable, relation, relation_visitor, disjointness_visitor, @@ -491,6 +498,7 @@ impl<'db> FixedLengthTuple> { let element_constraints = self_ty.has_relation_to_impl( db, *other_ty, + inferable, relation, relation_visitor, disjointness_visitor, @@ -510,6 +518,7 @@ impl<'db> FixedLengthTuple> { self_ty.has_relation_to_impl( db, other.variable, + inferable, relation, relation_visitor, disjointness_visitor, @@ -524,13 +533,14 @@ impl<'db> FixedLengthTuple> { &self, db: &'db dyn Db, other: &Self, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { ConstraintSet::from(self.0.len() == other.0.len()).and(db, || { (self.0.iter()) .zip(&other.0) .when_all(db, |(self_ty, other_ty)| { - self_ty.is_equivalent_to_impl(db, *other_ty, visitor) + self_ty.is_equivalent_to_impl(db, *other_ty, inferable, visitor) }) }) } @@ -793,6 +803,7 @@ impl<'db> VariableLengthTuple> { &self, db: &'db dyn Db, other: &Tuple>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -825,6 +836,7 @@ impl<'db> VariableLengthTuple> { let element_constraints = self_ty.has_relation_to_impl( db, other_ty, + inferable, relation, relation_visitor, disjointness_visitor, @@ -844,6 +856,7 @@ impl<'db> VariableLengthTuple> { let element_constraints = self_ty.has_relation_to_impl( db, other_ty, + inferable, relation, relation_visitor, disjointness_visitor, @@ -884,6 +897,7 @@ impl<'db> VariableLengthTuple> { EitherOrBoth::Both(self_ty, other_ty) => self_ty.has_relation_to_impl( db, other_ty, + inferable, relation, relation_visitor, disjointness_visitor, @@ -891,6 +905,7 @@ impl<'db> VariableLengthTuple> { EitherOrBoth::Left(self_ty) => self_ty.has_relation_to_impl( db, other.variable, + inferable, relation, relation_visitor, disjointness_visitor, @@ -918,6 +933,7 @@ impl<'db> VariableLengthTuple> { EitherOrBoth::Both(self_ty, other_ty) => self_ty.has_relation_to_impl( db, *other_ty, + inferable, relation, relation_visitor, disjointness_visitor, @@ -925,6 +941,7 @@ impl<'db> VariableLengthTuple> { EitherOrBoth::Left(self_ty) => self_ty.has_relation_to_impl( db, other.variable, + inferable, relation, relation_visitor, disjointness_visitor, @@ -945,6 +962,7 @@ impl<'db> VariableLengthTuple> { self.variable.has_relation_to_impl( db, other.variable, + inferable, relation, relation_visitor, disjointness_visitor, @@ -958,16 +976,17 @@ impl<'db> VariableLengthTuple> { &self, db: &'db dyn Db, other: &Self, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { self.variable - .is_equivalent_to_impl(db, other.variable, visitor) + .is_equivalent_to_impl(db, other.variable, inferable, visitor) .and(db, || { (self.prenormalized_prefix_elements(db, None)) .zip_longest(other.prenormalized_prefix_elements(db, None)) .when_all(db, |pair| match pair { EitherOrBoth::Both(self_ty, other_ty) => { - self_ty.is_equivalent_to_impl(db, other_ty, visitor) + self_ty.is_equivalent_to_impl(db, other_ty, inferable, visitor) } EitherOrBoth::Left(_) | EitherOrBoth::Right(_) => { ConstraintSet::from(false) @@ -979,7 +998,7 @@ impl<'db> VariableLengthTuple> { .zip_longest(other.prenormalized_suffix_elements(db, None)) .when_all(db, |pair| match pair { EitherOrBoth::Both(self_ty, other_ty) => { - self_ty.is_equivalent_to_impl(db, other_ty, visitor) + self_ty.is_equivalent_to_impl(db, other_ty, inferable, visitor) } EitherOrBoth::Left(_) | EitherOrBoth::Right(_) => { ConstraintSet::from(false) @@ -1170,6 +1189,7 @@ impl<'db> Tuple> { &self, db: &'db dyn Db, other: &Self, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -1178,6 +1198,7 @@ impl<'db> Tuple> { Tuple::Fixed(self_tuple) => self_tuple.has_relation_to_impl( db, other, + inferable, relation, relation_visitor, disjointness_visitor, @@ -1185,6 +1206,7 @@ impl<'db> Tuple> { Tuple::Variable(self_tuple) => self_tuple.has_relation_to_impl( db, other, + inferable, relation, relation_visitor, disjointness_visitor, @@ -1196,14 +1218,15 @@ impl<'db> Tuple> { &self, db: &'db dyn Db, other: &Self, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { match (self, other) { (Tuple::Fixed(self_tuple), Tuple::Fixed(other_tuple)) => { - self_tuple.is_equivalent_to_impl(db, other_tuple, visitor) + self_tuple.is_equivalent_to_impl(db, other_tuple, inferable, visitor) } (Tuple::Variable(self_tuple), Tuple::Variable(other_tuple)) => { - self_tuple.is_equivalent_to_impl(db, other_tuple, visitor) + self_tuple.is_equivalent_to_impl(db, other_tuple, inferable, visitor) } (Tuple::Fixed(_), Tuple::Variable(_)) | (Tuple::Variable(_), Tuple::Fixed(_)) => { ConstraintSet::from(false) @@ -1215,6 +1238,7 @@ impl<'db> Tuple> { &self, db: &'db dyn Db, other: &Self, + inferable: InferableTypeVars<'_, 'db>, disjointness_visitor: &IsDisjointVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>, ) -> ConstraintSet<'db> { @@ -1234,6 +1258,7 @@ impl<'db> Tuple> { db: &'db dyn Db, a: impl IntoIterator>, b: impl IntoIterator>, + inferable: InferableTypeVars<'_, 'db>, disjointness_visitor: &IsDisjointVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>, ) -> ConstraintSet<'db> @@ -1244,6 +1269,7 @@ impl<'db> Tuple> { self_element.is_disjoint_from_impl( db, *other_element, + inferable, disjointness_visitor, relation_visitor, ) @@ -1255,6 +1281,7 @@ impl<'db> Tuple> { db, self_tuple.elements(), other_tuple.elements(), + inferable, disjointness_visitor, relation_visitor, ), @@ -1266,6 +1293,7 @@ impl<'db> Tuple> { db, self_tuple.prefix_elements(), other_tuple.prefix_elements(), + inferable, disjointness_visitor, relation_visitor, ) @@ -1274,6 +1302,7 @@ impl<'db> Tuple> { db, self_tuple.suffix_elements().rev(), other_tuple.suffix_elements().rev(), + inferable, disjointness_visitor, relation_visitor, ) @@ -1284,6 +1313,7 @@ impl<'db> Tuple> { db, fixed.elements(), variable.prefix_elements(), + inferable, disjointness_visitor, relation_visitor, ) @@ -1292,6 +1322,7 @@ impl<'db> Tuple> { db, fixed.elements().rev(), variable.suffix_elements().rev(), + inferable, disjointness_visitor, relation_visitor, )