diff --git a/newsfragments/5889.changed.md b/newsfragments/5889.changed.md new file mode 100644 index 00000000000..5fa485f28d0 --- /dev/null +++ b/newsfragments/5889.changed.md @@ -0,0 +1 @@ +`PyCapsule::new`: allow zero-sized types \ No newline at end of file diff --git a/src/types/capsule.rs b/src/types/capsule.rs index 9d4ad5197ce..69db18bdfad 100644 --- a/src/types/capsule.rs +++ b/src/types/capsule.rs @@ -75,17 +75,7 @@ impl PyCapsule { /// assert_eq!(unsafe { *val.as_ref() }, 123); /// }); /// ``` - /// - /// However, attempting to construct a `PyCapsule` with a zero-sized type will not compile: - /// - /// ```compile_fail - /// use pyo3::{prelude::*, types::PyCapsule}; - /// - /// Python::attach(|py| { - /// let capsule = PyCapsule::new(py, (), None).unwrap(); // Oops! `()` is zero sized! - /// }); - /// ``` - pub fn new( + pub fn new( py: Python<'_>, value: T, name: Option, @@ -100,17 +90,12 @@ impl PyCapsule { /// /// The `destructor` must be `Send`, because there is no guarantee which thread it will eventually /// be called from. - pub fn new_with_destructor< - T: 'static + Send + AssertNotZeroSized, - F: FnOnce(T, *mut c_void) + Send, - >( + pub fn new_with_destructor( py: Python<'_>, value: T, name: Option, destructor: F, ) -> PyResult> { - AssertNotZeroSized::assert_not_zero_sized(&value); - // Sanity check for capsule layout debug_assert_eq!(offset_of!(CapsuleContents::, value), 0); @@ -564,21 +549,6 @@ unsafe extern "C" fn capsule_destructor` -#[doc(hidden)] -pub trait AssertNotZeroSized: Sized { - const _CONDITION: usize = (std::mem::size_of::() == 0) as usize; - const _CHECK: &'static str = - ["PyCapsule value type T must not be zero-sized!"][Self::_CONDITION]; - #[allow(path_statements, clippy::no_effect)] - fn assert_not_zero_sized(&self) { - ::_CHECK; - } -} - -impl AssertNotZeroSized for T {} - fn ensure_no_error(py: Python<'_>) -> PyResult<()> { if let Some(err) = PyErr::take(py) { Err(err) @@ -958,7 +928,7 @@ mod tests { Python::attach(|py| { // Create a capsule and register it let cap = PyCapsule::new(py, 123u32, Some(c"builtins.test_cap".to_owned())).unwrap(); - let module = crate::prelude::PyModule::import(py, "builtins").unwrap(); + let module = PyModule::import(py, "builtins").unwrap(); module.add("test_cap", cap).unwrap(); // Try to import with wrong attribute name @@ -968,4 +938,14 @@ mod tests { assert!(result.is_err()); }); } + + #[test] + fn test_capsule_with_zero_sized_type() { + Python::attach(|py| { + let cap = PyCapsule::new(py, (), None).unwrap(); + let content = cap.pointer_checked(None).unwrap(); + // SAFETY: Capsule invariant: the returned pointer is the given pointer + assert_eq!(*unsafe { content.cast::<()>().as_ref() }, ()); + }) + } }