}
struct Exception {
+ // This needs to be an Option because the object's lifetime follows C++
+ // semantics: when catch_unwind moves the Box out of the exception it must
+ // still leave the exception object in a valid state because its destructor
+ // is still going to be called by __cxa_end_catch..
data: Option<Box<dyn Any + Send>>,
}
// used as the result of the exception copy. This is used by the C++ runtime to
// support capturing exceptions with std::exception_ptr, which we can't support
// because Box<dyn Any> isn't clonable.
-cfg_if::cfg_if! {
- if #[cfg(target_arch = "x86")] {
- unsafe extern "thiscall" fn exception_cleanup(e: *mut [u64; 2]) {
- if (*e)[0] != 0 {
- cleanup(*e);
- }
- }
- #[unwind(allowed)]
- unsafe extern "thiscall" fn exception_copy(_dest: *mut [u64; 2],
- _src: *mut [u64; 2])
- -> *mut [u64; 2] {
- panic!("Rust panics cannot be copied");
- }
- } else {
- unsafe extern "C" fn exception_cleanup(e: *mut [u64; 2]) {
+macro_rules! define_cleanup {
+ ($abi:tt) => {
+ unsafe extern $abi fn exception_cleanup(e: *mut [u64; 2]) {
if (*e)[0] != 0 {
cleanup(*e);
}
}
#[unwind(allowed)]
- unsafe extern "C" fn exception_copy(_dest: *mut [u64; 2],
- _src: *mut [u64; 2])
- -> *mut [u64; 2] {
+ unsafe extern $abi fn exception_copy(_dest: *mut [u64; 2],
+ _src: *mut [u64; 2])
+ -> *mut [u64; 2] {
panic!("Rust panics cannot be copied");
}
}
}
+cfg_if::cfg_if! {
+ if #[cfg(target_arch = "x86")] {
+ define_cleanup!("thiscall");
+ } else {
+ define_cleanup!("C");
+ }
+}
pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
use core::intrinsics::atomic_store;
catchswitch.add_handler(cs, catchpad.llbb());
// The flag value of 8 indicates that we are catching the exception by
- // reference instead of by value.
+ // reference instead of by value. We can't use catch by value because
+ // that requires copying the exception object, which we don't support
+ // since our exception object effectively contains a Box.
//
// Source: MicrosoftCXXABI::getAddrOfCXXCatchHandlerType in clang
let flags = bx.const_i32(8);
catchpad.store(payload, local_ptr, i64_align);
// Clear the first word of the exception so avoid double-dropping it.
+ // This will be read by the destructor which is implicitly called at the
+ // end of the catch block by the runtime.
let payload_0_ptr = catchpad.inbounds_gep(payload_ptr, &[bx.const_i32(0), bx.const_i32(0)]);
catchpad.store(bx.const_u64(0), payload_0_ptr, i64_align);