libstdc++: Implement P2259R1 changes [PR95983]
authorPatrick Palka <ppalka@redhat.com>
Tue, 20 Apr 2021 13:18:50 +0000 (09:18 -0400)
committerPatrick Palka <ppalka@redhat.com>
Tue, 20 Apr 2021 13:18:50 +0000 (09:18 -0400)
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.

libstdc++-v3/include/bits/stl_iterator.h
libstdc++-v3/include/std/ranges
libstdc++-v3/testsuite/24_iterators/headers/iterator/synopsis_c++20.cc
libstdc++-v3/testsuite/std/ranges/p2259.cc [new file with mode: 0644]

index dc8b101..049f83c 100644 (file)
@@ -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
 
index baec8c0..74075a2 100644 (file)
@@ -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
@@ -2211,17 +2248,61 @@ namespace views::__adaptor
       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>
@@ -2561,14 +2649,18 @@ namespace views::__adaptor
     {
     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>;
 
@@ -3293,13 +3375,41 @@ namespace views::__adaptor
 
     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:
index 3ebb1e0..3103094 100644 (file)
@@ -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 (file)
index 0000000..1b422e4
--- /dev/null
@@ -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)>);
+}