diff --git a/libstdc++-v3/include/bits/vector.tcc b/libstdc++-v3/include/bits/vector.tcc index acd11e2dc68e73b29a8b8672647aaae8c24fe007..cda52fbbc4a1bf2dd63f5d00d7268ce933f31f0c 100644 --- a/libstdc++-v3/include/bits/vector.tcc +++ b/libstdc++-v3/include/bits/vector.tcc @@ -458,73 +458,109 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER _M_realloc_insert(iterator __position, const _Tp& __x) #endif { - const size_type __len = - _M_check_len(size_type(1), "vector::_M_realloc_insert"); + const size_type __len = _M_check_len(1u, "vector::_M_realloc_insert"); pointer __old_start = this->_M_impl._M_start; pointer __old_finish = this->_M_impl._M_finish; const size_type __elems_before = __position - begin(); pointer __new_start(this->_M_allocate(__len)); pointer __new_finish(__new_start); - __try + + // RAII guard for allocated storage. + struct _Guard + { + pointer _M_storage; // Storage to deallocate + size_type _M_len; + _Tp_alloc_type& _M_alloc; + + _GLIBCXX20_CONSTEXPR + _Guard(pointer __s, size_type __l, _Tp_alloc_type& __a) + : _M_storage(__s), _M_len(__l), _M_alloc(__a) + { } + + _GLIBCXX20_CONSTEXPR + ~_Guard() { - // The order of the three operations is dictated by the C++11 - // case, where the moves could alter a new element belonging - // to the existing vector. This is an issue only for callers - // taking the element by lvalue ref (see last bullet of C++11 - // [res.on.arguments]). - _Alloc_traits::construct(this->_M_impl, - __new_start + __elems_before, + if (_M_storage) + __gnu_cxx::__alloc_traits<_Tp_alloc_type>:: + deallocate(_M_alloc, _M_storage, _M_len); + } + + private: + _Guard(const _Guard&); + }; + _Guard __guard(__new_start, __len, _M_impl); + + // The order of the three operations is dictated by the C++11 + // case, where the moves could alter a new element belonging + // to the existing vector. This is an issue only for callers + // taking the element by lvalue ref (see last bullet of C++11 + // [res.on.arguments]). + + // If this throws, the existing elements are unchanged. #if __cplusplus >= 201103L - std::forward<_Args>(__args)...); + _Alloc_traits::construct(this->_M_impl, + std::__to_address(__new_start + __elems_before), + std::forward<_Args>(__args)...); #else - __x); + _Alloc_traits::construct(this->_M_impl, + __new_start + __elems_before, + __x); #endif - __new_finish = pointer(); #if __cplusplus >= 201103L - if _GLIBCXX17_CONSTEXPR (_S_use_relocate()) - { - __new_finish = _S_relocate(__old_start, __position.base(), - __new_start, _M_get_Tp_allocator()); + if _GLIBCXX17_CONSTEXPR (_S_use_relocate()) + { + // Relocation cannot throw. + __new_finish = _S_relocate(__old_start, __position.base(), + __new_start, _M_get_Tp_allocator()); + ++__new_finish; + __new_finish = _S_relocate(__position.base(), __old_finish, + __new_finish, _M_get_Tp_allocator()); + } + else +#endif + { + // RAII type to destroy initialized elements. + struct _Guard_elts + { + pointer _M_first, _M_last; // Elements to destroy + _Tp_alloc_type& _M_alloc; - ++__new_finish; + _GLIBCXX20_CONSTEXPR + _Guard_elts(pointer __elt, _Tp_alloc_type& __a) + : _M_first(__elt), _M_last(__elt + 1), _M_alloc(__a) + { } - __new_finish = _S_relocate(__position.base(), __old_finish, - __new_finish, _M_get_Tp_allocator()); - } - else -#endif - { - __new_finish - = std::__uninitialized_move_if_noexcept_a - (__old_start, __position.base(), - __new_start, _M_get_Tp_allocator()); + _GLIBCXX20_CONSTEXPR + ~_Guard_elts() + { std::_Destroy(_M_first, _M_last, _M_alloc); } - ++__new_finish; + private: + _Guard_elts(const _Guard_elts&); + }; - __new_finish - = std::__uninitialized_move_if_noexcept_a - (__position.base(), __old_finish, - __new_finish, _M_get_Tp_allocator()); - } - } - __catch(...) - { - if (!__new_finish) - _Alloc_traits::destroy(this->_M_impl, - __new_start + __elems_before); - else - std::_Destroy(__new_start, __new_finish, _M_get_Tp_allocator()); - _M_deallocate(__new_start, __len); - __throw_exception_again; + // Guard the new element so it will be destroyed if anything throws. + _Guard_elts __guard_elts(__new_start + __elems_before, _M_impl); + + __new_finish = std::__uninitialized_move_if_noexcept_a( + __old_start, __position.base(), + __new_start, _M_get_Tp_allocator()); + + ++__new_finish; + // Guard everything before the new element too. + __guard_elts._M_first = __new_start; + + __new_finish = std::__uninitialized_move_if_noexcept_a( + __position.base(), __old_finish, + __new_finish, _M_get_Tp_allocator()); + + // New storage has been fully initialized, destroy the old elements. + __guard_elts._M_first = __old_start; + __guard_elts._M_last = __old_finish; } -#if __cplusplus >= 201103L - if _GLIBCXX17_CONSTEXPR (!_S_use_relocate()) -#endif - std::_Destroy(__old_start, __old_finish, _M_get_Tp_allocator()); - _GLIBCXX_ASAN_ANNOTATE_REINIT; - _M_deallocate(__old_start, - this->_M_impl._M_end_of_storage - __old_start); + __guard._M_storage = __old_start; + __guard._M_len = this->_M_impl._M_end_of_storage - __old_start; + this->_M_impl._M_start = __new_start; this->_M_impl._M_finish = __new_finish; this->_M_impl._M_end_of_storage = __new_start + __len;