Skip to content

Commit 1bbf3a3

Browse files
huixie90ldionne
andauthored
[libc++] Fix reverse_iterator when underlying is c++20 bidirectional_iterator but not Cpp17BidirectionalIterator (#112100)
`reverse_iterator` supports either c++20 `bidirectional_iterator` or `Cpp17BidirectionalIterator ` http://eel.is/c++draft/reverse.iter.requirements The current `reverse_iterator` uses `std::prev` in its `operator->`, which only supports the `Cpp17BidirectionalIterator` properly. If the underlying iterator is c++20 `bidirectional_iterator` but does not satisfy the named requirement `Cpp17BidirectionalIterator`, (examples are `zip_view::iterator`, `flat_map::iterator`), the current `std::prev` silently compiles but does a no-op and returns the same iterator back. So `reverse_iterator::operator->` will silently give a wrong answer. Even if we fix the behaviour of `std::prev`, at best, we could fail to compile the code. But this is not ok, because we need to support this kind of iterators in `reverse_iterator`. The solution is simply to not use `std::prev`. --------- Co-authored-by: Louis Dionne <[email protected]>
1 parent aa32060 commit 1bbf3a3

26 files changed

+229
-56
lines changed

libcxx/include/__iterator/reverse_iterator.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,12 @@ class _LIBCPP_TEMPLATE_VIS reverse_iterator
136136
_LIBCPP_HIDE_FROM_ABI constexpr pointer operator->() const
137137
requires is_pointer_v<_Iter> || requires(const _Iter __i) { __i.operator->(); }
138138
{
139+
_Iter __tmp = current;
140+
--__tmp;
139141
if constexpr (is_pointer_v<_Iter>) {
140-
return std::prev(current);
142+
return __tmp;
141143
} else {
142-
return std::prev(current).operator->();
144+
return __tmp.operator->();
143145
}
144146
}
145147
#else

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cmp/equal.pass.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ TEST_CONSTEXPR_CXX17 bool tests() {
3333
test(bidirectional_iterator<const char*>(s), bidirectional_iterator<const char*>(s+1), false);
3434
test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s), true);
3535
test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s+1), false);
36+
#if TEST_STD_VER >= 20
37+
test(cpp20_random_access_iterator<const char*>(s), cpp20_random_access_iterator<const char*>(s), true);
38+
test(cpp20_random_access_iterator<const char*>(s), cpp20_random_access_iterator<const char*>(s + 1), false);
39+
#endif
3640
test(s, s, true);
3741
test(s, s+1, false);
3842
return true;

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cmp/greater-equal.pass.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ TEST_CONSTEXPR_CXX17 bool tests() {
3232
test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s), true);
3333
test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s+1), true);
3434
test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s), false);
35+
#if TEST_STD_VER >= 20
36+
test(cpp20_random_access_iterator<const char*>(s), cpp20_random_access_iterator<const char*>(s), true);
37+
test(cpp20_random_access_iterator<const char*>(s), cpp20_random_access_iterator<const char*>(s + 1), true);
38+
test(cpp20_random_access_iterator<const char*>(s + 1), cpp20_random_access_iterator<const char*>(s), false);
39+
#endif
3540
test(s, s, true);
3641
test(s, s+1, true);
3742
test(s+1, s, false);

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cmp/greater.pass.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ TEST_CONSTEXPR_CXX17 bool tests() {
3232
test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s), false);
3333
test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s+1), true);
3434
test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s), false);
35+
#if TEST_STD_VER >= 20
36+
test(cpp20_random_access_iterator<const char*>(s), cpp20_random_access_iterator<const char*>(s), false);
37+
test(cpp20_random_access_iterator<const char*>(s), cpp20_random_access_iterator<const char*>(s + 1), true);
38+
test(cpp20_random_access_iterator<const char*>(s + 1), cpp20_random_access_iterator<const char*>(s), false);
39+
#endif
3540
test(s, s, false);
3641
test(s, s+1, true);
3742
test(s+1, s, false);

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cmp/less-equal.pass.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ TEST_CONSTEXPR_CXX17 bool tests() {
3232
test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s), true);
3333
test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s+1), false);
3434
test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s), true);
35+
#if TEST_STD_VER >= 20
36+
test(cpp20_random_access_iterator<const char*>(s), cpp20_random_access_iterator<const char*>(s), true);
37+
test(cpp20_random_access_iterator<const char*>(s), cpp20_random_access_iterator<const char*>(s + 1), false);
38+
test(cpp20_random_access_iterator<const char*>(s + 1), cpp20_random_access_iterator<const char*>(s), true);
39+
#endif
3540
test(s, s, true);
3641
test(s, s+1, false);
3742
test(s+1, s, true);

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cmp/less.pass.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ TEST_CONSTEXPR_CXX17 bool tests() {
3232
test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s), false);
3333
test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s+1), false);
3434
test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s), true);
35+
#if TEST_STD_VER >= 20
36+
test(cpp20_random_access_iterator<const char*>(s), cpp20_random_access_iterator<const char*>(s), false);
37+
test(cpp20_random_access_iterator<const char*>(s), cpp20_random_access_iterator<const char*>(s + 1), false);
38+
test(cpp20_random_access_iterator<const char*>(s + 1), cpp20_random_access_iterator<const char*>(s), true);
39+
#endif
3540
test(s, s, false);
3641
test(s, s+1, false);
3742
test(s+1, s, true);

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cmp/not-equal.pass.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ TEST_CONSTEXPR_CXX17 bool tests() {
3333
test(bidirectional_iterator<const char*>(s), bidirectional_iterator<const char*>(s+1), true);
3434
test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s), false);
3535
test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s+1), true);
36+
#if TEST_STD_VER >= 20
37+
test(cpp20_random_access_iterator<const char*>(s), cpp20_random_access_iterator<const char*>(s), false);
38+
test(cpp20_random_access_iterator<const char*>(s), cpp20_random_access_iterator<const char*>(s + 1), true);
39+
#endif
3640
test(s, s, false);
3741
test(s, s+1, true);
3842
return true;

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cons/assign.pass.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ TEST_CONSTEXPR_CXX17 bool tests() {
5959
Derived d;
6060
test<bidirectional_iterator<Base*> >(bidirectional_iterator<Derived*>(&d));
6161
test<random_access_iterator<const Base*> >(random_access_iterator<Derived*>(&d));
62+
#if TEST_STD_VER >= 20
63+
test<cpp20_random_access_iterator<const Base*> >(cpp20_random_access_iterator<Derived*>(&d));
64+
#endif
6265
test<Base*>(&d);
6366

6467
char c = '\0';

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cons/ctor.default.pass.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ TEST_CONSTEXPR_CXX17 void test() {
2626
TEST_CONSTEXPR_CXX17 bool tests() {
2727
test<bidirectional_iterator<const char*> >();
2828
test<random_access_iterator<char*> >();
29+
#if TEST_STD_VER >= 20
30+
test<cpp20_random_access_iterator<char*> >();
31+
#endif
2932
test<char*>();
3033
test<const char*>();
3134
return true;

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cons/ctor.iter.pass.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ TEST_CONSTEXPR_CXX17 bool tests() {
2828
const char s[] = "123";
2929
test(bidirectional_iterator<const char*>(s));
3030
test(random_access_iterator<const char*>(s));
31+
#if TEST_STD_VER >= 20
32+
test(cpp20_random_access_iterator<const char*>(s));
33+
#endif
3134
test(s);
3235
return true;
3336
}

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.cons/ctor.reverse_iterator.pass.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ TEST_CONSTEXPR_CXX17 bool tests() {
3333
Derived d;
3434
test<bidirectional_iterator<Base*> >(bidirectional_iterator<Derived*>(&d));
3535
test<random_access_iterator<const Base*> >(random_access_iterator<Derived*>(&d));
36+
#if TEST_STD_VER >= 20
37+
test<cpp20_random_access_iterator<const Base*> >(cpp20_random_access_iterator<Derived*>(&d));
38+
#endif
3639
test<Base*>(&d);
3740
return true;
3841
}

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.conv/base.pass.cpp

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,28 @@
1818
#include "test_macros.h"
1919
#include "test_iterators.h"
2020

21-
TEST_CONSTEXPR_CXX17 bool test() {
22-
typedef bidirectional_iterator<int*> Iter;
23-
int i = 0;
24-
Iter iter(&i);
25-
std::reverse_iterator<Iter> const reverse(iter);
26-
std::reverse_iterator<Iter>::iterator_type base = reverse.base();
27-
assert(base == Iter(&i));
28-
return true;
21+
template <class Iter>
22+
TEST_CONSTEXPR_CXX17 void test() {
23+
int i = 0;
24+
Iter iter(&i);
25+
std::reverse_iterator<Iter> const reverse(iter);
26+
typename std::reverse_iterator<Iter>::iterator_type base = reverse.base();
27+
assert(base == Iter(&i));
28+
}
29+
30+
TEST_CONSTEXPR_CXX17 bool tests() {
31+
test<bidirectional_iterator<int*> >();
32+
test<random_access_iterator<int*> >();
33+
#if TEST_STD_VER >= 20
34+
test<cpp20_random_access_iterator<int*>>();
35+
#endif
36+
return true;
2937
}
3038

3139
int main(int, char**) {
32-
test();
40+
tests();
3341
#if TEST_STD_VER > 14
34-
static_assert(test(), "");
42+
static_assert(tests(), "");
3543
#endif
36-
return 0;
44+
return 0;
3745
}

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.elem/arrow.pass.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,55 @@
2424

2525
#include "test_macros.h"
2626

27+
#if TEST_STD_VER >= 20
28+
// C++20 bidirectional_iterator that does not satisfy the Cpp17BidirectionalIterator named requirement.
29+
template <class It>
30+
class cpp20_bidirectional_iterator_with_arrow {
31+
It it_;
32+
33+
public:
34+
using iterator_category = std::input_iterator_tag;
35+
using iterator_concept = std::bidirectional_iterator_tag;
36+
using value_type = std::iterator_traits<It>::value_type;
37+
using difference_type = std::iterator_traits<It>::difference_type;
38+
39+
cpp20_bidirectional_iterator_with_arrow() : it_() {}
40+
explicit cpp20_bidirectional_iterator_with_arrow(It it) : it_(it) {}
41+
42+
decltype(auto) operator*() const { return *it_; }
43+
44+
auto operator->() const {
45+
if constexpr (std::is_pointer_v<It>) {
46+
return it_;
47+
} else {
48+
return it_.operator->();
49+
}
50+
}
51+
52+
cpp20_bidirectional_iterator_with_arrow& operator++() {
53+
++it_;
54+
return *this;
55+
}
56+
cpp20_bidirectional_iterator_with_arrow& operator--() {
57+
--it_;
58+
return *this;
59+
}
60+
cpp20_bidirectional_iterator_with_arrow operator++(int) { return cpp20_bidirectional_iterator_with_arrow(it_++); }
61+
cpp20_bidirectional_iterator_with_arrow operator--(int) { return cpp20_bidirectional_iterator_with_arrow(it_--); }
62+
63+
friend bool
64+
operator==(const cpp20_bidirectional_iterator_with_arrow& x, const cpp20_bidirectional_iterator_with_arrow& y) {
65+
return x.it_ == y.it_;
66+
}
67+
friend bool
68+
operator!=(const cpp20_bidirectional_iterator_with_arrow& x, const cpp20_bidirectional_iterator_with_arrow& y) {
69+
return x.it_ != y.it_;
70+
}
71+
72+
friend It base(const cpp20_bidirectional_iterator_with_arrow& i) { return i.it_; }
73+
};
74+
#endif
75+
2776
class A
2877
{
2978
int data_;
@@ -113,6 +162,16 @@ int main(int, char**)
113162

114163
static_assert(it1->get() == gC.get(), "");
115164
}
165+
#endif
166+
#if TEST_STD_VER >= 20
167+
{
168+
// The underlying iterator models c++20 bidirectional_iterator,
169+
// but does not satisfy c++17 BidirectionalIterator named requirement
170+
B data[] = {1, 2, 3};
171+
cpp20_bidirectional_iterator_with_arrow<B*> iter(data + 3);
172+
auto ri = std::make_reverse_iterator(iter);
173+
assert(ri->get() == 3);
174+
}
116175
#endif
117176
{
118177
((void)gC);

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.elem/bracket.pass.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ TEST_CONSTEXPR_CXX17 bool tests() {
3333
const char* s = "1234567890";
3434
test(random_access_iterator<const char*>(s+5), 4, '1');
3535
test(random_access_iterator<const char*>(s+5), 0, '5');
36+
#if TEST_STD_VER >= 20
37+
test(cpp20_random_access_iterator<const char*>(s + 5), 4, '1');
38+
test(cpp20_random_access_iterator<const char*>(s + 5), 0, '5');
39+
#endif
3640
test(s+5, 4, '1');
3741
test(s+5, 0, '5');
3842
return true;

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.elem/dereference.pass.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <cassert>
2222

2323
#include "test_macros.h"
24+
#include "test_iterators.h"
2425

2526
class A
2627
{
@@ -47,6 +48,10 @@ int main(int, char**)
4748
{
4849
A a;
4950
test(&a+1, A());
51+
test(random_access_iterator<A*>(&a + 1), A());
52+
#if TEST_STD_VER >= 20
53+
test(cpp20_random_access_iterator<A*>(&a + 1), A());
54+
#endif
5055

5156
#if TEST_STD_VER > 14
5257
{

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nav/decrement-assign.pass.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ TEST_CONSTEXPR_CXX17 void test(It i, typename std::iterator_traits<It>::differen
3030
TEST_CONSTEXPR_CXX17 bool tests() {
3131
const char* s = "1234567890";
3232
test(random_access_iterator<const char*>(s+5), 5, random_access_iterator<const char*>(s+10));
33+
#if TEST_STD_VER >= 20
34+
test(cpp20_random_access_iterator<const char*>(s + 5), 5, cpp20_random_access_iterator<const char*>(s + 10));
35+
#endif
3336
test(s+5, 5, s+10);
3437
return true;
3538
}

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nav/increment-assign.pass.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ TEST_CONSTEXPR_CXX17 void test(It i, typename std::iterator_traits<It>::differen
2929

3030
TEST_CONSTEXPR_CXX17 bool tests() {
3131
char const* s = "1234567890";
32-
test(random_access_iterator<const char*>(s+5), 5, random_access_iterator<const char*>(s));
32+
test(random_access_iterator<const char*>(s + 5), 5, random_access_iterator<const char*>(s));
33+
#if TEST_STD_VER >= 20
34+
test(cpp20_random_access_iterator<const char*>(s + 5), 5, cpp20_random_access_iterator<const char*>(s));
35+
#endif
3336
test(s+5, 5, s);
3437
return true;
3538
}

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nav/minus.pass.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ TEST_CONSTEXPR_CXX17 void test(It i, typename std::iterator_traits<It>::differen
2828

2929
TEST_CONSTEXPR_CXX17 bool tests() {
3030
const char* s = "1234567890";
31-
test(random_access_iterator<const char*>(s+5), 5, random_access_iterator<const char*>(s+10));
31+
test(random_access_iterator<const char*>(s + 5), 5, random_access_iterator<const char*>(s + 10));
32+
#if TEST_STD_VER >= 20
33+
test(cpp20_random_access_iterator<const char*>(s + 5), 5, cpp20_random_access_iterator<const char*>(s + 10));
34+
#endif
3235
test(s+5, 5, s+10);
3336
return true;
3437
}

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nav/plus.pass.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ TEST_CONSTEXPR_CXX17 void test(It i, typename std::iterator_traits<It>::differen
2828

2929
TEST_CONSTEXPR_CXX17 bool tests() {
3030
const char* s = "1234567890";
31-
test(random_access_iterator<const char*>(s+5), 5, random_access_iterator<const char*>(s));
31+
test(random_access_iterator<const char*>(s + 5), 5, random_access_iterator<const char*>(s));
32+
#if TEST_STD_VER >= 20
33+
test(cpp20_random_access_iterator<const char*>(s + 5), 5, cpp20_random_access_iterator<const char*>(s));
34+
#endif
3235
test(s+5, 5, s);
3336
return true;
3437
}

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nav/postdecrement.pass.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ TEST_CONSTEXPR_CXX17 bool tests() {
3030
const char* s = "123";
3131
test(bidirectional_iterator<const char*>(s+1), bidirectional_iterator<const char*>(s+2));
3232
test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s+2));
33+
#if TEST_STD_VER >= 20
34+
test(cpp20_random_access_iterator<const char*>(s + 1), cpp20_random_access_iterator<const char*>(s + 2));
35+
#endif
3336
test(s+1, s+2);
3437
return true;
3538
}

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nav/postincrement.pass.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ TEST_CONSTEXPR_CXX17 bool tests() {
3030
const char* s = "123";
3131
test(bidirectional_iterator<const char*>(s+1), bidirectional_iterator<const char*>(s));
3232
test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s));
33+
#if TEST_STD_VER >= 20
34+
test(cpp20_random_access_iterator<const char*>(s + 1), cpp20_random_access_iterator<const char*>(s));
35+
#endif
3336
test(s+1, s);
3437
return true;
3538
}

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nav/predecrement.pass.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ TEST_CONSTEXPR_CXX17 bool tests() {
3030
const char* s = "123";
3131
test(bidirectional_iterator<const char*>(s+1), bidirectional_iterator<const char*>(s+2));
3232
test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s+2));
33+
#if TEST_STD_VER >= 20
34+
test(cpp20_random_access_iterator<const char*>(s + 1), cpp20_random_access_iterator<const char*>(s + 2));
35+
#endif
3336
test(s+1, s+2);
3437
return true;
3538
}

libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iter.nav/preincrement.pass.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ TEST_CONSTEXPR_CXX17 bool tests() {
3030
const char* s = "123";
3131
test(bidirectional_iterator<const char*>(s+1), bidirectional_iterator<const char*>(s));
3232
test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s));
33+
#if TEST_STD_VER >= 20
34+
test(cpp20_random_access_iterator<const char*>(s + 1), cpp20_random_access_iterator<const char*>(s));
35+
#endif
3336
test(s+1, s);
3437
return true;
3538
}

0 commit comments

Comments
 (0)