Skip to content

Commit ac4fadc

Browse files
committed
Remove extra parentheses from Block::call
By having multiple Block::call methods, with different signatures depending on the block's generics. Fixes #575.
1 parent 6311078 commit ac4fadc

14 files changed

Lines changed: 276 additions & 119 deletions

crates/block2/CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

77
## Unreleased - YYYY-MM-DD
88

9+
### Changed
10+
* **BREAKING**: Removed extra parentheses when invoking `Block::call`.
11+
912

1013
## [0.6.2] - 2025-10-04
1114
[0.6.2]: https://github.com/madsmtm/objc2/compare/block2-0.6.1...block2-0.6.2
1215

13-
## Fixed
16+
### Fixed
1417
* Fixed documentation on docs.rs.
1518

1619

crates/block2/src/block.rs

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -96,24 +96,9 @@ impl<F: ?Sized> Block<F> {
9696
unsafe { RcBlock::copy(ptr) }.unwrap_or_else(|| block_copy_fail())
9797
}
9898

99-
/// Call the block.
100-
///
101-
/// The arguments must be passed as a tuple. The return is the output of
102-
/// the block.
103-
#[doc(alias = "invoke")]
104-
pub fn call(&self, args: F::Args) -> F::Output
105-
where
106-
F: BlockFn,
107-
{
99+
pub(crate) fn invoke_ptr(&self) -> unsafe extern "C-unwind" fn() {
108100
// TODO: Is `invoke` actually ever null?
109-
let invoke = self.header().invoke.unwrap_or_else(|| unreachable!());
110-
111-
let ptr: NonNull<Self> = NonNull::from(self);
112-
let ptr: *mut Self = ptr.as_ptr();
113-
114-
// SAFETY: The closure is an `Fn`, and as such is safe to call from an
115-
// immutable reference.
116-
unsafe { F::__call_block(invoke, ptr, args) }
101+
self.header().invoke.unwrap_or_else(|| unreachable!())
117102
}
118103
}
119104

@@ -138,15 +123,15 @@ mod tests {
138123
#[test]
139124
fn test_rust_dyn_lifetime_semantics() {
140125
fn takes_static(block: &Block<dyn Fn() + 'static>) {
141-
block.call(());
126+
block.call();
142127
}
143128

144129
fn takes_elided(block: &Block<dyn Fn() + '_>) {
145-
block.call(());
130+
block.call();
146131
}
147132

148133
fn takes_unspecified(block: &Block<dyn Fn()>) {
149-
block.call(());
134+
block.call();
150135
}
151136

152137
// Static lifetime
@@ -188,7 +173,7 @@ mod tests {
188173

189174
#[allow(dead_code)]
190175
fn lending_block<'b>(block: &Block<dyn Fn() -> &'b i32 + 'b>) {
191-
let _ = *block.call(());
176+
let _ = *block.call();
192177
}
193178

194179
#[allow(dead_code)]

crates/block2/src/global.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ impl<F: ?Sized> GlobalBlock<F> {
7777
// TODO: Add some constructor for when `F: Copy`.
7878
}
7979

80-
impl<F: ?Sized + BlockFn> Deref for GlobalBlock<F> {
80+
impl<F: ?Sized> Deref for GlobalBlock<F> {
8181
type Target = Block<F>;
8282

8383
#[inline]
@@ -119,7 +119,7 @@ impl<F: ?Sized> fmt::Debug for GlobalBlock<F> {
119119
/// 42
120120
/// };
121121
/// }
122-
/// assert_eq!(MY_BLOCK.call(()), 42);
122+
/// assert_eq!(MY_BLOCK.call(), 42);
123123
/// ```
124124
///
125125
/// ```
@@ -129,7 +129,7 @@ impl<F: ?Sized> fmt::Debug for GlobalBlock<F> {
129129
/// x + y
130130
/// };
131131
/// }
132-
/// assert_eq!(ADDER_BLOCK.call((5, 7)), 12);
132+
/// assert_eq!(ADDER_BLOCK.call(5, 7), 12);
133133
/// ```
134134
///
135135
/// The following does not compile because [`Box`] is not [`EncodeArgument`]:
@@ -144,15 +144,15 @@ impl<F: ?Sized> fmt::Debug for GlobalBlock<F> {
144144
/// This also doesn't work (yet), as blocks are overly restrictive about the
145145
/// lifetimes involved.
146146
///
147-
/// ```compile_fail
147+
/// ```compile_error
148148
/// use block2::global_block;
149149
/// global_block! {
150150
/// pub static BLOCK_WITH_LIFETIME = |x: &i32| -> i32 {
151151
/// *x + 42
152152
/// };
153153
/// }
154154
/// let x = 5;
155-
/// let res = BLOCK_WITH_LIFETIME.call((&x,));
155+
/// let res = BLOCK_WITH_LIFETIME.call(&x);
156156
/// assert_eq!(res, 47);
157157
/// ```
158158
///
@@ -228,15 +228,15 @@ mod tests {
228228

229229
#[test]
230230
fn test_noop_block() {
231-
NOOP_BLOCK.call(());
231+
NOOP_BLOCK.call();
232232
}
233233

234234
#[test]
235235
fn test_defined_in_function() {
236236
global_block!(static MY_BLOCK = || -> i32 {
237237
42
238238
});
239-
assert_eq!(MY_BLOCK.call(()), 42);
239+
assert_eq!(MY_BLOCK.call(), 42);
240240
}
241241

242242
#[cfg(target_vendor = "apple")]

crates/block2/src/lib.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
//! #
3737
//! # impl ExampleObject {
3838
//! # fn someMethod(&self, block: &block2::Block<dyn Fn(i32, i32) -> i32>) {
39-
//! # assert_eq!(block.call((5, 8)), 18);
39+
//! # assert_eq!(block.call(5, 8), 18);
4040
//! # }
4141
//! # }
4242
//! #
@@ -140,8 +140,8 @@
140140
//! let b = RcBlock::new(fnmut_to_fn(|| {
141141
//! x += 1;
142142
//! }));
143-
//! b.call(());
144-
//! b.call(());
143+
//! b.call();
144+
//! b.call();
145145
//! drop(b);
146146
//! assert_eq!(x, 2);
147147
//! ```
@@ -182,7 +182,7 @@
182182
//! let b = RcBlock::new(fnonce_to_fn(move || {
183183
//! drop(v);
184184
//! }));
185-
//! b.call(());
185+
//! b.call();
186186
//! ```
187187
//!
188188
//!
@@ -258,13 +258,10 @@
258258
//!
259259
//! #[no_mangle]
260260
//! extern "C" fn check_addition(block: &Block<dyn Fn(i32, i32) -> i32>) {
261-
//! assert_eq!(block.call((5, 8)), 13);
261+
//! assert_eq!(block.call(5, 8), 13);
262262
//! }
263263
//! ```
264264
//!
265-
//! Note the extra parentheses in the `call` method, since the arguments must
266-
//! be passed as a tuple.
267-
//!
268265
//!
269266
//! ## Specifying a runtime
270267
//!

crates/block2/src/rc_block.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ impl<F: ?Sized> RcBlock<F> {
177177
/// let my_block = RcBlock::with_encoding::<_, _, _, MyBlockEncoding>(|_err: *mut NSError| {
178178
/// 42i32
179179
/// });
180-
/// assert_eq!(my_block.call((core::ptr::null_mut(),)), 42);
180+
/// assert_eq!(my_block.call(core::ptr::null_mut()), 42);
181181
/// ```
182182
#[inline]
183183
pub fn with_encoding<'f, A, R, Closure, E>(closure: Closure) -> Self
@@ -300,8 +300,8 @@ mod tests {
300300
}
301301

302302
let add2 = get_adder(2);
303-
assert_eq!(add2.call((5,)), 7);
304-
assert_eq!(add2.call((-1,)), 1);
303+
assert_eq!(add2.call(5), 7);
304+
assert_eq!(add2.call(-1), 1);
305305
}
306306

307307
#[test]
@@ -350,16 +350,16 @@ mod tests {
350350
match n {
351351
0 => 0,
352352
1 => 1,
353-
n => captured_fibonacci.call((n - 1,)) + captured_fibonacci.call((n - 2,)),
353+
n => captured_fibonacci.call(n - 1) + captured_fibonacci.call(n - 2),
354354
}
355355
};
356356

357357
let block = block.get_or_init(|| RcBlock::new(fibonacci));
358358

359-
assert_eq!(block.call((0,)), 0);
360-
assert_eq!(block.call((1,)), 1);
361-
assert_eq!(block.call((6,)), 8);
362-
assert_eq!(block.call((10,)), 55);
363-
assert_eq!(block.call((19,)), 4181);
359+
assert_eq!(block.call(0), 0);
360+
assert_eq!(block.call(1), 1);
361+
assert_eq!(block.call(6), 8);
362+
assert_eq!(block.call(10), 55);
363+
assert_eq!(block.call(19), 4181);
364364
}
365365
}

crates/block2/src/stack.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ where
176176
/// use block2::StackBlock;
177177
/// #
178178
/// # extern "C" fn check_addition(block: &block2::Block<dyn Fn(i32, i32) -> i32>) {
179-
/// # assert_eq!(block.call((5, 8)), 13);
179+
/// # assert_eq!(block.call(5, 8), 13);
180180
/// # }
181181
///
182182
/// let block = StackBlock::new(|a, b| a + b);
@@ -246,7 +246,7 @@ where
246246
/// let my_block = StackBlock::with_encoding::<MyBlockEncoding>(|_err: *mut NSError| {
247247
/// 42i32
248248
/// });
249-
/// assert_eq!(my_block.call((core::ptr::null_mut(),)), 42);
249+
/// assert_eq!(my_block.call(core::ptr::null_mut()), 42);
250250
/// ```
251251
#[inline]
252252
pub fn with_encoding<E>(closure: Closure) -> Self

crates/block2/src/traits.rs

Lines changed: 33 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,6 @@ pub unsafe trait BlockFn: private::Sealed<Self::Args, Self::Output> {
3232

3333
/// The return type of the block.
3434
type Output: EncodeReturn;
35-
36-
/// Calls the given invoke function with the block and arguments.
37-
#[doc(hidden)]
38-
unsafe fn __call_block(
39-
invoke: unsafe extern "C-unwind" fn(),
40-
block: *mut Block<Self>,
41-
args: Self::Args,
42-
) -> Self::Output;
4335
}
4436

4537
/// Types that may be converted into a block.
@@ -66,7 +58,7 @@ where
6658
}
6759

6860
macro_rules! impl_traits {
69-
($($a:ident: $t:ident),*) => (
61+
($num_args:literal; $($a:ident: $t:ident),*) => (
7062
impl<$($t: EncodeArgument,)* R: EncodeReturn, Closure> private::Sealed<($($t,)*), R> for Closure
7163
where
7264
Closure: ?Sized + Fn($($t),*) -> R,
@@ -76,20 +68,6 @@ macro_rules! impl_traits {
7668
unsafe impl<$($t: EncodeArgument,)* R: EncodeReturn> BlockFn for dyn Fn($($t),*) -> R + '_ {
7769
type Args = ($($t,)*);
7870
type Output = R;
79-
80-
#[inline]
81-
unsafe fn __call_block(
82-
invoke: unsafe extern "C-unwind" fn(),
83-
block: *mut Block<Self>,
84-
($($a,)*): Self::Args,
85-
) -> Self::Output {
86-
// Very similar to `MessageArguments::__invoke`
87-
let invoke: unsafe extern "C-unwind" fn(*mut Block<Self> $(, $t)*) -> R = unsafe {
88-
mem::transmute(invoke)
89-
};
90-
91-
unsafe { invoke(block $(, $a)*) }
92-
}
9371
}
9472

9573
unsafe impl<'f, $($t,)* R, Closure> IntoBlock<'f, ($($t,)*), R> for Closure
@@ -121,22 +99,41 @@ macro_rules! impl_traits {
12199
}
122100
}
123101
}
102+
103+
impl<'f, $($t: EncodeArgument,)* R: EncodeReturn> Block<dyn Fn($($t),*) -> R + 'f> {
104+
#[doc = concat!("Call the block with ", $num_args, " arguments.")]
105+
///
106+
/// The return value is the output of the block.
107+
#[doc(alias = "invoke")]
108+
#[inline]
109+
#[allow(clippy::too_many_arguments)]
110+
pub fn call(&self, $($a: $t),*) -> R {
111+
// Very similar to `MessageArguments::__invoke`
112+
let invoke: unsafe extern "C-unwind" fn(&Self $(, $t)*) -> R = unsafe {
113+
mem::transmute(self.invoke_ptr())
114+
};
115+
116+
// SAFETY: The closure is an `Fn`, and as such is safe to call
117+
// from an immutable reference.
118+
unsafe { invoke(self $(, $a)*) }
119+
}
120+
}
124121
);
125122
}
126123

127-
impl_traits!();
128-
impl_traits!(t0: T0);
129-
impl_traits!(t0: T0, t1: T1);
130-
impl_traits!(t0: T0, t1: T1, t2: T2);
131-
impl_traits!(t0: T0, t1: T1, t2: T2, t3: T3);
132-
impl_traits!(t0: T0, t1: T1, t2: T2, t3: T3, t4: T4);
133-
impl_traits!(t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5);
134-
impl_traits!(t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6);
135-
impl_traits!(t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7);
136-
impl_traits!(t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8);
137-
impl_traits!(t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8, t9: T9);
138-
impl_traits!(t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8, t9: T9, t10: T10);
139-
impl_traits!(t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8, t9: T9, t10: T10, t11: T11);
124+
impl_traits!(0;);
125+
impl_traits!(1; t0: T0);
126+
impl_traits!(2; t0: T0, t1: T1);
127+
impl_traits!(3; t0: T0, t1: T1, t2: T2);
128+
impl_traits!(4; t0: T0, t1: T1, t2: T2, t3: T3);
129+
impl_traits!(5; t0: T0, t1: T1, t2: T2, t3: T3, t4: T4);
130+
impl_traits!(6; t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5);
131+
impl_traits!(7; t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6);
132+
impl_traits!(8; t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7);
133+
impl_traits!(9; t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8);
134+
impl_traits!(10; t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8, t9: T9);
135+
impl_traits!(11; t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8, t9: T9, t10: T10);
136+
impl_traits!(12; t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8, t9: T9, t10: T10, t11: T11);
140137

141138
/// Interim abstraction to manually provide block encodings for use at compile
142139
/// time with [`StackBlock::with_encoding`] and [`RcBlock::with_encoding`].
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
use block2::{Block, RcBlock, StackBlock};
2+
3+
fn takes_block0(block: &Block<dyn Fn()>) {
4+
block.call(1);
5+
}
6+
7+
fn takes_block1(block: &Block<dyn Fn(i32)>) {
8+
block.call();
9+
block.call(1, 2);
10+
}
11+
12+
fn main() {
13+
takes_block0(&StackBlock::new(|_x| {}));
14+
takes_block0(&RcBlock::new(|_x| {}));
15+
16+
takes_block1(&StackBlock::new(|| {}));
17+
takes_block1(&RcBlock::new(|| {}));
18+
19+
takes_block1(&StackBlock::new(|_x, _y| {}));
20+
takes_block1(&RcBlock::new(|_x, _y| {}));
21+
}

0 commit comments

Comments
 (0)