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 #ifndef SUPPORT_EXCEPTION_SAFETY_HELPERS_H 10 #define SUPPORT_EXCEPTION_SAFETY_HELPERS_H 11 12 #include <cassert> 13 #include <cstddef> 14 #include <functional> 15 #include <utility> 16 #include "test_macros.h" 17 18 #if !defined(TEST_HAS_NO_EXCEPTIONS) 19 template <int N> 20 struct ThrowingCopy { 21 static bool throwing_enabled; 22 static int created_by_copying; 23 static int destroyed; 24 int x = 0; // Allows distinguishing between different instances. 25 26 ThrowingCopy() = default; ThrowingCopyThrowingCopy27 ThrowingCopy(int value) : x(value) {} ~ThrowingCopyThrowingCopy28 ~ThrowingCopy() { 29 ++destroyed; 30 } 31 ThrowingCopyThrowingCopy32 ThrowingCopy(const ThrowingCopy& other) : x(other.x) { 33 ++created_by_copying; 34 if (throwing_enabled && created_by_copying == N) { 35 throw -1; 36 } 37 } 38 39 // Defined to silence GCC warnings. For test purposes, only copy construction is considered `created_by_copying`. 40 ThrowingCopy& operator=(const ThrowingCopy& other) { 41 x = other.x; 42 return *this; 43 } 44 45 friend bool operator==(const ThrowingCopy& lhs, const ThrowingCopy& rhs) { return lhs.x == rhs.x; } 46 friend bool operator<(const ThrowingCopy& lhs, const ThrowingCopy& rhs) { return lhs.x < rhs.x; } 47 resetThrowingCopy48 static void reset() { 49 created_by_copying = destroyed = 0; 50 } 51 }; 52 53 template <int N> 54 bool ThrowingCopy<N>::throwing_enabled = true; 55 template <int N> 56 int ThrowingCopy<N>::created_by_copying = 0; 57 template <int N> 58 int ThrowingCopy<N>::destroyed = 0; 59 60 template <int N> 61 struct std::hash<ThrowingCopy<N>> { 62 std::size_t operator()(const ThrowingCopy<N>& value) const { 63 return value.x; 64 } 65 }; 66 67 template <int ThrowOn, int Size, class Func> 68 void test_exception_safety_throwing_copy(Func&& func) { 69 using T = ThrowingCopy<ThrowOn>; 70 T::reset(); 71 T in[Size]; 72 73 try { 74 func(in, in + Size); 75 assert(false); // The function call above should throw. 76 77 } catch (int) { 78 assert(T::created_by_copying == ThrowOn); 79 assert(T::destroyed == ThrowOn - 1); // No destructor call for the partially-constructed element. 80 } 81 } 82 83 // Destroys the container outside the user callback to avoid destroying extra elements upon throwing (which would 84 // complicate asserting that the expected number of elements was destroyed). 85 template <class Container, int ThrowOn, int Size, class Func> 86 void test_exception_safety_throwing_copy_container(Func&& func) { 87 using T = ThrowingCopy<ThrowOn>; 88 T::throwing_enabled = false; 89 T in[Size]; 90 Container c(in, in + Size); 91 T::throwing_enabled = true; 92 T::reset(); 93 94 try { 95 func(std::move(c)); 96 assert(false); // The function call above should throw. 97 98 } catch (int) { 99 assert(T::created_by_copying == ThrowOn); 100 assert(T::destroyed == ThrowOn - 1); // No destructor call for the partially-constructed element. 101 } 102 } 103 104 #endif // !defined(TEST_HAS_NO_EXCEPTIONS) 105 106 #endif // SUPPORT_EXCEPTION_SAFETY_HELPERS_H 107