diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2017-01-27 16:17:04 +0000 |
---|---|---|
committer | Jonathan Wakely <redi@gcc.gnu.org> | 2017-01-27 16:17:04 +0000 |
commit | a7d47f352663d16c12cb3a40459b546d166023d7 (patch) | |
tree | cf112fca8fe937bfad8a4ba585c171cccc678a55 /libstdc++-v3 | |
parent | f75bbf3fc68e1e03b55ae1f60c861faf213968ab (diff) | |
download | gcc-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/ChangeLog | 19 | ||||
-rw-r--r-- | libstdc++-v3/config/abi/pre/gnu.ver | 7 | ||||
-rw-r--r-- | libstdc++-v3/include/bits/basic_string.h | 29 | ||||
-rw-r--r-- | libstdc++-v3/include/bits/basic_string.tcc | 64 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/copy_assign.cc | 25 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/21_strings/basic_string/allocator/wchar_t/copy_assign.cc | 25 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/util/testsuite_allocator.h | 2 |
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 |