[2/2] libstdc++: Implement ranges [specialized.algorithms]

Message ID 20200212204107.4124217-2-ppalka@redhat.com
State New
Headers show
Series
  • [1/2] libstdc++: Move some ranges algos to a new header <bits/ranges_algobase.h>
Related show

Commit Message

Patrick Palka Feb. 12, 2020, 8:41 p.m.
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/include/Makefile.am              |   1 +
 libstdc++-v3/include/Makefile.in              |   1 +
 .../include/bits/ranges_uninitialized.h       | 491 ++++++++++++++++++
 libstdc++-v3/include/std/memory               |   1 +
 .../destroy/constrained.cc                    |  76 +++
 .../uninitialized_copy/constrained.cc         | 166 ++++++
 .../constrained.cc                            | 147 ++++++
 .../uninitialized_fill/constrained.cc         | 137 +++++
 .../uninitialized_move/constrained.cc         | 176 +++++++
 .../constrained.cc                            | 140 +++++
 10 files changed, 1336 insertions(+)
 create mode 100644 libstdc++-v3/include/bits/ranges_uninitialized.h
 create mode 100644 libstdc++-v3/testsuite/20_util/specialized_algorithms/destroy/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_copy/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_default_construct/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_fill/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_move/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct/constrained.cc

-- 
2.25.0.191.gde93cc14ab

Comments

Jonathan Wakely Feb. 13, 2020, 4:12 p.m. | #1
On 12/02/20 15:41 -0500, Patrick Palka wrote:
>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.




>+  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(addressof(*__first));


This should be std::__addressof

>+	  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(addressof(*__first));


Same here.

OK for master with those two adjustments.
Patrick Palka Feb. 13, 2020, 8:46 p.m. | #2
On Thu, 13 Feb 2020, Jonathan Wakely wrote:

> On 12/02/20 15:41 -0500, Patrick Palka wrote:

> > 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.

> 

> 

> 

> > +  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(addressof(*__first));

> 

> This should be std::__addressof

> 

> > +	  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(addressof(*__first));

> 

> Same here.

> 

> OK for master with those two adjustments.


Incorporated those two changes and committed both patches.  Thanks for
the review!

Patch

diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 614222db400..e131ce04f8c 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -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 \
diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
index 7ee6a1e3f61..ae20f6b1d21 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -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
index 00000000000..252295faed7
--- /dev/null
+++ b/libstdc++-v3/include/bits/ranges_uninitialized.h
@@ -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(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(addressof(*__first));
+	  return __first;
+	}
+    }
+}
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
+#endif // concepts
+#endif // C++20
+#endif // _RANGES_UNINITIALIZED_H
diff --git a/libstdc++-v3/include/std/memory b/libstdc++-v3/include/std/memory
index b4caa1167a7..823bd21a5ad 100644
--- a/libstdc++-v3/include/std/memory
+++ b/libstdc++-v3/include/std/memory
@@ -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
index 00000000000..730625d9a21
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/destroy/constrained.cc
@@ -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
index 00000000000..948406432cb
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_copy/constrained.cc
@@ -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
index 00000000000..6ef24cc7ea0
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_default_construct/constrained.cc
@@ -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
index 00000000000..c95fd666942
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_fill/constrained.cc
@@ -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
index 00000000000..796c7ca8f46
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_move/constrained.cc
@@ -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
index 00000000000..5928bc04c70
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct/constrained.cc
@@ -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>();
+}