Ok, I've added those changes to the attached patch. Testing in progress. Thanks, -Ben
-- 8< -- In order to emplace a value in the middle of a deque, a temporary was previously constructed directly with __args... in _M_emplace_aux. This would not work since std::deque is allocator-aware and should construct elements with _Alloc_traits::construct instead before the element is moved. Using the suggestion in PR118087, we can define _Temporary_value similar to the one used in std::vector, so the temporary can be constructed with uses-allocator construction. PR libstdc++/118087 libstdc++-v3/ChangeLog: * include/bits/deque.tcc: Use _Temporary_value in _M_emplace_aux. * include/bits/stl_deque.h: Introduce _Temporary_value. * testsuite/23_containers/deque/modifiers/emplace/118087.cc: New test. Reviewed-by: Jonathan Wakely <jwak...@redhat.com> Signed-off-by: Ben Wu <soggysocks...@gmail.com> --- libstdc++-v3/include/bits/deque.tcc | 11 ++++- libstdc++-v3/include/bits/stl_deque.h | 29 +++++++++++++ .../deque/modifiers/emplace/118087.cc | 43 +++++++++++++++++++ 3 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 libstdc++-v3/testsuite/23_containers/deque/modifiers/emplace/118087.cc diff --git a/libstdc++-v3/include/bits/deque.tcc b/libstdc++-v3/include/bits/deque.tcc index dabb6ec5365..c15b046691e 100644 --- a/libstdc++-v3/include/bits/deque.tcc +++ b/libstdc++-v3/include/bits/deque.tcc @@ -664,7 +664,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER deque<_Tp, _Alloc>:: _M_emplace_aux(iterator __pos, _Args&&... __args) { - value_type __x_copy(std::forward<_Args>(__args)...); // XXX copy + // We should construct this temporary while the deque is + // in its current state in case something in __args... + // depends on that state before shuffling elements around. + _Temporary_value __tmp(this, std::forward<_Args>(__args)...); #else typename deque<_Tp, _Alloc>::iterator deque<_Tp, _Alloc>:: @@ -695,7 +698,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER __pos = this->_M_impl._M_start + __index; _GLIBCXX_MOVE_BACKWARD3(__pos, __back2, __back1); } - *__pos = _GLIBCXX_MOVE(__x_copy); +#if __cplusplus >= 201103L + *__pos = std::move(__tmp._M_val()); +#else + *__pos = __x_copy; +#endif return __pos; } diff --git a/libstdc++-v3/include/bits/stl_deque.h b/libstdc++-v3/include/bits/stl_deque.h index 7055641ad4e..7cc711efca8 100644 --- a/libstdc++-v3/include/bits/stl_deque.h +++ b/libstdc++-v3/include/bits/stl_deque.h @@ -2163,6 +2163,35 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER iterator _M_insert_aux(iterator __pos, const value_type& __x); #else + struct _Temporary_value + { + template<typename... _Args> + _GLIBCXX20_CONSTEXPR explicit + _Temporary_value(deque* __deque, _Args&&... __args) : _M_this(__deque) + { + _Alloc_traits::construct(_M_this->_M_impl, _M_ptr(), + std::forward<_Args>(__args)...); + } + + _GLIBCXX20_CONSTEXPR + ~_Temporary_value() + { _Alloc_traits::destroy(_M_this->_M_impl, _M_ptr()); } + + _GLIBCXX20_CONSTEXPR value_type& + _M_val() noexcept { return __tmp_val; } + + private: + _GLIBCXX20_CONSTEXPR _Tp* + _M_ptr() noexcept { return std::__addressof(__tmp_val); } + + union + { + _Tp __tmp_val; + }; + + deque* _M_this; + }; + iterator _M_insert_aux(iterator __pos, const value_type& __x) { return _M_emplace_aux(__pos, __x); } diff --git a/libstdc++-v3/testsuite/23_containers/deque/modifiers/emplace/118087.cc b/libstdc++-v3/testsuite/23_containers/deque/modifiers/emplace/118087.cc new file mode 100644 index 00000000000..33b7c4cee0b --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/deque/modifiers/emplace/118087.cc @@ -0,0 +1,43 @@ +// { dg-do run { target c++11 } } + +#include <testsuite_allocator.h> +#include <testsuite_hooks.h> +#include <deque> +#include <scoped_allocator> + +template <typename T> +using Alloc = __gnu_test::propagating_allocator<T, true>; + +struct X +{ + using allocator_type = Alloc<int>; + X() { } + X(const X&) { } + X(X&&) { } + X(const allocator_type& a) : alloc(a) { } + X(const X&, const allocator_type& a) : alloc(a) { } + X(X&&, const allocator_type& a) : alloc(a) { } + + X& operator=(const X&) = default; + + allocator_type alloc{-1}; +}; + +int main() +{ + + std::deque<X, std::scoped_allocator_adaptor<Alloc<X>>> d(2, Alloc<X>(50)); + VERIFY(d[0].alloc.get_personality() == 50); + VERIFY(d[1].alloc.get_personality() == 50); + + d.emplace(d.begin() + 1); + VERIFY(d[1].alloc.get_personality() == 50); + + d.emplace_front(); + VERIFY(d[0].alloc.get_personality() == 50); + + d.emplace_back(); + VERIFY(d[d.size() - 1].alloc.get_personality() == 50); + + return 0; +} -- 2.43.0 On Fri, Sep 19, 2025 at 4:26 AM Jonathan Wakely <jwak...@redhat.com> wrote: > On Fri, 19 Sept 2025 at 11:56, Jonathan Wakely <jwak...@redhat.com> wrote: > > > > On Thu, 18 Sep 2025 at 18:52 -0700, Ben Wu wrote: > > >Bootstrapped and tested with check-target-libstdc++-v3 on > > >x86_64-pc-linux-gnu. > > > > > >Could someone help review and commit? > > > > > >For applying, please use the attached patch file since my email client > has > > >not preserved > > >the tabs. > > > > > >Thanks, > > >-Ben > > > > > >-- 8< -- > > > > > >In order to emplace a value in the middle of a deque, a temporary was > > >previously constructed directly with __args... in _M_emplace_aux. > > >This would not work since std::deque is allocator-aware and should > > >construct elements with _Alloc_traits::construct instead before the > > >element is moved. > > > > > >Using the suggestion in PR118087, we can define _Temporary_value > > >similar to the one used in std::vector, so the temporary can be > > >constructed with uses-allocator construction. > > > > > >PR libstdc++/118087 > > > > > >libstdc++-v3/ChangeLog: > > > > > >* include/bits/deque.tcc: Use _Temporary_value in > > >_M_emplace_aux. > > >* include/bits/stl_deque.h: Introduce _Temporary_value. > > >* testsuite/23_containers/deque/modifiers/emplace/118087.cc: > > >New test. > > > > > > In order to apply the patch we need to satisfy a legal prerequisite: > https://gcc.gnu.org/contribute.html#legal > If you don't have a copyright assignment for GCC on file with the FSF, > you can contribute the patch under the DCO terms: > https://gcc.gnu.org/dco.html > If you want to use the DCO could you please read the link above > carefully and if it applies to your contribution then amend the patch > or just reply to this email with the sign-off line. > >
From 6034d8c6fc08500837cbc5dba05de41498a769ec Mon Sep 17 00:00:00 2001 From: benwu25 <soggysocks...@gmail.com> Date: Fri, 19 Sep 2025 11:01:59 -0700 Subject: [PATCH] libstdc++: fix element construction in std::deque::emplace [PR118087] In order to emplace a value in the middle of a deque, a temporary was previously constructed directly with __args... in _M_emplace_aux. This would not work since std::deque is allocator-aware and should construct elements with _Alloc_traits::construct instead before the element is moved. Using the suggestion in PR118087, we can define _Temporary_value similar to the one used in std::vector, so the temporary can be constructed with uses-allocator construction. PR libstdc++/118087 libstdc++-v3/ChangeLog: * include/bits/deque.tcc: Use _Temporary_value in _M_emplace_aux. * include/bits/stl_deque.h: Introduce _Temporary_value. * testsuite/23_containers/deque/modifiers/emplace/118087.cc: New test. Reviewed-by: Jonathan Wakely <jwak...@redhat.com> Signed-off-by: Ben Wu <soggysocks...@gmail.com> --- libstdc++-v3/include/bits/deque.tcc | 11 ++++- libstdc++-v3/include/bits/stl_deque.h | 29 +++++++++++++ .../deque/modifiers/emplace/118087.cc | 43 +++++++++++++++++++ 3 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 libstdc++-v3/testsuite/23_containers/deque/modifiers/emplace/118087.cc diff --git a/libstdc++-v3/include/bits/deque.tcc b/libstdc++-v3/include/bits/deque.tcc index dabb6ec5365..c15b046691e 100644 --- a/libstdc++-v3/include/bits/deque.tcc +++ b/libstdc++-v3/include/bits/deque.tcc @@ -664,7 +664,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER deque<_Tp, _Alloc>:: _M_emplace_aux(iterator __pos, _Args&&... __args) { - value_type __x_copy(std::forward<_Args>(__args)...); // XXX copy + // We should construct this temporary while the deque is + // in its current state in case something in __args... + // depends on that state before shuffling elements around. + _Temporary_value __tmp(this, std::forward<_Args>(__args)...); #else typename deque<_Tp, _Alloc>::iterator deque<_Tp, _Alloc>:: @@ -695,7 +698,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER __pos = this->_M_impl._M_start + __index; _GLIBCXX_MOVE_BACKWARD3(__pos, __back2, __back1); } - *__pos = _GLIBCXX_MOVE(__x_copy); +#if __cplusplus >= 201103L + *__pos = std::move(__tmp._M_val()); +#else + *__pos = __x_copy; +#endif return __pos; } diff --git a/libstdc++-v3/include/bits/stl_deque.h b/libstdc++-v3/include/bits/stl_deque.h index 7055641ad4e..7cc711efca8 100644 --- a/libstdc++-v3/include/bits/stl_deque.h +++ b/libstdc++-v3/include/bits/stl_deque.h @@ -2163,6 +2163,35 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER iterator _M_insert_aux(iterator __pos, const value_type& __x); #else + struct _Temporary_value + { + template<typename... _Args> + _GLIBCXX20_CONSTEXPR explicit + _Temporary_value(deque* __deque, _Args&&... __args) : _M_this(__deque) + { + _Alloc_traits::construct(_M_this->_M_impl, _M_ptr(), + std::forward<_Args>(__args)...); + } + + _GLIBCXX20_CONSTEXPR + ~_Temporary_value() + { _Alloc_traits::destroy(_M_this->_M_impl, _M_ptr()); } + + _GLIBCXX20_CONSTEXPR value_type& + _M_val() noexcept { return __tmp_val; } + + private: + _GLIBCXX20_CONSTEXPR _Tp* + _M_ptr() noexcept { return std::__addressof(__tmp_val); } + + union + { + _Tp __tmp_val; + }; + + deque* _M_this; + }; + iterator _M_insert_aux(iterator __pos, const value_type& __x) { return _M_emplace_aux(__pos, __x); } diff --git a/libstdc++-v3/testsuite/23_containers/deque/modifiers/emplace/118087.cc b/libstdc++-v3/testsuite/23_containers/deque/modifiers/emplace/118087.cc new file mode 100644 index 00000000000..33b7c4cee0b --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/deque/modifiers/emplace/118087.cc @@ -0,0 +1,43 @@ +// { dg-do run { target c++11 } } + +#include <testsuite_allocator.h> +#include <testsuite_hooks.h> +#include <deque> +#include <scoped_allocator> + +template <typename T> +using Alloc = __gnu_test::propagating_allocator<T, true>; + +struct X +{ + using allocator_type = Alloc<int>; + X() { } + X(const X&) { } + X(X&&) { } + X(const allocator_type& a) : alloc(a) { } + X(const X&, const allocator_type& a) : alloc(a) { } + X(X&&, const allocator_type& a) : alloc(a) { } + + X& operator=(const X&) = default; + + allocator_type alloc{-1}; +}; + +int main() +{ + + std::deque<X, std::scoped_allocator_adaptor<Alloc<X>>> d(2, Alloc<X>(50)); + VERIFY(d[0].alloc.get_personality() == 50); + VERIFY(d[1].alloc.get_personality() == 50); + + d.emplace(d.begin() + 1); + VERIFY(d[1].alloc.get_personality() == 50); + + d.emplace_front(); + VERIFY(d[0].alloc.get_personality() == 50); + + d.emplace_back(); + VERIFY(d[d.size() - 1].alloc.get_personality() == 50); + + return 0; +} -- 2.43.0