Skip to content

Commit

Permalink
Allow protocol objects to be Send + Sync
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Dec 3, 2023
1 parent 18df32f commit 1c663ba
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 27 deletions.
4 changes: 4 additions & 0 deletions crates/objc2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* Added `Allocated::set_ivars`, which sets the instance variables of an
object, and returns the new `rc::PartialInit`.
* Added the ability for `msg_send_id!` to call `super` methods.
* Implement `Send` and `Sync` for `ProtocolObject` if the underlying protocol
implements it.
* Added ability to create `Send` and `Sync` versions of
`ProtocolObject<dyn NSObjectProtocol>`.

### Changed
* **BREAKING**: Changed how instance variables work in `declare_class!`.
Expand Down
3 changes: 3 additions & 0 deletions crates/objc2/src/macros/extern_protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ macro_rules! __inner_extern_protocol {
{
const __INNER: () = ();
}

// TODO: Should we also implement `ImplementedBy` for `Send + Sync`
// types, as is done for `NSObjectProtocol`?
};
}

Expand Down
29 changes: 29 additions & 0 deletions crates/objc2/src/runtime/nsobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use crate::runtime::{AnyClass, AnyObject, ProtocolObject};
use crate::{extern_methods, msg_send, msg_send_id, Message};
use crate::{ClassType, ProtocolType};

use super::ImplementedBy;

/// The root class of most Objective-C class hierarchies.
///
/// This represents the [`NSObject` class][cls]. The name "NSObject" also
Expand Down Expand Up @@ -163,6 +165,33 @@ crate::__inner_extern_protocol!(
("NSObject")
);

// SAFETY: Anything that implements `NSObjectProtocol` and is `Send` is valid
// to convert to `ProtocolObject<dyn NSObjectProtocol + Send>`.
unsafe impl<T> ImplementedBy<T> for dyn NSObjectProtocol + Send
where
T: ?Sized + Message + NSObjectProtocol + Send,
{
const __INNER: () = ();
}

// SAFETY: Anything that implements `NSObjectProtocol` and is `Sync` is valid
// to convert to `ProtocolObject<dyn NSObjectProtocol + Sync>`.
unsafe impl<T> ImplementedBy<T> for dyn NSObjectProtocol + Sync
where
T: ?Sized + Message + NSObjectProtocol + Sync,
{
const __INNER: () = ();
}

// SAFETY: Anything that implements `NSObjectProtocol` and is `Send + Sync` is
// valid to convert to `ProtocolObject<dyn NSObjectProtocol + Send + Sync>`.
unsafe impl<T> ImplementedBy<T> for dyn NSObjectProtocol + Send + Sync
where
T: ?Sized + Message + NSObjectProtocol + Send + Sync,
{
const __INNER: () = ();
}

unsafe impl NSObjectProtocol for NSObject {}

extern_methods!(
Expand Down
23 changes: 23 additions & 0 deletions crates/objc2/src/runtime/protocol_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ pub struct ProtocolObject<P: ?Sized> {
p: PhantomData<P>,
}

// SAFETY: `Send` if the underlying trait promises `Send`.
//
// E.g. `ProtocolObject<dyn NSObjectProtocol + Send>` is naturally `Send`.
unsafe impl<P: ?Sized + Send> Send for ProtocolObject<P> {}

// SAFETY: `Sync` if the underlying trait promises `Sync`.
//
// E.g. `ProtocolObject<dyn NSObjectProtocol + Sync>` is naturally `Sync`.
unsafe impl<P: ?Sized + Sync> Sync for ProtocolObject<P> {}

// SAFETY: The type is `#[repr(C)]` and `AnyObject` internally
unsafe impl<P: ?Sized> RefEncode for ProtocolObject<P> {
const ENCODING_REF: Encoding = Encoding::Object;
Expand Down Expand Up @@ -264,6 +274,9 @@ mod tests {
unsafe impl FooBar for DummyClass {}
// unsafe impl FooFooBar for DummyClass {}

unsafe impl Send for DummyClass {}
unsafe impl Sync for DummyClass {}

extern_methods!(
unsafe impl DummyClass {
#[method_id(new)]
Expand All @@ -275,6 +288,12 @@ mod tests {
fn impl_traits() {
assert_impl_all!(NSObject: NSObjectProtocol);
assert_impl_all!(ProtocolObject<dyn NSObjectProtocol>: NSObjectProtocol);
assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol>: Send, Sync);
assert_impl_all!(ProtocolObject<dyn NSObjectProtocol + Send>: NSObjectProtocol, Send);
assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol + Send>: Sync);
assert_impl_all!(ProtocolObject<dyn NSObjectProtocol + Sync>: NSObjectProtocol, Sync);
assert_not_impl_any!(ProtocolObject<dyn NSObjectProtocol + Sync>: Send);
assert_impl_all!(ProtocolObject<dyn NSObjectProtocol + Send + Sync>: NSObjectProtocol, Send, Sync);
assert_not_impl_any!(ProtocolObject<dyn Foo>: NSObjectProtocol);
assert_impl_all!(ProtocolObject<dyn Bar>: NSObjectProtocol);
assert_impl_all!(ProtocolObject<dyn FooBar>: NSObjectProtocol);
Expand Down Expand Up @@ -332,6 +351,10 @@ mod tests {
let _nsobject: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(bar);
let nsobject: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(&*obj);
let _nsobject: &ProtocolObject<dyn NSObjectProtocol> = ProtocolObject::from_ref(nsobject);
let _: &ProtocolObject<dyn NSObjectProtocol + Send> = ProtocolObject::from_ref(&*obj);
let _: &ProtocolObject<dyn NSObjectProtocol + Sync> = ProtocolObject::from_ref(&*obj);
let _: &ProtocolObject<dyn NSObjectProtocol + Send + Sync> =
ProtocolObject::from_ref(&*obj);

let _foobar: &mut ProtocolObject<dyn FooBar> = ProtocolObject::from_mut(&mut *obj);
let _foobar: Id<ProtocolObject<dyn FooBar>> = ProtocolObject::from_id(obj);
Expand Down
171 changes: 144 additions & 27 deletions crates/test-ui/ui/protocol_object_only_protocols.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ error[E0277]: the trait bound `NSObject: ImplementedBy<NSObject>` is not satisfi
|
= help: the following other types implement trait `ImplementedBy<T>`:
(dyn NSObjectProtocol + 'static)
(dyn NSObjectProtocol + Send + 'static)
(dyn NSObjectProtocol + Sync + 'static)
(dyn NSObjectProtocol + Send + Sync + 'static)
(dyn NSAccessibilityColor + 'static)
(dyn NSAccessibilityCustomRotorItemSearchDelegate + 'static)
(dyn NSAccessibilityElementProtocol + 'static)
(dyn NSAccessibilityGroup + 'static)
(dyn NSAccessibilityButton + 'static)
(dyn NSAccessibilitySwitch + 'static)
(dyn NSAccessibilityRadioButton + 'static)
and $N others
note: required by a bound in `ProtocolObject::<P>::from_ref`
--> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs
Expand All @@ -35,13 +35,13 @@ error[E0277]: the trait bound `dyn Send: ImplementedBy<NSObject>` is not satisfi
|
= help: the following other types implement trait `ImplementedBy<T>`:
(dyn NSObjectProtocol + 'static)
(dyn NSObjectProtocol + Send + 'static)
(dyn NSObjectProtocol + Sync + 'static)
(dyn NSObjectProtocol + Send + Sync + 'static)
(dyn NSAccessibilityColor + 'static)
(dyn NSAccessibilityCustomRotorItemSearchDelegate + 'static)
(dyn NSAccessibilityElementProtocol + 'static)
(dyn NSAccessibilityGroup + 'static)
(dyn NSAccessibilityButton + 'static)
(dyn NSAccessibilitySwitch + 'static)
(dyn NSAccessibilityRadioButton + 'static)
and $N others
note: required by a bound in `ProtocolObject::<P>::from_ref`
--> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs
Expand All @@ -62,13 +62,13 @@ error[E0277]: the trait bound `dyn Foo: ImplementedBy<NSObject>` is not satisfie
|
= help: the following other types implement trait `ImplementedBy<T>`:
(dyn NSObjectProtocol + 'static)
(dyn NSObjectProtocol + Send + 'static)
(dyn NSObjectProtocol + Sync + 'static)
(dyn NSObjectProtocol + Send + Sync + 'static)
(dyn NSAccessibilityColor + 'static)
(dyn NSAccessibilityCustomRotorItemSearchDelegate + 'static)
(dyn NSAccessibilityElementProtocol + 'static)
(dyn NSAccessibilityGroup + 'static)
(dyn NSAccessibilityButton + 'static)
(dyn NSAccessibilitySwitch + 'static)
(dyn NSAccessibilityRadioButton + 'static)
and $N others
note: required by a bound in `ProtocolObject::<P>::from_ref`
--> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs
Expand All @@ -79,24 +79,52 @@ note: required by a bound in `ProtocolObject::<P>::from_ref`
| P: ImplementedBy<T>,
| ^^^^^^^^^^^^^^^^ required by this bound in `ProtocolObject::<P>::from_ref`

error[E0277]: the trait bound `dyn NSObjectProtocol + Send: ImplementedBy<NSObject>` is not satisfied
error[E0277]: `*const UnsafeCell<()>` cannot be sent between threads safely
--> ui/protocol_object_only_protocols.rs
|
| let _: &ProtocolObject<dyn NSObjectProtocol + Send> = ProtocolObject::from_ref(&*obj);
| ------------------------ ^^^^^ the trait `ImplementedBy<NSObject>` is not implemented for `dyn NSObjectProtocol + Send`
| ------------------------ ^^^^^ `*const UnsafeCell<()>` cannot be sent between threads safely
| |
| required by a bound introduced by this call
|
= help: within `NSObject`, the trait `Send` is not implemented for `*const UnsafeCell<()>`
= help: the following other types implement trait `ImplementedBy<T>`:
(dyn NSObjectProtocol + 'static)
(dyn NSObjectProtocol + Send + 'static)
(dyn NSObjectProtocol + Sync + 'static)
(dyn NSObjectProtocol + Send + Sync + 'static)
(dyn NSAccessibilityColor + 'static)
(dyn NSAccessibilityCustomRotorItemSearchDelegate + 'static)
(dyn NSAccessibilityElementProtocol + 'static)
(dyn NSAccessibilityGroup + 'static)
(dyn NSAccessibilityButton + 'static)
(dyn NSAccessibilitySwitch + 'static)
(dyn NSAccessibilityRadioButton + 'static)
and $N others
= note: required because it appears within the type `(*const UnsafeCell<()>, PhantomPinned)`
note: required because it appears within the type `PhantomData<(*const UnsafeCell<()>, PhantomPinned)>`
--> $RUST/core/src/marker.rs
|
| pub struct PhantomData<T: ?Sized>;
| ^^^^^^^^^^^
note: required because it appears within the type `UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>`
--> $RUST/core/src/cell.rs
|
| pub struct UnsafeCell<T: ?Sized> {
| ^^^^^^^^^^
note: required because it appears within the type `objc_object`
--> $WORKSPACE/crates/objc-sys/src/object.rs
|
| pub struct objc_object {
| ^^^^^^^^^^^
note: required because it appears within the type `AnyObject`
--> $WORKSPACE/crates/objc2/src/runtime/mod.rs
|
| pub struct AnyObject(ffi::objc_object);
| ^^^^^^^^^
note: required because it appears within the type `NSObject`
--> $WORKSPACE/crates/objc2/src/runtime/nsobject.rs
|
| pub struct NSObject {
| ^^^^^^^^
= note: required for `dyn NSObjectProtocol + Send` to implement `ImplementedBy<NSObject>`
note: required by a bound in `ProtocolObject::<P>::from_ref`
--> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs
|
Expand All @@ -106,24 +134,85 @@ note: required by a bound in `ProtocolObject::<P>::from_ref`
| P: ImplementedBy<T>,
| ^^^^^^^^^^^^^^^^ required by this bound in `ProtocolObject::<P>::from_ref`

error[E0277]: the trait bound `dyn NSObjectProtocol + Sync: ImplementedBy<NSObject>` is not satisfied
error[E0277]: `UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>` cannot be shared between threads safely
--> ui/protocol_object_only_protocols.rs
|
| let _: &ProtocolObject<dyn NSObjectProtocol + Sync> = ProtocolObject::from_ref(&*obj);
| ------------------------ ^^^^^ the trait `ImplementedBy<NSObject>` is not implemented for `dyn NSObjectProtocol + Sync`
| ------------------------ ^^^^^ `UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>` cannot be shared between threads safely
| |
| required by a bound introduced by this call
|
= help: within `NSObject`, the trait `Sync` is not implemented for `UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>`
= help: the following other types implement trait `ImplementedBy<T>`:
(dyn NSObjectProtocol + 'static)
(dyn NSObjectProtocol + Send + 'static)
(dyn NSObjectProtocol + Sync + 'static)
(dyn NSObjectProtocol + Send + Sync + 'static)
(dyn NSAccessibilityColor + 'static)
(dyn NSAccessibilityCustomRotorItemSearchDelegate + 'static)
(dyn NSAccessibilityElementProtocol + 'static)
(dyn NSAccessibilityGroup + 'static)
and $N others
note: required because it appears within the type `objc_object`
--> $WORKSPACE/crates/objc-sys/src/object.rs
|
| pub struct objc_object {
| ^^^^^^^^^^^
note: required because it appears within the type `AnyObject`
--> $WORKSPACE/crates/objc2/src/runtime/mod.rs
|
| pub struct AnyObject(ffi::objc_object);
| ^^^^^^^^^
note: required because it appears within the type `NSObject`
--> $WORKSPACE/crates/objc2/src/runtime/nsobject.rs
|
| pub struct NSObject {
| ^^^^^^^^
= note: required for `dyn NSObjectProtocol + Sync` to implement `ImplementedBy<NSObject>`
note: required by a bound in `ProtocolObject::<P>::from_ref`
--> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs
|
| pub fn from_ref<T: ?Sized + Message>(obj: &T) -> &Self
| -------- required by a bound in this associated function
| where
| P: ImplementedBy<T>,
| ^^^^^^^^^^^^^^^^ required by this bound in `ProtocolObject::<P>::from_ref`

error[E0277]: `UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>` cannot be shared between threads safely
--> ui/protocol_object_only_protocols.rs
|
| let _: &ProtocolObject<dyn NSObjectProtocol + Send + Sync> = ProtocolObject::from_ref(&*obj);
| ------------------------ ^^^^^ `UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>` cannot be shared between threads safely
| |
| required by a bound introduced by this call
|
= help: within `NSObject`, the trait `Sync` is not implemented for `UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>`
= help: the following other types implement trait `ImplementedBy<T>`:
(dyn NSObjectProtocol + 'static)
(dyn NSObjectProtocol + Send + 'static)
(dyn NSObjectProtocol + Sync + 'static)
(dyn NSObjectProtocol + Send + Sync + 'static)
(dyn NSAccessibilityColor + 'static)
(dyn NSAccessibilityCustomRotorItemSearchDelegate + 'static)
(dyn NSAccessibilityElementProtocol + 'static)
(dyn NSAccessibilityGroup + 'static)
(dyn NSAccessibilityButton + 'static)
(dyn NSAccessibilitySwitch + 'static)
(dyn NSAccessibilityRadioButton + 'static)
and $N others
note: required because it appears within the type `objc_object`
--> $WORKSPACE/crates/objc-sys/src/object.rs
|
| pub struct objc_object {
| ^^^^^^^^^^^
note: required because it appears within the type `AnyObject`
--> $WORKSPACE/crates/objc2/src/runtime/mod.rs
|
| pub struct AnyObject(ffi::objc_object);
| ^^^^^^^^^
note: required because it appears within the type `NSObject`
--> $WORKSPACE/crates/objc2/src/runtime/nsobject.rs
|
| pub struct NSObject {
| ^^^^^^^^
= note: required for `dyn NSObjectProtocol + Send + Sync` to implement `ImplementedBy<NSObject>`
note: required by a bound in `ProtocolObject::<P>::from_ref`
--> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs
|
Expand All @@ -133,24 +222,52 @@ note: required by a bound in `ProtocolObject::<P>::from_ref`
| P: ImplementedBy<T>,
| ^^^^^^^^^^^^^^^^ required by this bound in `ProtocolObject::<P>::from_ref`

error[E0277]: the trait bound `dyn NSObjectProtocol + Send + Sync: ImplementedBy<NSObject>` is not satisfied
error[E0277]: `*const UnsafeCell<()>` cannot be sent between threads safely
--> ui/protocol_object_only_protocols.rs
|
| let _: &ProtocolObject<dyn NSObjectProtocol + Send + Sync> = ProtocolObject::from_ref(&*obj);
| ------------------------ ^^^^^ the trait `ImplementedBy<NSObject>` is not implemented for `dyn NSObjectProtocol + Send + Sync`
| ------------------------ ^^^^^ `*const UnsafeCell<()>` cannot be sent between threads safely
| |
| required by a bound introduced by this call
|
= help: within `NSObject`, the trait `Send` is not implemented for `*const UnsafeCell<()>`
= help: the following other types implement trait `ImplementedBy<T>`:
(dyn NSObjectProtocol + 'static)
(dyn NSObjectProtocol + Send + 'static)
(dyn NSObjectProtocol + Sync + 'static)
(dyn NSObjectProtocol + Send + Sync + 'static)
(dyn NSAccessibilityColor + 'static)
(dyn NSAccessibilityCustomRotorItemSearchDelegate + 'static)
(dyn NSAccessibilityElementProtocol + 'static)
(dyn NSAccessibilityGroup + 'static)
(dyn NSAccessibilityButton + 'static)
(dyn NSAccessibilitySwitch + 'static)
(dyn NSAccessibilityRadioButton + 'static)
and $N others
= note: required because it appears within the type `(*const UnsafeCell<()>, PhantomPinned)`
note: required because it appears within the type `PhantomData<(*const UnsafeCell<()>, PhantomPinned)>`
--> $RUST/core/src/marker.rs
|
| pub struct PhantomData<T: ?Sized>;
| ^^^^^^^^^^^
note: required because it appears within the type `UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>`
--> $RUST/core/src/cell.rs
|
| pub struct UnsafeCell<T: ?Sized> {
| ^^^^^^^^^^
note: required because it appears within the type `objc_object`
--> $WORKSPACE/crates/objc-sys/src/object.rs
|
| pub struct objc_object {
| ^^^^^^^^^^^
note: required because it appears within the type `AnyObject`
--> $WORKSPACE/crates/objc2/src/runtime/mod.rs
|
| pub struct AnyObject(ffi::objc_object);
| ^^^^^^^^^
note: required because it appears within the type `NSObject`
--> $WORKSPACE/crates/objc2/src/runtime/nsobject.rs
|
| pub struct NSObject {
| ^^^^^^^^
= note: required for `dyn NSObjectProtocol + Send + Sync` to implement `ImplementedBy<NSObject>`
note: required by a bound in `ProtocolObject::<P>::from_ref`
--> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs
|
Expand Down Expand Up @@ -198,13 +315,13 @@ error[E0277]: the trait bound `dyn NSCopying + Send: ImplementedBy<NSObject>` is
|
= help: the following other types implement trait `ImplementedBy<T>`:
(dyn NSObjectProtocol + 'static)
(dyn NSObjectProtocol + Send + 'static)
(dyn NSObjectProtocol + Sync + 'static)
(dyn NSObjectProtocol + Send + Sync + 'static)
(dyn NSAccessibilityColor + 'static)
(dyn NSAccessibilityCustomRotorItemSearchDelegate + 'static)
(dyn NSAccessibilityElementProtocol + 'static)
(dyn NSAccessibilityGroup + 'static)
(dyn NSAccessibilityButton + 'static)
(dyn NSAccessibilitySwitch + 'static)
(dyn NSAccessibilityRadioButton + 'static)
and $N others
note: required by a bound in `ProtocolObject::<P>::from_ref`
--> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs
Expand Down

0 comments on commit 1c663ba

Please sign in to comment.