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