libstdc++: Implement ranges [specialized.algorithms]
authorPatrick Palka <ppalka@redhat.com>
Wed, 12 Feb 2020 17:54:47 +0000 (12:54 -0500)
committerPatrick Palka <ppalka@redhat.com>
Thu, 13 Feb 2020 20:22:28 +0000 (15:22 -0500)
This implements all the ranges members defined in [specialized.algorithms]:

  ranges::uninitialized_default_construct
  ranges::uninitialized_value_construct
  ranges::uninitialized_copy
  ranges::uninitialized_copy_n
  ranges::uninitialized_move
  ranges::uninitialized_move_n
  ranges::uninitialized_fill
  ranges::uninitialized_fill_n
  ranges::construct_at
  ranges::destroy_at
  ranges::destroy

It also implements (hopefully correctly) the "obvious" optimizations for these
algos, namely that if the output range has a trivial value type and if the
appropriate operation won't throw then we can dispatch to the standard ranges
version of the algorithm which will then potentially enable further
optimizations.

libstdc++-v3/ChangeLog:

* include/Makefile.am: Add <bits/ranges_uninitialized.h>.
* include/Makefile.in: Regenerate.
* include/bits/ranges_uninitialized.h: New header.
* include/std/memory: Include it.
* testsuite/20_util/specialized_algorithms/destroy/constrained.cc: New
test.
* .../uninitialized_copy/constrained.cc: New test.
* .../uninitialized_default_construct/constrained.cc: New test.
* .../uninitialized_fill/constrained.cc: New test.
* .../uninitialized_move/constrained.cc: New test.
* .../uninitialized_value_construct/constrained.cc: New test.

libstdc++-v3/ChangeLog
libstdc++-v3/include/Makefile.am
libstdc++-v3/include/Makefile.in
libstdc++-v3/include/bits/ranges_uninitialized.h [new file with mode: 0644]
libstdc++-v3/include/std/memory
libstdc++-v3/testsuite/20_util/specialized_algorithms/destroy/constrained.cc [new file with mode: 0644]
libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_copy/constrained.cc [new file with mode: 0644]
libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_default_construct/constrained.cc [new file with mode: 0644]
libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_fill/constrained.cc [new file with mode: 0644]
libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_move/constrained.cc [new file with mode: 0644]
libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct/constrained.cc [new file with mode: 0644]

index 8715e50daba4b7f97dc2bf3dcbcf6b17f7b3780d..d1e5aad1b68cdb9438ee6e39bb0f7779a642e195 100644 (file)
@@ -1,5 +1,17 @@
 2020-02-13  Patrick Palka  <ppalka@redhat.com>
 
+       * include/Makefile.am: Add <bits/ranges_uninitialized.h>.
+       * include/Makefile.in: Regenerate.
+       * include/bits/ranges_uninitialized.h: New header.
+       * include/std/memory: Include it.
+       * testsuite/20_util/specialized_algorithms/destroy/constrained.cc: New
+       test.
+       * .../uninitialized_copy/constrained.cc: New test.
+       * .../uninitialized_default_construct/constrained.cc: New test.
+       * .../uninitialized_fill/constrained.cc: New test.
+       * .../uninitialized_move/constrained.cc: New test.
+       * .../uninitialized_value_construct/constrained.cc: New test.
+
        * include/Makefile.am: Add bits/ranges_algobase.h
        * include/Makefile.in: Regenerate.
        * bits/ranges_algo.h: Include <bits/ranges_algobase.h> and refactor
index 614222db400b7a557702ccae76b9d71b8f28aed3..e131ce04f8cae133b8f3880d2a52539b04017c12 100644 (file)
@@ -159,6 +159,7 @@ bits_headers = \
        ${bits_srcdir}/range_cmp.h \
        ${bits_srcdir}/ranges_algobase.h \
        ${bits_srcdir}/ranges_algo.h \
+       ${bits_srcdir}/ranges_uninitialized.h \
        ${bits_srcdir}/refwrap.h \
        ${bits_srcdir}/regex.h \
        ${bits_srcdir}/regex.tcc \
index 7ee6a1e3f61abd526a6a72064454aa4cbf26d05a..ae20f6b1d212ab30cc1c34e787a6d2610e8540ea 100644 (file)
@@ -504,6 +504,7 @@ bits_headers = \
        ${bits_srcdir}/range_cmp.h \
        ${bits_srcdir}/ranges_algobase.h \
        ${bits_srcdir}/ranges_algo.h \
+       ${bits_srcdir}/ranges_uninitialized.h \
        ${bits_srcdir}/refwrap.h \
        ${bits_srcdir}/regex.h \
        ${bits_srcdir}/regex.tcc \
diff --git a/libstdc++-v3/include/bits/ranges_uninitialized.h b/libstdc++-v3/include/bits/ranges_uninitialized.h
new file mode 100644 (file)
index 0000000..5ff2eaa
--- /dev/null
@@ -0,0 +1,491 @@
+// Raw memory manipulators -*- C++ -*-
+
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file bits/ranges_uninitialized.h
+ *  This is an internal header file, included by other library headers.
+ *  Do not attempt to use it directly. @headername{memory}
+ */
+
+#ifndef _RANGES_UNINITIALIZED_H
+#define _RANGES_UNINITIALIZED_H 1
+
+#if __cplusplus > 201703L
+#if __cpp_lib_concepts
+
+#include <bits/ranges_algobase.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+namespace ranges
+{
+  namespace __detail
+  {
+    template<typename _Tp>
+      constexpr void*
+      __voidify(_Tp& __obj) noexcept
+      {
+       return const_cast<void*>
+                (static_cast<const volatile void*>(std::__addressof(__obj)));
+      }
+
+    template<typename _Iter>
+      concept __nothrow_input_iterator
+       = (input_iterator<_Iter>
+          && is_lvalue_reference_v<iter_reference_t<_Iter>>
+          && same_as<remove_cvref_t<iter_reference_t<_Iter>>,
+                     iter_value_t<_Iter>>);
+
+    template<typename _Sent, typename _Iter>
+      concept __nothrow_sentinel = sentinel_for<_Sent, _Iter>;
+
+    template<typename _Range>
+      concept __nothrow_input_range
+       = (range<_Range>
+          && __nothrow_input_iterator<iterator_t<_Range>>
+          && __nothrow_sentinel<sentinel_t<_Range>, iterator_t<_Range>>);
+
+    template<typename _Iter>
+      concept __nothrow_forward_iterator
+       = (__nothrow_input_iterator<_Iter>
+          && forward_iterator<_Iter>
+          && __nothrow_sentinel<_Iter, _Iter>);
+
+    template<typename _Range>
+      concept __nothrow_forward_range
+       = (__nothrow_input_range<_Range>
+          && __nothrow_forward_iterator<iterator_t<_Range>>);
+  } // namespace __detail
+
+  template<__detail::__nothrow_input_iterator _Iter,
+          __detail::__nothrow_sentinel<_Iter> _Sent>
+    requires destructible<iter_value_t<_Iter>>
+    constexpr _Iter
+    destroy(_Iter __first, _Sent __last) noexcept;
+
+  namespace __detail
+  {
+    template<typename _Iter>
+      requires destructible<iter_value_t<_Iter>>
+      struct _DestroyGuard
+      {
+      private:
+       _Iter _M_first;
+       const _Iter* _M_cur;
+
+      public:
+       explicit
+       _DestroyGuard(const _Iter* __iter)
+         : _M_first(*__iter), _M_cur(__iter)
+       { }
+
+       void
+       release() noexcept
+       { _M_cur = nullptr; }
+
+       ~_DestroyGuard()
+       {
+         if (_M_cur != nullptr)
+           ranges::destroy(std::move(_M_first), *_M_cur);
+       }
+      };
+
+    template<typename _Iter>
+      requires destructible<iter_value_t<_Iter>>
+       && is_trivially_destructible_v<iter_value_t<_Iter>>
+      struct _DestroyGuard<_Iter>
+      {
+       explicit
+       _DestroyGuard(const _Iter*)
+       { }
+
+       void
+       release() noexcept
+       { }
+      };
+  } // namespace __detail
+
+  template<__detail::__nothrow_forward_iterator _Iter,
+          __detail::__nothrow_sentinel<_Iter> _Sent>
+    requires default_initializable<iter_value_t<_Iter>>
+    _Iter
+    uninitialized_default_construct(_Iter __first, _Sent __last)
+    {
+      using _ValueType = remove_reference_t<iter_reference_t<_Iter>>;
+      if constexpr (is_trivially_default_constructible_v<_ValueType>)
+       return ranges::next(__first, __last);
+      else
+       {
+         auto __guard = __detail::_DestroyGuard(&__first);
+         for (; __first != __last; ++__first)
+           ::new (__detail::__voidify(*__first)) _ValueType;
+         __guard.release();
+         return __first;
+       }
+    }
+
+  template<__detail::__nothrow_forward_range _Range>
+    requires default_initializable<range_value_t<_Range>>
+    safe_iterator_t<_Range>
+    uninitialized_default_construct(_Range&& __r)
+    {
+      return ranges::uninitialized_default_construct(ranges::begin(__r),
+                                                    ranges::end(__r));
+    }
+
+  template<__detail::__nothrow_forward_iterator _Iter>
+    requires default_initializable<iter_value_t<_Iter>>
+    _Iter
+    uninitialized_default_construct_n(_Iter __first,
+                                     iter_difference_t<_Iter> __n)
+    {
+      using _ValueType = remove_reference_t<iter_reference_t<_Iter>>;
+      if constexpr (is_trivially_default_constructible_v<_ValueType>)
+       return ranges::next(__first, __n);
+      else
+       {
+         auto __guard = __detail::_DestroyGuard(&__first);
+         for (; __n > 0; ++__first, (void) --__n)
+           ::new (__detail::__voidify(*__first)) _ValueType;
+         __guard.release();
+         return __first;
+       }
+    }
+
+  template<__detail::__nothrow_forward_iterator _Iter,
+          __detail::__nothrow_sentinel<_Iter> _Sent>
+    requires default_initializable<iter_value_t<_Iter>>
+    _Iter
+    uninitialized_value_construct(_Iter __first, _Sent __last)
+    {
+      using _ValueType = remove_reference_t<iter_reference_t<_Iter>>;
+      if constexpr (is_trivial_v<_ValueType>
+                   && is_copy_assignable_v<_ValueType>)
+       return ranges::fill(__first, __last, _ValueType());
+      else
+       {
+         auto __guard = __detail::_DestroyGuard(&__first);
+         for (; __first != __last; ++__first)
+           ::new (__detail::__voidify(*__first)) _ValueType();
+         __guard.release();
+         return __first;
+       }
+    }
+
+  template<__detail::__nothrow_forward_range _Range>
+    requires default_initializable<range_value_t<_Range>>
+    safe_iterator_t<_Range>
+    uninitialized_value_construct(_Range&& __r)
+    {
+      return ranges::uninitialized_value_construct(ranges::begin(__r),
+                                                  ranges::end(__r));
+    }
+
+  template<__detail::__nothrow_forward_iterator _Iter>
+    requires default_initializable<iter_value_t<_Iter>>
+    _Iter
+    uninitialized_value_construct_n(_Iter __first, iter_difference_t<_Iter> __n)
+    {
+      using _ValueType = remove_reference_t<iter_reference_t<_Iter>>;
+      if constexpr (is_trivial_v<_ValueType>
+                   && is_copy_assignable_v<_ValueType>)
+       return ranges::fill_n(__first, __n, _ValueType());
+      else
+       {
+         auto __guard = __detail::_DestroyGuard(&__first);
+         for (; __n > 0; ++__first, (void) --__n)
+           ::new (__detail::__voidify(*__first)) _ValueType();
+         __guard.release();
+         return __first;
+       }
+    }
+
+  template<typename _Iter, typename _Out>
+    using uninitialized_copy_result = copy_result<_Iter, _Out>;
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _ISent,
+          __detail::__nothrow_forward_iterator _Out,
+          __detail::__nothrow_sentinel<_Out> _OSent>
+    requires constructible_from<iter_value_t<_Out>, iter_reference_t<_Iter>>
+    uninitialized_copy_result<_Iter, _Out>
+    uninitialized_copy(_Iter __ifirst, _ISent __ilast,
+                      _Out __ofirst, _OSent __olast)
+    {
+      using _OutType = remove_reference_t<iter_reference_t<_Out>>;
+      if constexpr (sized_sentinel_for<_ISent, _Iter>
+                   && sized_sentinel_for<_OSent, _Out>
+                   && is_trivial_v<_OutType>
+                   && is_nothrow_assignable_v<_OutType,
+                                              iter_reference_t<_Iter>>)
+       {
+         auto __d1 = ranges::distance(__ifirst, __ilast);
+         auto __d2 = ranges::distance(__ofirst, __olast);
+         return ranges::copy_n(__ifirst, std::min(__d1, __d2), __ofirst);
+       }
+      else
+       {
+         auto __guard = __detail::_DestroyGuard(&__ofirst);
+         for (; __ifirst != __ilast && __ofirst != __olast;
+              ++__ofirst, (void)++__ifirst)
+           ::new (__detail::__voidify(*__ofirst)) _OutType(*__ifirst);
+         __guard.release();
+         return {__ifirst, __ofirst};
+       }
+    }
+
+  template<input_range _IRange, __detail::__nothrow_forward_range _ORange>
+    requires constructible_from<range_value_t<_ORange>,
+                               range_reference_t<_IRange>>
+    uninitialized_copy_result<safe_iterator_t<_IRange>,
+                             safe_iterator_t<_ORange>>
+    uninitialized_copy(_IRange&& __inr, _ORange&& __outr)
+    {
+      return ranges::uninitialized_copy(ranges::begin(__inr),
+                                       ranges::end(__inr),
+                                       ranges::begin(__outr),
+                                       ranges::end(__outr));
+    }
+
+  template<typename _Iter, typename _Out>
+    using uninitialized_copy_n_result = uninitialized_copy_result<_Iter, _Out>;
+
+    template<input_iterator _Iter, __detail::__nothrow_forward_iterator _Out,
+      __detail::__nothrow_sentinel<_Out> _Sent>
+    requires constructible_from<iter_value_t<_Out>, iter_reference_t<_Iter>>
+    uninitialized_copy_n_result<_Iter, _Out>
+    uninitialized_copy_n(_Iter __ifirst, iter_difference_t<_Iter> __n,
+                        _Out __ofirst, _Sent __olast)
+    {
+      using _OutType = remove_reference_t<iter_reference_t<_Out>>;
+      if constexpr (sized_sentinel_for<_Sent, _Out>
+                   && is_trivial_v<_OutType>
+                   && is_nothrow_assignable_v<_OutType,
+                                              iter_reference_t<_Iter>>)
+       {
+         auto __d = ranges::distance(__ofirst, __olast);
+         return ranges::copy_n(__ifirst, std::min(__n, __d), __ofirst);
+       }
+      else
+       {
+         auto __guard = __detail::_DestroyGuard(&__ofirst);
+         for (; __n > 0 && __ofirst != __olast;
+              ++__ofirst, (void)++__ifirst, (void)--__n)
+           ::new (__detail::__voidify(*__ofirst)) _OutType(*__ifirst);
+         __guard.release();
+         return {__ifirst, __ofirst};
+       }
+    }
+
+  template<typename _Iter, typename _Out>
+    using uninitialized_move_result = uninitialized_copy_result<_Iter, _Out>;
+
+  template<input_iterator _Iter, sentinel_for<_Iter> _ISent,
+          __detail::__nothrow_forward_iterator _Out,
+          __detail::__nothrow_sentinel<_Out> _OSent>
+    requires constructible_from<iter_value_t<_Out>,
+                               iter_rvalue_reference_t<_Iter>>
+    uninitialized_move_result<_Iter, _Out>
+    uninitialized_move(_Iter __ifirst, _ISent __ilast,
+                      _Out __ofirst, _OSent __olast)
+    {
+      using _OutType = remove_reference_t<iter_reference_t<_Out>>;
+      if constexpr (sized_sentinel_for<_ISent, _Iter>
+                   && sized_sentinel_for<_OSent, _Out>
+                   && is_trivial_v<_OutType>
+                   && is_nothrow_assignable_v<_OutType,
+                                              iter_rvalue_reference_t<_Iter>>)
+       {
+         auto __d1 = ranges::distance(__ifirst, __ilast);
+         auto __d2 = ranges::distance(__ofirst, __olast);
+         return ranges::copy_n(std::make_move_iterator(__ifirst),
+                               std::min(__d1, __d2), __ofirst);
+       }
+      else
+       {
+         auto __guard = __detail::_DestroyGuard(&__ofirst);
+         for (; __ifirst != __ilast && __ofirst != __olast;
+              ++__ofirst, (void)++__ifirst)
+           ::new (__detail::__voidify(*__ofirst))
+                 _OutType(ranges::iter_move(__ifirst));
+         __guard.release();
+         return {__ifirst, __ofirst};
+       }
+    }
+
+  template<input_range _IRange, __detail::__nothrow_forward_range _ORange>
+    requires constructible_from<range_value_t<_ORange>,
+            range_rvalue_reference_t<_IRange>>
+    uninitialized_move_result<safe_iterator_t<_IRange>,
+                             safe_iterator_t<_ORange>>
+    uninitialized_move(_IRange&& __inr, _ORange&& __outr)
+    {
+      return ranges::uninitialized_move(ranges::begin(__inr),
+                                       ranges::end(__inr),
+                                       ranges::begin(__outr),
+                                       ranges::end(__outr));
+    }
+
+  template<typename _Iter, typename _Out>
+    using uninitialized_move_n_result = uninitialized_copy_result<_Iter, _Out>;
+
+  template<input_iterator _Iter, __detail::__nothrow_forward_iterator _Out,
+    __detail::__nothrow_sentinel<_Out> _Sent>
+      requires constructible_from<iter_value_t<_Out>,
+                                 iter_rvalue_reference_t<_Iter>>
+    uninitialized_move_n_result<_Iter, _Out>
+    uninitialized_move_n(_Iter __ifirst, iter_difference_t<_Iter> __n,
+                        _Out __ofirst, _Sent __olast)
+    {
+      using _OutType = remove_reference_t<iter_reference_t<_Out>>;
+      if constexpr (sized_sentinel_for<_Sent, _Out>
+                   && is_trivial_v<_OutType>
+                   && is_nothrow_assignable_v<_OutType,
+                                              iter_rvalue_reference_t<_Iter>>)
+       {
+         auto __d = ranges::distance(__ofirst, __olast);
+         return ranges::copy_n(std::make_move_iterator(__ifirst),
+                               std::min(__n, __d), __ofirst);
+       }
+      else
+       {
+         auto __guard = __detail::_DestroyGuard(&__ofirst);
+         for (; __n > 0 && __ofirst != __olast;
+              ++__ofirst, (void)++__ifirst, (void)--__n)
+           ::new (__detail::__voidify(*__ofirst))
+                 _OutType(ranges::iter_move(__ifirst));
+         __guard.release();
+         return {__ifirst, __ofirst};
+       }
+    }
+
+  template<__detail::__nothrow_forward_iterator _Iter,
+          __detail::__nothrow_sentinel<_Iter> _Sent, typename _Tp>
+    requires constructible_from<iter_value_t<_Iter>, const _Tp&>
+    _Iter
+    uninitialized_fill(_Iter __first, _Sent __last, const _Tp& __x)
+    {
+      using _ValueType = remove_reference_t<iter_reference_t<_Iter>>;
+      if constexpr (is_trivial_v<_ValueType>
+                   && is_nothrow_assignable_v<_ValueType, const _Tp&>)
+       return ranges::fill(__first, __last, __x);
+      else
+       {
+         auto __guard = __detail::_DestroyGuard(&__first);
+         for (; __first != __last; ++__first)
+           ::new (__detail::__voidify(*__first)) _ValueType(__x);
+         __guard.release();
+         return __first;
+       }
+    }
+
+  template<__detail::__nothrow_forward_range _Range, typename _Tp>
+    requires constructible_from<range_value_t<_Range>, const _Tp&>
+    safe_iterator_t<_Range>
+    uninitialized_fill(_Range&& __r, const _Tp& __x)
+    {
+      return ranges::uninitialized_fill(ranges::begin(__r), ranges::end(__r),
+                                       __x);
+    }
+
+  template<__detail::__nothrow_forward_iterator _Iter, typename _Tp>
+    requires constructible_from<iter_value_t<_Iter>, const _Tp&>
+    _Iter
+    uninitialized_fill_n(_Iter __first, iter_difference_t<_Iter> __n,
+                        const _Tp& __x)
+    {
+      using _ValueType = remove_reference_t<iter_reference_t<_Iter>>;
+      if constexpr (is_trivial_v<_ValueType>
+                   && is_nothrow_assignable_v<_ValueType, const _Tp&>)
+       return ranges::fill_n(__first, __n, __x);
+      else
+       {
+         auto __guard = __detail::_DestroyGuard(&__first);
+         for (; __n > 0; ++__first, (void)--__n)
+           ::new (__detail::__voidify(*__first)) _ValueType(__x);
+         __guard.release();
+         return __first;
+       }
+    }
+
+  template<typename _Tp, typename... _Args>
+    requires requires { ::new (declval<void*>()) _Tp(declval<_Args>()...); }
+    constexpr _Tp*
+    construct_at(_Tp* __location, _Args&&... __args)
+    {
+      return ::new (__detail::__voidify(*__location))
+                  _Tp(std::forward<_Args>(__args)...);
+    }
+
+  template<destructible _Tp>
+    constexpr void
+    destroy_at(_Tp* __location) noexcept
+    {
+      if constexpr (is_array_v<_Tp>)
+       ranges::destroy(ranges::begin(*__location), ranges::end(*__location));
+      else
+       __location->~_Tp();
+    }
+
+  template<__detail::__nothrow_input_iterator _Iter,
+          __detail::__nothrow_sentinel<_Iter> _Sent>
+    requires destructible<iter_value_t<_Iter>>
+    constexpr _Iter
+    destroy(_Iter __first, _Sent __last) noexcept
+    {
+      if constexpr (is_trivially_destructible_v<iter_value_t<_Iter>>)
+       return ranges::next(__first, __last);
+      else
+       {
+         for (; __first != __last; ++__first)
+           ranges::destroy_at(std::__addressof(*__first));
+         return __first;
+       }
+    }
+
+  template<__detail::__nothrow_input_range _Range>
+    requires destructible<range_value_t<_Range>>
+    constexpr safe_iterator_t<_Range>
+    destroy(_Range&& __r) noexcept
+    { return ranges::destroy(ranges::begin(__r), ranges::end(__r)); }
+
+  template<__detail::__nothrow_input_iterator _Iter>
+    requires destructible<iter_value_t<_Iter>>
+    constexpr _Iter
+    destroy_n(_Iter __first, iter_difference_t<_Iter> __n) noexcept
+    {
+      if constexpr (is_trivially_destructible_v<iter_value_t<_Iter>>)
+       return ranges::next(__first, __n);
+      else
+       {
+         for (; __n > 0; ++__first, (void)--__n)
+           ranges::destroy_at(std::__addressof(*__first));
+         return __first;
+       }
+    }
+}
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
+#endif // concepts
+#endif // C++20
+#endif // _RANGES_UNINITIALIZED_H
index b4caa1167a7a9f1b6a93bf422d5dcb645ba22db9..823bd21a5ad74c1bbc08466b8236145463968911 100644 (file)
@@ -66,6 +66,7 @@
 #include <bits/stl_uninitialized.h>
 #include <bits/stl_tempbuf.h>
 #include <bits/stl_raw_storage_iter.h>
+#include <bits/ranges_uninitialized.h>
 
 #if __cplusplus >= 201103L
 #  include <exception>           // std::exception
diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/destroy/constrained.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/destroy/constrained.cc
new file mode 100644 (file)
index 0000000..730625d
--- /dev/null
@@ -0,0 +1,76 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <cstring>
+#include <deque>
+#include <list>
+#include <memory>
+#include <span>
+#include <string>
+#include <vector>
+
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+namespace ranges = std::ranges;
+
+struct X
+{
+  X()
+  { ++count; }
+
+  ~X()
+  { --count; }
+
+  static inline int count = 0;
+};
+
+void
+test01()
+{
+  for (int k = 0; k < 3; k++)
+    {
+      constexpr int size = 1024;
+      auto buffer = std::unique_ptr<char[]>(new char[sizeof(X)*size]);
+      std::span<X> rx((X *)buffer.get(), size);
+
+      ranges::uninitialized_default_construct(rx);
+      VERIFY( X::count == size );
+
+      auto i = rx.cbegin();
+      if (k == 0)
+       i = ranges::destroy(rx);
+      else if (k == 1)
+       i = ranges::destroy(rx.begin(), rx.end());
+      else if (k == 2)
+       i = ranges::destroy_n(rx.begin(), size);
+      else
+       __builtin_abort();
+
+      VERIFY( i == rx.cend() );
+      VERIFY( X::count == 0 );
+    }
+}
+
+int
+main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_copy/constrained.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_copy/constrained.cc
new file mode 100644 (file)
index 0000000..9484064
--- /dev/null
@@ -0,0 +1,166 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <cstring>
+#include <deque>
+#include <list>
+#include <memory>
+#include <span>
+#include <string>
+#include <vector>
+
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_input_range;
+using __gnu_test::test_forward_range;
+
+namespace ranges = std::ranges;
+
+template<typename T>
+void
+test01(const std::vector<T> &ix)
+{
+  static_assert(std::copy_constructible<T>);
+  static_assert(std::equality_comparable<T>);
+
+  for (int k = 0; k < 7; k++)
+    {
+      int size = ix.size();
+      auto buffer = std::unique_ptr<char[]>(new char[sizeof(T)*size]);
+      std::span<T> rx((T *)buffer.get(), size);
+
+      ranges::uninitialized_copy_result res = {ix.cbegin(), rx.cbegin()};
+      if (k == 0)
+       res = ranges::uninitialized_copy(ix.begin(), ix.end(),
+                                        rx.begin(), rx.end());
+      else if (k == 1)
+       res = ranges::uninitialized_copy(ix, rx);
+      else if (k == 2)
+       res = ranges::uninitialized_copy_n(ix.begin(), size,
+                                          rx.begin(), rx.end());
+      else if (k == 3)
+       res = ranges::uninitialized_copy(ix.begin(), ix.end(),
+                                        rx.cbegin(), rx.cend());
+      else if (k == 4)
+       res = ranges::uninitialized_copy(ix, std::as_const(rx));
+      else if (k == 5)
+       res = ranges::uninitialized_copy_n(ix.begin(), size,
+                                          rx.cbegin(), rx.cend());
+      else if (k == 6)
+       res = ranges::uninitialized_copy_n(ix.begin(), size/2,
+                                          rx.cbegin(), rx.cend());
+      else if (k == 7)
+       res = ranges::uninitialized_copy_n(ix.begin(), size,
+                                          rx.cbegin(), rx.cbegin()+size/2);
+      else
+       __builtin_abort();
+
+      if (k == 6 || k == 7)
+       {
+         VERIFY( ranges::distance(ix.cbegin(), res.in) == size/2 );
+         VERIFY( ranges::distance(rx.cbegin(), res.out) == size/2 );
+         VERIFY( ranges::equal(ix.begin(), ix.begin()+size/2,
+                               rx.begin(), rx.begin()+size/2) );
+         ranges::destroy(rx.begin(), rx.begin()+size/2);
+       }
+      else
+       {
+         VERIFY( res.in == ix.cend() );
+         VERIFY( res.out == rx.cend() );
+         VERIFY( ranges::equal(ix, rx) );
+         ranges::destroy(rx);
+       }
+    }
+}
+
+struct X
+{
+  static constexpr int limit = 67;
+  static inline int copy_construct_count = 0;
+  static inline int destruct_count = 0;
+
+  struct exception {};
+
+  bool live = false;
+
+  X()
+  { live = true; }
+
+  X& operator=(const X&) = delete;
+
+  X(const X&)
+  {
+    live = true;
+    if (copy_construct_count >= limit)
+      throw exception{};
+    copy_construct_count++;
+  }
+
+  ~X()
+  {
+    VERIFY( live );
+    live = false;
+    destruct_count++;
+  }
+};
+
+template<bool test_sized>
+void
+test02()
+{
+  constexpr int size = 100;
+  X x[size];
+  // FIXME: Should be test_input_range?
+  test_forward_range<X> ix(x);
+
+  auto buffer = std::unique_ptr<char[]>(new char[sizeof(X)*size]);
+  test_forward_range<X> rx((X *)buffer.get(), (X *)buffer.get() + size);
+  try
+    {
+      X::copy_construct_count = 0;
+      X::destruct_count = 0;
+      if constexpr (test_sized)
+       ranges::uninitialized_copy_n(ix.begin(), size, rx.begin(), rx.end());
+      else
+       ranges::uninitialized_copy(ix, rx);
+      VERIFY( false && "exception not thrown" );
+    }
+  catch (const X::exception&)
+    {
+      VERIFY( X::copy_construct_count == X::limit );
+      VERIFY( X::destruct_count == X::limit );
+    }
+}
+
+int
+main()
+{
+  test01<char>({1,2,3,4,5});
+  test01<int>({1,2,3,4,5});
+  test01<long long>({1,2,3,4,5});
+  test01<float>({1.1,2.1,3.1,4.1});
+  test01<double>({1.1,2.1,3.1,4.1});
+  test01<std::vector<char>>({{'a','b'}, {'c','d'}, {'e','f'}});
+  test01<std::string>({"the", "quick", "brown", "fox"});
+
+  test02<false>();
+  test02<true>();
+}
diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_default_construct/constrained.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_default_construct/constrained.cc
new file mode 100644 (file)
index 0000000..6ef24cc
--- /dev/null
@@ -0,0 +1,147 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <cstring>
+#include <deque>
+#include <list>
+#include <memory>
+#include <span>
+#include <string>
+#include <vector>
+
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_forward_range;
+
+namespace ranges = std::ranges;
+
+template<typename T>
+void
+test01()
+{
+  static_assert(std::default_initializable<T>);
+  static_assert(std::equality_comparable<T>);
+
+  for (int k = 0; k < 6; k++)
+    {
+      constexpr int size = 1024;
+      auto buffer = std::unique_ptr<char[]>(new char[sizeof(T)*size]);
+      std::span<T> rx((T *)buffer.get(), size);
+
+      T t;
+      if constexpr (std::is_fundamental_v<T>)
+       {
+         std::memset(&t, 0xCC, sizeof(t));
+         ranges::fill(rx, t);
+       }
+
+      auto i = rx.cbegin();
+      if (k == 0)
+       i = ranges::uninitialized_default_construct(rx.begin(), rx.end());
+      else if (k == 1)
+       i = ranges::uninitialized_default_construct(rx);
+      else if (k == 2)
+       i = ranges::uninitialized_default_construct_n(rx.begin(), 1024);
+      else if constexpr (std::is_fundamental_v<T>)
+       continue;
+      else if (k == 3)
+       i = ranges::uninitialized_default_construct(rx.cbegin(), rx.cend());
+      else if (k == 4)
+       i = ranges::uninitialized_default_construct(std::as_const(rx));
+      else if (k == 5)
+       i = ranges::uninitialized_default_construct_n(rx.cbegin(), 1024);
+      else
+       __builtin_abort();
+
+      VERIFY( i == rx.cend() );
+      VERIFY( ranges::find_if(rx, [&t](const T& v) { return t != v; }) == i );
+
+      ranges::destroy(rx);
+    }
+}
+
+struct X
+{
+  static constexpr int limit = 67;
+  static inline int construct_count = 0;
+  static inline int destruct_count = 0;
+
+  struct exception {};
+
+  bool live = false;
+
+  X()
+  {
+    if (construct_count >= limit)
+      throw exception{};
+    construct_count++;
+    live = true;
+  }
+
+  ~X()
+  {
+    VERIFY( live );
+    live = false;
+    destruct_count++;
+  }
+};
+
+template<bool test_sized>
+void
+test02()
+{
+  constexpr int size = 100;
+  auto buffer = std::unique_ptr<char[]>(new char[sizeof(X)*size]);
+  test_forward_range<X> rx((X *)buffer.get(), (X *)buffer.get() + size);
+  try
+    {
+      X::construct_count = 0;
+      X::destruct_count = 0;
+      if constexpr (test_sized)
+       ranges::uninitialized_default_construct_n(rx.begin(), size);
+      else
+       ranges::uninitialized_default_construct(rx);
+      VERIFY( false && "exception not thrown" );
+    }
+  catch (const X::exception&)
+    {
+      VERIFY( X::construct_count == X::limit );
+      VERIFY( X::destruct_count == X::limit );
+    }
+}
+
+int
+main()
+{
+  test01<char>();
+  test01<int>();
+  test01<long long>();
+  test01<float>();
+  test01<double>();
+  test01<std::vector<char>>();
+  test01<std::string>();
+  test01<std::deque<double>>();
+  test01<std::list<std::vector<std::deque<double>>>>();
+  test01<std::unique_ptr<std::string>>();
+
+  test02<false>();
+  test02<true>();
+}
diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_fill/constrained.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_fill/constrained.cc
new file mode 100644 (file)
index 0000000..c95fd66
--- /dev/null
@@ -0,0 +1,137 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <cstring>
+#include <deque>
+#include <list>
+#include <memory>
+#include <span>
+#include <string>
+#include <vector>
+
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_forward_range;
+
+namespace ranges = std::ranges;
+
+template<typename T>
+void
+test01(const T& value)
+{
+  static_assert(std::equality_comparable<T>);
+
+  for (int k = 0; k < 6; k++)
+    {
+      constexpr int size = 1024;
+      auto buffer = std::unique_ptr<char[]>(new char[sizeof(T)*size]);
+      std::span<T> rx((T *)buffer.get(), size);
+
+      auto i = rx.cbegin();
+      if (k == 0)
+       i = ranges::uninitialized_fill(rx.begin(), rx.end(), value);
+      else if (k == 1)
+       i = ranges::uninitialized_fill(rx, value);
+      else if (k == 2)
+       i = ranges::uninitialized_fill_n(rx.begin(), 1024, value);
+      else if (k == 3)
+       i = ranges::uninitialized_fill(rx.cbegin(), rx.cend(), value);
+      else if (k == 4)
+       i = ranges::uninitialized_fill(std::as_const(rx), value);
+      else if (k == 5)
+       i = ranges::uninitialized_fill_n(rx.cbegin(), 1024, value);
+      else
+       __builtin_abort();
+
+      VERIFY( i == rx.cend() );
+      VERIFY( ranges::find_if(rx, [&value](const T& v) { return value != v; }) == i );
+
+      ranges::destroy(rx);
+    }
+}
+
+struct X
+{
+  static constexpr int limit = 67;
+  static inline int construct_count = 0;
+  static inline int destruct_count = 0;
+
+  struct exception {};
+
+  bool live = false;
+
+  X(int)
+  {
+    if (construct_count >= limit)
+      throw exception{};
+    construct_count++;
+    live = true;
+  }
+
+  ~X()
+  {
+    VERIFY( live );
+    live = false;
+    destruct_count++;
+  }
+};
+
+template<bool test_sized>
+void
+test02()
+{
+  constexpr int size = 100;
+  auto buffer = std::unique_ptr<char[]>(new char[sizeof(X)*size]);
+  test_forward_range<X> rx((X *)buffer.get(), (X *)buffer.get() + size);
+  int value = 5;
+  try
+    {
+      X::construct_count = 0;
+      X::destruct_count = 0;
+      if constexpr (test_sized)
+       ranges::uninitialized_fill_n(rx.begin(), size, value);
+      else
+       ranges::uninitialized_fill(rx, value);
+      VERIFY( false && "exception not thrown" );
+    }
+  catch (const X::exception&)
+    {
+      VERIFY( X::construct_count == X::limit );
+      VERIFY( X::destruct_count == X::limit );
+    }
+}
+
+int
+main()
+{
+  test01<char>(5);
+  test01<int>(3);
+  test01<long long>(17);
+  test01<float>(2.18);
+  test01<double>(3.98);
+  test01<std::vector<char>>({'a', 'b', 'c', 'd'});
+  test01<std::string>("hello");
+  test01<std::deque<double>>({1.1,2.1,3.1});
+  test01<std::list<std::vector<std::deque<double>>>>({{{3.4},{1}},{{7.9}}});
+
+  test02<false>();
+  test02<true>();
+}
diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_move/constrained.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_move/constrained.cc
new file mode 100644 (file)
index 0000000..796c7ca
--- /dev/null
@@ -0,0 +1,176 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <cstring>
+#include <deque>
+#include <list>
+#include <memory>
+#include <span>
+#include <string>
+#include <vector>
+
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_input_range;
+using __gnu_test::test_forward_range;
+
+namespace ranges = std::ranges;
+
+template<typename T>
+void
+test01(std::vector<T> ix)
+{
+  static_assert(std::move_constructible<T>);
+  static_assert(std::equality_comparable<T>);
+
+  const auto saved_ix = ix;
+
+  for (int k = 0; k < 7; k++)
+    {
+      ix = saved_ix;
+
+      int size = ix.size();
+      auto buffer = std::unique_ptr<char[]>(new char[sizeof(T)*size]);
+      std::span<T> rx((T *)buffer.get(), size);
+
+      ranges::uninitialized_move_result res = {ix.cbegin(), rx.cbegin()};
+      if (k == 0)
+       res = ranges::uninitialized_move(ix.begin(), ix.end(),
+                                        rx.begin(), rx.end());
+      else if (k == 1)
+       res = ranges::uninitialized_move(ix, rx);
+      else if (k == 2)
+       res = ranges::uninitialized_move_n(ix.begin(), size,
+                                          rx.begin(), rx.end());
+      else if (k == 3)
+       res = ranges::uninitialized_move(ix.begin(), ix.end(),
+                                        rx.cbegin(), rx.cend());
+      else if (k == 4)
+       res = ranges::uninitialized_move(ix, std::as_const(rx));
+      else if (k == 5)
+       res = ranges::uninitialized_move_n(ix.begin(), size,
+                                          rx.cbegin(), rx.cend());
+      else if (k == 6)
+       res = ranges::uninitialized_move_n(ix.begin(), size/2,
+                                          rx.cbegin(), rx.cend());
+      else if (k == 7)
+       res = ranges::uninitialized_move_n(ix.begin(), size,
+                                          rx.cbegin(), rx.cbegin()+size/2);
+      else
+       __builtin_abort();
+
+      if (k == 6 || k == 7)
+       {
+         VERIFY( ranges::distance(ix.cbegin(), res.in) == size/2 );
+         VERIFY( ranges::distance(rx.cbegin(), res.out) == size/2 );
+         VERIFY( ranges::equal(saved_ix.begin(), saved_ix.begin()+size/2,
+                               rx.begin(), rx.begin()+size/2) );
+         ranges::destroy(rx.begin(), rx.begin()+size/2);
+       }
+      else
+       {
+         VERIFY( res.in == ix.cend() );
+         VERIFY( res.out == rx.cend() );
+         VERIFY( ranges::equal(saved_ix, rx) );
+         ranges::destroy(rx);
+       }
+    }
+}
+
+struct X
+{
+  static constexpr int limit = 67;
+  static inline int move_construct_count = 0;
+  static inline int destruct_count = 0;
+
+  struct exception {};
+
+  bool live = false;
+  bool moved_from = false;
+
+  X()
+  { live = true; moved_from = false; }
+
+  X& operator=(const X&) = delete;
+  X(const X&) = delete;
+
+  X&& operator=(X&&) = delete;
+
+  X(X&& other)
+  {
+    VERIFY( !other.moved_from );
+    other.moved_from = true;
+    live = true;
+    if (move_construct_count >= limit)
+      throw exception{};
+    move_construct_count++;
+  }
+
+  ~X()
+  {
+    VERIFY( live );
+    live = false;
+    destruct_count++;
+  }
+};
+
+template<bool test_sized>
+void
+test02()
+{
+  constexpr int size = 100;
+  X x[size];
+  // FIXME: Should be test_input_range?
+  test_forward_range<X> ix(x);
+
+  auto buffer = std::unique_ptr<char[]>(new char[sizeof(X)*size]);
+  test_forward_range<X> rx((X *)buffer.get(), (X *)buffer.get() + size);
+  try
+    {
+      X::move_construct_count = 0;
+      X::destruct_count = 0;
+      if constexpr (test_sized)
+       ranges::uninitialized_move_n(ix.begin(), size, rx.begin(), rx.end());
+      else
+       ranges::uninitialized_move(ix, rx);
+      VERIFY( false && "exception not thrown" );
+    }
+  catch (const X::exception&)
+    {
+      VERIFY( X::move_construct_count == X::limit );
+      VERIFY( X::destruct_count == X::limit );
+    }
+}
+
+int
+main()
+{
+  test01<char>({1,2,3,4,5});
+  test01<int>({1,2,3,4,5});
+  test01<long long>({1,2,3,4,5});
+  test01<float>({1.1,2.1,3.1,4.1});
+  test01<double>({1.1,2.1,3.1,4.1});
+  test01<std::vector<char>>({{'a','b'}, {'c','d'}, {'e','f'}});
+  test01<std::string>({"the", "quick", "brown", "fox"});
+
+  test02<false>();
+  test02<true>();
+}
diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct/constrained.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct/constrained.cc
new file mode 100644 (file)
index 0000000..5928bc0
--- /dev/null
@@ -0,0 +1,140 @@
+// Copyright (C) 2020 Free Software Foundation, Inc.
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <cstring>
+#include <deque>
+#include <list>
+#include <memory>
+#include <span>
+#include <string>
+#include <vector>
+
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_forward_range;
+
+namespace ranges = std::ranges;
+
+template<typename T>
+void
+test01()
+{
+  static_assert(std::default_initializable<T>);
+  static_assert(std::equality_comparable<T>);
+
+  for (int k = 0; k < 6; k++)
+    {
+      constexpr int size = 1024;
+      auto buffer = std::unique_ptr<char[]>(new char[sizeof(T)*size]);
+      std::span<T> rx((T *)buffer.get(), size);
+
+      T t{};
+
+      auto i = rx.cbegin();
+      if (k == 0)
+       i = ranges::uninitialized_value_construct(rx.begin(), rx.end());
+      else if (k == 1)
+       i = ranges::uninitialized_value_construct(rx);
+      else if (k == 2)
+       i = ranges::uninitialized_value_construct_n(rx.begin(), 1024);
+      else if (k == 3)
+       i = ranges::uninitialized_value_construct(rx.cbegin(), rx.cend());
+      else if (k == 4)
+       i = ranges::uninitialized_value_construct(std::as_const(rx));
+      else if (k == 5)
+       i = ranges::uninitialized_value_construct_n(rx.cbegin(), 1024);
+      else
+       __builtin_abort();
+
+      VERIFY( i == rx.cend() );
+      VERIFY( ranges::find_if(rx, [&t](const T& v) { return t != v; }) == i );
+
+      ranges::destroy(rx);
+    }
+}
+
+struct X
+{
+  static constexpr int limit = 67;
+  static inline int construct_count = 0;
+  static inline int destruct_count = 0;
+
+  struct exception {};
+
+  bool live = false;
+
+  X()
+  {
+    if (construct_count >= limit)
+      throw exception{};
+    construct_count++;
+    live = true;
+  }
+
+  ~X()
+  {
+    VERIFY( live );
+    live = false;
+    destruct_count++;
+  }
+};
+
+template<bool test_sized>
+void
+test02()
+{
+  constexpr int size = 100;
+  auto buffer = std::unique_ptr<char[]>(new char[sizeof(X)*size]);
+  test_forward_range<X> rx((X *)buffer.get(), (X *)buffer.get() + size);
+  try
+    {
+      X::construct_count = 0;
+      X::destruct_count = 0;
+      if constexpr (test_sized)
+       ranges::uninitialized_value_construct_n(rx.begin(), size);
+      else
+       ranges::uninitialized_value_construct(rx);
+      VERIFY( false && "exception not thrown" );
+    }
+  catch (const X::exception&)
+    {
+      VERIFY( X::construct_count == X::limit );
+      VERIFY( X::destruct_count == X::limit );
+    }
+}
+
+int
+main()
+{
+  test01<char>();
+  test01<int>();
+  test01<long long>();
+  test01<float>();
+  test01<double>();
+  test01<std::vector<char>>();
+  test01<std::string>();
+  test01<std::deque<double>>();
+  test01<std::list<std::vector<std::deque<double>>>>();
+  test01<std::unique_ptr<std::string>>();
+
+  test02<false>();
+  test02<true>();
+}