341 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++1410// UNSUPPORTED: no-exceptions11// `check_assertion.h` requires Unix headers and regex support.12// REQUIRES: has-unix-headers13// UNSUPPORTED: no-localization14 15// UNSUPPORTED: libcpp-has-no-incomplete-pstl16 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 29template <class F>30void 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 47struct ThrowToken {48 void activate() { active_ = true; }49 void deactivate() { active_ = false; }50 bool active() const { return active_; }51 52private:53 bool active_{false};54};55 56template <class Func>57struct on_scope_exit {58 explicit on_scope_exit(Func func) : func_(func) {}59 ~on_scope_exit() { func_(); }60 61private:62 Func func_;63};64template <class Func>65on_scope_exit(Func) -> on_scope_exit<Func>;66 67int 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 then77 // throw in a given operation only when that token is active. That way we check that each argument78 // 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