1 | //===----------------------------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | // UNSUPPORTED: c++03, c++11, c++14, c++17 |
10 | |
11 | // constexpr iterator& operator--(); |
12 | // requires ref-is-glvalue && bidirectional_range<Base> && |
13 | // bidirectional_range<range_reference_t<Base>> && |
14 | // common_range<range_reference_t<Base>>; |
15 | // constexpr iterator operator--(int); |
16 | // requires ref-is-glvalue && bidirectional_range<Base> && |
17 | // bidirectional_range<range_reference_t<Base>> && |
18 | // common_range<range_reference_t<Base>>; |
19 | |
20 | #include <algorithm> |
21 | #include <array> |
22 | #include <cassert> |
23 | #include <ranges> |
24 | #include <type_traits> |
25 | #include <vector> |
26 | |
27 | #include "../types.h" |
28 | |
29 | template <class T> |
30 | concept CanPreDecrement = requires(T& t) { --t; }; |
31 | |
32 | template <class T> |
33 | concept CanPostDecrement = requires(T& t) { t--; }; |
34 | |
35 | constexpr void noDecrementTest(auto&& jv) { |
36 | auto iter = jv.begin(); |
37 | static_assert(!CanPreDecrement<decltype(iter)>); |
38 | static_assert(!CanPostDecrement<decltype(iter)>); |
39 | } |
40 | |
41 | constexpr bool test() { |
42 | int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; |
43 | |
44 | { |
45 | // outer == ranges::end |
46 | std::ranges::join_view jv(buffer); |
47 | auto iter = std::next(jv.begin(), 16); |
48 | for (int i = 16; i != 0; --i) { |
49 | assert(*--iter == i); |
50 | } |
51 | } |
52 | |
53 | { |
54 | // outer == ranges::end |
55 | std::ranges::join_view jv(buffer); |
56 | auto iter = std::next(jv.begin(), 13); |
57 | for (int i = 13; i != 0; --i) { |
58 | assert(*--iter == i); |
59 | } |
60 | } |
61 | |
62 | { |
63 | // outer != ranges::end |
64 | std::ranges::join_view jv(buffer); |
65 | auto iter = std::next(jv.begin(), 12); |
66 | for (int i = 12; i != 0; --i) { |
67 | assert(*--iter == i); |
68 | } |
69 | } |
70 | |
71 | { |
72 | // outer != ranges::end |
73 | std::ranges::join_view jv(buffer); |
74 | auto iter = std::next(jv.begin()); |
75 | for (int i = 1; i != 0; --i) { |
76 | assert(*--iter == i); |
77 | } |
78 | } |
79 | |
80 | { |
81 | int small[2][1] = {{1}, {2}}; |
82 | std::ranges::join_view jv(small); |
83 | auto iter = std::next(jv.begin(), 2); |
84 | for (int i = 2; i != 0; --i) { |
85 | assert(*--iter == i); |
86 | } |
87 | } |
88 | |
89 | { |
90 | #if defined(__GNUG__) && !defined(__clang__) |
91 | // This seems to be a gcc bug where evaluating the following code |
92 | // at compile time results in wrong array index |
93 | if (!std::is_constant_evaluated()) { |
94 | #endif |
95 | // skip empty inner |
96 | BidiCommonInner inners[4] = {buffer[0], {nullptr, 0}, {nullptr, 0}, buffer[1]}; |
97 | std::ranges::join_view jv(inners); |
98 | auto iter = jv.end(); |
99 | for (int i = 8; i != 0; --i) { |
100 | assert(*--iter == i); |
101 | } |
102 | #if defined(__GNUG__) && !defined(__clang__) |
103 | } |
104 | #endif |
105 | } |
106 | |
107 | { |
108 | // basic type checking |
109 | std::ranges::join_view jv(buffer); |
110 | auto iter1 = std::ranges::next(jv.begin(), 4); |
111 | using iterator = decltype(iter1); |
112 | |
113 | decltype(auto) iter2 = --iter1; |
114 | static_assert(std::same_as<decltype(iter2), iterator&>); |
115 | assert(&iter1 == &iter2); |
116 | |
117 | std::same_as<iterator> decltype(auto) iter3 = iter1--; |
118 | assert(iter3 == std::next(iter1)); |
119 | } |
120 | |
121 | { |
122 | // !ref-is-glvalue |
123 | BidiCommonInner inners[2] = {buffer[0], buffer[1]}; |
124 | InnerRValue<BidiCommonOuter<BidiCommonInner>> outer{inners}; |
125 | std::ranges::join_view jv(outer); |
126 | noDecrementTest(jv); |
127 | } |
128 | |
129 | { |
130 | // !bidirectional_range<Base> |
131 | BidiCommonInner inners[2] = {buffer[0], buffer[1]}; |
132 | SimpleForwardCommonOuter<BidiCommonInner> outer{inners}; |
133 | std::ranges::join_view jv(outer); |
134 | noDecrementTest(jv); |
135 | } |
136 | |
137 | { |
138 | // !bidirectional_range<range_reference_t<Base>> |
139 | ForwardCommonInner inners[2] = {buffer[0], buffer[1]}; |
140 | std::ranges::join_view jv(inners); |
141 | noDecrementTest(jv); |
142 | } |
143 | |
144 | { |
145 | // LWG3313 `join_view::iterator::operator--` is incorrectly constrained |
146 | // `join_view::iterator` should not have `operator--` if |
147 | // !common_range<range_reference_t<Base>> |
148 | BidiNonCommonInner inners[2] = {buffer[0], buffer[1]}; |
149 | std::ranges::join_view jv(inners); |
150 | auto iter = jv.begin(); |
151 | static_assert(!CanPreDecrement<decltype(iter)>); |
152 | static_assert(!CanPostDecrement<decltype(iter)>); |
153 | } |
154 | |
155 | { |
156 | // LWG3791: `join_view::iterator::operator--` may be ill-formed |
157 | std::vector<std::vector<int>> vec = {{1, 2}, {3, 4}, {5, 6}}; |
158 | auto r = vec | std::views::transform([](auto& x) -> auto&& { return std::move(x); }) | std::views::join; |
159 | auto e = --r.end(); |
160 | assert(*e == 6); |
161 | assert(std::ranges::equal(std::views::reverse(r), std::array{6, 5, 4, 3, 2, 1})); |
162 | } |
163 | |
164 | return true; |
165 | } |
166 | |
167 | int main(int, char**) { |
168 | test(); |
169 | static_assert(test()); |
170 | |
171 | return 0; |
172 | } |
173 | |