[libcxx-commits] [libcxx] 754ea6f - [libc++][ranges] Implement `uninitialized_value_construct{, _n}` and `uninitialized_fill{, _n}`.

Konstantin Varlamov via libcxx-commits libcxx-commits at lists.llvm.org
Mon Dec 20 00:24:44 PST 2021


Author: Konstantin Varlamov
Date: 2021-12-20T00:24:27-08:00
New Revision: 754ea6fd4d52f4699644a3c6dfc854a231c881c4

URL: https://github.com/llvm/llvm-project/commit/754ea6fd4d52f4699644a3c6dfc854a231c881c4
DIFF: https://github.com/llvm/llvm-project/commit/754ea6fd4d52f4699644a3c6dfc854a231c881c4.diff

LOG: [libc++][ranges] Implement `uninitialized_value_construct{,_n}` and `uninitialized_fill{,_n}`.

Also:
- refactor out `__voidify`;
- use the `destroy` algorithm internally;
- refactor out helper classes used in tests for `uninitialized_*`
  algorithms.

Differential Revision: https://reviews.llvm.org/D115626

Added: 
    libcxx/include/__memory/voidify.h
    libcxx/test/libcxx/diagnostics/detail.headers/memory/voidify.module.verify.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/buffer.h
    libcxx/test/std/utilities/memory/specialized.algorithms/counted.h
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct_n.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/ranges_uninitialized_fill_n.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/ranges_uninitialized_fill.pass.cpp

Modified: 
    libcxx/docs/Status/RangesAlgorithms.csv
    libcxx/docs/Status/RangesPaper.csv
    libcxx/include/CMakeLists.txt
    libcxx/include/__memory/construct_at.h
    libcxx/include/__memory/ranges_uninitialized_algorithms.h
    libcxx/include/__memory/uninitialized_algorithms.h
    libcxx/include/memory
    libcxx/include/module.modulemap
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct_n.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv
index 5e8cad6b96400..0d8beb37984c1 100644
--- a/libcxx/docs/Status/RangesAlgorithms.csv
+++ b/libcxx/docs/Status/RangesAlgorithms.csv
@@ -84,17 +84,17 @@ Permutation,pop_heap,Not assigned,n/a,Not started
 Permutation,sort_heap,Not assigned,n/a,Not started
 Permutation,prev_permutation,Not assigned,n/a,Not started
 Permutation,next_permutation,Not assigned,n/a,Not started
-Uninitialised memory,uninitialized_copy,var-const,n/a,Not started
-Uninitialised memory,uninitialized_copy_n,var-const,n/a,Not started
-Uninitialised memory,uninitialized_fill,var-const,n/a,Not started
-Uninitialised memory,uninitialized_fill_n,var-const,n/a,Not started
-Uninitialised memory,uninitialized_move,var-const,n/a,Not started
-Uninitialised memory,uninitialized_move_n,var-const,n/a,Not started
-Uninitialised memory,uninitialized_default_construct,var-const,n/a,Not started
-Uninitialised memory,uninitialized_default_construct_n,var-const,n/a,Not started
-Uninitialised memory,uninitialized_value_construct,var-const,n/a,Not started
-Uninitialised memory,uninitialized_value_construct_n,var-const,n/a,Not started
-Uninitialised memory,destroy,var-const,n/a,Not started
-Uninitialised memory,destroy_n,var-const,n/a,Not started
-Uninitialised memory,destroy_at,var-const,n/a,Not started
-Uninitialised memory,construct_at,var-const,n/a,Not started
+Uninitialised memory,uninitialized_copy,Konstantin Varlamov,n/a,Not started
+Uninitialised memory,uninitialized_copy_n,Konstantin Varlamov,n/a,Not started
+Uninitialised memory,uninitialized_fill,Konstantin Varlamov,`D115626 <https://llvm.org/D115626>`_,✅
+Uninitialised memory,uninitialized_fill_n,Konstantin Varlamov,`D115626 <https://llvm.org/D115626>`_,✅
+Uninitialised memory,uninitialized_move,Konstantin Varlamov,n/a,Not started
+Uninitialised memory,uninitialized_move_n,Konstantin Varlamov,n/a,Not started
+Uninitialised memory,uninitialized_default_construct,Konstantin Varlamov,`D115315 <https://llvm.org/D115315>`_,✅
+Uninitialised memory,uninitialized_default_construct_n,Konstantin Varlamov,`D115315 <https://llvm.org/D115315>`_,✅
+Uninitialised memory,uninitialized_value_construct,Konstantin Varlamov,`D115626 <https://llvm.org/D115626>`_,✅
+Uninitialised memory,uninitialized_value_construct_n,Konstantin Varlamov,`D115626 <https://llvm.org/D115626>`_,✅
+Uninitialised memory,destroy,Konstantin Varlamov,n/a,Not started
+Uninitialised memory,destroy_n,Konstantin Varlamov,n/a,Not started
+Uninitialised memory,destroy_at,Konstantin Varlamov,n/a,Not started
+Uninitialised memory,construct_at,Konstantin Varlamov,n/a,Not started

diff  --git a/libcxx/docs/Status/RangesPaper.csv b/libcxx/docs/Status/RangesPaper.csv
index 2cf510d6dcc23..ed1900bd12441 100644
--- a/libcxx/docs/Status/RangesPaper.csv
+++ b/libcxx/docs/Status/RangesPaper.csv
@@ -18,16 +18,16 @@ Section,Description,Dependencies,Assignee,Complete
 | *nothrow-forward-iterator*
 | *nothrow-forward-range*","| [iterator.concepts]
 | [range.refinements]",Konstantin Varlamov,✅
-`[specialized.algorithms] <http://wg21.link/specialized.algorithms>`_,"| `ranges::uninitialized_default_construct <https://llvm.org/D115315>`
-| `ranges::uninitialized_default_construct_n <https://llvm.org/D115315>`
-| ranges::uninitialized_value_construct
-| ranges::uninitialized_value_construct_n
+`[specialized.algorithms] <http://wg21.link/specialized.algorithms>`_,"| `ranges::uninitialized_default_construct <https://llvm.org/D115315>`_
+| `ranges::uninitialized_default_construct_n <https://llvm.org/D115315>`_
+| `ranges::uninitialized_value_construct <https://llvm.org/D115626>`_
+| `ranges::uninitialized_value_construct_n <https://llvm.org/D115626>`_
 | ranges::uninitialized_copy
 | ranges::uninitialized_copy_n
 | ranges::uninitialized_move
 | ranges::uninitialized_move_n
-| ranges::uninitialized_fill
-| ranges::uninitialized_fill_n
+| `ranges::uninitialized_fill <https://llvm.org/D115626>`_
+| `ranges::uninitialized_fill_n <https://llvm.org/D115626>`_
 | ranges::construct_at
 | ranges::destroy
 | ranges::destroy_at

diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 0d4ddbf0a4777..e0801c7ee5226 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -238,6 +238,7 @@ set(files
   __memory/uninitialized_algorithms.h
   __memory/unique_ptr.h
   __memory/uses_allocator.h
+  __memory/voidify.h
   __mutex_base
   __numeric/accumulate.h
   __numeric/adjacent_
diff erence.h

diff  --git a/libcxx/include/__memory/construct_at.h b/libcxx/include/__memory/construct_at.h
index 789677d7a6139..3b58451c5009f 100644
--- a/libcxx/include/__memory/construct_at.h
+++ b/libcxx/include/__memory/construct_at.h
@@ -41,44 +41,67 @@ constexpr _Tp* construct_at(_Tp* __location, _Args&& ...__args) {
 
 // destroy_at
 
-#if _LIBCPP_STD_VER > 14
+// The internal functions are available regardless of the language version (with the exception of the `__destroy_at`
+// taking an array).
 
 template <class _ForwardIterator>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
-void destroy(_ForwardIterator, _ForwardIterator);
+void __destroy(_ForwardIterator, _ForwardIterator);
 
-template <class _Tp, enable_if_t<!is_array_v<_Tp>, int> = 0>
+template <class _Tp, typename enable_if<!is_array<_Tp>::value, int>::type = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
-void destroy_at(_Tp* __loc) {
+void __destroy_at(_Tp* __loc) {
     _LIBCPP_ASSERT(__loc, "null pointer given to destroy_at");
     __loc->~_Tp();
 }
 
+#if _LIBCPP_STD_VER > 17
+template <class _Tp, typename enable_if<is_array<_Tp>::value, int>::type = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
+void __destroy_at(_Tp* __loc) {
+    _LIBCPP_ASSERT(__loc, "null pointer given to destroy_at");
+    _VSTD::__destroy(_VSTD::begin(*__loc), _VSTD::end(*__loc));
+}
+#endif
+
+template <class _ForwardIterator>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
+void __destroy(_ForwardIterator __first, _ForwardIterator __last) {
+    for (; __first != __last; ++__first)
+        _VSTD::__destroy_at(_VSTD::addressof(*__first));
+}
+
+#if _LIBCPP_STD_VER > 14
+
+template <class _Tp, enable_if_t<!is_array_v<_Tp>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
+void destroy_at(_Tp* __loc) {
+    _VSTD::__destroy_at(__loc);
+}
+
 #if _LIBCPP_STD_VER > 17
 template <class _Tp, enable_if_t<is_array_v<_Tp>, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
 void destroy_at(_Tp* __loc) {
-    _LIBCPP_ASSERT(__loc, "null pointer given to destroy_at");
-    _VSTD::destroy(_VSTD::begin(*__loc), _VSTD::end(*__loc));
+  _VSTD::__destroy_at(__loc);
 }
 #endif
 
 template <class _ForwardIterator>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
 void destroy(_ForwardIterator __first, _ForwardIterator __last) {
-    for (; __first != __last; ++__first)
-        _VSTD::destroy_at(_VSTD::addressof(*__first));
+    _VSTD::__destroy(_VSTD::move(__first), _VSTD::move(__last));
 }
 
 template <class _ForwardIterator, class _Size>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
 _ForwardIterator destroy_n(_ForwardIterator __first, _Size __n) {
     for (; __n > 0; (void)++__first, --__n)
-        _VSTD::destroy_at(_VSTD::addressof(*__first));
+        _VSTD::__destroy_at(_VSTD::addressof(*__first));
     return __first;
 }
 
-#endif
+#endif // _LIBCPP_STD_VER > 14
 
 _LIBCPP_END_NAMESPACE_STD
 

diff  --git a/libcxx/include/__memory/ranges_uninitialized_algorithms.h b/libcxx/include/__memory/ranges_uninitialized_algorithms.h
index 3b09a8cfaec67..6ec803806c05a 100644
--- a/libcxx/include/__memory/ranges_uninitialized_algorithms.h
+++ b/libcxx/include/__memory/ranges_uninitialized_algorithms.h
@@ -21,6 +21,7 @@
 #include <__ranges/access.h>
 #include <__ranges/concepts.h>
 #include <__ranges/dangling.h>
+#include <__utility/move.h>
 #include <type_traits>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -45,7 +46,8 @@ struct __fn final : private __function_like {
     requires default_initializable<iter_value_t<_ForwardIterator>>
   _ForwardIterator operator()(_ForwardIterator __first, _Sentinel __last) const {
     using _ValueType = remove_reference_t<iter_reference_t<_ForwardIterator>>;
-    return _VSTD::__uninitialized_default_construct<_ValueType>(__first, __last);
+    return _VSTD::__uninitialized_default_construct<_ValueType>(
+        _VSTD::move(__first), _VSTD::move(__last));
   }
 
   template <__nothrow_forward_range _ForwardRange>
@@ -56,7 +58,7 @@ struct __fn final : private __function_like {
 
 };
 
-} // namespace __uninitialized_default_construct_ns
+} // namespace __uninitialized_default_construct
 
 inline namespace __cpo {
 inline constexpr auto uninitialized_default_construct =
@@ -77,18 +79,131 @@ struct __fn final : private __function_like {
   _ForwardIterator operator()(_ForwardIterator __first,
                               iter_
diff erence_t<_ForwardIterator> __n) const {
     using _ValueType = remove_reference_t<iter_reference_t<_ForwardIterator>>;
-    return _VSTD::__uninitialized_default_construct_n<_ValueType>(__first, __n);
+    return _VSTD::__uninitialized_default_construct_n<_ValueType>(_VSTD::move(__first), __n);
   }
 
 };
 
-} // namespace __uninitialized_default_construct_n_ns
+} // namespace __uninitialized_default_construct_n
 
 inline namespace __cpo {
 inline constexpr auto uninitialized_default_construct_n =
     __uninitialized_default_construct_n::__fn(__function_like::__tag());
 } // namespace __cpo
 
+// uninitialized_value_construct
+
+namespace __uninitialized_value_construct {
+
+struct __fn final : private __function_like {
+
+  constexpr explicit __fn(__tag __x) noexcept : __function_like(__x) {}
+
+  template <__nothrow_forward_iterator _ForwardIterator,
+            __nothrow_sentinel_for<_ForwardIterator> _Sentinel>
+    requires default_initializable<iter_value_t<_ForwardIterator>>
+  _ForwardIterator operator()(_ForwardIterator __first, _Sentinel __last) const {
+    using _ValueType = remove_reference_t<iter_reference_t<_ForwardIterator>>;
+    return _VSTD::__uninitialized_value_construct<_ValueType>(
+        _VSTD::move(__first), _VSTD::move(__last));
+  }
+
+  template <__nothrow_forward_range _ForwardRange>
+    requires default_initializable<range_value_t<_ForwardRange>>
+  borrowed_iterator_t<_ForwardRange> operator()(_ForwardRange&& __range) const {
+    return (*this)(ranges::begin(__range), ranges::end(__range));
+  }
+
+};
+
+} // namespace __uninitialized_value_construct
+
+inline namespace __cpo {
+inline constexpr auto uninitialized_value_construct =
+    __uninitialized_value_construct::__fn(__function_like::__tag());
+} // namespace __cpo
+
+// uninitialized_value_construct_n
+
+namespace __uninitialized_value_construct_n {
+
+struct __fn final : private __function_like {
+
+  constexpr explicit __fn(__tag __x) noexcept : __function_like(__x) {}
+
+  template <__nothrow_forward_iterator _ForwardIterator>
+    requires default_initializable<iter_value_t<_ForwardIterator>>
+  _ForwardIterator operator()(_ForwardIterator __first,
+                              iter_
diff erence_t<_ForwardIterator> __n) const {
+    using _ValueType = remove_reference_t<iter_reference_t<_ForwardIterator>>;
+    return _VSTD::__uninitialized_value_construct_n<_ValueType>(_VSTD::move(__first), __n);
+  }
+
+};
+
+} // namespace __uninitialized_value_construct_n
+
+inline namespace __cpo {
+inline constexpr auto uninitialized_value_construct_n =
+    __uninitialized_value_construct_n::__fn(__function_like::__tag());
+} // namespace __cpo
+
+// uninitialized_fill
+
+namespace __uninitialized_fill {
+
+struct __fn final : private __function_like {
+
+  constexpr explicit __fn(__tag __x) noexcept : __function_like(__x) {}
+
+  template <__nothrow_forward_iterator _ForwardIterator,
+            __nothrow_sentinel_for<_ForwardIterator> _Sentinel,
+            class _Tp>
+    requires constructible_from<iter_value_t<_ForwardIterator>, const _Tp&>
+  _ForwardIterator operator()(_ForwardIterator __first, _Sentinel __last, const _Tp& __x) const {
+    using _ValueType = remove_reference_t<iter_reference_t<_ForwardIterator>>;
+    return _VSTD::__uninitialized_fill<_ValueType>(_VSTD::move(__first), _VSTD::move(__last), __x);
+  }
+
+  template <__nothrow_forward_range _ForwardRange, class _Tp>
+    requires constructible_from<range_value_t<_ForwardRange>, const _Tp&>
+  borrowed_iterator_t<_ForwardRange> operator()(_ForwardRange&& __range, const _Tp& __x) const {
+    return (*this)(ranges::begin(__range), ranges::end(__range), __x);
+  }
+
+};
+
+} // namespace __uninitialized_fil
+
+inline namespace __cpo {
+inline constexpr auto uninitialized_fill = __uninitialized_fill::__fn(__function_like::__tag());
+} // namespace __cpo
+
+// uninitialized_fill_n
+
+namespace __uninitialized_fill_n {
+
+struct __fn final : private __function_like {
+
+  constexpr explicit __fn(__tag __x) noexcept : __function_like(__x) {}
+
+  template <__nothrow_forward_iterator _ForwardIterator, class _Tp>
+    requires constructible_from<iter_value_t<_ForwardIterator>, const _Tp&>
+  _ForwardIterator operator()(_ForwardIterator __first,
+                              iter_
diff erence_t<_ForwardIterator> __n,
+                              const _Tp& __x) const {
+    using _ValueType = remove_reference_t<iter_reference_t<_ForwardIterator>>;
+    return _VSTD::__uninitialized_fill_n<_ValueType>(_VSTD::move(__first), __n, __x);
+  }
+
+};
+
+} // namespace __uninitialized_fill_n
+
+inline namespace __cpo {
+inline constexpr auto uninitialized_fill_n = __uninitialized_fill_n::__fn(__function_like::__tag());
+} // namespace __cpo
+
 } // namespace ranges
 #endif // !defined(_LIBCPP_HAS_NO_RANGES)
 

diff  --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index 6f2e7abb18364..69132633a48e3 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -13,6 +13,7 @@
 #include <__config>
 #include <__memory/addressof.h>
 #include <__memory/construct_at.h>
+#include <__memory/voidify.h>
 #include <iterator>
 #include <utility>
 
@@ -70,51 +71,70 @@ uninitialized_copy_n(_InputIterator __f, _Size __n, _ForwardIterator __r)
     return __r;
 }
 
-template <class _ForwardIterator, class _Tp>
-void
-uninitialized_fill(_ForwardIterator __f, _ForwardIterator __l, const _Tp& __x)
+// uninitialized_fill
+
+template <class _ValueType, class _ForwardIterator, class _Sentinel, class _Tp>
+inline _LIBCPP_HIDE_FROM_ABI
+_ForwardIterator __uninitialized_fill(_ForwardIterator __first, _Sentinel __last, const _Tp& __x)
 {
-    typedef typename iterator_traits<_ForwardIterator>::value_type value_type;
+    _ForwardIterator __idx = __first;
 #ifndef _LIBCPP_NO_EXCEPTIONS
-    _ForwardIterator __s = __f;
     try
     {
 #endif
-        for (; __f != __l; ++__f)
-            ::new ((void*)_VSTD::addressof(*__f)) value_type(__x);
+        for (; __idx != __last; ++__idx)
+            ::new (_VSTD::__voidify(*__idx)) _ValueType(__x);
 #ifndef _LIBCPP_NO_EXCEPTIONS
     }
     catch (...)
     {
-        for (; __s != __f; ++__s)
-            __s->~value_type();
+        _VSTD::__destroy(__first, __idx);
         throw;
     }
 #endif
+
+    return __idx;
 }
 
-template <class _ForwardIterator, class _Size, class _Tp>
-_ForwardIterator
-uninitialized_fill_n(_ForwardIterator __f, _Size __n, const _Tp& __x)
+template <class _ForwardIterator, class _Tp>
+inline _LIBCPP_HIDE_FROM_ABI
+void uninitialized_fill(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __x)
 {
-    typedef typename iterator_traits<_ForwardIterator>::value_type value_type;
+    typedef typename iterator_traits<_ForwardIterator>::value_type _ValueType;
+    (void)_VSTD::__uninitialized_fill<_ValueType>(__first, __last, __x);
+}
+
+// uninitialized_fill_n
+
+template <class _ValueType, class _ForwardIterator, class _Size, class _Tp>
+inline _LIBCPP_HIDE_FROM_ABI
+_ForwardIterator __uninitialized_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x)
+{
+    _ForwardIterator __idx = __first;
 #ifndef _LIBCPP_NO_EXCEPTIONS
-    _ForwardIterator __s = __f;
     try
     {
 #endif
-        for (; __n > 0; ++__f, (void) --__n)
-            ::new ((void*)_VSTD::addressof(*__f)) value_type(__x);
+        for (; __n > 0; ++__idx, (void) --__n)
+            ::new (_VSTD::__voidify(*__idx)) _ValueType(__x);
 #ifndef _LIBCPP_NO_EXCEPTIONS
     }
     catch (...)
     {
-        for (; __s != __f; ++__s)
-            __s->~value_type();
+        _VSTD::__destroy(__first, __idx);
         throw;
     }
 #endif
-    return __f;
+
+    return __idx;
+}
+
+template <class _ForwardIterator, class _Size, class _Tp>
+inline _LIBCPP_HIDE_FROM_ABI
+_ForwardIterator uninitialized_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x)
+{
+    typedef typename iterator_traits<_ForwardIterator>::value_type _ValueType;
+    return _VSTD::__uninitialized_fill_n<_ValueType>(__first, __n, __x);
 }
 
 #if _LIBCPP_STD_VER > 14
@@ -129,10 +149,10 @@ _ForwardIterator __uninitialized_default_construct(_ForwardIterator __first, _Se
     try {
 #endif
     for (; __idx != __last; ++__idx)
-        ::new ((void*)_VSTD::addressof(*__idx)) _ValueType;
+        ::new (_VSTD::__voidify(*__idx)) _ValueType;
 #ifndef _LIBCPP_NO_EXCEPTIONS
     } catch (...) {
-        _VSTD::destroy(__first, __idx);
+        _VSTD::__destroy(__first, __idx);
         throw;
     }
 #endif
@@ -141,10 +161,11 @@ _ForwardIterator __uninitialized_default_construct(_ForwardIterator __first, _Se
 }
 
 template <class _ForwardIterator>
-inline _LIBCPP_INLINE_VISIBILITY
+inline _LIBCPP_HIDE_FROM_ABI
 void uninitialized_default_construct(_ForwardIterator __first, _ForwardIterator __last) {
     using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
-    (void)_VSTD::__uninitialized_default_construct<_ValueType>(__first, __last);
+    (void)_VSTD::__uninitialized_default_construct<_ValueType>(
+        _VSTD::move(__first), _VSTD::move(__last));
 }
 
 // uninitialized_default_construct_n
@@ -157,10 +178,10 @@ _ForwardIterator __uninitialized_default_construct_n(_ForwardIterator __first, _
     try {
 #endif
     for (; __n > 0; ++__idx, (void) --__n)
-        ::new ((void*)_VSTD::addressof(*__idx)) _ValueType;
+        ::new (_VSTD::__voidify(*__idx)) _ValueType;
 #ifndef _LIBCPP_NO_EXCEPTIONS
     } catch (...) {
-        _VSTD::destroy(__first, __idx);
+        _VSTD::__destroy(__first, __idx);
         throw;
     }
 #endif
@@ -169,49 +190,68 @@ _ForwardIterator __uninitialized_default_construct_n(_ForwardIterator __first, _
 }
 
 template <class _ForwardIterator, class _Size>
-inline _LIBCPP_INLINE_VISIBILITY
+inline _LIBCPP_HIDE_FROM_ABI
 _ForwardIterator uninitialized_default_construct_n(_ForwardIterator __first, _Size __n) {
     using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
-    return _VSTD::__uninitialized_default_construct_n<_ValueType>(__first, __n);
+    return _VSTD::__uninitialized_default_construct_n<_ValueType>(_VSTD::move(__first), __n);
 }
 
-template <class _ForwardIterator>
-inline _LIBCPP_INLINE_VISIBILITY
-void uninitialized_value_construct(_ForwardIterator __first, _ForwardIterator __last) {
-    using _Vt = typename iterator_traits<_ForwardIterator>::value_type;
+// uninitialized_value_construct
+
+template <class _ValueType, class _ForwardIterator, class _Sentinel>
+inline _LIBCPP_HIDE_FROM_ABI
+_ForwardIterator __uninitialized_value_construct(_ForwardIterator __first, _Sentinel __last) {
     auto __idx = __first;
 #ifndef _LIBCPP_NO_EXCEPTIONS
     try {
 #endif
     for (; __idx != __last; ++__idx)
-        ::new ((void*)_VSTD::addressof(*__idx)) _Vt();
+        ::new (_VSTD::__voidify(*__idx)) _ValueType();
 #ifndef _LIBCPP_NO_EXCEPTIONS
     } catch (...) {
-        _VSTD::destroy(__first, __idx);
+        _VSTD::__destroy(__first, __idx);
         throw;
     }
 #endif
+
+    return __idx;
 }
 
-template <class _ForwardIterator, class _Size>
-inline _LIBCPP_INLINE_VISIBILITY
-_ForwardIterator uninitialized_value_construct_n(_ForwardIterator __first, _Size __n) {
-    using _Vt = typename iterator_traits<_ForwardIterator>::value_type;
+template <class _ForwardIterator>
+inline _LIBCPP_HIDE_FROM_ABI
+void uninitialized_value_construct(_ForwardIterator __first, _ForwardIterator __last) {
+    using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
+    (void)_VSTD::__uninitialized_value_construct<_ValueType>(
+        _VSTD::move(__first), _VSTD::move(__last));
+}
+
+// uninitialized_value_construct_n
+
+template <class _ValueType, class _ForwardIterator, class _Size>
+inline _LIBCPP_HIDE_FROM_ABI
+_ForwardIterator __uninitialized_value_construct_n(_ForwardIterator __first, _Size __n) {
     auto __idx = __first;
 #ifndef _LIBCPP_NO_EXCEPTIONS
     try {
 #endif
     for (; __n > 0; ++__idx, (void) --__n)
-        ::new ((void*)_VSTD::addressof(*__idx)) _Vt();
-    return __idx;
+        ::new (_VSTD::__voidify(*__idx)) _ValueType();
 #ifndef _LIBCPP_NO_EXCEPTIONS
     } catch (...) {
-        _VSTD::destroy(__first, __idx);
+        _VSTD::__destroy(__first, __idx);
         throw;
     }
 #endif
+
+    return __idx;
 }
 
+template <class _ForwardIterator, class _Size>
+inline _LIBCPP_HIDE_FROM_ABI
+_ForwardIterator uninitialized_value_construct_n(_ForwardIterator __first, _Size __n) {
+    using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
+    return __uninitialized_value_construct_n<_ValueType>(_VSTD::move(__first), __n);
+}
 
 template <class _InputIt, class _ForwardIt>
 inline _LIBCPP_INLINE_VISIBILITY

diff  --git a/libcxx/include/__memory/voidify.h b/libcxx/include/__memory/voidify.h
new file mode 100644
index 0000000000000..3a65c0e83fb7f
--- /dev/null
+++ b/libcxx/include/__memory/voidify.h
@@ -0,0 +1,30 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_VOIDIFY_H
+#define _LIBCPP___MEMORY_VOIDIFY_H
+
+#include <__config>
+#include <__memory/addressof.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <typename _Tp>
+_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 void* __voidify(_Tp& __from) {
+  // Cast away cv-qualifiers to allow modifying elements of a range through const iterators.
+  return const_cast<void*>(static_cast<const volatile void*>(_VSTD::addressof(__from)));
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___MEMORY_VOIDIFY_H

diff  --git a/libcxx/include/memory b/libcxx/include/memory
index 3898bdc9a9e38..3ed1530f2a756 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -188,10 +188,22 @@ uninitialized_copy_n(InputIterator first, Size n, ForwardIterator result);
 template <class ForwardIterator, class T>
 void uninitialized_fill(ForwardIterator first, ForwardIterator last, const T& x);
 
+template <nothrow-forward-iterator ForwardIterator, nothrow-sentinel-for<ForwardIterator> Sentinel, class T>
+  requires constructible_from<iter_value_t<ForwardIterator>, const T&>
+ForwardIterator ranges::uninitialized_fill(ForwardIterator first, Sentinel last, const T& x); // since C++20
+
+template <nothrow-forward-range ForwardRange, class T>
+  requires constructible_from<range_value_t<ForwardRange>, const T&>
+borrowed_iterator_t<ForwardRange> ranges::uninitialized_fill(ForwardRange&& range, const T& x); // since C++20
+
 template <class ForwardIterator, class Size, class T>
 ForwardIterator
 uninitialized_fill_n(ForwardIterator first, Size n, const T& x);
 
+template <nothrow-forward-iterator ForwardIterator, class T>
+  requires constructible_from<iter_value_t<ForwardIterator>, const T&>
+ForwardIterator ranges::uninitialized_fill_n(ForwardIterator first, iter_
diff erence_t<ForwardIterator> n); // since C++20
+
 template <class T, class ...Args>
 constexpr T* construct_at(T* location, Args&& ...args); // since C++20
 
@@ -213,9 +225,21 @@ template <class InputIterator, class Size, class ForwardIterator>
 template <class ForwardIterator>
  void uninitialized_value_construct(ForwardIterator first, ForwardIterator last);
 
+template <nothrow-forward-iterator ForwardIterator, nothrow-sentinel-for<ForwardIterator> Sentinel>
+  requires default_initializable<iter_value_t<ForwardIterator>>
+ ForwardIterator ranges::uninitialized_value_construct(ForwardIterator first, Sentinel last); // since C++20
+
+template <nothrow-forward-range ForwardRange>
+  requires default_initializable<range_value_t<ForwardRange>>
+ borrowed_iterator_t<ForwardRange> ranges::uninitialized_value_construct(ForwardRange&& r); // since C++20
+
 template <class ForwardIterator, class Size>
  ForwardIterator uninitialized_value_construct_n(ForwardIterator first, Size n);
 
+template <nothrow-forward-iterator ForwardIterator>
+  requires default_initializable<iter_value_t<ForwardIterator>>
+ ForwardIterator ranges::uninitialized_value_construct_n(ForwardIterator first, iter_
diff erence_t<ForwardIterator> n); // since C++20
+
 template <class ForwardIterator>
  void uninitialized_default_construct(ForwardIterator first, ForwardIterator last);
 

diff  --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 1cd5c837e6299..12e0a15f52f58 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -650,6 +650,7 @@ module std [system] {
       module uninitialized_algorithms        { private header "__memory/uninitialized_algorithms.h" }
       module unique_ptr                      { private header "__memory/unique_ptr.h" }
       module uses_allocator                  { private header "__memory/uses_allocator.h" }
+      module voidify                         { private header "__memory/voidify.h" }
     }
   }
   module mutex {

diff  --git a/libcxx/test/libcxx/diagnostics/detail.headers/memory/voidify.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/memory/voidify.module.verify.cpp
new file mode 100644
index 0000000000000..a8fa2dd399132
--- /dev/null
+++ b/libcxx/test/libcxx/diagnostics/detail.headers/memory/voidify.module.verify.cpp
@@ -0,0 +1,15 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: modules-build
+
+// WARNING: This test was generated by 'generate_private_header_tests.py'
+// and should not be edited manually.
+
+// expected-error@*:* {{use of private header from outside its module: '__memory/voidify.h'}}
+#include <__memory/voidify.h>

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/buffer.h b/libcxx/test/std/utilities/memory/specialized.algorithms/buffer.h
new file mode 100644
index 0000000000000..1114fdc68e724
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/buffer.h
@@ -0,0 +1,25 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_TEST_STD_UTILITIES_MEMORY_SPECIALIZED_ALGORITHMS_BUFFER_H
+#define LIBCPP_TEST_STD_UTILITIES_MEMORY_SPECIALIZED_ALGORITHMS_BUFFER_H
+
+template <typename T, int N>
+struct Buffer {
+  alignas(T) char buffer[sizeof(T) * N] = {};
+
+  T* begin() { return reinterpret_cast<T*>(buffer); }
+  T* end() { return begin() + N; }
+  const T* cbegin() const { return reinterpret_cast<const T*>(buffer); }
+  const T* cend() const { return cbegin() + N; }
+
+  constexpr int size() const { return N; }
+};
+
+#endif // LIBCPP_TEST_STD_UTILITIES_MEMORY_SPECIALIZED_ALGORITHMS_BUFFER_H

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/counted.h b/libcxx/test/std/utilities/memory/specialized.algorithms/counted.h
new file mode 100644
index 0000000000000..384fa7d78f4b9
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/counted.h
@@ -0,0 +1,62 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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_TEST_STD_UTILITIES_MEMORY_SPECIALIZED_ALGORITHMS_COUNTED_H
+#define LIBCPP_TEST_STD_UTILITIES_MEMORY_SPECIALIZED_ALGORITHMS_COUNTED_H
+
+#include "test_macros.h"
+
+struct Counted {
+  static int current_objects;
+  static int total_objects;
+  static int throw_on;
+
+  int value;
+
+  explicit Counted() {
+    check_throw();
+    increase_counters();
+  }
+
+  explicit Counted(int v) : value(v) {
+    check_throw();
+    increase_counters();
+  }
+
+  ~Counted() { --current_objects; }
+
+  static void reset() {
+    current_objects = total_objects = 0;
+    throw_on = -1;
+  }
+
+  Counted(const Counted& rhs) : value(rhs.value) {
+    check_throw();
+    increase_counters();
+  }
+
+  friend void operator&(Counted) = delete;
+
+private:
+  void check_throw() {
+    if (throw_on == total_objects) {
+      TEST_THROW(1);
+    }
+  }
+
+  void increase_counters() {
+    ++current_objects;
+    ++total_objects;
+  }
+};
+int Counted::current_objects = 0;
+int Counted::total_objects = 0;
+int Counted::throw_on = -1;
+
+#endif // LIBCPP_TEST_STD_UTILITIES_MEMORY_SPECIALIZED_ALGORITHMS_COUNTED_H

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 8552850fa3d67..04ee5b8e4d6eb 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
@@ -25,47 +25,11 @@
 #include <ranges>
 #include <type_traits>
 
+#include "../buffer.h"
+#include "../counted.h"
 #include "test_macros.h"
 #include "test_iterators.h"
 
-struct Counted {
-  static int current_objects;
-  static int total_objects;
-  static int throw_on;
-
-  explicit Counted() {
-    if (throw_on == total_objects) {
-      TEST_THROW(1);
-    }
-
-    ++current_objects;
-    ++total_objects;
-  }
-
-  ~Counted() { --current_objects; }
-
-  static void reset() {
-    current_objects = total_objects = 0;
-    throw_on = -1;
-  }
-
-  Counted(Counted const&) = delete;
-  friend void operator&(Counted) = delete;
-};
-int Counted::current_objects = 0;
-int Counted::total_objects = 0;
-int Counted::throw_on = -1;
-
-template <typename T, int N>
-struct Buffer {
-  alignas(T) char buffer[sizeof(T) * N] = {};
-
-  T* begin() { return reinterpret_cast<T*>(buffer); }
-  T* end() { return begin() + N; }
-  const T* cbegin() const { return reinterpret_cast<const T*>(buffer); }
-  const T* cend() const { return cbegin() + N; }
-};
-
 // Because this is a variable and not a function, it's guaranteed that ADL won't be used. However,
 // implementations are allowed to use a 
diff erent mechanism to achieve this effect, so this check is
 // libc++-specific.
@@ -145,8 +109,7 @@ int main(int, char**) {
     constexpr int N = 3;
     Buffer<Counted, 5> buf;
 
-    auto counted_range = std::views::counted(buf.begin(), N);
-    std::ranges::uninitialized_default_construct(counted_range);
+    std::ranges::uninitialized_default_construct(std::views::counted(buf.begin(), N));
     assert(Counted::current_objects == N);
     assert(Counted::total_objects == N);
 

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 742b16e9cc030..904366fe315c4 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
@@ -20,47 +20,11 @@
 #include <memory>
 #include <ranges>
 
+#include "../buffer.h"
+#include "../counted.h"
 #include "test_macros.h"
 #include "test_iterators.h"
 
-struct Counted {
-  static int current_objects;
-  static int total_objects;
-  static int throw_on;
-
-  explicit Counted() {
-    if (throw_on == total_objects) {
-      TEST_THROW(1);
-    }
-
-    ++current_objects;
-    ++total_objects;
-  }
-
-  ~Counted() { --current_objects; }
-
-  static void reset() {
-    current_objects = total_objects = 0;
-    throw_on = -1;
-  }
-
-  Counted(Counted const&) = delete;
-  friend void operator&(Counted) = delete;
-};
-int Counted::current_objects = 0;
-int Counted::total_objects = 0;
-int Counted::throw_on = -1;
-
-template <typename T, int N>
-struct Buffer {
-  alignas(T) char buffer[sizeof(T) * N] = {};
-
-  T* begin() { return reinterpret_cast<T*>(buffer); }
-  T* end() { return begin() + N; }
-  const T* cbegin() const { return reinterpret_cast<const T*>(buffer); }
-  const T* cend() const { return cbegin() + N; }
-};
-
 // Because this is a variable and not a function, it's guaranteed that ADL won't be used. However,
 // implementations are allowed to use a 
diff erent mechanism to achieve this effect, so this check is
 // libc++-specific.

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct.pass.cpp
new file mode 100644
index 0000000000000..cc89c69514b93
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct.pass.cpp
@@ -0,0 +1,213 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges
+
+// <memory>
+
+// template <nothrow-forward-iterator ForwardIterator, nothrow-sentinel-for<ForwardIterator> Sentinel>
+//   requires default_initializable<iter_value_t<ForwardIterator>>
+// ForwardIterator ranges::uninitialized_value_construct(ForwardIterator first, Sentinel last);
+//
+// template <nothrow-forward-range ForwardRange>
+//   requires default_initializable<range_value_t<ForwardRange>>
+// borrowed_iterator_t<ForwardRange> ranges::uninitialized_value_construct(ForwardRange&& range);
+
+#include <cassert>
+#include <iterator>
+#include <memory>
+#include <ranges>
+#include <type_traits>
+
+#include "../buffer.h"
+#include "../counted.h"
+#include "test_macros.h"
+#include "test_iterators.h"
+
+// Because this is a variable and not a function, it's guaranteed that ADL won't be used. However,
+// implementations are allowed to use a 
diff erent mechanism to achieve this effect, so this check is
+// libc++-specific.
+LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::uninitialized_value_construct)>);
+
+struct NotDefaultCtrable {
+  NotDefaultCtrable() = delete;
+};
+static_assert(
+    !std::is_invocable_v<decltype(std::ranges::uninitialized_value_construct), NotDefaultCtrable*, NotDefaultCtrable*>);
+
+int main(int, char**) {
+  // An empty range -- no default constructors should be invoked.
+  {
+    Buffer<Counted, 1> buf;
+
+    std::ranges::uninitialized_value_construct(buf.begin(), buf.begin());
+    assert(Counted::current_objects == 0);
+    assert(Counted::total_objects == 0);
+
+    std::ranges::uninitialized_value_construct(std::ranges::empty_view<Counted>());
+    assert(Counted::current_objects == 0);
+    assert(Counted::total_objects == 0);
+
+    forward_iterator<Counted*> it(buf.begin());
+    auto range = std::ranges::subrange(it, sentinel_wrapper<forward_iterator<Counted*>>(it));
+    std::ranges::uninitialized_value_construct(range.begin(), range.end());
+    assert(Counted::current_objects == 0);
+    assert(Counted::total_objects == 0);
+
+    std::ranges::uninitialized_value_construct(range);
+    assert(Counted::current_objects == 0);
+    assert(Counted::total_objects == 0);
+  }
+
+  // A range containing several objects, (iter, sentinel) overload.
+  {
+    constexpr int N = 5;
+    Buffer<Counted, N> buf;
+
+    std::ranges::uninitialized_value_construct(buf.begin(), buf.end());
+    assert(Counted::current_objects == N);
+    assert(Counted::total_objects == N);
+
+    std::destroy(buf.begin(), buf.end());
+    Counted::reset();
+  }
+
+  // A range containing several objects, (range) overload.
+  {
+    constexpr int N = 5;
+    Buffer<Counted, N> buf;
+
+    auto range = std::ranges::subrange(buf.begin(), buf.end());
+    std::ranges::uninitialized_value_construct(range);
+    assert(Counted::current_objects == N);
+    assert(Counted::total_objects == N);
+
+    std::destroy(buf.begin(), buf.end());
+    Counted::reset();
+  }
+
+  // Using `counted_iterator`.
+  {
+    constexpr int N = 3;
+    Buffer<Counted, 5> buf;
+
+    std::ranges::uninitialized_value_construct(std::counted_iterator(buf.begin(), N), std::default_sentinel);
+    assert(Counted::current_objects == N);
+    assert(Counted::total_objects == N);
+
+    std::destroy(buf.begin(), buf.begin() + N);
+    Counted::reset();
+  }
+
+  // Using `views::counted`.
+  {
+    constexpr int N = 3;
+    Buffer<Counted, 5> buf;
+
+    std::ranges::uninitialized_value_construct(std::views::counted(buf.begin(), N));
+    assert(Counted::current_objects == N);
+    assert(Counted::total_objects == N);
+
+    std::destroy(buf.begin(), buf.begin() + N);
+    Counted::reset();
+  }
+
+  // Using `reverse_view`.
+  {
+    constexpr int N = 3;
+    Buffer<Counted, 5> buf;
+
+    auto range = std::ranges::subrange(buf.begin(), buf.begin() + N);
+    std::ranges::uninitialized_value_construct(std::ranges::reverse_view(range));
+    assert(Counted::current_objects == N);
+    assert(Counted::total_objects == N);
+
+    std::destroy(buf.begin(), buf.begin() + N);
+    Counted::reset();
+  }
+
+  // Any existing values should be overwritten by value constructors.
+  {
+    constexpr int N = 5;
+    int buffer[N] = {42, 42, 42, 42, 42};
+
+    std::ranges::uninitialized_value_construct(buffer, buffer + 1);
+    assert(buffer[0] == 0);
+    assert(buffer[1] == 42);
+
+    std::ranges::uninitialized_value_construct(buffer, buffer + N);
+    assert(buffer[0] == 0);
+    assert(buffer[1] == 0);
+    assert(buffer[2] == 0);
+    assert(buffer[3] == 0);
+    assert(buffer[4] == 0);
+  }
+
+  // An exception is thrown while objects are being created -- the existing objects should stay
+  // valid. (iterator, sentinel) overload.
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  {
+    Buffer<Counted, 5> buf;
+
+    Counted::throw_on = 3; // When constructing the fourth object (counting from one).
+    try {
+      std::ranges::uninitialized_value_construct(buf.begin(), buf.end());
+    } catch (...) {
+    }
+    assert(Counted::current_objects == 0);
+    assert(Counted::total_objects == 3);
+    std::destroy(buf.begin(), buf.begin() + Counted::total_objects);
+    Counted::reset();
+  }
+
+  // An exception is thrown while objects are being created -- the existing objects should stay
+  // valid. (range) overload.
+  {
+    Buffer<Counted, 5> buf;
+
+    Counted::throw_on = 3; // When constructing the fourth object.
+    try {
+      auto range = std::ranges::subrange(buf.begin(), buf.end());
+      std::ranges::uninitialized_value_construct(range);
+    } catch (...) {
+    }
+    assert(Counted::current_objects == 0);
+    assert(Counted::total_objects == 3);
+    std::destroy(buf.begin(), buf.begin() + Counted::total_objects);
+    Counted::reset();
+  }
+#endif // TEST_HAS_NO_EXCEPTIONS
+
+  // Works with const iterators, (iter, sentinel) overload.
+  {
+    constexpr int N = 5;
+    Buffer<Counted, N> buf;
+
+    std::ranges::uninitialized_value_construct(buf.cbegin(), buf.cend());
+    assert(Counted::current_objects == N);
+    assert(Counted::total_objects == N);
+    std::destroy(buf.begin(), buf.end());
+    Counted::reset();
+  }
+
+  // Works with const iterators, (range) overload.
+  {
+    constexpr int N = 5;
+    Buffer<Counted, N> buf;
+
+    auto range = std::ranges::subrange(buf.cbegin(), buf.cend());
+    std::ranges::uninitialized_value_construct(range);
+    assert(Counted::current_objects == N);
+    assert(Counted::total_objects == N);
+    std::destroy(buf.begin(), buf.end());
+    Counted::reset();
+  }
+
+  return 0;
+}

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct_n.pass.cpp
new file mode 100644
index 0000000000000..3207f286d14b7
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct_n.pass.cpp
@@ -0,0 +1,110 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges
+
+// <memory>
+
+// template <nothrow-forward-iterator ForwardIterator>
+//   requires default_initializable<iter_value_t<ForwardIterator>>
+// ForwardIterator ranges::uninitialized_value_construct_n(ForwardIterator first,
+//     iter_
diff erence_t<ForwardIterator> n);
+
+#include <cassert>
+#include <memory>
+#include <ranges>
+
+#include "../buffer.h"
+#include "../counted.h"
+#include "test_macros.h"
+#include "test_iterators.h"
+
+// Because this is a variable and not a function, it's guaranteed that ADL won't be used. However,
+// implementations are allowed to use a 
diff erent mechanism to achieve this effect, so this check is
+// libc++-specific.
+LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::uninitialized_value_construct_n)>);
+
+struct NotDefaultCtrable {
+  NotDefaultCtrable() = delete;
+};
+static_assert(!std::is_invocable_v<decltype(std::ranges::uninitialized_value_construct_n), NotDefaultCtrable*, int>);
+
+int main(int, char**) {
+  // An empty range -- no default constructors should be invoked.
+  {
+    Buffer<Counted, 1> buf;
+
+    std::ranges::uninitialized_value_construct_n(buf.begin(), 0);
+    assert(Counted::current_objects == 0);
+    assert(Counted::total_objects == 0);
+  }
+
+  // A range containing several objects.
+  {
+    constexpr int N = 5;
+    Buffer<Counted, N> buf;
+
+    std::ranges::uninitialized_value_construct_n(buf.begin(), N);
+    assert(Counted::current_objects == N);
+    assert(Counted::total_objects == N);
+
+    std::destroy(buf.begin(), buf.end());
+    Counted::reset();
+  }
+
+  // Any existing values should be overwritten by value constructors.
+  {
+    constexpr int N = 5;
+    int buffer[N] = {42, 42, 42, 42, 42};
+
+    std::ranges::uninitialized_value_construct_n(buffer, 1);
+    assert(buffer[0] == 0);
+    assert(buffer[1] == 42);
+
+    std::ranges::uninitialized_value_construct_n(buffer, N);
+    assert(buffer[0] == 0);
+    assert(buffer[1] == 0);
+    assert(buffer[2] == 0);
+    assert(buffer[3] == 0);
+    assert(buffer[4] == 0);
+  }
+
+  // An exception is thrown while objects are being created -- the existing objects should stay
+  // valid.
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  {
+    constexpr int N = 5;
+    Buffer<Counted, N> buf;
+
+    Counted::throw_on = 3; // When constructing the fourth object (counting from one).
+    try {
+      std::ranges::uninitialized_value_construct_n(buf.begin(), N);
+    } catch (...) {
+    }
+    assert(Counted::current_objects == 0);
+    assert(Counted::total_objects == 3);
+    std::destroy(buf.begin(), buf.begin() + Counted::total_objects);
+    Counted::reset();
+  }
+#endif // TEST_HAS_NO_EXCEPTIONS
+
+  // Works with const iterators.
+  {
+    constexpr int N = 5;
+    Buffer<Counted, N> buf;
+
+    std::ranges::uninitialized_value_construct_n(buf.cbegin(), N);
+    assert(Counted::current_objects == N);
+    assert(Counted::total_objects == N);
+    std::destroy(buf.begin(), buf.end());
+    Counted::reset();
+  }
+
+  return 0;
+}

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/ranges_uninitialized_fill_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/ranges_uninitialized_fill_n.pass.cpp
new file mode 100644
index 0000000000000..a93ab532f2cea
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/ranges_uninitialized_fill_n.pass.cpp
@@ -0,0 +1,120 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges
+
+// <memory>
+
+// template <nothrow-forward-iterator ForwardIterator, class T>
+//   requires constructible_from<iter_value_t<ForwardIterator>, const T&>
+// ForwardIterator ranges::uninitialized_fill_n(ForwardIterator first, iter_
diff erence_t<ForwardIterator> n);
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <memory>
+#include <ranges>
+#include <type_traits>
+
+#include "../buffer.h"
+#include "../counted.h"
+#include "test_macros.h"
+#include "test_iterators.h"
+
+// Because this is a variable and not a function, it's guaranteed that ADL won't be used. However,
+// implementations are allowed to use a 
diff erent mechanism to achieve this effect, so this check is
+// libc++-specific.
+LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::uninitialized_fill_n)>);
+
+struct NotConvertibleFromInt {};
+static_assert(!std::is_invocable_v<decltype(std::ranges::uninitialized_fill_n), NotConvertibleFromInt*,
+                                   NotConvertibleFromInt*, int>);
+
+int main(int, char**) {
+  constexpr int value = 42;
+  Counted x(value);
+  Counted::reset();
+  auto pred = [](const Counted& e) { return e.value == value; };
+
+  // An empty range -- no default constructors should be invoked.
+  {
+    Buffer<Counted, 1> buf;
+
+    std::ranges::uninitialized_fill_n(buf.begin(), 0, x);
+    assert(Counted::current_objects == 0);
+    assert(Counted::total_objects == 0);
+  }
+
+  // A range containing several objects.
+  {
+    constexpr int N = 5;
+    Buffer<Counted, N> buf;
+
+    std::ranges::uninitialized_fill_n(buf.begin(), N, x);
+    assert(Counted::current_objects == N);
+    assert(Counted::total_objects == N);
+    assert(std::all_of(buf.begin(), buf.end(), pred));
+
+    std::destroy(buf.begin(), buf.end());
+    Counted::reset();
+  }
+
+  // Any existing values should be overwritten by value constructors.
+  {
+    constexpr int N = 5;
+    int buffer[N] = {value, value, value, value, value};
+
+    std::ranges::uninitialized_fill_n(buffer, 1, 0);
+    assert(buffer[0] == 0);
+    assert(buffer[1] == value);
+
+    std::ranges::uninitialized_fill_n(buffer, N, 0);
+    assert(buffer[0] == 0);
+    assert(buffer[1] == 0);
+    assert(buffer[2] == 0);
+    assert(buffer[3] == 0);
+    assert(buffer[4] == 0);
+  }
+
+  // An exception is thrown while objects are being created -- the existing objects should stay
+  // valid. (iterator, sentinel) overload.
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  {
+    constexpr int N = 5;
+    Buffer<Counted, N> buf;
+
+    Counted::throw_on = 3; // When constructing the fourth object.
+    try {
+      std::ranges::uninitialized_fill_n(buf.begin(), N, x);
+    } catch (...) {
+    }
+    assert(Counted::current_objects == 0);
+    assert(Counted::total_objects == 3);
+
+    std::destroy(buf.begin(), buf.begin() + 3);
+    Counted::reset();
+  }
+#endif // TEST_HAS_NO_EXCEPTIONS
+
+  // Works with const iterators.
+  {
+    constexpr int N = 5;
+    Buffer<Counted, N> buf;
+
+    std::ranges::uninitialized_fill_n(buf.cbegin(), N, x);
+    assert(Counted::current_objects == N);
+    assert(Counted::total_objects == N);
+    assert(std::all_of(buf.begin(), buf.end(), pred));
+
+    std::destroy(buf.begin(), buf.end());
+    Counted::reset();
+  }
+
+  return 0;
+}

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/ranges_uninitialized_fill.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/ranges_uninitialized_fill.pass.cpp
new file mode 100644
index 0000000000000..d9135a036eebb
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/ranges_uninitialized_fill.pass.cpp
@@ -0,0 +1,232 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges
+
+// <memory>
+
+// template <nothrow-forward-iterator ForwardIterator, nothrow-sentinel-for<ForwardIterator> Sentinel, class T>
+//   requires constructible_from<iter_value_t<ForwardIterator>, const T&>
+// ForwardIterator ranges::uninitialized_fill(ForwardIterator first, Sentinel last, const T& x);
+//
+// template <nothrow-forward-range ForwardRange, class T>
+//   requires constructible_from<range_value_t<ForwardRange>, const T&>
+// borrowed_iterator_t<ForwardRange> ranges::uninitialized_fill(ForwardRange&& range, const T& x);
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <memory>
+#include <ranges>
+#include <type_traits>
+
+#include "../buffer.h"
+#include "../counted.h"
+#include "test_macros.h"
+#include "test_iterators.h"
+
+// Because this is a variable and not a function, it's guaranteed that ADL won't be used. However,
+// implementations are allowed to use a 
diff erent mechanism to achieve this effect, so this check is
+// libc++-specific.
+LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::uninitialized_fill)>);
+
+struct NotConvertibleFromInt {};
+static_assert(!std::is_invocable_v<decltype(std::ranges::uninitialized_fill), NotConvertibleFromInt*,
+                                   NotConvertibleFromInt*, int>);
+
+int main(int, char**) {
+  constexpr int value = 42;
+  Counted x(value);
+  Counted::reset();
+  auto pred = [](const Counted& e) { return e.value == value; };
+
+  // An empty range -- no default constructors should be invoked.
+  {
+    Buffer<Counted, 1> buf;
+
+    std::ranges::uninitialized_fill(buf.begin(), buf.begin(), x);
+    assert(Counted::current_objects == 0);
+    assert(Counted::total_objects == 0);
+
+    std::ranges::uninitialized_fill(std::ranges::empty_view<Counted>(), x);
+    assert(Counted::current_objects == 0);
+    assert(Counted::total_objects == 0);
+
+    forward_iterator<Counted*> it(buf.begin());
+    auto range = std::ranges::subrange(it, sentinel_wrapper<forward_iterator<Counted*>>(it));
+    std::ranges::uninitialized_fill(range.begin(), range.end(), x);
+    assert(Counted::current_objects == 0);
+    assert(Counted::total_objects == 0);
+    Counted::reset();
+
+    std::ranges::uninitialized_fill(range, x);
+    assert(Counted::current_objects == 0);
+    assert(Counted::total_objects == 0);
+    Counted::reset();
+  }
+
+  // A range containing several objects, (iter, sentinel) overload.
+  {
+    constexpr int N = 5;
+    Buffer<Counted, N> buf;
+
+    std::ranges::uninitialized_fill(buf.begin(), buf.end(), x);
+    assert(Counted::current_objects == N);
+    assert(Counted::total_objects == N);
+    assert(std::all_of(buf.begin(), buf.end(), pred));
+
+    std::destroy(buf.begin(), buf.end());
+    Counted::reset();
+  }
+
+  // A range containing several objects, (range) overload.
+  {
+    constexpr int N = 5;
+    Buffer<Counted, N> buf;
+
+    auto range = std::ranges::subrange(buf.begin(), buf.end());
+    std::ranges::uninitialized_fill(range, x);
+    assert(Counted::current_objects == N);
+    assert(Counted::total_objects == N);
+    assert(std::all_of(buf.begin(), buf.end(), pred));
+
+    std::destroy(buf.begin(), buf.end());
+    Counted::reset();
+  }
+
+  // Using `counted_iterator`.
+  {
+    constexpr int N = 3;
+    Buffer<Counted, 5> buf;
+
+    std::ranges::uninitialized_fill(std::counted_iterator(buf.begin(), N), std::default_sentinel, x);
+    assert(Counted::current_objects == N);
+    assert(Counted::total_objects == N);
+    assert(std::all_of(buf.begin(), buf.begin() + N, pred));
+
+    std::destroy(buf.begin(), buf.begin() + N);
+    Counted::reset();
+  }
+
+  // Using `views::counted`.
+  {
+    constexpr int N = 3;
+    Buffer<Counted, 5> buf;
+
+    std::ranges::uninitialized_fill(std::views::counted(buf.begin(), N), x);
+    assert(Counted::current_objects == N);
+    assert(Counted::total_objects == N);
+    assert(std::all_of(buf.begin(), buf.begin() + N, pred));
+
+    std::destroy(buf.begin(), buf.begin() + N);
+    Counted::reset();
+  }
+
+  // Using `reverse_view`.
+  {
+    constexpr int N = 3;
+    Buffer<Counted, 5> buf;
+
+    auto range = std::ranges::subrange(buf.begin(), buf.begin() + N);
+    std::ranges::uninitialized_fill(std::ranges::reverse_view(range), x);
+    assert(Counted::current_objects == N);
+    assert(Counted::total_objects == N);
+    assert(std::all_of(buf.begin(), buf.begin() + N, pred));
+
+    std::destroy(buf.begin(), buf.begin() + N);
+    Counted::reset();
+  }
+
+  // Any existing values should be overwritten by value constructors.
+  {
+    constexpr int N = 5;
+    int buffer[N] = {value, value, value, value, value};
+
+    std::ranges::uninitialized_fill(buffer, buffer + 1, 0);
+    assert(buffer[0] == 0);
+    assert(buffer[1] == value);
+
+    std::ranges::uninitialized_fill(buffer, buffer + N, 0);
+    assert(buffer[0] == 0);
+    assert(buffer[1] == 0);
+    assert(buffer[2] == 0);
+    assert(buffer[3] == 0);
+    assert(buffer[4] == 0);
+  }
+
+  // An exception is thrown while objects are being created -- the existing objects should stay
+  // valid. (iterator, sentinel) overload.
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  {
+    constexpr int N = 3;
+    Buffer<Counted, 5> buf;
+
+    Counted::throw_on = N; // When constructing the fourth object.
+    try {
+      std::ranges::uninitialized_fill(buf.begin(), buf.end(), x);
+    } catch (...) {
+    }
+    assert(Counted::current_objects == 0);
+    assert(Counted::total_objects == N);
+
+    std::destroy(buf.begin(), buf.begin() + N);
+    Counted::reset();
+  }
+
+  // An exception is thrown while objects are being created -- the existing objects should stay
+  // valid. (range) overload.
+  {
+    constexpr int N = 3;
+    Buffer<Counted, 5> buf;
+
+    Counted::throw_on = N; // When constructing the fourth object.
+    try {
+      auto range = std::ranges::subrange(buf.begin(), buf.end());
+      std::ranges::uninitialized_fill(range, x);
+    } catch (...) {
+    }
+    assert(Counted::current_objects == 0);
+    assert(Counted::total_objects == N);
+
+    std::destroy(buf.begin(), buf.begin() + N);
+    Counted::reset();
+  }
+#endif // TEST_HAS_NO_EXCEPTIONS
+
+  // Works with const iterators, (iter, sentinel) overload.
+  {
+    constexpr int N = 5;
+    Buffer<Counted, N> buf;
+
+    std::ranges::uninitialized_fill(buf.cbegin(), buf.cend(), x);
+    assert(Counted::current_objects == N);
+    assert(Counted::total_objects == N);
+    assert(std::all_of(buf.begin(), buf.end(), pred));
+
+    std::destroy(buf.begin(), buf.end());
+    Counted::reset();
+  }
+
+  // Works with const iterators, (range) overload.
+  {
+    constexpr int N = 5;
+    Buffer<Counted, N> buf;
+
+    auto range = std::ranges::subrange(buf.cbegin(), buf.cend());
+    std::ranges::uninitialized_fill(range, x);
+    assert(Counted::current_objects == N);
+    assert(Counted::total_objects == N);
+    assert(std::all_of(buf.begin(), buf.end(), pred));
+
+    std::destroy(buf.begin(), buf.end());
+    Counted::reset();
+  }
+
+  return 0;
+}


        


More information about the libcxx-commits mailing list