diff mbox series

libstdc++: Add __gnu_cxx::__alloc_traits::_Destroy_static_type [PR110057]

Message ID 20241002105817.3171616-1-jwakely@redhat.com
State New
Headers show
Series libstdc++: Add __gnu_cxx::__alloc_traits::_Destroy_static_type [PR110057] | expand

Commit Message

Jonathan Wakely Oct. 2, 2024, 10:50 a.m. UTC
We could also use this in all containers (and node handles) and in
the control block created by std::allocate_shared.

To use it for containers we will need to decide whether to change the
non-standard std::_Destroy(FwdIter, FwdIter, Allocator&) to use this, or
whether to add a new _Destroy_static function and make the containers
use that instead.

For this RFC patch, I've only used it in a few places in std::vector
where a single object is created/destroyed directly, so not when
destroying multiple elements using std::_Destroy.

Tested x86_64-linux.

-- >8 --

Add a version of allocator_traits::destroy that can be used when the
static type is known, so that we can make an explicit destructor call
that avoids virtual lookup.

This is only possible when the allocator doesn't provide a destroy
member, so that we know we're going to invoke the destructor directly.
In containers like std::vector we know that we're never destroying
elements through a pointer to base, because the dynamic type is known to
be the same as the static type.

libstdc++-v3/ChangeLog:

	PR libstdc++/110057
	* include/bits/stl_vector.h: Use _Destroy_static_type instead of
	destroy.
	* include/bits/vector.tcc: Likewise.
	* include/ext/alloc_traits.h (__alloc_traits::_Destroy_static_type):
	New function template.
---
 libstdc++-v3/include/bits/stl_vector.h  |  5 ++--
 libstdc++-v3/include/bits/vector.tcc    |  3 ++-
 libstdc++-v3/include/ext/alloc_traits.h | 32 ++++++++++++++++++++++++-
 3 files changed, 36 insertions(+), 4 deletions(-)
diff mbox series

Patch

diff --git a/libstdc++-v3/include/bits/stl_vector.h b/libstdc++-v3/include/bits/stl_vector.h
index e284536ad31..f7451fa9e18 100644
--- a/libstdc++-v3/include/bits/stl_vector.h
+++ b/libstdc++-v3/include/bits/stl_vector.h
@@ -1325,7 +1325,8 @@  _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       {
 	__glibcxx_requires_nonempty();
 	--this->_M_impl._M_finish;
-	_Alloc_traits::destroy(this->_M_impl, this->_M_impl._M_finish);
+	_Alloc_traits::_Destroy_static_type(this->_M_impl,
+					    this->_M_impl._M_finish);
 	_GLIBCXX_ASAN_ANNOTATE_SHRINK(1);
       }
 
@@ -1870,7 +1871,7 @@  _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 
 	_GLIBCXX20_CONSTEXPR
 	~_Temporary_value()
-	{ _Alloc_traits::destroy(_M_this->_M_impl, _M_ptr()); }
+	{ _Alloc_traits::_Destroy_static_type(_M_this->_M_impl, _M_ptr()); }
 
 	_GLIBCXX20_CONSTEXPR value_type&
 	_M_val() noexcept { return _M_storage._M_val; }
diff --git a/libstdc++-v3/include/bits/vector.tcc b/libstdc++-v3/include/bits/vector.tcc
index a99a5b56b77..660a8b4e414 100644
--- a/libstdc++-v3/include/bits/vector.tcc
+++ b/libstdc++-v3/include/bits/vector.tcc
@@ -184,7 +184,8 @@  _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       if (__position + 1 != end())
 	_GLIBCXX_MOVE3(__position + 1, end(), __position);
       --this->_M_impl._M_finish;
-      _Alloc_traits::destroy(this->_M_impl, this->_M_impl._M_finish);
+      _Alloc_traits::_Destroy_static_type(this->_M_impl,
+					  this->_M_impl._M_finish);
       _GLIBCXX_ASAN_ANNOTATE_SHRINK(1);
       return __position;
     }
diff --git a/libstdc++-v3/include/ext/alloc_traits.h b/libstdc++-v3/include/ext/alloc_traits.h
index d2560531bac..17caaee2715 100644
--- a/libstdc++-v3/include/ext/alloc_traits.h
+++ b/libstdc++-v3/include/ext/alloc_traits.h
@@ -33,7 +33,7 @@ 
 #pragma GCC system_header
 #endif
 
-# include <bits/alloc_traits.h>
+#include <bits/alloc_traits.h>
 
 namespace __gnu_cxx _GLIBCXX_VISIBILITY(default)
 {
@@ -95,6 +95,30 @@  template<typename _Alloc, typename = typename _Alloc::value_type>
       noexcept(noexcept(_Base_type::destroy(__a, std::__to_address(__p))))
       { _Base_type::destroy(__a, std::__to_address(__p)); }
 
+    // Equivalent to `destroy` except that when `a.destroy(p)` is not valid,
+    // the destructor will be called using a qualified name, so that no
+    // dynamic dispatch to a virtual destructor is done. This can be used
+    // in e.g. std::vector where we know that the elements do not have a
+    // dynamic type that is different from the static type.
+    template<typename _Ptr>
+      [[__gnu__::__always_inline__]]
+      static _GLIBCXX20_CONSTEXPR void
+      _Destroy_static_type(_Alloc& __a, _Ptr __p)
+      {
+#if __cpp_concepts
+	auto __ptr = std::__to_address(__p);
+	if constexpr (requires { __a.destroy(__ptr); })
+	  __a.destroy(__ptr);
+	else
+	  {
+	    using _Tp = std::remove_pointer_t<decltype(__ptr)>;
+	    __ptr->_Tp::~_Tp();
+	  }
+#else
+	destroy(__a, __p);
+#endif
+      }
+
     [[__gnu__::__always_inline__]]
     static constexpr _Alloc _S_select_on_copy(const _Alloc& __a)
     { return _Base_type::select_on_container_copy_construction(__a); }
@@ -178,6 +202,12 @@  template<typename _Alloc, typename = typename _Alloc::value_type>
     template<typename _Tp>
       struct rebind
       { typedef typename _Alloc::template rebind<_Tp>::other other; };
+
+    template<typename _Ptr>
+      __attribute__((__always_inline__))
+      static void
+      _Destroy_static_type(_Alloc& __a, _Ptr __p)
+      { __a.destroy(__p); }
 #endif // C++11
   };