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
39struct 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
51template <std::input_or_output_iterator T>
52 requires(!std::input_iterator<T>)
53void 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
71template <std::equality_comparable S>
72 requires(!std::sentinel_for<int*, S>)
73void 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
78struct 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
85template <class F>
86 requires(!std::copy_constructible<F>)
87void 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
101struct not_invocable_with_lvalue_rhs {
102 int operator()(int, int&&);
103};
104
105template <class F>
106 requires(!std::invocable<F&, int, std::iter_reference_t<int*>>)
107void 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
119struct S {};
120
121struct non_decayable_result {
122 S volatile& operator()(S, S) const;
123};
124
125template <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*>>>>)
128void 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
142struct non_movable {
143 non_movable(int);
144 non_movable(non_movable&&) = delete;
145
146 int apply(non_movable const&) const;
147};
148
149template <class T>
150 requires(!std::movable<T>)
151void 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
164struct 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
175template <class T>
176 requires(!std::movable<T>)
177void 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
189struct 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
195template <class T>
196 requires(!std::convertible_to<T, int>)
197void 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
213struct 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
220template <class T>
221 requires(!std::invocable<std::plus<>&, T, T&>)
222void 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
236struct 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
245template <class T>
246 requires(!std::assignable_from<T&, T volatile&>)
247void 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
261void 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

source code of libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/requirements.compile.pass.cpp