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

Reply via email to