Skip to content

Commit 1efc650

Browse files
committed
[ty] Reduce memory usage of TupleSpec and TupleType
1 parent f34b65b commit 1efc650

File tree

2 files changed

+136
-79
lines changed

2 files changed

+136
-79
lines changed

crates/ty_python_semantic/src/types/infer.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ use crate::types::generics::{GenericContext, bind_typevar};
114114
use crate::types::instance::SliceLiteral;
115115
use crate::types::mro::MroErrorKind;
116116
use crate::types::signatures::{CallableSignature, Signature};
117-
use crate::types::tuple::{Tuple, TupleSpec, TupleType};
117+
use crate::types::tuple::{Tuple, TupleSpec, TupleSpecBuilder, TupleType};
118118
use crate::types::unpacker::{UnpackResult, Unpacker};
119119
use crate::types::{
120120
CallDunderError, CallableType, ClassLiteral, ClassType, DataclassParams, DynamicType,
@@ -10042,7 +10042,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
1004210042
return result;
1004310043
}
1004410044

10045-
let mut element_types = TupleSpec::with_capacity(elements.len());
10045+
let mut element_types = TupleSpecBuilder::with_capacity(elements.len());
1004610046

1004710047
// Whether to infer `Todo` for the whole tuple
1004810048
// (see docstring for `element_could_alter_type_of_whole_tuple`)
@@ -10067,7 +10067,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
1006710067
let ty = if return_todo {
1006810068
TupleType::homogeneous(self.db(), todo_type!("PEP 646"))
1006910069
} else {
10070-
TupleType::new(self.db(), element_types)
10070+
TupleType::new(self.db(), element_types.build())
1007110071
};
1007210072

1007310073
// Here, we store the type for the inner `int, str` tuple-expression,

crates/ty_python_semantic/src/types/tuple.rs

Lines changed: 133 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -303,15 +303,11 @@ pub(crate) type TupleSpec<'db> = Tuple<Type<'db>>;
303303
/// Our tuple representation can hold instances of any Rust type. For tuples containing Python
304304
/// types, use [`TupleSpec`], which defines some additional type-specific methods.
305305
#[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
306-
pub struct FixedLengthTuple<T>(Vec<T>);
306+
pub struct FixedLengthTuple<T>(Box<[T]>);
307307

308308
impl<T> FixedLengthTuple<T> {
309309
fn empty() -> Self {
310-
Self(Vec::new())
311-
}
312-
313-
pub(crate) fn with_capacity(capacity: usize) -> Self {
314-
Self(Vec::with_capacity(capacity))
310+
Self(Box::default())
315311
}
316312

317313
fn from_elements(elements: impl IntoIterator<Item = T>) -> Self {
@@ -338,27 +334,9 @@ impl<T> FixedLengthTuple<T> {
338334
pub(crate) fn len(&self) -> usize {
339335
self.0.len()
340336
}
341-
342-
pub(crate) fn push(&mut self, element: T) {
343-
self.0.push(element);
344-
}
345337
}
346338

347339
impl<'db> FixedLengthTuple<Type<'db>> {
348-
fn concat(&self, other: &Tuple<Type<'db>>) -> Tuple<Type<'db>> {
349-
match other {
350-
TupleSpec::Fixed(other) => TupleSpec::Fixed(FixedLengthTuple::from_elements(
351-
self.elements().chain(other.elements()).copied(),
352-
)),
353-
354-
TupleSpec::Variable(other) => VariableLengthTuple::mixed(
355-
self.elements().chain(other.prefix_elements()).copied(),
356-
other.variable,
357-
other.suffix_elements().copied(),
358-
),
359-
}
360-
}
361-
362340
fn resize(
363341
&self,
364342
db: &'db dyn Db,
@@ -482,7 +460,7 @@ where
482460
unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
483461
unsafe {
484462
let old_value = &mut *old_pointer;
485-
Vec::maybe_update(&raw mut old_value.0, new_value.0)
463+
Box::maybe_update(&raw mut old_value.0, new_value.0)
486464
}
487465
}
488466
}
@@ -491,7 +469,7 @@ impl<'db> PyIndex<'db> for &FixedLengthTuple<Type<'db>> {
491469
type Item = Type<'db>;
492470

493471
fn py_index(self, db: &'db dyn Db, index: i32) -> Result<Self::Item, OutOfBoundsError> {
494-
self.0.as_slice().py_index(db, index).copied()
472+
self.0.py_index(db, index).copied()
495473
}
496474
}
497475

@@ -518,9 +496,9 @@ impl<'db> PySlice<'db> for FixedLengthTuple<Type<'db>> {
518496
/// types, use [`TupleSpec`], which defines some additional type-specific methods.
519497
#[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
520498
pub struct VariableLengthTuple<T> {
521-
pub(crate) prefix: Vec<T>,
499+
pub(crate) prefix: Box<[T]>,
522500
pub(crate) variable: T,
523-
pub(crate) suffix: Vec<T>,
501+
pub(crate) suffix: Box<[T]>,
524502
}
525503

526504
impl<T> VariableLengthTuple<T> {
@@ -569,10 +547,6 @@ impl<T> VariableLengthTuple<T> {
569547
fn len(&self) -> TupleLength {
570548
TupleLength::Variable(self.prefix.len(), self.suffix.len())
571549
}
572-
573-
fn push(&mut self, element: T) {
574-
self.suffix.push(element);
575-
}
576550
}
577551

578552
impl<'db> VariableLengthTuple<Type<'db>> {
@@ -639,30 +613,6 @@ impl<'db> VariableLengthTuple<Type<'db>> {
639613
.copied()
640614
}
641615

642-
fn concat(&self, db: &'db dyn Db, other: &Tuple<Type<'db>>) -> Tuple<Type<'db>> {
643-
match other {
644-
TupleSpec::Fixed(other) => VariableLengthTuple::mixed(
645-
self.prefix_elements().copied(),
646-
self.variable,
647-
self.suffix_elements().chain(other.elements()).copied(),
648-
),
649-
650-
Tuple::Variable(other) => {
651-
let variable = UnionType::from_elements(
652-
db,
653-
(self.suffix_elements().copied())
654-
.chain([self.variable, other.variable])
655-
.chain(other.prefix_elements().copied()),
656-
);
657-
VariableLengthTuple::mixed(
658-
self.prefix_elements().copied(),
659-
variable,
660-
other.suffix_elements().copied(),
661-
)
662-
}
663-
}
664-
}
665-
666616
fn resize(
667617
&self,
668618
db: &'db dyn Db,
@@ -711,13 +661,17 @@ impl<'db> VariableLengthTuple<Type<'db>> {
711661
let prefix = self
712662
.prenormalized_prefix_elements(db, None)
713663
.map(|ty| ty.normalized_impl(db, visitor))
714-
.collect::<Vec<_>>();
664+
.collect::<Box<_>>();
715665
let suffix = self
716666
.prenormalized_suffix_elements(db, None)
717667
.map(|ty| ty.normalized_impl(db, visitor))
718-
.collect::<Vec<_>>();
668+
.collect::<Box<_>>();
719669
let variable = self.variable.normalized_impl(db, visitor);
720-
Self::mixed(prefix, variable, suffix)
670+
TupleSpec::Variable(Self {
671+
prefix,
672+
variable,
673+
suffix,
674+
})
721675
}
722676

723677
fn materialize(&self, db: &'db dyn Db, variance: TypeVarVariance) -> TupleSpec<'db> {
@@ -901,9 +855,9 @@ where
901855
unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
902856
let old_value = unsafe { &mut *old_pointer };
903857
let mut changed = false;
904-
changed |= unsafe { Vec::maybe_update(&raw mut old_value.prefix, new_value.prefix) };
858+
changed |= unsafe { Box::maybe_update(&raw mut old_value.prefix, new_value.prefix) };
905859
changed |= unsafe { T::maybe_update(&raw mut old_value.variable, new_value.variable) };
906-
changed |= unsafe { Vec::maybe_update(&raw mut old_value.suffix, new_value.suffix) };
860+
changed |= unsafe { Box::maybe_update(&raw mut old_value.suffix, new_value.suffix) };
907861
changed
908862
}
909863
}
@@ -971,10 +925,6 @@ impl<T> Tuple<T> {
971925
FixedLengthTuple::from_elements(elements).into()
972926
}
973927

974-
pub(crate) fn with_capacity(capacity: usize) -> Self {
975-
Tuple::Fixed(FixedLengthTuple::with_capacity(capacity))
976-
}
977-
978928
/// Returns an iterator of all of the fixed-length element types of this tuple.
979929
pub(crate) fn fixed_elements(&self) -> impl Iterator<Item = &T> + '_ {
980930
match self {
@@ -1017,13 +967,6 @@ impl<T> Tuple<T> {
1017967
_ => Truthiness::Ambiguous,
1018968
}
1019969
}
1020-
1021-
pub(crate) fn push(&mut self, element: T) {
1022-
match self {
1023-
Tuple::Fixed(tuple) => tuple.push(element),
1024-
Tuple::Variable(tuple) => tuple.push(element),
1025-
}
1026-
}
1027970
}
1028971

1029972
impl<'db> Tuple<Type<'db>> {
@@ -1033,10 +976,7 @@ impl<'db> Tuple<Type<'db>> {
1033976

1034977
/// Concatenates another tuple to the end of this tuple, returning a new tuple.
1035978
pub(crate) fn concat(&self, db: &'db dyn Db, other: &Self) -> Self {
1036-
match self {
1037-
Tuple::Fixed(tuple) => tuple.concat(other),
1038-
Tuple::Variable(tuple) => tuple.concat(db, other),
1039-
}
979+
TupleSpecBuilder::from(self).concat(db, other).build()
1040980
}
1041981

1042982
/// Resizes this tuple to a different length, if possible. If this tuple cannot satisfy the
@@ -1349,3 +1289,120 @@ pub(crate) enum ResizeTupleError {
13491289
TooFewValues,
13501290
TooManyValues,
13511291
}
1292+
1293+
/// A builder for creating a new [`TupleSpec`]
1294+
pub(crate) enum TupleSpecBuilder<'db> {
1295+
Fixed(Vec<Type<'db>>),
1296+
Variable {
1297+
prefix: Vec<Type<'db>>,
1298+
variable: Type<'db>,
1299+
suffix: Vec<Type<'db>>,
1300+
},
1301+
}
1302+
1303+
impl<'db> TupleSpecBuilder<'db> {
1304+
pub(crate) fn with_capacity(capacity: usize) -> Self {
1305+
TupleSpecBuilder::Fixed(Vec::with_capacity(capacity))
1306+
}
1307+
1308+
pub(crate) fn push(&mut self, element: Type<'db>) {
1309+
match self {
1310+
TupleSpecBuilder::Fixed(elements) => elements.push(element),
1311+
TupleSpecBuilder::Variable { suffix, .. } => suffix.push(element),
1312+
}
1313+
}
1314+
1315+
/// Concatenates another tuple to the end of this tuple, returning a new tuple.
1316+
pub(crate) fn concat(mut self, db: &'db dyn Db, other: &TupleSpec<'db>) -> Self {
1317+
match (&mut self, other) {
1318+
(TupleSpecBuilder::Fixed(left_tuple), TupleSpec::Fixed(right_tuple)) => {
1319+
left_tuple.extend_from_slice(&right_tuple.0);
1320+
self
1321+
}
1322+
1323+
(
1324+
TupleSpecBuilder::Fixed(left_tuple),
1325+
TupleSpec::Variable(VariableLengthTuple {
1326+
prefix,
1327+
variable,
1328+
suffix,
1329+
}),
1330+
) => {
1331+
left_tuple.extend_from_slice(prefix);
1332+
TupleSpecBuilder::Variable {
1333+
prefix: std::mem::take(left_tuple),
1334+
variable: *variable,
1335+
suffix: suffix.to_vec(),
1336+
}
1337+
}
1338+
1339+
(
1340+
TupleSpecBuilder::Variable {
1341+
prefix: _,
1342+
variable: _,
1343+
suffix,
1344+
},
1345+
TupleSpec::Fixed(right),
1346+
) => {
1347+
suffix.extend_from_slice(&right.0);
1348+
self
1349+
}
1350+
1351+
(
1352+
TupleSpecBuilder::Variable {
1353+
prefix: left_prefix,
1354+
variable: left_variable,
1355+
suffix: left_suffix,
1356+
},
1357+
TupleSpec::Variable(VariableLengthTuple {
1358+
prefix: right_prefix,
1359+
variable: right_variable,
1360+
suffix: right_suffix,
1361+
}),
1362+
) => {
1363+
let variable = UnionType::from_elements(
1364+
db,
1365+
left_suffix
1366+
.iter()
1367+
.chain([left_variable, right_variable])
1368+
.chain(right_prefix),
1369+
);
1370+
TupleSpecBuilder::Variable {
1371+
prefix: std::mem::take(left_prefix),
1372+
variable,
1373+
suffix: right_suffix.to_vec(),
1374+
}
1375+
}
1376+
}
1377+
}
1378+
1379+
pub(super) fn build(self) -> TupleSpec<'db> {
1380+
match self {
1381+
TupleSpecBuilder::Fixed(elements) => {
1382+
TupleSpec::Fixed(FixedLengthTuple(elements.into_boxed_slice()))
1383+
}
1384+
TupleSpecBuilder::Variable {
1385+
prefix,
1386+
variable,
1387+
suffix,
1388+
} => TupleSpec::Variable(VariableLengthTuple {
1389+
prefix: prefix.into_boxed_slice(),
1390+
variable,
1391+
suffix: suffix.into_boxed_slice(),
1392+
}),
1393+
}
1394+
}
1395+
}
1396+
1397+
impl<'db> From<&TupleSpec<'db>> for TupleSpecBuilder<'db> {
1398+
fn from(tuple: &TupleSpec<'db>) -> Self {
1399+
match tuple {
1400+
TupleSpec::Fixed(fixed) => TupleSpecBuilder::Fixed(fixed.0.to_vec()),
1401+
TupleSpec::Variable(variable) => TupleSpecBuilder::Variable {
1402+
prefix: variable.prefix.to_vec(),
1403+
variable: variable.variable,
1404+
suffix: variable.suffix.to_vec(),
1405+
},
1406+
}
1407+
}
1408+
}

0 commit comments

Comments
 (0)