diff mbox series

[committed] libstdc++: Relax range adaptors for move-only types (P2494R2)

Message ID 20230907071134.866862-1-jwakely@redhat.com
State New
Headers show
Series [committed] libstdc++: Relax range adaptors for move-only types (P2494R2) | expand

Commit Message

Jonathan Wakely Sept. 7, 2023, 7:11 a.m. UTC
Tested x86_64-linux. Pushed to trunk.

-- >8 --

This is a C++23 feature that relaxes the constraints on some range
adaptors, to support move-only types.

libstdc++-v3/ChangeLog:

	* include/bits/version.def (ranges): Update value.
	* include/bits/version.h: Regenerate.
	* include/std/ranges (__detail::__boxable): Use
	move_constructible instead of copy_constructible for C++23.
	(__detail::__box<T>): Adjust constraints for partial
	specialization.
	(single_view, transform_view): Use __box_constructible instead
	of copy_constructible in constraints.
	(zip_transform_view, adjacent_transform_view, repeat_view): Use
	move_constructible instead of copy_constructible in constraints.
	* testsuite/std/ranges/adaptors/adjacent_transform/1.cc: Check
	construction from move-only argument.
	* testsuite/std/ranges/adaptors/transform.cc: Likewise.
	* testsuite/std/ranges/repeat/1.cc: Likewise.
	* testsuite/std/ranges/single_view.cc: Likewise.
	* testsuite/std/ranges/zip_transform/1.cc: Likewise.
	* testsuite/std/ranges/version_c++23.cc: Adjust expected value
	of __cpp_lib_ranges.
---
 libstdc++-v3/include/bits/version.def         |  2 +-
 libstdc++-v3/include/bits/version.h           |  4 +-
 libstdc++-v3/include/std/ranges               | 72 +++++++++++++------
 .../ranges/adaptors/adjacent_transform/1.cc   | 14 ++++
 .../std/ranges/adaptors/transform.cc          | 19 +++++
 libstdc++-v3/testsuite/std/ranges/repeat/1.cc | 12 ++++
 .../testsuite/std/ranges/single_view.cc       | 17 +++++
 .../testsuite/std/ranges/version_c++23.cc     |  2 +-
 .../testsuite/std/ranges/zip_transform/1.cc   | 14 ++++
 9 files changed, 131 insertions(+), 25 deletions(-)
diff mbox series

Patch

diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
index 80c13d4a447..44b916e3897 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1045,7 +1045,7 @@  ftms = {
 ftms = {
   name = ranges;
   values = {
-    v = 202202;
+    v = 202207;
     cxxmin = 23;
     extra_cond = "__glibcxx_concepts";
   };
diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
index 5bddb4b8adc..9fada98eee3 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -1290,9 +1290,9 @@ 
 // from version.def line 1046
 #if !defined(__cpp_lib_ranges)
 # if (__cplusplus >= 202302L) && (__glibcxx_concepts)
-#  define __glibcxx_ranges 202202L
+#  define __glibcxx_ranges 202207L
 #  if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges)
-#   define __cpp_lib_ranges 202202L
+#   define __cpp_lib_ranges 202207L
 #  endif
 # elif (__cplusplus >= 202002L) && (__glibcxx_concepts)
 #  define __glibcxx_ranges 202110L
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index 3477323d871..1d529a886be 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -106,8 +106,14 @@  namespace ranges
 
   namespace __detail
   {
+#if __cpp_lib_ranges >= 202207L // C++ >= 23
+    // P2494R2 Relaxing range adaptors to allow for move only types
+    template<typename _Tp>
+      concept __boxable = move_constructible<_Tp> && is_object_v<_Tp>;
+#else
     template<typename _Tp>
       concept __boxable = copy_constructible<_Tp> && is_object_v<_Tp>;
+#endif
 
     template<__boxable _Tp>
       struct __box : std::optional<_Tp>
@@ -132,7 +138,7 @@  namespace ranges
 	constexpr __box&
 	operator=(const __box& __that)
 	noexcept(is_nothrow_copy_constructible_v<_Tp>)
-	requires (!copyable<_Tp>)
+	requires (!copyable<_Tp>) && copy_constructible<_Tp>
 	{
 	  if (this != std::__addressof(__that))
 	    {
@@ -160,13 +166,22 @@  namespace ranges
 	}
       };
 
-    // For types which are already copyable, this specialization of the
-    // copyable wrapper stores the object directly without going through
-    // std::optional.  It provides just the subset of the primary template's
-    // API that we currently use.
+    template<typename _Tp>
+      concept __boxable_copyable
+	= copy_constructible<_Tp>
+	    && (copyable<_Tp> || (is_nothrow_move_constructible_v<_Tp>
+				    && is_nothrow_copy_constructible_v<_Tp>));
+    template<typename _Tp>
+      concept __boxable_movable
+	= (!copy_constructible<_Tp>)
+	    && (movable<_Tp> || is_nothrow_move_constructible_v<_Tp>);
+
+    // For types which are already copyable (or since C++23, movable)
+    // this specialization of the box wrapper stores the object directly
+    // without going through std::optional.  It provides just the subset of
+    // the primary template's API that we currently use.
     template<__boxable _Tp>
-      requires copyable<_Tp> || (is_nothrow_move_constructible_v<_Tp>
-				 && is_nothrow_copy_constructible_v<_Tp>)
+      requires __boxable_copyable<_Tp> || __boxable_movable<_Tp>
       struct __box<_Tp>
       {
       private:
@@ -178,6 +193,7 @@  namespace ranges
 	constexpr explicit
 	__box(const _Tp& __t)
 	noexcept(is_nothrow_copy_constructible_v<_Tp>)
+	requires copy_constructible<_Tp>
 	: _M_value(__t)
 	{ }
 
@@ -198,12 +214,13 @@  namespace ranges
 	__box(const __box&) = default;
 	__box(__box&&) = default;
 	__box& operator=(const __box&) requires copyable<_Tp> = default;
-	__box& operator=(__box&&) requires copyable<_Tp> = default;
+	__box& operator=(__box&&) requires movable<_Tp> = default;
 
 	// When _Tp is nothrow_copy_constructible but not copy_assignable,
 	// copy assignment is implemented via destroy-then-copy-construct.
 	constexpr __box&
 	operator=(const __box& __that) noexcept
+	requires (!copyable<_Tp>) && copy_constructible<_Tp>
 	{
 	  static_assert(is_nothrow_copy_constructible_v<_Tp>);
 	  if (this != std::__addressof(__that))
@@ -217,6 +234,7 @@  namespace ranges
 	// Likewise for move assignment.
 	constexpr __box&
 	operator=(__box&& __that) noexcept
+	requires (!movable<_Tp>)
 	{
 	  static_assert(is_nothrow_move_constructible_v<_Tp>);
 	  if (this != std::__addressof(__that))
@@ -250,7 +268,12 @@  namespace ranges
   } // namespace __detail
 
   /// A view that contains exactly one element.
-  template<copy_constructible _Tp> requires is_object_v<_Tp>
+#if __cpp_lib_ranges >= 202207L // C++ >= 23
+  template<move_constructible _Tp>
+#else
+  template<copy_constructible _Tp>
+#endif
+    requires is_object_v<_Tp>
     class single_view : public view_interface<single_view<_Tp>>
     {
     public:
@@ -259,6 +282,7 @@  namespace ranges
       constexpr explicit
       single_view(const _Tp& __t)
       noexcept(is_nothrow_copy_constructible_v<_Tp>)
+      requires copy_constructible<_Tp>
       : _M_value(__t)
       { }
 
@@ -1147,7 +1171,8 @@  namespace views::__adaptor
     };
 } // namespace views::__adaptor
 
-#if __cplusplus > 202002L
+#if __cpp_lib_ranges >= 202202L
+  // P2387R3 Pipe support for user-defined range adaptors
   template<typename _Derived>
     requires is_class_v<_Derived> && same_as<_Derived, remove_cv_t<_Derived>>
     class range_adaptor_closure
@@ -1751,7 +1776,11 @@  namespace views::__adaptor
     inline constexpr _Filter filter;
   } // namespace views
 
+#if __cpp_lib_ranges >= 202207L // C++ >= 23
+  template<input_range _Vp, move_constructible _Fp>
+#else
   template<input_range _Vp, copy_constructible _Fp>
+#endif
     requires view<_Vp> && is_object_v<_Fp>
       && regular_invocable<_Fp&, range_reference_t<_Vp>>
       && std::__detail::__can_reference<invoke_result_t<_Fp&,
@@ -4623,7 +4652,7 @@  namespace views::__adaptor
 	return input_iterator_tag{};
     }
 
-    template<copy_constructible _Fp, input_range... _Ws>
+    template<move_constructible _Fp, input_range... _Ws>
       requires (view<_Ws> && ...) && (sizeof...(_Ws) > 0) && is_object_v<_Fp>
 	&& regular_invocable<_Fp&, range_reference_t<_Ws>...>
 	&& std::__detail::__can_reference<invoke_result_t<_Fp&, range_reference_t<_Ws>...>>
@@ -4894,7 +4923,7 @@  namespace views::__adaptor
 	= typename iterator_traits<iterator_t<__maybe_const_t<_Const, _Range>>>::iterator_category;
   }
 
-  template<copy_constructible _Fp, input_range... _Vs>
+  template<move_constructible _Fp, input_range... _Vs>
     requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0) && is_object_v<_Fp>
       && regular_invocable<_Fp&, range_reference_t<_Vs>...>
       && std::__detail::__can_reference<invoke_result_t<_Fp&, range_reference_t<_Vs>...>>
@@ -5001,7 +5030,7 @@  namespace views::__adaptor
   template<class _Fp, class... Rs>
     zip_transform_view(_Fp, Rs&&...) -> zip_transform_view<_Fp, views::all_t<Rs>...>;
 
-  template<copy_constructible _Fp, input_range... _Vs>
+  template<move_constructible _Fp, input_range... _Vs>
     requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0) && is_object_v<_Fp>
       && regular_invocable<_Fp&, range_reference_t<_Vs>...>
       && std::__detail::__can_reference<invoke_result_t<_Fp&, range_reference_t<_Vs>...>>
@@ -5131,7 +5160,7 @@  namespace views::__adaptor
     { return __x._M_inner - __y._M_inner; }
   };
 
-  template<copy_constructible _Fp, input_range... _Vs>
+  template<move_constructible _Fp, input_range... _Vs>
     requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0) && is_object_v<_Fp>
       && regular_invocable<_Fp&, range_reference_t<_Vs>...>
       && std::__detail::__can_reference<invoke_result_t<_Fp&, range_reference_t<_Vs>...>>
@@ -5338,7 +5367,7 @@  namespace views::__adaptor
 
     friend class adjacent_view;
 
-    template<forward_range _Wp, copy_constructible _Fp, size_t _Mm>
+    template<forward_range _Wp, move_constructible _Fp, size_t _Mm>
       requires view<_Wp> && (_Mm > 0) && is_object_v<_Fp>
         && regular_invocable<__detail::__unarize<_Fp&, _Mm>, range_reference_t<_Wp>>
         && std::__detail::__can_reference<invoke_result_t<__detail::__unarize<_Fp&, _Mm>,
@@ -5580,7 +5609,7 @@  namespace views::__adaptor
     inline constexpr auto pairwise = adjacent<2>;
   }
 
-  template<forward_range _Vp, copy_constructible _Fp, size_t _Nm>
+  template<forward_range _Vp, move_constructible _Fp, size_t _Nm>
    requires view<_Vp> && (_Nm > 0) && is_object_v<_Fp>
      && regular_invocable<__detail::__unarize<_Fp&, _Nm>, range_reference_t<_Vp>>
      && std::__detail::__can_reference<invoke_result_t<__detail::__unarize<_Fp&, _Nm>,
@@ -5650,7 +5679,7 @@  namespace views::__adaptor
     { return _M_inner.size(); }
   };
 
-  template<forward_range _Vp, copy_constructible _Fp, size_t _Nm>
+  template<forward_range _Vp, move_constructible _Fp, size_t _Nm>
    requires view<_Vp> && (_Nm > 0) && is_object_v<_Fp>
      && regular_invocable<__detail::__unarize<_Fp&, _Nm>, range_reference_t<_Vp>>
      && std::__detail::__can_reference<invoke_result_t<__detail::__unarize<_Fp&, _Nm>,
@@ -5819,7 +5848,7 @@  namespace views::__adaptor
     { return __x._M_inner - __y._M_inner; }
   };
 
-  template<forward_range _Vp, copy_constructible _Fp, size_t _Nm>
+  template<forward_range _Vp, move_constructible _Fp, size_t _Nm>
    requires view<_Vp> && (_Nm > 0) && is_object_v<_Fp>
      && regular_invocable<__detail::__unarize<_Fp&, _Nm>, range_reference_t<_Vp>>
      && std::__detail::__can_reference<invoke_result_t<__detail::__unarize<_Fp&, _Nm>,
@@ -7528,8 +7557,8 @@  namespace views::__adaptor
   } // namespace views
 #endif // __cpp_lib_ranges_join_with
 
-#ifdef __cpp_lib_ranges_repeat // C++ >= 32
-  template<copy_constructible _Tp, semiregular _Bound = unreachable_sentinel_t>
+#ifdef __cpp_lib_ranges_repeat // C++ >= 23
+  template<move_constructible _Tp, semiregular _Bound = unreachable_sentinel_t>
     requires is_object_v<_Tp> && same_as<_Tp, remove_cv_t<_Tp>>
       && (__detail::__is_integer_like<_Bound> || same_as<_Bound, unreachable_sentinel_t>)
   class repeat_view : public view_interface<repeat_view<_Tp, _Bound>>
@@ -7552,6 +7581,7 @@  namespace views::__adaptor
 
     constexpr explicit
     repeat_view(const _Tp& __value, _Bound __bound = _Bound())
+    requires copy_constructible<_Tp>
     : _M_value(__value), _M_bound(__bound)
     {
       if constexpr (!same_as<_Bound, unreachable_sentinel_t>)
@@ -7594,7 +7624,7 @@  namespace views::__adaptor
   template<typename _Tp, typename _Bound>
     repeat_view(_Tp, _Bound) -> repeat_view<_Tp, _Bound>;
 
-  template<copy_constructible _Tp, semiregular _Bound>
+  template<move_constructible _Tp, semiregular _Bound>
     requires is_object_v<_Tp> && same_as<_Tp, remove_cv_t<_Tp>>
       && (__detail::__is_integer_like<_Bound> || same_as<_Bound, unreachable_sentinel_t>)
   class repeat_view<_Tp, _Bound>::_Iterator
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc
index 07f20b702dd..c32c639e815 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc
@@ -97,10 +97,24 @@  test03()
   return true;
 }
 
+void
+test04()
+{
+  extern int x[5];
+  struct move_only {
+    move_only() { }
+    move_only(move_only&&) { }
+    int operator()(int i, int j) const { return i + j; }
+  };
+  // P2494R2 Relaxing range adaptors to allow for move only types
+  static_assert( requires { views::pairwise_transform(x, move_only{}); } );
+}
+
 int
 main()
 {
   static_assert(test01());
   static_assert(test02());
   static_assert(test03());
+  test04();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
index e89ae4f9f3f..9d52cb01dad 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
@@ -175,6 +175,24 @@  test08()
   static_assert(!requires { views::all | transform; });
 }
 
+template<auto transform = views::transform>
+void
+test09()
+{
+  extern int x[5];
+  struct move_only {
+    move_only() { }
+    move_only(move_only&&) { }
+    int operator()(int i) const { return i; }
+  };
+#if __cpp_lib_ranges >= 202207L
+  // P2494R2 Relaxing range adaptors to allow for move only types
+  static_assert( requires { transform(x, move_only{}); } );
+#else
+  static_assert( ! requires { transform(x, move_only{}); } );
+#endif
+}
+
 int
 main()
 {
@@ -186,4 +204,5 @@  main()
   test06();
   test07();
   test08();
+  test09();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/repeat/1.cc b/libstdc++-v3/testsuite/std/ranges/repeat/1.cc
index 07b70891042..c62122c078f 100644
--- a/libstdc++-v3/testsuite/std/ranges/repeat/1.cc
+++ b/libstdc++-v3/testsuite/std/ranges/repeat/1.cc
@@ -127,6 +127,17 @@  test05()
   ranges::repeat_view<int> r;
 }
 
+void
+test06()
+{
+  struct move_only {
+    move_only() { }
+    move_only(move_only&&) { }
+  };
+  // P2494R2 Relaxing range adaptors to allow for move only types
+  static_assert( requires { views::repeat(move_only{}, 2); } );
+}
+
 int
 main()
 {
@@ -135,4 +146,5 @@  main()
   static_assert(test03());
   static_assert(test04());
   test05();
+  test06();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/single_view.cc b/libstdc++-v3/testsuite/std/ranges/single_view.cc
index 38a3946ca43..8dfe6bb4bd1 100644
--- a/libstdc++-v3/testsuite/std/ranges/single_view.cc
+++ b/libstdc++-v3/testsuite/std/ranges/single_view.cc
@@ -123,6 +123,22 @@  test07()
   static_assert(!requires { single(uncopyable{}); });
 }
 
+template<auto single = std::views::single>
+void
+test08()
+{
+  struct move_only {
+    move_only() { }
+    move_only(move_only&&) { }
+  };
+#if __cpp_lib_ranges >= 202207L
+  // P2494R2 Relaxing range adaptors to allow for move only types
+  static_assert( requires { single(move_only{}); } );
+#else
+  static_assert( ! requires { single(move_only{}); } );
+#endif
+}
+
 int main()
 {
   test01();
@@ -132,4 +148,5 @@  int main()
   test05();
   test06();
   test07();
+  test08();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/version_c++23.cc b/libstdc++-v3/testsuite/std/ranges/version_c++23.cc
index e8342fa986a..8e4a8b466aa 100644
--- a/libstdc++-v3/testsuite/std/ranges/version_c++23.cc
+++ b/libstdc++-v3/testsuite/std/ranges/version_c++23.cc
@@ -4,7 +4,7 @@ 
 #include <version>
 
 #if __STDC_HOSTED__
-# if __cpp_lib_ranges != 202202L
+# if __cpp_lib_ranges != 202207L
 #  error "Feature-test macro __cpp_lib_ranges has wrong value in <version>"
 # endif
 #endif
diff --git a/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc b/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc
index 5ea24c578a8..d858b02f199 100644
--- a/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc
+++ b/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc
@@ -99,10 +99,24 @@  test03()
   return true;
 }
 
+void
+test04()
+{
+  extern int x[5];
+  struct move_only {
+    move_only() { }
+    move_only(move_only&&) { }
+    int operator()(int i, int j) const { return i + j; }
+  };
+  // P2494R2 Relaxing range adaptors to allow for move only types
+  static_assert( requires { views::zip_transform(move_only{}, x, x); } );
+}
+
 int
 main()
 {
   static_assert(test01());
   static_assert(test02());
   static_assert(test03());
+  test04();
 }