diff --git a/libcxx/include/exception b/libcxx/include/exception
--- a/libcxx/include/exception
+++ b/libcxx/include/exception
@@ -85,8 +85,10 @@
 #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,7 +98,62 @@
 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, <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.
+
+// Note: details for an ABI compatible implementation were taken from:
+// https://github.com/microsoft/STL/blob/afe0800cd6bf0d163bf678ad4e1feb3c130d1ec5/stl/inc/exception#L80
+
+struct __std_exception_data{
+    char const* _What;
+    bool _DoFree;
+};
+
+class _LIBCPP_EXCEPTION_ABI exception { // base of all library exceptions
+public:
+  exception() _NOEXCEPT :_Data() {}
+
+  explicit exception( char const* _Message) _NOEXCEPT : _Data() {
+      _Data._What = _Message;
+      _Data._DoFree = true;
+  }
+
+  exception( char const* _Message, int) _NOEXCEPT : _Data() {
+      _Data._What = _Message;
+  }
+
+  exception(exception const&) _NOEXCEPT {}
+
+  exception& operator=(exception const&) _NOEXCEPT { return *this; }
+
+  virtual ~exception() _NOEXCEPT {}
+
+  _LIBCPP_NODISCARD virtual char const* what() const { return "unknown exception"; }
+
+private:
+  __std_exception_data _Data;
+
+};
+
+class _LIBCPP_EXCEPTION_ABI bad_exception : public exception {
+public:
+  bad_exception() _NOEXCEPT : exception("bad exception", 1) {}
+
+  ~bad_exception() _NOEXCEPT {}
+};
+
+#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
@@ -148,6 +148,43 @@
 
 #endif // !_LIBCPP_ABI_VCRUNTIME
 
+
+// When _HAS_EXCEPTIONS == 0, these complete definitions are needed, 
+// since they would normally be provided in vcruntime_exception.h
+#if defined(_LIBCPP_ABI_VCRUNTIME) && _HAS_EXCEPTIONS == 0
+class _LIBCPP_EXCEPTION_ABI bad_alloc
+    : public exception
+{
+public:
+
+    bad_alloc() noexcept
+        : exception("bad allocation", 1)
+    {
+    }
+
+private:
+
+    friend class bad_array_new_length;
+
+    bad_alloc(char const* const _Message) noexcept
+        : exception(_Message, 1)
+    {
+    }
+};
+
+class _LIBCPP_EXCEPTION_ABI bad_array_new_length
+    : public bad_alloc
+{
+public:
+
+    bad_array_new_length() noexcept
+        : bad_alloc("bad array new length")
+    {
+    }
+};
+
+#endif // _LIBCPP_ABI_VCRUNTIME _HAS_EXCEPTIONS == 0
+
 _LIBCPP_NORETURN _LIBCPP_FUNC_VIS void __throw_bad_alloc();  // not in C++ spec
 
 _LIBCPP_NORETURN inline _LIBCPP_INLINE_VISIBILITY
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 <vcruntime_typeinfo.h>
 #else
 
@@ -366,6 +366,81 @@
 
 #endif // defined(_LIBCPP_ABI_VCRUNTIME)
 
+#if defined(_LIBCPP_ABI_VCRUNTIME) && _HAS_EXCEPTIONS == 0
+
+namespace std {
+
+class _LIBCPP_EXCEPTION_ABI bad_cast
+    : public exception
+{
+public:
+
+    bad_cast() _NOEXCEPT
+        : exception("bad cast", 1)
+    {
+    }
+
+    static bad_cast __construct_from_string_literal(const char* const _Message) _NOEXCEPT
+    {
+        return bad_cast(_Message, 1);
+    }
+
+private:
+
+    bad_cast(const char* const _Message, int) _NOEXCEPT
+        : exception(_Message, 1)
+    {
+    }
+};
+
+class  _LIBCPP_EXCEPTION_ABI bad_typeid
+    : public exception
+{
+public:
+
+    bad_typeid() _NOEXCEPT
+        : exception("bad typeid", 1)
+    {
+    }
+
+    static bad_typeid __construct_from_string_literal(const char* const _Message) _NOEXCEPT
+    {
+        return bad_typeid(_Message, 1);
+    }
+
+private:
+
+    friend class __non_rtti_object;
+
+    bad_typeid(const char* const _Message, int) _NOEXCEPT
+        : exception(_Message, 1)
+    {
+    }
+};
+
+class __non_rtti_object
+    : public bad_typeid
+{
+public:
+
+    static __non_rtti_object __construct_from_string_literal(const char* const _Message) _NOEXCEPT
+    {
+        return __non_rtti_object(_Message, 1);
+    }
+
+private:
+
+    __non_rtti_object(const char* const _Message, int) _NOEXCEPT
+        : bad_typeid(_Message, 1)
+    {
+    }
+};
+
+} // namespace std
+
+#endif
+
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_NORETURN inline _LIBCPP_INLINE_VISIBILITY
 void __throw_bad_cast()
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')),