1 //===----------------------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // UNSUPPORTED: c++03, c++11, c++14
10 // UNSUPPORTED: no-exceptions
11 // `check_assertion.h` requires Unix headers and regex support.
12 // REQUIRES: has-unix-headers
13 // UNSUPPORTED: no-localization
15 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
20 // Check that PSTL algorithms terminate on user-thrown exceptions.
25 #include "check_assertion.h"
26 #include "test_execution_policies.h"
27 #include "test_iterators.h"
30 void assert_non_throwing(F f
) {
31 // We wrap this whole test in EXPECT_STD_TERMINATE because if f() terminates, we want the test to pass,
32 // since this signals proper handling of user exceptions in the PSTL.
33 EXPECT_STD_TERMINATE([&] {
40 // If nothing was thrown, call std::terminate() to pass the EXPECT_STD_TERMINATE assertion.
41 // Otherwise, don't call std::terminate() to fail the assertion.
48 void activate() { active_
= true; }
49 void deactivate() { active_
= false; }
50 bool active() const { return active_
; }
57 struct on_scope_exit
{
58 explicit on_scope_exit(Func func
) : func_(func
) {}
59 ~on_scope_exit() { func_(); }
65 on_scope_exit(Func
) -> on_scope_exit
<Func
>;
67 int main(int, char**) {
68 test_execution_policies([&](auto&& policy
) {
69 int a
[] = {1, 2, 3, 4};
76 // We generate a certain number of "tokens" and we activate exactly one on each iteration. We then
77 // throw in a given operation only when that token is active. That way we check that each argument
78 // of the algorithm is handled properly.
80 for (ThrowToken
& t
: tokens
) {
82 on_scope_exit
_([&] { t
.deactivate(); });
84 auto first1
= util::throw_on_move_iterator(std::begin(a
), tokens
[0].active() ? 1 : -1);
85 auto last1
= util::throw_on_move_iterator(std::end(a
), tokens
[1].active() ? 1 : -1);
86 auto first2
= util::throw_on_move_iterator(std::begin(b
), tokens
[2].active() ? 1 : -1);
87 auto last2
= util::throw_on_move_iterator(std::end(b
), tokens
[3].active() ? 1 : -1);
88 auto dest
= util::throw_on_move_iterator(std::end(storage
), tokens
[4].active() ? 1 : -1);
89 auto maybe_throw
= [](ThrowToken
const& token
, auto f
) {
90 return [&token
, f
](auto... args
) {
98 auto pred
= maybe_throw(tokens
[5], [](int x
) -> bool { return x
% 2 == 0; });
100 // all_of(first, last, pred)
101 assert_non_throwing([=, &policy
] { (void)std::all_of(policy
, std::move(first1
), std::move(last1
), pred
); });
103 // any_of(first, last, pred)
104 assert_non_throwing([=, &policy
] { (void)std::any_of(policy
, std::move(first1
), std::move(last1
), pred
); });
106 // none_of(first, last, pred)
107 assert_non_throwing([=, &policy
] { (void)std::none_of(policy
, std::move(first1
), std::move(last1
), pred
); });
111 // copy(first, last, dest)
112 assert_non_throwing([=, &policy
] {
113 (void)std::copy(policy
, std::move(first1
), std::move(last1
), std::move(dest
));
116 // copy_n(first, n, dest)
117 assert_non_throwing([=, &policy
] { (void)std::copy_n(policy
, std::move(first1
), n
, std::move(dest
)); });
121 auto pred
= maybe_throw(tokens
[5], [](int x
) -> bool { return x
% 2 == 0; });
123 // count(first, last, val)
124 assert_non_throwing([=, &policy
] { (void)std::count(policy
, std::move(first1
), std::move(last1
), val
); });
126 // count_if(first, last, pred)
127 assert_non_throwing([=, &policy
] { (void)std::count_if(policy
, std::move(first1
), std::move(last1
), pred
); });
131 auto binary_pred
= maybe_throw(tokens
[5], [](int x
, int y
) -> bool { return x
== y
; });
133 // equal(first1, last1, first2)
134 assert_non_throwing([=, &policy
] {
135 (void)std::equal(policy
, std::move(first1
), std::move(last1
), std::move(first2
));
138 // equal(first1, last1, first2, binary_pred)
139 assert_non_throwing([=, &policy
] {
140 (void)std::equal(policy
, std::move(first1
), std::move(last1
), std::move(first2
), binary_pred
);
143 // equal(first1, last1, first2, last2)
144 assert_non_throwing([=, &policy
] {
145 (void)std::equal(policy
, std::move(first1
), std::move(last1
), std::move(first2
), std::move(last2
));
148 // equal(first1, last1, first2, last2, binary_pred)
149 assert_non_throwing([=, &policy
] {
151 policy
, std::move(first1
), std::move(last1
), std::move(first2
), std::move(last2
), binary_pred
);
156 // fill(first, last, val)
157 assert_non_throwing([=, &policy
] { (void)std::fill(policy
, std::move(first1
), std::move(last1
), val
); });
159 // fill_n(first, n, val)
160 assert_non_throwing([=, &policy
] { (void)std::fill_n(policy
, std::move(first1
), n
, val
); });
164 auto pred
= maybe_throw(tokens
[5], [](int x
) -> bool { return x
% 2 == 0; });
166 // find(first, last, val)
167 assert_non_throwing([=, &policy
] { (void)std::find(policy
, std::move(first1
), std::move(last1
), val
); });
169 // find_if(first, last, pred)
170 assert_non_throwing([=, &policy
] { (void)std::find_if(policy
, std::move(first1
), std::move(last1
), pred
); });
172 // find_if_not(first, last, pred)
173 assert_non_throwing([=, &policy
] {
174 (void)std::find_if_not(policy
, std::move(first1
), std::move(last1
), pred
);
179 auto func
= maybe_throw(tokens
[5], [](int) {});
181 // for_each(first, last, func)
182 assert_non_throwing([=, &policy
] { (void)std::for_each(policy
, std::move(first1
), std::move(last1
), func
); });
184 // for_each_n(first, n, func)
185 assert_non_throwing([=, &policy
] { (void)std::for_each_n(policy
, std::move(first1
), n
, func
); });
189 auto gen
= maybe_throw(tokens
[5], []() -> int { return 42; });
191 // generate(first, last, func)
192 assert_non_throwing([=, &policy
] { (void)std::generate(policy
, std::move(first1
), std::move(last1
), gen
); });
194 // generate_n(first, n, func)
195 assert_non_throwing([=, &policy
] { (void)std::generate_n(policy
, std::move(first1
), n
, gen
); });
199 auto pred
= maybe_throw(tokens
[5], [](int x
) -> bool { return x
% 2 == 0; });
201 // is_partitioned(first, last, pred)
202 assert_non_throwing([=, &policy
] {
203 (void)std::is_partitioned(policy
, std::move(first1
), std::move(last1
), pred
);
208 auto compare
= maybe_throw(tokens
[5], [](int x
, int y
) -> bool { return x
< y
; });
210 // merge(first1, last1, first2, last2, dest)
211 assert_non_throwing([=, &policy
] {
213 policy
, std::move(first1
), std::move(last1
), std::move(first2
), std::move(last2
), std::move(dest
));
216 // merge(first1, last1, first2, last2, dest, comp)
217 assert_non_throwing([=, &policy
] {
230 // move(first, last, dest)
231 assert_non_throwing([=, &policy
] {
232 (void)std::move(policy
, std::move(first1
), std::move(last1
), std::move(dest
));
237 auto pred
= maybe_throw(tokens
[5], [](int x
) -> bool { return x
% 2 == 0; });
239 // replace_if(first, last, pred, val)
240 assert_non_throwing([=, &policy
] {
241 (void)std::replace_if(policy
, std::move(first1
), std::move(last1
), pred
, val
);
244 // replace(first, last, val1, val2)
245 assert_non_throwing([=, &policy
] {
246 (void)std::replace(policy
, std::move(first1
), std::move(last1
), val
, val
);
249 // replace_copy_if(first, last, dest, pred, val)
250 assert_non_throwing([=, &policy
] {
251 (void)std::replace_copy_if(policy
, std::move(first1
), std::move(last1
), std::move(dest
), pred
, val
);
254 // replace_copy(first, last, dest, val1, val2)
255 assert_non_throwing([=, &policy
] {
256 (void)std::replace_copy(policy
, std::move(first1
), std::move(last1
), std::move(dest
), val
, val
);
261 auto mid1
= util::throw_on_move_iterator(std::begin(a
) + 2, tokens
[5].active() ? 1 : -1);
263 // rotate_copy(first, mid, last, dest)
264 assert_non_throwing([=, &policy
] {
265 (void)std::rotate_copy(policy
, std::move(first1
), std::move(mid1
), std::move(last1
), std::move(dest
));
270 auto compare
= maybe_throw(tokens
[5], [](int x
, int y
) -> bool { return x
< y
; });
273 assert_non_throwing([=, &policy
] { (void)std::sort(policy
, std::move(first1
), std::move(last1
)); });
275 // sort(first, last, comp)
276 assert_non_throwing([=, &policy
] { (void)std::sort(policy
, std::move(first1
), std::move(last1
), compare
); });
278 // stable_sort(first, last)
279 assert_non_throwing([=, &policy
] { (void)std::stable_sort(policy
, std::move(first1
), std::move(last1
)); });
281 // stable_sort(first, last, comp)
282 assert_non_throwing([=, &policy
] {
283 (void)std::stable_sort(policy
, std::move(first1
), std::move(last1
), compare
);
288 auto unary
= maybe_throw(tokens
[5], [](int x
) -> int { return x
* 2; });
289 auto binary
= maybe_throw(tokens
[5], [](int x
, int y
) -> int { return x
* y
; });
291 // transform(first, last, dest, func)
292 assert_non_throwing([=, &policy
] {
293 (void)std::transform(policy
, std::move(first1
), std::move(last1
), std::move(dest
), unary
);
296 // transform(first1, last1, first2, dest, func)
297 assert_non_throwing([=, &policy
] {
298 (void)std::transform(policy
, std::move(first1
), std::move(last1
), std::move(first2
), std::move(dest
), binary
);
303 auto reduction
= maybe_throw(tokens
[5], [](int x
, int y
) -> int { return x
+ y
; });
304 auto transform_unary
= maybe_throw(tokens
[6], [](int x
) -> int { return x
* 2; });
305 auto transform_binary
= maybe_throw(tokens
[6], [](int x
, int y
) -> int { return x
* y
; });
307 // transform_reduce(first1, last1, first2, init)
308 assert_non_throwing([=, &policy
] {
309 (void)std::transform_reduce(policy
, std::move(first1
), std::move(last1
), std::move(first2
), init
);
312 // transform_reduce(first1, last1, init, reduce, transform)
313 assert_non_throwing([=, &policy
] {
314 (void)std::transform_reduce(policy
, std::move(first1
), std::move(last1
), init
, reduction
, transform_unary
);
317 // transform_reduce(first1, last1, first2, init, reduce, transform)
318 assert_non_throwing([=, &policy
] {
319 (void)std::transform_reduce(
320 policy
, std::move(first1
), std::move(last1
), std::move(first2
), init
, reduction
, transform_binary
);
325 auto reduction
= maybe_throw(tokens
[5], [](int x
, int y
) -> int { return x
+ y
; });
327 // reduce(first, last)
328 assert_non_throwing([=, &policy
] { (void)std::reduce(policy
, std::move(first1
), std::move(last1
)); });
330 // reduce(first, last, init)
331 assert_non_throwing([=, &policy
] { (void)std::reduce(policy
, std::move(first1
), std::move(last1
), init
); });
333 // reduce(first, last, init, binop)
334 assert_non_throwing([=, &policy
] {
335 (void)std::reduce(policy
, std::move(first1
), std::move(last1
), init
, reduction
);