Skip to content

Commit 5578141

Browse files
authored
clean up error messages for Send / Sync requirements on #[pyclass] (#5750)
* clean up error messages for Send / Sync requirements on `#[pyclass]` * fix doctest
1 parent 18aa0c2 commit 5578141

File tree

5 files changed

+53
-123
lines changed

5 files changed

+53
-123
lines changed

guide/src/class.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1528,7 +1528,7 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
15281528
const IS_SEQUENCE: bool = false;
15291529
type Layout = <Self::BaseNativeType as pyo3::impl_::pyclass::PyClassBaseType>::Layout<Self>;
15301530
type BaseType = PyAny;
1531-
type ThreadChecker = pyo3::impl_::pyclass::SendablePyClass<MyClass>;
1531+
type ThreadChecker = pyo3::impl_::pyclass::NoopThreadChecker;
15321532
type PyClassMutability = <<pyo3::PyAny as pyo3::impl_::pyclass::PyClassBaseType>::PyClassMutability as pyo3::impl_::pycell::PyClassMutability>::MutableChild;
15331533
type Dict = pyo3::impl_::pyclass::PyClassDummySlot;
15341534
type WeakRef = pyo3::impl_::pyclass::PyClassDummySlot;

pyo3-macros-backend/src/pyclass.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2612,7 +2612,7 @@ impl<'a> PyClassImplsBuilder<'a> {
26122612
let thread_checker = if self.attr.options.unsendable.is_some() {
26132613
quote! { #pyo3_path::impl_::pyclass::ThreadCheckerImpl }
26142614
} else {
2615-
quote! { #pyo3_path::impl_::pyclass::SendablePyClass<#cls> }
2615+
quote! { #pyo3_path::impl_::pyclass::NoopThreadChecker }
26162616
};
26172617

26182618
let (pymethods_items, inventory, inventory_class) = match self.methods_type {
@@ -2697,11 +2697,9 @@ impl<'a> PyClassImplsBuilder<'a> {
26972697
let assertions = if attr.options.unsendable.is_some() {
26982698
TokenStream::new()
26992699
} else {
2700-
let assert = quote_spanned! { cls.span() => #pyo3_path::impl_::pyclass::assert_pyclass_sync::<#cls>(); };
2700+
let assert = quote_spanned! { cls.span() => #pyo3_path::impl_::pyclass::assert_pyclass_send_sync::<#cls>() };
27012701
quote! {
2702-
const _: () = {
2703-
#assert
2704-
};
2702+
const _: () = #assert;
27052703
}
27062704
};
27072705

src/impl_/pyclass.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ mod sealed {
5151
impl Sealed for super::PyClassDictSlot {}
5252
impl Sealed for super::PyClassWeakRefSlot {}
5353
impl Sealed for super::ThreadCheckerImpl {}
54-
impl<T: Send> Sealed for super::SendablePyClass<T> {}
54+
impl Sealed for super::NoopThreadChecker {}
5555
}
5656

5757
/// Represents the `__dict__` field for `#[pyclass]`.
@@ -1028,14 +1028,10 @@ pub trait PyClassThreadChecker<T>: Sized + sealed::Sealed {
10281028
}
10291029

10301030
/// Default thread checker for `#[pyclass]`.
1031-
///
1032-
/// Keeping the T: Send bound here slightly improves the compile
1033-
/// error message to hint to users to figure out what's wrong
1034-
/// when `#[pyclass]` types do not implement `Send`.
10351031
#[doc(hidden)]
1036-
pub struct SendablePyClass<T: Send>(PhantomData<T>);
1032+
pub struct NoopThreadChecker;
10371033

1038-
impl<T: Send> PyClassThreadChecker<T> for SendablePyClass<T> {
1034+
impl<T> PyClassThreadChecker<T> for NoopThreadChecker {
10391035
fn ensure(&self) {}
10401036
fn check(&self) -> bool {
10411037
true
@@ -1045,7 +1041,7 @@ impl<T: Send> PyClassThreadChecker<T> for SendablePyClass<T> {
10451041
}
10461042
#[inline]
10471043
fn new() -> Self {
1048-
SendablePyClass(PhantomData)
1044+
NoopThreadChecker
10491045
}
10501046
}
10511047

src/impl_/pyclass/assertions.rs

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,19 @@
11
/// Helper function that can be used at compile time to emit a diagnostic if
2-
/// the type does not implement `Sync` when it should.
3-
///
4-
/// The mere act of invoking this function will cause the diagnostic to be
5-
/// emitted if `T` does not implement `Sync` when it should.
6-
///
7-
/// The additional `const IS_SYNC: bool` parameter is used to allow the custom
8-
/// diagnostic to be emitted; if `PyClassSync`
9-
#[allow(unused)]
10-
pub const fn assert_pyclass_sync<T>()
2+
/// the type does not implement `Send` or `Sync` when it should; the mere act
3+
/// of invoking this function will cause the diagnostic to be emitted if needed.
4+
pub const fn assert_pyclass_send_sync<T>()
115
where
12-
T: PyClassSync + Sync,
6+
T: Send + Sync,
137
{
148
}
159

16-
#[diagnostic::on_unimplemented(
17-
message = "the trait `Sync` is not implemented for `{Self}`",
18-
label = "required by `#[pyclass]`",
19-
note = "replace thread-unsafe fields with thread-safe alternatives",
20-
note = "see <TODO INSERT PYO3 GUIDE> for more information"
21-
)]
22-
pub trait PyClassSync<T: Sync = Self> {}
23-
24-
impl<T> PyClassSync for T where T: Sync {}
25-
2610
mod tests {
2711
#[cfg(feature = "macros")]
2812
#[test]
29-
fn test_assert_pyclass_sync() {
30-
use super::assert_pyclass_sync;
31-
13+
fn test_assert_pyclass_send_sync() {
3214
#[crate::pyclass(crate = "crate")]
3315
struct MyClass {}
3416

35-
assert_pyclass_sync::<MyClass>();
17+
super::assert_pyclass_send_sync::<MyClass>();
3618
}
3719
}

tests/ui/pyclass_send.stderr

Lines changed: 39 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,23 @@
11
error[E0277]: `*mut c_void` cannot be sent between threads safely
2-
--> tests/ui/pyclass_send.rs:4:1
3-
|
4-
4 | #[pyclass]
5-
| ^^^^^^^^^^ `*mut c_void` cannot be sent between threads safely
6-
|
7-
= help: within `NotSyncNotSend`, the trait `Send` is not implemented for `*mut c_void`
8-
help: the trait `pyo3::impl_::pyclass::PyClassThreadChecker<T>` is implemented for `SendablePyClass<T>`
9-
--> src/impl_/pyclass.rs
10-
|
11-
| impl<T: Send> PyClassThreadChecker<T> for SendablePyClass<T> {
12-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13-
note: required because it appears within the type `NotSyncNotSend`
142
--> tests/ui/pyclass_send.rs:5:8
153
|
164
5 | struct NotSyncNotSend(*mut c_void);
17-
| ^^^^^^^^^^^^^^
18-
= note: required for `SendablePyClass<NotSyncNotSend>` to implement `pyo3::impl_::pyclass::PyClassThreadChecker<NotSyncNotSend>`
19-
note: required by a bound in `PyClassImpl::ThreadChecker`
20-
--> src/impl_/pyclass.rs
21-
|
22-
| type ThreadChecker: PyClassThreadChecker<Self>;
23-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `PyClassImpl::ThreadChecker`
24-
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
25-
26-
error[E0277]: `*mut c_void` cannot be sent between threads safely
27-
--> tests/ui/pyclass_send.rs:11:1
28-
|
29-
11 | #[pyclass]
30-
| ^^^^^^^^^^ `*mut c_void` cannot be sent between threads safely
31-
|
32-
= help: within `SyncNotSend`, the trait `Send` is not implemented for `*mut c_void`
33-
help: the trait `pyo3::impl_::pyclass::PyClassThreadChecker<T>` is implemented for `SendablePyClass<T>`
34-
--> src/impl_/pyclass.rs
35-
|
36-
| impl<T: Send> PyClassThreadChecker<T> for SendablePyClass<T> {
37-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
38-
note: required because it appears within the type `SyncNotSend`
39-
--> tests/ui/pyclass_send.rs:12:8
40-
|
41-
12 | struct SyncNotSend(*mut c_void);
42-
| ^^^^^^^^^^^
43-
= note: required for `SendablePyClass<SyncNotSend>` to implement `pyo3::impl_::pyclass::PyClassThreadChecker<SyncNotSend>`
44-
note: required by a bound in `PyClassImpl::ThreadChecker`
45-
--> src/impl_/pyclass.rs
46-
|
47-
| type ThreadChecker: PyClassThreadChecker<Self>;
48-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `PyClassImpl::ThreadChecker`
49-
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
50-
51-
error[E0277]: `*mut c_void` cannot be sent between threads safely
52-
--> tests/ui/pyclass_send.rs:4:1
53-
|
54-
4 | #[pyclass]
55-
| ^^^^^^^^^^ `*mut c_void` cannot be sent between threads safely
5+
| ^^^^^^^^^^^^^^ `*mut c_void` cannot be sent between threads safely
566
|
577
= help: within `NotSyncNotSend`, the trait `Send` is not implemented for `*mut c_void`
588
note: required because it appears within the type `NotSyncNotSend`
599
--> tests/ui/pyclass_send.rs:5:8
6010
|
6111
5 | struct NotSyncNotSend(*mut c_void);
6212
| ^^^^^^^^^^^^^^
63-
note: required by a bound in `SendablePyClass`
64-
--> src/impl_/pyclass.rs
13+
note: required by a bound in `assert_pyclass_send_sync`
14+
--> src/impl_/pyclass/assertions.rs
6515
|
66-
| pub struct SendablePyClass<T: Send>(PhantomData<T>);
67-
| ^^^^ required by this bound in `SendablePyClass`
68-
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
69-
70-
error[E0277]: `*mut c_void` cannot be sent between threads safely
71-
--> tests/ui/pyclass_send.rs:11:1
72-
|
73-
11 | #[pyclass]
74-
| ^^^^^^^^^^ `*mut c_void` cannot be sent between threads safely
75-
|
76-
= help: within `SyncNotSend`, the trait `Send` is not implemented for `*mut c_void`
77-
note: required because it appears within the type `SyncNotSend`
78-
--> tests/ui/pyclass_send.rs:12:8
79-
|
80-
12 | struct SyncNotSend(*mut c_void);
81-
| ^^^^^^^^^^^
82-
note: required by a bound in `SendablePyClass`
83-
--> src/impl_/pyclass.rs
84-
|
85-
| pub struct SendablePyClass<T: Send>(PhantomData<T>);
86-
| ^^^^ required by this bound in `SendablePyClass`
87-
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
16+
| pub const fn assert_pyclass_send_sync<T>()
17+
| ------------------------ required by a bound in this function
18+
| where
19+
| T: Send + Sync,
20+
| ^^^^ required by this bound in `assert_pyclass_send_sync`
8821

8922
error[E0277]: `*mut c_void` cannot be shared between threads safely
9023
--> tests/ui/pyclass_send.rs:5:8
@@ -98,14 +31,14 @@ note: required because it appears within the type `NotSyncNotSend`
9831
|
9932
5 | struct NotSyncNotSend(*mut c_void);
10033
| ^^^^^^^^^^^^^^
101-
note: required by a bound in `assert_pyclass_sync`
34+
note: required by a bound in `assert_pyclass_send_sync`
10235
--> src/impl_/pyclass/assertions.rs
10336
|
104-
| pub const fn assert_pyclass_sync<T>()
105-
| ------------------- required by a bound in this function
37+
| pub const fn assert_pyclass_send_sync<T>()
38+
| ------------------------ required by a bound in this function
10639
| where
107-
| T: PyClassSync + Sync,
108-
| ^^^^ required by this bound in `assert_pyclass_sync`
40+
| T: Send + Sync,
41+
| ^^^^ required by this bound in `assert_pyclass_send_sync`
10942

11043
error[E0277]: `*mut c_void` cannot be shared between threads safely
11144
--> tests/ui/pyclass_send.rs:8:8
@@ -119,11 +52,32 @@ note: required because it appears within the type `SendNotSync`
11952
|
12053
8 | struct SendNotSync(*mut c_void);
12154
| ^^^^^^^^^^^
122-
note: required by a bound in `assert_pyclass_sync`
55+
note: required by a bound in `assert_pyclass_send_sync`
12356
--> src/impl_/pyclass/assertions.rs
12457
|
125-
| pub const fn assert_pyclass_sync<T>()
126-
| ------------------- required by a bound in this function
58+
| pub const fn assert_pyclass_send_sync<T>()
59+
| ------------------------ required by a bound in this function
12760
| where
128-
| T: PyClassSync + Sync,
129-
| ^^^^ required by this bound in `assert_pyclass_sync`
61+
| T: Send + Sync,
62+
| ^^^^ required by this bound in `assert_pyclass_send_sync`
63+
64+
error[E0277]: `*mut c_void` cannot be sent between threads safely
65+
--> tests/ui/pyclass_send.rs:12:8
66+
|
67+
12 | struct SyncNotSend(*mut c_void);
68+
| ^^^^^^^^^^^ `*mut c_void` cannot be sent between threads safely
69+
|
70+
= help: within `SyncNotSend`, the trait `Send` is not implemented for `*mut c_void`
71+
note: required because it appears within the type `SyncNotSend`
72+
--> tests/ui/pyclass_send.rs:12:8
73+
|
74+
12 | struct SyncNotSend(*mut c_void);
75+
| ^^^^^^^^^^^
76+
note: required by a bound in `assert_pyclass_send_sync`
77+
--> src/impl_/pyclass/assertions.rs
78+
|
79+
| pub const fn assert_pyclass_send_sync<T>()
80+
| ------------------------ required by a bound in this function
81+
| where
82+
| T: Send + Sync,
83+
| ^^^^ required by this bound in `assert_pyclass_send_sync`

0 commit comments

Comments
 (0)