Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consistently implement MethodImplementation wrt. lifetimes #193

Merged
merged 1 commit into from
Jul 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ runtime libraries.
It is recommended that you upgrade in small steps, to decrease the chance of
making a mistake somewhere. This way, you can under each release review the
relevant changelogs and run your test suite, to ensure you haven't missed
something.
something. For the most common errors, the changelogs will include a helpful
example on how to upgrade.

As an example, if you want to migrate to `objc2`, you'd start by using it
instead of `objc` in your `Cargo.toml`:
Expand Down
3 changes: 1 addition & 2 deletions objc2-foundation/examples/class_with_lifetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@ impl<'a> MyObject<'a> {
}

unsafe {
let init_with_ptr: unsafe extern "C" fn(*mut Object, Sel, *mut u8) -> *mut Object =
init_with_ptr;
let init_with_ptr: unsafe extern "C" fn(_, _, _) -> _ = init_with_ptr;
builder.add_method(sel!(initWithPtr:), init_with_ptr);
}

Expand Down
4 changes: 2 additions & 2 deletions objc2-foundation/examples/custom_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ impl MYObject {
}

unsafe {
let set_number: extern "C" fn(&mut MYObject, Sel, u32) = my_object_set_number;
let set_number: extern "C" fn(_, _, _) = my_object_set_number;
builder.add_method(sel!(setNumber:), set_number);
let get_number: extern "C" fn(&MYObject, Sel) -> u32 = my_object_get_number;
let get_number: extern "C" fn(_, _) -> _ = my_object_get_number;
builder.add_method(sel!(number), get_number);
}

Expand Down
28 changes: 28 additions & 0 deletions objc2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,34 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* **BREAKING**: Changed `MessageReceiver::send_message` to panic instead of
returning an error.
* **BREAKING**: Renamed `catch_all` feature to `catch-all`.
* **BREAKING**: Made passing the function pointer argument to
`ClassBuilder::add_method`, `ClassBuilder::add_class_method` and similar
more ergonomic.

Let's say you have the following code:
```rust
// Before
let init: extern "C" fn(&mut Object, Sel) -> *mut Object = init;
builder.add_method(sel!(init), init);
```

Unfortunately, you will now encounter a very confusing error:
```
|
2 | builder.add_method(sel!(init), init);
| ^^^^^^^^^^ implementation of `MethodImplementation` is not general enough
|
= note: `MethodImplementation` would have to be implemented for the type `for<'r> extern "C" fn(&'r mut Object, Sel) -> *mut Object`
= note: ...but `MethodImplementation` is actually implemented for the type `extern "C" fn(&'0 mut Object, Sel) -> *mut Object`, for some specific lifetime `'0`
```

Fret not, the fix is easy! Just let the compiler infer the argument and
return types:
```rust
// After
let init: extern "C" fn(_, _) -> _ = init;
builder.add_method(sel!(init), init);
```

### Fixed
* **BREAKING**: Disallow throwing `nil` exceptions in `exception::throw`.
Expand Down
42 changes: 21 additions & 21 deletions objc2/src/declare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
//! unsafe {
//! decl.add_method(
//! sel!(number),
//! my_number_get as extern "C" fn(&Object, Sel) -> u32,
//! my_number_get as extern "C" fn(_, _) -> _,
//! );
//! }
//!
Expand Down Expand Up @@ -69,21 +69,21 @@ pub trait MethodImplementation: private::Sealed {
}

macro_rules! method_decl_impl {
(-$s:ident, $r:ident, $f:ty, $($t:ident),*) => {
impl<$s, $r, $($t),*> private::Sealed for $f
(@<$($l:lifetime),*> T, $r:ident, $f:ty, $($t:ident),*) => {
impl<$($l,)* T, $r, $($t),*> private::Sealed for $f
where
$s: Message + ?Sized,
T: Message + ?Sized,
$r: Encode,
$($t: Encode,)*
{}

impl<$s, $r, $($t),*> MethodImplementation for $f
impl<$($l,)* T, $r, $($t),*> MethodImplementation for $f
where
$s: Message + ?Sized,
T: Message + ?Sized,
$r: Encode,
$($t: Encode,)*
{
type Callee = $s;
type Callee = T;
type Ret = $r;
type Args = ($($t,)*);

Expand All @@ -92,19 +92,19 @@ macro_rules! method_decl_impl {
}
}
};
(@$s:ident, $r:ident, $f:ty, $($t:ident),*) => {
impl<$r, $($t),*> private::Sealed for $f
(@<$($l:lifetime),*> Class, $r:ident, $f:ty, $($t:ident),*) => {
impl<$($l,)* $r, $($t),*> private::Sealed for $f
where
$r: Encode,
$($t: Encode,)*
{}

impl<$r, $($t),*> MethodImplementation for $f
impl<$($l,)* $r, $($t),*> MethodImplementation for $f
where
$r: Encode,
$($t: Encode,)*
{
type Callee = $s;
type Callee = Class;
type Ret = $r;
type Args = ($($t,)*);

Expand All @@ -114,16 +114,16 @@ macro_rules! method_decl_impl {
}
};
(# $abi:literal; $($t:ident),*) => {
method_decl_impl!(-T, R, extern $abi fn(&T, Sel $(, $t)*) -> R, $($t),*);
method_decl_impl!(-T, R, extern $abi fn(&mut T, Sel $(, $t)*) -> R, $($t),*);
method_decl_impl!(-T, R, unsafe extern $abi fn(*const T, Sel $(, $t)*) -> R, $($t),*);
method_decl_impl!(-T, R, unsafe extern $abi fn(*mut T, Sel $(, $t)*) -> R, $($t),*);
method_decl_impl!(-T, R, unsafe extern $abi fn(&T, Sel $(, $t)*) -> R, $($t),*);
method_decl_impl!(-T, R, unsafe extern $abi fn(&mut T, Sel $(, $t)*) -> R, $($t),*);

method_decl_impl!(@Class, R, extern $abi fn(&Class, Sel $(, $t)*) -> R, $($t),*);
method_decl_impl!(@Class, R, unsafe extern $abi fn(*const Class, Sel $(, $t)*) -> R, $($t),*);
method_decl_impl!(@Class, R, unsafe extern $abi fn(&Class, Sel $(, $t)*) -> R, $($t),*);
method_decl_impl!(@<'a> T, R, extern $abi fn(&'a T, Sel $(, $t)*) -> R, $($t),*);
method_decl_impl!(@<'a> T, R, extern $abi fn(&'a mut T, Sel $(, $t)*) -> R, $($t),*);
method_decl_impl!(@<> T, R, unsafe extern $abi fn(*const T, Sel $(, $t)*) -> R, $($t),*);
method_decl_impl!(@<> T, R, unsafe extern $abi fn(*mut T, Sel $(, $t)*) -> R, $($t),*);
method_decl_impl!(@<'a> T, R, unsafe extern $abi fn(&'a T, Sel $(, $t)*) -> R, $($t),*);
method_decl_impl!(@<'a> T, R, unsafe extern $abi fn(&'a mut T, Sel $(, $t)*) -> R, $($t),*);

method_decl_impl!(@<'a> Class, R, extern $abi fn(&'a Class, Sel $(, $t)*) -> R, $($t),*);
method_decl_impl!(@<> Class, R, unsafe extern $abi fn(*const Class, Sel $(, $t)*) -> R, $($t),*);
method_decl_impl!(@<'a> Class, R, unsafe extern $abi fn(&'a Class, Sel $(, $t)*) -> R, $($t),*);
};
($($t:ident),*) => {
method_decl_impl!(# "C"; $($t),*);
Expand Down
2 changes: 1 addition & 1 deletion objc2/src/rc/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ impl<T: Message, O: Ownership> Id<T, O> {
/// unsafe {
/// builder.add_class_method(
/// sel!(get),
/// get as extern "C" fn(&Class, Sel) -> *mut Object,
/// get as extern "C" fn(_, _) -> _,
/// );
/// }
///
Expand Down
29 changes: 7 additions & 22 deletions objc2/src/rc/test_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,31 +118,16 @@ impl RcTestObject {

let mut builder = ClassBuilder::new("RcTestObject", class!(NSObject)).unwrap();
unsafe {
builder.add_class_method(
sel!(alloc),
alloc as extern "C" fn(&Class, Sel) -> *mut RcTestObject,
);
builder.add_method(
sel!(init),
init as extern "C" fn(&mut RcTestObject, Sel) -> _,
);
builder.add_method(
sel!(retain),
retain as extern "C" fn(&RcTestObject, Sel) -> _,
);
builder.add_class_method(sel!(alloc), alloc as extern "C" fn(_, _) -> _);
builder.add_method(sel!(init), init as extern "C" fn(_, _) -> _);
builder.add_method(sel!(retain), retain as extern "C" fn(_, _) -> _);
builder.add_method(
sel!(_tryRetain),
try_retain as unsafe extern "C" fn(&RcTestObject, Sel) -> Bool,
);
builder.add_method(sel!(release), release as extern "C" fn(&RcTestObject, Sel));
builder.add_method(
sel!(autorelease),
autorelease as extern "C" fn(&RcTestObject, Sel) -> _,
);
builder.add_method(
sel!(dealloc),
dealloc as unsafe extern "C" fn(*mut RcTestObject, Sel),
try_retain as unsafe extern "C" fn(_, _) -> _,
);
builder.add_method(sel!(release), release as extern "C" fn(_, _));
builder.add_method(sel!(autorelease), autorelease as extern "C" fn(_, _) -> _);
builder.add_method(sel!(dealloc), dealloc as unsafe extern "C" fn(_, _));
}

builder.register();
Expand Down
24 changes: 15 additions & 9 deletions objc2/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ pub(crate) fn custom_class() -> &'static Class {

let mut builder = ClassBuilder::root(
"CustomObject",
custom_obj_class_initialize as extern "C" fn(&Class, Sel),
custom_obj_class_initialize as extern "C" fn(_, _),
)
.unwrap();
let proto = custom_protocol();
Expand All @@ -115,6 +115,10 @@ pub(crate) fn custom_class() -> &'static Class {
unsafe { *this.ivar::<u32>("_foo") }
}

extern "C" fn custom_obj_get_foo_reference(this: &Object, _cmd: Sel) -> &u32 {
unsafe { this.ivar::<u32>("_foo") }
}

extern "C" fn custom_obj_get_struct(_this: &Object, _cmd: Sel) -> CustomStruct {
CustomStruct {
a: 1,
Expand Down Expand Up @@ -144,21 +148,23 @@ pub(crate) fn custom_class() -> &'static Class {
}

unsafe {
let release: unsafe extern "C" fn(*mut Object, Sel) = custom_obj_release;
let release: unsafe extern "C" fn(_, _) = custom_obj_release;
builder.add_method(sel!(release), release);

let set_foo: extern "C" fn(&mut Object, Sel, u32) = custom_obj_set_foo;
let set_foo: extern "C" fn(_, _, _) = custom_obj_set_foo;
builder.add_method(sel!(setFoo:), set_foo);
let get_foo: extern "C" fn(&Object, Sel) -> u32 = custom_obj_get_foo;
let get_foo: extern "C" fn(_, _) -> _ = custom_obj_get_foo;
builder.add_method(sel!(foo), get_foo);
let get_struct: extern "C" fn(&Object, Sel) -> CustomStruct = custom_obj_get_struct;
let get_foo_reference: extern "C" fn(_, _) -> _ = custom_obj_get_foo_reference;
builder.add_method(sel!(fooReference), get_foo_reference);
let get_struct: extern "C" fn(_, _) -> CustomStruct = custom_obj_get_struct;
builder.add_method(sel!(customStruct), get_struct);
let class_method: extern "C" fn(&Class, Sel) -> u32 = custom_obj_class_method;
let class_method: extern "C" fn(_, _) -> _ = custom_obj_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;
let protocol_instance_method: extern "C" fn(_, _, _) = custom_obj_set_bar;
builder.add_method(sel!(setBar:), protocol_instance_method);
let protocol_class_method: extern "C" fn(&Class, Sel, i32, i32) -> i32 =
let protocol_class_method: extern "C" fn(_, _, _, _) -> _ =
custom_obj_add_number_to_number;
builder.add_class_method(sel!(addNumber:toNumber:), protocol_class_method);
}
Expand Down Expand Up @@ -218,7 +224,7 @@ pub(crate) fn custom_subclass() -> &'static Class {
}

unsafe {
let get_foo: extern "C" fn(&Object, Sel) -> u32 = custom_subclass_get_foo;
let get_foo: extern "C" fn(_, _) -> _ = custom_subclass_get_foo;
builder.add_method(sel!(foo), get_foo);
}

Expand Down
4 changes: 2 additions & 2 deletions tests/ui/fn_ptr_reference_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ fn main() {
let mut builder = ClassBuilder::new("SomeTestClass", class!(NSObject)).unwrap();
unsafe {
// Works
builder.add_method(sel!(first:), my_fn as extern "C" fn(&Object, _, _));
builder.add_method(sel!(none:), my_fn as extern "C" fn(_, _, _));

// 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!(first:), my_fn as extern "C" fn(&Object, _, _));
builder.add_method(sel!(both:), my_fn as extern "C" fn(&Object, _, &Object));
}
}
44 changes: 10 additions & 34 deletions tests/ui/fn_ptr_reference_method.stderr
Original file line number Diff line number Diff line change
@@ -1,44 +1,20 @@
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 109 others
note: required by a bound in `ClassBuilder::add_method`
--> $WORKSPACE/objc2/src/declare.rs
|
| F: MethodImplementation<Callee = T>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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
--> ui/fn_ptr_reference_method.rs:21:42
|
22 | builder.add_method(sel!(third:), my_fn as extern "C" fn(_, _, &Object));
21 | 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
extern "C" fn(&'a T, objc2::runtime::Sel) -> R
extern "C" fn(&'a T, objc2::runtime::Sel, A) -> R
extern "C" fn(&'a T, objc2::runtime::Sel, A, B) -> R
extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C) -> R
extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D) -> R
extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E) -> R
extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F) -> R
extern "C" fn(&'a T, objc2::runtime::Sel, A, B, C, D, E, F, G) -> R
and 109 others
note: required by a bound in `ClassBuilder::add_method`
--> $WORKSPACE/objc2/src/declare.rs
Expand Down
1 change: 1 addition & 0 deletions tests/ui/fn_ptr_reference_method2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ extern "C" fn my_fn(_this: &Object, _cmd: Sel, _x: &Object) {}
fn main() {
let mut builder = ClassBuilder::new("SomeTestClass", class!(NSObject)).unwrap();
unsafe {
builder.add_method(sel!(first:), my_fn as extern "C" fn(&Object, _, _));
builder.add_method(sel!(both:), my_fn as extern "C" fn(&Object, Sel, &Object));
}
}
22 changes: 20 additions & 2 deletions tests/ui/fn_ptr_reference_method2.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,26 @@
error: implementation of `MethodImplementation` is not general enough
--> ui/fn_ptr_reference_method2.rs:12:9
|
12 | builder.add_method(sel!(both:), my_fn as extern "C" fn(&Object, Sel, &Object));
12 | 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: ...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
--> ui/fn_ptr_reference_method2.rs:13:9
|
13 | 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 `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
--> ui/fn_ptr_reference_method2.rs:13:9
|
13 | 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`
= 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`