aboutsummaryrefslogtreecommitdiff
path: root/libstdc++-v3
diff options
context:
space:
mode:
authorJonathan Wakely <jwakely@redhat.com>2017-01-27 16:17:04 +0000
committerJonathan Wakely <redi@gcc.gnu.org>2017-01-27 16:17:04 +0000
commita7d47f352663d16c12cb3a40459b546d166023d7 (patch)
treecf112fca8fe937bfad8a4ba585c171cccc678a55 /libstdc++-v3
parentf75bbf3fc68e1e03b55ae1f60c861faf213968ab (diff)
downloadgcc-a7d47f352663d16c12cb3a40459b546d166023d7.zip
gcc-a7d47f352663d16c12cb3a40459b546d166023d7.tar.gz
gcc-a7d47f352663d16c12cb3a40459b546d166023d7.tar.bz2
PR libstdc++/79254 fix exception-safety in std::string::operator=
PR libstdc++/79254 * config/abi/pre/gnu.ver: Add new symbols. * include/bits/basic_string.h [_GLIBCXX_USE_CXX11_ABI] (basic_string::_M_copy_assign): New overloaded functions to perform copy assignment. (basic_string::operator=(const basic_string&)): Dispatch to _M_copy_assign. * include/bits/basic_string.tcc [_GLIBCXX_USE_CXX11_ABI] (basic_string::_M_copy_assign(const basic_string&, true_type)): Define, performing rollback on exception. * testsuite/21_strings/basic_string/allocator/char/copy_assign.cc: Test exception-safety guarantee. * testsuite/21_strings/basic_string/allocator/wchar_t/copy_assign.cc: Likewise. * testsuite/util/testsuite_allocator.h (uneq_allocator::swap): Make std::swap visible. From-SVN: r244986
Diffstat (limited to 'libstdc++-v3')
-rw-r--r--libstdc++-v3/ChangeLog19
-rw-r--r--libstdc++-v3/config/abi/pre/gnu.ver7
-rw-r--r--libstdc++-v3/include/bits/basic_string.h29
-rw-r--r--libstdc++-v3/include/bits/basic_string.tcc64
-rw-r--r--libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/copy_assign.cc25
-rw-r--r--libstdc++-v3/testsuite/21_strings/basic_string/allocator/wchar_t/copy_assign.cc25
-rw-r--r--libstdc++-v3/testsuite/util/testsuite_allocator.h2
7 files changed, 155 insertions, 16 deletions
diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog
index caa4900..7b0031e 100644
--- a/libstdc++-v3/ChangeLog
+++ b/libstdc++-v3/ChangeLog
@@ -1,3 +1,22 @@
+2017-01-27 Jonathan Wakely <jwakely@redhat.com>
+
+ PR libstdc++/79254
+ * config/abi/pre/gnu.ver: Add new symbols.
+ * include/bits/basic_string.h [_GLIBCXX_USE_CXX11_ABI]
+ (basic_string::_M_copy_assign): New overloaded functions to perform
+ copy assignment.
+ (basic_string::operator=(const basic_string&)): Dispatch to
+ _M_copy_assign.
+ * include/bits/basic_string.tcc [_GLIBCXX_USE_CXX11_ABI]
+ (basic_string::_M_copy_assign(const basic_string&, true_type)):
+ Define, performing rollback on exception.
+ * testsuite/21_strings/basic_string/allocator/char/copy_assign.cc:
+ Test exception-safety guarantee.
+ * testsuite/21_strings/basic_string/allocator/wchar_t/copy_assign.cc:
+ Likewise.
+ * testsuite/util/testsuite_allocator.h (uneq_allocator::swap): Make
+ std::swap visible.
+
2017-01-26 Jonathan Wakely <jwakely@redhat.com>
PR libstdc++/70607
diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
index 3e6e70b..1bea4b4 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -1700,7 +1700,9 @@ GLIBCXX_3.4.21 {
_ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE1[01]**;
_ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE12_Alloc_hiderC[12]EP[cw]RKS3_;
_ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE12_M*;
- _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE1[3-9]*;
+ _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE13*;
+ _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE14_M_replace_aux*;
+ _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE1[5-9]*;
_ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE[2-9]*;
_ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EEC[12]EOS4_*;
_ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EEC[12]EPK*;
@@ -1953,6 +1955,9 @@ GLIBCXX_3.4.23 {
_ZNSsC[12]ERKSs[jmy]RKSaIcE;
_ZNSbIwSt11char_traitsIwESaIwEEC[12]ERKS2_mRKS1_;
+ # basic_string<C, T, A>::_M_copy_assign(const basic_string&, {true,false}_type)
+ _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE14_M_copy_assign*;
+
#ifndef HAVE_EXCEPTION_PTR_SINCE_GCC46
# std::future symbols are exported in the first version to support
# std::exception_ptr
diff --git a/libstdc++-v3/include/bits/basic_string.h b/libstdc++-v3/include/bits/basic_string.h
index 9dffcf9..97fe797 100644
--- a/libstdc++-v3/include/bits/basic_string.h
+++ b/libstdc++-v3/include/bits/basic_string.h
@@ -384,7 +384,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
}
void
- _M_assign(const basic_string& __rcs);
+ _M_assign(const basic_string&);
void
_M_mutate(size_type __pos, size_type __len1, const _CharT* __s,
@@ -393,6 +393,15 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
void
_M_erase(size_type __pos, size_type __n);
+#if __cplusplus >= 201103L
+ void
+ _M_copy_assign(const basic_string& __str, /* pocca = */ true_type);
+
+ void
+ _M_copy_assign(const basic_string& __str, /* pocca = */ false_type)
+ { this->_M_assign(__str); }
+#endif
+
public:
// Construct/copy/destroy:
// NB: We overload ctors in some cases instead of using default
@@ -627,20 +636,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
operator=(const basic_string& __str)
{
#if __cplusplus >= 201103L
- if (_Alloc_traits::_S_propagate_on_copy_assign())
- {
- 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);
- }
- std::__alloc_on_copy(_M_get_allocator(), __str._M_get_allocator());
- }
+ _M_copy_assign(__str,
+ typename _Alloc_traits::propagate_on_container_copy_assignment());
+#else
+ this->_M_assign(__str);
#endif
- return this->assign(__str);
+ return *this;
}
/**
diff --git a/libstdc++-v3/include/bits/basic_string.tcc b/libstdc++-v3/include/bits/basic_string.tcc
index 41b7fa1..adc8b85 100644
--- a/libstdc++-v3/include/bits/basic_string.tcc
+++ b/libstdc++-v3/include/bits/basic_string.tcc
@@ -275,6 +275,70 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
}
+#if __cplusplus >= 201103L
+ template<typename _CharT, typename _Traits, typename _Alloc>
+ void
+ basic_string<_CharT, _Traits, _Alloc>::
+ _M_copy_assign(const basic_string& __str, true_type)
+ {
+ struct _Guard // RAII type for strong exception-safety guarantee.
+ {
+ // Takes ownership of string's original state.
+ _Guard(basic_string* __self)
+ : _M_self(__self), _M_alloc(std::move(__self->_M_get_allocator())),
+ _M_ptr(__self->_M_data()),
+ _M_capacity(__self->_M_allocated_capacity), _M_len(__self->length())
+ {
+ __self->_M_data(__self->_M_local_data());
+ __self->_M_length(0);
+ }
+
+ // Restores string's original state if _M_release() was not called.
+ ~_Guard()
+ {
+ if (_M_ptr)
+ {
+ _M_self->_M_get_allocator() = std::move(_M_alloc);
+ _M_self->_M_data(_M_ptr);
+ _M_self->_M_capacity(_M_capacity);
+ _M_self->_M_length(_M_len);
+ }
+ }
+
+ _Guard(const _Guard&) = delete;
+ _Guard& operator=(const _Guard&) = delete;
+
+ void _M_release()
+ {
+ // Original state can be freed now.
+ _Alloc_traits::deallocate(_M_alloc, _M_ptr, _M_capacity + 1);
+ _M_ptr = nullptr;
+ }
+
+ basic_string* _M_self;
+ allocator_type _M_alloc;
+ pointer _M_ptr;
+ size_type _M_capacity;
+ size_type _M_len;
+ };
+
+ if (!_Alloc_traits::_S_always_equal() && !_M_is_local()
+ && _M_get_allocator() != __str._M_get_allocator())
+ {
+ // The propagating allocator cannot free existing storage.
+ _Guard __guard(this);
+ _M_get_allocator() = __str._M_get_allocator();
+ this->_M_assign(__str);
+ __guard._M_release();
+ }
+ else
+ {
+ _M_get_allocator() = __str._M_get_allocator();
+ this->_M_assign(__str);
+ }
+ }
+#endif
+
template<typename _CharT, typename _Traits, typename _Alloc>
void
basic_string<_CharT, _Traits, _Alloc>::
diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/copy_assign.cc b/libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/copy_assign.cc
index 32ee708..0fc80d7 100644
--- a/libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/copy_assign.cc
+++ b/libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/copy_assign.cc
@@ -22,6 +22,7 @@
#include <string>
#include <testsuite_hooks.h>
#include <testsuite_allocator.h>
+#include <ext/throw_allocator.h>
using C = char;
const C c = 'a';
@@ -99,9 +100,33 @@ 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 );
+}
+
int main()
{
test01();
test02();
+ test03();
return 0;
}
diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/allocator/wchar_t/copy_assign.cc b/libstdc++-v3/testsuite/21_strings/basic_string/allocator/wchar_t/copy_assign.cc
index 89593ba..c35e001 100644
--- a/libstdc++-v3/testsuite/21_strings/basic_string/allocator/wchar_t/copy_assign.cc
+++ b/libstdc++-v3/testsuite/21_strings/basic_string/allocator/wchar_t/copy_assign.cc
@@ -22,6 +22,7 @@
#include <string>
#include <testsuite_hooks.h>
#include <testsuite_allocator.h>
+#include <ext/throw_allocator.h>
using C = wchar_t;
const C c = L'a';
@@ -99,9 +100,33 @@ 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 );
+}
+
int main()
{
test01();
test02();
+ test03();
return 0;
}
diff --git a/libstdc++-v3/testsuite/util/testsuite_allocator.h b/libstdc++-v3/testsuite/util/testsuite_allocator.h
index 20387a8..813fc81 100644
--- a/libstdc++-v3/testsuite/util/testsuite_allocator.h
+++ b/libstdc++-v3/testsuite/util/testsuite_allocator.h
@@ -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