xref: /llvm-project/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp (revision bd3f5a4bd3d9d7ee8ae801c24c5081073b20abd4)
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 // UNSUPPORTED: !has-unix-headers, no-localization
13 
14 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
15 
16 // <algorithm>
17 // <numeric>
18 //
19 // Check that PSTL algorithms terminate on user-thrown exceptions.
20 
21 #include <algorithm>
22 #include <numeric>
23 
24 #include "check_assertion.h"
25 #include "test_execution_policies.h"
26 #include "test_iterators.h"
27 
28 template <class F>
29 void assert_non_throwing(F f) {
30   // We wrap this whole test in EXPECT_STD_TERMINATE because if f() terminates, we want the test to pass,
31   // since this signals proper handling of user exceptions in the PSTL.
32   EXPECT_STD_TERMINATE([&] {
33     bool threw = false;
34     try {
35       f();
36     } catch (...) {
37       threw = true;
38     }
39     // If nothing was thrown, call std::terminate() to pass the EXPECT_STD_TERMINATE assertion.
40     // Otherwise, don't call std::terminate() to fail the assertion.
41     if (!threw)
42       std::terminate();
43   });
44 }
45 
46 struct ThrowToken {
47   void activate() { active_ = true; }
48   void deactivate() { active_ = false; }
49   bool active() const { return active_; }
50 
51 private:
52   bool active_{false};
53 };
54 
55 template <class Func>
56 struct on_scope_exit {
57   explicit on_scope_exit(Func func) : func_(func) {}
58   ~on_scope_exit() { func_(); }
59 
60 private:
61   Func func_;
62 };
63 template <class Func>
64 on_scope_exit(Func) -> on_scope_exit<Func>;
65 
66 int main(int, char**) {
67   test_execution_policies([&](auto&& policy) {
68     int a[] = {1, 2, 3, 4};
69     int b[] = {1, 2, 3};
70     int n   = 2;
71     int storage[999];
72     int val  = 99;
73     int init = 1;
74 
75     // We generate a certain number of "tokens" and we activate exactly one on each iteration. We then
76     // throw in a given operation only when that token is active. That way we check that each argument
77     // of the algorithm is handled properly.
78     ThrowToken tokens[7];
79     for (ThrowToken& t : tokens) {
80       t.activate();
81       on_scope_exit _([&] { t.deactivate(); });
82 
83       auto first1      = util::throw_on_move_iterator(std::begin(a), tokens[0].active() ? 1 : -1);
84       auto last1       = util::throw_on_move_iterator(std::end(a), tokens[1].active() ? 1 : -1);
85       auto first2      = util::throw_on_move_iterator(std::begin(b), tokens[2].active() ? 1 : -1);
86       auto last2       = util::throw_on_move_iterator(std::end(b), tokens[3].active() ? 1 : -1);
87       auto dest        = util::throw_on_move_iterator(std::end(storage), tokens[4].active() ? 1 : -1);
88       auto maybe_throw = [](ThrowToken const& token, auto f) {
89         return [&token, f](auto... args) {
90           if (token.active())
91             throw 1;
92           return f(args...);
93         };
94       };
95 
96       {
97         auto pred = maybe_throw(tokens[5], [](int x) -> bool { return x % 2 == 0; });
98 
99         // all_of(first, last, pred)
100         assert_non_throwing([=, &policy] { (void)std::all_of(policy, std::move(first1), std::move(last1), pred); });
101 
102         // any_of(first, last, pred)
103         assert_non_throwing([=, &policy] { (void)std::any_of(policy, std::move(first1), std::move(last1), pred); });
104 
105         // none_of(first, last, pred)
106         assert_non_throwing([=, &policy] { (void)std::none_of(policy, std::move(first1), std::move(last1), pred); });
107       }
108 
109       {
110         // copy(first, last, dest)
111         assert_non_throwing([=, &policy] {
112           (void)std::copy(policy, std::move(first1), std::move(last1), std::move(dest));
113         });
114 
115         // copy_n(first, n, dest)
116         assert_non_throwing([=, &policy] { (void)std::copy_n(policy, std::move(first1), n, std::move(dest)); });
117       }
118 
119       {
120         auto pred = maybe_throw(tokens[5], [](int x) -> bool { return x % 2 == 0; });
121 
122         // count(first, last, val)
123         assert_non_throwing([=, &policy] { (void)std::count(policy, std::move(first1), std::move(last1), val); });
124 
125         // count_if(first, last, pred)
126         assert_non_throwing([=, &policy] { (void)std::count_if(policy, std::move(first1), std::move(last1), pred); });
127       }
128 
129       {
130         auto binary_pred = maybe_throw(tokens[5], [](int x, int y) -> bool { return x == y; });
131 
132         // equal(first1, last1, first2)
133         assert_non_throwing([=, &policy] {
134           (void)std::equal(policy, std::move(first1), std::move(last1), std::move(first2));
135         });
136 
137         // equal(first1, last1, first2, binary_pred)
138         assert_non_throwing([=, &policy] {
139           (void)std::equal(policy, std::move(first1), std::move(last1), std::move(first2), binary_pred);
140         });
141 
142         // equal(first1, last1, first2, last2)
143         assert_non_throwing([=, &policy] {
144           (void)std::equal(policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2));
145         });
146 
147         // equal(first1, last1, first2, last2, binary_pred)
148         assert_non_throwing([=, &policy] {
149           (void)std::equal(
150               policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), binary_pred);
151         });
152       }
153 
154       {
155         // fill(first, last, val)
156         assert_non_throwing([=, &policy] { (void)std::fill(policy, std::move(first1), std::move(last1), val); });
157 
158         // fill_n(first, n, val)
159         assert_non_throwing([=, &policy] { (void)std::fill_n(policy, std::move(first1), n, val); });
160       }
161 
162       {
163         auto pred = maybe_throw(tokens[5], [](int x) -> bool { return x % 2 == 0; });
164 
165         // find(first, last, val)
166         assert_non_throwing([=, &policy] { (void)std::find(policy, std::move(first1), std::move(last1), val); });
167 
168         // find_if(first, last, pred)
169         assert_non_throwing([=, &policy] { (void)std::find_if(policy, std::move(first1), std::move(last1), pred); });
170 
171         // find_if_not(first, last, pred)
172         assert_non_throwing([=, &policy] {
173           (void)std::find_if_not(policy, std::move(first1), std::move(last1), pred);
174         });
175       }
176 
177       {
178         auto func = maybe_throw(tokens[5], [](int) {});
179 
180         // for_each(first, last, func)
181         assert_non_throwing([=, &policy] { (void)std::for_each(policy, std::move(first1), std::move(last1), func); });
182 
183         // for_each_n(first, n, func)
184         assert_non_throwing([=, &policy] { (void)std::for_each_n(policy, std::move(first1), n, func); });
185       }
186 
187       {
188         auto gen = maybe_throw(tokens[5], []() -> int { return 42; });
189 
190         // generate(first, last, func)
191         assert_non_throwing([=, &policy] { (void)std::generate(policy, std::move(first1), std::move(last1), gen); });
192 
193         // generate_n(first, n, func)
194         assert_non_throwing([=, &policy] { (void)std::generate_n(policy, std::move(first1), n, gen); });
195       }
196 
197       {
198         auto pred = maybe_throw(tokens[5], [](int x) -> bool { return x % 2 == 0; });
199 
200         // is_partitioned(first, last, pred)
201         assert_non_throwing([=, &policy] {
202           (void)std::is_partitioned(policy, std::move(first1), std::move(last1), pred);
203         });
204       }
205 
206       {
207         auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
208 
209         // merge(first1, last1, first2, last2, dest)
210         assert_non_throwing([=, &policy] {
211           (void)std::merge(
212               policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), std::move(dest));
213         });
214 
215         // merge(first1, last1, first2, last2, dest, comp)
216         assert_non_throwing([=, &policy] {
217           (void)std::merge(
218               policy,
219               std::move(first1),
220               std::move(last1),
221               std::move(first2),
222               std::move(last2),
223               std::move(dest),
224               compare);
225         });
226       }
227 
228       {
229         // move(first, last, dest)
230         assert_non_throwing([=, &policy] {
231           (void)std::move(policy, std::move(first1), std::move(last1), std::move(dest));
232         });
233       }
234 
235       {
236         auto pred = maybe_throw(tokens[5], [](int x) -> bool { return x % 2 == 0; });
237 
238         // replace_if(first, last, pred, val)
239         assert_non_throwing([=, &policy] {
240           (void)std::replace_if(policy, std::move(first1), std::move(last1), pred, val);
241         });
242 
243         // replace(first, last, val1, val2)
244         assert_non_throwing([=, &policy] {
245           (void)std::replace(policy, std::move(first1), std::move(last1), val, val);
246         });
247 
248         // replace_copy_if(first, last, dest, pred, val)
249         assert_non_throwing([=, &policy] {
250           (void)std::replace_copy_if(policy, std::move(first1), std::move(last1), std::move(dest), pred, val);
251         });
252 
253         // replace_copy(first, last, dest, val1, val2)
254         assert_non_throwing([=, &policy] {
255           (void)std::replace_copy(policy, std::move(first1), std::move(last1), std::move(dest), val, val);
256         });
257       }
258 
259       {
260         auto mid1 = util::throw_on_move_iterator(std::begin(a) + 2, tokens[5].active() ? 1 : -1);
261 
262         // rotate_copy(first, mid, last, dest)
263         assert_non_throwing([=, &policy] {
264           (void)std::rotate_copy(policy, std::move(first1), std::move(mid1), std::move(last1), std::move(dest));
265         });
266       }
267 
268       {
269         auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
270 
271         // sort(first, last)
272         assert_non_throwing([=, &policy] { (void)std::sort(policy, std::move(first1), std::move(last1)); });
273 
274         // sort(first, last, comp)
275         assert_non_throwing([=, &policy] { (void)std::sort(policy, std::move(first1), std::move(last1), compare); });
276 
277         // stable_sort(first, last)
278         assert_non_throwing([=, &policy] { (void)std::stable_sort(policy, std::move(first1), std::move(last1)); });
279 
280         // stable_sort(first, last, comp)
281         assert_non_throwing([=, &policy] {
282           (void)std::stable_sort(policy, std::move(first1), std::move(last1), compare);
283         });
284       }
285 
286       {
287         auto unary  = maybe_throw(tokens[5], [](int x) -> int { return x * 2; });
288         auto binary = maybe_throw(tokens[5], [](int x, int y) -> int { return x * y; });
289 
290         // transform(first, last, dest, func)
291         assert_non_throwing([=, &policy] {
292           (void)std::transform(policy, std::move(first1), std::move(last1), std::move(dest), unary);
293         });
294 
295         // transform(first1, last1, first2, dest, func)
296         assert_non_throwing([=, &policy] {
297           (void)std::transform(policy, std::move(first1), std::move(last1), std::move(first2), std::move(dest), binary);
298         });
299       }
300 
301       {
302         auto reduction        = maybe_throw(tokens[5], [](int x, int y) -> int { return x + y; });
303         auto transform_unary  = maybe_throw(tokens[6], [](int x) -> int { return x * 2; });
304         auto transform_binary = maybe_throw(tokens[6], [](int x, int y) -> int { return x * y; });
305 
306         // transform_reduce(first1, last1, first2, init)
307         assert_non_throwing([=, &policy] {
308           (void)std::transform_reduce(policy, std::move(first1), std::move(last1), std::move(first2), init);
309         });
310 
311         // transform_reduce(first1, last1, init, reduce, transform)
312         assert_non_throwing([=, &policy] {
313           (void)std::transform_reduce(policy, std::move(first1), std::move(last1), init, reduction, transform_unary);
314         });
315 
316         // transform_reduce(first1, last1, first2, init, reduce, transform)
317         assert_non_throwing([=, &policy] {
318           (void)std::transform_reduce(
319               policy, std::move(first1), std::move(last1), std::move(first2), init, reduction, transform_binary);
320         });
321       }
322 
323       {
324         auto reduction = maybe_throw(tokens[5], [](int x, int y) -> int { return x + y; });
325 
326         // reduce(first, last)
327         assert_non_throwing([=, &policy] { (void)std::reduce(policy, std::move(first1), std::move(last1)); });
328 
329         // reduce(first, last, init)
330         assert_non_throwing([=, &policy] { (void)std::reduce(policy, std::move(first1), std::move(last1), init); });
331 
332         // reduce(first, last, init, binop)
333         assert_non_throwing([=, &policy] {
334           (void)std::reduce(policy, std::move(first1), std::move(last1), init, reduction);
335         });
336       }
337     }
338   });
339 }
340