Skip to content

Commit 302d3cd

Browse files
Quuxplusonememfrob
authored and
memfrob
committed
[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
1 parent 1a7d104 commit 302d3cd

File tree

5 files changed

+132
-106
lines changed

5 files changed

+132
-106
lines changed

libcxx/include/__ranges/iota_view.h

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@
99
#ifndef _LIBCPP___RANGES_IOTA_VIEW_H
1010
#define _LIBCPP___RANGES_IOTA_VIEW_H
1111

12+
#include <__compare/three_way_comparable.h>
13+
#include <__concepts/arithmetic.h>
14+
#include <__concepts/constructible.h>
15+
#include <__concepts/convertible_to.h>
16+
#include <__concepts/copyable.h>
17+
#include <__concepts/equality_comparable.h>
18+
#include <__concepts/invocable.h>
19+
#include <__concepts/same_as.h>
20+
#include <__concepts/semiregular.h>
21+
#include <__concepts/totally_ordered.h>
1222
#include <__config>
1323
#include <__debug>
1424
#include <__functional/ranges_operations.h>
@@ -21,7 +31,6 @@
2131
#include <__ranges/view_interface.h>
2232
#include <__utility/forward.h>
2333
#include <__utility/move.h>
24-
#include <concepts>
2534
#include <type_traits>
2635

2736
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -212,11 +221,11 @@ namespace ranges {
212221
return !(__x < __y);
213222
}
214223

215-
// friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
216-
// requires totally_ordered<_Start> && three_way_comparable<_Start>
217-
// {
218-
// return __x.__value_ <=> __y.__value_;
219-
// }
224+
friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
225+
requires totally_ordered<_Start> && three_way_comparable<_Start>
226+
{
227+
return __x.__value_ <=> __y.__value_;
228+
}
220229

221230
_LIBCPP_HIDE_FROM_ABI
222231
friend constexpr __iterator operator+(__iterator __i, difference_type __n)

libcxx/include/__ranges/transform_view.h

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@
99
#ifndef _LIBCPP___RANGES_TRANSFORM_VIEW_H
1010
#define _LIBCPP___RANGES_TRANSFORM_VIEW_H
1111

12+
#include <__compare/three_way_comparable.h>
13+
#include <__concepts/constructible.h>
14+
#include <__concepts/convertible_to.h>
15+
#include <__concepts/copyable.h>
16+
#include <__concepts/derived_from.h>
17+
#include <__concepts/equality_comparable.h>
18+
#include <__concepts/invocable.h>
1219
#include <__config>
1320
#include <__functional/bind_back.h>
1421
#include <__functional/invoke.h>
@@ -27,7 +34,6 @@
2734
#include <__utility/forward.h>
2835
#include <__utility/in_place.h>
2936
#include <__utility/move.h>
30-
#include <concepts>
3137
#include <type_traits>
3238

3339
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -297,13 +303,14 @@ class transform_view<_View, _Fn>::__iterator
297303
return __x.__current_ >= __y.__current_;
298304
}
299305

300-
// TODO: Fix this as soon as soon as three_way_comparable is implemented.
301-
// _LIBCPP_HIDE_FROM_ABI
302-
// friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
303-
// requires random_access_range<_Base> && three_way_comparable<iterator_t<_Base>>
304-
// {
305-
// return __x.__current_ <=> __y.__current_;
306-
// }
306+
#if !defined(_LIBCPP_HAS_NO_SPACESHIP_OPERATOR)
307+
_LIBCPP_HIDE_FROM_ABI
308+
friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
309+
requires random_access_range<_Base> && three_way_comparable<iterator_t<_Base>>
310+
{
311+
return __x.__current_ <=> __y.__current_;
312+
}
313+
#endif // !defined(_LIBCPP_HAS_NO_SPACESHIP_OPERATOR)
307314

308315
_LIBCPP_HIDE_FROM_ABI
309316
friend constexpr __iterator operator+(__iterator __i, difference_type __n)

libcxx/test/std/ranges/range.adaptors/range.transform/iterator/compare.pass.cpp

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,51 +10,59 @@
1010
// UNSUPPORTED: libcpp-no-concepts
1111
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
1212

13-
// transform_view::<iterator>::operator{<,>,<=,>=}
13+
// transform_view::<iterator>::operator{<,>,<=,>=,==,!=,<=>}
1414

1515
#include <ranges>
1616
#include <compare>
1717

1818
#include "test_macros.h"
19+
#include "test_iterators.h"
1920
#include "../types.h"
2021

2122
constexpr bool test() {
2223
{
23-
std::ranges::transform_view<ContiguousView, PlusOne> transformView1;
24-
auto iter1 = std::move(transformView1).begin();
25-
std::ranges::transform_view<ContiguousView, PlusOne> transformView2;
26-
auto iter2 = std::move(transformView2).begin();
27-
assert(iter1 == iter2);
28-
assert(iter1 + 1 != iter2);
29-
assert(iter1 + 1 == iter2 + 1);
24+
// Test a new-school iterator with operator<=>; the transform iterator should also have operator<=>.
25+
using It = three_way_contiguous_iterator<int*>;
26+
static_assert(std::three_way_comparable<It>);
27+
using R = std::ranges::transform_view<std::ranges::subrange<It>, PlusOne>;
28+
static_assert(std::three_way_comparable<std::ranges::iterator_t<R>>);
3029

31-
assert(iter1 < iter1 + 1);
32-
assert(iter1 + 1 > iter1);
33-
assert(iter1 <= iter1 + 1);
34-
assert(iter1 <= iter2);
35-
assert(iter1 + 1 >= iter2);
36-
assert(iter1 >= iter2);
30+
int a[] = {1,2,3};
31+
std::same_as<R> auto r = std::ranges::subrange<It>(It(a), It(a+3)) | std::views::transform(PlusOne());
32+
auto iter1 = r.begin();
33+
auto iter2 = iter1 + 1;
34+
35+
assert(!(iter1 < iter1)); assert(iter1 < iter2); assert(!(iter2 < iter1));
36+
assert(iter1 <= iter1); assert(iter1 <= iter2); assert(!(iter2 <= iter1));
37+
assert(!(iter1 > iter1)); assert(!(iter1 > iter2)); assert(iter2 > iter1);
38+
assert(iter1 >= iter1); assert(!(iter1 >= iter2)); assert(iter2 >= iter1);
39+
assert(iter1 == iter1); assert(!(iter1 == iter2)); assert(iter2 == iter2);
40+
assert(!(iter1 != iter1)); assert(iter1 != iter2); assert(!(iter2 != iter2));
41+
42+
assert((iter1 <=> iter2) == std::strong_ordering::less);
43+
assert((iter1 <=> iter1) == std::strong_ordering::equal);
44+
assert((iter2 <=> iter1) == std::strong_ordering::greater);
3745
}
3846

39-
// TODO: when three_way_comparable is implemented and std::is_eq is implemented,
40-
// uncomment this.
41-
// {
42-
// std::ranges::transform_view<ThreeWayCompView, PlusOne> transformView1;
43-
// auto iter1 = transformView1.begin();
44-
// std::ranges::transform_view<ThreeWayCompView, PlusOne> transformView2;
45-
// auto iter2 = transformView2.begin();
46-
//
47-
// assert(std::is_eq(iter1 <=> iter2));
48-
// assert(std::is_lteq(iter1 <=> iter2));
49-
// ++iter2;
50-
// assert(std::is_neq(iter1 <=> iter2));
51-
// assert(std::is_lt(iter1 <=> iter2));
52-
// assert(std::is_gt(iter2 <=> iter1));
53-
// assert(std::is_gteq(iter2 <=> iter1));
54-
//
55-
// static_assert( std::three_way_comparable<std::iterator_t<std::ranges::transform_view<ThreeWayCompView, PlusOne>>>);
56-
// static_assert(!std::three_way_comparable<std::iterator_t<std::ranges::transform_view<ContiguousView, PlusOne>>>);
57-
// }
47+
{
48+
// Test an old-school iterator with no operator<=>; the transform iterator shouldn't have operator<=> either.
49+
using It = random_access_iterator<int*>;
50+
static_assert(!std::three_way_comparable<It>);
51+
using R = std::ranges::transform_view<std::ranges::subrange<It>, PlusOne>;
52+
static_assert(!std::three_way_comparable<std::ranges::iterator_t<R>>);
53+
54+
int a[] = {1,2,3};
55+
std::same_as<R> auto r = std::ranges::subrange<It>(It(a), It(a+3)) | std::views::transform(PlusOne());
56+
auto iter1 = r.begin();
57+
auto iter2 = iter1 + 1;
58+
59+
assert(!(iter1 < iter1)); assert(iter1 < iter2); assert(!(iter2 < iter1));
60+
assert(iter1 <= iter1); assert(iter1 <= iter2); assert(!(iter2 <= iter1));
61+
assert(!(iter1 > iter1)); assert(!(iter1 > iter2)); assert(iter2 > iter1);
62+
assert(iter1 >= iter1); assert(!(iter1 >= iter2)); assert(iter2 >= iter1);
63+
assert(iter1 == iter1); assert(!(iter1 == iter2)); assert(iter2 == iter2);
64+
assert(!(iter1 != iter1)); assert(iter1 != iter2); assert(!(iter2 != iter2));
65+
}
5866

5967
return true;
6068
}

libcxx/test/std/ranges/range.adaptors/range.transform/types.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -129,14 +129,6 @@ struct CountedView : std::ranges::view_base {
129129
constexpr CountedIter end() const { return CountedIter(ForwardIter(globalBuff + 8)); }
130130
};
131131

132-
using ThreeWayCompIter = three_way_contiguous_iterator<int*>;
133-
struct ThreeWayCompView : std::ranges::view_base {
134-
constexpr ThreeWayCompIter begin() { return ThreeWayCompIter(globalBuff); }
135-
constexpr ThreeWayCompIter begin() const { return ThreeWayCompIter(globalBuff); }
136-
constexpr ThreeWayCompIter end() { return ThreeWayCompIter(globalBuff + 8); }
137-
constexpr ThreeWayCompIter end() const { return ThreeWayCompIter(globalBuff + 8); }
138-
};
139-
140132
struct TimesTwo {
141133
constexpr int operator()(int x) const { return x * 2; }
142134
};

libcxx/test/std/ranges/range.factories/range.iota.view/iterator/compare.pass.cpp

Lines changed: 61 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -10,69 +10,79 @@
1010
// UNSUPPORTED: libcpp-no-concepts
1111
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
1212

13-
// friend constexpr bool operator<(const iterator& x, const iterator& y)
14-
// requires totally_ordered<W>;
15-
// friend constexpr bool operator>(const iterator& x, const iterator& y)
16-
// requires totally_ordered<W>;
17-
// friend constexpr bool operator<=(const iterator& x, const iterator& y)
18-
// requires totally_ordered<W>;
19-
// friend constexpr bool operator>=(const iterator& x, const iterator& y)
20-
// requires totally_ordered<W>;
21-
// friend constexpr bool operator==(const iterator& x, const iterator& y)
22-
// requires equality_comparable<W>;
23-
24-
// TODO: test spaceship operator once it's implemented.
13+
// iota_view::<iterator>::operator{<,>,<=,>=,==,!=,<=>}
2514

2615
#include <ranges>
27-
#include <cassert>
16+
#include <compare>
2817

2918
#include "test_macros.h"
19+
#include "test_iterators.h"
3020
#include "../types.h"
3121

3222
constexpr bool test() {
3323
{
34-
const std::ranges::iota_view<int> io(0);
35-
assert( io.begin() == io.begin() );
36-
assert( io.begin() != std::ranges::next(io.begin()));
37-
assert( io.begin() < std::ranges::next(io.begin()));
38-
assert(std::ranges::next(io.begin()) > io.begin() );
39-
assert( io.begin() <= std::ranges::next(io.begin()));
40-
assert(std::ranges::next(io.begin()) >= io.begin() );
41-
assert( io.begin() <= io.begin() );
42-
assert( io.begin() >= io.begin() );
43-
}
44-
{
45-
std::ranges::iota_view<int> io(0);
46-
assert( io.begin() == io.begin() );
47-
assert( io.begin() != std::ranges::next(io.begin()));
48-
assert( io.begin() < std::ranges::next(io.begin()));
49-
assert(std::ranges::next(io.begin()) > io.begin() );
50-
assert( io.begin() <= std::ranges::next(io.begin()));
51-
assert(std::ranges::next(io.begin()) >= io.begin() );
52-
assert( io.begin() <= io.begin() );
53-
assert( io.begin() >= io.begin() );
24+
// Test `int`, which has operator<=>; the iota iterator should also have operator<=>.
25+
using R = std::ranges::iota_view<int>;
26+
static_assert(std::three_way_comparable<std::ranges::iterator_t<R>>);
27+
28+
std::same_as<R> auto r = std::views::iota(42);
29+
auto iter1 = r.begin();
30+
auto iter2 = iter1 + 1;
31+
32+
assert(!(iter1 < iter1)); assert(iter1 < iter2); assert(!(iter2 < iter1));
33+
assert(iter1 <= iter1); assert(iter1 <= iter2); assert(!(iter2 <= iter1));
34+
assert(!(iter1 > iter1)); assert(!(iter1 > iter2)); assert(iter2 > iter1);
35+
assert(iter1 >= iter1); assert(!(iter1 >= iter2)); assert(iter2 >= iter1);
36+
assert(iter1 == iter1); assert(!(iter1 == iter2)); assert(iter2 == iter2);
37+
assert(!(iter1 != iter1)); assert(iter1 != iter2); assert(!(iter2 != iter2));
38+
39+
assert((iter1 <=> iter2) == std::strong_ordering::less);
40+
assert((iter1 <=> iter1) == std::strong_ordering::equal);
41+
assert((iter2 <=> iter1) == std::strong_ordering::greater);
5442
}
43+
5544
{
56-
const std::ranges::iota_view<SomeInt> io(SomeInt(0));
57-
assert( io.begin() == io.begin() );
58-
assert( io.begin() != std::ranges::next(io.begin()));
59-
assert( io.begin() < std::ranges::next(io.begin()));
60-
assert(std::ranges::next(io.begin()) > io.begin() );
61-
assert( io.begin() <= std::ranges::next(io.begin()));
62-
assert(std::ranges::next(io.begin()) >= io.begin() );
63-
assert( io.begin() <= io.begin() );
64-
assert( io.begin() >= io.begin() );
45+
// Test a new-school iterator with operator<=>; the iota iterator should also have operator<=>.
46+
using It = three_way_contiguous_iterator<int*>;
47+
static_assert(std::three_way_comparable<It>);
48+
using R = std::ranges::iota_view<It>;
49+
static_assert(std::three_way_comparable<std::ranges::iterator_t<R>>);
50+
51+
int a[] = {1,2,3};
52+
std::same_as<R> auto r = std::views::iota(It(a));
53+
auto iter1 = r.begin();
54+
auto iter2 = iter1 + 1;
55+
56+
assert(!(iter1 < iter1)); assert(iter1 < iter2); assert(!(iter2 < iter1));
57+
assert(iter1 <= iter1); assert(iter1 <= iter2); assert(!(iter2 <= iter1));
58+
assert(!(iter1 > iter1)); assert(!(iter1 > iter2)); assert(iter2 > iter1);
59+
assert(iter1 >= iter1); assert(!(iter1 >= iter2)); assert(iter2 >= iter1);
60+
assert(iter1 == iter1); assert(!(iter1 == iter2)); assert(iter2 == iter2);
61+
assert(!(iter1 != iter1)); assert(iter1 != iter2); assert(!(iter2 != iter2));
62+
63+
assert((iter1 <=> iter2) == std::strong_ordering::less);
64+
assert((iter1 <=> iter1) == std::strong_ordering::equal);
65+
assert((iter2 <=> iter1) == std::strong_ordering::greater);
6566
}
67+
6668
{
67-
std::ranges::iota_view<SomeInt> io(SomeInt(0));
68-
assert( io.begin() == io.begin() );
69-
assert( io.begin() != std::ranges::next(io.begin()));
70-
assert( io.begin() < std::ranges::next(io.begin()));
71-
assert(std::ranges::next(io.begin()) > io.begin() );
72-
assert( io.begin() <= std::ranges::next(io.begin()));
73-
assert(std::ranges::next(io.begin()) >= io.begin() );
74-
assert( io.begin() <= io.begin() );
75-
assert( io.begin() >= io.begin() );
69+
// Test an old-school iterator with no operator<=>; the iota iterator shouldn't have operator<=> either.
70+
using It = random_access_iterator<int*>;
71+
static_assert(!std::three_way_comparable<It>);
72+
using R = std::ranges::iota_view<It>;
73+
static_assert(!std::three_way_comparable<std::ranges::iterator_t<R>>);
74+
75+
int a[] = {1,2,3};
76+
std::same_as<R> auto r = std::views::iota(It(a));
77+
auto iter1 = r.begin();
78+
auto iter2 = iter1 + 1;
79+
80+
assert(!(iter1 < iter1)); assert(iter1 < iter2); assert(!(iter2 < iter1));
81+
assert(iter1 <= iter1); assert(iter1 <= iter2); assert(!(iter2 <= iter1));
82+
assert(!(iter1 > iter1)); assert(!(iter1 > iter2)); assert(iter2 > iter1);
83+
assert(iter1 >= iter1); assert(!(iter1 >= iter2)); assert(iter2 >= iter1);
84+
assert(iter1 == iter1); assert(!(iter1 == iter2)); assert(iter2 == iter2);
85+
assert(!(iter1 != iter1)); assert(iter1 != iter2); assert(!(iter2 != iter2));
7686
}
7787

7888
return true;

0 commit comments

Comments
 (0)