[libcxx-commits] [libcxx] 56a3445 - [libcxx] Fix using the vcruntime ABI with _HAS_EXCEPTIONS=0 defined

Paul Kirth via libcxx-commits libcxx-commits at lists.llvm.org
Wed Aug 17 14:14:33 PDT 2022


Author: Paul Kirth
Date: 2022-08-17T21:14:25Z
New Revision: 56a34451e1cc54375e5fd35d46c7dfba88b32fc5

URL: https://github.com/llvm/llvm-project/commit/56a34451e1cc54375e5fd35d46c7dfba88b32fc5
DIFF: https://github.com/llvm/llvm-project/commit/56a34451e1cc54375e5fd35d46c7dfba88b32fc5.diff

LOG: [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

Added: 
    libcxx/test/configs/llvm-libc++-shared-no-vcruntime-clangcl.cfg.in

Modified: 
    libcxx/include/exception
    libcxx/include/new
    libcxx/include/typeinfo
    libcxx/utils/ci/buildkite-pipeline.yml
    libcxx/utils/ci/run-buildbot

Removed: 
    


################################################################################
diff  --git a/libcxx/include/exception b/libcxx/include/exception
index 8294b6d0cc8df..6b164e1dbd119 100644
--- a/libcxx/include/exception
+++ b/libcxx/include/exception
@@ -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
-{
+#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, <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 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
index ed95caed1c41d..85f2d9578ec45 100644
--- a/libcxx/include/new
+++ b/libcxx/include/new
@@ -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
 

diff  --git a/libcxx/include/typeinfo b/libcxx/include/typeinfo
index d81a30995723c..8cf45f7527c04 100644
--- a/libcxx/include/typeinfo
+++ b/libcxx/include/typeinfo
@@ -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()

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
index 0000000000000..c6ddfb79ea4f4
--- /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 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
+)

diff  --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index a4d6f72900407..8e804552f3591 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -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:

diff  --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 2cdbf8b9abe73..27431f3c2cac8 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -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


        


More information about the libcxx-commits mailing list