Merge pull request #406 from dtolnay/relocatable
Expose way to bypass trivial destr/move on extern types passed by value
diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs
index ab2b465..7a4ddcd 100644
--- a/gen/src/builtin.rs
+++ b/gen/src/builtin.rs
@@ -23,6 +23,7 @@
pub rust_slice_new: bool,
pub rust_slice_repr: bool,
pub exception: bool,
+ pub relocatable: bool,
pub content: Content<'a>,
}
@@ -73,6 +74,10 @@
include.basetsd = true;
}
+ if builtin.relocatable {
+ include.type_traits = true;
+ }
+
out.begin_block(Block::Namespace("rust"));
out.begin_block(Block::InlineNamespace("cxxbridge05"));
writeln!(out, "// #include \"rust/cxx.h\"");
@@ -100,6 +105,7 @@
ifndef::write(out, builtin.rust_fn, "CXXBRIDGE05_RUST_FN");
ifndef::write(out, builtin.rust_error, "CXXBRIDGE05_RUST_ERROR");
ifndef::write(out, builtin.rust_isize, "CXXBRIDGE05_RUST_ISIZE");
+ ifndef::write(out, builtin.relocatable, "CXXBRIDGE05_RELOCATABLE");
if builtin.manually_drop {
out.next_section();
diff --git a/gen/src/write.rs b/gen/src/write.rs
index 2c3234c..c806e55 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -262,7 +262,7 @@
}
fn check_trivial_extern_type(out: &mut OutFile, id: &Pair) {
- // NOTE: The following two static assertions are just nice-to-have and not
+ // NOTE: The following static assertion is just nice-to-have and not
// necessary for soundness. That's because triviality is always declared by
// the user in the form of an unsafe impl of cxx::ExternType:
//
@@ -273,31 +273,35 @@
//
// Since the user went on the record with their unsafe impl to unsafely
// claim they KNOW that the type is trivial, it's fine for that to be on
- // them if that were wrong.
+ // them if that were wrong. However, in practice correctly reasoning about
+ // the relocatability of C++ types is challenging, particularly if the type
+ // definition were to change over time, so for now we add this check.
//
- // There may be a legitimate reason we'll want to remove these assertions
- // for support of types that the programmer knows are Rust-movable despite
- // not being recognized as such by the C++ type system due to a move
- // constructor or destructor.
+ // There may be legitimate reasons to opt out of this assertion for support
+ // of types that the programmer knows are soundly Rust-movable despite not
+ // being recognized as such by the C++ type system due to a move constructor
+ // or destructor. To opt out of the relocatability check, they need to do
+ // one of the following things in any header used by `include!` in their
+ // bridge.
+ //
+ // --- if they define the type:
+ // struct MyType {
+ // ...
+ // + using IsRelocatable = std::true_type;
+ // };
+ //
+ // --- otherwise:
+ // + template <>
+ // + struct rust::IsRelocatable<MyType> : std::true_type {};
+ //
- let id = &id.to_fully_qualified();
- out.include.type_traits = true;
+ let id = id.to_fully_qualified();
+ out.builtin.relocatable = true;
writeln!(out, "static_assert(");
+ writeln!(out, " ::rust::IsRelocatable<{}>::value,", id);
writeln!(
out,
- " ::std::is_trivially_move_constructible<{}>::value,",
- id,
- );
- writeln!(
- out,
- " \"type {} marked as Trivial in Rust is not trivially move constructible in C++\");",
- id,
- );
- writeln!(out, "static_assert(");
- writeln!(out, " ::std::is_trivially_destructible<{}>::value,", id);
- writeln!(
- out,
- " \"type {} marked as Trivial in Rust is not trivially destructible in C++\");",
+ " \"type {} marked as Trivial in Rust is not trivially move constructible and trivially destructible in C++\");",
id,
);
}
diff --git a/include/cxx.h b/include/cxx.h
index 45c3828..b048e27 100644
--- a/include/cxx.h
+++ b/include/cxx.h
@@ -267,6 +267,27 @@
std::ostream &operator<<(std::ostream &, const String &);
std::ostream &operator<<(std::ostream &, const Str &);
+// IsRelocatable<T> is used in assertions that a C++ type passed by value
+// between Rust and C++ is soundly relocatable by Rust.
+//
+// There may be legitimate reasons to opt out of the check for support of types
+// that the programmer knows are soundly Rust-movable despite not being
+// recognized as such by the C++ type system due to a move constructor or
+// destructor. To opt out of the relocatability check, do either of the
+// following things in any header used by `include!` in the bridge.
+//
+// --- if you define the type:
+// struct MyType {
+// ...
+// + using IsRelocatable = std::true_type;
+// };
+//
+// --- otherwise:
+// + template <>
+// + struct rust::IsRelocatable<MyType> : std::true_type {};
+template <typename T>
+struct IsRelocatable;
+
// Snake case aliases for use in code that uses this style for type names.
using string = String;
using str = Str;
@@ -281,6 +302,8 @@
using fn = Fn<Signature, Throws>;
template <typename Signature>
using try_fn = TryFn<Signature>;
+template <typename T>
+using is_relocatable = IsRelocatable<T>;
@@ -552,5 +575,42 @@
Vec<T>::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {}
#endif // CXXBRIDGE05_RUST_VEC
+#ifndef CXXBRIDGE05_RELOCATABLE
+#define CXXBRIDGE05_RELOCATABLE
+namespace detail {
+template <typename... Ts>
+struct make_void {
+ using type = void;
+};
+
+template <typename... Ts>
+using void_t = typename make_void<Ts...>::type;
+
+template <typename Void, template <typename...> class, typename...>
+struct detect : std::false_type {};
+template <template <typename...> class T, typename... A>
+struct detect<void_t<T<A...>>, T, A...> : std::true_type {};
+
+template <template <typename...> class T, typename... A>
+using is_detected = detect<void, T, A...>;
+
+template <typename T>
+using detect_IsRelocatable = typename T::IsRelocatable;
+
+template <typename T>
+struct get_IsRelocatable
+ : std::is_same<typename T::IsRelocatable, std::true_type> {};
+} // namespace detail
+
+template <typename T>
+struct IsRelocatable
+ : std::conditional<
+ detail::is_detected<detail::detect_IsRelocatable, T>::value,
+ detail::get_IsRelocatable<T>,
+ std::integral_constant<
+ bool, std::is_trivially_move_constructible<T>::value &&
+ std::is_trivially_destructible<T>::value>>::type {};
+#endif // CXXBRIDGE05_RELOCATABLE
+
} // namespace cxxbridge05
} // namespace rust