[gcc r10-9796] libstdc++: Implement P2259R1 changes [PR95983]

Patrick Palka ppalka@gcc.gnu.org
Tue May 4 13:50:59 GMT 2021


https://gcc.gnu.org/g:1cb39945993c89746b0347746bd1267de85cbc42

commit r10-9796-g1cb39945993c89746b0347746bd1267de85cbc42
Author: Patrick Palka <ppalka@redhat.com>
Date:   Tue Apr 20 09:18:50 2021 -0400

    libstdc++: Implement P2259R1 changes [PR95983]
    
    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.
    
    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.cc:
            Adjust constraints on iterator_traits<counted_iterator>.
            * testsuite/std/ranges/p2259.cc: New test.
    
    (cherry picked from commit 902b40c797a86f76791c32d537ba06dff5f1ba27)

Diff:
---
 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(-)

diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h
index 49c9a5c17d2..3ffd23219cf 100644
--- a/libstdc++-v3/include/bits/stl_iterator.h
+++ b/libstdc++-v3/include/bits/stl_iterator.h
@@ -1270,6 +1270,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
@@ -1281,13 +1299,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
 
@@ -1296,8 +1315,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;
@@ -1612,6 +1630,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.
@@ -1634,11 +1656,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;
@@ -1649,6 +1671,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()
@@ -1805,7 +1842,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  return std::__addressof(__tmp);
 	}
       else
-	return _Proxy{*_M_it};
+	return __arrow_proxy{*_M_it};
     }
 
     common_iterator&
@@ -1826,8 +1863,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>
@@ -1958,12 +2001,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;
@@ -1972,12 +2024,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;
 
@@ -2028,6 +2116,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       requires __detail::__dereferenceable<const _It>
       { return *_M_current; }
 
+      constexpr auto
+      operator->() const noexcept
+      requires contiguous_iterator<_It>
+      { return std::to_address(_M_current); }
+
       constexpr counted_iterator&
       operator++()
       {
@@ -2170,16 +2263,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 cb7c6c8b5b7..d1eb8eaefe0 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -610,6 +610,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,
@@ -621,11 +628,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>)
@@ -639,7 +646,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>;
 
@@ -1399,7 +1407,32 @@ namespace views
 	  _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,
@@ -1410,7 +1443,7 @@ namespace views
     private:
       struct _Sentinel;
 
-      struct _Iterator
+      struct _Iterator : __detail::__filter_view_iter_cat<_Vp>
       {
       private:
 	static constexpr auto
@@ -1424,18 +1457,6 @@ namespace views
 	    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>;
@@ -1445,7 +1466,7 @@ namespace views
 
       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>;
 
@@ -1626,36 +1647,26 @@ namespace views
     {
     private:
       template<bool _Const>
-	struct _Sentinel;
+	using _Base = __detail::__maybe_const_t<_Const, _Vp>;
+
+      template<bool _Const>
+	struct __iter_cat
+	{ };
 
       template<bool _Const>
-	struct _Iterator
+	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
@@ -1664,6 +1675,32 @@ namespace views
 	    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>;
 
@@ -1672,7 +1709,7 @@ namespace views
 
 	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>;
@@ -1838,7 +1875,7 @@ namespace views
 	{
 	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
@@ -2408,18 +2445,62 @@ namespace views
     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()
@@ -2459,30 +2540,8 @@ namespace views
 	      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();
@@ -2490,7 +2549,7 @@ namespace views
 
 	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>,
@@ -2610,7 +2669,7 @@ namespace views
 	{
 	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
@@ -2735,6 +2794,35 @@ namespace views
 	&& 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>
@@ -2745,15 +2833,19 @@ namespace views
     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
@@ -2792,7 +2884,7 @@ namespace views
 	  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>
@@ -2908,9 +3000,10 @@ namespace views
 
       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
@@ -2944,17 +3037,6 @@ namespace views
 	      }
 	  }
 
-	  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(); }
@@ -2969,7 +3051,7 @@ namespace views
 	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>;
 
@@ -3431,14 +3513,42 @@ namespace views
       { 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>();
 
@@ -3454,11 +3564,24 @@ namespace views
 	      }
 	  }
 
+	  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>;
@@ -3610,7 +3733,7 @@ namespace views
 	  _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 fbe116be2fd..a75826e5408 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)>);
+}


More information about the Libstdc++-cvs mailing list