From 2b57f622580a249fd9e028d4348bde1dd96a0342 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 3 Dec 2023 08:47:13 +0100 Subject: [PATCH] Allow protocol objects to be Send + Sync --- crates/objc2/CHANGELOG.md | 4 + crates/objc2/src/macros/extern_protocol.rs | 3 + crates/objc2/src/runtime/nsobject.rs | 29 +++ crates/objc2/src/runtime/protocol_object.rs | 23 +++ .../ui/protocol_object_only_protocols.stderr | 171 +++++++++++++++--- 5 files changed, 203 insertions(+), 27 deletions(-) diff --git a/crates/objc2/CHANGELOG.md b/crates/objc2/CHANGELOG.md index ce13dae31..931643621 100644 --- a/crates/objc2/CHANGELOG.md +++ b/crates/objc2/CHANGELOG.md @@ -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`. ### Changed * **BREAKING**: Changed how instance variables work in `declare_class!`. diff --git a/crates/objc2/src/macros/extern_protocol.rs b/crates/objc2/src/macros/extern_protocol.rs index 9771871cb..1a99770a0 100644 --- a/crates/objc2/src/macros/extern_protocol.rs +++ b/crates/objc2/src/macros/extern_protocol.rs @@ -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`? }; } diff --git a/crates/objc2/src/runtime/nsobject.rs b/crates/objc2/src/runtime/nsobject.rs index 3672a5c85..deb48c856 100644 --- a/crates/objc2/src/runtime/nsobject.rs +++ b/crates/objc2/src/runtime/nsobject.rs @@ -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 @@ -163,6 +165,33 @@ crate::__inner_extern_protocol!( ("NSObject") ); +// SAFETY: Anything that implements `NSObjectProtocol` and is `Send` is valid +// to convert to `ProtocolObject`. +unsafe impl ImplementedBy 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`. +unsafe impl ImplementedBy 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`. +unsafe impl ImplementedBy for dyn NSObjectProtocol + Send + Sync +where + T: ?Sized + Message + NSObjectProtocol + Send + Sync, +{ + const __INNER: () = (); +} + unsafe impl NSObjectProtocol for NSObject {} extern_methods!( diff --git a/crates/objc2/src/runtime/protocol_object.rs b/crates/objc2/src/runtime/protocol_object.rs index 62a9b40d1..384ebf99c 100644 --- a/crates/objc2/src/runtime/protocol_object.rs +++ b/crates/objc2/src/runtime/protocol_object.rs @@ -62,6 +62,16 @@ pub struct ProtocolObject { p: PhantomData

, } +// SAFETY: `Send` if the underlying trait promises `Send`. +// +// E.g. `ProtocolObject` is naturally `Send`. +unsafe impl Send for ProtocolObject

{} + +// SAFETY: `Sync` if the underlying trait promises `Sync`. +// +// E.g. `ProtocolObject` is naturally `Sync`. +unsafe impl Sync for ProtocolObject

{} + // SAFETY: The type is `#[repr(C)]` and `AnyObject` internally unsafe impl RefEncode for ProtocolObject

{ const ENCODING_REF: Encoding = Encoding::Object; @@ -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)] @@ -275,6 +288,12 @@ mod tests { fn impl_traits() { assert_impl_all!(NSObject: NSObjectProtocol); assert_impl_all!(ProtocolObject: NSObjectProtocol); + assert_not_impl_any!(ProtocolObject: Send, Sync); + assert_impl_all!(ProtocolObject: NSObjectProtocol, Send); + assert_not_impl_any!(ProtocolObject: Sync); + assert_impl_all!(ProtocolObject: NSObjectProtocol, Sync); + assert_not_impl_any!(ProtocolObject: Send); + assert_impl_all!(ProtocolObject: NSObjectProtocol, Send, Sync); assert_not_impl_any!(ProtocolObject: NSObjectProtocol); assert_impl_all!(ProtocolObject: NSObjectProtocol); assert_impl_all!(ProtocolObject: NSObjectProtocol); @@ -332,6 +351,10 @@ mod tests { let _nsobject: &ProtocolObject = ProtocolObject::from_ref(bar); let nsobject: &ProtocolObject = ProtocolObject::from_ref(&*obj); let _nsobject: &ProtocolObject = ProtocolObject::from_ref(nsobject); + let _: &ProtocolObject = ProtocolObject::from_ref(&*obj); + let _: &ProtocolObject = ProtocolObject::from_ref(&*obj); + let _: &ProtocolObject = + ProtocolObject::from_ref(&*obj); let _foobar: &mut ProtocolObject = ProtocolObject::from_mut(&mut *obj); let _foobar: Id> = ProtocolObject::from_id(obj); diff --git a/crates/test-ui/ui/protocol_object_only_protocols.stderr b/crates/test-ui/ui/protocol_object_only_protocols.stderr index c337ea52c..98c6d27af 100644 --- a/crates/test-ui/ui/protocol_object_only_protocols.stderr +++ b/crates/test-ui/ui/protocol_object_only_protocols.stderr @@ -8,13 +8,13 @@ error[E0277]: the trait bound `NSObject: ImplementedBy` is not satisfi | = help: the following other types implement trait `ImplementedBy`: (dyn NSObjectProtocol + 'static) + (dyn NSObjectProtocol + Send + 'static) + (dyn NSObjectProtocol + Send + Sync + 'static) + (dyn NSObjectProtocol + 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::

::from_ref` --> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs @@ -35,13 +35,13 @@ error[E0277]: the trait bound `dyn Send: ImplementedBy` is not satisfi | = help: the following other types implement trait `ImplementedBy`: (dyn NSObjectProtocol + 'static) + (dyn NSObjectProtocol + Send + 'static) + (dyn NSObjectProtocol + Send + Sync + 'static) + (dyn NSObjectProtocol + 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::

::from_ref` --> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs @@ -62,13 +62,13 @@ error[E0277]: the trait bound `dyn Foo: ImplementedBy` is not satisfie | = help: the following other types implement trait `ImplementedBy`: (dyn NSObjectProtocol + 'static) + (dyn NSObjectProtocol + Send + 'static) + (dyn NSObjectProtocol + Send + Sync + 'static) + (dyn NSObjectProtocol + 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::

::from_ref` --> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs @@ -79,24 +79,52 @@ note: required by a bound in `ProtocolObject::

::from_ref` | P: ImplementedBy, | ^^^^^^^^^^^^^^^^ required by this bound in `ProtocolObject::

::from_ref` -error[E0277]: the trait bound `dyn NSObjectProtocol + Send: ImplementedBy` is not satisfied +error[E0277]: `*const UnsafeCell<()>` cannot be sent between threads safely --> ui/protocol_object_only_protocols.rs | | let _: &ProtocolObject = ProtocolObject::from_ref(&*obj); - | ------------------------ ^^^^^ the trait `ImplementedBy` 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`: (dyn NSObjectProtocol + 'static) + (dyn NSObjectProtocol + Send + 'static) + (dyn NSObjectProtocol + Send + Sync + 'static) + (dyn NSObjectProtocol + 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; + | ^^^^^^^^^^^ +note: required because it appears within the type `UnsafeCell, PhantomPinned)>>` + --> $RUST/core/src/cell.rs + | + | pub struct UnsafeCell { + | ^^^^^^^^^^ +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` note: required by a bound in `ProtocolObject::

::from_ref` --> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs | @@ -106,24 +134,85 @@ note: required by a bound in `ProtocolObject::

::from_ref` | P: ImplementedBy, | ^^^^^^^^^^^^^^^^ required by this bound in `ProtocolObject::

::from_ref` -error[E0277]: the trait bound `dyn NSObjectProtocol + Sync: ImplementedBy` is not satisfied +error[E0277]: `UnsafeCell, PhantomPinned)>>` cannot be shared between threads safely --> ui/protocol_object_only_protocols.rs | | let _: &ProtocolObject = ProtocolObject::from_ref(&*obj); - | ------------------------ ^^^^^ the trait `ImplementedBy` is not implemented for `dyn NSObjectProtocol + Sync` + | ------------------------ ^^^^^ `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, PhantomPinned)>>` + = help: the following other types implement trait `ImplementedBy`: + (dyn NSObjectProtocol + 'static) + (dyn NSObjectProtocol + Send + 'static) + (dyn NSObjectProtocol + Send + Sync + 'static) + (dyn NSObjectProtocol + 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` +note: required by a bound in `ProtocolObject::

::from_ref` + --> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs + | + | pub fn from_ref(obj: &T) -> &Self + | -------- required by a bound in this associated function + | where + | P: ImplementedBy, + | ^^^^^^^^^^^^^^^^ required by this bound in `ProtocolObject::

::from_ref` + +error[E0277]: `UnsafeCell, PhantomPinned)>>` cannot be shared between threads safely + --> ui/protocol_object_only_protocols.rs + | + | let _: &ProtocolObject = ProtocolObject::from_ref(&*obj); + | ------------------------ ^^^^^ `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, PhantomPinned)>>` = help: the following other types implement trait `ImplementedBy`: (dyn NSObjectProtocol + 'static) + (dyn NSObjectProtocol + Send + 'static) + (dyn NSObjectProtocol + Send + Sync + 'static) + (dyn NSObjectProtocol + 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` note: required by a bound in `ProtocolObject::

::from_ref` --> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs | @@ -133,24 +222,52 @@ note: required by a bound in `ProtocolObject::

::from_ref` | P: ImplementedBy, | ^^^^^^^^^^^^^^^^ required by this bound in `ProtocolObject::

::from_ref` -error[E0277]: the trait bound `dyn NSObjectProtocol + Send + Sync: ImplementedBy` is not satisfied +error[E0277]: `*const UnsafeCell<()>` cannot be sent between threads safely --> ui/protocol_object_only_protocols.rs | | let _: &ProtocolObject = ProtocolObject::from_ref(&*obj); - | ------------------------ ^^^^^ the trait `ImplementedBy` 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`: (dyn NSObjectProtocol + 'static) + (dyn NSObjectProtocol + Send + 'static) + (dyn NSObjectProtocol + Send + Sync + 'static) + (dyn NSObjectProtocol + 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; + | ^^^^^^^^^^^ +note: required because it appears within the type `UnsafeCell, PhantomPinned)>>` + --> $RUST/core/src/cell.rs + | + | pub struct UnsafeCell { + | ^^^^^^^^^^ +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` note: required by a bound in `ProtocolObject::

::from_ref` --> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs | @@ -198,13 +315,13 @@ error[E0277]: the trait bound `dyn NSCopying + Send: ImplementedBy` is | = help: the following other types implement trait `ImplementedBy`: (dyn NSObjectProtocol + 'static) + (dyn NSObjectProtocol + Send + 'static) + (dyn NSObjectProtocol + Send + Sync + 'static) + (dyn NSObjectProtocol + 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::

::from_ref` --> $WORKSPACE/crates/objc2/src/runtime/protocol_object.rs