diff --git a/book/src/extern-c++.md b/book/src/extern-c++.md index 4fda537a3..9a4bd0716 100644 --- a/book/src/extern-c++.md +++ b/book/src/extern-c++.md @@ -189,23 +189,40 @@ manually implemented as described below. #### Safely unifying occurrences of an extern type across bridges -In the following snippet, two #\[cxx::bridge\] invocations in different files +In the following snippet, two `#[cxx::bridge]` invocations in different files (possibly different crates) both contain function signatures involving the same -C++ type `example::Demo`. If both were written just containing `type Demo;`, +C++ type `example::CppType`. If both were written just containing +`type CppType;`, then both macro expansions would produce their own separate Rust type called -`Demo` and thus the compiler wouldn't allow us to take the `Demo` returned by -`file1::ffi::create_demo` and pass it as the `Demo` argument accepted by -`file2::ffi::take_ref_demo`. Instead, one of the two `Demo`s has been defined as +`CppType` and thus the compiler wouldn't allow us to take the `CppType` returned +by +`file1::ffi::create_cpp_type` and pass it as the `CppType` argument accepted by +`file2::ffi::take_cpp_type_by_ref`. Instead, one of the two `CppType`s has been +defined as an extern type alias of the other, making them the same type in Rust. +Problems would also happen if `type RustType` appeared in both `#[cxx::bridge]` +invocations. The solution is similar - replacing one of the occurences with +an extern type alias of the other type. Note that this type alias has to be +present in `extern "C++"` section even though we are aliasing a Rust type. +Also note that the type alias depends on `impl ExternType for RustType` which +the example below generates via `#[derive(ExternType)]`. + ```rust,noplayground // file1.rs #[cxx::bridge(namespace = "example")] pub mod ffi { unsafe extern "C++" { - type Demo; + type CppType; + + fn create_cpp_type() -> UniquePtr; + } - fn create_demo() -> UniquePtr; + extern "Rust { + #[derive(ExternType)] + type RustType; + + fn create_rust_type() -> Box; } } ``` @@ -215,9 +232,14 @@ pub mod ffi { #[cxx::bridge(namespace = "example")] pub mod ffi { unsafe extern "C++" { - type Demo = crate::file1::ffi::Demo; + type CppType = crate::file1::ffi::CppType; + type RustType = crate::file1::ffi::RustType; + + fn take_cpp_type_by_ref(x: &CppType); + } - fn take_ref_demo(demo: &Demo); + extern "Rust { + fn take_rust_type_by_ref(x: &RustType); } } ``` diff --git a/gen/src/write.rs b/gen/src/write.rs index a8bffcec3..4e2fb04e2 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -427,6 +427,7 @@ fn write_opaque_type<'a>(out: &mut OutFile<'a>, ety: &'a ExternType, methods: &[ } writeln!(out, " ~{}() = delete;", ety.name.cxx); + writeln!(out, " using IsRelocatable = std::true_type;"); writeln!(out); out.builtin.layout = true; diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 33a54b673..9b7e330cc 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -991,7 +991,7 @@ fn expand_rust_type_impl(ety: &ExternType) -> TokenStream { #[allow(unused_attributes)] // incorrect lint #[doc(hidden)] type Id = #type_id; - type Kind = ::cxx::kind::Opaque; + type Kind = ::cxx::kind::Trivial; } }); } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 4f63231c7..dc367f0b9 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -369,6 +369,15 @@ pub mod ffi { impl SharedPtr {} impl SharedPtr {} impl UniquePtr {} + + extern "C++" { + type CrossModuleRustType = crate::module::CrossModuleRustType; + } + + extern "Rust" { + fn r_get_value_from_cross_module_rust_type(value: &CrossModuleRustType) -> i32; + fn r_mut_ref_cross_module_rust_type(x: &mut CrossModuleRustType, new_value: i32); + } } mod other { @@ -701,3 +710,11 @@ fn r_try_return_mutsliceu8(slice: &mut [u8]) -> Result<&mut [u8], Error> { fn r_aliased_function(x: i32) -> String { x.to_string() } + +fn r_get_value_from_cross_module_rust_type(value: &crate::module::CrossModuleRustType) -> i32 { + value.0 +} + +fn r_mut_ref_cross_module_rust_type(x: &mut crate::module::CrossModuleRustType, new_value: i32) { + x.0 = new_value; +} diff --git a/tests/ffi/module.rs b/tests/ffi/module.rs index e298c0250..0f5bbb89f 100644 --- a/tests/ffi/module.rs +++ b/tests/ffi/module.rs @@ -78,3 +78,21 @@ pub mod ffi2 { impl UniquePtr {} impl UniquePtr {} } + +#[cxx::bridge(namespace = "tests")] +pub mod ffi3 { + extern "Rust" { + #[derive(ExternType)] + type CrossModuleRustType; + + #[allow(clippy::unnecessary_box_returns)] + fn r_boxed_cross_module_rust_type(value: i32) -> Box; + } +} + +pub struct CrossModuleRustType(pub i32); + +#[allow(clippy::unnecessary_box_returns)] +fn r_boxed_cross_module_rust_type(value: i32) -> Box { + Box::new(CrossModuleRustType(value)) +} diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 48102b67f..eb2e7c7d1 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -992,6 +992,12 @@ extern "C" const char *cxx_run_test() noexcept { (void)rust::Vec(); (void)rust::Vec(); + { + auto r = r_boxed_cross_module_rust_type(123); + r_mut_ref_cross_module_rust_type(*r, 456); + ASSERT(456 == r_get_value_from_cross_module_rust_type(*r)); + } + cxx_test_suite_set_correct(); return nullptr; }