Index: include/__config =================================================================== --- include/__config +++ include/__config @@ -283,7 +283,7 @@ typedef __char32_t char32_t; #endif -#if !(__has_feature(cxx_exceptions)) +#if !(__has_feature(cxx_exceptions)) && !defined(_LIBCPP_NO_EXCEPTIONS) #define _LIBCPP_NO_EXCEPTIONS #endif @@ -835,6 +835,20 @@ #define _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK #endif +#ifdef _LIBCPP_NO_EXCEPTIONS +_LIBCPP_NORETURN _LIBCPP_WEAK void __libcxx_noexceptions_abort(const char *msg); +extern "C++" { +inline void __libcxx_noexceptions_report(const char *msg = nullptr) +{ + if (__libcxx_noexceptions_abort) + __libcxx_noexceptions_abort(msg); +} +} +#define _LIBCPP_THROW(E, MSG) __libcxx_noexceptions_report(MSG) +#else // !_LIBCPP_NO_EXCEPTIONS +#define _LIBCPP_THROW(E, MSG) throw E +#endif // _LIBCPP_NO_EXCEPTIONS + #endif // __cplusplus #endif // _LIBCPP_CONFIG Index: include/array =================================================================== --- include/array +++ include/array @@ -202,11 +202,8 @@ array<_Tp, _Size>::at(size_type __n) { if (__n >= _Size) -#ifndef _LIBCPP_NO_EXCEPTIONS - throw out_of_range("array::at"); -#else - assert(!"array::at out_of_range"); -#endif + _LIBCPP_THROW(out_of_range("array::at out_of_range"), + "array::at out_of_range"); return __elems_[__n]; } @@ -216,11 +213,8 @@ array<_Tp, _Size>::at(size_type __n) const { if (__n >= _Size) -#ifndef _LIBCPP_NO_EXCEPTIONS - throw out_of_range("array::at"); -#else - assert(!"array::at out_of_range"); -#endif + _LIBCPP_THROW(out_of_range("array::at out_of_range"), + "array::at out_of_range"); return __elems_[__n]; } Index: test/std/containers/sequences/array/at.pass.cpp =================================================================== --- test/std/containers/sequences/array/at.pass.cpp +++ test/std/containers/sequences/array/at.pass.cpp @@ -7,7 +7,6 @@ // //===----------------------------------------------------------------------===// -// XFAIL: libcpp-no-exceptions // // reference operator[] (size_type) @@ -19,6 +18,7 @@ #include #include "test_macros.h" +#include "noexcept.h" // std::array is explicitly allowed to be initialized with A a = { init-list };. // Disable the missing braces warning for this reason. @@ -40,8 +40,8 @@ r2 = 7.5; assert(c.back() == 7.5); - try { (void) c.at(3); } - catch (const std::out_of_range &) {} + TEST_TRY { (void) c.at(3); } + TEST_CATCH (const std::out_of_range &) {} } { typedef double T; @@ -53,8 +53,8 @@ C::const_reference r2 = c.at(2); assert(r2 == 3.5); - try { (void) c.at(3); } - catch (const std::out_of_range &) {} + TEST_TRY { (void) c.at(3); } + TEST_CATCH (const std::out_of_range &) {} } #if TEST_STD_VER > 11 Index: test/support/noexcept.h =================================================================== --- /dev/null +++ test/support/noexcept.h @@ -0,0 +1,61 @@ +// -*- C++ -*- +//===----------------------------- noexcept.h -----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef NOEXCEPT_H +#define NOEXCEPT_H + +// These helper macros enable writing tests for the standard library exceptions +// (i.e. tests that check the library throws where it should) while at the same +// time catering for the no-exceptions library variant. In the no-exceptions +// variant, the macros translate into a setjmp/longjmp duo that mimic the +// control flow of exceptions. The goal here is to allow the tests to verify +// that the library called __libcxx_noexceptions_abort() at the point where it +// originally threw. Note that the longjmp (and hence the setjmp) is necessary +// here as we cannot allow the execution to proceed past the point where the +// library detects an error. Finally, this simple translation does not work when +// tests use multiple catch statements, in those cases we have to use the +// _LIBCPP_NO_EXCEPTIONS macro and exclude the additional catch statements. +#ifndef _LIBCPP_NO_EXCEPTIONS +#define TEST_TRY try +#define TEST_CATCH(E) catch (E) +#define TEST_THROW(E) throw E +#else +#define TEST_TRY if (!setjmp(try_buf)) +#define TEST_CATCH(E) else +#define TEST_THROW(E) __libcxx_noexceptions_abort("exception") + +#include +#include +#include "test_macros.h" + +#ifndef _LIBCPP_HAS_NO_THREADS +# if TEST_STD_VER >= 11 +# define TLS_SPEC thread_local +# elif defined(_LIBCPP_MSVC) +# define TLS_SPEC __declspec(thread) +# else +# define TLS_SPEC __thread +# endif +#else +# define TLS_SPEC +#endif + +// Some tests launch multiple threads, in which case we need to make sure that +// try_buf is maintained per-thread, otherwise setjmp()/longjmp() will attempt +// to jump between threads! +TLS_SPEC jmp_buf try_buf; +#undef TLS_SPEC + +void __libcxx_noexceptions_abort(const char *msg) { + fprintf(stderr, "%s\n", msg); + longjmp(try_buf, 1); +} +#endif // _LIBCPP_NO_EXCEPTIONS + +#endif // NOEXCEPT_H Index: test/support/test_allocator.h =================================================================== --- test/support/test_allocator.h +++ test/support/test_allocator.h @@ -65,13 +65,8 @@ pointer allocate(size_type n, const void* = 0) { assert(data_ >= 0); - if (time_to_throw >= throw_after) { -#ifndef _LIBCPP_NO_EXCEPTIONS - throw std::bad_alloc(); -#else - std::terminate(); -#endif - } + if (time_to_throw >= throw_after) + _LIBCPP_THROW(std::bad_alloc(), "std::bad_alloc"); ++time_to_throw; ++alloc_count; return (pointer)::operator new(n * sizeof(T)); @@ -125,13 +120,8 @@ pointer allocate(size_type n, const void* = 0) { assert(data_ >= 0); - if (time_to_throw >= throw_after) { -#ifndef _LIBCPP_NO_EXCEPTIONS - throw std::bad_alloc(); -#else - std::terminate(); -#endif - } + if (time_to_throw >= throw_after) + _LIBCPP_THROW(std::bad_alloc(), "std::bad_alloc"); ++time_to_throw; ++alloc_count; return (pointer)::operator new (n * sizeof(T));