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