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 // Making this test file support C++03 is difficult; the lack of support for initializer lists is a major issue.
10 // UNSUPPORTED: c++03
11
12 // <algorithm>
13
14 #include <algorithm>
15 #include <array>
16 #include <cassert>
17 #include <random>
18 #include <set>
19
20 #include "test_macros.h"
21
22 // This file contains checks for lifetime issues across all the classic algorithms. It uses two complementary
23 // approaches:
24 // - runtime checks using a proxy iterator that tracks the lifetime of itself and its objects to catch potential
25 // lifetime issues;
26 // - `constexpr` checks using a `constexpr`-friendly proxy iterator that catch undefined behavior.
27
28 // A random-access proxy iterator that tracks the lifetime of itself and its `value_type` and `reference` objects to
29 // prevent potential lifetime issues in algorithms.
30 //
31 // This class cannot be `constexpr` because its cache is a static variable. The cache cannot be provided as
32 // a constructor parameter because `LifetimeIterator` has to be default-constructible.
33 class LifetimeIterator {
34 // The cache simply tracks addresses of the local variables.
35 class LifetimeCache {
36 std::set<const void*> cache_;
37
38 public:
contains(const void * ptr) const39 bool contains(const void* ptr) const { return cache_.find(ptr) != cache_.end(); }
40
insert(const void * ptr)41 void insert(const void* ptr) {
42 assert(!contains(ptr));
43 cache_.insert(ptr);
44 }
45
erase(const void * ptr)46 void erase(const void* ptr) {
47 assert(contains(ptr));
48 cache_.erase(ptr);
49 }
50 };
51
52 public:
53 struct Value {
54 int i_;
55 bool moved_from_ = false; // Check for double moves and reads after moving.
56
ValueLifetimeIterator::Value57 Value() { lifetime_cache.insert(this); }
ValueLifetimeIterator::Value58 Value(int i) : i_(i) { lifetime_cache.insert(this); }
~ValueLifetimeIterator::Value59 ~Value() { lifetime_cache.erase(this); }
60
ValueLifetimeIterator::Value61 Value(const Value& rhs) : i_(rhs.i_) {
62 assert(lifetime_cache.contains(&rhs));
63 assert(!rhs.moved_from_);
64
65 lifetime_cache.insert(this);
66 }
67
ValueLifetimeIterator::Value68 Value(Value&& rhs) noexcept : i_(rhs.i_) {
69 assert(lifetime_cache.contains(&rhs));
70
71 assert(!rhs.moved_from_);
72 rhs.moved_from_ = true;
73
74 // It's ok if it throws -- since it's a test, terminating the program is acceptable.
75 lifetime_cache.insert(this);
76 }
77
operator =LifetimeIterator::Value78 Value& operator=(const Value& rhs) {
79 assert(lifetime_cache.contains(this) && lifetime_cache.contains(&rhs));
80 assert(!rhs.moved_from_);
81
82 i_ = rhs.i_;
83 moved_from_ = false;
84
85 return *this;
86 }
87
operator =LifetimeIterator::Value88 Value& operator=(Value&& rhs) noexcept {
89 assert(lifetime_cache.contains(this) && lifetime_cache.contains(&rhs));
90
91 assert(!rhs.moved_from_);
92 rhs.moved_from_ = true;
93
94 i_ = rhs.i_;
95 moved_from_ = false;
96
97 return *this;
98 }
99
operator <(const Value & lhs,const Value & rhs)100 friend bool operator<(const Value& lhs, const Value& rhs) {
101 assert(lifetime_cache.contains(&lhs) && lifetime_cache.contains(&rhs));
102 assert(!lhs.moved_from_ && !rhs.moved_from_);
103
104 return lhs.i_ < rhs.i_;
105 }
106
operator ==(const Value & lhs,const Value & rhs)107 friend bool operator==(const Value& lhs, const Value& rhs) {
108 assert(lifetime_cache.contains(&lhs) && lifetime_cache.contains(&rhs));
109 assert(!lhs.moved_from_ && !rhs.moved_from_);
110
111 return lhs.i_ == rhs.i_;
112 }
113
114 };
115
116 struct Reference {
117 Value* v_;
118 bool moved_from_ = false; // Check for double moves and reads after moving.
119
ReferenceLifetimeIterator::Reference120 Reference(Value& v) : v_(&v) {
121 lifetime_cache.insert(this);
122 }
123
~ReferenceLifetimeIterator::Reference124 ~Reference() {
125 lifetime_cache.erase(this);
126 }
127
ReferenceLifetimeIterator::Reference128 Reference(const Reference& rhs) : v_(rhs.v_) {
129 assert(lifetime_cache.contains(&rhs));
130 assert(!rhs.moved_from_);
131
132 lifetime_cache.insert(this);
133 }
134
ReferenceLifetimeIterator::Reference135 Reference(Reference&& rhs) noexcept : v_(rhs.v_) {
136 assert(lifetime_cache.contains(&rhs));
137
138 assert(!rhs.moved_from_);
139 rhs.moved_from_ = true;
140
141 lifetime_cache.insert(this);
142 }
143
operator =LifetimeIterator::Reference144 Reference& operator=(const Reference& rhs) {
145 assert(lifetime_cache.contains(this) && lifetime_cache.contains(&rhs));
146 assert(!rhs.moved_from_);
147
148 v_ = rhs.v_;
149 moved_from_ = false;
150
151 return *this;
152 }
153
operator =LifetimeIterator::Reference154 Reference& operator=(Reference&& rhs) noexcept {
155 assert(lifetime_cache.contains(this) && lifetime_cache.contains(&rhs));
156
157 assert(!rhs.moved_from_);
158 rhs.moved_from_ = true;
159
160 v_ = rhs.v_;
161 moved_from_ = false;
162
163 return *this;
164 }
165
operator ValueLifetimeIterator::Reference166 operator Value() const {
167 assert(lifetime_cache.contains(this));
168 assert(!moved_from_);
169
170 return *v_;
171 }
172
operator =LifetimeIterator::Reference173 Reference& operator=(Value v) {
174 assert(lifetime_cache.contains(this));
175 assert(!moved_from_);
176
177 *v_ = v;
178 moved_from_ = false;
179
180 return *this;
181 }
182
operator <(const Reference & lhs,const Reference & rhs)183 friend bool operator<(const Reference& lhs, const Reference& rhs) {
184 assert(lifetime_cache.contains(&lhs) && lifetime_cache.contains(&rhs));
185 assert(!lhs.moved_from_ && !rhs.moved_from_);
186
187 return *lhs.v_ < *rhs.v_;
188 }
189
operator ==(const Reference & lhs,const Reference & rhs)190 friend bool operator==(const Reference& lhs, const Reference& rhs) {
191 assert(lifetime_cache.contains(&lhs) && lifetime_cache.contains(&rhs));
192 assert(!lhs.moved_from_ && !rhs.moved_from_);
193
194 return *lhs.v_ == *rhs.v_;
195 }
196
swap(Reference lhs,Reference rhs)197 friend void swap(Reference lhs, Reference rhs) {
198 assert(lifetime_cache.contains(&lhs) && lifetime_cache.contains(&rhs));
199 assert(!lhs.moved_from_ && !rhs.moved_from_);
200
201 std::swap(*(lhs.v_), *(rhs.v_));
202 }
203 };
204
205 using difference_type = int;
206 using value_type = Value;
207 using reference = Reference;
208 using pointer = void;
209 using iterator_category = std::random_access_iterator_tag;
210
211 Value* ptr_ = nullptr;
212 bool moved_from_ = false; // Check for double moves and reads after moving.
213
214 LifetimeIterator() = default;
LifetimeIterator(Value * ptr)215 LifetimeIterator(Value* ptr) : ptr_(ptr) {}
216
LifetimeIterator(const LifetimeIterator & rhs)217 LifetimeIterator(const LifetimeIterator& rhs) : ptr_(rhs.ptr_) {
218 assert(!rhs.moved_from_);
219 }
220
operator =(const LifetimeIterator & rhs)221 LifetimeIterator& operator=(const LifetimeIterator& rhs) {
222 assert(!rhs.moved_from_);
223
224 ptr_ = rhs.ptr_;
225 moved_from_ = false;
226
227 return *this;
228 }
229
LifetimeIterator(LifetimeIterator && rhs)230 LifetimeIterator(LifetimeIterator&& rhs) noexcept : ptr_(rhs.ptr_) {
231 assert(!rhs.moved_from_);
232 rhs.moved_from_ = true;
233 rhs.ptr_ = nullptr;
234 }
235
operator =(LifetimeIterator && rhs)236 LifetimeIterator& operator=(LifetimeIterator&& rhs) noexcept {
237 assert(!rhs.moved_from_);
238 rhs.moved_from_ = true;
239 moved_from_ = false;
240
241 ptr_ = rhs.ptr_;
242 rhs.ptr_ = nullptr;
243
244 return *this;
245 }
246
operator *() const247 Reference operator*() const {
248 assert(!moved_from_);
249 return Reference(*ptr_);
250 }
251
operator ++()252 LifetimeIterator& operator++() {
253 assert(!moved_from_);
254
255 ++ptr_;
256 return *this;
257 }
258
operator ++(int)259 LifetimeIterator operator++(int) {
260 assert(!moved_from_);
261
262 auto tmp = *this;
263 ++*this;
264 return tmp;
265 }
266
operator ==(const LifetimeIterator & lhs,const LifetimeIterator & rhs)267 friend bool operator==(const LifetimeIterator& lhs, const LifetimeIterator& rhs) {
268 assert(!lhs.moved_from_ && !rhs.moved_from_);
269 return lhs.ptr_ == rhs.ptr_;
270 }
operator !=(const LifetimeIterator & lhs,const LifetimeIterator & rhs)271 friend bool operator!=(const LifetimeIterator& lhs, const LifetimeIterator& rhs) {
272 assert(!lhs.moved_from_ && !rhs.moved_from_);
273 return lhs.ptr_ != rhs.ptr_;
274 }
275
operator --()276 LifetimeIterator& operator--() {
277 assert(!moved_from_);
278
279 --ptr_;
280 return *this;
281 }
282
operator --(int)283 LifetimeIterator operator--(int) {
284 assert(!moved_from_);
285
286 auto tmp = *this;
287 --*this;
288 return tmp;
289 }
290
operator +=(difference_type n)291 LifetimeIterator& operator+=(difference_type n) {
292 assert(!moved_from_);
293
294 ptr_ += n;
295 return *this;
296 }
297
operator -=(difference_type n)298 LifetimeIterator& operator-=(difference_type n) {
299 assert(!moved_from_);
300
301 ptr_ -= n;
302 return *this;
303 }
304
operator [](difference_type i) const305 Reference operator[](difference_type i) const {
306 assert(!moved_from_);
307 return Reference(*(ptr_ + i));
308 }
309
operator <(const LifetimeIterator & lhs,const LifetimeIterator & rhs)310 friend bool operator<(const LifetimeIterator& lhs, const LifetimeIterator& rhs) {
311 assert(!lhs.moved_from_ && !rhs.moved_from_);
312 return lhs.ptr_ < rhs.ptr_;
313 }
314
operator >(const LifetimeIterator & lhs,const LifetimeIterator & rhs)315 friend bool operator>(const LifetimeIterator& lhs, const LifetimeIterator& rhs) {
316 assert(!lhs.moved_from_ && !rhs.moved_from_);
317 return lhs.ptr_ > rhs.ptr_;
318 }
319
operator <=(const LifetimeIterator & lhs,const LifetimeIterator & rhs)320 friend bool operator<=(const LifetimeIterator& lhs, const LifetimeIterator& rhs) {
321 assert(!lhs.moved_from_ && !rhs.moved_from_);
322 return lhs.ptr_ <= rhs.ptr_;
323 }
324
operator >=(const LifetimeIterator & lhs,const LifetimeIterator & rhs)325 friend bool operator>=(const LifetimeIterator& lhs, const LifetimeIterator& rhs) {
326 assert(!lhs.moved_from_ && !rhs.moved_from_);
327 return lhs.ptr_ >= rhs.ptr_;
328 }
329
operator +(const LifetimeIterator & lhs,difference_type n)330 friend LifetimeIterator operator+(const LifetimeIterator& lhs, difference_type n) {
331 assert(!lhs.moved_from_);
332 return LifetimeIterator(lhs.ptr_ + n);
333 }
334
operator +(difference_type n,const LifetimeIterator & lhs)335 friend LifetimeIterator operator+(difference_type n, const LifetimeIterator& lhs) {
336 assert(!lhs.moved_from_);
337 return LifetimeIterator(n + lhs.ptr_);
338 }
339
operator -(const LifetimeIterator & lhs,difference_type n)340 friend LifetimeIterator operator-(const LifetimeIterator& lhs, difference_type n) {
341 assert(!lhs.moved_from_);
342 return LifetimeIterator(lhs.ptr_ - n);
343 }
344
operator -(LifetimeIterator lhs,LifetimeIterator rhs)345 friend difference_type operator-(LifetimeIterator lhs, LifetimeIterator rhs) {
346 assert(!lhs.moved_from_ && !rhs.moved_from_);
347 return static_cast<int>(lhs.ptr_ - rhs.ptr_);
348 }
349
350 static LifetimeCache lifetime_cache;
351 };
352
353 LifetimeIterator::LifetimeCache LifetimeIterator::lifetime_cache;
354
355 #if TEST_STD_VER > 17
356 // A constexpr-friendly proxy iterator to check for undefined behavior in algorithms (since undefined behavior is
357 // statically caught in `constexpr` context).
358 class ConstexprIterator {
359 public:
360 struct Reference {
361 int* v_;
362 bool moved_from_ = false; // Check for double moves and reads after moving.
363
ReferenceConstexprIterator::Reference364 constexpr Reference(int& v) : v_(&v) { }
365
366 constexpr Reference(const Reference& rhs) = default;
operator =ConstexprIterator::Reference367 constexpr Reference& operator=(const Reference& rhs) {
368 assert(!rhs.moved_from_);
369 v_ = rhs.v_;
370 moved_from_ = false;
371
372 return *this;
373 }
374
ReferenceConstexprIterator::Reference375 constexpr Reference(Reference&& rhs) noexcept : v_(rhs.v_) {
376 assert(!rhs.moved_from_);
377 rhs.moved_from_ = true;
378 }
379
operator =ConstexprIterator::Reference380 constexpr Reference& operator=(Reference&& rhs) noexcept {
381 assert(!rhs.moved_from_);
382 rhs.moved_from_ = true;
383 moved_from_ = false;
384
385 v_ = rhs.v_;
386 return *this;
387 }
388
operator intConstexprIterator::Reference389 constexpr operator int() const {
390 assert(!moved_from_);
391 return *v_;
392 }
393
operator =ConstexprIterator::Reference394 constexpr Reference& operator=(int v) {
395 *v_ = v;
396 moved_from_ = false;
397
398 return *this;
399 }
400
operator <(const Reference & lhs,const Reference & rhs)401 friend constexpr bool operator<(const Reference& lhs, const Reference& rhs) {
402 assert(!lhs.moved_from_ && !rhs.moved_from_);
403 return *lhs.v_ < *rhs.v_;
404 }
405
operator ==(const Reference & lhs,const Reference & rhs)406 friend constexpr bool operator==(const Reference& lhs, const Reference& rhs) {
407 assert(!lhs.moved_from_ && !rhs.moved_from_);
408 return *lhs.v_ == *rhs.v_;
409 }
410
swap(Reference lhs,Reference rhs)411 friend constexpr void swap(Reference lhs, Reference rhs) {
412 assert(!lhs.moved_from_ && !rhs.moved_from_);
413 std::swap(*(lhs.v_), *(rhs.v_));
414 }
415 };
416
417 using difference_type = int;
418 using value_type = int;
419 using reference = Reference;
420 using pointer = void;
421 using iterator_category = std::random_access_iterator_tag;
422
423 int* ptr_ = nullptr;
424 bool moved_from_ = false; // Check for double moves and reads after moving.
425
426 constexpr ConstexprIterator() = default;
ConstexprIterator(int * ptr)427 constexpr ConstexprIterator(int* ptr) : ptr_(ptr) {}
428
ConstexprIterator(const ConstexprIterator & rhs)429 constexpr ConstexprIterator(const ConstexprIterator& rhs) : ptr_(rhs.ptr_) {
430 assert(!rhs.moved_from_);
431 }
432
operator =(const ConstexprIterator & rhs)433 constexpr ConstexprIterator& operator=(const ConstexprIterator& rhs) {
434 assert(!rhs.moved_from_);
435
436 ptr_ = rhs.ptr_;
437 moved_from_ = false;
438
439 return *this;
440 }
441
ConstexprIterator(ConstexprIterator && rhs)442 constexpr ConstexprIterator(ConstexprIterator&& rhs) noexcept : ptr_(rhs.ptr_) {
443 assert(!rhs.moved_from_);
444 rhs.moved_from_ = true;
445 rhs.ptr_ = nullptr;
446 }
447
operator =(ConstexprIterator && rhs)448 constexpr ConstexprIterator& operator=(ConstexprIterator&& rhs) noexcept {
449 assert(!rhs.moved_from_);
450 rhs.moved_from_ = true;
451 moved_from_ = false;
452
453 ptr_ = rhs.ptr_;
454 rhs.ptr_ = nullptr;
455
456 return *this;
457 }
458
operator *() const459 constexpr Reference operator*() const {
460 assert(!moved_from_);
461 return Reference(*ptr_);
462 }
463
operator ++()464 constexpr ConstexprIterator& operator++() {
465 assert(!moved_from_);
466
467 ++ptr_;
468 return *this;
469 }
470
operator ++(int)471 constexpr ConstexprIterator operator++(int) {
472 assert(!moved_from_);
473
474 auto tmp = *this;
475 ++*this;
476 return tmp;
477 }
478
operator ==(const ConstexprIterator & lhs,const ConstexprIterator & rhs)479 friend constexpr bool operator==(const ConstexprIterator& lhs, const ConstexprIterator& rhs) {
480 assert(!lhs.moved_from_ && !rhs.moved_from_);
481 return lhs.ptr_ == rhs.ptr_;
482 }
483
operator !=(const ConstexprIterator & lhs,const ConstexprIterator & rhs)484 friend constexpr bool operator!=(const ConstexprIterator& lhs, const ConstexprIterator& rhs) {
485 assert(!lhs.moved_from_ && !rhs.moved_from_);
486 return lhs.ptr_ != rhs.ptr_;
487 }
488
operator --()489 constexpr ConstexprIterator& operator--() {
490 assert(!moved_from_);
491
492 --ptr_;
493 return *this;
494 }
495
operator --(int)496 constexpr ConstexprIterator operator--(int) {
497 assert(!moved_from_);
498
499 auto tmp = *this;
500 --*this;
501 return tmp;
502 }
503
operator +=(difference_type n)504 constexpr ConstexprIterator& operator+=(difference_type n) {
505 assert(!moved_from_);
506
507 ptr_ += n;
508 return *this;
509 }
510
operator -=(difference_type n)511 constexpr ConstexprIterator& operator-=(difference_type n) {
512 assert(!moved_from_);
513
514 ptr_ -= n;
515 return *this;
516 }
517
operator [](difference_type i) const518 constexpr Reference operator[](difference_type i) const {
519 return Reference(*(ptr_ + i));
520 }
521
operator <=>(const ConstexprIterator & lhs,const ConstexprIterator & rhs)522 friend constexpr auto operator<=>(const ConstexprIterator& lhs, const ConstexprIterator& rhs) {
523 assert(!lhs.moved_from_ && !rhs.moved_from_);
524 return lhs.ptr_ <=> rhs.ptr_;
525 }
526
operator +(const ConstexprIterator & lhs,difference_type n)527 friend constexpr ConstexprIterator operator+(const ConstexprIterator& lhs, difference_type n) {
528 assert(!lhs.moved_from_);
529 return ConstexprIterator(lhs.ptr_ + n);
530 }
531
operator +(difference_type n,const ConstexprIterator & lhs)532 friend constexpr ConstexprIterator operator+(difference_type n, const ConstexprIterator& lhs) {
533 assert(!lhs.moved_from_);
534 return ConstexprIterator(n + lhs.ptr_);
535 }
536
operator -(const ConstexprIterator & lhs,difference_type n)537 friend constexpr ConstexprIterator operator-(const ConstexprIterator& lhs, difference_type n) {
538 assert(!lhs.moved_from_);
539 return ConstexprIterator(lhs.ptr_ - n);
540 }
541
operator -(ConstexprIterator lhs,ConstexprIterator rhs)542 friend constexpr difference_type operator-(ConstexprIterator lhs, ConstexprIterator rhs) {
543 assert(!lhs.moved_from_ && !rhs.moved_from_);
544 return static_cast<int>(lhs.ptr_ - rhs.ptr_);
545 }
546 };
547
548 #endif // TEST_STD_VER > 17
549
550 template <class T, size_t N = 32>
551 class Input {
552 using Array = std::array<T, N>;
553
554 size_t size_ = 0;
555 Array values_ = {};
556
557 public:
558 template <size_t N2>
Input(std::array<T,N2> from)559 TEST_CONSTEXPR_CXX20 Input(std::array<T, N2> from) {
560 static_assert(N2 <= N, "");
561
562 std::copy(from.begin(), from.end(), begin());
563 size_ = N2;
564 }
565
begin()566 TEST_CONSTEXPR_CXX20 typename Array::iterator begin() { return values_.begin(); }
end()567 TEST_CONSTEXPR_CXX20 typename Array::iterator end() { return values_.begin() + size_; }
size() const568 TEST_CONSTEXPR_CXX20 size_t size() const { return size_; }
569 };
570
571 // TODO: extend `Value` and `Reference` so that it's possible to pass plain integers to all the algorithms.
572
573 // Several generic inputs that are useful for many algorithms. Provides two unsorted sequences with and without
574 // duplicates, with positive and negative values; and a few corner cases, like an empty sequence, a sequence of all
575 // duplicates, and so on.
576 template <class Iter>
get_simple_in()577 TEST_CONSTEXPR_CXX20 std::array<Input<typename Iter::value_type>, 8> get_simple_in() {
578 using T = typename Iter::value_type;
579 std::array<Input<T>, 8> result = {
580 Input<T>({std::array<T, 0>{ }}),
581 Input<T>({std::array<T, 1>{ T{1} }}),
582 Input<T>({std::array<T, 1>{ T{-1} }}),
583 Input<T>({std::array<T, 2>{ T{-1}, {1} }}),
584 Input<T>({std::array<T, 3>{ T{1}, {1}, {1} }}),
585 Input<T>({std::array<T, 3>{ T{-1}, {-1}, {-1} }}),
586 Input<T>({std::array<T, 9>{ T{-8}, {6}, {3}, {2}, {1}, {5}, {-4}, {-9}, {3} }}),
587 Input<T>({std::array<T, 9>{ T{-8}, {3}, {3}, {2}, {5}, {-4}, {-4}, {-4}, {1} }}),
588 };
589 return result;
590 }
591
592 // Sorted inputs of varying lengths.
593 template <class Iter>
get_sorted_in()594 TEST_CONSTEXPR_CXX20 std::array<Input<typename Iter::value_type>, 8> get_sorted_in() {
595 using T = typename Iter::value_type;
596 std::array<Input<T>, 8> result = {
597 Input<T>({std::array<T, 0>{ }}),
598 Input<T>({std::array<T, 1>{ T{1} }}),
599 Input<T>({std::array<T, 1>{ T{-1} }}),
600 Input<T>({std::array<T, 2>{ T{-1}, {1} }}),
601 Input<T>({std::array<T, 3>{ T{1}, {1}, {1} }}),
602 Input<T>({std::array<T, 3>{ T{-1}, {-1}, {-1} }}),
603 Input<T>({std::array<T, 8>{ T{-8}, {-5}, {-3}, {-1}, {1}, {4}, {5}, {9} }}),
604 Input<T>({std::array<T, 11>{ T{-8}, {-5}, {-3}, {-3}, {-1}, {1}, {4}, {5}, {5}, {9}, {9} }}),
605 };
606 return result;
607 }
608
609 // Inputs for testing `std::sort`. These have been manually verified to exercise all internal functions in `std::sort`
610 // except the branchless sort ones (which can't be triggered with proxy arrays).
611 template <class Iter>
get_sort_test_in()612 TEST_CONSTEXPR_CXX20 std::array<Input<typename Iter::value_type>, 8> get_sort_test_in() {
613 using T = typename Iter::value_type;
614 std::array<Input<T>, 8> result = {
615 Input<T>({std::array<T, 0>{ }}),
616 Input<T>({std::array<T, 1>{ T{1} }}),
617 Input<T>({std::array<T, 1>{ T{-1} }}),
618 Input<T>({std::array<T, 2>{ T{-1}, {1} }}),
619 Input<T>({std::array<T, 3>{ T{1}, {1}, {1} }}),
620 Input<T>({std::array<T, 3>{ T{-1}, {-1}, {-1} }}),
621 Input<T>({std::array<T, 8>{ T{-8}, {-5}, {-3}, {-1}, {1}, {4}, {5}, {9} }}),
622 Input<T>({std::array<T, 11>{ T{-8}, {-5}, {-3}, {-3}, {-1}, {1}, {4}, {5}, {5}, {9}, {9} }}),
623 };
624 return result;
625 }
626
627 template <class Input, size_t N, class Func>
test(std::array<Input,N> inputs,Func func)628 TEST_CONSTEXPR_CXX20 void test(std::array<Input, N> inputs, Func func) {
629 for (auto&& in : inputs) {
630 func(in.begin(), in.end());
631 }
632 }
633
634 template <class Input, size_t N, class Func>
test_n(std::array<Input,N> inputs,Func func)635 TEST_CONSTEXPR_CXX20 void test_n(std::array<Input, N> inputs, Func func) {
636 for (auto&& in : inputs) {
637 func(in.begin(), in.size());
638 }
639 }
640
to_int(int x)641 constexpr int to_int(int x) { return x; }
to_int(LifetimeIterator::Value x)642 int to_int(LifetimeIterator::Value x) { return x.i_; }
643
rand_gen()644 std::mt19937 rand_gen() { return std::mt19937(); }
645
646 template <class Iter>
test()647 TEST_CONSTEXPR_CXX20 bool test() {
648 using T = typename Iter::value_type;
649
650 auto is_neg = [](const T& val) { return to_int(val) < 0; };
651 auto gen = [] { return T{42}; };
652 auto identity = [] (T val) -> T { return val; };
653
654 constexpr int N = 32;
655 std::array<T, N> output;
656 auto out = output.begin();
657 T x{1};
658 T y{3};
659
660 auto simple_in = get_simple_in<Iter>();
661 auto sorted_in = get_sorted_in<Iter>();
662 auto sort_test_in = get_sort_test_in<Iter>();
663
664 using I = Iter;
665
666 test(simple_in, [&](I b, I e) { std::any_of(b, e, is_neg); });
667 test(simple_in, [&](I b, I e) { std::all_of(b, e, is_neg); });
668 test(simple_in, [&](I b, I e) { std::none_of(b, e, is_neg); });
669 test(simple_in, [&](I b, I e) { std::find(b, e, T{1}); });
670 test(simple_in, [&](I b, I e) { std::find_if(b, e, is_neg); });
671 test(simple_in, [&](I b, I e) { std::find_if_not(b, e, is_neg); });
672 // TODO: find_first_of
673 test(simple_in, [&](I b, I e) { std::adjacent_find(b, e); });
674 // TODO: mismatch
675 // TODO: equal
676 // TODO: lexicographical_compare
677 // TODO: partition_point
678 test(sorted_in, [&](I b, I e) { std::lower_bound(b, e, x); });
679 test(sorted_in, [&](I b, I e) { std::upper_bound(b, e, x); });
680 test(sorted_in, [&](I b, I e) { std::equal_range(b, e, x); });
681 test(sorted_in, [&](I b, I e) { std::binary_search(b, e, x); });
682 // `min`, `max` and `minmax` don't use iterators.
683 test(simple_in, [&](I b, I e) { std::min_element(b, e); });
684 test(simple_in, [&](I b, I e) { std::max_element(b, e); });
685 test(simple_in, [&](I b, I e) { std::minmax_element(b, e); });
686 test(simple_in, [&](I b, I e) { std::count(b, e, x); });
687 test(simple_in, [&](I b, I e) { std::count_if(b, e, is_neg); });
688 // TODO: search
689 // TODO: search_n
690 // TODO: find_end
691 // TODO: is_partitioned
692 // TODO: is_sorted
693 // TODO: is_sorted_until
694 // TODO: includes
695 // TODO: is_heap
696 // TODO: is_heap_until
697 // `clamp` doesn't use iterators.
698 // TODO: is_permutation
699 test(simple_in, [&](I b, I e) { std::for_each(b, e, is_neg); });
700 #if TEST_STD_VER > 14
701 test_n(simple_in, [&](I b, size_t n) { std::for_each_n(b, n, is_neg); });
702 #endif
703 test(simple_in, [&](I b, I e) { std::copy(b, e, out); });
704 test_n(simple_in, [&](I b, size_t n) { std::copy_n(b, n, out); });
705 test(simple_in, [&](I b, I e) { std::copy_backward(b, e, out + N); });
706 test(simple_in, [&](I b, I e) { std::copy_if(b, e, out, is_neg); });
707 test(simple_in, [&](I b, I e) { std::move(b, e, out); });
708 test(simple_in, [&](I b, I e) { std::move_backward(b, e, out + N); });
709 test(simple_in, [&](I b, I e) { std::transform(b, e, out, identity); });
710 test(simple_in, [&](I b, I e) { std::generate(b, e, gen); });
711 test_n(simple_in, [&](I b, size_t n) { std::generate_n(b, n, gen); });
712 test(simple_in, [&](I b, I e) { std::remove_copy(b, e, out, x); });
713 test(simple_in, [&](I b, I e) { std::remove_copy_if(b, e, out, is_neg); });
714 test(simple_in, [&](I b, I e) { std::replace(b, e, x, y); });
715 test(simple_in, [&](I b, I e) { std::replace_if(b, e, is_neg, y); });
716 test(simple_in, [&](I b, I e) { std::replace_copy(b, e, out, x, y); });
717 test(simple_in, [&](I b, I e) { std::replace_copy_if(b, e, out, is_neg, y); });
718 // TODO: swap_ranges
719 test(simple_in, [&](I b, I e) { std::reverse_copy(b, e, out); });
720 // TODO: rotate_copy
721 // TODO: sample
722 // TODO: unique_copy
723 // TODO: partition_copy
724 // TODO: partial_sort_copy
725 // TODO: merge
726 // TODO: set_difference
727 // TODO: set_intersection
728 // TODO: set_symmetric_difference
729 // TODO: set_union
730 test(simple_in, [&](I b, I e) { std::remove(b, e, x); });
731 test(simple_in, [&](I b, I e) { std::remove_if(b, e, is_neg); });
732 test(simple_in, [&](I b, I e) { std::reverse(b, e); });
733 // TODO: rotate
734 if (!TEST_IS_CONSTANT_EVALUATED)
735 test(simple_in, [&](I b, I e) { std::shuffle(b, e, rand_gen()); });
736 // TODO: unique
737 test(simple_in, [&](I b, I e) { std::partition(b, e, is_neg); });
738 if (!TEST_IS_CONSTANT_EVALUATED)
739 test(simple_in, [&](I b, I e) { std::stable_partition(b, e, is_neg); });
740 if (!TEST_IS_CONSTANT_EVALUATED)
741 test(sort_test_in, [&](I b, I e) { std::sort(b, e); });
742 if (!TEST_IS_CONSTANT_EVALUATED)
743 test(sort_test_in, [&](I b, I e) { std::stable_sort(b, e); });
744 // TODO: partial_sort
745 // TODO: nth_element
746 // TODO: inplace_merge
747 test(simple_in, [&](I b, I e) { std::make_heap(b, e); });
748 // TODO: push_heap
749 // TODO: pop_heap
750 // TODO: sort_heap
751 test(simple_in, [&](I b, I e) { std::prev_permutation(b, e); });
752 test(simple_in, [&](I b, I e) { std::next_permutation(b, e); });
753
754 // TODO: algorithms in `<numeric>`
755 // TODO: algorithms in `<memory>`
756
757 return true;
758 }
759
test_all()760 void test_all() {
761 test<LifetimeIterator>();
762 #if TEST_STD_VER > 17 // Most algorithms are only `constexpr` starting from C++20.
763 static_assert(test<ConstexprIterator>());
764 #endif
765 }
766
main(int,char **)767 int main(int, char**) {
768 test_all();
769
770 return 0;
771 }
772