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..a6d5da4ab 100644 --- a/objc2/examples/delegate.rs +++ b/objc2/examples/delegate.rs @@ -9,17 +9,26 @@ 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 { + struct CustomAppDelegate { pub ivar: u8, another_ivar: Bool, } + unsafe impl ClassType for CustomAppDelegate { + #[inherits(NSObject)] + type Superclass = NSResponder; + } + unsafe impl { #[sel(initWith:another:)] fn init_with( 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/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/declare_class.rs b/objc2/src/macros/declare_class.rs index 2df15dd08..fc59dcca9 100644 --- a/objc2/src/macros/declare_class.rs +++ b/objc2/src/macros/declare_class.rs @@ -144,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 @@ -162,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 @@ -186,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. @@ -199,11 +204,11 @@ 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 @@ -234,11 +239,15 @@ macro_rules! __inner_declare_class { /// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() }; /// /// declare_class! { -/// unsafe struct MyCustomObject: NSObject { +/// struct MyCustomObject { /// foo: u8, /// pub bar: c_int, /// } /// +/// unsafe impl ClassType for MyCustomObject { +/// type Superclass = NSObject; +/// } +/// /// unsafe impl { /// #[sel(initWithFoo:)] /// fn init_with(&mut self, foo: u8) -> Option<&mut Self> { @@ -367,10 +376,15 @@ 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,)* } + unsafe impl ClassType for $for:ty { + $(#[inherits($($inheritance_rest:ty)+)])? + type Superclass = $superclass:ty; + } + $( $(#[$impl_m:meta])* unsafe impl $(protocol $protocol:ident)? { @@ -394,7 +408,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. @@ -402,11 +416,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` @@ -415,7 +433,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), diff --git a/objc2/src/macros/extern_class.rs b/objc2/src/macros/extern_class.rs index e258d3ae8..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; +/// +/// // Specify the superclass, in this case `NSObject` +/// unsafe impl ClassType for NSFormatter { +/// type Superclass = NSObject; +/// } +/// ); /// -/// // Provided by the macro (it implements `ClassType`) +/// // 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,)* + } }; } diff --git a/objc2/src/rc/test_object.rs b/objc2/src/rc/test_object.rs index 2d2c3f946..2bffbb191 100644 --- a/objc2/src/rc/test_object.rs +++ b/objc2/src/rc/test_object.rs @@ -52,7 +52,11 @@ 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 ClassType for RcTestObject { + type Superclass = NSObject; + } unsafe impl { #[sel(alloc)] 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)