Skip to content

Commit

Permalink
[libc++] [P2927] std::try_cast
Browse files Browse the repository at this point in the history
This implements my latest proposed wording for P2927, namely:
> Mandates: E is a cv-unqualified complete object type. E is not an array type.
> E is not a pointer or pointer-to-member type. [Note: When E is a pointer or
> pointer-to-member type, a handler of type const E& can match without binding
> to the exception object itself. —end note]
> Returns: A pointer to the exception object referred to by p, if p is not null
> and a handler of type const E& would be a match [except.handle] for that
> exception object. Otherwise, nullptr.
  • Loading branch information
Quuxplusone committed Feb 13, 2024
1 parent 3f549be commit 6e20a0b
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 0 deletions.
36 changes: 36 additions & 0 deletions libcxx/include/__exception/exception_ptr.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
#include <__memory/addressof.h>
#include <__memory/construct_at.h>
#include <__type_traits/decay.h>
#include <__type_traits/is_array.h>
#include <__type_traits/is_member_pointer.h>
#include <__type_traits/is_object.h>
#include <__type_traits/is_pointer.h>
#include <__type_traits/is_same.h>
#include <__type_traits/remove_cvref.h>
#include <cstddef>
#include <cstdlib>
#include <new>
Expand Down Expand Up @@ -159,6 +165,36 @@ _LIBCPP_HIDE_FROM_ABI exception_ptr make_exception_ptr(_Ep __e) _NOEXCEPT {
}

#endif // _LIBCPP_ABI_MICROSOFT

template <class _Ep>
_LIBCPP_HIDE_FROM_ABI const __remove_cvref_t<_Ep>* try_cast(const exception_ptr& __p) _NOEXCEPT {
static_assert(is_object<_Ep>::value && is_same<__remove_cvref_t<_Ep>, _Ep>::value &&
!is_array<_Ep>::value && sizeof(_Ep) >= 0,
"E must be a complete cv-unqualified object type, and not an array type");
static_assert(!is_pointer<_Ep>::value && !is_member_pointer<_Ep>::value,
"E must not be a pointer or pointer-to-member type");
if (__p == nullptr) {
return nullptr;
}
#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
try {
std::rethrow_exception(__p);
} catch (const _Ep& __ex) {
try {
std::rethrow_exception(__p);
} catch (const _Ep& __also_ex) {
_LIBCPP_ASSERT(std::addressof(__ex) == std::addressof(__also_ex), "this should never happen");
}
return std::addressof(__ex);
} catch (...) {
}
return nullptr;
#else
((void)__p);
std::abort();
#endif // _LIBCPP_HAS_NO_EXCEPTIONS
}

} // namespace std

#endif // _LIBCPP___EXCEPTION_EXCEPTION_PTR_H
1 change: 1 addition & 0 deletions libcxx/include/exception
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ typedef unspecified exception_ptr;
exception_ptr current_exception() noexcept;
void rethrow_exception [[noreturn]] (exception_ptr p);
template<class E> exception_ptr make_exception_ptr(E e) noexcept;
template<class E> const E* try_cast(const exception_ptr& p) noexcept; // P2927R1
class nested_exception
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: no-exceptions
// <exception>

// template<class E> const E* try_cast(const exception_ptr& p);

#include <exception>
#include <cassert>

#include "test_macros.h"

struct A
{
static int constructed;
int data_;

A(int data = 0) : data_(data) {++constructed;}
~A() {--constructed;}
A(const A& a) : data_(a.data_) {++constructed;}
};

int A::constructed = 0;

struct Abstract {
explicit Abstract() = default;
Abstract(const Abstract&) = default;
virtual int f() const = 0;
virtual ~Abstract() = default;
};
struct Concrete : Abstract {
int f() const override { return 42; };
};
struct Incomplete;

int main(int, char**)
{
{
std::exception_ptr p = std::make_exception_ptr(A(5));
int cted = A::constructed;
#ifdef _LIBCPP_ABI_MICROSOFT
// On Windows exception_ptr copies the exception
assert(cted == 2);
#else
assert(cted == 1);
#endif
ASSERT_SAME_TYPE(decltype(std::try_cast<A>(p)), const A*);
ASSERT_SAME_TYPE(decltype(std::try_cast<A>(nullptr)), const A*);
const auto *pa = std::try_cast<A>(p);
// The in-flight exception object is not copied
assert(A::constructed == cted);
const auto *pb = std::try_cast<A>(p);
assert(A::constructed == cted);
// The in-flight exception object's address doesn't change
assert(pa == pb);
assert(pa->data_ == 5);
}
assert(A::constructed == 0);

{
std::exception_ptr p;
assert(std::try_cast<A>(p) == nullptr);
assert(std::try_cast<A>(nullptr) == nullptr);
}

// Try modifying an object in flight
{
try {
try {
throw A(1);
} catch (const A& a) {
std::exception_ptr p = std::current_exception();
const auto *pa = std::try_cast<A>(p);
assert(pa == &a);
assert(pa->data_ == 1);
const_cast<A*>(pa)->data_ = 2;
std::rethrow_exception(p);
assert(false);
}
} catch (const A& a2) {
assert(a2.data_ == 2);
}
assert(A::constructed == 0);
}

// Try catching some exotic types
{
std::exception_ptr p = std::make_exception_ptr(1);
assert(std::try_cast<int>(p) != nullptr);
assert(*std::try_cast<int>(p) == 1);
assert(std::try_cast<long>(p) == nullptr);
assert(std::try_cast<unsigned int>(p) == nullptr);

p = std::make_exception_ptr(Concrete());
assert(std::try_cast<Abstract>(p) != nullptr);
assert(std::try_cast<Abstract>(p)->f() == 42);
}
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: no-exceptions
// <exception>

// template<class E> const E* try_cast(const exception_ptr& p);
// Mandates:
// - T is a complete object type.
// - T is not cv-qualified.
// - T is not an array type.

#include <exception>

struct Incomplete;
struct Base { int x; int f(); };

int main(int, char**)
{
std::exception_ptr p;
(void)std::try_cast<void>(p); //expected-error@*:* {{invalid application of 'sizeof' to an incomplete type 'void'}}
(void)std::try_cast<int&>(p); //expected-error@*:* {{E must be a complete cv-unqualified object type, and not an array type}}
(void)std::try_cast<int&&>(p); //expected-error@*:* {{E must be a complete cv-unqualified object type, and not an array type}}
(void)std::try_cast<int*&>(p); //expected-error@*:* {{E must be a complete cv-unqualified object type, and not an array type}}
(void)std::try_cast<const int>(p); //expected-error@*:* {{E must be a complete cv-unqualified object type, and not an array type}}
(void)std::try_cast<volatile int>(p); //expected-error@*:* {{E must be a complete cv-unqualified object type, and not an array type}}
//expected-error@*:* {{cannot initialize return object}}
(void)std::try_cast<Incomplete>(p); //expected-error@*:* {{invalid application of 'sizeof' to an incomplete type 'Incomplete'}}
(void)std::try_cast<int()>(p); //expected-error@*:* {{invalid application of 'sizeof' to a function type}}
(void)std::try_cast<int[]>(p); //expected-error@*:* {{invalid application of 'sizeof' to an incomplete type 'int[]'}}
(void)std::try_cast<int[5]>(p); //expected-error@*:* {{E must be a complete cv-unqualified object type, and not an array type}}
(void)std::try_cast<int(&)()>(p); //expected-error@*:* {{invalid application of 'sizeof' to a function type}}
(void)std::try_cast<int(&)[]>(p); //expected-error@*:* {{invalid application of 'sizeof' to an incomplete type 'int[]'}}
(void)std::try_cast<int(&)[5]>(p); //expected-error@*:* {{E must be a complete cv-unqualified object type}}

(void)std::try_cast<void*>(p); //expected-error@*:* {{E must not be a pointer or pointer-to-member type}}
(void)std::try_cast<int*>(p); //expected-error@*:* {{E must not be a pointer or pointer-to-member type}}
(void)std::try_cast<int(*)[5]>(p); //expected-error@*:* {{E must not be a pointer or pointer-to-member type}}
(void)std::try_cast<Base*>(p); //expected-error@*:* {{E must not be a pointer or pointer-to-member type}}
(void)std::try_cast<const Base*>(p); //expected-error@*:* {{E must not be a pointer or pointer-to-member type}}
(void)std::try_cast<Incomplete*>(p); //expected-error@*:* {{E must not be a pointer or pointer-to-member type}}
(void)std::try_cast<int(*)()>(p); //expected-error@*:* {{E must not be a pointer or pointer-to-member type}}
(void)std::try_cast<int Base::*>(p); //expected-error@*:* {{E must not be a pointer or pointer-to-member type}}
(void)std::try_cast<int (Base::*)()>(p); //expected-error@*:* {{E must not be a pointer or pointer-to-member type}}

return 0;
}

0 comments on commit 6e20a0b

Please sign in to comment.