Repository: dtolnay/cxx
Pull request #1021 (2022-03-08 20:04:25)

Recognize C array of relocatable element type in struct as relocatable


Patch:
diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs
index 7ac9209c..7ee91c56 100644
--- a/gen/src/builtin.rs
+++ b/gen/src/builtin.rs
@@ -28,6 +28,7 @@ pub struct Builtins<'a> {
     pub rust_slice_repr: bool,
     pub exception: bool,
     pub relocatable: bool,
+    pub relocatable_or_array: bool,
     pub friend_impl: bool,
     pub is_complete: bool,
     pub destroy: bool,
@@ -113,6 +114,11 @@ pub(super) fn write(out: &mut OutFile) {
         include.sys_types = true;
     }
 
+    if builtin.relocatable_or_array {
+        include.cstddef = true;
+        builtin.relocatable = true;
+    }
+
     if builtin.relocatable {
         include.type_traits = true;
     }
@@ -357,6 +363,17 @@ pub(super) fn write(out: &mut OutFile) {
         writeln!(out, "}};");
     }
 
+    if builtin.relocatable_or_array {
+        out.next_section();
+        writeln!(out, "template <typename T>");
+        writeln!(out, "struct IsRelocatableOrArray : IsRelocatable<T> {{}};");
+        writeln!(out, "template <typename T, ::std::size_t N>");
+        writeln!(
+            out,
+            "struct IsRelocatableOrArray<T[N]> : IsRelocatableOrArray<T> {{}};",
+        );
+    }
+
     out.end_block(Block::AnonymousNamespace);
     out.end_block(Block::InlineNamespace("cxxbridge1"));
 
diff --git a/gen/src/write.rs b/gen/src/write.rs
index c2d79c0e..982fb3ca 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -470,7 +470,25 @@ fn check_trivial_extern_type(out: &mut OutFile, alias: &TypeAlias, reasons: &[Tr
     let id = alias.name.to_fully_qualified();
     out.builtin.relocatable = true;
     writeln!(out, "static_assert(");
-    writeln!(out, "    ::rust::IsRelocatable<{}>::value,", id);
+    if reasons
+        .iter()
+        .all(|r| matches!(r, TrivialReason::StructField(_)))
+    {
+        // If the type is only used as a struct field and not as by-value
+        // function argument or any other use, then C array of trivially
+        // relocatable type is also permissible.
+        //
+        //     --- means something sane:
+        //     struct T { char buf[N]; };
+        //
+        //     --- means something totally different:
+        //     void f(char buf[N]);
+        //
+        out.builtin.relocatable_or_array = true;
+        writeln!(out, "    ::rust::IsRelocatableOrArray<{}>::value,", id);
+    } else {
+        writeln!(out, "    ::rust::IsRelocatable<{}>::value,", id);
+    }
     writeln!(
         out,
         "    \"type {} should be trivially move constructible and trivially destructible in C++ to be used as {} in Rust\");",


Test patch:
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index d58c8eba..ffc4b50e 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -15,7 +15,7 @@
 pub mod cast;
 pub mod module;
 
-use cxx::{CxxString, CxxVector, SharedPtr, UniquePtr};
+use cxx::{type_id, CxxString, CxxVector, ExternType, SharedPtr, UniquePtr};
 use std::fmt::{self, Display};
 use std::mem::MaybeUninit;
 use std::os::raw::c_char;
@@ -80,6 +80,7 @@ pub mod ffi {
 
     pub struct Array {
         a: [i32; 4],
+        b: Buffer,
     }
 
     #[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
@@ -248,6 +249,10 @@ pub mod ffi {
         CVal2,
     }
 
+    extern "C++" {
+        type Buffer = crate::Buffer;
+    }
+
     extern "Rust" {
         type R;
 
@@ -412,6 +417,15 @@ impl ffi::Array {
     }
 }
 
+#[derive(Default)]
+#[repr(C)]
+pub struct Buffer([c_char; 12]);
+
+unsafe impl ExternType for Buffer {
+    type Id = type_id!("tests::Buffer");
+    type Kind = cxx::kind::Trivial;
+}
+
 #[derive(Debug)]
 struct Error;
 
diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h
index e508a3f5..10cb37ae 100644
--- a/tests/ffi/tests.h
+++ b/tests/ffi/tests.h
@@ -84,6 +84,8 @@ struct Borrow {
   const std::string &s;
 };
 
+typedef char Buffer[12];
+
 size_t c_return_primitive();
 Shared c_return_shared();
 ::A::AShared c_return_ns_shared();
diff --git a/tests/test.rs b/tests/test.rs
index 13ce3456..75a5d48a 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -261,7 +261,10 @@ fn test_c_method_calls() {
     assert_eq!(2023, *ffi::Shared { z: 2023 }.c_method_mut_on_shared());
 
     let val = 42;
-    let mut array = ffi::Array { a: [0, 0, 0, 0] };
+    let mut array = ffi::Array {
+        a: [0, 0, 0, 0],
+        b: ffi::Buffer::default(),
+    };
     array.c_set_array(val);
     assert_eq!(array.a.len() as i32 * val, array.r_get_array_sum());
 }