diff --git a/libcxx/include/exception b/libcxx/include/exception --- a/libcxx/include/exception +++ b/libcxx/include/exception @@ -84,7 +84,9 @@ #include #include -#if defined(_LIBCPP_ABI_VCRUNTIME) +// 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) && _HAS_EXCEPTIONS == 1 #include #endif @@ -95,7 +97,63 @@ namespace std // purposefully not using versioning namespace { -#if !defined(_LIBCPP_ABI_VCRUNTIME) + //Already included above +#if defined(_LIBCPP_ABI_VCRUNTIME) && _HAS_EXCEPTIONS == 1 + +// However, does not define std::exception and std::bad_exception +// under -fno-exceptions. +// +// Since libc++ still wants to provide the std::exception hierarchy under -fno-exceptions +// (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. +#elif defined(_LIBCPP_ABI_VCRUNTIME) && _HAS_EXCEPTIONS == 0 + + // Note: details for an ABI compatable 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 + // successfully header new for _HAS_EXCEPTIONS==0 scenario + explicit __CLR_OR_THIS_CALL exception(const char* _Message = "unknown", int = 1) _NOEXCEPT {} + + __CLR_OR_THIS_CALL exception(const exception& _Right) _NOEXCEPT {} + + exception& __CLR_OR_THIS_CALL operator=(const exception& _Right) _NOEXCEPT { + return *this; + } + + virtual __CLR_OR_THIS_CALL ~exception() _NOEXCEPT {} + + _NODISCARD virtual const char* __CLR_OR_THIS_CALL what() const _NOEXCEPT { // return pointer to message string + return "unknown exception"; + } + + _LIBCPP_NORETURN void __CLR_OR_THIS_CALL _Raise() const { + } + +protected: + virtual void __CLR_OR_THIS_CALL _Doraise() const {} + +protected: + const char* _Ptr; +}; + +class _LIBCPP_EXCEPTION_ABI bad_exception : public exception { +public: + __CLR_OR_THIS_CALL bad_exception(const char* _Message = "bad exception") noexcept : exception(_Message) {} + + __CLR_OR_THIS_CALL ~bad_exception() noexcept override {} + +protected: + void __CLR_OR_THIS_CALL _Doraise() const override { + } +}; + +// 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. +#else// !defined(_LIBCPP_ABI_VCRUNTIME) class _LIBCPP_EXCEPTION_ABI exception { public: 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 @@ -185,7 +185,8 @@ #endif #if !TEST_HAS_FEATURE(cxx_exceptions) && !defined(__cpp_exceptions) \ - && !defined(__EXCEPTIONS) + && !defined(__EXCEPTIONS) \ + || (defined(_HAS_EXCEPTIONS) && _HAS_EXCEPTIONS == 0) #define TEST_HAS_NO_EXCEPTIONS #endif 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 @@ -565,6 +565,18 @@ limit: 2 timeout_in_minutes: 120 + - label: "Windows (_HAS_EXCEPTIONS=0)" + 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 @@ -592,6 +592,18 @@ 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_COMPILER_FLAGS="_HAS_EXCEPTIONS=0" \ + -DLIBCXX_TEST_CONFIG="llvm-libc++-shared-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')),