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