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>) |
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>) |
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>) |
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*>>) |
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*>>>>) |
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>) |
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>) |
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>) |
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&>) |
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&>) |
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 | |
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 | |