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 // `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> 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> 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> 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> 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 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 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 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 296 int main(int, const char**) { 297 test(); 298 static_assert(test()); 299 300 return 0; 301 } 302