[libcxx-commits] [libcxx] 8320017 - [libc++] [ranges] Uncomment operator<=> in transform and iota iterators.

Arthur O'Dwyer via libcxx-commits libcxx-commits at lists.llvm.org
Sat Oct 2 18:29:50 PDT 2021


Author: Arthur O'Dwyer
Date: 2021-10-02T21:26:27-04:00
New Revision: 8320017b79eb8006b85a53ede8dfc92caa6da5a7

URL: https://github.com/llvm/llvm-project/commit/8320017b79eb8006b85a53ede8dfc92caa6da5a7
DIFF: https://github.com/llvm/llvm-project/commit/8320017b79eb8006b85a53ede8dfc92caa6da5a7.diff

LOG: [libc++] [ranges] Uncomment operator<=> in transform and iota iterators.

The existing tests for transform_view::iterator weren't quite right,
and can be simplified now that we have more of C++20 available to us.
Having done that, let's use the same pattern for iota_view::iterator
as well.

Differential Revision: https://reviews.llvm.org/D110774

Added: 
    

Modified: 
    libcxx/include/__ranges/iota_view.h
    libcxx/include/__ranges/transform_view.h
    libcxx/test/std/ranges/range.adaptors/range.transform/iterator/compare.pass.cpp
    libcxx/test/std/ranges/range.adaptors/range.transform/types.h
    libcxx/test/std/ranges/range.factories/range.iota.view/iterator/compare.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__ranges/iota_view.h b/libcxx/include/__ranges/iota_view.h
index bd679cdf35d60..da712b8e6f4fd 100644
--- a/libcxx/include/__ranges/iota_view.h
+++ b/libcxx/include/__ranges/iota_view.h
@@ -9,6 +9,16 @@
 #ifndef _LIBCPP___RANGES_IOTA_VIEW_H
 #define _LIBCPP___RANGES_IOTA_VIEW_H
 
+#include <__compare/three_way_comparable.h>
+#include <__concepts/arithmetic.h>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/copyable.h>
+#include <__concepts/equality_comparable.h>
+#include <__concepts/invocable.h>
+#include <__concepts/same_as.h>
+#include <__concepts/semiregular.h>
+#include <__concepts/totally_ordered.h>
 #include <__config>
 #include <__debug>
 #include <__functional/ranges_operations.h>
@@ -21,7 +31,6 @@
 #include <__ranges/view_interface.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
-#include <concepts>
 #include <type_traits>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -212,11 +221,11 @@ namespace ranges {
         return !(__x < __y);
       }
 
-//       friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
-//         requires totally_ordered<_Start> && three_way_comparable<_Start>
-//       {
-//         return __x.__value_ <=> __y.__value_;
-//       }
+      friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
+        requires totally_ordered<_Start> && three_way_comparable<_Start>
+      {
+        return __x.__value_ <=> __y.__value_;
+      }
 
       _LIBCPP_HIDE_FROM_ABI
       friend constexpr __iterator operator+(__iterator __i, 
diff erence_type __n)

diff  --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 7c58a85b4a92e..de1296cbd260d 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -9,6 +9,13 @@
 #ifndef _LIBCPP___RANGES_TRANSFORM_VIEW_H
 #define _LIBCPP___RANGES_TRANSFORM_VIEW_H
 
+#include <__compare/three_way_comparable.h>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/copyable.h>
+#include <__concepts/derived_from.h>
+#include <__concepts/equality_comparable.h>
+#include <__concepts/invocable.h>
 #include <__config>
 #include <__functional/bind_back.h>
 #include <__functional/invoke.h>
@@ -27,7 +34,6 @@
 #include <__utility/forward.h>
 #include <__utility/in_place.h>
 #include <__utility/move.h>
-#include <concepts>
 #include <type_traits>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -297,13 +303,14 @@ class transform_view<_View, _Fn>::__iterator
     return __x.__current_ >= __y.__current_;
   }
 
-// TODO: Fix this as soon as soon as three_way_comparable is implemented.
-//   _LIBCPP_HIDE_FROM_ABI
-//   friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
-//     requires random_access_range<_Base> && three_way_comparable<iterator_t<_Base>>
-//   {
-//     return __x.__current_ <=> __y.__current_;
-//   }
+#if !defined(_LIBCPP_HAS_NO_SPACESHIP_OPERATOR)
+  _LIBCPP_HIDE_FROM_ABI
+  friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
+    requires random_access_range<_Base> && three_way_comparable<iterator_t<_Base>>
+  {
+    return __x.__current_ <=> __y.__current_;
+  }
+#endif // !defined(_LIBCPP_HAS_NO_SPACESHIP_OPERATOR)
 
   _LIBCPP_HIDE_FROM_ABI
   friend constexpr __iterator operator+(__iterator __i, 
diff erence_type __n)

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/compare.pass.cpp
index a6d0a5fbed8f1..f12d3b5c36283 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/compare.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/compare.pass.cpp
@@ -10,51 +10,59 @@
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: libcpp-has-no-incomplete-ranges
 
-// transform_view::<iterator>::operator{<,>,<=,>=}
+// transform_view::<iterator>::operator{<,>,<=,>=,==,!=,<=>}
 
 #include <ranges>
 #include <compare>
 
 #include "test_macros.h"
+#include "test_iterators.h"
 #include "../types.h"
 
 constexpr bool test() {
   {
-    std::ranges::transform_view<ContiguousView, PlusOne> transformView1;
-    auto iter1 = std::move(transformView1).begin();
-    std::ranges::transform_view<ContiguousView, PlusOne> transformView2;
-    auto iter2 = std::move(transformView2).begin();
-    assert(iter1 == iter2);
-    assert(iter1 + 1 != iter2);
-    assert(iter1 + 1 == iter2 + 1);
+    // Test a new-school iterator with operator<=>; the transform iterator should also have operator<=>.
+    using It = three_way_contiguous_iterator<int*>;
+    static_assert(std::three_way_comparable<It>);
+    using R = std::ranges::transform_view<std::ranges::subrange<It>, PlusOne>;
+    static_assert(std::three_way_comparable<std::ranges::iterator_t<R>>);
 
-    assert(iter1 < iter1 + 1);
-    assert(iter1 + 1 > iter1);
-    assert(iter1 <= iter1 + 1);
-    assert(iter1 <= iter2);
-    assert(iter1 + 1 >= iter2);
-    assert(iter1     >= iter2);
+    int a[] = {1,2,3};
+    std::same_as<R> auto r = std::ranges::subrange<It>(It(a), It(a+3)) | std::views::transform(PlusOne());
+    auto iter1 = r.begin();
+    auto iter2 = iter1 + 1;
+
+    assert(!(iter1 < iter1));  assert(iter1 < iter2);     assert(!(iter2 < iter1));
+    assert(iter1 <= iter1);    assert(iter1 <= iter2);    assert(!(iter2 <= iter1));
+    assert(!(iter1 > iter1));  assert(!(iter1 > iter2));  assert(iter2 > iter1);
+    assert(iter1 >= iter1);    assert(!(iter1 >= iter2)); assert(iter2 >= iter1);
+    assert(iter1 == iter1);    assert(!(iter1 == iter2)); assert(iter2 == iter2);
+    assert(!(iter1 != iter1)); assert(iter1 != iter2);    assert(!(iter2 != iter2));
+
+    assert((iter1 <=> iter2) == std::strong_ordering::less);
+    assert((iter1 <=> iter1) == std::strong_ordering::equal);
+    assert((iter2 <=> iter1) == std::strong_ordering::greater);
   }
 
-// TODO: when three_way_comparable is implemented and std::is_eq is implemented,
-// uncomment this.
-//   {
-//     std::ranges::transform_view<ThreeWayCompView, PlusOne> transformView1;
-//     auto iter1 = transformView1.begin();
-//     std::ranges::transform_view<ThreeWayCompView, PlusOne> transformView2;
-//     auto iter2 = transformView2.begin();
-//
-//     assert(std::is_eq(iter1   <=> iter2));
-//     assert(std::is_lteq(iter1 <=> iter2));
-//     ++iter2;
-//     assert(std::is_neq(iter1  <=> iter2));
-//     assert(std::is_lt(iter1   <=> iter2));
-//     assert(std::is_gt(iter2   <=> iter1));
-//     assert(std::is_gteq(iter2 <=> iter1));
-//
-//     static_assert( std::three_way_comparable<std::iterator_t<std::ranges::transform_view<ThreeWayCompView, PlusOne>>>);
-//     static_assert(!std::three_way_comparable<std::iterator_t<std::ranges::transform_view<ContiguousView, PlusOne>>>);
-//   }
+  {
+    // Test an old-school iterator with no operator<=>; the transform iterator shouldn't have operator<=> either.
+    using It = random_access_iterator<int*>;
+    static_assert(!std::three_way_comparable<It>);
+    using R = std::ranges::transform_view<std::ranges::subrange<It>, PlusOne>;
+    static_assert(!std::three_way_comparable<std::ranges::iterator_t<R>>);
+
+    int a[] = {1,2,3};
+    std::same_as<R> auto r = std::ranges::subrange<It>(It(a), It(a+3)) | std::views::transform(PlusOne());
+    auto iter1 = r.begin();
+    auto iter2 = iter1 + 1;
+
+    assert(!(iter1 < iter1));  assert(iter1 < iter2);     assert(!(iter2 < iter1));
+    assert(iter1 <= iter1);    assert(iter1 <= iter2);    assert(!(iter2 <= iter1));
+    assert(!(iter1 > iter1));  assert(!(iter1 > iter2));  assert(iter2 > iter1);
+    assert(iter1 >= iter1);    assert(!(iter1 >= iter2)); assert(iter2 >= iter1);
+    assert(iter1 == iter1);    assert(!(iter1 == iter2)); assert(iter2 == iter2);
+    assert(!(iter1 != iter1)); assert(iter1 != iter2);    assert(!(iter2 != iter2));
+  }
 
   return true;
 }

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
index 9c5d07935ab42..fff7b287f322d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
@@ -129,14 +129,6 @@ struct CountedView : std::ranges::view_base {
   constexpr CountedIter end() const { return CountedIter(ForwardIter(globalBuff + 8)); }
 };
 
-using ThreeWayCompIter = three_way_contiguous_iterator<int*>;
-struct ThreeWayCompView : std::ranges::view_base {
-  constexpr ThreeWayCompIter begin() { return ThreeWayCompIter(globalBuff); }
-  constexpr ThreeWayCompIter begin() const { return ThreeWayCompIter(globalBuff); }
-  constexpr ThreeWayCompIter end() { return ThreeWayCompIter(globalBuff + 8); }
-  constexpr ThreeWayCompIter end() const { return ThreeWayCompIter(globalBuff + 8); }
-};
-
 struct TimesTwo {
   constexpr int operator()(int x) const { return x * 2; }
 };

diff  --git a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/compare.pass.cpp b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/compare.pass.cpp
index 6ef4751c64408..b98f33aee5e40 100644
--- a/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/compare.pass.cpp
+++ b/libcxx/test/std/ranges/range.factories/range.iota.view/iterator/compare.pass.cpp
@@ -10,69 +10,79 @@
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: libcpp-has-no-incomplete-ranges
 
-// friend constexpr bool operator<(const iterator& x, const iterator& y)
-//   requires totally_ordered<W>;
-// friend constexpr bool operator>(const iterator& x, const iterator& y)
-//   requires totally_ordered<W>;
-// friend constexpr bool operator<=(const iterator& x, const iterator& y)
-//   requires totally_ordered<W>;
-// friend constexpr bool operator>=(const iterator& x, const iterator& y)
-//   requires totally_ordered<W>;
-// friend constexpr bool operator==(const iterator& x, const iterator& y)
-//   requires equality_comparable<W>;
-
-// TODO: test spaceship operator once it's implemented.
+// iota_view::<iterator>::operator{<,>,<=,>=,==,!=,<=>}
 
 #include <ranges>
-#include <cassert>
+#include <compare>
 
 #include "test_macros.h"
+#include "test_iterators.h"
 #include "../types.h"
 
 constexpr bool test() {
   {
-    const std::ranges::iota_view<int> io(0);
-    assert(                  io.begin()  ==                   io.begin() );
-    assert(                  io.begin()  != std::ranges::next(io.begin()));
-    assert(                  io.begin()  <  std::ranges::next(io.begin()));
-    assert(std::ranges::next(io.begin()) >                    io.begin() );
-    assert(                  io.begin()  <= std::ranges::next(io.begin()));
-    assert(std::ranges::next(io.begin()) >=                   io.begin() );
-    assert(                  io.begin()  <=                   io.begin() );
-    assert(                  io.begin()  >=                   io.begin() );
-  }
-  {
-    std::ranges::iota_view<int> io(0);
-    assert(                  io.begin()  ==                   io.begin() );
-    assert(                  io.begin()  != std::ranges::next(io.begin()));
-    assert(                  io.begin()  <  std::ranges::next(io.begin()));
-    assert(std::ranges::next(io.begin()) >                    io.begin() );
-    assert(                  io.begin()  <= std::ranges::next(io.begin()));
-    assert(std::ranges::next(io.begin()) >=                   io.begin() );
-    assert(                  io.begin()  <=                   io.begin() );
-    assert(                  io.begin()  >=                   io.begin() );
+    // Test `int`, which has operator<=>; the iota iterator should also have operator<=>.
+    using R = std::ranges::iota_view<int>;
+    static_assert(std::three_way_comparable<std::ranges::iterator_t<R>>);
+
+    std::same_as<R> auto r = std::views::iota(42);
+    auto iter1 = r.begin();
+    auto iter2 = iter1 + 1;
+
+    assert(!(iter1 < iter1));  assert(iter1 < iter2);     assert(!(iter2 < iter1));
+    assert(iter1 <= iter1);    assert(iter1 <= iter2);    assert(!(iter2 <= iter1));
+    assert(!(iter1 > iter1));  assert(!(iter1 > iter2));  assert(iter2 > iter1);
+    assert(iter1 >= iter1);    assert(!(iter1 >= iter2)); assert(iter2 >= iter1);
+    assert(iter1 == iter1);    assert(!(iter1 == iter2)); assert(iter2 == iter2);
+    assert(!(iter1 != iter1)); assert(iter1 != iter2);    assert(!(iter2 != iter2));
+
+    assert((iter1 <=> iter2) == std::strong_ordering::less);
+    assert((iter1 <=> iter1) == std::strong_ordering::equal);
+    assert((iter2 <=> iter1) == std::strong_ordering::greater);
   }
+
   {
-    const std::ranges::iota_view<SomeInt> io(SomeInt(0));
-    assert(                  io.begin()  ==                   io.begin() );
-    assert(                  io.begin()  != std::ranges::next(io.begin()));
-    assert(                  io.begin()  <  std::ranges::next(io.begin()));
-    assert(std::ranges::next(io.begin()) >                    io.begin() );
-    assert(                  io.begin()  <= std::ranges::next(io.begin()));
-    assert(std::ranges::next(io.begin()) >=                   io.begin() );
-    assert(                  io.begin()  <=                   io.begin() );
-    assert(                  io.begin()  >=                   io.begin() );
+    // Test a new-school iterator with operator<=>; the iota iterator should also have operator<=>.
+    using It = three_way_contiguous_iterator<int*>;
+    static_assert(std::three_way_comparable<It>);
+    using R = std::ranges::iota_view<It>;
+    static_assert(std::three_way_comparable<std::ranges::iterator_t<R>>);
+
+    int a[] = {1,2,3};
+    std::same_as<R> auto r = std::views::iota(It(a));
+    auto iter1 = r.begin();
+    auto iter2 = iter1 + 1;
+
+    assert(!(iter1 < iter1));  assert(iter1 < iter2);     assert(!(iter2 < iter1));
+    assert(iter1 <= iter1);    assert(iter1 <= iter2);    assert(!(iter2 <= iter1));
+    assert(!(iter1 > iter1));  assert(!(iter1 > iter2));  assert(iter2 > iter1);
+    assert(iter1 >= iter1);    assert(!(iter1 >= iter2)); assert(iter2 >= iter1);
+    assert(iter1 == iter1);    assert(!(iter1 == iter2)); assert(iter2 == iter2);
+    assert(!(iter1 != iter1)); assert(iter1 != iter2);    assert(!(iter2 != iter2));
+
+    assert((iter1 <=> iter2) == std::strong_ordering::less);
+    assert((iter1 <=> iter1) == std::strong_ordering::equal);
+    assert((iter2 <=> iter1) == std::strong_ordering::greater);
   }
+
   {
-    std::ranges::iota_view<SomeInt> io(SomeInt(0));
-    assert(                  io.begin()  ==                   io.begin() );
-    assert(                  io.begin()  != std::ranges::next(io.begin()));
-    assert(                  io.begin()  <  std::ranges::next(io.begin()));
-    assert(std::ranges::next(io.begin()) >                    io.begin() );
-    assert(                  io.begin()  <= std::ranges::next(io.begin()));
-    assert(std::ranges::next(io.begin()) >=                   io.begin() );
-    assert(                  io.begin()  <=                   io.begin() );
-    assert(                  io.begin()  >=                   io.begin() );
+    // Test an old-school iterator with no operator<=>; the iota iterator shouldn't have operator<=> either.
+    using It = random_access_iterator<int*>;
+    static_assert(!std::three_way_comparable<It>);
+    using R = std::ranges::iota_view<It>;
+    static_assert(!std::three_way_comparable<std::ranges::iterator_t<R>>);
+
+    int a[] = {1,2,3};
+    std::same_as<R> auto r = std::views::iota(It(a));
+    auto iter1 = r.begin();
+    auto iter2 = iter1 + 1;
+
+    assert(!(iter1 < iter1));  assert(iter1 < iter2);     assert(!(iter2 < iter1));
+    assert(iter1 <= iter1);    assert(iter1 <= iter2);    assert(!(iter2 <= iter1));
+    assert(!(iter1 > iter1));  assert(!(iter1 > iter2));  assert(iter2 > iter1);
+    assert(iter1 >= iter1);    assert(!(iter1 >= iter2)); assert(iter2 >= iter1);
+    assert(iter1 == iter1);    assert(!(iter1 == iter2)); assert(iter2 == iter2);
+    assert(!(iter1 != iter1)); assert(iter1 != iter2);    assert(!(iter2 != iter2));
   }
 
   return true;


        


More information about the libcxx-commits mailing list