diff --git a/.upstream-tests/test/std/utilities/expected/expected.expected/observers/value.pass.cpp b/.upstream-tests/test/std/utilities/expected/expected.expected/observers/value.pass.cpp index fb27e61ecc..734e371061 100644 --- a/.upstream-tests/test/std/utilities/expected/expected.expected/observers/value.pass.cpp +++ b/.upstream-tests/test/std/utilities/expected/expected.expected/observers/value.pass.cpp @@ -63,17 +63,72 @@ __host__ __device__ constexpr bool test() { return true; } -__host__ __device__ void testException() { #ifndef TEST_HAS_NO_EXCEPTIONS - // int - { - const cuda::std::expected e(cuda::std::unexpect, 5); +enum constructor_used : unsigned int { + default_constructed = 1 << 0, + lvalue_constructed = 1 << 1, + const_lvalue_constructed = 1 << 2, + rvalue_constructed = 1 << 3, + const_rvalue_constructed = 1 << 4, +}; + +struct track_construction { + track_construction() = default; + track_construction& operator=(const track_construction&) = default; + track_construction& operator=(track_construction&&) = default; + + __host__ __device__ track_construction(track_construction& other) noexcept + : val_(other.val_ | lvalue_constructed) {} + __host__ __device__ track_construction(const track_construction& other) noexcept + : val_(other.val_ | const_lvalue_constructed) {} + __host__ __device__ track_construction(track_construction&& other) noexcept + : val_(other.val_ | rvalue_constructed) {} + __host__ __device__ track_construction(const track_construction&& other) noexcept + : val_(other.val_ | const_rvalue_constructed) {} + + unsigned int val_ = default_constructed; +}; + +__host__ __device__ void testException() { + { // & try { + cuda::std::expected e(cuda::std::unexpect); (void) e.value(); assert(false); } catch (const cuda::std::bad_expected_access& ex) { - assert(ex.error() == 5); + // We need to pass the error via `as_const` + assert(ex.error().val_ == const_lvalue_constructed | rvalue_constructed); + } + } + + { // const & + try { + cuda::std::expected e(cuda::std::unexpect); + (void) cuda::std::as_const(e).value(); + assert(false); + } catch (const cuda::std::bad_expected_access& ex) { + assert(ex.error().val_ == const_lvalue_constructed | rvalue_constructed); + } + } + + { // && + try { + cuda::std::expected e(cuda::std::unexpect); + (void) cuda::std::move(e).value(); + assert(false); + } catch (const cuda::std::bad_expected_access& ex) { + assert(ex.error().val_ == rvalue_constructed); + } + } + + { // const && + try { + cuda::std::expected e(cuda::std::unexpect); + (void) cuda::std::move(cuda::std::as_const(e)).value(); + assert(false); + } catch (const cuda::std::bad_expected_access& ex) { + assert(ex.error().val_ == const_rvalue_constructed | rvalue_constructed); } } @@ -87,15 +142,16 @@ __host__ __device__ void testException() { assert(ex.error() == 5); } } - -#endif // TEST_HAS_NO_EXCEPTIONS } +#endif // TEST_HAS_NO_EXCEPTIONS int main(int, char**) { test(); #if TEST_STD_VER > 17 && defined(_LIBCUDACXX_ADDRESSOF) static_assert(test(), ""); #endif // TEST_STD_VER > 17 && defined(_LIBCUDACXX_ADDRESSOF) +#ifndef TEST_HAS_NO_EXCEPTIONS testException(); +#endif // TEST_HAS_NO_EXCEPTIONS return 0; } diff --git a/.upstream-tests/test/std/utilities/expected/expected.void/observers/value.pass.cpp b/.upstream-tests/test/std/utilities/expected/expected.void/observers/value.pass.cpp index d44dce123b..142fb0c1a8 100644 --- a/.upstream-tests/test/std/utilities/expected/expected.void/observers/value.pass.cpp +++ b/.upstream-tests/test/std/utilities/expected/expected.void/observers/value.pass.cpp @@ -52,18 +52,73 @@ __host__ __device__ constexpr bool test() { return true; } +#ifndef TEST_HAS_NO_EXCEPTIONS + +enum constructor_used : unsigned int { + default_constructed = 1 << 0, + lvalue_constructed = 1 << 1, + const_lvalue_constructed = 1 << 2, + rvalue_constructed = 1 << 3, + const_rvalue_constructed = 1 << 4, +}; + +struct track_construction { + track_construction() = default; + track_construction& operator=(const track_construction&) = default; + track_construction& operator=(track_construction&&) = default; + + __host__ __device__ track_construction(track_construction& other) noexcept + : val_(other.val_ | lvalue_constructed) {} + __host__ __device__ track_construction(const track_construction& other) noexcept + : val_(other.val_ | const_lvalue_constructed) {} + __host__ __device__ track_construction(track_construction&& other) noexcept + : val_(other.val_ | rvalue_constructed) {} + __host__ __device__ track_construction(const track_construction&& other) noexcept + : val_(other.val_ | const_rvalue_constructed) {} + + unsigned int val_ = default_constructed; +}; __host__ __device__ void testException() { -#ifndef TEST_HAS_NO_EXCEPTIONS + { // & + try { + cuda::std::expected e(cuda::std::unexpect); + (void) e.value(); + assert(false); + } catch (const cuda::std::bad_expected_access& ex) { + // We bind against the `const &` overload + assert(ex.error().val_ == const_lvalue_constructed | rvalue_constructed); + } + } - // int - { - const cuda::std::expected e(cuda::std::unexpect, 5); + { // const & try { - e.value(); + cuda::std::expected e(cuda::std::unexpect); + (void) cuda::std::as_const(e).value(); assert(false); } catch (const cuda::std::bad_expected_access& ex) { - assert(ex.error() == 5); + assert(ex.error().val_ == const_lvalue_constructed | rvalue_constructed); + } + } + + { // && + try { + cuda::std::expected e(cuda::std::unexpect); + (void) cuda::std::move(e).value(); + assert(false); + } catch (const cuda::std::bad_expected_access& ex) { + assert(ex.error().val_ == rvalue_constructed); + } + } + + { // const && + try { + cuda::std::expected e(cuda::std::unexpect); + (void) cuda::std::move(cuda::std::as_const(e)).value(); + assert(false); + } catch (const cuda::std::bad_expected_access& ex) { + // We bind against the const& overload + assert(ex.error().val_ == const_lvalue_constructed | rvalue_constructed); } } @@ -71,19 +126,21 @@ __host__ __device__ void testException() { { cuda::std::expected e(cuda::std::unexpect, 5); try { - cuda::std::move(e).value(); + (void) cuda::std::move(e).value(); assert(false); } catch (const cuda::std::bad_expected_access& ex) { assert(ex.error() == 5); } } - -#endif // TEST_HAS_NO_EXCEPTIONS } +#endif // TEST_HAS_NO_EXCEPTIONS int main(int, char**) { test(); static_assert(test(), ""); +#ifndef TEST_HAS_NO_EXCEPTIONS testException(); +#endif // TEST_HAS_NO_EXCEPTIONS + return 0; } diff --git a/include/cuda/std/detail/libcxx/include/__expected/expected.h b/include/cuda/std/detail/libcxx/include/__expected/expected.h index 2e02cf72c4..022e35190c 100644 --- a/include/cuda/std/detail/libcxx/include/__expected/expected.h +++ b/include/cuda/std/detail/libcxx/include/__expected/expected.h @@ -51,6 +51,7 @@ #include "../__type_traits/negation.h" #include "../__type_traits/remove_cv.h" #include "../__type_traits/remove_cvref.h" +#include "../__utility/as_const.h" #include "../__utility/exception_guard.h" #include "../__utility/forward.h" #include "../__utility/in_place.h" @@ -509,7 +510,8 @@ class expected : private __expected_move_assign<_Tp, _Err> _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY constexpr bool has_value() const noexcept { return this->__has_val_; } - _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY constexpr const _Tp& value() const& { + _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY constexpr + const _Tp& value() const& { if (!this->__has_val_) { __expected::__throw_bad_expected_access<_Err>(this->__union_.__unex_); } @@ -519,13 +521,17 @@ class expected : private __expected_move_assign<_Tp, _Err> _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY constexpr _Tp& value() & { if (!this->__has_val_) { - __expected::__throw_bad_expected_access<_Err>(this->__union_.__unex_); + __expected::__throw_bad_expected_access<_Err>(_CUDA_VSTD::as_const(this->__union_.__unex_)); } return this->__union_.__val_; } _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY constexpr const _Tp&& value() const&& { + static_assert(_LIBCUDACXX_TRAIT(is_copy_constructible, _Err), + "expected::value() const&& requires is_copy_constructible_v"); + static_assert(_LIBCUDACXX_TRAIT(is_constructible, _Err, const _Err&&), + "expected::value() const&& requires is_constructible_v"); if (!this->__has_val_) { __expected::__throw_bad_expected_access<_Err>(_CUDA_VSTD::move(this->__union_.__unex_)); } @@ -534,6 +540,10 @@ class expected : private __expected_move_assign<_Tp, _Err> _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY constexpr _Tp&& value() && { + static_assert(_LIBCUDACXX_TRAIT(is_copy_constructible, _Err), + "expected::value() && requires is_copy_constructible_v"); + static_assert(_LIBCUDACXX_TRAIT(is_move_constructible, _Err), + "expected::value() && requires is_move_constructible"); if (!this->__has_val_) { __expected::__throw_bad_expected_access<_Err>(_CUDA_VSTD::move(this->__union_.__unex_)); } @@ -1324,6 +1334,10 @@ class expected : private __expected_move_assign _LIBCUDACXX_HIDE_FROM_ABI _LIBCUDACXX_INLINE_VISIBILITY constexpr void value() && { + static_assert(_LIBCUDACXX_TRAIT(is_copy_constructible, _Err), + "expected::value() && requires is_copy_constructible_v"); + static_assert(_LIBCUDACXX_TRAIT(is_move_constructible, _Err), + "expected::value() && requires is_move_constructible_v"); if (!this->__has_val_) { __expected::__throw_bad_expected_access<_Err>(_CUDA_VSTD::move(this->__union_.__unex_)); }