Skip to content

Commit 46953ce

Browse files
committed
Add encoding UI test for function pointers
1 parent 1054a7b commit 46953ce

File tree

8 files changed

+131
-3
lines changed

8 files changed

+131
-3
lines changed

objc2-encode/src/encode.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,8 +430,7 @@ encode_pointer_impls!(
430430
///
431431
/// Ideally we'd implement it for all function pointers, but due to coherence
432432
/// issues, see <https://github.com/rust-lang/rust/issues/56105>, function
433-
/// pointers that take arguments with "special lifetimes" (don't know the
434-
/// termonology) don't get implemented properly.
433+
/// pointers that are higher-ranked over lifetimes don't get implemented.
435434
///
436435
/// We could fix it by adding those impls and allowing `coherence_leak_check`,
437436
/// but it would have to be done for _all_ references, `Option<&T>` and such as

objc2-foundation/src/array.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,10 @@ impl<T: Message, O: Ownership> NSMutableArray<T, O> {
303303
NSComparisonResult::from((*closure)(obj1, obj2))
304304
}
305305

306-
let f: extern "C" fn(_, _, _) -> _ = compare_with_closure::<T, F>;
306+
// We can't name the actual lifetimes in use here, so use `_`.
307+
// See also https://github.com/rust-lang/rust/issues/56105
308+
let f: extern "C" fn(_, _, *mut c_void) -> NSComparisonResult =
309+
compare_with_closure::<T, F>;
307310

308311
// Grab a type-erased pointer to the closure (a pointer to stack).
309312
let mut closure = compare;

tests/ui/fn_ptr_reference_encode.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//! Test that `Encode` is not implemented for function pointers that are
2+
//! higher-ranked over lifetimes.
3+
//!
4+
//! Ideally, they should be, but they can't be right now.
5+
//!
6+
//! (Also test that we can use `_` to work around this).
7+
use objc2::Encode;
8+
9+
extern "C" fn my_fn(_x: &i32) {}
10+
11+
fn e<T: Encode>(_x: T) {}
12+
13+
fn main() {
14+
// Works
15+
e(my_fn as extern "C" fn(_));
16+
// Can't be written:
17+
// let encoding = <extern "C" fn(_) as Encode>::ENCODING;
18+
19+
// Fails
20+
e(my_fn as extern "C" fn(&i32));
21+
let encoding = <extern "C" fn(&i32) as Encode>::ENCODING;
22+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: implementation of `Encode` is not general enough
2+
--> ui/fn_ptr_reference_encode.rs:20:5
3+
|
4+
20 | e(my_fn as extern "C" fn(&i32));
5+
| ^ implementation of `Encode` is not general enough
6+
|
7+
= note: `Encode` would have to be implemented for the type `for<'r> extern "C" fn(&'r i32)`
8+
= note: ...but `Encode` is actually implemented for the type `extern "C" fn(&'0 i32)`, for some specific lifetime `'0`

tests/ui/fn_ptr_reference_method.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//! Test how `MethodImplementation` is implemented regarding lifetimes in
2+
//! function pointers and whether they're higher-ranked over them.
3+
//!
4+
//! Ideally it should work for all of these, but it can't be right now.
5+
//!
6+
//! (`_` can be used to work around this, by letting the compiler choose an
7+
//! appropriate lifetime '0 that the trait is implemented for).
8+
use objc2::{class, sel};
9+
use objc2::declare::ClassBuilder;
10+
use objc2::runtime::{Object, Sel};
11+
12+
extern "C" fn my_fn(_this: &Object, _cmd: Sel, _x: &Object) {}
13+
14+
fn main() {
15+
let builder = ClassBuilder::new("SomeTestClass", class!(NSObject)).unwrap();
16+
unsafe {
17+
// Works
18+
builder.add_method(sel!(first:), my_fn as extern "C" fn(&Object, _, _));
19+
20+
// Fails
21+
builder.add_method(sel!(none:), my_fn as extern "C" fn(_, _, _));
22+
builder.add_method(sel!(third:), my_fn as extern "C" fn(_, _, &Object));
23+
24+
// Also fails, properly tested in `fn_ptr_reference_method2`
25+
builder.add_method(sel!(both:), my_fn as extern "C" fn(&Object, _, &Object));
26+
}
27+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
error[E0277]: the trait bound `extern "C" fn(_, _, _): MethodImplementation` is not satisfied
2+
--> ui/fn_ptr_reference_method.rs:21:41
3+
|
4+
21 | builder.add_method(sel!(none:), my_fn as extern "C" fn(_, _, _));
5+
| ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MethodImplementation` is not implemented for `extern "C" fn(_, _, _)`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
= help: the following other types implement trait `MethodImplementation`:
10+
for<'r> extern "C" fn(&'r T, objc2::runtime::Sel) -> R
11+
for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A) -> R
12+
for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B) -> R
13+
for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B, C) -> R
14+
for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B, C, D) -> R
15+
for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B, C, D, E) -> R
16+
for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B, C, D, E, F) -> R
17+
for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B, C, D, E, F, G) -> R
18+
and 70 others
19+
note: required by a bound in `ClassBuilder::add_method`
20+
--> $WORKSPACE/objc2/src/declare.rs
21+
|
22+
| F: MethodImplementation<Callee = T>,
23+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ClassBuilder::add_method`
24+
25+
error[E0277]: the trait bound `for<'r> extern "C" fn(_, _, &'r objc2::runtime::Object): MethodImplementation` is not satisfied
26+
--> ui/fn_ptr_reference_method.rs:22:42
27+
|
28+
22 | builder.add_method(sel!(third:), my_fn as extern "C" fn(_, _, &Object));
29+
| ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MethodImplementation` is not implemented for `for<'r> extern "C" fn(_, _, &'r objc2::runtime::Object)`
30+
| |
31+
| required by a bound introduced by this call
32+
|
33+
= help: the following other types implement trait `MethodImplementation`:
34+
for<'r> extern "C" fn(&'r T, objc2::runtime::Sel) -> R
35+
for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A) -> R
36+
for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B) -> R
37+
for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B, C) -> R
38+
for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B, C, D) -> R
39+
for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B, C, D, E) -> R
40+
for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B, C, D, E, F) -> R
41+
for<'r> extern "C" fn(&'r T, objc2::runtime::Sel, A, B, C, D, E, F, G) -> R
42+
and 70 others
43+
note: required by a bound in `ClassBuilder::add_method`
44+
--> $WORKSPACE/objc2/src/declare.rs
45+
|
46+
| F: MethodImplementation<Callee = T>,
47+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ClassBuilder::add_method`

tests/ui/fn_ptr_reference_method2.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//! Extra test for `fn_ptr_reference_method`
2+
//! (They fail at different compilation passes).
3+
use objc2::{class, sel};
4+
use objc2::declare::ClassBuilder;
5+
use objc2::runtime::{Object, Sel};
6+
7+
extern "C" fn my_fn(_this: &Object, _cmd: Sel, _x: &Object) {}
8+
9+
fn main() {
10+
let builder = ClassBuilder::new("SomeTestClass", class!(NSObject)).unwrap();
11+
unsafe {
12+
builder.add_method(sel!(both:), my_fn as extern "C" fn(&Object, Sel, &Object));
13+
}
14+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: implementation of `MethodImplementation` is not general enough
2+
--> ui/fn_ptr_reference_method2.rs:12:17
3+
|
4+
12 | builder.add_method(sel!(both:), my_fn as extern "C" fn(&Object, Sel, &Object));
5+
| ^^^^^^^^^^ implementation of `MethodImplementation` is not general enough
6+
|
7+
= 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)`
8+
= 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`

0 commit comments

Comments
 (0)