From 9e13720eb81fd8a68fe82ac962c17fd522547464 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 10 Jul 2026 11:26:24 -0700 Subject: [PATCH] [libc++] Add MSVC's implementation of exception_ptr for Windows (#94977) Microsoft is happy to contribute our implementation of exception_ptr for Windows, under the Apache License v2.0 with LLVM Exception that our STLs share. * excptptr.cpp, copied verbatim as: https://github.com/microsoft/STL/blob/e36ee6c2b9bc6f5b1f70776c18cf5d3a93a69798/stl/src/excptptr.cpp * , copied verbatim as: https://github.com/microsoft/STL/blob/e36ee6c2b9bc6f5b1f70776c18cf5d3a93a69798/stl/inc/exception The implementation was integrated into libc++: * Deleted STL copy of . All standard exception classes are already provided by libc++'s standard headers. * Replaced MSVC STL naming conventions and macros (_STD, _CRTIMP2_PURE, _Ref_count_base, _Ptr_base, _EXCEPTION_RECORD, etc.) with standard libc++ style and identifiers (_LIBCPP_EXPORTED_FROM_ABI, __double_leading_underscore variable/function names). * Replaced internal MSVC CRT headers (, , ) with portable standard C++ constructs (e.g., standard C++ function-local static initialization for immortal static exception objects) and inline pointer-to-member invocation helpers. * Removed all exported __ExceptionPtr... C functions. These symbols were originally present in Microsoft's CRT DLL exports solely to support inline methods in MSVC STL's header. * Moved all SEH/EH exception record definitions, SEH copying helpers (__populate_cpp_exception_record, __copy_exception_object), and reference-counted control blocks (__exception_ptr_static and __exception_ptr_normal) directly into exception_pointer_msvc.ipp. * Use reference-counted control block representation on Windows which removed the platform #ifdef _LIBCPP_ABI_MICROSOFT bifurcation for class exception_ptr in exception_ptr.h. std::exception_ptr now uses the same layout across all operating systems. Co-authored-by: Petr Hosek --- libcxx/CREDITS.TXT | 2 +- libcxx/docs/ReleaseNotes/22.rst | 6 + libcxx/include/__exception/exception_ptr.h | 33 +- .../runtime/exception_pointer_msvc.ipp | 460 ++++++++++++++++-- 4 files changed, 436 insertions(+), 65 deletions(-) diff --git a/libcxx/CREDITS.TXT b/libcxx/CREDITS.TXT index aa3c8cf1a8c6..6ed18072de9a 100644 --- a/libcxx/CREDITS.TXT +++ b/libcxx/CREDITS.TXT @@ -98,7 +98,7 @@ E: lebrungrandt@ornl.gov D: Implementation of mdspan. N: Microsoft Corporation -D: Contributed floating-point to_chars. +D: Contributed floating-point to_chars and an implementation of exception_ptr for Windows. N: Bruce Mitchener, Jr. E: bruce.mitchener@gmail.com diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst index 9f9927095a61..556fea76d33a 100644 --- a/libcxx/docs/ReleaseNotes/22.rst +++ b/libcxx/docs/ReleaseNotes/22.rst @@ -128,6 +128,9 @@ Potentially breaking changes ``_LIBCPP_ABI_NO_ITERATOR_BASES``. If you are using this flag and care about ABI stability, you should set ``_LIBCPP_ABI_NO_REVERSE_ITERATOR_SECOND_MEMBER`` as well. +- ``std::exception_ptr`` on Windows no longer relies on STL and is now implemented entirely within libc++. + The implementation was donated by Microsoft and has been refactored to follow libc++ standards and conventions. + Announcements About Future Releases ----------------------------------- @@ -154,5 +157,8 @@ ABI Affecting Changes ``_LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF``. Please inform the libc++ team if you need this flag, since it will be removed in LLVM 24 if there is no evidence that it's required. +- ``std::exception_ptr`` now uses the same layout on Windows as on other platforms, changing the size from two to one + pointer. + Build System Changes -------------------- diff --git a/libcxx/include/__exception/exception_ptr.h b/libcxx/include/__exception/exception_ptr.h index f7ed00d55583..4ba107accdb7 100644 --- a/libcxx/include/__exception/exception_ptr.h +++ b/libcxx/include/__exception/exception_ptr.h @@ -62,8 +62,6 @@ _LIBCPP_OVERRIDABLE_FUNC_VIS __cxa_exception* __cxa_init_primary_exception( _LIBCPP_BEGIN_UNVERSIONED_NAMESPACE_STD _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS -#ifndef _LIBCPP_ABI_MICROSOFT - inline _LIBCPP_HIDE_FROM_ABI void swap(exception_ptr& __x, exception_ptr& __y) _NOEXCEPT; class _LIBCPP_EXPORTED_FROM_ABI exception_ptr { @@ -74,6 +72,8 @@ class _LIBCPP_EXPORTED_FROM_ABI exception_ptr { template friend _LIBCPP_HIDE_FROM_ABI exception_ptr __make_exception_ptr_explicit(_Ep&) _NOEXCEPT; + friend _LIBCPP_EXPORTED_FROM_ABI exception_ptr __copy_exception_ptr(void*, const void*); + public: // exception_ptr is basically a COW string so it is trivially relocatable. using __trivially_relocatable _LIBCPP_NODEBUG = exception_ptr; @@ -113,6 +113,8 @@ inline _LIBCPP_HIDE_FROM_ABI void swap(exception_ptr& __x, exception_ptr& __y) _ std::swap(__x.__ptr_, __y.__ptr_); } +#ifndef _LIBCPP_ABI_MICROSOFT + # if _LIBCPP_HAS_EXCEPTIONS # if _LIBCPP_HAS_RTTI && _LIBCPP_AVAILABILITY_HAS_INIT_PRIMARY_EXCEPTION template @@ -180,34 +182,7 @@ _LIBCPP_HIDE_FROM_ABI exception_ptr make_exception_ptr(_Ep) _NOEXCEPT { #else // defined(_LIBCPP_ABI_MICROSOFT) -class _LIBCPP_EXPORTED_FROM_ABI exception_ptr { - _LIBCPP_DIAGNOSTIC_PUSH - _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wunused-private-field") - void* __ptr1_; - void* __ptr2_; - _LIBCPP_DIAGNOSTIC_POP - -public: - exception_ptr() _NOEXCEPT; - exception_ptr(nullptr_t) _NOEXCEPT; - exception_ptr(const exception_ptr& __other) _NOEXCEPT; - exception_ptr& operator=(const exception_ptr& __other) _NOEXCEPT; - exception_ptr& operator=(nullptr_t) _NOEXCEPT; - ~exception_ptr() _NOEXCEPT; - explicit operator bool() const _NOEXCEPT; -}; - -_LIBCPP_EXPORTED_FROM_ABI bool operator==(const exception_ptr& __x, const exception_ptr& __y) _NOEXCEPT; - -inline _LIBCPP_HIDE_FROM_ABI bool operator!=(const exception_ptr& __x, const exception_ptr& __y) _NOEXCEPT { - return !(__x == __y); -} - -_LIBCPP_EXPORTED_FROM_ABI void swap(exception_ptr&, exception_ptr&) _NOEXCEPT; - _LIBCPP_EXPORTED_FROM_ABI exception_ptr __copy_exception_ptr(void* __except, const void* __ptr); -_LIBCPP_EXPORTED_FROM_ABI exception_ptr current_exception() _NOEXCEPT; -[[__noreturn__]] _LIBCPP_EXPORTED_FROM_ABI void rethrow_exception(exception_ptr); // This is a built-in template function which automagically extracts the required // information. diff --git a/libcxx/src/support/runtime/exception_pointer_msvc.ipp b/libcxx/src/support/runtime/exception_pointer_msvc.ipp index 4141e0312349..72360a2d791b 100644 --- a/libcxx/src/support/runtime/exception_pointer_msvc.ipp +++ b/libcxx/src/support/runtime/exception_pointer_msvc.ipp @@ -7,62 +7,452 @@ // //===----------------------------------------------------------------------===// -#include -#include -#include +_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wmultichar") -_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*); +#include <__exception/exception_ptr.h> +#include <__memory/shared_count.h> +#include +#include + +#include +#include +struct _ThrowInfo; +#include +#include +#include // alloca + +// 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(__current_exception()); +} + +inline void __call_member_function_0(void* __this, void* __mfn) { + auto __fn = reinterpret_cast(__mfn); + __fn(__this); +} + +inline void __call_member_function_1(void* __this, void* __mfn, void* __arg) { + auto __fn = reinterpret_cast(__mfn); + __fn(__this, __arg); +} + +inline void __call_member_function_2(void* __this, void* __mfn, void* __arg1, int __arg2) { + auto __fn = reinterpret_cast(__mfn); + __fn(__this, __arg1, __arg2); +} + +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; // No SEH to chain. + __record.ExceptionAddress = nullptr; // Address of exception. This will be overwritten by OS. + __record.NumberParameters = EH_EXCEPTION_PARAMETERS; + __record.ExceptionInformation[0] = EH_MAGIC_NUMBER1; + __record.ExceptionInformation[1] = reinterpret_cast(__except_obj); + + if (__throw_info && (__throw_info->attributes & TI_IsWinRT)) { + // The pointer to the ExceptionInfo structure is stored sizeof(void*) in front of each WinRT Exception Info. + const auto __wei = (*static_cast<__winrt_exception_info** const*>(const_cast(__except_obj)))[-1]; + __throw_info = __wei->__throw_info; + } + + __record.ExceptionInformation[2] = reinterpret_cast(__throw_info); + +#if _EH_RELATIVE_TYPEINFO + void* __throw_image_base = + __throw_info ? RtlPcToFileHeader(const_cast(static_cast(__throw_info)), &__throw_image_base) + : nullptr; + __record.ExceptionInformation[3] = reinterpret_cast(__throw_image_base); +#endif // _EH_RELATIVE_TYPEINFO + + // If the throw info indicates this throw is from a pure region, set the magic number to the Pure one, so only a + // pure-region catch will see it. + // + // Also use the Pure magic number on 64-bit platforms if we were unable to determine an image base, since that was the + // old way to determine a pure throw, before the TI_IsPure bit was added to the FuncInfo attributes field. + if (__throw_info && ((__throw_info->attributes & TI_IsPure) +#if _EH_RELATIVE_TYPEINFO + || !__throw_image_base +#endif // _EH_RELATIVE_TYPEINFO + )) + __record.ExceptionInformation[0] = EH_PURE_MAGIC_NUMBER1; +} + +void __copy_exception_record(_EXCEPTION_RECORD& __dest, const _EXCEPTION_RECORD& __src) noexcept { + __dest.ExceptionCode = __src.ExceptionCode; + // We force EXCEPTION_NONCONTINUABLE because rethrow_exception is [[noreturn]]. + __dest.ExceptionFlags = __src.ExceptionFlags | EXCEPTION_NONCONTINUABLE; + __dest.ExceptionRecord = nullptr; // We don't chain SEH exceptions. + __dest.ExceptionAddress = nullptr; // Useless field to copy. It will be overwritten by RaiseException(). + const auto __parameters = __src.NumberParameters; + __dest.NumberParameters = __parameters; + + // Copy the number of parameters in use. + constexpr auto __max_parameters = static_cast(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 // _EH_RELATIVE_TYPEINFO +) { + // Copy an object of type denoted by *_PType from __src to __dDest; throws whatever the copy ctor of the type denoted + // by *_PType throws. + if ((__type->properties & CT_IsSimpleType) || __type->copyFunction == 0) { + std::memcpy(__dest, __src, __type->sizeOrOffset); + + if (__type->properties & CT_IsWinRTHandle) { + const auto __unknown = *static_cast(const_cast(__src)); + if (__unknown) + __unknown->AddRef(); + } + return; + } + +#if _EH_RELATIVE_TYPEINFO + const auto __copy_func = reinterpret_cast(__throw_image_base + __type->copyFunction); +#else // _EH_RELATIVE_TYPEINFO + const auto __copy_func = __type->copyFunction; +#endif // _EH_RELATIVE_TYPEINFO + + const auto __adjusted = __AdjustPointer(const_cast(__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 +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(__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(__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(__cpp_eh_record.params.pThrowImageBase); + const auto* __catchable_type_array = reinterpret_cast( + static_cast(__throw_info->pCatchableTypeArray) + __throw_image_base); + const auto* __type = reinterpret_cast( + static_cast(__catchable_type_array->arrayOfCatchableTypes[0]) + __throw_image_base); +#else // _EH_RELATIVE_TYPEINFO + const auto* __type = __throw_info->pCatchableTypeArray->arrayOfCatchableTypes[0]; +#endif // _EH_RELATIVE_TYPEINFO + if (__throw_info->pmfnUnwind) { + // The exception was a user defined type with a nontrivial destructor, call it. +#if _EH_RELATIVE_TYPEINFO + __call_member_function_0(__cpp_eh_record.params.pExceptionObject, + reinterpret_cast(__throw_info->pmfnUnwind + __throw_image_base)); +#else // _EH_RELATIVE_TYPEINFO + __call_member_function_0(__cpp_eh_record.params.pExceptionObject, __throw_info->pmfnUnwind); +#endif // _EH_RELATIVE_TYPEINFO + } else if (__type->properties & CT_IsWinRTHandle) { + const auto* __unknown = *static_cast(__cpp_eh_record.params.pExceptionObject); + if (__unknown) + const_cast(__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"); + +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::__get(); + return; + } + + __dest = ::new (__rx_raw) __exception_ptr_normal(__record); +} + +void __assign_cpp_exception_ptr_from_record(void*& __dest, const EHExceptionRecord& __record) noexcept { + // Construct a reference count control block for the C++ exception recorded by __record, and bind it to __dest. + // + // * If allocating memory for the reference count control block fails, sets __dest to bad_alloc. + // * If copying the exception object referred to __record throws, constructs a reference count control block for that + // exception instead. + // * If copying the exception object thrown by copying the original exception object throws, sets __dest to + // bad_exception. + const auto* __throw_info = __record.params.pThrowInfo; +#if _EH_RELATIVE_TYPEINFO + const auto __throw_image_base = reinterpret_cast(__record.params.pThrowImageBase); + const auto* __catchable_type_array = reinterpret_cast( + static_cast(__throw_info->pCatchableTypeArray) + __throw_image_base); + const auto* __type = reinterpret_cast( + static_cast(__catchable_type_array->arrayOfCatchableTypes[0]) + __throw_image_base); +#else // _EH_RELATIVE_TYPEINFO + const auto* __type = __throw_info->pCatchableTypeArray->arrayOfCatchableTypes[0]; +#endif // _EH_RELATIVE_TYPEINFO + + const auto __except_obj_size = static_cast(__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::__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 // _EH_RELATIVE_TYPEINFO + ); + + const auto* __rx = ::new (__rx_raw) __exception_ptr_normal(reinterpret_cast(__record)); + reinterpret_cast(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 (...) { // Copying the exception object threw an exception. + // Exception thrown by the original exception's copy ctor. + const auto* __inner_record_ptr = __get_current_exception(); + if (!__inner_record_ptr) { + std::free(__rx_raw); + __dest = __exception_ptr_static::__get(); + return; + } + const auto& __inner_record = *__inner_record_ptr; + if (__inner_record.ExceptionCode == MANAGED_EXCEPTION_CODE || + __inner_record.ExceptionCode == MANAGED_EXCEPTION_CODE_V4) { + // We don't support managed exceptions and don't want to say there's no active exception, so give up and say + // bad_exception. + std::free(__rx_raw); + __dest = __exception_ptr_static::__get(); + return; + } + + if (!PER_IS_MSVC_PURE_OR_NATIVE_EH(&__inner_record)) { // Catching a non-C++ exception depends on /EHa. + __assign_seh_exception_ptr_from_record( + __dest, reinterpret_cast(__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(__inner_record.params.pThrowImageBase); + const auto* __inner_catchable_type_array = reinterpret_cast( + static_cast(__inner_throw->pCatchableTypeArray) + __inner_throw_image_base); + const auto* __inner_type = reinterpret_cast( + static_cast(__inner_catchable_type_array->arrayOfCatchableTypes[0]) + __inner_throw_image_base); +#else // _EH_RELATIVE_TYPEINFO + const auto* __inner_type = __inner_throw->pCatchableTypeArray->arrayOfCatchableTypes[0]; +#endif // _EH_RELATIVE_TYPEINFO + + const auto __inner_except_size = static_cast(__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::__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 // _EH_RELATIVE_TYPEINFO + ); + } catch (...) { // Copying the exception emitted while copying the original exception also threw, give up. + std::free(__rx_raw); + __dest = __exception_ptr_static::__get(); + return; + } + + // This next block must be duplicated inside the catch (even though it looks identical to the block in the try) so + // that __inner_record is held alive; exiting the catch will destroy it. + const auto* __rx = + ::new (__rx_raw) __exception_ptr_normal(reinterpret_cast(__inner_record)); + reinterpret_cast(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 { __ExceptionPtrCreate(this); } -exception_ptr::exception_ptr(nullptr_t) noexcept { __ExceptionPtrCreate(this); } +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(const exception_ptr& __other) noexcept { __ExceptionPtrCopy(this, &__other); } exception_ptr& exception_ptr::operator=(const exception_ptr& __other) noexcept { - __ExceptionPtrAssign(this, &__other); + 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::operator=(nullptr_t) noexcept { - exception_ptr dummy; - __ExceptionPtrAssign(this, &dummy); - 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::~exception_ptr() noexcept { __ExceptionPtrDestroy(this); } - -exception_ptr::operator bool() const noexcept { return __ExceptionPtrToBool(this); } - -bool operator==(const exception_ptr& __x, const exception_ptr& __y) noexcept { - return __ExceptionPtrCompare(&__x, &__y); -} - -void swap(exception_ptr& lhs, exception_ptr& rhs) noexcept { __ExceptionPtrSwap(&rhs, &lhs); } - 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(const_cast(__ptr))); + __assign_cpp_exception_ptr_from_record(__ret.__ptr_, reinterpret_cast(__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(*__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(__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(__cpp_record.params.pThrowImageBase); + const auto* __catchable_type_array = + reinterpret_cast(__throw_image_base + __throw_info->pCatchableTypeArray); +#else // _EH_RELATIVE_TYPEINFO + const auto* __catchable_type_array = __throw_info->pCatchableTypeArray; +#endif // _EH_RELATIVE_TYPEINFO + + if (__catchable_type_array->nCatchableTypes <= 0) + terminate(); + +#if _EH_RELATIVE_TYPEINFO + const auto* __type = reinterpret_cast( + static_cast(__catchable_type_array->arrayOfCatchableTypes[0]) + __throw_image_base); +#else // _EH_RELATIVE_TYPEINFO + const auto* __type = __throw_info->pCatchableTypeArray->arrayOfCatchableTypes[0]; +#endif // _EH_RELATIVE_TYPEINFO + +#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 // _EH_RELATIVE_TYPEINFO + ); + + __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()) {}