diff mbox series

[committed,2/3] libstdc++: Add remaining C++20 changes to iterator adaptors

Message ID 20200327232944.GC71320@redhat.com
State New
Headers show
Series [committed,1/3] libstdc++: Implement C++20 changes to insert iterators | expand

Commit Message

Li, Pan2 via Gcc-patches March 27, 2020, 11:29 p.m. UTC
This adds the missing parts of P0896R4 to reverse_iterator and
move_iterator, so that they meet the C++20 requirements. This should be
the last piece of P0896R4, meaning ranges support is now complete.

The PR 94354 bug with reverse_iterator's comparisons is fixed for C++20
only, but that change should be extended to C++11, C++14 and C++17 modes
in stage 1.

	* include/bits/stl_iterator.h (reverse_iterator::iterator_concept)
	(reverse_iterator::iterator_category): Define for C++20.
	(reverse_iterator): Define comparison operators correctly for C++20.
	(__normal_iterator): Add constraints to comparison operators for C++20.
	(move_iterator::operator++(int)) [__cpp_lib_concepts]: Define new
	overload for input iterators.
	(move_iterator): Add constraints to comparison operators for C++20.
	Define operator<=> for C++20.
	* testsuite/24_iterators/move_iterator/input_iterator.cc: New test.
	* testsuite/24_iterators/move_iterator/move_only.cc: New test.
	* testsuite/24_iterators/move_iterator/rel_ops_c++20.cc: New test.
	* testsuite/24_iterators/reverse_iterator/rel_ops_c++20.cc: New test.

Tested powerpc64le-linux, committed to master.

Comments

Li, Pan2 via Gcc-patches March 28, 2020, 12:16 a.m. UTC | #1
On 27/03/20 23:29 +0000, Jonathan Wakely wrote:
>@@ -403,6 +428,49 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     operator>=(const reverse_iterator<_IteratorL>& __x,
> 	       const reverse_iterator<_IteratorR>& __y)
>     { return !(__x < __y); }
>+#else // C++20
>+  template<typename _IteratorL, typename _IteratorR>
>+    constexpr auto
>+    operator==(const reverse_iterator<_IteratorL>& __x,
>+	       const reverse_iterator<_IteratorR>& __y)
>+    -> decltype(__detail::__convbool(__x.base() == __y.base()))
>+    { return __x.base() == __y.base(); }
>+
>+  template<typename _IteratorL, typename _IteratorR>
>+    constexpr auto
>+    operator!=(const reverse_iterator<_IteratorL>& __x,
>+	       const reverse_iterator<_IteratorR>& __y)
>+    -> decltype(__detail::__convbool(__x.base() != __y.base()))
>+    { return __x.base() != __y.base(); }
>+
>+  template<typename _IteratorL, typename _IteratorR>
>+    constexpr auto
>+    operator<(const reverse_iterator<_IteratorL>& __x,
>+	      const reverse_iterator<_IteratorR>& __y)
>+    -> decltype(__detail::__convbool(__x.base() < __y.base()))
>+    { return __x.base() < __y.base(); }

Drat, these relational operators use the wrong operator. The sense
needs to be flipped because they're reverse_iterators.

Looks like I lost an edit to this file that I made on another machine,
and committed the wrong version. I'll fix it ASAP.

>+  template<typename _IteratorL, typename _IteratorR>
>+    constexpr auto
>+    operator>(const reverse_iterator<_IteratorL>& __x,
>+	      const reverse_iterator<_IteratorR>& __y)
>+    -> decltype(__detail::__convbool(__x.base() > __y.base()))
>+    { return __x.base() > __y.base(); }
>+
>+  template<typename _IteratorL, typename _IteratorR>
>+    constexpr auto
>+    operator<=(const reverse_iterator<_IteratorL>& __x,
>+	       const reverse_iterator<_IteratorR>& __y)
>+    -> decltype(__detail::__convbool(__x.base() <= __y.base()))
>+    { return __x.base() <= __y.base(); }
>+
>+  template<typename _IteratorL, typename _IteratorR>
>+    constexpr auto
>+    operator>=(const reverse_iterator<_IteratorL>& __x,
>+	       const reverse_iterator<_IteratorR>& __y)
>+    -> decltype(__detail::__convbool(__x.base() >= __y.base()))
>+    { return __x.base() >= __y.base(); }
>+#endif // C++20
>   //@}
> 
> #if __cplusplus < 201103L
Li, Pan2 via Gcc-patches March 28, 2020, 10:22 p.m. UTC | #2
On 28/03/20 00:16 +0000, Jonathan Wakely wrote:
>On 27/03/20 23:29 +0000, Jonathan Wakely wrote:
>>@@ -403,6 +428,49 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>    operator>=(const reverse_iterator<_IteratorL>& __x,
>>	       const reverse_iterator<_IteratorR>& __y)
>>    { return !(__x < __y); }
>>+#else // C++20
>>+  template<typename _IteratorL, typename _IteratorR>
>>+    constexpr auto
>>+    operator==(const reverse_iterator<_IteratorL>& __x,
>>+	       const reverse_iterator<_IteratorR>& __y)
>>+    -> decltype(__detail::__convbool(__x.base() == __y.base()))
>>+    { return __x.base() == __y.base(); }
>>+
>>+  template<typename _IteratorL, typename _IteratorR>
>>+    constexpr auto
>>+    operator!=(const reverse_iterator<_IteratorL>& __x,
>>+	       const reverse_iterator<_IteratorR>& __y)
>>+    -> decltype(__detail::__convbool(__x.base() != __y.base()))
>>+    { return __x.base() != __y.base(); }
>>+
>>+  template<typename _IteratorL, typename _IteratorR>
>>+    constexpr auto
>>+    operator<(const reverse_iterator<_IteratorL>& __x,
>>+	      const reverse_iterator<_IteratorR>& __y)
>>+    -> decltype(__detail::__convbool(__x.base() < __y.base()))
>>+    { return __x.base() < __y.base(); }
>
>Drat, these relational operators use the wrong operator. The sense
>needs to be flipped because they're reverse_iterators.
>
>Looks like I lost an edit to this file that I made on another machine,
>and committed the wrong version. I'll fix it ASAP.

Here's the fix. Tested powerpc64le-linux, committed to master.
diff mbox series

Patch

commit 81a8d137c22953df2ea046466c62cd26c0dba103
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Fri Mar 27 23:21:58 2020 +0000

    libstdc++: Add remaining C++20 changes to iterator adaptors
    
    This adds the missing parts of P0896R4 to reverse_iterator and
    move_iterator, so that they meet the C++20 requirements. This should be
    the last piece of P0896R4, meaning ranges support is now complete.
    
    The PR 94354 bug with reverse_iterator's comparisons is fixed for C++20
    only, but that change should be extended to C++11, C++14 and C++17 modes
    in stage 1.
    
            * include/bits/stl_iterator.h (reverse_iterator::iterator_concept)
            (reverse_iterator::iterator_category): Define for C++20.
            (reverse_iterator): Define comparison operators correctly for C++20.
            (__normal_iterator): Add constraints to comparison operators for C++20.
            (move_iterator::operator++(int)) [__cpp_lib_concepts]: Define new
            overload for input iterators.
            (move_iterator): Add constraints to comparison operators for C++20.
            Define operator<=> for C++20.
            * testsuite/24_iterators/move_iterator/input_iterator.cc: New test.
            * testsuite/24_iterators/move_iterator/move_only.cc: New test.
            * testsuite/24_iterators/move_iterator/rel_ops_c++20.cc: New test.
            * testsuite/24_iterators/reverse_iterator/rel_ops_c++20.cc: New test.

diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h
index d10c30cbfcc..26eb599993d 100644
--- a/libstdc++-v3/include/bits/stl_iterator.h
+++ b/libstdc++-v3/include/bits/stl_iterator.h
@@ -88,6 +88,17 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
    * @{
    */
 
+#if __cplusplus > 201703L && __cpp_lib_concepts
+  namespace __detail
+  {
+    // Weaken iterator_category _Cat to _Limit if it is derived from that,
+    // otherwise use _Otherwise.
+    template<typename _Cat, typename _Limit, typename _Otherwise = _Cat>
+      using __clamp_iter_cat
+	= conditional_t<derived_from<_Cat, _Limit>, _Limit, _Otherwise>;
+  }
+#endif
+
   // 24.4.1 Reverse iterators
   /**
    *  Bidirectional and random access iterators have corresponding reverse
@@ -126,6 +137,16 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef typename __traits_type::pointer		pointer;
       typedef typename __traits_type::reference		reference;
 
+#if __cplusplus > 201703L && __cpp_lib_concepts
+      using iterator_concept
+	= conditional_t<random_access_iterator<_Iterator>,
+			random_access_iterator_tag,
+			bidirectional_iterator_tag>;
+      using iterator_category
+	= __detail::__clamp_iter_cat<typename __traits_type::iterator_category,
+				     random_access_iterator_tag>;
+#endif
+
       /**
        *  The default constructor value-initializes member @p current.
        *  If it is a pointer, that means it is zero-initialized.
@@ -320,16 +341,20 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
         { return __t.operator->(); }
     };
 
+  // Used in unevaluated expressions to test for implicit conversion to bool.
+  namespace __detail { bool __convbool(bool); }
+
   //@{
   /**
    *  @param  __x  A %reverse_iterator.
    *  @param  __y  A %reverse_iterator.
    *  @return  A simple bool.
    *
-   *  Reverse iterators forward many operations to their underlying base()
-   *  iterators.  Others are implemented in terms of one another.
+   *  Reverse iterators forward comparisons to their underlying base()
+   *  iterators.
    *
   */
+#if __cplusplus <= 201703L
   template<typename _Iterator>
     inline _GLIBCXX17_CONSTEXPR bool
     operator==(const reverse_iterator<_Iterator>& __x,
@@ -403,6 +428,49 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     operator>=(const reverse_iterator<_IteratorL>& __x,
 	       const reverse_iterator<_IteratorR>& __y)
     { return !(__x < __y); }
+#else // C++20
+  template<typename _IteratorL, typename _IteratorR>
+    constexpr auto
+    operator==(const reverse_iterator<_IteratorL>& __x,
+	       const reverse_iterator<_IteratorR>& __y)
+    -> decltype(__detail::__convbool(__x.base() == __y.base()))
+    { return __x.base() == __y.base(); }
+
+  template<typename _IteratorL, typename _IteratorR>
+    constexpr auto
+    operator!=(const reverse_iterator<_IteratorL>& __x,
+	       const reverse_iterator<_IteratorR>& __y)
+    -> decltype(__detail::__convbool(__x.base() != __y.base()))
+    { return __x.base() != __y.base(); }
+
+  template<typename _IteratorL, typename _IteratorR>
+    constexpr auto
+    operator<(const reverse_iterator<_IteratorL>& __x,
+	      const reverse_iterator<_IteratorR>& __y)
+    -> decltype(__detail::__convbool(__x.base() < __y.base()))
+    { return __x.base() < __y.base(); }
+
+  template<typename _IteratorL, typename _IteratorR>
+    constexpr auto
+    operator>(const reverse_iterator<_IteratorL>& __x,
+	      const reverse_iterator<_IteratorR>& __y)
+    -> decltype(__detail::__convbool(__x.base() > __y.base()))
+    { return __x.base() > __y.base(); }
+
+  template<typename _IteratorL, typename _IteratorR>
+    constexpr auto
+    operator<=(const reverse_iterator<_IteratorL>& __x,
+	       const reverse_iterator<_IteratorR>& __y)
+    -> decltype(__detail::__convbool(__x.base() <= __y.base()))
+    { return __x.base() <= __y.base(); }
+
+  template<typename _IteratorL, typename _IteratorR>
+    constexpr auto
+    operator>=(const reverse_iterator<_IteratorL>& __x,
+	       const reverse_iterator<_IteratorR>& __y)
+    -> decltype(__detail::__convbool(__x.base() >= __y.base()))
+    { return __x.base() >= __y.base(); }
+#endif // C++20
   //@}
 
 #if __cplusplus < 201103L
@@ -1000,8 +1068,11 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   // Random access iterator requirements
   template<typename _IteratorL, typename _IteratorR, typename _Container>
-    _GLIBCXX20_CONSTEXPR
+#if __cplusplus > 201703L
+    constexpr auto
+#else
     inline bool
+#endif
     operator<(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	      const __normal_iterator<_IteratorR, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
@@ -1016,8 +1087,11 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() < __rhs.base(); }
 
   template<typename _IteratorL, typename _IteratorR, typename _Container>
-    _GLIBCXX20_CONSTEXPR
+#if __cplusplus > 201703L
+    constexpr auto
+#else
     inline bool
+#endif
     operator>(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	      const __normal_iterator<_IteratorR, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
@@ -1032,8 +1106,11 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() > __rhs.base(); }
 
   template<typename _IteratorL, typename _IteratorR, typename _Container>
-    _GLIBCXX20_CONSTEXPR
+#if __cplusplus > 201703L
+    constexpr auto
+#else
     inline bool
+#endif
     operator<=(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	       const __normal_iterator<_IteratorR, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
@@ -1048,8 +1125,11 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return __lhs.base() <= __rhs.base(); }
 
   template<typename _IteratorL, typename _IteratorR, typename _Container>
-    _GLIBCXX20_CONSTEXPR
+#if __cplusplus > 201703L
+    constexpr auto
+#else
     inline bool
+#endif
     operator>=(const __normal_iterator<_IteratorL, _Container>& __lhs,
 	       const __normal_iterator<_IteratorR, _Container>& __rhs)
     _GLIBCXX_NOEXCEPT
@@ -1157,15 +1237,6 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     private:
       _Sent _M_last;
     };
-
-  namespace __detail
-  {
-    // Weaken iterator_category _Cat to _Limit if it is derived from that,
-    // otherwise use _Otherwise.
-    template<typename _Cat, typename _Limit, typename _Otherwise = _Cat>
-      using __clamp_iter_cat
-	= conditional_t<derived_from<_Cat, _Limit>, _Limit, _Otherwise>;
-  }
 #endif // C++20
 
   // 24.4.3  Move iterators
@@ -1266,6 +1337,12 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	return __tmp;
       }
 
+#if __cpp_lib_concepts
+      constexpr void
+      operator++(int) requires (!forward_iterator<_Iterator>)
+      { ++_M_current; }
+#endif
+
       _GLIBCXX17_CONSTEXPR move_iterator&
       operator--()
       {
@@ -1343,6 +1420,9 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     inline _GLIBCXX17_CONSTEXPR bool
     operator==(const move_iterator<_IteratorL>& __x,
 	       const move_iterator<_IteratorR>& __y)
+#if __cplusplus > 201703L && __cpp_lib_concepts
+    requires requires { __detail::__convbool(__x.base() == __y.base()); }
+#endif
     { return __x.base() == __y.base(); }
 
   template<typename _Iterator>
@@ -1351,6 +1431,14 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	       const move_iterator<_Iterator>& __y)
     { return __x.base() == __y.base(); }
 
+#if __cpp_lib_three_way_comparison
+  template<typename _IteratorL,
+	   three_way_comparable_with<_IteratorL> _IteratorR>
+    constexpr compare_three_way_result_t<_IteratorL, _IteratorR>
+    operator<=>(const move_iterator<_IteratorL>& __x,
+		const move_iterator<_IteratorR>& __y)
+    { return __x.base() <=> __y.base(); }
+#else
   template<typename _IteratorL, typename _IteratorR>
     inline _GLIBCXX17_CONSTEXPR bool
     operator!=(const move_iterator<_IteratorL>& __x,
@@ -1362,11 +1450,15 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     operator!=(const move_iterator<_Iterator>& __x,
 	       const move_iterator<_Iterator>& __y)
     { return !(__x == __y); }
+#endif
 
   template<typename _IteratorL, typename _IteratorR>
     inline _GLIBCXX17_CONSTEXPR bool
     operator<(const move_iterator<_IteratorL>& __x,
 	      const move_iterator<_IteratorR>& __y)
+#if __cplusplus > 201703L && __cpp_lib_concepts
+    requires requires { __detail::__convbool(__x.base() < __y.base()); }
+#endif
     { return __x.base() < __y.base(); }
 
   template<typename _Iterator>
@@ -1379,6 +1471,9 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     inline _GLIBCXX17_CONSTEXPR bool
     operator<=(const move_iterator<_IteratorL>& __x,
 	       const move_iterator<_IteratorR>& __y)
+#if __cplusplus > 201703L && __cpp_lib_concepts
+    requires requires { __detail::__convbool(__y.base() < __x.base()); }
+#endif
     { return !(__y < __x); }
 
   template<typename _Iterator>
@@ -1391,6 +1486,9 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     inline _GLIBCXX17_CONSTEXPR bool
     operator>(const move_iterator<_IteratorL>& __x,
 	      const move_iterator<_IteratorR>& __y)
+#if __cplusplus > 201703L && __cpp_lib_concepts
+    requires requires { __detail::__convbool(__y.base() < __x.base()); }
+#endif
     { return __y < __x; }
 
   template<typename _Iterator>
@@ -1403,6 +1501,9 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     inline _GLIBCXX17_CONSTEXPR bool
     operator>=(const move_iterator<_IteratorL>& __x,
 	       const move_iterator<_IteratorR>& __y)
+#if __cplusplus > 201703L && __cpp_lib_concepts
+    requires requires { __detail::__convbool(__x.base() < __y.base()); }
+#endif
     { return !(__x < __y); }
 
   template<typename _Iterator>
diff --git a/libstdc++-v3/testsuite/24_iterators/move_iterator/input_iterator.cc b/libstdc++-v3/testsuite/24_iterators/move_iterator/input_iterator.cc
new file mode 100644
index 00000000000..32cf55ca932
--- /dev/null
+++ b/libstdc++-v3/testsuite/24_iterators/move_iterator/input_iterator.cc
@@ -0,0 +1,42 @@ 
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <iterator>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+void
+test01()
+{
+  int a[2] = { 1, 2 };
+  __gnu_test::test_container<int, __gnu_test::input_iterator_wrapper> c(a);
+  auto miter = std::make_move_iterator(c.begin());
+  VERIFY( *miter == 1 );
+  miter++;
+  VERIFY( *miter == 2 );
+
+  static_assert( std::is_void_v<decltype(miter++)> );
+}
+
+int
+main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/24_iterators/move_iterator/move_only.cc b/libstdc++-v3/testsuite/24_iterators/move_iterator/move_only.cc
new file mode 100644
index 00000000000..d64e61e4448
--- /dev/null
+++ b/libstdc++-v3/testsuite/24_iterators/move_iterator/move_only.cc
@@ -0,0 +1,60 @@ 
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <iterator>
+
+struct move_only_iterator
+{
+  move_only_iterator() = default;
+  move_only_iterator(move_only_iterator&&) = default;
+  move_only_iterator& operator=(move_only_iterator&&) = default;
+
+  move_only_iterator& operator++();
+  move_only_iterator operator++(int);
+  int& operator*() const;
+
+  bool operator==(const move_only_iterator&) const;
+};
+
+template<> struct std::iterator_traits<move_only_iterator>
+{
+  using value_type = int;
+  using difference_type = std::ptrdiff_t;
+  using iterator_category = std::input_iterator_tag;
+};
+
+static_assert(std::input_iterator<move_only_iterator>);
+
+template<typename T>
+  concept has_member_base = requires (T t) { std::forward<T>(t).base(); };
+
+static_assert( ! has_member_base<std::move_iterator<move_iterator>&> );
+static_assert( ! has_member_base<const std::move_iterator<move_iterator>&> );
+static_assert( has_member_base<std::move_iterator<move_iterator>> );
+static_assert( ! has_member_base<const std::move_iterator<move_iterator>> );
+
+void
+test01()
+{
+  std::move_iterator<move_only_iterator> m1, m2;
+  m1 = std::make_move_iterator(move_only_iterator{});
+  m2 = std::move(m1);
+  m1.swap(m2);
+}
diff --git a/libstdc++-v3/testsuite/24_iterators/move_iterator/rel_ops_c++20.cc b/libstdc++-v3/testsuite/24_iterators/move_iterator/rel_ops_c++20.cc
new file mode 100644
index 00000000000..8f2d73c520f
--- /dev/null
+++ b/libstdc++-v3/testsuite/24_iterators/move_iterator/rel_ops_c++20.cc
@@ -0,0 +1,134 @@ 
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <iterator>
+
+template<int>
+struct Iter
+{
+  using iterator_category = std::random_access_iterator_tag;
+  using value_type = int;
+  using pointer = int*;
+  using reference = int&;
+  using difference_type = std::ptrdiff_t;
+
+  Iter();
+
+  Iter& operator++();
+  Iter operator++(int);
+  Iter& operator--();
+  Iter operator--(int);
+  int& operator*() const;
+  int* operator->() const;
+
+  int& operator[](difference_type) const;
+
+  Iter& operator+=(difference_type);
+  Iter& operator-=(difference_type);
+
+  template<int N> friend Iter operator+(Iter<N>, difference_type);
+  template<int N> friend Iter operator+(difference_type, Iter<N>);
+  template<int N> friend Iter operator-(Iter<N>, difference_type);
+  template<int N> friend difference_type operator-(Iter<N>, Iter<N>);
+
+  // Define the full set of operators for same-type comparisons
+  template<int N> friend bool operator==(Iter<N>, Iter<N>); // synthesizes !=
+  template<int N> friend bool operator<(Iter<N>, Iter<N>);
+  template<int N> friend bool operator>(Iter<N>, Iter<N>);
+  template<int N> friend bool operator<=(Iter<N>, Iter<N>);
+  template<int N> friend bool operator>=(Iter<N>, Iter<N>);
+};
+
+
+static_assert( std::random_access_iterator<Iter<0>> );
+
+int   operator==(Iter<0>, long*);
+void* operator< (Iter<1>, long*);
+bool& operator< (long*, Iter<2>);
+
+using std::move_iterator;
+
+static_assert( std::three_way_comparable<move_iterator<Iter<0>>> );
+
+move_iterator<Iter<0>> l0{Iter<0>()};
+move_iterator<Iter<1>> l1{Iter<1>()};
+move_iterator<Iter<2>> l2{Iter<2>()};
+move_iterator<long*> r{nullptr};
+
+bool b0 = l0 == r;
+bool b1 = l0 != r;
+bool b2 = l1 < r;
+bool b3 = l2 > r;
+bool b4 = l2 <= r;
+bool b5 = l1 >= r;
+
+template<int N>
+  concept has_eq
+    = requires (move_iterator<Iter<N>> l, move_iterator<long*> r)
+      { l == r; };
+
+template<int N>
+  concept has_ne
+    = requires (move_iterator<Iter<N>> l, move_iterator<long*> r)
+      { l != r; };
+
+template<int N>
+  concept has_lt
+    = requires (move_iterator<Iter<N>> l, move_iterator<long*> r)
+      { l < r; };
+
+template<int N>
+  concept has_gt
+    = requires (move_iterator<Iter<N>> l, move_iterator<long*> r)
+      { l > r; };
+
+template<int N>
+  concept has_le
+    = requires (move_iterator<Iter<N>> l, move_iterator<long*> r)
+      { l <= r; };
+
+template<int N>
+  concept has_ge
+    = requires (move_iterator<Iter<N>> l, move_iterator<long*> r)
+      { l >= r; };
+
+static_assert( has_eq<0> );
+static_assert( ! has_eq<1> );
+static_assert( ! has_eq<2> );
+
+static_assert( has_ne<0> ); // uses synthesized operator!=
+static_assert( ! has_ne<1> );
+static_assert( ! has_ne<2> );
+
+static_assert( ! has_lt<0> );
+static_assert( has_lt<1> );
+static_assert( ! has_lt<2> );
+
+static_assert( ! has_gt<0> );
+static_assert( ! has_gt<1> );
+static_assert( has_gt<2> );
+
+static_assert( ! has_le<0> );
+static_assert( ! has_le<1> );
+static_assert( has_le<2> );
+
+static_assert( ! has_ge<0> );
+static_assert( has_ge<1> );
+static_assert( ! has_ge<2> );
diff --git a/libstdc++-v3/testsuite/24_iterators/reverse_iterator/rel_ops_c++20.cc b/libstdc++-v3/testsuite/24_iterators/reverse_iterator/rel_ops_c++20.cc
new file mode 100644
index 00000000000..3e91a0396fe
--- /dev/null
+++ b/libstdc++-v3/testsuite/24_iterators/reverse_iterator/rel_ops_c++20.cc
@@ -0,0 +1,156 @@ 
+// Copyright (C) 2020 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <iterator>
+
+template<int>
+struct Iter
+{
+  using iterator_category = std::random_access_iterator_tag;
+  using value_type = int;
+  using pointer = int*;
+  using reference = int&;
+  using difference_type = std::ptrdiff_t;
+
+  Iter();
+
+  Iter& operator++();
+  Iter operator++(int);
+  Iter& operator--();
+  Iter operator--(int);
+  int& operator*() const;
+  int* operator->() const;
+
+  int& operator[](difference_type) const;
+
+  Iter& operator+=(difference_type);
+  Iter& operator-=(difference_type);
+
+  template<int N> friend Iter operator+(Iter<N>, difference_type);
+  template<int N> friend Iter operator+(difference_type, Iter<N>);
+  template<int N> friend Iter operator-(Iter<N>, difference_type);
+  template<int N> friend difference_type operator-(Iter<N>, Iter<N>);
+
+  // Define the full set of operators for same-type comparisons
+  template<int N> friend bool operator==(Iter<N>, Iter<N>); // synthesizes !=
+  template<int N> friend bool operator<(Iter<N>, Iter<N>);
+  template<int N> friend bool operator>(Iter<N>, Iter<N>);
+  template<int N> friend bool operator<=(Iter<N>, Iter<N>);
+  template<int N> friend bool operator>=(Iter<N>, Iter<N>);
+};
+
+static_assert( std::random_access_iterator<Iter<0>> );
+
+// Define a single kind of mixed-type comparison for each specialization.
+int   operator==(Iter<0>, long*);
+void* operator!=(Iter<1>, long*);
+bool& operator< (Iter<2>, long*);
+int   operator> (Iter<3>, long*);
+void* operator<=(Iter<4>, long*);
+bool& operator>=(Iter<5>, long*);
+
+using std::reverse_iterator;
+
+reverse_iterator<Iter<0>> l0{Iter<0>()};
+reverse_iterator<Iter<1>> l1{Iter<1>()};
+reverse_iterator<Iter<2>> l2{Iter<2>()};
+reverse_iterator<Iter<3>> l3{Iter<3>()};
+reverse_iterator<Iter<4>> l4{Iter<4>()};
+reverse_iterator<Iter<5>> l5{Iter<5>()};
+reverse_iterator<long*> r{nullptr};
+
+bool b0 = l0 == r;
+bool b1 = l1 != r;
+bool b2 = l2 < r;
+bool b3 = l3 > r;
+bool b4 = l4 <= r;
+bool b5 = l5 >= r;
+
+template<int N>
+  concept has_eq
+    = requires (reverse_iterator<Iter<N>> l, reverse_iterator<long*> r)
+      { l == r; };
+
+template<int N>
+  concept has_ne
+    = requires (reverse_iterator<Iter<N>> l, reverse_iterator<long*> r)
+      { l != r; };
+
+template<int N>
+  concept has_lt
+    = requires (reverse_iterator<Iter<N>> l, reverse_iterator<long*> r)
+      { l < r; };
+
+template<int N>
+  concept has_gt
+    = requires (reverse_iterator<Iter<N>> l, reverse_iterator<long*> r)
+      { l > r; };
+
+template<int N>
+  concept has_le
+    = requires (reverse_iterator<Iter<N>> l, reverse_iterator<long*> r)
+      { l <= r; };
+
+template<int N>
+  concept has_ge
+    = requires (reverse_iterator<Iter<N>> l, reverse_iterator<long*> r)
+      { l >= r; };
+
+static_assert( has_eq<0> );
+static_assert( ! has_eq<1> );
+static_assert( ! has_eq<2> );
+static_assert( ! has_eq<3> );
+static_assert( ! has_eq<4> );
+static_assert( ! has_eq<5> );
+
+static_assert( has_ne<0> ); // uses synthesized operator!=
+static_assert( has_ne<1> );
+static_assert( ! has_ne<2> );
+static_assert( ! has_ne<3> );
+static_assert( ! has_ne<4> );
+static_assert( ! has_ne<5> );
+
+static_assert( ! has_lt<0> );
+static_assert( ! has_lt<1> );
+static_assert( has_lt<2> );
+static_assert( ! has_lt<3> );
+static_assert( ! has_lt<4> );
+static_assert( ! has_lt<5> );
+
+static_assert( ! has_gt<0> );
+static_assert( ! has_gt<1> );
+static_assert( ! has_gt<2> );
+static_assert( has_gt<3> );
+static_assert( ! has_gt<4> );
+static_assert( ! has_gt<5> );
+
+static_assert( ! has_le<0> );
+static_assert( ! has_le<1> );
+static_assert( ! has_le<2> );
+static_assert( ! has_le<3> );
+static_assert( has_le<4> );
+static_assert( ! has_le<5> );
+
+static_assert( ! has_ge<0> );
+static_assert( ! has_ge<1> );
+static_assert( ! has_ge<2> );
+static_assert( ! has_ge<3> );
+static_assert( ! has_ge<4> );
+static_assert( has_ge<5> );