Split "zero" trait into more targeted traits and make them more precise.
Swap is trivial regardless of default values.
Message types can initialize with zero, but not clear with zero.

PiperOrigin-RevId: 489282728
diff --git a/src/google/protobuf/compiler/cpp/helpers.cc b/src/google/protobuf/compiler/cpp/helpers.cc
index 2429bbc..5898717 100644
--- a/src/google/protobuf/compiler/cpp/helpers.cc
+++ b/src/google/protobuf/compiler/cpp/helpers.cc
@@ -323,7 +323,9 @@
 const char kThinSeparator[] =
     "// -------------------------------------------------------------------\n";
 
-bool CanInitializeByZeroing(const FieldDescriptor* field) {
+bool CanInitializeByZeroing(const FieldDescriptor* field,
+                            const Options& options,
+                            MessageSCCAnalyzer* scc_analyzer) {
   if (field->is_repeated() || field->is_extension()) return false;
   switch (field->cpp_type()) {
     case FieldDescriptor::CPPTYPE_ENUM:
@@ -342,6 +344,57 @@
       return field->default_value_double() == 0;
     case FieldDescriptor::CPPTYPE_BOOL:
       return field->default_value_bool() == false;
+    case FieldDescriptor::CPPTYPE_MESSAGE:
+      // Non-repeated, non-lazy message fields are raw pointers initialized to
+      // null.
+      return !IsLazy(field, options, scc_analyzer);
+    default:
+      return false;
+  }
+}
+
+bool CanClearByZeroing(const FieldDescriptor* field) {
+  if (field->is_repeated() || field->is_extension()) return false;
+  switch (field->cpp_type()) {
+    case FieldDescriptor::CPPTYPE_ENUM:
+      return field->default_value_enum()->number() == 0;
+    case FieldDescriptor::CPPTYPE_INT32:
+      return field->default_value_int32() == 0;
+    case FieldDescriptor::CPPTYPE_INT64:
+      return field->default_value_int64() == 0;
+    case FieldDescriptor::CPPTYPE_UINT32:
+      return field->default_value_uint32() == 0;
+    case FieldDescriptor::CPPTYPE_UINT64:
+      return field->default_value_uint64() == 0;
+    case FieldDescriptor::CPPTYPE_FLOAT:
+      return field->default_value_float() == 0;
+    case FieldDescriptor::CPPTYPE_DOUBLE:
+      return field->default_value_double() == 0;
+    case FieldDescriptor::CPPTYPE_BOOL:
+      return field->default_value_bool() == false;
+    default:
+      return false;
+  }
+}
+
+// Determines if swap can be implemented via memcpy.
+bool HasTrivialSwap(const FieldDescriptor* field, const Options& options,
+                    MessageSCCAnalyzer* scc_analyzer) {
+  if (field->is_repeated() || field->is_extension()) return false;
+  switch (field->cpp_type()) {
+    case FieldDescriptor::CPPTYPE_ENUM:
+    case FieldDescriptor::CPPTYPE_INT32:
+    case FieldDescriptor::CPPTYPE_INT64:
+    case FieldDescriptor::CPPTYPE_UINT32:
+    case FieldDescriptor::CPPTYPE_UINT64:
+    case FieldDescriptor::CPPTYPE_FLOAT:
+    case FieldDescriptor::CPPTYPE_DOUBLE:
+    case FieldDescriptor::CPPTYPE_BOOL:
+      return true;
+    case FieldDescriptor::CPPTYPE_MESSAGE:
+      // Non-repeated, non-lazy message fields are simply raw pointers, so we
+      // can swap them with memcpy.
+      return !IsLazy(field, options, scc_analyzer);
     default:
       return false;
   }
diff --git a/src/google/protobuf/compiler/cpp/helpers.h b/src/google/protobuf/compiler/cpp/helpers.h
index 42c5464..ae1ed68 100644
--- a/src/google/protobuf/compiler/cpp/helpers.h
+++ b/src/google/protobuf/compiler/cpp/helpers.h
@@ -122,8 +122,17 @@
 std::string Namespace(const FieldDescriptor* d);
 std::string Namespace(const EnumDescriptor* d);
 
+class MessageSCCAnalyzer;
+
+// Returns true if it's safe to init "field" to zero.
+bool CanInitializeByZeroing(const FieldDescriptor* field,
+                            const Options& options,
+                            MessageSCCAnalyzer* scc_analyzer);
 // Returns true if it's safe to reset "field" to zero.
-bool CanInitializeByZeroing(const FieldDescriptor* field);
+bool CanClearByZeroing(const FieldDescriptor* field);
+// Determines if swap can be implemented via memcpy.
+bool HasTrivialSwap(const FieldDescriptor* field, const Options& options,
+                    MessageSCCAnalyzer* scc_analyzer);
 
 std::string ClassName(const Descriptor* descriptor);
 std::string ClassName(const EnumDescriptor* enum_descriptor);
@@ -363,8 +372,6 @@
          EffectiveStringCType(field, options) == FieldOptions::STRING_PIECE;
 }
 
-class MessageSCCAnalyzer;
-
 // Does the given FileDescriptor use lazy fields?
 bool HasLazyFields(const FileDescriptor* file, const Options& options,
                    MessageSCCAnalyzer* scc_analyzer);
diff --git a/src/google/protobuf/compiler/cpp/message.cc b/src/google/protobuf/compiler/cpp/message.cc
index 7be63c2..d99f7d0 100644
--- a/src/google/protobuf/compiler/cpp/message.cc
+++ b/src/google/protobuf/compiler/cpp/message.cc
@@ -176,23 +176,6 @@
   }
 }
 
-// Helper for the code that emits the SharedCtor() and InternalSwap() methods.
-// Anything that is a POD or a "normal" message (represented by a pointer) can
-// be manipulated as raw bytes.
-bool CanBeManipulatedAsRawBytes(const FieldDescriptor* field,
-                                const Options& options,
-                                MessageSCCAnalyzer* scc_analyzer) {
-  bool ret = CanInitializeByZeroing(field);
-
-  // Non-repeated, non-lazy message fields are simply raw pointers, so we can
-  // swap them or use memset to initialize these in SharedCtor. We cannot use
-  // this in Clear, as we need to potentially delete the existing value.
-  ret =
-      ret || (!field->is_repeated() && !IsLazy(field, options, scc_analyzer) &&
-              field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE);
-  return ret;
-}
-
 // Finds runs of fields for which `predicate` is true.
 // RunMap maps from fields that start each run to the number of fields in that
 // run.  This is optimized for the common case that there are very few runs in
@@ -3069,7 +3052,7 @@
   // checks all hasbits in the chunk and skips it if none are set.
   int zero_init_bytes = 0;
   for (const auto& field : optimized_order_) {
-    if (CanInitializeByZeroing(field)) {
+    if (CanClearByZeroing(field)) {
       zero_init_bytes += EstimateAlignmentSize(field);
     }
   }
@@ -3082,12 +3065,12 @@
         chunk_count++;
         // This predicate guarantees that there is only a single zero-init
         // (memset) per chunk, and if present it will be at the beginning.
-        bool same = HasByteIndex(a) == HasByteIndex(b) &&
-                    a->is_repeated() == b->is_repeated() &&
-                    ShouldSplit(a, options_) == ShouldSplit(b, options_) &&
-                    (CanInitializeByZeroing(a) == CanInitializeByZeroing(b) ||
-                     (CanInitializeByZeroing(a) &&
-                      (chunk_count == 1 || merge_zero_init)));
+        bool same =
+            HasByteIndex(a) == HasByteIndex(b) &&
+            a->is_repeated() == b->is_repeated() &&
+            ShouldSplit(a, options_) == ShouldSplit(b, options_) &&
+            (CanClearByZeroing(a) == CanClearByZeroing(b) ||
+             (CanClearByZeroing(a) && (chunk_count == 1 || merge_zero_init)));
         if (!same) chunk_count = 0;
         return same;
       });
@@ -3116,7 +3099,7 @@
     }
 
     for (const auto& field : chunk) {
-      if (CanInitializeByZeroing(field)) {
+      if (CanClearByZeroing(field)) {
         GOOGLE_CHECK(!saw_non_zero_init);
         if (!memset_start) memset_start = field;
         memset_end = field;
@@ -3172,7 +3155,7 @@
 
     // Clear all non-zero-initializable fields in the chunk.
     for (const auto& field : chunk) {
-      if (CanInitializeByZeroing(field)) continue;
+      if (CanClearByZeroing(field)) continue;
       // It's faster to just overwrite primitive types, but we should only
       // clear strings and messages if they were set.
       //
@@ -3310,7 +3293,7 @@
     const RunMap runs =
         FindRuns(optimized_order_, [this](const FieldDescriptor* field) {
           return !ShouldSplit(field, options_) &&
-                 CanBeManipulatedAsRawBytes(field, options_, scc_analyzer_);
+                 HasTrivialSwap(field, options_, scc_analyzer_);
         });
 
     for (size_t i = 0; i < optimized_order_.size(); ++i) {
diff --git a/src/google/protobuf/compiler/cpp/padding_optimizer.cc b/src/google/protobuf/compiler/cpp/padding_optimizer.cc
index 74804b2..d611c34 100644
--- a/src/google/protobuf/compiler/cpp/padding_optimizer.cc
+++ b/src/google/protobuf/compiler/cpp/padding_optimizer.cc
@@ -121,7 +121,7 @@
       if (IsLazy(field, options, scc_analyzer)) {
         f = LAZY_MESSAGE;
       }
-    } else if (CanInitializeByZeroing(field)) {
+    } else if (CanInitializeByZeroing(field, options, scc_analyzer)) {
       f = ZERO_INITIALIZABLE;
     }
 
diff --git a/src/google/protobuf/descriptor.pb.cc b/src/google/protobuf/descriptor.pb.cc
index 51a1934..2660791 100644
--- a/src/google/protobuf/descriptor.pb.cc
+++ b/src/google/protobuf/descriptor.pb.cc
@@ -4448,13 +4448,11 @@
       &other->_impl_.json_name_, rhs_arena
   );
   ::PROTOBUF_NAMESPACE_ID::internal::memswap<
-      PROTOBUF_FIELD_OFFSET(FieldDescriptorProto, _impl_.proto3_optional_)
-      + sizeof(FieldDescriptorProto::_impl_.proto3_optional_)
+      PROTOBUF_FIELD_OFFSET(FieldDescriptorProto, _impl_.type_)
+      + sizeof(FieldDescriptorProto::_impl_.type_)
       - PROTOBUF_FIELD_OFFSET(FieldDescriptorProto, _impl_.options_)>(
           reinterpret_cast<char*>(&_impl_.options_),
           reinterpret_cast<char*>(&other->_impl_.options_));
-  swap(_impl_.label_, other->_impl_.label_);
-  swap(_impl_.type_, other->_impl_.type_);
 }
 
 ::PROTOBUF_NAMESPACE_ID::Metadata FieldDescriptorProto::GetMetadata() const {
@@ -7619,13 +7617,11 @@
       &other->_impl_.ruby_package_, rhs_arena
   );
   ::PROTOBUF_NAMESPACE_ID::internal::memswap<
-      PROTOBUF_FIELD_OFFSET(FileOptions, _impl_.deprecated_)
-      + sizeof(FileOptions::_impl_.deprecated_)
+      PROTOBUF_FIELD_OFFSET(FileOptions, _impl_.cc_enable_arenas_)
+      + sizeof(FileOptions::_impl_.cc_enable_arenas_)
       - PROTOBUF_FIELD_OFFSET(FileOptions, _impl_.java_multiple_files_)>(
           reinterpret_cast<char*>(&_impl_.java_multiple_files_),
           reinterpret_cast<char*>(&other->_impl_.java_multiple_files_));
-  swap(_impl_.optimize_for_, other->_impl_.optimize_for_);
-  swap(_impl_.cc_enable_arenas_, other->_impl_.cc_enable_arenas_);
 }
 
 ::PROTOBUF_NAMESPACE_ID::Metadata FileOptions::GetMetadata() const {