]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
PR libstdc++/79254 fix exception-safety of std::string copy assignment
authorJonathan Wakely <jwakely@redhat.com>
Wed, 1 Feb 2017 12:18:43 +0000 (12:18 +0000)
committerJonathan Wakely <redi@gcc.gnu.org>
Wed, 1 Feb 2017 12:18:43 +0000 (12:18 +0000)
PR libstdc++/79254
* include/bits/basic_string.h [_GLIBCXX_USE_CXX11_ABI]
(basic_string::operator=(const basic_string&)): If source object is
small just deallocate, otherwise perform new allocation before
making any changes.
* testsuite/21_strings/basic_string/allocator/wchar_t/copy_assign.cc:
Test exception-safety of copy assignment when allocator propagates.
* testsuite/21_strings/basic_string/allocator/char/copy_assign.cc:
Likewise.
* testsuite/util/testsuite_allocator.h (uneq_allocator::swap): Make
std::swap visible.

From-SVN: r245088

libstdc++-v3/ChangeLog
libstdc++-v3/include/bits/basic_string.h
libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/copy_assign.cc
libstdc++-v3/testsuite/21_strings/basic_string/allocator/wchar_t/copy_assign.cc
libstdc++-v3/testsuite/util/testsuite_allocator.h

index c7f446554d269f5a6fe5bdbd611b8da46b06a29a..35c852d8ed2c8dd75d217c23e738931e13555cf0 100644 (file)
@@ -1,3 +1,17 @@
+2017-02-01  Jonathan Wakely  <jwakely@redhat.com>
+
+       PR libstdc++/79254
+       * include/bits/basic_string.h [_GLIBCXX_USE_CXX11_ABI]
+       (basic_string::operator=(const basic_string&)): If source object is
+       small just deallocate, otherwise perform new allocation before
+       making any changes.
+       * testsuite/21_strings/basic_string/allocator/wchar_t/copy_assign.cc:
+       Test exception-safety of copy assignment when allocator propagates.
+       * testsuite/21_strings/basic_string/allocator/char/copy_assign.cc:
+       Likewise.
+       * testsuite/util/testsuite_allocator.h (uneq_allocator::swap): Make
+       std::swap visible.
+
 2017-01-22  Gerald Pfeifer  <gerald@pfeifer.com>
 
        Merge from mainline
index 9e3aec0940a58f4d47d3759879b03bd5487d4aeb..de56d86fd83b2ef786b6b84237cbff78464a201b 100644 (file)
@@ -571,10 +571,25 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
            if (!_Alloc_traits::_S_always_equal() && !_M_is_local()
                && _M_get_allocator() != __str._M_get_allocator())
              {
-               // replacement allocator cannot free existing storage
-               _M_destroy(_M_allocated_capacity);
-               _M_data(_M_local_data());
-               _M_set_length(0);
+               // Propagating allocator cannot free existing storage so must
+               // deallocate it before replacing current allocator.
+               if (__str.size() <= _S_local_capacity)
+                 {
+                   _M_destroy(_M_allocated_capacity);
+                   _M_data(_M_local_data());
+                   _M_set_length(0);
+                 }
+               else
+                 {
+                   const auto __len = __str.size();
+                   auto __alloc = __str._M_get_allocator();
+                   // If this allocation throws there are no effects:
+                   auto __ptr = _Alloc_traits::allocate(__alloc, __len + 1);
+                   _M_destroy(_M_allocated_capacity);
+                   _M_data(__ptr);
+                   _M_capacity(__len);
+                   _M_set_length(__len);
+                 }
              }
            std::__alloc_on_copy(_M_get_allocator(), __str._M_get_allocator());
          }
index 94e0796116869a93a8770e2812cb8dc55fac3c83..ccb474e5fa6a0bad510be92d49fdce289f015208 100644 (file)
@@ -20,6 +20,7 @@
 #include <string>
 #include <testsuite_hooks.h>
 #include <testsuite_allocator.h>
+#include <ext/throw_allocator.h>
  
 #if _GLIBCXX_USE_CXX11_ABI
 using C = char;
@@ -100,10 +101,44 @@ void test02()
   VERIFY(1 == v5.get_allocator().get_personality());
 }
 
+void test03()
+{
+  // PR libstdc++/79254
+  using throw_alloc = __gnu_cxx::throw_allocator_limit<C>;
+  typedef propagating_allocator<C, true, throw_alloc> alloc_type;
+  typedef std::basic_string<C, traits, alloc_type> test_type;
+  alloc_type a1(1), a2(2);
+  throw_alloc::set_limit(2); // Throw on third allocation (during assignment).
+  const C* s1 = "a string that is longer than a small string";
+  const C* s2 = "another string that is longer than a small string";
+  test_type v1(s1, a1);
+  test_type v2(s2, a2);
+  bool caught = false;
+  try {
+    v1 = v2;
+  } catch (__gnu_cxx::forced_error&) {
+    caught = true;
+  }
+  VERIFY( caught );
+  VERIFY( v1 == s1 );
+  VERIFY( v1.get_allocator() == a1 );
+
+  throw_alloc::set_limit(1); // Allow one more allocation (and no more).
+  test_type v3(s1, a1);
+  // No allocation when allocators are equal and capacity is sufficient:
+  VERIFY( v1.capacity() >= v3.size() );
+  v1 = v3;
+  // No allocation when the contents fit in the small-string buffer:
+  v2 = "sso";
+  v1 = v2;
+  VERIFY( v1.get_allocator() == a2 );
+}
+
 int main()
 {
   test01();
   test02();
+  test03();
   return 0;
 }
 #else
index ed8feb7b33e4e6b3b9e227f6dd2b6d39ef82123b..68a452463f05189252f5528c34b4fa165e8811ea 100644 (file)
@@ -20,6 +20,7 @@
 #include <string>
 #include <testsuite_hooks.h>
 #include <testsuite_allocator.h>
+#include <ext/throw_allocator.h>
  
 #if _GLIBCXX_USE_CXX11_ABI
 using C = wchar_t;
@@ -100,10 +101,44 @@ void test02()
   VERIFY(1 == v5.get_allocator().get_personality());
 }
 
+void test03()
+{
+  // PR libstdc++/79254
+  using throw_alloc = __gnu_cxx::throw_allocator_limit<C>;
+  typedef propagating_allocator<C, true, throw_alloc> alloc_type;
+  typedef std::basic_string<C, traits, alloc_type> test_type;
+  alloc_type a1(1), a2(2);
+  throw_alloc::set_limit(2); // Throw on third allocation (during assignment).
+  const C* s1 = L"a string that is longer than a small string";
+  const C* s2 = L"another string that is longer than a small string";
+  test_type v1(s1, a1);
+  test_type v2(s2, a2);
+  bool caught = false;
+  try {
+    v1 = v2;
+  } catch (__gnu_cxx::forced_error&) {
+    caught = true;
+  }
+  VERIFY( caught );
+  VERIFY( v1 == s1 );
+  VERIFY( v1.get_allocator() == a1 );
+
+  throw_alloc::set_limit(1); // Allow one more allocation (and no more).
+  test_type v3(s1, a1);
+  // No allocation when allocators are equal and capacity is sufficient:
+  VERIFY( v1.capacity() >= v3.size() );
+  v1 = v3;
+  // No allocation when the contents fit in the small-string buffer:
+  v2 = L"sso";
+  v1 = v2;
+  VERIFY( v1.get_allocator() == a2 );
+}
+
 int main()
 {
   test01();
   test02();
+  test03();
   return 0;
 }
 #else
index f94dd7088b531a8659803342c2e3ef7e27e5751c..876d16ddb083f036c706410b5f245b214fb66872 100644 (file)
@@ -287,7 +287,7 @@ namespace __gnu_test
 
       Alloc& base() { return *this; }
       const Alloc& base() const  { return *this; }
-      void swap_base(Alloc& b) { swap(b, this->base()); }
+      void swap_base(Alloc& b) { using std::swap; swap(b, this->base()); }
 
     public:
       typedef typename check_consistent_alloc_value_type<Tp, Alloc>::value_type