diff --git a/objc2-encode/src/encode.rs b/objc2-encode/src/encode.rs index bec6a89ad..81a2765df 100644 --- a/objc2-encode/src/encode.rs +++ b/objc2-encode/src/encode.rs @@ -430,8 +430,7 @@ encode_pointer_impls!( /// /// Ideally we'd implement it for all function pointers, but due to coherence /// issues, see , function -/// pointers that take arguments with "special lifetimes" (don't know the -/// termonology) don't get implemented properly. +/// pointers that are higher-ranked over lifetimes don't get implemented. /// /// We could fix it by adding those impls and allowing `coherence_leak_check`, /// but it would have to be done for _all_ references, `Option<&T>` and such as diff --git a/objc2-foundation/examples/class_with_lifetime.rs b/objc2-foundation/examples/class_with_lifetime.rs index a9ebded0f..309bd7628 100644 --- a/objc2-foundation/examples/class_with_lifetime.rs +++ b/objc2-foundation/examples/class_with_lifetime.rs @@ -1,7 +1,8 @@ +#![deny(unsafe_op_in_unsafe_fn)] use std::marker::PhantomData; use std::sync::Once; -use objc2::declare::ClassDecl; +use objc2::declare::ClassBuilder; use objc2::rc::{Id, Owned, Shared}; use objc2::runtime::{Class, Object, Sel}; use objc2::{msg_send, sel}; @@ -47,23 +48,30 @@ impl<'a> MyObject<'a> { fn class() -> &'static Class { MYOBJECT_REGISTER_CLASS.call_once(|| { let superclass = NSObject::class(); - let mut decl = ClassDecl::new("MyObject", superclass).unwrap(); - decl.add_ivar::>("_number_ptr"); - - extern "C" fn init_with_ptr(this: &mut Object, _cmd: Sel, ptr: *mut u8) -> *mut Object { - unsafe { - this.set_ivar("_number_ptr", ptr); + let mut builder = ClassBuilder::new("MyObject", superclass).unwrap(); + builder.add_ivar::>("_number_ptr"); + + unsafe extern "C" fn init_with_ptr( + this: *mut Object, + _cmd: Sel, + ptr: *mut u8, + ) -> *mut Object { + let this: *mut Object = unsafe { msg_send![super(this, NSObject::class()), init] }; + if let Some(this) = unsafe { this.as_mut() } { + unsafe { + this.set_ivar("_number_ptr", ptr); + } } this } unsafe { - let init_with_ptr: extern "C" fn(&mut Object, Sel, *mut u8) -> *mut Object = + let init_with_ptr: unsafe extern "C" fn(*mut Object, Sel, *mut u8) -> *mut Object = init_with_ptr; - decl.add_method(sel!(initWithPtr:), init_with_ptr); + builder.add_method(sel!(initWithPtr:), init_with_ptr); } - decl.register(); + builder.register(); }); Class::get("MyObject").unwrap() diff --git a/objc2-foundation/examples/custom_class.rs b/objc2-foundation/examples/custom_class.rs index 140536cd8..2029ec105 100644 --- a/objc2-foundation/examples/custom_class.rs +++ b/objc2-foundation/examples/custom_class.rs @@ -1,6 +1,6 @@ use std::sync::Once; -use objc2::declare::ClassDecl; +use objc2::declare::ClassBuilder; use objc2::rc::{Id, Owned}; use objc2::runtime::{Class, Object, Sel}; use objc2::{msg_send, sel}; @@ -39,28 +39,26 @@ impl MYObject { fn class() -> &'static Class { MYOBJECT_REGISTER_CLASS.call_once(|| { let superclass = NSObject::class(); - let mut decl = ClassDecl::new("MYObject", superclass).unwrap(); - decl.add_ivar::("_number"); + let mut builder = ClassBuilder::new("MYObject", superclass).unwrap(); + builder.add_ivar::("_number"); // Add ObjC methods for getting and setting the number - extern "C" fn my_object_set_number(this: &mut Object, _cmd: Sel, number: u32) { - unsafe { - this.set_ivar("_number", number); - } + extern "C" fn my_object_set_number(this: &mut MYObject, _cmd: Sel, number: u32) { + this.set_number(number); } - extern "C" fn my_object_get_number(this: &Object, _cmd: Sel) -> u32 { - unsafe { *this.ivar("_number") } + extern "C" fn my_object_get_number(this: &MYObject, _cmd: Sel) -> u32 { + this.number() } unsafe { - let set_number: extern "C" fn(&mut Object, Sel, u32) = my_object_set_number; - decl.add_method(sel!(setNumber:), set_number); - let get_number: extern "C" fn(&Object, Sel) -> u32 = my_object_get_number; - decl.add_method(sel!(number), get_number); + let set_number: extern "C" fn(&mut MYObject, Sel, u32) = my_object_set_number; + builder.add_method(sel!(setNumber:), set_number); + let get_number: extern "C" fn(&MYObject, Sel) -> u32 = my_object_get_number; + builder.add_method(sel!(number), get_number); } - decl.register(); + builder.register(); }); Class::get("MYObject").unwrap() diff --git a/objc2-foundation/src/array.rs b/objc2-foundation/src/array.rs index c2fa4c703..e9d31187a 100644 --- a/objc2-foundation/src/array.rs +++ b/objc2-foundation/src/array.rs @@ -303,7 +303,10 @@ impl NSMutableArray { NSComparisonResult::from((*closure)(obj1, obj2)) } - let f: extern "C" fn(_, _, _) -> _ = compare_with_closure::; + // We can't name the actual lifetimes in use here, so use `_`. + // See also https://github.com/rust-lang/rust/issues/56105 + let f: extern "C" fn(_, _, *mut c_void) -> NSComparisonResult = + compare_with_closure::; // Grab a type-erased pointer to the closure (a pointer to stack). let mut closure = compare; diff --git a/objc2/CHANGELOG.md b/objc2/CHANGELOG.md index 7155d1ada..1ac1ca7ad 100644 --- a/objc2/CHANGELOG.md +++ b/objc2/CHANGELOG.md @@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Consistently allow trailing commas in `msg_send!`. * Added `msg_send_bool!`, a less error-prone version of `msg_send!` for Objective-C methods that return `BOOL`. +* Implemented `MethodImplementation` for `unsafe` function pointers. ### Changed * **BREAKING**: Changed signature of `Id::new` and `Id::retain` from @@ -33,6 +34,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). let obj: *mut Object = unsafe { msg_send![class!(NSObject), new] }; let obj = unsafe { Id::new(obj) }.expect("Failed to allocate object."); ``` +* Allow specifying any receiver `T: Message` for methods added with + `ClassBuilder::add_method`. +* Renamed `ClassDecl` and `ProtocolDecl` to `ClassBuilder` and + `ProtocolBuilder`. The old names are kept as deprecated aliases. ### Fixed * Properly sealed the `MessageArguments` trait (it already had a hidden diff --git a/objc2/src/declare.rs b/objc2/src/declare.rs index 2c2da05d9..c1e22184e 100644 --- a/objc2/src/declare.rs +++ b/objc2/src/declare.rs @@ -1,6 +1,6 @@ //! Functionality for declaring Objective-C classes. //! -//! Classes can be declared using the [`ClassDecl`] struct. Instance variables +//! Classes can be declared using the [`ClassBuilder`] struct. Instance variables //! and methods can then be added before the class is ultimately registered. //! //! # Example @@ -11,11 +11,11 @@ //! //! ```no_run //! use objc2::{class, sel}; -//! use objc2::declare::ClassDecl; +//! use objc2::declare::ClassBuilder; //! use objc2::runtime::{Class, Object, Sel}; //! //! let superclass = class!(NSObject); -//! let mut decl = ClassDecl::new("MyNumber", superclass).unwrap(); +//! let mut decl = ClassBuilder::new("MyNumber", superclass).unwrap(); //! //! // Add an instance variable //! decl.add_ivar::("_number"); @@ -78,6 +78,10 @@ macro_rules! method_decl_impl { ($($t:ident),*) => ( method_decl_impl!(-T, R, extern "C" fn(&T, Sel $(, $t)*) -> R, $($t),*); method_decl_impl!(-T, R, extern "C" fn(&mut T, Sel $(, $t)*) -> R, $($t),*); + method_decl_impl!(-T, R, unsafe extern "C" fn(*const T, Sel $(, $t)*) -> R, $($t),*); + method_decl_impl!(-T, R, unsafe extern "C" fn(*mut T, Sel $(, $t)*) -> R, $($t),*); + method_decl_impl!(-T, R, unsafe extern "C" fn(&T, Sel $(, $t)*) -> R, $($t),*); + method_decl_impl!(-T, R, unsafe extern "C" fn(&mut T, Sel $(, $t)*) -> R, $($t),*); ); } @@ -120,10 +124,14 @@ fn log2_align_of() -> u8 { /// A type for declaring a new class and adding new methods and ivars to it /// before registering it. #[derive(Debug)] -pub struct ClassDecl { +pub struct ClassBuilder { cls: NonNull, } +#[doc(hidden)] +#[deprecated = "Use `ClassBuilder` instead."] +pub type ClassDecl = ClassBuilder; + // SAFETY: The stuff that touch global state does so using locks internally. // // Modifying the class itself can only be done through `&mut`, so Sync is @@ -134,11 +142,11 @@ pub struct ClassDecl { // when doing so...). // // Finally, there are no requirements that the class must be registered on the -// same thread that allocated it. -unsafe impl Send for ClassDecl {} -unsafe impl Sync for ClassDecl {} +// same thread that allocated it (so Send is safe). +unsafe impl Send for ClassBuilder {} +unsafe impl Sync for ClassBuilder {} -impl ClassDecl { +impl ClassBuilder { fn as_ptr(&self) -> *mut ffi::objc_class { self.cls.as_ptr().cast() } @@ -150,7 +158,7 @@ impl ClassDecl { NonNull::new(cls.cast()).map(|cls| Self { cls }) } - /// Constructs a [`ClassDecl`] with the given name and superclass. + /// Constructs a [`ClassBuilder`] with the given name and superclass. /// /// Returns [`None`] if the class couldn't be allocated, or a class with /// that name already exist. @@ -158,8 +166,8 @@ impl ClassDecl { Self::with_superclass(name, Some(superclass)) } - /// Constructs a [`ClassDecl`] declaring a new root class with the given - /// name. + /// Constructs a [`ClassBuilder`] declaring a new root class with the + /// given name. /// /// Returns [`None`] if the class couldn't be allocated. /// @@ -173,11 +181,10 @@ impl ClassDecl { /// Functionality it expects, like implementations of `-retain` and /// `-release` used by ARC, will not be present otherwise. pub fn root(name: &str, intitialize_fn: extern "C" fn(&Class, Sel)) -> Option { - let mut decl = Self::with_superclass(name, None); - if let Some(ref mut decl) = decl { - unsafe { decl.add_class_method(sel!(initialize), intitialize_fn) }; - } - decl + Self::with_superclass(name, None).map(|mut this| { + unsafe { this.add_class_method(sel!(initialize), intitialize_fn) }; + this + }) } /// Adds a method with the given name and implementation. @@ -191,9 +198,10 @@ impl ClassDecl { /// /// The caller must ensure that the types match those that are expected /// when the method is invoked from Objective-C. - pub unsafe fn add_method(&mut self, sel: Sel, func: F) + pub unsafe fn add_method(&mut self, sel: Sel, func: F) where - F: MethodImplementation, + T: Message + ?Sized, // TODO: Disallow `Class` + F: MethodImplementation, { let encs = F::Args::ENCODINGS; let sel_args = count_args(sel); @@ -290,8 +298,8 @@ impl ClassDecl { // fn add_property(&self, name: &str, attributes: &[ffi::objc_property_attribute_t]); - /// Registers the [`ClassDecl`], consuming it, and returns a reference to - /// the newly registered [`Class`]. + /// Registers the [`ClassBuilder`], consuming it, and returns a reference + /// to the newly registered [`Class`]. pub fn register(self) -> &'static Class { // Forget self, otherwise the class will be disposed in drop let cls = ManuallyDrop::new(self).cls; @@ -300,7 +308,7 @@ impl ClassDecl { } } -impl Drop for ClassDecl { +impl Drop for ClassBuilder { fn drop(&mut self) { unsafe { ffi::objc_disposeClassPair(self.as_ptr()) } } @@ -309,20 +317,24 @@ impl Drop for ClassDecl { /// A type for declaring a new protocol and adding new methods to it /// before registering it. #[derive(Debug)] -pub struct ProtocolDecl { +pub struct ProtocolBuilder { proto: NonNull, } -// SAFETY: Similar to ClassDecl -unsafe impl Send for ProtocolDecl {} -unsafe impl Sync for ProtocolDecl {} +#[doc(hidden)] +#[deprecated = "Use `ProtocolBuilder` instead."] +pub type ProtocolDecl = ProtocolBuilder; + +// SAFETY: Similar to ClassBuilder +unsafe impl Send for ProtocolBuilder {} +unsafe impl Sync for ProtocolBuilder {} -impl ProtocolDecl { +impl ProtocolBuilder { fn as_ptr(&self) -> *mut ffi::objc_protocol { self.proto.as_ptr().cast() } - /// Constructs a [`ProtocolDecl`] with the given name. + /// Constructs a [`ProtocolBuilder`] with the given name. /// /// Returns [`None`] if the protocol couldn't be allocated. pub fn new(name: &str) -> Option { @@ -386,7 +398,7 @@ impl ProtocolDecl { } } - /// Registers the [`ProtocolDecl`], consuming it and returning a reference + /// Registers the [`ProtocolBuilder`], consuming it and returning a reference /// to the newly registered [`Protocol`]. pub fn register(self) -> &'static Protocol { unsafe { diff --git a/objc2/src/test_utils.rs b/objc2/src/test_utils.rs index 12f44d82a..440d88955 100644 --- a/objc2/src/test_utils.rs +++ b/objc2/src/test_utils.rs @@ -2,7 +2,7 @@ use core::ops::{Deref, DerefMut}; use std::os::raw::c_char; use std::sync::Once; -use crate::declare::{ClassDecl, ProtocolDecl}; +use crate::declare::{ClassBuilder, ProtocolBuilder}; use crate::runtime::{Class, Object, Protocol, Sel}; use crate::{ffi, Encode, Encoding, MessageReceiver}; @@ -75,11 +75,11 @@ pub(crate) fn custom_class() -> &'static Class { // The runtime will call this method, so it has to be implemented extern "C" fn custom_obj_class_initialize(_this: &Class, _cmd: Sel) {} - let mut decl = ClassDecl::root("CustomObject", custom_obj_class_initialize).unwrap(); + let mut builder = ClassBuilder::root("CustomObject", custom_obj_class_initialize).unwrap(); let proto = custom_protocol(); - decl.add_protocol(proto); - decl.add_ivar::("_foo"); + builder.add_protocol(proto); + builder.add_ivar::("_foo"); extern "C" fn custom_obj_set_foo(this: &mut Object, _cmd: Sel, foo: u32) { unsafe { @@ -121,22 +121,22 @@ pub(crate) fn custom_class() -> &'static Class { unsafe { let set_foo: extern "C" fn(&mut Object, Sel, u32) = custom_obj_set_foo; - decl.add_method(sel!(setFoo:), set_foo); + builder.add_method(sel!(setFoo:), set_foo); let get_foo: extern "C" fn(&Object, Sel) -> u32 = custom_obj_get_foo; - decl.add_method(sel!(foo), get_foo); + builder.add_method(sel!(foo), get_foo); let get_struct: extern "C" fn(&Object, Sel) -> CustomStruct = custom_obj_get_struct; - decl.add_method(sel!(customStruct), get_struct); + builder.add_method(sel!(customStruct), get_struct); let class_method: extern "C" fn(&Class, Sel) -> u32 = custom_obj_class_method; - decl.add_class_method(sel!(classFoo), class_method); + builder.add_class_method(sel!(classFoo), class_method); let protocol_instance_method: extern "C" fn(&mut Object, Sel, u32) = custom_obj_set_bar; - decl.add_method(sel!(setBar:), protocol_instance_method); + builder.add_method(sel!(setBar:), protocol_instance_method); let protocol_class_method: extern "C" fn(&Class, Sel, i32, i32) -> i32 = custom_obj_add_number_to_number; - decl.add_class_method(sel!(addNumber:toNumber:), protocol_class_method); + builder.add_class_method(sel!(addNumber:toNumber:), protocol_class_method); } - decl.register(); + builder.register(); }); class!(CustomObject) @@ -146,13 +146,13 @@ pub(crate) fn custom_protocol() -> &'static Protocol { static REGISTER_CUSTOM_PROTOCOL: Once = Once::new(); REGISTER_CUSTOM_PROTOCOL.call_once(|| { - let mut decl = ProtocolDecl::new("CustomProtocol").unwrap(); + let mut builder = ProtocolBuilder::new("CustomProtocol").unwrap(); - decl.add_method_description::<(i32,), ()>(sel!(setBar:), true); - decl.add_method_description::<(), *const c_char>(sel!(getName), false); - decl.add_class_method_description::<(i32, i32), i32>(sel!(addNumber:toNumber:), true); + builder.add_method_description::<(i32,), ()>(sel!(setBar:), true); + builder.add_method_description::<(), *const c_char>(sel!(getName), false); + builder.add_class_method_description::<(i32, i32), i32>(sel!(addNumber:toNumber:), true); - decl.register(); + builder.register(); }); Protocol::get("CustomProtocol").unwrap() @@ -163,12 +163,12 @@ pub(crate) fn custom_subprotocol() -> &'static Protocol { REGISTER_CUSTOM_SUBPROTOCOL.call_once(|| { let super_proto = custom_protocol(); - let mut decl = ProtocolDecl::new("CustomSubProtocol").unwrap(); + let mut builder = ProtocolBuilder::new("CustomSubProtocol").unwrap(); - decl.add_protocol(super_proto); - decl.add_method_description::<(u32,), u32>(sel!(calculateFoo:), true); + builder.add_protocol(super_proto); + builder.add_method_description::<(u32,), u32>(sel!(calculateFoo:), true); - decl.register(); + builder.register(); }); Protocol::get("CustomSubProtocol").unwrap() @@ -183,7 +183,7 @@ pub(crate) fn custom_subclass() -> &'static Class { REGISTER_CUSTOM_SUBCLASS.call_once(|| { let superclass = custom_class(); - let mut decl = ClassDecl::new("CustomSubclassObject", superclass).unwrap(); + let mut builder = ClassBuilder::new("CustomSubclassObject", superclass).unwrap(); extern "C" fn custom_subclass_get_foo(this: &Object, _cmd: Sel) -> u32 { let foo: u32 = unsafe { msg_send![super(this, custom_class()), foo] }; @@ -192,10 +192,10 @@ pub(crate) fn custom_subclass() -> &'static Class { unsafe { let get_foo: extern "C" fn(&Object, Sel) -> u32 = custom_subclass_get_foo; - decl.add_method(sel!(foo), get_foo); + builder.add_method(sel!(foo), get_foo); } - decl.register(); + builder.register(); }); class!(CustomSubclassObject) diff --git a/tests/ui/fn_ptr_reference_encode.rs b/tests/ui/fn_ptr_reference_encode.rs new file mode 100644 index 000000000..377c269e4 --- /dev/null +++ b/tests/ui/fn_ptr_reference_encode.rs @@ -0,0 +1,23 @@ +//! Test that `Encode` is not implemented for function pointers that are +//! higher-ranked over lifetimes. +//! +//! Ideally, they should be, but they can't be right now. +//! +//! (Also test that we can use `_` to work around this). +use objc2::Encode; + +extern "C" fn my_fn(_x: &i32) {} + +fn e(_x: T) {} + +fn main() { + // Works + e(my_fn as extern "C" fn(_)); + // Can't be written: + // let encoding = ::ENCODING; + + // Fails + e(my_fn as extern "C" fn(&i32)); + // Also fails, properly tested in `fn_ptr_reference_encode2` + let encoding = ::ENCODING; +} diff --git a/tests/ui/fn_ptr_reference_encode.stderr b/tests/ui/fn_ptr_reference_encode.stderr new file mode 100644 index 000000000..f43345222 --- /dev/null +++ b/tests/ui/fn_ptr_reference_encode.stderr @@ -0,0 +1,8 @@ +error: implementation of `Encode` is not general enough + --> ui/fn_ptr_reference_encode.rs:20:5 + | +20 | e(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: ...but `Encode` is actually implemented for the type `extern "C" fn(&'0 i32)`, for some specific lifetime `'0` diff --git a/tests/ui/fn_ptr_reference_encode2.rs b/tests/ui/fn_ptr_reference_encode2.rs new file mode 100644 index 000000000..976fd8668 --- /dev/null +++ b/tests/ui/fn_ptr_reference_encode2.rs @@ -0,0 +1,7 @@ +//! Extra test for `fn_ptr_reference_encode` +//! (They fail at different compilation passes). +use objc2::Encode; + +fn main() { + let encoding = ::ENCODING; +} diff --git a/tests/ui/fn_ptr_reference_encode2.stderr b/tests/ui/fn_ptr_reference_encode2.stderr new file mode 100644 index 000000000..450cf8a79 --- /dev/null +++ b/tests/ui/fn_ptr_reference_encode2.stderr @@ -0,0 +1,8 @@ +error: implementation of `Encode` is not general enough + --> ui/fn_ptr_reference_encode2.rs:6:20 + | +6 | 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: ...but `Encode` is actually implemented for the type `extern "C" fn(&'0 i32)`, for some specific lifetime `'0` diff --git a/tests/ui/fn_ptr_reference_method.rs b/tests/ui/fn_ptr_reference_method.rs new file mode 100644 index 000000000..fea09732a --- /dev/null +++ b/tests/ui/fn_ptr_reference_method.rs @@ -0,0 +1,27 @@ +//! Test how `MethodImplementation` is implemented regarding lifetimes in +//! function pointers and whether they're higher-ranked over them. +//! +//! Ideally it should work for all of these, but it can't be right now. +//! +//! (`_` can be used to work around this, by letting the compiler choose an +//! appropriate lifetime '0 that the trait is implemented for). +use objc2::{class, sel}; +use objc2::declare::ClassBuilder; +use objc2::runtime::{Object, Sel}; + +extern "C" fn my_fn(_this: &Object, _cmd: Sel, _x: &Object) {} + +fn main() { + let builder = ClassBuilder::new("SomeTestClass", class!(NSObject)).unwrap(); + unsafe { + // Works + builder.add_method(sel!(first:), my_fn as extern "C" fn(&Object, _, _)); + + // Fails + builder.add_method(sel!(none:), my_fn as extern "C" fn(_, _, _)); + builder.add_method(sel!(third:), my_fn as extern "C" fn(_, _, &Object)); + + // Also fails, properly tested in `fn_ptr_reference_method2` + builder.add_method(sel!(both:), my_fn as extern "C" fn(&Object, _, &Object)); + } +} diff --git a/tests/ui/fn_ptr_reference_method.stderr b/tests/ui/fn_ptr_reference_method.stderr new file mode 100644 index 000000000..ac1f11291 --- /dev/null +++ b/tests/ui/fn_ptr_reference_method.stderr @@ -0,0 +1,47 @@ +error[E0277]: the trait bound `extern "C" fn(_, _, _): MethodImplementation` is not satisfied + --> ui/fn_ptr_reference_method.rs:21:41 + | +21 | builder.add_method(sel!(none:), my_fn as extern "C" fn(_, _, _)); + | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MethodImplementation` is not implemented for `extern "C" fn(_, _, _)` + | | + | required by a bound introduced by this call + | + = help: the following other types implement trait `MethodImplementation`: + for<'r> extern "C" fn(&'r T, objc2::runtime::Sel) -> R + for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A) -> R + for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B) -> R + for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B, C) -> R + for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B, C, D) -> R + for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B, C, D, E) -> R + for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B, C, D, E, F) -> R + for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B, C, D, E, F, G) -> R + and 70 others +note: required by a bound in `ClassBuilder::add_method` + --> $WORKSPACE/objc2/src/declare.rs + | + | F: MethodImplementation, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ClassBuilder::add_method` + +error[E0277]: the trait bound `for<'r> extern "C" fn(_, _, &'r objc2::runtime::Object): MethodImplementation` is not satisfied + --> ui/fn_ptr_reference_method.rs:22:42 + | +22 | 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)` + | | + | required by a bound introduced by this call + | + = help: the following other types implement trait `MethodImplementation`: + for<'r> extern "C" fn(&'r T, objc2::runtime::Sel) -> R + for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A) -> R + for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B) -> R + for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B, C) -> R + for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B, C, D) -> R + for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B, C, D, E) -> R + for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B, C, D, E, F) -> R + for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B, C, D, E, F, G) -> R + and 70 others +note: required by a bound in `ClassBuilder::add_method` + --> $WORKSPACE/objc2/src/declare.rs + | + | F: MethodImplementation, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ClassBuilder::add_method` diff --git a/tests/ui/fn_ptr_reference_method2.rs b/tests/ui/fn_ptr_reference_method2.rs new file mode 100644 index 000000000..4142b8beb --- /dev/null +++ b/tests/ui/fn_ptr_reference_method2.rs @@ -0,0 +1,14 @@ +//! Extra test for `fn_ptr_reference_method` +//! (They fail at different compilation passes). +use objc2::{class, sel}; +use objc2::declare::ClassBuilder; +use objc2::runtime::{Object, Sel}; + +extern "C" fn my_fn(_this: &Object, _cmd: Sel, _x: &Object) {} + +fn main() { + let builder = ClassBuilder::new("SomeTestClass", class!(NSObject)).unwrap(); + unsafe { + builder.add_method(sel!(both:), my_fn as extern "C" fn(&Object, Sel, &Object)); + } +} diff --git a/tests/ui/fn_ptr_reference_method2.stderr b/tests/ui/fn_ptr_reference_method2.stderr new file mode 100644 index 000000000..5b39229d4 --- /dev/null +++ b/tests/ui/fn_ptr_reference_method2.stderr @@ -0,0 +1,8 @@ +error: implementation of `MethodImplementation` is not general enough + --> ui/fn_ptr_reference_method2.rs:12:17 + | +12 | 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: ...but `MethodImplementation` is actually implemented for the type `for<'r> extern "C" fn(&'r objc2::runtime::Object, objc2::runtime::Sel, &'0 objc2::runtime::Object)`, for some specific lifetime `'0`