Repository: dtolnay/cxx (rust)

Issue:
Returning `&CxxVector<SharedString>` from C++ FFI gives "not FFI-safe" error
Similar to https://github.com/dtolnay/cxx/issues/249, but instead of using the SharedString directly, I get this error when the struct containing the string is returned via a CxxVector:

```
#[cxx::bridge]
pub mod ffi {
  struct SharedString {
    x: String,
  }
  unsafe extern "C++" {
    type View;
    fn get_strings(view: &View) -> &CxxVector<SharedString>;
  }
}
```

Pull request:
Fix "not FFI-safe" on CxxVector containing struct containing Rust string
Fixes #857.

Patch:
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index ec9e4618..ce02c430 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -1593,10 +1593,10 @@ fn expand_cxx_vector(
                     #[link_name = #link_push_back]
                     fn __push_back #impl_generics(
                         this: ::std::pin::Pin<&mut ::cxx::CxxVector<#elem #ty_generics>>,
-                        value: &mut ::std::mem::ManuallyDrop<#elem #ty_generics>,
+                        value: *mut ::std::ffi::c_void,
                     );
                 }
-                __push_back(this, value);
+                __push_back(this, value as *mut ::std::mem::ManuallyDrop<Self> as *mut ::std::ffi::c_void);
             }
             #[doc(hidden)]
             unsafe fn __pop_back(
@@ -1607,10 +1607,10 @@ fn expand_cxx_vector(
                     #[link_name = #link_pop_back]
                     fn __pop_back #impl_generics(
                         this: ::std::pin::Pin<&mut ::cxx::CxxVector<#elem #ty_generics>>,
-                        out: &mut ::std::mem::MaybeUninit<#elem #ty_generics>,
+                        out: *mut ::std::ffi::c_void,
                     );
                 }
-                __pop_back(this, out);
+                __pop_back(this, out as *mut ::std::mem::MaybeUninit<Self> as *mut ::std::ffi::c_void);
             }
         })
     } else {
@@ -1635,9 +1635,12 @@ fn expand_cxx_vector(
             unsafe fn __get_unchecked(v: *mut ::cxx::CxxVector<Self>, pos: usize) -> *mut Self {
                 extern "C" {
                     #[link_name = #link_get_unchecked]
-                    fn __get_unchecked #impl_generics(_: *mut ::cxx::CxxVector<#elem #ty_generics>, _: usize) -> *mut #elem #ty_generics;
+                    fn __get_unchecked #impl_generics(
+                        v: *mut ::cxx::CxxVector<#elem #ty_generics>,
+                        pos: usize,
+                    ) -> *mut ::std::ffi::c_void;
                 }
-                __get_unchecked(v, pos)
+                __get_unchecked(v, pos) as *mut Self
             }
             #by_value_methods
             #[doc(hidden)]
diff --git a/src/cxx_vector.rs b/src/cxx_vector.rs
index ef9d9107..23cbcc1d 100644
--- a/src/cxx_vector.rs
+++ b/src/cxx_vector.rs
@@ -23,7 +23,13 @@ use core::slice;
 /// pointer, as in `&CxxVector<T>` or `UniquePtr<CxxVector<T>>`.
 #[repr(C, packed)]
 pub struct CxxVector<T> {
-    _private: [T; 0],
+    // A thing, because repr(C) structs are not allowed to consist exclusively
+    // of PhantomData fields.
+    _void: [c_void; 0],
+    // The conceptual vector elements to ensure that autotraits are propagated
+    // correctly, e.g. CxxVector is UnwindSafe iff T is.
+    _elements: PhantomData<[T]>,
+    // Prevent unpin operation from Pin<&mut CxxVector<T>> to &mut CxxVector<T>.
     _pinned: PhantomData<PhantomPinned>,
 }
 


Test patch:
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index eb889e05..25b4ccdc 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -326,6 +326,7 @@ pub mod ffi {
     }
 
     impl Box<Shared> {}
+    impl CxxVector<SharedString> {}
 }
 
 mod other {
diff --git a/tests/ui/vector_autotraits.stderr b/tests/ui/vector_autotraits.stderr
index 904ed3df..c35d54d2 100644
--- a/tests/ui/vector_autotraits.stderr
+++ b/tests/ui/vector_autotraits.stderr
@@ -15,5 +15,6 @@ note: required because it appears within the type `NotThreadSafe`
    |
 7  |         type NotThreadSafe;
    |              ^^^^^^^^^^^^^
-   = note: required because it appears within the type `[NotThreadSafe; 0]`
+   = note: required because it appears within the type `[NotThreadSafe]`
+   = note: required because it appears within the type `PhantomData<[NotThreadSafe]>`
    = note: required because it appears within the type `CxxVector<NotThreadSafe>`