Index: include/__config =================================================================== --- include/__config +++ include/__config @@ -281,7 +281,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 @@ -825,6 +825,20 @@ #define _LIBCPP_HAS_NO_ATOMIC_HEADER #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 @@ -201,11 +201,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]; } @@ -215,11 +212,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. Index: test/support/noexcept.h =================================================================== --- /dev/null +++ test/support/noexcept.h @@ -0,0 +1,60 @@ +// -*- 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 + +#ifdef _LIBCPP_NO_EXCEPTIONS + +#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 + +// Re-write try/catch with if/else to mimic a similar control flow when testing +// the no-exceptions library variant. The idea is to save as much of the usual +// with-exceptions assertions as possible. This of course does not work when +// there are multiple catch statements, in those cases we have to use the +// _LIBCPP_NO_EXCEPTIONS macro as appropriate; such cases are rare. +#define try if(!setjmp(try_buf)) +#define catch(ex) else + +// Jump back to the catch (now else) clause. +void __libcxx_noexceptions_abort(const char *msg) { + fprintf(stderr, "%s\n", msg); + longjmp(try_buf, 1); +} + +#endif // _LIBCPP_NO_EXCEPTIONS + +// A throw helper macro for the use of tests. +#ifndef _LIBCPP_NO_EXCEPTIONS +#define TEST_THROW(E) throw E +#else +#define TEST_THROW(E) __libcxx_noexceptions_abort("exception") +#endif + +#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));