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 
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     int i = 5, j = 6;
62     Proxy<int&> p5{i};
63     p5 = Proxy<int&>{j};
64     assert(p5.data == 6);
65   }
66 
67   // const assignment
68   {
69     int i = 5;
70     int j = 6;
71     const Proxy<int&> p1{i};
72     const Proxy<int&> p2{j};
73     p1 = p2;
74     assert(i == 6);
75 
76     MoveOnly m1{8};
77     MoveOnly m2{9};
78     Proxy<MoveOnly&&> p3       = std::move(m1);
79     const Proxy<MoveOnly&&> p4 = std::move(m2);
80     p4                         = std::move(p3);
81     assert(p4.data.get() == 8);
82   }
83 
84   // compare
85   {
86     Proxy<int> p1{5};
87     Proxy<int> p2{6};
88     assert(p1 != p2);
89     assert(p1 < p2);
90   }
91 }
92 
93 static_assert(std::input_iterator<ProxyIterator<cpp20_input_iterator<int*>>>);
94 static_assert(!std::forward_iterator<ProxyIterator<cpp20_input_iterator<int*>>>);
95 
96 static_assert(std::forward_iterator<ProxyIterator<forward_iterator<int*>>>);
97 static_assert(!std::bidirectional_iterator<ProxyIterator<forward_iterator<int*>>>);
98 
99 static_assert(std::bidirectional_iterator<ProxyIterator<bidirectional_iterator<int*>>>);
100 static_assert(!std::random_access_iterator<ProxyIterator<bidirectional_iterator<int*>>>);
101 
102 static_assert(std::random_access_iterator<ProxyIterator<random_access_iterator<int*>>>);
103 static_assert(!std::contiguous_iterator<ProxyIterator<random_access_iterator<int*>>>);
104 
105 static_assert(std::random_access_iterator<ProxyIterator<contiguous_iterator<int*>>>);
106 static_assert(!std::contiguous_iterator<ProxyIterator<contiguous_iterator<int*>>>);
107 
108 template <class Iter>
109 constexpr void testInputIteratorOperation() {
110   int data[] = {1, 2};
111   ProxyIterator<Iter> iter{Iter{data}};
112   sentinel_wrapper<ProxyIterator<Iter>> sent{ProxyIterator<Iter>{Iter{data + 2}}};
113 
114   std::same_as<Proxy<int&>> decltype(auto) result = *iter;
115   assert(result.data == 1);
116   auto& iter2 = ++iter;
117   static_assert(std::is_same_v<decltype(++iter), ProxyIterator<Iter>&>);
118   assert(&iter2 == &iter);
119   assert((*iter).data == 2);
120   ++iter;
121   assert(iter == sent);
122 }
123 
124 template <class Iter>
125 constexpr void testForwardIteratorOperation() {
126   int data[] = {1, 2};
127   ProxyIterator<Iter> iter{Iter{data}};
128 
129   std::same_as<ProxyIterator<Iter>> decltype(auto) it2 = iter++;
130   assert((*it2).data == 1);
131   assert((*iter).data == 2);
132 }
133 
134 template <class Iter>
135 constexpr void testBidirectionalIteratorOperation() {
136   int data[] = {1, 2};
137   ProxyIterator<Iter> iter{Iter{data}};
138   ++iter;
139   assert((*iter).data == 2);
140 
141   auto& iter2 = --iter;
142   static_assert(std::is_same_v<decltype(--iter), ProxyIterator<Iter>&>);
143   assert(&iter2 == &iter);
144   assert((*iter).data == 1);
145   ++iter;
146 
147   std::same_as<ProxyIterator<Iter>> decltype(auto) iter3 = iter--;
148   assert((*iter).data == 1);
149   assert((*iter3).data == 2);
150 }
151 
152 template <class Iter>
153 constexpr void testRandomAccessIteratorOperation() {
154   int data[] = {1, 2, 3, 4, 5};
155   ProxyIterator<Iter> iter{Iter{data}};
156 
157   auto& iter2 = iter += 2;
158   static_assert(std::is_same_v<decltype(iter += 2), ProxyIterator<Iter>&>);
159   assert(&iter2 == &iter);
160   assert((*iter).data == 3);
161 
162   auto& iter3 = iter -= 1;
163   static_assert(std::is_same_v<decltype(iter -= 1), ProxyIterator<Iter>&>);
164   assert(&iter3 == &iter);
165   assert((*iter).data == 2);
166 
167   std::same_as<Proxy<int&>> decltype(auto) r = iter[2];
168   assert(r.data == 4);
169 
170   std::same_as<ProxyIterator<Iter>> decltype(auto) iter4 = iter - 1;
171   assert((*iter4).data == 1);
172 
173   std::same_as<ProxyIterator<Iter>> decltype(auto) iter5 = iter4 + 2;
174   assert((*iter5).data == 3);
175 
176   std::same_as<ProxyIterator<Iter>> decltype(auto) iter6 = 3 + iter4;
177   assert((*iter6).data == 4);
178 
179   std::same_as<std::iter_difference_t<Iter>> decltype(auto) n = iter6 - iter5;
180   assert(n == 1);
181 
182   assert(iter4 < iter5);
183   assert(iter3 <= iter5);
184   assert(iter5 > iter4);
185   assert(iter6 >= iter4);
186 }
187 
188 constexpr void testProxyIterator() {
189   // input iterator operations
190   {
191     testInputIteratorOperation<cpp20_input_iterator<int*>>();
192     testInputIteratorOperation<forward_iterator<int*>>();
193     testInputIteratorOperation<bidirectional_iterator<int*>>();
194     testInputIteratorOperation<random_access_iterator<int*>>();
195     testInputIteratorOperation<contiguous_iterator<int*>>();
196   }
197 
198   // forward iterator operations
199   {
200     testForwardIteratorOperation<forward_iterator<int*>>();
201     testForwardIteratorOperation<bidirectional_iterator<int*>>();
202     testForwardIteratorOperation<random_access_iterator<int*>>();
203     testForwardIteratorOperation<contiguous_iterator<int*>>();
204   }
205 
206   // bidirectional iterator operations
207   {
208     testBidirectionalIteratorOperation<bidirectional_iterator<int*>>();
209     testBidirectionalIteratorOperation<random_access_iterator<int*>>();
210     testBidirectionalIteratorOperation<contiguous_iterator<int*>>();
211   }
212 
213   // random access iterator operations
214   {
215     testRandomAccessIteratorOperation<random_access_iterator<int*>>();
216     testRandomAccessIteratorOperation<contiguous_iterator<int*>>();
217   }
218 }
219 
220 constexpr void testProxyRange() {
221   int data[] = {3, 4, 5};
222   ProxyRange r{data};
223   std::same_as<ProxyIterator<int*>> decltype(auto) it = std::ranges::begin(r);
224   assert((*it).data == 3);
225   it += 3;
226   assert(it == std::ranges::end(r));
227 }
228 
229 template <class Iter>
230 concept StdMoveWorks = requires(std::iter_value_t<Iter> val, Iter iter) { val = std::move(*iter); };
231 
232 static_assert(StdMoveWorks<MoveOnly*>);
233 static_assert(!StdMoveWorks<ProxyIterator<MoveOnly*>>);
234 
235 // although this "works" but it actually creates a copy instead of move
236 static_assert(StdMoveWorks<ProxyIterator<int*>>);
237 
238 using std::swap;
239 
240 template <class Iter>
241 concept SwapWorks = requires(Iter iter1, Iter iter2) { swap(*iter1, *iter2); };
242 
243 static_assert(SwapWorks<int*>);
244 static_assert(!SwapWorks<ProxyIterator<int*>>);
245 
246 constexpr bool test() {
247   testProxy();
248   testProxyIterator();
249   testProxyRange();
250 
251   // iter_move
252   {
253     MoveOnly data[] = {5, 6, 7};
254     ProxyRange r{data};
255     auto it                               = r.begin();
256     std::iter_value_t<decltype(it)> moved = std::ranges::iter_move(it);
257     assert(moved.data.get() == 5);
258   }
259 
260   // iter_swap
261   {
262     MoveOnly data[] = {5, 6, 7};
263     ProxyRange r{data};
264     auto it1 = r.begin();
265     auto it2 = it1 + 2;
266     std::ranges::iter_swap(it1, it2);
267     assert(data[0].get() == 7);
268     assert(data[2].get() == 5);
269   }
270 
271   return true;
272 }
273 
274 int main(int, const char**) {
275   test();
276   static_assert(test());
277 
278   return 0;
279 }
280