[PATCH] libstdc++: Implement P2259R1 changes [PR95983]

Patrick Palka ppalka@redhat.com
Tue Apr 20 02:25:21 GMT 2021


This implements the wording changes of P2259R1 "Repairing input range
adaptors and counted_iterator", which resolves LWG 3283, 3289 and 3408.

The wording changes are relatively straightforward, but they require
some boilerplate to implement: the changes to make a type alias
"conditionally present" in some iterator class are realized by defining
a base class template and an appropriately constrained partial
specialization thereof that contains the type alias, and having the
iterator class derive from this base class.  Sometimes the relevant
condition depend on members from the iterator class, but because a
class is incomplete when instantiating its bases, the constraints on
the partial specialization can't use anything from the iterator class.
This patch works around this by hoisting these members out to the
enclosing scope (e.g. transform_view::_Iterator::_Base is hoisted out
to transform_view::_Base so that transform_view::__iter_cat can use it).

This patch also implements the proposed resolution of LWG 3291 to rename
iota_view::iterator_category to iota_view::iterator_concept, which was
previously problematic due to LWG 3408.

Tested on x86_64-pc-linux-gnu.

libstdc++-v3/ChangeLog:

	PR libstdc++/95983
	* include/bits/stl_iterator.h (__detail::__move_iter_cat):
	Define.
	(move_iterator): Derive from the above in C++20 in order to
	conditionally define iterator_category as per P2259.
	(move_iterator::__base_cat): No longer used, so remove.
	(move_iterator::iterator_category): Remove in C++20.
	(__detail::__common_iter_use_postfix_proxy): Define.
	(common_iterator::_Proxy): Rename to ...
	(common_iterator:__arrow_proxy): ... this.
	(common_iterator::__postfix_proxy): Define as per P2259.
	(common_iterator::operator->): Adjust.
	(common_iterator::operator++): Adjust as per P2259.
	(iterator_traits<common_iterator>::_S_iter_cat): Define.
	(iterator_traits<common_iterator>::iterator_category): Change as
	per P2259.
	(__detail::__counted_iter_value_type): Define.
	(__detail::__counted_iter_concept): Define.
	(__detail::__counted_iter_cat): Define.
	(counted_iterator): Derive from the above three classes in order
	to conditionally define value_type, iterator_concept and
	iterator category respectively as per P2259.
	(counted_iterator::operator->): Define as per P2259.
	(incrementable_traits<counted_iterator>): Remove as per P2259.
	(iterator_traits<counted_iterator>): Adjust as per P2259.
	* include/std/ranges (__detail::__iota_view_iter_cat): Define.
	(iota_view::_Iterator): Derive from the above in order to
	conditionally define iterator_category as per P2259.
	(iota_view::_S_iter_cat): Rename to ...
	(iota_view::_S_iter_concept): ... this.
	(iota_view::iterator_concept): Use it to apply LWG 3291 changes.
	(iota_view::iterator_category): Remove.
	(__detail::__filter_view_iter_cat): Define.
	(filter_view::_Iterator): Derive from the above in order to
	conditionally define iterator_category as per P2259.
	(filter_view::_Iterator): Move to struct __filter_view_iter_cat.
	(filter_view::_Iterator::iterator_category): Remove.
	(transform_view::_Base): Define.
	(transform_view::__iter_cat): Define.
	(transform_view::_Iterator): Derive from the above in order to
	conditionally define iterator_category as per P2259.
	(transform_view::_Iterator::_Base): Just alias
	transform_view::_Base.
	(transform_view::_Iterator::_S_iter_cat): Move to struct
	transform_view::__iter_cat.
	(transform_view::_Iterator::iterator_category): Remove.
	(transform_view::_Sentinel::_Base): Just alias
	transform_view::_Base.
	(join_view::_Base): Define.
	(join_view::_Outer_iter): Define.
	(join_view::_Inner_iter): Define.
	(join_view::_S_ref_is_glvalue): Define.
	(join_view::__iter_cat): Define.
	(join_view::_Iterator): Derive from it in order to conditionally
	define iterator_category as per P2259.
	(join_view::_Iterator::_Base): Just alias join_view::_Base.
	(join_view::_Iterator::_S_ref_is_glvalue): Just alias
	join_view::_S_ref_is_glvalue.
	(join_view::_Iterator::_S_iter_cat): Move to struct
	transform_view::__iter_cat.
	(join_view::_Iterator::_Outer_iter): Just alias
	join_view::_Outer_iter.
	(join_view::_Iterator::_Inner_iter): Just alias
	join_view::_Inner_iter.
	(join_view::_Iterator::iterator_category): Remove.
	(join_view::_Sentinel::_Base): Just alias join_view::_Base.
	(__detail::__split_view_outer_iter_cat): Define.
	(__detail::__split_view_inner_iter_cat): Define.
	(split_view::_Base): Define.
	(split_view::_Outer_iter): Derive from __split_view_outer_iter_cat
	in order to conditionally define iterator_category as per P2259.
	(split_view::_Outer_iter::iterator_category): Remove.
	(split_view::_Inner_iter): Derive from __split_view_inner_iter_cat
	in order to conditionally define iterator_category as per P2259.
	(split_view::_Inner_iter::_S_iter_cat): Move to
	__split_view_inner_iter_cat.
	(split_view::_Inner_iter::iterator_category): Remove.
	(elements_view::_Base): Define.
	(elements_view::__iter_cat): Define.
	(elements_view::_Iterator): Derive from the above in order to
	conditionall define iterator_category as per P2259.
	(elements_view::_Iterator::_Base): Just alias
	elements_view::_Base.
	(elements_view::_Iterator::_S_iter_concept)
	(elements_view::_Iterator::iterator_concept): Define as per
	P2259.
	(elements_view::_Iterator::iterator_category): Remove.
	(elements_view::_Sentinel::_Base): Just alias
	elements_view::_Base.
	* testsuite/24_iterators/headers/iterator/synopsis_c++20: Adjust
	constraints on iterator_traits<counted_iterator>.
	* testsuite/std/ranges/p2259.cc: New test.
---
 libstdc++-v3/include/bits/stl_iterator.h      | 128 ++++++--
 libstdc++-v3/include/std/ranges               | 299 ++++++++++++------
 .../headers/iterator/synopsis_c++20.cc        |   1 +
 libstdc++-v3/testsuite/std/ranges/p2259.cc    |  91 ++++++
 4 files changed, 412 insertions(+), 107 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/std/ranges/p2259.cc

diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h
index dc8b101e8f8..049f83cff90 100644
--- a/libstdc++-v3/include/bits/stl_iterator.h
+++ b/libstdc++-v3/include/bits/stl_iterator.h
@@ -1302,6 +1302,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 #endif // C++20
 
+  namespace __detail
+  {
+#if __cplusplus > 201703L && __cpp_lib_concepts
+    template<typename _Iterator>
+      struct __move_iter_cat
+      { };
+
+    template<typename _Iterator>
+      requires requires { typename iterator_traits<_Iterator>::iterator_category; }
+      struct __move_iter_cat<_Iterator>
+      {
+	using iterator_category
+	  = __clamp_iter_cat<typename iterator_traits<_Iterator>::iterator_category,
+			     random_access_iterator_tag>;
+      };
+#endif
+  }
+
   // 24.4.3  Move iterators
   /**
    *  Class template move_iterator is an iterator adapter with the same
@@ -1313,13 +1331,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    */
   template<typename _Iterator>
     class move_iterator
+#if __cplusplus > 201703L && __cpp_lib_concepts
+      : public __detail::__move_iter_cat<_Iterator>
+#endif
     {
       _Iterator _M_current;
 
       using __traits_type = iterator_traits<_Iterator>;
-#if __cplusplus > 201703L && __cpp_lib_concepts
-      using __base_cat = typename __traits_type::iterator_category;
-#else
+#if ! (__cplusplus > 201703L && __cpp_lib_concepts)
       using __base_ref = typename __traits_type::reference;
 #endif
 
@@ -1339,8 +1358,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 #if __cplusplus > 201703L && __cpp_lib_concepts
       using iterator_concept = input_iterator_tag;
-      using iterator_category
-	= __detail::__clamp_iter_cat<__base_cat, random_access_iterator_tag>;
+      // iterator_category defined in __move_iter_cat
       using value_type = iter_value_t<_Iterator>;
       using difference_type = iter_difference_t<_Iterator>;
       using pointer = _Iterator;
@@ -1662,6 +1680,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    || is_reference_v<iter_reference_t<_It>>
 	    || constructible_from<iter_value_t<_It>, iter_reference_t<_It>>);
 
+    template<typename _It>
+      concept __common_iter_use_postfix_proxy
+	= (!requires (_It& __i) { { *__i++ } -> __can_reference; })
+	  && constructible_from<iter_value_t<_It>, iter_reference_t<_It>>;
   } // namespace __detail
 
   /// An iterator/sentinel adaptor for representing a non-common range.
@@ -1684,11 +1706,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _S_noexcept()
       { return _S_noexcept1<_It, _It2>() && _S_noexcept1<_Sent, _Sent2>(); }
 
-    class _Proxy
+    class __arrow_proxy
     {
       iter_value_t<_It> _M_keep;
 
-      _Proxy(iter_reference_t<_It>&& __x)
+      __arrow_proxy(iter_reference_t<_It>&& __x)
       : _M_keep(std::move(__x)) { }
 
       friend class common_iterator;
@@ -1699,6 +1721,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return std::__addressof(_M_keep); }
     };
 
+    class __postfix_proxy
+    {
+      iter_value_t<_It> _M_keep;
+
+      __postfix_proxy(iter_reference_t<_It>&& __x)
+      : _M_keep(std::move(__x)) { }
+
+      friend class common_iterator;
+
+    public:
+      const iter_value_t<_It>&
+      operator*() const
+      { return _M_keep; }
+    };
+
   public:
     constexpr
     common_iterator()
@@ -1855,7 +1892,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  return std::__addressof(__tmp);
 	}
       else
-	return _Proxy{*_M_it};
+	return __arrow_proxy{*_M_it};
     }
 
     common_iterator&
@@ -1876,8 +1913,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  ++*this;
 	  return __tmp;
 	}
-      else
+      else if constexpr (!__detail::__common_iter_use_postfix_proxy<_It>)
 	return _M_it++;
+      else
+	{
+	  __postfix_proxy __p(**this);
+	  ++*this;
+	  return __p;
+	}
     }
 
     template<typename _It2, sentinel_for<_It> _Sent2>
@@ -2008,12 +2051,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  using type = decltype(std::declval<const _CIter&>().operator->());
 	};
 
+      static auto
+      _S_iter_cat()
+      {
+	using _Traits = iterator_traits<_It>;
+	if constexpr (requires { requires derived_from<typename _Traits::iterator_category,
+						       forward_iterator_tag>; })
+	  return forward_iterator_tag{};
+	else
+	  return input_iterator_tag{};
+      }
+
     public:
       using iterator_concept = conditional_t<forward_iterator<_It>,
 	    forward_iterator_tag, input_iterator_tag>;
-      using iterator_category = __detail::__clamp_iter_cat<
-	typename iterator_traits<_It>::iterator_category,
-	forward_iterator_tag, input_iterator_tag>;
+      using iterator_category = decltype(_S_iter_cat());
       using value_type = iter_value_t<_It>;
       using difference_type = iter_difference_t<_It>;
       using pointer = typename __ptr<_It>::type;
@@ -2022,12 +2074,48 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   // [iterators.counted] Counted iterators
 
+  namespace __detail
+  {
+    template<typename _It>
+      struct __counted_iter_value_type
+      { };
+
+    template<indirectly_readable _It>
+      struct __counted_iter_value_type<_It>
+      { using value_type = iter_value_t<_It>; };
+
+    template<typename _It>
+      struct __counted_iter_concept
+      { };
+
+    template<typename _It>
+      requires requires { typename _It::iterator_concept; }
+      struct __counted_iter_concept<_It>
+      { using iterator_concept = typename _It::iterator_concept; };
+
+    template<typename _It>
+      struct __counted_iter_cat
+      { };
+
+    template<typename _It>
+      requires requires { typename _It::iterator_category; }
+      struct __counted_iter_cat<_It>
+      { using iterator_category = typename _It::iterator_category; };
+  }
+
   /// An iterator adaptor that keeps track of the distance to the end.
   template<input_or_output_iterator _It>
     class counted_iterator
+      : public __detail::__counted_iter_value_type<_It>,
+	public __detail::__counted_iter_concept<_It>,
+	public __detail::__counted_iter_cat<_It>
     {
     public:
       using iterator_type = _It;
+      // value_type defined in __counted_iter_value_type
+      using difference_type = iter_difference_t<_It>;
+      // iterator_concept defined in __counted_iter_concept
+      // iterator_category defined in __counted_iter_cat
 
       constexpr counted_iterator() = default;
 
@@ -2084,6 +2172,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	return *_M_current;
       }
 
+      constexpr auto
+      operator->() const noexcept
+      requires contiguous_iterator<_It>
+      { return std::to_address(_M_current); }
+
       constexpr counted_iterator&
       operator++()
       {
@@ -2232,16 +2325,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       iter_difference_t<_It> _M_length = 0;
     };
 
-  template<typename _It>
-    struct incrementable_traits<counted_iterator<_It>>
-    {
-      using difference_type = iter_difference_t<_It>;
-    };
-
   template<input_iterator _It>
+    requires same_as<__detail::__iter_traits<_It>, iterator_traits<_It>>
     struct iterator_traits<counted_iterator<_It>> : iterator_traits<_It>
     {
-      using pointer = void;
+      using pointer = conditional_t<contiguous_iterator<_It>,
+				    add_pointer_t<iter_reference_t<_It>>,
+				    void>;
     };
 #endif // C++20
 
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index baec8c0efef..74075a2d6d3 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -290,6 +290,13 @@ namespace ranges
 	  { __j - __j } -> convertible_to<__iota_diff_t<_It>>;
 	};
 
+    template<typename _Winc>
+      struct __iota_view_iter_cat
+      { };
+
+    template<incrementable _Winc>
+      struct __iota_view_iter_cat<_Winc>
+      { using iterator_category = input_iterator_tag; };
   } // namespace __detail
 
   template<weakly_incrementable _Winc,
@@ -301,11 +308,11 @@ namespace ranges
     private:
       struct _Sentinel;
 
-      struct _Iterator
+      struct _Iterator : __detail::__iota_view_iter_cat<_Winc>
       {
       private:
 	static auto
-	_S_iter_cat()
+	_S_iter_concept()
 	{
 	  using namespace __detail;
 	  if constexpr (__advanceable<_Winc>)
@@ -319,7 +326,8 @@ namespace ranges
 	}
 
       public:
-	using iterator_category = decltype(_S_iter_cat());
+	using iterator_concept = decltype(_S_iter_concept());
+	// iterator_category defined in __iota_view_iter_cat
 	using value_type = _Winc;
 	using difference_type = __detail::__iota_diff_t<_Winc>;
 
@@ -1100,7 +1108,32 @@ namespace views::__adaptor
 	  _M_offset = __it - ranges::begin(__r);
 	}
       };
+  } // namespace __detail
+
+  namespace __detail
+  {
+    template<typename _Base>
+      struct __filter_view_iter_cat
+      { };
 
+    template<forward_range _Base>
+      struct __filter_view_iter_cat<_Base>
+      {
+      private:
+	static auto
+	_S_iter_cat()
+	{
+	  using _Cat = typename iterator_traits<iterator_t<_Base>>::iterator_category;
+	  if constexpr (derived_from<_Cat, bidirectional_iterator_tag>)
+	    return bidirectional_iterator_tag{};
+	  else if constexpr (derived_from<_Cat, forward_iterator_tag>)
+	    return forward_iterator_tag{};
+	  else
+	    return _Cat{};
+	}
+      public:
+	using iterator_category = decltype(_S_iter_cat());
+      };
   } // namespace __detail
 
   template<input_range _Vp,
@@ -1111,7 +1144,7 @@ namespace views::__adaptor
     private:
       struct _Sentinel;
 
-      struct _Iterator
+      struct _Iterator : __detail::__filter_view_iter_cat<_Vp>
       {
       private:
 	static constexpr auto
@@ -1125,18 +1158,6 @@ namespace views::__adaptor
 	    return input_iterator_tag{};
 	}
 
-	static constexpr auto
-	_S_iter_cat()
-	{
-	  using _Cat = typename iterator_traits<_Vp_iter>::iterator_category;
-	  if constexpr (derived_from<_Cat, bidirectional_iterator_tag>)
-	    return bidirectional_iterator_tag{};
-	  else if constexpr (derived_from<_Cat, forward_iterator_tag>)
-	    return forward_iterator_tag{};
-	  else
-	    return _Cat{};
-	}
-
 	friend filter_view;
 
 	using _Vp_iter = iterator_t<_Vp>;
@@ -1146,7 +1167,7 @@ namespace views::__adaptor
 
       public:
 	using iterator_concept = decltype(_S_iter_concept());
-	using iterator_category = decltype(_S_iter_cat());
+	// iterator_category defined in __filter_view_iter_cat
 	using value_type = range_value_t<_Vp>;
 	using difference_type = range_difference_t<_Vp>;
 
@@ -1344,36 +1365,26 @@ namespace views::__adaptor
     {
     private:
       template<bool _Const>
-	struct _Sentinel;
+	using _Base = __detail::__maybe_const_t<_Const, _Vp>;
 
       template<bool _Const>
-	struct _Iterator
+	struct __iter_cat
+	{ };
+
+      template<bool _Const>
+	requires forward_range<_Base<_Const>>
+	struct __iter_cat<_Const>
 	{
 	private:
-	  using _Parent = __detail::__maybe_const_t<_Const, transform_view>;
-	  using _Base = __detail::__maybe_const_t<_Const, _Vp>;
-
-	  static constexpr auto
-	  _S_iter_concept()
-	  {
-	    if constexpr (random_access_range<_Vp>)
-	      return random_access_iterator_tag{};
-	    else if constexpr (bidirectional_range<_Vp>)
-	      return bidirectional_iterator_tag{};
-	    else if constexpr (forward_range<_Vp>)
-	      return forward_iterator_tag{};
-	    else
-	      return input_iterator_tag{};
-	  }
-
-	  static constexpr auto
+	  static auto
 	  _S_iter_cat()
 	  {
+	    using _Base = transform_view::_Base<_Const>;
 	    using _Res = invoke_result_t<_Fp&, range_reference_t<_Base>>;
 	    if constexpr (is_lvalue_reference_v<_Res>)
 	      {
 		using _Cat
-		  = typename iterator_traits<_Base_iter>::iterator_category;
+		  = typename iterator_traits<iterator_t<_Base>>::iterator_category;
 		if constexpr (derived_from<_Cat, contiguous_iterator_tag>)
 		  return random_access_iterator_tag{};
 		else
@@ -1382,6 +1393,32 @@ namespace views::__adaptor
 	    else
 	      return input_iterator_tag{};
 	  }
+	public:
+	  using iterator_category = decltype(_S_iter_cat());
+	};
+
+      template<bool _Const>
+	struct _Sentinel;
+
+      template<bool _Const>
+	struct _Iterator : __iter_cat<_Const>
+	{
+	private:
+	  using _Parent = __detail::__maybe_const_t<_Const, transform_view>;
+	  using _Base = transform_view::_Base<_Const>;
+
+	  static auto
+	  _S_iter_concept()
+	  {
+	    if constexpr (random_access_range<_Vp>)
+	      return random_access_iterator_tag{};
+	    else if constexpr (bidirectional_range<_Vp>)
+	      return bidirectional_iterator_tag{};
+	    else if constexpr (forward_range<_Vp>)
+	      return forward_iterator_tag{};
+	    else
+	      return input_iterator_tag{};
+	  }
 
 	  using _Base_iter = iterator_t<_Base>;
 
@@ -1390,7 +1427,7 @@ namespace views::__adaptor
 
 	public:
 	  using iterator_concept = decltype(_S_iter_concept());
-	  using iterator_category = decltype(_S_iter_cat());
+	  // iterator_category defined in __transform_view_iter_cat
 	  using value_type
 	    = remove_cvref_t<invoke_result_t<_Fp&, range_reference_t<_Base>>>;
 	  using difference_type = range_difference_t<_Base>;
@@ -1556,7 +1593,7 @@ namespace views::__adaptor
 	{
 	private:
 	  using _Parent = __detail::__maybe_const_t<_Const, transform_view>;
-	  using _Base = __detail::__maybe_const_t<_Const, _Vp>;
+	  using _Base = transform_view::_Base<_Const>;
 
 	  template<bool _Const2>
 	    constexpr auto
@@ -2210,18 +2247,62 @@ namespace views::__adaptor
     private:
       using _InnerRange = range_reference_t<_Vp>;
 
+      template<bool _Const>
+	using _Base = __detail::__maybe_const_t<_Const, _Vp>;
+
+      template<bool _Const>
+	using _Outer_iter = iterator_t<_Base<_Const>>;
+
+      template<bool _Const>
+	using _Inner_iter = iterator_t<range_reference_t<_Base<_Const>>>;
+
+      template<bool _Const>
+	static constexpr bool _S_ref_is_glvalue
+	  = is_reference_v<range_reference_t<_Base<_Const>>>;
+
+      template<bool _Const>
+	struct __iter_cat
+	{ };
+
+      template<bool _Const>
+	requires _S_ref_is_glvalue<_Const>
+	  && forward_range<_Base<_Const>>
+	  && forward_range<range_reference_t<_Base<_Const>>>
+	struct __iter_cat<_Const>
+	{
+	private:
+	  static constexpr auto
+	  _S_iter_cat()
+	  {
+	    using _Outer_iter = join_view::_Outer_iter<_Const>;
+	    using _Inner_iter = join_view::_Inner_iter<_Const>;
+	    using _OuterCat = typename iterator_traits<_Outer_iter>::iterator_category;
+	    using _InnerCat = typename iterator_traits<_Inner_iter>::iterator_category;
+	    if constexpr (derived_from<_OuterCat, bidirectional_iterator_tag>
+			  && derived_from<_InnerCat, bidirectional_iterator_tag>)
+	      return bidirectional_iterator_tag{};
+	    else if constexpr (derived_from<_OuterCat, forward_iterator_tag>
+			       && derived_from<_InnerCat, forward_iterator_tag>)
+	      return forward_iterator_tag{};
+	    else
+	      return input_iterator_tag{};
+	  }
+	public:
+	  using iterator_category = decltype(_S_iter_cat());
+	};
+
       template<bool _Const>
 	struct _Sentinel;
 
       template<bool _Const>
-	struct _Iterator
+	struct _Iterator : __iter_cat<_Const>
 	{
 	private:
 	  using _Parent = __detail::__maybe_const_t<_Const, join_view>;
-	  using _Base = __detail::__maybe_const_t<_Const, _Vp>;
+	  using _Base = join_view::_Base<_Const>;
 
 	  static constexpr bool _S_ref_is_glvalue
-	    = is_reference_v<range_reference_t<_Base>>;
+	    = join_view::_S_ref_is_glvalue<_Const>;
 
 	  constexpr void
 	  _M_satisfy()
@@ -2261,30 +2342,8 @@ namespace views::__adaptor
 	      return input_iterator_tag{};
 	  }
 
-	  static constexpr auto
-	  _S_iter_cat()
-	  {
-	    using _OuterCat
-	      = typename iterator_traits<_Outer_iter>::iterator_category;
-	    using _InnerCat
-	      = typename iterator_traits<_Inner_iter>::iterator_category;
-	    if constexpr (_S_ref_is_glvalue
-			  && derived_from<_OuterCat, bidirectional_iterator_tag>
-			  && derived_from<_InnerCat, bidirectional_iterator_tag>)
-	      return bidirectional_iterator_tag{};
-	    else if constexpr (_S_ref_is_glvalue
-			       && derived_from<_OuterCat, forward_iterator_tag>
-			       && derived_from<_InnerCat, forward_iterator_tag>)
-	      return forward_iterator_tag{};
-	    else if constexpr (derived_from<_OuterCat, input_iterator_tag>
-			       && derived_from<_InnerCat, input_iterator_tag>)
-	      return input_iterator_tag{};
-	    else
-	      return output_iterator_tag{};
-	  }
-
-	  using _Outer_iter = iterator_t<_Base>;
-	  using _Inner_iter = iterator_t<range_reference_t<_Base>>;
+	  using _Outer_iter = join_view::_Outer_iter<_Const>;
+	  using _Inner_iter = join_view::_Inner_iter<_Const>;
 
 	  _Outer_iter _M_outer = _Outer_iter();
 	  _Inner_iter _M_inner = _Inner_iter();
@@ -2292,7 +2351,7 @@ namespace views::__adaptor
 
 	public:
 	  using iterator_concept = decltype(_S_iter_concept());
-	  using iterator_category = decltype(_S_iter_cat());
+	  // iterator_category defined in __join_view_iter_cat
 	  using value_type = range_value_t<range_reference_t<_Base>>;
 	  using difference_type
 	    = common_type_t<range_difference_t<_Base>,
@@ -2412,7 +2471,7 @@ namespace views::__adaptor
 	{
 	private:
 	  using _Parent = __detail::__maybe_const_t<_Const, join_view>;
-	  using _Base = __detail::__maybe_const_t<_Const, _Vp>;
+	  using _Base = join_view::_Base<_Const>;
 
 	  template<bool _Const2>
 	    constexpr bool
@@ -2550,6 +2609,35 @@ namespace views::__adaptor
 	&& requires
 	   { typename __require_constant<remove_reference_t<_Range>::size()>; }
 	&& (remove_reference_t<_Range>::size() <= 1);
+
+    template<typename _Base>
+      struct __split_view_outer_iter_cat
+      { };
+
+    template<forward_range _Base>
+      struct __split_view_outer_iter_cat<_Base>
+      { using iterator_category = input_iterator_tag; };
+
+    template<typename _Base>
+      struct __split_view_inner_iter_cat
+      { };
+
+    template<forward_range _Base>
+      struct __split_view_inner_iter_cat<_Base>
+      {
+      private:
+	static constexpr auto
+	_S_iter_cat()
+	{
+	  using _Cat = typename iterator_traits<iterator_t<_Base>>::iterator_category;
+	  if constexpr (derived_from<_Cat, forward_iterator_tag>)
+	    return forward_iterator_tag{};
+	  else
+	    return _Cat{};
+	}
+      public:
+	using iterator_category = decltype(_S_iter_cat());
+      };
   }
 
   template<input_range _Vp, forward_range _Pattern>
@@ -2560,15 +2648,19 @@ namespace views::__adaptor
     class split_view : public view_interface<split_view<_Vp, _Pattern>>
     {
     private:
+      template<bool _Const>
+	using _Base = __detail::__maybe_const_t<_Const, _Vp>;
+
       template<bool _Const>
 	struct _InnerIter;
 
       template<bool _Const>
 	struct _OuterIter
+	  : __detail::__split_view_outer_iter_cat<_Base<_Const>>
 	{
 	private:
 	  using _Parent = __detail::__maybe_const_t<_Const, split_view>;
-	  using _Base = __detail::__maybe_const_t<_Const, _Vp>;
+	  using _Base = split_view::_Base<_Const>;
 
 	  constexpr bool
 	  __at_end() const
@@ -2607,7 +2699,7 @@ namespace views::__adaptor
 	  using iterator_concept = conditional_t<forward_range<_Base>,
 						 forward_iterator_tag,
 						 input_iterator_tag>;
-	  using iterator_category = input_iterator_tag;
+	  // iterator_category defined in __split_view_outer_iter_cat
 	  using difference_type = range_difference_t<_Base>;
 
 	  struct value_type : view_interface<value_type>
@@ -2723,9 +2815,10 @@ namespace views::__adaptor
 
       template<bool _Const>
 	struct _InnerIter
+	  : __detail::__split_view_inner_iter_cat<_Base<_Const>>
 	{
 	private:
-	  using _Base = __detail::__maybe_const_t<_Const, _Vp>;
+	  using _Base = split_view::_Base<_Const>;
 
 	  constexpr bool
 	  __at_end() const
@@ -2759,17 +2852,6 @@ namespace views::__adaptor
 	      }
 	  }
 
-	  static constexpr auto
-	  _S_iter_cat()
-	  {
-	    using _Cat
-              = typename iterator_traits<iterator_t<_Base>>::iterator_category;
-	    if constexpr (derived_from<_Cat, forward_iterator_tag>)
-	      return forward_iterator_tag{};
-	    else
-	      return _Cat{};
-	  }
-
 	  constexpr auto&
 	  _M_i_current() noexcept
 	  { return _M_i.__current(); }
@@ -2784,7 +2866,7 @@ namespace views::__adaptor
 	public:
 	  using iterator_concept
 	    = typename _OuterIter<_Const>::iterator_concept;
-	  using iterator_category = decltype(_S_iter_cat());
+	  // iterator_category defined in __split_view_inner_iter_cat
 	  using value_type = range_value_t<_Base>;
 	  using difference_type = range_difference_t<_Base>;
 
@@ -3292,14 +3374,42 @@ namespace views::__adaptor
       { return ranges::size(_M_base); }
 
     private:
+      template<bool _Const>
+	using _Base = __detail::__maybe_const_t<_Const, _Vp>;
+
+      template<bool _Const>
+	struct __iter_cat
+	{ };
+
+      template<bool _Const>
+	requires forward_range<_Base<_Const>>
+	struct __iter_cat<_Const>
+	{
+	private:
+	  static auto _S_iter_cat()
+	  {
+	    using _Base = elements_view::_Base<_Const>;
+	    using _Cat = iterator_traits<iterator_t<_Base>>::iterator_category;
+	    using _Res = decltype((std::get<_Nm>(*std::declval<iterator_t<_Base>>())));
+	    if constexpr (!is_lvalue_reference_v<_Res>)
+	      return input_iterator_tag{};
+	    else if constexpr (derived_from<_Cat, random_access_iterator_tag>)
+	      return random_access_iterator_tag{};
+	    else
+	      return _Cat{};
+	  }
+	public:
+	  using iterator_category = decltype(_S_iter_cat());
+	};
+
       template<bool _Const>
 	struct _Sentinel;
 
       template<bool _Const>
-	struct _Iterator
+	struct _Iterator : __iter_cat<_Const>
 	{
 	private:
-	  using _Base = __detail::__maybe_const_t<_Const, _Vp>;
+	  using _Base = elements_view::_Base<_Const>;
 
 	  iterator_t<_Base> _M_current = iterator_t<_Base>();
 
@@ -3315,11 +3425,24 @@ namespace views::__adaptor
 	      }
 	  }
 
+	  static auto
+	  _S_iter_concept()
+	  {
+	    if constexpr (random_access_range<_Vp>)
+	      return random_access_iterator_tag{};
+	    else if constexpr (bidirectional_range<_Vp>)
+	      return bidirectional_iterator_tag{};
+	    else if constexpr (forward_range<_Vp>)
+	      return forward_iterator_tag{};
+	    else
+	      return input_iterator_tag{};
+	  }
+
 	  friend _Iterator<!_Const>;
 
 	public:
-	  using iterator_category
-	    = typename iterator_traits<iterator_t<_Base>>::iterator_category;
+	  using iterator_concept = decltype(_S_iter_concept());
+	  // iterator_category defined in elements_view::__iter_cat
 	  using value_type
 	    = remove_cvref_t<tuple_element_t<_Nm, range_value_t<_Base>>>;
 	  using difference_type = range_difference_t<_Base>;
@@ -3471,7 +3594,7 @@ namespace views::__adaptor
 	  _M_equal(const _Iterator<_Const>& __x) const
 	  { return __x._M_current == _M_end; }
 
-	  using _Base = __detail::__maybe_const_t<_Const, _Vp>;
+	  using _Base = elements_view::_Base<_Const>;
 	  sentinel_t<_Base> _M_end = sentinel_t<_Base>();
 
 	public:
diff --git a/libstdc++-v3/testsuite/24_iterators/headers/iterator/synopsis_c++20.cc b/libstdc++-v3/testsuite/24_iterators/headers/iterator/synopsis_c++20.cc
index 3ebb1e0e204..3103094b36c 100644
--- a/libstdc++-v3/testsuite/24_iterators/headers/iterator/synopsis_c++20.cc
+++ b/libstdc++-v3/testsuite/24_iterators/headers/iterator/synopsis_c++20.cc
@@ -73,6 +73,7 @@ namespace std
     struct incrementable_traits<counted_iterator<I>>;
 
   template<input_iterator I>
+    requires same_as<__detail::__iter_traits<I>, iterator_traits<I>>
     struct iterator_traits<counted_iterator<I>>;
 
   struct unreachable_sentinel_t;
diff --git a/libstdc++-v3/testsuite/std/ranges/p2259.cc b/libstdc++-v3/testsuite/std/ranges/p2259.cc
new file mode 100644
index 00000000000..1b422e44f16
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/p2259.cc
@@ -0,0 +1,91 @@
+// Copyright (C) 2021 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 compile { target c++2a } }
+
+// Verify P2259 changes.
+
+#include <ranges>
+#include <tuple>
+#include <vector>
+
+namespace ranges = std::ranges;
+namespace views = std::views;
+
+using std::__detail::__iter_without_category;
+
+template<typename _Range>
+concept only_cxx20_input_range = ranges::input_range<_Range>
+  && !ranges::forward_range<_Range>
+  && __iter_without_category<ranges::iterator_t<_Range>>;
+
+void
+test01()
+{
+  extern std::vector<int> vec;
+  only_cxx20_input_range auto v0
+    = vec
+    | views::transform([](int c) { return views::single(c); })
+    | views::join;
+
+  // Verify the changes to filter_view.
+  only_cxx20_input_range auto v1 = v0 | views::filter([](int c) { return c > 0; });
+
+  // Verify the changes to transform_view.
+  only_cxx20_input_range auto v2 = v0 | views::transform([](int& c) -> auto& { return c; });
+
+  // Verify the changes to split_view.
+  only_cxx20_input_range auto v3 = v0 | views::split(12);
+  static_assert(only_cxx20_input_range<decltype(*v3.begin())>);
+
+  // Verify the changes to join_view.
+  only_cxx20_input_range auto v4 = v0 | views::split(12) | views::join;
+
+  // Verify the changes to elements_view.
+  only_cxx20_input_range auto v5
+    = v0
+    | views::transform([](int c) { return std::make_tuple(c, c); })
+    | views::elements<0>;
+
+  // Verify the changes to common_iterator.
+  only_cxx20_input_range auto v6 = v0 | views::common;
+  *(v6.begin()++);
+
+  // Verify the changes to iota_view.
+  only_cxx20_input_range auto v8 = ranges::iota_view{v0.begin()};
+
+  // Verify the changes to move_iterator.
+  __iter_without_category auto i9 = std::make_move_iterator(v0.begin());
+
+  // Verify the changes to counted_iterator.
+  extern std::counted_iterator<int*> i10;
+  static_assert(std::contiguous_iterator<decltype(i10)>);
+  static_assert(std::same_as<std::iterator_traits<decltype(i10)>::iterator_category,
+			     std::random_access_iterator_tag>);
+  i10.operator->();
+  __iter_without_category auto i11 = std::counted_iterator{v0.begin(), 5};
+}
+
+void
+test02()
+{
+  // Verify LWG 3291 example.
+  auto v = views::iota(0);
+  auto i = std::counted_iterator{v.begin(), 5};
+  static_assert(std::random_access_iterator<decltype(i)>);
+}
-- 
2.31.1.305.gd1b10fc6d8



More information about the Libstdc++ mailing list