Skip to content

Commit

Permalink
Add encoding UI test for function pointers
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Jun 6, 2022
1 parent 1054a7b commit 92f85da
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 3 deletions.
3 changes: 1 addition & 2 deletions objc2-encode/src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,8 +430,7 @@ encode_pointer_impls!(
///
/// Ideally we'd implement it for all function pointers, but due to coherence
/// issues, see <https://github.com/rust-lang/rust/issues/56105>, 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
Expand Down
5 changes: 4 additions & 1 deletion objc2-foundation/src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,10 @@ impl<T: Message, O: Ownership> NSMutableArray<T, O> {
NSComparisonResult::from((*closure)(obj1, obj2))
}

let f: extern "C" fn(_, _, _) -> _ = compare_with_closure::<T, F>;
// 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::<T, F>;

// Grab a type-erased pointer to the closure (a pointer to stack).
let mut closure = compare;
Expand Down
23 changes: 23 additions & 0 deletions tests/ui/fn_ptr_reference_encode.rs
Original file line number Diff line number Diff line change
@@ -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<T: Encode>(_x: T) {}

fn main() {
// Works
e(my_fn as extern "C" fn(_));
// Can't be written:
// let encoding = <extern "C" fn(_) as Encode>::ENCODING;

// Fails
e(my_fn as extern "C" fn(&i32));
// Also fails, properly tested in `fn_ptr_reference_encode2`
let encoding = <extern "C" fn(&i32) as Encode>::ENCODING;
}
8 changes: 8 additions & 0 deletions tests/ui/fn_ptr_reference_encode.stderr
Original file line number Diff line number Diff line change
@@ -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`
7 changes: 7 additions & 0 deletions tests/ui/fn_ptr_reference_encode2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//! Extra test for `fn_ptr_reference_encode`
//! (They fail at different compilation passes).
use objc2::Encode;

fn main() {
let encoding = <extern "C" fn(&i32) as Encode>::ENCODING;
}
8 changes: 8 additions & 0 deletions tests/ui/fn_ptr_reference_encode2.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: implementation of `Encode` is not general enough
--> ui/fn_ptr_reference_encode2.rs:6:20
|
6 | let encoding = <extern "C" fn(&i32) as Encode>::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`
27 changes: 27 additions & 0 deletions tests/ui/fn_ptr_reference_method.rs
Original file line number Diff line number Diff line change
@@ -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));
}
}
47 changes: 47 additions & 0 deletions tests/ui/fn_ptr_reference_method.stderr
Original file line number Diff line number Diff line change
@@ -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<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
|
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<Callee = T>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ClassBuilder::add_method`
14 changes: 14 additions & 0 deletions tests/ui/fn_ptr_reference_method2.rs
Original file line number Diff line number Diff line change
@@ -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));
}
}
8 changes: 8 additions & 0 deletions tests/ui/fn_ptr_reference_method2.stderr
Original file line number Diff line number Diff line change
@@ -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`

0 comments on commit 92f85da

Please sign in to comment.