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