mirror of
https://github.com/astral-sh/ruff
synced 2026-01-21 05:20:49 -05:00
[ty] Return slices for Tuple methods (#22192)
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)?;
|
||||
}
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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(_)
|
||||
|
||||
@@ -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)),
|
||||
)
|
||||
}),
|
||||
|
||||
|
||||
@@ -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(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user