diff --git a/src/class/impl_.rs b/src/class/impl_.rs index 57c86203a75..ad2515c6b25 100644 --- a/src/class/impl_.rs +++ b/src/class/impl_.rs @@ -108,229 +108,148 @@ impl PyClassCallImpl for &'_ PyClassImplCollector { } } -#[allow(non_camel_case_types)] -pub trait PyClass__setattr__SlotFragment: Sized { - #[inline] - #[allow(non_snake_case)] - fn __setattr__implemented(self) -> bool { - false - } - - #[inline] - unsafe fn __setattr__( - self, - _slf: *mut ffi::PyObject, - _attr: *mut ffi::PyObject, - _value: NonNull, - ) -> PyResult<()> { - Err(PyAttributeError::new_err("can't set attribute")) - } -} - -impl PyClass__setattr__SlotFragment for &'_ PyClassImplCollector {} - -#[allow(non_camel_case_types)] -pub trait PyClass__delattr__SlotFragment: Sized { - #[inline] - #[allow(non_snake_case)] - fn __delattr__implemented(self) -> bool { - false - } - - #[inline] - unsafe fn __delattr__( - self, - _slf: *mut ffi::PyObject, - _attr: *mut ffi::PyObject, - ) -> PyResult<()> { - Err(PyAttributeError::new_err("can't delete attribute")) - } -} +macro_rules! slot_fragment_trait { + ($trait_name:ident, $implemented_name:ident, $($default_method:tt)*) => { + #[allow(non_camel_case_types)] + pub trait $trait_name: Sized { + #[inline] + #[allow(non_snake_case)] + fn $implemented_name(self) -> bool { + false + } -impl PyClass__delattr__SlotFragment for &'_ PyClassImplCollector {} + $($default_method)* + } -#[doc(hidden)] -#[macro_export] -macro_rules! generate_pyclass_setattr_slot { - ($cls:ty) => {{ - use ::std::option::Option::*; - use $crate::class::impl_::*; - let collector = PyClassImplCollector::<$cls>::new(); - if collector.__setattr__implemented() || collector.__delattr__implemented() { - unsafe extern "C" fn __wrap( - _slf: *mut $crate::ffi::PyObject, - attr: *mut $crate::ffi::PyObject, - value: *mut $crate::ffi::PyObject, - ) -> ::std::os::raw::c_int { - $crate::callback::handle_panic(|py| { - let collector = PyClassImplCollector::<$cls>::new(); - $crate::callback::convert(py, { - if let Some(value) = ::std::ptr::NonNull::new(value) { - collector.__setattr__(_slf, attr, value) - } else { - collector.__delattr__(_slf, attr) - } - }) - }) + impl $trait_name for &'_ PyClassImplCollector {} + } +} + +/// Macro which expands to three items +/// - Trait for a __setitem__ dunder +/// - Trait for the corresponding __delitem__ dunder +/// - A macro which will use dtolnay specialisation to generate the shared slot for the two dunders +macro_rules! define_pyclass_setattr_slot { + ( + $set_trait:ident, + $del_trait:ident, + $set_implemented:ident, + $del_implemented:ident, + $set:ident, + $del:ident, + $set_error:expr, + $del_error:expr, + $generate_macro:ident, + $slot:ident, + $func_ty:ident, + ) => { + slot_fragment_trait! { + $set_trait, + $set_implemented, + + /// # Safety: _slf and _attr must be valid non-null Python objects + #[inline] + unsafe fn $set( + self, + _slf: *mut ffi::PyObject, + _attr: *mut ffi::PyObject, + _value: NonNull, + ) -> PyResult<()> { + $set_error } - Some($crate::ffi::PyType_Slot { - slot: $crate::ffi::Py_tp_setattro, - pfunc: __wrap as $crate::ffi::setattrofunc as _, - }) - } else { - None } - }}; -} - -#[allow(non_camel_case_types)] -pub trait PyClass__set__SlotFragment: Sized { - #[inline] - #[allow(non_snake_case)] - fn __set__implemented(self) -> bool { - false - } - - #[inline] - unsafe fn __set__( - self, - _slf: *mut ffi::PyObject, - _attr: *mut ffi::PyObject, - _value: NonNull, - ) -> PyResult<()> { - Err(PyNotImplementedError::new_err("can't set descriptor")) - } -} - -impl PyClass__set__SlotFragment for &'_ PyClassImplCollector {} - -#[allow(non_camel_case_types)] -pub trait PyClass__delete__SlotFragment: Sized { - #[allow(non_snake_case)] - #[inline] - fn __delete__implemented(self) -> bool { - false - } - #[inline] - unsafe fn __delete__( - self, - _slf: *mut ffi::PyObject, - _attr: *mut ffi::PyObject, - ) -> PyResult<()> { - Err(PyNotImplementedError::new_err("can't delete descriptor")) - } -} - -impl PyClass__delete__SlotFragment for &'_ PyClassImplCollector {} - -#[doc(hidden)] -#[macro_export] -macro_rules! generate_pyclass_setdescr_slot { - ($cls:ty) => {{ - use ::std::option::Option::*; - use $crate::class::impl_::*; - let collector = PyClassImplCollector::<$cls>::new(); - if collector.__set__implemented() || collector.__delete__implemented() { - unsafe extern "C" fn __wrap( - _slf: *mut $crate::ffi::PyObject, - attr: *mut $crate::ffi::PyObject, - value: *mut $crate::ffi::PyObject, - ) -> ::std::os::raw::c_int { - $crate::callback::handle_panic(|py| { - let collector = PyClassImplCollector::<$cls>::new(); - $crate::callback::convert(py, { - if let Some(value) = ::std::ptr::NonNull::new(value) { - collector.__set__(_slf, attr, value) - } else { - collector.__delete__(_slf, attr) - } - }) - }) + slot_fragment_trait! { + $del_trait, + $del_implemented, + + /// # Safety: _slf and _attr must be valid non-null Python objects + #[inline] + unsafe fn $del( + self, + _slf: *mut ffi::PyObject, + _attr: *mut ffi::PyObject, + ) -> PyResult<()> { + $del_error } - Some($crate::ffi::PyType_Slot { - slot: $crate::ffi::Py_tp_descr_set, - pfunc: __wrap as $crate::ffi::descrsetfunc as _, - }) - } else { - None } - }}; -} - -#[allow(non_camel_case_types)] -pub trait PyClass__setitem__SlotFragment: Sized { - #[inline] - #[allow(non_snake_case)] - fn __setitem__implemented(self) -> bool { - false - } - - #[inline] - unsafe fn __setitem__( - self, - _slf: *mut ffi::PyObject, - _attr: *mut ffi::PyObject, - _value: NonNull, - ) -> PyResult<()> { - Err(PyNotImplementedError::new_err("can't set item")) - } -} -impl PyClass__setitem__SlotFragment for &'_ PyClassImplCollector {} - -#[allow(non_camel_case_types)] -pub trait PyClass__delitem__SlotFragment: Sized { - #[allow(non_snake_case)] - #[inline] - fn __delitem__implemented(self) -> bool { - false - } - - #[inline] - unsafe fn __delitem__( - self, - _slf: *mut ffi::PyObject, - _attr: *mut ffi::PyObject, - ) -> PyResult<()> { - Err(PyNotImplementedError::new_err("can't delete item")) - } -} - -impl PyClass__delitem__SlotFragment for &'_ PyClassImplCollector {} - -#[doc(hidden)] -#[macro_export] -macro_rules! generate_pyclass_setitem_slot { - ($cls:ty) => {{ - use ::std::option::Option::*; - use $crate::class::impl_::*; - let collector = PyClassImplCollector::<$cls>::new(); - if collector.__setitem__implemented() || collector.__delitem__implemented() { - unsafe extern "C" fn __wrap( - _slf: *mut $crate::ffi::PyObject, - attr: *mut $crate::ffi::PyObject, - value: *mut $crate::ffi::PyObject, - ) -> ::std::os::raw::c_int { - $crate::callback::handle_panic(|py| { - let collector = PyClassImplCollector::<$cls>::new(); - $crate::callback::convert(py, { - if let Some(value) = ::std::ptr::NonNull::new(value) { - collector.__setitem__(_slf, attr, value) - } else { - collector.__delitem__(_slf, attr) - } + #[doc(hidden)] + #[macro_export] + macro_rules! $generate_macro { + ($cls:ty) => {{ + use ::std::option::Option::*; + use $crate::class::impl_::*; + let collector = PyClassImplCollector::<$cls>::new(); + if collector.$set_implemented() || collector.$del_implemented() { + unsafe extern "C" fn __wrap( + _slf: *mut $crate::ffi::PyObject, + attr: *mut $crate::ffi::PyObject, + value: *mut $crate::ffi::PyObject, + ) -> ::std::os::raw::c_int { + $crate::callback::handle_panic(|py| { + let collector = PyClassImplCollector::<$cls>::new(); + $crate::callback::convert(py, { + if let Some(value) = ::std::ptr::NonNull::new(value) { + collector.$set(_slf, attr, value) + } else { + collector.$del(_slf, attr) + } + }) + }) + } + Some($crate::ffi::PyType_Slot { + slot: $crate::ffi::$slot, + pfunc: __wrap as $crate::ffi::$func_ty as _, }) - }) - } - Some($crate::ffi::PyType_Slot { - slot: $crate::ffi::Py_mp_ass_subscript, - pfunc: __wrap as $crate::ffi::objobjargproc as _, - }) - } else { - None + } else { + None + } + }}; } - }}; + }; +} + +define_pyclass_setattr_slot! { + PyClass__setattr__SlotFragment, + PyClass__delattr__SlotFragment, + __setattr__implemented, + __delattr__implemented, + __setattr__, + __delattr__, + Err(PyAttributeError::new_err("can't set attribute")), + Err(PyAttributeError::new_err("can't delete attribute")), + generate_pyclass_setattr_slot, + Py_tp_setattro, + setattrofunc, +} + +define_pyclass_setattr_slot! { + PyClass__set__SlotFragment, + PyClass__delete__SlotFragment, + __set__implemented, + __delete__implemented, + __set__, + __delete__, + Err(PyNotImplementedError::new_err("can't set descriptor")), + Err(PyNotImplementedError::new_err("can't delete descriptor")), + generate_pyclass_setdescr_slot, + Py_tp_descr_set, + descrsetfunc, +} + +define_pyclass_setattr_slot! { + PyClass__setitem__SlotFragment, + PyClass__delitem__SlotFragment, + __setitem__implemented, + __delitem__implemented, + __setitem__, + __delitem__, + Err(PyNotImplementedError::new_err("can't set item")), + Err(PyNotImplementedError::new_err("can't delete item")), + generate_pyclass_setitem_slot, + Py_mp_ass_subscript, + objobjargproc, } pub trait PyClassAllocImpl {