diff --git a/crates/icrate/src/additions/Foundation/array.rs b/crates/icrate/src/additions/Foundation/array.rs index 6866222a6..f9cf94136 100644 --- a/crates/icrate/src/additions/Foundation/array.rs +++ b/crates/icrate/src/additions/Foundation/array.rs @@ -391,7 +391,7 @@ __impl_iter! { pub struct IterMut<'a, T: Message>(iter::IterMut<'a, NSArray>); __impl_iter! { - impl<'a, T: IsMutable> Iterator for IterMut<'a, T> { ... } + impl<'a, T: Message + IsMutable> Iterator for IterMut<'a, T> { ... } } /// An iterator that retains the items of a `NSArray`. @@ -399,7 +399,7 @@ __impl_iter! { pub struct IterRetained<'a, T: Message>(iter::IterRetained<'a, NSArray>); __impl_iter! { - impl<'a, T: IsIdCloneable> Iterator> for IterRetained<'a, T> { ... } + impl<'a, T: Message + IsIdCloneable> Iterator> for IterRetained<'a, T> { ... } } /// A consuming iterator over the items of a `NSArray`. @@ -420,16 +420,16 @@ __impl_into_iter! { type IntoIter = Iter<'_, T>; } - impl IntoIterator for &mut NSArray { + impl IntoIterator for &mut NSArray { type IntoIter = IterMut<'_, T>; } #[cfg(feature = "Foundation_NSMutableArray")] - impl IntoIterator for &mut NSMutableArray { + impl IntoIterator for &mut NSMutableArray { type IntoIter = IterMut<'_, T>; } - impl IntoIterator for Id> { + impl IntoIterator for Id> { type IntoIter = IntoIter; } @@ -456,14 +456,14 @@ impl Index for NSMutableArray { } } -impl IndexMut for NSArray { +impl IndexMut for NSArray { fn index_mut(&mut self, index: usize) -> &mut T { self.get_mut(index).unwrap() } } #[cfg(feature = "Foundation_NSMutableArray")] -impl IndexMut for NSMutableArray { +impl IndexMut for NSMutableArray { fn index_mut(&mut self, index: usize) -> &mut T { self.get_mut(index).unwrap() } @@ -484,7 +484,7 @@ impl Extend> for NSMutableArray { } #[cfg(feature = "Foundation_NSMutableArray")] -impl<'a, T: IsRetainable> Extend<&'a T> for NSMutableArray { +impl<'a, T: Message + IsRetainable> Extend<&'a T> for NSMutableArray { fn extend>(&mut self, iter: I) { // SAFETY: Because of the `T: IsRetainable` bound, it is safe for the // array to retain the object here. @@ -493,7 +493,7 @@ impl<'a, T: IsRetainable> Extend<&'a T> for NSMutableArray { } } -impl<'a, T: IsRetainable + 'a> IdFromIterator<&'a T> for NSArray { +impl<'a, T: Message + IsRetainable + 'a> IdFromIterator<&'a T> for NSArray { fn id_from_iter>(iter: I) -> Id { let vec = Vec::from_iter(iter); Self::from_slice(&vec) @@ -508,7 +508,7 @@ impl IdFromIterator> for NSArray { } #[cfg(feature = "Foundation_NSMutableArray")] -impl<'a, T: IsRetainable + 'a> IdFromIterator<&'a T> for NSMutableArray { +impl<'a, T: Message + IsRetainable + 'a> IdFromIterator<&'a T> for NSMutableArray { fn id_from_iter>(iter: I) -> Id { let vec = Vec::from_iter(iter); Self::from_slice(&vec) diff --git a/crates/icrate/src/additions/Foundation/dictionary.rs b/crates/icrate/src/additions/Foundation/dictionary.rs index f2fef8d08..2feac65af 100644 --- a/crates/icrate/src/additions/Foundation/dictionary.rs +++ b/crates/icrate/src/additions/Foundation/dictionary.rs @@ -516,7 +516,7 @@ mod iter_helpers { ); __impl_iter! { - impl<'a, K: IsIdCloneable, V: Message> Iterator> for KeysRetained<'a, K, V> { ... } + impl<'a, K: Message + IsIdCloneable, V: Message> Iterator> for KeysRetained<'a, K, V> { ... } } /// An iterator over the values of a `NSDictionary`. @@ -536,7 +536,7 @@ mod iter_helpers { ); __impl_iter! { - impl<'a, K: Message, V: IsMutable> Iterator for ValuesMut<'a, K, V> { ... } + impl<'a, K: Message, V: Message + IsMutable> Iterator for ValuesMut<'a, K, V> { ... } } /// A iterator that retains the values of a `NSDictionary`. @@ -546,7 +546,7 @@ mod iter_helpers { ); __impl_iter! { - impl<'a, K: Message, V: IsIdCloneable> Iterator> for ValuesRetained<'a, K, V> { ... } + impl<'a, K: Message, V: Message + IsIdCloneable> Iterator> for ValuesRetained<'a, K, V> { ... } } /// A consuming iterator over the values of a `NSDictionary`. @@ -580,14 +580,16 @@ impl<'a, K: Message + Eq + Hash, V: Message> Index<&'a K> for NSMutableDictionar } } -impl<'a, K: Message + Eq + Hash, V: IsMutable> IndexMut<&'a K> for NSDictionary { +impl<'a, K: Message + Eq + Hash, V: Message + IsMutable> IndexMut<&'a K> for NSDictionary { fn index_mut<'s>(&'s mut self, index: &'a K) -> &'s mut V { self.get_mut(index).unwrap() } } #[cfg(feature = "Foundation_NSMutableDictionary")] -impl<'a, K: Message + Eq + Hash, V: IsMutable> IndexMut<&'a K> for NSMutableDictionary { +impl<'a, K: Message + Eq + Hash, V: Message + IsMutable> IndexMut<&'a K> + for NSMutableDictionary +{ fn index_mut<'s>(&'s mut self, index: &'a K) -> &'s mut V { self.get_mut(index).unwrap() } diff --git a/crates/icrate/src/additions/Foundation/enumerator.rs b/crates/icrate/src/additions/Foundation/enumerator.rs index cec199dd9..6a3f09399 100644 --- a/crates/icrate/src/additions/Foundation/enumerator.rs +++ b/crates/icrate/src/additions/Foundation/enumerator.rs @@ -1,7 +1,5 @@ //! Utilities for the `NSEnumerator` class. #![cfg(feature = "Foundation_NSEnumerator")] -use objc2::mutability::{IsIdCloneable, IsMutable}; - use super::iter; use crate::common::*; use crate::Foundation::NSEnumerator; diff --git a/crates/icrate/src/additions/Foundation/iter.rs b/crates/icrate/src/additions/Foundation/iter.rs index 51fa429e1..59b317be0 100644 --- a/crates/icrate/src/additions/Foundation/iter.rs +++ b/crates/icrate/src/additions/Foundation/iter.rs @@ -428,7 +428,7 @@ impl IntoIter { } } - pub(crate) fn new_mutable>(collection: Id) -> Self + pub(crate) fn new_mutable + IsMutable>(collection: Id) -> Self where C: IsIdCloneable, { @@ -648,7 +648,7 @@ where } } - pub(crate) unsafe fn new_mutable>( + pub(crate) unsafe fn new_mutable + IsMutable>( collection: Id, enumerator: Id, ) -> Self @@ -696,9 +696,9 @@ where #[doc(hidden)] macro_rules! __impl_iter { ( - impl<$($lifetime:lifetime, )? $t1:ident: $bound1:ident $(, $t2:ident: $bound2:ident)?> Iterator for $for:ty { ... } + impl<$($lifetime:lifetime, )? $t1:ident: $bound1:ident $(+ $bound1_b:ident)? $(, $t2:ident: $bound2:ident $(+ $bound2_b:ident)?)?> Iterator for $for:ty { ... } ) => { - impl<$($lifetime, )? $t1: $bound1 $(, $t2: $bound2)?> Iterator for $for { + impl<$($lifetime, )? $t1: $bound1 $(+ $bound1_b)? $(, $t2: $bound2 $(+ $bound2_b)?)?> Iterator for $for { type Item = $item; #[inline] @@ -743,14 +743,14 @@ macro_rules! __impl_into_iter { }; ( $(#[$m:meta])* - impl IntoIterator for &mut $ty:ident { + impl IntoIterator for &mut $ty:ident { type IntoIter = $iter_mut:ident<'_, T>; } $($rest:tt)* ) => { $(#[$m])* - impl<'a, T: IsMutable> IntoIterator for &'a mut $ty { + impl<'a, T: Message + IsMutable> IntoIterator for &'a mut $ty { type Item = &'a mut T; type IntoIter = $iter_mut<'a, T>; @@ -766,14 +766,14 @@ macro_rules! __impl_into_iter { }; ( $(#[$m:meta])* - impl IntoIterator for Id<$ty:ident> { + impl IntoIterator for Id<$ty:ident> { type IntoIter = $into_iter:ident; } $($rest:tt)* ) => { $(#[$m])* - impl objc2::rc::IdIntoIterator for $ty { + impl objc2::rc::IdIntoIterator for $ty { type Item = Id; type IntoIter = $into_iter; diff --git a/crates/icrate/src/additions/Foundation/set.rs b/crates/icrate/src/additions/Foundation/set.rs index 0d338b8fb..39eed720a 100644 --- a/crates/icrate/src/additions/Foundation/set.rs +++ b/crates/icrate/src/additions/Foundation/set.rs @@ -501,7 +501,7 @@ __impl_iter! { pub struct IterRetained<'a, T: Message>(iter::IterRetained<'a, NSSet>); __impl_iter! { - impl<'a, T: IsIdCloneable> Iterator> for IterRetained<'a, T> { ... } + impl<'a, T: Message + IsIdCloneable> Iterator> for IterRetained<'a, T> { ... } } /// A consuming iterator over the items of a `NSSet`. @@ -522,7 +522,7 @@ __impl_into_iter! { type IntoIter = Iter<'_, T>; } - impl IntoIterator for Id> { + impl IntoIterator for Id> { type IntoIter = IntoIter; } @@ -549,7 +549,7 @@ impl Extend> for NSMutableSet { } #[cfg(feature = "Foundation_NSMutableSet")] -impl<'a, T: IsRetainable + Eq + Hash + HasStableHash> Extend<&'a T> for NSMutableSet { +impl<'a, T: Message + Eq + Hash + HasStableHash + IsRetainable> Extend<&'a T> for NSMutableSet { fn extend>(&mut self, iter: I) { iter.into_iter().for_each(move |item| { self.insert(item); @@ -557,7 +557,9 @@ impl<'a, T: IsRetainable + Eq + Hash + HasStableHash> Extend<&'a T> for NSMutabl } } -impl<'a, T: IsRetainable + Eq + Hash + HasStableHash + 'a> IdFromIterator<&'a T> for NSSet { +impl<'a, T: Message + Eq + Hash + HasStableHash + IsRetainable + 'a> IdFromIterator<&'a T> + for NSSet +{ fn id_from_iter>(iter: I) -> Id { let vec = Vec::from_iter(iter); Self::from_slice(&vec) @@ -572,7 +574,7 @@ impl IdFromIterator> for NSSet } #[cfg(feature = "Foundation_NSMutableSet")] -impl<'a, T: IsRetainable + Eq + Hash + HasStableHash + 'a> IdFromIterator<&'a T> +impl<'a, T: Message + Eq + Hash + HasStableHash + IsRetainable + 'a> IdFromIterator<&'a T> for NSMutableSet { fn id_from_iter>(iter: I) -> Id { diff --git a/crates/icrate/src/additions/Foundation/util.rs b/crates/icrate/src/additions/Foundation/util.rs index 181dcabc1..e49456b14 100644 --- a/crates/icrate/src/additions/Foundation/util.rs +++ b/crates/icrate/src/additions/Foundation/util.rs @@ -68,7 +68,7 @@ pub(crate) fn id_ptr_cast_const(objects: *const Id) -> *mut NonNul #[inline] pub(crate) unsafe fn collection_retain_id(obj: &T) -> Id where - T: IsIdCloneable, + T: Message + IsIdCloneable, { // SAFETY: We're allowed to access `&Id` from `&self` in collections, // and since `T: IsIdCloneable`, we can convert that to `Id`. diff --git a/crates/icrate/src/fixes/Foundation/copy.rs b/crates/icrate/src/fixes/Foundation/copy.rs index 4f4f03b28..a0eed6d09 100644 --- a/crates/icrate/src/fixes/Foundation/copy.rs +++ b/crates/icrate/src/fixes/Foundation/copy.rs @@ -4,7 +4,7 @@ use crate::common::*; use crate::Foundation::{self, NSCopying, NSMutableCopying}; #[cfg(feature = "Foundation_NSArray")] -impl ToOwned for Foundation::NSArray { +impl ToOwned for Foundation::NSArray { type Owned = Id; fn to_owned(&self) -> Self::Owned { self.copy() @@ -12,7 +12,7 @@ impl ToOwned for Foundation::NSArray { } #[cfg(feature = "Foundation_NSMutableArray")] -impl ToOwned for Foundation::NSMutableArray { +impl ToOwned for Foundation::NSMutableArray { type Owned = Id; fn to_owned(&self) -> Self::Owned { self.mutableCopy() @@ -44,7 +44,7 @@ impl ToOwned for Foundation::NSException { } #[cfg(feature = "Foundation_NSSet")] -impl ToOwned for Foundation::NSSet { +impl ToOwned for Foundation::NSSet { type Owned = Id; fn to_owned(&self) -> Self::Owned { self.copy() @@ -52,7 +52,7 @@ impl ToOwned for Foundation::NSSet { } #[cfg(feature = "Foundation_NSMutableSet")] -impl ToOwned for Foundation::NSMutableSet { +impl ToOwned for Foundation::NSMutableSet { type Owned = Id; fn to_owned(&self) -> Self::Owned { self.mutableCopy() diff --git a/crates/icrate/tests/array.rs b/crates/icrate/tests/array.rs index 68e0ee019..b02cf0229 100644 --- a/crates/icrate/tests/array.rs +++ b/crates/icrate/tests/array.rs @@ -1,8 +1,10 @@ #![cfg(feature = "Foundation_NSArray")] #![cfg(feature = "Foundation_NSNumber")] use icrate::Foundation::{NSArray, NSNumber, NSObject}; -use objc2::rc::Id; -use objc2::rc::{__RcTestObject, __ThreadTestData}; +use objc2::mutability::IsRetainable; +use objc2::rc::{Id, __RcTestObject, __ThreadTestData}; +use objc2::runtime::ProtocolObject; +use objc2::{extern_protocol, ProtocolType}; fn sample_array(len: usize) -> Id> { let mut vec = Vec::with_capacity(len); @@ -262,3 +264,22 @@ fn test_generic_ownership_traits() { assert_partialeq::>(); } + +#[test] +fn test_trait_retainable() { + extern_protocol!( + #[allow(clippy::missing_safety_doc)] + unsafe trait TestProtocol: IsRetainable {} + + unsafe impl ProtocolType for dyn TestProtocol { + const NAME: &'static str = "NSObject"; + } + ); + + unsafe impl TestProtocol for NSNumber {} + + let obj: Id> = ProtocolObject::from_id(NSNumber::new_i32(42)); + let _ = NSArray::from_slice(&[&*obj, &*obj]); + let _ = NSArray::from_id_slice(&[obj.clone(), obj.clone()]); + let _ = NSArray::from_vec(vec![obj.clone(), obj.clone()]); +} diff --git a/crates/icrate/tests/copying.rs b/crates/icrate/tests/copying.rs new file mode 100644 index 000000000..5bd8c4161 --- /dev/null +++ b/crates/icrate/tests/copying.rs @@ -0,0 +1,18 @@ +#![cfg(feature = "Foundation")] +#![cfg(feature = "Foundation_NSString")] +use icrate::Foundation::{NSCopying, NSMutableCopying, NSString}; +use objc2::{rc::Id, runtime::ProtocolObject}; + +#[test] +fn copy() { + let obj = NSString::new(); + let protocol_object: &ProtocolObject = ProtocolObject::from_ref(&*obj); + let _: Id> = protocol_object.copy(); +} + +#[test] +fn copy_mutable() { + let obj = NSString::new(); + let protocol_object: &ProtocolObject = ProtocolObject::from_ref(&*obj); + let _: Id> = protocol_object.mutableCopy(); +} diff --git a/crates/objc2/CHANGELOG.md b/crates/objc2/CHANGELOG.md index 2ed8ef40e..30b4e28db 100644 --- a/crates/objc2/CHANGELOG.md +++ b/crates/objc2/CHANGELOG.md @@ -18,6 +18,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed * **BREAKING**: `AnyClass::verify_sel` now take more well-defined types `EncodeArguments` and `EncodeReturn`. +* **BREAKING**: Changed how the `mutability` traits work; these no longer have + `ClassType` as a super trait, allowing them to work for `ProtocolObject` as + well. + + This effectively means you can now `copy` a `ProtocolObject`. +* **BREAKING**: Allow implementing `DefaultId` for any type, not just those + who are `IsAllocableAnyThread`. ### Deprecated * Soft deprecated using `msg_send!` without a comma between arguments (i.e. diff --git a/crates/objc2/src/declare/mod.rs b/crates/objc2/src/declare/mod.rs index 52a7f3777..2accc2688 100644 --- a/crates/objc2/src/declare/mod.rs +++ b/crates/objc2/src/declare/mod.rs @@ -167,17 +167,17 @@ pub trait MethodImplementation: private::Sealed + Sized { } macro_rules! method_decl_impl { - (@<$($l:lifetime),*> T: $t_bound:ident, $r:ident, $f:ty, $($t:ident),*) => { + (@<$($l:lifetime),*> T: $t_bound:ident $(+ $t_bound2:ident)?, $r:ident, $f:ty, $($t:ident),*) => { impl<$($l,)* T, $r, $($t),*> private::Sealed for $f where - T: ?Sized + $t_bound, + T: ?Sized + $t_bound $(+ $t_bound2)?, $r: EncodeReturn, $($t: EncodeArgument,)* {} impl<$($l,)* T, $r, $($t),*> MethodImplementation for $f where - T: ?Sized + $t_bound, + T: ?Sized + $t_bound $(+ $t_bound2)?, $r: EncodeReturn, $($t: EncodeArgument,)* { @@ -244,11 +244,11 @@ macro_rules! method_decl_impl { }; (# $abi:literal; $($t:ident),*) => { method_decl_impl!(@<'a> T: Message, R, extern $abi fn(&'a T, Sel $(, $t)*) -> R, $($t),*); - method_decl_impl!(@<'a> T: IsMutable, R, extern $abi fn(&'a mut T, Sel $(, $t)*) -> R, $($t),*); + method_decl_impl!(@<'a> T: Message + IsMutable, R, extern $abi fn(&'a mut T, Sel $(, $t)*) -> R, $($t),*); method_decl_impl!(@<> T: Message, R, unsafe extern $abi fn(*const T, Sel $(, $t)*) -> R, $($t),*); method_decl_impl!(@<> T: Message, R, unsafe extern $abi fn(*mut T, Sel $(, $t)*) -> R, $($t),*); method_decl_impl!(@<'a> T: Message, R, unsafe extern $abi fn(&'a T, Sel $(, $t)*) -> R, $($t),*); - method_decl_impl!(@<'a> T: IsMutable, R, unsafe extern $abi fn(&'a mut T, Sel $(, $t)*) -> R, $($t),*); + method_decl_impl!(@<'a> T: Message + IsMutable, R, unsafe extern $abi fn(&'a mut T, Sel $(, $t)*) -> R, $($t),*); method_decl_impl!(@<'a> AnyObject, R, extern $abi fn(&'a mut AnyObject, Sel $(, $t)*) -> R, $($t),*); method_decl_impl!(@<'a> AnyObject, R, unsafe extern $abi fn(&'a mut AnyObject, Sel $(, $t)*) -> R, $($t),*); diff --git a/crates/objc2/src/message/mod.rs b/crates/objc2/src/message/mod.rs index 4e797c330..e2dbdf13b 100644 --- a/crates/objc2/src/message/mod.rs +++ b/crates/objc2/src/message/mod.rs @@ -448,8 +448,8 @@ unsafe impl<'a, T: ?Sized + Message> MessageReceiver for &'a Id { } } -impl<'a, T: ?Sized + IsMutable> private::Sealed for &'a mut Id {} -unsafe impl<'a, T: ?Sized + IsMutable> MessageReceiver for &'a mut Id { +impl<'a, T: ?Sized + Message + IsMutable> private::Sealed for &'a mut Id {} +unsafe impl<'a, T: ?Sized + Message + IsMutable> MessageReceiver for &'a mut Id { type __Inner = T; #[inline] diff --git a/crates/objc2/src/mutability.rs b/crates/objc2/src/mutability.rs index 2e621fd0f..d619dfbd0 100644 --- a/crates/objc2/src/mutability.rs +++ b/crates/objc2/src/mutability.rs @@ -32,7 +32,8 @@ //! bug. use core::marker::PhantomData; -use crate::ClassType; +use crate::runtime::ProtocolObject; +use crate::{ClassType, Message, ProtocolType}; mod private_mutability { pub trait Sealed {} @@ -259,6 +260,13 @@ pub struct MainThreadOnly { inner: Never, } +mod private_traits { + pub trait Sealed {} +} + +impl private_traits::Sealed for T {} +impl private_traits::Sealed for ProtocolObject

{} + /// Marker trait for classes where [`Id::clone`] is safe. /// /// Since the `Foundation` collection types (`NSArray`, @@ -274,7 +282,13 @@ pub struct MainThreadOnly { /// /// [`Id`]: crate::rc::Id /// [`Id::clone`]: crate::rc::Id#impl-Clone-for-Id -pub trait IsIdCloneable: ClassType {} +/// +/// +/// # Safety +/// +/// This is a sealed trait, and should not need to be implemented. Open an +/// issue if you know a use-case where this restrition should be lifted! +pub unsafe trait IsIdCloneable: private_traits::Sealed {} trait MutabilityIsIdCloneable: Mutability {} impl MutabilityIsIdCloneable for Root {} @@ -283,7 +297,8 @@ impl MutabilityIsIdCloneable for ImmutableWithMutableSubclass {} impl MutabilityIsIdCloneable for InteriorMutable {} impl MutabilityIsIdCloneable for MainThreadOnly {} -impl IsIdCloneable for T where T::Mutability: MutabilityIsIdCloneable {} +unsafe impl IsIdCloneable for T where T::Mutability: MutabilityIsIdCloneable {} +unsafe impl IsIdCloneable for ProtocolObject

{} /// Marker trait for classes where the `retain` selector is always safe. /// @@ -296,15 +311,25 @@ impl IsIdCloneable for T where T::Mutability: MutabilityI /// - [`InteriorMutable`]. /// - [`MainThreadOnly`]. /// +/// This trait inherits [`IsIdCloneable`], so if a function is bound by this, +/// functionality given with that trait is available. +/// /// [`Id::clone`]: crate::rc::Id#impl-Clone-for-Id -pub trait IsRetainable: IsIdCloneable {} +/// +/// +/// # Safety +/// +/// This is a sealed trait, and should not need to be implemented. Open an +/// issue if you know a use-case where this restrition should be lifted! +pub unsafe trait IsRetainable: IsIdCloneable {} trait MutabilityIsRetainable: MutabilityIsIdCloneable {} impl MutabilityIsRetainable for Immutable {} impl MutabilityIsRetainable for InteriorMutable {} impl MutabilityIsRetainable for MainThreadOnly {} -impl IsRetainable for T where T::Mutability: MutabilityIsRetainable {} +unsafe impl IsRetainable for T where T::Mutability: MutabilityIsRetainable {} +unsafe impl IsRetainable for ProtocolObject

{} /// Marker trait for classes that can be allocated from any thread. /// @@ -315,7 +340,13 @@ impl IsRetainable for T where T::Mutability: MutabilityIs /// - [`ImmutableWithMutableSubclass`]. /// - [`MutableWithImmutableSuperclass`]. /// - [`InteriorMutable`]. -pub trait IsAllocableAnyThread: ClassType {} +/// +/// +/// # Safety +/// +/// This is a sealed trait, and should not need to be implemented. Open an +/// issue if you know a use-case where this restrition should be lifted! +pub unsafe trait IsAllocableAnyThread: private_traits::Sealed {} trait MutabilityIsAllocableAnyThread: Mutability {} impl MutabilityIsAllocableAnyThread for Root {} @@ -325,10 +356,14 @@ impl MutabilityIsAllocableAnyThread for ImmutableWithMutableSubclass impl MutabilityIsAllocableAnyThread for MutableWithImmutableSuperclass {} impl MutabilityIsAllocableAnyThread for InteriorMutable {} -impl IsAllocableAnyThread for T where +unsafe impl IsAllocableAnyThread for T where T::Mutability: MutabilityIsAllocableAnyThread { } +unsafe impl IsAllocableAnyThread + for ProtocolObject

+{ +} /// Marker trait for classes that are only mutable through `&mut`. /// @@ -339,13 +374,20 @@ impl IsAllocableAnyThread for T where /// Notably, [`InteriorMutable`] does not implement this (though it is /// technically mutable), since it is allowed to mutate through shared /// references. -pub trait IsMutable: ClassType {} +/// +/// +/// # Safety +/// +/// This is a sealed trait, and should not need to be implemented. Open an +/// issue if you know a use-case where this restrition should be lifted! +pub unsafe trait IsMutable: private_traits::Sealed {} trait MutabilityIsMutable: Mutability {} impl MutabilityIsMutable for Mutable {} impl MutabilityIsMutable for MutableWithImmutableSuperclass {} -impl IsMutable for T where T::Mutability: MutabilityIsMutable {} +unsafe impl IsMutable for T where T::Mutability: MutabilityIsMutable {} +unsafe impl IsMutable for ProtocolObject

{} /// Marker trait for classes that are only available on the main thread. /// @@ -354,15 +396,23 @@ impl IsMutable for T where T::Mutability: MutabilityIsMut /// /// Since `MainThreadOnly` types must be `!Send` and `!Sync`, if you hold a /// type that implements this trait, then you're guaranteed to be on the main -/// thread. -// -// Note: MainThreadMarker::from relies on this. -pub trait IsMainThreadOnly: ClassType {} +/// thread (and can get a `MainThreadMarker` using `MainThreadMarker::from`). +/// +/// +/// # Safety +/// +/// This is a sealed trait, and should not need to be implemented. Open an +/// issue if you know a use-case where this restrition should be lifted! +pub unsafe trait IsMainThreadOnly: private_traits::Sealed {} trait MutabilityIsMainThreadOnly: Mutability {} impl MutabilityIsMainThreadOnly for MainThreadOnly {} -impl IsMainThreadOnly for T where T::Mutability: MutabilityIsMainThreadOnly {} +unsafe impl IsMainThreadOnly for T where + T::Mutability: MutabilityIsMainThreadOnly +{ +} +unsafe impl IsMainThreadOnly for ProtocolObject

{} /// Marker trait for classes whose `hash` and `isEqual:` methods are stable. /// @@ -379,9 +429,15 @@ impl IsMainThreadOnly for T where T::Mutability: Mutabili /// and `isEqual:` methods are required to not use external sources like /// thread locals or randomness to determine their result, we can guarantee /// that the hash is stable for these types. +/// +/// +/// # Safety +/// +/// This is a sealed trait, and should not need to be implemented. Open an +/// issue if you know a use-case where this restrition should be lifted! // // TODO: Exclude generic types like `NSArray` from this! -pub trait HasStableHash: ClassType {} +pub unsafe trait HasStableHash: private_traits::Sealed {} trait MutabilityHashIsStable: Mutability {} impl MutabilityHashIsStable for Immutable {} @@ -389,37 +445,57 @@ impl MutabilityHashIsStable for Mutable {} impl MutabilityHashIsStable for ImmutableWithMutableSubclass {} impl MutabilityHashIsStable for MutableWithImmutableSuperclass {} -impl HasStableHash for T where T::Mutability: MutabilityHashIsStable {} +unsafe impl HasStableHash for T where T::Mutability: MutabilityHashIsStable {} +unsafe impl HasStableHash for ProtocolObject

{} /// Retrieve the immutable/mutable counterpart class, and fall back to `Self` /// if not applicable. /// -/// This is mostly used for describing the return type of `NSCopying` and +/// This is used for describing the return type of `NSCopying` and /// `NSMutableCopying`, since due to Rust trait limitations, those two can't -/// have associated types themselves (at least not since we want to use them -/// in `ProtocolObject`). -pub trait CounterpartOrSelf: ClassType { +/// have associated types themselves (since we want to use them in +/// `ProtocolObject`). +/// +/// +/// # Usage notes +/// +/// You may not rely on this being implemented entirely correctly for protocol +/// objects, since we have less type-information available there. +/// +/// In particular, the immutable counterpart of a mutable object converted to +/// `ProtocolObject` may not itself implement the protocol, and +/// invalidly assuming it does is unsound. +/// +/// All of this is to say: Do not use this trait in isolation, either require +/// `NSCopying` or `ClassType` along with it. +/// +/// +/// # Safety +/// +/// This is a sealed trait, and should not need to be implemented. Open an +/// issue if you know a use-case where this restrition should be lifted! +pub unsafe trait CounterpartOrSelf: Message + private_traits::Sealed { /// The immutable counterpart of the type, or `Self` if the type has no /// immutable counterpart. /// /// The implementation for `NSString` has itself (`NSString`) here, while /// `NSMutableString` instead has `NSString`. - type Immutable: ?Sized + ClassType; + type Immutable: ?Sized + Message; /// The mutable counterpart of the type, or `Self` if the type has no /// mutable counterpart. /// /// The implementation for `NSString` has `NSMutableString` here, while /// `NSMutableString` has itself (`NSMutableString`). - type Mutable: ?Sized + ClassType; + type Mutable: ?Sized + Message; } mod private_counterpart { use super::*; pub trait MutabilityCounterpartOrSelf: Mutability { - type Immutable: ?Sized + ClassType; - type Mutable: ?Sized + ClassType; + type Immutable: ?Sized + Message; + type Mutable: ?Sized + Message; } impl> MutabilityCounterpartOrSelf for Root { type Immutable = T; @@ -461,7 +537,7 @@ mod private_counterpart { } } -impl CounterpartOrSelf for T +unsafe impl CounterpartOrSelf for T where T::Mutability: private_counterpart::MutabilityCounterpartOrSelf, { @@ -470,6 +546,27 @@ where type Mutable = >::Mutable; } +unsafe impl CounterpartOrSelf for ProtocolObject

{ + // SAFETY: The only place where this would differ from `Self` is for + // classes with `MutableWithImmutableSuperclass`. + // + // Superclasses are not in general required to implement the same traits + // as their subclasses, but we're not dealing with normal classes, we're + // dealing with with immutable/mutable class counterparts! + // + // We could probably get away with requiring that mutable classes + // only implement the same protocols as their immutable counterparts, but + // for now we relax the requirements of `CounterpartOrSelf`. + type Immutable = Self; + // SAFETY: The only place where this would differ from `Self` is for + // classes with `ImmutableWithMutableSubclass`. + // + // But subclasses are required to always implement the same traits as + // their superclasses, so a mutable subclass is required to implement the + // same traits too. + type Mutable = Self; +} + #[cfg(test)] mod tests { use crate::runtime::NSObject; diff --git a/crates/objc2/src/rc/id.rs b/crates/objc2/src/rc/id.rs index a428982a6..c35af57c2 100644 --- a/crates/objc2/src/rc/id.rs +++ b/crates/objc2/src/rc/id.rs @@ -621,7 +621,7 @@ where } // TODO: Add ?Sized bound -impl Clone for Id { +impl Clone for Id { /// Makes a clone of the shared object. /// /// This increases the object's reference count. @@ -815,12 +815,12 @@ where // // See https://doc.rust-lang.org/1.54.0/src/alloc/boxed.rs.html#1652-1675 // and the `Arc` implementation. -impl Unpin for Id {} +impl Unpin for Id {} -impl RefUnwindSafe for Id {} +impl RefUnwindSafe for Id {} // TODO: Relax this bound -impl UnwindSafe for Id {} +impl UnwindSafe for Id {} #[cfg(test)] mod tests { diff --git a/crates/objc2/src/rc/id_traits.rs b/crates/objc2/src/rc/id_traits.rs index 38db37acc..4ab267273 100644 --- a/crates/objc2/src/rc/id_traits.rs +++ b/crates/objc2/src/rc/id_traits.rs @@ -1,11 +1,10 @@ //! Helper traits for Id. use super::Id; -use crate::mutability::{IsAllocableAnyThread, IsMutable}; +use crate::mutability::IsMutable; /// Helper trait to implement [`Default`] on [`Id`]. -// TODO: Maybe make this `unsafe` and provide a default implementation? -pub trait DefaultId: IsAllocableAnyThread { +pub trait DefaultId { /// The default [`Id`] for a type. /// /// On most objects the implementation would be sending a message to the diff --git a/crates/objc2/src/rc/weak_id.rs b/crates/objc2/src/rc/weak_id.rs index ead36e0bd..253e3112d 100644 --- a/crates/objc2/src/rc/weak_id.rs +++ b/crates/objc2/src/rc/weak_id.rs @@ -116,7 +116,7 @@ impl Drop for WeakId { } // TODO: Add ?Sized -impl Clone for WeakId { +impl Clone for WeakId { /// Make a clone of the weak pointer that points to the same object. #[doc(alias = "objc_copyWeak")] fn clone(&self) -> Self { @@ -130,7 +130,7 @@ impl Clone for WeakId { } // TODO: Add ?Sized -impl Default for WeakId { +impl Default for WeakId { /// Constructs a new weak pointer that doesn't reference any object. /// /// Calling [`Self::load`] on the return value always gives [`None`]. @@ -151,35 +151,35 @@ impl fmt::Debug for WeakId { } // Same as `std::sync::Weak`. -unsafe impl Sync for WeakId {} +unsafe impl Sync for WeakId {} // Same as `std::sync::Weak`. -unsafe impl Send for WeakId {} +unsafe impl Send for WeakId {} // Same as `std::sync::Weak`. impl Unpin for WeakId {} // Same as `std::sync::Weak`. -impl RefUnwindSafe for WeakId {} +impl RefUnwindSafe for WeakId {} // Same as `std::sync::Weak`. -impl UnwindSafe for WeakId {} +impl UnwindSafe for WeakId {} -impl From<&T> for WeakId { +impl From<&T> for WeakId { #[inline] fn from(obj: &T) -> Self { WeakId::new(obj) } } -impl From<&Id> for WeakId { +impl From<&Id> for WeakId { #[inline] fn from(obj: &Id) -> Self { WeakId::from_id(obj) } } -impl From> for WeakId { +impl From> for WeakId { #[inline] fn from(obj: Id) -> Self { WeakId::from_id(&obj) diff --git a/crates/test-ui/ui/mutability_traits_unimplementable.rs b/crates/test-ui/ui/mutability_traits_unimplementable.rs index 8192b1091..96fe63505 100644 --- a/crates/test-ui/ui/mutability_traits_unimplementable.rs +++ b/crates/test-ui/ui/mutability_traits_unimplementable.rs @@ -1,6 +1,4 @@ -//! Check that the `mutability` traits are not implementable manually. -//! -//! Since they are not `unsafe`, it would be a soundness hole if you could. +//! Check that `mutability` traits are not implementable manually. use objc2::runtime::NSObject; use objc2::{declare_class, mutability, ClassType}; @@ -14,6 +12,6 @@ declare_class!( } ); -impl mutability::IsMutable for CustomObject {} +unsafe impl mutability::IsMutable for CustomObject {} fn main() {} diff --git a/crates/test-ui/ui/mutability_traits_unimplementable.stderr b/crates/test-ui/ui/mutability_traits_unimplementable.stderr index a64dc8752..8f857db41 100644 --- a/crates/test-ui/ui/mutability_traits_unimplementable.stderr +++ b/crates/test-ui/ui/mutability_traits_unimplementable.stderr @@ -1,8 +1,8 @@ error[E0119]: conflicting implementations of trait `IsMutable` for type `CustomObject` --> ui/mutability_traits_unimplementable.rs | - | impl mutability::IsMutable for CustomObject {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | unsafe impl mutability::IsMutable for CustomObject {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: conflicting implementation in crate `objc2`: - impl IsMutable for T diff --git a/crates/test-ui/ui/mutability_traits_unimplementable2.rs b/crates/test-ui/ui/mutability_traits_unimplementable2.rs new file mode 100644 index 000000000..d75828407 --- /dev/null +++ b/crates/test-ui/ui/mutability_traits_unimplementable2.rs @@ -0,0 +1,8 @@ +//! Check that `mutability` traits are not implementable manually. +use objc2::mutability; + +struct CustomStruct; + +unsafe impl mutability::IsMutable for CustomStruct {} + +fn main() {} diff --git a/crates/test-ui/ui/mutability_traits_unimplementable2.stderr b/crates/test-ui/ui/mutability_traits_unimplementable2.stderr new file mode 100644 index 000000000..a73104fe5 --- /dev/null +++ b/crates/test-ui/ui/mutability_traits_unimplementable2.stderr @@ -0,0 +1,17 @@ +error[E0277]: the trait bound `CustomStruct: ClassType` is not satisfied + --> ui/mutability_traits_unimplementable2.rs + | + | unsafe impl mutability::IsMutable for CustomStruct {} + | ^^^^^^^^^^^^ the trait `ClassType` is not implemented for `CustomStruct` + | + = help: the following other types implement trait `ClassType`: + __RcTestObject + NSObject + __NSProxy + = note: required for `CustomStruct` to implement `mutability::private_traits::Sealed` +note: required by a bound in `IsMutable` + --> $WORKSPACE/crates/objc2/src/mutability.rs + | + | pub unsafe trait IsMutable: private_traits::Sealed {} + | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `IsMutable` + = note: `IsMutable` is a "sealed trait", because to implement it you also need to implement `objc2::mutability::private_traits::Sealed`, which is not accessible; this is usually done to force you to use one of the provided types that already implement it diff --git a/crates/test-ui/ui/mutable_id_not_clone_not_retain.stderr b/crates/test-ui/ui/mutable_id_not_clone_not_retain.stderr index 0ab547688..0a3019975 100644 --- a/crates/test-ui/ui/mutable_id_not_clone_not_retain.stderr +++ b/crates/test-ui/ui/mutable_id_not_clone_not_retain.stderr @@ -24,8 +24,8 @@ error[E0599]: the method `clone` exists for struct `Id`, but it note: the trait `IsIdCloneable` must be implemented --> $WORKSPACE/crates/objc2/src/mutability.rs | - | pub trait IsIdCloneable: ClassType {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | pub unsafe trait IsIdCloneable: private_traits::Sealed {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `Mutable: mutability::MutabilityIsRetainable` is not satisfied --> ui/mutable_id_not_clone_not_retain.rs