Skip to content

Commit

Permalink
Make objc2-foundation work with unsized types
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Dec 22, 2021
1 parent 8839f20 commit 048b72f
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 58 deletions.
3 changes: 2 additions & 1 deletion objc2-foundation/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Changed
* **BREAKING**: Made some creation methods a bit less generic (e.g.
`INSDictionary::from_keys_and_objects` now always returns `Id<_, Shared>`).
* Relax bounds on generic `INSObject` impls.
* Relaxed bounds on generic `INSObject` impls.
* Relaxed `Sized` bounds on generic objects like `NSArray`.

### Removed
* **BREAKING**: Removed associated `Ownership` type from `INSObject`; instead,
Expand Down
50 changes: 25 additions & 25 deletions objc2-foundation/src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ unsafe fn from_refs<A: INSArray + ?Sized>(refs: &[&A::Item]) -> Id<A, A::Ownersh

pub unsafe trait INSArray: INSObject {
type Ownership: Ownership;
type Item: INSObject;
type Item: INSObject + ?Sized;
type ItemOwnership: Ownership;

unsafe_def_fn!(fn new -> Self::Ownership);
Expand Down Expand Up @@ -169,7 +169,7 @@ object!(
/// TODO: Can we make it impossible? Should we?
///
/// What about `Id<NSArray<T, Shared>, Owned>`?
unsafe pub struct NSArray<T, O: Ownership> {
unsafe pub struct NSArray<T: ?Sized, O: Ownership> {
item: PhantomData<Id<T, O>>,
}
);
Expand All @@ -179,22 +179,22 @@ object!(
// The `PhantomData` can't get these impls to display in the docs.
//
// TODO: Properly verify this
unsafe impl<T: Sync + Send> Sync for NSArray<T, Shared> {}
unsafe impl<T: Sync + Send> Send for NSArray<T, Shared> {}
unsafe impl<T: Sync> Sync for NSArray<T, Owned> {}
unsafe impl<T: Send> Send for NSArray<T, Owned> {}
unsafe impl<T: Sync + Send + ?Sized> Sync for NSArray<T, Shared> {}
unsafe impl<T: Sync + Send + ?Sized> Send for NSArray<T, Shared> {}
unsafe impl<T: Sync + ?Sized> Sync for NSArray<T, Owned> {}
unsafe impl<T: Send + ?Sized> Send for NSArray<T, Owned> {}

/// ```compile_fail
/// use objc2::rc::Shared;
/// use objc2::runtime::Object;
/// use objc2_foundation::NSArray;
/// fn needs_send_sync<T: Send + Sync>() {}
/// fn needs_send_sync<T: Send + Sync + ?Sized>() {}
/// needs_send_sync::<NSArray<Object, Shared>>();
/// ```
#[cfg(doctest)]
pub struct NSArrayWithObjectNotSendSync;

unsafe impl<T: INSObject, O: Ownership> INSArray for NSArray<T, O> {
unsafe impl<T: INSObject + ?Sized, O: Ownership> INSArray for NSArray<T, O> {
/// The `NSArray` itself (length and number of items) is always immutable,
/// but we would like to know when we're the only owner of the array, to
/// allow mutation of the array's items.
Expand All @@ -208,20 +208,20 @@ unsafe impl<T: INSObject, O: Ownership> INSArray for NSArray<T, O> {

// Copying only possible when ItemOwnership = Shared

unsafe impl<T: INSObject> INSCopying for NSArray<T, Shared> {
unsafe impl<T: INSObject + ?Sized> INSCopying for NSArray<T, Shared> {
type Ownership = Shared;
type Output = NSArray<T, Shared>;
}

unsafe impl<T: INSObject> INSMutableCopying for NSArray<T, Shared> {
unsafe impl<T: INSObject + ?Sized> INSMutableCopying for NSArray<T, Shared> {
type Output = NSMutableArray<T, Shared>;
}

unsafe impl<T: INSObject, O: Ownership> INSFastEnumeration for NSArray<T, O> {
unsafe impl<T: INSObject + ?Sized, O: Ownership> INSFastEnumeration for NSArray<T, O> {
type Item = T;
}

impl<T: INSObject, O: Ownership> Index<usize> for NSArray<T, O> {
impl<T: INSObject + ?Sized, O: Ownership> Index<usize> for NSArray<T, O> {
type Output = T;

fn index(&self, index: usize) -> &T {
Expand Down Expand Up @@ -306,7 +306,7 @@ pub unsafe trait INSMutableArray: INSArray {
where
F: FnMut(&Self::Item, &Self::Item) -> Ordering,
{
extern "C" fn compare_with_closure<T, F: FnMut(&T, &T) -> Ordering>(
extern "C" fn compare_with_closure<T: ?Sized, F: FnMut(&T, &T) -> Ordering>(
obj1: &T,
obj2: &T,
context: *mut c_void,
Expand Down Expand Up @@ -334,43 +334,43 @@ pub unsafe trait INSMutableArray: INSArray {
}

object!(
unsafe pub struct NSMutableArray<T, O: Ownership> {
unsafe pub struct NSMutableArray<T: ?Sized, O: Ownership> {
item: PhantomData<Id<T, O>>,
}
);

// SAFETY: Same as NSArray.
//
// TODO: Properly verify this
unsafe impl<T: Sync + Send> Sync for NSMutableArray<T, Shared> {}
unsafe impl<T: Sync + Send> Send for NSMutableArray<T, Shared> {}
unsafe impl<T: Sync> Sync for NSMutableArray<T, Owned> {}
unsafe impl<T: Send> Send for NSMutableArray<T, Owned> {}
unsafe impl<T: Sync + Send + ?Sized> Sync for NSMutableArray<T, Shared> {}
unsafe impl<T: Sync + Send + ?Sized> Send for NSMutableArray<T, Shared> {}
unsafe impl<T: Sync + ?Sized> Sync for NSMutableArray<T, Owned> {}
unsafe impl<T: Send + ?Sized> Send for NSMutableArray<T, Owned> {}

unsafe impl<T: INSObject, O: Ownership> INSArray for NSMutableArray<T, O> {
unsafe impl<T: INSObject + ?Sized, O: Ownership> INSArray for NSMutableArray<T, O> {
type Ownership = Owned;
type Item = T;
type ItemOwnership = O;
}

unsafe impl<T: INSObject, O: Ownership> INSMutableArray for NSMutableArray<T, O> {}
unsafe impl<T: INSObject + ?Sized, O: Ownership> INSMutableArray for NSMutableArray<T, O> {}

// Copying only possible when ItemOwnership = Shared

unsafe impl<T: INSObject> INSCopying for NSMutableArray<T, Shared> {
unsafe impl<T: INSObject + ?Sized> INSCopying for NSMutableArray<T, Shared> {
type Ownership = Shared;
type Output = NSArray<T, Shared>;
}

unsafe impl<T: INSObject> INSMutableCopying for NSMutableArray<T, Shared> {
unsafe impl<T: INSObject + ?Sized> INSMutableCopying for NSMutableArray<T, Shared> {
type Output = NSMutableArray<T, Shared>;
}

unsafe impl<T: INSObject, O: Ownership> INSFastEnumeration for NSMutableArray<T, O> {
unsafe impl<T: INSObject + ?Sized, O: Ownership> INSFastEnumeration for NSMutableArray<T, O> {
type Item = T;
}

impl<T: INSObject, O: Ownership> Index<usize> for NSMutableArray<T, O> {
impl<T: INSObject + ?Sized, O: Ownership> Index<usize> for NSMutableArray<T, O> {
type Output = T;

fn index(&self, index: usize) -> &T {
Expand Down Expand Up @@ -571,7 +571,7 @@ mod tests {

#[test]
fn test_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
fn assert_send_sync<T: Send + Sync + ?Sized>() {}

assert_send_sync::<NSArray<NSString, Shared>>();
assert_send_sync::<NSMutableArray<NSString, Shared>>();
Expand Down
4 changes: 2 additions & 2 deletions objc2-foundation/src/copying.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub unsafe trait INSCopying: INSObject {
///
/// This is usually `Self`, but e.g. `NSMutableString` returns `NSString`.
/// TODO: Verify???
type Output: INSObject;
type Output: INSObject + ?Sized;

fn copy(&self) -> Id<Self::Output, Self::Ownership> {
unsafe {
Expand All @@ -47,7 +47,7 @@ pub unsafe trait INSCopying: INSObject {
/// Note that the `mutableCopy` selector must return an owned object!
pub unsafe trait INSMutableCopying: INSObject {
/// TODO
type Output: INSObject;
type Output: INSObject + ?Sized;

fn mutable_copy(&self) -> Id<Self::Output, Owned> {
unsafe {
Expand Down
20 changes: 11 additions & 9 deletions objc2-foundation/src/dictionary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ where
}

pub unsafe trait INSDictionary: INSObject {
type Key: INSObject;
type Value: INSObject;
type Key: INSObject + ?Sized;
type Value: INSObject + ?Sized;
type ValueOwnership: Ownership;

#[doc(alias = "count")]
Expand Down Expand Up @@ -139,31 +139,33 @@ pub unsafe trait INSDictionary: INSObject {
}

object!(
unsafe pub struct NSDictionary<K, V> {
unsafe pub struct NSDictionary<K: ?Sized, V: ?Sized> {
key: PhantomData<Id<K, Shared>>,
obj: PhantomData<Id<V, Owned>>,
}
);

// TODO: SAFETY
unsafe impl<K: Sync + Send, V: Sync> Sync for NSDictionary<K, V> {}
unsafe impl<K: Sync + Send, V: Send> Send for NSDictionary<K, V> {}
unsafe impl<K: Sync + Send + ?Sized, V: Sync + ?Sized> Sync for NSDictionary<K, V> {}
unsafe impl<K: Sync + Send + ?Sized, V: Send + ?Sized> Send for NSDictionary<K, V> {}

impl<K: INSObject, V: INSObject> NSDictionary<K, V> {
impl<K: INSObject + ?Sized, V: INSObject + ?Sized> NSDictionary<K, V> {
unsafe_def_fn!(pub fn new -> Shared);
}

unsafe impl<K: INSObject, V: INSObject> INSDictionary for NSDictionary<K, V> {
unsafe impl<K: INSObject + ?Sized, V: INSObject + ?Sized> INSDictionary for NSDictionary<K, V> {
type Key = K;
type Value = V;
type ValueOwnership = Owned;
}

unsafe impl<K: INSObject, V: INSObject> INSFastEnumeration for NSDictionary<K, V> {
unsafe impl<K: INSObject + ?Sized, V: INSObject + ?Sized> INSFastEnumeration
for NSDictionary<K, V>
{
type Item = K;
}

impl<'a, K: INSObject, V: INSObject> Index<&'a K> for NSDictionary<K, V> {
impl<'a, K: INSObject + ?Sized, V: INSObject + ?Sized> Index<&'a K> for NSDictionary<K, V> {
type Output = V;

fn index(&self, index: &K) -> &V {
Expand Down
21 changes: 13 additions & 8 deletions objc2-foundation/src/enumerator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ use objc2::{msg_send, Encode, Encoding, RefEncode};

use super::INSObject;

pub struct NSEnumerator<'a, T: INSObject> {
pub struct NSEnumerator<'a, T: INSObject + ?Sized> {
id: Id<Object, Owned>,
item: PhantomData<&'a T>,
}

impl<'a, T: INSObject> NSEnumerator<'a, T> {
impl<'a, T: INSObject + ?Sized> NSEnumerator<'a, T> {
/// TODO
///
/// # Safety
Expand All @@ -33,7 +33,7 @@ impl<'a, T: INSObject> NSEnumerator<'a, T> {
}
}

impl<'a, T: INSObject> Iterator for NSEnumerator<'a, T> {
impl<'a, T: INSObject + ?Sized> Iterator for NSEnumerator<'a, T> {
type Item = &'a T;

fn next(&mut self) -> Option<&'a T> {
Expand All @@ -42,15 +42,15 @@ impl<'a, T: INSObject> Iterator for NSEnumerator<'a, T> {
}

pub unsafe trait INSFastEnumeration: INSObject {
type Item: INSObject;
type Item: INSObject + ?Sized;

fn enumerator(&self) -> NSFastEnumerator<'_, Self> {
NSFastEnumerator::new(self)
}
}

#[repr(C)]
struct NSFastEnumerationState<T: INSObject> {
struct NSFastEnumerationState<T: INSObject + ?Sized> {
state: c_ulong, // TODO: Verify this is actually always 64 bit
items_ptr: *const *const T,
mutations_ptr: *mut c_ulong,
Expand Down Expand Up @@ -78,7 +78,7 @@ const U_LONG_ENCODING: Encoding<'static> = {
}
};

unsafe impl<T: INSObject> Encode for NSFastEnumerationState<T> {
unsafe impl<T: INSObject + ?Sized> Encode for NSFastEnumerationState<T> {
const ENCODING: Encoding<'static> = Encoding::Struct(
"?",
&[
Expand All @@ -90,7 +90,7 @@ unsafe impl<T: INSObject> Encode for NSFastEnumerationState<T> {
);
}

unsafe impl<T: INSObject> RefEncode for NSFastEnumerationState<T> {
unsafe impl<T: INSObject + ?Sized> RefEncode for NSFastEnumerationState<T> {
const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
}

Expand Down Expand Up @@ -131,14 +131,19 @@ pub struct NSFastEnumerator<'a, C: 'a + INSFastEnumeration + ?Sized> {

impl<'a, C: INSFastEnumeration + ?Sized> NSFastEnumerator<'a, C> {
fn new(object: &'a C) -> Self {
// SAFETY: C::Item is ?Sized, but it should always be a thin pointer.
// This is just a way to get a null pointer because we can't apply the
// correct ptr::Thin bound yet.
let null_ptr = unsafe { mem::zeroed::<*const C::Item>() };
Self {
object,

ptr: ptr::null(),
end: ptr::null(),

state: unsafe { mem::zeroed() },
buf: [ptr::null(); FAST_ENUM_BUF_SIZE],
// SAFETY:
buf: [null_ptr; FAST_ENUM_BUF_SIZE],
}
}

Expand Down
18 changes: 9 additions & 9 deletions objc2-foundation/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,25 @@ macro_rules! object {
};
(
$(#[$m:meta])*
unsafe $v:vis struct $name:ident<$($t:ident $(: $b:ident)?),*> {
unsafe $v:vis struct $name:ident<$($t:ident $(: $b:ident)? $(: ?$sized:ident)?),*> {
$($p:ident: $pty:ty,)*
}
) => {
// TODO: `extern type`
$(#[$m])*
#[repr(C)]
$v struct $name<$($t $(: $b)?),*> {
$v struct $name<$($t $(: $b)? $(: ?$sized)?),*> {
_private: [u8; 0],
$($p: $pty),*
}

unsafe impl<$($t $(: $b)?),*> ::objc2::Message for $name<$($t),*> { }
unsafe impl<$($t $(: $b)? $(: ?$sized)?),*> ::objc2::Message for $name<$($t),*> { }

unsafe impl<$($t $(: $b)?),*> ::objc2::RefEncode for $name<$($t),*> {
unsafe impl<$($t $(: $b)? $(: ?$sized)?),*> ::objc2::RefEncode for $name<$($t),*> {
const ENCODING_REF: ::objc2::Encoding<'static> = ::objc2::Encoding::Object;
}

unsafe impl<$($t $(: $b)?),*> $crate::INSObject for $name<$($t),*> {
unsafe impl<$($t $(: $b)? $(: ?$sized)?),*> $crate::INSObject for $name<$($t),*> {
fn class() -> &'static ::objc2::runtime::Class {
::objc2::class!($name)
}
Expand All @@ -49,7 +49,7 @@ macro_rules! object {
// (instead of shallow) equality comparisons.
//
// See also https://nshipster.com/equality/
impl<$($t: ::core::cmp::PartialEq $(+ $b)?),*> ::core::cmp::PartialEq for $name<$($t),*> {
impl<$($t: ::core::cmp::PartialEq $(+ $b)? $(+ ?$sized)?),*> ::core::cmp::PartialEq for $name<$($t),*> {
#[inline]
fn eq(&self, other: &Self) -> bool {
use $crate::INSObject;
Expand All @@ -61,15 +61,15 @@ macro_rules! object {
//
// `T: Eq` bound added to prevent e.g. `NSValue<f32>` from being `Eq`
// (even though `[NAN isEqual: NAN]` is true in Objective-C).
impl<$($t: ::core::cmp::Eq $(+ $b)?),*> ::core::cmp::Eq for $name<$($t),*> {}
impl<$($t: ::core::cmp::Eq $(+ $b)? $(+ ?$sized)?),*> ::core::cmp::Eq for $name<$($t),*> {}

// Hashing in Objective-C has the exact same requirement as in Rust:
//
// > If two objects are equal (as determined by the isEqual: method),
// > they must have the same hash value.
//
// See https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418859-hash
impl<$($t: ::core::hash::Hash $(+ $b)?),*> ::core::hash::Hash for $name<$($t),*> {
impl<$($t: ::core::hash::Hash $(+ $b)? $(+ ?$sized)?),*> ::core::hash::Hash for $name<$($t),*> {
#[inline]
fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
use $crate::INSObject;
Expand All @@ -78,7 +78,7 @@ macro_rules! object {
}

// TODO: Consider T: Debug bound
impl<$($t $(: $b)?),*> ::core::fmt::Debug for $name<$($t),*> {
impl<$($t $(: $b)? $(: ?$sized)?),*> ::core::fmt::Debug for $name<$($t),*> {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
use $crate::{INSObject, INSString};
::objc2::rc::autoreleasepool(|pool| {
Expand Down
6 changes: 3 additions & 3 deletions objc2-foundation/src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub unsafe trait INSObject: Message {
unsafe { msg_send![self, hash] }
}

fn is_equal<T: INSObject>(&self, other: &T) -> bool {
fn is_equal<T: INSObject + ?Sized>(&self, other: &T) -> bool {
let result: Bool = unsafe { msg_send![self, isEqual: other] };
result.is_true()
}
Expand All @@ -40,12 +40,12 @@ object!(unsafe pub struct NSObject<> {

/// ```compile_fail
/// use objc2_foundation::NSObject;
/// fn needs_sync<T: Sync>() {}
/// fn needs_sync<T: Sync + ?Sized>() {}
/// needs_sync::<NSObject>();
/// ```
/// ```compile_fail
/// use objc2_foundation::NSObject;
/// fn needs_send<T: Send>() {}
/// fn needs_send<T: Send + ?Sized>() {}
/// needs_send::<NSObject>();
/// ```
#[cfg(doctest)]
Expand Down
Loading

0 comments on commit 048b72f

Please sign in to comment.