Automated rollback of commit 1db8ed47c29fa04b51df373ce5bbb7e7f95cbd56.

PiperOrigin-RevId: 591045566
diff --git a/src/google/protobuf/generated_message_reflection.cc b/src/google/protobuf/generated_message_reflection.cc
index 0c914e8..647b4cc 100644
--- a/src/google/protobuf/generated_message_reflection.cc
+++ b/src/google/protobuf/generated_message_reflection.cc
@@ -1431,7 +1431,7 @@
         switch (field->options().ctype()) {
           default:  // TODO:  Support other string reps.
           case FieldOptions::STRING:
-            MutableRaw<RepeatedPtrFieldBase>(message, field)->Clear();
+            MutableRaw<RepeatedPtrField<std::string> >(message, field)->Clear();
             break;
         }
         break;
@@ -1441,7 +1441,10 @@
         if (IsMapFieldInApi(field)) {
           MutableRaw<MapFieldBase>(message, field)->Clear();
         } else {
-          MutableRaw<RepeatedPtrFieldBase>(message, field)->Clear();
+          // We don't know which subclass of RepeatedPtrFieldBase the type is,
+          // so we use RepeatedPtrFieldBase directly.
+          MutableRaw<RepeatedPtrFieldBase>(message, field)
+              ->Clear<GenericTypeHandler<Message> >();
         }
         break;
       }
@@ -1478,7 +1481,8 @@
         switch (field->options().ctype()) {
           default:  // TODO:  Support other string reps.
           case FieldOptions::STRING:
-            MutableRaw<RepeatedPtrFieldBase>(message, field)->RemoveLast();
+            MutableRaw<RepeatedPtrField<std::string> >(message, field)
+                ->RemoveLast();
             break;
         }
         break;
@@ -1487,9 +1491,10 @@
         if (IsMapFieldInApi(field)) {
           MutableRaw<MapFieldBase>(message, field)
               ->MutableRepeatedField()
-              ->RemoveLast();
+              ->RemoveLast<GenericTypeHandler<Message> >();
         } else {
-          MutableRaw<RepeatedPtrFieldBase>(message, field)->RemoveLast();
+          MutableRaw<RepeatedPtrFieldBase>(message, field)
+              ->RemoveLast<GenericTypeHandler<Message> >();
         }
         break;
     }
diff --git a/src/google/protobuf/implicit_weak_message.h b/src/google/protobuf/implicit_weak_message.h
index 7856c22..708f73a 100644
--- a/src/google/protobuf/implicit_weak_message.h
+++ b/src/google/protobuf/implicit_weak_message.h
@@ -201,7 +201,7 @@
   }
 
   T* Add() { return weak.Add(); }
-  void Clear() { base().Clear(); }
+  void Clear() { base().template Clear<TypeHandler>(); }
   void MergeFrom(const WeakRepeatedPtrField& other) {
     if (other.empty()) return;
     base().template MergeFrom<MessageLite>(other.base());
diff --git a/src/google/protobuf/repeated_field_unittest.cc b/src/google/protobuf/repeated_field_unittest.cc
index e2c3294..d99aae6 100644
--- a/src/google/protobuf/repeated_field_unittest.cc
+++ b/src/google/protobuf/repeated_field_unittest.cc
@@ -1658,6 +1658,7 @@
   EXPECT_EQ(field.ClearedCount(), 0);
 
   field.RemoveLast();
+  EXPECT_TRUE(original->empty());
   EXPECT_EQ(field.ClearedCount(), 1);
 
   EXPECT_EQ(field.Add(),
diff --git a/src/google/protobuf/repeated_ptr_field.cc b/src/google/protobuf/repeated_ptr_field.cc
index 4d92937..49c29fe 100644
--- a/src/google/protobuf/repeated_ptr_field.cc
+++ b/src/google/protobuf/repeated_ptr_field.cc
@@ -54,7 +54,7 @@
     new_rep = reinterpret_cast<Rep*>(Arena::CreateArray<char>(arena, bytes));
   }
 
-  if (using_element()) {
+  if (using_sso()) {
     new_rep->allocated_size = tagged_rep_or_elem_ != nullptr ? 1 : 0;
     new_rep->elements[0] = tagged_rep_or_elem_;
   } else {
@@ -75,7 +75,7 @@
 
   tagged_rep_or_elem_ =
       reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(new_rep) + 1);
-  capacity_proxy_ = new_capacity - kInlinedCapacity;
+  capacity_proxy_ = new_capacity - kSSOCapacity;
   return &new_rep->elements[current_size_];
 }
 
@@ -94,31 +94,18 @@
   tagged_rep_or_elem_ = nullptr;
 }
 
-namespace {
-template <typename T>
-struct ElementRecycler {
-  static void clear(void* p) { static_cast<T*>(p)->Clear(); }
-};
-
-template <>
-struct ElementRecycler<std::string> {
-  static void clear(void* str) { static_cast<std::string*>(str)->clear(); }
-};
-
-}  // namespace
-
-template <typename Recycler, typename Factory>
-void* RepeatedPtrFieldBase::AddInternal(Factory factory) {
+template <typename F>
+auto* RepeatedPtrFieldBase::AddInternal(F factory) {
   Arena* const arena = GetArena();
+  using Result = decltype(factory(arena));
   if (tagged_rep_or_elem_ == nullptr) {
     ExchangeCurrentSize(1);
     tagged_rep_or_elem_ = factory(arena);
-    return tagged_rep_or_elem_;
+    return static_cast<Result>(tagged_rep_or_elem_);
   }
-  if (using_element()) {
+  if (using_sso()) {
     if (ExchangeCurrentSize(1) == 0) {
-      Recycler::clear(tagged_rep_or_elem_);
-      return tagged_rep_or_elem_;
+      return static_cast<Result>(tagged_rep_or_elem_);
     }
   } else {
     absl::PrefetchToLocalCache(rep());
@@ -128,28 +115,23 @@
   } else {
     Rep* r = rep();
     if (current_size_ != r->allocated_size) {
-      void* cached = r->elements[ExchangeCurrentSize(current_size_ + 1)];
-      Recycler::clear(cached);
-      return cached;
+      return static_cast<Result>(
+          r->elements[ExchangeCurrentSize(current_size_ + 1)]);
     }
   }
   Rep* r = rep();
   ++r->allocated_size;
   void*& result = r->elements[ExchangeCurrentSize(current_size_ + 1)];
   result = factory(arena);
-  return result;
+  return static_cast<Result>(result);
 }
 
-void* RepeatedPtrFieldBase::AddMessageLite(ElementFactory factory) {
-  return AddInternal<ElementRecycler<MessageLite>>(factory);
-}
-
-void* RepeatedPtrFieldBase::AddString() {
-  return AddInternal<ElementRecycler<std::string>>(NewStringElement);
+void* RepeatedPtrFieldBase::AddOutOfLineHelper(ElementFactory factory) {
+  return AddInternal(factory);
 }
 
 void RepeatedPtrFieldBase::CloseGap(int start, int num) {
-  if (using_element()) {
+  if (using_sso()) {
     if (start == 0 && num == 1) {
       tagged_rep_or_elem_ = nullptr;
     }
@@ -164,8 +146,7 @@
 }
 
 MessageLite* RepeatedPtrFieldBase::AddMessage(const MessageLite* prototype) {
-  return static_cast<MessageLite*>(AddInternal<ElementRecycler<MessageLite>>(
-      [prototype](Arena* a) { return prototype->New(a); }));
+  return AddInternal([prototype](Arena* a) { return prototype->New(a); });
 }
 
 void InternalOutOfLineDeleteMessageLite(MessageLite* message) {
@@ -216,7 +197,6 @@
     ABSL_DCHECK(typeid(*src[i]) == typeid(*src[0]))
         << typeid(*src[i]).name() << " vs " << typeid(*src[0]).name();
 #endif
-    dst[i]->Clear();
     dst[i]->CheckTypeAndMergeFrom(*src[i]);
   }
   return count;
diff --git a/src/google/protobuf/repeated_ptr_field.h b/src/google/protobuf/repeated_ptr_field.h
index 05bb789..644bdb4 100644
--- a/src/google/protobuf/repeated_ptr_field.h
+++ b/src/google/protobuf/repeated_ptr_field.h
@@ -116,7 +116,7 @@
   template <typename Handler>
   using Value = typename Handler::Type;
 
-  static constexpr int kInlinedCapacity = 1;
+  static constexpr int kSSOCapacity = 1;
 
   using ElementFactory = void* (*)(Arena*);
 
@@ -158,7 +158,7 @@
   //
   //   * prefer `SizeAtCapacity()` to `size() == Capacity()`;
   //   * prefer `AllocatedSizeAtCapacity()` to `allocated_size() == Capacity()`.
-  int Capacity() const { return capacity_proxy_ + kInlinedCapacity; }
+  int Capacity() const { return capacity_proxy_ + kSSOCapacity; }
 
   template <typename TypeHandler>
   const Value<TypeHandler>& at(int index) const {
@@ -183,10 +183,7 @@
 
   template <typename Handler>
   Value<Handler>* Add() {
-    if (std::is_same<Value<Handler>, std::string>{}) {
-      return cast<Handler>(AddString());
-    }
-    return cast<Handler>(AddMessageLite(Handler::GetNewFunc()));
+    return cast<Handler>(AddOutOfLineHelper(Handler::GetNewFunc()));
   }
 
   template <
@@ -199,7 +196,7 @@
       return;
     }
     MaybeExtend();
-    if (!using_element()) ++rep()->allocated_size;
+    if (!using_sso()) ++rep()->allocated_size;
     auto* result = TypeHandler::New(arena_, std::move(value));
     element_at(ExchangeCurrentSize(current_size_ + 1)) = result;
   }
@@ -221,14 +218,15 @@
     for (int i = 0; i < n; i++) {
       Delete<H>(elems[i], nullptr);
     }
-    if (!using_element()) {
+    if (!using_sso()) {
       internal::SizedDelete(rep(),
                             Capacity() * sizeof(elems[0]) + kRepHeaderSize);
     }
   }
 
   inline bool NeedsDestroy() const {
-    // tagged_rep_or_elem_ contains either allocated element or allocated `Rep`.
+    // Either there is an allocated element in SSO buffer or there is an
+    // allocated Rep.
     return tagged_rep_or_elem_ != nullptr;
   }
   void DestroyProtos();
@@ -251,7 +249,15 @@
   // Pre-condition: prototype must not be nullptr.
   MessageLite* AddMessage(const MessageLite* prototype);
 
-  void Clear() { ExchangeCurrentSize(0); }
+  template <typename TypeHandler>
+  void Clear() {
+    const int n = current_size_;
+    ABSL_DCHECK_GE(n, 0);
+    if (n > 0) {
+      using H = CommonHandler<TypeHandler>;
+      ClearNonEmpty<H>();
+    }
+  }
 
   // Appends all message values from `from` to this instance.
   template <typename T>
@@ -288,21 +294,24 @@
     ABSL_DCHECK_EQ(current_size_, allocated_size());
     MaybeExtend();
     element_at(current_size_++) = value;
-    if (!using_element()) ++rep()->allocated_size;
+    if (!using_sso()) ++rep()->allocated_size;
   }
 
  protected:
+  template <typename TypeHandler>
   void RemoveLast() {
     ABSL_DCHECK_GT(current_size_, 0);
     ExchangeCurrentSize(current_size_ - 1);
+    using H = CommonHandler<TypeHandler>;
+    H::Clear(cast<H>(element_at(current_size_)));
   }
 
   template <typename TypeHandler>
   void CopyFrom(const RepeatedPtrFieldBase& other) {
     if (&other == this) return;
-    RepeatedPtrFieldBase::Clear();
+    RepeatedPtrFieldBase::Clear<TypeHandler>();
     if (other.empty()) return;
-    RepeatedPtrFieldBase::MergeFrom<Value<TypeHandler>>(other);
+    RepeatedPtrFieldBase::MergeFrom<typename TypeHandler::Type>(other);
   }
 
   void CloseGap(int start, int num);
@@ -357,7 +366,7 @@
   template <typename TypeHandler>
   PROTOBUF_NOINLINE size_t SpaceUsedExcludingSelfLong() const {
     size_t allocated_bytes =
-        using_element()
+        using_sso()
             ? 0
             : static_cast<size_t>(Capacity()) * sizeof(void*) + kRepHeaderSize;
     const int n = allocated_size();
@@ -371,15 +380,15 @@
 
   // Advanced memory management --------------------------------------
 
-  // Returns a pointer to a cleared object ready to reuse if there is a spare
-  // allocated object or nullptr otherwise.
+  // Like Add(), but if there are no cleared objects to use, returns nullptr.
   template <typename TypeHandler>
   Value<TypeHandler>* AddFromCleared() {
-    if (ClearedCount() == 0) return nullptr;
-    auto* value =
-        cast<TypeHandler>(element_at(ExchangeCurrentSize(current_size_ + 1)));
-    CommonHandler<TypeHandler>::Clear(value);
-    return value;
+    if (current_size_ < allocated_size()) {
+      return cast<TypeHandler>(
+          element_at(ExchangeCurrentSize(current_size_ + 1)));
+    } else {
+      return nullptr;
+    }
   }
 
   template <typename TypeHandler>
@@ -401,7 +410,7 @@
       elems[allocated_size()] = elems[current_size_];
     }
     elems[ExchangeCurrentSize(current_size_ + 1)] = value;
-    if (!using_element()) ++rep()->allocated_size;
+    if (!using_sso()) ++rep()->allocated_size;
   }
 
   template <typename TypeHandler>
@@ -409,24 +418,24 @@
     ABSL_DCHECK_NE(value, nullptr);
     // Make room for the new pointer.
     if (SizeAtCapacity()) {
-      // The array is completely full, so grow it.
+      // The array is completely full with no cleared objects, so grow it.
       InternalExtend(1);
       ++rep()->allocated_size;
     } else if (AllocatedSizeAtCapacity()) {
       // There is no more space in the pointer array because it contains some
-      // objects awaiting reuse.  We don't want to grow the array in
+      // cleared objects awaiting reuse.  We don't want to grow the array in
       // this case because otherwise a loop calling AddAllocated() followed by
       // Clear() would leak memory.
       using H = CommonHandler<TypeHandler>;
       Delete<H>(element_at(current_size_), arena_);
     } else if (current_size_ < allocated_size()) {
-      // We have some unused allocated objects.  Their order is not important,
-      // so we move the first one to the end to make room for the pointer.
+      // We have some cleared objects.  We don't care about their order, so we
+      // can just move the first one to the end to make space.
       element_at(allocated_size()) = element_at(current_size_);
       ++rep()->allocated_size;
     } else {
-      // There are no unused allocated objects.
-      if (!using_element()) ++rep()->allocated_size;
+      // There are no cleared objects.
+      if (!using_sso()) ++rep()->allocated_size;
     }
 
     element_at(ExchangeCurrentSize(current_size_ + 1)) = value;
@@ -455,13 +464,13 @@
     ABSL_DCHECK_GT(current_size_, 0);
     ExchangeCurrentSize(current_size_ - 1);
     auto* result = cast<TypeHandler>(element_at(current_size_));
-    if (using_element()) {
+    if (using_sso()) {
       tagged_rep_or_elem_ = nullptr;
     } else {
       --rep()->allocated_size;
       if (current_size_ < allocated_size()) {
-        // There are unused allocated elements on the end; replace the removed
-        // element with the last allocated element.
+        // There are cleared elements on the end; replace the removed element
+        // with the last allocated element.
         element_at(current_size_) = element_at(allocated_size());
       }
     }
@@ -477,7 +486,7 @@
     ABSL_DCHECK(TypeHandler::GetArena(value) == nullptr)
         << "AddCleared() can only accept values not on an arena.";
     MaybeExtend();
-    if (using_element()) {
+    if (using_sso()) {
       tagged_rep_or_elem_ = value;
     } else {
       element_at(rep()->allocated_size++) = value;
@@ -491,14 +500,13 @@
         << "an arena.";
     ABSL_DCHECK(tagged_rep_or_elem_ != nullptr);
     ABSL_DCHECK_GT(allocated_size(), current_size_);
-    void* result;
-    if (using_element()) {
-      result = std::exchange(tagged_rep_or_elem_, nullptr);
+    if (using_sso()) {
+      auto* result = cast<TypeHandler>(tagged_rep_or_elem_);
+      tagged_rep_or_elem_ = nullptr;
+      return result;
     } else {
-      result = element_at(--rep()->allocated_size);
+      return cast<TypeHandler>(element_at(--rep()->allocated_size));
     }
-    TypeHandler::Clear(cast<TypeHandler>(result));
-    return cast<TypeHandler>(result);
   }
 
   // Slowpath handles all cases, copying if necessary.
@@ -536,7 +544,7 @@
     // than three times.
     RepeatedPtrFieldBase temp(other->GetArena());
     if (!this->empty()) {
-      temp.MergeFrom<Value<TypeHandler>>(*this);
+      temp.MergeFrom<typename TypeHandler::Type>(*this);
     }
     this->CopyFrom<TypeHandler>(*other);
     other->InternalSwap(&temp);
@@ -617,29 +625,30 @@
     ABSL_DCHECK_LE(size(), allocated_size());
     ABSL_DCHECK_LE(allocated_size(), Capacity());
     // This is equivalent to `current_size_ == Capacity()`.
-    //
-    // Using less than instead of equality gives compiler an opportunity to
-    // generate less instructions.
+    // Assuming `Capacity()` function is inlined, compiler is likely to optimize
+    // away "+ kSSOCapacity" and reduce it to "current_size_ > capacity_proxy_"
+    // which is an instruction less than "current_size_ == capacity_proxy_ + 1".
     return current_size_ >= Capacity();
   }
   inline bool AllocatedSizeAtCapacity() const {
     // Harden invariant size() <= allocated_size() <= Capacity().
     ABSL_DCHECK_LE(size(), allocated_size());
     ABSL_DCHECK_LE(allocated_size(), Capacity());
-    // See comment in SizeAtCapacity().
-    return using_element() ? (tagged_rep_or_elem_ != nullptr)
-                           : rep()->allocated_size >= Capacity();
+    // This combines optimization mentioned in `SizeAtCapacity()` and simplifies
+    // `allocated_size()` in sso case.
+    return using_sso() ? (tagged_rep_or_elem_ != nullptr)
+                       : rep()->allocated_size >= Capacity();
   }
 
   void* const* elements() const {
-    return using_element() ? &tagged_rep_or_elem_ : +rep()->elements;
+    return using_sso() ? &tagged_rep_or_elem_ : +rep()->elements;
   }
   void** elements() {
-    return using_element() ? &tagged_rep_or_elem_ : +rep()->elements;
+    return using_sso() ? &tagged_rep_or_elem_ : +rep()->elements;
   }
 
   void*& element_at(int index) {
-    if (using_element()) {
+    if (using_sso()) {
       ABSL_DCHECK_EQ(index, 0);
       return tagged_rep_or_elem_;
     }
@@ -650,11 +659,11 @@
   }
 
   int allocated_size() const {
-    return using_element() ? (tagged_rep_or_elem_ != nullptr ? 1 : 0)
-                           : rep()->allocated_size;
+    return using_sso() ? (tagged_rep_or_elem_ != nullptr ? 1 : 0)
+                       : rep()->allocated_size;
   }
   Rep* rep() {
-    ABSL_DCHECK(!using_element());
+    ABSL_DCHECK(!using_sso());
     return reinterpret_cast<Rep*>(
         reinterpret_cast<uintptr_t>(tagged_rep_or_elem_) - 1);
   }
@@ -662,7 +671,7 @@
     return const_cast<RepeatedPtrFieldBase*>(this)->rep();
   }
 
-  bool using_element() const {
+  bool using_sso() const {
     return (reinterpret_cast<uintptr_t>(tagged_rep_or_elem_) & 1) == 0;
   }
 
@@ -679,13 +688,28 @@
     TypeHandler::Delete(cast<TypeHandler>(obj), arena);
   }
 
-  // Merges messages from `from` into available, allocated messages sitting in
-  // the range `[size(), allocated_size())`. Returns the number of message
-  // merged which is `ClearedCount(), from.size())`.
-  // Note that this function does explicitly NOT update `current_size_`. This
-  // function is out of line as it should be the slow path: this scenario only
-  // happens when a caller constructs and fills a repeated field, then shrinks
-  // it, and then merges additional messages into it.
+  // Out-of-line helper routine for Clear() once the inlined check has
+  // determined the container is non-empty
+  template <typename TypeHandler>
+  PROTOBUF_NOINLINE void ClearNonEmpty() {
+    const int n = current_size_;
+    void* const* elems = elements();
+    int i = 0;
+    ABSL_DCHECK_GT(n, 0);
+    // do/while loop to avoid initial test because we know n > 0
+    do {
+      TypeHandler::Clear(cast<TypeHandler>(elems[i++]));
+    } while (i < n);
+    ExchangeCurrentSize(0);
+  }
+
+  // Merges messages from `from` into available, cleared messages sitting in the
+  // range `[size(), allocated_size())`. Returns the number of message merged
+  // which is `ClearedCount(), from.size())`.
+  // Note that this function does explicitly NOT update `current_size_`.
+  // This function is out of line as it should be the slow path: this scenario
+  // only happens when a caller constructs and fills a repeated field, then
+  // shrinks it, and then merges additional messages into it.
   int MergeIntoClearedMessages(const RepeatedPtrFieldBase& from);
 
   // Appends all messages from `from` to this instance, using the
@@ -712,55 +736,40 @@
   // Returns a pointer to the element directly beyond the last element.
   inline void** InternalReserve(int n) {
     if (n <= Capacity()) {
-      return elements() + current_size_;
+      void** elements = using_sso() ? &tagged_rep_or_elem_ : rep()->elements;
+      return elements + current_size_;
     }
     return InternalExtend(n - Capacity());
   }
 
-  // Internal helpers for Add that keep definition out-of-line.
-  void* AddMessageLite(ElementFactory factory);
-  void* AddString();
+  // Internal helper for Add that keeps definition out-of-line.
+  void* AddOutOfLineHelper(ElementFactory factory);
 
   // Common implementation used by various Add* methods. `factory` is an object
-  // used to construct a new element unless there are spare allocated elements
+  // used to construct a new element unless there are spare cleared elements
   // ready for reuse. Returns pointer to the new element.
   //
   // Note: avoid inlining this function in methods such as `Add()` as this would
   // drastically increase binary size due to template instantiation and implicit
-  // inlining.
-  template <typename Recycler, typename Factory>
-  void* AddInternal(Factory factory);
+  // inlining. Instead, use wrapper functions with out-of-line definition
+  // similar to `AddOutOfLineHelper`.
+  template <typename F>
+  auto* AddInternal(F factory);
 
   // A few notes on internal representation:
   //
-  // * Class layout is optimized to minimize the size: 24 bytes on x86-64.
-  // * The elements can be stored in one of the two ways and `using_element()`
-  //   tells which one is currently used.
-  //
-  // In case of using_element():
-  //
-  //   tagged_rep_or_elem_ is a storage for at most one pointer.
-  //   Number of allocated objects (0 or 1) is determined whether
-  //   tagged_rep_or_elem_ is nullptr.
-  //
-  // Otherwise,
-  //
-  //   tagged_rep_or_elem_ is tagged (LSB is 1) pointer to `Rep`, where
-  //   `Rep` contains number of allocated objects as well as the buffer with
-  //   pointers to allocated elements. Rep allows to (a) keep the sizeof small
-  //   (b) allocate both buffer for elements and an integer with allocated
-  //   objects count in one shot.
-  //
-  // In both cases, RepeatedPtrFieldBase may own allocated but unused objects:
-  //
-  //   1. Their count is determined by `ClearedCount()`.
-  //   2. Pointers to them are stored directly after pointers to used objects.
-  //   3. They can be reused in order to avoid extra allocation (note that in
-  //      some cases these objects need to be cleared with `TypeHandler::Clear`
-  //      before they can be reused).
+  // We use an indirected approach, with struct Rep, to keep
+  // sizeof(RepeatedPtrFieldBase) equivalent to what it was before arena support
+  // was added; namely, 3 8-byte machine words on x86-64. An instance of Rep is
+  // allocated only when the repeated field is non-empty, and it is a
+  // dynamically-sized struct (the header is directly followed by elements[]).
+  // We place arena_ and current_size_ directly in the object to avoid cache
+  // misses due to the indirection, because these fields are checked frequently.
+  // Placing all fields directly in the RepeatedPtrFieldBase instance would cost
+  // significant performance for memory-sensitive workloads.
   void* tagged_rep_or_elem_;
   int current_size_;
-  int capacity_proxy_;  // See `Capacity()`
+  int capacity_proxy_;  // we store `capacity - kSSOCapacity` as an optimization
   Arena* arena_;
 };
 
@@ -975,7 +984,7 @@
   pointer Mutable(int index) ABSL_ATTRIBUTE_LIFETIME_BOUND;
 
   // Unlike std::vector, adding an element to a RepeatedPtrField doesn't always
-  // make a new element; it might reuse an element left over from when the
+  // make a new element; it might re-use an element left over from when the
   // field was Clear()'d or resize()'d smaller.  For this reason, Add() is the
   // fastest API for adding a new element.
   pointer Add() ABSL_ATTRIBUTE_LIFETIME_BOUND;
@@ -1157,9 +1166,9 @@
   void UnsafeArenaExtractSubrange(int start, int num, Element** elements);
 
   // When elements are removed by calls to RemoveLast() or Clear(), they
-  // are not actually freed.  Instead, they are kept so that they can be reused
-  // later.  This can save lots of CPU time when repeatedly reusing a protocol
-  // message for similar purposes.
+  // are not actually freed.  Instead, they are cleared and kept so that
+  // they can be reused later.  This can save lots of CPU time when
+  // repeatedly reusing a protocol message for similar purposes.
   //
   // Hardcore programs may choose to manipulate these cleared objects
   // to better optimize memory management using the following routines.
@@ -1375,7 +1384,7 @@
 
 template <typename Element>
 inline void RepeatedPtrField<Element>::RemoveLast() {
-  RepeatedPtrFieldBase::RemoveLast();
+  RepeatedPtrFieldBase::RemoveLast<TypeHandler>();
 }
 
 template <typename Element>
@@ -1450,7 +1459,7 @@
 
 template <typename Element>
 inline void RepeatedPtrField<Element>::Clear() {
-  RepeatedPtrFieldBase::Clear();
+  RepeatedPtrFieldBase::Clear<TypeHandler>();
 }
 
 template <typename Element>