diff --git a/libcxx/include/exception b/libcxx/include/exception --- a/libcxx/include/exception +++ b/libcxx/include/exception @@ -85,8 +85,10 @@ #include #include +// 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 +# include #endif #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -96,24 +98,66 @@ namespace std // purposefully not using versioning namespace { -#if !defined(_LIBCPP_ABI_VCRUNTIME) -class _LIBCPP_EXCEPTION_ABI exception -{ +#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. + +#elif defined(_LIBCPP_ABI_VCRUNTIME) && _HAS_EXCEPTIONS == 0 +// However, 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 exception { // base of all library exceptions public: - _LIBCPP_INLINE_VISIBILITY exception() _NOEXCEPT {} - _LIBCPP_INLINE_VISIBILITY exception(const exception&) _NOEXCEPT = default; + 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"; } - virtual ~exception() _NOEXCEPT; - virtual const char* what() const _NOEXCEPT; +private: + __std_exception_data _Data; }; -class _LIBCPP_EXCEPTION_ABI bad_exception - : public exception -{ +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; + _LIBCPP_INLINE_VISIBILITY bad_exception() _NOEXCEPT {} + virtual ~bad_exception() _NOEXCEPT; + virtual const char* what() const _NOEXCEPT; }; #endif // !_LIBCPP_ABI_VCRUNTIME diff --git a/libcxx/include/new b/libcxx/include/new --- a/libcxx/include/new +++ b/libcxx/include/new @@ -146,7 +146,25 @@ _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 diff --git a/libcxx/include/typeinfo b/libcxx/include/typeinfo --- a/libcxx/include/typeinfo +++ b/libcxx/include/typeinfo @@ -370,6 +370,30 @@ #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() diff --git a/libcxx/test/configs/llvm-libc++-shared-no-vcruntime-clangcl.cfg.in b/libcxx/test/configs/llvm-libc++-shared-no-vcruntime-clangcl.cfg.in new file mode 100644 --- /dev/null +++ b/libcxx/test/configs/llvm-libc++-shared-no-vcruntime-clangcl.cfg.in @@ -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 defintions 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 +) diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml --- a/libcxx/utils/ci/buildkite-pipeline.yml +++ b/libcxx/utils/ci/buildkite-pipeline.yml @@ -592,6 +592,18 @@ 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: diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot --- a/libcxx/utils/ci/run-buildbot +++ b/libcxx/utils/ci/run-buildbot @@ -577,6 +577,17 @@ 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