Skip to main content

objc2/macros/
extern_class.rs

1/// Create a new type to represent a class.
2///
3/// This is similar to an `@interface` declaration in Objective-C.
4///
5/// It is useful for things like `objc2-foundation`, which needs to create
6/// interfaces to existing, externally defined classes like `NSString`,
7/// `NSURL` and so on, but can also be useful for users that have custom
8/// classes written in Objective-C that they want to access from Rust.
9///
10///
11/// # Specification
12///
13/// The syntax is similar enough to Rust syntax that if you invoke the macro
14/// with parentheses (as opposed to curly brackets), [`rustfmt` will be able to
15/// format the contents][rustfmt-macros] (so e.g. as `extern_class!( ... );`).
16///
17/// The macro creates an opaque struct containing the superclass (which means
18/// that auto traits are inherited from the superclass), and implements the
19/// following traits for it to allow easier usage as an Objective-C object:
20///
21/// - [`RefEncode`][crate::RefEncode]
22/// - [`Message`][crate::Message]
23/// - [`Deref<Target = $superclass>`][core::ops::Deref]
24/// - [`ClassType`][crate::ClassType]
25/// - [`DowncastTarget`][$crate::DowncastTarget]
26/// - [`AsRef<$inheritance_chain>`][AsRef]
27/// - [`Borrow<$inheritance_chain>`][core::borrow::Borrow]
28///
29/// If generics are specified, these will be placed in a [`PhantomData`].
30///
31/// [rustfmt-macros]: https://github.com/rust-lang/rustfmt/discussions/5437
32/// [`PhantomData`]: core::marker::PhantomData
33///
34///
35/// ## Attributes
36///
37/// You can add most normal attributes to the class, including `#[cfg(...)]`,
38/// `#[allow(...)]` and doc comments.
39///
40/// Exceptions and special attributes are noted below.
41///
42///
43/// ### `#[unsafe(super(...))]` (required)
44///
45/// Controls the [superclass][crate::ClassType::Super] and the rest of the
46/// inheritance chain. This attribute is required.
47///
48/// Due to Rust trait limitations, specifying e.g. the superclass `NSData`
49/// would not give you the ability to convert via `AsRef` to `NSObject`.
50/// Therefore, you can optionally specify additional parts of the inheritance
51/// in this attribute.
52///
53///
54/// ### `#[thread_kind = ...]` (optional)
55///
56/// Controls the [thread kind][crate::ClassType::ThreadKind], i.e. it can be
57/// set to [`MainThreadOnly`] if the object is only usable on the main thread.
58///
59/// [`MainThreadOnly`]: crate::MainThreadOnly
60///
61///
62/// ### `#[name = "..."]` (optional)
63///
64/// Controls the [name][crate::ClassType::NAME] of the class.
65///
66/// If not specified, this will default to the struct name.
67///
68///
69/// ### `#[derive(...)]`
70///
71/// This is overridden, and only works with [`PartialEq`], [`Eq`], [`Hash`]
72/// and [`Debug`].
73///
74/// [`Hash`]: std::hash::Hash
75/// [`Debug`]: std::fmt::Debug
76///
77///
78/// ### `#[cfg_attr(..., ...)]`
79///
80/// This is only supported for attributes that apply to the struct itself
81/// (i.e. not supported for attributes that apply to implementations, or any
82/// of the custom attributes).
83///
84///
85/// ### `#[repr(...)]`
86///
87/// Not allowed (the macro uses this attribute internally).
88///
89///
90/// # Safety
91///
92/// When writing `#[unsafe(super(...))]`, you must ensure that:
93/// 1. The first superclass is correct.
94/// 2. The thread kind is set to `MainThreadOnly` if the class can only be
95///    used from the main thread.
96///
97///
98/// # Examples
99///
100/// Create a new type to represent the `NSFormatter` class (for demonstration,
101/// `objc2_foundation::NSFormatter` exist for exactly this purpose).
102///
103/// ```
104/// # #[cfg(not_available)]
105/// use objc2_foundation::{NSCoding, NSCopying, NSObjectProtocol};
106/// # use objc2::runtime::NSObjectProtocol;
107/// use objc2::rc::Retained;
108/// use objc2::runtime::NSObject;
109/// use objc2::{extern_class, extern_conformance, msg_send, ClassType};
110///
111/// extern_class!(
112///     /// An example description, to show that doc comments work.
113///     // Specify the superclass, in this case `NSObject`
114///     #[unsafe(super(NSObject))]
115///     // We could specify that the class is only usable on the main thread.
116///     // #[thread_kind = MainThreadOnly];
117///     // And specify the name of the class, if it differed from the struct.
118///     // #[name = "NSFormatter"];
119///     // These derives use the superclass' implementation.
120///     #[derive(PartialEq, Eq, Hash, Debug)]
121///     pub struct NSFormatter;
122/// );
123///
124/// // Note: We have to specify the protocols for the superclasses as well,
125/// // since Rust doesn't do inheritance.
126/// extern_conformance!(unsafe impl NSObjectProtocol for NSFormatter {});
127/// # #[cfg(not_available)]
128/// extern_conformance!(unsafe impl NSCopying for NSFormatter {});
129/// # #[cfg(not_available)]
130/// extern_conformance!(unsafe impl NSCoding for NSFormatter {});
131///
132/// fn main() {
133///     // Provided by the implementation of `ClassType`
134///     let cls = NSFormatter::class();
135///
136///     // `NSFormatter` implements `Message`:
137///     let obj: Retained<NSFormatter> = unsafe { msg_send![cls, new] };
138/// }
139/// ```
140///
141/// Represent the `NSDateFormatter` class, using the `NSFormatter` type we
142/// declared previously to specify as its superclass.
143///
144/// ```
145/// # #[cfg(not_available)]
146/// use objc2_foundation::{NSCoding, NSCopying, NSObjectProtocol};
147/// # use objc2::runtime::NSObjectProtocol;
148/// use objc2::runtime::NSObject;
149/// use objc2::{extern_class, extern_conformance, ClassType};
150/// #
151/// # extern_class!(
152/// #     #[unsafe(super(NSObject))]
153/// #     #[derive(PartialEq, Eq, Hash, Debug)]
154/// #     pub struct NSFormatter;
155/// # );
156///
157/// extern_class!(
158///     // Specify the correct inheritance chain
159///     #[unsafe(super(NSFormatter, NSObject))]
160///     #[derive(PartialEq, Eq, Hash, Debug)]
161///     pub struct NSDateFormatter;
162/// );
163///
164/// // Similarly, we can specify the protocols that this implements here:
165/// extern_conformance!(unsafe impl NSObjectProtocol for NSDateFormatter {});
166/// # #[cfg(not_available)]
167/// extern_conformance!(unsafe impl NSCopying for NSDateFormatter {});
168/// # #[cfg(not_available)]
169/// extern_conformance!(unsafe impl NSCoding for NSDateFormatter {});
170/// ```
171///
172/// See the source code of `objc2-foundation` for many more examples.
173#[doc(alias = "@interface")]
174#[macro_export]
175macro_rules! extern_class {
176    (
177        // The following special attributes are supported:
178        // - #[unsafe(super($($superclasses:path),*))]
179        // - #[unsafe(super = $superclass:path)]
180        // - #[thread_kind = $thread_kind:path]
181        // - #[name = $name:literal]
182        //
183        // As well as the following standard attributes:
184        // - #[derive(Eq, PartialEq, Hash, Debug)] (only those four are supported)
185        // - #[cfg(...)]
186        // - #[cfg_attr(..., ...)] (only for standard attributes)
187        // - #[doc(...)]
188        // - #[deprecated(...)]
189        // - #[allow/expect/warn/deny/forbid]
190        //
191        // Note that `#[repr(...)]` and `#[non_exhaustive]` are intentionally not supported.
192        $(#[$($attrs:tt)*])*
193        $v:vis struct $class:ident;
194    ) => {
195        $crate::__extract_struct_attributes! {
196            ($(#[$($attrs)*])*)
197
198            ($crate::__extern_class_inner)
199            ($v)
200            ($class)
201            () // No generics
202        }
203    };
204    (
205        // Generic version. Currently pretty ill supported.
206        $(#[$($attrs:tt)*])*
207        $v:vis struct $class:ident<
208            $($generic:ident $(: $(?$bound_sized:ident)? $($bound:ident)?)? $(= $default:ty)?),*
209            $(,)?
210        >;
211    ) => {
212        $crate::__extract_struct_attributes! {
213            ($(#[$($attrs)*])*)
214
215            ($crate::__extern_class_inner)
216            ($v)
217            ($class)
218            ($(
219                ($generic)
220                ($($(?$bound_sized)? $($bound)?)?)
221                ($($default)?)
222            )*)
223        }
224    };
225}
226
227#[doc(hidden)]
228#[macro_export]
229macro_rules! __extern_class_inner {
230    (
231        ($v:vis)
232        ($class:ident)
233        ($($(
234            ($generic:ident)
235            ($($($bounds:tt)+)?)
236            ($($default:ty)?)
237        )+)?)
238
239        ($($safety:tt $superclass:path $(, $superclasses:path)* $(,)?)?)
240        ($($($thread_kind:tt)+)?)
241        ($($name:tt)*)
242        ($($ivars:tt)*)
243        ($($derives:tt)*)
244        ($($attr_struct:tt)*)
245        ($($attr_impl:tt)*)
246    ) => {
247        // Ensure that the type has the same layout as the superclass.
248        // #[repr(transparent)] doesn't work because the superclass is a ZST.
249        #[repr(C)]
250        $($attr_struct)*
251        $v struct $class $(<$($generic $(: $($bounds)+)? $(= $default)?),*>)? {
252            __superclass: $crate::__fallback_if_not_set! {
253                ($($superclass)?)
254                // For diagnostics / rust-analyzer's sake, we choose a default
255                // superclass so that we can continue compiling, even though
256                // we're going to error if the super class is not set in
257                // `__extern_class_check_super_unsafe` below.
258                ($crate::runtime::NSObject)
259            },
260            // Bind generics (and make them invariant).
261            $(__generics: $crate::__macro_helpers::PhantomData<($(*mut $generic),+)>,)?
262        }
263
264        $crate::__extern_class_impl_traits! {
265            ($($attr_impl)*)
266            (unsafe impl $(<$($generic: $($($bounds)+ +)? $crate::Message),+>)?)
267            ($class $(<$($generic),*>)?)
268            ($($superclass, $($superclasses,)*)? $crate::runtime::AnyObject)
269        }
270
271        $crate::__extern_class_derives! {
272            ($($attr_impl)*)
273            (impl $(<$($generic: $($($bounds)+)?),+>)?)
274            ($class $(<$($generic),*>)?)
275            ($($derives)*)
276        }
277
278        // SAFETY: This maps `SomeClass<T, ...>` to a single `SomeClass<AnyObject, ...>` type and
279        // implements `DowncastTarget` on that type. This is safe because the "base container" class
280        // is the same and each generic argument is replaced with `AnyObject`, which can represent
281        // any Objective-C class instance.
282        $($attr_impl)*
283        unsafe impl $crate::DowncastTarget for $class $(<$($crate::__extern_class_map_anyobject!($generic)),+>)? {}
284
285        $($attr_impl)*
286        unsafe impl $(<$($generic $(: $($bounds)+ + $crate::Message)?),*>)? $crate::ClassType for $class $(<$($generic),*>)? {
287            type Super = $crate::__fallback_if_not_set! {
288                ($($superclass)?)
289                // See __superclass, this is still just for better diagnostics.
290                ($crate::runtime::NSObject)
291            };
292
293            type ThreadKind = $crate::__fallback_if_not_set! {
294                ($(dyn ($($thread_kind)+))?)
295                // Default to the super class' thread kind
296                (<<Self as $crate::ClassType>::Super as $crate::ClassType>::ThreadKind)
297            };
298
299            const NAME: &'static $crate::__macro_helpers::str = $crate::__fallback_if_not_set! {
300                ($($name)*)
301                ($crate::__macro_helpers::stringify!($class))
302            };
303
304            #[inline]
305            fn class() -> &'static $crate::runtime::AnyClass {
306                let _ = <Self as $crate::__macro_helpers::ValidThreadKind<<Self as $crate::ClassType>::ThreadKind>>::check;
307                let _ = <Self as $crate::__macro_helpers::MainThreadOnlyDoesNotImplSendSync<_>>::check;
308                let _ = <Self as $crate::__macro_helpers::DoesNotImplDrop<_>>::check;
309
310                $crate::__class_inner!($crate::__fallback_if_not_set! {
311                    ($($name)*)
312                    ($crate::__macro_helpers::stringify!($class))
313                }, $crate::__hash_idents!($class))
314            }
315
316            #[inline]
317            fn as_super(&self) -> &Self::Super {
318                &self.__superclass
319            }
320
321            const __INNER: () = ();
322
323            type __SubclassingType = Self;
324        }
325
326        $($attr_impl)*
327        $crate::__extern_class_check_super_unsafe!($($safety $superclass)?);
328
329        $($attr_impl)*
330        $crate::__extern_class_check_no_ivars!($($ivars)*);
331    };
332}
333
334#[doc(hidden)]
335#[macro_export]
336macro_rules! __extern_class_check_super_unsafe {
337    (unsafe $($superclass:tt)+) => {};
338    (safe $($superclass:tt)+) => {
339        $crate::__macro_helpers::compile_error!(
340            "#[super(...)] must be wrapped in `unsafe`, as in #[unsafe(super(...))]"
341        );
342    };
343    () => {
344        $crate::__macro_helpers::compile_error!(
345            "must specify the superclass with #[unsafe(super(...))]"
346        );
347    };
348}
349
350#[doc(hidden)]
351#[macro_export]
352macro_rules! __extern_class_map_anyobject {
353    ($t:ident) => {
354        $crate::runtime::AnyObject
355    };
356}
357
358#[doc(hidden)]
359#[macro_export]
360macro_rules! __extern_class_check_no_ivars {
361    () => {};
362    ($($ivars:tt)*) => {
363        $crate::__macro_helpers::compile_error!("#[ivars] is not supported in extern_class!");
364    };
365}
366
367#[doc(hidden)]
368#[macro_export]
369macro_rules! __extern_class_impl_traits {
370    (
371        ($($attr_impl:tt)*)
372        (unsafe impl $($after_impl:tt)*)
373        ($($for:tt)*)
374        ($superclass:path $(, $remaining_superclasses:path)*)
375    ) => {
376        // SAFETY:
377        // - The item is FFI-safe with `#[repr(C)]`.
378        // - The encoding is taken from the inner item, and caller verifies
379        //   that it actually inherits said object.
380        // - The rest of the struct's fields are ZSTs, so they don't influence
381        //   the layout.
382        //
383        // Be aware that very rarely, this implementation is wrong because the
384        // class' instances do not have the encoding `Encoding::Object`.
385        //
386        // A known case is that `NSAutoreleasePool` has a different encoding.
387        // This should be fairly problem-free though, since that is still
388        // valid in Objective-C to represent that class' instances as
389        // `NSObject*`.
390        $($attr_impl)*
391        unsafe impl $($after_impl)* $crate::RefEncode for $($for)* {
392            const ENCODING_REF: $crate::Encoding
393                = <$superclass as $crate::RefEncode>::ENCODING_REF;
394        }
395
396        // SAFETY: This is a newtype wrapper over `AnyObject` (we even ensure
397        // that `AnyObject` is always last in our inheritance tree), so it is
398        // always safe to reinterpret as that.
399        //
400        // That the object must work with standard memory management is
401        // properly upheld by the fact that the superclass is required by
402        // `ValidThreadKind` to implement `ClassType`, and hence must also be
403        // a subclass of one of `NSObject`, `NSProxy` or some other class that
404        // ensures this (e.g. the object itself is not a root class).
405        $($attr_impl)*
406        unsafe impl $($after_impl)* $crate::Message for $($for)* {}
407
408        // SAFETY: An instance can always be _used_ in exactly the same way as
409        // its superclasses (though not necessarily _constructed_ in the same
410        // way, but `Deref` doesn't allow this).
411        //
412        // Remember; while we (the Rust side) may intentionally be forgetting
413        // which instance we're holding, the Objective-C side will remember,
414        // and will always dispatch to the correct method implementations.
415        //
416        // TODO: If the object has a lifetime, we must keep that lifetime
417        // information, since all objects can be retained using
418        // `Message::retain`, and that could possibly make it unsound to allow
419        // non-`'static` here.
420        //
421        // `&NSMutableArray<T>` -> `&NSArray<T>` -> `Retained<NSArray<T>>` is
422        // fine, but `&UserClass<'a>` -> `&NSObject` -> `Retained<NSObject>`
423        // is not, and hence `&NSArray<UserClass<'a>>` -> `&NSObject` ->
424        // `Retained<NSObject>` isn't either.
425        $($attr_impl)*
426        impl $($after_impl)* $crate::__macro_helpers::Deref for $($for)* {
427            type Target = $superclass;
428
429            #[inline]
430            fn deref(&self) -> &Self::Target {
431                &self.__superclass
432            }
433        }
434
435        $($attr_impl)*
436        impl $($after_impl)* $crate::__macro_helpers::AsRef<Self> for $($for)* {
437            #[inline]
438            fn as_ref(&self) -> &Self {
439                self
440            }
441        }
442
443        $crate::__extern_class_impl_as_ref_borrow! {
444            ($superclass $(, $remaining_superclasses)*)
445
446            ($($attr_impl)*)
447            (impl $($after_impl)*)
448            ($($for)*)
449            fn as_ref(&self) {
450                // Triggers Deref coercion depending on return type
451                &*self
452            }
453        }
454    };
455}
456
457#[doc(hidden)]
458#[macro_export]
459macro_rules! __extern_class_impl_as_ref_borrow {
460    // Base case
461    {
462        ()
463
464        ($($attr_impl:tt)*)
465        (impl $($after_impl:tt)*)
466        ($($for:tt)*)
467        fn as_ref($($self:tt)*) $as_ref:block
468    } => {};
469
470    // For each superclass
471    {
472        ($superclass:path $(, $remaining_superclasses:path)*)
473
474        ($($attr_impl:tt)*)
475        (impl $($after_impl:tt)*)
476        ($($for:tt)*)
477        fn as_ref($($self:tt)*) $as_ref:block
478    } => {
479        $($attr_impl)*
480        impl $($after_impl)* $crate::__macro_helpers::AsRef<$superclass> for $($for)* {
481            #[inline]
482            fn as_ref($($self)*) -> &$superclass $as_ref
483        }
484
485        // Borrow is correct, since subclasses behaves identical to the class
486        // they inherit (message sending doesn't care).
487        //
488        // In particular, `Eq`, `Ord` and `Hash` all give the same results
489        // after borrow.
490
491        $($attr_impl)*
492        impl $($after_impl)* $crate::__macro_helpers::Borrow<$superclass> for $($for)* {
493            #[inline]
494            fn borrow($($self)*) -> &$superclass $as_ref
495        }
496
497        $crate::__extern_class_impl_as_ref_borrow! {
498            ($($remaining_superclasses),*)
499
500            ($($attr_impl)*)
501            (impl $($after_impl)*)
502            ($($for)*)
503            fn as_ref($($self)*) $as_ref
504        }
505    };
506}
507
508/// Note: We intentionally don't add e.g. `T: PartialEq`, as generic objects
509/// are always comparable, hashable and debuggable, regardless of their
510/// generic parameters.
511#[doc(hidden)]
512#[macro_export]
513macro_rules! __extern_class_derives {
514    // Base case
515    (
516        ($($attr_impl:tt)*)
517        (impl $($after_impl:tt)*)
518        ($($for:tt)*)
519        ($(,)*)
520    ) => {};
521
522    // Debug
523    (
524        ($($attr_impl:tt)*)
525        (impl $($after_impl:tt)*)
526        ($($for:tt)*)
527        (
528            $(,)*
529            Debug
530            $($rest:tt)*
531        )
532    ) => {
533        $($attr_impl)*
534        #[automatically_derived]
535        impl $($after_impl)* $crate::__macro_helpers::fmt::Debug for $($for)* {
536            fn fmt(&self, f: &mut $crate::__macro_helpers::fmt::Formatter<'_>) -> $crate::__macro_helpers::fmt::Result {
537                // Delegate to the superclass
538                $crate::__macro_helpers::fmt::Debug::fmt(&self.__superclass, f)
539            }
540        }
541
542        $crate::__extern_class_derives! {
543            ($($attr_impl)*)
544            (impl $($after_impl)*)
545            ($($for)*)
546            ($($rest)*)
547        }
548    };
549
550    // PartialEq
551    (
552        ($($attr_impl:tt)*)
553        (impl $($after_impl:tt)*)
554        ($($for:tt)*)
555        (
556            $(,)*
557            PartialEq
558            $($rest:tt)*
559        )
560    ) => {
561        $($attr_impl)*
562        #[automatically_derived]
563        impl $($after_impl)* $crate::__macro_helpers::PartialEq for $($for)* {
564            #[inline]
565            fn eq(&self, other: &Self) -> $crate::__macro_helpers::bool {
566                // Delegate to the superclass
567                $crate::__macro_helpers::PartialEq::eq(&self.__superclass, &other.__superclass)
568            }
569        }
570
571        $crate::__extern_class_derives! {
572            ($($attr_impl)*)
573            (impl $($after_impl)*)
574            ($($for)*)
575            ($($rest)*)
576        }
577    };
578
579    // Eq
580    (
581        ($($attr_impl:tt)*)
582        (impl $($after_impl:tt)*)
583        ($($for:tt)*)
584        (
585            $(,)*
586            Eq
587            $($rest:tt)*
588        )
589    ) => {
590        $($attr_impl)*
591        #[automatically_derived]
592        impl $($after_impl)* $crate::__macro_helpers::Eq for $($for)* {}
593
594        $crate::__extern_class_derives! {
595            ($($attr_impl)*)
596            (impl $($after_impl)*)
597            ($($for)*)
598            ($($rest)*)
599        }
600    };
601
602    // Hash
603    (
604        ($($attr_impl:tt)*)
605        (impl $($after_impl:tt)*)
606        ($($for:tt)*)
607        (
608            $(,)*
609            Hash
610            $($rest:tt)*
611        )
612    ) => {
613        $($attr_impl)*
614        #[automatically_derived]
615        impl $($after_impl)* $crate::__macro_helpers::Hash for $($for)* {
616            #[inline]
617            fn hash<H: $crate::__macro_helpers::Hasher>(&self, state: &mut H) {
618                // Delegate to the superclass
619                $crate::__macro_helpers::Hash::hash(&self.__superclass, state)
620            }
621        }
622
623        $crate::__extern_class_derives! {
624            ($($attr_impl)*)
625            (impl $($after_impl)*)
626            ($($for)*)
627            ($($rest)*)
628        }
629    };
630
631    // Unhandled derive
632    (
633        ($($attr_impl:tt)*)
634        (impl $($after_impl:tt)*)
635        ($($for:tt)*)
636        (
637            $(,)*
638            $derive:path
639            $(, $($rest:tt)*)?
640        )
641    ) => {
642        const _: () = {
643            // For better diagnostics.
644            #[derive($derive)]
645            struct Derive;
646        };
647        $crate::__macro_helpers::compile_error!($crate::__macro_helpers::stringify!(
648            #[derive($derive)] is not supported in extern_class!
649        ));
650
651        $crate::__extern_class_derives! {
652            ($($attr_impl)*)
653            (impl $($after_impl)*)
654            ($($for)*)
655            ($($($rest)*)?)
656        }
657    };
658}
659
660#[doc(hidden)]
661#[macro_export]
662macro_rules! __select_name {
663    ($_name:ident; $name_const:expr) => {
664        $name_const
665    };
666    ($name:ident;) => {
667        $crate::__macro_helpers::stringify!($name)
668    };
669}