[libcxx-commits] [libcxx] [libc++] P3369R0: constexpr for `uninitialized_default_construct` (PR #200163)

A. Jiang via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jun 12 04:27:50 PDT 2026


https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/200163

>From 84cac5669dcc13f04aac12d509fe6599a2468441 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Thu, 28 May 2026 19:29:22 +0800
Subject: [PATCH 1/2] [libc++] P3369R0: constexpr for
 `uninitialized_default_construct`

Remarks:
- Tests also verify that `uninitialized_default_construct(_n)`
algorithms do not initialize trivially default-constructible elements
(`int` in these tests) to determined values during constant evaluation.
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +
 libcxx/docs/ReleaseNotes/23.rst               |   1 +
 libcxx/docs/Status/Cxx2cPapers.csv            |   2 +-
 .../ranges_uninitialized_algorithms.h         |   8 +-
 .../__memory/uninitialized_algorithms.h       |  11 +-
 libcxx/include/version                        |   5 +-
 .../memory.version.compile.pass.cpp           |   4 +-
 .../version.version.compile.pass.cpp          |   4 +-
 ...s_uninitialized_default_construct.pass.cpp | 179 ++++++++++++++++++
 ...uninitialized_default_construct_n.pass.cpp | 109 +++++++++++
 .../uninitialized_default_construct.pass.cpp  | 109 ++++++++++-
 ...uninitialized_default_construct_n.pass.cpp | 111 ++++++++++-
 .../generate_feature_test_macro_components.py |   2 +-
 13 files changed, 531 insertions(+), 16 deletions(-)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index ae48eaed1f46b..1ea8f55dba5eb 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -512,6 +512,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ratio``                                        ``202306L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_raw_memory_algorithms``                        ``202411L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_rcu``                                          *unimplemented*
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_reference_wrapper``                            ``202403L``
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 843f25dc7708a..a76524573fdae 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -49,6 +49,7 @@ Implemented Papers
 - P2322R6: ``ranges::fold`` (`Github <https://llvm.org/PR105208>`__)
 - P4144R1: Remove ``span``'s ``initializer_list`` constructor for C++26 (`Github <https://llvm.org/PR189612>`__)
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
+- P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
 
 Improvements and New Features
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 5b229c75cc8c0..f605a6de8f0e4 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -88,7 +88,7 @@
 "`P2835R7 <https://wg21.link/P2835R7>`__","Expose ``std::atomic_ref``'s object address","2024-11 (Wrocław)","|Complete|","22","`#118377 <https://github.com/llvm/llvm-project/issues/118377>`__",""
 "`P3323R1 <https://wg21.link/P3323R1>`__","cv-qualified types in ``atomic`` and ``atomic_ref``","2024-11 (Wrocław)","","","`#118378 <https://github.com/llvm/llvm-project/issues/118378>`__",""
 "`P3508R0 <https://wg21.link/P3508R0>`__","Wording for ""constexpr for specialized memory algorithms""","2024-11 (Wrocław)","|Complete|","23","`#118379 <https://github.com/llvm/llvm-project/issues/118379>`__",""
-"`P3369R0 <https://wg21.link/P3369R0>`__","constexpr for ``uninitialized_default_construct``","2024-11 (Wrocław)","","","`#118380 <https://github.com/llvm/llvm-project/issues/118380>`__",""
+"`P3369R0 <https://wg21.link/P3369R0>`__","constexpr for ``uninitialized_default_construct``","2024-11 (Wrocław)","|Complete|","23","`#118380 <https://github.com/llvm/llvm-project/issues/118380>`__",""
 "`P3370R1 <https://wg21.link/P3370R1>`__","Add new library headers from C23","2024-11 (Wrocław)","","","`#118381 <https://github.com/llvm/llvm-project/issues/118381>`__",""
 "`P3309R3 <https://wg21.link/P3309R3>`__","constexpr ``atomic`` and ``atomic_ref``","2024-11 (Wrocław)","","","`#118382 <https://github.com/llvm/llvm-project/issues/118382>`__",""
 "`P1928R15 <https://wg21.link/P1928R15>`__","``std::simd`` — merge data-parallel types from the Parallelism TS 2","2024-11 (Wrocław)","","","`#118384 <https://github.com/llvm/llvm-project/issues/118384>`__",""
diff --git a/libcxx/include/__memory/ranges_uninitialized_algorithms.h b/libcxx/include/__memory/ranges_uninitialized_algorithms.h
index 6cfc3924d53b5..796e6175af632 100644
--- a/libcxx/include/__memory/ranges_uninitialized_algorithms.h
+++ b/libcxx/include/__memory/ranges_uninitialized_algorithms.h
@@ -44,14 +44,16 @@ namespace ranges {
 struct __uninitialized_default_construct {
   template <__nothrow_forward_iterator _ForwardIterator, __nothrow_sentinel_for<_ForwardIterator> _Sentinel>
     requires default_initializable<iter_value_t<_ForwardIterator>>
-  _LIBCPP_HIDE_FROM_ABI _ForwardIterator operator()(_ForwardIterator __first, _Sentinel __last) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _ForwardIterator
+  operator()(_ForwardIterator __first, _Sentinel __last) const {
     using _ValueType = remove_reference_t<iter_reference_t<_ForwardIterator>>;
     return std::__uninitialized_default_construct<_ValueType>(std::move(__first), std::move(__last));
   }
 
   template <__nothrow_forward_range _ForwardRange>
     requires default_initializable<range_value_t<_ForwardRange>>
-  _LIBCPP_HIDE_FROM_ABI borrowed_iterator_t<_ForwardRange> operator()(_ForwardRange&& __range) const {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 borrowed_iterator_t<_ForwardRange>
+  operator()(_ForwardRange&& __range) const {
     return (*this)(ranges::begin(__range), ranges::end(__range));
   }
 };
@@ -65,7 +67,7 @@ inline constexpr auto uninitialized_default_construct = __uninitialized_default_
 struct __uninitialized_default_construct_n {
   template <__nothrow_forward_iterator _ForwardIterator>
     requires default_initializable<iter_value_t<_ForwardIterator>>
-  _LIBCPP_HIDE_FROM_ABI _ForwardIterator
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _ForwardIterator
   operator()(_ForwardIterator __first, iter_difference_t<_ForwardIterator> __n) const {
     using _ValueType = remove_reference_t<iter_reference_t<_ForwardIterator>>;
     return std::__uninitialized_default_construct_n<_ValueType>(std::move(__first), __n);
diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 329b3df45dd70..45c8b71459eef 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -148,7 +148,7 @@ uninitialized_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x) {
 // uninitialized_default_construct
 
 template <class _ValueType, class _ForwardIterator, class _Sentinel>
-inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _ForwardIterator
 __uninitialized_default_construct(_ForwardIterator __first, _Sentinel __last) {
   auto __idx   = __first;
   auto __guard = std::__make_exception_guard([&] { std::__destroy(__first, __idx); });
@@ -160,7 +160,8 @@ __uninitialized_default_construct(_ForwardIterator __first, _Sentinel __last) {
 }
 
 template <class _ForwardIterator>
-inline _LIBCPP_HIDE_FROM_ABI void uninitialized_default_construct(_ForwardIterator __first, _ForwardIterator __last) {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
+uninitialized_default_construct(_ForwardIterator __first, _ForwardIterator __last) {
   using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
   (void)std::__uninitialized_default_construct<_ValueType>(std::move(__first), std::move(__last));
 }
@@ -168,7 +169,8 @@ inline _LIBCPP_HIDE_FROM_ABI void uninitialized_default_construct(_ForwardIterat
 // uninitialized_default_construct_n
 
 template <class _ValueType, class _ForwardIterator, class _Size>
-inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator __uninitialized_default_construct_n(_ForwardIterator __first, _Size __n) {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _ForwardIterator
+__uninitialized_default_construct_n(_ForwardIterator __first, _Size __n) {
   auto __idx = __first;
   auto __guard = std::__make_exception_guard([&] { std::__destroy(__first, __idx); });
   for (; __n > 0; ++__idx, (void)--__n)
@@ -179,7 +181,8 @@ inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator __uninitialized_default_construct_
 }
 
 template <class _ForwardIterator, class _Size>
-inline _LIBCPP_HIDE_FROM_ABI _ForwardIterator uninitialized_default_construct_n(_ForwardIterator __first, _Size __n) {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _ForwardIterator
+uninitialized_default_construct_n(_ForwardIterator __first, _Size __n) {
   using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
   return std::__uninitialized_default_construct_n<_ValueType>(std::move(__first), __n);
 }
diff --git a/libcxx/include/version b/libcxx/include/version
index 1c683b67e5700..77f98b5717a2e 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -223,7 +223,8 @@ __cpp_lib_ranges_stride                                 202207L <ranges>
 __cpp_lib_ranges_to_container                           202202L <ranges>
 __cpp_lib_ranges_zip                                    202110L <ranges> <tuple> <utility>
 __cpp_lib_ratio                                         202306L <ratio>
-__cpp_lib_raw_memory_algorithms                         201606L <memory>
+__cpp_lib_raw_memory_algorithms                         202411L <memory>
+                                                        201606L // C++17
 __cpp_lib_rcu                                           202306L <rcu>
 __cpp_lib_reference_from_temporary                      202202L <type_traits>
 __cpp_lib_reference_wrapper                             202403L <functional>
@@ -617,6 +618,8 @@ __cpp_lib_void_t                                        201411L <type_traits>
 // # define __cpp_lib_ranges_concat                        202403L
 # define __cpp_lib_ranges_indices                       202506L
 # define __cpp_lib_ratio                                202306L
+# undef  __cpp_lib_raw_memory_algorithms
+# define __cpp_lib_raw_memory_algorithms                202411L
 // # define __cpp_lib_rcu                                  202306L
 # define __cpp_lib_reference_wrapper                    202403L
 # define __cpp_lib_saturation_arithmetic                202603L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
index f287e1ad9b3ad..c58357a463ffd 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
@@ -623,8 +623,8 @@
 #  ifndef __cpp_lib_raw_memory_algorithms
 #    error "__cpp_lib_raw_memory_algorithms should be defined in c++26"
 #  endif
-#  if __cpp_lib_raw_memory_algorithms != 201606L
-#    error "__cpp_lib_raw_memory_algorithms should have the value 201606L in c++26"
+#  if __cpp_lib_raw_memory_algorithms != 202411L
+#    error "__cpp_lib_raw_memory_algorithms should have the value 202411L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_shared_ptr_arrays
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index dfee4b6d458db..c73181afe5d49 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -7811,8 +7811,8 @@
 #  ifndef __cpp_lib_raw_memory_algorithms
 #    error "__cpp_lib_raw_memory_algorithms should be defined in c++26"
 #  endif
-#  if __cpp_lib_raw_memory_algorithms != 201606L
-#    error "__cpp_lib_raw_memory_algorithms should have the value 201606L in c++26"
+#  if __cpp_lib_raw_memory_algorithms != 202411L
+#    error "__cpp_lib_raw_memory_algorithms should have the value 202411L in c++26"
 #  endif
 
 #  if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct.pass.cpp
index ef969190c6314..69480a1af81a4 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct.pass.cpp
@@ -22,6 +22,7 @@
 #include <iterator>
 #include <memory>
 #include <ranges>
+#include <span>
 #include <type_traits>
 
 #include "../buffer.h"
@@ -39,6 +40,179 @@ struct NotDefaultCtrable { NotDefaultCtrable() = delete; };
 static_assert(!std::is_invocable_v<decltype(std::ranges::uninitialized_default_construct),
     NotDefaultCtrable*, NotDefaultCtrable*>);
 
+TEST_CONSTEXPR_CXX26 bool test() {
+  struct IntWrapper {
+    int n_ = 42;
+  };
+
+  // (iter, sentinel) overload.
+  {
+    constexpr int n = 3;
+    std::allocator<int> alloc;
+    auto data = alloc.allocate(n);
+    auto last = data + n;
+
+    std::ranges::uninitialized_default_construct(data, last);
+    for (int i = 0; i != n; ++i) {
+      data[i] = -i;
+      assert(data[i] == -i);
+    }
+
+    std::ranges::destroy(data, last);
+    alloc.deallocate(data, n);
+  }
+  // (range) overload.
+  {
+    constexpr int n = 3;
+    std::allocator<int> alloc;
+    auto data = alloc.allocate(n);
+    auto r    = std::span<int>{data, n};
+
+    std::ranges::uninitialized_default_construct(r);
+    for (int i = 0; i != n; ++i) {
+      r[i] = -i;
+      assert(r[i] == -i);
+    }
+
+    std::ranges::destroy(r);
+    alloc.deallocate(data, n);
+  }
+  // (iter, sentinel) overload.
+  {
+    constexpr int n = 3;
+    std::allocator<IntWrapper> alloc;
+    auto data = alloc.allocate(n);
+    auto last = data + n;
+
+    std::ranges::uninitialized_default_construct(data, last);
+    for (int i = 0; i != n; ++i)
+      assert(data[i].n_ == 42);
+
+    std::ranges::destroy(data, last);
+    alloc.deallocate(data, n);
+  }
+  // (range) overload.
+  {
+    constexpr int n = 3;
+    std::allocator<IntWrapper> alloc;
+    auto data = alloc.allocate(n);
+    auto r    = std::span<IntWrapper>{data, n};
+
+    std::ranges::uninitialized_default_construct(r);
+    for (int i = 0; i != n; ++i)
+      assert(r[i].n_ == 42);
+
+    std::ranges::destroy(r);
+    alloc.deallocate(data, n);
+  }
+
+  // (iter, sentinel) overload.
+  {
+    using It        = forward_iterator<int*>;
+    constexpr int n = 5;
+    int pool[n]{-1, -1, -1, -1, -1};
+    int* p    = pool;
+    int* pend = p + n;
+
+    std::ranges::uninitialized_default_construct(It(p), It(pend));
+
+    for (int i = 0; i != n; ++i) {
+      pool[i] = i + 17;
+      assert(pool[i] == i + 17);
+    }
+  }
+  // (range) overload.
+  {
+    using It        = forward_iterator<int*>;
+    constexpr int n = 5;
+    int pool[n]{-1, -1, -1, -1, -1};
+    int* p    = pool;
+    int* pend = p + n;
+
+    std::ranges::uninitialized_default_construct(std::ranges::subrange(It(p), It(pend)));
+
+    for (int i = 0; i != n; ++i) {
+      pool[i] = i + 17;
+      assert(pool[i] == i + 17);
+    }
+  }
+  // (iter, sentinel) overload.
+  {
+    using It        = forward_iterator<IntWrapper*>;
+    constexpr int n = 5;
+    IntWrapper pool[n]{IntWrapper{-31}, IntWrapper{-41}, IntWrapper{-59}, IntWrapper{-26}, IntWrapper{-53}};
+    IntWrapper* p = pool;
+    std::ranges::uninitialized_default_construct(It(p), It(p + 1));
+    assert(pool[0].n_ == 42);
+    assert(pool[1].n_ == -41);
+    std::ranges::uninitialized_default_construct(It(p + 1), It(p + n));
+    assert(pool[1].n_ == 42);
+    assert(pool[2].n_ == 42);
+    assert(pool[3].n_ == 42);
+    assert(pool[4].n_ == 42);
+  }
+  // (range) overload.
+  {
+    using It        = forward_iterator<IntWrapper*>;
+    constexpr int n = 5;
+    IntWrapper pool[n]{IntWrapper{-31}, IntWrapper{-41}, IntWrapper{-59}, IntWrapper{-26}, IntWrapper{-53}};
+    IntWrapper* p = pool;
+    std::ranges::uninitialized_default_construct(std::ranges::subrange(It(p), It(p + 1)));
+    assert(pool[0].n_ == 42);
+    assert(pool[1].n_ == -41);
+    std::ranges::uninitialized_default_construct(std::ranges::subrange(It(p + 1), It(p + n)));
+    assert(pool[1].n_ == 42);
+    assert(pool[2].n_ == 42);
+    assert(pool[3].n_ == 42);
+    assert(pool[4].n_ == 42);
+  }
+
+  return true;
+}
+
+#if TEST_STD_VER >= 26
+// Test that std::ranges::uninitialized_default_construct initializes int elements to indeterminate values,
+// and thus can cause constant evaluation failure.
+
+enum class assign_elements : bool { no, yes };
+enum class algorithm_style { iterator_sentinel, range };
+
+constexpr int test_indeterminate_values_helper(algorithm_style style, assign_elements choice) {
+  constexpr int n = 3;
+
+  std::allocator<int> alloc;
+  auto data = alloc.allocate(n);
+  auto last = data + n;
+
+  if (style == algorithm_style::range)
+    std::ranges::uninitialized_default_construct(std::span<int>{data, n});
+  else
+    std::ranges::uninitialized_default_construct(data, last);
+
+  if (choice == assign_elements::yes)
+    for (int i = 0; i != n; ++i)
+      data[i] = i + 1;
+
+  int sum = 0;
+  for (int i = 0; i != n; ++i)
+    sum += data[i];
+
+  std::ranges::destroy(data, last);
+  alloc.deallocate(data, n);
+
+  return sum;
+}
+
+template <algorithm_style Style, assign_elements Choice>
+concept test_indeterminate_values_result =
+    requires { typename std::integral_constant<int, test_indeterminate_values_helper(Style, Choice)>; };
+
+static_assert(!test_indeterminate_values_result<algorithm_style::iterator_sentinel, assign_elements::no>);
+static_assert(!test_indeterminate_values_result<algorithm_style::range, assign_elements::no>);
+static_assert(test_indeterminate_values_result<algorithm_style::iterator_sentinel, assign_elements::yes>);
+static_assert(test_indeterminate_values_result<algorithm_style::range, assign_elements::yes>);
+#endif // TEST_STD_VER >= 26
+
 int main(int, char**) {
   // An empty range -- no default constructors should be invoked.
   {
@@ -163,5 +337,10 @@ int main(int, char**) {
   }
 #endif  // TEST_HAS_NO_EXCEPTIONS
 
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif // TEST_STD_VER >= 26
+
   return 0;
 }
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct_n.pass.cpp
index 097547cc28d9d..a26e5f938562c 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct_n.pass.cpp
@@ -35,6 +35,110 @@ struct NotDefaultCtrable { NotDefaultCtrable() = delete; };
 static_assert(!std::is_invocable_v<decltype(std::ranges::uninitialized_default_construct_n),
     NotDefaultCtrable*, int>);
 
+TEST_CONSTEXPR_CXX26 bool test() {
+  struct IntWrapper {
+    int n_ = 42;
+  };
+
+  {
+    constexpr int n = 3;
+    std::allocator<int> alloc;
+    auto data = alloc.allocate(n);
+
+    auto result = std::ranges::uninitialized_default_construct_n(data, n);
+    assert(result == data + n);
+    for (int i = 0; i != n; ++i) {
+      data[i] = -i;
+      assert(data[i] == -i);
+    }
+
+    std::ranges::destroy_n(data, n);
+    alloc.deallocate(data, n);
+  }
+  {
+    constexpr int n = 3;
+    std::allocator<IntWrapper> alloc;
+    auto data = alloc.allocate(n);
+
+    auto result = std::ranges::uninitialized_default_construct_n(data, n);
+    assert(result == data + n);
+    for (int i = 0; i != n; ++i)
+      assert(data[i].n_ == 42);
+
+    std::ranges::destroy_n(data, n);
+    alloc.deallocate(data, n);
+  }
+
+  {
+    using It        = forward_iterator<int*>;
+    constexpr int n = 5;
+    int pool[n]{-1, -1, -1, -1, -1};
+    int* p    = pool;
+    auto end1 = std::ranges::uninitialized_default_construct_n(It(p), 1);
+    assert(end1 == It(p + 1));
+    auto end2 = std::ranges::uninitialized_default_construct_n(It(p + 1), 4);
+    assert(end2 == It(p + n));
+
+    for (int i = 0; i != n; ++i) {
+      pool[i] = i + 17;
+      assert(pool[i] == i + 17);
+    }
+  }
+  {
+    using It        = forward_iterator<IntWrapper*>;
+    constexpr int n = 5;
+    IntWrapper pool[n]{IntWrapper{-31}, IntWrapper{-41}, IntWrapper{-59}, IntWrapper{-26}, IntWrapper{-53}};
+    IntWrapper* p = pool;
+    auto end1     = std::ranges::uninitialized_default_construct_n(It(p), 1);
+    assert(end1 == It(p + 1));
+    assert(pool[0].n_ == 42);
+    assert(pool[1].n_ == -41);
+    auto end2 = std::ranges::uninitialized_default_construct_n(It(p + 1), 4);
+    assert(end2 == It(p + n));
+    assert(pool[1].n_ == 42);
+    assert(pool[2].n_ == 42);
+    assert(pool[3].n_ == 42);
+    assert(pool[4].n_ == 42);
+  }
+
+  return true;
+}
+
+#if TEST_STD_VER >= 26
+// Test that std::ranges::uninitialized_default_construct_n initializes int elements to indeterminate values,
+// and thus can cause constant evaluation failure.
+
+enum class assign_elements : bool { no, yes };
+
+constexpr int test_indeterminate_values_helper(assign_elements choice) {
+  constexpr int n = 3;
+
+  std::allocator<int> alloc;
+  auto data = alloc.allocate(n);
+  std::ranges::uninitialized_default_construct_n(data, n);
+
+  if (choice == assign_elements::yes)
+    for (int i = 0; i != n; ++i)
+      data[i] = i + 1;
+
+  int sum = 0;
+  for (int i = 0; i != n; ++i)
+    sum += data[i];
+
+  std::ranges::destroy_n(data, n);
+  alloc.deallocate(data, n);
+
+  return sum;
+}
+
+template <assign_elements Choice>
+concept test_indeterminate_values_result =
+    requires { typename std::integral_constant<int, test_indeterminate_values_helper(Choice)>; };
+
+static_assert(!test_indeterminate_values_result<assign_elements::no>);
+static_assert(test_indeterminate_values_result<assign_elements::yes>);
+#endif // TEST_STD_VER >= 26
+
 int main(int, char**) {
   // An empty range -- no default constructors should be invoked.
   {
@@ -76,5 +180,10 @@ int main(int, char**) {
   }
 #endif  // TEST_HAS_NO_EXCEPTIONS
 
+  test();
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif // TEST_STD_VER >= 26
+
   return 0;
 }
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct.pass.cpp
index 89e684b970e5c..d65223c3620cd 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct.pass.cpp
@@ -85,10 +85,117 @@ void test_counted()
     assert(Counted::count == 0);
 }
 
+TEST_CONSTEXPR_CXX26 bool test() {
+  struct IntWrapper {
+    int n_ = 42;
+  };
+
+  {
+    constexpr int n = 3;
+    std::allocator<int> alloc;
+    auto data = alloc.allocate(n);
+    auto last = data + n;
+
+    std::uninitialized_default_construct(data, last);
+    for (int i = 0; i != n; ++i) {
+      data[i] = -i;
+      assert(data[i] == -i);
+    }
+
+    std::destroy(data, last);
+    alloc.deallocate(data, n);
+  }
+  {
+    constexpr int n = 3;
+    std::allocator<IntWrapper> alloc;
+    auto data = alloc.allocate(n);
+    auto last = data + n;
+
+    std::uninitialized_default_construct(data, last);
+    for (int i = 0; i != n; ++i)
+      assert(data[i].n_ == 42);
+
+    std::destroy(data, last);
+    alloc.deallocate(data, n);
+  }
+
+  {
+    using It        = forward_iterator<int*>;
+    constexpr int n = 5;
+    int pool[n]{-1, -1, -1, -1, -1};
+    int* p    = pool;
+    int* pend = p + n;
+
+    std::uninitialized_default_construct(It(p), It(pend));
+
+    for (int i = 0; i != n; ++i) {
+      pool[i] = i + 17;
+      assert(pool[i] == i + 17);
+    }
+  }
+  {
+    using It        = forward_iterator<IntWrapper*>;
+    constexpr int n = 5;
+    IntWrapper pool[n]{IntWrapper{-31}, IntWrapper{-41}, IntWrapper{-59}, IntWrapper{-26}, IntWrapper{-53}};
+    IntWrapper* p = pool;
+    std::uninitialized_default_construct(It(p), It(p + 1));
+    assert(pool[0].n_ == 42);
+    assert(pool[1].n_ == -41);
+    std::uninitialized_default_construct(It(p + 1), It(p + n));
+    assert(pool[1].n_ == 42);
+    assert(pool[2].n_ == 42);
+    assert(pool[3].n_ == 42);
+    assert(pool[4].n_ == 42);
+  }
+
+  return true;
+}
+
+#if TEST_STD_VER >= 26
+// Test that std::uninitialized_default_construct initializes int elements to indeterminate values,
+// and thus can cause constant evaluation failure.
+
+enum class assign_elements : bool { no, yes };
+
+constexpr int test_indeterminate_values_helper(assign_elements choice) {
+  constexpr int n = 3;
+
+  std::allocator<int> alloc;
+  auto data = alloc.allocate(n);
+  auto last = data + n;
+  std::uninitialized_default_construct(data, last);
+
+  if (choice == assign_elements::yes)
+    for (int i = 0; i != n; ++i)
+      data[i] = i + 1;
+
+  int sum = 0;
+  for (int i = 0; i != n; ++i)
+    sum += data[i];
+
+  std::destroy(data, last);
+  alloc.deallocate(data, n);
+
+  return sum;
+}
+
+template <assign_elements Choice>
+concept test_indeterminate_values_result =
+    requires { typename std::integral_constant<int, test_indeterminate_values_helper(Choice)>; };
+
+static_assert(!test_indeterminate_values_result<assign_elements::no>);
+static_assert(test_indeterminate_values_result<assign_elements::yes>);
+#endif // TEST_STD_VER >= 26
+
 int main(int, char**)
 {
     test_counted();
     test_ctor_throws();
 
-  return 0;
+    test();
+#if TEST_STD_VER >= 26
+    static_assert(test());
+#endif // TEST_STD_VER >= 26
+
+    return 0;
 }
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct_n.pass.cpp
index 95a664a1285b4..42a56522557b4 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/uninitialized_default_construct_n.pass.cpp
@@ -89,10 +89,119 @@ void test_counted()
     assert(Counted::count == 0);
 }
 
+TEST_CONSTEXPR_CXX26 bool test() {
+  struct IntWrapper {
+    int n_ = 42;
+  };
+
+  {
+    constexpr int n = 3;
+    std::allocator<int> alloc;
+    auto data = alloc.allocate(n);
+
+    auto result = std::uninitialized_default_construct_n(data, n);
+    assert(result == data + n);
+    for (int i = 0; i != n; ++i) {
+      data[i] = -i;
+      assert(data[i] == -i);
+    }
+
+    std::destroy_n(data, n);
+    alloc.deallocate(data, n);
+  }
+  {
+    constexpr int n = 3;
+    std::allocator<IntWrapper> alloc;
+    auto data = alloc.allocate(n);
+
+    auto result = std::uninitialized_default_construct_n(data, n);
+    assert(result == data + n);
+    for (int i = 0; i != n; ++i)
+      assert(data[i].n_ == 42);
+
+    std::destroy_n(data, n);
+    alloc.deallocate(data, n);
+  }
+
+  {
+    using It        = forward_iterator<int*>;
+    constexpr int n = 5;
+    int pool[n]{-1, -1, -1, -1, -1};
+    int* p    = pool;
+    auto end1 = std::uninitialized_default_construct_n(It(p), 1);
+    assert(end1 == It(p + 1));
+    auto end2 = std::uninitialized_default_construct_n(It(p + 1), 4);
+    assert(end2 == It(p + n));
+
+    for (int i = 0; i != n; ++i) {
+      pool[i] = i + 17;
+      assert(pool[i] == i + 17);
+    }
+  }
+  {
+    using It        = forward_iterator<IntWrapper*>;
+    constexpr int n = 5;
+    IntWrapper pool[n]{IntWrapper{-31}, IntWrapper{-41}, IntWrapper{-59}, IntWrapper{-26}, IntWrapper{-53}};
+    IntWrapper* p = pool;
+    auto end1     = std::uninitialized_default_construct_n(It(p), 1);
+    assert(end1 == It(p + 1));
+    assert(pool[0].n_ == 42);
+    assert(pool[1].n_ == -41);
+    auto end2 = std::uninitialized_default_construct_n(It(p + 1), 4);
+    assert(end2 == It(p + n));
+    assert(pool[1].n_ == 42);
+    assert(pool[2].n_ == 42);
+    assert(pool[3].n_ == 42);
+    assert(pool[4].n_ == 42);
+  }
+
+  return true;
+}
+
+#if TEST_STD_VER >= 26
+// Test that std::uninitialized_default_construct_n initializes int elements to indeterminate values,
+// and thus can cause constant evaluation failure.
+
+enum class assign_elements : bool { no, yes };
+
+constexpr int test_indeterminate_values_helper(assign_elements choice) {
+  constexpr int n = 3;
+
+  std::allocator<int> alloc;
+  auto data = alloc.allocate(n);
+  std::uninitialized_default_construct_n(data, n);
+
+  if (choice == assign_elements::yes)
+    for (int i = 0; i != n; ++i)
+      data[i] = i + 1;
+
+  int sum = 0;
+  for (int i = 0; i != n; ++i)
+    sum += data[i];
+
+  std::destroy_n(data, n);
+  alloc.deallocate(data, n);
+
+  return sum;
+}
+
+template <assign_elements Choice>
+concept test_indeterminate_values_result =
+    requires { typename std::integral_constant<int, test_indeterminate_values_helper(Choice)>; };
+
+static_assert(!test_indeterminate_values_result<assign_elements::no>);
+static_assert(test_indeterminate_values_result<assign_elements::yes>);
+#endif // TEST_STD_VER >= 26
+
 int main(int, char**)
 {
     test_counted();
     test_ctor_throws();
 
-  return 0;
+    test();
+#if TEST_STD_VER >= 26
+    static_assert(test());
+#endif // TEST_STD_VER >= 26
+
+    return 0;
 }
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f81eb0e0e8060..59cd821838c76 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1200,7 +1200,7 @@ def add_version_header(tc):
         },
         {
             "name": "__cpp_lib_raw_memory_algorithms",
-            "values": {"c++17": 201606},
+            "values": {"c++17": 201606, "c++26": 202411},
             "headers": ["memory"],
         },
         {

>From b909c1551d1df2c3350cf059d64d106159ef4268 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Fri, 12 Jun 2026 19:27:01 +0800
Subject: [PATCH 2/2] Add a`.verify.cpp` to test the behavior more clearly

---
 .../constant_evaluation.verify.cpp            | 61 +++++++++++++++++++
 1 file changed, 61 insertions(+)
 create mode 100644 libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/constant_evaluation.verify.cpp

diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/constant_evaluation.verify.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/constant_evaluation.verify.cpp
new file mode 100644
index 0000000000000..0304871169b6f
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/constant_evaluation.verify.cpp
@@ -0,0 +1,61 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <memory>
+
+// Test that during constant evaluation, std::(ranges::}uninitialized_default_construct(_n)
+// do not initialize elements to determined values.
+
+#include <memory>
+
+constexpr int test_uninitialized_default_construct() {
+  int c[2];
+  std::uninitialized_default_construct(&c[0], &c[2]);
+  return c[0]; // expected-note {{read of uninitialized object is not allowed in a constant expression}}
+}
+
+// expected-error at +1 {{static assertion expression is not an integral constant expression}}
+static_assert(test_uninitialized_default_construct() == 0);
+
+constexpr int test_uninitialized_default_construct_n() {
+  int c[2];
+  std::uninitialized_default_construct_n(&c[0], 2);
+  return c[0]; // expected-note {{read of uninitialized object is not allowed in a constant expression}}
+}
+
+// expected-error at +1 {{static assertion expression is not an integral constant expression}}
+static_assert(test_uninitialized_default_construct_n() == 0);
+
+constexpr int test_ranges_uninitialized_default_construct_iter_sent() {
+  int c[2];
+  std::ranges::uninitialized_default_construct(&c[0], &c[2]);
+  return c[0]; // expected-note {{read of uninitialized object is not allowed in a constant expression}}
+}
+
+// expected-error at +1 {{static assertion expression is not an integral constant expression}}
+static_assert(test_ranges_uninitialized_default_construct_iter_sent() == 0);
+
+constexpr int test_ranges_uninitialized_default_construct_range() {
+  int c[2];
+  std::ranges::uninitialized_default_construct(c);
+  return c[0]; // expected-note {{read of uninitialized object is not allowed in a constant expression}}
+}
+
+// expected-error at +1 {{static assertion expression is not an integral constant expression}}
+static_assert(test_ranges_uninitialized_default_construct_range() == 0);
+
+constexpr int test_ranges_uninitialized_default_construct_n() {
+  int c[2];
+  std::ranges::uninitialized_default_construct_n(&c[0], 2);
+  return c[0]; // expected-note {{read of uninitialized object is not allowed in a constant expression}}
+}
+
+// expected-error at +1 {{static assertion expression is not an integral constant expression}}
+static_assert(test_ranges_uninitialized_default_construct_n() == 0);



More information about the libcxx-commits mailing list