Skip to content

Commit a545fb0

Browse files
committed
Use extern "C-unwind"
C-unwind was added in Rust 1.71, and allows panicking/unwinding /exceptions across foreign function interfaces. Additionally, Rust decided to let handling of foreign unwinds be implementation defined behavior (instead of undefined), so we can now mark `throw` as safe, see rust-lang/rust#128321. This has a cost in that we now have landing pads on every message send; this is strictly the correct choice, though, so we will have to bear with it. Fixes #539.
1 parent dfb7562 commit a545fb0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+4227
-1144
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ jobs:
492492

493493
- name: Test Foundation with unstable features
494494
if: ${{ matrix.nightly }}
495-
run: cargo test $ARGS $PUBLIC_CRATES -ptests $INTERESTING_FEATURES -pobjc2-foundation --features=catch-all,unstable-autoreleasesafe,unstable-c-unwind ${{ matrix.sdk != '10.12' && '--features=unstable-simd' || '' }}
495+
run: cargo test $ARGS $PUBLIC_CRATES -ptests $INTERESTING_FEATURES -pobjc2-foundation --features=catch-all,unstable-autoreleasesafe ${{ matrix.sdk != '10.12' && '--features=unstable-simd' || '' }}
496496

497497
# TODO: Re-enable this on all of Foundation once we do some form of
498498
# availability checking.

Cargo.toml

-3
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,6 @@ ptr_as_ptr = "warn"
4141
inherits = "release"
4242
# Enable LTO to allow testing the `unstable-static-sel-inlined` feature
4343
lto = true
44-
# Don't emit unwind info; while important to get right, the control flow is
45-
# very hard to glean from assembly output.
46-
panic = "abort"
4744

4845
# Release data for framework crates
4946
[workspace.metadata.release]

crates/block2/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
7373
* **BREAKING**: Fixed `GlobalBlock` not having the correct variance. This may
7474
break if you were using lifetimes in your parameters, as those are now a bit
7575
too restrictive.
76+
* **BREAKING**: Converted function signatures into using `extern "C-unwind"`.
77+
This allows unwinding through blocks.
7678

7779

7880
## 0.4.0 - 2023-12-03

crates/block2/Cargo.toml

-8
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,6 @@ unstable-winobjc = ["gnustep-1-8"]
4141
# Link to ObjFW.
4242
unstable-objfw = []
4343

44-
# Uses `extern "C-unwind"` on relevant function declarations.
45-
#
46-
# This raises MSRV to `1.71`.
47-
#
48-
# Warning: Enabling this is a breaking change for consumer crates, as it
49-
# changes the signature of functions.
50-
unstable-c-unwind = []
51-
5244
# Expose private ffi functions and statics.
5345
unstable-private = []
5446

crates/block2/src/abi.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ pub struct BlockHeader {
199199
/// If the BLOCK_USE_SRET & BLOCK_HAS_SIGNATURE flag is set, there is an
200200
/// additional hidden parameter, which is a pointer to the space on the
201201
/// stack allocated to hold the return value.
202-
pub invoke: Option<crate::__c_unwind!(unsafe extern "C" fn())>,
202+
pub invoke: Option<unsafe extern "C-unwind" fn()>,
203203
/// The block's descriptor.
204204
pub(crate) descriptor: BlockDescriptorPtr,
205205
}
@@ -257,13 +257,12 @@ pub(crate) struct BlockDescriptorCopyDispose {
257257
///
258258
/// This may be NULL since macOS 11.0.1 in Apple's runtime, but this
259259
/// should not be relied on.
260-
pub(crate) copy:
261-
Option<crate::__c_unwind!(unsafe extern "C" fn(dst: *mut c_void, src: *const c_void))>,
260+
pub(crate) copy: Option<unsafe extern "C-unwind" fn(dst: *mut c_void, src: *const c_void)>,
262261
/// Helper to destroy the block after being copied.
263262
///
264263
/// This may be NULL since macOS 11.0.1 in Apple's runtime, but this
265264
/// should not be relied on.
266-
pub(crate) dispose: Option<crate::__c_unwind!(unsafe extern "C" fn(src: *mut c_void))>,
265+
pub(crate) dispose: Option<unsafe extern "C-unwind" fn(src: *mut c_void)>,
267266
}
268267

269268
/// Block descriptor that has an encoding / a signature.
@@ -303,13 +302,12 @@ pub(crate) struct BlockDescriptorCopyDisposeSignature {
303302
///
304303
/// This may be NULL since macOS 11.0.1 in Apple's runtime, but this
305304
/// should not be relied on.
306-
pub(crate) copy:
307-
Option<crate::__c_unwind!(unsafe extern "C" fn(dst: *mut c_void, src: *const c_void))>,
305+
pub(crate) copy: Option<unsafe extern "C-unwind" fn(dst: *mut c_void, src: *const c_void)>,
308306
/// Helper to destroy the block after being copied.
309307
///
310308
/// This may be NULL since macOS 11.0.1 in Apple's runtime, but this
311309
/// should not be relied on.
312-
pub(crate) dispose: Option<crate::__c_unwind!(unsafe extern "C" fn(src: *mut c_void))>,
310+
pub(crate) dispose: Option<unsafe extern "C-unwind" fn(src: *mut c_void)>,
313311

314312
/// Objective-C type encoding of the block.
315313
#[doc(alias = "signature")]

crates/block2/src/ffi.rs

+6-54
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,6 @@ use core::ffi::c_int;
55
use core::ffi::c_void;
66
use core::marker::{PhantomData, PhantomPinned};
77

8-
#[cfg(not(feature = "unstable-c-unwind"))]
9-
#[doc(hidden)]
10-
#[macro_export]
11-
macro_rules! __c_unwind {
12-
(unsafe extern "C" $($t:tt)*) => {
13-
unsafe extern "C" $($t)*
14-
};
15-
(extern "C" $($t:tt)*) => {
16-
extern "C" $($t)*
17-
};
18-
}
19-
20-
#[cfg(feature = "unstable-c-unwind")]
21-
#[doc(hidden)]
22-
#[macro_export]
23-
macro_rules! __c_unwind {
24-
(unsafe extern "C" $($t:tt)*) => {
25-
unsafe extern "C-unwind" $($t)*
26-
};
27-
(extern "C" $($t:tt)*) => {
28-
extern "C-unwind" $($t)*
29-
};
30-
}
31-
328
/// Type for block class ISAs.
339
///
3410
/// This will likely become an extern type in the future.
@@ -55,26 +31,8 @@ pub struct Class {
5531
_opaque: UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>,
5632
}
5733

58-
#[cfg(not(feature = "unstable-c-unwind"))]
59-
macro_rules! extern_c_unwind {
60-
($($t:tt)*) => {
61-
extern "C" {
62-
$($t)*
63-
}
64-
};
65-
}
66-
67-
#[cfg(feature = "unstable-c-unwind")]
68-
macro_rules! extern_c_unwind {
69-
($($t:tt)*) => {
70-
extern "C-unwind" {
71-
$($t)*
72-
}
73-
};
74-
}
75-
7634
// Use `extern "C-unwind"`, runtime functions may call external routines.
77-
extern_c_unwind! {
35+
extern "C-unwind" {
7836
/// Class ISA used for global blocks.
7937
pub static _NSConcreteGlobalBlock: Class;
8038

@@ -126,7 +84,7 @@ pub mod private {
12684
#[cfg(any(doc, target_vendor = "apple", feature = "compiler-rt"))]
12785
use core::ffi::c_ulong;
12886

129-
extern_c_unwind! {
87+
extern "C-unwind" {
13088
pub static _NSConcreteMallocBlock: Class;
13189
#[cfg(any(doc, target_vendor = "apple", feature = "compiler-rt"))]
13290
pub static _NSConcreteAutoBlock: Class;
@@ -203,21 +161,15 @@ mod tests {
203161
println!("{:?}", unsafe {
204162
ptr::addr_of!(private::_NSConcreteMallocBlock)
205163
});
164+
println!("{:p}", _Block_copy as unsafe extern "C-unwind" fn(_) -> _);
206165
println!(
207166
"{:p}",
208-
_Block_copy as __c_unwind!(unsafe extern "C" fn(_) -> _)
209-
);
210-
println!(
211-
"{:p}",
212-
_Block_object_assign as __c_unwind!(unsafe extern "C" fn(_, _, _))
213-
);
214-
println!(
215-
"{:p}",
216-
_Block_object_dispose as __c_unwind!(unsafe extern "C" fn(_, _))
167+
_Block_object_assign as unsafe extern "C-unwind" fn(_, _, _)
217168
);
218169
println!(
219170
"{:p}",
220-
_Block_release as __c_unwind!(unsafe extern "C" fn(_))
171+
_Block_object_dispose as unsafe extern "C-unwind" fn(_, _)
221172
);
173+
println!("{:p}", _Block_release as unsafe extern "C-unwind" fn(_));
222174
}
223175
}

crates/block2/src/global.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -187,17 +187,17 @@ macro_rules! global_block {
187187
let mut header = $crate::GlobalBlock::<dyn Fn($($t),*) $(-> $r)? + 'static>::__DEFAULT_HEADER;
188188
header.isa = ::core::ptr::addr_of!($crate::ffi::_NSConcreteGlobalBlock);
189189
header.invoke = ::core::option::Option::Some({
190-
$crate::__c_unwind!(unsafe extern "C" fn inner(
190+
unsafe extern "C-unwind" fn inner(
191191
_: *mut $crate::GlobalBlock<dyn Fn($($t),*) $(-> $r)? + 'static>,
192192
$($a: $t),*
193193
) $(-> $r)? {
194194
$body
195-
});
195+
}
196196

197197
// TODO: SAFETY
198198
::core::mem::transmute::<
199-
$crate::__c_unwind!(unsafe extern "C" fn(*mut $crate::GlobalBlock<dyn Fn($($t),*) $(-> $r)? + 'static>, $($a: $t),*) $(-> $r)?),
200-
$crate::__c_unwind!(unsafe extern "C" fn()),
199+
unsafe extern "C-unwind" fn(*mut $crate::GlobalBlock<dyn Fn($($t),*) $(-> $r)? + 'static>, $($a: $t),*) $(-> $r)?,
200+
unsafe extern "C-unwind" fn(),
201201
>(inner)
202202
});
203203
$crate::GlobalBlock::from_header(header)

crates/block2/src/stack.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ impl<'f, A, R, Closure> StackBlock<'f, A, R, Closure> {
7777
const SIZE: c_ulong = mem::size_of::<Self>() as _;
7878

7979
// Drop the closure that this block contains.
80-
crate::__c_unwind! {unsafe extern "C" fn drop_closure(block: *mut c_void) {
80+
unsafe extern "C-unwind" fn drop_closure(block: *mut c_void) {
8181
let block: *mut Self = block.cast();
8282
// When this function is called, the block no longer lives on the
8383
// stack, it has been moved to the heap as part of some `_Block_copy`
@@ -97,7 +97,7 @@ impl<'f, A, R, Closure> StackBlock<'f, A, R, Closure> {
9797
// part of some `_Block_copy` operation, and as such it is valid to
9898
// drop here.
9999
unsafe { ptr::drop_in_place(closure) };
100-
}}
100+
}
101101

102102
const DESCRIPTOR_BASIC: BlockDescriptor = BlockDescriptor {
103103
reserved: 0,
@@ -108,7 +108,7 @@ impl<'f, A, R, Closure> StackBlock<'f, A, R, Closure> {
108108
// `StackBlock::new`
109109
impl<'f, A, R, Closure: Clone> StackBlock<'f, A, R, Closure> {
110110
// Clone the closure from one block to another.
111-
crate::__c_unwind! {unsafe extern "C" fn clone_closure(dst: *mut c_void, src: *const c_void) {
111+
unsafe extern "C-unwind" fn clone_closure(dst: *mut c_void, src: *const c_void) {
112112
let dst: *mut Self = dst.cast();
113113
let src: *const Self = src.cast();
114114
// When this function is called as part of some `_Block_copy`
@@ -132,7 +132,7 @@ impl<'f, A, R, Closure: Clone> StackBlock<'f, A, R, Closure> {
132132
// already `memmove`d data once more, which is unnecessary for closure
133133
// captures that implement `Copy`.
134134
unsafe { ptr::write(dst_closure, src_closure.clone()) };
135-
}}
135+
}
136136

137137
const DESCRIPTOR_WITH_CLONE: BlockDescriptorCopyDispose = BlockDescriptorCopyDispose {
138138
reserved: 0,
@@ -198,10 +198,10 @@ impl<'f, A, R, Closure> StackBlock<'f, A, R, Closure> {
198198

199199
// `RcBlock::new`
200200
impl<'f, A, R, Closure> StackBlock<'f, A, R, Closure> {
201-
crate::__c_unwind! {unsafe extern "C" fn empty_clone_closure(_dst: *mut c_void, _src: *const c_void) {
201+
unsafe extern "C-unwind" fn empty_clone_closure(_dst: *mut c_void, _src: *const c_void) {
202202
// We do nothing, the closure has been `memmove`'d already, and
203203
// ownership will be passed in `RcBlock::new`.
204-
}}
204+
}
205205

206206
const DESCRIPTOR_WITH_DROP: BlockDescriptorCopyDispose = BlockDescriptorCopyDispose {
207207
reserved: 0,

crates/block2/src/traits.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub unsafe trait BlockFn: private::Sealed<Self::Args, Self::Output> {
3434
/// Calls the given invoke function with the block and arguments.
3535
#[doc(hidden)]
3636
unsafe fn __call_block(
37-
invoke: crate::__c_unwind!(unsafe extern "C" fn()),
37+
invoke: unsafe extern "C-unwind" fn(),
3838
block: *mut Block<Self>,
3939
args: Self::Args,
4040
) -> Self::Output;
@@ -60,7 +60,7 @@ where
6060
type Dyn: ?Sized + BlockFn<Args = A, Output = R>;
6161

6262
#[doc(hidden)]
63-
fn __get_invoke_stack_block() -> crate::__c_unwind!(unsafe extern "C" fn());
63+
fn __get_invoke_stack_block() -> unsafe extern "C-unwind" fn();
6464
}
6565

6666
macro_rules! impl_traits {
@@ -77,12 +77,12 @@ macro_rules! impl_traits {
7777

7878
#[inline]
7979
unsafe fn __call_block(
80-
invoke: crate::__c_unwind!(unsafe extern "C" fn()),
80+
invoke: unsafe extern "C-unwind" fn(),
8181
block: *mut Block<Self>,
8282
($($a,)*): Self::Args,
8383
) -> Self::Output {
8484
// Very similar to `MessageArguments::__invoke`
85-
let invoke: unsafe extern "C" fn(*mut Block<Self> $(, $t)*) -> R = unsafe {
85+
let invoke: unsafe extern "C-unwind" fn(*mut Block<Self> $(, $t)*) -> R = unsafe {
8686
mem::transmute(invoke)
8787
};
8888

@@ -99,8 +99,8 @@ macro_rules! impl_traits {
9999
type Dyn = dyn Fn($($t),*) -> R + 'f;
100100

101101
#[inline]
102-
fn __get_invoke_stack_block() -> crate::__c_unwind!(unsafe extern "C" fn()) {
103-
crate::__c_unwind!(unsafe extern "C" fn invoke<'f, $($t,)* R, Closure>(
102+
fn __get_invoke_stack_block() -> unsafe extern "C-unwind" fn() {
103+
unsafe extern "C-unwind" fn invoke<'f, $($t,)* R, Closure>(
104104
block: *mut StackBlock<'f, ($($t,)*), R, Closure>,
105105
$($a: $t,)*
106106
) -> R
@@ -109,12 +109,12 @@ macro_rules! impl_traits {
109109
{
110110
let closure = unsafe { &*ptr::addr_of!((*block).closure) };
111111
(closure)($($a),*)
112-
});
112+
}
113113

114114
unsafe {
115115
mem::transmute::<
116-
crate::__c_unwind!(unsafe extern "C" fn(*mut StackBlock<'f, ($($t,)*), R, Closure>, $($t,)*) -> R),
117-
crate::__c_unwind!(unsafe extern "C" fn()),
116+
unsafe extern "C-unwind" fn(*mut StackBlock<'f, ($($t,)*), R, Closure>, $($t,)*) -> R,
117+
unsafe extern "C-unwind" fn(),
118118
>(invoke)
119119
}
120120
}

crates/header-translator/src/rust_type.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1279,7 +1279,11 @@ impl Ty {
12791279
if *nullability != Nullability::NonNull {
12801280
write!(f, "Option<")?;
12811281
}
1282-
write!(f, "unsafe extern \"C\" fn(")?;
1282+
// Allow pointers that the user provides to unwind.
1283+
//
1284+
// This is not _necessarily_ safe, though in practice
1285+
// it will be for all of Apple's frameworks.
1286+
write!(f, "unsafe extern \"C-unwind\" fn(")?;
12831287
for arg in arguments {
12841288
write!(f, "{},", arg.plain())?;
12851289
}

crates/header-translator/src/stmt.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -2370,7 +2370,9 @@ impl Stmt {
23702370
body: None,
23712371
safe: false,
23722372
} => {
2373-
writeln!(f, "extern \"C\" {{")?;
2373+
// Functions are always C-unwind, since we don't know
2374+
// anything about them.
2375+
writeln!(f, "extern \"C-unwind\" {{")?;
23742376

23752377
write!(f, " {}", self.cfg_gate_ln(config))?;
23762378
write!(f, " {availability}")?;
@@ -2395,14 +2397,14 @@ impl Stmt {
23952397
write!(f, "{}", self.cfg_gate_ln(config))?;
23962398
write!(f, "{availability}")?;
23972399
writeln!(f, "#[inline]")?;
2398-
write!(f, "pub extern \"C\" fn {}(", id.name)?;
2400+
write!(f, "pub extern \"C-unwind\" fn {}(", id.name)?;
23992401
for (param, arg_ty) in arguments {
24002402
let param = handle_reserved(&crate::to_snake_case(param));
24012403
write!(f, "{param}: {},", arg_ty.fn_argument())?;
24022404
}
24032405
writeln!(f, "){} {{", result_type.fn_return())?;
24042406

2405-
writeln!(f, " extern \"C\" {{")?;
2407+
writeln!(f, " extern \"C-unwind\" {{")?;
24062408

24072409
write!(f, " fn {}(", id.name)?;
24082410
for (param, arg_ty) in arguments {

crates/objc2-exception-helper/Cargo.toml

-8
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,6 @@ gnustep-1-9 = ["gnustep-1-8"]
3838
gnustep-2-0 = ["gnustep-1-9"]
3939
gnustep-2-1 = ["gnustep-2-0"]
4040

41-
# Uses `extern "C-unwind"` on relevant function declarations.
42-
#
43-
# This raises MSRV to `1.71`.
44-
#
45-
# Warning: Enabling this is a breaking change for consumer crates, as it
46-
# changes the signature of functions.
47-
unstable-c-unwind = []
48-
4941
[build-dependencies]
5042
cc = "1.0.80"
5143

crates/objc2-exception-helper/src/lib.rs

-8
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ extern crate std;
1818

1919
use core::ffi::c_void;
2020

21-
#[cfg(not(feature = "unstable-c-unwind"))]
22-
type TryCatchClosure = extern "C" fn(*mut c_void);
23-
#[cfg(feature = "unstable-c-unwind")]
2421
type TryCatchClosure = extern "C-unwind" fn(*mut c_void);
2522

2623
// `try_catch` is deliberately `extern "C"`, we just prevented the unwind.
@@ -59,11 +56,6 @@ mod tests {
5956

6057
static VALUE: SyncPtr = SyncPtr(&VALUE.0 as *const *mut c_void as *mut c_void);
6158

62-
#[cfg(not(feature = "unstable-c-unwind"))]
63-
extern "C" fn check_value(value: *mut c_void) {
64-
assert_eq!(VALUE.0, value);
65-
}
66-
#[cfg(feature = "unstable-c-unwind")]
6759
extern "C-unwind" fn check_value(value: *mut c_void) {
6860
assert_eq!(VALUE.0, value);
6961
}

crates/objc2/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
120120
### Fixed
121121
* Remove an incorrect assertion when adding protocols to classes in an unexpected
122122
order.
123+
* **BREAKING**: Converted function signatures into using `extern "C-unwind"`
124+
where applicable. This allows Rust and Objective-C unwinding to interoperate.
123125

124126

125127
## 0.5.2 - 2024-05-21

0 commit comments

Comments
 (0)