| 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 | |