diff --git a/objc2/CHANGELOG.md b/objc2/CHANGELOG.md index b65756317..ce04d888e 100644 --- a/objc2/CHANGELOG.md +++ b/objc2/CHANGELOG.md @@ -22,7 +22,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added `Id::into_superclass`. ### Changed -* **BREAKING**: Change selector syntax in `declare_class!` macro to be more Rust-like. +* **BREAKING**: Change syntax in `extern_class!` macro to be more Rust-like. +* **BREAKING**: Change syntax in `declare_class!` macro to be more Rust-like. * **BREAKING**: Renamed `Id::from_owned` to `Id::into_shared`. diff --git a/objc2/examples/delegate.rs b/objc2/examples/delegate.rs index a1255f041..76db7f7ec 100644 --- a/objc2/examples/delegate.rs +++ b/objc2/examples/delegate.rs @@ -9,27 +9,30 @@ use objc2::{declare_class, extern_class, msg_send, msg_send_id, ClassType}; extern "C" {} #[cfg(all(feature = "apple", target_os = "macos"))] -extern_class! { - unsafe struct NSResponder: NSObject; -} +extern_class!( + struct NSResponder; + + unsafe impl ClassType for NSResponder { + type Superclass = NSObject; + } +); #[cfg(all(feature = "apple", target_os = "macos"))] -declare_class! { - unsafe struct CustomAppDelegate: NSResponder, NSObject { +declare_class!( + struct CustomAppDelegate { pub ivar: u8, another_ivar: Bool, } - unsafe impl { + unsafe impl ClassType for CustomAppDelegate { + #[inherits(NSObject)] + type Superclass = NSResponder; + } + + unsafe impl CustomAppDelegate { #[sel(initWith:another:)] - fn init_with( - self: &mut Self, - ivar: u8, - another_ivar: Bool, - ) -> *mut Self { - let this: *mut Self = unsafe { - msg_send![super(self, NSResponder::class()), init] - }; + fn init_with(self: &mut Self, ivar: u8, another_ivar: Bool) -> *mut Self { + let this: *mut Self = unsafe { msg_send![super(self, NSResponder::class()), init] }; if let Some(this) = unsafe { this.as_mut() } { // TODO: Allow initialization through MaybeUninit *this.ivar = ivar; @@ -49,7 +52,7 @@ declare_class! { // `clang` only when used in Objective-C... // // TODO: Investigate this! - unsafe impl { + unsafe impl CustomAppDelegate { /// This is `unsafe` because it expects `sender` to be valid #[sel(applicationDidFinishLaunching:)] unsafe fn did_finish_launching(&self, sender: *mut Object) { @@ -65,7 +68,7 @@ declare_class! { println!("Will terminate!"); } } -} +); #[cfg(all(feature = "apple", target_os = "macos"))] impl CustomAppDelegate { diff --git a/objc2/examples/nspasteboard.rs b/objc2/examples/nspasteboard.rs index 6dd5dfa3e..42cea21c9 100644 --- a/objc2/examples/nspasteboard.rs +++ b/objc2/examples/nspasteboard.rs @@ -22,11 +22,15 @@ extern "C" { } #[cfg(all(feature = "apple", target_os = "macos"))] -extern_class! { +extern_class!( /// + pub struct NSPasteboard; + // SAFETY: NSPasteboard actually inherits from NSObject. - unsafe pub struct NSPasteboard: NSObject; -} + unsafe impl ClassType for NSPasteboard { + type Superclass = NSObject; + } +); #[cfg(all(feature = "apple", target_os = "macos"))] impl NSPasteboard { diff --git a/objc2/examples/speech_synthethis.rs b/objc2/examples/speech_synthethis.rs index 2b92806dc..923a9ae47 100644 --- a/objc2/examples/speech_synthethis.rs +++ b/objc2/examples/speech_synthethis.rs @@ -26,10 +26,14 @@ mod appkit { #[link(name = "AppKit", kind = "framework")] extern "C" {} - extern_class! { + extern_class!( /// - unsafe pub struct NSSpeechSynthesizer: NSObject; - } + pub struct NSSpeechSynthesizer; + + unsafe impl ClassType for NSSpeechSynthesizer { + type Superclass = NSObject; + } + ); impl NSSpeechSynthesizer { // Uses default voice @@ -100,11 +104,15 @@ mod avfaudio { #[link(name = "AVFoundation", kind = "framework")] extern "C" {} - extern_class! { + extern_class!( /// #[derive(Debug)] - unsafe pub struct AVSpeechSynthesizer: NSObject; - } + pub struct AVSpeechSynthesizer; + + unsafe impl ClassType for AVSpeechSynthesizer { + type Superclass = NSObject; + } + ); impl AVSpeechSynthesizer { pub fn new() -> Id { @@ -122,11 +130,15 @@ mod avfaudio { } } - extern_class! { + extern_class!( /// #[derive(Debug)] - unsafe pub struct AVSpeechUtterance: NSObject; - } + pub struct AVSpeechUtterance; + + unsafe impl ClassType for AVSpeechUtterance { + type Superclass = NSObject; + } + ); impl AVSpeechUtterance { pub fn new(string: &NSString) -> Id { diff --git a/objc2/src/__macro_helpers.rs b/objc2/src/__macro_helpers.rs index 119e5b840..7559027ba 100644 --- a/objc2/src/__macro_helpers.rs +++ b/objc2/src/__macro_helpers.rs @@ -180,7 +180,7 @@ pub const fn in_selector_family(mut selector: &[u8], mut family: &[u8]) -> bool // Skip leading underscores from selector loop { selector = match selector { - [b'_', selector @ ..] => (selector), + [b'_', rest @ ..] => rest, _ => break, } } diff --git a/objc2/src/class_type.rs b/objc2/src/class_type.rs index be922702e..3b51004d5 100644 --- a/objc2/src/class_type.rs +++ b/objc2/src/class_type.rs @@ -44,9 +44,13 @@ use crate::Message; /// ```ignore /// use objc2::{extern_class, ClassType}; /// -/// extern_class! { -/// unsafe struct MyClass: NSObject; -/// } +/// extern_class!( +/// struct MyClass; +/// +/// unsafe impl ClassType for MyClass { +/// type Superclass = NSObject; +/// } +/// ); /// /// let cls = MyClass::class(); /// ``` diff --git a/objc2/src/foundation/array.rs b/objc2/src/foundation/array.rs index 8cc529af9..936ded83c 100644 --- a/objc2/src/foundation/array.rs +++ b/objc2/src/foundation/array.rs @@ -12,7 +12,7 @@ use crate::rc::{DefaultId, Id, Owned, Ownership, Shared, SliceId}; use crate::runtime::{Class, Object}; use crate::{ClassType, Message, __inner_extern_class, msg_send, msg_send_id}; -__inner_extern_class! { +__inner_extern_class!( /// An immutable ordered collection of objects. /// /// This is the Objective-C equivalent of a "boxed slice" (`Box<[T]>`), @@ -52,11 +52,15 @@ __inner_extern_class! { // `T: PartialEq` bound correct because `NSArray` does deep (instead of // shallow) equality comparisons. #[derive(PartialEq, Eq, Hash)] - unsafe pub struct NSArray: NSObject { + pub struct NSArray { item: PhantomData>, notunwindsafe: PhantomData<&'static mut ()>, } -} + + unsafe impl ClassType for NSArray { + type Superclass = NSObject; + } +); // SAFETY: Same as Id (which is what NSArray effectively stores). unsafe impl Sync for NSArray {} diff --git a/objc2/src/foundation/attributed_string.rs b/objc2/src/foundation/attributed_string.rs index 7830e3d2f..5600fe676 100644 --- a/objc2/src/foundation/attributed_string.rs +++ b/objc2/src/foundation/attributed_string.rs @@ -8,7 +8,7 @@ use crate::rc::{DefaultId, Id, Shared}; use crate::runtime::Object; use crate::{extern_class, msg_send, msg_send_id, ClassType}; -extern_class! { +extern_class!( /// A string that has associated attributes for portions of its text. /// /// Examples of attributes could be: Visual style, hyperlinks, or @@ -22,8 +22,12 @@ extern_class! { /// /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsattributedstring?language=objc). #[derive(PartialEq, Eq, Hash)] - unsafe pub struct NSAttributedString: NSObject; -} + pub struct NSAttributedString; + + unsafe impl ClassType for NSAttributedString { + type Superclass = NSObject; + } +); // SAFETY: `NSAttributedString` is immutable and `NSMutableAttributedString` // can only be mutated from `&mut` methods. diff --git a/objc2/src/foundation/data.rs b/objc2/src/foundation/data.rs index 229f962a0..151fb0d1b 100644 --- a/objc2/src/foundation/data.rs +++ b/objc2/src/foundation/data.rs @@ -11,15 +11,19 @@ use crate::rc::{DefaultId, Id, Shared}; use crate::runtime::{Class, Object}; use crate::{extern_class, msg_send, msg_send_id, ClassType}; -extern_class! { +extern_class!( /// A static byte buffer in memory. /// /// This is similar to a [`slice`][`prim@slice`] of [`u8`]. /// /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsdata?language=objc). #[derive(PartialEq, Eq, Hash)] - unsafe pub struct NSData: NSObject; -} + pub struct NSData; + + unsafe impl ClassType for NSData { + type Superclass = NSObject; + } +); // SAFETY: `NSData` is immutable and `NSMutableData` can only be mutated from // `&mut` methods. diff --git a/objc2/src/foundation/dictionary.rs b/objc2/src/foundation/dictionary.rs index 88612c38b..2ec444b9c 100644 --- a/objc2/src/foundation/dictionary.rs +++ b/objc2/src/foundation/dictionary.rs @@ -10,13 +10,17 @@ use super::{NSArray, NSCopying, NSEnumerator, NSFastEnumeration, NSObject}; use crate::rc::{DefaultId, Id, Owned, Shared, SliceId}; use crate::{ClassType, __inner_extern_class, msg_send, msg_send_id, Message}; -__inner_extern_class! { +__inner_extern_class!( #[derive(PartialEq, Eq, Hash)] - unsafe pub struct NSDictionary: NSObject { + pub struct NSDictionary { key: PhantomData>, obj: PhantomData>, } -} + + unsafe impl ClassType for NSDictionary { + type Superclass = NSObject; + } +); // TODO: SAFETY // Approximately same as `NSArray` diff --git a/objc2/src/foundation/error.rs b/objc2/src/foundation/error.rs index bf448a879..832bc5cd3 100644 --- a/objc2/src/foundation/error.rs +++ b/objc2/src/foundation/error.rs @@ -6,7 +6,7 @@ use crate::ffi::NSInteger; use crate::rc::{Id, Shared}; use crate::{extern_class, msg_send, msg_send_id, ClassType}; -extern_class! { +extern_class!( /// Information about an error condition including a domain, a /// domain-specific error code, and application-specific information. /// @@ -16,8 +16,12 @@ extern_class! { /// [err]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ErrorHandlingCocoa/ErrorHandling/ErrorHandling.html#//apple_ref/doc/uid/TP40001806-CH201-SW1 /// [api]: https://developer.apple.com/documentation/foundation/nserror?language=objc #[derive(PartialEq, Eq, Hash)] - unsafe pub struct NSError: NSObject; -} + pub struct NSError; + + unsafe impl ClassType for NSError { + type Superclass = NSObject; + } +); // SAFETY: Error objects are immutable data containers. unsafe impl Sync for NSError {} diff --git a/objc2/src/foundation/exception.rs b/objc2/src/foundation/exception.rs index 3238e4bb6..a46ff6525 100644 --- a/objc2/src/foundation/exception.rs +++ b/objc2/src/foundation/exception.rs @@ -8,7 +8,7 @@ use crate::rc::{Id, Shared}; use crate::runtime::Object; use crate::{extern_class, msg_send, msg_send_id, sel, ClassType}; -extern_class! { +extern_class!( /// A special condition that interrupts the normal flow of program /// execution. /// @@ -19,8 +19,12 @@ extern_class! { /// /// [doc]: https://developer.apple.com/documentation/foundation/nsexception?language=objc #[derive(PartialEq, Eq, Hash)] - unsafe pub struct NSException: NSObject; -} + pub struct NSException; + + unsafe impl ClassType for NSException { + type Superclass = NSObject; + } +); // SAFETY: Exception objects are immutable data containers, and documented as // thread safe. diff --git a/objc2/src/foundation/mutable_array.rs b/objc2/src/foundation/mutable_array.rs index 1bcb2d9d9..92e3831ac 100644 --- a/objc2/src/foundation/mutable_array.rs +++ b/objc2/src/foundation/mutable_array.rs @@ -13,7 +13,7 @@ use super::{ use crate::rc::{DefaultId, Id, Owned, Ownership, Shared, SliceId}; use crate::{ClassType, Message, __inner_extern_class, msg_send, msg_send_id}; -__inner_extern_class! { +__inner_extern_class!( /// A growable ordered collection of objects. /// /// See the documentation for [`NSArray`] and/or [Apple's @@ -21,10 +21,15 @@ __inner_extern_class! { /// /// [apple-doc]: https://developer.apple.com/documentation/foundation/nsmutablearray?language=objc #[derive(PartialEq, Eq, Hash)] - unsafe pub struct NSMutableArray: NSArray, NSObject { + pub struct NSMutableArray { p: PhantomData<*mut ()>, } -} + + unsafe impl ClassType for NSMutableArray { + #[inherits(NSObject)] + type Superclass = NSArray; + } +); // SAFETY: Same as NSArray // diff --git a/objc2/src/foundation/mutable_attributed_string.rs b/objc2/src/foundation/mutable_attributed_string.rs index f2db77db5..1156656cb 100644 --- a/objc2/src/foundation/mutable_attributed_string.rs +++ b/objc2/src/foundation/mutable_attributed_string.rs @@ -4,13 +4,18 @@ use super::{NSAttributedString, NSCopying, NSMutableCopying, NSObject, NSString} use crate::rc::{DefaultId, Id, Owned, Shared}; use crate::{extern_class, msg_send, msg_send_id, ClassType}; -extern_class! { +extern_class!( /// A mutable string that has associated attributes. /// /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsmutableattributedstring?language=objc). #[derive(PartialEq, Eq, Hash)] - unsafe pub struct NSMutableAttributedString: NSAttributedString, NSObject; -} + pub struct NSMutableAttributedString; + + unsafe impl ClassType for NSMutableAttributedString { + #[inherits(NSObject)] + type Superclass = NSAttributedString; + } +); /// Creating mutable attributed strings. impl NSMutableAttributedString { diff --git a/objc2/src/foundation/mutable_data.rs b/objc2/src/foundation/mutable_data.rs index 08927b09d..db73a56ad 100644 --- a/objc2/src/foundation/mutable_data.rs +++ b/objc2/src/foundation/mutable_data.rs @@ -11,7 +11,7 @@ use super::{NSCopying, NSData, NSMutableCopying, NSObject, NSRange}; use crate::rc::{DefaultId, Id, Owned, Shared}; use crate::{extern_class, msg_send, msg_send_id, ClassType}; -extern_class! { +extern_class!( /// A dynamic byte buffer in memory. /// /// This is the Objective-C equivalent of a [`Vec`] containing [`u8`]. @@ -20,8 +20,13 @@ extern_class! { /// /// [`Vec`]: std::vec::Vec #[derive(PartialEq, Eq, Hash)] - unsafe pub struct NSMutableData: NSData, NSObject; -} + pub struct NSMutableData; + + unsafe impl ClassType for NSMutableData { + #[inherits(NSObject)] + type Superclass = NSData; + } +); /// Creation methods impl NSMutableData { diff --git a/objc2/src/foundation/mutable_string.rs b/objc2/src/foundation/mutable_string.rs index 9589feac8..6f52983ef 100644 --- a/objc2/src/foundation/mutable_string.rs +++ b/objc2/src/foundation/mutable_string.rs @@ -7,13 +7,18 @@ use super::{NSCopying, NSMutableCopying, NSObject, NSString}; use crate::rc::{DefaultId, Id, Owned, Shared}; use crate::{extern_class, msg_send, msg_send_id, ClassType}; -extern_class! { +extern_class!( /// 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; -} + pub struct NSMutableString; + + unsafe impl ClassType for NSMutableString { + #[inherits(NSObject)] + type Superclass = NSString; + } +); /// Creating mutable strings. impl NSMutableString { diff --git a/objc2/src/foundation/number.rs b/objc2/src/foundation/number.rs index 3abb15a0f..170907359 100644 --- a/objc2/src/foundation/number.rs +++ b/objc2/src/foundation/number.rs @@ -13,7 +13,7 @@ use crate::rc::{Id, Shared}; use crate::runtime::Bool; use crate::{extern_class, msg_send, msg_send_bool, msg_send_id, ClassType, Encoding}; -extern_class! { +extern_class!( /// An object wrapper for primitive scalars. /// /// This is the Objective-C equivalant of a Rust enum containing the @@ -39,8 +39,13 @@ extern_class! { /// See [Apple's documentation][apple-doc] for more information. /// /// [apple-doc]: https://developer.apple.com/documentation/foundation/nsnumber?language=objc - unsafe pub struct NSNumber: NSValue, NSObject; -} + pub struct NSNumber; + + unsafe impl ClassType for NSNumber { + #[inherits(NSObject)] + type Superclass = NSValue; + } +); // SAFETY: `NSNumber` is just a wrapper around an integer/float/bool, and it // is immutable. diff --git a/objc2/src/foundation/object.rs b/objc2/src/foundation/object.rs index 177a241a6..b3ed54461 100644 --- a/objc2/src/foundation/object.rs +++ b/objc2/src/foundation/object.rs @@ -8,7 +8,11 @@ use crate::{ClassType, __inner_extern_class, class, msg_send, msg_send_bool, msg __inner_extern_class! { @__inner - unsafe pub struct NSObject<>: Object {} + pub struct NSObject () {} + + unsafe impl () for NSObject { + INHERITS = [Object]; + } } unsafe impl ClassType for NSObject { diff --git a/objc2/src/foundation/process_info.rs b/objc2/src/foundation/process_info.rs index 80e929314..a6a312c5b 100644 --- a/objc2/src/foundation/process_info.rs +++ b/objc2/src/foundation/process_info.rs @@ -5,13 +5,17 @@ use super::{NSObject, NSString}; use crate::rc::{Id, Shared}; use crate::{extern_class, msg_send_id, ClassType}; -extern_class! { +extern_class!( /// A collection of information about the current process. /// /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsprocessinfo?language=objc). #[derive(PartialEq, Eq, Hash)] - unsafe pub struct NSProcessInfo: NSObject; -} + pub struct NSProcessInfo; + + unsafe impl ClassType for NSProcessInfo { + type Superclass = NSObject; + } +); // SAFETY: The documentation explicitly states: // > NSProcessInfo is thread-safe in macOS 10.7 and later. diff --git a/objc2/src/foundation/string.rs b/objc2/src/foundation/string.rs index 2bc3f9ee2..302dbcce0 100644 --- a/objc2/src/foundation/string.rs +++ b/objc2/src/foundation/string.rs @@ -24,7 +24,7 @@ const UTF8_ENCODING: i32 = 4; #[allow(non_upper_case_globals)] const NSNotFound: ffi::NSInteger = ffi::NSIntegerMax; -extern_class! { +extern_class!( /// An immutable, plain-text Unicode string object. /// /// Can be created statically using the [`ns_string!`] macro. @@ -33,12 +33,16 @@ extern_class! { /// /// [`ns_string!`]: crate::ns_string #[derive(PartialEq, Eq, Hash)] - unsafe pub struct NSString: NSObject; + pub struct NSString; // TODO: Use isEqualToString: for comparison (instead of just isEqual:) // The former is more performant // TODO: Check if performance of NSSelectorFromString is worthwhile -} + + unsafe impl ClassType for NSString { + type Superclass = NSObject; + } +); // SAFETY: `NSString` is immutable and `NSMutableString` can only be mutated // from `&mut` methods. diff --git a/objc2/src/foundation/thread.rs b/objc2/src/foundation/thread.rs index 074ae29c8..a719dd0e0 100644 --- a/objc2/src/foundation/thread.rs +++ b/objc2/src/foundation/thread.rs @@ -6,13 +6,17 @@ use super::{NSObject, NSString}; use crate::rc::{Id, Shared}; use crate::{extern_class, msg_send, msg_send_bool, msg_send_id, ClassType}; -extern_class! { +extern_class!( /// A thread of execution. /// /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsthread?language=objc). #[derive(PartialEq, Eq, Hash)] - unsafe pub struct NSThread: NSObject; -} + pub struct NSThread; + + unsafe impl ClassType for NSThread { + type Superclass = NSObject; + } +); unsafe impl Send for NSThread {} unsafe impl Sync for NSThread {} diff --git a/objc2/src/foundation/uuid.rs b/objc2/src/foundation/uuid.rs index 34ee14c1a..9ccc34485 100644 --- a/objc2/src/foundation/uuid.rs +++ b/objc2/src/foundation/uuid.rs @@ -5,7 +5,7 @@ use super::{NSCopying, NSObject, NSString}; use crate::rc::{DefaultId, Id, Shared}; use crate::{extern_class, msg_send, msg_send_id, ClassType, Encode, Encoding, RefEncode}; -extern_class! { +extern_class!( /// A universally unique value. /// /// Can be used to identify types, interfaces, and other items. @@ -17,8 +17,12 @@ extern_class! { /// /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsuuid?language=objc). #[derive(PartialEq, Eq, Hash)] - unsafe pub struct NSUUID: NSObject; -} + pub struct NSUUID; + + unsafe impl ClassType for NSUUID { + type Superclass = NSObject; + } +); /// The headers describe `initWithUUIDBytes:` and `getUUIDBytes:` as /// taking `uuid_t`, but something fishy is going on, in reality they diff --git a/objc2/src/foundation/value.rs b/objc2/src/foundation/value.rs index d2d7599f3..670c96bd8 100644 --- a/objc2/src/foundation/value.rs +++ b/objc2/src/foundation/value.rs @@ -12,7 +12,7 @@ use super::{NSCopying, NSObject, NSPoint, NSRange, NSRect, NSSize}; use crate::rc::{Id, Shared}; use crate::{extern_class, msg_send, msg_send_bool, msg_send_id, ClassType, Encode}; -extern_class! { +extern_class!( /// A container wrapping any encodable type as an Obective-C object. /// /// Since Objective-C collections like [`NSArray`] can only contain @@ -30,8 +30,12 @@ extern_class! { /// [`NSRange`]: super::NSRange /// [`NSNumber`]: super::NSNumber /// [apple-doc]: https://developer.apple.com/documentation/foundation/nsnumber?language=objc - unsafe pub struct NSValue: NSObject; -} + pub struct NSValue; + + unsafe impl ClassType for NSValue { + type Superclass = NSObject; + } +); // We can't implement any auto traits for NSValue, since it can contain an // arbitary object! diff --git a/objc2/src/macros.rs b/objc2/src/macros.rs index 3b1ff97fe..7e67b1815 100644 --- a/objc2/src/macros.rs +++ b/objc2/src/macros.rs @@ -1,3 +1,4 @@ +mod __rewrite_self_arg; mod declare_class; mod extern_class; diff --git a/objc2/src/macros/__rewrite_self_arg.rs b/objc2/src/macros/__rewrite_self_arg.rs new file mode 100644 index 000000000..1684afb27 --- /dev/null +++ b/objc2/src/macros/__rewrite_self_arg.rs @@ -0,0 +1,102 @@ +#[doc(hidden)] +#[macro_export] +macro_rules! __rewrite_self_arg { + { + ($out_macro:path) + ($($args:tt)*) + $($macro_args:tt)* + } => { + $crate::__rewrite_self_arg! { + ($out_macro) + // Duplicate args out so that we can match on `self`, while still + // use it as a function argument + @($($args)*) + @($($args)*) + $($macro_args)* + } + }; + + // Instance method + { + ($out_macro:path) + @(&mut self $($__rest_args:tt)*) + @(&mut $self:ident $(, $($rest_args:tt)*)?) + $($macro_args:tt)* + } => { + $out_macro! { + $($macro_args)* + @(instance_method) + @( + $self: &mut Self, + _: $crate::runtime::Sel, + $($($rest_args)*)? + ) + } + }; + { + ($out_macro:path) + @(&self $($__rest_args:tt)*) + @(&$self:ident $(, $($rest_args:tt)*)?) + $($macro_args:tt)* + } => { + $out_macro! { + $($macro_args)* + @(instance_method) + @( + $self: &Self, + _: $crate::runtime::Sel, + $($($rest_args)*)? + ) + } + }; + { + ($out_macro:path) + @(mut self: $__self_ty:ty $(, $($__rest_args:tt)*)?) + @(mut $self:ident: $self_ty:ty $(, $($rest_args:tt)*)?) + $($macro_args:tt)* + } => { + $out_macro! { + $($macro_args)* + @(instance_method) + @( + mut $self: $self_ty, + _: $crate::runtime::Sel, + $($($rest_args)*)? + ) + } + }; + { + ($out_macro:path) + @(self: $__self_ty:ty $(, $($__rest_args:tt)*)?) + @($self:ident: $self_ty:ty $(, $($rest_args:tt)*)?) + $($macro_args:tt)* + } => { + $out_macro! { + $($macro_args)* + @(instance_method) + @( + $self: $self_ty, + _: $crate::runtime::Sel, + $($($rest_args)*)? + ) + } + }; + + // Class method + { + ($out_macro:path) + @($($__args:tt)*) + @($($args:tt)*) + $($macro_args:tt)* + } => { + $out_macro! { + $($macro_args)* + @(class_method) + @( + _: &$crate::runtime::Class, + _: $crate::runtime::Sel, + $($args)* + ) + } + }; +} diff --git a/objc2/src/macros/declare_class.rs b/objc2/src/macros/declare_class.rs index c9cdb8f34..c45381134 100644 --- a/objc2/src/macros/declare_class.rs +++ b/objc2/src/macros/declare_class.rs @@ -12,22 +12,20 @@ macro_rules! __inner_declare_class { $($rest:tt)* } => { - $crate::__inner_declare_class! { - @rewrite_self_arg + $crate::__rewrite_self_arg! { + ($crate::__inner_declare_class) + ($($args)*) - // Duplicate args out so that we can match on `self`, while still - // use it as a function argument - @($($args)*) // Split the function into parts, and send the arguments down to // be used later on - @($($args)*) + @$($output)* @($(#[$($m)*])*) @(unsafe extern "C") @($name) @($($ret)?) @($body) - - #($($output)*) + // Will add @(kind) + // Will add @(args) } $crate::__inner_declare_class! { @@ -47,18 +45,15 @@ macro_rules! __inner_declare_class { $($rest:tt)* } => { - $crate::__inner_declare_class! { - @rewrite_self_arg - - @($($args)*) - @($($args)*) + $crate::__rewrite_self_arg! { + ($crate::__inner_declare_class) + ($($args)*) + @$($output)* @($(#[$($m)*])*) @(extern "C") @($name) @($($ret)?) @($body) - - #($($output)*) } $crate::__inner_declare_class! { @@ -69,119 +64,15 @@ macro_rules! __inner_declare_class { } }; - // Instance method - { - @rewrite_self_arg - @(&mut self $($__rest_args:tt)*) - @(&mut $self:ident $(, $($rest_args:tt)*)?) - $($rest:tt)* - } => { - $crate::__inner_declare_class! { - @dispatch - @(instance_method) - @( - $self: &mut Self, - _: $crate::runtime::Sel, - $($($rest_args)*)? - ) - $($rest)* - } - }; - { - @rewrite_self_arg - @(&self $($__rest_args:tt)*) - @(&$self:ident $(, $($rest_args:tt)*)?) - $($rest:tt)* - } => { - $crate::__inner_declare_class! { - @dispatch - @(instance_method) - @( - $self: &Self, - _: $crate::runtime::Sel, - $($($rest_args)*)? - ) - $($rest)* - } - }; - { - @rewrite_self_arg - @(mut self: $__self_ty:ty $(, $($__rest_args:tt)*)?) - @(mut $self:ident: $self_ty:ty $(, $($rest_args:tt)*)?) - $($rest:tt)* - } => { - $crate::__inner_declare_class! { - @dispatch - @(instance_method) - @( - mut $self: $self_ty, - _: $crate::runtime::Sel, - $($($rest_args)*)? - ) - $($rest)* - } - }; - { - @rewrite_self_arg - @(self: $__self_ty:ty $(, $($__rest_args:tt)*)?) - @($self:ident: $self_ty:ty $(, $($rest_args:tt)*)?) - $($rest:tt)* - } => { - $crate::__inner_declare_class! { - @dispatch - @(instance_method) - @( - $self: $self_ty, - _: $crate::runtime::Sel, - $($($rest_args)*)? - ) - $($rest)* - } - }; - // Class method - { - @rewrite_self_arg - @($($__args:tt)*) - @($($args:tt)*) - $($rest:tt)* - } => { - $crate::__inner_declare_class! { - @dispatch - @(class_method) - @( - _: &$crate::runtime::Class, - _: $crate::runtime::Sel, - $($args)* - ) - $($rest)* - } - }; - - { - @dispatch - $( - @($($items:tt)*) - )* - - #($($output:tt)*) - } => { - $crate::__inner_declare_class! { - @$($output)* - $( - @($($items)*) - )* - } - }; - { @method_out - @($($_kind:tt)*) - @($($args:tt)*) @($(#[$($m:tt)*])*) @($($qualifiers:tt)*) @($name:ident) @($($ret:ty)?) @($($body:tt)*) + @($($_kind:tt)*) + @($($args:tt)*) } => { $crate::__attribute_helper! { @strip_sel @@ -192,18 +83,20 @@ macro_rules! __inner_declare_class { { @register_out($builder:ident) - @(class_method) - @($($args:tt)*) @($(#[$($m:tt)*])*) @($($qualifiers:tt)*) @($name:ident) @($($_ret:tt)*) @($($_body:tt)*) + @(class_method) + @($($args:tt)*) } => { $builder.add_class_method( $crate::__attribute_helper! { @extract_sel - $(#[$($m)*])* + ($crate::__inner_declare_class) + ($(#[$($m)*])*) + @call_sel }, Self::$name as $crate::__fn_ptr! { @($($qualifiers)*) $($args)* @@ -212,24 +105,33 @@ macro_rules! __inner_declare_class { }; { @register_out($builder:ident) - @(instance_method) - @($($args:tt)*) @($(#[$($m:tt)*])*) @($($qualifiers:tt)*) @($name:ident) @($($_ret:tt)*) @($($_body:tt)*) + @(instance_method) + @($($args:tt)*) } => { $builder.add_method( $crate::__attribute_helper! { @extract_sel - $(#[$($m)*])* + ($crate::__inner_declare_class) + ($(#[$($m)*])*) + @call_sel }, Self::$name as $crate::__fn_ptr! { @($($qualifiers)*) $($args)* }, ); }; + + { + @call_sel + @($($sel:tt)*) + } => { + $crate::sel!($($sel)*) + }; } /// Declare a new Objective-C class. @@ -242,8 +144,10 @@ macro_rules! __inner_declare_class { /// /// # Specification /// -/// This macro consists of three parts; the class definition, the method -/// definition, and the protocol definition. +/// This macro consists of three parts: +/// - The class definition + ivar definition + inheritance specification. +/// - A set of method definitions. +/// - A set of protocol definitions. /// /// /// ## Class and ivar definition @@ -260,10 +164,13 @@ macro_rules! __inner_declare_class { /// alias like `pub type CustomObject = MyCrateCustomObject;` instead. /// /// The class is guaranteed to have been created and registered with the -/// Objective-C runtime after the associated function `class` has been called. +/// Objective-C runtime after the [`ClassType::class`] function has been +/// called. /// +/// [`ClassType::class`]: crate::ClassType::class /// -/// ## Method definition +/// +/// ## Method definitions /// /// Within the `impl` block you can define two types of functions; /// ["associated functions"] and ["methods"]. These are then mapped to the @@ -284,10 +191,10 @@ macro_rules! __inner_declare_class { /// [`msg_send!`]: crate::msg_send /// /// -/// ## Protocol definition +/// ## Protocol definitions /// -/// You can specify the protocols that the class should implement, along with -/// any required methods for said protocols. +/// You can specify protocols that the class should implement, along with any +/// required/optional methods for said protocols. /// /// The methods work exactly as normal, they're only put "under" the protocol /// definition to make things easier to read. @@ -297,19 +204,19 @@ macro_rules! __inner_declare_class { /// /// Using this macro requires writing a few `unsafe` markers: /// -/// `unsafe struct ...` has the following safety requirements: +/// `unsafe impl ClassType for T` has the following safety requirements: /// - Same as [`extern_class!`] (the inheritance chain has to be correct). -/// - Any instance variables you specify must either be able to be created -/// using [`MaybeUninit::zeroed`], or be properly initialized in an `init` -/// method. +/// - Any instance variables you specify under the struct definition must +/// either be able to be created using [`MaybeUninit::zeroed`], or be +/// properly initialized in an `init` method. /// -/// `unsafe impl { ... }` asserts that the types match those that are expected -/// when the method is invoked from Objective-C. Note that there are no -/// safe-guards here; you can easily write `i8`, but if Objective-C thinks +/// `unsafe impl T { ... }` asserts that the types match those that are +/// expected when the method is invoked from Objective-C. Note that there are +/// no safe-guards here; you can easily write `i8`, but if Objective-C thinks /// it's an `u32`, it will cause UB when called! /// -/// `unsafe impl protocol ... { ... }` requires that all required methods of -/// the specified protocol is implemented, and that any extra requirements +/// `unsafe impl Protocol

for T { ... }` requires that all required methods +/// of the specified protocol is implemented, and that any extra requirements /// (implicit or explicit) that the protocol has are upheld. The methods in /// this definition has the same safety requirements as above. /// @@ -331,13 +238,17 @@ macro_rules! __inner_declare_class { /// # #[cfg(feature = "gnustep-1-7")] /// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() }; /// -/// declare_class! { -/// unsafe struct MyCustomObject: NSObject { +/// declare_class!( +/// struct MyCustomObject { /// foo: u8, /// pub bar: c_int, /// } /// -/// unsafe impl { +/// unsafe impl ClassType for MyCustomObject { +/// type Superclass = NSObject; +/// } +/// +/// unsafe impl MyCustomObject { /// #[sel(initWithFoo:)] /// fn init_with(&mut self, foo: u8) -> Option<&mut Self> { /// let this: Option<&mut Self> = unsafe { @@ -364,7 +275,7 @@ macro_rules! __inner_declare_class { /// } /// } /// -/// unsafe impl protocol NSCopying { +/// unsafe impl Protocol for MyCustomObject { /// #[sel(copyWithZone:)] /// fn copy_with_zone(&self, _zone: *const NSZone) -> *mut Self { /// let mut obj = Self::new(*self.foo); @@ -372,7 +283,7 @@ macro_rules! __inner_declare_class { /// obj.autorelease_return() /// } /// } -/// } +/// ); /// /// impl MyCustomObject { /// pub fn new(foo: u8) -> Id { @@ -465,16 +376,16 @@ macro_rules! __inner_declare_class { macro_rules! declare_class { { $(#[$m:meta])* - unsafe $v:vis struct $name:ident: $inherits:ty $(, $inheritance_rest:ty)* { + $v:vis struct $name:ident { $($ivar_v:vis $ivar:ident: $ivar_ty:ty,)* } - $( - $(#[$impl_m:meta])* - unsafe impl $(protocol $protocol:ident)? { - $($methods:tt)* - } - )* + unsafe impl ClassType for $for:ty { + $(#[inherits($($inheritance_rest:ty)+)])? + type Superclass = $superclass:ty; + } + + $($methods:tt)* } => { $( #[allow(non_camel_case_types)] @@ -492,7 +403,7 @@ macro_rules! declare_class { @__inner $(#[$m])* // SAFETY: Upheld by caller - unsafe $v struct $name<>: $inherits, $($inheritance_rest,)* $crate::runtime::Object { + $v struct $name () { // SAFETY: // - The ivars are in a type used as an Objective-C object. // - The ivar is added to the class below. @@ -500,11 +411,15 @@ macro_rules! declare_class { // - Caller upholds that the ivars are properly initialized. $($ivar_v $ivar: $crate::declare::Ivar<$ivar>,)* } + + unsafe impl () for $for { + INHERITS = [$superclass, $($($inheritance_rest,)+)? $crate::runtime::Object]; + } } // Creation - unsafe impl $crate::ClassType for $name { - type Superclass = $inherits; + unsafe impl $crate::ClassType for $for { + type Superclass = $superclass; fn class() -> &'static $crate::runtime::Class { // TODO: Use `core::cell::LazyCell` @@ -513,7 +428,7 @@ macro_rules! declare_class { static REGISTER_CLASS: Once = Once::new(); REGISTER_CLASS.call_once(|| { - let superclass = <$inherits as $crate::ClassType>::class(); + let superclass = <$superclass as $crate::ClassType>::class(); let err_str = concat!( "could not create new class ", stringify!($name), @@ -526,24 +441,11 @@ macro_rules! declare_class { builder.add_static_ivar::<$ivar>(); )* - $( - // Implement protocol if any specified - $( - let err_str = concat!("could not find protocol ", stringify!($protocol)); - builder.add_protocol($crate::runtime::Protocol::get(stringify!($protocol)).expect(err_str)); - )? - - // Implement methods - // SAFETY: Upheld by caller - unsafe { - $crate::__inner_declare_class! { - @rewrite_methods - @(register_out(builder)) - - $($methods)* - } - } - )* + // Implement protocols and methods + $crate::__declare_class_methods!( + @register_out(builder) + $($methods)* + ); let _cls = builder.register(); }); @@ -554,16 +456,123 @@ macro_rules! declare_class { } // Methods - $( - $(#[$impl_m])* - impl $name { - $crate::__inner_declare_class! { - @rewrite_methods - @(method_out) - - $($methods)* - } + $crate::__declare_class_methods!( + @method_out + $($methods)* + ); + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __declare_class_methods { + (@method_out) => {}; + // With protocol + ( + @method_out + + $(#[$m:meta])* + unsafe impl Protocol<$protocol:ident> for $for:ty { + $($methods:tt)* + } + + $($rest:tt)* + ) => { + $(#[$m])* + impl $for { + $crate::__inner_declare_class! { + @rewrite_methods + @(method_out) + $($methods)* } - )* + } + + $crate::__declare_class_methods!( + @method_out + $($rest)* + ); + }; + // Without protocol + ( + @method_out + + $(#[$m:meta])* + unsafe impl $for:ty { + $($methods:tt)* + } + + $($rest:tt)* + ) => { + $(#[$m])* + impl $for { + $crate::__inner_declare_class! { + @rewrite_methods + @(method_out) + $($methods)* + } + } + + $crate::__declare_class_methods!( + @method_out + $($rest)* + ); + }; + + (@register_out($builder:ident)) => {}; + // With protocol + ( + @register_out($builder:ident) + + $(#[$m:meta])* + unsafe impl Protocol<$protocol:ident> for $for:ty { + $($methods:tt)* + } + + $($rest:tt)* + ) => { + // Implement protocol + let err_str = concat!("could not find protocol ", stringify!($protocol)); + $builder.add_protocol($crate::runtime::Protocol::get(stringify!($protocol)).expect(err_str)); + + // SAFETY: Upheld by caller + unsafe { + $crate::__inner_declare_class! { + @rewrite_methods + @(register_out($builder)) + + $($methods)* + } + } + + $crate::__declare_class_methods!( + @register_out($builder) + $($rest)* + ); + }; + // Without protocol + ( + @register_out($builder:ident) + + $(#[$m:meta])* + unsafe impl $for:ty { + $($methods:tt)* + } + + $($rest:tt)* + ) => { + // SAFETY: Upheld by caller + unsafe { + $crate::__inner_declare_class! { + @rewrite_methods + @(register_out($builder)) + + $($methods)* + } + } + + $crate::__declare_class_methods!( + @register_out($builder) + $($rest)* + ); }; } diff --git a/objc2/src/macros/extern_class.rs b/objc2/src/macros/extern_class.rs index 2365dcc23..4199a1aa5 100644 --- a/objc2/src/macros/extern_class.rs +++ b/objc2/src/macros/extern_class.rs @@ -7,22 +7,29 @@ /// `NSAutoreleasePool` does not have this!) /// /// You must specify the superclass of this class, similar to how you would -/// in Objective-C. Due to Rust trait limitations, specifying e.g. the -/// superclass `NSData` would not give you easy access to `NSObject`'s -/// functionality, therefore you may specify additional parts of the -/// inheritance chain. +/// in Objective-C. +/// +/// Due to Rust trait limitations, specifying e.g. the superclass `NSData` +/// would not give you easy access to `NSObject`'s functionality. Therefore, +/// you may specify additional parts of the inheritance chain using the +/// `#[inherits(...)]` attribute. /// /// [`Encoding::Object`]: crate::Encoding::Object /// /// /// # Specification /// +/// The syntax is similar enough to Rust syntax that if you invoke the macro +/// with parentheses (as opposed to curly brackets), [`rustfmt` will be able to +/// format the contents][rustfmt-macros]. +/// /// This creates an opaque struct containing the superclass (which means that /// auto traits are inherited from the superclass), and implements the /// following traits for it to allow easier usage as an Objective-C object: /// /// - [`RefEncode`][crate::RefEncode] /// - [`Message`][crate::Message] +/// - [`ClassType`][crate::ClassType] /// - [`Deref`][core::ops::Deref] /// - [`DerefMut`][core::ops::DerefMut] /// - [`AsRef<$inheritance_chain>`][AsRef] @@ -30,13 +37,10 @@ /// - [`Borrow<$inheritance_chain>`][core::borrow::Borrow] /// - [`BorrowMut<$inheritance_chain>`][core::borrow::BorrowMut] /// -/// An associated function `class` is created on the object as a convenient -/// shorthand so that you can do `MyObject::class()` instead of -/// `class!(MyObject)`. -/// /// The macro allows specifying fields on the struct, but _only_ zero-sized /// types like [`PhantomData`] and [`declare::Ivar`] are allowed here! /// +/// [rustfmt-macros]: https://github.com/rust-lang/rustfmt/discussions/5437 /// [`PhantomData`]: core::marker::PhantomData /// [`declare::Ivar`]: crate::declare::Ivar /// @@ -50,7 +54,7 @@ /// [`NSObject`]: crate::foundation::NSObject /// /// -/// # Example +/// # Examples /// /// Create a new type to represent the `NSFormatter` class. /// @@ -62,15 +66,19 @@ /// # #[cfg(feature = "gnustep-1-7")] /// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() }; /// -/// extern_class! { +/// extern_class!( /// /// 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; -/// } +/// #[derive(PartialEq, Eq, Hash)] // Uses the superclass' implementation +/// // Specify the class and struct name to be used +/// pub struct NSFormatter; /// -/// // Provided by the macro (it implements `ClassType`) +/// // Specify the superclass, in this case `NSObject` +/// unsafe impl ClassType for NSFormatter { +/// type Superclass = NSObject; +/// } +/// ); +/// +/// // Provided by the implementation of `ClassType` /// let cls = NSFormatter::class(); /// /// // `NSFormatter` implements `Message`: @@ -81,20 +89,28 @@ /// declared previously to specify as its superclass. /// /// ``` -/// use objc2::extern_class; /// use objc2::foundation::NSObject; +/// use objc2::{extern_class, ClassType}; /// # -/// # extern_class! { +/// # extern_class!( /// # #[derive(PartialEq, Eq, Hash)] -/// # unsafe pub struct NSFormatter: NSObject; -/// # } +/// # pub struct NSFormatter; +/// # +/// # unsafe impl ClassType for NSFormatter { +/// # type Superclass = NSObject; +/// # } +/// # ); /// -/// extern_class! { +/// extern_class!( /// #[derive(PartialEq, Eq, Hash)] -/// // Specify the correct inheritance chain -/// // `NSDateFormatter` subclasses `NSFormatter` which subclasses `NSObject` -/// unsafe pub struct NSDateFormatter: NSFormatter, NSObject; -/// } +/// pub struct NSDateFormatter; +/// +/// unsafe impl ClassType for NSDateFormatter { +/// // Specify the correct inheritance chain +/// #[inherits(NSObject)] +/// type Superclass = NSFormatter; +/// } +/// ); /// ``` /// /// See the source code of `objc2::foundation` in general for more examples. @@ -103,26 +119,46 @@ macro_rules! extern_class { ( $(#[$m:meta])* - unsafe $v:vis struct $name:ident: $superclass:ty $(, $inheritance_rest:ty)*; + $v:vis struct $name:ident; + + unsafe impl ClassType for $for:ty { + $(#[inherits($($inheritance_rest:ty)+)])? + type Superclass = $superclass:ty; + } ) => { // Just shorthand syntax for the following - $crate::extern_class! { + $crate::extern_class!( $(#[$m])* - unsafe $v struct $name: $superclass $(, $inheritance_rest)* {} - } + $v struct $name {} + + unsafe impl ClassType for $for { + $(#[inherits($($inheritance_rest)+)])? + type Superclass = $superclass; + } + ); }; ( $(#[$m:meta])* - unsafe $v:vis struct $name:ident: $superclass:ty $(, $inheritance_rest:ty)* { + $v:vis struct $name:ident { $($field_vis:vis $field:ident: $field_ty:ty,)* } + + unsafe impl ClassType for $for:ty { + $(#[inherits($($inheritance_rest:ty)+)])? + type Superclass = $superclass:ty; + } ) => { - $crate::__inner_extern_class! { + $crate::__inner_extern_class!( $(#[$m])* - unsafe $v struct $name<>: $superclass $(, $inheritance_rest)* { + $v struct $name<> { $($field_vis $field: $field_ty,)* } - } + + unsafe impl<> ClassType for $for { + $(#[inherits($($inheritance_rest)+)])? + type Superclass = $superclass; + } + ); const _: () = { if $crate::__macro_helpers::size_of::<$name>() != 0 { @@ -139,9 +175,13 @@ macro_rules! extern_class { #[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)?),*> $crate::__macro_helpers::AsRef<$item> for $name<$($t),*> { + { + impl ($($t:tt)*) for $for:ty; + } => {}; + { + impl ($($t:tt)*) for $for:ty; $item:ty, $($tail:ty,)* + } => { + impl<$($t)*> $crate::__macro_helpers::AsRef<$item> for $for { #[inline] fn as_ref(&self) -> &$item { // Triggers Deref coercion depending on return type @@ -149,7 +189,7 @@ macro_rules! __impl_as_ref_borrow { } } - impl<$($t $(: $b)?),*> $crate::__macro_helpers::AsMut<$item> for $name<$($t),*> { + impl<$($t)*> $crate::__macro_helpers::AsMut<$item> for $for { #[inline] fn as_mut(&mut self) -> &mut $item { // Triggers DerefMut coercion depending on return type @@ -163,7 +203,7 @@ macro_rules! __impl_as_ref_borrow { // In particular, `Eq`, `Ord` and `Hash` all give the same results // after borrow. - impl<$($t $(: $b)?),*> $crate::__macro_helpers::Borrow<$item> for $name<$($t),*> { + impl<$($t)*> $crate::__macro_helpers::Borrow<$item> for $for { #[inline] fn borrow(&self) -> &$item { // Triggers Deref coercion depending on return type @@ -171,7 +211,7 @@ macro_rules! __impl_as_ref_borrow { } } - impl<$($t $(: $b)?),*> $crate::__macro_helpers::BorrowMut<$item> for $name<$($t),*> { + impl<$($t)*> $crate::__macro_helpers::BorrowMut<$item> for $for { #[inline] fn borrow_mut(&mut self) -> &mut $item { // Triggers Deref coercion depending on return type @@ -179,7 +219,9 @@ macro_rules! __impl_as_ref_borrow { } } - $crate::__impl_as_ref_borrow!($name<$($t $(: $b)?),*>, $($tail,)*); + $crate::__impl_as_ref_borrow! { + impl ($($t)*) for $for; $($tail,)* + } }; } @@ -189,19 +231,28 @@ macro_rules! __inner_extern_class { // TODO: Expose this variant in the `object` macro. ( $(#[$m:meta])* - unsafe $v:vis struct $name:ident<$($t:ident $(: $b:ident $(= $default:ty)?)?),*>: $superclass:ty $(, $inheritance_rest:ty)* { + $v:vis struct $name:ident<$($t_struct:ident $(: $b_struct:ident $(= $default:ty)?)?),*> { $($field_vis:vis $field:ident: $field_ty:ty,)* } + + unsafe impl<$($t_for:ident $(: $b_for:ident)?),*> ClassType for $for:ty { + $(#[inherits($($inheritance_rest:ty)+)])? + type Superclass = $superclass:ty; + } ) => { $crate::__inner_extern_class! { @__inner $(#[$m])* - unsafe $v struct $name<$($t $(: $b $(= $default)?)?),*>: $superclass, $($inheritance_rest,)* $crate::runtime::Object { + $v struct $name ($($t_struct $(: $b_struct $(= $default)?)?),*) { $($field_vis $field: $field_ty,)* } + + unsafe impl ($($t_for $(: $b_for)?),*) for $for { + INHERITS = [$superclass, $($($inheritance_rest,)+)? $crate::runtime::Object]; + } } - unsafe impl<$($t $(: $b)?),*> $crate::ClassType for $name<$($t),*> { + unsafe impl<$($t_for $(: $b_for)?),*> ClassType for $for { type Superclass = $superclass; #[inline] @@ -212,15 +263,20 @@ macro_rules! __inner_extern_class { }; ( @__inner + $(#[$m:meta])* - unsafe $v:vis struct $name:ident<$($t:ident $(: $b:ident $(= $default:ty)?)?),*>: $superclass:ty $(, $inheritance_rest:ty)* { + $v:vis struct $name:ident ($($t_struct:tt)*) { $($field_vis:vis $field:ident: $field_ty:ty,)* } + + unsafe impl ($($t:tt)*) for $for:ty { + INHERITS = [$superclass:ty $(, $inheritance_rest:ty)*]; + } ) => { $(#[$m])* // TODO: repr(transparent) when the inner pointer is no longer a ZST. #[repr(C)] - $v struct $name<$($t $(: $b $(= $default)?)?),*> { + $v struct $name<$($t_struct)*> { __inner: $superclass, // Additional fields (should only be zero-sized PhantomData or ivars). $($field_vis $field: $field_ty,)* @@ -232,7 +288,7 @@ macro_rules! __inner_extern_class { // that it actually inherits said object. // - The rest of the struct's fields are ZSTs, so they don't influence // the layout. - unsafe impl<$($t $(: $b)?),*> $crate::RefEncode for $name<$($t),*> { + unsafe impl<$($t)*> $crate::RefEncode for $for { const ENCODING_REF: $crate::Encoding<'static> = <$superclass as $crate::RefEncode>::ENCODING_REF; } @@ -243,7 +299,7 @@ macro_rules! __inner_extern_class { // // That the object must work with standard memory management is upheld // by the caller. - unsafe impl<$($t $(: $b)?),*> $crate::Message for $name<$($t),*> {} + unsafe impl<$($t)*> $crate::Message for $for {} // SAFETY: An instance can always be _used_ in exactly the same way as // its superclasses (though not necessarily _constructed_ in the same @@ -263,7 +319,7 @@ macro_rules! __inner_extern_class { // 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)?),*> $crate::__macro_helpers::Deref for $name<$($t),*> { + impl<$($t)*> $crate::__macro_helpers::Deref for $for { type Target = $superclass; #[inline] @@ -283,28 +339,30 @@ macro_rules! __inner_extern_class { // 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)?),*> $crate::__macro_helpers::DerefMut for $name<$($t),*> { + impl<$($t)*> $crate::__macro_helpers::DerefMut for $for { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.__inner } } - impl<$($t $(: $b)?),*> $crate::__macro_helpers::AsRef for $name<$($t),*> { + impl<$($t)*> $crate::__macro_helpers::AsRef for $for { #[inline] fn as_ref(&self) -> &Self { self } } - impl<$($t $(: $b)?),*> $crate::__macro_helpers::AsMut for $name<$($t),*> { + impl<$($t)*> $crate::__macro_helpers::AsMut for $for { #[inline] fn as_mut(&mut self) -> &mut Self { self } } - $crate::__impl_as_ref_borrow!($name<$($t $(: $b)?),*>, $superclass, $($inheritance_rest,)*); + $crate::__impl_as_ref_borrow! { + impl ($($t)*) for $for; $superclass, $($inheritance_rest,)* + } }; } @@ -355,30 +413,48 @@ macro_rules! __attribute_helper { $($fn)* }; - // Extract and convert the `#[sel(...)]` attribute to a `sel!` invocation. + // Extract the `#[sel(...)]` attribute and send it to another macro { @extract_sel - #[sel($($sel:tt)*)] - $($rest:tt)* + ($out_macro:path) + ( + #[sel($($sel:tt)*)] + $($rest:tt)* + ) + $($macro_args:tt)* } => {{ $crate::__attribute_helper! { @extract_sel_duplicate $($rest)* } - $crate::sel!($($sel)*) + $out_macro!( + $($macro_args)* + @($($sel)*) + ) }}; { @extract_sel - #[$($m_checked:tt)*] - $($rest:tt)* + ($out_macro:path) + ( + #[$($m_checked:tt)*] + $($rest:tt)* + ) + $($macro_args:tt)* } => {{ $crate::__attribute_helper! { @extract_sel - $($rest)* + ($out_macro) + ($($rest)*) + $($macro_args)* } }}; - {@extract_sel} => {{ + { + @extract_sel + ($out_macro:path) + () + $($macro_args:tt)* + } => {{ compile_error!("Must specify the desired selector using `#[sel(...)]`"); }}; diff --git a/objc2/src/rc/test_object.rs b/objc2/src/rc/test_object.rs index 2d2c3f946..936430c3a 100644 --- a/objc2/src/rc/test_object.rs +++ b/objc2/src/rc/test_object.rs @@ -48,13 +48,17 @@ std::thread_local! { pub(crate) static TEST_DATA: RefCell = RefCell::new(Default::default()); } -declare_class! { +declare_class!( /// A helper object that counts how many times various reference-counting /// primitives are called. #[derive(Debug, PartialEq)] - unsafe pub(crate) struct RcTestObject: NSObject {} + pub(crate) struct RcTestObject {} - unsafe impl { + unsafe impl ClassType for RcTestObject { + type Superclass = NSObject; + } + + unsafe impl RcTestObject { #[sel(alloc)] fn alloc() -> *mut Self { TEST_DATA.with(|data| data.borrow_mut().alloc += 1); @@ -123,7 +127,7 @@ declare_class! { Id::consume_as_ptr(ManuallyDrop::new(Self::new())) } } -} +); unsafe impl Send for RcTestObject {} unsafe impl Sync for RcTestObject {} diff --git a/test-ui/ui/extern_class_not_zst.rs b/test-ui/ui/extern_class_not_zst.rs index b058d98e0..c9233171b 100644 --- a/test-ui/ui/extern_class_not_zst.rs +++ b/test-ui/ui/extern_class_not_zst.rs @@ -1,10 +1,14 @@ -use objc2::extern_class; +use objc2::{extern_class, ClassType}; use objc2::foundation::NSObject; -extern_class! { - unsafe pub struct NSNumber: NSObject { +extern_class!( + pub struct NSNumber { var: u32, } -} + + unsafe impl ClassType for NSNumber { + type Superclass = NSObject; + } +); fn main() {} diff --git a/test-ui/ui/extern_class_not_zst.stderr b/test-ui/ui/extern_class_not_zst.stderr index f2a22d597..e19827fd1 100644 --- a/test-ui/ui/extern_class_not_zst.stderr +++ b/test-ui/ui/extern_class_not_zst.stderr @@ -1,11 +1,13 @@ error[E0080]: evaluation of constant value failed - --> ui/extern_class_not_zst.rs - | - | / extern_class! { - | | unsafe pub struct NSNumber: NSObject { - | | var: u32, - | | } - | | } - | |_^ the evaluated program panicked at 'the struct NSNumber is not zero-sized!', $DIR/ui/extern_class_not_zst.rs:4:1 - | - = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `extern_class` (in Nightly builds, run with -Z macro-backtrace for more info) + --> ui/extern_class_not_zst.rs + | + | / extern_class!( + | | pub struct NSNumber { + | | var: u32, + | | } +... | + | | } + | | ); + | |_^ the evaluated program panicked at 'the struct NSNumber is not zero-sized!', $DIR/ui/extern_class_not_zst.rs:4:1 + | + = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `extern_class` (in Nightly builds, run with -Z macro-backtrace for more info)