diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index e4dfee1af7..492a107e9a 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -10033,9 +10033,9 @@ impl<'db> TypeVarInstance<'db> { .map_or_else(Parameters::unknown, |tuple_spec| { Parameters::new( db, - tuple_spec.all_elements().map(|ty| { - Parameter::positional_only(None).with_annotated_type(*ty) - }), + tuple_spec + .iter_all_elements() + .map(|ty| Parameter::positional_only(None).with_annotated_type(ty)), ) }), Type::Dynamic(dynamic) => match dynamic { diff --git a/crates/ty_python_semantic/src/types/call/arguments.rs b/crates/ty_python_semantic/src/types/call/arguments.rs index ae377785b8..04621c995d 100644 --- a/crates/ty_python_semantic/src/types/call/arguments.rs +++ b/crates/ty_python_semantic/src/types/call/arguments.rs @@ -348,8 +348,8 @@ pub(crate) fn is_expandable_type<'db>(db: &'db dyn Db, ty: Type<'db>) -> bool { class.is_known(db, KnownClass::Bool) || instance.tuple_spec(db).is_some_and(|spec| match &*spec { Tuple::Fixed(fixed_length_tuple) => fixed_length_tuple - .all_elements() - .any(|element| is_expandable_type(db, *element)), + .iter_all_elements() + .any(|element| is_expandable_type(db, element)), Tuple::Variable(_) => false, }) || enum_metadata(db, class.class_literal(db).0).is_some() @@ -380,12 +380,12 @@ fn expand_type<'db>(db: &'db dyn Db, ty: Type<'db>) -> Option>> { return match &*spec { Tuple::Fixed(fixed_length_tuple) => { let expanded = fixed_length_tuple - .all_elements() + .iter_all_elements() .map(|element| { - if let Some(expanded) = expand_type(db, *element) { + if let Some(expanded) = expand_type(db, element) { Either::Left(expanded.into_iter()) } else { - Either::Right(std::iter::once(*element)) + Either::Right(std::iter::once(element)) } }) .multi_cartesian_product() diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs index 236a9e11be..343d1a57dc 100644 --- a/crates/ty_python_semantic/src/types/call/bind.rs +++ b/crates/ty_python_semantic/src/types/call/bind.rs @@ -14,7 +14,7 @@ use std::borrow::Cow; use std::collections::HashSet; use std::fmt; -use itertools::{Either, Itertools}; +use itertools::Itertools; use ruff_db::parsed::parsed_module; use ruff_python_ast::name::Name; use rustc_hash::{FxHashMap, FxHashSet}; @@ -2754,24 +2754,19 @@ impl<'a, 'db> ArgumentMatcher<'a, 'db> { None => VariadicArgumentType::None, }; - let (mut argument_types, length, variable_element) = match &variadic_type { - VariadicArgumentType::ParamSpec(paramspec) => ( - Either::Right(std::iter::empty()), - TupleLength::unknown(), - Some(*paramspec), - ), + let (argument_types, length, variable_element) = match &variadic_type { + VariadicArgumentType::ParamSpec(paramspec) => { + ([].as_slice(), TupleLength::unknown(), Some(*paramspec)) + } VariadicArgumentType::Other(tuple) => ( - Either::Left(tuple.all_elements().copied()), + tuple.all_elements(), tuple.len(), tuple.variable_element().copied(), ), - VariadicArgumentType::None => ( - Either::Right(std::iter::empty()), - TupleLength::unknown(), - None, - ), + VariadicArgumentType::None => ([].as_slice(), TupleLength::unknown(), None), }; + let mut argument_types = argument_types.iter().copied(); let is_variable = length.is_variable(); // We must be able to match up the fixed-length portion of the argument with positional diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs index 0520647d46..5daaf077fc 100644 --- a/crates/ty_python_semantic/src/types/class.rs +++ b/crates/ty_python_semantic/src/types/class.rs @@ -912,8 +912,10 @@ impl<'db> ClassType<'db> { TupleSpec::Fixed(fixed_length_tuple) => { let tuple_length = fixed_length_tuple.len(); - for (index, ty) in fixed_length_tuple.elements().enumerate() { - let entry = element_type_to_indices.entry(*ty).or_default(); + for (index, ty) in + fixed_length_tuple.iter_all_elements().enumerate() + { + let entry = element_type_to_indices.entry(ty).or_default(); if let Ok(index) = i64::try_from(index) { entry.push(index); } @@ -933,7 +935,9 @@ impl<'db> ClassType<'db> { // __getitem__(self, index: Literal[-3], /) -> float | str // TupleSpec::Variable(variable_length_tuple) => { - for (index, ty) in variable_length_tuple.prefix.iter().enumerate() { + for (index, ty) in + variable_length_tuple.prefix_elements().iter().enumerate() + { if let Ok(index) = i64::try_from(index) { element_type_to_indices.entry(*ty).or_default().push(index); } @@ -941,18 +945,18 @@ impl<'db> ClassType<'db> { let one_based_index = index + 1; if let Ok(i) = i64::try_from( - variable_length_tuple.suffix.len() + one_based_index, + variable_length_tuple.suffix_elements().len() + + one_based_index, ) { let overload_return = UnionType::from_elements( db, - std::iter::once(variable_length_tuple.variable).chain( - variable_length_tuple - .prefix - .iter() - .rev() - .take(one_based_index) - .copied(), - ), + std::iter::once(variable_length_tuple.variable()) + .chain( + variable_length_tuple + .iter_prefix_elements() + .rev() + .take(one_based_index), + ), ); element_type_to_indices .entry(overload_return) @@ -961,30 +965,31 @@ impl<'db> ClassType<'db> { } } - for (index, ty) in - variable_length_tuple.suffix.iter().rev().enumerate() + for (index, ty) in variable_length_tuple + .iter_suffix_elements() + .rev() + .enumerate() { if let Some(index) = index.checked_add(1).and_then(|i| i64::try_from(i).ok()) { element_type_to_indices - .entry(*ty) + .entry(ty) .or_default() .push(0 - index); } - if let Ok(i) = - i64::try_from(variable_length_tuple.prefix.len() + index) - { + if let Ok(i) = i64::try_from( + variable_length_tuple.prefix_elements().len() + index, + ) { let overload_return = UnionType::from_elements( db, - std::iter::once(variable_length_tuple.variable).chain( - variable_length_tuple - .suffix - .iter() - .take(index + 1) - .copied(), - ), + std::iter::once(variable_length_tuple.variable()) + .chain( + variable_length_tuple + .iter_suffix_elements() + .take(index + 1), + ), ); element_type_to_indices .entry(overload_return) @@ -1080,10 +1085,10 @@ impl<'db> ClassType<'db> { if tuple_len.minimum() == 0 && tuple_len.maximum().is_none() { // If the tuple has no length restrictions, // any iterable is allowed as long as the iterable has the correct element type. - let mut tuple_elements = tuple.all_elements(); + let mut tuple_elements = tuple.iter_all_elements(); iterable_parameter = iterable_parameter.with_annotated_type( KnownClass::Iterable - .to_specialized_instance(db, [*tuple_elements.next().unwrap()]), + .to_specialized_instance(db, [tuple_elements.next().unwrap()]), ); assert_eq!( tuple_elements.next(), diff --git a/crates/ty_python_semantic/src/types/diagnostic.rs b/crates/ty_python_semantic/src/types/diagnostic.rs index 7f1a2e7437..10acd6d231 100644 --- a/crates/ty_python_semantic/src/types/diagnostic.rs +++ b/crates/ty_python_semantic/src/types/diagnostic.rs @@ -4350,8 +4350,9 @@ pub(super) fn report_unsupported_comparison<'db>( && let Some(TupleSpec::Fixed(rhs_spec)) = right_ty.tuple_instance_spec(db).as_deref() && lhs_spec.len() == rhs_spec.len() && let Some(position) = lhs_spec - .elements() - .zip(rhs_spec.elements()) + .all_elements() + .iter() + .zip(rhs_spec.all_elements()) .position(|tup| tup == (&error.left_ty, &error.right_ty)) { if error.left_ty == error.right_ty { diff --git a/crates/ty_python_semantic/src/types/display.rs b/crates/ty_python_semantic/src/types/display.rs index 1825632f79..5f577ac12f 100644 --- a/crates/ty_python_semantic/src/types/display.rs +++ b/crates/ty_python_semantic/src/types/display.rs @@ -238,7 +238,7 @@ impl<'a, 'b, 'db> TypeWriter<'a, 'b, 'db> { } } -impl std::fmt::Write for TypeWriter<'_, '_, '_> { +impl Write for TypeWriter<'_, '_, '_> { fn write_str(&mut self, val: &str) -> fmt::Result { match self { TypeWriter::Formatter(formatter) => formatter.write_str(val), @@ -246,7 +246,7 @@ impl std::fmt::Write for TypeWriter<'_, '_, '_> { } } } -impl std::fmt::Write for TypeDetailsWriter<'_> { +impl Write for TypeDetailsWriter<'_> { fn write_str(&mut self, val: &str) -> fmt::Result { self.label.write_str(val) } @@ -424,7 +424,7 @@ enum AmbiguityState<'db> { RequiresFileAndLineNumber, } -impl<'db> super::visitor::TypeVisitor<'db> for AmbiguousClassCollector<'db> { +impl<'db> TypeVisitor<'db> for AmbiguousClassCollector<'db> { fn should_visit_lazy_type_attributes(&self) -> bool { false } @@ -1103,14 +1103,14 @@ impl<'db> FmtDetailed<'db> for DisplayTuple<'_, 'db> { // S is included if there is either a prefix or a suffix. The initial `tuple[` and // trailing `]` are printed elsewhere. The `yyy, ...` is printed no matter what.) TupleSpec::Variable(tuple) => { - if !tuple.prefix.is_empty() { + if !tuple.prefix_elements().is_empty() { tuple - .prefix + .prefix_elements() .display_with(self.db, self.settings.singleline()) .fmt_detailed(f)?; f.write_str(", ")?; } - if !tuple.prefix.is_empty() || !tuple.suffix.is_empty() { + if !tuple.prefix_elements().is_empty() || !tuple.suffix_elements().is_empty() { f.write_char('*')?; // Might as well link the type again here too f.with_type(KnownClass::Tuple.to_class_literal(self.db)) @@ -1118,17 +1118,17 @@ impl<'db> FmtDetailed<'db> for DisplayTuple<'_, 'db> { f.write_char('[')?; } tuple - .variable + .variable() .display_with(self.db, self.settings.singleline()) .fmt_detailed(f)?; f.write_str(", ...")?; - if !tuple.prefix.is_empty() || !tuple.suffix.is_empty() { + if !tuple.prefix_elements().is_empty() || !tuple.suffix_elements().is_empty() { f.write_str("]")?; } - if !tuple.suffix.is_empty() { + if !tuple.suffix_elements().is_empty() { f.write_str(", ")?; tuple - .suffix + .suffix_elements() .display_with(self.db, self.settings.singleline()) .fmt_detailed(f)?; } diff --git a/crates/ty_python_semantic/src/types/generics.rs b/crates/ty_python_semantic/src/types/generics.rs index 8f553ccf91..6f9ab3cc99 100644 --- a/crates/ty_python_semantic/src/types/generics.rs +++ b/crates/ty_python_semantic/src/types/generics.rs @@ -208,7 +208,7 @@ pub struct GenericContext<'db> { variables_inner: FxOrderMap, BoundTypeVarInstance<'db>>, } -pub(super) fn walk_generic_context<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>( +pub(super) fn walk_generic_context<'db, V: TypeVisitor<'db> + ?Sized>( db: &'db dyn Db, context: GenericContext<'db>, visitor: &V, @@ -549,7 +549,7 @@ impl<'db> GenericContext<'db> { db: &'db dyn Db, types: Box<[Type<'db>]>, ) -> Specialization<'db> { - assert!(self.len(db) == types.len()); + assert_eq!(self.len(db), types.len()); Specialization::new(db, self, types, None, None) } @@ -719,7 +719,7 @@ impl LegacyGenericBase { } } -impl std::fmt::Display for LegacyGenericBase { +impl Display for LegacyGenericBase { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(self.as_str()) } @@ -756,7 +756,7 @@ pub struct Specialization<'db> { // The Salsa heap is tracked separately. impl get_size2::GetSize for Specialization<'_> {} -pub(super) fn walk_specialization<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>( +pub(super) fn walk_specialization<'db, V: TypeVisitor<'db> + ?Sized>( db: &'db dyn Db, specialization: Specialization<'db>, visitor: &V, @@ -825,16 +825,16 @@ fn is_subtype_in_invariant_position<'db>( // range of types covered by derived and within the range covered by base, because if such a type // exists, it's a subtype of `Top[base]` and a supertype of `Bottom[derived]`. (MaterializationKind::Bottom, MaterializationKind::Top) => { - (is_subtype_of(base_bottom, derived_bottom) - .and(db, || is_subtype_of(derived_bottom, base_top))) - .or(db, || { - is_subtype_of(base_bottom, derived_top) - .and(db, || is_subtype_of(derived_top, base_top)) - }) - .or(db, || { - is_subtype_of(base_top, derived_top) - .and(db, || is_subtype_of(derived_bottom, base_top)) - }) + is_subtype_of(base_bottom, derived_bottom) + .and(db, || is_subtype_of(derived_bottom, base_top)) + .or(db, || { + is_subtype_of(base_bottom, derived_top) + .and(db, || is_subtype_of(derived_top, base_top)) + }) + .or(db, || { + is_subtype_of(base_top, derived_top) + .and(db, || is_subtype_of(derived_bottom, base_top)) + }) } // A top materialization is a subtype of a bottom materialization only if both original // un-materialized types are the same fully static type. @@ -1102,7 +1102,7 @@ impl<'db> Specialization<'db> { /// Panics if the two specializations are not for the same generic context. pub(crate) fn combine(self, db: &'db dyn Db, other: Self) -> Self { let generic_context = self.generic_context(db); - assert!(other.generic_context(db) == generic_context); + assert_eq!(other.generic_context(db), generic_context); // TODO special-casing Unknown to mean "no mapping" is not right here, and can give // confusing/wrong results in cases where there was a mapping found for a typevar, and it // was of type Unknown. We should probably add a bitset or similar to Specialization that @@ -1402,7 +1402,8 @@ impl<'db> Specialization<'db> { } let mut result = ConstraintSet::from(true); - for ((bound_typevar, self_type), other_type) in (generic_context.variables(db)) + for ((bound_typevar, self_type), other_type) in generic_context + .variables(db) .zip(self.types(db)) .zip(other.types(db)) { @@ -1739,7 +1740,11 @@ impl<'db> SpecializationBuilder<'db> { else { return Ok(()); }; - if (actual_union.elements(self.db).iter()).any(|ty| ty.is_type_var()) { + if actual_union + .elements(self.db) + .iter() + .any(|ty| ty.is_type_var()) + { return Ok(()); } let remaining_actual = @@ -1761,19 +1766,20 @@ impl<'db> SpecializationBuilder<'db> { // // without specializing `T` to `None`. if !actual.is_never() { - let assignable_elements = - (union_formal.elements(self.db).iter()).filter(|ty| { - actual - .when_subtype_of(self.db, **ty, self.inferable) - .is_always_satisfied(self.db) - }); + let assignable_elements = union_formal.elements(self.db).iter().filter(|ty| { + actual + .when_subtype_of(self.db, **ty, self.inferable) + .is_always_satisfied(self.db) + }); if assignable_elements.exactly_one().is_ok() { return Ok(()); } } - let mut bound_typevars = - (union_formal.elements(self.db).iter()).filter_map(|ty| ty.as_typevar()); + let mut bound_typevars = union_formal + .elements(self.db) + .iter() + .filter_map(|ty| ty.as_typevar()); // TODO: // Handling more than one bare typevar is something that we can't handle yet. @@ -1907,8 +1913,10 @@ impl<'db> SpecializationBuilder<'db> { let Ok(actual_tuple) = actual_tuple.resize(self.db, most_precise_length) else { return Ok(()); }; - for (formal_element, actual_element) in - formal_tuple.all_elements().zip(actual_tuple.all_elements()) + for (formal_element, actual_element) in formal_tuple + .all_elements() + .iter() + .zip(actual_tuple.all_elements()) { let variance = TypeVarVariance::Covariant.compose(polarity); self.infer_map_impl(*formal_element, *actual_element, variance, &mut f)?; diff --git a/crates/ty_python_semantic/src/types/infer/builder.rs b/crates/ty_python_semantic/src/types/infer/builder.rs index c7c7640c44..96a2c97eb8 100644 --- a/crates/ty_python_semantic/src/types/infer/builder.rs +++ b/crates/ty_python_semantic/src/types/infer/builder.rs @@ -3284,7 +3284,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { let mut builder = UnionBuilder::new(self.db()); let mut invalid_elements = vec![]; - for (index, element) in tuple_spec.all_elements().enumerate() { + for (index, element) in tuple_spec.all_elements().iter().enumerate() { builder = builder.add( if element.is_assignable_to(self.db(), type_base_exception) { element.to_instance(self.db()).expect( @@ -5248,7 +5248,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { if let Some(tuple_spec) = assigned_ty.and_then(|ty| ty.tuple_instance_spec(self.db())) { - let assigned_tys = tuple_spec.all_elements().copied().collect::>(); + let assigned_tys = tuple_spec.all_elements().to_vec(); for (i, element) in elts.iter().enumerate() { match assigned_tys.get(i).copied() { @@ -7932,11 +7932,16 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { .ok() }); - let mut annotated_elt_tys = annotated_tuple.as_ref().map(Tuple::all_elements); + let mut annotated_elt_tys = annotated_tuple + .as_ref() + .map(Tuple::all_elements) + .unwrap_or_default() + .iter() + .copied(); let db = self.db(); let element_types = elts.iter().map(|element| { - let annotated_elt_ty = annotated_elt_tys.as_mut().and_then(Iterator::next).copied(); + let annotated_elt_ty = annotated_elt_tys.next(); self.infer_expression(element, TypeContext::new(annotated_elt_ty)) }); @@ -9201,10 +9206,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { return; }; - let Some(builder) = self - .context - .report_lint(&crate::types::diagnostic::DEPRECATED, ranged) - else { + let Some(builder) = self.context.report_lint(&diagnostic::DEPRECATED, ranged) else { return; }; @@ -11347,7 +11349,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { let mut any_eq = false; let mut any_ambiguous = false; - for ty in rhs_tuple.all_elements().copied() { + for ty in rhs_tuple.iter_all_elements() { let eq_result = self.infer_binary_type_comparison( left, ast::CmpOp::Eq, @@ -11538,8 +11540,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { return Ok(Type::unknown()); }; - let left_iter = left.elements().copied(); - let right_iter = right.elements().copied(); + let left_iter = left.iter_all_elements(); + let right_iter = right.iter_all_elements(); let mut builder = UnionBuilder::new(self.db()); diff --git a/crates/ty_python_semantic/src/types/infer/builder/type_expression.rs b/crates/ty_python_semantic/src/types/infer/builder/type_expression.rs index d4c5701f1d..1e186b2f0f 100644 --- a/crates/ty_python_semantic/src/types/infer/builder/type_expression.rs +++ b/crates/ty_python_semantic/src/types/infer/builder/type_expression.rs @@ -1593,8 +1593,10 @@ impl<'db> TypeInferenceBuilder<'db, '_> { let mut argument_elements = arguments_as_tuple .as_ref() - .map(|tup| Either::Left(tup.all_elements().copied())) - .unwrap_or(Either::Right(std::iter::once(arguments))); + .map(|tup| tup.all_elements()) + .unwrap_or(std::slice::from_ref(&arguments)) + .iter() + .copied(); let probably_meant_literal = argument_elements.all(|ty| match ty { Type::StringLiteral(_) diff --git a/crates/ty_python_semantic/src/types/narrow.rs b/crates/ty_python_semantic/src/types/narrow.rs index 3a589d81bb..e60d1ee641 100644 --- a/crates/ty_python_semantic/src/types/narrow.rs +++ b/crates/ty_python_semantic/src/types/narrow.rs @@ -215,8 +215,8 @@ impl ClassInfoConstraintFunction { UnionType::try_from_elements( db, tuple - .all_elements() - .map(|element| self.generate_constraint(db, *element)), + .iter_all_elements() + .map(|element| self.generate_constraint(db, element)), ) }), diff --git a/crates/ty_python_semantic/src/types/tuple.rs b/crates/ty_python_semantic/src/types/tuple.rs index 2fd0c5f243..88c1ffd264 100644 --- a/crates/ty_python_semantic/src/types/tuple.rs +++ b/crates/ty_python_semantic/src/types/tuple.rs @@ -20,6 +20,7 @@ use std::cmp::Ordering; use std::hash::Hash; use itertools::{Either, EitherOrBoth, Itertools}; +use smallvec::{SmallVec, smallvec_inline}; use crate::semantic_index::definition::Definition; use crate::subscript::{Nth, OutOfBoundsError, PyIndex, PySlice, StepSizeZeroError}; @@ -140,8 +141,8 @@ pub(super) fn walk_tuple_type<'db, V: super::visitor::TypeVisitor<'db> + ?Sized> tuple: TupleType<'db>, visitor: &V, ) { - for element in tuple.tuple(db).all_elements() { - visitor.visit_type(db, *element); + for element in tuple.tuple(db).iter_all_elements() { + visitor.visit_type(db, element); } } @@ -160,9 +161,11 @@ impl<'db> TupleType<'db> { // If the variable-length portion is Never, it can only be instantiated with zero elements. // That means this isn't a variable-length tuple after all! if let TupleSpec::Variable(tuple) = spec { - if tuple.variable.is_never() { + if tuple.variable().is_never() { let tuple = TupleSpec::Fixed(FixedLengthTuple::from_elements( - tuple.prefix.iter().chain(&tuple.suffix).copied(), + tuple + .iter_prefix_elements() + .chain(tuple.iter_suffix_elements()), )); return Some(TupleType::new_internal::<_, TupleSpec<'db>>(db, tuple)); } @@ -370,12 +373,15 @@ impl FixedLengthTuple { self.0 } - pub(crate) fn elements(&self) -> impl DoubleEndedIterator + ExactSizeIterator + '_ { - self.0.iter() + pub(crate) fn all_elements(&self) -> &[T] { + &self.0 } - pub(crate) fn all_elements(&self) -> impl Iterator { - self.0.iter() + pub(crate) fn iter_all_elements(&self) -> impl DoubleEndedIterator + where + T: Copy, + { + self.0.iter().copied() } pub(crate) fn into_all_elements_with_kind(self) -> impl Iterator> { @@ -409,16 +415,12 @@ impl<'db> FixedLengthTuple> { // Extract rhs values into the prefix, then into the starred target, then into the // suffix. - let mut elements = self.elements().copied(); - let prefix = elements.by_ref().take(prefix).collect(); + let mut elements = self.iter_all_elements(); + let prefix: Vec<_> = elements.by_ref().take(prefix).collect(); let variable = UnionType::from_elements_leave_aliases(db, elements.by_ref().take(variable)); - let suffix = elements.by_ref().take(suffix).collect(); - Ok(Tuple::Variable(VariableLengthTuple { - prefix, - variable, - suffix, - })) + let suffix = elements.by_ref().take(suffix); + Ok(VariableLengthTuple::mixed(prefix, variable, suffix)) } } } @@ -474,9 +476,11 @@ impl<'db> FixedLengthTuple> { let tcx_elements = match tcx_tuple.as_ref() { None => Either::Right(std::iter::repeat(TypeContext::default())), - Some(tuple) => { - Either::Left(tuple.all_elements().map(|tcx| TypeContext::new(Some(*tcx)))) - } + Some(tuple) => Either::Left( + tuple + .iter_all_elements() + .map(|tcx| TypeContext::new(Some(tcx))), + ), }; Self::from_elements( @@ -529,7 +533,7 @@ impl<'db> FixedLengthTuple> { // and suffix, and each of those elements must pairwise satisfy the relation. let mut result = ConstraintSet::from(true); let mut self_iter = self.0.iter(); - for other_ty in &other.prefix { + for other_ty in other.prefix_elements() { let Some(self_ty) = self_iter.next() else { return ConstraintSet::from(false); }; @@ -548,13 +552,13 @@ impl<'db> FixedLengthTuple> { return result; } } - for other_ty in other.suffix.iter().rev() { + for other_ty in other.iter_suffix_elements().rev() { let Some(self_ty) = self_iter.next_back() else { return ConstraintSet::from(false); }; let element_constraints = self_ty.has_relation_to_impl( db, - *other_ty, + other_ty, inferable, relation, relation_visitor, @@ -574,7 +578,7 @@ impl<'db> FixedLengthTuple> { self_iter.when_all(db, |self_ty| { self_ty.has_relation_to_impl( db, - other.variable, + other.variable(), inferable, relation, relation_visitor, @@ -638,16 +642,19 @@ impl<'db> PySlice<'db> for FixedLengthTuple> { /// types, use [`TupleSpec`], which defines some additional type-specific methods. #[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize, salsa::Update)] pub struct VariableLengthTuple { - pub(crate) prefix: Box<[T]>, - pub(crate) variable: T, - pub(crate) suffix: Box<[T]>, + pub(crate) elements: smallvec::SmallVec<[T; 1]>, + variable_index: usize, } impl VariableLengthTuple { /// Creates a new tuple spec containing zero or more elements of a given type, with no prefix /// or suffix. - fn homogeneous(ty: T) -> Tuple { - Self::mixed([], ty, []) + const fn homogeneous(ty: T) -> Self { + let elements = smallvec_inline![ty]; + Self { + elements, + variable_index: 0, + } } fn mixed( @@ -655,43 +662,136 @@ impl VariableLengthTuple { variable: T, suffix: impl IntoIterator, ) -> Tuple { - Tuple::Variable(Self { - prefix: prefix.into_iter().collect(), - variable, - suffix: suffix.into_iter().collect(), + Tuple::Variable(Self::new(prefix, variable, suffix)) + } + + fn try_new(prefix: P, variable: T, suffix: S) -> Option + where + P: IntoIterator>, + P::IntoIter: ExactSizeIterator, + S: IntoIterator>, + S::IntoIter: ExactSizeIterator, + { + let prefix = prefix.into_iter(); + let suffix = suffix.into_iter(); + + let mut elements = + SmallVec::with_capacity(prefix.len().saturating_add(suffix.len()).saturating_add(1)); + + for element in prefix { + elements.push(element?); + } + + let variable_index = elements.len(); + elements.push(variable); + + for element in suffix { + elements.push(element?); + } + + Some(Self { + elements, + variable_index, }) } - pub(crate) fn prefix_elements( - &self, - ) -> impl DoubleEndedIterator + ExactSizeIterator + '_ { - self.prefix.iter() + fn new( + prefix: impl IntoIterator, + variable: T, + suffix: impl IntoIterator, + ) -> Self { + let mut elements = SmallVec::new_const(); + elements.extend(prefix); + + let variable_index = elements.len(); + elements.push(variable); + elements.extend(suffix); + + Self { + elements, + variable_index, + } } - pub(crate) fn suffix_elements( - &self, - ) -> impl DoubleEndedIterator + ExactSizeIterator + '_ { - self.suffix.iter() + pub(crate) fn variable(&self) -> T + where + T: Copy, + { + self.elements[self.variable_index] + } + + pub(crate) fn variable_element(&self) -> &T { + &self.elements[self.variable_index] + } + + pub(crate) fn variable_element_mut(&mut self) -> &mut T { + &mut self.elements[self.variable_index] + } + + pub(crate) fn prefix_elements(&self) -> &[T] { + &self.elements[..self.variable_index] + } + + pub(crate) fn iter_prefix_elements(&self) -> impl DoubleEndedIterator + where + T: Copy, + { + self.prefix_elements().iter().copied() + } + + pub(crate) fn prefix_elements_mut(&mut self) -> &mut [T] { + &mut self.elements[..self.variable_index] + } + + pub(crate) fn suffix_elements(&self) -> &[T] { + &self.elements[self.suffix_offset()..] + } + + pub(crate) fn iter_suffix_elements(&self) -> impl DoubleEndedIterator + where + T: Copy, + { + self.suffix_elements().iter().copied() + } + + pub(crate) fn suffix_elements_mut(&mut self) -> &mut [T] { + let suffix_offset = self.suffix_offset(); + &mut self.elements[suffix_offset..] + } + + fn suffix_offset(&self) -> usize { + self.variable_index + 1 } fn fixed_elements(&self) -> impl Iterator + '_ { - self.prefix_elements().chain(self.suffix_elements()) + self.prefix_elements().iter().chain(self.suffix_elements()) } - fn all_elements(&self) -> impl Iterator + '_ { - (self.prefix_elements()) - .chain(std::iter::once(&self.variable)) - .chain(self.suffix_elements()) + fn all_elements(&self) -> &[T] { + &self.elements } fn into_all_elements_with_kind(self) -> impl Iterator> { - (self.prefix.into_iter().map(TupleElement::Prefix)) - .chain(std::iter::once(TupleElement::Variable(self.variable))) - .chain(self.suffix.into_iter().map(TupleElement::Suffix)) + self.elements + .into_iter() + .enumerate() + .map(move |(i, element)| match i.cmp(&self.variable_index) { + Ordering::Less => TupleElement::Prefix(element), + Ordering::Equal => TupleElement::Variable(element), + Ordering::Greater => TupleElement::Suffix(element), + }) + } + + fn prefix_len(&self) -> usize { + self.variable_index + } + + fn suffix_len(&self) -> usize { + self.elements.len() - self.suffix_offset() } fn len(&self) -> TupleLength { - TupleLength::Variable(self.prefix.len(), self.suffix.len()) + TupleLength::Variable(self.prefix_len(), self.suffix_len()) } } @@ -720,13 +820,11 @@ impl<'db> VariableLengthTuple> { db: &'db dyn Db, variable: Option>, ) -> impl Iterator> + 'a { - let variable = variable.unwrap_or(self.variable); - self.prefix_elements() - .chain( - self.suffix_elements() - .take_while(move |element| element.is_equivalent_to(db, variable)), - ) - .copied() + let variable = variable.unwrap_or(self.variable()); + self.iter_prefix_elements().chain( + self.iter_suffix_elements() + .take_while(move |element| element.is_equivalent_to(db, variable)), + ) } /// Returns the suffix of the prenormalization of this tuple. @@ -753,10 +851,9 @@ impl<'db> VariableLengthTuple> { db: &'db dyn Db, variable: Option>, ) -> impl Iterator> + 'a { - let variable = variable.unwrap_or(self.variable); - self.suffix_elements() + let variable = variable.unwrap_or(self.variable()); + self.iter_suffix_elements() .skip_while(move |element| element.is_equivalent_to(db, variable)) - .copied() } fn resize( @@ -772,9 +869,9 @@ impl<'db> VariableLengthTuple> { return Err(ResizeTupleError::TooManyValues); }; Ok(Tuple::Fixed(FixedLengthTuple::from_elements( - (self.prefix_elements().copied()) - .chain(std::iter::repeat_n(self.variable, variable_count)) - .chain(self.suffix_elements().copied()), + (self.iter_prefix_elements()) + .chain(std::iter::repeat_n(self.variable(), variable_count)) + .chain(self.iter_suffix_elements()), ))) } @@ -782,21 +879,22 @@ impl<'db> VariableLengthTuple> { // "Overflow" are elements of our prefix/suffix that will be folded into the // result's variable-length portion. "Underflow" are elements of the result // prefix/suffix that will come from our variable-length portion. - let self_prefix_length = self.prefix.len(); + let self_prefix_length = self.prefix_elements().len(); let prefix_underflow = prefix_length.saturating_sub(self_prefix_length); - let self_suffix_length = self.suffix.len(); + let self_suffix_length = self.suffix_elements().len(); let suffix_overflow = self_suffix_length.saturating_sub(suffix_length); let suffix_underflow = suffix_length.saturating_sub(self_suffix_length); - let prefix = (self.prefix_elements().copied().take(prefix_length)) - .chain(std::iter::repeat_n(self.variable, prefix_underflow)); + let prefix = (self.iter_prefix_elements().take(prefix_length)) + .chain(std::iter::repeat_n(self.variable(), prefix_underflow)); let variable = UnionType::from_elements_leave_aliases( db, - (self.prefix_elements().copied().skip(prefix_length)) - .chain(std::iter::once(self.variable)) - .chain(self.suffix_elements().copied().take(suffix_overflow)), + self.iter_prefix_elements() + .skip(prefix_length) + .chain(std::iter::once(self.variable())) + .chain(self.iter_suffix_elements().take(suffix_overflow)), ); - let suffix = std::iter::repeat_n(self.variable, suffix_underflow) - .chain(self.suffix_elements().copied().skip(suffix_overflow)); + let suffix = std::iter::repeat_n(self.variable(), suffix_underflow) + .chain(self.iter_suffix_elements().skip(suffix_overflow)); Ok(VariableLengthTuple::mixed(prefix, variable, suffix)) } } @@ -806,18 +904,12 @@ impl<'db> VariableLengthTuple> { fn normalized_impl(&self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> TupleSpec<'db> { let prefix = self .prenormalized_prefix_elements(db, None) - .map(|ty| ty.normalized_impl(db, visitor)) - .collect::>(); + .map(|ty| ty.normalized_impl(db, visitor)); let suffix = self .prenormalized_suffix_elements(db, None) - .map(|ty| ty.normalized_impl(db, visitor)) - .collect::>(); - let variable = self.variable.normalized_impl(db, visitor); - TupleSpec::Variable(Self { - prefix, - variable, - suffix, - }) + .map(|ty| ty.normalized_impl(db, visitor)); + let variable = self.variable().normalized_impl(db, visitor); + TupleSpec::Variable(Self::new(prefix, variable, suffix)) } fn recursive_type_normalized_impl( @@ -826,47 +918,40 @@ impl<'db> VariableLengthTuple> { div: Type<'db>, nested: bool, ) -> Option { - let prefix = if nested { - self.prefix + if nested { + let prefix = self + .prefix_elements() .iter() - .map(|ty| ty.recursive_type_normalized_impl(db, div, true)) - .collect::>>()? + .map(|ty| ty.recursive_type_normalized_impl(db, div, true)); + + let variable = self + .variable() + .recursive_type_normalized_impl(db, div, true)?; + + let suffix = self + .suffix_elements() + .iter() + .map(|ty| ty.recursive_type_normalized_impl(db, div, true)); + + Self::try_new(prefix, variable, suffix) } else { - self.prefix - .iter() - .map(|ty| { - ty.recursive_type_normalized_impl(db, div, true) - .unwrap_or(div) - }) - .collect::>() - }; - let suffix = if nested { - self.suffix - .iter() - .map(|ty| ty.recursive_type_normalized_impl(db, div, true)) - .collect::>>()? - } else { - self.suffix - .iter() - .map(|ty| { - ty.recursive_type_normalized_impl(db, div, true) - .unwrap_or(div) - }) - .collect::>() - }; - let variable = if nested { - self.variable - .recursive_type_normalized_impl(db, div, true)? - } else { - self.variable + let prefix = self.prefix_elements().iter().map(|ty| { + ty.recursive_type_normalized_impl(db, div, true) + .unwrap_or(div) + }); + + let variable = self + .variable() .recursive_type_normalized_impl(db, div, true) - .unwrap_or(div) - }; - Some(Self { - prefix, - variable, - suffix, - }) + .unwrap_or(div); + + let suffix = self.suffix_elements().iter().map(|ty| { + ty.recursive_type_normalized_impl(db, div, true) + .unwrap_or(div) + }); + + Some(Self::new(prefix, variable, suffix)) + } } fn apply_type_mapping_impl<'a>( @@ -877,12 +962,12 @@ impl<'db> VariableLengthTuple> { visitor: &ApplyTypeMappingVisitor<'db>, ) -> TupleSpec<'db> { Self::mixed( - self.prefix + self.prefix_elements() .iter() .map(|ty| ty.apply_type_mapping_impl(db, type_mapping, tcx, visitor)), - self.variable + self.variable() .apply_type_mapping_impl(db, type_mapping, tcx, visitor), - self.suffix + self.suffix_elements() .iter() .map(|ty| ty.apply_type_mapping_impl(db, type_mapping, tcx, visitor)), ) @@ -895,12 +980,12 @@ impl<'db> VariableLengthTuple> { typevars: &mut FxOrderSet>, visitor: &FindLegacyTypeVarsVisitor<'db>, ) { - for ty in &self.prefix { + for ty in self.prefix_elements() { ty.find_legacy_typevars_impl(db, binding_context, typevars, visitor); } - self.variable + self.variable() .find_legacy_typevars_impl(db, binding_context, typevars, visitor); - for ty in &self.suffix { + for ty in self.suffix_elements() { ty.find_legacy_typevars_impl(db, binding_context, typevars, visitor); } } @@ -926,7 +1011,7 @@ impl<'db> VariableLengthTuple> { // (or any other dynamic type), then the `...` is the _gradual choice_ of all // possible lengths. This means that `tuple[Any, ...]` can match any tuple of any // length. - if !relation.is_assignability() || !self.variable.is_dynamic() { + if !relation.is_assignability() || !self.variable().is_dynamic() { return ConstraintSet::from(false); } @@ -934,7 +1019,7 @@ impl<'db> VariableLengthTuple> { // tuple's prefix and suffix, and each of those elements must pairwise satisfy the // relation. let mut result = ConstraintSet::from(true); - let mut other_iter = other.elements().copied(); + let mut other_iter = other.iter_all_elements(); for self_ty in self.prenormalized_prefix_elements(db, None) { let Some(other_ty) = other_iter.next() else { return ConstraintSet::from(false); @@ -981,12 +1066,12 @@ impl<'db> VariableLengthTuple> { Tuple::Variable(other) => { // When prenormalizing below, we assume that a dynamic variable-length portion of // one tuple materializes to the variable-length portion of the other tuple. - let self_prenormalize_variable = match self.variable { - Type::Dynamic(_) => Some(other.variable), + let self_prenormalize_variable = match self.variable() { + Type::Dynamic(_) => Some(other.variable()), _ => None, }; - let other_prenormalize_variable = match other.variable { - Type::Dynamic(_) => Some(self.variable), + let other_prenormalize_variable = match other.variable() { + Type::Dynamic(_) => Some(self.variable()), _ => None, }; @@ -994,7 +1079,8 @@ impl<'db> VariableLengthTuple> { // Any remaining parts must satisfy the relation with the other tuple's // variable-length part. let mut result = ConstraintSet::from(true); - let pairwise = (self.prenormalized_prefix_elements(db, self_prenormalize_variable)) + let pairwise = self + .prenormalized_prefix_elements(db, self_prenormalize_variable) .zip_longest( other.prenormalized_prefix_elements(db, other_prenormalize_variable), ); @@ -1010,7 +1096,7 @@ impl<'db> VariableLengthTuple> { ), EitherOrBoth::Left(self_ty) => self_ty.has_relation_to_impl( db, - other.variable, + other.variable(), inferable, relation, relation_visitor, @@ -1021,10 +1107,10 @@ impl<'db> VariableLengthTuple> { // provide, unless the lhs has a dynamic variable-length portion // that can materialize to provide it (for assignability only), // as in `tuple[Any, ...]` matching `tuple[int, int]`. - if !relation.is_assignability() || !self.variable.is_dynamic() { + if !relation.is_assignability() || !self.variable().is_dynamic() { return ConstraintSet::from(false); } - self.variable.has_relation_to_impl( + self.variable().has_relation_to_impl( db, other_ty, inferable, @@ -1048,7 +1134,10 @@ impl<'db> VariableLengthTuple> { let other_suffix: Vec<_> = other .prenormalized_suffix_elements(db, other_prenormalize_variable) .collect(); - let pairwise = (self_suffix.iter().rev()).zip_longest(other_suffix.iter().rev()); + let pairwise = self_suffix + .iter() + .rev() + .zip_longest(other_suffix.iter().rev()); for pair in pairwise { let pair_constraints = match pair { EitherOrBoth::Both(self_ty, other_ty) => self_ty.has_relation_to_impl( @@ -1061,7 +1150,7 @@ impl<'db> VariableLengthTuple> { ), EitherOrBoth::Left(self_ty) => self_ty.has_relation_to_impl( db, - other.variable, + other.variable(), inferable, relation, relation_visitor, @@ -1072,10 +1161,10 @@ impl<'db> VariableLengthTuple> { // provide, unless the lhs has a dynamic variable-length portion // that can materialize to provide it (for assignability only), // as in `tuple[Any, ...]` matching `tuple[int, int]`. - if !relation.is_assignability() || !self.variable.is_dynamic() { + if !relation.is_assignability() || !self.variable().is_dynamic() { return ConstraintSet::from(false); } - self.variable.has_relation_to_impl( + self.variable().has_relation_to_impl( db, *other_ty, inferable, @@ -1095,9 +1184,9 @@ impl<'db> VariableLengthTuple> { // And lastly, the variable-length portions must satisfy the relation. result.and(db, || { - self.variable.has_relation_to_impl( + self.variable().has_relation_to_impl( db, - other.variable, + other.variable(), inferable, relation, relation_visitor, @@ -1115,10 +1204,10 @@ impl<'db> VariableLengthTuple> { inferable: InferableTypeVars<'_, 'db>, visitor: &IsEquivalentVisitor<'db>, ) -> ConstraintSet<'db> { - self.variable - .is_equivalent_to_impl(db, other.variable, inferable, visitor) + self.variable() + .is_equivalent_to_impl(db, other.variable(), inferable, visitor) .and(db, || { - (self.prenormalized_prefix_elements(db, None)) + 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) => { @@ -1130,7 +1219,7 @@ impl<'db> VariableLengthTuple> { }) }) .and(db, || { - (self.prenormalized_suffix_elements(db, None)) + self.prenormalized_suffix_elements(db, None) .zip_longest(other.prenormalized_suffix_elements(db, None)) .when_all(db, |pair| match pair { EitherOrBoth::Both(self_ty, other_ty) => { @@ -1150,7 +1239,7 @@ impl<'db> PyIndex<'db> for &VariableLengthTuple> { fn py_index(self, db: &'db dyn Db, index: i32) -> Result { match Nth::from_index(index) { Nth::FromStart(index) => { - if let Some(element) = self.prefix.get(index) { + if let Some(element) = self.prefix_elements().get(index) { // index is small enough that it lands in the prefix of the tuple. return Ok(*element); } @@ -1158,30 +1247,36 @@ impl<'db> PyIndex<'db> for &VariableLengthTuple> { // index is large enough that it lands past the prefix. The tuple can always be // large enough that it lands in the variable-length portion. It might also be // small enough to land in the suffix. - let index_past_prefix = index - self.prefix.len() + 1; + let index_past_prefix = index - self.prefix_elements().len() + 1; Ok(UnionType::from_elements_leave_aliases( db, - std::iter::once(self.variable) - .chain(self.suffix_elements().copied().take(index_past_prefix)), + std::iter::once(self.variable()).chain( + self.suffix_elements() + .iter() + .take(index_past_prefix) + .copied(), + ), )) } Nth::FromEnd(index_from_end) => { - if index_from_end < self.suffix.len() { + if index_from_end < self.suffix_elements().len() { // index is small enough that it lands in the suffix of the tuple. - return Ok(self.suffix[self.suffix.len() - index_from_end - 1]); + return Ok( + self.suffix_elements()[self.suffix_elements().len() - index_from_end - 1] + ); } // index is large enough that it lands past the suffix. The tuple can always be // large enough that it lands in the variable-length portion. It might also be // small enough to land in the prefix. - let index_past_suffix = index_from_end - self.suffix.len() + 1; + let index_past_suffix = index_from_end - self.suffix_elements().len() + 1; Ok(UnionType::from_elements_leave_aliases( db, - (self.prefix_elements().rev().copied()) + (self.prefix_elements().iter().rev().copied()) .take(index_past_suffix) .rev() - .chain(std::iter::once(self.variable)), + .chain(std::iter::once(self.variable())), )) } } @@ -1199,8 +1294,8 @@ pub enum Tuple { } impl Tuple { - pub(crate) fn homogeneous(element: T) -> Self { - VariableLengthTuple::homogeneous(element) + pub(crate) const fn homogeneous(element: T) -> Self { + Self::Variable(VariableLengthTuple::homogeneous(element)) } pub(crate) fn heterogeneous(elements: impl IntoIterator) -> Self { @@ -1208,30 +1303,40 @@ impl Tuple { } /// Returns the variable-length element of this tuple, if it has one. - pub(crate) fn variable_element(&self) -> Option<&T> { + pub(crate) fn variable_element(&self) -> Option<&T> + where + T: Copy, + { match self { Tuple::Fixed(_) => None, - Tuple::Variable(tuple) => Some(&tuple.variable), + Tuple::Variable(tuple) => Some(tuple.variable_element()), } } /// Returns an iterator of all of the fixed-length element types of this tuple. pub(crate) fn fixed_elements(&self) -> impl Iterator + '_ { match self { - Tuple::Fixed(tuple) => Either::Left(tuple.elements()), + Tuple::Fixed(tuple) => Either::Left(tuple.all_elements().iter()), Tuple::Variable(tuple) => Either::Right(tuple.fixed_elements()), } } /// Returns an iterator of all of the element types of this tuple. Does not deduplicate the /// elements, and does not distinguish between fixed- and variable-length elements. - pub(crate) fn all_elements(&self) -> impl Iterator + '_ { + pub(crate) fn all_elements(&self) -> &[T] { match self { - Tuple::Fixed(tuple) => Either::Left(tuple.all_elements()), - Tuple::Variable(tuple) => Either::Right(tuple.all_elements()), + Tuple::Fixed(tuple) => tuple.all_elements(), + Tuple::Variable(tuple) => tuple.all_elements(), } } + pub(crate) fn iter_all_elements(&self) -> impl DoubleEndedIterator + '_ + where + T: Copy, + { + self.all_elements().iter().copied() + } + pub(crate) fn into_all_elements_with_kind(self) -> impl Iterator> { match self { Tuple::Fixed(tuple) => Either::Left(tuple.into_all_elements_with_kind()), @@ -1408,34 +1513,53 @@ impl<'db> Tuple> { #[allow(clippy::items_after_statements)] fn any_disjoint<'s, 'db>( db: &'db dyn Db, - a: impl IntoIterator>, - b: impl IntoIterator>, + a: &'s [Type<'db>], + b: &'s [Type<'db>], inferable: InferableTypeVars<'_, 'db>, disjointness_visitor: &IsDisjointVisitor<'db>, relation_visitor: &HasRelationToVisitor<'db>, + rev: bool, ) -> ConstraintSet<'db> where 'db: 's, { - (a.into_iter().zip(b)).when_any(db, |(self_element, other_element)| { - self_element.is_disjoint_from_impl( - db, - *other_element, - inferable, - disjointness_visitor, - relation_visitor, - ) - }) + if rev { + a.iter() + .zip(b) + .when_any(db, |(self_element, other_element)| { + self_element.is_disjoint_from_impl( + db, + *other_element, + inferable, + disjointness_visitor, + relation_visitor, + ) + }) + } else { + a.iter() + .rev() + .zip(b.iter().rev()) + .when_any(db, |(self_element, other_element)| { + self_element.is_disjoint_from_impl( + db, + *other_element, + inferable, + disjointness_visitor, + relation_visitor, + ) + }) + } } match (self, other) { (Tuple::Fixed(self_tuple), Tuple::Fixed(other_tuple)) => any_disjoint( db, - self_tuple.elements(), - other_tuple.elements(), + self_tuple.all_elements(), + other_tuple.all_elements(), inferable, disjointness_visitor, relation_visitor, + false, ), // Note that we don't compare the variable-length portions; two pure homogeneous tuples @@ -1448,35 +1572,39 @@ impl<'db> Tuple> { inferable, disjointness_visitor, relation_visitor, + false, ) .or(db, || { any_disjoint( db, - self_tuple.suffix_elements().rev(), - other_tuple.suffix_elements().rev(), + self_tuple.suffix_elements(), + other_tuple.suffix_elements(), inferable, disjointness_visitor, relation_visitor, + true, ) }), (Tuple::Fixed(fixed), Tuple::Variable(variable)) | (Tuple::Variable(variable), Tuple::Fixed(fixed)) => any_disjoint( db, - fixed.elements(), + fixed.all_elements(), variable.prefix_elements(), inferable, disjointness_visitor, relation_visitor, + false, ) .or(db, || { any_disjoint( db, - fixed.elements().rev(), - variable.suffix_elements().rev(), + fixed.all_elements(), + variable.suffix_elements(), inferable, disjointness_visitor, relation_visitor, + true, ) }), } @@ -1623,7 +1751,7 @@ impl<'db> TupleUnpacker<'db> { impl<'db> FixedLengthTuple> { fn unpack_tuple(&mut self, values: &FixedLengthTuple>) { // We have already verified above that the two tuples have the same length. - for (target, value) in self.0.iter_mut().zip(values.elements().copied()) { + for (target, value) in self.0.iter_mut().zip(values.iter_all_elements()) { target.add_in_place(value); } } @@ -1632,12 +1760,16 @@ impl<'db> FixedLengthTuple> { impl<'db> VariableLengthTuple> { fn unpack_tuple(&mut self, db: &'db dyn Db, values: &VariableLengthTuple>) { // We have already verified above that the two tuples have the same length. - for (target, value) in (self.prefix.iter_mut()).zip(values.prefix_elements().copied()) { + for (target, value) in + (self.prefix_elements_mut().iter_mut()).zip(values.iter_prefix_elements()) + { target.add_in_place(value); } - self.variable - .add_in_place(KnownClass::List.to_specialized_instance(db, [values.variable])); - for (target, value) in (self.suffix.iter_mut()).zip(values.suffix_elements().copied()) { + self.variable_element_mut() + .add_in_place(KnownClass::List.to_specialized_instance(db, [values.variable()])); + for (target, value) in + (self.suffix_elements_mut().iter_mut()).zip(values.iter_suffix_elements()) + { target.add_in_place(value); } } @@ -1680,19 +1812,12 @@ impl<'db> TupleSpecBuilder<'db> { self } - ( - TupleSpecBuilder::Fixed(left_tuple), - TupleSpec::Variable(VariableLengthTuple { - prefix, - variable, - suffix, - }), - ) => { - left_tuple.extend_from_slice(prefix); + (TupleSpecBuilder::Fixed(left_tuple), TupleSpec::Variable(variable_tuple)) => { + left_tuple.extend_from_slice(variable_tuple.prefix_elements()); TupleSpecBuilder::Variable { prefix: std::mem::take(left_tuple), - variable: *variable, - suffix: suffix.to_vec(), + variable: variable_tuple.variable(), + suffix: variable_tuple.suffix_elements().to_vec(), } } @@ -1714,23 +1839,19 @@ impl<'db> TupleSpecBuilder<'db> { variable: left_variable, suffix: left_suffix, }, - TupleSpec::Variable(VariableLengthTuple { - prefix: right_prefix, - variable: right_variable, - suffix: right_suffix, - }), + TupleSpec::Variable(right), ) => { let variable = UnionType::from_elements_leave_aliases( db, left_suffix .iter() - .chain([left_variable, right_variable]) - .chain(right_prefix), + .chain([left_variable, &right.variable()]) + .chain(right.prefix_elements()), ); TupleSpecBuilder::Variable { prefix: std::mem::take(left_prefix), variable, - suffix: right_suffix.to_vec(), + suffix: right.suffix_elements().to_vec(), } } } @@ -1764,7 +1885,7 @@ impl<'db> TupleSpecBuilder<'db> { (TupleSpecBuilder::Fixed(our_elements), TupleSpec::Fixed(new_elements)) if our_elements.len() == new_elements.len() => { - for (existing, new) in our_elements.iter_mut().zip(new_elements.elements()) { + for (existing, new) in our_elements.iter_mut().zip(new_elements.all_elements()) { *existing = UnionType::from_elements_leave_aliases(db, [*existing, *new]); } self @@ -1802,7 +1923,7 @@ impl<'db> TupleSpecBuilder<'db> { (TupleSpecBuilder::Fixed(our_elements), TupleSpec::Fixed(new_elements)) if our_elements.len() == new_elements.len() => { - for (existing, new) in our_elements.iter_mut().zip(new_elements.elements()) { + for (existing, new) in our_elements.iter_mut().zip(new_elements.all_elements()) { *existing = IntersectionType::from_elements(db, [*existing, *new]); } return self; @@ -1832,11 +1953,13 @@ impl<'db> TupleSpecBuilder<'db> { }, TupleSpec::Variable(var), ) => { - if prefix.len() == var.prefix.len() && suffix.len() == var.suffix.len() { + if prefix.len() == var.prefix_elements().len() + && suffix.len() == var.suffix_elements().len() + { for (existing, new) in prefix.iter_mut().zip(var.prefix_elements()) { *existing = IntersectionType::from_elements(db, [*existing, *new]); } - *variable = IntersectionType::from_elements(db, [*variable, var.variable]); + *variable = IntersectionType::from_elements(db, [*variable, var.variable()]); for (existing, new) in suffix.iter_mut().zip(var.suffix_elements()) { *existing = IntersectionType::from_elements(db, [*existing, *new]); } @@ -1875,11 +1998,7 @@ impl<'db> TupleSpecBuilder<'db> { prefix, variable, suffix, - } => TupleSpec::Variable(VariableLengthTuple { - prefix: prefix.into_boxed_slice(), - variable, - suffix: suffix.into_boxed_slice(), - }), + } => TupleSpec::Variable(VariableLengthTuple::new(prefix, variable, suffix)), } } } @@ -1889,9 +2008,9 @@ impl<'db> From<&TupleSpec<'db>> for TupleSpecBuilder<'db> { match tuple { TupleSpec::Fixed(fixed) => TupleSpecBuilder::Fixed(fixed.0.to_vec()), TupleSpec::Variable(variable) => TupleSpecBuilder::Variable { - prefix: variable.prefix.to_vec(), - variable: variable.variable, - suffix: variable.suffix.to_vec(), + prefix: variable.prefix_elements().to_vec(), + variable: variable.variable(), + suffix: variable.suffix_elements().to_vec(), }, } }