Add Message::Clear() and MessageMut::Clear() (behind the Clear trait)

Distinct from any clear_submsg(), this clears the message contents and doesn't affect presence on parent (and also allows for clearing owned messages which don't exist as a field to be cleared).

PiperOrigin-RevId: 656453234
diff --git a/rust/codegen_traits.rs b/rust/codegen_traits.rs
index ddfe3cf..ad8dd64 100644
--- a/rust/codegen_traits.rs
+++ b/rust/codegen_traits.rs
@@ -11,7 +11,7 @@
 use create::Parse;
 use read::Serialize;
 use std::fmt::Debug;
-use write::ClearAndParse;
+use write::{Clear, ClearAndParse};
 
 /// A trait that all generated owned message types implement.
 pub trait Message: MutProxied
@@ -20,8 +20,7 @@
   // Read traits:
   + Debug + Serialize
   // Write traits:
-  // TODO: Msg should impl Clear.
-  + ClearAndParse
+  + Clear + ClearAndParse
   // Thread safety:
   + Send + Sync
   // Copy/Clone:
@@ -47,10 +46,12 @@
     // Read traits:
     + Debug + Serialize
     // Write traits:
-    // TODO: MsgMut should impl Clear and ClearAndParse.
+    // TODO: MsgMut should impl ClearAndParse.
+    + Clear
     // Thread safety:
     + Sync
-    // Copy/Clone: (Neither)
+    // Copy/Clone:
+    // (Neither)
 {
     #[doc(hidden)]
     type Message: Message;
diff --git a/rust/test/shared/accessors_proto3_test.rs b/rust/test/shared/accessors_proto3_test.rs
index 63aaf1a..1df120d 100644
--- a/rust/test/shared/accessors_proto3_test.rs
+++ b/rust/test/shared/accessors_proto3_test.rs
@@ -5,9 +5,12 @@
 // license that can be found in the LICENSE file or at
 // https://developers.google.com/open-source/licenses/bsd
 
-/// Tests covering accessors for singular bool, int32, int64, and bytes fields
-/// on proto3.
+//! Tests covering accessors for singular bool, int32, int64, and bytes fields
+//! on proto3.
+
 use googletest::prelude::*;
+use protobuf::prelude::*;
+
 use protobuf::Optional;
 use unittest_proto3_optional_rust_proto::{test_proto3_optional, TestProto3Optional};
 use unittest_proto3_rust_proto::{test_all_types, TestAllTypes};
@@ -251,3 +254,29 @@
     msg.set_optional_string_piece("hello");
     assert_that!(msg.optional_string_piece(), eq("hello"));
 }
+
+#[googletest::test]
+fn test_msg_clear() {
+    let mut m = TestAllTypes::new();
+    m.set_optional_int32(42);
+    assert_that!(m.optional_int32(), eq(42));
+    m.clear();
+    assert_that!(m.optional_int32(), eq(0));
+}
+
+#[googletest::test]
+fn test_submsg_clear() {
+    let mut m = TestAllTypes::new();
+    let mut sub = m.optional_nested_message_mut();
+    sub.set_bb(7);
+
+    assert_that!(m.has_optional_nested_message(), eq(true));
+    assert_that!(m.optional_nested_message().bb(), eq(7));
+
+    m.optional_nested_message_mut().clear();
+
+    // .clear() on the submsg doesn't affect its presence on the parent:
+    assert_that!(m.has_optional_nested_message(), eq(true));
+    // ...but it does clear the submsg's value:
+    assert_that!(m.optional_nested_message().bb(), eq(0));
+}
diff --git a/rust/test/shared/accessors_test.rs b/rust/test/shared/accessors_test.rs
index 2a7795e..af020f8 100644
--- a/rust/test/shared/accessors_test.rs
+++ b/rust/test/shared/accessors_test.rs
@@ -8,6 +8,8 @@
 //! Tests covering accessors for singular bool, int32, int64, and bytes fields.
 
 use googletest::prelude::*;
+use protobuf::prelude::*;
+
 use protobuf::{Optional, ProtoBytes, ProtoStr, ProtoString};
 use std::borrow::Cow;
 use std::ffi::OsString;
@@ -917,3 +919,29 @@
     assert_that!(msg.optional_string_piece(), eq("hello"));
     assert_that!(msg.has_optional_string_piece(), eq(true));
 }
+
+#[googletest::test]
+fn test_msg_clear() {
+    let mut m = TestAllTypes::new();
+    m.set_optional_int32(42);
+    assert_that!(m.has_optional_int32(), eq(true));
+    m.clear();
+    assert_that!(m.has_optional_int32(), eq(false));
+}
+
+#[googletest::test]
+fn test_submsg_clear() {
+    let mut m = TestAllTypes::new();
+    let mut sub = m.optional_nested_message_mut();
+    sub.set_bb(7);
+
+    assert_that!(m.has_optional_nested_message(), eq(true));
+    assert_that!(m.optional_nested_message().bb(), eq(7));
+
+    m.optional_nested_message_mut().clear();
+
+    // .clear() on the submsg doesn't affect its presence on the parent:
+    assert_that!(m.has_optional_nested_message(), eq(true));
+    // ...but it does clear the submsg's value:
+    assert_that!(m.optional_nested_message().bb(), eq(0));
+}
diff --git a/rust/upb/lib.rs b/rust/upb/lib.rs
index 67319af..3511918 100644
--- a/rust/upb/lib.rs
+++ b/rust/upb/lib.rs
@@ -32,8 +32,9 @@
 
 mod message;
 pub use message::{
-    upb_Message, upb_Message_DeepClone, upb_Message_DeepCopy, upb_Message_IsEqual,
-    upb_Message_MergeFrom, upb_Message_New, upb_Message_SetBaseField, RawMessage,
+    upb_Message, upb_Message_Clear, upb_Message_DeepClone, upb_Message_DeepCopy,
+    upb_Message_IsEqual, upb_Message_MergeFrom, upb_Message_New, upb_Message_SetBaseField,
+    RawMessage,
 };
 
 mod message_value;
diff --git a/rust/upb/message.rs b/rust/upb/message.rs
index 67d1e06..b78e495 100644
--- a/rust/upb/message.rs
+++ b/rust/upb/message.rs
@@ -13,10 +13,20 @@
 pub type RawMessage = NonNull<upb_Message>;
 
 extern "C" {
-    /// SAFETY: No constraints.
+    /// SAFETY:
+    /// - `mini_table` and `arena` must be valid to deref
     pub fn upb_Message_New(mini_table: *const upb_MiniTable, arena: RawArena)
     -> Option<RawMessage>;
 
+    /// SAFETY:
+    /// - `m` and `mini_table` must be valid to deref
+    /// - `mini_table` must be the MiniTable associtaed with `m`
+    pub fn upb_Message_Clear(m: RawMessage, mini_table: *const upb_MiniTable);
+
+    /// SAFETY:
+    /// - All four arguments must be valid to deref
+    /// - `mini_table` must be the MiniTable associated with both `dst` and
+    ///   `src`
     pub fn upb_Message_DeepCopy(
         dst: RawMessage,
         src: RawMessage,
@@ -24,18 +34,29 @@
         arena: RawArena,
     );
 
+    /// SAFETY:
+    /// - All three arguments must be valid to deref
+    /// - `mini_table` must be the MiniTable associated with `m`
     pub fn upb_Message_DeepClone(
         m: RawMessage,
         mini_table: *const upb_MiniTable,
         arena: RawArena,
     ) -> Option<RawMessage>;
 
+    /// SAFETY:
+    /// - `m` and `mini_table` must be valid to deref
+    /// - `mini_table` must be the MiniTable associated with `m`
+    /// - `val` must be a pointer to legally readable memory of the correct type
+    ///   for the field described by `mini_table`
     pub fn upb_Message_SetBaseField(
         m: RawMessage,
         mini_table: *const upb_MiniTableField,
         val: *const std::ffi::c_void,
     );
 
+    /// SAFETY:
+    /// - All four arguments must be valid to deref
+    /// - `mini_table` must be the MiniTable associated with both `m1` and `m2`
     pub fn upb_Message_IsEqual(
         m1: RawMessage,
         m2: RawMessage,
@@ -43,6 +64,11 @@
         options: i32,
     ) -> bool;
 
+    /// SAFETY:
+    /// - `dst`, `src`, `mini_table` and `arena` must be valid to deref
+    /// - `extreg` must be valid to deref or nullptr
+    /// - `mini_table` must be the MiniTable associated with both `dst` and
+    ///   `src`
     pub fn upb_Message_MergeFrom(
         dst: RawMessage,
         src: RawMessage,
diff --git a/src/google/protobuf/compiler/rust/message.cc b/src/google/protobuf/compiler/rust/message.cc
index a6dca4b..c3c5013 100644
--- a/src/google/protobuf/compiler/rust/message.cc
+++ b/src/google/protobuf/compiler/rust/message.cc
@@ -92,6 +92,28 @@
   ABSL_LOG(FATAL) << "unreachable";
 }
 
+void MessageMutClear(Context& ctx, const Descriptor& msg) {
+  switch (ctx.opts().kernel) {
+    case Kernel::kCpp:
+      ctx.Emit({{"clear_thunk", ThunkName(ctx, msg, "clear")}},
+               R"rs(
+          unsafe { $clear_thunk$(self.raw_msg()) }
+        )rs");
+      return;
+    case Kernel::kUpb:
+      ctx.Emit(
+          {
+              {"minitable", UpbMinitableName(msg)},
+          },
+          R"rs(
+          unsafe {
+            $pbr$::upb_Message_Clear(self.raw_msg(), $std$::ptr::addr_of!($minitable$))
+          }
+        )rs");
+      return;
+  }
+}
+
 void MessageClearAndParse(Context& ctx, const Descriptor& msg) {
   switch (ctx.opts().kernel) {
     case Kernel::kCpp:
@@ -180,6 +202,7 @@
           {
               {"new_thunk", ThunkName(ctx, msg, "new")},
               {"delete_thunk", ThunkName(ctx, msg, "delete")},
+              {"clear_thunk", ThunkName(ctx, msg, "clear")},
               {"serialize_thunk", ThunkName(ctx, msg, "serialize")},
               {"parse_thunk", ThunkName(ctx, msg, "parse")},
               {"copy_from_thunk", ThunkName(ctx, msg, "copy_from")},
@@ -200,6 +223,7 @@
           R"rs(
           fn $new_thunk$() -> $pbr$::RawMessage;
           fn $delete_thunk$(raw_msg: $pbr$::RawMessage);
+          fn $clear_thunk$(raw_msg: $pbr$::RawMessage);
           fn $serialize_thunk$(raw_msg: $pbr$::RawMessage, out: &mut $pbr$::SerializedData) -> bool;
           fn $parse_thunk$(raw_msg: $pbr$::RawMessage, data: $pbr$::SerializedData) -> bool;
           fn $copy_from_thunk$(dst: $pbr$::RawMessage, src: $pbr$::RawMessage);
@@ -824,6 +848,7 @@
       {{"Msg", RsSafeName(msg.name())},
        {"Msg::new", [&] { MessageNew(ctx, msg); }},
        {"Msg::serialize", [&] { MessageSerialize(ctx, msg); }},
+       {"MsgMut::clear", [&] { MessageMutClear(ctx, msg); }},
        {"Msg::clear_and_parse", [&] { MessageClearAndParse(ctx, msg); }},
        {"Msg::drop", [&] { MessageDrop(ctx, msg); }},
        {"Msg::debug", [&] { MessageDebug(ctx, msg); }},
@@ -977,6 +1002,12 @@
           }
         }
 
+        impl $pb$::Clear for $Msg$ {
+          fn clear(&mut self) {
+            self.as_mut().clear()
+          }
+        }
+
         impl $pb$::ClearAndParse for $Msg$ {
           fn clear_and_parse(&mut self, data: &[u8]) -> Result<(), $pb$::ParseError> {
             self.clear_and_parse(data)
@@ -1096,6 +1127,12 @@
           }
         }
 
+        impl $pb$::Clear for $Msg$Mut<'_> {
+          fn clear(&mut self) {
+            $MsgMut::clear$
+          }
+        }
+
         #[allow(dead_code)]
         impl<'msg> $Msg$Mut<'msg> {
           #[doc(hidden)]
@@ -1312,6 +1349,7 @@
        {"QualifiedMsg", cpp::QualifiedClassName(&msg)},
        {"new_thunk", ThunkName(ctx, msg, "new")},
        {"delete_thunk", ThunkName(ctx, msg, "delete")},
+       {"clear_thunk", ThunkName(ctx, msg, "clear")},
        {"serialize_thunk", ThunkName(ctx, msg, "serialize")},
        {"parse_thunk", ThunkName(ctx, msg, "parse")},
        {"copy_from_thunk", ThunkName(ctx, msg, "copy_from")},
@@ -1354,6 +1392,9 @@
         extern $abi$ {
         void* $new_thunk$() { return new $QualifiedMsg$(); }
         void $delete_thunk$(void* ptr) { delete static_cast<$QualifiedMsg$*>(ptr); }
+        void $clear_thunk$(void* ptr) {
+          static_cast<$QualifiedMsg$*>(ptr)->Clear();
+        }
         bool $serialize_thunk$($QualifiedMsg$* msg, google::protobuf::rust::SerializedData* out) {
           return google::protobuf::rust::SerializeMsg(msg, out);
         }