[ty] Various minor cleanups to tuple internals (#19891)

This commit is contained in:
Alex Waygood 2025-08-13 14:46:22 +01:00 committed by GitHub
parent 2f3c7ad1fc
commit 5725c4b17f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 57 additions and 60 deletions

View file

@ -4826,7 +4826,7 @@ impl<'db> Type<'db> {
// with each individual character, instead of just an array of
// `LiteralString`, but there would be a cost and it's not clear that
// it's worth it.
return Ok(Cow::Owned(TupleSpec::from_elements(std::iter::repeat_n(
return Ok(Cow::Owned(TupleSpec::heterogeneous(std::iter::repeat_n(
Type::LiteralString,
string_literal_ty.python_len(db),
))));

View file

@ -237,7 +237,7 @@ fn expand_type<'db>(db: &'db dyn Db, ty: Type<'db>) -> Option<Vec<Type<'db>>> {
}
})
.multi_cartesian_product()
.map(|types| Type::tuple(TupleType::from_elements(db, types)))
.map(|types| Type::tuple(TupleType::heterogeneous(db, types)))
.collect::<Vec<_>>();
if expanded.len() == 1 {

View file

@ -1349,7 +1349,7 @@ impl<'db> ClassLiteral<'db> {
semantic_index(db, self.file(db)).expect_single_definition(class_stmt);
if self.is_known(db, KnownClass::VersionInfo) {
let tuple_type = TupleType::new(db, TupleSpec::version_info_spec(db))
let tuple_type = TupleType::new(db, &TupleSpec::version_info_spec(db))
.expect("sys.version_info tuple spec should always be a valid tuple");
Box::new([

View file

@ -241,7 +241,7 @@ impl<'db> GenericContext<'db> {
db,
self,
partial.types(db),
TupleType::homogeneous(db, Type::unknown()),
Some(TupleType::homogeneous(db, Type::unknown())),
)
} else {
partial

View file

@ -8561,8 +8561,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
}
let tuple_generic_alias = |db: &'db dyn Db, tuple: Option<TupleType<'db>>| {
let tuple =
tuple.unwrap_or_else(|| TupleType::homogeneous(db, Type::unknown()).unwrap());
let tuple = tuple.unwrap_or_else(|| TupleType::homogeneous(db, Type::unknown()));
Type::from(tuple.to_class_type(db))
};
@ -10075,8 +10074,8 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
self.infer_expression(ellipsis);
let result =
TupleType::homogeneous(self.db(), self.infer_type_expression(element));
self.store_expression_type(tuple_slice, Type::tuple(result));
return result;
self.store_expression_type(tuple_slice, Type::tuple(Some(result)));
return Some(result);
}
let mut element_types = TupleSpecBuilder::with_capacity(elements.len());
@ -10102,9 +10101,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
}
let ty = if return_todo {
TupleType::homogeneous(self.db(), todo_type!("PEP 646"))
Some(TupleType::homogeneous(self.db(), todo_type!("PEP 646")))
} else {
TupleType::new(self.db(), element_types.build())
TupleType::new(self.db(), &element_types.build())
};
// Here, we store the type for the inner `int, str` tuple-expression,
@ -10118,9 +10117,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
let single_element_ty = self.infer_type_expression(single_element);
if element_could_alter_type_of_whole_tuple(single_element, single_element_ty, self)
{
TupleType::homogeneous(self.db(), todo_type!("PEP 646"))
Some(TupleType::homogeneous(self.db(), todo_type!("PEP 646")))
} else {
TupleType::from_elements(self.db(), std::iter::once(single_element_ty))
TupleType::heterogeneous(self.db(), std::iter::once(single_element_ty))
}
}
}

View file

@ -41,6 +41,30 @@ impl<'db> Type<'db> {
let Some(tuple) = tuple else {
return Type::Never;
};
Type::tuple_instance(tuple)
}
pub(crate) fn homogeneous_tuple(db: &'db dyn Db, element: Type<'db>) -> Self {
Type::tuple_instance(TupleType::homogeneous(db, element))
}
pub(crate) fn heterogeneous_tuple<I, T>(db: &'db dyn Db, elements: I) -> Self
where
I: IntoIterator<Item = T>,
T: Into<Type<'db>>,
{
Type::tuple(TupleType::heterogeneous(
db,
elements.into_iter().map(Into::into),
))
}
pub(crate) fn empty_tuple(db: &'db dyn Db) -> Self {
Type::tuple_instance(TupleType::empty(db))
}
/// **Private** helper function to create a `Type::NominalInstance` from a tuple.
fn tuple_instance(tuple: TupleType<'db>) -> Self {
Type::NominalInstance(NominalInstanceType(NominalInstanceInner::ExactTuple(tuple)))
}
@ -275,11 +299,9 @@ impl<'db> NominalInstanceType<'db> {
other: Self,
visitor: &PairVisitor<'db>,
) -> bool {
let self_spec = self.tuple_spec(db);
if let Some(self_spec) = self_spec.as_deref() {
let other_spec = other.tuple_spec(db);
if let Some(other_spec) = other_spec.as_deref() {
if self_spec.is_disjoint_from_impl(db, other_spec, visitor) {
if let Some(self_spec) = self.tuple_spec(db) {
if let Some(other_spec) = other.tuple_spec(db) {
if self_spec.is_disjoint_from_impl(db, &other_spec, visitor) {
return true;
}
}

View file

@ -16,7 +16,6 @@
//! that adds that "collapse `Never`" behavior, whereas [`TupleSpec`] allows you to add any element
//! types, including `Never`.)
use std::borrow::Borrow;
use std::cmp::Ordering;
use std::hash::Hash;
@ -145,44 +144,18 @@ pub(super) fn walk_tuple_type<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>
// The Salsa heap is tracked separately.
impl get_size2::GetSize for TupleType<'_> {}
impl<'db> Type<'db> {
pub(crate) fn homogeneous_tuple(db: &'db dyn Db, element: Type<'db>) -> Self {
Type::tuple(TupleType::homogeneous(db, element))
}
pub(crate) fn heterogeneous_tuple<I, T>(db: &'db dyn Db, elements: I) -> Self
where
I: IntoIterator<Item = T>,
T: Into<Type<'db>>,
{
Type::tuple(TupleType::from_elements(
db,
elements.into_iter().map(Into::into),
))
}
pub(crate) fn empty_tuple(db: &'db dyn Db) -> Self {
Type::tuple(Some(TupleType::empty(db)))
}
}
#[salsa::tracked]
impl<'db> TupleType<'db> {
pub(crate) fn new<T>(db: &'db dyn Db, tuple_key: T) -> Option<Self>
where
T: Borrow<TupleSpec<'db>> + Hash + salsa::plumbing::interned::Lookup<TupleSpec<'db>>,
TupleSpec<'db>: salsa::plumbing::interned::HashEqLike<T>,
{
pub(crate) fn new(db: &'db dyn Db, spec: &TupleSpec<'db>) -> Option<Self> {
// If a fixed-length (i.e., mandatory) element of the tuple is `Never`, then it's not
// possible to instantiate the tuple as a whole.
let tuple = tuple_key.borrow();
if tuple.fixed_elements().any(Type::is_never) {
if spec.fixed_elements().any(Type::is_never) {
return None;
}
// 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) = tuple {
if let TupleSpec::Variable(tuple) = spec {
if tuple.variable.is_never() {
let tuple = TupleSpec::Fixed(FixedLengthTuple::from_elements(
tuple.prefix.iter().chain(&tuple.suffix).copied(),
@ -191,19 +164,18 @@ impl<'db> TupleType<'db> {
}
}
Some(TupleType::new_internal(db, tuple_key))
Some(TupleType::new_internal(db, spec))
}
pub(crate) fn empty(db: &'db dyn Db) -> Self {
TupleType::new(db, TupleSpec::from(FixedLengthTuple::empty()))
.expect("TupleType::new() should always return `Some` for an empty `TupleSpec`")
TupleType::new_internal(db, TupleSpec::from(FixedLengthTuple::empty()))
}
pub(crate) fn from_elements(
pub(crate) fn heterogeneous(
db: &'db dyn Db,
types: impl IntoIterator<Item = Type<'db>>,
) -> Option<Self> {
TupleType::new(db, TupleSpec::from_elements(types))
TupleType::new(db, &TupleSpec::heterogeneous(types))
}
#[cfg(test)]
@ -213,11 +185,14 @@ impl<'db> TupleType<'db> {
variable: Type<'db>,
suffix: impl IntoIterator<Item = Type<'db>>,
) -> Option<Self> {
TupleType::new(db, VariableLengthTuple::mixed(prefix, variable, suffix))
TupleType::new(db, &VariableLengthTuple::mixed(prefix, variable, suffix))
}
pub(crate) fn homogeneous(db: &'db dyn Db, element: Type<'db>) -> Option<Self> {
TupleType::new(db, TupleSpec::homogeneous(element))
pub(crate) fn homogeneous(db: &'db dyn Db, element: Type<'db>) -> Self {
match element {
Type::Never => TupleType::empty(db),
_ => TupleType::new_internal(db, TupleSpec::homogeneous(element)),
}
}
// N.B. If this method is not Salsa-tracked, we take 10 minutes to check
@ -248,11 +223,11 @@ impl<'db> TupleType<'db> {
db: &'db dyn Db,
visitor: &TypeTransformer<'db>,
) -> Option<Self> {
TupleType::new(db, self.tuple(db).normalized_impl(db, visitor))
TupleType::new(db, &self.tuple(db).normalized_impl(db, visitor))
}
pub(crate) fn materialize(self, db: &'db dyn Db, variance: TypeVarVariance) -> Option<Self> {
TupleType::new(db, self.tuple(db).materialize(db, variance))
TupleType::new(db, &self.tuple(db).materialize(db, variance))
}
pub(crate) fn apply_type_mapping_impl<'a>(
@ -263,7 +238,8 @@ impl<'db> TupleType<'db> {
) -> Option<Self> {
TupleType::new(
db,
self.tuple(db)
&self
.tuple(db)
.apply_type_mapping_impl(db, type_mapping, visitor),
)
}
@ -935,7 +911,7 @@ impl<T> Tuple<T> {
VariableLengthTuple::homogeneous(element)
}
pub(crate) fn from_elements(elements: impl IntoIterator<Item = T>) -> Self {
pub(crate) fn heterogeneous(elements: impl IntoIterator<Item = T>) -> Self {
FixedLengthTuple::from_elements(elements).into()
}
@ -1182,7 +1158,7 @@ impl<'db> Tuple<Type<'db>> {
Type::Union(UnionType::new(db, elements))
};
TupleSpec::from_elements([
TupleSpec::heterogeneous([
Type::IntLiteral(python_version.major.into()),
Type::IntLiteral(python_version.minor.into()),
int_instance_ty,