From 103debe3881585cb817bbe5ee9fa5a18c6a1836b Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 5 Jul 2022 23:46:26 +0200 Subject: [PATCH 1/3] Move common trait impls from `object!` macro to being derived instead --- objc2-foundation/CHANGELOG.md | 3 ++ objc2-foundation/src/array.rs | 4 ++ objc2-foundation/src/attributed_string.rs | 1 + objc2-foundation/src/data.rs | 2 + objc2-foundation/src/dictionary.rs | 1 + objc2-foundation/src/exception.rs | 25 ++++++--- objc2-foundation/src/macros.rs | 54 +------------------ .../src/mutable_attributed_string.rs | 1 + objc2-foundation/src/mutable_string.rs | 7 +++ objc2-foundation/src/object.rs | 39 ++++++++++++++ objc2-foundation/src/process_info.rs | 1 + objc2-foundation/src/string.rs | 13 ++++- objc2-foundation/src/thread.rs | 1 + objc2-foundation/src/uuid.rs | 1 + objc2-foundation/src/value.rs | 3 ++ 15 files changed, 94 insertions(+), 62 deletions(-) diff --git a/objc2-foundation/CHANGELOG.md b/objc2-foundation/CHANGELOG.md index 2bce3a57a..9abc4b643 100644 --- a/objc2-foundation/CHANGELOG.md +++ b/objc2-foundation/CHANGELOG.md @@ -11,6 +11,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). safe to call on the main thread. * Added `NSException` object. +### Changed +* Changed a few `Debug` impls. + ## 0.2.0-alpha.5 - 2022-06-13 diff --git a/objc2-foundation/src/array.rs b/objc2-foundation/src/array.rs index aba3741c2..86fc1735d 100644 --- a/objc2-foundation/src/array.rs +++ b/objc2-foundation/src/array.rs @@ -25,6 +25,9 @@ object! { /// TODO: Can we make it impossible? Should we? /// /// What about `Id, Owned>`? + // `T: PartialEq` bound correct because `NSArray` does deep (instead of + // shallow) equality comparisons. + #[derive(Debug, PartialEq, Eq, Hash)] unsafe pub struct NSArray: NSObject { item: PhantomData>, } @@ -43,6 +46,7 @@ unsafe impl Send for NSArray {} object! { // TODO: Ensure that this deref to NSArray is safe! // This "inherits" NSArray, and has the same `Send`/`Sync` impls as that. + #[derive(Debug, PartialEq, Eq, Hash)] unsafe pub struct NSMutableArray: NSArray, NSObject {} } diff --git a/objc2-foundation/src/attributed_string.rs b/objc2-foundation/src/attributed_string.rs index 57e779543..55846cf86 100644 --- a/objc2-foundation/src/attributed_string.rs +++ b/objc2-foundation/src/attributed_string.rs @@ -19,6 +19,7 @@ object! { /// framework contains most of the extension methods. /// /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsattributedstring?language=objc). + #[derive(Debug, PartialEq, Eq, Hash)] unsafe pub struct NSAttributedString: NSObject; } diff --git a/objc2-foundation/src/data.rs b/objc2-foundation/src/data.rs index 846f80278..3ee61f8db 100644 --- a/objc2-foundation/src/data.rs +++ b/objc2-foundation/src/data.rs @@ -17,6 +17,7 @@ object! { /// This is similar to a [`slice`][`prim@slice`] of [`u8`]. /// /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsdata?language=objc). + #[derive(Debug, PartialEq, Eq, Hash)] unsafe pub struct NSData: NSObject; } @@ -32,6 +33,7 @@ object! { /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsmutabledata?language=objc). /// /// [`Vec`]: std::vec::Vec + #[derive(Debug, PartialEq, Eq, Hash)] unsafe pub struct NSMutableData: NSData, NSObject; } diff --git a/objc2-foundation/src/dictionary.rs b/objc2-foundation/src/dictionary.rs index fd15bfef7..2a6f8a767 100644 --- a/objc2-foundation/src/dictionary.rs +++ b/objc2-foundation/src/dictionary.rs @@ -10,6 +10,7 @@ use objc2::{msg_send, msg_send_id, Message}; use super::{NSArray, NSCopying, NSEnumerator, NSFastEnumeration, NSObject}; object! { + #[derive(Debug, PartialEq, Eq, Hash)] unsafe pub struct NSDictionary: NSObject { key: PhantomData>, obj: PhantomData>, diff --git a/objc2-foundation/src/exception.rs b/objc2-foundation/src/exception.rs index 9cf126e9e..1e8d758db 100644 --- a/objc2-foundation/src/exception.rs +++ b/objc2-foundation/src/exception.rs @@ -1,3 +1,4 @@ +use core::fmt; use core::hint::unreachable_unchecked; use core::panic::{RefUnwindSafe, UnwindSafe}; @@ -18,6 +19,7 @@ object! { /// See also [Apple's documentation][doc]. /// /// [doc]: https://developer.apple.com/documentation/foundation/nsexception?language=objc + #[derive(PartialEq, Eq, Hash)] unsafe pub struct NSException: NSObject; } @@ -120,7 +122,18 @@ impl alloc::borrow::ToOwned for NSException { } } -// TODO: Better Debug impl +impl fmt::Debug for NSException { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let obj: &Object = self.as_ref(); + write!(f, "{:?} '{}'", obj, self.name())?; + if let Some(reason) = self.reason() { + write!(f, " reason:{}", reason)?; + } else { + write!(f, " reason:(NULL)")?; + } + Ok(()) + } +} #[cfg(test)] mod tests { @@ -148,15 +161,13 @@ mod tests { }; assert_eq!(exc.description(), NSString::from_str(&description)); - assert_eq!(format!("{:?}", exc), format!("\"{}\"", description)); + + let debug = format!(" 'abc' reason:def", exc); + assert_eq!(format!("{:?}", exc), debug); } #[test] - #[cfg_attr( - feature = "apple", - should_panic = "called `Result::unwrap()` on an `Err` value: \"def\"" - )] - #[cfg_attr(feature = "gnustep-1-7", should_panic = "> NAME:abc REASON:def")] + #[should_panic = "'abc' reason:def"] fn unwrap() { let exc = NSException::new( &NSString::from_str("abc"), diff --git a/objc2-foundation/src/macros.rs b/objc2-foundation/src/macros.rs index 5ae7df445..74e7dde50 100644 --- a/objc2-foundation/src/macros.rs +++ b/objc2-foundation/src/macros.rs @@ -99,7 +99,7 @@ macro_rules! object { } impl<$($t $(: $b)?),*> $name<$($t),*> { - pub fn class() -> &'static ::objc2::runtime::Class { + $v fn class() -> &'static ::objc2::runtime::Class { ::objc2::class!($name) } } @@ -164,58 +164,6 @@ macro_rules! object { } __impl_as_ref_borrow!($name<$($t $(: $b)?),*>, $inherits, $($inheritance_rest,)*); - - // Objective-C equality has approximately the same semantics as Rust - // equality (although less aptly specified). - // - // At the very least, equality is _expected_ to be symmetric and - // transitive, and that's about the best we can do. - // - // `T: PartialEq` bound added because e.g. `NSArray` does deep - // (instead of shallow) equality comparisons. - // - // See also https://nshipster.com/equality/ - impl<$($t: ::core::cmp::PartialEq $(+ $b)?),*> ::core::cmp::PartialEq for $name<$($t),*> { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.is_equal(other.as_ref()) - } - } - - // Most types' equality is reflexive. - // - // `T: Eq` bound added to prevent e.g. `NSValue` 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),*> {} - - // 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),*> { - #[inline] - fn hash(&self, state: &mut H) { - self.hash_code().hash(state); - } - } - - // TODO: Consider T: Debug bound - impl<$($t $(: $b)?),*> ::core::fmt::Debug for $name<$($t),*> { - fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - use ::alloc::borrow::ToOwned; - use $crate::NSObject; - // "downgrading" to NSObject and calling `to_owned` to work - // around `f` and Self not being AutoreleaseSafe. - // TODO: Fix this! - let this: &NSObject = self.as_ref(); - let s = ::objc2::rc::autoreleasepool(|pool| { - this.description().as_str(pool).to_owned() - }); - ::core::fmt::Debug::fmt(&s, f) - } - } }; } diff --git a/objc2-foundation/src/mutable_attributed_string.rs b/objc2-foundation/src/mutable_attributed_string.rs index ef6c1635c..2be81c0e4 100644 --- a/objc2-foundation/src/mutable_attributed_string.rs +++ b/objc2-foundation/src/mutable_attributed_string.rs @@ -7,6 +7,7 @@ object! { /// A mutable string that has associated attributes. /// /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsmutableattributedstring?language=objc). + #[derive(Debug, PartialEq, Eq, Hash)] unsafe pub struct NSMutableAttributedString: NSAttributedString, NSObject; } diff --git a/objc2-foundation/src/mutable_string.rs b/objc2-foundation/src/mutable_string.rs index aa7b18fc0..5b8ab0bf1 100644 --- a/objc2-foundation/src/mutable_string.rs +++ b/objc2-foundation/src/mutable_string.rs @@ -12,6 +12,7 @@ object! { /// A dynamic plain-text Unicode string object. /// /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsmutablestring?language=objc). + #[derive(PartialEq, Eq, Hash)] unsafe pub struct NSMutableString: NSString, NSObject; } @@ -166,6 +167,12 @@ impl fmt::Display for NSMutableString { } } +impl fmt::Debug for NSMutableString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + #[cfg(test)] mod tests { use alloc::string::ToString; diff --git a/objc2-foundation/src/object.rs b/objc2-foundation/src/object.rs index d2b45e437..28071bd53 100644 --- a/objc2-foundation/src/object.rs +++ b/objc2-foundation/src/object.rs @@ -1,3 +1,6 @@ +use core::fmt; +use core::hash; + use objc2::rc::{DefaultId, Id, Owned, Shared}; use objc2::runtime::{Class, Object}; use objc2::{msg_send, msg_send_bool, msg_send_id}; @@ -30,6 +33,42 @@ impl NSObject { } } +/// Objective-C equality has approximately the same semantics as Rust +/// equality (although less aptly specified). +/// +/// At the very least, equality is _expected_ to be symmetric and +/// transitive, and that's about the best we can do. +/// +/// See also +impl PartialEq for NSObject { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.is_equal(other) + } +} + +/// Most types' equality is reflexive. +impl Eq for NSObject {} + +/// 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 +impl hash::Hash for NSObject { + #[inline] + fn hash(&self, state: &mut H) { + self.hash_code().hash(state); + } +} + +impl fmt::Debug for NSObject { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.description(), f) + } +} + impl DefaultId for NSObject { type Ownership = Owned; diff --git a/objc2-foundation/src/process_info.rs b/objc2-foundation/src/process_info.rs index 696d4dc65..abec6911a 100644 --- a/objc2-foundation/src/process_info.rs +++ b/objc2-foundation/src/process_info.rs @@ -7,6 +7,7 @@ object! { /// A collection of information about the current process. /// /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsprocessinfo?language=objc). + #[derive(Debug, PartialEq, Eq, Hash)] unsafe pub struct NSProcessInfo: NSObject; // TODO: This contains a lot more important functionality! diff --git a/objc2-foundation/src/string.rs b/objc2-foundation/src/string.rs index abcca0e20..40bddb398 100644 --- a/objc2-foundation/src/string.rs +++ b/objc2-foundation/src/string.rs @@ -31,12 +31,11 @@ object! { /// A static, plain-text Unicode string object. /// /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsstring?language=objc). + #[derive(PartialEq, Eq, Hash)] unsafe pub struct NSString: NSObject; // TODO: Use isEqualToString: for comparison (instead of just isEqual:) // The former is more performant - // TODO: Use more performant Debug implementation. - // TODO: Check if performance of NSSelectorFromString is worthwhile } @@ -282,6 +281,16 @@ impl fmt::Display for NSString { } } +impl fmt::Debug for NSString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // The call to `to_owned` is unfortunate, but is required to work + // around `f` not being AutoreleaseSafe. + // TODO: Fix this! + let s = autoreleasepool(|pool| self.as_str(pool).to_owned()); + fmt::Debug::fmt(&s, f) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/objc2-foundation/src/thread.rs b/objc2-foundation/src/thread.rs index 869fd23f0..df3e9ce17 100644 --- a/objc2-foundation/src/thread.rs +++ b/objc2-foundation/src/thread.rs @@ -9,6 +9,7 @@ object! { /// A thread of execution. /// /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsthread?language=objc). + #[derive(Debug, PartialEq, Eq, Hash)] unsafe pub struct NSThread: NSObject; } diff --git a/objc2-foundation/src/uuid.rs b/objc2-foundation/src/uuid.rs index f1c4ae363..20b1c8181 100644 --- a/objc2-foundation/src/uuid.rs +++ b/objc2-foundation/src/uuid.rs @@ -9,6 +9,7 @@ object! { /// Can be used to identify types, interfaces, and other items. /// /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsuuid?language=objc). + #[derive(Debug, PartialEq, Eq, Hash)] unsafe pub struct NSUUID: NSObject; } diff --git a/objc2-foundation/src/value.rs b/objc2-foundation/src/value.rs index 9213139b7..ce9b49326 100644 --- a/objc2-foundation/src/value.rs +++ b/objc2-foundation/src/value.rs @@ -15,6 +15,9 @@ use objc2::{msg_send, msg_send_id}; use super::{NSCopying, NSObject}; object! { + // `T: Eq` bound correct to prevent `NSValue` from being `Eq` + // (even though `[NAN isEqual: NAN]` is true in Objective-C). + #[derive(Debug, PartialEq, Eq, Hash)] unsafe pub struct NSValue: NSObject { value: PhantomData, } From b5eb1ed995bd7e85bb3278d905aecf21120674b3 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 6 Jul 2022 01:41:45 +0200 Subject: [PATCH 2/3] Expose and document object! macro --- objc2-foundation/CHANGELOG.md | 2 + objc2-foundation/src/array.rs | 4 +- objc2-foundation/src/dictionary.rs | 2 +- objc2-foundation/src/lib.rs | 6 ++ objc2-foundation/src/macros.rs | 167 ++++++++++++++++++++++------- objc2-foundation/src/object.rs | 2 +- objc2-foundation/src/value.rs | 2 +- tests/ui/nsvalue_f32_not_eq.stderr | 8 +- 8 files changed, 144 insertions(+), 49 deletions(-) diff --git a/objc2-foundation/CHANGELOG.md b/objc2-foundation/CHANGELOG.md index 9abc4b643..65e696684 100644 --- a/objc2-foundation/CHANGELOG.md +++ b/objc2-foundation/CHANGELOG.md @@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added `MainThreadMarker` to help with designing APIs where a method is only safe to call on the main thread. * Added `NSException` object. +* Added `object!` macro to help with defining other classes. +* Expose the `objc2` version that this uses in the crate root. ### Changed * Changed a few `Debug` impls. diff --git a/objc2-foundation/src/array.rs b/objc2-foundation/src/array.rs index 86fc1735d..1f86f5e15 100644 --- a/objc2-foundation/src/array.rs +++ b/objc2-foundation/src/array.rs @@ -14,7 +14,7 @@ use super::{ NSRange, }; -object! { +__inner_object! { /// TODO /// /// You can have a `Id, Owned>`, which allows mutable access @@ -43,7 +43,7 @@ unsafe impl Send for NSArray {} unsafe impl Sync for NSArray {} unsafe impl Send for NSArray {} -object! { +__inner_object! { // TODO: Ensure that this deref to NSArray is safe! // This "inherits" NSArray, and has the same `Send`/`Sync` impls as that. #[derive(Debug, PartialEq, Eq, Hash)] diff --git a/objc2-foundation/src/dictionary.rs b/objc2-foundation/src/dictionary.rs index 2a6f8a767..c8f47f454 100644 --- a/objc2-foundation/src/dictionary.rs +++ b/objc2-foundation/src/dictionary.rs @@ -9,7 +9,7 @@ use objc2::{msg_send, msg_send_id, Message}; use super::{NSArray, NSCopying, NSEnumerator, NSFastEnumeration, NSObject}; -object! { +__inner_object! { #[derive(Debug, PartialEq, Eq, Hash)] unsafe pub struct NSDictionary: NSObject { key: PhantomData>, diff --git a/objc2-foundation/src/lib.rs b/objc2-foundation/src/lib.rs index deed1b0a3..174fb91fd 100644 --- a/objc2-foundation/src/lib.rs +++ b/objc2-foundation/src/lib.rs @@ -71,6 +71,12 @@ pub use self::value::NSValue; #[doc(no_inline)] pub use objc2::ffi::{NSInteger, NSUInteger}; +#[doc(hidden)] +pub use core as __core; + +// Expose the version of objc2 that this crate uses +pub use objc2; + #[cfg(feature = "apple")] #[link(name = "Foundation", kind = "framework")] extern "C" {} diff --git a/objc2-foundation/src/macros.rs b/objc2-foundation/src/macros.rs index 74e7dde50..930cc71a7 100644 --- a/objc2-foundation/src/macros.rs +++ b/objc2-foundation/src/macros.rs @@ -1,7 +1,108 @@ +/// Create a new type to represent an Objective-C class. +/// +/// The given name should correspond to a valid Objective-C class, whose +/// instances have the encoding `Encoding::Object` (as an example: +/// `NSAutoreleasePool` does not have this). +/// +/// +/// # Specification +/// +/// This creates an opaque struct, and implements traits for it to allow +/// easier usage as an Objective-C object. +/// +/// The traits [`objc2::RefEncode`] and [`objc2::Message`] are implemented to +/// allow sending messages to the object and using it in [`objc2::rc::Id`]. +/// +/// [`Deref`] and [`DerefMut`] are implemented and delegate to the first +/// superclass (direct parent). Auto traits are inherited from this superclass +/// as well (this macro effectively just creates a newtype wrapper around the +/// superclass). +/// +/// Finally, [`AsRef`], [`AsMut`], [`Borrow`] and [`BorrowMut`] are +/// implemented to allow conversion to an arbitary superclasses in the +/// inheritance chain (since an instance of a class can always be interpreted +/// as it's superclasses). +/// +/// [`Deref`]: core::ops::Deref +/// [`DerefMut`]: core::ops::DerefMut +/// [`Borrow`]: core::borrow::Borrow +/// [`BorrowMut`]: core::borrow::BorrowMut +/// +/// +/// # Safety +/// +/// The specified inheritance chain must be correct (including in the correct +/// order), and the types in said chain must be valid as Objective-C objects - +/// this is easiest to ensure by also creating those using this macro. +/// +/// +/// # Example +/// +/// Create a new type to represent the `NSFormatter` class. +/// +/// ``` +/// use objc2::msg_send_id; +/// use objc2::rc::{Id, Shared}; +/// use objc2_foundation::{object, NSObject}; +/// # +/// # #[cfg(feature = "gnustep-1-7")] +/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() }; +/// +/// object! { +/// /// An example description. +/// #[derive(PartialEq, Eq, Hash)] // Uses `NSObject`'s implementation +/// // Specify class and superclass +/// // In this case the class `NSFormatter`, which subclasses `NSObject` +/// unsafe pub struct NSFormatter: NSObject; +/// } +/// +/// // Provided by the macro +/// let cls = NSFormatter::class(); +/// +/// // `NSFormatter` implements `Message`: +/// let obj: Id = unsafe { msg_send_id![cls, new].unwrap() }; +/// ``` +/// +/// Represent the `NSDateFormatter` class, using the `NSFormatter` type we +/// declared previously to specify as its superclass. +/// +/// ``` +/// use objc2_foundation::{object, NSObject}; +/// # +/// # object! { +/// # #[derive(PartialEq, Eq, Hash)] +/// # unsafe pub struct NSFormatter: NSObject; +/// # } +/// +/// object! { +/// #[derive(PartialEq, Eq, Hash)] +/// // Specify the correct inheritance chain +/// // `NSDateFormatter` subclasses `NSFormatter` which subclasses `NSObject` +/// unsafe pub struct NSDateFormatter: NSFormatter, NSObject; +/// } +/// ``` +/// +/// See the source code of `objc2_foundation` in general for more examples. +#[macro_export] +macro_rules! object { + ( + $(#[$m:meta])* + unsafe $v:vis struct $name:ident: $($inheritance_chain:ty),+; + ) => { + $crate::__inner_object! { + @__inner + $(#[$m])* + unsafe $v struct $name<>: $($inheritance_chain,)+ $crate::objc2::runtime::Object {} + } + }; +} + +#[doc(hidden)] +#[macro_export] macro_rules! __impl_as_ref_borrow { ($name:ident<$($t:ident $(: $b:ident)?),*>,) => {}; ($name:ident<$($t:ident $(: $b:ident)?),*>, $item:ty, $($tail:ty,)*) => { - impl<$($t $(: $b)?),*> AsRef<$item> for $name<$($t),*> { + impl<$($t $(: $b)?),*> $crate::__core::convert::AsRef<$item> for $name<$($t),*> { #[inline] fn as_ref(&self) -> &$item { // Triggers Deref coercion depending on return type @@ -9,7 +110,7 @@ macro_rules! __impl_as_ref_borrow { } } - impl<$($t $(: $b)?),*> AsMut<$item> for $name<$($t),*> { + impl<$($t $(: $b)?),*> $crate::__core::convert::AsMut<$item> for $name<$($t),*> { #[inline] fn as_mut(&mut self) -> &mut $item { // Triggers DerefMut coercion depending on return type @@ -23,7 +124,7 @@ macro_rules! __impl_as_ref_borrow { // In particular, `Eq`, `Ord` and `Hash` all give the same results // after borrow. - impl<$($t $(: $b)?),*> ::core::borrow::Borrow<$item> for $name<$($t),*> { + impl<$($t $(: $b)?),*> $crate::__core::borrow::Borrow<$item> for $name<$($t),*> { #[inline] fn borrow(&self) -> &$item { // Triggers Deref coercion depending on return type @@ -31,7 +132,7 @@ macro_rules! __impl_as_ref_borrow { } } - impl<$($t $(: $b)?),*> ::core::borrow::BorrowMut<$item> for $name<$($t),*> { + impl<$($t $(: $b)?),*> $crate::__core::borrow::BorrowMut<$item> for $name<$($t),*> { #[inline] fn borrow_mut(&mut self) -> &mut $item { // Triggers Deref coercion depending on return type @@ -39,39 +140,24 @@ macro_rules! __impl_as_ref_borrow { } } - __impl_as_ref_borrow!($name<$($t $(: $b)?),*>, $($tail,)*); + $crate::__impl_as_ref_borrow!($name<$($t $(: $b)?),*>, $($tail,)*); }; } -/// TODO -/// -/// # Safety -/// -/// The given name must be a valid Objective-C class that inherits NSObject -/// and it's instances must have the raw encoding `Encoding::Object` (an -/// example: `NSAutoreleasePool` does not have this). Finally the ownership -/// must be correct for this class. -macro_rules! object { - ( - $(#[$m:meta])* - unsafe $v:vis struct $name:ident: $($inheritance_chain:ty),+ $(;)? - ) => { - object! { - @__inner - $(#[$m])* - unsafe $v struct $name<>: $($inheritance_chain,)+ ::objc2::runtime::Object {} - } - }; +#[doc(hidden)] +#[macro_export] +macro_rules! __inner_object { + // TODO: Expose this variant in the `object` macro. ( $(#[$m:meta])* unsafe $v:vis struct $name:ident<$($t:ident $(: $b:ident)?),*>: $($inheritance_chain:ty),+ { $($p:ident: $pty:ty,)* } ) => { - object! { + $crate::__inner_object! { @__inner $(#[$m])* - unsafe $v struct $name<$($t $(: $b)?),*>: $($inheritance_chain,)+ ::objc2::runtime::Object { + unsafe $v struct $name<$($t $(: $b)?),*>: $($inheritance_chain,)+ $crate::objc2::runtime::Object { $($p: $pty,)* } } @@ -87,20 +173,21 @@ macro_rules! object { // TODO: repr(transparent) when the inner pointer is no longer a ZST. #[repr(C)] $v struct $name<$($t $(: $b)?),*> { - inner: $inherits, + __inner: $inherits, // Additional fields (should only be zero-sized PhantomData). $($p: $pty),* } - unsafe impl<$($t $(: $b)?),*> ::objc2::Message for $name<$($t),*> { } + unsafe impl<$($t $(: $b)?),*> $crate::objc2::Message for $name<$($t),*> { } - unsafe impl<$($t $(: $b)?),*> ::objc2::RefEncode for $name<$($t),*> { - const ENCODING_REF: ::objc2::Encoding<'static> = ::objc2::Encoding::Object; + unsafe impl<$($t $(: $b)?),*> $crate::objc2::RefEncode for $name<$($t),*> { + const ENCODING_REF: $crate::objc2::Encoding<'static> + = <$inherits as $crate::objc2::RefEncode>::ENCODING_REF; } impl<$($t $(: $b)?),*> $name<$($t),*> { - $v fn class() -> &'static ::objc2::runtime::Class { - ::objc2::class!($name) + $v fn class() -> &'static $crate::objc2::runtime::Class { + $crate::objc2::class!($name) } } @@ -122,12 +209,12 @@ macro_rules! object { // Note that you can easily have two different variables pointing to // the same object, `x: &T` and `y: &T::Target`, and this would be // perfectly safe! - impl<$($t $(: $b)?),*> ::core::ops::Deref for $name<$($t),*> { + impl<$($t $(: $b)?),*> $crate::__core::ops::Deref for $name<$($t),*> { type Target = $inherits; #[inline] fn deref(&self) -> &Self::Target { - &self.inner + &self.__inner } } @@ -142,28 +229,28 @@ macro_rules! object { // But `&mut NSMutableString` -> `&mut NSString` safe, since the // `NSCopying` implementation of `NSMutableString` is used, and that // is guaranteed to return a different object. - impl<$($t $(: $b)?),*> ::core::ops::DerefMut for $name<$($t),*> { + impl<$($t $(: $b)?),*> $crate::__core::ops::DerefMut for $name<$($t),*> { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner + &mut self.__inner } } - impl<$($t $(: $b)?),*> AsRef for $name<$($t),*> { + impl<$($t $(: $b)?),*> $crate::__core::convert::AsRef for $name<$($t),*> { #[inline] fn as_ref(&self) -> &Self { self } } - impl<$($t $(: $b)?),*> AsMut for $name<$($t),*> { + impl<$($t $(: $b)?),*> $crate::__core::convert::AsMut for $name<$($t),*> { #[inline] fn as_mut(&mut self) -> &mut Self { self } } - __impl_as_ref_borrow!($name<$($t $(: $b)?),*>, $inherits, $($inheritance_rest,)*); + $crate::__impl_as_ref_borrow!($name<$($t $(: $b)?),*>, $inherits, $($inheritance_rest,)*); }; } @@ -175,7 +262,7 @@ macro_rules! unsafe_def_fn { $(#[$m])* $v fn new() -> Id { let cls = Self::class(); - unsafe { ::objc2::msg_send_id![cls, new].unwrap() } + unsafe { $crate::objc2::msg_send_id![cls, new].unwrap() } } }; } diff --git a/objc2-foundation/src/object.rs b/objc2-foundation/src/object.rs index 28071bd53..1b8b2d7f8 100644 --- a/objc2-foundation/src/object.rs +++ b/objc2-foundation/src/object.rs @@ -7,7 +7,7 @@ use objc2::{msg_send, msg_send_bool, msg_send_id}; use super::NSString; -object! { +__inner_object! { @__inner unsafe pub struct NSObject<>: Object {} } diff --git a/objc2-foundation/src/value.rs b/objc2-foundation/src/value.rs index ce9b49326..687ea19a3 100644 --- a/objc2-foundation/src/value.rs +++ b/objc2-foundation/src/value.rs @@ -14,7 +14,7 @@ use objc2::{msg_send, msg_send_id}; use super::{NSCopying, NSObject}; -object! { +__inner_object! { // `T: Eq` bound correct to prevent `NSValue` from being `Eq` // (even though `[NAN isEqual: NAN]` is true in Objective-C). #[derive(Debug, PartialEq, Eq, Hash)] diff --git a/tests/ui/nsvalue_f32_not_eq.stderr b/tests/ui/nsvalue_f32_not_eq.stderr index d604a4cbe..324e93bb6 100644 --- a/tests/ui/nsvalue_f32_not_eq.stderr +++ b/tests/ui/nsvalue_f32_not_eq.stderr @@ -1,10 +1,10 @@ -error[E0277]: the trait bound `f32: Eq` is not satisfied +error[E0277]: the trait bound `f32: std::cmp::Eq` is not satisfied --> ui/nsvalue_f32_not_eq.rs:11:5 | 11 | needs_eq::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `f32` + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::cmp::Eq` is not implemented for `f32` | - = help: the following other types implement trait `Eq`: + = help: the following other types implement trait `std::cmp::Eq`: i128 i16 i32 @@ -14,7 +14,7 @@ error[E0277]: the trait bound `f32: Eq` is not satisfied u128 u16 and 4 others - = note: required because of the requirements on the impl of `Eq` for `NSValue` + = note: required because of the requirements on the impl of `std::cmp::Eq` for `NSValue` note: required by a bound in `needs_eq` --> ui/nsvalue_f32_not_eq.rs:5:16 | From c4a660f61d9dbc7872e8c59c3f99b91bfba4a1c7 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 6 Jul 2022 00:56:55 +0200 Subject: [PATCH 3/3] Renamed object! macro to extern_class! --- objc2-foundation/CHANGELOG.md | 2 +- objc2-foundation/src/array.rs | 4 ++-- objc2-foundation/src/attributed_string.rs | 2 +- objc2-foundation/src/data.rs | 4 ++-- objc2-foundation/src/dictionary.rs | 2 +- objc2-foundation/src/exception.rs | 2 +- objc2-foundation/src/macros.rs | 18 +++++++++--------- .../src/mutable_attributed_string.rs | 2 +- objc2-foundation/src/mutable_string.rs | 2 +- objc2-foundation/src/object.rs | 2 +- objc2-foundation/src/process_info.rs | 2 +- objc2-foundation/src/string.rs | 2 +- objc2-foundation/src/thread.rs | 2 +- objc2-foundation/src/uuid.rs | 2 +- objc2-foundation/src/value.rs | 2 +- 15 files changed, 25 insertions(+), 25 deletions(-) diff --git a/objc2-foundation/CHANGELOG.md b/objc2-foundation/CHANGELOG.md index 65e696684..33b08fa74 100644 --- a/objc2-foundation/CHANGELOG.md +++ b/objc2-foundation/CHANGELOG.md @@ -10,7 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added `MainThreadMarker` to help with designing APIs where a method is only safe to call on the main thread. * Added `NSException` object. -* Added `object!` macro to help with defining other classes. +* Added `extern_class!` macro to help with defining other classes. * Expose the `objc2` version that this uses in the crate root. ### Changed diff --git a/objc2-foundation/src/array.rs b/objc2-foundation/src/array.rs index 1f86f5e15..33881d6a7 100644 --- a/objc2-foundation/src/array.rs +++ b/objc2-foundation/src/array.rs @@ -14,7 +14,7 @@ use super::{ NSRange, }; -__inner_object! { +__inner_extern_class! { /// TODO /// /// You can have a `Id, Owned>`, which allows mutable access @@ -43,7 +43,7 @@ unsafe impl Send for NSArray {} unsafe impl Sync for NSArray {} unsafe impl Send for NSArray {} -__inner_object! { +__inner_extern_class! { // TODO: Ensure that this deref to NSArray is safe! // This "inherits" NSArray, and has the same `Send`/`Sync` impls as that. #[derive(Debug, PartialEq, Eq, Hash)] diff --git a/objc2-foundation/src/attributed_string.rs b/objc2-foundation/src/attributed_string.rs index 55846cf86..81fd48b19 100644 --- a/objc2-foundation/src/attributed_string.rs +++ b/objc2-foundation/src/attributed_string.rs @@ -6,7 +6,7 @@ use crate::{ NSCopying, NSDictionary, NSMutableAttributedString, NSMutableCopying, NSObject, NSString, }; -object! { +extern_class! { /// A string that has associated attributes for portions of its text. /// /// Examples of attributes could be: Visual style, hyperlinks, or diff --git a/objc2-foundation/src/data.rs b/objc2-foundation/src/data.rs index 3ee61f8db..1994712ee 100644 --- a/objc2-foundation/src/data.rs +++ b/objc2-foundation/src/data.rs @@ -11,7 +11,7 @@ use objc2::{msg_send, msg_send_id}; use super::{NSCopying, NSMutableCopying, NSObject, NSRange}; -object! { +extern_class! { /// A static byte buffer in memory. /// /// This is similar to a [`slice`][`prim@slice`] of [`u8`]. @@ -25,7 +25,7 @@ object! { unsafe impl Sync for NSData {} unsafe impl Send for NSData {} -object! { +extern_class! { /// A dynamic byte buffer in memory. /// /// This is the Objective-C equivalent of a [`Vec`] containing [`u8`]. diff --git a/objc2-foundation/src/dictionary.rs b/objc2-foundation/src/dictionary.rs index c8f47f454..8bca6ebe9 100644 --- a/objc2-foundation/src/dictionary.rs +++ b/objc2-foundation/src/dictionary.rs @@ -9,7 +9,7 @@ use objc2::{msg_send, msg_send_id, Message}; use super::{NSArray, NSCopying, NSEnumerator, NSFastEnumeration, NSObject}; -__inner_object! { +__inner_extern_class! { #[derive(Debug, PartialEq, Eq, Hash)] unsafe pub struct NSDictionary: NSObject { key: PhantomData>, diff --git a/objc2-foundation/src/exception.rs b/objc2-foundation/src/exception.rs index 1e8d758db..91c1f45ab 100644 --- a/objc2-foundation/src/exception.rs +++ b/objc2-foundation/src/exception.rs @@ -9,7 +9,7 @@ use objc2::{msg_send, msg_send_id, sel}; use crate::{NSCopying, NSDictionary, NSObject, NSString}; -object! { +extern_class! { /// A special condition that interrupts the normal flow of program /// execution. /// diff --git a/objc2-foundation/src/macros.rs b/objc2-foundation/src/macros.rs index 930cc71a7..e5fefb014 100644 --- a/objc2-foundation/src/macros.rs +++ b/objc2-foundation/src/macros.rs @@ -43,12 +43,12 @@ /// ``` /// use objc2::msg_send_id; /// use objc2::rc::{Id, Shared}; -/// use objc2_foundation::{object, NSObject}; +/// use objc2_foundation::{extern_class, NSObject}; /// # /// # #[cfg(feature = "gnustep-1-7")] /// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() }; /// -/// object! { +/// extern_class! { /// /// An example description. /// #[derive(PartialEq, Eq, Hash)] // Uses `NSObject`'s implementation /// // Specify class and superclass @@ -67,14 +67,14 @@ /// declared previously to specify as its superclass. /// /// ``` -/// use objc2_foundation::{object, NSObject}; +/// use objc2_foundation::{extern_class, NSObject}; /// # -/// # object! { +/// # extern_class! { /// # #[derive(PartialEq, Eq, Hash)] /// # unsafe pub struct NSFormatter: NSObject; /// # } /// -/// object! { +/// extern_class! { /// #[derive(PartialEq, Eq, Hash)] /// // Specify the correct inheritance chain /// // `NSDateFormatter` subclasses `NSFormatter` which subclasses `NSObject` @@ -84,12 +84,12 @@ /// /// See the source code of `objc2_foundation` in general for more examples. #[macro_export] -macro_rules! object { +macro_rules! extern_class { ( $(#[$m:meta])* unsafe $v:vis struct $name:ident: $($inheritance_chain:ty),+; ) => { - $crate::__inner_object! { + $crate::__inner_extern_class! { @__inner $(#[$m])* unsafe $v struct $name<>: $($inheritance_chain,)+ $crate::objc2::runtime::Object {} @@ -146,7 +146,7 @@ macro_rules! __impl_as_ref_borrow { #[doc(hidden)] #[macro_export] -macro_rules! __inner_object { +macro_rules! __inner_extern_class { // TODO: Expose this variant in the `object` macro. ( $(#[$m:meta])* @@ -154,7 +154,7 @@ macro_rules! __inner_object { $($p:ident: $pty:ty,)* } ) => { - $crate::__inner_object! { + $crate::__inner_extern_class! { @__inner $(#[$m])* unsafe $v struct $name<$($t $(: $b)?),*>: $($inheritance_chain,)+ $crate::objc2::runtime::Object { diff --git a/objc2-foundation/src/mutable_attributed_string.rs b/objc2-foundation/src/mutable_attributed_string.rs index 2be81c0e4..2b3b9228d 100644 --- a/objc2-foundation/src/mutable_attributed_string.rs +++ b/objc2-foundation/src/mutable_attributed_string.rs @@ -3,7 +3,7 @@ use objc2::{msg_send, msg_send_id}; use crate::{NSAttributedString, NSCopying, NSMutableCopying, NSObject, NSString}; -object! { +extern_class! { /// A mutable string that has associated attributes. /// /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsmutableattributedstring?language=objc). diff --git a/objc2-foundation/src/mutable_string.rs b/objc2-foundation/src/mutable_string.rs index 5b8ab0bf1..5834ff556 100644 --- a/objc2-foundation/src/mutable_string.rs +++ b/objc2-foundation/src/mutable_string.rs @@ -8,7 +8,7 @@ use objc2::{msg_send, msg_send_id}; use crate::{NSCopying, NSMutableCopying, NSObject, NSString}; -object! { +extern_class! { /// A dynamic plain-text Unicode string object. /// /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsmutablestring?language=objc). diff --git a/objc2-foundation/src/object.rs b/objc2-foundation/src/object.rs index 1b8b2d7f8..b8dfbe274 100644 --- a/objc2-foundation/src/object.rs +++ b/objc2-foundation/src/object.rs @@ -7,7 +7,7 @@ use objc2::{msg_send, msg_send_bool, msg_send_id}; use super::NSString; -__inner_object! { +__inner_extern_class! { @__inner unsafe pub struct NSObject<>: Object {} } diff --git a/objc2-foundation/src/process_info.rs b/objc2-foundation/src/process_info.rs index abec6911a..ede97994f 100644 --- a/objc2-foundation/src/process_info.rs +++ b/objc2-foundation/src/process_info.rs @@ -3,7 +3,7 @@ use objc2::rc::{Id, Shared}; use crate::{NSObject, NSString}; -object! { +extern_class! { /// A collection of information about the current process. /// /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsprocessinfo?language=objc). diff --git a/objc2-foundation/src/string.rs b/objc2-foundation/src/string.rs index 40bddb398..c417005fd 100644 --- a/objc2-foundation/src/string.rs +++ b/objc2-foundation/src/string.rs @@ -27,7 +27,7 @@ const UTF8_ENCODING: i32 = 4; #[allow(non_upper_case_globals)] const NSNotFound: ffi::NSInteger = ffi::NSIntegerMax; -object! { +extern_class! { /// A static, plain-text Unicode string object. /// /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsstring?language=objc). diff --git a/objc2-foundation/src/thread.rs b/objc2-foundation/src/thread.rs index df3e9ce17..5a99f6f10 100644 --- a/objc2-foundation/src/thread.rs +++ b/objc2-foundation/src/thread.rs @@ -5,7 +5,7 @@ use objc2::{msg_send, msg_send_bool, msg_send_id}; use crate::{NSObject, NSString}; -object! { +extern_class! { /// A thread of execution. /// /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsthread?language=objc). diff --git a/objc2-foundation/src/uuid.rs b/objc2-foundation/src/uuid.rs index 20b1c8181..b19337169 100644 --- a/objc2-foundation/src/uuid.rs +++ b/objc2-foundation/src/uuid.rs @@ -3,7 +3,7 @@ use objc2::{msg_send, msg_send_id, Encode, Encoding, RefEncode}; use super::{NSCopying, NSObject}; -object! { +extern_class! { /// A universally unique value. /// /// Can be used to identify types, interfaces, and other items. diff --git a/objc2-foundation/src/value.rs b/objc2-foundation/src/value.rs index 687ea19a3..34d668364 100644 --- a/objc2-foundation/src/value.rs +++ b/objc2-foundation/src/value.rs @@ -14,7 +14,7 @@ use objc2::{msg_send, msg_send_id}; use super::{NSCopying, NSObject}; -__inner_object! { +__inner_extern_class! { // `T: Eq` bound correct to prevent `NSValue` from being `Eq` // (even though `[NAN isEqual: NAN]` is true in Objective-C). #[derive(Debug, PartialEq, Eq, Hash)]