On Sat, 1 Sep 2018, Marc Glisse wrote:
this patch passed bootstrap+regtest on powerpc64le-unknown-linux-gnu.
I realized afterwards that for a C++17-only feature, that's not testing much... So I changed it to apply in C++14 and fixed a minor issue. There is now a single regression:
23_containers/vector/modifiers/push_back/49836.ccThe PR was about not using assignment for an operation that should only use construction, and that's fine. But we ended up with a stricter testcase using CopyConsOnlyType, where the type has a deleted move constructor which, as far as I understand the standard, makes it an invalid type for use in vector::push_back. Is that something we want to keep supporting, or may I break it? What is happening is that the definition of __use_relocate asks if some expression involving a move of _Tp is noexcept, which causes a hard error. It would certainly be possible to work around that, but it would complicate the code and seems quite pointless to me.
-- Marc Glisse
Index: include/bits/alloc_traits.h =================================================================== --- include/bits/alloc_traits.h (revision 264027) +++ include/bits/alloc_traits.h (working copy) @@ -233,38 +233,43 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION using type = decltype(__test<_Alloc>(0)); }; template<typename _Tp, typename... _Args> using __has_construct = typename __construct_helper<_Tp, _Args...>::type; template<typename _Tp, typename... _Args> static _Require<__has_construct<_Tp, _Args...>> _S_construct(_Alloc& __a, _Tp* __p, _Args&&... __args) + noexcept(noexcept(__a.construct(__p, std::forward<_Args>(__args)...))) { __a.construct(__p, std::forward<_Args>(__args)...); } template<typename _Tp, typename... _Args> static _Require<__and_<__not_<__has_construct<_Tp, _Args...>>, is_constructible<_Tp, _Args...>>> _S_construct(_Alloc&, _Tp* __p, _Args&&... __args) + noexcept(noexcept(::new((void*)__p) + _Tp(std::forward<_Args>(__args)...))) { ::new((void*)__p) _Tp(std::forward<_Args>(__args)...); } template<typename _Alloc2, typename _Tp> static auto _S_destroy(_Alloc2& __a, _Tp* __p, int) + noexcept(noexcept(__a.destroy(__p))) -> decltype(__a.destroy(__p)) { __a.destroy(__p); } template<typename _Alloc2, typename _Tp> static void _S_destroy(_Alloc2&, _Tp* __p, ...) + noexcept(noexcept(__p->~_Tp())) { __p->~_Tp(); } template<typename _Alloc2> static auto _S_max_size(_Alloc2& __a, int) -> decltype(__a.max_size()) { return __a.max_size(); } template<typename _Alloc2> static size_type @@ -333,33 +338,36 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @param __p Pointer to memory of suitable size and alignment for Tp * @param __args Constructor arguments. * * Calls <tt> __a.construct(__p, std::forward<Args>(__args)...) </tt> * if that expression is well-formed, otherwise uses placement-new * to construct an object of type @a _Tp at location @a __p from the * arguments @a __args... */ template<typename _Tp, typename... _Args> static auto construct(_Alloc& __a, _Tp* __p, _Args&&... __args) + noexcept(noexcept(_S_construct(__a, __p, + std::forward<_Args>(__args)...))) -> decltype(_S_construct(__a, __p, std::forward<_Args>(__args)...)) { _S_construct(__a, __p, std::forward<_Args>(__args)...); } /** * @brief Destroy an object of type @a _Tp * @param __a An allocator. * @param __p Pointer to the object to destroy * * Calls @c __a.destroy(__p) if that expression is well-formed, * otherwise calls @c __p->~_Tp() */ template<typename _Tp> static void destroy(_Alloc& __a, _Tp* __p) + noexcept(noexcept(_S_destroy(__a, __p, 0))) { _S_destroy(__a, __p, 0); } /** * @brief The maximum supported allocation size * @param __a An allocator. * @return @c __a.max_size() or @c numeric_limits<size_type>::max() * * Returns @c __a.max_size() if that expression is well-formed, * otherwise returns @c numeric_limits<size_type>::max() */ @@ -465,32 +473,34 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @brief Construct an object of type @a _Up * @param __a An allocator. * @param __p Pointer to memory of suitable size and alignment for Tp * @param __args Constructor arguments. * * Calls <tt> __a.construct(__p, std::forward<Args>(__args)...) </tt> */ template<typename _Up, typename... _Args> static void construct(allocator_type& __a, _Up* __p, _Args&&... __args) + noexcept(noexcept(__a.construct(__p, std::forward<_Args>(__args)...))) { __a.construct(__p, std::forward<_Args>(__args)...); } /** * @brief Destroy an object of type @a _Up * @param __a An allocator. * @param __p Pointer to the object to destroy * * Calls @c __a.destroy(__p). */ template<typename _Up> static void destroy(allocator_type& __a, _Up* __p) + noexcept(noexcept(__a.destroy(__p))) { __a.destroy(__p); } /** * @brief The maximum supported allocation size * @param __a An allocator. * @return @c __a.max_size() */ static size_type max_size(const allocator_type& __a) noexcept { return __a.max_size(); } Index: include/bits/allocator.h =================================================================== --- include/bits/allocator.h (revision 264027) +++ include/bits/allocator.h (working copy) @@ -81,25 +81,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #if __cplusplus >= 201103L // _GLIBCXX_RESOLVE_LIB_DEFECTS // 2103. std::allocator propagate_on_container_move_assignment typedef true_type propagate_on_container_move_assignment; typedef true_type is_always_equal; template<typename _Up, typename... _Args> void construct(_Up* __p, _Args&&... __args) + noexcept(noexcept(::new((void *)__p) + _Up(std::forward<_Args>(__args)...))) { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } template<typename _Up> void - destroy(_Up* __p) { __p->~_Up(); } + destroy(_Up* __p) + noexcept(noexcept(__p->~_Up())) + { __p->~_Up(); } #endif }; /** * @brief The @a standard allocator, as per [20.4]. * * See https://gcc.gnu.org/onlinedocs/libstdc++/manual/memory.html#std.util.memory.allocator * for further details. * * @tparam _Tp Type of allocated object. Index: include/bits/stl_uninitialized.h =================================================================== --- include/bits/stl_uninitialized.h (revision 264027) +++ include/bits/stl_uninitialized.h (working copy) @@ -872,14 +872,84 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION uninitialized_move_n(_InputIterator __first, _Size __count, _ForwardIterator __result) { auto __res = std::__uninitialized_copy_n_pair (_GLIBCXX_MAKE_MOVE_ITERATOR(__first), __count, __result); return {__res.first.base(), __res.second}; } #endif +#if __cplusplus >= 201402L + template<typename _Tp, typename _Up> + inline void + __relocate(_Tp* __dest, _Up* __orig) + noexcept(is_nothrow_constructible<_Tp, _Up&&>::value) + { + ::new((void*)__dest) _Tp(std::move(*__orig)); + __orig->~_Up(); + } + + template<typename _Tp, typename _Up, typename _Allocator> + inline void + __relocate_a(_Tp* __dest, _Up* __orig, _Allocator& __alloc) + noexcept(noexcept(__gnu_cxx::__alloc_traits<_Allocator>::construct(__alloc, + __dest, std::move(*__orig))) + && noexcept(__gnu_cxx::__alloc_traits<_Allocator>::destroy( + __alloc, std::__addressof(*__orig)))) + { + typedef __gnu_cxx::__alloc_traits<_Allocator> __traits; + __traits::construct(__alloc, __dest, std::move(*__orig)); + __traits::destroy(__alloc, std::__addressof(*__orig)); + } + + template<typename _Tp> + struct __is_trivially_relocatable + : is_trivially_move_constructible<_Tp> { }; + + template <typename _Tp, typename _Up> + inline std::enable_if_t<std::__is_trivially_relocatable<_Tp>::value, _Tp*> + __relocate_a_1(_Tp* __first, _Tp* __last, + _Tp* __result, allocator<_Up>& __alloc) + { + ptrdiff_t __count = __last - __first; + __builtin_memmove(__result, __first, __count * sizeof(_Tp)); + return __result + __count; + } + + template <typename _InputIterator, typename _ForwardIterator, + typename _Allocator> + inline _ForwardIterator + __relocate_a_1(_InputIterator __first, _InputIterator __last, + _ForwardIterator __result, _Allocator& __alloc) + { + typedef typename iterator_traits<_InputIterator>::value_type + _ValueType; + typedef typename iterator_traits<_ForwardIterator>::value_type + _ValueType2; + static_assert(std::is_same<_ValueType, _ValueType2>::value); + static_assert(noexcept(std::__relocate_a(std::addressof(*__result), + std::addressof(*__first), + __alloc))); + _ForwardIterator __cur = __result; + for (; __first != __last; ++__first, (void)++__cur) + std::__relocate_a(std::__addressof(*__cur), + std::__addressof(*__first), __alloc); + return __cur; + } + + template <typename _InputIterator, typename _ForwardIterator, + typename _Allocator> + inline _ForwardIterator + __relocate_a(_InputIterator __first, _InputIterator __last, + _ForwardIterator __result, _Allocator& __alloc) + { + return __relocate_a_1(std::__niter_base(__first), + std::__niter_base(__last), + std::__niter_base(__result), __alloc); + } +#endif + _GLIBCXX_END_NAMESPACE_VERSION } // namespace #endif /* _STL_UNINITIALIZED_H */ Index: include/bits/stl_vector.h =================================================================== --- include/bits/stl_vector.h (revision 264027) +++ include/bits/stl_vector.h (working copy) @@ -414,20 +414,28 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER typedef typename _Alloc_traits::const_reference const_reference; typedef __gnu_cxx::__normal_iterator<pointer, vector> iterator; typedef __gnu_cxx::__normal_iterator<const_pointer, vector> const_iterator; typedef std::reverse_iterator<const_iterator> const_reverse_iterator; typedef std::reverse_iterator<iterator> reverse_iterator; typedef size_t size_type; typedef ptrdiff_t difference_type; typedef _Alloc allocator_type; + private: +#if __cplusplus >= 201402L + static constexpr bool __use_relocate = + noexcept(std::__relocate_a(std::addressof(*std::declval<pointer>()), + std::addressof(*std::declval<pointer>()), + std::declval<_Tp_alloc_type&>())); +#endif + protected: using _Base::_M_allocate; using _Base::_M_deallocate; using _Base::_M_impl; using _Base::_M_get_Tp_allocator; public: // [23.2.4.1] construct/copy/destroy // (assign() and get_allocator() are also listed in this section) Index: include/bits/vector.tcc =================================================================== --- include/bits/vector.tcc (revision 264027) +++ include/bits/vector.tcc (working copy) @@ -64,26 +64,39 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER template<typename _Tp, typename _Alloc> void vector<_Tp, _Alloc>:: reserve(size_type __n) { if (__n > this->max_size()) __throw_length_error(__N("vector::reserve")); if (this->capacity() < __n) { const size_type __old_size = size(); - pointer __tmp = _M_allocate_and_copy(__n, + pointer __tmp; +#if __cplusplus >= 201402L + if constexpr (__use_relocate) + { + __tmp = this->_M_allocate(__n); + std::__relocate_a(this->_M_impl._M_start, + this->_M_impl._M_finish, + __tmp, _M_get_Tp_allocator()); + } + else +#endif + { + __tmp = _M_allocate_and_copy(__n, _GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(this->_M_impl._M_start), _GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(this->_M_impl._M_finish)); - _GLIBCXX_ASAN_ANNOTATE_REINIT; std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, _M_get_Tp_allocator()); + } + _GLIBCXX_ASAN_ANNOTATE_REINIT; _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start); this->_M_impl._M_start = __tmp; this->_M_impl._M_finish = __tmp + __old_size; this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n; } } #if __cplusplus >= 201103L @@ -288,23 +301,23 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER vector<_Tp, _Alloc>:: _M_assign_aux(_ForwardIterator __first, _ForwardIterator __last, std::forward_iterator_tag) { const size_type __len = std::distance(__first, __last); if (__len > capacity()) { _S_check_init_len(__len, _M_get_Tp_allocator()); pointer __tmp(_M_allocate_and_copy(__len, __first, __last)); - _GLIBCXX_ASAN_ANNOTATE_REINIT; std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, _M_get_Tp_allocator()); + _GLIBCXX_ASAN_ANNOTATE_REINIT; _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start); this->_M_impl._M_start = __tmp; this->_M_impl._M_finish = this->_M_impl._M_start + __len; this->_M_impl._M_end_of_storage = this->_M_impl._M_finish; } else if (size() >= __len) _M_erase_at_end(std::copy(__first, __last, this->_M_impl._M_start)); else @@ -436,44 +449,66 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER // [res.on.arguments]). _Alloc_traits::construct(this->_M_impl, __new_start + __elems_before, #if __cplusplus >= 201103L std::forward<_Args>(__args)...); #else __x); #endif __new_finish = pointer(); +#if __cplusplus >= 201402L + if constexpr (__use_relocate) + { + __new_finish + = std::__relocate_a + (__old_start, __position.base(), + __new_start, _M_get_Tp_allocator()); + + ++__new_finish; + + __new_finish + = std::__relocate_a + (__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()); ++__new_finish; __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; } - _GLIBCXX_ASAN_ANNOTATE_REINIT; +#if __cplusplus >= 201402L + if constexpr (!__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); this->_M_impl._M_start = __new_start; this->_M_impl._M_finish = __new_finish; this->_M_impl._M_end_of_storage = __new_start + __len; } template<typename _Tp, typename _Alloc> void vector<_Tp, _Alloc>:: @@ -555,23 +590,23 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER if (!__new_finish) std::_Destroy(__new_start + __elems_before, __new_start + __elems_before + __n, _M_get_Tp_allocator()); else std::_Destroy(__new_start, __new_finish, _M_get_Tp_allocator()); _M_deallocate(__new_start, __len); __throw_exception_again; } - _GLIBCXX_ASAN_ANNOTATE_REINIT; std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, _M_get_Tp_allocator()); + _GLIBCXX_ASAN_ANNOTATE_REINIT; _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_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; } } } @@ -596,41 +631,62 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER this->_M_impl._M_finish = std::__uninitialized_default_n_a(this->_M_impl._M_finish, __n, _M_get_Tp_allocator()); _GLIBCXX_ASAN_ANNOTATE_GREW(__n); } else { const size_type __len = _M_check_len(__n, "vector::_M_default_append"); pointer __new_start(this->_M_allocate(__len)); +#if __cplusplus >= 201402L + if constexpr (__use_relocate) + { + __try + { + std::__uninitialized_default_n_a(__new_start + __size, + __n, _M_get_Tp_allocator()); + } + __catch(...) + { + _M_deallocate(__new_start, __len); + __throw_exception_again; + } + std::__relocate_a(this->_M_impl._M_start, + this->_M_impl._M_finish, + __new_start, _M_get_Tp_allocator()); + } + else +#endif + { pointer __destroy_from = pointer(); __try { std::__uninitialized_default_n_a(__new_start + __size, __n, _M_get_Tp_allocator()); __destroy_from = __new_start + __size; std::__uninitialized_move_if_noexcept_a( this->_M_impl._M_start, this->_M_impl._M_finish, __new_start, _M_get_Tp_allocator()); } __catch(...) { if (__destroy_from) std::_Destroy(__destroy_from, __destroy_from + __n, _M_get_Tp_allocator()); _M_deallocate(__new_start, __len); __throw_exception_again; } - _GLIBCXX_ASAN_ANNOTATE_REINIT; std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, _M_get_Tp_allocator()); + } + _GLIBCXX_ASAN_ANNOTATE_REINIT; _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start); this->_M_impl._M_start = __new_start; this->_M_impl._M_finish = __new_start + __size + __n; this->_M_impl._M_end_of_storage = __new_start + __len; } } } @@ -735,23 +791,23 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER (__position.base(), this->_M_impl._M_finish, __new_finish, _M_get_Tp_allocator()); } __catch(...) { std::_Destroy(__new_start, __new_finish, _M_get_Tp_allocator()); _M_deallocate(__new_start, __len); __throw_exception_again; } - _GLIBCXX_ASAN_ANNOTATE_REINIT; std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, _M_get_Tp_allocator()); + _GLIBCXX_ASAN_ANNOTATE_REINIT; _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_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; } } } Index: include/ext/alloc_traits.h =================================================================== --- include/ext/alloc_traits.h (revision 264027) +++ include/ext/alloc_traits.h (working copy) @@ -73,29 +73,32 @@ template<typename _Alloc, typename = typ template<typename _Ptr> using __is_custom_pointer = std::__and_<std::is_same<pointer, _Ptr>, std::__not_<std::is_pointer<_Ptr>>>; public: // overload construct for non-standard pointer types template<typename _Ptr, typename... _Args> static typename std::enable_if<__is_custom_pointer<_Ptr>::value>::type construct(_Alloc& __a, _Ptr __p, _Args&&... __args) + noexcept(noexcept(_Base_type::construct(__a, std::__to_address(__p), + std::forward<_Args>(__args)...))) { _Base_type::construct(__a, std::__to_address(__p), std::forward<_Args>(__args)...); } // overload destroy for non-standard pointer types template<typename _Ptr> static typename std::enable_if<__is_custom_pointer<_Ptr>::value>::type destroy(_Alloc& __a, _Ptr __p) + noexcept(noexcept(_Base_type::destroy(__a, std::__to_address(__p)))) { _Base_type::destroy(__a, std::__to_address(__p)); } static _Alloc _S_select_on_copy(const _Alloc& __a) { return _Base_type::select_on_container_copy_construction(__a); } static void _S_on_swap(_Alloc& __a, _Alloc& __b) { std::__alloc_on_swap(__a, __b); } static constexpr bool _S_propagate_on_copy_assign() { return _Base_type::propagate_on_container_copy_assignment::value; } Index: include/ext/malloc_allocator.h =================================================================== --- include/ext/malloc_allocator.h (revision 264027) +++ include/ext/malloc_allocator.h (working copy) @@ -138,25 +138,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { std::free(static_cast<void*>(__p)); } size_type max_size() const _GLIBCXX_USE_NOEXCEPT { return size_t(-1) / sizeof(_Tp); } #if __cplusplus >= 201103L template<typename _Up, typename... _Args> void construct(_Up* __p, _Args&&... __args) + noexcept(noexcept(::new((void *)__p) + _Up(std::forward<_Args>(__args)...))) { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } template<typename _Up> void - destroy(_Up* __p) { __p->~_Up(); } + destroy(_Up* __p) + noexcept(noexcept(__p->~_Up())) + { __p->~_Up(); } #else // _GLIBCXX_RESOLVE_LIB_DEFECTS // 402. wrong new expression in [some_] allocator::construct void construct(pointer __p, const _Tp& __val) { ::new((void *)__p) value_type(__val); } void destroy(pointer __p) { __p->~_Tp(); } #endif Index: include/ext/new_allocator.h =================================================================== --- include/ext/new_allocator.h (revision 264027) +++ include/ext/new_allocator.h (working copy) @@ -129,25 +129,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } size_type max_size() const _GLIBCXX_USE_NOEXCEPT { return size_t(-1) / sizeof(_Tp); } #if __cplusplus >= 201103L template<typename _Up, typename... _Args> void construct(_Up* __p, _Args&&... __args) + noexcept(noexcept(::new((void *)__p) + _Up(std::forward<_Args>(__args)...))) { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } template<typename _Up> void - destroy(_Up* __p) { __p->~_Up(); } + destroy(_Up* __p) + noexcept(noexcept( __p->~_Up())) + { __p->~_Up(); } #else // _GLIBCXX_RESOLVE_LIB_DEFECTS // 402. wrong new expression in [some_] allocator::construct void construct(pointer __p, const _Tp& __val) { ::new((void *)__p) _Tp(__val); } void destroy(pointer __p) { __p->~_Tp(); } #endif
Index: include/bits/alloc_traits.h =================================================================== --- include/bits/alloc_traits.h (revision 264027) +++ include/bits/alloc_traits.h (working copy) @@ -233,38 +233,43 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION using type = decltype(__test<_Alloc>(0)); }; template<typename _Tp, typename... _Args> using __has_construct = typename __construct_helper<_Tp, _Args...>::type; template<typename _Tp, typename... _Args> static _Require<__has_construct<_Tp, _Args...>> _S_construct(_Alloc& __a, _Tp* __p, _Args&&... __args) + noexcept(noexcept(__a.construct(__p, std::forward<_Args>(__args)...))) { __a.construct(__p, std::forward<_Args>(__args)...); } template<typename _Tp, typename... _Args> static _Require<__and_<__not_<__has_construct<_Tp, _Args...>>, is_constructible<_Tp, _Args...>>> _S_construct(_Alloc&, _Tp* __p, _Args&&... __args) + noexcept(noexcept(::new((void*)__p) + _Tp(std::forward<_Args>(__args)...))) { ::new((void*)__p) _Tp(std::forward<_Args>(__args)...); } template<typename _Alloc2, typename _Tp> static auto _S_destroy(_Alloc2& __a, _Tp* __p, int) + noexcept(noexcept(__a.destroy(__p))) -> decltype(__a.destroy(__p)) { __a.destroy(__p); } template<typename _Alloc2, typename _Tp> static void _S_destroy(_Alloc2&, _Tp* __p, ...) + noexcept(noexcept(__p->~_Tp())) { __p->~_Tp(); } template<typename _Alloc2> static auto _S_max_size(_Alloc2& __a, int) -> decltype(__a.max_size()) { return __a.max_size(); } template<typename _Alloc2> static size_type @@ -333,33 +338,36 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @param __p Pointer to memory of suitable size and alignment for Tp * @param __args Constructor arguments. * * Calls <tt> __a.construct(__p, std::forward<Args>(__args)...) </tt> * if that expression is well-formed, otherwise uses placement-new * to construct an object of type @a _Tp at location @a __p from the * arguments @a __args... */ template<typename _Tp, typename... _Args> static auto construct(_Alloc& __a, _Tp* __p, _Args&&... __args) + noexcept(noexcept(_S_construct(__a, __p, + std::forward<_Args>(__args)...))) -> decltype(_S_construct(__a, __p, std::forward<_Args>(__args)...)) { _S_construct(__a, __p, std::forward<_Args>(__args)...); } /** * @brief Destroy an object of type @a _Tp * @param __a An allocator. * @param __p Pointer to the object to destroy * * Calls @c __a.destroy(__p) if that expression is well-formed, * otherwise calls @c __p->~_Tp() */ template<typename _Tp> static void destroy(_Alloc& __a, _Tp* __p) + noexcept(noexcept(_S_destroy(__a, __p, 0))) { _S_destroy(__a, __p, 0); } /** * @brief The maximum supported allocation size * @param __a An allocator. * @return @c __a.max_size() or @c numeric_limits<size_type>::max() * * Returns @c __a.max_size() if that expression is well-formed, * otherwise returns @c numeric_limits<size_type>::max() */ @@ -465,32 +473,34 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @brief Construct an object of type @a _Up * @param __a An allocator. * @param __p Pointer to memory of suitable size and alignment for Tp * @param __args Constructor arguments. * * Calls <tt> __a.construct(__p, std::forward<Args>(__args)...) </tt> */ template<typename _Up, typename... _Args> static void construct(allocator_type& __a, _Up* __p, _Args&&... __args) + noexcept(noexcept(__a.construct(__p, std::forward<_Args>(__args)...))) { __a.construct(__p, std::forward<_Args>(__args)...); } /** * @brief Destroy an object of type @a _Up * @param __a An allocator. * @param __p Pointer to the object to destroy * * Calls @c __a.destroy(__p). */ template<typename _Up> static void destroy(allocator_type& __a, _Up* __p) + noexcept(noexcept(__a.destroy(__p))) { __a.destroy(__p); } /** * @brief The maximum supported allocation size * @param __a An allocator. * @return @c __a.max_size() */ static size_type max_size(const allocator_type& __a) noexcept { return __a.max_size(); } Index: include/bits/allocator.h =================================================================== --- include/bits/allocator.h (revision 264027) +++ include/bits/allocator.h (working copy) @@ -81,25 +81,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #if __cplusplus >= 201103L // _GLIBCXX_RESOLVE_LIB_DEFECTS // 2103. std::allocator propagate_on_container_move_assignment typedef true_type propagate_on_container_move_assignment; typedef true_type is_always_equal; template<typename _Up, typename... _Args> void construct(_Up* __p, _Args&&... __args) + noexcept(noexcept(::new((void *)__p) + _Up(std::forward<_Args>(__args)...))) { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } template<typename _Up> void - destroy(_Up* __p) { __p->~_Up(); } + destroy(_Up* __p) + noexcept(noexcept(__p->~_Up())) + { __p->~_Up(); } #endif }; /** * @brief The @a standard allocator, as per [20.4]. * * See https://gcc.gnu.org/onlinedocs/libstdc++/manual/memory.html#std.util.memory.allocator * for further details. * * @tparam _Tp Type of allocated object. Index: include/bits/stl_uninitialized.h =================================================================== --- include/bits/stl_uninitialized.h (revision 264027) +++ include/bits/stl_uninitialized.h (working copy) @@ -872,14 +872,84 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION uninitialized_move_n(_InputIterator __first, _Size __count, _ForwardIterator __result) { auto __res = std::__uninitialized_copy_n_pair (_GLIBCXX_MAKE_MOVE_ITERATOR(__first), __count, __result); return {__res.first.base(), __res.second}; } #endif +#if __cplusplus >= 201402L + template<typename _Tp, typename _Up> + inline void + __relocate(_Tp* __dest, _Up* __orig) + noexcept(is_nothrow_constructible<_Tp, _Up&&>::value) + { + ::new((void*)__dest) _Tp(std::move(*__orig)); + __orig->~_Up(); + } + + template<typename _Tp, typename _Up, typename _Allocator> + inline void + __relocate_a(_Tp* __dest, _Up* __orig, _Allocator& __alloc) + noexcept(noexcept(__gnu_cxx::__alloc_traits<_Allocator>::construct(__alloc, + __dest, std::move(*__orig))) + && noexcept(__gnu_cxx::__alloc_traits<_Allocator>::destroy( + __alloc, std::__addressof(*__orig)))) + { + typedef __gnu_cxx::__alloc_traits<_Allocator> __traits; + __traits::construct(__alloc, __dest, std::move(*__orig)); + __traits::destroy(__alloc, std::__addressof(*__orig)); + } + + template<typename _Tp> + struct __is_trivially_relocatable + : is_trivially_move_constructible<_Tp> { }; + + template <typename _Tp, typename _Up> + inline std::enable_if_t<std::__is_trivially_relocatable<_Tp>::value, _Tp*> + __relocate_a_1(_Tp* __first, _Tp* __last, + _Tp* __result, allocator<_Up>& __alloc) + { + ptrdiff_t __count = __last - __first; + __builtin_memmove(__result, __first, __count * sizeof(_Tp)); + return __result + __count; + } + + template <typename _InputIterator, typename _ForwardIterator, + typename _Allocator> + inline _ForwardIterator + __relocate_a_1(_InputIterator __first, _InputIterator __last, + _ForwardIterator __result, _Allocator& __alloc) + { + typedef typename iterator_traits<_InputIterator>::value_type + _ValueType; + typedef typename iterator_traits<_ForwardIterator>::value_type + _ValueType2; + static_assert(std::is_same<_ValueType, _ValueType2>::value); + static_assert(noexcept(std::__relocate_a(std::addressof(*__result), + std::addressof(*__first), + __alloc))); + _ForwardIterator __cur = __result; + for (; __first != __last; ++__first, (void)++__cur) + std::__relocate_a(std::__addressof(*__cur), + std::__addressof(*__first), __alloc); + return __cur; + } + + template <typename _InputIterator, typename _ForwardIterator, + typename _Allocator> + inline _ForwardIterator + __relocate_a(_InputIterator __first, _InputIterator __last, + _ForwardIterator __result, _Allocator& __alloc) + { + return __relocate_a_1(std::__niter_base(__first), + std::__niter_base(__last), + std::__niter_base(__result), __alloc); + } +#endif + _GLIBCXX_END_NAMESPACE_VERSION } // namespace #endif /* _STL_UNINITIALIZED_H */ Index: include/bits/stl_vector.h =================================================================== --- include/bits/stl_vector.h (revision 264027) +++ include/bits/stl_vector.h (working copy) @@ -414,20 +414,28 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER typedef typename _Alloc_traits::const_reference const_reference; typedef __gnu_cxx::__normal_iterator<pointer, vector> iterator; typedef __gnu_cxx::__normal_iterator<const_pointer, vector> const_iterator; typedef std::reverse_iterator<const_iterator> const_reverse_iterator; typedef std::reverse_iterator<iterator> reverse_iterator; typedef size_t size_type; typedef ptrdiff_t difference_type; typedef _Alloc allocator_type; + private: +#if __cplusplus >= 201402L + static constexpr bool __use_relocate = + noexcept(std::__relocate_a(std::addressof(*std::declval<pointer>()), + std::addressof(*std::declval<pointer>()), + std::declval<_Tp_alloc_type&>())); +#endif + protected: using _Base::_M_allocate; using _Base::_M_deallocate; using _Base::_M_impl; using _Base::_M_get_Tp_allocator; public: // [23.2.4.1] construct/copy/destroy // (assign() and get_allocator() are also listed in this section) Index: include/bits/vector.tcc =================================================================== --- include/bits/vector.tcc (revision 264027) +++ include/bits/vector.tcc (working copy) @@ -64,26 +64,39 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER template<typename _Tp, typename _Alloc> void vector<_Tp, _Alloc>:: reserve(size_type __n) { if (__n > this->max_size()) __throw_length_error(__N("vector::reserve")); if (this->capacity() < __n) { const size_type __old_size = size(); - pointer __tmp = _M_allocate_and_copy(__n, - _GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(this->_M_impl._M_start), - _GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(this->_M_impl._M_finish)); + pointer __tmp; +#if __cplusplus >= 201402L + if constexpr (__use_relocate) + { + __tmp = this->_M_allocate(__n); + std::__relocate_a(this->_M_impl._M_start, + this->_M_impl._M_finish, + __tmp, _M_get_Tp_allocator()); + } + else +#endif + { + __tmp = _M_allocate_and_copy(__n, + _GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(this->_M_impl._M_start), + _GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(this->_M_impl._M_finish)); + std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, + _M_get_Tp_allocator()); + } _GLIBCXX_ASAN_ANNOTATE_REINIT; - std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, - _M_get_Tp_allocator()); _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start); this->_M_impl._M_start = __tmp; this->_M_impl._M_finish = __tmp + __old_size; this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n; } } #if __cplusplus >= 201103L @@ -288,23 +301,23 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER vector<_Tp, _Alloc>:: _M_assign_aux(_ForwardIterator __first, _ForwardIterator __last, std::forward_iterator_tag) { const size_type __len = std::distance(__first, __last); if (__len > capacity()) { _S_check_init_len(__len, _M_get_Tp_allocator()); pointer __tmp(_M_allocate_and_copy(__len, __first, __last)); - _GLIBCXX_ASAN_ANNOTATE_REINIT; std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, _M_get_Tp_allocator()); + _GLIBCXX_ASAN_ANNOTATE_REINIT; _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start); this->_M_impl._M_start = __tmp; this->_M_impl._M_finish = this->_M_impl._M_start + __len; this->_M_impl._M_end_of_storage = this->_M_impl._M_finish; } else if (size() >= __len) _M_erase_at_end(std::copy(__first, __last, this->_M_impl._M_start)); else @@ -436,44 +449,66 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER // [res.on.arguments]). _Alloc_traits::construct(this->_M_impl, __new_start + __elems_before, #if __cplusplus >= 201103L std::forward<_Args>(__args)...); #else __x); #endif __new_finish = pointer(); - __new_finish - = std::__uninitialized_move_if_noexcept_a - (__old_start, __position.base(), - __new_start, _M_get_Tp_allocator()); - - ++__new_finish; - - __new_finish - = std::__uninitialized_move_if_noexcept_a - (__position.base(), __old_finish, - __new_finish, _M_get_Tp_allocator()); +#if __cplusplus >= 201402L + if constexpr (__use_relocate) + { + __new_finish + = std::__relocate_a + (__old_start, __position.base(), + __new_start, _M_get_Tp_allocator()); + + ++__new_finish; + + __new_finish + = std::__relocate_a + (__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()); + + ++__new_finish; + + __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; } +#if __cplusplus >= 201402L + if constexpr (!__use_relocate) +#endif + std::_Destroy(__old_start, __old_finish, _M_get_Tp_allocator()); _GLIBCXX_ASAN_ANNOTATE_REINIT; - std::_Destroy(__old_start, __old_finish, _M_get_Tp_allocator()); _M_deallocate(__old_start, 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; } template<typename _Tp, typename _Alloc> void vector<_Tp, _Alloc>:: @@ -555,23 +590,23 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER if (!__new_finish) std::_Destroy(__new_start + __elems_before, __new_start + __elems_before + __n, _M_get_Tp_allocator()); else std::_Destroy(__new_start, __new_finish, _M_get_Tp_allocator()); _M_deallocate(__new_start, __len); __throw_exception_again; } - _GLIBCXX_ASAN_ANNOTATE_REINIT; std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, _M_get_Tp_allocator()); + _GLIBCXX_ASAN_ANNOTATE_REINIT; _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_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; } } } @@ -596,41 +631,62 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER this->_M_impl._M_finish = std::__uninitialized_default_n_a(this->_M_impl._M_finish, __n, _M_get_Tp_allocator()); _GLIBCXX_ASAN_ANNOTATE_GREW(__n); } else { const size_type __len = _M_check_len(__n, "vector::_M_default_append"); pointer __new_start(this->_M_allocate(__len)); - pointer __destroy_from = pointer(); - __try +#if __cplusplus >= 201402L + if constexpr (__use_relocate) { - std::__uninitialized_default_n_a(__new_start + __size, - __n, _M_get_Tp_allocator()); - __destroy_from = __new_start + __size; - std::__uninitialized_move_if_noexcept_a( - this->_M_impl._M_start, this->_M_impl._M_finish, - __new_start, _M_get_Tp_allocator()); + __try + { + std::__uninitialized_default_n_a(__new_start + __size, + __n, _M_get_Tp_allocator()); + } + __catch(...) + { + _M_deallocate(__new_start, __len); + __throw_exception_again; + } + std::__relocate_a(this->_M_impl._M_start, + this->_M_impl._M_finish, + __new_start, _M_get_Tp_allocator()); } - __catch(...) + else +#endif { - if (__destroy_from) - std::_Destroy(__destroy_from, __destroy_from + __n, - _M_get_Tp_allocator()); - _M_deallocate(__new_start, __len); - __throw_exception_again; + pointer __destroy_from = pointer(); + __try + { + std::__uninitialized_default_n_a(__new_start + __size, + __n, _M_get_Tp_allocator()); + __destroy_from = __new_start + __size; + std::__uninitialized_move_if_noexcept_a( + this->_M_impl._M_start, this->_M_impl._M_finish, + __new_start, _M_get_Tp_allocator()); + } + __catch(...) + { + if (__destroy_from) + std::_Destroy(__destroy_from, __destroy_from + __n, + _M_get_Tp_allocator()); + _M_deallocate(__new_start, __len); + __throw_exception_again; + } + std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, + _M_get_Tp_allocator()); } _GLIBCXX_ASAN_ANNOTATE_REINIT; - std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, - _M_get_Tp_allocator()); _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start); this->_M_impl._M_start = __new_start; this->_M_impl._M_finish = __new_start + __size + __n; this->_M_impl._M_end_of_storage = __new_start + __len; } } } @@ -735,23 +791,23 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER (__position.base(), this->_M_impl._M_finish, __new_finish, _M_get_Tp_allocator()); } __catch(...) { std::_Destroy(__new_start, __new_finish, _M_get_Tp_allocator()); _M_deallocate(__new_start, __len); __throw_exception_again; } - _GLIBCXX_ASAN_ANNOTATE_REINIT; std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, _M_get_Tp_allocator()); + _GLIBCXX_ASAN_ANNOTATE_REINIT; _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_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; } } } Index: include/ext/alloc_traits.h =================================================================== --- include/ext/alloc_traits.h (revision 264027) +++ include/ext/alloc_traits.h (working copy) @@ -73,29 +73,32 @@ template<typename _Alloc, typename = typ template<typename _Ptr> using __is_custom_pointer = std::__and_<std::is_same<pointer, _Ptr>, std::__not_<std::is_pointer<_Ptr>>>; public: // overload construct for non-standard pointer types template<typename _Ptr, typename... _Args> static typename std::enable_if<__is_custom_pointer<_Ptr>::value>::type construct(_Alloc& __a, _Ptr __p, _Args&&... __args) + noexcept(noexcept(_Base_type::construct(__a, std::__to_address(__p), + std::forward<_Args>(__args)...))) { _Base_type::construct(__a, std::__to_address(__p), std::forward<_Args>(__args)...); } // overload destroy for non-standard pointer types template<typename _Ptr> static typename std::enable_if<__is_custom_pointer<_Ptr>::value>::type destroy(_Alloc& __a, _Ptr __p) + noexcept(noexcept(_Base_type::destroy(__a, std::__to_address(__p)))) { _Base_type::destroy(__a, std::__to_address(__p)); } static _Alloc _S_select_on_copy(const _Alloc& __a) { return _Base_type::select_on_container_copy_construction(__a); } static void _S_on_swap(_Alloc& __a, _Alloc& __b) { std::__alloc_on_swap(__a, __b); } static constexpr bool _S_propagate_on_copy_assign() { return _Base_type::propagate_on_container_copy_assignment::value; } Index: include/ext/malloc_allocator.h =================================================================== --- include/ext/malloc_allocator.h (revision 264027) +++ include/ext/malloc_allocator.h (working copy) @@ -138,25 +138,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { std::free(static_cast<void*>(__p)); } size_type max_size() const _GLIBCXX_USE_NOEXCEPT { return size_t(-1) / sizeof(_Tp); } #if __cplusplus >= 201103L template<typename _Up, typename... _Args> void construct(_Up* __p, _Args&&... __args) + noexcept(noexcept(::new((void *)__p) + _Up(std::forward<_Args>(__args)...))) { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } template<typename _Up> void - destroy(_Up* __p) { __p->~_Up(); } + destroy(_Up* __p) + noexcept(noexcept(__p->~_Up())) + { __p->~_Up(); } #else // _GLIBCXX_RESOLVE_LIB_DEFECTS // 402. wrong new expression in [some_] allocator::construct void construct(pointer __p, const _Tp& __val) { ::new((void *)__p) value_type(__val); } void destroy(pointer __p) { __p->~_Tp(); } #endif Index: include/ext/new_allocator.h =================================================================== --- include/ext/new_allocator.h (revision 264027) +++ include/ext/new_allocator.h (working copy) @@ -129,25 +129,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } size_type max_size() const _GLIBCXX_USE_NOEXCEPT { return size_t(-1) / sizeof(_Tp); } #if __cplusplus >= 201103L template<typename _Up, typename... _Args> void construct(_Up* __p, _Args&&... __args) + noexcept(noexcept(::new((void *)__p) + _Up(std::forward<_Args>(__args)...))) { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } template<typename _Up> void - destroy(_Up* __p) { __p->~_Up(); } + destroy(_Up* __p) + noexcept(noexcept( __p->~_Up())) + { __p->~_Up(); } #else // _GLIBCXX_RESOLVE_LIB_DEFECTS // 402. wrong new expression in [some_] allocator::construct void construct(pointer __p, const _Tp& __val) { ::new((void *)__p) _Tp(__val); } void destroy(pointer __p) { __p->~_Tp(); } #endif