Replace `NonNull<u8>` for raw messages with a dedicated opaque pointer
These are more type safe, and more clearly distinguish between a
raw message and serialized data.
This also defines a macro to create new opaque pointer types, and
switches `RawArena` to using it.
PiperOrigin-RevId: 552957136
diff --git a/rust/cpp.rs b/rust/cpp.rs
index 9b683bb..46cead7 100644
--- a/rust/cpp.rs
+++ b/rust/cpp.rs
@@ -30,6 +30,7 @@
// Rust Protobuf runtime using the C++ kernel.
+use crate::__internal::RawArena;
use std::alloc::Layout;
use std::cell::UnsafeCell;
use std::fmt;
@@ -50,7 +51,7 @@
#[derive(Debug)]
pub struct Arena {
#[allow(dead_code)]
- ptr: NonNull<u8>,
+ ptr: RawArena,
_not_sync: PhantomData<UnsafeCell<()>>,
}
diff --git a/rust/internal.rs b/rust/internal.rs
index ada1025..7423a3c 100644
--- a/rust/internal.rs
+++ b/rust/internal.rs
@@ -37,6 +37,54 @@
/// Used to protect internal-only items from being used accidentally.
pub struct Private;
+/// Defines a set of opaque pointers and a unique non-accessible pointees.
+///
+/// This provides a type safety benefit over using `NonNull<u8>` everywhere.
+/// The [Rustonomicon][nomicon] currently recommends a zero-sized struct,
+/// though this should use [`extern type`] when that is stabilized.
+///
+/// Because this defines a new private module, it can only be called once per
+/// module.
+///
+/// [nomicon]: https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs
+/// [`extern type`]: https://github.com/rust-lang/rust/issues/43467
+macro_rules! define_opaque_nonnulls {
+ ($($(#[$meta:meta])* $vis:vis type $name:ident = NonNull<$raw_name:ident>;)*) => {
+ mod _opaque_pointees {
+ $(
+ #[doc = concat!("Opaque pointee for [`", stringify!($name), "`][pointer]")]
+ ///
+ /// This type is not meant to be dereferenced in Rust code.
+ /// It is only meant to provide type safety for raw pointers
+ /// which are manipulated behind FFI.
+ #[doc = concat!("[pointer]: super::", stringify!($name))]
+ #[repr(C)]
+ pub struct $raw_name {
+ _data: [u8; 0],
+ _marker: ::std::marker::PhantomData<(*mut u8, ::std::marker::PhantomPinned)>,
+ }
+ )*
+ }
+ $(
+ $(#[$meta])*
+ ///
+ /// This is an opaque pointer used for FFI:
+ /// do not dereference this type in Rust code.
+ $vis type $name = ::std::ptr::NonNull<_opaque_pointees::$raw_name>;
+ )*
+ };
+}
+
+pub(crate) use define_opaque_nonnulls;
+
+define_opaque_nonnulls!(
+ /// A raw pointer to the underlying arena for this runtime.
+ pub type RawArena = NonNull<RawArenaData>;
+
+ /// A raw pointer to the underlying message for this runtime.
+ pub type RawMessage = NonNull<RawMessageData>;
+);
+
/// Represents an ABI-stable version of `NonNull<[u8]>`/`string_view` (a
/// borrowed slice of bytes) for FFI use only.
///
diff --git a/rust/test/cpp/interop/main.rs b/rust/test/cpp/interop/main.rs
index 8821fb7..3158430 100644
--- a/rust/test/cpp/interop/main.rs
+++ b/rust/test/cpp/interop/main.rs
@@ -28,7 +28,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-use std::ptr::NonNull;
+use protobuf_cpp::__internal::PtrAndLen;
+use protobuf_cpp::__internal::RawMessage;
use unittest_proto::proto2_unittest::TestAllExtensions;
use unittest_proto::proto2_unittest::TestAllTypes;
@@ -46,12 +47,12 @@
// Helper functions invoking C++ Protobuf APIs directly in C++.
// Defined in `test_utils.cc`.
extern "C" {
- fn DeserializeTestAllTypes(data: *const u8, len: usize) -> NonNull<u8>;
- fn MutateTestAllTypes(msg: NonNull<u8>);
- fn SerializeTestAllTypes(msg: NonNull<u8>) -> protobuf_cpp::__runtime::SerializedData;
+ fn DeserializeTestAllTypes(data: *const u8, len: usize) -> RawMessage;
+ fn MutateTestAllTypes(msg: RawMessage);
+ fn SerializeTestAllTypes(msg: RawMessage) -> protobuf_cpp::__runtime::SerializedData;
- fn NewWithExtension() -> NonNull<u8>;
- fn GetBytesExtension(msg: NonNull<u8>) -> protobuf_cpp::__internal::PtrAndLen;
+ fn NewWithExtension() -> RawMessage;
+ fn GetBytesExtension(msg: RawMessage) -> PtrAndLen;
}
#[test]
diff --git a/rust/upb.rs b/rust/upb.rs
index 204d3db..a887052 100644
--- a/rust/upb.rs
+++ b/rust/upb.rs
@@ -30,6 +30,7 @@
//! UPB FFI wrapper code for use by Rust Protobuf.
+use crate::__internal::RawArena;
use std::alloc;
use std::alloc::Layout;
use std::cell::UnsafeCell;
@@ -43,15 +44,6 @@
/// See `upb/port/def.inc`.
const UPB_MALLOC_ALIGN: usize = 8;
-/// A UPB-managed pointer to a raw arena.
-pub type RawArena = NonNull<RawArenaData>;
-
-/// The data behind a [`RawArena`]. Do not use this type.
-#[repr(C)]
-pub struct RawArenaData {
- _data: [u8; 0],
-}
-
/// A wrapper over a `upb_Arena`.
///
/// This is not a safe wrapper per se, because the allocation functions still
diff --git a/src/google/protobuf/compiler/rust/accessors/singular_bytes.cc b/src/google/protobuf/compiler/rust/accessors/singular_bytes.cc
index b9e0a89..831fec1 100644
--- a/src/google/protobuf/compiler/rust/accessors/singular_bytes.cc
+++ b/src/google/protobuf/compiler/rust/accessors/singular_bytes.cc
@@ -87,10 +87,10 @@
{"clearer_thunk", Thunk(field, "clear")},
},
R"rs(
- fn $hazzer_thunk$(raw_msg: $NonNull$<u8>) -> bool;
- fn $getter_thunk$(raw_msg: $NonNull$<u8>) -> $pbi$::PtrAndLen;
- fn $setter_thunk$(raw_msg: $NonNull$<u8>, val: *const u8, len: usize);
- fn $clearer_thunk$(raw_msg: $NonNull$<u8>);
+ fn $hazzer_thunk$(raw_msg: $pbi$::RawMessage) -> bool;
+ fn $getter_thunk$(raw_msg: $pbi$::RawMessage) -> $pbi$::PtrAndLen;
+ fn $setter_thunk$(raw_msg: $pbi$::RawMessage, val: *const u8, len: usize);
+ fn $clearer_thunk$(raw_msg: $pbi$::RawMessage);
)rs");
}
diff --git a/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc b/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc
index 8053f21..2db381f 100644
--- a/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc
+++ b/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc
@@ -99,10 +99,10 @@
{"clearer_thunk", Thunk(field, "clear")},
},
R"rs(
- fn $hazzer_thunk$(raw_msg: $NonNull$<u8>) -> bool;
- fn $getter_thunk$(raw_msg: $NonNull$<u8>) -> $Scalar$;
- fn $setter_thunk$(raw_msg: $NonNull$<u8>, val: $Scalar$);
- fn $clearer_thunk$(raw_msg: $NonNull$<u8>);
+ fn $hazzer_thunk$(raw_msg: $pbi$::RawMessage) -> bool;
+ fn $getter_thunk$(raw_msg: $pbi$::RawMessage) -> $Scalar$;
+ fn $setter_thunk$(raw_msg: $pbi$::RawMessage, val: $Scalar$);
+ fn $clearer_thunk$(raw_msg: $pbi$::RawMessage);
)rs");
}
diff --git a/src/google/protobuf/compiler/rust/message.cc b/src/google/protobuf/compiler/rust/message.cc
index 6468f4b..24bbd88 100644
--- a/src/google/protobuf/compiler/rust/message.cc
+++ b/src/google/protobuf/compiler/rust/message.cc
@@ -49,13 +49,13 @@
switch (msg.opts().kernel) {
case Kernel::kCpp:
msg.Emit(R"rs(
- msg: $NonNull$<u8>,
+ msg: $pbi$::RawMessage,
)rs");
return;
case Kernel::kUpb:
msg.Emit(R"rs(
- msg: $NonNull$<u8>,
+ msg: $pbi$::RawMessage,
//~ rustc incorrectly thinks this field is never read, even though
//~ it has a destructor!
#[allow(dead_code)]
@@ -136,9 +136,7 @@
msg.Emit({{"deserialize_thunk", Thunk(msg, "parse")}}, R"rs(
let arena = $pbr$::Arena::new();
let msg = unsafe {
- $NonNull$::<u8>::new(
- $deserialize_thunk$(data.as_ptr(), data.len(), arena.raw())
- )
+ $deserialize_thunk$(data.as_ptr(), data.len(), arena.raw())
};
match msg {
@@ -169,10 +167,10 @@
{"deserialize_thunk", Thunk(msg, "deserialize")},
},
R"rs(
- fn $new_thunk$() -> $NonNull$<u8>;
- fn $delete_thunk$(raw_msg: $NonNull$<u8>);
- fn $serialize_thunk$(raw_msg: $NonNull$<u8>) -> $pbr$::SerializedData;
- fn $deserialize_thunk$(raw_msg: $NonNull$<u8>, data: $pbr$::SerializedData) -> bool;
+ fn $new_thunk$() -> $pbi$::RawMessage;
+ fn $delete_thunk$(raw_msg: $pbi$::RawMessage);
+ fn $serialize_thunk$(raw_msg: $pbi$::RawMessage) -> $pbr$::SerializedData;
+ fn $deserialize_thunk$(raw_msg: $pbi$::RawMessage, data: $pbr$::SerializedData) -> bool;
)rs");
return;
@@ -184,9 +182,9 @@
{"deserialize_thunk", Thunk(msg, "parse")},
},
R"rs(
- fn $new_thunk$(arena: $pbr$::RawArena) -> $NonNull$<u8>;
- fn $serialize_thunk$(msg: $NonNull$<u8>, arena: $pbr$::RawArena, len: &mut usize) -> $NonNull$<u8>;
- fn $deserialize_thunk$(data: *const u8, size: usize, arena: $pbr$::RawArena) -> *mut u8;
+ fn $new_thunk$(arena: $pbi$::RawArena) -> $pbi$::RawMessage;
+ fn $serialize_thunk$(msg: $pbi$::RawMessage, arena: $pbi$::RawArena, len: &mut usize) -> $NonNull$<u8>;
+ fn $deserialize_thunk$(data: *const u8, size: usize, arena: $pbi$::RawArena) -> Option<$pbi$::RawMessage>;
)rs");
return;
}
@@ -304,7 +302,7 @@
#[derive(Debug, Copy, Clone)]
pub struct $Msg$View<'a> {
- msg: $NonNull$<u8>,
+ _msg: $pbi$::RawMessage,
_phantom: std::marker::PhantomData<&'a ()>,
}
@@ -379,10 +377,10 @@
msg.printer().PrintRaw("\n");
msg.Emit({{"Msg", msg.desc().name()}}, R"rs(
impl $Msg$ {
- pub fn __unstable_wrap_cpp_grant_permission_to_break(msg: $NonNull$<u8>) -> Self {
+ pub fn __unstable_wrap_cpp_grant_permission_to_break(msg: $pbi$::RawMessage) -> Self {
Self { msg }
}
- pub fn __unstable_cpp_repr_grant_permission_to_break(&mut self) -> $NonNull$<u8> {
+ pub fn __unstable_cpp_repr_grant_permission_to_break(&mut self) -> $pbi$::RawMessage {
self.msg
}
}