[libcxx-commits] [libcxx] [libc++] Split the multidimensional algorithms out of uninitialized_algorithms.h (PR #207447)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Sat Jul 4 00:59:54 PDT 2026


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/207447

>From a9408c940b1ba59bc8bc3adcefc6b758feac59b1 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 3 Jul 2026 21:00:01 +0200
Subject: [PATCH 1/2] [libc++] Split the multidimensional algorithms out of
 uninitialized_algorithms.h

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__memory/shared_ptr.h          |   1 +
 .../__memory/uninitialized_algorithms.h       | 159 +-------------
 ...ninitialized_multidimensional_algorithms.h | 194 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   3 +
 5 files changed, 200 insertions(+), 158 deletions(-)
 create mode 100644 libcxx/include/__memory/uninitialized_multidimensional_algorithms.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index d65e66e221766..b40f586161e62 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -618,6 +618,7 @@ set(files
   __memory/temp_value.h
   __memory/temporary_buffer.h
   __memory/uninitialized_algorithms.h
+  __memory/uninitialized_multidimensional_algorithms.h
   __memory/unique_ptr.h
   __memory/unique_temporary_buffer.h
   __memory/uses_allocator.h
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index 750d253912ab1..ebd542ba14d01 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -33,6 +33,7 @@
 #include <__memory/pointer_traits.h>
 #include <__memory/shared_count.h>
 #include <__memory/uninitialized_algorithms.h>
+#include <__memory/uninitialized_multidimensional_algorithms.h>
 #include <__memory/unique_ptr.h>
 #include <__type_traits/add_reference.h>
 #include <__type_traits/conditional.h>
diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 45c8b71459eef..d4cd49a1deb6e 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -11,11 +11,10 @@
 #define _LIBCPP___MEMORY_UNINITIALIZED_ALGORITHMS_H
 
 #include <__algorithm/copy.h>
-#include <__algorithm/move.h>
+#include <__algorithm/in_out_result.h>
 #include <__algorithm/unwrap_iter.h>
 #include <__algorithm/unwrap_range.h>
 #include <__config>
-#include <__cstddef/size_t.h>
 #include <__fwd/memory.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/reverse_iterator.h>
@@ -25,15 +24,12 @@
 #include <__memory/destroy.h>
 #include <__memory/pointer_traits.h>
 #include <__type_traits/enable_if.h>
-#include <__type_traits/extent.h>
-#include <__type_traits/is_array.h>
 #include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_assignable.h>
 #include <__type_traits/is_trivially_constructible.h>
 #include <__type_traits/is_trivially_relocatable.h>
 #include <__type_traits/remove_const.h>
-#include <__type_traits/remove_extent.h>
 #include <__utility/exception_guard.h>
 #include <__utility/move.h>
 #include <__utility/pair.h>
@@ -295,159 +291,6 @@ uninitialized_move_n(_InputIterator __ifirst, _Size __n, _ForwardIterator __ofir
   return {std::move(__result.__in_), std::move(__result.__out_)};
 }
 
-// TODO: Rewrite this to iterate left to right and use reverse_iterators when calling
-// Destroys every element in the range [first, last) FROM RIGHT TO LEFT using allocator
-// destruction. If elements are themselves C-style arrays, they are recursively destroyed
-// in the same manner.
-//
-// This function assumes that destructors do not throw, and that the allocator is bound to
-// the correct type.
-template <class _Alloc,
-          class _BidirIter,
-          __enable_if_t<__has_bidirectional_iterator_category<_BidirIter>::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__allocator_destroy_multidimensional(_Alloc& __alloc, _BidirIter __first, _BidirIter __last) noexcept {
-  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
-  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _ValueType>,
-                "The allocator should already be rebound to the correct type");
-
-  if (__first == __last)
-    return;
-
-  if constexpr (is_array_v<_ValueType>) {
-    static_assert(!__is_unbounded_array_v<_ValueType>,
-                  "arrays of unbounded arrays don't exist, but if they did we would mess up here");
-
-    using _Element = remove_extent_t<_ValueType>;
-    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
-    do {
-      --__last;
-      decltype(auto) __array = *__last;
-      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + extent_v<_ValueType>);
-    } while (__last != __first);
-  } else {
-    do {
-      --__last;
-      allocator_traits<_Alloc>::destroy(__alloc, std::addressof(*__last));
-    } while (__last != __first);
-  }
-}
-
-// Constructs the object at the given location using the allocator's construct method.
-//
-// If the object being constructed is an array, each element of the array is allocator-constructed,
-// recursively. If an exception is thrown during the construction of an array, the initialized
-// elements are destroyed in reverse order of initialization using allocator destruction.
-//
-// This function assumes that the allocator is bound to the correct type.
-template <class _Alloc, class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc) {
-  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
-                "The allocator should already be rebound to the correct type");
-
-  if constexpr (is_array_v<_Tp>) {
-    using _Element = remove_extent_t<_Tp>;
-    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
-    size_t __i   = 0;
-    _Tp& __array = *__loc;
-
-    // If an exception is thrown, destroy what we have constructed so far in reverse order.
-    auto __guard = std::__make_exception_guard([&]() {
-      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
-    });
-
-    for (; __i != extent_v<_Tp>; ++__i) {
-      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]));
-    }
-    __guard.__complete();
-  } else {
-    allocator_traits<_Alloc>::construct(__alloc, __loc);
-  }
-}
-
-// Constructs the object at the given location using the allocator's construct method, passing along
-// the provided argument.
-//
-// If the object being constructed is an array, the argument is also assumed to be an array. Each
-// each element of the array being constructed is allocator-constructed from the corresponding
-// element of the argument array. If an exception is thrown during the construction of an array,
-// the initialized elements are destroyed in reverse order of initialization using allocator
-// destruction.
-//
-// This function assumes that the allocator is bound to the correct type.
-template <class _Alloc, class _Tp, class _Arg>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc, _Arg const& __arg) {
-  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
-                "The allocator should already be rebound to the correct type");
-
-  if constexpr (is_array_v<_Tp>) {
-    static_assert(is_array_v<_Arg>,
-                  "Provided non-array initialization argument to __allocator_construct_at_multidimensional when "
-                  "trying to construct an array.");
-
-    using _Element = remove_extent_t<_Tp>;
-    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
-    size_t __i   = 0;
-    _Tp& __array = *__loc;
-
-    // If an exception is thrown, destroy what we have constructed so far in reverse order.
-    auto __guard = std::__make_exception_guard([&]() {
-      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
-    });
-    for (; __i != extent_v<_Tp>; ++__i) {
-      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]), __arg[__i]);
-    }
-    __guard.__complete();
-  } else {
-    allocator_traits<_Alloc>::construct(__alloc, __loc, __arg);
-  }
-}
-
-// Given a range starting at it and containing n elements, initializes each element in the
-// range from left to right using the construct method of the allocator (rebound to the
-// correct type).
-//
-// If an exception is thrown, the initialized elements are destroyed in reverse order of
-// initialization using allocator_traits destruction. If the elements in the range are C-style
-// arrays, they are initialized element-wise using allocator construction, and recursively so.
-template <class _Alloc,
-          class _BidirIter,
-          class _Tp,
-          class _Size = typename iterator_traits<_BidirIter>::difference_type>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__uninitialized_allocator_fill_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n, _Tp const& __value) {
-  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
-  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
-  _BidirIter __begin = __it;
-
-  // If an exception is thrown, destroy what we have constructed so far in reverse order.
-  auto __guard =
-      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
-  for (; __n != 0; --__n, ++__it) {
-    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it), __value);
-  }
-  __guard.__complete();
-}
-
-// Same as __uninitialized_allocator_fill_n_multidimensional, but doesn't pass any initialization argument
-// to the allocator's construct method, which results in value initialization.
-template <class _Alloc, class _BidirIter, class _Size = typename iterator_traits<_BidirIter>::difference_type>
-_LIBCPP_HIDE_FROM_ABI constexpr void
-__uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n) {
-  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
-  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
-  _BidirIter __begin = __it;
-
-  // If an exception is thrown, destroy what we have constructed so far in reverse order.
-  auto __guard =
-      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
-  for (; __n != 0; --__n, ++__it) {
-    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it));
-  }
-  __guard.__complete();
-}
-
 #endif // _LIBCPP_STD_VER >= 17
 
 template <class _Alloc, class _Iter>
diff --git a/libcxx/include/__memory/uninitialized_multidimensional_algorithms.h b/libcxx/include/__memory/uninitialized_multidimensional_algorithms.h
new file mode 100644
index 0000000000000..2496ce6af1e15
--- /dev/null
+++ b/libcxx/include/__memory/uninitialized_multidimensional_algorithms.h
@@ -0,0 +1,194 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
+#define _LIBCPP___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
+
+#include <__config>
+#include <__cstddef/size_t.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__memory/allocator_traits.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/extent.h>
+#include <__type_traits/is_array.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/remove_extent.h>
+#include <__utility/exception_guard.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// TODO: Rewrite this to iterate left to right and use reverse_iterators when calling
+// Destroys every element in the range [first, last) FROM RIGHT TO LEFT using allocator
+// destruction. If elements are themselves C-style arrays, they are recursively destroyed
+// in the same manner.
+//
+// This function assumes that destructors do not throw, and that the allocator is bound to
+// the correct type.
+template <class _Alloc,
+          class _BidirIter,
+          __enable_if_t<__has_bidirectional_iterator_category<_BidirIter>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__allocator_destroy_multidimensional(_Alloc& __alloc, _BidirIter __first, _BidirIter __last) noexcept {
+  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _ValueType>,
+                "The allocator should already be rebound to the correct type");
+
+  if (__first == __last)
+    return;
+
+  if constexpr (is_array_v<_ValueType>) {
+    static_assert(!__is_unbounded_array_v<_ValueType>,
+                  "arrays of unbounded arrays don't exist, but if they did we would mess up here");
+
+    using _Element = remove_extent_t<_ValueType>;
+    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+    do {
+      --__last;
+      decltype(auto) __array = *__last;
+      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + extent_v<_ValueType>);
+    } while (__last != __first);
+  } else {
+    do {
+      --__last;
+      allocator_traits<_Alloc>::destroy(__alloc, std::addressof(*__last));
+    } while (__last != __first);
+  }
+}
+
+// Constructs the object at the given location using the allocator's construct method.
+//
+// If the object being constructed is an array, each element of the array is allocator-constructed,
+// recursively. If an exception is thrown during the construction of an array, the initialized
+// elements are destroyed in reverse order of initialization using allocator destruction.
+//
+// This function assumes that the allocator is bound to the correct type.
+template <class _Alloc, class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc) {
+  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
+                "The allocator should already be rebound to the correct type");
+
+  if constexpr (is_array_v<_Tp>) {
+    using _Element = remove_extent_t<_Tp>;
+    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+    size_t __i   = 0;
+    _Tp& __array = *__loc;
+
+    // If an exception is thrown, destroy what we have constructed so far in reverse order.
+    auto __guard = std::__make_exception_guard([&]() {
+      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
+    });
+
+    for (; __i != extent_v<_Tp>; ++__i) {
+      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]));
+    }
+    __guard.__complete();
+  } else {
+    allocator_traits<_Alloc>::construct(__alloc, __loc);
+  }
+}
+
+// Constructs the object at the given location using the allocator's construct method, passing along
+// the provided argument.
+//
+// If the object being constructed is an array, the argument is also assumed to be an array. Each
+// each element of the array being constructed is allocator-constructed from the corresponding
+// element of the argument array. If an exception is thrown during the construction of an array,
+// the initialized elements are destroyed in reverse order of initialization using allocator
+// destruction.
+//
+// This function assumes that the allocator is bound to the correct type.
+template <class _Alloc, class _Tp, class _Arg>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__allocator_construct_at_multidimensional(_Alloc& __alloc, _Tp* __loc, _Arg const& __arg) {
+  static_assert(is_same_v<typename allocator_traits<_Alloc>::value_type, _Tp>,
+                "The allocator should already be rebound to the correct type");
+
+  if constexpr (is_array_v<_Tp>) {
+    static_assert(is_array_v<_Arg>,
+                  "Provided non-array initialization argument to __allocator_construct_at_multidimensional when "
+                  "trying to construct an array.");
+
+    using _Element = remove_extent_t<_Tp>;
+    __allocator_traits_rebind_t<_Alloc, _Element> __elem_alloc(__alloc);
+    size_t __i   = 0;
+    _Tp& __array = *__loc;
+
+    // If an exception is thrown, destroy what we have constructed so far in reverse order.
+    auto __guard = std::__make_exception_guard([&]() {
+      std::__allocator_destroy_multidimensional(__elem_alloc, __array, __array + __i);
+    });
+    for (; __i != extent_v<_Tp>; ++__i) {
+      std::__allocator_construct_at_multidimensional(__elem_alloc, std::addressof(__array[__i]), __arg[__i]);
+    }
+    __guard.__complete();
+  } else {
+    allocator_traits<_Alloc>::construct(__alloc, __loc, __arg);
+  }
+}
+
+// Given a range starting at it and containing n elements, initializes each element in the
+// range from left to right using the construct method of the allocator (rebound to the
+// correct type).
+//
+// If an exception is thrown, the initialized elements are destroyed in reverse order of
+// initialization using allocator_traits destruction. If the elements in the range are C-style
+// arrays, they are initialized element-wise using allocator construction, and recursively so.
+template <class _Alloc,
+          class _BidirIter,
+          class _Tp,
+          class _Size = typename iterator_traits<_BidirIter>::difference_type>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__uninitialized_allocator_fill_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n, _Tp const& __value) {
+  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
+  _BidirIter __begin = __it;
+
+  // If an exception is thrown, destroy what we have constructed so far in reverse order.
+  auto __guard =
+      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
+  for (; __n != 0; --__n, ++__it) {
+    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it), __value);
+  }
+  __guard.__complete();
+}
+
+// Same as __uninitialized_allocator_fill_n_multidimensional, but doesn't pass any initialization argument
+// to the allocator's construct method, which results in value initialization.
+template <class _Alloc, class _BidirIter, class _Size = typename iterator_traits<_BidirIter>::difference_type>
+_LIBCPP_HIDE_FROM_ABI constexpr void
+__uninitialized_allocator_value_construct_n_multidimensional(_Alloc& __alloc, _BidirIter __it, _Size __n) {
+  using _ValueType = typename iterator_traits<_BidirIter>::value_type;
+  __allocator_traits_rebind_t<_Alloc, _ValueType> __value_alloc(__alloc);
+  _BidirIter __begin = __it;
+
+  // If an exception is thrown, destroy what we have constructed so far in reverse order.
+  auto __guard =
+      std::__make_exception_guard([&]() { std::__allocator_destroy_multidimensional(__value_alloc, __begin, __it); });
+  for (; __n != 0; --__n, ++__it) {
+    std::__allocator_construct_at_multidimensional(__value_alloc, std::addressof(*__it));
+  }
+  __guard.__complete();
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___MEMORY_UNINITIALIZED_MULTIDIMENSIONAL_ALGORITHMS_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 3efceae54599d..fc987f7bab93d 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1702,6 +1702,9 @@ module std {
       export std.memory.destroy
       export std.utility.pair
     }
+    module uninitialized_multidimensional_algorithms {
+      header "__memory/uninitialized_algorithms.h"
+    }
     module unique_ptr {
       header "__memory/unique_ptr.h"
       export std.functional.hash

>From c3a3e713dee8ba2e32a918c86f2a434e6a7f0b57 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 3 Jul 2026 21:56:01 +0200
Subject: [PATCH 2/2] [libc++] Optimize {ranges::,}uninitialized_copy

This essentially moves the `memmove` optimization from
`__uninitialized_allocator_copy` to `__uninitialized_copy` and makes use
of the latter in the allocator version.
---
 .../ranges_uninitialized_algorithms.h         | 19 +++-
 .../__memory/uninitialized_algorithms.h       | 97 +++++++++++--------
 libcxx/include/module.modulemap.in            |  2 +-
 3 files changed, 75 insertions(+), 43 deletions(-)

diff --git a/libcxx/include/__memory/ranges_uninitialized_algorithms.h b/libcxx/include/__memory/ranges_uninitialized_algorithms.h
index 796e6175af632..201d778056679 100644
--- a/libcxx/include/__memory/ranges_uninitialized_algorithms.h
+++ b/libcxx/include/__memory/ranges_uninitialized_algorithms.h
@@ -11,6 +11,7 @@
 #define _LIBCPP___MEMORY_RANGES_UNINITIALIZED_ALGORITHMS_H
 
 #include <__algorithm/in_out_result.h>
+#include <__algorithm/min.h>
 #include <__concepts/constructible.h>
 #include <__config>
 #include <__iterator/concepts.h>
@@ -18,12 +19,14 @@
 #include <__iterator/iter_move.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/readable_traits.h>
+#include <__memory/addressof.h>
 #include <__memory/concepts.h>
 #include <__memory/uninitialized_algorithms.h>
 #include <__ranges/access.h>
 #include <__ranges/concepts.h>
 #include <__ranges/dangling.h>
 #include <__type_traits/remove_reference.h>
+#include <__utility/exception_guard.h>
 #include <__utility/move.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -171,9 +174,19 @@ struct __uninitialized_copy {
   operator()(_InputIterator __ifirst, _Sentinel1 __ilast, _OutputIterator __ofirst, _Sentinel2 __olast) const {
     using _ValueType = remove_reference_t<iter_reference_t<_OutputIterator>>;
 
-    auto __stop_copying = [&__olast](auto&& __out_iter) -> bool { return __out_iter == __olast; };
-    return std::__uninitialized_copy<_ValueType>(
-        std::move(__ifirst), std::move(__ilast), std::move(__ofirst), __stop_copying);
+    if constexpr (sized_sentinel_for<_Sentinel1, _InputIterator> && random_access_iterator<_InputIterator> &&
+                  sized_sentinel_for<_Sentinel2, _OutputIterator>) {
+      auto __len = std::min(__ilast - __ifirst, __olast - __ofirst);
+      return std::__uninitialized_copy<_ValueType>(__ifirst, __ifirst + __len, __ofirst);
+    } else {
+      _OutputIterator __idx = __ofirst;
+      auto __guard          = std::__make_exception_guard([&] { std::__destroy(__ofirst, __idx); });
+      for (; __ifirst != __ilast && __idx != __olast; ++__ifirst, (void)++__idx)
+        ::new (static_cast<void*>(std::addressof(*__idx))) _ValueType(*__ifirst);
+      __guard.__complete();
+
+      return {std::move(__ifirst), std::move(__idx)};
+    }
   }
 
   template <input_range _InputRange, __nothrow_forward_range _OutputRange>
diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index d4cd49a1deb6e..f5ebe847f4054 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -10,7 +10,6 @@
 #ifndef _LIBCPP___MEMORY_UNINITIALIZED_ALGORITHMS_H
 #define _LIBCPP___MEMORY_UNINITIALIZED_ALGORITHMS_H
 
-#include <__algorithm/copy.h>
 #include <__algorithm/in_out_result.h>
 #include <__algorithm/unwrap_iter.h>
 #include <__algorithm/unwrap_range.h>
@@ -23,12 +22,15 @@
 #include <__memory/construct_at.h>
 #include <__memory/destroy.h>
 #include <__memory/pointer_traits.h>
+#include <__string/constexpr_c_functions.h>
 #include <__type_traits/enable_if.h>
+#include <__type_traits/is_always_bitcastable.h>
 #include <__type_traits/is_constant_evaluated.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_assignable.h>
 #include <__type_traits/is_trivially_constructible.h>
 #include <__type_traits/is_trivially_relocatable.h>
+#include <__type_traits/is_volatile.h>
 #include <__type_traits/remove_const.h>
 #include <__utility/exception_guard.h>
 #include <__utility/move.h>
@@ -52,25 +54,47 @@ struct __always_false {
 
 // uninitialized_copy
 
-template <class _ValueType, class _InputIterator, class _Sentinel1, class _ForwardIterator, class _EndPredicate>
+template <class _ValueType, class _InputIterator, class _Sentinel1, class _ForwardIterator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __in_out_result<_InputIterator, _ForwardIterator>
-__uninitialized_copy(
-    _InputIterator __ifirst, _Sentinel1 __ilast, _ForwardIterator __ofirst, _EndPredicate __stop_copying) {
+__uninitialized_copy(_InputIterator __ifirst, _Sentinel1 __ilast, _ForwardIterator __ofirst) {
   _ForwardIterator __idx = __ofirst;
   auto __guard           = std::__make_exception_guard([&] { std::__destroy(__ofirst, __idx); });
-  for (; __ifirst != __ilast && !__stop_copying(__idx); ++__ifirst, (void)++__idx)
+  for (; __ifirst != __ilast; ++__ifirst, (void)++__idx)
     ::new (static_cast<void*>(std::addressof(*__idx))) _ValueType(*__ifirst);
   __guard.__complete();
 
   return {std::move(__ifirst), std::move(__idx)};
 }
 
+template <
+    class _ValueType,
+    class _In,
+    class _Out,
+    __enable_if_t<is_trivially_constructible<_Out, const _In&>::value && __is_always_bitcastable<_In, _Out>::value &&
+                      !is_volatile<_In>::value && !is_volatile<_Out>::value,
+                  int> = 0>
+_LIBCPP_CONSTEXPR_SINCE_CXX20 __in_out_result<_In*, _Out*>
+__uninitialized_copy(_In* __first, _In* __last, _Out* __result) {
+  if (__libcpp_is_constant_evaluated()) {
+    while (__first != __last) {
+      std::__construct_at(__result, *__first);
+      ++__first;
+      ++__result;
+    }
+    return {__first, __result};
+  } else {
+    // __constexpr_memmove only supports constant evaluation assignment, not construction
+    auto __count = __last - __first;
+    std::__constexpr_memmove(__result, __first, __element_count(__count));
+    return {__last, __result + __count};
+  }
+}
+
 template <class _InputIterator, class _ForwardIterator>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _ForwardIterator
 uninitialized_copy(_InputIterator __ifirst, _InputIterator __ilast, _ForwardIterator __ofirst) {
-  typedef typename iterator_traits<_ForwardIterator>::value_type _ValueType;
-  auto __result = std::__uninitialized_copy<_ValueType>(
-      std::move(__ifirst), std::move(__ilast), std::move(__ofirst), __always_false());
+  auto __result = std::__uninitialized_copy<__iterator_value_type<_ForwardIterator>>(
+      std::move(__ifirst), std::move(__ilast), std::move(__ofirst));
   return std::move(__result.__out_);
 }
 
@@ -310,16 +334,26 @@ class _AllocatorDestroyRangeReverse {
   _Iter& __last_;
 };
 
+template <class _Alloc, class _Type>
+inline const bool __allocator_has_trivial_copy_construct_v = !__has_construct_v<_Alloc, _Type*, const _Type&>;
+
+template <class _Type>
+inline const bool __allocator_has_trivial_copy_construct_v<allocator<_Type>, _Type> = true;
+
 // Copy-construct [__first1, __last1) in [__first2, __first2 + N), where N is distance(__first1, __last1).
 //
 // The caller has to ensure that __first2 can hold at least N uninitialized elements. If an exception is thrown the
 // already copied elements are destroyed in reverse order of their construction.
-template <class _Alloc, class _Iter1, class _Sent1, class _Iter2>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter2
-__uninitialized_allocator_copy_impl(_Alloc& __alloc, _Iter1 __first1, _Sent1 __last1, _Iter2 __first2) {
+template <class _Alloc,
+          class _InIter,
+          class _Sent,
+          class _OutIter,
+          __enable_if_t<!__allocator_has_trivial_copy_construct_v<_Alloc, __iterator_value_type<_InIter> >, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutIter
+__uninitialized_allocator_copy_impl(_Alloc& __alloc, _InIter __first1, _Sent __last1, _OutIter __first2) {
   auto __destruct_first = __first2;
   auto __guard =
-      std::__make_exception_guard(_AllocatorDestroyRangeReverse<_Alloc, _Iter2>(__alloc, __destruct_first, __first2));
+      std::__make_exception_guard(_AllocatorDestroyRangeReverse<_Alloc, _OutIter>(__alloc, __destruct_first, __first2));
   while (__first1 != __last1) {
     allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__first2), *__first1);
     ++__first1;
@@ -329,36 +363,21 @@ __uninitialized_allocator_copy_impl(_Alloc& __alloc, _Iter1 __first1, _Sent1 __l
   return __first2;
 }
 
-template <class _Alloc, class _Type>
-inline const bool __allocator_has_trivial_copy_construct_v = !__has_construct_v<_Alloc, _Type*, const _Type&>;
-
-template <class _Type>
-inline const bool __allocator_has_trivial_copy_construct_v<allocator<_Type>, _Type> = true;
-
 template <class _Alloc,
-          class _In,
-          class _Out,
-          __enable_if_t<is_trivially_copy_constructible<_In>::value && is_trivially_assignable<_Out&, _In&>::value &&
-                            is_same<__remove_const_t<_In>, __remove_const_t<_Out> >::value &&
-                            __allocator_has_trivial_copy_construct_v<_Alloc, _In>,
-                        int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Out*
-__uninitialized_allocator_copy_impl(_Alloc&, _In* __first1, _In* __last1, _Out* __first2) {
-  if (__libcpp_is_constant_evaluated()) {
-    while (__first1 != __last1) {
-      std::__construct_at(std::__to_address(__first2), *__first1);
-      ++__first1;
-      ++__first2;
-    }
-    return __first2;
-  } else {
-    return std::copy(__first1, __last1, __first2);
-  }
+          class _InIter,
+          class _Sent,
+          class _OutIter,
+          __enable_if_t<__allocator_has_trivial_copy_construct_v<_Alloc, __iterator_value_type<_OutIter> >, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutIter
+__uninitialized_allocator_copy_impl(_Alloc&, _InIter __first, _Sent __last, _OutIter __result) {
+  return std::__uninitialized_copy<__iterator_value_type<_OutIter>>(
+             std::move(__first), std::move(__last), std::move(__result))
+      .__out_;
 }
 
-template <class _Alloc, class _Iter1, class _Sent1, class _Iter2>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter2
-__uninitialized_allocator_copy(_Alloc& __alloc, _Iter1 __first1, _Sent1 __last1, _Iter2 __first2) {
+template <class _Alloc, class _InIter, class _Sent, class _OutIter>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutIter
+__uninitialized_allocator_copy(_Alloc& __alloc, _InIter __first1, _Sent __last1, _OutIter __first2) {
   auto __unwrapped_range = std::__unwrap_range(std::move(__first1), std::move(__last1));
   auto __result          = std::__uninitialized_allocator_copy_impl(
       __alloc, std::move(__unwrapped_range.first), std::move(__unwrapped_range.second), std::__unwrap_iter(__first2));
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index fc987f7bab93d..f4cde849a8773 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1703,7 +1703,7 @@ module std {
       export std.utility.pair
     }
     module uninitialized_multidimensional_algorithms {
-      header "__memory/uninitialized_algorithms.h"
+      header "__memory/uninitialized_multidimensional_algorithms.h"
     }
     module unique_ptr {
       header "__memory/unique_ptr.h"



More information about the libcxx-commits mailing list