+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
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());
}
#include <string>
#include <testsuite_hooks.h>
#include <testsuite_allocator.h>
+#include <ext/throw_allocator.h>
#if _GLIBCXX_USE_CXX11_ABI
using C = char;
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
#include <string>
#include <testsuite_hooks.h>
#include <testsuite_allocator.h>
+#include <ext/throw_allocator.h>
#if _GLIBCXX_USE_CXX11_ABI
using C = wchar_t;
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
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