[libcxx-commits] [libcxx] [libc++] Add __uninitialized_allocator_{fill, value_construct, move} and use them in vector (PR #207500)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Wed Jul 22 07:52:01 PDT 2026


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/207500

>From 9dde19ee8b7ad4dce0f74550288727526285fdfb Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sat, 4 Jul 2026 11:06:55 +0200
Subject: [PATCH] [libc++] Add
 __uninitialized_allocator_{fill,value_construct,move} and use them in vector

---
 libcxx/docs/ReleaseNotes/23.rst               |  3 ++
 .../__memory/uninitialized_algorithms.h       | 38 +++++++++++++++++++
 libcxx/include/__vector/vector.h              | 14 ++-----
 .../sequences/vector/asan_throw.pass.cpp      |  2 +-
 4 files changed, 45 insertions(+), 12 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index f7a8e5b04f1ea..27c68b93506ce 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -101,6 +101,9 @@ Potentially breaking changes
 - ``std::exception_ptr`` on Windows no longer relies on STL and is now implemented entirely within libc++.
   The implementation was donated by Microsoft and has been refactored to follow libc++ standards and conventions.
 
+- The destruction order as well as whether elements are destructed in ``vector`` when appending has changed.
+  Specifically, when an exception is thrown, the appended elements are now destructed from first to last.
+
 Announcements About Future Releases
 -----------------------------------
 
diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index f184b2b9dd925..baca90fb10554 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -302,6 +302,30 @@ uninitialized_move_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofir
 
 #endif // _LIBCPP_STD_VER >= 17
 
+template <class _Alloc, class _Iter, class _Sent, class _Tp>
+_LIBCPP_CONSTEXPR_SINCE_CXX20
+_Iter __uninitialized_allocator_fill(_Alloc& __alloc, _Iter __first, _Sent __last, const _Tp& __value) {
+  auto __iter = __first;
+
+  auto __guard = std::__make_exception_guard([&] { std::__allocator_destroy(__alloc, __first, __iter); });
+  for (; __iter != __last; ++__iter)
+    allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__iter), __value);
+  __guard.__complete();
+  return __iter;
+}
+
+template <class _Alloc, class _Iter, class _Sent>
+_LIBCPP_CONSTEXPR_SINCE_CXX20
+_Iter __uninitialized_allocator_value_construct(_Alloc& __alloc, _Iter __first, _Sent __last) {
+  auto __iter = __first;
+
+  auto __guard = std::__make_exception_guard([&] { std::__allocator_destroy(__alloc, __first, __iter); });
+  for (; __iter != __last; ++__iter)
+    allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__iter));
+  __guard.__complete();
+  return __iter;
+}
+
 template <class _Alloc, class _Iter>
 class _AllocatorDestroyRangeReverse {
 public:
@@ -380,6 +404,20 @@ inline const bool __allocator_has_trivial_move_construct_v = !__has_construct_v<
 template <class _Type>
 inline const bool __allocator_has_trivial_move_construct_v<allocator<_Type>, _Type> = true;
 
+template <class _Alloc, class _InIter, class _Sent, class _OutIter>
+_LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_InIter, _OutIter>
+__uninitialized_allocator_move(_Alloc& __alloc, _InIter __first, _Sent __last, _OutIter __result) {
+  auto __destruct_first = __result;
+  auto __guard = std::__make_exception_guard([&] { std::__allocator_destroy(__alloc, __destruct_first, __result); });
+  while (__first != __last) {
+    allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__result), std::move(*__first));
+    ++__first;
+    ++__result;
+  }
+  __guard.__complete();
+  return {std::move(__first), std::move(__result)};
+}
+
 template <class _Alloc, class _Tp>
 inline const bool __allocator_has_trivial_destroy_v = !__has_destroy_v<_Alloc, _Tp*>;
 
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 8226a7f87a119..6f700440ae321 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -878,10 +878,7 @@ vector<_Tp, _Allocator>::__recommend(size_type __new_size) const {
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<_Tp, _Allocator>::__construct_at_end(size_type __n) {
   _ConstructTransaction __tx(*this, __n);
-  const_pointer __new_end = __tx.__new_end_;
-  for (pointer __pos = __tx.__pos_; __pos != __new_end; __tx.__pos_ = ++__pos) {
-    __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos));
-  }
+  __tx.__pos_ = std::__uninitialized_allocator_value_construct(__layout_.__alloc(), __tx.__pos_, __tx.__new_end_);
 }
 
 //  Copy constructs __n objects starting at __layout_.__end_ptr() from __x
@@ -894,10 +891,7 @@ template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 inline void
 vector<_Tp, _Allocator>::__construct_at_end(size_type __n, const_reference __x) {
   _ConstructTransaction __tx(*this, __n);
-  const_pointer __new_end = __tx.__new_end_;
-  for (pointer __pos = __tx.__pos_; __pos != __new_end; __tx.__pos_ = ++__pos) {
-    __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos), __x);
-  }
+  __tx.__pos_ = std::__uninitialized_allocator_fill(__layout_.__alloc(), __tx.__pos_, __tx.__new_end_, __x);
 }
 
 template <class _Tp, class _Allocator>
@@ -1121,9 +1115,7 @@ vector<_Tp, _Allocator>::__move_range(pointer __from_s, pointer __from_e, pointe
   {
     pointer __i = __from_s + __n;
     _ConstructTransaction __tx(*this, __from_e - __i);
-    for (pointer __pos = __tx.__pos_; __i < __from_e; ++__i, (void)++__pos, __tx.__pos_ = __pos) {
-      __alloc_traits::construct(this->__layout_.__alloc(), std::__to_address(__pos), std::move(*__i));
-    }
+    __tx.__pos_ = std::__uninitialized_allocator_move(__layout_.__alloc(), __i, __from_e, __tx.__pos_).__out_;
   }
   std::move_backward(__from_s, __from_s + __n, __old_last);
 }
diff --git a/libcxx/test/libcxx/containers/sequences/vector/asan_throw.pass.cpp b/libcxx/test/libcxx/containers/sequences/vector/asan_throw.pass.cpp
index dcfa8029cfc0d..5089cd716266a 100644
--- a/libcxx/test/libcxx/containers/sequences/vector/asan_throw.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/vector/asan_throw.pass.cpp
@@ -182,7 +182,7 @@ void test_insert_n2() {
     v.insert(v.cbegin(), 5, ThrowOnCopy());
     assert(0);
   } catch (int e) {
-    assert(v.size() == 11);
+    assert(v.size() == 10);
     assert(is_contiguous_container_asan_correct(v));
     return;
   }



More information about the libcxx-commits mailing list