Skip to content

Commit f8199d7

Browse files
authored
impl basicsize (#6557)
* shape_differs * may_add_dict * basicsize
1 parent ee74713 commit f8199d7

5 files changed

Lines changed: 20 additions & 16 deletions

File tree

Lib/test/test_types.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -591,8 +591,6 @@ def test_format_spec_errors(self):
591591
for code in 'xXobns':
592592
self.assertRaises(ValueError, format, 0, ',' + code)
593593

594-
# TODO: RUSTPYTHON
595-
@unittest.expectedFailure
596594
def test_internal_sizes(self):
597595
self.assertGreater(object.__basicsize__, 0)
598596
self.assertGreater(tuple.__itemsize__, 0)

crates/derive-impl/src/pyclass.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ fn generate_class_def(
455455
});
456456
// If repr(transparent) with a base, the type has the same memory layout as base,
457457
// so basicsize should be 0 (no additional space beyond the base type)
458+
// Otherwise, basicsize = sizeof(payload). The header size is added in __basicsize__ getter.
458459
let basicsize = if is_repr_transparent && base.is_some() {
459460
quote!(0)
460461
} else {

crates/vm/src/builtins/type.rs

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -782,8 +782,8 @@ impl PyType {
782782
}
783783

784784
#[pygetset]
785-
const fn __basicsize__(&self) -> usize {
786-
self.slots.basicsize
785+
fn __basicsize__(&self) -> usize {
786+
crate::object::SIZEOF_PYOBJECT_HEAD + self.slots.basicsize
787787
}
788788

789789
#[pygetset]
@@ -1338,17 +1338,24 @@ impl Constructor for PyType {
13381338
let member_count: usize = base_member_count + heaptype_member_count;
13391339

13401340
let mut flags = PyTypeFlags::heap_type_flags();
1341+
1342+
// Check if we may add dict
1343+
// We can only add a dict if the primary base class doesn't already have one
1344+
// In CPython, this checks tp_dictoffset == 0
1345+
let may_add_dict = !base.slots.flags.has_feature(PyTypeFlags::HAS_DICT);
1346+
13411347
// Add HAS_DICT and MANAGED_DICT if:
1342-
// 1. __slots__ is not defined, OR
1348+
// 1. __slots__ is not defined AND base doesn't have dict, OR
13431349
// 2. __dict__ is in __slots__
1344-
if heaptype_slots.is_none() || add_dict {
1350+
if (heaptype_slots.is_none() && may_add_dict) || add_dict {
13451351
flags |= PyTypeFlags::HAS_DICT | PyTypeFlags::MANAGED_DICT;
13461352
}
13471353

13481354
let (slots, heaptype_ext) = {
13491355
let slots = PyTypeSlots {
13501356
flags,
13511357
member_count,
1358+
itemsize: base.slots.itemsize,
13521359
..PyTypeSlots::heap_default()
13531360
};
13541361
let heaptype_ext = HeapTypeExt {
@@ -2009,23 +2016,19 @@ fn calculate_meta_class(
20092016
Ok(winner)
20102017
}
20112018

2019+
/// Returns true if the two types have different instance layouts.
2020+
fn shape_differs(t1: &Py<PyType>, t2: &Py<PyType>) -> bool {
2021+
t1.__basicsize__() != t2.__basicsize__() || t1.slots.itemsize != t2.slots.itemsize
2022+
}
2023+
20122024
fn solid_base<'a>(typ: &'a Py<PyType>, vm: &VirtualMachine) -> &'a Py<PyType> {
20132025
let base = if let Some(base) = &typ.base {
20142026
solid_base(base, vm)
20152027
} else {
20162028
vm.ctx.types.object_type
20172029
};
20182030

2019-
// Check for extra instance variables (CPython's extra_ivars)
2020-
let t_size = typ.__basicsize__();
2021-
let b_size = base.__basicsize__();
2022-
let t_itemsize = typ.slots.itemsize;
2023-
let b_itemsize = base.slots.itemsize;
2024-
2025-
// Has extra ivars if: sizes differ AND (has items OR t_size > b_size)
2026-
let has_extra_ivars = t_size != b_size && (t_itemsize > 0 || b_itemsize > 0 || t_size > b_size);
2027-
2028-
if has_extra_ivars { typ } else { base }
2031+
if shape_differs(typ, base) { typ } else { base }
20292032
}
20302033

20312034
fn best_base<'a>(bases: &'a [PyTypeRef], vm: &VirtualMachine) -> PyResult<&'a Py<PyType>> {

crates/vm/src/object/core.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ pub(super) struct PyInner<T> {
114114

115115
pub(super) payload: T,
116116
}
117+
pub(crate) const SIZEOF_PYOBJECT_HEAD: usize = std::mem::size_of::<PyInner<()>>();
117118

118119
impl<T: fmt::Debug> fmt::Debug for PyInner<T> {
119120
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {

crates/vm/src/object/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ mod traverse_object;
77
pub use self::core::*;
88
pub use self::ext::*;
99
pub use self::payload::*;
10+
pub(crate) use core::SIZEOF_PYOBJECT_HEAD;
1011
pub use traverse::{MaybeTraverse, Traverse, TraverseFn};

0 commit comments

Comments
 (0)