diff --git a/rust/codegen_traits.rs b/rust/codegen_traits.rs index 3cd94df6dd..d9a39411f8 100644 --- a/rust/codegen_traits.rs +++ b/rust/codegen_traits.rs @@ -8,8 +8,10 @@ //! Traits that are implemeted by codegen types. use crate::__internal::SealedInternal; +use crate::__runtime::RawMessage; use crate::{MutProxied, MutProxy, ViewProxy}; use create::Parse; +use interop::{MessageMutInterop, MessageViewInterop, OwnedMessageInterop}; use read::Serialize; use std::fmt::Debug; use write::{Clear, ClearAndParse}; @@ -27,7 +29,10 @@ pub trait Message: SealedInternal + Send + Sync // Copy/Clone: + Clone - {} + // C++ Interop: + + OwnedMessageInterop +{ +} /// A trait that all generated message views implement. pub trait MessageView<'msg>: SealedInternal @@ -38,6 +43,8 @@ pub trait MessageView<'msg>: SealedInternal + Send + Sync // Copy/Clone: + Copy + Clone + // C++ Interop: + + MessageViewInterop<'msg> { #[doc(hidden)] type Message: Message; @@ -55,6 +62,8 @@ pub trait MessageMut<'msg>: SealedInternal + Sync // Copy/Clone: // (Neither) + // C++ Interop: + + MessageMutInterop<'msg> { #[doc(hidden)] type Message: Message; @@ -94,3 +103,77 @@ pub(crate) mod write { fn clear_and_parse(&mut self, data: &[u8]) -> Result<(), crate::ParseError>; } } + +/// Traits related to interop with C or C++. +/// +/// These traits are deliberately not available on the prelude, as they should +/// be used rarely and with great care. +pub(crate) mod interop { + use super::{RawMessage, SealedInternal}; + + /// Traits related to owned message interop. Note that these trait fns + /// are only available on C++ kernel as upb interop of owned messages + /// requires more work to handle the Arena behavior. + pub trait OwnedMessageInterop: SealedInternal { + /// Drops `self` and returns the `RawMessage` that it was wrapping + /// without deleting it. + /// + /// The caller is responsible for ensuring the returned RawMessage is + /// subsequently deleted (eg by moving it into a std::unique_ptr in + /// C++), or else it will leak. + #[cfg(cpp_kernel)] + fn __unstable_leak_raw_message(self) -> RawMessage; + + /// Takes exclusive ownership of the `raw_message`. + /// + /// # Safety + /// - The underlying message must be for the same type as `Self` + /// - The pointer passed in must not be used by the caller after being + /// passed here (must not be read, written, or deleted) + #[cfg(cpp_kernel)] + unsafe fn __unstable_take_ownership_of_raw_message(raw_message: RawMessage) -> Self; + } + + /// Traits related to message view interop. + pub trait MessageViewInterop<'msg>: SealedInternal { + /// Borrows `self` as an underlying `RawMessage`. + /// + /// Note that the returned Value must be used under the same constraints + /// as though it were a borrow of `self`: it should be treated as a + /// `const Message*` in C++, and not be mutated in any way, and any + /// mutation to the parent message may invalidate it, and it + /// must not be deleted. + fn __unstable_as_raw_message(&self) -> RawMessage; + + /// Wraps the provided `RawMessage` as a MessageView. + /// + /// # Safety + /// - The underlying message must be for the same type as `Self` + /// - The underlying message must be alive for 'msg and not mutated + /// while the wrapper is live. + unsafe fn __unstable_wrap_raw_message(raw: &'msg RawMessage) -> Self; + } + + /// Traits related to message mut interop. Note that these trait fns + /// are only available on C++ kernel as upb interop of owned messages + /// requires more work to handle the Arena behavior. + pub trait MessageMutInterop<'msg>: SealedInternal { + /// Exclusive borrows `self` as a `RawMessage`. + /// + /// Note that the returned Value must be used under the same constraints + /// as though it were a mut borrow of `self`: it should be treated as a + /// non-owned `Message*` in C++. And any mutation to the parent message + /// may invalidate it, and it must not be deleted. + #[cfg(cpp_kernel)] + fn __unstable_as_raw_message_mut(&mut self) -> RawMessage; + + /// Wraps the provided `RawMessage` as a MessageMut. + /// + /// # Safety + /// - The underlying message must be for the same type as `Self` + /// - The underlying message must be alive for 'msg and not read or + /// mutated while the wrapper is live. + #[cfg(cpp_kernel)] + unsafe fn __unstable_wrap_raw_message_mut(raw: &'msg mut RawMessage) -> Self; + } +} diff --git a/rust/shared.rs b/rust/shared.rs index 80dd3827dd..59e7bd8205 100644 --- a/rust/shared.rs +++ b/rust/shared.rs @@ -24,6 +24,7 @@ use std::fmt; pub mod __public { pub use crate::codegen_traits::{ create::Parse, + interop::{MessageMutInterop, MessageViewInterop, OwnedMessageInterop}, read::Serialize, write::{Clear, ClearAndParse}, Message, MessageMut, MessageView, diff --git a/rust/test/cpp/interop/main.rs b/rust/test/cpp/interop/main.rs index 7c9a4dda55..467f4559d0 100644 --- a/rust/test/cpp/interop/main.rs +++ b/rust/test/cpp/interop/main.rs @@ -9,6 +9,7 @@ use googletest::prelude::*; use protobuf_cpp::prelude::*; use protobuf_cpp::__runtime::{PtrAndLen, RawMessage}; +use protobuf_cpp::{MessageMutInterop, MessageViewInterop, OwnedMessageInterop}; use unittest_rust_proto::{TestAllExtensions, TestAllTypes, TestAllTypesMut, TestAllTypesView}; macro_rules! proto_assert_eq { @@ -39,9 +40,7 @@ extern "C" { fn send_to_cpp() { let mut msg1 = TestAllTypes::new(); msg1.set_optional_int32(7); - let i = unsafe { - TakeOwnershipAndGetOptionalInt32(msg1.__unstable_leak_cpp_repr_grant_permission_to_break()) - }; + let i = unsafe { TakeOwnershipAndGetOptionalInt32(msg1.__unstable_leak_raw_message()) }; assert_eq!(i, 7); } @@ -49,7 +48,7 @@ fn send_to_cpp() { fn mutate_message_mut_in_cpp() { let mut msg1 = TestAllTypes::new(); unsafe { - MutateTestAllTypes(msg1.as_mut().__unstable_cpp_repr_grant_permission_to_break()); + MutateTestAllTypes(msg1.as_mut().__unstable_as_raw_message_mut()); } let mut msg2 = TestAllTypes::new(); @@ -65,9 +64,7 @@ fn deserialize_in_rust() { let mut msg1 = TestAllTypes::new(); msg1.set_optional_int64(-1); msg1.set_optional_bytes(b"some cool data I guess"); - let serialized = unsafe { - SerializeTestAllTypes(msg1.as_view().__unstable_cpp_repr_grant_permission_to_break()) - }; + let serialized = unsafe { SerializeTestAllTypes(msg1.as_view().__unstable_as_raw_message()) }; let msg2 = TestAllTypes::parse(&serialized).unwrap(); proto_assert_eq!(msg1, msg2); @@ -81,7 +78,7 @@ fn deserialize_in_cpp() { let data = msg1.serialize().unwrap(); let msg2 = unsafe { - TestAllTypes::__unstable_wrap_cpp_grant_permission_to_break(DeserializeTestAllTypes( + TestAllTypes::__unstable_take_ownership_of_raw_message(DeserializeTestAllTypes( (*data).as_ptr(), data.len(), )) @@ -98,7 +95,7 @@ fn deserialize_in_cpp_into_mut() { let data = msg1.serialize().unwrap(); let mut raw_msg = unsafe { DeserializeTestAllTypes((*data).as_ptr(), data.len()) }; - let msg2 = TestAllTypesMut::__unstable_wrap_cpp_grant_permission_to_break(&mut raw_msg); + let msg2 = unsafe { TestAllTypesMut::__unstable_wrap_raw_message_mut(&mut raw_msg) }; proto_assert_eq!(msg1, msg2); @@ -116,7 +113,7 @@ fn deserialize_in_cpp_into_view() { let data = msg1.serialize().unwrap(); let raw_msg = unsafe { DeserializeTestAllTypes((*data).as_ptr(), data.len()) }; - let msg2 = TestAllTypesView::__unstable_wrap_cpp_grant_permission_to_break(&raw_msg); + let msg2 = unsafe { TestAllTypesView::__unstable_wrap_raw_message(&raw_msg) }; proto_assert_eq!(msg1, msg2); @@ -130,14 +127,12 @@ fn deserialize_in_cpp_into_view() { // accidentally get destroyed by Rust. #[googletest::test] fn smuggle_extension() { - let msg1 = unsafe { - TestAllExtensions::__unstable_wrap_cpp_grant_permission_to_break(NewWithExtension()) - }; + let msg1 = + unsafe { TestAllExtensions::__unstable_take_ownership_of_raw_message(NewWithExtension()) }; let data = msg1.serialize().unwrap(); let mut msg2 = TestAllExtensions::parse(&data).unwrap(); - let bytes = unsafe { - GetBytesExtension(msg2.as_mut().__unstable_cpp_repr_grant_permission_to_break()).as_ref() - }; + let bytes = + unsafe { GetBytesExtension(msg2.as_mut().__unstable_as_raw_message_mut()).as_ref() }; assert_eq!(bytes, b"smuggled"); } diff --git a/src/google/protobuf/compiler/rust/message.cc b/src/google/protobuf/compiler/rust/message.cc index 8f3cafaee1..d18a58deda 100644 --- a/src/google/protobuf/compiler/rust/message.cc +++ b/src/google/protobuf/compiler/rust/message.cc @@ -1287,8 +1287,8 @@ void GenerateRs(Context& ctx, const Descriptor& msg) { $nested_in_msg$ )rs"); + ctx.printer().PrintRaw("\n"); if (ctx.is_cpp()) { - ctx.printer().PrintRaw("\n"); ctx.Emit({{"Msg", RsSafeName(msg.name())}}, R"rs( impl $Msg$ { pub fn __unstable_wrap_cpp_grant_permission_to_break(msg: $pbr$::RawMessage) -> Self { @@ -1299,7 +1299,6 @@ void GenerateRs(Context& ctx, const Descriptor& msg) { s.raw_msg() } } - impl<'a> $Msg$Mut<'a> { //~ msg is a &mut so that the borrow checker enforces exclusivity to //~ prevent constructing multiple Muts/Views from the same RawMessage. @@ -1325,9 +1324,58 @@ void GenerateRs(Context& ctx, const Descriptor& msg) { self.msg } } + + impl $pb$::OwnedMessageInterop for $Msg$ { + unsafe fn __unstable_take_ownership_of_raw_message(msg: $pbr$::RawMessage) -> Self { + Self { inner: $pbr$::MessageInner { msg } } + } + + fn __unstable_leak_raw_message(self) -> $pbr$::RawMessage { + let s = std::mem::ManuallyDrop::new(self); + s.raw_msg() + } + } + + impl<'a> $pb$::MessageMutInterop<'a> for $Msg$Mut<'a> { + unsafe fn __unstable_wrap_raw_message_mut( + msg: &'a mut $pbr$::RawMessage) -> Self { + Self { + inner: $pbr$::MutatorMessageRef::from_raw_msg($pbi$::Private, msg) + } + } + fn __unstable_as_raw_message_mut(&mut self) -> $pbr$::RawMessage { + self.raw_msg() + } + } + + impl<'a> $pb$::MessageViewInterop<'a> for $Msg$View<'a> { + unsafe fn __unstable_wrap_raw_message( + msg: &'a $pbr$::RawMessage) -> Self { + Self::new($pbi$::Private, *msg) + } + fn __unstable_as_raw_message(&self) -> $pbr$::RawMessage { + self.msg + } + } + )rs"); + } else { + ctx.Emit({{"Msg", RsSafeName(msg.name())}}, R"rs( + // upb kernel doesn't support any owned message or message mut interop. + impl $pb$::OwnedMessageInterop for $Msg$ {} + impl<'a> $pb$::MessageMutInterop<'a> for $Msg$Mut<'a> {} + + impl<'a> $pb$::MessageViewInterop<'a> for $Msg$View<'a> { + unsafe fn __unstable_wrap_raw_message( + msg: &'a $pbr$::RawMessage) -> Self { + Self::new($pbi$::Private, *msg) + } + fn __unstable_as_raw_message(&self) -> $pbr$::RawMessage { + self.msg + } + } )rs"); } -} +} // NOLINT(readability/fn_size) // Generates code for a particular message in `.pb.thunk.cc`. void GenerateThunksCc(Context& ctx, const Descriptor& msg) {