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

commit r14-10662-gd5d6d3ff43c5166ead1787c4334553be26cc84da
Author: Jonathan Wakely <jwak...@redhat.com>
Date:   Tue Sep 10 14:25:41 2024 +0100

    libstdc++: std::string move assignment should not use POCCA trait [PR116641]
    
    The changes to implement LWG 2579 (r10-327-gdb33efde17932f) made
    std::string::assign use the propagate_on_container_copy_assignment
    (POCCA) trait, for consistency with operator=(const basic_string&).
    However, this also unintentionally affected operator=(basic_string&&)
    which calls assign(str) to make a deep copy when performing a move is
    not possible. The fix is for the move assignment operator to call
    _M_assign(str) instead of assign(str), as this just does the deep copy
    and doesn't check the POCCA trait first.
    
    The bug only affects the unlikely/useless combination of POCCA==true and
    POCMA==false, but we should fix it for correctness anyway. it should
    also make move assignment slightly cheaper to compile and execute,
    because we skip the extra code in assign(const basic_string&).
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/116641
            * include/bits/basic_string.h (operator=(basic_string&&)): Call
            _M_assign instead of assign.
            * testsuite/21_strings/basic_string/allocator/116641.cc: New
            test.
    
    (cherry picked from commit c07cf418fdde0c192e370a8d76a991cc7215e9c4)

Diff:
---
 libstdc++-v3/include/bits/basic_string.h           |  2 +-
 .../21_strings/basic_string/allocator/116641.cc    | 53 ++++++++++++++++++++++
 2 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/libstdc++-v3/include/bits/basic_string.h 
b/libstdc++-v3/include/bits/basic_string.h
index 8a695a494efd..2794ec6419ac 100644
--- a/libstdc++-v3/include/bits/basic_string.h
+++ b/libstdc++-v3/include/bits/basic_string.h
@@ -912,7 +912,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
              __str._M_data(__str._M_use_local_data());
          }
        else // Need to do a deep copy
-         assign(__str);
+         _M_assign(__str);
        __str.clear();
        return *this;
       }
diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/allocator/116641.cc 
b/libstdc++-v3/testsuite/21_strings/basic_string/allocator/116641.cc
new file mode 100644
index 000000000000..a1a411b87faa
--- /dev/null
+++ b/libstdc++-v3/testsuite/21_strings/basic_string/allocator/116641.cc
@@ -0,0 +1,53 @@
+// { dg-do run { target c++11 } }
+// { dg-require-effective-target cxx11_abi }
+
+// Bug 116641 - std::string move assignment incorrectly depends on POCCA
+
+#include <string>
+#include <testsuite_hooks.h>
+
+template<typename T>
+struct Alloc
+{
+  using value_type = T;
+  using propagate_on_container_swap = std::false_type;
+  using propagate_on_container_copy_assignment = std::true_type;
+  using propagate_on_container_move_assignment = std::false_type;
+
+  Alloc(int id) : id(id) { }
+
+  template<typename U>
+    Alloc(const Alloc<U>& a) : id(a.id) { }
+
+  T* allocate(unsigned long n)
+  { return std::allocator<T>().allocate(n); }
+
+  void deallocate(T* p, unsigned long n)
+  { std::allocator<T>().deallocate(p, n); }
+
+  Alloc& operator=(const Alloc&) { throw; }
+
+  bool operator==(const Alloc& a) const { return id == a.id; }
+  bool operator!=(const Alloc& a) const { return id != a.id; }
+
+  int id;
+};
+
+void
+test_pr116641()
+{
+  Alloc<char> a1(1), a2(2);
+  std::basic_string<char, std::char_traits<char>, Alloc<char>> s1(a1), s2(a2);
+
+  s1 = "allocator should not propagate on move assignment";
+  VERIFY( s1.get_allocator() == a1 );
+  VERIFY( s2.get_allocator() == a2 );
+  s2 = std::move(s1);
+  VERIFY( s1.get_allocator() == a1 );
+  VERIFY( s2.get_allocator() == a2 );
+}
+
+int main()
+{
+  test_pr116641();
+}

Reply via email to