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