Repository: dtolnay/cxx
Pull request #1372 (2024-08-30 22:14:29)

Hide the UnsafeCell content of opaque C++ types in regard to unwind safety
Opaque C++ types used to be automatically RefUnwindSafe prior to #1370, which is a fine default. This PR fixes a regression that manifests like this:

```console
error[E0277]: the type `UnsafeCell<PhantomData<()>>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
   --> tests/test.rs:389:61
    |
389 |     let _ref_unwind_safe = |c: &ffi::C| panic::catch_unwind(|| inspect(c));
    |                                         ------------------- ^^^^^^^^^^^^^ `UnsafeCell<PhantomData<()>>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
    |                                         |
    |                                         required by a bound introduced by this call
    |
    = help: within `cxx_test_suite::ffi::C`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<PhantomData<()>>`, which is required by `{closure@tests/test.rs:389:61: 389:63}: UnwindSafe`
note: required because it appears within the type `cxx::opaque::SyncUnsafeCell<PhantomData<()>>`
   --> src/opaque.rs:24:8
    |
24  | struct SyncUnsafeCell<T>(UnsafeCell<T>);
    |        ^^^^^^^^^^^^^^
note: required because it appears within the type `cxx::private::Opaque`
   --> src/opaque.rs:16:12
    |
16  | pub struct Opaque {
    |            ^^^^^^
note: required because it appears within the type `cxx_test_suite::ffi::C`
   --> tests/ffi/lib.rs:98:14
    |
98  |         type C;
    |              ^
    = note: required for `&cxx_test_suite::ffi::C` to implement `UnwindSafe`
note: required because it's used within this closure
   --> tests/test.rs:389:61
    |
389 |     let _ref_unwind_safe = |c: &ffi::C| panic::catch_unwind(|| inspect(c));
    |                                                             ^^
note: required by a bound in `std::panic::catch_unwind`
   --> $RUSTUP_HOME/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:344:40
    |
344 | pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
    |                                        ^^^^^^^^^^ required by this bound in `catch_unwind`
```


Patch:
diff --git a/src/opaque.rs b/src/opaque.rs
index bd482785..11c011cc 100644
--- a/src/opaque.rs
+++ b/src/opaque.rs
@@ -4,6 +4,7 @@ use crate::void;
 use core::cell::UnsafeCell;
 use core::marker::{PhantomData, PhantomPinned};
 use core::mem;
+use core::panic::RefUnwindSafe;
 
 // . size = 0
 // . align = 1
@@ -12,6 +13,7 @@ use core::mem;
 // . !Sync
 // . !Unpin
 // . not readonly
+// . unwind-safe
 #[repr(C, packed)]
 pub struct Opaque {
     _private: [*const void; 0],
@@ -19,6 +21,8 @@ pub struct Opaque {
     _mutable: SyncUnsafeCell<PhantomData<()>>,
 }
 
+impl RefUnwindSafe for Opaque {}
+
 // TODO: https://github.com/rust-lang/rust/issues/95439
 #[repr(transparent)]
 struct SyncUnsafeCell<T>(UnsafeCell<T>);


Test patch:
diff --git a/tests/test.rs b/tests/test.rs
index 5c6ff16f..ac396589 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -11,11 +11,12 @@
     clippy::unseparated_literal_suffix
 )]
 
-use cxx::SharedPtr;
+use cxx::{SharedPtr, UniquePtr};
 use cxx_test_suite::module::ffi2;
 use cxx_test_suite::{cast, ffi, R};
 use std::cell::Cell;
 use std::ffi::CStr;
+use std::panic::{self, RefUnwindSafe, UnwindSafe};
 
 thread_local! {
     static CORRECT: Cell<bool> = const { Cell::new(false) };
@@ -380,3 +381,17 @@ fn test_raw_ptr() {
     assert_eq!(2025, unsafe { ffi::c_take_const_ptr(c3) });
     assert_eq!(2025, unsafe { ffi::c_take_mut_ptr(c3 as *mut ffi::C) }); // deletes c3
 }
+
+#[test]
+#[allow(clippy::items_after_statements, clippy::no_effect_underscore_binding)]
+fn test_unwind_safe() {
+    fn inspect(_c: &ffi::C) {}
+    let _unwind_safe = |c: UniquePtr<ffi::C>| panic::catch_unwind(|| drop(c));
+    let _ref_unwind_safe = |c: &ffi::C| panic::catch_unwind(|| inspect(c));
+
+    fn require_unwind_safe<T: UnwindSafe>() {}
+    require_unwind_safe::<ffi::C>();
+
+    fn require_ref_unwind_safe<T: RefUnwindSafe>() {}
+    require_ref_unwind_safe::<ffi::C>();
+}