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#include "MoveOnly.h"
12#include "test_iterators.h"
13
14#include <cassert>
15
16constexpr void testProxy() {
17 // constructor value
18 {
19 Proxy<int> p{5};
20 assert(p.data == 5);
21 }
22
23 // constructor reference
24 {
25 int i = 5;
26 Proxy<int&> p{i};
27 assert(&p.data == &i);
28 }
29
30 // constructor conversion
31 {
32 int i = 5;
33 Proxy<int&> p1{i};
34 Proxy<int> p2 = p1;
35 assert(p2.data == 5);
36
37 Proxy<int&> p3{p2};
38 assert(&(p3.data) == &(p2.data));
39
40 MoveOnly m1{8};
41 Proxy<MoveOnly&&> p4 = std::move(m1);
42
43 Proxy<MoveOnly> p5 = std::move(p4);
44 assert(p5.data.get() == 8);
45 }
46
47 // assignment
48 {
49 Proxy<int> p1{5};
50 Proxy<int> p2{6};
51 p1 = p2;
52 assert(p1.data == 6);
53
54 MoveOnly m1{8};
55 Proxy<MoveOnly&&> p3 = std::move(m1);
56 Proxy<MoveOnly> p4{MoveOnly{9}};
57 p4 = std::move(p3);
58 assert(p4.data.get() == 8);
59
60 // `T` is a reference type.
61 int i = 5, j = 6, k = 7, x = 8;
62 Proxy<int&> p5{i};
63 // `Other` is a prvalue.
64 p5 = Proxy<int&>{j};
65 assert(p5.data == 6);
66 // `Other` is a const lvalue.
67 const Proxy<int&> p_ref{k};
68 p5 = p_ref;
69 assert(p5.data == 7);
70 // `Other` is an xvalue.
71 Proxy<int&> px{x};
72 p5 = std::move(px);
73 assert(p5.data == 8);
74 }
75
76 // const assignment
77 {
78 int i = 5;
79 int j = 6;
80 const Proxy<int&> p1{i};
81 const Proxy<int&> p2{j};
82 p1 = p2;
83 assert(i == 6);
84
85 MoveOnly m1{8};
86 MoveOnly m2{9};
87 Proxy<MoveOnly&&> p3 = std::move(m1);
88 const Proxy<MoveOnly&&> p4 = std::move(m2);
89 p4 = std::move(p3);
90 assert(p4.data.get() == 8);
91 }
92
93 // compare
94 {
95 Proxy<int> p1{5};
96 Proxy<int> p2{6};
97 assert(p1 != p2);
98 assert(p1 < p2);
99
100 // Comparing `T` and `T&`.
101 int i = 5, j = 6;
102 Proxy<int&> p_ref{i};
103 Proxy<const int&> p_cref{j};
104 assert(p1 == p_ref);
105 assert(p2 == p_cref);
106 assert(p_ref == p1);
107 assert(p_cref == p2);
108 assert(p_ref == p_ref);
109 assert(p_cref == p_cref);
110 assert(p_ref != p_cref);
111 }
112}
113
114static_assert(std::input_iterator<ProxyIterator<cpp20_input_iterator<int*>>>);
115static_assert(!std::forward_iterator<ProxyIterator<cpp20_input_iterator<int*>>>);
116
117static_assert(std::forward_iterator<ProxyIterator<forward_iterator<int*>>>);
118static_assert(!std::bidirectional_iterator<ProxyIterator<forward_iterator<int*>>>);
119
120static_assert(std::bidirectional_iterator<ProxyIterator<bidirectional_iterator<int*>>>);
121static_assert(!std::random_access_iterator<ProxyIterator<bidirectional_iterator<int*>>>);
122
123static_assert(std::random_access_iterator<ProxyIterator<random_access_iterator<int*>>>);
124static_assert(!std::contiguous_iterator<ProxyIterator<random_access_iterator<int*>>>);
125
126static_assert(std::random_access_iterator<ProxyIterator<contiguous_iterator<int*>>>);
127static_assert(!std::contiguous_iterator<ProxyIterator<contiguous_iterator<int*>>>);
128
129template <class Iter>
130constexpr void testInputIteratorOperation() {
131 int data[] = {1, 2};
132 ProxyIterator<Iter> iter{Iter{data}};
133 sentinel_wrapper<ProxyIterator<Iter>> sent{ProxyIterator<Iter>{Iter{data + 2}}};
134
135 std::same_as<Proxy<int&>> decltype(auto) result = *iter;
136 assert(result.data == 1);
137 auto& iter2 = ++iter;
138 static_assert(std::is_same_v<decltype(++iter), ProxyIterator<Iter>&>);
139 assert(&iter2 == &iter);
140 assert((*iter).data == 2);
141 ++iter;
142 assert(iter == sent);
143}
144
145template <class Iter>
146constexpr void testForwardIteratorOperation() {
147 int data[] = {1, 2};
148 ProxyIterator<Iter> iter{Iter{data}};
149
150 std::same_as<ProxyIterator<Iter>> decltype(auto) it2 = iter++;
151 assert((*it2).data == 1);
152 assert((*iter).data == 2);
153}
154
155template <class Iter>
156constexpr void testBidirectionalIteratorOperation() {
157 int data[] = {1, 2};
158 ProxyIterator<Iter> iter{Iter{data}};
159 ++iter;
160 assert((*iter).data == 2);
161
162 auto& iter2 = --iter;
163 static_assert(std::is_same_v<decltype(--iter), ProxyIterator<Iter>&>);
164 assert(&iter2 == &iter);
165 assert((*iter).data == 1);
166 ++iter;
167
168 std::same_as<ProxyIterator<Iter>> decltype(auto) iter3 = iter--;
169 assert((*iter).data == 1);
170 assert((*iter3).data == 2);
171}
172
173template <class Iter>
174constexpr void testRandomAccessIteratorOperation() {
175 int data[] = {1, 2, 3, 4, 5};
176 ProxyIterator<Iter> iter{Iter{data}};
177
178 auto& iter2 = iter += 2;
179 static_assert(std::is_same_v<decltype(iter += 2), ProxyIterator<Iter>&>);
180 assert(&iter2 == &iter);
181 assert((*iter).data == 3);
182
183 auto& iter3 = iter -= 1;
184 static_assert(std::is_same_v<decltype(iter -= 1), ProxyIterator<Iter>&>);
185 assert(&iter3 == &iter);
186 assert((*iter).data == 2);
187
188 std::same_as<Proxy<int&>> decltype(auto) r = iter[2];
189 assert(r.data == 4);
190
191 std::same_as<ProxyIterator<Iter>> decltype(auto) iter4 = iter - 1;
192 assert((*iter4).data == 1);
193
194 std::same_as<ProxyIterator<Iter>> decltype(auto) iter5 = iter4 + 2;
195 assert((*iter5).data == 3);
196
197 std::same_as<ProxyIterator<Iter>> decltype(auto) iter6 = 3 + iter4;
198 assert((*iter6).data == 4);
199
200 std::same_as<std::iter_difference_t<Iter>> decltype(auto) n = iter6 - iter5;
201 assert(n == 1);
202
203 assert(iter4 < iter5);
204 assert(iter3 <= iter5);
205 assert(iter5 > iter4);
206 assert(iter6 >= iter4);
207}
208
209constexpr void testProxyIterator() {
210 // input iterator operations
211 {
212 testInputIteratorOperation<cpp20_input_iterator<int*>>();
213 testInputIteratorOperation<forward_iterator<int*>>();
214 testInputIteratorOperation<bidirectional_iterator<int*>>();
215 testInputIteratorOperation<random_access_iterator<int*>>();
216 testInputIteratorOperation<contiguous_iterator<int*>>();
217 }
218
219 // forward iterator operations
220 {
221 testForwardIteratorOperation<forward_iterator<int*>>();
222 testForwardIteratorOperation<bidirectional_iterator<int*>>();
223 testForwardIteratorOperation<random_access_iterator<int*>>();
224 testForwardIteratorOperation<contiguous_iterator<int*>>();
225 }
226
227 // bidirectional iterator operations
228 {
229 testBidirectionalIteratorOperation<bidirectional_iterator<int*>>();
230 testBidirectionalIteratorOperation<random_access_iterator<int*>>();
231 testBidirectionalIteratorOperation<contiguous_iterator<int*>>();
232 }
233
234 // random access iterator operations
235 {
236 testRandomAccessIteratorOperation<random_access_iterator<int*>>();
237 testRandomAccessIteratorOperation<contiguous_iterator<int*>>();
238 }
239}
240
241constexpr void testProxyRange() {
242 int data[] = {3, 4, 5};
243 ProxyRange r{data};
244 std::same_as<ProxyIterator<int*>> decltype(auto) it = std::ranges::begin(r);
245 assert((*it).data == 3);
246 it += 3;
247 assert(it == std::ranges::end(r));
248}
249
250template <class Iter>
251concept StdMoveWorks = requires(std::iter_value_t<Iter> val, Iter iter) { val = std::move(*iter); };
252
253static_assert(StdMoveWorks<MoveOnly*>);
254static_assert(!StdMoveWorks<ProxyIterator<MoveOnly*>>);
255
256// although this "works" but it actually creates a copy instead of move
257static_assert(StdMoveWorks<ProxyIterator<int*>>);
258
259using std::swap;
260
261template <class Iter>
262concept SwapWorks = requires(Iter iter1, Iter iter2) { swap(*iter1, *iter2); };
263
264static_assert(SwapWorks<int*>);
265static_assert(!SwapWorks<ProxyIterator<int*>>);
266
267constexpr bool test() {
268 testProxy();
269 testProxyIterator();
270 testProxyRange();
271
272 // iter_move
273 {
274 MoveOnly data[] = {5, 6, 7};
275 ProxyRange r{data};
276 auto it = r.begin();
277 std::iter_value_t<decltype(it)> moved = std::ranges::iter_move(it);
278 assert(moved.data.get() == 5);
279 }
280
281 // iter_swap
282 {
283 MoveOnly data[] = {5, 6, 7};
284 ProxyRange r{data};
285 auto it1 = r.begin();
286 auto it2 = it1 + 2;
287 std::ranges::iter_swap(it1, it2);
288 assert(data[0].get() == 7);
289 assert(data[2].get() == 5);
290 }
291
292 return true;
293}
294
295int main(int, char**) {
296 test();
297 static_assert(test());
298
299 return 0;
300}
301

source code of libcxx/test/support/test.support/test_proxy.pass.cpp