Skip to content

Commit

Permalink
Allow using MainThreadMarker in extern_methods!
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Jul 29, 2023
1 parent 4cb7772 commit d90209d
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 0 deletions.
3 changes: 3 additions & 0 deletions crates/objc2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## Unreleased - YYYY-MM-DD

### Added
* Allow using `MainThreadMarker` in `extern_methods!`.

### Changed
* Renamed `runtime` types:
- `Object` to `AnyObject`.
Expand Down
50 changes: 50 additions & 0 deletions crates/objc2/src/macros/__method_msg_send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,30 @@ macro_rules! __method_msg_send {
}
};

// Skip using `MainThreadMarker` in the message send.
//
// This is a purely textual match, and using e.g.
// `Foundation::MainThreadMarker` would fail - but that would just be
// detected as giving a wrong number of arguments, so it's fine for now.
(
($receiver:expr)
($($sel_rest:tt)*)
($arg:ident: MainThreadMarker $(, $($args_rest:tt)*)?)

($($sel_parsed:tt)*)
($($arg_parsed:tt)*)
) => ({
let _ = $arg;
$crate::__method_msg_send! {
($receiver)
($($sel_rest)*)
($($($args_rest)*)?)

($($sel_parsed)*)
($($arg_parsed)*)
}
});

// Parse each argument-selector pair
(
($receiver:expr)
Expand Down Expand Up @@ -173,6 +197,32 @@ macro_rules! __method_msg_send_id {
}
};

// Skip using `MainThreadMarker` in the message send.
//
// This is a purely textual match, and using e.g.
// `Foundation::MainThreadMarker` would fail - but that would just be
// detected as giving a wrong number of arguments, so it's fine for now.
(
($receiver:expr)
($($sel_rest:tt)*)
($arg:ident: MainThreadMarker $(, $($args_rest:tt)*)?)

($($sel_parsed:tt)*)
($($arg_parsed:tt)*)
($($retain_semantics:ident)?)
) => ({
let _ = $arg;
$crate::__method_msg_send_id! {
($receiver)
($($sel_rest)*)
($($($args_rest)*)?)

($($sel_parsed)*)
($($arg_parsed)*)
($($retain_semantics)?)
}
});

// Parse each argument-selector pair
(
($receiver:expr)
Expand Down
6 changes: 6 additions & 0 deletions crates/objc2/src/macros/extern_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@
/// [`Result`]. See the error section in [`msg_send!`] and [`msg_send_id!`]
/// for details.
///
/// If you use `icrate::Foundation::MainThreadMarker` as a parameter type, the
/// macro will ignore it, allowing you to neatly specify "this method must be
/// run on the main thread". Note that due to type-system limitations, this is
/// currently a textual match on `MainThreadMarker`; so you must use that
/// exact identifier.
///
/// Putting other attributes on the method such as `cfg`, `allow`, `doc`,
/// `deprecated` and so on is supported. However, note that `cfg_attr` may not
/// work correctly, due to implementation difficulty - if you have a concrete
Expand Down
79 changes: 79 additions & 0 deletions crates/objc2/tests/macros_mainthreadmarker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use objc2::rc::Id;
use objc2::runtime::{NSObject, NSObjectProtocol};
use objc2::{declare_class, extern_methods, extern_protocol, mutability, ClassType, ProtocolType};

extern_protocol!(
#[allow(clippy::missing_safety_doc)]
unsafe trait Proto: NSObjectProtocol {
#[method(myMethod:)]
fn protocol_method(mtm: MainThreadMarker, arg: i32) -> i32;

#[method_id(myMethodId:)]
fn protocol_method_id(mtm: MainThreadMarker, arg: &Self) -> Id<Self>;
}

unsafe impl ProtocolType for dyn Proto {
const NAME: &'static str = "MainThreadMarkerTestProtocol";
}
);

declare_class!(
#[derive(PartialEq, Eq, Hash, Debug)]
struct Cls;

unsafe impl ClassType for Cls {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
const NAME: &'static str = "MainThreadMarkerTest";
}

unsafe impl Proto for Cls {
#[method(myMethod:)]
fn _my_mainthreadonly_method(arg: i32) -> i32 {
arg + 1
}

#[method_id(myMethodId:)]
fn _my_mainthreadonly_method_id(arg: &Self) -> Id<Self> {
unsafe { Id::retain(arg as *const Self as *mut Self).unwrap() }
}
}
);

unsafe impl NSObjectProtocol for Cls {}

// The macro does a textual match; but when users actually use
// `icrate::Foundation::MainThreadMarker` to ensure soundness, they will not
// do this!
#[derive(Clone, Copy)]
struct MainThreadMarker(bool);

extern_methods!(
unsafe impl Cls {
#[method_id(new)]
fn new() -> Id<Self>;

#[method(myMethod:)]
fn method(mtm: MainThreadMarker, arg: i32, mtm2: MainThreadMarker) -> i32;

#[method_id(myMethodId:)]
fn method_id(mtm: MainThreadMarker, arg: &Self, mtm2: MainThreadMarker) -> Id<Self>;
}
);

#[test]
fn call() {
let obj1 = Cls::new();
let mtm = MainThreadMarker(true);

let res = Cls::method(mtm, 2, mtm);
assert_eq!(res, 3);
let res = Cls::protocol_method(mtm, 3);
assert_eq!(res, 4);

let obj2 = Cls::method_id(mtm, &obj1, mtm);
assert_eq!(obj1, obj2);

let obj2 = Cls::protocol_method_id(mtm, &obj1);
assert_eq!(obj1, obj2);
}
8 changes: 8 additions & 0 deletions crates/test-ui/ui/extern_methods_invalid_type.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use icrate::Foundation::MainThreadMarker;
use objc2::rc::Id;
use objc2::runtime::NSObject;
use objc2::{extern_class, extern_methods, mutability, ClassType};
Expand Down Expand Up @@ -46,4 +47,11 @@ extern_methods!(
}
);

extern_methods!(
unsafe impl MyObject {
#[method(mainThreadMarkerAsReturn)]
fn main_thread_marker_as_return() -> MainThreadMarker;
}
);

fn main() {}
33 changes: 33 additions & 0 deletions crates/test-ui/ui/extern_methods_invalid_type.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,36 @@ note: required by a bound in `send_message_id`
| unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<Input = U>>(
| ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `MsgSendId::send_message_id`
= note: this error originates in the macro `$crate::__msg_send_id_helper` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `MainThreadMarker: Encode` is not satisfied
--> ui/extern_methods_invalid_type.rs
|
| / extern_methods!(
| | unsafe impl MyObject {
| | #[method(mainThreadMarkerAsReturn)]
| | fn main_thread_marker_as_return() -> MainThreadMarker;
| | }
| | );
| |_^ the trait `Encode` is not implemented for `MainThreadMarker`
|
= help: the following other types implement trait `Encode`:
&'a T
&'a mut T
*const T
*const c_void
*mut T
*mut c_void
AtomicI16
AtomicI32
and $N others
= note: required for `MainThreadMarker` to implement `EncodeReturn`
= note: required for `MainThreadMarker` to implement `EncodeConvertReturn`
note: required by a bound in `send_message`
--> $WORKSPACE/crates/objc2/src/message/mod.rs
|
| unsafe fn send_message<A, R>(self, sel: Sel, args: A) -> R
| ------------ required by a bound in this associated function
...
| R: EncodeConvertReturn,
| ^^^^^^^^^^^^^^^^^^^ required by this bound in `MessageReceiver::send_message`
= note: this error originates in the macro `$crate::__msg_send_helper` which comes from the expansion of the macro `extern_methods` (in Nightly builds, run with -Z macro-backtrace for more info)

0 comments on commit d90209d

Please sign in to comment.