Skip to content

Commit

Permalink
Relax requirements for receivers in MethodImplementation
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Sep 20, 2023
1 parent 284f988 commit ff33337
Show file tree
Hide file tree
Showing 11 changed files with 160 additions and 215 deletions.
3 changes: 3 additions & 0 deletions crates/objc2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

In particular automatic conversion of `bool` is not supported in
`MessageReceiver`.
* Relaxed the requirements for receivers in `MethodImplementation`; now,
anything that implements `MessageReceiver` can be used as the receiver of
a method.

### Deprecated
* Soft deprecated using `msg_send!` without a comma between arguments (i.e.
Expand Down
5 changes: 2 additions & 3 deletions crates/objc2/src/macros/declare_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,9 @@
///
/// On instance methods, you can freely choose between different types of
/// receivers, e.g. `&self`, `this: *const Self`, `&mut self`, and so on. Note
/// though that using raw pointers requires the function to be `unsafe`, and
/// using `&mut self` requires the class' mutability to be
/// though that using `&mut self` requires the class' mutability to be
/// [`IsAllowedMutable`].
/// If you require mutation of your class' instance variables, consider using
/// If you need mutation of your class' instance variables, consider using
/// [`Cell`] or similar instead.
///
/// The desired selector can be specified using the `#[method(my:selector:)]`
Expand Down
84 changes: 20 additions & 64 deletions crates/objc2/src/runtime/method_implementation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ use core::mem;

use crate::__macro_helpers::IdReturnValue;
use crate::encode::{EncodeArgument, EncodeArguments, EncodeReturn, RefEncode};
use crate::mutability::IsAllowedMutable;
use crate::rc::Allocated;
use crate::runtime::{AnyClass, Imp, Sel};
use crate::runtime::{Imp, MessageReceiver, Sel};
use crate::Message;

mod private {
Expand All @@ -19,7 +18,7 @@ mod private {
// Note: `Sized` is intentionally added to make the trait not object safe.
pub trait MethodImplementation: private::Sealed + Sized {
/// The callee type of the method.
type Callee: RefEncode + ?Sized;
type Callee: ?Sized + RefEncode;
/// The return type of the method.
type Ret: EncodeReturn;
/// The argument types of the method.
Expand All @@ -29,67 +28,39 @@ pub trait MethodImplementation: private::Sealed + Sized {
fn __imp(self) -> Imp;
}

macro_rules! method_impl_generic {
(<$($l:lifetime),*> T: $t_bound:ident $(+ $t_bound2:ident)?, $r:ident, $f:ty, $($t:ident),*) => {
impl<$($l,)* T, $r, $($t),*> private::Sealed for $f
macro_rules! method_impl_inner {
($(($unsafe:ident))? $abi:literal; $($t:ident),*) => {
impl<T, R, $($t),*> private::Sealed for $($unsafe)? extern $abi fn(T, Sel $(, $t)*) -> R
where
T: ?Sized + $t_bound $(+ $t_bound2)?,
$r: EncodeReturn,
T: ?Sized + MessageReceiver,
R: EncodeReturn,
$($t: EncodeArgument,)*
{}

impl<$($l,)* T, $r, $($t),*> MethodImplementation for $f
impl<T, R, $($t),*> MethodImplementation for $($unsafe)? extern $abi fn(T, Sel $(, $t)*) -> R
where
T: ?Sized + $t_bound $(+ $t_bound2)?,
$r: EncodeReturn,
T: ?Sized + MessageReceiver,
R: EncodeReturn,
$($t: EncodeArgument,)*
{
type Callee = T;
type Ret = $r;
type Args = ($($t,)*);

fn __imp(self) -> Imp {
unsafe { mem::transmute(self) }
}
}
};
}

macro_rules! method_impl_concrete {
(<$($l:lifetime),*> $callee:ident, $r:ident, $f:ty, $($t:ident),*) => {
impl<$($l,)* $r, $($t),*> private::Sealed for $f
where
$r: EncodeReturn,
$($t: EncodeArgument,)*
{}

impl<$($l,)* $r, $($t),*> MethodImplementation for $f
where
$r: EncodeReturn,
$($t: EncodeArgument,)*
{
type Callee = $callee;
type Ret = $r;
type Callee = T::__Inner;
type Ret = R;
type Args = ($($t,)*);

fn __imp(self) -> Imp {
// SAFETY: Transmuting to an `unsafe` function pointer
unsafe { mem::transmute(self) }
}
}
};
}

macro_rules! method_impl_allocated {
(<> Allocated<T>, $f:ty, $($t:ident),*) => {
#[doc(hidden)]
impl<T, $($t),*> private::Sealed for $f
impl<T, $($t),*> private::Sealed for $($unsafe)? extern $abi fn(Allocated<T>, Sel $(, $t)*) -> IdReturnValue
where
T: ?Sized + Message,
$($t: EncodeArgument,)*
{}

#[doc(hidden)]
impl<T, $($t),*> MethodImplementation for $f
impl<T, $($t),*> MethodImplementation for $($unsafe)? extern $abi fn(Allocated<T>, Sel $(, $t)*) -> IdReturnValue
where
T: ?Sized + Message,
$($t: EncodeArgument,)*
Expand All @@ -113,29 +84,14 @@ macro_rules! method_impl_allocated {
};
}

macro_rules! method_impl_abi {
($abi:literal; $($t:ident),*) => {
method_impl_generic!(<'a> T: Message, R, extern $abi fn(&'a T, Sel $(, $t)*) -> R, $($t),*);
method_impl_generic!(<'a> T: Message + IsAllowedMutable, R, extern $abi fn(&'a mut T, Sel $(, $t)*) -> R, $($t),*);
method_impl_generic!(<> T: Message, R, unsafe extern $abi fn(*const T, Sel $(, $t)*) -> R, $($t),*);
method_impl_generic!(<> T: Message, R, unsafe extern $abi fn(*mut T, Sel $(, $t)*) -> R, $($t),*);
method_impl_generic!(<'a> T: Message, R, unsafe extern $abi fn(&'a T, Sel $(, $t)*) -> R, $($t),*);
method_impl_generic!(<'a> T: Message + IsAllowedMutable, R, unsafe extern $abi fn(&'a mut T, Sel $(, $t)*) -> R, $($t),*);

method_impl_concrete!(<'a> AnyClass, R, extern $abi fn(&'a AnyClass, Sel $(, $t)*) -> R, $($t),*);
method_impl_concrete!(<> AnyClass, R, unsafe extern $abi fn(*const AnyClass, Sel $(, $t)*) -> R, $($t),*);
method_impl_concrete!(<'a> AnyClass, R, unsafe extern $abi fn(&'a AnyClass, Sel $(, $t)*) -> R, $($t),*);

method_impl_allocated!(<> Allocated<T>, extern $abi fn(Allocated<T>, Sel $(, $t)*) -> IdReturnValue, $($t),*);
method_impl_allocated!(<> Allocated<T>, unsafe extern $abi fn(Allocated<T>, Sel $(, $t)*) -> IdReturnValue, $($t),*);
};
}

macro_rules! method_impl {
($($t:ident),*) => {
method_impl_abi!("C"; $($t),*);
method_impl_inner!((unsafe) "C"; $($t),*);
method_impl_inner!("C"; $($t),*);
#[cfg(feature = "unstable-c-unwind")]
method_impl_inner!((unsafe) "C-unwind"; $($t),*);
#[cfg(feature = "unstable-c-unwind")]
method_impl_abi!("C-unwind"; $($t),*);
method_impl_inner!("C-unwind"; $($t),*);
};
}

Expand Down
29 changes: 28 additions & 1 deletion crates/objc2/tests/declare_class.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#![deny(deprecated, unreachable_code)]
use core::ptr;
use core::ptr::{self, NonNull};

use objc2::declare::IvarEncode;
use objc2::mutability::{Immutable, Mutable};
Expand Down Expand Up @@ -538,3 +538,30 @@ fn out_param3() {
fn out_param4() {
OutParam::unsupported4(None);
}

#[test]
fn test_pointer_receiver_allowed() {
declare_class!(
#[derive(Debug)]
struct PointerReceiver;

unsafe impl ClassType for PointerReceiver {
type Super = NSObject;
type Mutability = Immutable;
const NAME: &'static str = "PointerReceiver";
}

unsafe impl PointerReceiver {
#[method(constPtr)]
fn const_ptr(_this: *const Self) {}

#[method(mutPtr)]
fn mut_ptr(_this: *mut Self) {}

#[method(nonnullPtr)]
fn nonnull_ptr(_this: NonNull<Self>) {}
}
);

let _ = PointerReceiver::class();
}
Loading

0 comments on commit ff33337

Please sign in to comment.