xref: /llvm-project/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/requirements.compile.pass.cpp (revision f0ea888e01aabcb131a8931b9e1fe1c5212a6cba)
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 // <algorithm>
10 
11 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
12 
13 // template<input_iterator I, sentinel_for<I> S, class T,
14 //          indirectly-binary-left-foldable<T, I> F>
15 //   constexpr see below ranges::fold_left_with_iter(I first, S last, T init, F f);
16 //
17 // template<input_range R, class T, indirectly-binary-left-foldable<T, iterator_t<R>> F>
18 //   constexpr see below ranges::fold_left_with_iter(R&& r, T init, F f);
19 
20 // template<input_iterator I, sentinel_for<I> S, class T,
21 //          indirectly-binary-left-foldable<T, I> F>
22 //   constexpr see below ranges::fold_left(I first, S last, T init, F f);
23 //
24 // template<input_range R, class T, indirectly-binary-left-foldable<T, iterator_t<R>> F>
25 //   constexpr see below ranges::fold_left(R&& r, T init, F f);
26 
27 // Checks that the algorithm requirements reject parameters that don't meet the overloads' constraints.
28 
29 #include <algorithm>
30 #include <concepts>
31 #include <cstddef>
32 #include <functional>
33 #include <iterator>
34 #include <ranges>
35 
36 #include "test_iterators.h"
37 
38 // FIXME(cjdb): deduplicate
39 struct bad_iterator_category {
40   using value_type        = int;
41   using difference_type   = std::ptrdiff_t;
42   using iterator_category = void;
43 
44   value_type operator*() const;
45 
46   bad_iterator_category& operator++();
47   void operator++(int);
48 };
49 
50 // Covers indirectly_readable<I> too
51 template <std::input_or_output_iterator T>
52   requires(!std::input_iterator<T>)
requires_input_iterator()53 void requires_input_iterator() {
54   struct bad_range {
55     T begin();
56     std::unreachable_sentinel_t end();
57   };
58 
59   static_assert(!requires(bad_range r) {
60     std::ranges::fold_left_with_iter(r.begin(), r.end(), std::unreachable_sentinel, 0, std::plus());
61   });
62   static_assert(!requires(bad_range r) { std::ranges::fold_left_with_iter(r, 0, std::plus()); });
63 
64   static_assert(!requires(bad_range r) {
65     std::ranges::fold_left(r.begin(), r.end(), std::unreachable_sentinel, 0, std::plus());
66   });
67 
68   static_assert(!requires(bad_range r) { std::ranges::fold_left(r, 0, std::plus()); });
69 }
70 
71 template <std::equality_comparable S>
72   requires(!std::sentinel_for<int*, S>)
requires_sentinel()73 void requires_sentinel() {
74   static_assert(!requires(S first, S last) { std::ranges::fold_left_with_iter(first, last, 0, std::plus()); });
75   static_assert(!requires(S first, S last) { std::ranges::fold_left(first, last, 0, std::plus()); });
76 }
77 
78 struct non_copy_constructible_callable {
79   non_copy_constructible_callable(non_copy_constructible_callable&&)      = default;
80   non_copy_constructible_callable(non_copy_constructible_callable const&) = delete;
81 
82   int operator()(int, int) const;
83 };
84 
85 template <class F>
86   requires(!std::copy_constructible<F>)
requires_copy_constructible_F()87 void requires_copy_constructible_F() {
88   static_assert(!requires(std::ranges::subrange<int*, int*> r, F f) {
89     std::ranges::fold_left_with_iter(r.begin(), r.end(), 0, std::move(f));
90   });
91   static_assert(!requires(std::ranges::subrange<int*, int*> r, F f) {
92     std::ranges::fold_left_with_iter(r, 0, std::move(f));
93   });
94 
95   static_assert(!requires(std::ranges::subrange<int*, int*> r, F f) {
96     std::ranges::fold_left(r.begin(), r.end(), 0, std::move(f));
97   });
98   static_assert(!requires(std::ranges::subrange<int*, int*> r, F f) { std::ranges::fold_left(r, 0, std::move(f)); });
99 }
100 
101 struct not_invocable_with_lvalue_rhs {
102   int operator()(int, int&&);
103 };
104 
105 template <class F>
106   requires(!std::invocable<F&, int, std::iter_reference_t<int*>>)
requires_raw_invocable()107 void requires_raw_invocable() {
108   static_assert(!requires(std::ranges::subrange<int*, int*> r, F f) {
109     std::ranges::fold_left_with_iter(r.begin(), r.end(), 0, f);
110   });
111   static_assert(!requires(std::ranges::subrange<int*, int*> r, F f) { std::ranges::fold_left_with_iter(r, 0, f); });
112 
113   static_assert(!requires(std::ranges::subrange<int*, int*> r, F f) {
114     std::ranges::fold_left(r.begin(), r.end(), 0, f);
115   });
116   static_assert(!requires(std::ranges::subrange<int*, int*> r, F f) { std::ranges::fold_left(r, 0, f); });
117 }
118 
119 struct S {};
120 
121 struct non_decayable_result {
122   S volatile& operator()(S, S) const;
123 };
124 
125 template <std::invocable<S, std::iter_reference_t<S*>> F>
126   requires(!std::convertible_to<std::invoke_result_t<F&, S, std::iter_reference_t<S*>>,
127                                 std::decay_t<std::invoke_result_t<F&, S, std::iter_reference_t<S*>>>>)
requires_decaying_invoke_result()128 void requires_decaying_invoke_result() {
129   static_assert(!requires(std::ranges::subrange<S*, S*> r, S init, F f) {
130     std::ranges::fold_left_with_iter(r.begin(), r.end(), init, f);
131   });
132   static_assert(!requires(std::ranges::subrange<S*, S*> r, S init, F f) {
133     std::ranges::fold_left_with_iter(r, init, f);
134   });
135 
136   static_assert(!requires(std::ranges::subrange<S*, S*> r, S init, F f) {
137     std::ranges::fold_left(r.begin(), r.end(), init, f);
138   });
139   static_assert(!requires(std::ranges::subrange<S*, S*> r, S init, F f) { std::ranges::fold_left(r, init, f); });
140 }
141 
142 struct non_movable {
143   non_movable(int);
144   non_movable(non_movable&&) = delete;
145 
146   int apply(non_movable const&) const;
147 };
148 
149 template <class T>
150   requires(!std::movable<T>)
requires_movable_init()151 void requires_movable_init() {
152   static_assert(!requires(std::ranges::subrange<T*, T*> r, T init) {
153     std::ranges::fold_left_with_iter(r.begin(), r.end(), init, &T::apply);
154   });
155   static_assert(!requires(std::ranges::subrange<T*, T*> r, T init) {
156     std::ranges::fold_left_with_iter(r, init, &T::apply);
157   });
158   static_assert(!requires(std::ranges::subrange<T*, T*> r, T init) {
159     std::ranges::fold_left(r.begin(), r.end(), init, &T::apply);
160   });
161   static_assert(!requires(std::ranges::subrange<T*, T*> r, T init) { std::ranges::fold_left(r, init, &T::apply); });
162 }
163 
164 struct result_not_movable_after_decay {
165   result_not_movable_after_decay(int);
166   result_not_movable_after_decay(result_not_movable_after_decay&&) = delete;
167   result_not_movable_after_decay(result_not_movable_after_decay const&);
168 
169   friend result_not_movable_after_decay const& operator+(int, result_not_movable_after_decay const&);
170   friend result_not_movable_after_decay const& operator+(result_not_movable_after_decay const&, int);
171   friend result_not_movable_after_decay const&
172   operator+(result_not_movable_after_decay const&, result_not_movable_after_decay const&);
173 };
174 
175 template <class T>
176   requires(!std::movable<T>)
requires_movable_decayed()177 void requires_movable_decayed() {
178   static_assert(!requires(std::ranges::subrange<T*, T*> r) {
179     std::ranges::fold_left_with_iter(r.begin(), r.end(), 0, std::plus());
180   });
181   static_assert(!requires(std::ranges::subrange<T*, T*> r) { std::ranges::fold_left_with_iter(r, 0, std::plus()); });
182 
183   static_assert(!requires(std::ranges::subrange<T*, T*> r) {
184     std::ranges::fold_left(r.begin(), r.end(), 0, T::apply);
185   });
186   static_assert(!requires(std::ranges::subrange<T*, T*> r) { std::ranges::fold_left(r, 0, std::plus()); });
187 }
188 
189 struct not_convertible_to_int {
190   friend int operator+(not_convertible_to_int, not_convertible_to_int);
191   friend int operator+(not_convertible_to_int, int);
192   friend int operator+(int, not_convertible_to_int);
193 };
194 
195 template <class T>
196   requires(!std::convertible_to<T, int>)
requires_init_is_convertible_to_decayed()197 void requires_init_is_convertible_to_decayed() {
198   static_assert(!requires(std::ranges::subrange<int*, int*> r, T init) {
199     std::ranges::fold_left_with_iter(r.begin(), r.end(), init, std::plus());
200   });
201   static_assert(!requires(std::ranges::subrange<int*, int*> r, T init) {
202     std::ranges::fold_left_with_iter(r, init, std::plus());
203   });
204 
205   static_assert(!requires(std::ranges::subrange<int*, int*> r, T init) {
206     std::ranges::fold_left(r.begin(), r.end(), init, std::plus());
207   });
208   static_assert(!requires(std::ranges::subrange<int*, int*> r, T init) {
209     std::ranges::fold_left(r, init, std::plus());
210   });
211 }
212 
213 struct not_invocable_with_decayed {
214   not_invocable_with_decayed(int);
215   friend not_invocable_with_decayed& operator+(int, not_invocable_with_decayed&);
216   friend not_invocable_with_decayed& operator+(not_invocable_with_decayed&, int);
217   friend not_invocable_with_decayed& operator+(not_invocable_with_decayed volatile&, not_invocable_with_decayed&);
218 };
219 
220 template <class T>
221   requires(!std::invocable<std::plus<>&, T, T&>)
requires_invocable_with_decayed()222 void requires_invocable_with_decayed() {
223   static_assert(!requires(std::ranges::subrange<T*, T*> r, int init) {
224     std::ranges::fold_left_with_iter(r.begin(), r.end(), init, std::plus());
225   });
226   static_assert(!requires(std::ranges::subrange<T*, T*> r, int init) {
227     std::ranges::fold_left_with_iter(r, init, std::plus());
228   });
229 
230   static_assert(!requires(std::ranges::subrange<T*, T*> r, int init) {
231     std::ranges::fold_left(r.begin(), r.end(), init, std::plus());
232   });
233   static_assert(!requires(std::ranges::subrange<T*, T*> r, int init) { std::ranges::fold_left(r, init, std::plus()); });
234 }
235 
236 struct not_assignable_to_decayed {
237   not_assignable_to_decayed();
238   not_assignable_to_decayed(not_assignable_to_decayed&);
239   not_assignable_to_decayed(not_assignable_to_decayed const&);
240   not_assignable_to_decayed(not_assignable_to_decayed volatile&);
241   not_assignable_to_decayed(not_assignable_to_decayed const volatile&);
242   friend not_assignable_to_decayed volatile& operator+(not_assignable_to_decayed, not_assignable_to_decayed);
243 };
244 
245 template <class T>
246   requires(!std::assignable_from<T&, T volatile&>)
requires_assignable_from_invoke_result()247 void requires_assignable_from_invoke_result() {
248   static_assert(!requires(std::ranges::subrange<T*, T*> r, T init) {
249     std::ranges::fold_left_with_iter(r.begin(), r.end(), init, std::plus());
250   });
251   static_assert(!requires(std::ranges::subrange<T*, T*> r, T init) {
252     std::ranges::fold_left_with_iter(r, init, std::plus());
253   });
254 
255   static_assert(!requires(std::ranges::subrange<T*, T*> r, T init) {
256     std::ranges::fold_left(r.begin(), r.end(), init, std::plus());
257   });
258   static_assert(!requires(std::ranges::subrange<T*, T*> r, T init) { std::ranges::fold_left(r, init, std::plus()); });
259 }
260 
test()261 void test() {
262   requires_input_iterator<bad_iterator_category>();
263   requires_sentinel<cpp17_input_iterator<int*>>();
264   requires_copy_constructible_F<non_copy_constructible_callable>();
265   requires_raw_invocable<not_invocable_with_lvalue_rhs>();
266   requires_decaying_invoke_result<non_decayable_result>();
267   requires_movable_init<non_movable>();
268   requires_movable_decayed<result_not_movable_after_decay>();
269   requires_init_is_convertible_to_decayed<not_convertible_to_int>();
270   requires_invocable_with_decayed<not_invocable_with_decayed>();
271   requires_assignable_from_invoke_result<not_assignable_to_decayed>();
272 }
273