xref: /llvm-project/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp (revision a0cdd32b79318fc45e07bc0cef7e57308b1166ed)
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 // 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
14 
15 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
16 
17 // <algorithm>
18 // <numeric>
19 //
20 // Check that PSTL algorithms terminate on user-thrown exceptions.
21 
22 #include <algorithm>
23 #include <numeric>
24 
25 #include "check_assertion.h"
26 #include "test_execution_policies.h"
27 #include "test_iterators.h"
28 
29 template <class F>
assert_non_throwing(F f)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([&] {
34     bool threw = false;
35     try {
36       f();
37     } catch (...) {
38       threw = true;
39     }
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.
42     if (!threw)
43       std::terminate();
44   });
45 }
46 
47 struct ThrowToken {
activateThrowToken48   void activate() { active_ = true; }
deactivateThrowToken49   void deactivate() { active_ = false; }
activeThrowToken50   bool active() const { return active_; }
51 
52 private:
53   bool active_{false};
54 };
55 
56 template <class Func>
57 struct on_scope_exit {
on_scope_exiton_scope_exit58   explicit on_scope_exit(Func func) : func_(func) {}
~on_scope_exiton_scope_exit59   ~on_scope_exit() { func_(); }
60 
61 private:
62   Func func_;
63 };
64 template <class Func>
65 on_scope_exit(Func) -> on_scope_exit<Func>;
66 
main(int,char **)67 int main(int, char**) {
68   test_execution_policies([&](auto&& policy) {
69     int a[] = {1, 2, 3, 4};
70     int b[] = {1, 2, 3};
71     int n   = 2;
72     int storage[999];
73     int val  = 99;
74     int init = 1;
75 
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.
79     ThrowToken tokens[7];
80     for (ThrowToken& t : tokens) {
81       t.activate();
82       on_scope_exit _([&] { t.deactivate(); });
83 
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) {
91           if (token.active())
92             throw 1;
93           return f(args...);
94         };
95       };
96 
97       {
98         auto pred = maybe_throw(tokens[5], [](int x) -> bool { return x % 2 == 0; });
99 
100         // all_of(first, last, pred)
101         assert_non_throwing([=, &policy] { (void)std::all_of(policy, std::move(first1), std::move(last1), pred); });
102 
103         // any_of(first, last, pred)
104         assert_non_throwing([=, &policy] { (void)std::any_of(policy, std::move(first1), std::move(last1), pred); });
105 
106         // none_of(first, last, pred)
107         assert_non_throwing([=, &policy] { (void)std::none_of(policy, std::move(first1), std::move(last1), pred); });
108       }
109 
110       {
111         // copy(first, last, dest)
112         assert_non_throwing([=, &policy] {
113           (void)std::copy(policy, std::move(first1), std::move(last1), std::move(dest));
114         });
115 
116         // copy_n(first, n, dest)
117         assert_non_throwing([=, &policy] { (void)std::copy_n(policy, std::move(first1), n, std::move(dest)); });
118       }
119 
120       {
121         auto pred = maybe_throw(tokens[5], [](int x) -> bool { return x % 2 == 0; });
122 
123         // count(first, last, val)
124         assert_non_throwing([=, &policy] { (void)std::count(policy, std::move(first1), std::move(last1), val); });
125 
126         // count_if(first, last, pred)
127         assert_non_throwing([=, &policy] { (void)std::count_if(policy, std::move(first1), std::move(last1), pred); });
128       }
129 
130       {
131         auto binary_pred = maybe_throw(tokens[5], [](int x, int y) -> bool { return x == y; });
132 
133         // equal(first1, last1, first2)
134         assert_non_throwing([=, &policy] {
135           (void)std::equal(policy, std::move(first1), std::move(last1), std::move(first2));
136         });
137 
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);
141         });
142 
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));
146         });
147 
148         // equal(first1, last1, first2, last2, binary_pred)
149         assert_non_throwing([=, &policy] {
150           (void)std::equal(
151               policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), binary_pred);
152         });
153       }
154 
155       {
156         // fill(first, last, val)
157         assert_non_throwing([=, &policy] { (void)std::fill(policy, std::move(first1), std::move(last1), val); });
158 
159         // fill_n(first, n, val)
160         assert_non_throwing([=, &policy] { (void)std::fill_n(policy, std::move(first1), n, val); });
161       }
162 
163       {
164         auto pred = maybe_throw(tokens[5], [](int x) -> bool { return x % 2 == 0; });
165 
166         // find(first, last, val)
167         assert_non_throwing([=, &policy] { (void)std::find(policy, std::move(first1), std::move(last1), val); });
168 
169         // find_if(first, last, pred)
170         assert_non_throwing([=, &policy] { (void)std::find_if(policy, std::move(first1), std::move(last1), pred); });
171 
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);
175         });
176       }
177 
178       {
179         auto func = maybe_throw(tokens[5], [](int) {});
180 
181         // for_each(first, last, func)
182         assert_non_throwing([=, &policy] { (void)std::for_each(policy, std::move(first1), std::move(last1), func); });
183 
184         // for_each_n(first, n, func)
185         assert_non_throwing([=, &policy] { (void)std::for_each_n(policy, std::move(first1), n, func); });
186       }
187 
188       {
189         auto gen = maybe_throw(tokens[5], []() -> int { return 42; });
190 
191         // generate(first, last, func)
192         assert_non_throwing([=, &policy] { (void)std::generate(policy, std::move(first1), std::move(last1), gen); });
193 
194         // generate_n(first, n, func)
195         assert_non_throwing([=, &policy] { (void)std::generate_n(policy, std::move(first1), n, gen); });
196       }
197 
198       {
199         auto pred = maybe_throw(tokens[5], [](int x) -> bool { return x % 2 == 0; });
200 
201         // is_partitioned(first, last, pred)
202         assert_non_throwing([=, &policy] {
203           (void)std::is_partitioned(policy, std::move(first1), std::move(last1), pred);
204         });
205       }
206 
207       {
208         auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
209 
210         // merge(first1, last1, first2, last2, dest)
211         assert_non_throwing([=, &policy] {
212           (void)std::merge(
213               policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), std::move(dest));
214         });
215 
216         // merge(first1, last1, first2, last2, dest, comp)
217         assert_non_throwing([=, &policy] {
218           (void)std::merge(
219               policy,
220               std::move(first1),
221               std::move(last1),
222               std::move(first2),
223               std::move(last2),
224               std::move(dest),
225               compare);
226         });
227       }
228 
229       {
230         // move(first, last, dest)
231         assert_non_throwing([=, &policy] {
232           (void)std::move(policy, std::move(first1), std::move(last1), std::move(dest));
233         });
234       }
235 
236       {
237         auto pred = maybe_throw(tokens[5], [](int x) -> bool { return x % 2 == 0; });
238 
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);
242         });
243 
244         // replace(first, last, val1, val2)
245         assert_non_throwing([=, &policy] {
246           (void)std::replace(policy, std::move(first1), std::move(last1), val, val);
247         });
248 
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);
252         });
253 
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);
257         });
258       }
259 
260       {
261         auto mid1 = util::throw_on_move_iterator(std::begin(a) + 2, tokens[5].active() ? 1 : -1);
262 
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));
266         });
267       }
268 
269       {
270         auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
271 
272         // sort(first, last)
273         assert_non_throwing([=, &policy] { (void)std::sort(policy, std::move(first1), std::move(last1)); });
274 
275         // sort(first, last, comp)
276         assert_non_throwing([=, &policy] { (void)std::sort(policy, std::move(first1), std::move(last1), compare); });
277 
278         // stable_sort(first, last)
279         assert_non_throwing([=, &policy] { (void)std::stable_sort(policy, std::move(first1), std::move(last1)); });
280 
281         // stable_sort(first, last, comp)
282         assert_non_throwing([=, &policy] {
283           (void)std::stable_sort(policy, std::move(first1), std::move(last1), compare);
284         });
285       }
286 
287       {
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; });
290 
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);
294         });
295 
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);
299         });
300       }
301 
302       {
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; });
306 
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);
310         });
311 
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);
315         });
316 
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);
321         });
322       }
323 
324       {
325         auto reduction = maybe_throw(tokens[5], [](int x, int y) -> int { return x + y; });
326 
327         // reduce(first, last)
328         assert_non_throwing([=, &policy] { (void)std::reduce(policy, std::move(first1), std::move(last1)); });
329 
330         // reduce(first, last, init)
331         assert_non_throwing([=, &policy] { (void)std::reduce(policy, std::move(first1), std::move(last1), init); });
332 
333         // reduce(first, last, init, binop)
334         assert_non_throwing([=, &policy] {
335           (void)std::reduce(policy, std::move(first1), std::move(last1), init, reduction);
336         });
337       }
338     }
339   });
340 }
341