diff --git a/Architecture.md b/Architecture.md index 474381d4..8e393473 100644 --- a/Architecture.md +++ b/Architecture.md @@ -102,7 +102,7 @@ Since we need lots of boilerplate for implementing common traits for these types [`src/pycell.rs`], [`src/pyclass.rs`], and [`src/type_object.rs`] contain types and traits to make `#[pyclass]` work. -Also, [`src/pyclass_init.rs`] and [`src/pyclass_slots.rs`] have related functionalities. +Also, [`src/pyclass_init.rs`] and [`src/impl_/pyclass.rs`] have related functionalities. To realize object-oriented programming in C, all Python objects must have the following two fields at the beginning. diff --git a/CHANGELOG.md b/CHANGELOG.md index f85aad96..693ea4f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,7 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Reduce generated LLVM code size (to improve compile times) for: - internal `handle_panic` helper [#2074](https://github.com/PyO3/pyo3/pull/2074) - `#[pyfunction]` and `#[pymethods]` argument extraction [#2075](https://github.com/PyO3/pyo3/pull/2075) - - `#[pyclass]` type object creation [#2076](https://github.com/PyO3/pyo3/pull/2076) + - `#[pyclass]` type object creation [#2076](https://github.com/PyO3/pyo3/pull/2076) [#2081](https://github.com/PyO3/pyo3/pull/2081) ### Removed diff --git a/guide/src/class.md b/guide/src/class.md index 9ba39ed7..651032ae 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -838,8 +838,8 @@ unsafe impl pyo3::PyTypeInfo for MyClass { } impl pyo3::pyclass::PyClass for MyClass { - type Dict = pyo3::pyclass_slots::PyClassDummySlot; - type WeakRef = pyo3::pyclass_slots::PyClassDummySlot; + type Dict = pyo3::impl_::pyclass::PyClassDummySlot; + type WeakRef = pyo3::impl_::pyclass::PyClassDummySlot; type BaseNativeType = PyAny; } diff --git a/pyo3-macros-backend/src/pyclass.rs b/pyo3-macros-backend/src/pyclass.rs index 4b924357..f7367eef 100644 --- a/pyo3-macros-backend/src/pyclass.rs +++ b/pyo3-macros-backend/src/pyclass.rs @@ -663,16 +663,16 @@ impl<'a> PyClassImplsBuilder<'a> { let cls = self.cls; let attr = self.attr; let dict = if attr.has_dict { - quote! { _pyo3::pyclass_slots::PyClassDictSlot } + quote! { _pyo3::impl_::pyclass::PyClassDictSlot } } else { - quote! { _pyo3::pyclass_slots::PyClassDummySlot } + quote! { _pyo3::impl_::pyclass::PyClassDummySlot } }; // insert space for weak ref let weakref = if attr.has_weaklist { - quote! { _pyo3::pyclass_slots::PyClassWeakRefSlot } + quote! { _pyo3::impl_::pyclass::PyClassWeakRefSlot } } else { - quote! { _pyo3::pyclass_slots::PyClassDummySlot } + quote! { _pyo3::impl_::pyclass::PyClassDummySlot } }; let base_nativetype = if attr.has_extends { @@ -727,6 +727,27 @@ impl<'a> PyClassImplsBuilder<'a> { let base = &self.attr.base; let is_subclass = self.attr.has_extends; + let dict_offset = if self.attr.has_dict { + quote! { + fn dict_offset() -> ::std::option::Option<_pyo3::ffi::Py_ssize_t> { + ::std::option::Option::Some(_pyo3::impl_::pyclass::dict_offset::()) + } + } + } else { + TokenStream::new() + }; + + // insert space for weak ref + let weaklist_offset = if self.attr.has_weaklist { + quote! { + fn weaklist_offset() -> ::std::option::Option<_pyo3::ffi::Py_ssize_t> { + ::std::option::Option::Some(_pyo3::impl_::pyclass::weaklist_offset::()) + } + } + } else { + TokenStream::new() + }; + let thread_checker = if self.attr.has_unsendable { quote! { _pyo3::class::impl_::ThreadCheckerImpl<#cls> } } else if self.attr.has_extends { @@ -831,6 +852,8 @@ impl<'a> PyClassImplsBuilder<'a> { let collector = PyClassImplCollector::::new(); collector.buffer_procs() } + #dict_offset + #weaklist_offset } #inventory_class diff --git a/src/class/impl_.rs b/src/class/impl_.rs index 3f385d1a..61d97018 100644 --- a/src/class/impl_.rs +++ b/src/class/impl_.rs @@ -71,19 +71,31 @@ pub trait PyClassImpl: Sized { type Inventory: PyClassInventory; fn for_each_method_def(_visitor: &mut dyn FnMut(&[PyMethodDefType])) {} + fn for_each_proto_slot(_visitor: &mut dyn FnMut(&[ffi::PyType_Slot])) {} + + #[inline] fn get_new() -> Option { None } + #[inline] fn get_alloc() -> Option { None } + #[inline] fn get_free() -> Option { None } - fn for_each_proto_slot(_visitor: &mut dyn FnMut(&[ffi::PyType_Slot])) {} fn get_buffer() -> Option<&'static PyBufferProcs> { None } + #[inline] + fn dict_offset() -> Option { + None + } + #[inline] + fn weaklist_offset() -> Option { + None + } } // Traits describing known special methods. diff --git a/src/impl_.rs b/src/impl_.rs index 61705224..d591886f 100644 --- a/src/impl_.rs +++ b/src/impl_.rs @@ -11,4 +11,5 @@ pub mod freelist; pub mod frompyobject; pub(crate) mod not_send; #[doc(hidden)] +pub mod pyclass; pub mod pymodule; diff --git a/src/pyclass_slots.rs b/src/impl_/pyclass.rs similarity index 83% rename from src/pyclass_slots.rs rename to src/impl_/pyclass.rs index 43426aa4..ae534611 100644 --- a/src/pyclass_slots.rs +++ b/src/impl_/pyclass.rs @@ -1,12 +1,19 @@ -//! Contains additional fields for `#[pyclass]`. -//! -//! Mainly used by PyO3's proc-macro code. -use crate::{ffi, Python}; +use crate::{ffi, PyCell, PyClass, Python}; + +/// Gets the offset of the dictionary from the start of the object in bytes. +#[inline] +pub fn dict_offset() -> ffi::Py_ssize_t { + PyCell::::dict_offset() +} + +/// Gets the offset of the weakref list from the start of the object in bytes. +#[inline] +pub fn weaklist_offset() -> ffi::Py_ssize_t { + PyCell::::weaklist_offset() +} /// Represents the `__dict__` field for `#[pyclass]`. pub trait PyClassDict { - /// Whether this `__dict__` field is capable of holding a dictionary. - const IS_DUMMY: bool = true; /// Initializes a [PyObject](crate::ffi::PyObject) `__dict__` reference. fn new() -> Self; /// Empties the dictionary of its key-value pairs. @@ -17,8 +24,6 @@ pub trait PyClassDict { /// Represents the `__weakref__` field for `#[pyclass]`. pub trait PyClassWeakRef { - /// Whether this `weakref` type is capable of holding weak references. - const IS_DUMMY: bool = true; /// Initializes a `weakref` instance. fn new() -> Self; /// Clears the weak references to the given object. @@ -58,7 +63,6 @@ pub struct PyClassDictSlot(*mut ffi::PyObject); impl PyClassDict for PyClassDictSlot { private_impl! {} - const IS_DUMMY: bool = false; #[inline] fn new() -> Self { Self(std::ptr::null_mut()) @@ -79,7 +83,6 @@ pub struct PyClassWeakRefSlot(*mut ffi::PyObject); impl PyClassWeakRef for PyClassWeakRefSlot { private_impl! {} - const IS_DUMMY: bool = false; #[inline] fn new() -> Self { Self(std::ptr::null_mut()) diff --git a/src/lib.rs b/src/lib.rs index 85e39825..bb6d6ace 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -337,7 +337,6 @@ pub mod prelude; pub mod pycell; pub mod pyclass; pub mod pyclass_init; -pub mod pyclass_slots; mod python; pub mod type_object; pub mod types; diff --git a/src/pycell.rs b/src/pycell.rs index a47c9fed..a0c9e197 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -175,9 +175,9 @@ //! [Interior Mutability]: https://doc.rust-lang.org/book/ch15-05-interior-mutability.html "RefCell and the Interior Mutability Pattern - The Rust Programming Language" use crate::exceptions::PyRuntimeError; +use crate::impl_::pyclass::{PyClassDict, PyClassWeakRef}; use crate::pyclass::PyClass; use crate::pyclass_init::PyClassInitializer; -use crate::pyclass_slots::{PyClassDict, PyClassWeakRef}; use crate::type_object::{PyLayout, PySizedLayout}; use crate::types::PyAny; use crate::{class::impl_::PyClassBaseType, class::impl_::PyClassThreadChecker}; @@ -253,84 +253,6 @@ pub(crate) struct PyCellContents { pub(crate) weakref: T::WeakRef, } -impl PyCell { - /// Get the offset of the dictionary from the start of the struct in bytes. - #[cfg(not(all(Py_LIMITED_API, not(Py_3_9))))] - pub(crate) fn dict_offset() -> Option { - use std::convert::TryInto; - if T::Dict::IS_DUMMY { - None - } else { - #[cfg(addr_of)] - let offset = { - // With std::ptr::addr_of - can measure offset using uninit memory without UB. - let cell = std::mem::MaybeUninit::::uninit(); - let base_ptr = cell.as_ptr(); - let dict_ptr = unsafe { std::ptr::addr_of!((*base_ptr).contents.dict) }; - unsafe { (dict_ptr as *const u8).offset_from(base_ptr as *const u8) } - }; - #[cfg(not(addr_of))] - let offset = { - // No std::ptr::addr_of - need to take references to PyCell to measure offsets; - // make a zero-initialised "fake" one so that referencing it is not UB. - let mut cell = std::mem::MaybeUninit::::uninit(); - unsafe { - std::ptr::write_bytes(cell.as_mut_ptr(), 0, 1); - } - let cell = unsafe { cell.assume_init() }; - let dict_ptr = &cell.contents.dict; - // offset_from wasn't stabilised until 1.47, so we also have to work around - // that... - let offset = (dict_ptr as *const _ as usize) - (&cell as *const _ as usize); - // This isn't a valid cell, so ensure no Drop code runs etc. - std::mem::forget(cell); - offset - }; - // Py_ssize_t may not be equal to isize on all platforms - #[allow(clippy::useless_conversion)] - Some(offset.try_into().expect("offset should fit in Py_ssize_t")) - } - } - - /// Get the offset of the weakref list from the start of the struct in bytes. - #[cfg(not(all(Py_LIMITED_API, not(Py_3_9))))] - pub(crate) fn weakref_offset() -> Option { - use std::convert::TryInto; - if T::WeakRef::IS_DUMMY { - None - } else { - #[cfg(addr_of)] - let offset = { - // With std::ptr::addr_of - can measure offset using uninit memory without UB. - let cell = std::mem::MaybeUninit::::uninit(); - let base_ptr = cell.as_ptr(); - let weaklist_ptr = unsafe { std::ptr::addr_of!((*base_ptr).contents.weakref) }; - unsafe { (weaklist_ptr as *const u8).offset_from(base_ptr as *const u8) } - }; - #[cfg(not(addr_of))] - let offset = { - // No std::ptr::addr_of - need to take references to PyCell to measure offsets; - // make a zero-initialised "fake" one so that referencing it is not UB. - let mut cell = std::mem::MaybeUninit::::uninit(); - unsafe { - std::ptr::write_bytes(cell.as_mut_ptr(), 0, 1); - } - let cell = unsafe { cell.assume_init() }; - let weaklist_ptr = &cell.contents.weakref; - // offset_from wasn't stabilised until 1.47, so we also have to work around - // that... - let offset = (weaklist_ptr as *const _ as usize) - (&cell as *const _ as usize); - // This isn't a valid cell, so ensure no Drop code runs etc. - std::mem::forget(cell); - offset - }; - // Py_ssize_t may not be equal to isize on all platforms - #[allow(clippy::useless_conversion)] - Some(offset.try_into().expect("offset should fit in Py_ssize_t")) - } - } -} - unsafe impl PyNativeType for PyCell {} impl PyCell { @@ -502,6 +424,72 @@ impl PyCell { fn get_ptr(&self) -> *mut T { self.contents.value.get() } + + /// Gets the offset of the dictionary from the start of the struct in bytes. + pub(crate) fn dict_offset() -> ffi::Py_ssize_t { + use std::convert::TryInto; + #[cfg(addr_of)] + let offset = { + // With std::ptr::addr_of - can measure offset using uninit memory without UB. + let cell = std::mem::MaybeUninit::::uninit(); + let base_ptr = cell.as_ptr(); + let dict_ptr = unsafe { std::ptr::addr_of!((*base_ptr).contents.dict) }; + unsafe { (dict_ptr as *const u8).offset_from(base_ptr as *const u8) } + }; + #[cfg(not(addr_of))] + let offset = { + // No std::ptr::addr_of - need to take references to PyCell to measure offsets; + // make a zero-initialised "fake" one so that referencing it is not UB. + let mut cell = std::mem::MaybeUninit::::uninit(); + unsafe { + std::ptr::write_bytes(cell.as_mut_ptr(), 0, 1); + } + let cell = unsafe { cell.assume_init() }; + let dict_ptr = &cell.contents.dict; + // offset_from wasn't stabilised until 1.47, so we also have to work around + // that... + let offset = (dict_ptr as *const _ as usize) - (&cell as *const _ as usize); + // This isn't a valid cell, so ensure no Drop code runs etc. + std::mem::forget(cell); + offset + }; + // Py_ssize_t may not be equal to isize on all platforms + #[allow(clippy::useless_conversion)] + offset.try_into().expect("offset should fit in Py_ssize_t") + } + + /// Gets the offset of the weakref list from the start of the struct in bytes. + pub(crate) fn weaklist_offset() -> ffi::Py_ssize_t { + use std::convert::TryInto; + #[cfg(addr_of)] + let offset = { + // With std::ptr::addr_of - can measure offset using uninit memory without UB. + let cell = std::mem::MaybeUninit::::uninit(); + let base_ptr = cell.as_ptr(); + let weaklist_ptr = unsafe { std::ptr::addr_of!((*base_ptr).contents.weakref) }; + unsafe { (weaklist_ptr as *const u8).offset_from(base_ptr as *const u8) } + }; + #[cfg(not(addr_of))] + let offset = { + // No std::ptr::addr_of - need to take references to PyCell to measure offsets; + // make a zero-initialised "fake" one so that referencing it is not UB. + let mut cell = std::mem::MaybeUninit::::uninit(); + unsafe { + std::ptr::write_bytes(cell.as_mut_ptr(), 0, 1); + } + let cell = unsafe { cell.assume_init() }; + let weaklist_ptr = &cell.contents.weakref; + // offset_from wasn't stabilised until 1.47, so we also have to work around + // that... + let offset = (weaklist_ptr as *const _ as usize) - (&cell as *const _ as usize); + // This isn't a valid cell, so ensure no Drop code runs etc. + std::mem::forget(cell); + offset + }; + // Py_ssize_t may not be equal to isize on all platforms + #[allow(clippy::useless_conversion)] + offset.try_into().expect("offset should fit in Py_ssize_t") + } } unsafe impl PyLayout for PyCell {} diff --git a/src/pyclass.rs b/src/pyclass.rs index 56039405..be6d7549 100644 --- a/src/pyclass.rs +++ b/src/pyclass.rs @@ -1,8 +1,8 @@ //! `PyClass` and related traits. use crate::{ - class::impl_::{fallback_new, tp_dealloc, PyClassImpl, PyBufferProcs}, + class::impl_::{fallback_new, tp_dealloc, PyBufferProcs, PyClassImpl}, ffi, - pyclass_slots::{PyClassDict, PyClassWeakRef}, + impl_::pyclass::{PyClassDict, PyClassWeakRef}, PyCell, PyErr, PyMethodDefType, PyNativeType, PyResult, PyTypeInfo, Python, }; use std::{ @@ -33,36 +33,37 @@ fn into_raw(vec: Vec) -> *mut c_void { Box::into_raw(vec.into_boxed_slice()) as _ } -pub(crate) fn create_type_object( - py: Python, -) -> *mut ffi::PyTypeObject +pub(crate) fn create_type_object(py: Python) -> *mut ffi::PyTypeObject where T: PyClass, { - match unsafe { create_type_object_impl( - py, - T::DOC, - T::MODULE, - T::NAME, - T::BaseType::type_object_raw(py), - std::mem::size_of::(), - T::get_new(), - tp_dealloc::, - T::get_alloc(), - T::get_free(), - PyCell::::dict_offset(), - PyCell::::weakref_offset(), - &T::for_each_method_def, - &T::for_each_proto_slot, - T::IS_GC, - T::IS_BASETYPE, - T::get_buffer(), - ) } { + match unsafe { + create_type_object_impl( + py, + T::DOC, + T::MODULE, + T::NAME, + T::BaseType::type_object_raw(py), + std::mem::size_of::(), + T::get_new(), + tp_dealloc::, + T::get_alloc(), + T::get_free(), + T::dict_offset(), + T::weaklist_offset(), + &T::for_each_method_def, + &T::for_each_proto_slot, + T::IS_GC, + T::IS_BASETYPE, + T::get_buffer(), + ) + } { Ok(type_object) => type_object, Err(e) => type_object_creation_failed(py, e, T::NAME), } } +#[allow(clippy::too_many_arguments)] unsafe fn create_type_object_impl( py: Python, tp_doc: &str, @@ -75,7 +76,7 @@ unsafe fn create_type_object_impl( tp_alloc: Option, tp_free: Option, dict_offset: Option, - weakref_offset: Option, + weaklist_offset: Option, for_each_method_def: &dyn Fn(&mut dyn FnMut(&[PyMethodDefType])), for_each_proto_slot: &dyn Fn(&mut dyn FnMut(&[ffi::PyType_Slot])), is_gc: bool, @@ -88,11 +89,7 @@ unsafe fn create_type_object_impl( slots.push(ffi::PyType_Slot { slot, pfunc }); } - push_slot( - &mut slots, - ffi::Py_tp_base, - base_type_object as _, - ); + push_slot(&mut slots, ffi::Py_tp_base, base_type_object as _); if let Some(doc) = py_class_doc(tp_doc) { push_slot(&mut slots, ffi::Py_tp_doc, doc as _); } @@ -113,7 +110,7 @@ unsafe fn create_type_object_impl( #[cfg(Py_3_9)] { - let members = py_class_members(dict_offset, weakref_offset); + let members = py_class_members(dict_offset, weaklist_offset); if !members.is_empty() { push_slot(&mut slots, ffi::Py_tp_members, into_raw(members)) } @@ -153,7 +150,13 @@ unsafe fn create_type_object_impl( if type_object.is_null() { Err(PyErr::fetch(py)) } else { - tp_init_additional(type_object as _, tp_doc, buffer_procs, dict_offset, weakref_offset); + tp_init_additional( + type_object as _, + tp_doc, + buffer_procs, + dict_offset, + weaklist_offset, + ); Ok(type_object as _) } } @@ -166,7 +169,13 @@ fn type_object_creation_failed(py: Python, e: PyErr, name: &'static str) -> ! { /// Additional type initializations necessary before Python 3.10 #[cfg(all(not(Py_LIMITED_API), not(Py_3_10)))] -fn tp_init_additional(type_object: *mut ffi::PyTypeObject, tp_doc: &str, buffer_procs: Option<&PyBufferProcs>, dict_offset: Option, weakref_offset: Option) { +fn tp_init_additional( + type_object: *mut ffi::PyTypeObject, + _tp_doc: &str, + _buffer_procs: Option<&PyBufferProcs>, + _dict_offset: Option, + _weaklist_offset: Option, +) { // Just patch the type objects for the things there's no // PyType_FromSpec API for... there's no reason this should work, // except for that it does and we have tests. @@ -174,53 +183,54 @@ fn tp_init_additional(type_object: *mut ffi::PyTypeObject, tp_doc: &str, buffer_ // Running this causes PyPy to segfault. #[cfg(all(not(PyPy), not(Py_3_10)))] { - if tp_doc != "\0" { + if _tp_doc != "\0" { unsafe { // Until CPython 3.10, tp_doc was treated specially for // heap-types, and it removed the text_signature value from it. // We go in after the fact and replace tp_doc with something // that _does_ include the text_signature value! ffi::PyObject_Free((*type_object).tp_doc as _); - let data = ffi::PyObject_Malloc(tp_doc.len()); - data.copy_from(tp_doc.as_ptr() as _, tp_doc.len()); + let data = ffi::PyObject_Malloc(_tp_doc.len()); + data.copy_from(_tp_doc.as_ptr() as _, _tp_doc.len()); (*type_object).tp_doc = data as _; } } } - // Setting buffer protocols via slots doesn't work until Python 3.9, so on older versions we - // must manually fixup the type object. + // Setting buffer protocols, tp_dictoffset and tp_weaklistoffset via slots doesn't work until + // Python 3.9, so on older versions we must manually fixup the type object. #[cfg(not(Py_3_9))] { - if let Some(buffer) = buffer_procs { + if let Some(buffer) = _buffer_procs { unsafe { (*(*type_object).tp_as_buffer).bf_getbuffer = buffer.bf_getbuffer; (*(*type_object).tp_as_buffer).bf_releasebuffer = buffer.bf_releasebuffer; } } - } - // Setting tp_dictoffset and tp_weaklistoffset via slots doesn't work until Python 3.9, so on - // older versions again we must fixup the type object. - #[cfg(not(Py_3_9))] - { - // __dict__ support - if let Some(dict_offset) = dict_offset { + if let Some(dict_offset) = _dict_offset { unsafe { (*type_object).tp_dictoffset = dict_offset; } } - // weakref support - if let Some(weakref_offset) = weakref_offset { + + if let Some(weaklist_offset) = _weaklist_offset { unsafe { - (*type_object).tp_weaklistoffset = weakref_offset; + (*type_object).tp_weaklistoffset = weaklist_offset; } } } } #[cfg(any(Py_LIMITED_API, Py_3_10))] -fn tp_init_additional(_type_object: *mut ffi::PyTypeObject, tp_doc: &str, buffer_procs: Option<&PyBufferProcs>, dict_offset: Option, weakref_offset: Option) {} +fn tp_init_additional( + _type_object: *mut ffi::PyTypeObject, + _tp_doc: &str, + _buffer_procs: Option<&PyBufferProcs>, + _dict_offset: Option, + _weaklist_offset: Option, +) { +} fn py_class_doc(class_doc: &str) -> Option<*mut c_char> { match class_doc { @@ -293,7 +303,7 @@ fn py_class_method_defs( #[cfg(Py_3_9)] fn py_class_members( dict_offset: Option, - weakref_offset: Option, + weaklist_offset: Option, ) -> Vec { #[inline(always)] fn offset_def(name: &'static str, offset: ffi::Py_ssize_t) -> ffi::structmember::PyMemberDef { @@ -314,8 +324,8 @@ fn py_class_members( } // weakref support - if let Some(weakref_offset) = weakref_offset { - members.push(offset_def("__weaklistoffset__\0", weakref_offset)); + if let Some(weaklist_offset) = weaklist_offset { + members.push(offset_def("__weaklistoffset__\0", weaklist_offset)); } if !members.is_empty() { diff --git a/src/pyclass_init.rs b/src/pyclass_init.rs index feb399d5..5fb84a96 100644 --- a/src/pyclass_init.rs +++ b/src/pyclass_init.rs @@ -1,6 +1,6 @@ //! Contains initialization utilities for `#[pyclass]`. use crate::class::impl_::PyClassThreadChecker; -use crate::pyclass_slots::{PyClassDict, PyClassWeakRef}; +use crate::impl_::pyclass::{PyClassDict, PyClassWeakRef}; use crate::{callback::IntoPyCallbackOutput, class::impl_::PyClassBaseType}; use crate::{ffi, PyCell, PyClass, PyErr, PyResult, Python}; use crate::{ diff --git a/tests/test_class_basics.rs b/tests/test_class_basics.rs index 1e360516..ad8c8c07 100644 --- a/tests/test_class_basics.rs +++ b/tests/test_class_basics.rs @@ -344,7 +344,6 @@ fn test_tuple_struct_class() { }); } - #[pyclass(dict, subclass)] struct DunderDictSupport {} @@ -419,8 +418,6 @@ fn weakref_dunder_dict_support() { ); } - - #[pyclass(weakref, subclass)] struct WeakRefSupport {} diff --git a/tests/ui/abi3_nativetype_inheritance.stderr b/tests/ui/abi3_nativetype_inheritance.stderr index 6faab088..4f9681d8 100644 --- a/tests/ui/abi3_nativetype_inheritance.stderr +++ b/tests/ui/abi3_nativetype_inheritance.stderr @@ -9,10 +9,9 @@ note: required by a bound in `PyClassBaseType` --> src/class/impl_.rs | | / pub trait PyClassBaseType: Sized { - | | type Dict; - | | type WeakRef; | | type LayoutAsBase: PyCellLayout; -... | + | | type BaseNativeType; + | | type ThreadChecker: PyClassThreadChecker; | | type Initializer: PyObjectInit; | | } | |_^ required by this bound in `PyClassBaseType`