diff --git a/crates/capi/src/pyerrors.rs b/crates/capi/src/pyerrors.rs index 034c21cffee..6650b52c50c 100644 --- a/crates/capi/src/pyerrors.rs +++ b/crates/capi/src/pyerrors.rs @@ -260,7 +260,7 @@ pub unsafe extern "C" fn PyException_GetTraceback(exc: *mut PyObject) -> *mut Py with_vm(|vm| { let exc = unsafe { &*exc }.try_downcast_ref::(vm)?; let tb = exc - .__traceback__() + .traceback() .map(|tb| tb.into_object().into_raw().as_ptr()) .unwrap_or_default(); Ok(tb) diff --git a/crates/derive-impl/src/pyclass.rs b/crates/derive-impl/src/pyclass.rs index ccedcc59455..69698d3a5bf 100644 --- a/crates/derive-impl/src/pyclass.rs +++ b/crates/derive-impl/src/pyclass.rs @@ -764,7 +764,7 @@ pub(crate) fn impl_pyclass(attr: PunctuatedNestedMeta, item: Item) -> Result bool { - ::BASICSIZE <= obj.class().slots.basicsize && obj.class().fast_issubclass(::static_type()) + ::BASICSIZE <= obj.class().payload().slots.basicsize && obj.class().fast_issubclass(::static_type()) } fn class(ctx: &::rustpython_vm::vm::Context) -> &'static ::rustpython_vm::Py<::rustpython_vm::builtins::PyType> { diff --git a/crates/derive-impl/src/pymodule.rs b/crates/derive-impl/src/pymodule.rs index d0cf5657e33..398a551e44c 100644 --- a/crates/derive-impl/src/pymodule.rs +++ b/crates/derive-impl/src/pymodule.rs @@ -755,7 +755,7 @@ impl ModuleItem for ClassItem { // module resolution, e.g. TypeAliasType) { let module_key = rustpython_vm::identifier!(ctx, __module__); - let has_module_getset = new_class.attributes + let has_module_getset = new_class.payload().attributes .get(module_key) .is_some_and(|v| v.downcastable::()); if !has_module_getset { @@ -849,7 +849,7 @@ impl ModuleItem for StructSequenceItem { let new_class = <#pytype_ident as ::rustpython_vm::class::PyClassImpl>::make_static_type(); { let module_key = rustpython_vm::identifier!(ctx, __module__); - let has_module_getset = new_class.attributes + let has_module_getset = new_class.payload().attributes .get(module_key) .is_some_and(|v| v.downcastable::()); if !has_module_getset { diff --git a/crates/stdlib/src/_asyncio.rs b/crates/stdlib/src/_asyncio.rs index fc758ffdda9..769774c9d97 100644 --- a/crates/stdlib/src/_asyncio.rs +++ b/crates/stdlib/src/_asyncio.rs @@ -322,7 +322,7 @@ pub(crate) mod _asyncio { // Save the original traceback for later restoration if let Ok(exc_ref) = exc.clone().downcast::() { - let tb = exc_ref.__traceback__().map(|tb| tb.into()); + let tb = exc_ref.traceback().map(|tb| tb.into()); *zelf.fut_exception_tb.write() = tb; } @@ -2296,7 +2296,7 @@ pub(crate) mod _asyncio { } else { task.base.fut_state.store(FutureState::Finished); // Save the original traceback for later restoration - let tb = exc.__traceback__().map(|tb| tb.into()); + let tb = exc.traceback().map(|tb| tb.into()); *task.base.fut_exception_tb.write() = tb; *task.base.fut_exception.write() = Some(exc.clone().into()); task.base.fut_log_tb.store(true, Ordering::Relaxed); diff --git a/crates/stdlib/src/elementtree.rs b/crates/stdlib/src/elementtree.rs index f72dd2a61be..d9c9362663e 100644 --- a/crates/stdlib/src/elementtree.rs +++ b/crates/stdlib/src/elementtree.rs @@ -239,6 +239,10 @@ pub(crate) mod _elementtree { self.inner.read().children.len() } + fn is_empty(&self) -> bool { + self.len() == 0 + } + fn child(&self, index: usize) -> Option { self.inner.read().children.get(index).cloned() } @@ -1209,7 +1213,7 @@ pub(crate) mod _elementtree { None, vm, )?; - Ok(zelf.len() != 0) + Ok(!zelf.is_empty()) }), ..PyNumberMethods::NOT_IMPLEMENTED }; diff --git a/crates/vm/src/builtins/bool.rs b/crates/vm/src/builtins/bool.rs index 6101fd62fd4..53e96998279 100644 --- a/crates/vm/src/builtins/bool.rs +++ b/crates/vm/src/builtins/bool.rs @@ -52,7 +52,7 @@ impl PyObject { #[cold] #[inline(never)] fn try_to_bool_slow(&self, vm: &VirtualMachine) -> PyResult { - let slots = &self.class().slots; + let slots = self.class().slots(); // 1. Try nb_bool slot first if let Some(nb_bool) = slots.as_number.boolean.load() { diff --git a/crates/vm/src/builtins/bytes.rs b/crates/vm/src/builtins/bytes.rs index 11e6956ad13..2a52705ded2 100644 --- a/crates/vm/src/builtins/bytes.rs +++ b/crates/vm/src/builtins/bytes.rs @@ -550,6 +550,11 @@ impl PyBytes { #[pyclass] impl Py { + #[inline] + pub fn as_bytes(&self) -> &[u8] { + self.payload().as_bytes() + } + #[pymethod] fn __reduce_ex__( &self, @@ -637,7 +642,7 @@ impl AsBuffer for PyBytes { fn as_buffer(zelf: &Py, _vm: &VirtualMachine) -> PyResult { let buf = PyBuffer::new( zelf.to_owned().into(), - BufferDescriptor::simple(zelf.len(), true), + BufferDescriptor::simple(zelf.as_bytes().len(), true), &BUFFER_METHODS, ); Ok(buf) @@ -647,7 +652,9 @@ impl AsBuffer for PyBytes { impl AsMapping for PyBytes { fn as_mapping() -> &'static PyMappingMethods { static AS_MAPPING: LazyLock = LazyLock::new(|| PyMappingMethods { - length: atomic_func!(|mapping, _vm| Ok(PyBytes::mapping_downcast(mapping).len())), + length: atomic_func!(|mapping, _vm| { + Ok(PyBytes::mapping_downcast(mapping).as_bytes().len()) + }), subscript: atomic_func!( |mapping, needle, vm| PyBytes::mapping_downcast(mapping)._getitem(needle, vm) ), @@ -660,7 +667,7 @@ impl AsMapping for PyBytes { impl AsSequence for PyBytes { fn as_sequence() -> &'static PySequenceMethods { static AS_SEQUENCE: LazyLock = LazyLock::new(|| PySequenceMethods { - length: atomic_func!(|seq, _vm| Ok(PyBytes::sequence_downcast(seq).len())), + length: atomic_func!(|seq, _vm| Ok(PyBytes::sequence_downcast(seq).as_bytes().len())), concat: atomic_func!(|seq, other, vm| { PyBytes::sequence_downcast(seq) .inner @@ -769,7 +776,7 @@ impl PyPayload for PyBytesIterator { impl PyBytesIterator { #[pymethod] fn __length_hint__(&self) -> usize { - self.internal.lock().length_hint(|obj| obj.len()) + self.internal.lock().length_hint(|obj| obj.as_bytes().len()) } #[pymethod] @@ -787,7 +794,7 @@ impl PyBytesIterator { fn __setstate__(&self, state: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { self.internal .lock() - .set_state(&state, |obj, pos| pos.min(obj.len()), vm) + .set_state(&state, |obj, pos| pos.min(obj.as_bytes().len()), vm) } } diff --git a/crates/vm/src/builtins/float.rs b/crates/vm/src/builtins/float.rs index 154e19e63cb..04a2bba704b 100644 --- a/crates/vm/src/builtins/float.rs +++ b/crates/vm/src/builtins/float.rs @@ -560,5 +560,5 @@ fn vectorcall_float( #[rustfmt::skip] // to avoid line splitting pub(crate) fn init(context: &'static Context) { PyFloat::extend_class(context, context.types.float_type); - context.types.float_type.slots.vectorcall.store(Some(vectorcall_float)); + context.types.float_type.slots().vectorcall.store(Some(vectorcall_float)); } diff --git a/crates/vm/src/builtins/genericalias.rs b/crates/vm/src/builtins/genericalias.rs index 7ccdf4eff12..b0780405431 100644 --- a/crates/vm/src/builtins/genericalias.rs +++ b/crates/vm/src/builtins/genericalias.rs @@ -369,11 +369,11 @@ fn subs_tvars( for arg in sub_params.iter() { if let Some(idx) = tuple_index(params.as_slice(), arg) { - let param = ¶ms[idx]; + let param = ¶ms.as_slice()[idx]; let substituted_arg = &arg_items[idx]; // Check if this is a TypeVarTuple (has tp_iter) - if param.class().slots.iter.load().is_some() + if param.class().slots().iter.load().is_some() && substituted_arg.try_to_ref::(vm).is_ok() { // TypeVarTuple case - extend with tuple elements diff --git a/crates/vm/src/builtins/list.rs b/crates/vm/src/builtins/list.rs index c20c8db378e..2fd40f721d4 100644 --- a/crates/vm/src/builtins/list.rs +++ b/crates/vm/src/builtins/list.rs @@ -128,6 +128,18 @@ impl ToPyObject for Vec { } } +impl Py { + #[inline] + pub fn borrow_vec(&self) -> PyMappedRwLockReadGuard<'_, [PyObjectRef]> { + self.payload().borrow_vec() + } + + #[inline] + pub fn borrow_vec_mut(&self) -> PyRwLockWriteGuard<'_, Vec> { + self.payload().borrow_vec_mut() + } +} + impl PyList { #[deprecated(note = "use PyList::from(...).into_ref() instead")] pub fn new_ref(elements: Vec, ctx: &Context) -> PyRef { @@ -792,7 +804,7 @@ fn object_lt( vm: &VirtualMachine, ) -> PyResult { #[allow(unpredictable_function_pointer_comparisons)] - if a.class().slots.richcompare.load() != Some(cmp) { + if a.class().slots().richcompare.load() != Some(cmp) { return a.rich_compare_bool(b, PyComparisonOp::Lt, vm); } match cmp(a, b, PyComparisonOp::Lt, vm)? { diff --git a/crates/vm/src/builtins/memory.rs b/crates/vm/src/builtins/memory.rs index cb53dbdc95e..cd567255e2a 100644 --- a/crates/vm/src/builtins/memory.rs +++ b/crates/vm/src/builtins/memory.rs @@ -1474,7 +1474,12 @@ static BUFFER_WRAPPER_METHODS: BufferMethods = BufferMethods { // A native release runs when the memoryview itself is torn down; only a // Python-level hook on a foreign exporter has to be called here. if !mv.buffer.obj.is(&wrapper.exporter) - && wrapper.exporter.class().slots.python_release_buffer.load() + && wrapper + .exporter + .class() + .slots() + .python_release_buffer + .load() { call_python_release_buffer(&wrapper.exporter, mv.clone()); } diff --git a/crates/vm/src/builtins/object.rs b/crates/vm/src/builtins/object.rs index 01b56b4e70a..d947389d695 100644 --- a/crates/vm/src/builtins/object.rs +++ b/crates/vm/src/builtins/object.rs @@ -186,7 +186,7 @@ fn type_slot_names(typ: &Py, vm: &VirtualMachine) -> PyResult PyResult { // Check itemsize - if required && obj.class().slots.itemsize > 0 { + if required && obj.class().slots().itemsize > 0 { return Err(vm.new_type_error(format!("cannot pickle {:.200} objects", obj.class().name()))); } @@ -205,22 +205,22 @@ fn object_getstate_default(obj: &PyObject, required: bool, vm: &VirtualMachine) if required { // Start with PyBaseObject_Type's basicsize - let mut basicsize = vm.ctx.types.object_type.slots.basicsize; + let mut basicsize = vm.ctx.types.object_type.slots().basicsize; // Add __dict__ size if type has dict - if obj.class().slots.flags.has_feature(PyTypeFlags::HAS_DICT) { + if obj.class().slots().flags.has_feature(PyTypeFlags::HAS_DICT) { basicsize += core::mem::size_of::(); } // Add __weakref__ size if type has weakref support - let has_weakref = if let Some(ref ext) = obj.class().heaptype_ext { + let has_weakref = if let Some(ext) = obj.class().heaptype_ext() { match &ext.slots { None => true, // Heap type without __slots__ has automatic weakref Some(slots) => slots.iter().any(|s| s.as_bytes() == b"__weakref__"), } } else { let weakref_name = vm.ctx.intern_str("__weakref__"); - obj.class().attributes.contains(weakref_name) + obj.class().attributes().contains(weakref_name) }; if has_weakref { basicsize += core::mem::size_of::(); @@ -232,7 +232,7 @@ fn object_getstate_default(obj: &PyObject, required: bool, vm: &VirtualMachine) } // Fail if actual type's basicsize > expected basicsize - if obj.class().slots.basicsize > basicsize { + if obj.class().slots().basicsize > basicsize { return Err(vm.new_type_error(format!("cannot pickle '{}' object", obj.class().name()))); } } @@ -326,7 +326,7 @@ impl PyBaseObject { } } PyComparisonOp::Ne => { - let cmp = zelf.class().slots.richcompare.load().unwrap(); + let cmp = zelf.class().slots().richcompare.load().unwrap(); let value = match cmp(zelf, other, PyComparisonOp::Eq, vm)? { Either::A(obj) => PyArithmeticValue::from_object(vm, obj) .map(|obj| obj.try_to_bool(vm)) @@ -524,7 +524,7 @@ impl PyBaseObject { #[pymethod] fn __sizeof__(zelf: PyObjectRef) -> usize { - zelf.class().slots.basicsize + zelf.class().slots().basicsize } } @@ -580,7 +580,11 @@ pub fn object_generic_set_dict( pub(crate) fn init(ctx: &'static Context) { // Manually set alloc/init slots - derive macro doesn't generate extend_slots // for trait impl that overrides #[pyslot] method - ctx.types.object_type.slots.alloc.store(Some(generic_alloc)); + ctx.types + .object_type + .slots() + .alloc + .store(Some(generic_alloc)); ctx.types .object_type .slots diff --git a/crates/vm/src/builtins/str.rs b/crates/vm/src/builtins/str.rs index 98bcd217ca2..76bf7f6cdf7 100644 --- a/crates/vm/src/builtins/str.rs +++ b/crates/vm/src/builtins/str.rs @@ -629,6 +629,16 @@ impl PyStr { } impl Py { + #[inline] + pub fn as_wtf8(&self) -> &Wtf8 { + self.payload().as_wtf8() + } + + #[inline] + pub fn as_bytes(&self) -> &[u8] { + self.payload().as_bytes() + } + pub fn as_utf8(&self) -> Option<&Py> { if self.is_utf8() { // SAFETY: is_utf8() guarantees the PyUtf8Str invariant. diff --git a/crates/vm/src/builtins/tuple.rs b/crates/vm/src/builtins/tuple.rs index b4edba7d996..7c4110dbf88 100644 --- a/crates/vm/src/builtins/tuple.rs +++ b/crates/vm/src/builtins/tuple.rs @@ -295,7 +295,7 @@ impl<'a, R> core::iter::IntoIterator for &'a PyTuple { type IntoIter = core::slice::Iter<'a, R>; fn into_iter(self) -> Self::IntoIter { - self.iter() + self.as_slice().iter() } } @@ -304,7 +304,7 @@ impl<'a, R> core::iter::IntoIterator for &'a Py> { type IntoIter = core::slice::Iter<'a, R>; fn into_iter(self) -> Self::IntoIter { - self.iter() + PyTuple::as_slice(self).iter() } } @@ -313,23 +313,6 @@ impl PyTuple { pub fn as_slice(&self) -> &[R] { &self.elements } - - #[inline] - #[must_use] - pub fn len(&self) -> usize { - self.elements.len() - } - - #[inline] - #[must_use] - pub fn is_empty(&self) -> bool { - self.elements.is_empty() - } - - #[inline] - pub fn iter(&self) -> core::slice::Iter<'_, R> { - self.elements.iter() - } } impl PyTuple { @@ -367,7 +350,7 @@ impl PyTuple { } fn repeat(zelf: PyRef, value: isize, vm: &VirtualMachine) -> PyResult> { - Ok(if zelf.elements.is_empty() || value == 0 { + Ok(if zelf.as_slice().is_empty() || value == 0 { vm.ctx.empty_tuple.clone() } else if value == 1 && zelf.class().is(vm.ctx.types.tuple_type) { // Special case: when some `tuple` is multiplied by `1`, @@ -387,6 +370,11 @@ impl PyTuple { } impl Py { + #[inline] + pub fn as_slice(&self) -> &[PyObjectRef] { + self.payload().as_slice() + } + pub fn extract_tuple<'a, T: FromPyTuple<'a>>(&'a self, vm: &VirtualMachine) -> PyResult { T::from_pytuple(self, vm) } @@ -433,12 +421,13 @@ impl PyTuple { vm: &VirtualMachine, ) -> PyArithmeticValue> { let added = other.downcast::().map(|other| { - if other.elements.is_empty() && zelf.class().is(vm.ctx.types.tuple_type) { + if other.as_slice().is_empty() && zelf.class().is(vm.ctx.types.tuple_type) { zelf - } else if zelf.elements.is_empty() && other.class().is(vm.ctx.types.tuple_type) { + } else if zelf.as_slice().is_empty() && other.class().is(vm.ctx.types.tuple_type) { other } else { let elements = zelf + .as_slice() .iter() .chain(other.as_slice()) .cloned() @@ -466,7 +455,7 @@ impl PyTuple { #[inline] #[must_use] pub fn __len__(&self) -> usize { - self.elements.len() + self.as_slice().len() } fn __mul__(zelf: PyRef, value: ArgSize, vm: &VirtualMachine) -> PyResult> { @@ -500,8 +489,8 @@ impl PyTuple { range: OptionalRangeArgs, vm: &VirtualMachine, ) -> PyResult { - let (start, stop) = range.saturate(self.len(), vm)?; - for (index, element) in self.elements.iter().enumerate().take(stop).skip(start) { + let (start, stop) = range.saturate(self.as_slice().len(), vm)?; + for (index, element) in self.as_slice().iter().enumerate().take(stop).skip(start) { if vm.identical_or_equal(element, &needle)? { return Ok(index); } @@ -548,7 +537,9 @@ impl PyTuple { impl AsMapping for PyTuple { fn as_mapping() -> &'static PyMappingMethods { static AS_MAPPING: LazyLock = LazyLock::new(|| PyMappingMethods { - length: atomic_func!(|mapping, _vm| Ok(PyTuple::mapping_downcast(mapping).len())), + length: atomic_func!(|mapping, _vm| { + Ok(PyTuple::mapping_downcast(mapping).as_slice().len()) + }), subscript: atomic_func!( |mapping, needle, vm| PyTuple::mapping_downcast(mapping)._getitem(needle, vm) ), @@ -595,7 +586,7 @@ impl AsNumber for PyTuple { static AS_NUMBER: PyNumberMethods = PyNumberMethods { boolean: Some(|number, _vm| { let zelf = number.obj.downcast_ref::().unwrap(); - Ok(!zelf.elements.is_empty()) + Ok(!zelf.as_slice().is_empty()) }), ..PyNumberMethods::NOT_IMPLEMENTED }; @@ -621,9 +612,10 @@ impl Comparable for PyTuple { return Ok(res.into()); } let other = class_or_notimplemented!(Self, other); - zelf.iter() + zelf.as_slice() + .iter() .map(|o| &**o) - .richcompare(other.iter().map(|o| &**o), op, vm) + .richcompare(other.as_slice().iter().map(|o| &**o), op, vm) .map(PyComparisonValue::Implemented) } } @@ -640,13 +632,20 @@ impl Iterable for PyTuple { impl Representable for PyTuple { #[inline] fn repr(zelf: &Py, vm: &VirtualMachine) -> PyResult { - let s = if zelf.is_empty() { + let s = if zelf.as_slice().is_empty() { vm.ctx.intern_str("()").to_owned() } else if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { - let s = if zelf.len() == 1 { - wtf8_concat!("(", zelf.elements[0].repr(vm)?.as_wtf8(), ",)") + let s = if zelf.as_slice().len() == 1 { + wtf8_concat!("(", zelf.as_slice()[0].repr(vm)?.as_wtf8(), ",)") } else { - collection_repr(None, "(", ")", "()", zelf.elements.iter().map(|o| &**o), vm)? + collection_repr( + None, + "(", + ")", + "()", + zelf.as_slice().iter().map(|o| &**o), + vm, + )? }; vm.ctx.new_str(s) } else { @@ -713,14 +712,14 @@ impl PyPayload for PyTupleIterator { impl PyTupleIterator { #[pymethod] fn __length_hint__(&self) -> usize { - self.internal.lock().length_hint(|obj| obj.len()) + self.internal.lock().length_hint(|obj| obj.as_slice().len()) } #[pymethod] fn __setstate__(&self, state: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { self.internal .lock() - .set_state(&state, |obj, pos| pos.min(obj.len()), vm) + .set_state(&state, |obj, pos| pos.min(obj.as_slice().len()), vm) } #[pymethod] diff --git a/crates/vm/src/builtins/type.rs b/crates/vm/src/builtins/type.rs index 63bcb4afbc0..f82ac6d42c4 100644 --- a/crates/vm/src/builtins/type.rs +++ b/crates/vm/src/builtins/type.rs @@ -797,7 +797,7 @@ impl PyType { interpreter_id: HeapTypeExt::creating_interpreter_id(), }; let bases = PyTuple::new_ref_typed(bases, ctx); - let base = bases[0].clone(); + let base = bases.as_slice()[0].clone(); Self::new_heap_inner( base, @@ -1651,6 +1651,26 @@ impl PyType { } impl Py { + #[inline] + pub fn slots(&self) -> &PyTypeSlots { + &self.payload().slots + } + + #[inline] + pub fn attributes(&self) -> &TypeNamespace { + &self.payload().attributes + } + + #[inline] + pub fn tp_version_tag(&self) -> &AtomicU32 { + &self.payload().tp_version_tag + } + + #[inline] + pub fn heaptype_ext(&self) -> Option<&HeapTypeExt> { + self.payload().heaptype_ext.as_deref() + } + pub fn is_subtype(&self, other: &Self) -> bool { is_subtype_with_mro(&self.mro.read(), self, other) } @@ -1722,7 +1742,7 @@ impl PyType { fn set_bases(zelf: &Py, bases_tuple: PyTupleRef, vm: &VirtualMachine) -> PyResult<()> { // TODO: Assigning to __bases__ is only used in typing.NamedTupleMeta.__new__ // Rather than correctly re-initializing the class, we are skipping a few steps for now - if zelf.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) { + if zelf.slots().flags.has_feature(PyTypeFlags::IMMUTABLETYPE) { return Err(vm.new_type_error(format!( "cannot set '__bases__' attribute of immutable type '{}'", zelf.name() @@ -3015,7 +3035,7 @@ impl GetAttr for PyType { let zelf_attr = zelf.get_attr(name); if let Some(attr) = zelf_attr { - let descr_get = attr.class().slots.descr_get.load(); + let descr_get = attr.class().slots().descr_get.load(); if let Some(descr_get) = descr_get { descr_get(attr.as_object(), None, Some(zelf.as_object()), vm) } else { @@ -3058,7 +3078,7 @@ impl Py { // CPython returns None if __doc__ is not in the type's own dict if let Some(doc_attr) = self.get_direct_attr(vm.ctx.intern_str("__doc__")) { // If it's a descriptor, call its __get__ method - let descr_get = doc_attr.class().slots.descr_get.load(); + let descr_get = doc_attr.class().slots().descr_get.load(); if let Some(descr_get) = descr_get { descr_get(doc_attr.as_object(), None, Some(self.as_object()), vm) } else { @@ -3135,7 +3155,7 @@ impl SetAttr for PyType { vm: &VirtualMachine, ) -> PyResult<()> { let attr_name = vm.ctx.intern_str(attr_name.as_wtf8()); - if zelf.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) { + if zelf.slots().flags.has_feature(PyTypeFlags::IMMUTABLETYPE) { return Err(vm.new_type_error(format!( "cannot set '{}' attribute of immutable type '{}'", attr_name, @@ -3143,7 +3163,7 @@ impl SetAttr for PyType { ))); } if let Some(attr) = zelf.get_class_attr(attr_name) { - let descr_set = attr.class().slots.descr_set.load(); + let descr_set = attr.class().slots().descr_set.load(); if let Some(descriptor) = descr_set { return descriptor(&attr, zelf.to_owned().into(), value, vm); } @@ -3160,9 +3180,9 @@ impl SetAttr for PyType { zelf.modified_inner(); let prev_value = if let PySetterValue::Assign(value) = value { - zelf.attributes.insert(attr_name, value) + zelf.attributes().insert(attr_name, value) } else { - let prev_value = zelf.attributes.remove(attr_name); + let prev_value = zelf.attributes().remove(attr_name); if prev_value.is_none() { return Err(vm.new_attribute_error(format!( "type object '{}' has no attribute '{}'", @@ -3203,8 +3223,10 @@ impl Callable for PyType { } } - let Some(slot_new) = zelf.slots.new.load() else { - return Err(vm.new_type_error(format!("cannot create '{}' instances", zelf.slots.name))); + let Some(slot_new) = zelf.slots().new.load() else { + return Err( + vm.new_type_error(format!("cannot create '{}' instances", zelf.slots().name)) + ); }; // Both the new and init slots consume args, so the init call gets a @@ -3223,7 +3245,7 @@ impl Callable for PyType { // so a mismatch is conservative: if it ever compared unequal for the // wrapper it would only take the slower cloning path, never the fast // path incorrectly. - if zelf.slots.init.load().is_none() + if zelf.slots().init.load().is_none() && !zelf.is(vm.ctx.types.type_type) && crate::types::fn_addr(slot_new) != crate::types::fn_addr(crate::types::new_wrapper as crate::types::NewFunc) @@ -3239,7 +3261,7 @@ impl Callable for PyType { return Ok(obj); } - if let Some(init_method) = obj.class().slots.init.load() { + if let Some(init_method) = obj.class().slots().init.load() { init_method(&obj, init_args, vm)?; } Ok(obj) @@ -3406,10 +3428,10 @@ fn vectorcall_type( if nargs == 1 && no_kwargs { return Ok(args[0].obj_type()); } - } else if zelf.slots.call.load().is_none() && zelf.slots.new.load().is_some() { + } else if zelf.slots().call.load().is_none() && zelf.slots().new.load().is_some() { // Per-type constructor vectorcall for non-callable types (dict, list, int, etc.) // Also guard on slots.new to avoid dispatching for DISALLOW_INSTANTIATION types. - if let Some(type_vc) = zelf.slots.vectorcall.load() { + if let Some(type_vc) = zelf.slots().vectorcall.load() { return type_vc(zelf_obj, args, nargs, kwnames, vm); } } diff --git a/crates/vm/src/builtins/union.rs b/crates/vm/src/builtins/union.rs index a30c87f2cd7..e56421eaea3 100644 --- a/crates/vm/src/builtins/union.rs +++ b/crates/vm/src/builtins/union.rs @@ -365,7 +365,7 @@ fn dedup_and_flatten_args(args: &Py, vm: &VirtualMachine) -> PyResult, vm: &VirtualMachine) -> PyResult { let result = dedup_and_flatten_args(args, vm)?; Ok(match result.args.len() { - 1 => result.args[0].to_owned(), + 1 => result.args.as_slice()[0].to_owned(), _ => PyUnion::from_components(result, vm)?.to_pyobject(vm), }) } @@ -383,7 +383,7 @@ impl PyUnion { Ok(if new_args.is_empty() { make_union(&new_args, vm)? } else { - let mut tmp = new_args[0].to_owned(); + let mut tmp = new_args.as_slice()[0].to_owned(); for arg in new_args.iter().skip(1) { tmp = vm._or(&tmp, arg)?; } diff --git a/crates/vm/src/builtins/weakproxy.rs b/crates/vm/src/builtins/weakproxy.rs index 0a959e4c9f6..0006674a9fb 100644 --- a/crates/vm/src/builtins/weakproxy.rs +++ b/crates/vm/src/builtins/weakproxy.rs @@ -23,7 +23,7 @@ impl PyPayload for PyWeakProxy { #[inline] unsafe fn validate_downcastable_from(obj: &PyObject) -> bool { - ::BASICSIZE <= obj.class().slots.basicsize + ::BASICSIZE <= obj.class().slots().basicsize && obj .class() .fast_issubclass(::static_type()) @@ -170,7 +170,7 @@ impl Iterable for PyWeakProxy { impl IterNext for PyWeakProxy { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { let obj = zelf.try_upgrade(vm)?; - if obj.class().slots.iternext.load().is_none() { + if obj.class().slots().iternext.load().is_none() { return Err(vm.new_type_error("Weakref proxy referenced a non-iterator")); } PyIter::new(obj).next(vm) diff --git a/crates/vm/src/byte.rs b/crates/vm/src/byte.rs index d6ed983e8aa..aadbc518403 100644 --- a/crates/vm/src/byte.rs +++ b/crates/vm/src/byte.rs @@ -57,7 +57,7 @@ fn collect_bytes( // which knows what it was being asked to do, rather than by the // iteration protocol saying the object is not iterable. let cls = obj.class(); - if cls.slots.iter.load().is_none() && !cls.has_attr(identifier!(vm, __getitem__)) { + if cls.slots().iter.load().is_none() && !cls.has_attr(identifier!(vm, __getitem__)) { return Err(vm.new_type_error(unusable(&cls.name()))); } let value = |x: PyObjectRef| value_from_object_with(vm, &x, element); diff --git a/crates/vm/src/cformat.rs b/crates/vm/src/cformat.rs index d5ce89a669b..a88eba9174b 100644 --- a/crates/vm/src/cformat.rs +++ b/crates/vm/src/cformat.rs @@ -138,7 +138,7 @@ fn spec_format_bytes( // CPython parity: bytes `%c` accepts a single byte or any object // with `__index__` in range(256). if let Some(b) = obj.downcast_ref::() { - if b.len() == 1 { + if b.as_bytes().len() == 1 { return Ok(spec.format_char(b.as_bytes()[0])); } } else if let Some(ba) = obj.downcast_ref::() { @@ -155,7 +155,7 @@ fn spec_format_bytes( // A bytes-like argument that is not one byte long is named by // its length rather than by its type. let what = if let Some(b) = obj.downcast_ref::() { - format!("a bytes object of length {}", b.len()) + format!("a bytes object of length {}", b.as_bytes().len()) } else if let Some(ba) = obj.downcast_ref::() { format!("a bytearray object of length {}", ba.borrow_buf().len()) } else { diff --git a/crates/vm/src/class.rs b/crates/vm/src/class.rs index c9d341e9337..620bf813731 100644 --- a/crates/vm/src/class.rs +++ b/crates/vm/src/class.rs @@ -24,7 +24,7 @@ pub fn add_operators(class: &'static Py, ctx: &Context) { // Special handling for __hash__ = None if def.name == "__hash__" - && class.slots.hash.load().is_some_and(|h| { + && class.slots().hash.load().is_some_and(|h| { fn_addr(h) == fn_addr(hash_not_implemented as crate::types::HashFunc) }) { @@ -40,13 +40,13 @@ pub fn add_operators(class: &'static Py, ctx: &Context) { } // Get the slot function wrapped in SlotFunc - let Some(slot_func) = def.accessor.get_slot_func_with_op(&class.slots, def.op) else { + let Some(slot_func) = def.accessor.get_slot_func_with_op(class.slots(), def.op) else { continue; }; // Check if attribute already exists in dict let attr_name = ctx.intern_str(def.name); - if class.attributes.contains(attr_name) { + if class.attributes().contains(attr_name) { continue; } @@ -157,7 +157,7 @@ pub trait PyClassImpl: PyClassDef { { // NOTE: `is_created_with_flags` if only available when debug_assertions is true #[cfg(debug_assertions)] - debug_assert!(class.slots.flags.is_created_with_flags()); + debug_assert!(class.slots().flags.is_created_with_flags()); let _ = ctx.intern_str(Self::NAME); // intern type name @@ -181,7 +181,7 @@ pub trait PyClassImpl: PyClassDef { // Only set __doc__ if it doesn't already exist (e.g., as a member descriptor) // This matches CPython's behavior in type_dict_set_doc let doc_attr_name = identifier!(ctx, __doc__); - if class.attributes.get(doc_attr_name).is_none() { + if class.attributes().get(doc_attr_name).is_none() { class.set_attr(doc_attr_name, ctx.new_str(doc).into()); } } @@ -191,7 +191,7 @@ pub trait PyClassImpl: PyClassDef { // Don't overwrite a getset descriptor for __module__ (e.g. TypeAliasType // has an instance-level __module__ getset that should not be replaced) let has_getset = class - .attributes + .attributes() .get(module_key) .is_some_and(|v| v.downcastable::()); if !has_getset { @@ -202,8 +202,8 @@ pub trait PyClassImpl: PyClassDef { // Don't add __new__ attribute if slot_new is inherited from object // (Python doesn't add __new__ to __dict__ for inherited slots) // Exception: object itself should have __new__ in its dict - if let Some(slot_new) = class.slots.new.load() { - let object_new = ctx.types.object_type.slots.new.load(); + if let Some(slot_new) = class.slots().new.load() { + let object_new = ctx.types.object_type.slots().new.load(); let is_object_itself = core::ptr::eq(class, ctx.types.object_type); let is_inherited_from_object = !is_object_itself && object_new.is_some_and(|obj_new| fn_addr(slot_new) == fn_addr(obj_new)); @@ -224,7 +224,7 @@ pub trait PyClassImpl: PyClassDef { class.inherit_slots(base); } - class.extend_methods(class.slots.methods, ctx); + class.extend_methods(class.slots().methods, ctx); } #[must_use] diff --git a/crates/vm/src/coroutine.rs b/crates/vm/src/coroutine.rs index ade2890cf69..1b7dbfe958a 100644 --- a/crates/vm/src/coroutine.rs +++ b/crates/vm/src/coroutine.rs @@ -655,7 +655,7 @@ pub(crate) fn unraisable_while_closing( ) { // Explicit close() leaves the traceback to the caller frame. // Finalize has no caller frame, so attach the generator site here. - if e.__traceback__().is_none() + if e.traceback().is_none() && let Some(frame) = coro.frame_opt() { let lasti = frame.lasti().saturating_mul(2); diff --git a/crates/vm/src/exceptions.rs b/crates/vm/src/exceptions.rs index 56a87980b5f..f01f8b249c9 100644 --- a/crates/vm/src/exceptions.rs +++ b/crates/vm/src/exceptions.rs @@ -358,11 +358,11 @@ impl VirtualMachine { 0 => vec![], 1 => { let args0_repr = if str_single { - varargs[0] + varargs.as_slice()[0] .str(vm) .unwrap_or_else(|_| PyStr::from("").into_ref(&vm.ctx)) } else { - varargs[0].repr(vm).unwrap_or_else(|_| { + varargs.as_slice()[0].repr(vm).unwrap_or_else(|_| { PyStr::from("").into_ref(&vm.ctx) }) }; @@ -383,7 +383,7 @@ impl VirtualMachine { &self, exc: PyBaseExceptionRef, ) -> (PyObjectRef, PyObjectRef, PyObjectRef) { - let tb = exc.__traceback__().to_pyobject(self); + let tb = exc.traceback().to_pyobject(self); let class = exc.class().to_owned(); (class.into(), exc.into(), tb) } @@ -777,6 +777,11 @@ impl PyBaseException { #[pyclass] impl Py { + #[inline] + pub fn traceback(&self) -> Option { + self.payload().__traceback__() + } + #[pymethod] pub(super) fn __str__(&self, vm: &VirtualMachine) -> PyStrRef { let str_args = vm.exception_args_as_string(&self.args(), true); @@ -1249,7 +1254,7 @@ impl serde::Serialize for SerializeException<'_, '_> { s.end() } } - self.exc.__traceback__().map(Tracebacks) + self.exc.traceback().map(Tracebacks) }; struc.serialize_field("traceback", &tbs)?; struc.serialize_field( @@ -3413,7 +3418,7 @@ pub fn exception_group_match( let wrapped = eg_type.call((vm.ctx.new_str(""), excs), vm)?; // Copy traceback from original exception if let Ok(exc) = exc_value.to_owned().downcast::() - && let Some(tb) = exc.__traceback__() + && let Some(tb) = exc.traceback() && let Ok(wrapped_exc) = wrapped.clone().downcast::() { wrapped_exc.set_traceback(Some(tb)); diff --git a/crates/vm/src/frame.rs b/crates/vm/src/frame.rs index 7b94b38dde3..e9af2edde42 100644 --- a/crates/vm/src/frame.rs +++ b/crates/vm/src/frame.rs @@ -2670,7 +2670,7 @@ pub(crate) fn trampoline_handle_exception( // Add traceback entry at the call site. if let Some((loc, _end_loc)) = exec.code.locations.get(idx) { - let next = exception.__traceback__(); + let next = exception.traceback(); let new_traceback = PyTraceback::new(next, exec.frame_object(vm), idx as u32 * 2, loc.line); exception.set_traceback(Some(new_traceback.into_ref(&vm.ctx))); } @@ -3513,7 +3513,7 @@ impl ExecutingFrame<'_> { let exc_type: PyObjectRef = exc.class().to_owned().into(); let exc_value: PyObjectRef = exc.to_owned().into(); let exc_tb: PyObjectRef = exc - .__traceback__() + .traceback() .map_or_else(|| vm.ctx.none(), |tb| -> PyObjectRef { tb.into() }); let tuple = vm.ctx.new_tuple(vec![exc_type, exc_value, exc_tb]).into(); vm.trace_event(crate::protocol::TraceEvent::Exception, Some(tuple))?; @@ -3588,7 +3588,7 @@ impl ExecutingFrame<'_> { Ok(_) => {} Err(exception) => { if let Some((loc, _end_loc)) = self.code.locations.get(idx) { - let next = exception.__traceback__(); + let next = exception.traceback(); let new_traceback = PyTraceback::new( next, self.frame_object(vm), @@ -3697,7 +3697,7 @@ impl ExecutingFrame<'_> { vm: &VirtualMachine, ) -> FrameResult { if let Some((loc, _end_loc)) = frame.code.locations.get(idx) { - let next = exception.__traceback__(); + let next = exception.traceback(); let new_traceback = PyTraceback::new( next, frame.frame_object(vm), @@ -3780,14 +3780,14 @@ impl ExecutingFrame<'_> { // Check if the exception already has traceback entries before // we add ours. If it does, it was propagated from a callee // function and we should not re-contextualize it. - let had_prior_traceback = exception.__traceback__().is_some(); + let had_prior_traceback = exception.traceback().is_some(); // PyTraceBack_Here always adds a new entry without // checking for duplicates. Each time an exception passes through // a frame (e.g., in a loop with repeated raise statements), // a new traceback entry is added. if let Some((loc, _end_loc)) = frame.code.locations.get(idx) { - let next = exception.__traceback__(); + let next = exception.traceback(); let new_traceback = PyTraceback::new( next, @@ -3999,7 +3999,7 @@ impl ExecutingFrame<'_> { let idx = self.lasti().saturating_sub(1) as usize; if idx < self.code.locations.len() { let (loc, _end_loc) = self.code.locations[idx]; - let next = err.__traceback__(); + let next = err.traceback(); let new_traceback = PyTraceback::new(next, self.frame_object(vm), idx as u32 * 2, loc.line); err.set_traceback(Some(new_traceback.into_ref(&vm.ctx))); @@ -4057,7 +4057,7 @@ impl ExecutingFrame<'_> { let idx = self.lasti().saturating_sub(1) as usize; if idx < self.code.locations.len() { let (loc, _end_loc) = self.code.locations[idx]; - let next = err.__traceback__(); + let next = err.traceback(); let new_traceback = PyTraceback::new( next, self.frame_object(vm), @@ -4106,7 +4106,7 @@ impl ExecutingFrame<'_> { let idx = self.lasti().saturating_sub(1) as usize; if idx < self.code.locations.len() { let (loc, _end_loc) = self.code.locations[idx]; - let next = exception.__traceback__(); + let next = exception.traceback(); let new_traceback = PyTraceback::new(next, self.frame_object(vm), idx as u32 * 2, loc.line); exception.set_traceback(Some(new_traceback.into_ref(&vm.ctx))); @@ -4907,7 +4907,7 @@ impl ExecutingFrame<'_> { // Only rewrite the error if the type is truly not iterable // (no __iter__ and no __getitem__). Preserve original TypeError // from custom iterables that raise during iteration. - let not_iterable = iterable.class().slots.iter.load().is_none() + let not_iterable = iterable.class().slots().iter.load().is_none() && iterable .get_class_attr(vm.ctx.intern_str("__getitem__")) .is_none(); @@ -6004,7 +6004,7 @@ impl ExecutingFrame<'_> { let type_version = self.code.instructions.read_cache_u32(cache_base + 1); if type_version != 0 - && owner.class().tp_version_tag.load(Acquire) == type_version + && owner.class().tp_version_tag().load(Acquire) == type_version && let Some(func) = self.try_read_cached_descriptor(cache_base, type_version) { let owner = self.pop_stackref(); @@ -6023,7 +6023,7 @@ impl ExecutingFrame<'_> { let type_version = self.code.instructions.read_cache_u32(cache_base + 1); if type_version != 0 - && owner.class().tp_version_tag.load(Acquire) == type_version + && owner.class().tp_version_tag().load(Acquire) == type_version && !owner.has_instance_dict() && let Some(func) = self.try_read_cached_descriptor(cache_base, type_version) { @@ -6043,7 +6043,8 @@ impl ExecutingFrame<'_> { let owner = self.top_value(); let type_version = self.code.instructions.read_cache_u32(cache_base + 1); - if type_version != 0 && owner.class().tp_version_tag.load(Acquire) == type_version { + if type_version != 0 && owner.class().tp_version_tag().load(Acquire) == type_version + { // Check instance dict doesn't shadow the method. let shadowed = match self.shadowing_instance_attr(cache_base, attr_name, vm) { Ok(shadowed) => shadowed.is_some(), @@ -6071,7 +6072,8 @@ impl ExecutingFrame<'_> { let owner = self.top_value(); let type_version = self.code.instructions.read_cache_u32(cache_base + 1); - if type_version != 0 && owner.class().tp_version_tag.load(Acquire) == type_version { + if type_version != 0 && owner.class().tp_version_tag().load(Acquire) == type_version + { // Type version matches — no data descriptor for this attr. // Try direct dict lookup, skipping full descriptor protocol. if let Some(dict) = owner.dict() @@ -6094,7 +6096,7 @@ impl ExecutingFrame<'_> { let type_version = self.code.instructions.read_cache_u32(cache_base + 1); if type_version != 0 - && owner.class().tp_version_tag.load(Acquire) == type_version + && owner.class().tp_version_tag().load(Acquire) == type_version && let Some(dict) = owner.dict() { // Try the cached entry index first; a hit is an identity @@ -6132,7 +6134,7 @@ impl ExecutingFrame<'_> { let type_version = self.code.instructions.read_cache_u32(cache_base + 1); if type_version != 0 - && owner.class().tp_version_tag.load(Acquire) == type_version + && owner.class().tp_version_tag().load(Acquire) == type_version && let Some(module) = owner.downcast_ref_if_exact::(vm) && let Ok(value) = module.get_attr(attr_name, vm) { @@ -6155,7 +6157,7 @@ impl ExecutingFrame<'_> { let type_version = self.code.instructions.read_cache_u32(cache_base + 1); if type_version != 0 - && owner.class().tp_version_tag.load(Acquire) == type_version + && owner.class().tp_version_tag().load(Acquire) == type_version && let Some(attr) = self.try_read_cached_descriptor(cache_base, type_version) { self.pop_stackref(); @@ -6177,7 +6179,8 @@ impl ExecutingFrame<'_> { let owner = self.top_value(); let type_version = self.code.instructions.read_cache_u32(cache_base + 1); - if type_version != 0 && owner.class().tp_version_tag.load(Acquire) == type_version { + if type_version != 0 && owner.class().tp_version_tag().load(Acquire) == type_version + { // Instance dict has priority — check if attr is shadowed if let Some(value) = self.shadowing_instance_attr(cache_base, attr_name, vm)? { self.pop_stackref(); @@ -6240,7 +6243,7 @@ impl ExecutingFrame<'_> { && metaclass_version != 0 && let Some(owner_type) = owner.downcast_ref::() && owner_type.tp_version_tag.load(Acquire) == type_version - && owner.class().tp_version_tag.load(Acquire) == metaclass_version + && owner.class().tp_version_tag().load(Acquire) == metaclass_version && let Some(attr) = self.try_read_cached_descriptor(cache_base, type_version) { self.pop_stackref(); @@ -6265,7 +6268,7 @@ impl ExecutingFrame<'_> { && !self.specialization_eval_frame_active(vm) && type_version != 0 && func_version != 0 - && owner.class().tp_version_tag.load(Acquire) == type_version + && owner.class().tp_version_tag().load(Acquire) == type_version && let Some(func_obj) = self.try_read_cached_descriptor(cache_base, type_version) && let Some(func) = func_obj.downcast_ref_if_exact::(vm) @@ -6289,7 +6292,8 @@ impl ExecutingFrame<'_> { let owner = self.top_value(); let type_version = self.code.instructions.read_cache_u32(cache_base + 1); - if type_version != 0 && owner.class().tp_version_tag.load(Acquire) == type_version { + if type_version != 0 && owner.class().tp_version_tag().load(Acquire) == type_version + { let slot_offset = self.code.instructions.read_cache_u32(cache_base + 3) as usize; if let Some(value) = owner.get_slot(slot_offset) { @@ -6315,7 +6319,7 @@ impl ExecutingFrame<'_> { if type_version != 0 && !self.specialization_eval_frame_active(vm) - && owner.class().tp_version_tag.load(Acquire) == type_version + && owner.class().tp_version_tag().load(Acquire) == type_version && let Some(fget_obj) = self.try_read_cached_descriptor(cache_base, type_version) && let Some(func) = fget_obj.downcast_ref_if_exact::(vm) @@ -6338,7 +6342,7 @@ impl ExecutingFrame<'_> { let type_version = self.code.instructions.read_cache_u32(cache_base + 1); if type_version != 0 - && owner.class().tp_version_tag.load(Acquire) == type_version + && owner.class().tp_version_tag().load(Acquire) == type_version && let Some(dict) = owner.dict() { self.pop_stackref(); // owner @@ -6360,7 +6364,7 @@ impl ExecutingFrame<'_> { let type_version = self.code.instructions.read_cache_u32(cache_base + 1); if type_version != 0 - && owner.class().tp_version_tag.load(Acquire) == type_version + && owner.class().tp_version_tag().load(Acquire) == type_version && let Some(dict) = owner.dict() { self.pop_stackref(); // owner @@ -6376,7 +6380,7 @@ impl ExecutingFrame<'_> { let type_version = self.code.instructions.read_cache_u32(cache_base + 1); let version_match = type_version != 0 && { let owner = self.top_value(); - owner.class().tp_version_tag.load(Acquire) == type_version + owner.class().tp_version_tag().load(Acquire) == type_version }; if version_match { @@ -6462,7 +6466,7 @@ impl ExecutingFrame<'_> { let owner = self.nth_value(1); if !self.specialization_eval_frame_active(vm) && type_version != 0 - && owner.class().tp_version_tag.load(Acquire) == type_version + && owner.class().tp_version_tag().load(Acquire) == type_version && let Some((func, func_version)) = owner.class().get_cached_getitem_for_specialization() && func.func_version() == func_version @@ -7128,7 +7132,7 @@ impl ExecutingFrame<'_> { let nargs: u32 = arg.into(); let callable = self.nth_value(nargs + 1); if let Some(cls) = callable.downcast_ref::() - && cls.slots.vectorcall.load().is_some() + && cls.slots().vectorcall.load().is_some() { let (callable, args_vec) = self.take_call_args(nargs as usize); let effective_nargs = args_vec.len(); @@ -7153,12 +7157,12 @@ impl ExecutingFrame<'_> { && !self_or_null_is_some && cached_version != 0 && let Some(cls) = callable.downcast_ref::() - && cls.tp_version_tag.load(Acquire) == cached_version + && cls.tp_version_tag().load(Acquire) == cached_version && let Some((init_func, init_func_version)) = cls.get_cached_init_for_specialization(cached_version) && init_func.func_version() == init_func_version && init_func.has_exact_argcount(nargs + 1) - && let Some(cls_alloc) = cls.slots.alloc.load() + && let Some(cls_alloc) = cls.slots().alloc.load() { // The specialization runs `__init__` directly with no // interpreter-visible trampoline frame. Deopt when the @@ -7510,14 +7514,14 @@ impl ExecutingFrame<'_> { if let Some(descr) = cls.get_direct_attr(attr_name) { let descr_cls = descr.class(); if descr_cls - .slots + .slots() .flags .has_feature(PyTypeFlags::METHOD_DESCRIPTOR) { // Method descriptor: push unbound func + self // CALL will prepend self as first positional arg found = Some((descr, true)); - } else if let Some(descr_get) = descr_cls.slots.descr_get.load() { + } else if let Some(descr_get) = descr_cls.slots().descr_get.load() { // Has __get__ but not METHOD_DESCRIPTOR: bind it let bound = descr_get( &descr, @@ -7695,7 +7699,8 @@ impl ExecutingFrame<'_> { let cache_base = instr_idx + 1; let obj = self.top_value(); let cached_version = self.code.instructions.read_cache_u32(cache_base + 1); - if cached_version != 0 && obj.class().tp_version_tag.load(Acquire) == cached_version + if cached_version != 0 + && obj.class().tp_version_tag().load(Acquire) == cached_version { self.pop_stackref(); self.push_bool_or_fused_jump(instruction.cache_entries(), true, vm); @@ -8775,7 +8780,7 @@ impl ExecutingFrame<'_> { // Stack: [callable, self_or_null] let callable = self.nth_value(1); let func_str = Self::object_function_str(callable, vm); - let not_iterable = args_obj.class().slots.iter.load().is_none() + let not_iterable = args_obj.class().slots().iter.load().is_none() && args_obj .get_class_attr(vm.ctx.intern_str("__getitem__")) .is_none(); @@ -9157,7 +9162,7 @@ impl ExecutingFrame<'_> { fn execute_unpack_ex(&mut self, vm: &VirtualMachine, before: u8, after: u32) -> FrameResult { let (before, after) = (before as usize, after as usize); let value = self.pop_value(); - let not_iterable = value.class().slots.iter.load().is_none() + let not_iterable = value.class().slots().iter.load().is_none() && value .get_class_attr(vm.ctx.intern_str("__getitem__")) .is_none(); @@ -9686,7 +9691,7 @@ impl ExecutingFrame<'_> { // General path — iterate up to `size + 1` elements to avoid // consuming the entire iterator (fixes hang on infinite sequences). - let not_iterable = value.class().slots.iter.load().is_none() + let not_iterable = value.class().slots().iter.load().is_none() && value .get_class_attr(vm.ctx.intern_str("__getitem__")) .is_none(); @@ -10073,7 +10078,7 @@ impl ExecutingFrame<'_> { } // Only specialize if getattro is the default (PyBaseObject::getattro) - let is_default_getattro = cls.slots.getattro.load().is_some_and(|f| { + let is_default_getattro = cls.slots().getattro.load().is_some_and(|f| { crate::types::fn_addr(f) == crate::types::fn_addr(PyBaseObject::getattro as crate::types::GetattroFunc) }); @@ -10147,7 +10152,7 @@ impl ExecutingFrame<'_> { } let cls_attr = cls.get_attr(attr_name); - let class_has_dict = cls.slots.flags.has_feature(PyTypeFlags::HAS_DICT); + let class_has_dict = cls.slots().flags.has_feature(PyTypeFlags::HAS_DICT); if oparg.is_method() { // Method specialization @@ -10186,12 +10191,12 @@ impl ExecutingFrame<'_> { // Regular attribute access let has_data_descr = cls_attr.as_ref().is_some_and(|descr| { let descr_cls = descr.class(); - descr_cls.slots.descr_get.load().is_some() - && descr_cls.slots.descr_set.load().is_some() + descr_cls.slots().descr_get.load().is_some() + && descr_cls.slots().descr_set.load().is_some() }); let has_descr_get = cls_attr .as_ref() - .is_some_and(|descr| descr.class().slots.descr_get.load().is_some()); + .is_some_and(|descr| descr.class().slots().descr_get.load().is_some()); if has_data_descr { // Check for member descriptor (slot access) @@ -10332,7 +10337,7 @@ impl ExecutingFrame<'_> { let (mcl_attr, mut metaclass_version) = mcl.lookup_ref_and_version_interned(attr_name, _vm); if let Some(ref attr) = mcl_attr { let attr_class = attr.class(); - if attr_class.slots.descr_set.load().is_some() { + if attr_class.slots().descr_set.load().is_some() { // Data descriptor on metaclass — can't specialize unsafe { self.code.instructions.write_adaptive_counter( @@ -10549,7 +10554,7 @@ impl ExecutingFrame<'_> { let cls = a.class(); // Check the cheap gates before the __getitem__ lookup, which // takes the global type lock and may allocate a version tag. - if cls.slots.flags.has_feature(PyTypeFlags::HEAPTYPE) + if cls.slots().flags.has_feature(PyTypeFlags::HEAPTYPE) && !self.specialization_eval_frame_active(vm) { let (getitem, type_version) = @@ -11058,7 +11063,7 @@ impl ExecutingFrame<'_> { // type/str/tuple(x) and class-call specializations if let Some(cls) = callable.downcast_ref::() { - if cls.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) { + if cls.slots().flags.has_feature(PyTypeFlags::IMMUTABLETYPE) { if !self_or_null_is_some && nargs == 1 { let new_op = if callable.is(&vm.ctx.types.type_type.as_object()) { Some(Instruction::CallType1) @@ -11074,7 +11079,7 @@ impl ExecutingFrame<'_> { return; } } - if cls.slots.vectorcall.load().is_some() { + if cls.slots().vectorcall.load().is_some() { self.specialize_at(instr_idx, cache_base, Instruction::CallBuiltinClass); return; } @@ -11090,15 +11095,15 @@ impl ExecutingFrame<'_> { } // CallAllocAndEnterInit: heap type with default __new__ - if !self_or_null_is_some && cls.slots.flags.has_feature(PyTypeFlags::HEAPTYPE) { + if !self_or_null_is_some && cls.slots().flags.has_feature(PyTypeFlags::HEAPTYPE) { // Capture the version before inspecting tp_new/tp_alloc so a // concurrently installed __new__ invalidates the version this // specialization is cached against. let type_version = cls.version_for_specialization(vm); - let object_new = vm.ctx.types.object_type.slots.new.load(); - let cls_new = cls.slots.new.load(); - let object_alloc = vm.ctx.types.object_type.slots.alloc.load(); - let cls_alloc = cls.slots.alloc.load(); + let object_new = vm.ctx.types.object_type.slots().new.load(); + let cls_new = cls.slots().new.load(); + let object_alloc = vm.ctx.types.object_type.slots().alloc.load(); + let cls_alloc = cls.slots().alloc.load(); if let (Some(cls_new_fn), Some(obj_new_fn), Some(cls_alloc_fn), Some(obj_alloc_fn)) = (cls_new, object_new, cls_alloc, object_alloc) && crate::types::fn_addr(cls_new_fn) == crate::types::fn_addr(obj_new_fn) @@ -11502,14 +11507,14 @@ impl ExecutingFrame<'_> { Some(Instruction::ToBoolList) } else if cls.is(PyStr::class(&vm.ctx)) { Some(Instruction::ToBoolStr) - } else if cls.slots.flags.has_feature(PyTypeFlags::HEAPTYPE) { + } else if cls.slots().flags.has_feature(PyTypeFlags::HEAPTYPE) { // Capture the version before inspecting the bool/len slots so a // concurrently installed __bool__/__len__ invalidates the version // the ToBoolAlwaysTrue guard is cached against. let type_version = cls.version_for_specialization(vm); - let has_bool_or_len = cls.slots.as_number.boolean.load().is_some() - || cls.slots.as_mapping.length.load().is_some() - || cls.slots.as_sequence.length.load().is_some(); + let has_bool_or_len = cls.slots().as_number.boolean.load().is_some() + || cls.slots().as_mapping.length.load().is_some() + || cls.slots().as_sequence.length.load().is_some(); if !has_bool_or_len { if type_version != 0 { unsafe { @@ -11977,7 +11982,7 @@ impl ExecutingFrame<'_> { } // Only specialize if setattr is the default (generic_setattr) - let is_default_setattr = cls.slots.setattro.load().is_some_and(|f| { + let is_default_setattr = cls.slots().setattro.load().is_some_and(|f| { crate::types::fn_addr(f) == crate::types::fn_addr(PyBaseObject::slot_setattro as crate::types::SetattroFunc) }); @@ -11997,7 +12002,8 @@ impl ExecutingFrame<'_> { let cls_attr = cls.get_attr(attr_name); let has_data_descr = cls_attr.as_ref().is_some_and(|descr| { let descr_cls = descr.class(); - descr_cls.slots.descr_get.load().is_some() && descr_cls.slots.descr_set.load().is_some() + descr_cls.slots().descr_get.load().is_some() + && descr_cls.slots().descr_set.load().is_some() }); if has_data_descr { diff --git a/crates/vm/src/import.rs b/crates/vm/src/import.rs index ba2be436615..3fb6e5fdc2e 100644 --- a/crates/vm/src/import.rs +++ b/crates/vm/src/import.rs @@ -271,7 +271,7 @@ pub fn remove_importlib_frames(vm: &VirtualMachine, exc: &Py) { let always_trim = exc.fast_isinstance(vm.ctx.exceptions.import_error); - if let Some(tb) = exc.__traceback__() { + if let Some(tb) = exc.traceback() { let trimmed_tb = remove_importlib_frames_inner(vm, Some(tb), always_trim).0; exc.set_traceback(trimmed_tb); } diff --git a/crates/vm/src/object/core.rs b/crates/vm/src/object/core.rs index 8feaeb5b81c..280f3bf917e 100644 --- a/crates/vm/src/object/core.rs +++ b/crates/vm/src/object/core.rs @@ -2038,7 +2038,7 @@ impl PyObject { // __del__ should only be called once (like _PyGC_FINALIZED check in GIL_DISABLED) // We call __del__ BEFORE clearing weakrefs to allow the finalizer to access // the object's weak references if needed. - let del = self.class().slots.del.load(); + let del = self.class().slots().del.load(); if let Some(slot_del) = del && !self.gc_finalized() { @@ -2148,7 +2148,7 @@ impl PyObject { /// This allows proper resurrection detection. /// PyObject_CallFinalizerFromDealloc pub fn try_call_finalizer(&self) { - let del = self.class().slots.del.load(); + let del = self.class().slots().del.load(); if let Some(slot_del) = del && !self.gc_finalized() && self diff --git a/crates/vm/src/protocol/buffer.rs b/crates/vm/src/protocol/buffer.rs index 9a184ee853f..500ea5992f7 100644 --- a/crates/vm/src/protocol/buffer.rs +++ b/crates/vm/src/protocol/buffer.rs @@ -312,7 +312,7 @@ impl PyBuffer { } // slot_bf_releasebuffer: a Python-level `__release_buffer__` runs first, // then the exporter's own release so export counts stay balanced. - if self.obj.class().slots.python_release_buffer.load() { + if self.obj.class().slots().python_release_buffer.load() { crate::builtins::memory::release_buffer_call_python(self); } (self.methods.release)(self) @@ -368,7 +368,7 @@ impl PyObject { /// `__buffer__` is not mistaken for "not a buffer". #[must_use] pub fn check_buffer(&self) -> bool { - self.class().slots.as_buffer.load().is_some() + self.class().slots().as_buffer.load().is_some() } } diff --git a/crates/vm/src/protocol/callable.rs b/crates/vm/src/protocol/callable.rs index 0bcd756d154..49172a15222 100644 --- a/crates/vm/src/protocol/callable.rs +++ b/crates/vm/src/protocol/callable.rs @@ -66,7 +66,7 @@ pub struct PyCallable<'a> { impl<'a> PyCallable<'a> { pub fn new(obj: &'a PyObject) -> Option { - let slots = &obj.class().slots; + let slots = obj.class().slots(); let call = slots.call.load()?; let vectorcall = slots.vectorcall.load(); Some(PyCallable { diff --git a/crates/vm/src/protocol/iter.rs b/crates/vm/src/protocol/iter.rs index d2001e96699..9322890f68e 100644 --- a/crates/vm/src/protocol/iter.rs +++ b/crates/vm/src/protocol/iter.rs @@ -26,7 +26,7 @@ unsafe impl> Traverse for PyIter { impl PyIter { pub fn check(obj: &PyObject) -> bool { - obj.class().slots.iternext.load().is_some() + obj.class().slots().iternext.load().is_some() } } @@ -136,7 +136,7 @@ impl TryFromObject for PyIter { // in the vm when a for loop is entered. Next, it is used when the builtin // function 'iter' is called. fn try_from_object(vm: &VirtualMachine, iter_target: PyObjectRef) -> PyResult { - let get_iter = iter_target.class().slots.iter.load(); + let get_iter = iter_target.class().slots().iter.load(); if let Some(get_iter) = get_iter { let iter = get_iter(iter_target, vm)?; if Self::check(&iter) { diff --git a/crates/vm/src/protocol/mapping.rs b/crates/vm/src/protocol/mapping.rs index 01db553ccbf..e791757901e 100644 --- a/crates/vm/src/protocol/mapping.rs +++ b/crates/vm/src/protocol/mapping.rs @@ -113,7 +113,7 @@ impl PyMapping<'_> { #[inline] #[must_use] pub fn slots(&self) -> &PyMappingSlots { - &self.obj.class().slots.as_mapping + &self.obj.class().slots().as_mapping } #[inline] diff --git a/crates/vm/src/protocol/number.rs b/crates/vm/src/protocol/number.rs index 2bf9d896b77..f3bd82da7a5 100644 --- a/crates/vm/src/protocol/number.rs +++ b/crates/vm/src/protocol/number.rs @@ -661,12 +661,12 @@ impl PyNumber<'_> { // PyIndex_Check #[must_use] pub fn is_index(self) -> bool { - self.class().slots.as_number.index.load().is_some() + self.class().slots().as_number.index.load().is_some() } #[inline] pub fn int(self, vm: &VirtualMachine) -> Option> { - self.class().slots.as_number.int.load().map(|f| { + self.class().slots().as_number.int.load().map(|f| { let ret = f(self, vm)?; if let Some(ret) = ret.downcast_ref_if_exact::(vm) { @@ -695,7 +695,7 @@ and may be removed in a future version of Python.", #[inline] pub fn index(self, vm: &VirtualMachine) -> Option> { - self.class().slots.as_number.index.load().map(|f| { + self.class().slots().as_number.index.load().map(|f| { let ret = f(self, vm)?; if let Some(ret) = ret.downcast_ref_if_exact::(vm) { @@ -724,7 +724,7 @@ and may be removed in a future version of Python.", #[inline] pub fn float(self, vm: &VirtualMachine) -> Option>> { - self.class().slots.as_number.float.load().map(|f| { + self.class().slots().as_number.float.load().map(|f| { let ret = f(self, vm)?; if let Some(ret) = ret.downcast_ref_if_exact::(vm) { @@ -756,7 +756,7 @@ and may be removed in a future version of Python.", // PyNumber_Check - slots are now inherited #[must_use] pub fn check(obj: &PyObject) -> bool { - let methods = &obj.class().slots.as_number; + let methods = &obj.class().slots().as_number; let has_number = methods.int.load().is_some() || methods.index.load().is_some() || methods.float.load().is_some(); diff --git a/crates/vm/src/protocol/object.rs b/crates/vm/src/protocol/object.rs index c3662b321e4..e9abad45ff1 100644 --- a/crates/vm/src/protocol/object.rs +++ b/crates/vm/src/protocol/object.rs @@ -134,7 +134,7 @@ impl PyObject { #[inline] pub(crate) fn get_attr_inner(&self, attr_name: &Py, vm: &VirtualMachine) -> PyResult { vm_trace!("object.__getattribute__: {:?} {:?}", self, attr_name); - let getattro = self.class().slots.getattro.load().unwrap(); + let getattro = self.class().slots().getattro.load().unwrap(); getattro(self, attr_name, vm).inspect_err(|exc| { vm.set_attribute_error_context(exc, self.to_owned(), attr_name.to_owned()); }) @@ -191,7 +191,7 @@ impl PyObject { .interned_str(attr_name) .and_then(|attr_name| self.get_class_attr(attr_name)); if let Some(attr) = &descr - && let Some(descriptor) = attr.class().slots.descr_set.load() + && let Some(descriptor) = attr.class().slots().descr_set.load() { return descriptor(attr, self.to_owned(), value, vm); } @@ -205,7 +205,7 @@ impl PyObject { } // Only a type that left __setattr__ alone can be told about the // missing __dict__, since overriding it is what hides the slot. - let generic_setattro = self.class().slots.setattro.load().is_some_and(|f| { + let generic_setattro = self.class().slots().setattro.load().is_some_and(|f| { crate::types::fn_addr(f) == crate::types::fn_addr( PyBaseObject::slot_setattro as crate::types::SetattroFunc, @@ -338,7 +338,7 @@ impl PyObject { ) -> PyResult> { let swapped = op.swapped(); let call_cmp = |obj: &Self, other: &Self, op| { - let Some(cmp) = obj.class().slots.richcompare.load() else { + let Some(cmp) = obj.class().slots().richcompare.load() else { return Ok(PyArithmeticValue::NotImplemented); }; let r = match cmp(obj, other, op, vm)? { @@ -420,7 +420,7 @@ impl PyObject { pub fn repr(&self, vm: &VirtualMachine) -> PyResult> { vm.with_recursion("while getting the repr of an object", || { - self.class().slots.repr.load().map_or_else( + self.class().slots().repr.load().map_or_else( || { Err(vm.new_runtime_error(format!( "BUG: object of type '{}' has no __repr__ method. This is a bug in RustPython.", @@ -460,7 +460,7 @@ impl PyObject { Err(obj) => obj, }; - // TODO: replace to obj.class().slots.str + // TODO: replace to obj.class().slots().str let Some(str_method) = vm.get_special_method(&obj, identifier!(vm, __str__))? else { return obj.repr(vm); }; @@ -720,7 +720,7 @@ impl PyObject { } pub fn hash(&self, vm: &VirtualMachine) -> PyResult { - if let Some(hash) = self.class().slots.hash.load() { + if let Some(hash) = self.class().slots().hash.load() { return vm.with_recursion("while hashing", || hash(self, vm)); } @@ -837,7 +837,7 @@ impl PyObject { // A type carrying a sequence table turns the deletion down in // PySequence_DelItem's words instead; every heap type carries one. let name = self.class().slot_name(); - let msg = if seq.slots().has_any() || self.class().heaptype_ext.is_some() { + let msg = if seq.slots().has_any() || self.class().heaptype_ext().is_some() { format!("'{name}' object doesn't support item deletion") } else { format!("'{name}' object does not support item deletion") @@ -859,7 +859,7 @@ impl PyObject { return Ok(None); }; - let descr_get = res.class().slots.descr_get.load(); + let descr_get = res.class().slots().descr_get.load(); if let Some(descr_get) = descr_get { descr_get(res.as_object(), Some(self), Some(obj_cls.as_object()), vm).map(Some) } else { diff --git a/crates/vm/src/protocol/sequence.rs b/crates/vm/src/protocol/sequence.rs index 0c0bb503718..241f71398d6 100644 --- a/crates/vm/src/protocol/sequence.rs +++ b/crates/vm/src/protocol/sequence.rs @@ -153,7 +153,7 @@ impl PySequence<'_> { #[inline] #[must_use] pub fn slots(&self) -> &PySequenceSlots { - &self.obj.class().slots.as_sequence + &self.obj.class().slots().as_sequence } #[must_use] diff --git a/crates/vm/src/stdlib/_codecs.rs b/crates/vm/src/stdlib/_codecs.rs index af57a433c2f..3a12bd502f1 100644 --- a/crates/vm/src/stdlib/_codecs.rs +++ b/crates/vm/src/stdlib/_codecs.rs @@ -287,7 +287,7 @@ mod _codecs { #[pyfunction] fn escape_encode(args: EscapeEncodeArgs, _vm: &VirtualMachine) -> (Vec, usize) { let encoded = encodings::escape::encode(args.data.as_bytes()); - (encoded, args.data.len()) + (encoded, args.data.as_bytes().len()) } #[derive(FromArgs)] diff --git a/crates/vm/src/stdlib/_ctypes/function.rs b/crates/vm/src/stdlib/_ctypes/function.rs index b2667190a28..0770880161d 100644 --- a/crates/vm/src/stdlib/_ctypes/function.rs +++ b/crates/vm/src/stdlib/_ctypes/function.rs @@ -751,8 +751,8 @@ impl Constructor for PyCFuncPtr { .ok_or_else(|| vm.new_type_error("Expected a string"))? .to_string(); let dll = tuple - .iter() - .nth(1) + .as_slice() + .get(1) .ok_or_else(|| vm.new_type_error("Expected a tuple with at least 2 elements"))? .clone(); diff --git a/crates/vm/src/stdlib/_ctypes/simple.rs b/crates/vm/src/stdlib/_ctypes/simple.rs index 0ade154e36c..5b8dfb94244 100644 --- a/crates/vm/src/stdlib/_ctypes/simple.rs +++ b/crates/vm/src/stdlib/_ctypes/simple.rs @@ -90,7 +90,7 @@ fn set_primitive(_type_: &str, value: &PyObject, vm: &VirtualMachine) -> PyResul // c_set: accepts bytes(len=1), bytearray(len=1), or int(0-255) if value .downcast_ref_if_exact::(vm) - .is_some_and(|v| v.len() == 1) + .is_some_and(|v| v.as_bytes().len() == 1) || value .downcast_ref_if_exact::(vm) .is_some_and(|v| v.borrow_buf().len() == 1) @@ -303,7 +303,7 @@ impl PyCSimpleType { // c_char: 1 byte character Some("c") => { if let Some(bytes) = value.downcast_ref::() - && bytes.len() == 1 + && bytes.as_bytes().len() == 1 { return create_simple_with_value("c", &value); } diff --git a/crates/vm/src/stdlib/_ctypes/structure.rs b/crates/vm/src/stdlib/_ctypes/structure.rs index 41c7af418aa..8539c0caa16 100644 --- a/crates/vm/src/stdlib/_ctypes/structure.rs +++ b/crates/vm/src/stdlib/_ctypes/structure.rs @@ -634,7 +634,7 @@ impl SetAttr for PyCStructType { // Check for data descriptor first if let Some(attr) = pytype.get_class_attr(attr_name_interned) { - let descr_set = attr.class().slots.descr_set.load(); + let descr_set = attr.class().slots().descr_set.load(); if let Some(descriptor) = descr_set { return descriptor(&attr, pytype.to_owned().into(), value, vm); } diff --git a/crates/vm/src/stdlib/_ctypes/union.rs b/crates/vm/src/stdlib/_ctypes/union.rs index c1d49374e3f..f02aef3049d 100644 --- a/crates/vm/src/stdlib/_ctypes/union.rs +++ b/crates/vm/src/stdlib/_ctypes/union.rs @@ -487,7 +487,7 @@ impl SetAttr for PyCUnionType { // 1. First, do PyType's setattro (PyType_Type.tp_setattro first) // Check for data descriptor first if let Some(attr) = pytype.get_class_attr(attr_name_interned) { - let descr_set = attr.class().slots.descr_set.load(); + let descr_set = attr.class().slots().descr_set.load(); if let Some(descriptor) = descr_set { descriptor(&attr, pytype.to_owned().into(), value.clone(), vm)?; // After successful setattro, check if _fields_ and call process_fields diff --git a/crates/vm/src/stdlib/_io.rs b/crates/vm/src/stdlib/_io.rs index fe98d5f43d6..6e911a32895 100644 --- a/crates/vm/src/stdlib/_io.rs +++ b/crates/vm/src/stdlib/_io.rs @@ -3431,7 +3431,7 @@ mod _io { num_to_skip -= n_decoded; break; } - skip_bytes -= dec_buffer.len() as isize; + skip_bytes -= dec_buffer.as_bytes().len() as isize; skip_back = 1; } else { skip_bytes -= skip_back; diff --git a/crates/vm/src/suggestion.rs b/crates/vm/src/suggestion.rs index d300d4de8ff..7f3077b9e7a 100644 --- a/crates/vm/src/suggestion.rs +++ b/crates/vm/src/suggestion.rs @@ -71,7 +71,7 @@ pub fn offer_suggestions(exc: &Py, vm: &VirtualMachine) -> Opti if vm.is_none(&name) { return None; } - let tb = exc.__traceback__()?; + let tb = exc.traceback()?; let tb = tb.iter().last().unwrap_or(tb); let varnames = tb.frame.iframe().code().to_owned().co_varnames(vm); diff --git a/crates/vm/src/types/slot_defs.rs b/crates/vm/src/types/slot_defs.rs index 9d1f2dd2141..ec393fb2b4b 100644 --- a/crates/vm/src/types/slot_defs.rs +++ b/crates/vm/src/types/slot_defs.rs @@ -427,14 +427,16 @@ impl SlotAccessor { macro_rules! inherit_main { ($slot:ident) => {{ - let inherited = mro.iter().find_map(|cls| cls.slots.$slot.load()); + let inherited = mro.iter().find_map(|cls| cls.payload().slots.$slot.load()); typ.slots.$slot.store(inherited); }}; } macro_rules! inherit_number { ($slot:ident) => {{ - let inherited = mro.iter().find_map(|cls| cls.slots.as_number.$slot.load()); + let inherited = mro + .iter() + .find_map(|cls| cls.payload().slots.as_number.$slot.load()); typ.slots.as_number.$slot.store(inherited); }}; } @@ -443,14 +445,16 @@ impl SlotAccessor { ($slot:ident) => {{ let inherited = mro .iter() - .find_map(|cls| cls.slots.as_sequence.$slot.load()); + .find_map(|cls| cls.payload().slots.as_sequence.$slot.load()); typ.slots.as_sequence.$slot.store(inherited); }}; } macro_rules! inherit_mapping { ($slot:ident) => {{ - let inherited = mro.iter().find_map(|cls| cls.slots.as_mapping.$slot.load()); + let inherited = mro + .iter() + .find_map(|cls| cls.payload().slots.as_mapping.$slot.load()); typ.slots.as_mapping.$slot.store(inherited); }}; } @@ -469,8 +473,8 @@ impl SlotAccessor { // vectorcall as a constructor fast path (call=None). // See vectorcall_type() in type.rs for the dual-use design rationale. let inherited_vc = mro.iter().find_map(|cls| { - if cls.slots.call.load().is_some() { - cls.slots.vectorcall.load() + if cls.payload().slots.call.load().is_some() { + cls.payload().slots.vectorcall.load() } else { None } @@ -543,13 +547,19 @@ impl SlotAccessor { // Buffer protocol Self::BfGetBuffer => { - let inherited = mro.iter().find_map(|cls| cls.slots.as_buffer.load()); + let inherited = mro + .iter() + .find_map(|cls| cls.payload().slots.as_buffer.load()); typ.slots.as_buffer.store(inherited); } Self::BfReleaseBuffer => { - let has_release = mro.iter().any(|cls| cls.slots.has_release_buffer.load()); + let has_release = mro + .iter() + .any(|cls| cls.payload().slots.has_release_buffer.load()); typ.slots.has_release_buffer.store(has_release); - let py_release = mro.iter().any(|cls| cls.slots.python_release_buffer.load()); + let py_release = mro + .iter() + .any(|cls| cls.payload().slots.python_release_buffer.load()); typ.slots.python_release_buffer.store(py_release); } @@ -623,7 +633,8 @@ impl SlotAccessor { && let Some(base_val) = base.slots.init.load() { let slot_defined = base.base.deref().is_none_or(|bb| { - bb.slots.init.load().map(|v| fn_addr(v)) != Some(fn_addr(base_val)) + bb.payload().slots.init.load().map(|v| fn_addr(v)) + != Some(fn_addr(base_val)) }); if slot_defined { typ.slots.init.store(Some(base_val)); diff --git a/crates/vm/src/vm/mod.rs b/crates/vm/src/vm/mod.rs index c0750c7b871..d1a676bec88 100644 --- a/crates/vm/src/vm/mod.rs +++ b/crates/vm/src/vm/mod.rs @@ -1637,7 +1637,7 @@ impl VirtualMachine { let unraisablehook = sys_module.get_attr("unraisablehook", self).unwrap(); let exc_type = e.class().to_owned(); - let exc_traceback = e.__traceback__().to_pyobject(self); // TODO: actual traceback + let exc_traceback = e.traceback().to_pyobject(self); // TODO: actual traceback let exc_value = e.into(); let args = stdlib::sys::UnraisableHookArgsData { exc_type, @@ -3439,7 +3439,7 @@ impl VirtualMachine { attr_name: impl AsPyStr<'a>, ) -> PyResult> { let attr_name = attr_name.as_pystr(&self.ctx); - let getattro = obj.class().slots.getattro.load().unwrap(); + let getattro = obj.class().slots().getattro.load().unwrap(); let result = if fn_addr(getattro) == fn_addr(PyBaseObject::getattro as GetattroFunc) { obj.generic_getattr_opt(attr_name, None, self) } else { diff --git a/crates/vm/src/vm/vm_object.rs b/crates/vm/src/vm/vm_object.rs index 789979addcd..baa0c8095c6 100644 --- a/crates/vm/src/vm/vm_object.rs +++ b/crates/vm/src/vm/vm_object.rs @@ -109,7 +109,7 @@ impl VirtualMachine { obj: Option<&PyObject>, cls: Option<&PyObject>, ) -> Option { - let descr_get = descr.class().slots.descr_get.load()?; + let descr_get = descr.class().slots().descr_get.load()?; Some(descr_get(descr, obj, cls, self)) } diff --git a/crates/vm/src/vm/vm_ops.rs b/crates/vm/src/vm/vm_ops.rs index 958ba65ff4c..2e2c11f81ab 100644 --- a/crates/vm/src/vm/vm_ops.rs +++ b/crates/vm/src/vm/vm_ops.rs @@ -292,7 +292,7 @@ impl VirtualMachine { iop_slot: PyNumberBinaryOp, op_slot: PyNumberBinaryOp, ) -> PyResult { - if let Some(slot) = a.class().slots.as_number.left_binary_op(iop_slot) { + if let Some(slot) = a.class().slots().as_number.left_binary_op(iop_slot) { let x = slot(a, b, self)?; if !x.is(&self.ctx.not_implemented) { return Ok(x); @@ -419,7 +419,7 @@ impl VirtualMachine { op_slot: PyNumberTernaryOp, op_str: &str, ) -> PyResult { - if let Some(slot) = a.class().slots.as_number.left_ternary_op(iop_slot) { + if let Some(slot) = a.class().slots().as_number.left_ternary_op(iop_slot) { let x = slot(a, b, c, self)?; if !x.is(&self.ctx.not_implemented) { return Ok(x);