[libcxx-commits] [libcxx] [libc++] Add MSVC's implementation of `exception_ptr` for Windows (PR #94977)

A. Jiang via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jul 7 05:44:05 PDT 2026


================
@@ -7,70 +7,453 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include <exception>
-#include <stdio.h>
-#include <stdlib.h>
-
-_LIBCPP_CRT_FUNC void __cdecl __ExceptionPtrCreate(void*);
-_LIBCPP_CRT_FUNC void __cdecl __ExceptionPtrDestroy(void*);
-_LIBCPP_CRT_FUNC void __cdecl __ExceptionPtrCopy(void*, const void*);
-_LIBCPP_CRT_FUNC void __cdecl __ExceptionPtrAssign(void*, const void*);
-_LIBCPP_CRT_FUNC bool __cdecl __ExceptionPtrCompare(const void*, const void*);
-_LIBCPP_CRT_FUNC bool __cdecl __ExceptionPtrToBool(const void*);
-_LIBCPP_CRT_FUNC void __cdecl __ExceptionPtrSwap(void*, void*);
-_LIBCPP_CRT_FUNC void __cdecl __ExceptionPtrCurrentException(void*);
-[[noreturn]] _LIBCPP_CRT_FUNC void __cdecl __ExceptionPtrRethrow(const void*);
-_LIBCPP_CRT_FUNC void __cdecl __ExceptionPtrCopyException(void*, const void*, const void*);
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wmultichar")
 
-namespace std {
+#include <__exception/exception_ptr.h>
+#include <__memory/shared_count.h>
+#include <cstdlib>
+#include <cstring>
 
-exception_ptr::exception_ptr() noexcept { __ExceptionPtrCreate(this); }
-exception_ptr::exception_ptr(nullptr_t) noexcept { __ExceptionPtrCreate(this); }
+#include <Unknwn.h>
+#include <Windows.h>
+struct _ThrowInfo;
+#include <eh.h>
+#include <ehdata.h>
+#include <malloc.h> // alloca
 
-exception_ptr::exception_ptr(const exception_ptr& __other) noexcept { __ExceptionPtrCopy(this, &__other); }
-exception_ptr& exception_ptr::operator=(const exception_ptr& __other) noexcept {
-  __ExceptionPtrAssign(this, &__other);
-  return *this;
+// Pre-V4 managed exception code
+#define MANAGED_EXCEPTION_CODE 0XE0434F4D
+
+// V4 and later managed exception code
+#define MANAGED_EXCEPTION_CODE_V4 0XE0434352
+
+extern "C" _LIBCPP_CRT_FUNC void* __cdecl __AdjustPointer(void*, const PMD&);
+extern "C" _LIBCPP_CRT_FUNC void** __cdecl __current_exception();
+
+namespace {
+
+typedef void(__stdcall* __prepare_for_throw_t)(void*);
+
+struct __winrt_exception_info {
+  void* __description;
+  void* __restricted_error_string;
+  void* __restricted_error_reference;
+  void* __capability_sid;
+  long __hr;
+  void* __restricted_info;
+  ThrowInfo* __throw_info;
+  unsigned int __size;
+  __prepare_for_throw_t __prepare_throw;
+};
+
+inline EHExceptionRecord* __get_current_exception() noexcept {
+  return *reinterpret_cast<EHExceptionRecord**>(__current_exception());
 }
 
-exception_ptr& exception_ptr::operator=(nullptr_t) noexcept {
-  exception_ptr dummy;
-  __ExceptionPtrAssign(this, &dummy);
-  return *this;
+inline void __call_member_function_0(void* __this, void* __mfn) {
+  auto __fn = reinterpret_cast<void(__thiscall*)(void*)>(__mfn);
+  __fn(__this);
+}
+
+inline void __call_member_function_1(void* __this, void* __mfn, void* __arg) {
+  auto __fn = reinterpret_cast<void(__thiscall*)(void*, void*)>(__mfn);
+  __fn(__this, __arg);
+}
+
+inline void __call_member_function_2(void* __this, void* __mfn, void* __arg1, int __arg2) {
+  auto __fn = reinterpret_cast<void(__thiscall*)(void*, void*, int)>(__mfn);
+  __fn(__this, __arg1, __arg2);
 }
 
-exception_ptr::~exception_ptr() noexcept { __ExceptionPtrDestroy(this); }
+void __populate_cpp_exception_record(
+    _EXCEPTION_RECORD& __record, const void* const __except_obj, ThrowInfo* __throw_info) noexcept {
+  __record.ExceptionCode           = EH_EXCEPTION_NUMBER;
+  __record.ExceptionFlags          = EXCEPTION_NONCONTINUABLE;
+  __record.ExceptionRecord         = nullptr;
+  __record.ExceptionAddress        = nullptr;
+  __record.NumberParameters        = EH_EXCEPTION_PARAMETERS;
+  __record.ExceptionInformation[0] = EH_MAGIC_NUMBER1;
+  __record.ExceptionInformation[1] = reinterpret_cast<ULONG_PTR>(__except_obj);
+
+  if (__throw_info && (__throw_info->attributes & TI_IsWinRT)) {
+    const auto __wei = (*static_cast<__winrt_exception_info** const*>(const_cast<void*>(__except_obj)))[-1];
+    __throw_info     = __wei->__throw_info;
+  }
+
+  __record.ExceptionInformation[2] = reinterpret_cast<ULONG_PTR>(__throw_info);
+
+#if _EH_RELATIVE_TYPEINFO
+  void* __throw_image_base =
+      __throw_info ? RtlPcToFileHeader(const_cast<void*>(static_cast<const void*>(__throw_info)), &__throw_image_base)
+                   : nullptr;
+  __record.ExceptionInformation[3] = reinterpret_cast<ULONG_PTR>(__throw_image_base);
+#endif
+
+  if (__throw_info && ((__throw_info->attributes & TI_IsPure)
+#if _EH_RELATIVE_TYPEINFO
+                       || !__throw_image_base
+#endif
+                       )) {
+    __record.ExceptionInformation[0] = EH_PURE_MAGIC_NUMBER1;
+  }
+}
+
+void __copy_exception_record(_EXCEPTION_RECORD& __dest, const _EXCEPTION_RECORD& __src) noexcept {
+  __dest.ExceptionCode    = __src.ExceptionCode;
+  __dest.ExceptionFlags   = __src.ExceptionFlags | EXCEPTION_NONCONTINUABLE;
+  __dest.ExceptionRecord  = nullptr;
+  __dest.ExceptionAddress = nullptr;
+  const auto __parameters = __src.NumberParameters;
+  __dest.NumberParameters = __parameters;
+
+  constexpr auto __max_parameters = static_cast<DWORD>(EXCEPTION_MAXIMUM_PARAMETERS);
+  const auto __in_use             = (__parameters < __max_parameters) ? __parameters : __max_parameters;
+  std::memcpy(__dest.ExceptionInformation, __src.ExceptionInformation, __in_use * sizeof(ULONG_PTR));
+  std::memset(&__dest.ExceptionInformation[__in_use], 0, (__max_parameters - __in_use) * sizeof(ULONG_PTR));
+}
+
+void __copy_exception_object(void* __dest, const void* __src, const CatchableType* const __type
+#if _EH_RELATIVE_TYPEINFO
+                             ,
+                             uintptr_t __throw_image_base
+#endif
+) {
+  if ((__type->properties & CT_IsSimpleType) || __type->copyFunction == 0) {
+    std::memcpy(__dest, __src, __type->sizeOrOffset);
+
+    if (__type->properties & CT_IsWinRTHandle) {
+      const auto __unknown = *static_cast<IUnknown* const*>(const_cast<void*>(__src));
+      if (__unknown) {
+        __unknown->AddRef();
+      }
+    }
+    return;
+  }
+
+#if _EH_RELATIVE_TYPEINFO
+  const auto __copy_func = reinterpret_cast<void*>(__throw_image_base + __type->copyFunction);
+#else
+  const auto __copy_func = __type->copyFunction;
+#endif
+
+  const auto __adjusted = __AdjustPointer(const_cast<void*>(__src), __type->thisDisplacement);
+  if (__type->properties & CT_HasVirtualBase) {
+    __call_member_function_2(__dest, __copy_func, __adjusted, 1);
+  } else {
+    __call_member_function_1(__dest, __copy_func, __adjusted);
+  }
+}
+
+struct __exception_ptr_storage : public std::__shared_count {
+  _EXCEPTION_RECORD __record_;
+  explicit __exception_ptr_storage(long __refs = 0) noexcept : std::__shared_count(__refs) {}
+};
+
+template <class _StaticEx>
+struct __exception_ptr_static final : public __exception_ptr_storage {
+  _StaticEx __ex_;
+
+  __exception_ptr_static() noexcept : __exception_ptr_storage(0) {
+    __populate_cpp_exception_record(__record_, &__ex_, static_cast<ThrowInfo*>(__GetExceptionInfo(__ex_)));
+  }
+
+  void __on_zero_shared() noexcept override {}
+
+  static __exception_ptr_storage* __get() noexcept {
+    struct __container {
+      union {
+        __exception_ptr_static __instance_;
+      };
+      __container() noexcept : __instance_() {}
+      ~__container() {}
+    };
+    static __container __storage;
+    return &__storage.__instance_;
+  }
+};
+
+struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) __exception_ptr_normal final : public __exception_ptr_storage {
+  explicit __exception_ptr_normal(const _EXCEPTION_RECORD& __record) noexcept : __exception_ptr_storage(0) {
+    __copy_exception_record(__record_, __record);
+  }
+
+  void __on_zero_shared() noexcept override {
+    const auto& __cpp_eh_record = reinterpret_cast<EHExceptionRecord&>(__record_);
+    if (PER_IS_MSVC_PURE_OR_NATIVE_EH(&__cpp_eh_record)) {
+      const auto* __throw_info = __cpp_eh_record.params.pThrowInfo;
+      if (__throw_info && __cpp_eh_record.params.pExceptionObject) {
+#if _EH_RELATIVE_TYPEINFO
+        const auto __throw_image_base = reinterpret_cast<uintptr_t>(__cpp_eh_record.params.pThrowImageBase);
+        const auto* __catchable_type_array = reinterpret_cast<const CatchableTypeArray*>(
+            static_cast<uintptr_t>(__throw_info->pCatchableTypeArray) + __throw_image_base);
+        const auto* __type = reinterpret_cast<CatchableType*>(
+            static_cast<uintptr_t>(__catchable_type_array->arrayOfCatchableTypes[0]) + __throw_image_base);
+#else
+        const auto* __type = __throw_info->pCatchableTypeArray->arrayOfCatchableTypes[0];
+#endif
+        if (__throw_info->pmfnUnwind) {
+#if _EH_RELATIVE_TYPEINFO
+          __call_member_function_0(__cpp_eh_record.params.pExceptionObject,
+                                   reinterpret_cast<void*>(__throw_info->pmfnUnwind + __throw_image_base));
+#else
+          __call_member_function_0(__cpp_eh_record.params.pExceptionObject, __throw_info->pmfnUnwind);
+#endif
+        } else if (__type->properties & CT_IsWinRTHandle) {
+          const auto* __unknown = *static_cast<IUnknown* const*>(__cpp_eh_record.params.pExceptionObject);
+          if (__unknown) {
+            const_cast<IUnknown*>(__unknown)->Release();
+          }
+        }
+      }
+    }
+    std::free(this);
+  }
+};
+
+static_assert(sizeof(__exception_ptr_normal) % __STDCPP_DEFAULT_NEW_ALIGNMENT__ == 0,
+              "Exception in exception_ptr would be constructed with the wrong alignment");
 
-exception_ptr::operator bool() const noexcept { return __ExceptionPtrToBool(this); }
+void __assign_seh_exception_ptr_from_record(
+    void*& __dest, const _EXCEPTION_RECORD& __record, void* const __rx_raw) noexcept {
+  if (!__rx_raw) {
+    __dest = __exception_ptr_static<std::bad_alloc>::__get();
+    return;
+  }
 
-bool operator==(const exception_ptr& __x, const exception_ptr& __y) noexcept {
-  return __ExceptionPtrCompare(&__x, &__y);
+  __dest = ::new (__rx_raw) __exception_ptr_normal(__record);
 }
 
-void swap(exception_ptr& lhs, exception_ptr& rhs) noexcept { __ExceptionPtrSwap(&rhs, &lhs); }
+void __assign_cpp_exception_ptr_from_record(void*& __dest, const EHExceptionRecord& __record) noexcept {
+  const auto* __throw_info = __record.params.pThrowInfo;
+#if _EH_RELATIVE_TYPEINFO
+  const auto __throw_image_base = reinterpret_cast<uintptr_t>(__record.params.pThrowImageBase);
+  const auto* __catchable_type_array = reinterpret_cast<const CatchableTypeArray*>(
+      static_cast<uintptr_t>(__throw_info->pCatchableTypeArray) + __throw_image_base);
+  const auto* __type = reinterpret_cast<CatchableType*>(
+      static_cast<uintptr_t>(__catchable_type_array->arrayOfCatchableTypes[0]) + __throw_image_base);
+#else
+  const auto* __type = __throw_info->pCatchableTypeArray->arrayOfCatchableTypes[0];
+#endif
+
+  const auto __except_obj_size = static_cast<size_t>(__type->sizeOrOffset);
+  const auto __alloc_size      = sizeof(__exception_ptr_normal) + __except_obj_size;
+  auto* __rx_raw               = std::malloc(__alloc_size);
+  if (!__rx_raw) {
+    __dest = __exception_ptr_static<std::bad_alloc>::__get();
+    return;
+  }
+
+  try {
+    __copy_exception_object(static_cast<__exception_ptr_normal*>(__rx_raw) + 1,
+                            __record.params.pExceptionObject,
+                            __type
+#if _EH_RELATIVE_TYPEINFO
+                            ,
+                            __throw_image_base
+#endif
+    );
+
+    const auto* __rx = ::new (__rx_raw) __exception_ptr_normal(reinterpret_cast<const _EXCEPTION_RECORD&>(__record));
+    reinterpret_cast<EHExceptionRecord&>(const_cast<__exception_ptr_normal*>(__rx)->__record_).params.pExceptionObject =
+        static_cast<__exception_ptr_normal*>(__rx_raw) + 1;
+    __dest = const_cast<__exception_ptr_normal*>(__rx);
+  } catch (...) {
+    const auto* __inner_record_ptr = __get_current_exception();
+    if (!__inner_record_ptr) {
+      std::free(__rx_raw);
+      __dest = __exception_ptr_static<std::bad_exception>::__get();
+      return;
+    }
+    const auto& __inner_record = *__inner_record_ptr;
+    if (__inner_record.ExceptionCode == MANAGED_EXCEPTION_CODE ||
+        __inner_record.ExceptionCode == MANAGED_EXCEPTION_CODE_V4) {
+      std::free(__rx_raw);
+      __dest = __exception_ptr_static<std::bad_exception>::__get();
+      return;
+    }
+
+    if (!PER_IS_MSVC_PURE_OR_NATIVE_EH(&__inner_record)) {
+      __assign_seh_exception_ptr_from_record(
+          __dest, reinterpret_cast<const _EXCEPTION_RECORD&>(__inner_record), __rx_raw);
+      return;
+    }
+
+    const auto* __inner_throw = __inner_record.params.pThrowInfo;
+#if _EH_RELATIVE_TYPEINFO
+    const auto __inner_throw_image_base = reinterpret_cast<uintptr_t>(__inner_record.params.pThrowImageBase);
+    const auto* __inner_catchable_type_array = reinterpret_cast<const CatchableTypeArray*>(
+        static_cast<uintptr_t>(__inner_throw->pCatchableTypeArray) + __inner_throw_image_base);
+    const auto* __inner_type = reinterpret_cast<CatchableType*>(
+        static_cast<uintptr_t>(__inner_catchable_type_array->arrayOfCatchableTypes[0]) + __inner_throw_image_base);
+#else
+    const auto* __inner_type = __inner_throw->pCatchableTypeArray->arrayOfCatchableTypes[0];
+#endif
+
+    const auto __inner_except_size = static_cast<size_t>(__inner_type->sizeOrOffset);
+    const auto __inner_alloc_size  = sizeof(__exception_ptr_normal) + __inner_except_size;
+    if (__inner_alloc_size > __alloc_size) {
+      std::free(__rx_raw);
+      __rx_raw = std::malloc(__inner_alloc_size);
+      if (!__rx_raw) {
+        __dest = __exception_ptr_static<std::bad_alloc>::__get();
+        return;
+      }
+    }
+
+    try {
+      __copy_exception_object(static_cast<__exception_ptr_normal*>(__rx_raw) + 1,
+                              __inner_record.params.pExceptionObject,
+                              __inner_type
+#if _EH_RELATIVE_TYPEINFO
+                              ,
+                              __inner_throw_image_base
+#endif
+      );
+    } catch (...) {
+      std::free(__rx_raw);
+      __dest = __exception_ptr_static<std::bad_exception>::__get();
+      return;
+    }
+
+    const auto* __rx =
+        ::new (__rx_raw) __exception_ptr_normal(reinterpret_cast<const _EXCEPTION_RECORD&>(__inner_record));
+    reinterpret_cast<EHExceptionRecord&>(const_cast<__exception_ptr_normal*>(__rx)->__record_).params.pExceptionObject =
+        static_cast<__exception_ptr_normal*>(__rx_raw) + 1;
+    __dest = const_cast<__exception_ptr_normal*>(__rx);
+  }
+}
+
+} // namespace
+
+namespace std {
+
+exception_ptr::~exception_ptr() noexcept {
+  if (__ptr_) {
+    static_cast<__shared_count*>(__ptr_)->__release_shared();
+  }
+}
+
+exception_ptr::exception_ptr(const exception_ptr& __other) noexcept : __ptr_(__other.__ptr_) {
+  if (__ptr_) {
+    static_cast<__shared_count*>(__ptr_)->__add_shared();
+  }
+}
+
+exception_ptr& exception_ptr::operator=(const exception_ptr& __other) noexcept {
+  if (__ptr_ != __other.__ptr_) {
+    if (__other.__ptr_) {
+      static_cast<__shared_count*>(__other.__ptr_)->__add_shared();
+    }
+    if (__ptr_) {
+      static_cast<__shared_count*>(__ptr_)->__release_shared();
+    }
+    __ptr_ = __other.__ptr_;
+  }
+  return *this;
+}
+
+exception_ptr exception_ptr::__from_native_exception_pointer(void* __p) noexcept {
+  exception_ptr __ret;
+  __ret.__ptr_ = __p;
+  if (__ret.__ptr_) {
+    static_cast<__shared_count*>(__ret.__ptr_)->__add_shared();
+  }
+  return __ret;
+}
 
 exception_ptr __copy_exception_ptr(void* __except, const void* __ptr) {
   exception_ptr __ret = nullptr;
-  if (__ptr)
-    __ExceptionPtrCopyException(&__ret, __except, __ptr);
+  if (!__ptr) {
+    return __ret;
+  }
+
+  _EXCEPTION_RECORD __record;
+  __populate_cpp_exception_record(__record, __except, static_cast<ThrowInfo*>(const_cast<void*>(__ptr)));
+  __assign_cpp_exception_ptr_from_record(__ret.__ptr_, reinterpret_cast<const EHExceptionRecord&>(__record));
   return __ret;
 }
 
 exception_ptr current_exception() noexcept {
   exception_ptr __ret;
-  __ExceptionPtrCurrentException(&__ret);
+  const auto* __record = __get_current_exception();
+  if (!__record || __record->ExceptionCode == MANAGED_EXCEPTION_CODE ||
+      __record->ExceptionCode == MANAGED_EXCEPTION_CODE_V4) {
+    return __ret;
+  }
+
+  if (PER_IS_MSVC_PURE_OR_NATIVE_EH(__record)) {
+    __assign_cpp_exception_ptr_from_record(__ret.__ptr_, *__record);
+  } else {
+    __assign_seh_exception_ptr_from_record(
+        __ret.__ptr_, reinterpret_cast<const _EXCEPTION_RECORD&>(*__record), std::malloc(sizeof(__exception_ptr_normal)));
+  }
   return __ret;
 }
 
-[[noreturn]] void rethrow_exception(exception_ptr p) { __ExceptionPtrRethrow(&p); }
+[[noreturn]] void rethrow_exception(exception_ptr __p) {
+  if (!__p) {
+    throw bad_exception();
+  }
+
+  auto* __rep = static_cast<__exception_ptr_storage*>(__p.__ptr_);
+  auto __record_copy = __rep->__record_;
+  auto& __cpp_record = reinterpret_cast<EHExceptionRecord&>(__record_copy);
+  if (PER_IS_MSVC_PURE_OR_NATIVE_EH(&__cpp_record)) {
+    const auto* __throw_info = __cpp_record.params.pThrowInfo;
+    if (!__cpp_record.params.pExceptionObject || !__throw_info || !__throw_info->pCatchableTypeArray) {
+      terminate();
+    }
+
+#if _EH_RELATIVE_TYPEINFO
+    const auto __throw_image_base = reinterpret_cast<uintptr_t>(__cpp_record.params.pThrowImageBase);
+    const auto* __catchable_type_array =
+        reinterpret_cast<const CatchableTypeArray*>(__throw_image_base + __throw_info->pCatchableTypeArray);
+#else
+    const auto* __catchable_type_array = __throw_info->pCatchableTypeArray;
+#endif
+
+    if (__catchable_type_array->nCatchableTypes <= 0) {
+      terminate();
+    }
+
+#if _EH_RELATIVE_TYPEINFO
+    const auto* __type = reinterpret_cast<CatchableType*>(
+        static_cast<uintptr_t>(__catchable_type_array->arrayOfCatchableTypes[0]) + __throw_image_base);
+#else
+    const auto* __type = __throw_info->pCatchableTypeArray->arrayOfCatchableTypes[0];
+#endif
+
+#pragma warning(suppress : 6255)
+    void* __exception_buffer = alloca(__type->sizeOrOffset);
+    __copy_exception_object(__exception_buffer, __cpp_record.params.pExceptionObject, __type
+#if _EH_RELATIVE_TYPEINFO
+                            ,
+                            __throw_image_base
+#endif
+    );
+
+    __cpp_record.params.pExceptionObject = __exception_buffer;
+  }
+
+  // Under MSVC C++ exception handling (/EHsc), C Win32 API functions like RaiseException
+  // are assumed not to throw synchronous C++ exceptions. As a result, stack unwinding
+  // initiated by RaiseException does not execute local destructors in this frame.
+  // We must explicitly reset __p here so that the reference count on the stored
+  // exception record is properly decremented before unwinding begins.
+  __p = nullptr;
+  RaiseException(__record_copy.ExceptionCode, __record_copy.ExceptionFlags, __record_copy.NumberParameters,
+                 __record_copy.ExceptionInformation);
+  terminate();
+}
 
 nested_exception::nested_exception() noexcept : __ptr_(current_exception()) {}
 
 nested_exception::~nested_exception() noexcept {}
 
 [[noreturn]] void nested_exception::rethrow_nested() const {
-  if (__ptr_ == nullptr)
+  if (__ptr_ == nullptr) {
     terminate();
+  }
----------------
frederick-vs-ja wrote:

These lines don't need to be touched. I _guess_ it's better to avoid unrelated changes.

https://github.com/llvm/llvm-project/pull/94977


More information about the libcxx-commits mailing list