[ty] Return slices for Tuple methods (#22192)

This commit is contained in:
Micha Reiser
2025-12-27 10:30:34 +01:00
committed by GitHub
parent 6342cec842
commit 5d32ab8175
11 changed files with 450 additions and 318 deletions

View File

@@ -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 {

View File

@@ -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<Vec<Type<'db>>> {
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()

View File

@@ -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

View File

@@ -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(),

View File

@@ -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 {

View File

@@ -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)?;
}

View File

@@ -208,7 +208,7 @@ pub struct GenericContext<'db> {
variables_inner: FxOrderMap<BoundTypeVarIdentity<'db>, 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)?;

View File

@@ -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::<Vec<_>>();
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());

View File

@@ -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(_)

View File

@@ -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)),
)
}),

View File

@@ -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<T> FixedLengthTuple<T> {
self.0
}
pub(crate) fn elements(&self) -> impl DoubleEndedIterator<Item = &T> + ExactSizeIterator + '_ {
self.0.iter()
pub(crate) fn all_elements(&self) -> &[T] {
&self.0
}
pub(crate) fn all_elements(&self) -> impl Iterator<Item = &T> {
self.0.iter()
pub(crate) fn iter_all_elements(&self) -> impl DoubleEndedIterator<Item = T>
where
T: Copy,
{
self.0.iter().copied()
}
pub(crate) fn into_all_elements_with_kind(self) -> impl Iterator<Item = TupleElement<T>> {
@@ -409,16 +415,12 @@ impl<'db> FixedLengthTuple<Type<'db>> {
// 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<Type<'db>> {
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<Type<'db>> {
// 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<Type<'db>> {
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<Type<'db>> {
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<Type<'db>> {
/// types, use [`TupleSpec`], which defines some additional type-specific methods.
#[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize, salsa::Update)]
pub struct VariableLengthTuple<T> {
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<T> VariableLengthTuple<T> {
/// Creates a new tuple spec containing zero or more elements of a given type, with no prefix
/// or suffix.
fn homogeneous(ty: T) -> Tuple<T> {
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<T> VariableLengthTuple<T> {
variable: T,
suffix: impl IntoIterator<Item = T>,
) -> Tuple<T> {
Tuple::Variable(Self {
prefix: prefix.into_iter().collect(),
variable,
suffix: suffix.into_iter().collect(),
Tuple::Variable(Self::new(prefix, variable, suffix))
}
fn try_new<P, S>(prefix: P, variable: T, suffix: S) -> Option<Self>
where
P: IntoIterator<Item = Option<T>>,
P::IntoIter: ExactSizeIterator,
S: IntoIterator<Item = Option<T>>,
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<Item = &T> + ExactSizeIterator + '_ {
self.prefix.iter()
fn new(
prefix: impl IntoIterator<Item = T>,
variable: T,
suffix: impl IntoIterator<Item = T>,
) -> 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<Item = &T> + 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<Item = T>
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<Item = T>
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<Item = &T> + '_ {
self.prefix_elements().chain(self.suffix_elements())
self.prefix_elements().iter().chain(self.suffix_elements())
}
fn all_elements(&self) -> impl Iterator<Item = &T> + '_ {
(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<Item = TupleElement<T>> {
(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<Type<'db>> {
db: &'db dyn Db,
variable: Option<Type<'db>>,
) -> impl Iterator<Item = Type<'db>> + '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<Type<'db>> {
db: &'db dyn Db,
variable: Option<Type<'db>>,
) -> impl Iterator<Item = Type<'db>> + '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<Type<'db>> {
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<Type<'db>> {
// "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<Type<'db>> {
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::<Box<_>>();
.map(|ty| ty.normalized_impl(db, visitor));
let suffix = self
.prenormalized_suffix_elements(db, None)
.map(|ty| ty.normalized_impl(db, visitor))
.collect::<Box<_>>();
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<Type<'db>> {
div: Type<'db>,
nested: bool,
) -> Option<Self> {
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::<Option<Box<_>>>()?
.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::<Box<_>>()
};
let suffix = if nested {
self.suffix
.iter()
.map(|ty| ty.recursive_type_normalized_impl(db, div, true))
.collect::<Option<Box<_>>>()?
} else {
self.suffix
.iter()
.map(|ty| {
ty.recursive_type_normalized_impl(db, div, true)
.unwrap_or(div)
})
.collect::<Box<_>>()
};
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<Type<'db>> {
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<Type<'db>> {
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
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<Type<'db>> {
// (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<Type<'db>> {
// 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<Type<'db>> {
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<Type<'db>> {
// 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<Type<'db>> {
),
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<Type<'db>> {
// 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<Type<'db>> {
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<Type<'db>> {
),
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<Type<'db>> {
// 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<Type<'db>> {
// 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<Type<'db>> {
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<Type<'db>> {
})
})
.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<Type<'db>> {
fn py_index(self, db: &'db dyn Db, index: i32) -> Result<Self::Item, OutOfBoundsError> {
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<Type<'db>> {
// 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<T> {
}
impl<T> Tuple<T> {
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<Item = T>) -> Self {
@@ -1208,30 +1303,40 @@ impl<T> Tuple<T> {
}
/// 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<Item = &T> + '_ {
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<Item = &T> + '_ {
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<Item = T> + '_
where
T: Copy,
{
self.all_elements().iter().copied()
}
pub(crate) fn into_all_elements_with_kind(self) -> impl Iterator<Item = TupleElement<T>> {
match self {
Tuple::Fixed(tuple) => Either::Left(tuple.into_all_elements_with_kind()),
@@ -1408,34 +1513,53 @@ impl<'db> Tuple<Type<'db>> {
#[allow(clippy::items_after_statements)]
fn any_disjoint<'s, 'db>(
db: &'db dyn Db,
a: impl IntoIterator<Item = &'s Type<'db>>,
b: impl IntoIterator<Item = &'s Type<'db>>,
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<Type<'db>> {
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<UnionBuilder<'db>> {
fn unpack_tuple(&mut self, values: &FixedLengthTuple<Type<'db>>) {
// 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<UnionBuilder<'db>> {
impl<'db> VariableLengthTuple<UnionBuilder<'db>> {
fn unpack_tuple(&mut self, db: &'db dyn Db, values: &VariableLengthTuple<Type<'db>>) {
// 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(),
},
}
}