diff --git a/guide/src/class.md b/guide/src/class.md index 42f765e9aea..d93e1a78b04 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -1138,7 +1138,7 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass { const IS_SUBCLASS: bool = false; type Layout = PyCell; type BaseType = PyAny; - type ThreadChecker = pyo3::impl_::pyclass::ThreadCheckerStub; + type ThreadChecker = pyo3::impl_::pyclass::SendablePyClass; type PyClassMutability = <::PyClassMutability as pyo3::impl_::pycell::PyClassMutability>::MutableChild; type Dict = pyo3::impl_::pyclass::PyClassDummySlot; type WeakRef = pyo3::impl_::pyclass::PyClassDummySlot; diff --git a/pyo3-macros-backend/src/pyclass.rs b/pyo3-macros-backend/src/pyclass.rs index c2cb66d76dd..e8f8d6e8f4b 100644 --- a/pyo3-macros-backend/src/pyclass.rs +++ b/pyo3-macros-backend/src/pyclass.rs @@ -947,13 +947,9 @@ impl<'a> PyClassImplsBuilder<'a> { }; let thread_checker = if self.attr.options.unsendable.is_some() { - quote! { _pyo3::impl_::pyclass::ThreadCheckerImpl<#cls> } - } else if self.attr.options.extends.is_some() { - quote! { - _pyo3::impl_::pyclass::ThreadCheckerInherited<#cls, <#cls as _pyo3::impl_::pyclass::PyClassImpl>::BaseType> - } + quote! { _pyo3::impl_::pyclass::ThreadCheckerImpl } } else { - quote! { _pyo3::impl_::pyclass::ThreadCheckerStub<#cls> } + quote! { _pyo3::impl_::pyclass::SendablePyClass<#cls> } }; let (pymethods_items, inventory, inventory_class) = match self.methods_type { diff --git a/src/impl_/pyclass.rs b/src/impl_/pyclass.rs index 892c9c677b5..6d74fbf1432 100644 --- a/src/impl_/pyclass.rs +++ b/src/impl_/pyclass.rs @@ -1018,53 +1018,46 @@ pub trait PyClassThreadChecker: Sized { private_decl! {} } -/// Stub checker for `Send` types. +/// Default thread checker for `#[pyclass]`. +/// +/// Keeping the T: Send bound here slightly improves the compile +/// error message to hint to users to figure out what's wrong +/// when `#[pyclass]` types do not implement `Send`. #[doc(hidden)] -pub struct ThreadCheckerStub(PhantomData); +pub struct SendablePyClass(PhantomData); -impl PyClassThreadChecker for ThreadCheckerStub { +impl PyClassThreadChecker for SendablePyClass { fn ensure(&self) {} fn can_drop(&self, _py: Python<'_>) -> bool { true } #[inline] fn new() -> Self { - ThreadCheckerStub(PhantomData) + SendablePyClass(PhantomData) } private_impl! {} } -impl PyClassThreadChecker for ThreadCheckerStub { - fn ensure(&self) {} - fn can_drop(&self, _py: Python<'_>) -> bool { - true - } - #[inline] - fn new() -> Self { - ThreadCheckerStub(PhantomData) - } - private_impl! {} -} - -/// Thread checker for unsendable types. +/// Thread checker for `#[pyclass(unsendable)]` types. /// Panics when the value is accessed by another thread. #[doc(hidden)] -pub struct ThreadCheckerImpl(thread::ThreadId, PhantomData); +pub struct ThreadCheckerImpl(thread::ThreadId); -impl PyClassThreadChecker for ThreadCheckerImpl { - fn ensure(&self) { +impl ThreadCheckerImpl { + fn ensure(&self, type_name: &'static str) { assert_eq!( thread::current().id(), self.0, - "{} is unsendable, but sent to another thread!", - std::any::type_name::() + "{} is unsendable, but sent to another thread", + type_name ); } - fn can_drop(&self, py: Python<'_>) -> bool { + + fn can_drop(&self, py: Python<'_>, type_name: &'static str) -> bool { if thread::current().id() != self.0 { PyRuntimeError::new_err(format!( - "{} is unsendbale, but is dropped on another thread!", - std::any::type_name::() + "{} is unsendable, but is being dropped on another thread", + type_name )) .write_unraisable(py, None); return false; @@ -1072,31 +1065,17 @@ impl PyClassThreadChecker for ThreadCheckerImpl { true } - fn new() -> Self { - ThreadCheckerImpl(thread::current().id(), PhantomData) - } - private_impl! {} } -/// Thread checker for types that have `Send` and `extends=...`. -/// Ensures that `T: Send` and the parent is not accessed by another thread. -#[doc(hidden)] -pub struct ThreadCheckerInherited( - PhantomData, - U::ThreadChecker, -); - -impl PyClassThreadChecker - for ThreadCheckerInherited -{ +impl PyClassThreadChecker for ThreadCheckerImpl { fn ensure(&self) { - self.1.ensure(); + self.ensure(std::any::type_name::()); } fn can_drop(&self, py: Python<'_>) -> bool { - self.1.can_drop(py) + self.can_drop(py, std::any::type_name::()) } fn new() -> Self { - ThreadCheckerInherited(PhantomData, U::ThreadChecker::new()) + ThreadCheckerImpl(thread::current().id()) } private_impl! {} } @@ -1105,7 +1084,6 @@ impl PyClassThreadChecker pub trait PyClassBaseType: Sized { type LayoutAsBase: PyCellLayout; type BaseNativeType; - type ThreadChecker: PyClassThreadChecker; type Initializer: PyObjectInit; type PyClassMutability: PyClassMutability; } @@ -1116,7 +1094,6 @@ pub trait PyClassBaseType: Sized { impl PyClassBaseType for T { type LayoutAsBase = crate::pycell::PyCell; type BaseNativeType = T::BaseNativeType; - type ThreadChecker = T::ThreadChecker; type Initializer = crate::pyclass_init::PyClassInitializer; type PyClassMutability = T::PyClassMutability; } diff --git a/src/types/mod.rs b/src/types/mod.rs index dc6e6d69a57..fd703c84096 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -250,7 +250,6 @@ macro_rules! pyobject_native_type_sized { impl<$($generics,)*> $crate::impl_::pyclass::PyClassBaseType for $name { type LayoutAsBase = $crate::pycell::PyCellBase<$layout>; type BaseNativeType = $name; - type ThreadChecker = $crate::impl_::pyclass::ThreadCheckerStub<$crate::PyObject>; type Initializer = $crate::pyclass_init::PyNativeTypeInitializer; type PyClassMutability = $crate::pycell::impl_::ImmutableClass; } diff --git a/tests/test_class_basics.rs b/tests/test_class_basics.rs index a0ec4b0c1b2..a83bd50c397 100644 --- a/tests/test_class_basics.rs +++ b/tests/test_class_basics.rs @@ -268,7 +268,7 @@ fn test_unsendable() -> PyResult<()> { #[test] #[cfg_attr(target_arch = "wasm32", ignore)] #[should_panic( - expected = "test_class_basics::UnsendableBase is unsendable, but sent to another thread!" + expected = "test_class_basics::UnsendableBase is unsendable, but sent to another thread" )] fn panic_unsendable_base() { test_unsendable::().unwrap(); @@ -277,7 +277,7 @@ fn panic_unsendable_base() { #[test] #[cfg_attr(target_arch = "wasm32", ignore)] #[should_panic( - expected = "test_class_basics::UnsendableBase is unsendable, but sent to another thread!" + expected = "test_class_basics::UnsendableBase is unsendable, but sent to another thread" )] fn panic_unsendable_child() { test_unsendable::().unwrap(); @@ -584,7 +584,7 @@ fn drop_unsendable_elsewhere() { assert!(!dropped.load(Ordering::SeqCst)); let (err, object) = capture.borrow_mut(py).capture.take().unwrap(); - assert_eq!(err.to_string(), "RuntimeError: test_class_basics::drop_unsendable_elsewhere::Unsendable is unsendbale, but is dropped on another thread!"); + assert_eq!(err.to_string(), "RuntimeError: test_class_basics::drop_unsendable_elsewhere::Unsendable is unsendable, but is being dropped on another thread"); assert!(object.is_none(py)); capture.borrow_mut(py).uninstall(py); diff --git a/tests/ui/abi3_nativetype_inheritance.stderr b/tests/ui/abi3_nativetype_inheritance.stderr index 4b4954e875a..eec14202fd5 100644 --- a/tests/ui/abi3_nativetype_inheritance.stderr +++ b/tests/ui/abi3_nativetype_inheritance.stderr @@ -1,18 +1,3 @@ -error[E0277]: the trait bound `PyDict: PyClass` is not satisfied - --> tests/ui/abi3_nativetype_inheritance.rs:5:1 - | -5 | #[pyclass(extends=PyDict)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `PyClass` is not implemented for `PyDict` - | - = help: the trait `PyClass` is implemented for `TestClass` - = note: required for `PyDict` to implement `PyClassBaseType` -note: required by a bound in `ThreadCheckerInherited` - --> src/impl_/pyclass.rs - | - | pub struct ThreadCheckerInherited( - | ^^^^^^^^^^^^^^^ required by this bound in `ThreadCheckerInherited` - = note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info) - error[E0277]: the trait bound `PyDict: PyClass` is not satisfied --> tests/ui/abi3_nativetype_inheritance.rs:5:1 | diff --git a/tests/ui/pyclass_send.stderr b/tests/ui/pyclass_send.stderr index ace1775dfab..f279acc5f9f 100644 --- a/tests/ui/pyclass_send.stderr +++ b/tests/ui/pyclass_send.stderr @@ -10,9 +10,9 @@ note: required because it appears within the type `NotThreadSafe` | 5 | struct NotThreadSafe { | ^^^^^^^^^^^^^ -note: required by a bound in `ThreadCheckerStub` +note: required by a bound in `SendablePyClass` --> src/impl_/pyclass.rs | - | pub struct ThreadCheckerStub(PhantomData); - | ^^^^ required by this bound in `ThreadCheckerStub` + | pub struct SendablePyClass(PhantomData); + | ^^^^ required by this bound in `SendablePyClass` = note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)