forked from OSchip/llvm-project
[libcxx] Fix using the vcruntime ABI with _HAS_EXCEPTIONS=0 defined
_HAS_EXCEPTIONS=0 allows disabling the exception parts of the MS STL and vcruntime, and e.g. compiler-rt/lib/fuzzer sets this define (to work around issues with MS STL). If using libc++ instead of MS STL, this define previously broke the libc++ headers. If _HAS_EXCEPTIONS is set to 0, the vcruntime_exception.h header doesn't define the ABI base class std::exception. If no exceptions are going to be thrown, this probably is fine (although it also breaks using subclasses of it as regular objects that aren't thrown), but it requires ifdeffing out all subclasses of all exception/error derived objects (which are sprinkled throughout the headers). Instead, libc++ will supply an ABI compatible definition when _HAS_EXCEPTIONS is set to 0, which will make the class hierarchies complete. In this build configuration, one can still create instances of exception subclasses, and those objects will be ABI incompatible with the ones from when _HAS_EXCEPTIONS isn't defined to 0 - but one may argue that's a pathological/self-imposed problem in that case. Reviewed By: #libc, ldionne Differential Revision: https://reviews.llvm.org/D103947
This commit is contained in:
parent
8de51375f1
commit
56a34451e1
|
@ -85,8 +85,10 @@ template <class E> void rethrow_if_nested(const E& e);
|
|||
#include <type_traits>
|
||||
#include <version>
|
||||
|
||||
// <vcruntime_exception.h> defines its own std::exception and std::bad_exception types,
|
||||
// which we use in order to be ABI-compatible with other STLs on Windows.
|
||||
#if defined(_LIBCPP_ABI_VCRUNTIME)
|
||||
#include <vcruntime_exception.h>
|
||||
# include <vcruntime_exception.h>
|
||||
#endif
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
|
@ -96,24 +98,66 @@ template <class E> void rethrow_if_nested(const E& e);
|
|||
namespace std // purposefully not using versioning namespace
|
||||
{
|
||||
|
||||
#if !defined(_LIBCPP_ABI_VCRUNTIME)
|
||||
class _LIBCPP_EXCEPTION_ABI exception
|
||||
{
|
||||
public:
|
||||
_LIBCPP_INLINE_VISIBILITY exception() _NOEXCEPT {}
|
||||
_LIBCPP_INLINE_VISIBILITY exception(const exception&) _NOEXCEPT = default;
|
||||
#if defined(_LIBCPP_ABI_VCRUNTIME) && (!defined(_HAS_EXCEPTIONS) || _HAS_EXCEPTIONS != 0)
|
||||
// The std::exception class was already included above, but we're explicit about this condition here for clarity.
|
||||
|
||||
virtual ~exception() _NOEXCEPT;
|
||||
virtual const char* what() const _NOEXCEPT;
|
||||
#elif defined(_LIBCPP_ABI_VCRUNTIME) && _HAS_EXCEPTIONS == 0
|
||||
// However, <vcruntime_exception.h> does not define std::exception and std::bad_exception
|
||||
// when _HAS_EXCEPTIONS == 0.
|
||||
//
|
||||
// Since libc++ still wants to provide the std::exception hierarchy even when _HAS_EXCEPTIONS == 0
|
||||
// (after all those are simply types like any other), we define an ABI-compatible version
|
||||
// of the VCRuntime std::exception and std::bad_exception types in that mode.
|
||||
|
||||
struct __std_exception_data {
|
||||
char const* _What;
|
||||
bool _DoFree;
|
||||
};
|
||||
|
||||
class _LIBCPP_EXCEPTION_ABI bad_exception
|
||||
: public exception
|
||||
{
|
||||
class exception { // base of all library exceptions
|
||||
public:
|
||||
_LIBCPP_INLINE_VISIBILITY bad_exception() _NOEXCEPT {}
|
||||
virtual ~bad_exception() _NOEXCEPT;
|
||||
virtual const char* what() const _NOEXCEPT;
|
||||
exception() _NOEXCEPT : _Data() {}
|
||||
|
||||
explicit exception(char const* __message) _NOEXCEPT : _Data() {
|
||||
_Data._What = __message;
|
||||
_Data._DoFree = true;
|
||||
}
|
||||
|
||||
exception(exception const&) _NOEXCEPT {}
|
||||
|
||||
exception& operator=(exception const&) _NOEXCEPT { return *this; }
|
||||
|
||||
virtual ~exception() _NOEXCEPT {}
|
||||
|
||||
virtual char const* what() const _NOEXCEPT { return _Data._What ? _Data._What : "Unknown exception"; }
|
||||
|
||||
private:
|
||||
__std_exception_data _Data;
|
||||
};
|
||||
|
||||
class bad_exception : public exception {
|
||||
public:
|
||||
bad_exception() _NOEXCEPT : exception("bad exception") {}
|
||||
};
|
||||
|
||||
#else // !defined(_LIBCPP_ABI_VCRUNTIME)
|
||||
// On all other platforms, we define our own std::exception and std::bad_exception types
|
||||
// regardless of whether exceptions are turned on as a language feature.
|
||||
|
||||
class _LIBCPP_EXCEPTION_ABI exception {
|
||||
public:
|
||||
_LIBCPP_INLINE_VISIBILITY exception() _NOEXCEPT {}
|
||||
_LIBCPP_INLINE_VISIBILITY exception(const exception&) _NOEXCEPT = default;
|
||||
|
||||
virtual ~exception() _NOEXCEPT;
|
||||
virtual const char* what() const _NOEXCEPT;
|
||||
};
|
||||
|
||||
class _LIBCPP_EXCEPTION_ABI bad_exception : public exception {
|
||||
public:
|
||||
_LIBCPP_INLINE_VISIBILITY bad_exception() _NOEXCEPT {}
|
||||
virtual ~bad_exception() _NOEXCEPT;
|
||||
virtual const char* what() const _NOEXCEPT;
|
||||
};
|
||||
#endif // !_LIBCPP_ABI_VCRUNTIME
|
||||
|
||||
|
|
|
@ -146,7 +146,25 @@ typedef void (*new_handler)();
|
|||
_LIBCPP_FUNC_VIS new_handler set_new_handler(new_handler) _NOEXCEPT;
|
||||
_LIBCPP_FUNC_VIS new_handler get_new_handler() _NOEXCEPT;
|
||||
|
||||
#endif // !_LIBCPP_ABI_VCRUNTIME
|
||||
#elif defined(_HAS_EXCEPTIONS) && _HAS_EXCEPTIONS == 0 // !_LIBCPP_ABI_VCRUNTIME
|
||||
|
||||
// When _HAS_EXCEPTIONS == 0, these complete definitions are needed,
|
||||
// since they would normally be provided in vcruntime_exception.h
|
||||
class bad_alloc : public exception {
|
||||
public:
|
||||
bad_alloc() noexcept : exception("bad allocation") {}
|
||||
|
||||
private:
|
||||
friend class bad_array_new_length;
|
||||
|
||||
bad_alloc(char const* const __message) noexcept : exception(__message) {}
|
||||
};
|
||||
|
||||
class bad_array_new_length : public bad_alloc {
|
||||
public:
|
||||
bad_array_new_length() noexcept : bad_alloc("bad array new length") {}
|
||||
};
|
||||
#endif // defined(_LIBCPP_ABI_VCRUNTIME) && defined(_HAS_EXCEPTIONS) &&_HAS_EXCEPTIONS == 0
|
||||
|
||||
_LIBCPP_NORETURN _LIBCPP_FUNC_VIS void __throw_bad_alloc(); // not in C++ spec
|
||||
|
||||
|
|
|
@ -370,6 +370,30 @@ class _LIBCPP_EXCEPTION_ABI bad_typeid
|
|||
|
||||
#endif // defined(_LIBCPP_ABI_VCRUNTIME)
|
||||
|
||||
#if defined(_LIBCPP_ABI_VCRUNTIME) && _HAS_EXCEPTIONS == 0
|
||||
|
||||
namespace std {
|
||||
|
||||
class bad_cast : public exception {
|
||||
public:
|
||||
bad_cast() _NOEXCEPT : exception("bad cast") {}
|
||||
|
||||
private:
|
||||
bad_cast(const char* const __message) _NOEXCEPT : exception(__message) {}
|
||||
};
|
||||
|
||||
class bad_typeid : public exception {
|
||||
public:
|
||||
bad_typeid() _NOEXCEPT : exception("bad typeid") {}
|
||||
|
||||
private:
|
||||
bad_typeid(const char* const __message) _NOEXCEPT : exception(__message) {}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // defined(_LIBCPP_ABI_VCRUNTIME) && _HAS_EXCEPTIONS == 0
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
_LIBCPP_NORETURN inline _LIBCPP_INLINE_VISIBILITY
|
||||
void __throw_bad_cast()
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# This testing configuration handles running the test suite against LLVM's libc++
|
||||
# using a DLL, with Clang-cl on Windows. This variant sets _HAS_EXCEPTIONS = 0
|
||||
# which removes exception class definitions from the vcruntime.
|
||||
|
||||
lit_config.load_config(config, '@CMAKE_CURRENT_BINARY_DIR@/cmake-bridge.cfg')
|
||||
|
||||
config.substitutions.append(('%{flags}', '--driver-mode=g++'))
|
||||
config.substitutions.append(('%{compile_flags}',
|
||||
'-nostdinc++ -I %{include} -I %{target-include} -I %{libcxx}/test/support -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_STDIO_ISO_WIDE_SPECIFIERS -DNOMINMAX -D_HAS_EXCEPTIONS=0'
|
||||
))
|
||||
config.substitutions.append(('%{link_flags}',
|
||||
'-nostdlib -L %{lib} -lc++ -lmsvcrt -lmsvcprt -loldnames'
|
||||
))
|
||||
config.substitutions.append(('%{exec}',
|
||||
'%{executor} --execdir %T --env PATH=%{lib} -- '
|
||||
))
|
||||
|
||||
import os, site
|
||||
site.addsitedir(os.path.join('@LIBCXX_SOURCE_DIR@', 'utils'))
|
||||
import libcxx.test.params, libcxx.test.newconfig
|
||||
libcxx.test.newconfig.configure(
|
||||
libcxx.test.params.DEFAULT_PARAMETERS,
|
||||
libcxx.test.features.DEFAULT_FEATURES,
|
||||
config,
|
||||
lit_config
|
||||
)
|
|
@ -592,6 +592,18 @@ steps:
|
|||
limit: 2
|
||||
timeout_in_minutes: 120
|
||||
|
||||
- label: "Clang-cl (no vcruntime exceptions)"
|
||||
command: "bash libcxx/utils/ci/run-buildbot clang-cl-no-vcruntime"
|
||||
artifact_paths:
|
||||
- "**/test-results.xml"
|
||||
- "**/*.abilist"
|
||||
agents:
|
||||
queue: "windows"
|
||||
retry:
|
||||
automatic:
|
||||
- exit_status: -1 # Agent was lost
|
||||
limit: 2
|
||||
|
||||
- label: "MinGW (DLL, x86_64)"
|
||||
command: "bash libcxx/utils/ci/run-buildbot mingw-dll"
|
||||
artifact_paths:
|
||||
|
|
|
@ -577,6 +577,17 @@ clang-cl-static)
|
|||
echo "+++ Running the libc++ tests"
|
||||
${NINJA} -vC "${BUILD_DIR}" check-cxx
|
||||
;;
|
||||
clang-cl-no-vcruntime)
|
||||
clean
|
||||
# Building libc++ in the same way as in clang-cl-dll above, but running
|
||||
# tests with -D_HAS_EXCEPTIONS=0, which users might set in certain
|
||||
# translation units while using libc++, even if libc++ is built with
|
||||
# exceptions enabled.
|
||||
generate-cmake-libcxx-win -DLIBCXX_TEST_PARAMS="enable_experimental=False" \
|
||||
-DLIBCXX_TEST_CONFIG="llvm-libc++-shared-no-vcruntime-clangcl.cfg.in"
|
||||
echo "+++ Running the libc++ tests"
|
||||
${NINJA} -vC "${BUILD_DIR}" check-cxx
|
||||
;;
|
||||
mingw-dll)
|
||||
clean
|
||||
# Explicitly specify the compiler with a triple prefix. The CI
|
||||
|
|
Loading…
Reference in New Issue