xref: /llvm-project/libcxx/test/std/algorithms/robust_against_proxy_iterators_lifetime_bugs.pass.cpp (revision 09e3a360581dc36d0820d3fb6da9bd7cfed87b5d)
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 #include <type_traits>
20 #include <utility>
21 
22 #include "test_macros.h"
23 
24 // This file contains checks for lifetime issues across all the classic algorithms. It uses two complementary
25 // approaches:
26 // - runtime checks using a proxy iterator that tracks the lifetime of itself and its objects to catch potential
27 //   lifetime issues;
28 // - `constexpr` checks using a `constexpr`-friendly proxy iterator that catch undefined behavior.
29 
30 // A random-access proxy iterator that tracks the lifetime of itself and its `value_type` and `reference` objects to
31 // prevent potential lifetime issues in algorithms.
32 //
33 // This class cannot be `constexpr` because its cache is a static variable. The cache cannot be provided as
34 // a constructor parameter because `LifetimeIterator` has to be default-constructible.
35 class LifetimeIterator {
36   // The cache simply tracks addresses of the local variables.
37   class LifetimeCache {
38     std::set<const void*> cache_;
39 
40   public:
41     bool contains(const void* ptr) const { return cache_.find(ptr) != cache_.end(); }
42 
43     void insert(const void* ptr) {
44       assert(!contains(ptr));
45       cache_.insert(ptr);
46     }
47 
48     void erase(const void* ptr) {
49       assert(contains(ptr));
50       cache_.erase(ptr);
51     }
52   };
53 
54  public:
55   struct Value {
56     int i_;
57     bool moved_from_ = false; // Check for double moves and reads after moving.
58 
59     Value() { lifetime_cache.insert(this); }
60     Value(int i) : i_(i) { lifetime_cache.insert(this); }
61     ~Value() { lifetime_cache.erase(this); }
62 
63     Value(const Value& rhs) : i_(rhs.i_) {
64       assert(lifetime_cache.contains(&rhs));
65       assert(!rhs.moved_from_);
66 
67       lifetime_cache.insert(this);
68     }
69 
70     Value(Value&& rhs) noexcept : i_(rhs.i_) {
71       assert(lifetime_cache.contains(&rhs));
72 
73       assert(!rhs.moved_from_);
74       rhs.moved_from_ = true;
75 
76       // It's ok if it throws -- since it's a test, terminating the program is acceptable.
77       lifetime_cache.insert(this);
78     }
79 
80     Value& operator=(const Value& rhs) {
81       assert(lifetime_cache.contains(this) && lifetime_cache.contains(&rhs));
82       assert(!rhs.moved_from_);
83 
84       i_ = rhs.i_;
85       moved_from_ = false;
86 
87       return *this;
88     }
89 
90     Value& operator=(Value&& rhs) noexcept {
91       assert(lifetime_cache.contains(this) && lifetime_cache.contains(&rhs));
92 
93       assert(!rhs.moved_from_);
94       rhs.moved_from_ = true;
95 
96       i_ = rhs.i_;
97       moved_from_ = false;
98 
99       return *this;
100     }
101 
102     friend bool operator<(const Value& lhs, const Value& rhs) {
103       assert(lifetime_cache.contains(&lhs) && lifetime_cache.contains(&rhs));
104       assert(!lhs.moved_from_ && !rhs.moved_from_);
105 
106       return lhs.i_ < rhs.i_;
107     }
108 
109     friend bool operator==(const Value& lhs, const Value& rhs) {
110       assert(lifetime_cache.contains(&lhs) && lifetime_cache.contains(&rhs));
111       assert(!lhs.moved_from_ && !rhs.moved_from_);
112 
113       return lhs.i_ == rhs.i_;
114     }
115 
116   };
117 
118   struct Reference {
119     Value* v_;
120     bool moved_from_ = false; // Check for double moves and reads after moving.
121 
122     Reference(Value& v) : v_(&v) {
123       lifetime_cache.insert(this);
124     }
125 
126     ~Reference() {
127       lifetime_cache.erase(this);
128     }
129 
130     Reference(const Reference& rhs) : v_(rhs.v_) {
131       assert(lifetime_cache.contains(&rhs));
132       assert(!rhs.moved_from_);
133 
134       lifetime_cache.insert(this);
135     }
136 
137     Reference(Reference&& rhs) noexcept : v_(rhs.v_) {
138       assert(lifetime_cache.contains(&rhs));
139 
140       assert(!rhs.moved_from_);
141       rhs.moved_from_ = true;
142 
143       lifetime_cache.insert(this);
144     }
145 
146     Reference& operator=(const Reference& rhs) {
147       assert(lifetime_cache.contains(this) && lifetime_cache.contains(&rhs));
148       assert(!rhs.moved_from_);
149 
150       *v_ = *rhs.v_;
151       moved_from_ = false;
152 
153       return *this;
154     }
155 
156     Reference& operator=(Reference&& rhs) noexcept {
157       assert(lifetime_cache.contains(this) && lifetime_cache.contains(&rhs));
158 
159       assert(!rhs.moved_from_);
160       rhs.moved_from_ = true;
161 
162       *v_ = *rhs.v_;
163       moved_from_ = false;
164 
165       return *this;
166     }
167 
168     operator Value() const {
169       assert(lifetime_cache.contains(this));
170       assert(!moved_from_);
171 
172       return *v_;
173     }
174 
175     Reference& operator=(Value v) {
176       assert(lifetime_cache.contains(this));
177       assert(!moved_from_);
178 
179       *v_ = v;
180       moved_from_ = false;
181 
182       return *this;
183     }
184 
185     friend bool operator<(const Reference& lhs, const Reference& rhs) {
186       assert(lifetime_cache.contains(&lhs) && lifetime_cache.contains(&rhs));
187       assert(!lhs.moved_from_ && !rhs.moved_from_);
188 
189       return *lhs.v_ < *rhs.v_;
190     }
191 
192     friend bool operator==(const Reference& lhs, const Reference& rhs) {
193       assert(lifetime_cache.contains(&lhs) && lifetime_cache.contains(&rhs));
194       assert(!lhs.moved_from_ && !rhs.moved_from_);
195 
196       return *lhs.v_ == *rhs.v_;
197     }
198 
199     friend void swap(Reference lhs, Reference rhs) {
200       assert(lifetime_cache.contains(&lhs) && lifetime_cache.contains(&rhs));
201       assert(!lhs.moved_from_ && !rhs.moved_from_);
202 
203       std::swap(*(lhs.v_), *(rhs.v_));
204     }
205   };
206 
207   using difference_type   = int;
208   using value_type        = Value;
209   using reference         = Reference;
210   using pointer           = void;
211   using iterator_category = std::random_access_iterator_tag;
212 
213   Value* ptr_ = nullptr;
214   bool moved_from_ = false; // Check for double moves and reads after moving.
215 
216   LifetimeIterator() = default;
217   LifetimeIterator(Value* ptr) : ptr_(ptr) {}
218 
219   LifetimeIterator(const LifetimeIterator& rhs) : ptr_(rhs.ptr_) {
220     assert(!rhs.moved_from_);
221   }
222 
223   LifetimeIterator& operator=(const LifetimeIterator& rhs) {
224     assert(!rhs.moved_from_);
225 
226     ptr_ = rhs.ptr_;
227     moved_from_ = false;
228 
229     return *this;
230   }
231 
232   LifetimeIterator(LifetimeIterator&& rhs) noexcept : ptr_(rhs.ptr_) {
233     assert(!rhs.moved_from_);
234     rhs.moved_from_ = true;
235     rhs.ptr_ = nullptr;
236   }
237 
238   LifetimeIterator& operator=(LifetimeIterator&& rhs) noexcept {
239     assert(!rhs.moved_from_);
240     rhs.moved_from_ = true;
241     moved_from_ = false;
242 
243     ptr_ = rhs.ptr_;
244     rhs.ptr_ = nullptr;
245 
246     return *this;
247   }
248 
249   Reference operator*() const {
250     assert(!moved_from_);
251     return Reference(*ptr_);
252   }
253 
254   LifetimeIterator& operator++() {
255     assert(!moved_from_);
256 
257     ++ptr_;
258     return *this;
259   }
260 
261   LifetimeIterator operator++(int) {
262     assert(!moved_from_);
263 
264     auto tmp = *this;
265     ++*this;
266     return tmp;
267   }
268 
269   friend bool operator==(const LifetimeIterator& lhs, const LifetimeIterator& rhs) {
270     assert(!lhs.moved_from_ && !rhs.moved_from_);
271     return lhs.ptr_ == rhs.ptr_;
272   }
273   friend bool operator!=(const LifetimeIterator& lhs, const LifetimeIterator& rhs) {
274     assert(!lhs.moved_from_ && !rhs.moved_from_);
275     return lhs.ptr_ != rhs.ptr_;
276   }
277 
278   LifetimeIterator& operator--() {
279     assert(!moved_from_);
280 
281     --ptr_;
282     return *this;
283   }
284 
285   LifetimeIterator operator--(int) {
286     assert(!moved_from_);
287 
288     auto tmp = *this;
289     --*this;
290     return tmp;
291   }
292 
293   LifetimeIterator& operator+=(difference_type n) {
294     assert(!moved_from_);
295 
296     ptr_ += n;
297     return *this;
298   }
299 
300   LifetimeIterator& operator-=(difference_type n) {
301     assert(!moved_from_);
302 
303     ptr_ -= n;
304     return *this;
305   }
306 
307   Reference operator[](difference_type i) const {
308     assert(!moved_from_);
309     return Reference(*(ptr_ + i));
310   }
311 
312   friend bool operator<(const LifetimeIterator& lhs, const LifetimeIterator& rhs) {
313     assert(!lhs.moved_from_ && !rhs.moved_from_);
314     return lhs.ptr_ < rhs.ptr_;
315   }
316 
317   friend bool operator>(const LifetimeIterator& lhs, const LifetimeIterator& rhs) {
318     assert(!lhs.moved_from_ && !rhs.moved_from_);
319     return lhs.ptr_ > rhs.ptr_;
320   }
321 
322   friend bool operator<=(const LifetimeIterator& lhs, const LifetimeIterator& rhs) {
323     assert(!lhs.moved_from_ && !rhs.moved_from_);
324     return lhs.ptr_ <= rhs.ptr_;
325   }
326 
327   friend bool operator>=(const LifetimeIterator& lhs, const LifetimeIterator& rhs) {
328     assert(!lhs.moved_from_ && !rhs.moved_from_);
329     return lhs.ptr_ >= rhs.ptr_;
330   }
331 
332   friend LifetimeIterator operator+(const LifetimeIterator& lhs, difference_type n) {
333     assert(!lhs.moved_from_);
334     return LifetimeIterator(lhs.ptr_ + n);
335   }
336 
337   friend LifetimeIterator operator+(difference_type n, const LifetimeIterator& lhs) {
338     assert(!lhs.moved_from_);
339     return LifetimeIterator(n + lhs.ptr_);
340   }
341 
342   friend LifetimeIterator operator-(const LifetimeIterator& lhs, difference_type n) {
343     assert(!lhs.moved_from_);
344     return LifetimeIterator(lhs.ptr_ - n);
345   }
346 
347   friend difference_type operator-(LifetimeIterator lhs, LifetimeIterator rhs) {
348     assert(!lhs.moved_from_ && !rhs.moved_from_);
349     return static_cast<int>(lhs.ptr_ - rhs.ptr_);
350   }
351 
352   static LifetimeCache lifetime_cache;
353 };
354 
355 LifetimeIterator::LifetimeCache LifetimeIterator::lifetime_cache;
356 
357 #if TEST_STD_VER > 17
358 // A constexpr-friendly proxy iterator to check for undefined behavior in algorithms (since undefined behavior is
359 // statically caught in `constexpr` context).
360 class ConstexprIterator {
361  public:
362   struct Reference {
363     int* v_;
364     bool moved_from_ = false; // Check for double moves and reads after moving.
365 
366     constexpr Reference(int& v) : v_(&v) { }
367 
368     constexpr Reference(const Reference& rhs) = default;
369     constexpr Reference& operator=(const Reference& rhs) {
370       assert(!rhs.moved_from_);
371       v_ = rhs.v_;
372       moved_from_ = false;
373 
374       return *this;
375     }
376 
377     constexpr Reference(Reference&& rhs) noexcept : v_(rhs.v_) {
378       assert(!rhs.moved_from_);
379       rhs.moved_from_ = true;
380     }
381 
382     constexpr Reference& operator=(Reference&& rhs) noexcept {
383       assert(!rhs.moved_from_);
384       rhs.moved_from_ = true;
385       moved_from_ = false;
386 
387       v_ = rhs.v_;
388       return *this;
389     }
390 
391     constexpr operator int() const {
392       assert(!moved_from_);
393       return *v_;
394     }
395 
396     constexpr Reference& operator=(int v) {
397       *v_ = v;
398       moved_from_ = false;
399 
400       return *this;
401     }
402 
403     friend constexpr bool operator<(const Reference& lhs, const Reference& rhs) {
404       assert(!lhs.moved_from_ && !rhs.moved_from_);
405       return *lhs.v_ < *rhs.v_;
406     }
407 
408     friend constexpr bool operator==(const Reference& lhs, const Reference& rhs) {
409       assert(!lhs.moved_from_ && !rhs.moved_from_);
410       return *lhs.v_ == *rhs.v_;
411     }
412 
413     friend constexpr void swap(Reference lhs, Reference rhs) {
414       assert(!lhs.moved_from_ && !rhs.moved_from_);
415       std::swap(*(lhs.v_), *(rhs.v_));
416     }
417   };
418 
419   using difference_type   = int;
420   using value_type        = int;
421   using reference         = Reference;
422   using pointer           = void;
423   using iterator_category = std::random_access_iterator_tag;
424 
425   int* ptr_ = nullptr;
426   bool moved_from_ = false; // Check for double moves and reads after moving.
427 
428   constexpr ConstexprIterator() = default;
429   constexpr ConstexprIterator(int* ptr) : ptr_(ptr) {}
430 
431   constexpr ConstexprIterator(const ConstexprIterator& rhs) : ptr_(rhs.ptr_) {
432     assert(!rhs.moved_from_);
433   }
434 
435   constexpr ConstexprIterator& operator=(const ConstexprIterator& rhs) {
436     assert(!rhs.moved_from_);
437 
438     ptr_ = rhs.ptr_;
439     moved_from_ = false;
440 
441     return *this;
442   }
443 
444   constexpr ConstexprIterator(ConstexprIterator&& rhs) noexcept : ptr_(rhs.ptr_) {
445     assert(!rhs.moved_from_);
446     rhs.moved_from_ = true;
447     rhs.ptr_ = nullptr;
448   }
449 
450   constexpr ConstexprIterator& operator=(ConstexprIterator&& rhs) noexcept {
451     assert(!rhs.moved_from_);
452     rhs.moved_from_ = true;
453     moved_from_ = false;
454 
455     ptr_ = rhs.ptr_;
456     rhs.ptr_ = nullptr;
457 
458     return *this;
459   }
460 
461   constexpr Reference operator*() const {
462     assert(!moved_from_);
463     return Reference(*ptr_);
464   }
465 
466   constexpr ConstexprIterator& operator++() {
467     assert(!moved_from_);
468 
469     ++ptr_;
470     return *this;
471   }
472 
473   constexpr ConstexprIterator operator++(int) {
474     assert(!moved_from_);
475 
476     auto tmp = *this;
477     ++*this;
478     return tmp;
479   }
480 
481   friend constexpr bool operator==(const ConstexprIterator& lhs, const ConstexprIterator& rhs) {
482     assert(!lhs.moved_from_ && !rhs.moved_from_);
483     return lhs.ptr_ == rhs.ptr_;
484   }
485 
486   friend constexpr bool operator!=(const ConstexprIterator& lhs, const ConstexprIterator& rhs) {
487     assert(!lhs.moved_from_ && !rhs.moved_from_);
488     return lhs.ptr_ != rhs.ptr_;
489   }
490 
491   constexpr ConstexprIterator& operator--() {
492     assert(!moved_from_);
493 
494     --ptr_;
495     return *this;
496   }
497 
498   constexpr ConstexprIterator operator--(int) {
499     assert(!moved_from_);
500 
501     auto tmp = *this;
502     --*this;
503     return tmp;
504   }
505 
506   constexpr ConstexprIterator& operator+=(difference_type n) {
507     assert(!moved_from_);
508 
509     ptr_ += n;
510     return *this;
511   }
512 
513   constexpr ConstexprIterator& operator-=(difference_type n) {
514     assert(!moved_from_);
515 
516     ptr_ -= n;
517     return *this;
518   }
519 
520   constexpr Reference operator[](difference_type i) const {
521     return Reference(*(ptr_ + i));
522   }
523 
524   friend constexpr auto operator<=>(const ConstexprIterator& lhs, const ConstexprIterator& rhs) {
525     assert(!lhs.moved_from_ && !rhs.moved_from_);
526     return lhs.ptr_ <=> rhs.ptr_;
527   }
528 
529   friend constexpr ConstexprIterator operator+(const ConstexprIterator& lhs, difference_type n) {
530     assert(!lhs.moved_from_);
531     return ConstexprIterator(lhs.ptr_ + n);
532   }
533 
534   friend constexpr ConstexprIterator operator+(difference_type n, const ConstexprIterator& lhs) {
535     assert(!lhs.moved_from_);
536     return ConstexprIterator(n + lhs.ptr_);
537   }
538 
539   friend constexpr ConstexprIterator operator-(const ConstexprIterator& lhs, difference_type n) {
540     assert(!lhs.moved_from_);
541     return ConstexprIterator(lhs.ptr_ - n);
542   }
543 
544   friend constexpr difference_type operator-(ConstexprIterator lhs, ConstexprIterator rhs) {
545     assert(!lhs.moved_from_ && !rhs.moved_from_);
546     return static_cast<int>(lhs.ptr_ - rhs.ptr_);
547   }
548 };
549 
550 #endif // TEST_STD_VER > 17
551 
552 template <class T, std::size_t StorageSize = 32>
553 class Input {
554   std::size_t size_ = 0;
555   T values_[StorageSize] = {};
556 
557 public:
558   template <std::size_t N>
559   TEST_CONSTEXPR_CXX20 Input(std::array<T, N> from) {
560     static_assert(N <= StorageSize, "");
561 
562     std::copy(from.begin(), from.end(), begin());
563     size_ = N;
564   }
565 
566   TEST_CONSTEXPR_CXX20 T* begin() { return values_; }
567   TEST_CONSTEXPR_CXX20 T* end() { return values_ + size_; }
568   TEST_CONSTEXPR_CXX20 std::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>
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>
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>
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, std::size_t N, class 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, std::size_t N, class 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 
641 constexpr int to_int(int x) { return x; }
642 int to_int(LifetimeIterator::Value x) { return x.i_; }
643 
644 std::mt19937 rand_gen() { return std::mt19937(); }
645 
646 template <class Iter>
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) { (void) std::any_of(b, e, is_neg); });
667   test(simple_in, [&](I b, I e) { (void) std::all_of(b, e, is_neg); });
668   test(simple_in, [&](I b, I e) { (void) std::none_of(b, e, is_neg); });
669   test(simple_in, [&](I b, I e) { (void) std::find(b, e, T{1}); });
670   test(simple_in, [&](I b, I e) { (void) std::find_if(b, e, is_neg); });
671   test(simple_in, [&](I b, I e) { (void) std::find_if_not(b, e, is_neg); });
672   // TODO: find_first_of
673   test(simple_in, [&](I b, I e) { (void) 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) { (void) std::lower_bound(b, e, x); });
679   test(sorted_in, [&](I b, I e) { (void) std::upper_bound(b, e, x); });
680   test(sorted_in, [&](I b, I e) { (void) std::equal_range(b, e, x); });
681   test(sorted_in, [&](I b, I e) { (void) std::binary_search(b, e, x); });
682   // `min`, `max` and `minmax` don't use iterators.
683   test(simple_in, [&](I b, I e) { (void) std::min_element(b, e); });
684   test(simple_in, [&](I b, I e) { (void) std::max_element(b, e); });
685   test(simple_in, [&](I b, I e) { (void) std::minmax_element(b, e); });
686   test(simple_in, [&](I b, I e) { (void) std::count(b, e, x); });
687   test(simple_in, [&](I b, I e) { (void) 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) { (void) std::for_each(b, e, is_neg); });
700 #if TEST_STD_VER > 14
701   test_n(simple_in, [&](I b, std::size_t n) { (void) std::for_each_n(b, n, is_neg); });
702 #endif
703   test(simple_in, [&](I b, I e) { (void) std::copy(b, e, out); });
704   test_n(simple_in, [&](I b, std::size_t n) { (void) std::copy_n(b, n, out); });
705   test(simple_in, [&](I b, I e) { (void) std::copy_backward(b, e, out + N); });
706   test(simple_in, [&](I b, I e) { (void) std::copy_if(b, e, out, is_neg); });
707   test(simple_in, [&](I b, I e) { (void) std::move(b, e, out); });
708   test(simple_in, [&](I b, I e) { (void) std::move_backward(b, e, out + N); });
709   test(simple_in, [&](I b, I e) { (void) std::transform(b, e, out, identity); });
710   test(simple_in, [&](I b, I e) { (void) std::generate(b, e, gen); });
711   test_n(simple_in, [&](I b, std::size_t n) { (void) std::generate_n(b, n, gen); });
712   test(simple_in, [&](I b, I e) { (void) std::remove_copy(b, e, out, x); });
713   test(simple_in, [&](I b, I e) { (void) std::remove_copy_if(b, e, out, is_neg); });
714   test(simple_in, [&](I b, I e) { (void) std::replace(b, e, x, y); });
715   test(simple_in, [&](I b, I e) { (void) std::replace_if(b, e, is_neg, y); });
716   test(simple_in, [&](I b, I e) { (void) std::replace_copy(b, e, out, x, y); });
717   test(simple_in, [&](I b, I e) { (void) std::replace_copy_if(b, e, out, is_neg, y); });
718   // TODO: swap_ranges
719   test(simple_in, [&](I b, I e) { (void) 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) { (void) std::remove(b, e, x); });
731   test(simple_in, [&](I b, I e) { (void) std::remove_if(b, e, is_neg); });
732   test(simple_in, [&](I b, I e) { (void) std::reverse(b, e); });
733   // TODO: rotate
734   if (!TEST_IS_CONSTANT_EVALUATED)
735     test(simple_in, [&](I b, I e) { (void) std::shuffle(b, e, rand_gen()); });
736   // TODO: unique
737   test(simple_in, [&](I b, I e) { (void) std::partition(b, e, is_neg); });
738   if (!TEST_IS_CONSTANT_EVALUATED)
739     test(simple_in, [&](I b, I e) { (void) std::stable_partition(b, e, is_neg); });
740   if (!TEST_IS_CONSTANT_EVALUATED)
741     test(sort_test_in, [&](I b, I e) { (void) std::sort(b, e); });
742   if (!TEST_IS_CONSTANT_EVALUATED)
743     test(sort_test_in, [&](I b, I e) { (void) 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) { (void) 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) { (void) std::prev_permutation(b, e); });
752   test(simple_in, [&](I b, I e) { (void) std::next_permutation(b, e); });
753 
754   // TODO: algorithms in `<numeric>`
755   // TODO: algorithms in `<memory>`
756 
757   return true;
758 }
759 
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 
767 int main(int, char**) {
768   test_all();
769 
770   return 0;
771 }
772