[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