diff --git a/libcxx/include/exception b/libcxx/include/exception --- a/libcxx/include/exception +++ b/libcxx/include/exception @@ -85,8 +85,14 @@ #include #include -#if defined(_LIBCPP_ABI_VCRUNTIME) -#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) && (!defined(_HAS_EXCEPTIONS) || _HAS_EXCEPTIONS != 0) +# include +#elif defined(_LIBCPP_ABI_VCRUNTIME) && _HAS_EXCEPTIONS == 0 +// vcruntime_exception.h transitivly includes vcruntime_new.h, which we want for other defs +// TODO: consider including vcruntime.h only +# include #endif #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -96,7 +102,55 @@ namespace std // purposefully not using versioning namespace { -#if !defined(_LIBCPP_ABI_VCRUNTIME) +#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. + +// Note: details for an ABI compatible implementation were taken from: +// https://github.com/microsoft/STL/blob/afe0800cd6bf0d163bf678ad4e1feb3c130d1ec5/stl/inc/exception#L80 + +class _LIBCPP_EXCEPTION_ABI exception { // base of all library exceptions +public: + // This constructor is necessary to compile header new for the _HAS_EXCEPTIONS==0 scenario. + explicit exception(const char* _Message = "unknown", int = 1) _NOEXCEPT : _Ptr(_Message) {} + + exception(const exception&) _NOEXCEPT {} + + exception& operator=(const exception&) _NOEXCEPT { return *this; } + + virtual ~exception() _NOEXCEPT {} + + _LIBCPP_NODISCARD virtual const char* what() const _NOEXCEPT { return "unknown exception"; } + + void _Raise() const {} + +protected: + virtual void _Doraise() const {} + + const char* _Ptr; +}; + +class _LIBCPP_EXCEPTION_ABI bad_exception : public exception { +public: + bad_exception(const char* _Message = "bad exception") _NOEXCEPT : exception(_Message) {} + + ~bad_exception() _NOEXCEPT {} + +protected: + void _Doraise() const {} +}; + +#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: diff --git a/libcxx/include/new b/libcxx/include/new --- a/libcxx/include/new +++ b/libcxx/include/new @@ -186,7 +186,7 @@ #define _THROW_BAD_ALLOC #endif -#if !defined(_LIBCPP_ABI_VCRUNTIME) +#if !defined(_LIBCPP_ABI_VCRUNTIME) || (!defined(_HAS_EXCEPTIONS) || _HAS_EXCEPTIONS == 0) _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new(std::size_t __sz) _THROW_BAD_ALLOC; _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new(std::size_t __sz, const std::nothrow_t&) _NOEXCEPT _LIBCPP_NOALIAS; diff --git a/libcxx/include/typeinfo b/libcxx/include/typeinfo --- a/libcxx/include/typeinfo +++ b/libcxx/include/typeinfo @@ -72,7 +72,7 @@ # pragma GCC system_header #endif -#if defined(_LIBCPP_ABI_VCRUNTIME) +#if defined(_LIBCPP_ABI_VCRUNTIME) #include #else diff --git a/libcxx/test/configs/llvm-libc++-shared-no-exception-clangcl.cfg.in b/libcxx/test/configs/llvm-libc++-shared-no-exception-clangcl.cfg.in new file mode 100644 --- /dev/null +++ b/libcxx/test/configs/llvm-libc++-shared-no-exception-clangcl.cfg.in @@ -0,0 +1,32 @@ +# This testing configuration handles running the test suite against LLVM's libc++ +# using a DLL, with Clang-cl on Windows. + +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} -- ' +)) + +# LIBCXX-WINDOWS-FIXME is the feature name used to XFAIL the +# initial Windows failures until they can be properly diagnosed +# and fixed. This allows easier detection of new test failures +# and regressions. Note: New failures should not be suppressed +# using this feature. (Also see llvm.org/PR32730) +config.available_features.add('LIBCXX-WINDOWS-FIXME') + +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/test/support/test_macros.h b/libcxx/test/support/test_macros.h --- a/libcxx/test/support/test_macros.h +++ b/libcxx/test/support/test_macros.h @@ -179,9 +179,9 @@ # define RTTI_ASSERT(X) #endif -#if !TEST_HAS_FEATURE(cxx_exceptions) && !defined(__cpp_exceptions) \ - && !defined(__EXCEPTIONS) -#define TEST_HAS_NO_EXCEPTIONS +#if (!TEST_HAS_FEATURE(cxx_exceptions) && !defined(__cpp_exceptions) && !defined(__EXCEPTIONS)) || \ + (defined(_HAS_EXCEPTIONS) && _HAS_EXCEPTIONS == 0) +# define TEST_HAS_NO_EXCEPTIONS #endif #if TEST_HAS_FEATURE(address_sanitizer) || TEST_HAS_FEATURE(memory_sanitizer) || \ 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 @@ -579,6 +579,18 @@ limit: 2 timeout_in_minutes: 120 + - label: "Clang-cl (no-exceptions)" + command: "bash libcxx/utils/ci/run-buildbot clang-cl-noexceptions" + artifact_paths: + - "**/test-results.xml" + - "**/*.abilist" + agents: + queue: "windows" + retry: + automatic: + - exit_status: -1 # Agent was lost + limit: 2 + - label: "MinGW (DLL)" 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 @@ -554,6 +554,17 @@ echo "+++ Running the libc++ tests" ${NINJA} -vC "${BUILD_DIR}" check-cxx ;; +clang-cl-noexceptions) + 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_ENABLE_EXPERIMENTAL_LIBRARY=OFF \ + -DLIBCXX_TEST_CONFIG="llvm-libc++-shared-no-exception-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 diff --git a/libcxx/utils/libcxx/test/features.py b/libcxx/utils/libcxx/test/features.py --- a/libcxx/utils/libcxx/test/features.py +++ b/libcxx/utils/libcxx/test/features.py @@ -39,6 +39,13 @@ when=lambda cfg: hasCompileFlag(cfg, '-Wuser-defined-warnings'), actions=[AddCompileFlag('-Wuser-defined-warnings')]), + # Normally, the feature 'no-exceptions' is set in params.py if + # running tests with enable_exceptions=false, but if we didn't set + # that and only defined _HAS_EXCEPTIONS=0, pick up on that and set the + # no-exceptions feature so certain tests are omitted. + # It would probably be fine to just run that test configuration with + # enable_exceptions=false too. + Feature(name='no-exceptions', when=lambda cfg: '_HAS_EXCEPTIONS' in compilerMacros(cfg) and compilerMacros(cfg)['_HAS_EXCEPTIONS'] == '0'), Feature(name='has-fblocks', when=lambda cfg: hasCompileFlag(cfg, '-fblocks')), Feature(name='-fsized-deallocation', when=lambda cfg: hasCompileFlag(cfg, '-fsized-deallocation')), Feature(name='-faligned-allocation', when=lambda cfg: hasCompileFlag(cfg, '-faligned-allocation')),