[gcc r16-3265] libstdc++: Fix-self element self-assigments when inserting an empty range [PR121313]

Tomasz Kaminski tkaminsk@gcc.gnu.org
Mon Aug 18 08:35:01 GMT 2025


https://gcc.gnu.org/g:cc54f2f47e63c9d404a44f618cf114ae63e81b40

commit r16-3265-gcc54f2f47e63c9d404a44f618cf114ae63e81b40
Author: Tomasz Kamiński <tkaminsk@redhat.com>
Date:   Thu Aug 14 15:20:36 2025 +0200

    libstdc++: Fix-self element self-assigments when inserting an empty range [PR121313]
    
    For __n == 0, the elements were self move-assigned by
    std::move_backward(__ins, __old_finish - __n, __old_finish).
    
            PR libstdc++/121313
    
    libstdc++-v3/ChangeLog:
    
            * include/bits/vector.tcc (vector::insert_range): Add check for
            empty size.
            * testsuite/23_containers/vector/modifiers/insert/insert_range.cc:
            New tests.

Diff:
---
 libstdc++-v3/include/bits/vector.tcc               |  9 ++--
 .../vector/modifiers/insert/insert_range.cc        | 50 ++++++++++++++++++++++
 2 files changed, 56 insertions(+), 3 deletions(-)

diff --git a/libstdc++-v3/include/bits/vector.tcc b/libstdc++-v3/include/bits/vector.tcc
index 70ead1d70836..dd3d3c6fd65e 100644
--- a/libstdc++-v3/include/bits/vector.tcc
+++ b/libstdc++-v3/include/bits/vector.tcc
@@ -1007,15 +1007,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 
 	if constexpr (ranges::forward_range<_Rg>)
 	  {
+	    const auto __ins_idx = __pos - cbegin();
+	    // Number of new elements to insert:
+	    const auto __n = size_type(ranges::distance(__rg));
+	    if (__n == 0)
+	      return begin() + __ins_idx;
+
 	    // Start of existing elements:
 	    pointer __old_start = this->_M_impl._M_start;
 	    // End of existing elements:
 	    pointer __old_finish = this->_M_impl._M_finish;
 	    // Insertion point:
-	    const auto __ins_idx = __pos - cbegin();
 	    pointer __ins = __old_start + __ins_idx;
-	    // Number of new elements to insert:
-	    const auto __n = size_type(ranges::distance(__rg));
 	    // Number of elements that can fit in unused capacity:
 	    const auto __cap = this->_M_impl._M_end_of_storage - __old_finish;
 	    if (__cap >= __n)
diff --git a/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert/insert_range.cc b/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert/insert_range.cc
index 506bebbe519e..e4b5982188f6 100644
--- a/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert/insert_range.cc
+++ b/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert/insert_range.cc
@@ -99,8 +99,58 @@ test_ranges()
   return true;
 }
 
+struct SelfAssignChecker {
+  static int moveCounter;
+  static int copyCounter;
+
+  SelfAssignChecker() = default;
+  constexpr SelfAssignChecker(int v) : val(v) { }
+  SelfAssignChecker(const SelfAssignChecker&) = default;
+  SelfAssignChecker(SelfAssignChecker&&) = default;
+
+  SelfAssignChecker operator=(const SelfAssignChecker& rhs)
+  {
+    if (this == &rhs)
+      ++copyCounter;
+    this->val = rhs.val;
+    return *this;
+  }
+
+  SelfAssignChecker operator=(SelfAssignChecker&& rhs)
+  {
+    if (this == &rhs)
+      ++moveCounter;
+    this->val = rhs.val;
+    return *this;
+  }
+
+  int val;
+
+  friend bool operator==(SelfAssignChecker, SelfAssignChecker) = default;
+};
+
+int SelfAssignChecker::moveCounter = 0;
+int SelfAssignChecker::copyCounter = 0;
+
+void
+test_pr121313()
+{
+  using namespace __gnu_test;
+
+  SelfAssignChecker::copyCounter = SelfAssignChecker::moveCounter = 0;
+  do_test<test_forward_range<int>, std::allocator<SelfAssignChecker>>();
+  VERIFY( SelfAssignChecker::moveCounter == 0 );
+  VERIFY( SelfAssignChecker::copyCounter == 0 );
+
+  SelfAssignChecker::copyCounter = SelfAssignChecker::moveCounter = 0;
+  do_test<test_input_range<int>, std::allocator<SelfAssignChecker>>();
+  VERIFY( SelfAssignChecker::moveCounter == 0 );
+  VERIFY( SelfAssignChecker::copyCounter == 0 );
+}
+
 int main()
 {
   test_ranges();
+  test_pr121313();
   static_assert( test_ranges() );
 }


More information about the Libstdc++-cvs mailing list