Skip to content

Commit 1c663ba

Browse files
committed
Allow protocol objects to be Send + Sync
1 parent 18df32f commit 1c663ba

File tree

5 files changed

+203
-27
lines changed

5 files changed

+203
-27
lines changed

crates/objc2/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
2222
* Added `Allocated::set_ivars`, which sets the instance variables of an
2323
object, and returns the new `rc::PartialInit`.
2424
* Added the ability for `msg_send_id!` to call `super` methods.
25+
* Implement `Send` and `Sync` for `ProtocolObject` if the underlying protocol
26+
implements it.
27+
* Added ability to create `Send` and `Sync` versions of
28+
`ProtocolObject<dyn NSObjectProtocol>`.
2529

2630
### Changed
2731
* **BREAKING**: Changed how instance variables work in `declare_class!`.

crates/objc2/src/macros/extern_protocol.rs

+3
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,9 @@ macro_rules! __inner_extern_protocol {
204204
{
205205
const __INNER: () = ();
206206
}
207+
208+
// TODO: Should we also implement `ImplementedBy` for `Send + Sync`
209+
// types, as is done for `NSObjectProtocol`?
207210
};
208211
}
209212

crates/objc2/src/runtime/nsobject.rs

+29
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use crate::runtime::{AnyClass, AnyObject, ProtocolObject};
77
use crate::{extern_methods, msg_send, msg_send_id, Message};
88
use crate::{ClassType, ProtocolType};
99

10+
use super::ImplementedBy;
11+
1012
/// The root class of most Objective-C class hierarchies.
1113
///
1214
/// This represents the [`NSObject` class][cls]. The name "NSObject" also
@@ -163,6 +165,33 @@ crate::__inner_extern_protocol!(
163165
("NSObject")
164166
);
165167

168+
// SAFETY: Anything that implements `NSObjectProtocol` and is `Send` is valid
169+
// to convert to `ProtocolObject<dyn NSObjectProtocol + Send>`.
170+
unsafe impl<T> ImplementedBy<T> for dyn NSObjectProtocol + Send
171+
where
172+
T: ?Sized + Message + NSObjectProtocol + Send,
173+
{
174+
const __INNER: () = ();
175+
}
176+
177+
// SAFETY: Anything that implements `NSObjectProtocol` and is `Sync` is valid
178+
// to convert to `ProtocolObject<dyn NSObjectProtocol + Sync>`.
179+
unsafe impl<T> ImplementedBy<T> for dyn NSObjectProtocol + Sync
180+
where
181+
T: ?Sized + Message + NSObjectProtocol + Sync,
182+
{
183+
const __INNER: () = ();
184+
}
185+
186+
// SAFETY: Anything that implements `NSObjectProtocol` and is `Send + Sync` is
187+
// valid to convert to `ProtocolObject<dyn NSObjectProtocol + Send + Sync>`.
188+
unsafe impl<T> ImplementedBy<T> for dyn NSObjectProtocol + Send + Sync
189+
where
190+
T: ?Sized + Message + NSObjectProtocol + Send + Sync,
191+
{
192+
const __INNER: () = ();
193+
}
194+
166195
unsafe impl NSObjectProtocol for NSObject {}
167196

168197
extern_methods!(

crates/objc2/src/runtime/protocol_object.rs

+23
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,16 @@ pub struct ProtocolObject<P: ?Sized> {
6262
p: PhantomData<P>,
6363
}
6464

65+
// SAFETY: `Send` if the underlying trait promises `Send`.
66+
//
67+
// E.g. `ProtocolObject<dyn NSObjectProtocol + Send>` is naturally `Send`.
68+
unsafe impl<P: ?Sized + Send> Send for ProtocolObject<P> {}
69+
70+
// SAFETY: `Sync` if the underlying trait promises `Sync`.
71+
//
72+
// E.g. `ProtocolObject<dyn NSObjectProtocol + Sync>` is naturally `Sync`.
73+
unsafe impl<P: ?Sized + Sync> Sync for ProtocolObject<P> {}
74+
6575
// SAFETY: The type is `#[repr(C)]` and `AnyObject` internally
6676
unsafe impl<P: ?Sized> RefEncode for ProtocolObject<P> {
6777
const ENCODING_REF: Encoding = Encoding::Object;
@@ -264,6 +274,9 @@ mod tests {
264274
unsafe impl FooBar for DummyClass {}
265275
// unsafe impl FooFooBar for DummyClass {}
266276

277+
unsafe impl Send for DummyClass {}
278+
unsafe impl Sync for DummyClass {}
279+
267280
extern_methods!(
268281
unsafe impl DummyClass {
269282
#[method_id(new)]
@@ -275,6 +288,12 @@ mod tests {
275288
fn impl_traits() {
276289
assert_impl_all!(NSObject: NSObjectProtocol);
277290
assert_impl_all!(ProtocolObject<dyn NSObjectProtocol>: NSObjectProtocol);
291+
assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol>: Send, Sync);
292+
assert_impl_all!(ProtocolObject<dyn NSObjectProtocol + Send>: NSObjectProtocol, Send);
293+
assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol + Send>: Sync);
294+
assert_impl_all!(ProtocolObject<dyn NSObjectProtocol + Sync>: NSObjectProtocol, Sync);
295+
assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol + Sync>: Send);
296+
assert_impl_all!(ProtocolObject<dyn NSObjectProtocol + Send + Sync>: NSObjectProtocol, Send, Sync);
278297
assert_not_impl_any!(ProtocolObject<dyn Foo>: NSObjectProtocol);
279298
assert_impl_all!(ProtocolObject<dyn Bar>: NSObjectProtocol);
280299
assert_impl_all!(ProtocolObject<dyn FooBar>: NSObjectProtocol);
@@ -332,6 +351,10 @@ mod tests {
332351
let _nsobject: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(bar);
333352
let nsobject: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(&*obj);
334353
let _nsobject: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(nsobject);
354+
let _: &ProtocolObject<dyn NSObjectProtocol + Send> = ProtocolObject::from_ref(&*obj);
355+
let _: &ProtocolObject<dyn NSObjectProtocol + Sync> = ProtocolObject::from_ref(&*obj);
356+
let _: &ProtocolObject<dyn NSObjectProtocol + Send + Sync> =
357+
ProtocolObject::from_ref(&*obj);
335358

336359
let _foobar: &mut ProtocolObject<dyn FooBar> = ProtocolObject::from_mut(&mut *obj);
337360
let _foobar: Id<ProtocolObject<dyn FooBar>> = ProtocolObject::from_id(obj);

crates/test-ui/ui/protocol_object_only_protocols.stderr

+144-27
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ error[E0277]: the trait bound `NSObject: ImplementedBy<NSObject>` is not satisfi
88
|
99
= help: the following other types implement trait `ImplementedBy<T>`:
1010
(dyn NSObjectProtocol + 'static)
11+
(dyn NSObjectProtocol + Send + 'static)
12+
(dyn NSObjectProtocol + Sync + 'static)
13+
(dyn NSObjectProtocol + Send + Sync + 'static)
1114
(dyn NSAccessibilityColor + 'static)
1215
(dyn NSAccessibilityCustomRotorItemSearchDelegate + 'static)
1316
(dyn NSAccessibilityElementProtocol + 'static)
1417
(dyn NSAccessibilityGroup + 'static)
15-
(dyn NSAccessibilityButton + 'static)
16-
(dyn NSAccessibilitySwitch + 'static)
17-
(dyn NSAccessibilityRadioButton + 'static)
1818
and $N others
1919
note: required by a bound in `ProtocolObject::<P>::from_ref`
2020
--> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs
@@ -35,13 +35,13 @@ error[E0277]: the trait bound `dyn Send: ImplementedBy<NSObject>` is not satisfi
3535
|
3636
= help: the following other types implement trait `ImplementedBy<T>`:
3737
(dyn NSObjectProtocol + 'static)
38+
(dyn NSObjectProtocol + Send + 'static)
39+
(dyn NSObjectProtocol + Sync + 'static)
40+
(dyn NSObjectProtocol + Send + Sync + 'static)
3841
(dyn NSAccessibilityColor + 'static)
3942
(dyn NSAccessibilityCustomRotorItemSearchDelegate + 'static)
4043
(dyn NSAccessibilityElementProtocol + 'static)
4144
(dyn NSAccessibilityGroup + 'static)
42-
(dyn NSAccessibilityButton + 'static)
43-
(dyn NSAccessibilitySwitch + 'static)
44-
(dyn NSAccessibilityRadioButton + 'static)
4545
and $N others
4646
note: required by a bound in `ProtocolObject::<P>::from_ref`
4747
--> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs
@@ -62,13 +62,13 @@ error[E0277]: the trait bound `dyn Foo: ImplementedBy<NSObject>` is not satisfie
6262
|
6363
= help: the following other types implement trait `ImplementedBy<T>`:
6464
(dyn NSObjectProtocol + 'static)
65+
(dyn NSObjectProtocol + Send + 'static)
66+
(dyn NSObjectProtocol + Sync + 'static)
67+
(dyn NSObjectProtocol + Send + Sync + 'static)
6568
(dyn NSAccessibilityColor + 'static)
6669
(dyn NSAccessibilityCustomRotorItemSearchDelegate + 'static)
6770
(dyn NSAccessibilityElementProtocol + 'static)
6871
(dyn NSAccessibilityGroup + 'static)
69-
(dyn NSAccessibilityButton + 'static)
70-
(dyn NSAccessibilitySwitch + 'static)
71-
(dyn NSAccessibilityRadioButton + 'static)
7272
and $N others
7373
note: required by a bound in `ProtocolObject::<P>::from_ref`
7474
--> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs
@@ -79,24 +79,52 @@ note: required by a bound in `ProtocolObject::<P>::from_ref`
7979
| P: ImplementedBy<T>,
8080
| ^^^^^^^^^^^^^^^^ required by this bound in `ProtocolObject::<P>::from_ref`
8181

82-
error[E0277]: the trait bound `dyn NSObjectProtocol + Send: ImplementedBy<NSObject>` is not satisfied
82+
error[E0277]: `*const UnsafeCell<()>` cannot be sent between threads safely
8383
--> ui/protocol_object_only_protocols.rs
8484
|
8585
| let _: &ProtocolObject<dyn NSObjectProtocol + Send> = ProtocolObject::from_ref(&*obj);
86-
| ------------------------ ^^^^^ the trait `ImplementedBy<NSObject>` is not implemented for `dyn NSObjectProtocol + Send`
86+
| ------------------------ ^^^^^ `*const UnsafeCell<()>` cannot be sent between threads safely
8787
| |
8888
| required by a bound introduced by this call
8989
|
90+
= help: within `NSObject`, the trait `Send` is not implemented for `*const UnsafeCell<()>`
9091
= help: the following other types implement trait `ImplementedBy<T>`:
9192
(dyn NSObjectProtocol + 'static)
93+
(dyn NSObjectProtocol + Send + 'static)
94+
(dyn NSObjectProtocol + Sync + 'static)
95+
(dyn NSObjectProtocol + Send + Sync + 'static)
9296
(dyn NSAccessibilityColor + 'static)
9397
(dyn NSAccessibilityCustomRotorItemSearchDelegate + 'static)
9498
(dyn NSAccessibilityElementProtocol + 'static)
9599
(dyn NSAccessibilityGroup + 'static)
96-
(dyn NSAccessibilityButton + 'static)
97-
(dyn NSAccessibilitySwitch + 'static)
98-
(dyn NSAccessibilityRadioButton + 'static)
99100
and $N others
101+
= note: required because it appears within the type `(*const UnsafeCell<()>, PhantomPinned)`
102+
note: required because it appears within the type `PhantomData<(*const UnsafeCell<()>, PhantomPinned)>`
103+
--> $RUST/core/src/marker.rs
104+
|
105+
| pub struct PhantomData<T: ?Sized>;
106+
| ^^^^^^^^^^^
107+
note: required because it appears within the type `UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>`
108+
--> $RUST/core/src/cell.rs
109+
|
110+
| pub struct UnsafeCell<T: ?Sized> {
111+
| ^^^^^^^^^^
112+
note: required because it appears within the type `objc_object`
113+
--> $WORKSPACE/crates/objc-sys/src/object.rs
114+
|
115+
| pub struct objc_object {
116+
| ^^^^^^^^^^^
117+
note: required because it appears within the type `AnyObject`
118+
--> $WORKSPACE/crates/objc2/src/runtime/mod.rs
119+
|
120+
| pub struct AnyObject(ffi::objc_object);
121+
| ^^^^^^^^^
122+
note: required because it appears within the type `NSObject`
123+
--> $WORKSPACE/crates/objc2/src/runtime/nsobject.rs
124+
|
125+
| pub struct NSObject {
126+
| ^^^^^^^^
127+
= note: required for `dyn NSObjectProtocol + Send` to implement `ImplementedBy<NSObject>`
100128
note: required by a bound in `ProtocolObject::<P>::from_ref`
101129
--> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs
102130
|
@@ -106,24 +134,85 @@ note: required by a bound in `ProtocolObject::<P>::from_ref`
106134
| P: ImplementedBy<T>,
107135
| ^^^^^^^^^^^^^^^^ required by this bound in `ProtocolObject::<P>::from_ref`
108136

109-
error[E0277]: the trait bound `dyn NSObjectProtocol + Sync: ImplementedBy<NSObject>` is not satisfied
137+
error[E0277]: `UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>` cannot be shared between threads safely
110138
--> ui/protocol_object_only_protocols.rs
111139
|
112140
| let _: &ProtocolObject<dyn NSObjectProtocol + Sync> = ProtocolObject::from_ref(&*obj);
113-
| ------------------------ ^^^^^ the trait `ImplementedBy<NSObject>` is not implemented for `dyn NSObjectProtocol + Sync`
141+
| ------------------------ ^^^^^ `UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>` cannot be shared between threads safely
114142
| |
115143
| required by a bound introduced by this call
116144
|
145+
= help: within `NSObject`, the trait `Sync` is not implemented for `UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>`
146+
= help: the following other types implement trait `ImplementedBy<T>`:
147+
(dyn NSObjectProtocol + 'static)
148+
(dyn NSObjectProtocol + Send + 'static)
149+
(dyn NSObjectProtocol + Sync + 'static)
150+
(dyn NSObjectProtocol + Send + Sync + 'static)
151+
(dyn NSAccessibilityColor + 'static)
152+
(dyn NSAccessibilityCustomRotorItemSearchDelegate + 'static)
153+
(dyn NSAccessibilityElementProtocol + 'static)
154+
(dyn NSAccessibilityGroup + 'static)
155+
and $N others
156+
note: required because it appears within the type `objc_object`
157+
--> $WORKSPACE/crates/objc-sys/src/object.rs
158+
|
159+
| pub struct objc_object {
160+
| ^^^^^^^^^^^
161+
note: required because it appears within the type `AnyObject`
162+
--> $WORKSPACE/crates/objc2/src/runtime/mod.rs
163+
|
164+
| pub struct AnyObject(ffi::objc_object);
165+
| ^^^^^^^^^
166+
note: required because it appears within the type `NSObject`
167+
--> $WORKSPACE/crates/objc2/src/runtime/nsobject.rs
168+
|
169+
| pub struct NSObject {
170+
| ^^^^^^^^
171+
= note: required for `dyn NSObjectProtocol + Sync` to implement `ImplementedBy<NSObject>`
172+
note: required by a bound in `ProtocolObject::<P>::from_ref`
173+
--> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs
174+
|
175+
| pub fn from_ref<T: ?Sized + Message>(obj: &T) -> &Self
176+
| -------- required by a bound in this associated function
177+
| where
178+
| P: ImplementedBy<T>,
179+
| ^^^^^^^^^^^^^^^^ required by this bound in `ProtocolObject::<P>::from_ref`
180+
181+
error[E0277]: `UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>` cannot be shared between threads safely
182+
--> ui/protocol_object_only_protocols.rs
183+
|
184+
| let _: &ProtocolObject<dyn NSObjectProtocol + Send + Sync> = ProtocolObject::from_ref(&*obj);
185+
| ------------------------ ^^^^^ `UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>` cannot be shared between threads safely
186+
| |
187+
| required by a bound introduced by this call
188+
|
189+
= help: within `NSObject`, the trait `Sync` is not implemented for `UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>`
117190
= help: the following other types implement trait `ImplementedBy<T>`:
118191
(dyn NSObjectProtocol + 'static)
192+
(dyn NSObjectProtocol + Send + 'static)
193+
(dyn NSObjectProtocol + Sync + 'static)
194+
(dyn NSObjectProtocol + Send + Sync + 'static)
119195
(dyn NSAccessibilityColor + 'static)
120196
(dyn NSAccessibilityCustomRotorItemSearchDelegate + 'static)
121197
(dyn NSAccessibilityElementProtocol + 'static)
122198
(dyn NSAccessibilityGroup + 'static)
123-
(dyn NSAccessibilityButton + 'static)
124-
(dyn NSAccessibilitySwitch + 'static)
125-
(dyn NSAccessibilityRadioButton + 'static)
126199
and $N others
200+
note: required because it appears within the type `objc_object`
201+
--> $WORKSPACE/crates/objc-sys/src/object.rs
202+
|
203+
| pub struct objc_object {
204+
| ^^^^^^^^^^^
205+
note: required because it appears within the type `AnyObject`
206+
--> $WORKSPACE/crates/objc2/src/runtime/mod.rs
207+
|
208+
| pub struct AnyObject(ffi::objc_object);
209+
| ^^^^^^^^^
210+
note: required because it appears within the type `NSObject`
211+
--> $WORKSPACE/crates/objc2/src/runtime/nsobject.rs
212+
|
213+
| pub struct NSObject {
214+
| ^^^^^^^^
215+
= note: required for `dyn NSObjectProtocol + Send + Sync` to implement `ImplementedBy<NSObject>`
127216
note: required by a bound in `ProtocolObject::<P>::from_ref`
128217
--> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs
129218
|
@@ -133,24 +222,52 @@ note: required by a bound in `ProtocolObject::<P>::from_ref`
133222
| P: ImplementedBy<T>,
134223
| ^^^^^^^^^^^^^^^^ required by this bound in `ProtocolObject::<P>::from_ref`
135224

136-
error[E0277]: the trait bound `dyn NSObjectProtocol + Send + Sync: ImplementedBy<NSObject>` is not satisfied
225+
error[E0277]: `*const UnsafeCell<()>` cannot be sent between threads safely
137226
--> ui/protocol_object_only_protocols.rs
138227
|
139228
| let _: &ProtocolObject<dyn NSObjectProtocol + Send + Sync> = ProtocolObject::from_ref(&*obj);
140-
| ------------------------ ^^^^^ the trait `ImplementedBy<NSObject>` is not implemented for `dyn NSObjectProtocol + Send + Sync`
229+
| ------------------------ ^^^^^ `*const UnsafeCell<()>` cannot be sent between threads safely
141230
| |
142231
| required by a bound introduced by this call
143232
|
233+
= help: within `NSObject`, the trait `Send` is not implemented for `*const UnsafeCell<()>`
144234
= help: the following other types implement trait `ImplementedBy<T>`:
145235
(dyn NSObjectProtocol + 'static)
236+
(dyn NSObjectProtocol + Send + 'static)
237+
(dyn NSObjectProtocol + Sync + 'static)
238+
(dyn NSObjectProtocol + Send + Sync + 'static)
146239
(dyn NSAccessibilityColor + 'static)
147240
(dyn NSAccessibilityCustomRotorItemSearchDelegate + 'static)
148241
(dyn NSAccessibilityElementProtocol + 'static)
149242
(dyn NSAccessibilityGroup + 'static)
150-
(dyn NSAccessibilityButton + 'static)
151-
(dyn NSAccessibilitySwitch + 'static)
152-
(dyn NSAccessibilityRadioButton + 'static)
153243
and $N others
244+
= note: required because it appears within the type `(*const UnsafeCell<()>, PhantomPinned)`
245+
note: required because it appears within the type `PhantomData<(*const UnsafeCell<()>, PhantomPinned)>`
246+
--> $RUST/core/src/marker.rs
247+
|
248+
| pub struct PhantomData<T: ?Sized>;
249+
| ^^^^^^^^^^^
250+
note: required because it appears within the type `UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>`
251+
--> $RUST/core/src/cell.rs
252+
|
253+
| pub struct UnsafeCell<T: ?Sized> {
254+
| ^^^^^^^^^^
255+
note: required because it appears within the type `objc_object`
256+
--> $WORKSPACE/crates/objc-sys/src/object.rs
257+
|
258+
| pub struct objc_object {
259+
| ^^^^^^^^^^^
260+
note: required because it appears within the type `AnyObject`
261+
--> $WORKSPACE/crates/objc2/src/runtime/mod.rs
262+
|
263+
| pub struct AnyObject(ffi::objc_object);
264+
| ^^^^^^^^^
265+
note: required because it appears within the type `NSObject`
266+
--> $WORKSPACE/crates/objc2/src/runtime/nsobject.rs
267+
|
268+
| pub struct NSObject {
269+
| ^^^^^^^^
270+
= note: required for `dyn NSObjectProtocol + Send + Sync` to implement `ImplementedBy<NSObject>`
154271
note: required by a bound in `ProtocolObject::<P>::from_ref`
155272
--> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs
156273
|
@@ -198,13 +315,13 @@ error[E0277]: the trait bound `dyn NSCopying + Send: ImplementedBy<NSObject>` is
198315
|
199316
= help: the following other types implement trait `ImplementedBy<T>`:
200317
(dyn NSObjectProtocol + 'static)
318+
(dyn NSObjectProtocol + Send + 'static)
319+
(dyn NSObjectProtocol + Sync + 'static)
320+
(dyn NSObjectProtocol + Send + Sync + 'static)
201321
(dyn NSAccessibilityColor + 'static)
202322
(dyn NSAccessibilityCustomRotorItemSearchDelegate + 'static)
203323
(dyn NSAccessibilityElementProtocol + 'static)
204324
(dyn NSAccessibilityGroup + 'static)
205-
(dyn NSAccessibilityButton + 'static)
206-
(dyn NSAccessibilitySwitch + 'static)
207-
(dyn NSAccessibilityRadioButton + 'static)
208325
and $N others
209326
note: required by a bound in `ProtocolObject::<P>::from_ref`
210327
--> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs

0 commit comments

Comments
 (0)