diff --git a/objc2/src/foundation/__ns_string.rs b/objc2/src/foundation/__ns_string.rs index b8b3186fb..9827d484a 100644 --- a/objc2/src/foundation/__ns_string.rs +++ b/objc2/src/foundation/__ns_string.rs @@ -9,6 +9,9 @@ //! generating a pure `NSString`. We don't support that yet (since I don't //! know the use-case), but we definitely could! //! See: +//! +//! See also the following crates that implement UTF-16 conversion: +//! `utf16_lit`, `windows`, `const_utf16`, `wide-literals`, ... use core::ffi::c_void; use core::mem::ManuallyDrop; use core::ptr; @@ -238,6 +241,12 @@ impl CachedNSString { /// Creates an [`NSString`][`crate::foundation::NSString`] from a static string. /// +/// Note: This works by placing statics in special sections, which may not +/// work completely reliably yet, see [#258]; until then, you should be +/// careful about using this in libraries intended for others to consume. +/// +/// [#258]: https://github.com/madsmtm/objc2/issues/258 +/// /// /// # Examples /// diff --git a/objc2/src/foundation/bundle.rs b/objc2/src/foundation/bundle.rs index c946545b1..9698469d2 100644 --- a/objc2/src/foundation/bundle.rs +++ b/objc2/src/foundation/bundle.rs @@ -3,7 +3,7 @@ use core::panic::{RefUnwindSafe, UnwindSafe}; use super::{NSCopying, NSDictionary, NSObject, NSString}; use crate::rc::{Id, Shared}; -use crate::{extern_class, extern_methods, msg_send_id, ns_string, ClassType}; +use crate::{extern_class, extern_methods, msg_send_id, ClassType}; extern_class!( /// A representation of the code and resources stored in a bundle @@ -36,13 +36,16 @@ extern_methods!( } pub fn name(&self) -> Option> { - self.info().get(ns_string!("CFBundleName")).map(|name| { - let ptr: *const NSObject = name; - let ptr: *const NSString = ptr.cast(); - // SAFETY: TODO - let name = unsafe { ptr.as_ref().unwrap_unchecked() }; - name.copy() - }) + // TODO: Use ns_string! + self.info() + .get(&NSString::from_str("CFBundleName")) + .map(|name| { + let ptr: *const NSObject = name; + let ptr: *const NSString = ptr.cast(); + // SAFETY: TODO + let name = unsafe { ptr.as_ref().unwrap_unchecked() }; + name.copy() + }) } } ); diff --git a/objc2/src/macros/declare_class.rs b/objc2/src/macros/declare_class.rs index 3720f4155..a2b8b5d28 100644 --- a/objc2/src/macros/declare_class.rs +++ b/objc2/src/macros/declare_class.rs @@ -1,305 +1,3 @@ -#[doc(hidden)] -#[macro_export] -macro_rules! __declare_class_rewrite_methods { - { - @($($macro:tt)*) - @($($macro_arguments:tt)*) - } => {}; - - // Unsafe variant - { - @($($macro:tt)*) - @($($macro_arguments:tt)*) - - $(#[$($m:tt)*])* - unsafe fn $name:ident($($args:tt)*) $(-> $ret:ty)? $body:block - - $($rest:tt)* - } => { - $crate::__rewrite_self_arg! { - ($($macro)*) - ($($args)*) - - // Split the function into parts, and send the arguments down to - // be used later on - @($($macro_arguments)*) - @($(#[$($m)*])*) - @(unsafe extern "C") - @($name) - @($($ret)?) - @($body) - // Will add @(kind) - // Will add @(args_start) - // Will add @(args_rest) - } - - $crate::__declare_class_rewrite_methods! { - @($($macro)*) - @($($macro_arguments)*) - - $($rest)* - } - }; - - // Safe variant - { - @($($macro:tt)*) - @($($macro_arguments:tt)*) - - $(#[$($m:tt)*])* - fn $name:ident($($args:tt)*) $(-> $ret:ty)? $body:block - - $($rest:tt)* - } => { - $crate::__rewrite_self_arg! { - ($($macro)*) - ($($args)*) - - @($($macro_arguments)*) - @($(#[$($m)*])*) - @(extern "C") - @($name) - @($($ret)?) - @($body) - - // Same as above - } - - $crate::__declare_class_rewrite_methods! { - @($($macro)*) - @($($macro_arguments)*) - - $($rest)* - } - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! __declare_class_method_out { - { - @() // No arguments needed - @($(#[$($m:tt)*])*) - @($($qualifiers:tt)*) - @($name:ident) - @($($ret:ty)?) - @($($body:tt)*) - @($($_kind:tt)*) - @($($args_start:tt)*) - @($($args_rest:tt)*) - } => { - $crate::__fn_args! { - ($crate::__declare_class_method_out) - ($($args_rest)*,) - () - () - @inner - @($(#[$($m)*])*) - @($($qualifiers)*) - @($name) - @($($ret)?) - @($($body)*) - @($($_kind)*) - @($($args_start)*) - // Will add @(args_converted) - // Will add @(body_prefix) - } - }; - - // No return type - { - @inner - @($(#[$($m:tt)*])*) - @($($qualifiers:tt)*) - @($name:ident) - @() - @($($body:tt)*) - @($($_kind:tt)*) - @($($args_start:tt)*) - @($($args_converted:tt)*) - @($($body_prefix:tt)*) - } => { - $crate::__attribute_helper! { - @strip_sel - $(@[$($m)*])* - ($($qualifiers)* fn $name($($args_start)* $($args_converted)*) { - $($body_prefix)* - $($body)* - }) - } - }; - - // With return type - { - @inner - @($(#[$($m:tt)*])*) - @($($qualifiers:tt)*) - @($name:ident) - @($ret:ty) - @($($body:tt)*) - @($($_kind:tt)*) - @($($args_start:tt)*) - @($($args_converted:tt)*) - @($($body_prefix:tt)*) - } => { - $crate::__attribute_helper! { - @strip_sel - $(@[$($m)*])* - ($($qualifiers)* fn $name($($args_start)* $($args_converted)*) -> <$ret as $crate::encode::EncodeConvert>::__Inner { - $($body_prefix)* - <$ret as $crate::encode::EncodeConvert>::__into_inner($($body)*) - }) - } - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! __declare_class_register_out { - // Class method - { - @($builder:ident) - @($(#[$($m:tt)*])*) - @($($qualifiers:tt)*) - @($name:ident) - @($($_ret:tt)*) - @($($_body:tt)*) - @(class_method) - @($($args_start:tt)*) - @($($args_rest:tt)*) - } => { - $builder.add_class_method( - $crate::__attribute_helper! { - @extract_sel - ($crate::__declare_class_register_out) - ($(#[$($m)*])*) - @call_sel - }, - Self::$name as $crate::__fn_ptr! { - @($($qualifiers)*) $($args_start)* $($args_rest)* - }, - ); - }; - - // Instance method - { - @($builder:ident) - @($(#[$($m:tt)*])*) - @($($qualifiers:tt)*) - @($name:ident) - @($($_ret:tt)*) - @($($_body:tt)*) - @(instance_method) - @($($args_start:tt)*) - @($($args_rest:tt)*) - } => { - $builder.add_method( - $crate::__attribute_helper! { - @extract_sel - ($crate::__declare_class_register_out) - ($(#[$($m)*])*) - @call_sel - }, - Self::$name as $crate::__fn_ptr! { - @($($qualifiers)*) $($args_start)* $($args_rest)* - }, - ); - }; - - { - @call_sel - @($($sel:tt)*) - } => { - $crate::sel!($($sel)*) - }; -} - -/// Create function pointer type with inferred arguments. -#[doc(hidden)] -#[macro_export] -macro_rules! __fn_ptr { - ( - @($($qualifiers:tt)*) - $($(mut)? $($param:ident)? $(_)?: $param_ty:ty),* $(,)? - ) => { - $($qualifiers)* fn($($crate::__fn_ptr!(@__to_anonymous $param_ty)),*) -> _ - }; - (@__to_anonymous $param_ty:ty) => { _ } -} - -#[doc(hidden)] -#[macro_export] -macro_rules! __fn_args { - // Ignore `_` - { - ($out_macro:path) - (_: $param_ty:ty, $($rest:tt)*) - ($($args_converted:tt)*) - ($($body_prefix:tt)*) - $($macro_args:tt)* - } => { - $crate::__fn_args! { - ($out_macro) - ($($rest)*) - ($($args_converted)* _: $param_ty,) - ($($body_prefix)*) - $($macro_args)* - } - }; - // Convert mut - { - ($out_macro:path) - (mut $param:ident: $param_ty:ty, $($rest:tt)*) - ($($args_converted:tt)*) - ($($body_prefix:tt)*) - $($macro_args:tt)* - } => { - $crate::__fn_args! { - ($out_macro) - ($($rest)*) - ($($args_converted)* $param: <$param_ty as $crate::encode::EncodeConvert>::__Inner,) - ( - $($body_prefix)* - let mut $param = <$param_ty as $crate::encode::EncodeConvert>::__from_inner($param); - ) - $($macro_args)* - } - }; - // Convert - { - ($out_macro:path) - ($param:ident: $param_ty:ty, $($rest:tt)*) - ($($args_converted:tt)*) - ($($body_prefix:tt)*) - $($macro_args:tt)* - } => { - $crate::__fn_args! { - ($out_macro) - ($($rest)*) - ($($args_converted)* $param: <$param_ty as $crate::encode::EncodeConvert>::__Inner,) - ( - $($body_prefix)* - let $param = <$param_ty as $crate::encode::EncodeConvert>::__from_inner($param); - ) - $($macro_args)* - } - }; - // Output result - { - ($out_macro:path) - ($(,)*) - ($($args_converted:tt)*) - ($($body_prefix:tt)*) - $($macro_args:tt)* - } => { - $out_macro! { - $($macro_args)* - @($($args_converted)*) - @($($body_prefix)*) - } - }; -} - /// Declare a new Objective-C class. /// /// This is mostly just a convenience macro on top of [`extern_class!`] and @@ -603,260 +301,570 @@ macro_rules! declare_class { $($ivar_v:vis $ivar:ident: $ivar_ty:ty,)* } - unsafe impl ClassType for $for:ty { - $(#[inherits($($inheritance_rest:ty),+)])? - type Super = $superclass:ty; + unsafe impl ClassType for $for:ty { + $(#[inherits($($inheritance_rest:ty),+)])? + type Super = $superclass:ty; + + $(const NAME: &'static str = $name_const:literal;)? + } + + $($methods:tt)* + } => { + $( + #[allow(non_camel_case_types)] + $ivar_v struct $ivar { + __priv: (), + } + + unsafe impl $crate::declare::IvarType for $ivar { + type Type = $ivar_ty; + const NAME: &'static str = stringify!($ivar); + } + )* + + $crate::__inner_extern_class! { + @__inner + $(#[$m])* + // SAFETY: Upheld by caller + $v struct ($name) { + // SAFETY: + // - The ivars are in a type used as an Objective-C object. + // - The ivar is added to the class below. + // - Rust prevents having two fields with the same name. + // - Caller upholds that the ivars are properly initialized + // + // Note that while I couldn't find a reference on whether + // ivars are zero-initialized or not, it has been true since + // the Objective-C version shipped with Mac OS X 10.0 [link] + // and is generally what one would expect coming from C. So I + // think it's a valid assumption to make! + // + // [link]: https://github.com/apple-oss-distributions/objc4/blob/objc4-208/runtime/objc-class.m#L367 + $($ivar_v $ivar: $crate::declare::Ivar<$ivar>,)* + } + + unsafe impl () for $for { + INHERITS = [$superclass, $($($inheritance_rest,)+)? $crate::runtime::Object]; + } + } + + // Creation + unsafe impl ClassType for $for { + type Super = $superclass; + const NAME: &'static str = $crate::__select_name!($name; $($name_const)?); + + fn class() -> &'static $crate::runtime::Class { + // TODO: Use `core::cell::LazyCell` + use $crate::__macro_helpers::Once; + + static REGISTER_CLASS: Once = Once::new(); + + REGISTER_CLASS.call_once(|| { + let superclass = <$superclass as $crate::ClassType>::class(); + let mut builder = $crate::declare::ClassBuilder::new(Self::NAME, superclass).unwrap_or_else(|| { + $crate::__macro_helpers::panic!( + "could not create new class {}. Perhaps a class with that name already exists?", + Self::NAME, + ) + }); + + // Ivars + $( + builder.add_static_ivar::<$ivar>(); + )* + + // Check whether we need to add a `dealloc` method + if false $( + || <<$ivar as $crate::declare::IvarType>::Type as $crate::declare::InnerIvarType>::__MAY_DROP + )* { + unsafe { + builder.add_method( + $crate::sel!(dealloc), + Self::__objc2_dealloc as unsafe extern "C" fn(_, _), + ); + } + } + + // Implement protocols and methods + $crate::__declare_class_methods!( + @register_out(builder) + $($methods)* + ); + + let _cls = builder.register(); + }); + + // We just registered the class, so it should be available + $crate::runtime::Class::get(Self::NAME).unwrap() + } + + #[inline] + fn as_super(&self) -> &Self::Super { + &self.__inner + } + + #[inline] + fn as_super_mut(&mut self) -> &mut Self::Super { + &mut self.__inner + } + } + + impl $for { + unsafe extern "C" fn __objc2_dealloc(&mut self, _cmd: $crate::runtime::Sel) { + $( + let ptr: *mut $crate::declare::Ivar<$ivar> = &mut self.$ivar; + // SAFETY: The ivar is valid, and since this is the + // `dealloc` method, we know the ivars are never going to + // be touched again. + unsafe { $crate::__macro_helpers::drop_in_place(ptr) }; + )* + } + } + + // Methods + $crate::__declare_class_methods!( + @method_out + $($methods)* + ); + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __select_name { + ($_name:ident; $name_const:literal) => { + $name_const + }; + ($name:ident;) => { + $crate::__macro_helpers::stringify!($name) + }; +} + +#[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::__declare_class_rewrite_methods! { + @($crate::__declare_class_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::__declare_class_rewrite_methods! { + @($crate::__declare_class_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 + #[allow(unused_mut)] + let mut protocol_builder = $builder.__add_protocol_methods( + $crate::runtime::Protocol::get(stringify!($protocol)).unwrap_or_else(|| { + $crate::__macro_helpers::panic!( + "could not find protocol {}", + $crate::__macro_helpers::stringify!($protocol), + ) + }) + ); + + // SAFETY: Upheld by caller + #[allow(unused_unsafe)] + unsafe { + $crate::__declare_class_rewrite_methods! { + @($crate::__declare_class_register_out) + @(protocol_builder) + + $($methods)* + } + } + + // Finished declaring protocol; get error message if any + #[allow(clippy::drop_ref)] + drop(protocol_builder); - $(const NAME: &'static str = $name_const:literal;)? + $crate::__declare_class_methods!( + @register_out($builder) + $($rest)* + ); + }; + // Without protocol + ( + @register_out($builder:ident) + + $(#[$m:meta])* + unsafe impl $for:ty { + $($methods:tt)* } - $($methods:tt)* - } => { - $( - #[allow(non_camel_case_types)] - $ivar_v struct $ivar { - __priv: (), - } + $($rest:tt)* + ) => { + // SAFETY: Upheld by caller + #[allow(unused_unsafe)] + unsafe { + $crate::__declare_class_rewrite_methods! { + @($crate::__declare_class_register_out) + @($builder) - unsafe impl $crate::declare::IvarType for $ivar { - type Type = $ivar_ty; - const NAME: &'static str = stringify!($ivar); + $($methods)* } - )* + } - $crate::__inner_extern_class! { - @__inner - $(#[$m])* - // SAFETY: Upheld by caller - $v struct ($name) { - // SAFETY: - // - The ivars are in a type used as an Objective-C object. - // - The ivar is added to the class below. - // - Rust prevents having two fields with the same name. - // - Caller upholds that the ivars are properly initialized. - $($ivar_v $ivar: $crate::declare::Ivar<$ivar>,)* - } + $crate::__declare_class_methods!( + @register_out($builder) + $($rest)* + ); + }; +} - unsafe impl () for $for { - INHERITS = [$superclass, $($($inheritance_rest,)+)? $crate::runtime::Object]; - } +#[doc(hidden)] +#[macro_export] +macro_rules! __declare_class_rewrite_methods { + { + @($($macro:tt)*) + @($($macro_arguments:tt)*) + } => {}; + + // Unsafe variant + { + @($($macro:tt)*) + @($($macro_arguments:tt)*) + + $(#[$($m:tt)*])* + unsafe fn $name:ident($($args:tt)*) $(-> $ret:ty)? $body:block + + $($rest:tt)* + } => { + $crate::__rewrite_self_arg! { + ($($macro)*) + ($($args)*) + + // Split the function into parts, and send the arguments down to + // be used later on + @($($macro_arguments)*) + @($(#[$($m)*])*) + @(unsafe extern "C") + @($name) + @($($ret)?) + @($body) + // Will add @(kind) + // Will add @(args_start) + // Will add @(args_rest) } - // Creation - unsafe impl ClassType for $for { - type Super = $superclass; - const NAME: &'static str = $crate::__select_name!($name; $($name_const)?); + $crate::__declare_class_rewrite_methods! { + @($($macro)*) + @($($macro_arguments)*) - fn class() -> &'static $crate::runtime::Class { - // TODO: Use `core::cell::LazyCell` - use $crate::__macro_helpers::Once; + $($rest)* + } + }; - static REGISTER_CLASS: Once = Once::new(); + // Safe variant + { + @($($macro:tt)*) + @($($macro_arguments:tt)*) - REGISTER_CLASS.call_once(|| { - let superclass = <$superclass as $crate::ClassType>::class(); - let mut builder = $crate::declare::ClassBuilder::new(Self::NAME, superclass).unwrap_or_else(|| { - $crate::__macro_helpers::panic!( - "could not create new class {}. Perhaps a class with that name already exists?", - Self::NAME, - ) - }); + $(#[$($m:tt)*])* + fn $name:ident($($args:tt)*) $(-> $ret:ty)? $body:block - // Ivars - $( - builder.add_static_ivar::<$ivar>(); - )* + $($rest:tt)* + } => { + $crate::__rewrite_self_arg! { + ($($macro)*) + ($($args)*) - // Check whether we need to add a `dealloc` method - if false $( - || <<$ivar as $crate::declare::IvarType>::Type as $crate::declare::InnerIvarType>::__MAY_DROP - )* { - unsafe { - builder.add_method( - $crate::sel!(dealloc), - Self::__objc2_dealloc as unsafe extern "C" fn(_, _), - ); - } - } + @($($macro_arguments)*) + @($(#[$($m)*])*) + @(extern "C") + @($name) + @($($ret)?) + @($body) - // Implement protocols and methods - $crate::__declare_class_methods!( - @register_out(builder) - $($methods)* - ); + // Same as above + } - let _cls = builder.register(); - }); + $crate::__declare_class_rewrite_methods! { + @($($macro)*) + @($($macro_arguments)*) - // We just registered the class, so it should be available - $crate::runtime::Class::get(Self::NAME).unwrap() - } + $($rest)* + } + }; +} - #[inline] - fn as_super(&self) -> &Self::Super { - &self.__inner - } +#[doc(hidden)] +#[macro_export] +macro_rules! __declare_class_method_out { + { + @() // No arguments needed + @($(#[$($m:tt)*])*) + @($($qualifiers:tt)*) + @($name:ident) + @($($ret:ty)?) + @($($body:tt)*) + @($($_kind:tt)*) + @($($args_start:tt)*) + @($($args_rest:tt)*) + } => { + $crate::__fn_args! { + ($crate::__declare_class_method_out) + ($($args_rest)*,) + () + () + @inner + @($(#[$($m)*])*) + @($($qualifiers)*) + @($name) + @($($ret)?) + @($($body)*) + @($($_kind)*) + @($($args_start)*) + // Will add @(args_converted) + // Will add @(body_prefix) + } + }; - #[inline] - fn as_super_mut(&mut self) -> &mut Self::Super { - &mut self.__inner - } + // No return type + { + @inner + @($(#[$($m:tt)*])*) + @($($qualifiers:tt)*) + @($name:ident) + @() + @($($body:tt)*) + @($($_kind:tt)*) + @($($args_start:tt)*) + @($($args_converted:tt)*) + @($($body_prefix:tt)*) + } => { + $crate::__attribute_helper! { + @strip_sel + $(@[$($m)*])* + ($($qualifiers)* fn $name($($args_start)* $($args_converted)*) { + $($body_prefix)* + $($body)* + }) } + }; - impl $for { - unsafe extern "C" fn __objc2_dealloc(&mut self, _cmd: $crate::runtime::Sel) { - $( - let ptr: *mut $crate::declare::Ivar<$ivar> = &mut self.$ivar; - // SAFETY: The ivar is valid, and since this is the - // `dealloc` method, we know the ivars are never going to - // be touched again. - unsafe { $crate::__macro_helpers::drop_in_place(ptr) }; - )* - } + // With return type + { + @inner + @($(#[$($m:tt)*])*) + @($($qualifiers:tt)*) + @($name:ident) + @($ret:ty) + @($($body:tt)*) + @($($_kind:tt)*) + @($($args_start:tt)*) + @($($args_converted:tt)*) + @($($body_prefix:tt)*) + } => { + $crate::__attribute_helper! { + @strip_sel + $(@[$($m)*])* + ($($qualifiers)* fn $name($($args_start)* $($args_converted)*) -> <$ret as $crate::encode::EncodeConvert>::__Inner { + $($body_prefix)* + <$ret as $crate::encode::EncodeConvert>::__into_inner($($body)*) + }) } - - // Methods - $crate::__declare_class_methods!( - @method_out - $($methods)* - ); }; } #[doc(hidden)] #[macro_export] -macro_rules! __select_name { - ($_name:ident; $name_const:literal) => { - $name_const +macro_rules! __fn_args { + // Ignore `_` + { + ($out_macro:path) + (_: $param_ty:ty, $($rest:tt)*) + ($($args_converted:tt)*) + ($($body_prefix:tt)*) + $($macro_args:tt)* + } => { + $crate::__fn_args! { + ($out_macro) + ($($rest)*) + ($($args_converted)* _: $param_ty,) + ($($body_prefix)*) + $($macro_args)* + } }; - ($name:ident;) => { - $crate::__macro_helpers::stringify!($name) + // Convert mut + { + ($out_macro:path) + (mut $param:ident: $param_ty:ty, $($rest:tt)*) + ($($args_converted:tt)*) + ($($body_prefix:tt)*) + $($macro_args:tt)* + } => { + $crate::__fn_args! { + ($out_macro) + ($($rest)*) + ($($args_converted)* $param: <$param_ty as $crate::encode::EncodeConvert>::__Inner,) + ( + $($body_prefix)* + let mut $param = <$param_ty as $crate::encode::EncodeConvert>::__from_inner($param); + ) + $($macro_args)* + } + }; + // Convert + { + ($out_macro:path) + ($param:ident: $param_ty:ty, $($rest:tt)*) + ($($args_converted:tt)*) + ($($body_prefix:tt)*) + $($macro_args:tt)* + } => { + $crate::__fn_args! { + ($out_macro) + ($($rest)*) + ($($args_converted)* $param: <$param_ty as $crate::encode::EncodeConvert>::__Inner,) + ( + $($body_prefix)* + let $param = <$param_ty as $crate::encode::EncodeConvert>::__from_inner($param); + ) + $($macro_args)* + } + }; + // Output result + { + ($out_macro:path) + ($(,)*) + ($($args_converted:tt)*) + ($($body_prefix:tt)*) + $($macro_args:tt)* + } => { + $out_macro! { + $($macro_args)* + @($($args_converted)*) + @($($body_prefix)*) + } }; } #[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::__declare_class_rewrite_methods! { - @($crate::__declare_class_method_out) - @() - $($methods)* - } - } - - $crate::__declare_class_methods!( - @method_out - $($rest)* +macro_rules! __declare_class_register_out { + // Class method + { + @($builder:ident) + @($(#[$($m:tt)*])*) + @($($qualifiers:tt)*) + @($name:ident) + @($($_ret:tt)*) + @($($_body:tt)*) + @(class_method) + @($($args_start:tt)*) + @($($args_rest:tt)*) + } => { + $builder.add_class_method( + $crate::__attribute_helper! { + @extract_sel + ($crate::__declare_class_register_out) + ($(#[$($m)*])*) + @call_sel + }, + Self::$name as $crate::__fn_ptr! { + @($($qualifiers)*) $($args_start)* $($args_rest)* + }, ); }; - // Without protocol - ( - @method_out - - $(#[$m:meta])* - unsafe impl $for:ty { - $($methods:tt)* - } - - $($rest:tt)* - ) => { - $(#[$m])* - impl $for { - $crate::__declare_class_rewrite_methods! { - @($crate::__declare_class_method_out) - @() - $($methods)* - } - } - $crate::__declare_class_methods!( - @method_out - $($rest)* + // Instance method + { + @($builder:ident) + @($(#[$($m:tt)*])*) + @($($qualifiers:tt)*) + @($name:ident) + @($($_ret:tt)*) + @($($_body:tt)*) + @(instance_method) + @($($args_start:tt)*) + @($($args_rest:tt)*) + } => { + $builder.add_method( + $crate::__attribute_helper! { + @extract_sel + ($crate::__declare_class_register_out) + ($(#[$($m)*])*) + @call_sel + }, + Self::$name as $crate::__fn_ptr! { + @($($qualifiers)*) $($args_start)* $($args_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 - #[allow(unused_mut)] - let mut protocol_builder = $builder.__add_protocol_methods( - $crate::runtime::Protocol::get(stringify!($protocol)).unwrap_or_else(|| { - $crate::__macro_helpers::panic!( - "could not find protocol {}", - $crate::__macro_helpers::stringify!($protocol), - ) - }) - ); - - // SAFETY: Upheld by caller - #[allow(unused_unsafe)] - unsafe { - $crate::__declare_class_rewrite_methods! { - @($crate::__declare_class_register_out) - @(protocol_builder) - - $($methods)* - } - } - - // Finished declaring protocol; get error message if any - #[allow(clippy::drop_ref)] - drop(protocol_builder); - - $crate::__declare_class_methods!( - @register_out($builder) - $($rest)* - ); + { + @call_sel + @($($sel:tt)*) + } => { + $crate::sel!($($sel)*) }; - // Without protocol - ( - @register_out($builder:ident) - - $(#[$m:meta])* - unsafe impl $for:ty { - $($methods:tt)* - } +} - $($rest:tt)* +/// Create function pointer type with inferred arguments. +#[doc(hidden)] +#[macro_export] +macro_rules! __fn_ptr { + ( + @($($qualifiers:tt)*) + $($(mut)? $($param:ident)? $(_)?: $param_ty:ty),* $(,)? ) => { - // SAFETY: Upheld by caller - #[allow(unused_unsafe)] - unsafe { - $crate::__declare_class_rewrite_methods! { - @($crate::__declare_class_register_out) - @($builder) - - $($methods)* - } - } - - $crate::__declare_class_methods!( - @register_out($builder) - $($rest)* - ); + $($qualifiers)* fn($($crate::__fn_ptr!(@__to_anonymous $param_ty)),*) -> _ }; + (@__to_anonymous $param_ty:ty) => { _ } } diff --git a/test-ui/ui/fn_ptr_reference_encode.stderr b/test-ui/ui/fn_ptr_reference_encode.stderr index 4869f930a..08f133a53 100644 --- a/test-ui/ui/fn_ptr_reference_encode.stderr +++ b/test-ui/ui/fn_ptr_reference_encode.stderr @@ -4,7 +4,7 @@ error: implementation of `Encode` is not general enough | impls_encode(my_fn as extern "C" fn(&i32)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Encode` is not general enough | - = note: `Encode` would have to be implemented for the type `for<'r> extern "C" fn(&'r i32)` + = note: `Encode` would have to be implemented for the type `for<'a> extern "C" fn(&'a i32)` = note: ...but `Encode` is actually implemented for the type `extern "C" fn(&'0 i32)`, for some specific lifetime `'0` error: implementation of `Encode` is not general enough @@ -13,5 +13,5 @@ error: implementation of `Encode` is not general enough | let _encoding = ::ENCODING; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Encode` is not general enough | - = note: `Encode` would have to be implemented for the type `for<'r> extern "C" fn(&'r i32)` + = note: `Encode` would have to be implemented for the type `for<'a> extern "C" fn(&'a i32)` = note: ...but `Encode` is actually implemented for the type `extern "C" fn(&'0 i32)`, for some specific lifetime `'0` diff --git a/test-ui/ui/fn_ptr_reference_method.stderr b/test-ui/ui/fn_ptr_reference_method.stderr index ae8c71a66..613d556ea 100644 --- a/test-ui/ui/fn_ptr_reference_method.stderr +++ b/test-ui/ui/fn_ptr_reference_method.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `for<'r> extern "C" fn(_, _, &'r objc2::runtime::Object): MethodImplementation` is not satisfied +error[E0277]: the trait bound `for<'a> extern "C" fn(_, _, &'a objc2::runtime::Object): MethodImplementation` is not satisfied --> ui/fn_ptr_reference_method.rs | | builder.add_method(sel!(third:), my_fn as extern "C" fn(_, _, &Object)); - | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MethodImplementation` is not implemented for `for<'r> extern "C" fn(_, _, &'r objc2::runtime::Object)` + | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MethodImplementation` is not implemented for `for<'a> extern "C" fn(_, _, &'a objc2::runtime::Object)` | | | required by a bound introduced by this call | diff --git a/test-ui/ui/fn_ptr_reference_method2.stderr b/test-ui/ui/fn_ptr_reference_method2.stderr index ad6a6bb95..d44077502 100644 --- a/test-ui/ui/fn_ptr_reference_method2.stderr +++ b/test-ui/ui/fn_ptr_reference_method2.stderr @@ -4,7 +4,7 @@ error: implementation of `MethodImplementation` is not general enough | builder.add_method(sel!(first:), my_fn as extern "C" fn(&Object, _, _)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `MethodImplementation` is not general enough | - = note: `MethodImplementation` would have to be implemented for the type `for<'r> extern "C" fn(&'r objc2::runtime::Object, objc2::runtime::Sel, &objc2::runtime::Object)` + = note: `MethodImplementation` would have to be implemented for the type `for<'a> extern "C" fn(&'a objc2::runtime::Object, objc2::runtime::Sel, &objc2::runtime::Object)` = note: ...but `MethodImplementation` is actually implemented for the type `extern "C" fn(&'0 objc2::runtime::Object, objc2::runtime::Sel, &objc2::runtime::Object)`, for some specific lifetime `'0` error: implementation of `MethodImplementation` is not general enough @@ -13,7 +13,7 @@ error: implementation of `MethodImplementation` is not general enough | builder.add_method(sel!(both:), my_fn as extern "C" fn(&Object, Sel, &Object)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `MethodImplementation` is not general enough | - = note: `MethodImplementation` would have to be implemented for the type `for<'r, 's> extern "C" fn(&'r objc2::runtime::Object, objc2::runtime::Sel, &'s objc2::runtime::Object)` + = note: `MethodImplementation` would have to be implemented for the type `for<'a, 'b> extern "C" fn(&'a objc2::runtime::Object, objc2::runtime::Sel, &'b objc2::runtime::Object)` = note: ...but `MethodImplementation` is actually implemented for the type `extern "C" fn(&'0 objc2::runtime::Object, objc2::runtime::Sel, &objc2::runtime::Object)`, for some specific lifetime `'0` error: implementation of `MethodImplementation` is not general enough @@ -22,5 +22,5 @@ error: implementation of `MethodImplementation` is not general enough | builder.add_method(sel!(both:), my_fn as extern "C" fn(&Object, Sel, &Object)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `MethodImplementation` is not general enough | - = note: `MethodImplementation` would have to be implemented for the type `for<'r, 's> extern "C" fn(&'r objc2::runtime::Object, objc2::runtime::Sel, &'s objc2::runtime::Object)` + = note: `MethodImplementation` would have to be implemented for the type `for<'a, 'b> extern "C" fn(&'a objc2::runtime::Object, objc2::runtime::Sel, &'b objc2::runtime::Object)` = note: ...but `MethodImplementation` is actually implemented for the type `extern "C" fn(&objc2::runtime::Object, objc2::runtime::Sel, &'0 objc2::runtime::Object)`, for some specific lifetime `'0`