From de2b6511364d7a4877cd40f5bd633ea8f9cf9d9b Mon Sep 17 00:00:00 2001 From: Douglas Creager Date: Tue, 11 Nov 2025 19:15:11 -0500 Subject: [PATCH] unwoof this too --- crates/ty_python_semantic/src/types.rs | 59 +++--- .../ty_python_semantic/src/types/call/bind.rs | 42 ++-- crates/ty_python_semantic/src/types/class.rs | 8 +- .../src/types/constraints.rs | 8 +- .../ty_python_semantic/src/types/function.rs | 4 +- .../ty_python_semantic/src/types/generics.rs | 183 +++++++----------- .../src/types/infer/builder.rs | 4 +- .../ty_python_semantic/src/types/instance.rs | 14 +- .../src/types/protocol_class.rs | 6 +- .../src/types/signatures.rs | 26 ++- .../src/types/subclass_of.rs | 4 +- crates/ty_python_semantic/src/types/tuple.rs | 20 +- 12 files changed, 171 insertions(+), 207 deletions(-) diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 0082e3401a..a2fd967759 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -548,7 +548,7 @@ impl<'db> PropertyInstanceType<'db> { self, db: &'db dyn Db, other: Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, ) -> ConstraintSet<'db> { self.is_equivalent_to_impl(db, other, inferable, &IsEquivalentVisitor::default()) } @@ -557,7 +557,7 @@ impl<'db> PropertyInstanceType<'db> { self, db: &'db dyn Db, other: Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { let getter_equivalence = if let Some(getter) = self.getter(db) { @@ -1286,7 +1286,7 @@ impl<'db> Type<'db> { self, db: &'db dyn Db, target: Type<'db>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, ) -> Type<'db> { self.filter_union(db, |elem| { !elem @@ -1605,7 +1605,7 @@ 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, InferableTypeVars::none()) + self.when_subtype_of(db, target, InferableTypeVars::None) .is_always_satisfied(db) } @@ -1613,7 +1613,7 @@ impl<'db> Type<'db> { self, db: &'db dyn Db, target: Type<'db>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, ) -> ConstraintSet<'db> { self.has_relation_to(db, target, inferable, TypeRelation::Subtyping) } @@ -1627,7 +1627,7 @@ impl<'db> Type<'db> { db: &'db dyn Db, target: Type<'db>, constraints: ConstraintSet<'db>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, ) -> ConstraintSet<'db> { self.has_relation_to( db, @@ -1641,7 +1641,7 @@ impl<'db> Type<'db> { /// /// 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, InferableTypeVars::none()) + self.when_assignable_to(db, target, InferableTypeVars::None) .is_always_satisfied(db) } @@ -1649,7 +1649,7 @@ impl<'db> Type<'db> { self, db: &'db dyn Db, target: Type<'db>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, ) -> ConstraintSet<'db> { self.has_relation_to(db, target, inferable, TypeRelation::Assignability) } @@ -1659,20 +1659,15 @@ impl<'db> Type<'db> { /// See [`TypeRelation::Redundancy`] for more details. #[salsa::tracked(cycle_initial=is_redundant_with_cycle_initial, heap_size=ruff_memory_usage::heap_size)] pub(crate) fn is_redundant_with(self, db: &'db dyn Db, other: Type<'db>) -> bool { - self.has_relation_to( - db, - other, - InferableTypeVars::none(), - TypeRelation::Redundancy, - ) - .is_always_satisfied(db) + self.has_relation_to(db, other, InferableTypeVars::None, TypeRelation::Redundancy) + .is_always_satisfied(db) } fn has_relation_to( self, db: &'db dyn Db, target: Type<'db>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, ) -> ConstraintSet<'db> { self.has_relation_to_impl( @@ -1689,7 +1684,7 @@ impl<'db> Type<'db> { self, db: &'db dyn Db, target: Type<'db>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -2549,7 +2544,7 @@ 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, InferableTypeVars::none()) + self.when_equivalent_to(db, other, InferableTypeVars::None) .is_always_satisfied(db) } @@ -2557,7 +2552,7 @@ impl<'db> Type<'db> { self, db: &'db dyn Db, other: Type<'db>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, ) -> ConstraintSet<'db> { self.is_equivalent_to_impl(db, other, inferable, &IsEquivalentVisitor::default()) } @@ -2566,7 +2561,7 @@ impl<'db> Type<'db> { self, db: &'db dyn Db, other: Type<'db>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { if self == other { @@ -2676,7 +2671,7 @@ 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, InferableTypeVars::none()) + self.when_disjoint_from(db, other, InferableTypeVars::None) .is_always_satisfied(db) } @@ -2684,7 +2679,7 @@ impl<'db> Type<'db> { self, db: &'db dyn Db, other: Type<'db>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, ) -> ConstraintSet<'db> { self.is_disjoint_from_impl( db, @@ -2699,7 +2694,7 @@ impl<'db> Type<'db> { self, db: &'db dyn Db, other: Type<'db>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, disjointness_visitor: &IsDisjointVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>, ) -> ConstraintSet<'db> { @@ -2707,7 +2702,7 @@ impl<'db> Type<'db> { db: &'db dyn Db, protocol: ProtocolInstanceType<'db>, other: Type<'db>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, disjointness_visitor: &IsDisjointVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>, ) -> ConstraintSet<'db> { @@ -10546,7 +10541,7 @@ impl<'db> BoundMethodType<'db> { self, db: &'db dyn Db, other: Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -10580,7 +10575,7 @@ impl<'db> BoundMethodType<'db> { self, db: &'db dyn Db, other: Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { self.function(db) @@ -10713,7 +10708,7 @@ impl<'db> CallableType<'db> { self, db: &'db dyn Db, other: Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -10738,7 +10733,7 @@ impl<'db> CallableType<'db> { self, db: &'db dyn Db, other: Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { if self == other { @@ -10822,7 +10817,7 @@ impl<'db> KnownBoundMethodType<'db> { self, db: &'db dyn Db, other: Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -10924,7 +10919,7 @@ impl<'db> KnownBoundMethodType<'db> { self, db: &'db dyn Db, other: Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { match (self, other) { @@ -11969,7 +11964,7 @@ impl<'db> UnionType<'db> { self, db: &'db dyn Db, other: Self, - _inferable: InferableTypeVars<'db>, + _inferable: InferableTypeVars<'_, 'db>, _visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { if self == other { @@ -12071,7 +12066,7 @@ impl<'db> IntersectionType<'db> { self, db: &'db dyn Db, other: Self, - _inferable: InferableTypeVars<'db>, + _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 1314f94230..c017ee68d4 100644 --- a/crates/ty_python_semantic/src/types/call/bind.rs +++ b/crates/ty_python_semantic/src/types/call/bind.rs @@ -9,6 +9,7 @@ use std::fmt; use itertools::{Either, Itertools}; use ruff_db::parsed::parsed_module; use ruff_python_ast::name::Name; +use rustc_hash::FxHashSet; use smallvec::{SmallVec, smallvec, smallvec_inline}; use super::{Argument, CallArguments, CallError, CallErrorKind, InferContext, Signature, Type}; @@ -700,7 +701,7 @@ impl<'db> Bindings<'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, InferableTypeVars::none()); + 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), @@ -711,7 +712,7 @@ 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, InferableTypeVars::none()); + 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), @@ -722,7 +723,7 @@ 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, InferableTypeVars::none()); + 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), @@ -733,7 +734,7 @@ 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, InferableTypeVars::none()); + 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), @@ -1182,7 +1183,7 @@ impl<'db> Bindings<'db> { db, *ty_b, tracked.constraints(db), - InferableTypeVars::none(), + InferableTypeVars::None, ); let tracked = TrackedConstraintSet::new(db, result); overload.set_return_type(Type::KnownInstance( @@ -1216,20 +1217,21 @@ impl<'db> Bindings<'db> { let extract_inferable = |instance: &NominalInstanceType<'db>| { if instance.has_known_class(db, KnownClass::NoneType) { // Caller explicitly passed None, so no typevars are inferable. - return Some(InferableTypeVars::none()); + return Some(FxHashSet::default()); } - Some(InferableTypeVars::from_bound_typevars( - db, - instance.tuple_spec(db)?.fixed_elements().filter_map(|ty| { + instance + .tuple_spec(db)? + .fixed_elements() + .map(|ty| { ty.as_typevar() .map(|bound_typevar| bound_typevar.identity(db)) - }), - )) + }) + .collect() }; let inferable = match overload.parameter_types() { // Caller did not provide argument, so no typevars are inferable. - [None] => InferableTypeVars::none(), + [None] => FxHashSet::default(), [Some(Type::NominalInstance(instance))] => { match extract_inferable(instance) { Some(inferable) => inferable, @@ -1241,7 +1243,7 @@ impl<'db> Bindings<'db> { let result = tracked .constraints(db) - .satisfied_by_all_typevars(db, inferable); + .satisfied_by_all_typevars(db, InferableTypeVars::One(&inferable)); overload.set_return_type(Type::BooleanLiteral(result)); } @@ -2690,7 +2692,7 @@ struct ArgumentTypeChecker<'a, 'db> { return_ty: Type<'db>, errors: &'a mut Vec>, - inferable_typevars: InferableTypeVars<'db>, + inferable_typevars: InferableTypeVars<'db, 'db>, specialization: Option>, } @@ -2717,7 +2719,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> { call_expression_tcx, return_ty, errors, - inferable_typevars: InferableTypeVars::none(), + inferable_typevars: InferableTypeVars::None, specialization: None, } } @@ -3039,7 +3041,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> { fn finish( self, ) -> ( - InferableTypeVars<'db>, + InferableTypeVars<'db, 'db>, Option>, Type<'db>, ) { @@ -3109,7 +3111,7 @@ pub(crate) struct Binding<'db> { return_ty: Type<'db>, /// The inferable typevars in this signature. - inferable_typevars: InferableTypeVars<'db>, + inferable_typevars: InferableTypeVars<'db, 'db>, /// The specialization that was inferred from the argument types, if the callable is generic. specialization: Option>, @@ -3137,7 +3139,7 @@ impl<'db> Binding<'db> { callable_type: signature_type, signature_type, return_ty: Type::unknown(), - inferable_typevars: InferableTypeVars::none(), + inferable_typevars: InferableTypeVars::None, specialization: None, argument_matches: Box::from([]), variadic_argument_matched_to_variadic_parameter: false, @@ -3350,7 +3352,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.inferable_typevars = InferableTypeVars::None; self.specialization = None; self.argument_matches = Box::from([]); self.parameter_tys = Box::from([]); @@ -3361,7 +3363,7 @@ impl<'db> Binding<'db> { #[derive(Clone, Debug)] struct BindingSnapshot<'db> { return_ty: Type<'db>, - inferable_typevars: InferableTypeVars<'db>, + inferable_typevars: InferableTypeVars<'db, 'db>, specialization: Option>, argument_matches: Box<[MatchedArgument<'db>]>, parameter_tys: Box<[Option>]>, diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs index 7dace10d6b..ee5c1b5cde 100644 --- a/crates/ty_python_semantic/src/types/class.rs +++ b/crates/ty_python_semantic/src/types/class.rs @@ -516,7 +516,7 @@ 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, InferableTypeVars::none()) + self.when_subclass_of(db, other, InferableTypeVars::None) .is_always_satisfied(db) } @@ -524,7 +524,7 @@ impl<'db> ClassType<'db> { self, db: &'db dyn Db, other: ClassType<'db>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, ) -> ConstraintSet<'db> { self.has_relation_to_impl( db, @@ -540,7 +540,7 @@ impl<'db> ClassType<'db> { self, db: &'db dyn Db, other: Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -593,7 +593,7 @@ impl<'db> ClassType<'db> { self, db: &'db dyn Db, other: ClassType<'db>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { if self == other { diff --git a/crates/ty_python_semantic/src/types/constraints.rs b/crates/ty_python_semantic/src/types/constraints.rs index 6b35e2eb0c..fc8f81ea5d 100644 --- a/crates/ty_python_semantic/src/types/constraints.rs +++ b/crates/ty_python_semantic/src/types/constraints.rs @@ -246,7 +246,7 @@ impl<'db> ConstraintSet<'db> { pub(crate) fn satisfied_by_all_typevars( self, db: &'db dyn Db, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, ) -> bool { self.node.satisfied_by_all_typevars(db, inferable) } @@ -843,7 +843,11 @@ impl<'db> Node<'db> { self.satisfies(db, constraint) } - fn satisfied_by_all_typevars(self, db: &'db dyn Db, inferable: InferableTypeVars<'db>) -> bool { + fn satisfied_by_all_typevars( + self, + db: &'db dyn Db, + inferable: InferableTypeVars<'_, 'db>, + ) -> bool { match self { Node::AlwaysTrue => return true, Node::AlwaysFalse => return false, diff --git a/crates/ty_python_semantic/src/types/function.rs b/crates/ty_python_semantic/src/types/function.rs index 8903e24698..5e216762e5 100644 --- a/crates/ty_python_semantic/src/types/function.rs +++ b/crates/ty_python_semantic/src/types/function.rs @@ -970,7 +970,7 @@ impl<'db> FunctionType<'db> { self, db: &'db dyn Db, other: Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -1009,7 +1009,7 @@ impl<'db> FunctionType<'db> { self, db: &'db dyn Db, other: Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { if self.normalized(db) == other.normalized(db) { diff --git a/crates/ty_python_semantic/src/types/generics.rs b/crates/ty_python_semantic/src/types/generics.rs index de1ea713db..ad00c984ba 100644 --- a/crates/ty_python_semantic/src/types/generics.rs +++ b/crates/ty_python_semantic/src/types/generics.rs @@ -2,10 +2,9 @@ use std::cell::RefCell; use std::collections::hash_map::Entry; use std::fmt::Display; -use itertools::Itertools; +use itertools::{Either, Itertools}; use ruff_python_ast as ast; -use rustc_hash::FxHashMap; -use smallvec::{SmallVec, smallvec}; +use rustc_hash::{FxHashMap, FxHashSet}; use crate::semantic_index::definition::Definition; use crate::semantic_index::scope::{FileScopeId, NodeWithScopeKind, ScopeId}; @@ -120,111 +119,79 @@ pub(crate) fn typing_self<'db>( } #[derive(Clone, Copy, Debug)] -pub(crate) struct InferableTypeVars<'db> { - inner: Option>, +pub(crate) enum InferableTypeVars<'a, 'db> { + None, + One(&'a FxHashSet>), + Two( + &'a InferableTypeVars<'a, 'db>, + &'a InferableTypeVars<'a, 'db>, + ), } -#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)] -struct InferableTypeVarsInner<'db> { - // The _set_ of typevars that are inferable. This will always be sorted and deduped. - #[returns(ref)] - inferable: SmallVec<[BoundTypeVarIdentity<'db>; 4]>, -} - -// The Salsa heap is tracked separately. -impl get_size2::GetSize for InferableTypeVarsInner<'_> {} - impl<'db> BoundTypeVarInstance<'db> { - pub(crate) fn is_inferable(self, db: &'db dyn Db, inferable: InferableTypeVars<'db>) -> bool { - match inferable.inner { - None => false, - Some(inner) => inner - .inferable(db) - .binary_search(&self.identity(db)) - .is_ok(), + pub(crate) fn is_inferable( + self, + db: &'db dyn Db, + inferable: InferableTypeVars<'_, 'db>, + ) -> bool { + match inferable { + InferableTypeVars::None => false, + InferableTypeVars::One(typevars) => typevars.contains(&self.identity(db)), + InferableTypeVars::Two(left, right) => { + self.is_inferable(db, *left) || self.is_inferable(db, *right) + } } } } -impl<'db> InferableTypeVars<'db> { - pub(crate) const fn none() -> Self { - InferableTypeVars { inner: None } +impl<'a, 'db> InferableTypeVars<'a, 'db> { + pub(crate) fn merge(&'a self, other: &'a InferableTypeVars<'a, 'db>) -> Self { + InferableTypeVars::Two(self, other) } - pub(crate) fn from_bound_typevars( - db: &'db dyn Db, - bound_typevars: impl IntoIterator>, - ) -> Self { - InferableTypeVars { - inner: Some(InferableTypeVarsInner::from_bound_typevars( - db, - bound_typevars, - )), + // This is not an IntoIterator implementation because I have no desire to try to name the + // iterator type. + pub(crate) fn iter(self) -> impl Iterator> { + match self { + InferableTypeVars::None => Either::Left(Either::Left(std::iter::empty())), + InferableTypeVars::One(typevars) => Either::Right(typevars.iter().copied()), + InferableTypeVars::Two(left, right) => { + let chained: Box>> = + Box::new(left.iter().chain(right.iter())); + Either::Left(Either::Right(chained)) + } } } - pub(crate) fn merge(self, db: &'db dyn Db, other: Self) -> Self { - match (self.inner, other.inner) { - (None, None) => self, - (Some(_), None) => self, - (None, Some(_)) => other, - (Some(self_inner), Some(other_inner)) => InferableTypeVars { - inner: Some(self_inner.merge(db, other_inner)), - }, - } - } - - pub(crate) fn iter( - self, - db: &'db dyn Db, - ) -> impl Iterator> + 'db { - self.inner - .into_iter() - .flat_map(|inner| inner.inferable(db).iter().copied()) - } - // Keep this around for debugging purposes #[expect(dead_code)] - pub(crate) fn display(self, db: &'db dyn Db) -> impl Display { - let inferable = match self.inner { - Some(inner) => inner.inferable(db), - None => return String::from("[]"), - }; + pub(crate) fn display(&self, db: &'db dyn Db) -> impl Display { + fn find_typevars<'db>( + result: &mut FxHashSet>, + inferable: &InferableTypeVars<'_, 'db>, + ) { + match inferable { + InferableTypeVars::None => {} + InferableTypeVars::One(typevars) => result.extend(typevars.iter().copied()), + InferableTypeVars::Two(left, right) => { + find_typevars(result, left); + find_typevars(result, right); + } + } + } + + let mut typevars = FxHashSet::default(); + find_typevars(&mut typevars, self); format!( "[{}]", - inferable - .iter() + typevars + .into_iter() .map(|identity| identity.display(db)) .format(", ") ) } } -#[salsa::tracked] -impl<'db> InferableTypeVarsInner<'db> { - fn from_bound_typevars( - db: &'db dyn Db, - bound_typevars: impl IntoIterator>, - ) -> Self { - let mut inferable: SmallVec<_> = bound_typevars.into_iter().collect(); - inferable.sort_unstable(); - inferable.dedup(); - InferableTypeVarsInner::new(db, inferable) - } - - #[salsa::tracked] - fn merge(self, db: &'db dyn Db, other: Self) -> Self { - // The input typevar vecs are already sorted, so we can merge/dedup them instead of - // having to do an expensive sort. - let self_inferable = self.inferable(db); - let self_typevars = self_inferable.iter().copied(); - let other_inferable = other.inferable(db); - let other_typevars = other_inferable.iter().copied(); - let inferable = self_typevars.merge(other_typevars).dedup().collect(); - InferableTypeVarsInner::new(db, inferable) - } -} - #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)] pub struct GenericContextTypeVar<'db> { bound_typevar: BoundTypeVarInstance<'db>, @@ -329,10 +296,10 @@ impl<'db> GenericContext<'db> { ) } - pub(crate) fn inferable_typevars(self, db: &'db dyn Db) -> InferableTypeVars<'db> { + pub(crate) fn inferable_typevars(self, db: &'db dyn Db) -> InferableTypeVars<'db, 'db> { #[derive(Default)] struct CollectTypeVars<'db> { - typevars: RefCell; 4]>>, + typevars: RefCell>>, recursion_guard: TypeCollector<'db>, } @@ -346,7 +313,9 @@ impl<'db> GenericContext<'db> { db: &'db dyn Db, bound_typevar: BoundTypeVarInstance<'db>, ) { - self.typevars.borrow_mut().push(bound_typevar.identity(db)); + self.typevars + .borrow_mut() + .insert(bound_typevar.identity(db)); walk_bound_type_var_type(db, bound_typevar, self); } @@ -356,29 +325,25 @@ impl<'db> GenericContext<'db> { } #[salsa::tracked( + returns(ref), cycle_initial=inferable_typevars_cycle_initial, heap_size=ruff_memory_usage::heap_size, )] fn inferable_typevars_inner<'db>( db: &'db dyn Db, generic_context: GenericContext<'db>, - ) -> InferableTypeVarsInner<'db> { + ) -> FxHashSet> { let visitor = CollectTypeVars::default(); for bound_typevar in generic_context.variables(db) { visitor.visit_bound_type_var_type(db, bound_typevar); } - let mut inferable = visitor.typevars.into_inner(); - inferable.sort_unstable(); - inferable.dedup(); - InferableTypeVarsInner::new(db, inferable) + visitor.typevars.into_inner() } - // This ensures that salsa caches the InferableTypeVarsInner, not the InferableTypeVars - // that wraps it. (That way InferableTypeVars can contain a reference, and doesn't need to - // impl salsa::Update.) - InferableTypeVars { - inner: Some(inferable_typevars_inner(db, self)), - } + // This ensures that salsa caches the FxHashSet, not the InferableTypeVars that wraps it. + // (That way InferableTypeVars can contain references, and doesn't need to impl + // salsa::Update.) + InferableTypeVars::One(inferable_typevars_inner(db, self)) } pub(crate) fn variables( @@ -664,11 +629,11 @@ impl<'db> GenericContext<'db> { } fn inferable_typevars_cycle_initial<'db>( - db: &'db dyn Db, + _db: &'db dyn Db, _id: salsa::Id, _self: GenericContext<'db>, -) -> InferableTypeVarsInner<'db> { - InferableTypeVarsInner::new(db, smallvec![]) +) -> FxHashSet> { + FxHashSet::default() } #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -739,7 +704,7 @@ fn is_subtype_in_invariant_position<'db>( derived_materialization: MaterializationKind, base_type: &Type<'db>, base_materialization: MaterializationKind, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, ) -> ConstraintSet<'db> { @@ -817,7 +782,7 @@ fn has_relation_in_invariant_position<'db>( derived_materialization: Option, base_type: &Type<'db>, base_materialization: Option, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -1165,7 +1130,7 @@ impl<'db> Specialization<'db> { self, db: &'db dyn Db, other: Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -1243,7 +1208,7 @@ impl<'db> Specialization<'db> { self, db: &'db dyn Db, other: Specialization<'db>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { if self.materialization_kind(db) != other.materialization_kind(db) { @@ -1357,12 +1322,12 @@ impl<'db> PartialSpecialization<'_, 'db> { /// specialization of a generic function. pub(crate) struct SpecializationBuilder<'db> { db: &'db dyn Db, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'db, 'db>, types: FxHashMap, Type<'db>>, } impl<'db> SpecializationBuilder<'db> { - pub(crate) fn new(db: &'db dyn Db, inferable: InferableTypeVars<'db>) -> Self { + pub(crate) fn new(db: &'db dyn Db, inferable: InferableTypeVars<'db, 'db>) -> Self { Self { db, inferable, diff --git a/crates/ty_python_semantic/src/types/infer/builder.rs b/crates/ty_python_semantic/src/types/infer/builder.rs index e159aa2f64..086e3f8f15 100644 --- a/crates/ty_python_semantic/src/types/infer/builder.rs +++ b/crates/ty_python_semantic/src/types/infer/builder.rs @@ -6829,7 +6829,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { .try_to_class_literal(self.db()) .and_then(|class| class.generic_context(self.db())) .map(|generic_context| generic_context.inferable_typevars(self.db())) - .unwrap_or(InferableTypeVars::none()); + .unwrap_or(InferableTypeVars::None); annotation.filter_disjoint_elements( self.db(), Type::homogeneous_tuple(self.db(), Type::unknown()), @@ -7149,7 +7149,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { annotation.filter_disjoint_elements( self.db(), collection_class.to_instance(self.db()), - InferableTypeVars::none(), + InferableTypeVars::None, ) }); diff --git a/crates/ty_python_semantic/src/types/instance.rs b/crates/ty_python_semantic/src/types/instance.rs index e1410ca070..84f8ab07d0 100644 --- a/crates/ty_python_semantic/src/types/instance.rs +++ b/crates/ty_python_semantic/src/types/instance.rs @@ -122,7 +122,7 @@ impl<'db> Type<'db> { self, db: &'db dyn Db, protocol: ProtocolInstanceType<'db>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -365,7 +365,7 @@ impl<'db> NominalInstanceType<'db> { self, db: &'db dyn Db, other: Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -398,7 +398,7 @@ impl<'db> NominalInstanceType<'db> { self, db: &'db dyn Db, other: Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { match (self.0, other.0) { @@ -420,7 +420,7 @@ impl<'db> NominalInstanceType<'db> { self, db: &'db dyn Db, other: Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, disjointness_visitor: &IsDisjointVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>, ) -> ConstraintSet<'db> { @@ -666,7 +666,7 @@ impl<'db> ProtocolInstanceType<'db> { .satisfies_protocol( db, protocol, - InferableTypeVars::none(), + InferableTypeVars::None, TypeRelation::Subtyping, &HasRelationToVisitor::default(), &IsDisjointVisitor::default(), @@ -719,7 +719,7 @@ impl<'db> ProtocolInstanceType<'db> { self, db: &'db dyn Db, other: Self, - _inferable: InferableTypeVars<'db>, + _inferable: InferableTypeVars<'_, 'db>, _visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { if self == other { @@ -741,7 +741,7 @@ impl<'db> ProtocolInstanceType<'db> { self, _db: &'db dyn Db, _other: Self, - _inferable: InferableTypeVars<'db>, + _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 31acf47416..6cb204231f 100644 --- a/crates/ty_python_semantic/src/types/protocol_class.rs +++ b/crates/ty_python_semantic/src/types/protocol_class.rs @@ -233,7 +233,7 @@ impl<'db> ProtocolInterface<'db> { self, db: &'db dyn Db, other: Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -608,7 +608,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> { &self, db: &'db dyn Db, other: Type<'db>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, disjointness_visitor: &IsDisjointVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>, ) -> ConstraintSet<'db> { @@ -633,7 +633,7 @@ impl<'a, 'db> ProtocolMember<'a, 'db> { &self, db: &'db dyn Db, other: Type<'db>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, diff --git a/crates/ty_python_semantic/src/types/signatures.rs b/crates/ty_python_semantic/src/types/signatures.rs index 03bc5d194d..b11e9db94f 100644 --- a/crates/ty_python_semantic/src/types/signatures.rs +++ b/crates/ty_python_semantic/src/types/signatures.rs @@ -217,7 +217,7 @@ impl<'db> CallableSignature<'db> { &self, db: &'db dyn Db, other: &Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, ) -> ConstraintSet<'db> { self.has_relation_to_impl( db, @@ -233,7 +233,7 @@ impl<'db> CallableSignature<'db> { &self, db: &'db dyn Db, other: &Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -255,7 +255,7 @@ impl<'db> CallableSignature<'db> { db: &'db dyn Db, self_signatures: &[Signature<'db>], other_signatures: &[Signature<'db>], - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -321,7 +321,7 @@ impl<'db> CallableSignature<'db> { &self, db: &'db dyn Db, other: &Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { match (self.overloads.as_slice(), other.overloads.as_slice()) { @@ -628,10 +628,10 @@ impl<'db> Signature<'db> { } } - fn inferable_typevars(&self, db: &'db dyn Db) -> InferableTypeVars<'db> { + fn inferable_typevars(&self, db: &'db dyn Db) -> InferableTypeVars<'db, 'db> { match self.generic_context { Some(generic_context) => generic_context.inferable_typevars(db), - None => InferableTypeVars::none(), + None => InferableTypeVars::None, } } @@ -642,7 +642,7 @@ impl<'db> Signature<'db> { &self, db: &'db dyn Db, other: &Signature<'db>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { let mut result = ConstraintSet::from(true); @@ -729,7 +729,7 @@ impl<'db> Signature<'db> { &self, db: &'db dyn Db, other: &Signature<'db>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -739,10 +739,8 @@ impl<'db> Signature<'db> { // specialization that causes the check to succeed. let self_inferable = self.inferable_typevars(db); let other_inferable = other.inferable_typevars(db); - let new_inferable = InferableTypeVars::none() - .merge(db, self_inferable) - .merge(db, other_inferable); - let inferable = inferable.merge(db, new_inferable); + let inferable = inferable.merge(&self_inferable); + let inferable = inferable.merge(&other_inferable); // `inner` will create a constraint set that references these newly inferable typevars. let when = self.has_relation_to_inner( @@ -758,14 +756,14 @@ impl<'db> Signature<'db> { // we produce, we reduce it back down to the inferable set that the caller asked about. // If we introduced new inferable typevars, those will be existentially quantified away // before returning. - when.reduce_inferable(db, new_inferable.iter(db)) + when.reduce_inferable(db, self_inferable.iter().chain(other_inferable.iter())) } fn has_relation_to_inner( &self, db: &'db dyn Db, other: &Signature<'db>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, diff --git a/crates/ty_python_semantic/src/types/subclass_of.rs b/crates/ty_python_semantic/src/types/subclass_of.rs index 3ff8cadf5c..20bf9f322b 100644 --- a/crates/ty_python_semantic/src/types/subclass_of.rs +++ b/crates/ty_python_semantic/src/types/subclass_of.rs @@ -136,7 +136,7 @@ impl<'db> SubclassOfType<'db> { self, db: &'db dyn Db, other: SubclassOfType<'db>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -174,7 +174,7 @@ impl<'db> SubclassOfType<'db> { self, db: &'db dyn Db, other: Self, - _inferable: InferableTypeVars<'db>, + _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 43d0873546..e773315d68 100644 --- a/crates/ty_python_semantic/src/types/tuple.rs +++ b/crates/ty_python_semantic/src/types/tuple.rs @@ -259,7 +259,7 @@ impl<'db> TupleType<'db> { self, db: &'db dyn Db, other: Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -278,7 +278,7 @@ impl<'db> TupleType<'db> { self, db: &'db dyn Db, other: Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { self.tuple(db) @@ -441,7 +441,7 @@ impl<'db> FixedLengthTuple> { &self, db: &'db dyn Db, other: &Tuple>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -528,7 +528,7 @@ impl<'db> FixedLengthTuple> { &self, db: &'db dyn Db, other: &Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { ConstraintSet::from(self.0.len() == other.0.len()).and(db, || { @@ -798,7 +798,7 @@ impl<'db> VariableLengthTuple> { &self, db: &'db dyn Db, other: &Tuple>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -977,7 +977,7 @@ impl<'db> VariableLengthTuple> { &self, db: &'db dyn Db, other: &Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { self.variable @@ -1190,7 +1190,7 @@ impl<'db> Tuple> { &self, db: &'db dyn Db, other: &Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, relation: TypeRelation<'db>, relation_visitor: &HasRelationToVisitor<'db>, disjointness_visitor: &IsDisjointVisitor<'db>, @@ -1219,7 +1219,7 @@ impl<'db> Tuple> { &self, db: &'db dyn Db, other: &Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { match (self, other) { @@ -1239,7 +1239,7 @@ impl<'db> Tuple> { &self, db: &'db dyn Db, other: &Self, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, disjointness_visitor: &IsDisjointVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>, ) -> ConstraintSet<'db> { @@ -1259,7 +1259,7 @@ impl<'db> Tuple> { db: &'db dyn Db, a: impl IntoIterator>, b: impl IntoIterator>, - inferable: InferableTypeVars<'db>, + inferable: InferableTypeVars<'_, 'db>, disjointness_visitor: &IsDisjointVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>, ) -> ConstraintSet<'db>