diff --git a/libcxx/include/optional b/libcxx/include/optional index 568c86556d156..91c0167ae8f06 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -477,14 +477,21 @@ struct __optional_storage_base : __optional_destruct_base<_Tp> { } } - template - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from_val(_Up&& __val) { - this->__get() = std::forward<_Up>(__val); - } - - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_storage_base& __rhs) { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void + __swap(__optional_storage_base& __rhs) noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>) { using std::swap; - swap(this->__get(), __rhs.__get()); + if (this->has_value() == __rhs.has_value()) { + if (this->has_value()) + swap(this->__get(), __rhs.__get()); + } else { + if (this->has_value()) { + __rhs.__construct(std::move(this->__get())); + this->reset(); + } else { + this->__construct(std::move(__rhs.__get())); + __rhs.reset(); + } + } } }; @@ -496,21 +503,25 @@ struct __optional_storage_base<_Tp, true> { _LIBCPP_HIDE_FROM_ABI constexpr __optional_storage_base() noexcept : __value_(nullptr) {} + template + _LIBCPP_HIDE_FROM_ABI constexpr void __convert_init_ref_val(_Up&& __val) noexcept { + _Tp& __r(std::forward<_Up>(__val)); + __value_ = std::addressof(__r); + } + template - _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_storage_base(in_place_t, _UArg&& __uarg) - : __value_(std::addressof(__uarg)) { + _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_storage_base(in_place_t, _UArg&& __uarg) { static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>, "Attempted to construct a reference element in optional from a " "possible temporary"); + __convert_init_ref_val(std::forward<_UArg>(__uarg)); } _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept { __value_ = nullptr; } [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __value_ != nullptr; } - _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() const& noexcept { return *__value_; } - - _LIBCPP_HIDE_FROM_ABI constexpr value_type&& __get() const&& noexcept { return std::forward(*__value_); } + _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() const noexcept { return *__value_; } template _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct(_UArg&& __val) { @@ -518,7 +529,7 @@ struct __optional_storage_base<_Tp, true> { static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>, "Attempted to construct a reference element in tuple from a " "possible temporary"); - __value_ = std::addressof(__val); + __convert_init_ref_val(std::forward<_UArg>(__val)); } template @@ -540,11 +551,6 @@ struct __optional_storage_base<_Tp, true> { } } - template - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from_val(_Up&& __val) noexcept { - __value_ = std::addressof(__val); - } - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __swap(__optional_storage_base& __rhs) noexcept { std::swap(__value_, __rhs.__value_); } @@ -706,7 +712,7 @@ struct __optional_iterator {}; # if _LIBCPP_STD_VER >= 26 template -struct __optional_iterator<_Tp, enable_if_t>> { +struct __optional_iterator<_Tp, enable_if_t>> { private: using __pointer _LIBCPP_NODEBUG = add_pointer_t<_Tp>; using __const_pointer _LIBCPP_NODEBUG = add_pointer_t; @@ -909,38 +915,61 @@ public: : __base(in_place, __il, std::forward<_Args>(__args)...) {} template ::template __enable_implicit<_Up>(), int> = 0> - _LIBCPP_HIDE_FROM_ABI constexpr optional(_Up&& __v) : __base(in_place, std::forward<_Up>(__v)) {} + _LIBCPP_HIDE_FROM_ABI constexpr optional(_Up&& __v) +# if _LIBCPP_STD_VER >= 26 + noexcept(is_lvalue_reference_v<_Tp> && is_nothrow_constructible_v<_Tp&, _Up>) +# endif + : __base(in_place, std::forward<_Up>(__v)) { + } template , enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), int> = 0> - _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Up&& __v) : __base(in_place, std::forward<_Up>(__v)) {} + _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Up&& __v) +# if _LIBCPP_STD_VER >= 26 + noexcept(is_lvalue_reference_v<_Tp> && is_nothrow_constructible_v<_Tp&, _Up>) +# endif + : __base(in_place, std::forward<_Up>(__v)) { + } // LWG2756: conditionally explicit conversion from const optional<_Up>& template ::template __enable_implicit<_Up>(), int> = 0> - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(const optional<_Up>& __v) { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(const optional<_Up>& __v) +# if _LIBCPP_STD_VER >= 26 + noexcept(is_lvalue_reference_v<_Tp> && is_nothrow_constructible_v<_Tp&, _Up&>) +# endif + { this->__construct_from(__v); } template ::template __enable_explicit<_Up>(), int> = 0> - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(const optional<_Up>& __v) { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(const optional<_Up>& __v) +# if _LIBCPP_STD_VER >= 26 + noexcept(is_lvalue_reference_v<_Tp> && is_nothrow_constructible_v<_Tp&, _Up&>) +# endif + { this->__construct_from(__v); } // LWG2756: conditionally explicit conversion from optional<_Up>&& template ::template __enable_implicit<_Up>(), int> = 0> - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(optional<_Up>&& __v) { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(optional<_Up>&& __v) +# if _LIBCPP_STD_VER >= 26 + noexcept(is_lvalue_reference_v<_Tp> && is_nothrow_constructible_v<_Tp&, _Up>) +# endif + { this->__construct_from(std::move(__v)); } template ::template __enable_explicit<_Up>(), int> = 0> - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(optional<_Up>&& __v) { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit optional(optional<_Up>&& __v) +# if _LIBCPP_STD_VER >= 26 + noexcept(is_lvalue_reference_v<_Tp> && is_nothrow_constructible_v<_Tp&, _Up>) +# endif + { this->__construct_from(std::move(__v)); } - // deleted optional constructors + // deleted optional constructors and additional optional constructors # if _LIBCPP_STD_VER >= 26 - template &, _Args...>, int> = 0> - requires __libcpp_opt_ref_ctor_deleted<_Up> - explicit optional(in_place_t, initializer_list<_Up>, _Args&&...) = delete; - + // optional(U&&) template ::template __enable_implicit<_Up>(), int> = 0> requires __libcpp_opt_ref_ctor_deleted<_Up> optional(_Up&&) = delete; @@ -950,6 +979,21 @@ public: requires __libcpp_opt_ref_ctor_deleted<_Up> explicit optional(_Up&&) = delete; + // optional(optional& rhs) + template + requires(!__libcpp_opt_ref_ctor_deleted<_Up>) && (!is_same_v, optional<_Up>>) && + (!is_same_v<_Tp&, _Up>) && is_constructible_v<_Tp&, _Up&> + _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up&, _Tp&>) + optional(optional<_Up>& __rhs) noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) { + this->__construct_from(__rhs); + } + + template + requires __libcpp_opt_ref_ctor_deleted<_Up> && (!is_same_v, optional<_Up>>) && + (!is_same_v<_Tp&, _Up>) && is_constructible_v<_Tp&, _Up&> + constexpr explicit optional(optional<_Up>& __rhs) noexcept = delete; + + // optional(const optional&) template ::template __enable_implicit<_Up>(), int> = 0> requires __libcpp_opt_ref_ctor_deleted<_Up> optional(const optional<_Up>&) = delete; @@ -958,6 +1002,7 @@ public: requires __libcpp_opt_ref_ctor_deleted<_Up> explicit optional(const optional<_Up>&) = delete; + // optional(optional&&) template ::template __enable_implicit<_Up>(), int> = 0> requires __libcpp_opt_ref_ctor_deleted<_Up> optional(optional<_Up>&&) = delete; @@ -965,6 +1010,20 @@ public: template ::template __enable_explicit<_Up>(), int> = 0> requires __libcpp_opt_ref_ctor_deleted<_Up> explicit optional(optional<_Up>&&) = delete; + + // optional(const optional&&) + template + requires(!__libcpp_opt_ref_ctor_deleted<_Up>) && (!is_same_v, optional<_Up>>) && + (!is_same_v<_Tp&, _Up>) && is_constructible_v<_Tp&, _Up> + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!is_convertible_v) + optional(const optional<_Up>&& __v) noexcept(is_nothrow_constructible_v<_Tp&, const _Up>) { + this->__construct_from(std::move(__v)); + } + + template + requires __libcpp_opt_ref_ctor_deleted<_Up> && (!is_same_v, optional<_Up>>) && + (!is_same_v<_Tp&, _Up>) && is_constructible_v<_Tp&, _Up> + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional(const optional<_Up>&& __v) noexcept = delete; # endif # if _LIBCPP_STD_VER >= 23 @@ -989,11 +1048,12 @@ public: enable_if_t<_And<_IsNotSame<__remove_cvref_t<_Up>, optional>, _Or<_IsNotSame<__remove_cvref_t<_Up>, _Tp>, _Not>>, is_constructible<_Tp, _Up>, - is_assignable<_Tp&, _Up>>::value, + is_assignable<_Tp&, _Up>, + is_object<_Tp>>::value, int> = 0> _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 optional& operator=(_Up&& __v) { if (this->has_value()) - this->__assign_from_val(std::forward<_Up>(__v)); + this->__get() = std::forward<_Up>(__v); else this->__construct(std::forward<_Up>(__v)); return *this; @@ -1013,8 +1073,12 @@ public: return *this; } - template , int> = 0> - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(_Args&&... __args) { + template , int> = 0> + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(_Args&&... __args) +# if _LIBCPP_STD_VER >= 26 + noexcept(is_lvalue_reference_v<_Tp> && is_nothrow_constructible_v<_Tp, _Args...>) +# endif + { reset(); this->__construct(std::forward<_Args>(__args)...); return this->__get(); @@ -1022,60 +1086,72 @@ public: template &, _Args...> -# if _LIBCPP_STD_VER >= 26 - && !reference_constructs_from_temporary_v<_Tp&, _Up> -# endif - , - int> = 0> + enable_if_t<__is_constructible_for_optional_initializer_list_v<_Tp, _Up, _Args...>, int> = 0> _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) { reset(); this->__construct(__il, std::forward<_Args>(__args)...); return this->__get(); } - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void - swap(optional& __opt) noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>) { - if (this->has_value() == __opt.has_value()) { - if (this->has_value()) - this->__swap(__opt); - } else { - if (this->has_value()) { - __opt.__construct(std::move(this->__get())); - reset(); - } else { - this->__construct(std::move(__opt.__get())); - __opt.reset(); - } - } + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(optional& __opt) noexcept( + (is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>) +# if _LIBCPP_STD_VER >= 26 + || is_lvalue_reference_v<_Tp> +# endif + ) { + this->__swap(__opt); } - _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp const> operator->() const noexcept { + _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp const> operator->() const noexcept +# if _LIBCPP_STD_VER >= 26 + requires(is_object_v<_Tp>) +# endif + { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value"); return std::addressof(this->__get()); } - _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp> operator->() noexcept { + _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp> operator->() noexcept +# if _LIBCPP_STD_VER >= 26 + requires(is_object_v<_Tp>) +# endif + { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value"); return std::addressof(this->__get()); } - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const& noexcept { + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const& noexcept +# if _LIBCPP_STD_VER >= 26 + requires(is_object_v<_Tp>) +# endif + { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value"); return this->__get(); } - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() & noexcept { + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() & noexcept +# if _LIBCPP_STD_VER >= 26 + requires(is_object_v<_Tp>) +# endif + { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value"); return this->__get(); } - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& operator*() && noexcept { + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& operator*() && noexcept +# if _LIBCPP_STD_VER >= 26 + requires(is_object_v<_Tp>) +# endif + { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value"); return std::move(this->__get()); } - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& operator*() const&& noexcept { + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& operator*() const&& noexcept +# if _LIBCPP_STD_VER >= 26 + requires(is_object_v<_Tp>) +# endif + { _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value"); return std::move(this->__get()); } @@ -1085,25 +1161,41 @@ public: using __base::__get; using __base::has_value; - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& value() const& { + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& value() const& +# if _LIBCPP_STD_VER >= 26 + requires(is_object_v<_Tp>) +# endif + { if (!this->has_value()) std::__throw_bad_optional_access(); return this->__get(); } - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() & { + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() & +# if _LIBCPP_STD_VER >= 26 + requires(is_object_v<_Tp>) +# endif + { if (!this->has_value()) std::__throw_bad_optional_access(); return this->__get(); } - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& value() && { + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& value() && +# if _LIBCPP_STD_VER >= 26 + requires(is_object_v<_Tp>) +# endif + { if (!this->has_value()) std::__throw_bad_optional_access(); return std::move(this->__get()); } - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp const&& value() const&& { + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp const&& value() const&& +# if _LIBCPP_STD_VER >= 26 + requires(is_object_v<_Tp>) +# endif + { if (!this->has_value()) std::__throw_bad_optional_access(); return std::move(this->__get()); @@ -1111,8 +1203,7 @@ public: template > # if _LIBCPP_STD_VER >= 26 - requires(!(is_lvalue_reference_v<_Tp> && is_function_v<__libcpp_remove_reference_t<_Tp>>) && - !(is_lvalue_reference_v<_Tp> && is_array_v<__libcpp_remove_reference_t<_Tp>>)) + requires(is_object_v<_Tp>) # endif [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) const& { static_assert(is_copy_constructible_v<_Tp>, "optional::value_or: T must be copy constructible"); @@ -1122,7 +1213,7 @@ public: template > # if _LIBCPP_STD_VER >= 26 - requires(!is_lvalue_reference_v<_Tp>) + requires(is_object_v<_Tp>) # endif [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && { static_assert(is_move_constructible_v<_Tp>, "optional::value_or: T must be move constructible"); @@ -1130,19 +1221,11 @@ public: return this->has_value() ? std::move(this->__get()) : static_cast<_Tp>(std::forward<_Up>(__v)); } -# if _LIBCPP_STD_VER >= 26 - template > - requires(is_lvalue_reference_v<_Tp> && - !(is_function_v<__libcpp_remove_reference_t<_Tp>> || is_array_v<__libcpp_remove_reference_t<_Tp>>)) - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && { - static_assert(is_move_constructible_v<_Tp>, "optional::value_or: T must be move constructible"); - static_assert(is_convertible_v<_Up, _Tp>, "optional::value_or: U must be convertible to T"); - return this->has_value() ? this->__get() : static_cast<_Tp>(std::forward<_Up>(__v)); - } -# endif - # if _LIBCPP_STD_VER >= 23 template +# if _LIBCPP_STD_VER >= 26 + requires(is_object_v<_Tp>) +# endif [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & { using _Up = invoke_result_t<_Func, _Tp&>; static_assert(__is_std_optional>::value, @@ -1153,6 +1236,9 @@ public: } template +# if _LIBCPP_STD_VER >= 26 + requires(is_object_v<_Tp>) +# endif [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const& { using _Up = invoke_result_t<_Func, const _Tp&>; static_assert(__is_std_optional>::value, @@ -1163,6 +1249,9 @@ public: } template +# if _LIBCPP_STD_VER >= 26 + requires(is_object_v<_Tp>) +# endif [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && { using _Up = invoke_result_t<_Func, _Tp&&>; static_assert(__is_std_optional>::value, @@ -1173,6 +1262,9 @@ public: } template +# if _LIBCPP_STD_VER >= 26 + requires(is_object_v<_Tp>) +# endif [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& { using _Up = invoke_result_t<_Func, const _Tp&&>; static_assert(__is_std_optional>::value, @@ -1183,6 +1275,9 @@ public: } template +# if _LIBCPP_STD_VER >= 26 + requires(is_object_v<_Tp>) +# endif [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) & { using _Up = remove_cv_t>; static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array"); @@ -1195,6 +1290,9 @@ public: } template +# if _LIBCPP_STD_VER >= 26 + requires(is_object_v<_Tp>) +# endif [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const& { using _Up = remove_cv_t>; static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array"); @@ -1207,6 +1305,9 @@ public: } template +# if _LIBCPP_STD_VER >= 26 + requires(is_object_v<_Tp>) +# endif [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) && { using _Up = remove_cv_t>; static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array"); @@ -1219,8 +1320,11 @@ public: } template +# if _LIBCPP_STD_VER >= 26 + requires(is_object_v<_Tp>) +# endif [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const&& { - using _Up = remove_cvref_t>; + using _Up = remove_cv_t>; static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array"); static_assert(!is_same_v<_Up, in_place_t>, "Result of f(std::move(value())) should not be std::in_place_t"); static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(std::move(value())) should not be std::nullopt_t"); @@ -1233,6 +1337,9 @@ public: template [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) const& requires is_copy_constructible_v<_Tp> +# if _LIBCPP_STD_VER >= 26 + && (is_object_v<_Tp>) +# endif { static_assert(is_same_v>, optional>, "Result of f() should be the same type as this optional"); @@ -1244,6 +1351,9 @@ public: template [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) && requires is_move_constructible_v<_Tp> +# if _LIBCPP_STD_VER >= 26 + && (is_object_v<_Tp>) +# endif { static_assert(is_same_v>, optional>, "Result of f() should be the same type as this optional"); @@ -1254,6 +1364,78 @@ public: # endif // _LIBCPP_STD_VER >= 23 using __base::reset; + +// optional overloads +# if _LIBCPP_STD_VER >= 26 + + _LIBCPP_HIDE_FROM_ABI constexpr add_pointer_t<_Tp> operator->() const noexcept + requires(is_lvalue_reference_v<_Tp>) + { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator-> called on a disengaged value"); + return std::addressof(this->__get()); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() const noexcept + requires(is_lvalue_reference_v<_Tp>) + { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value"); + return this->__get(); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() const + requires(is_lvalue_reference_v<_Tp>) + { + if (!this->has_value()) + std::__throw_bad_optional_access(); + return this->__get(); + } + + template > + requires(is_lvalue_reference_v<_Tp> && is_object_v<__libcpp_remove_reference_t<_Tp>> && + !is_array_v<__libcpp_remove_reference_t<_Tp>>) + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decay_t<_Tp> value_or(_Up&& __v) const { + static_assert( + is_constructible_v, _Tp&>, "optional::value_or: remove_cv_t must be constructible"); + static_assert( + is_convertible_v<_Up, remove_cvref_t<_Tp>>, "optional::value_or: U must be convertible to remove_cv_t"); + return this->has_value() ? this->__get() : static_cast>(std::forward<_Up>(__v)); + } + + template + requires(is_lvalue_reference_v<_Tp>) + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const { + using _Up = invoke_result_t<_Func, _Tp&>; + static_assert(__is_std_optional>::value, + "Result of f(value()) must be a specialization of std::optional"); + if (*this) + return std::invoke(std::forward<_Func>(__f), value()); + return remove_cvref_t<_Up>(); + } + + template + requires(is_lvalue_reference_v<_Tp>) + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional>> + transform(_Func&& __f) const { + using _Up = remove_cvref_t>; + static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array"); + static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t"); + static_assert(!is_same_v<_Up, nullopt_t>, "Result of f(value()) should not be std::nullopt_t"); + static_assert(is_object_v<_Up>, "Result of f(value()) should be an object type"); + if (*this) + return optional<_Up>(__optional_construct_from_invoke_tag{}, std::forward<_Func>(__f), value()); + return optional<_Up>(); + } + + template + requires(is_lvalue_reference_v<_Tp>) + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) const { + static_assert(is_same_v>, optional>, + "Result of f() should be the same type as this optional"); + if (*this) + return *this; + return std::forward<_Func>(__f)(); + } +# endif }; template diff --git a/libcxx/test/libcxx/utilities/optional/optional.object/optional.object.observe/value_or.compile.pass.cpp b/libcxx/test/libcxx/utilities/optional/optional.object/optional.object.observe/value_or.compile.pass.cpp index 25df0dd6c1936..1071184561dc1 100644 --- a/libcxx/test/libcxx/utilities/optional/optional.object/optional.object.observe/value_or.compile.pass.cpp +++ b/libcxx/test/libcxx/utilities/optional/optional.object/optional.object.observe/value_or.compile.pass.cpp @@ -22,7 +22,7 @@ concept has_value_or = requires(Opt opt, T&& t) { }; static_assert(has_value_or, int>); -static_assert(has_value_or, int&>); -static_assert(has_value_or, const int&>); +static_assert(has_value_or, int>); +static_assert(has_value_or, int>); static_assert(!has_value_or&&, int (&)[1]>); static_assert(!has_value_or&&, int (&)()>); diff --git a/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp index 810283a4bc80e..93bbd74cd8678 100644 --- a/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "test_macros.h" @@ -260,8 +261,9 @@ constexpr bool test() { #if TEST_STD_VER >= 26 constexpr bool test_ref() { - // Test & overload - { + // Test "overloads", only the const (no ref-qualifier) and_then() should be called + + { // & // Without & qualifier on F's operator() { int j = 42; @@ -276,21 +278,21 @@ constexpr bool test_ref() { { int j = 42; std::optional i{j}; + auto& io = i; RefQual l{}; NORefQual nl{}; - std::same_as> decltype(auto) r = i.and_then(l); + std::same_as> decltype(auto) r = io.and_then(l); assert(r == 1); - assert(i.and_then(nl) == std::nullopt); + assert(io.and_then(nl) == std::nullopt); } } - // Test const& overload - { + { // const& // Without & qualifier on F's operator() { int j = 42; - std::optional i{j}; + const std::optional i{j}; std::same_as> decltype(auto) r = i.and_then(CLVal{}); assert(r == 1); @@ -309,7 +311,8 @@ constexpr bool test_ref() { assert(i.and_then(nl) == std::nullopt); } } - // Test && overload + + // && { //With & qualifier on F's operator() { @@ -318,11 +321,11 @@ constexpr bool test_ref() { std::same_as> decltype(auto) r = i.and_then(RVRefQual{}); assert(r == 1); - assert(i.and_then(NORVRefQual{}) == std::nullopt); + assert(std::move(i).and_then(NORVRefQual{}) == std::nullopt); } } - // Test const&& overload + // const&& { //With & qualifier on F's operator() { @@ -330,10 +333,10 @@ constexpr bool test_ref() { const std::optional i{j}; const RVCRefQual l{}; const NORVCRefQual nl{}; - std::same_as> decltype(auto) r = i.and_then(std::move(l)); + std::same_as> decltype(auto) r = std::move(i).and_then(std::move(l)); assert(r == 1); - assert(i.and_then(std::move(nl)) == std::nullopt); + assert(std::move(i).and_then(std::move(nl)) == std::nullopt); } } return true; diff --git a/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp index 0c330e30055a2..326013cd6557f 100644 --- a/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp @@ -75,6 +75,13 @@ constexpr bool test() { }); assert(opt == j); } + + { + int i = 2; + const std::optional opt; + assert(opt.or_else([&] { return std::optional{i}; }) == i); + } + { int i = 2; std::optional opt; @@ -87,6 +94,16 @@ constexpr bool test() { }); assert(opt == j); } + + { + int i = 2; + const std::optional opt = i; + assert(std::move(opt).or_else([] { + assert(false); + return std::optional{}; + }) == i); + } + #endif return true; diff --git a/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp index 36939f0b6b62e..b8536b790f749 100644 --- a/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp @@ -204,6 +204,8 @@ constexpr bool test() { #if TEST_STD_VER >= 26 constexpr bool test_ref() { + // Test that no matter the ref qualifier on the object .transform() is invoked on, only the added + // const (no ref-qualifier) overload is used { std::optional opt1; std::same_as> decltype(auto) opt1r = opt1.transform([](int i) { return i + 2; }); @@ -218,7 +220,16 @@ constexpr bool test_ref() { assert(*o2 == 44); } - // Test & overload + + { + int i = 42; + float k = 4.0f; + std::optional opt{i}; + std::same_as> decltype(auto) o2 = opt.transform([&](int&) { return k; }); + assert(*o2 == 4.0f); + } + + // & { // Without & qualifier on F's operator() { @@ -244,7 +255,7 @@ constexpr bool test_ref() { // Without & qualifier on F's operator() { int i = 42; - std::optional opt{i}; + const std::optional opt{i}; std::same_as> decltype(auto) o3 = std::as_const(opt).transform(CLVal{}); assert(*o3 == 1); @@ -266,8 +277,8 @@ constexpr bool test_ref() { // Without & qualifier on F's operator() { int i = 42; - std::optional opt{i}; - std::same_as> decltype(auto) o3 = std::move(opt).transform(RVal{}); + std::optional opt{i}; + std::same_as> decltype(auto) o3 = std::move(opt).transform(LVal{}); assert(*o3 == 1); } @@ -286,9 +297,9 @@ constexpr bool test_ref() { //With & qualifier on F's operator() { int i = 42; - std::optional opt{i}; + const std::optional opt{i}; const RVCRefQual rvc{}; - std::same_as> decltype(auto) o3 = opt.transform(std::move(rvc)); + std::same_as> decltype(auto) o3 = std::move(opt).transform(std::move(rvc)); assert(*o3 == 1); } } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/assign_value.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/assign_value.pass.cpp index ddb9ffc4bf80c..f1e2493954d38 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/assign_value.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/assign_value.pass.cpp @@ -18,6 +18,9 @@ #include "test_macros.h" #include "archetypes.h" +#if TEST_STD_VER >= 26 +# include "copy_move_types.h" +#endif using std::optional; @@ -252,9 +255,9 @@ constexpr T pr38638(T v) #if TEST_STD_VER >= 26 -template _Val> -constexpr void test_with_ref() { - T t{_Val}; +template +constexpr bool test_with_ref(std::decay_t val) { + T t{val}; { // to empty optional opt; opt = t; @@ -280,7 +283,7 @@ constexpr void test_with_ref() { } // test two objects, make sure that the optional only changes what it holds a reference to { - T t2{_Val}; + T t2{val}; optional opt{t}; opt = t2; @@ -299,6 +302,20 @@ constexpr void test_with_ref() { assert(std::addressof(*opt) == std::addressof(j)); assert(*opt == 2); } + + { // test that no copy is made when assigning + TracedCopyMove t1{}; + TracedCopyMove t2{}; + + optional o(t1); + + o = t2; + assert(std::addressof(*o) == &t2); + assert(o->constCopy == 0); + assert(o->nonConstCopy == 0); + } + + return true; } #endif @@ -333,7 +350,10 @@ int main(int, char**) static_assert(pr38638(3) == 5, ""); #if TEST_STD_VER >= 26 - test_with_ref(); + test_with_ref(3); + test_with_ref({}); + static_assert(test_with_ref(3)); + static_assert(test_with_ref({})); #endif return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/const_optional_U.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/const_optional_U.pass.cpp index 4751f8e60994e..6b6dcbf428380 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/const_optional_U.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/const_optional_U.pass.cpp @@ -195,6 +195,34 @@ void test_ambiguous_assign() { } } +#if TEST_STD_VER >= 26 +constexpr bool test_ref() { + { + int t{2}; + const std::optional o1{t}; + std::optional o2 = o1; + + assert(&(*o2) == &t); + assert(o1.has_value()); + assert(o2.has_value()); + assert(*o2 == 2); + } + + { + int t{2}; + + const std::optional o1{t}; + std::optional o2 = o1; + + assert(&(*o2) != &t); + assert(o1.has_value()); + assert(o2.has_value()); + assert(*o2 == 2); + } + + return true; +} +#endif int main(int, char**) { @@ -251,5 +279,10 @@ int main(int, char**) } #endif - return 0; +#if TEST_STD_VER >= 26 + assert(test_ref()); + static_assert(test_ref()); +#endif + + return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/copy.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/copy.pass.cpp index 9753fdb46b862..b4702baa49e1c 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/copy.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/copy.pass.cpp @@ -49,6 +49,55 @@ constexpr bool assign_value(optional&& lhs) { return lhs.has_value() && rhs.has_value() && *lhs == *rhs; } +#if TEST_STD_VER >= 26 +constexpr bool test_ref() { + struct TraceCopyAssign { + int copyAssign = 0; + mutable int constCopyAssign = 0; + + constexpr TraceCopyAssign() = default; + constexpr TraceCopyAssign(TraceCopyAssign& r) : copyAssign(r.copyAssign + 1), constCopyAssign(r.constCopyAssign) {} + constexpr TraceCopyAssign(const TraceCopyAssign& r) + : copyAssign(r.copyAssign), constCopyAssign(r.constCopyAssign + 1) {} + constexpr TraceCopyAssign& operator=(const TraceCopyAssign&) { + copyAssign++; + return *this; + } + constexpr const TraceCopyAssign& operator=(const TraceCopyAssign&) const { + constCopyAssign++; + return *this; + } + }; + using T = TraceCopyAssign; + { + T t{}; + std::optional o1{t}; + std::optional o2 = o1; + + assert(&(*o2) == &t); + assert(&(*o2) == &(*o1)); + assert(t.constCopyAssign == 0); + assert(t.copyAssign == 0); + assert(o1.has_value()); + assert(o2.has_value()); + } + { + T t{}; + std::optional o1{t}; + std::optional o2 = o1; + + assert(&(*o2) != &t); + assert(&(*o2) != &(*o1)); + assert(o2->constCopyAssign == 0); + assert(o2->copyAssign == 1); + assert(o1.has_value()); + assert(o2.has_value()); + } + + return true; +} +#endif + int main(int, char**) { { @@ -100,5 +149,10 @@ int main(int, char**) } #endif - return 0; +#if TEST_STD_VER >= 26 + assert(test_ref()); + static_assert(test_ref()); +#endif + + return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/emplace.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/emplace.pass.cpp index 629e315add4d9..3321d55ec5869 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/emplace.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/emplace.pass.cpp @@ -231,6 +231,7 @@ constexpr bool test_ref() { auto& v = opt.emplace(t); static_assert(std::is_same_v); assert(static_cast(opt) == true); + ASSERT_NOEXCEPT(opt.emplace(t)); assert(*opt == t); assert(&v == &*opt); assert(&t == &*opt); diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/emplace.verify.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/emplace.verify.cpp new file mode 100644 index 0000000000000..a0bfa078f189c --- /dev/null +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/emplace.verify.cpp @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++26 +// + +// template T& optional::emplace(U&& arg); +// Ensure that emplace isn't found if reference_constructs_From_temporary_v == true or is_constructible_v<_Tp&, U> == false +#include +#include + +struct X {}; +void test() { + int i = 1; + std::optional f{}; + static_assert(!std::is_constructible_v); // is_constructible_v<_Tp&, U> == false + f.emplace(i); // expected-error {{no matching member function for call to 'emplace'}} + + std::optional t{}; // reference_constructs_from_temporary_v == false + t.emplace(1); // expected-error {{no matching member function for call to 'emplace'}} +} diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/move.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/move.pass.cpp index 85971e38f7b70..c447557cfa9d4 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/move.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/move.pass.cpp @@ -20,6 +20,9 @@ #include "test_macros.h" #include "archetypes.h" +#if TEST_STD_VER >= 26 +# include "copy_move_types.h" +#endif using std::optional; @@ -66,6 +69,23 @@ constexpr bool assign_value(optional&& lhs) { return lhs.has_value() && rhs.has_value() && *lhs == Tp{101}; } +#if TEST_STD_VER >= 26 +constexpr bool test_ref() { + using T = TracedAssignment; + { + T t{}; + std::optional o1{t}; + std::optional o2 = std::move(o1); + + assert(&(*o2) == &t); + assert(o1.has_value()); + assert(o2.has_value()); + assert(t.moveAssign == 0); + } + return true; +} +#endif + int main(int, char**) { { diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/nullopt_t.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/nullopt_t.pass.cpp index 9d0843b7338df..e85c0c435aa37 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/nullopt_t.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/nullopt_t.pass.cpp @@ -61,6 +61,17 @@ TEST_CONSTEXPR_CXX20 bool test() assert(state == State::destroyed); assert(static_cast(opt) == false); } + +#if TEST_STD_VER >= 26 + { + int i = 0; + optional o{i}; + ASSERT_NOEXCEPT(o = std::nullopt); + o = std::nullopt; + assert(!o.has_value()); + assert(static_cast(o) == false); + } +#endif return true; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/optional_U.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/optional_U.pass.cpp index cc1b17157f2b4..d23858e9974d5 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/optional_U.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.assign/optional_U.pass.cpp @@ -293,7 +293,18 @@ TEST_CONSTEXPR_CXX20 bool test() assert(state[0] == state_t::inactive); assert(state[1] == state_t::copy_assigned); } +#if TEST_STD_VER >= 26 + { + state_t state{state_t::constructed}; + StateTracker t{state}; + std::optional o1{t}; + std::optional o2 = std::move(o1); + (void)*o2; + assert(state != state_t::move_assigned); + assert(state == state_t::constructed); + } +#endif return true; } @@ -301,7 +312,8 @@ TEST_CONSTEXPR_CXX20 bool test() int main(int, char**) { #if TEST_STD_VER > 17 - static_assert(test()); + assert(test()); + static_assert(test()); #endif test_with_test_type(); test_ambiguous_assign(); diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/U.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/U.pass.cpp index 1e951ebdf1d74..1cdb429f485e5 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/U.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/U.pass.cpp @@ -21,6 +21,8 @@ #include "archetypes.h" #include "test_convertible.h" +#include "../optional_helper_types.h" + using std::optional; struct ImplicitThrow { @@ -149,9 +151,72 @@ void test_explicit() { #endif } +#if TEST_STD_VER >= 26 +constexpr bool test_ref() { + { + int i = 0; + std::optional o(i); + ASSERT_NOEXCEPT(std::optional(i)); + assert(o.has_value()); + assert(&(*o) == &i); + assert(*o == 0); + assert(o.value() == 0); + } + + { + ReferenceConversion t{1, 2}; + ASSERT_NOEXCEPT(std::optional(t)); + std::optional o(t); + assert(o.has_value()); + assert(&(*o) == &t.lvalue); + assert(*o == 1); + } + + { + ReferenceConversion t{1, 2}; + ASSERT_NOEXCEPT(std::optional(std::move(t))); + std::optional o(std::move(t)); + assert(o.has_value()); + assert(&(*o) == &t.rvalue); + assert(*o == 2); + } + +# ifndef TEST_HAS_NO_EXCEPTIONS + { + ReferenceConversionThrows t{1, 2, false}; + ASSERT_NOT_NOEXCEPT(std::optional(t)); + try { + std::optional o(t); + assert(o.has_value()); + assert(&(*o) == &t.lvalue); + assert(*o == 1); + } catch (int) { + assert(false); + } + } + { + ReferenceConversionThrows t{1, 2, false}; + ASSERT_NOT_NOEXCEPT(std::optional(std::move(t))); + try { + std::optional o(std::move(t)); + assert(o.has_value()); + assert(&(*o) == &t.rvalue); + assert(*o == 2); + } catch (int) { + assert(false); + } + } +# endif + return true; +} +#endif + int main(int, char**) { test_implicit(); test_explicit(); - +#if TEST_STD_VER >= 26 + assert(test_ref()); + static_assert(test_ref()); +#endif return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/const_optional_U.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/const_optional_U.pass.cpp index 70fd76ec6ed0b..597110bcb54fe 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/const_optional_U.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/const_optional_U.pass.cpp @@ -11,11 +11,13 @@ // template // optional(const optional& rhs); +//optional: optional(const optional&& rhs); #include #include #include +#include "../optional_helper_types.h" #include "test_macros.h" using std::optional; @@ -86,6 +88,73 @@ constexpr bool test_all() { return true; } +#if TEST_STD_VER >= 26 +constexpr bool test_ref() { + // optional(const optional&) + { + int i = 1; + const std::optional o1{i}; + const std::optional o2{o1}; + + ASSERT_NOEXCEPT(std::optional(o2)); + assert(o2.has_value()); + assert(&(*o1) == &(*o2)); + assert(*o1 == i); + assert(*o2 == i); + } + + { + const std::optional o1; + const std::optional o2{o1}; + ASSERT_NOEXCEPT(std::optional(o2)); + assert(!o2.has_value()); + } + + { + ReferenceConversion t{1, 2}; + const std::optional&> o1(t); + const std::optional o2(o1); + ASSERT_NOEXCEPT(std::optional(o1)); + assert(o2.has_value()); + assert(&(*o2) == &t.lvalue); + assert(*o2 == 1); + } + + // optional(const optional&&) + { + int i = 1; + const std::optional o1{i}; + const std::optional o2{std::move(o1)}; + + // trivial move constructor should just copy the reference + ASSERT_NOEXCEPT(std::optional(std::move(o2))); + assert(o2.has_value()); + assert(&(*o1) == &(*o2)); + assert(*o1 == i); + assert(*o2 == i); + } + + { + const std::optional o1; + const std::optional o2{std::move(o1)}; + ASSERT_NOEXCEPT(std::optional(o2)); + assert(!o2.has_value()); + } + + { + ReferenceConversion t{1, 2}; + const std::optional&> o1(t); + const std::optional o2(std::move(o1)); + ASSERT_NOEXCEPT(std::optional(o1)); + assert(o2.has_value()); + assert(&(*o2) == &t.lvalue); + assert(*o2 == 1); + } + + return true; +} +#endif + int main(int, char**) { test_all(); test_all(); @@ -109,6 +178,9 @@ int main(int, char**) { } static_assert(!(std::is_constructible, const optional&>::value), ""); - +#if TEST_STD_VER >= 26 + assert(test_ref()); + static_assert(test_ref()); +#endif return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/copy.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/copy.pass.cpp index f61a22c23a04d..fd7332f3b422b 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/copy.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/copy.pass.cpp @@ -69,8 +69,8 @@ void test_ref(InitArgs&&... args) { assert(&(*lhs) == &(*rhs)); } +#if TEST_STD_VER >= 26 void test_reference_extension() { -#if defined(_LIBCPP_VERSION) && 0 // FIXME these extensions are currently disabled. using T = TestTypes::TestType; T::reset(); { @@ -99,12 +99,15 @@ void test_reference_extension() { } assert(T::alive == 0); assert(T::destroyed == 1); + +# if 0 // optional is not permitted. { static_assert(!std::is_copy_constructible>::value, ""); static_assert(!std::is_copy_constructible>::value, ""); } -#endif +# endif } +#endif int main(int, char**) { test(); @@ -153,9 +156,11 @@ int main(int, char**) { { test_throwing_ctor(); } +#if TEST_STD_VER >= 26 { test_reference_extension(); } +#endif { constexpr std::optional o1{4}; constexpr std::optional o2 = o1; diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/in_place_t.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/in_place_t.pass.cpp index c8bdfc78225fa..c3b498e56a5a1 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/in_place_t.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/in_place_t.pass.cpp @@ -78,6 +78,23 @@ constexpr bool test_ref_initializer_list() { return true; } + +constexpr bool test_ref() { + { // optional(in_place_t, _Arg&&) + Y y{1, 2}; + optional xo(in_place, y); + + Y x2{1, 2}; + + assert(*xo == x2); + assert(&(*xo) == &y); + } + + assert(test_ref_initializer_list()); + static_assert(test_ref_initializer_list()); + + return true; +} #endif int main(int, char**) { @@ -148,8 +165,8 @@ int main(int, char**) { #endif #if TEST_STD_VER >= 26 - test_ref_initializer_list(); - static_assert(test_ref_initializer_list()); + test_ref(); + static_assert(test_ref()); #endif return 0; diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/move.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/move.pass.cpp index 583debcaac650..bb36283a5e3ea 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/move.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/move.pass.cpp @@ -78,26 +78,34 @@ void test_reference_extension() { T::reset(); { T t; - T::reset_constructors(); - test_ref(); - test_ref(t); + { + T::reset_constructors(); + test_ref(); + test_ref(t); + assert(T::alive == 1); + assert(T::constructed == 0); + assert(T::assigned == 0); + assert(T::destroyed == 0); + } // destroying the optional doesn't destroy the underlying T assert(T::alive == 1); - assert(T::constructed == 0); - assert(T::assigned == 0); assert(T::destroyed == 0); } assert(T::destroyed == 1); assert(T::alive == 0); { T t; - const T& ct = t; - T::reset_constructors(); - test_ref(); - test_ref(t); - test_ref(ct); + { + const T& ct = t; + T::reset_constructors(); + test_ref(); + test_ref(t); + test_ref(ct); + assert(T::alive == 1); + assert(T::constructed == 0); + assert(T::assigned == 0); + assert(T::destroyed == 0); + } assert(T::alive == 1); - assert(T::constructed == 0); - assert(T::assigned == 0); assert(T::destroyed == 0); } assert(T::alive == 0); diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/optional_U.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/optional_U.pass.cpp index 709b106c800a6..39a80efa475f7 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/optional_U.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/optional_U.pass.cpp @@ -12,6 +12,8 @@ // template // optional(optional&& rhs); +// optional: optional(optional& rhs) + #include #include #include @@ -19,6 +21,11 @@ #include #include "test_macros.h" +#if TEST_STD_VER >= 26 +# include "copy_move_types.h" +#endif + +#include "../optional_helper_types.h" using std::optional; @@ -68,6 +75,79 @@ TEST_CONSTEXPR_CXX20 bool test_all() { return true; } +#if TEST_STD_VER >= 26 +constexpr bool test_ref() { + // optional(optional&) + { + int i = 1; + std::optional o1{i}; + std::optional o2{o1}; + + ASSERT_NOEXCEPT(std::optional(o2)); + assert(o2.has_value()); + assert(&(*o1) == &(*o2)); + assert(*o1 == i); + assert(*o2 == i); + } + + { + std::optional o1; + std::optional o2{o1}; + ASSERT_NOEXCEPT(std::optional(o2)); + assert(!o2.has_value()); + } + + { + ReferenceConversion t{1, 2}; + std::optional&> o1(t); + std::optional o2(o1); + ASSERT_NOEXCEPT(std::optional(o1)); + assert(o2.has_value()); + assert(&(*o2) == &t.lvalue); + assert(*o2 == 1); + } + // optional(optional&&) + { + int i = 1; + std::optional o1{i}; + std::optional o2{std::move(o1)}; + + // trivial move constructor should just copy the reference + ASSERT_NOEXCEPT(std::optional(o2)); + assert(o2.has_value()); + assert(&(*o1) == &(*o2)); + assert(*o1 == i); + assert(*o2 == i); + } + + { + std::optional o1; + std::optional o2{std::move(o1)}; + ASSERT_NOEXCEPT(std::optional(o2)); + assert(!o2.has_value()); + } + { + TracedCopyMove t{}; + std::optional o1{t}; + std::optional o2{std::move(o1)}; + assert(t.constMove == 0); + assert(t.nonConstMove == 0); + } + + { + ReferenceConversion t{1, 2}; + std::optional&> o1(t); + std::optional o2(std::move(o1)); + ASSERT_NOEXCEPT(std::optional(o1)); + assert(o2.has_value()); + assert(&(*o2) == &t.lvalue); + assert(*o2 == 1); + } + + return true; +} +#endif + int main(int, char**) { test_all(); test_all(); @@ -86,5 +166,9 @@ int main(int, char**) { static_assert(!(std::is_constructible, optional>::value), ""); +#if TEST_STD_VER >= 26 + assert(test_ref()); + static_assert(test_ref()); +#endif return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_constructs_from_temporary.verify.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_constructs_from_temporary.verify.cpp index 01b241ffbe79b..53ac77a2e5620 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_constructs_from_temporary.verify.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/ref_constructs_from_temporary.verify.cpp @@ -8,7 +8,13 @@ // REQUIRES: std-at-least-c++26 -// optional +// + +// optional(U&& u) noexcept(is_nothrow_constructible_v); +// optional(optional& rhs) noexcept(is_nothrow_constructible_v); +// optional(const optional& rhs) noexcept(is_nothrow_constructible_v); +// optional(optional&& rhs) noexcept(is_nothrow_constructible_v); +// optional(const optional&& rhs) noexcept(is_nothrow_constructible_v); #include #include @@ -19,17 +25,20 @@ struct X { X(int j) : i(j) {} }; -int main(int, char**) { - const std::optional _co(1); - std::optional _o(1); - - // expected-error-re@*:* 8 {{call to deleted constructor of 'std::optional<{{.*}}>'}} - std::optional o1{1}; // optional(U&&) - std::optional o2{std::optional(1)}; // optional(optional&&) - std::optional o3{_co}; // optional(const optional&) - std::optional o4{_o}; // optional(optional&) - std::optional o5{1}; // optional(U&&) - std::optional o6{std::optional(1)}; // optional(optional&&) - std::optional o7{_co}; // optional(const optional&) - std::optional o8{_o}; // optional(optional&) +void test() { + const std::optional co(1); + std::optional o0(1); + + // expected-error-re@*:* 10 {{call to deleted constructor of 'std::optional<{{.*}}>'}} + std::optional o1{1}; // optional(U&&) + std::optional o2{o0}; // optional(optional&) + std::optional o3{co}; // optional(const optional&) + std::optional o4{std::move(o0)}; // optional(optional&&&) + std::optional o5{std::move(co)}; // optional(optional&&&) + + std::optional o6{1}; // optional(U&&) + std::optional o7{o0}; // optional(optional&) + std::optional o8(co); // optional(const optional&) + std::optional o9{std::move(o0)}; // optional(optional&&) + std::optional o10{std::move(co)}; // optional(const optional&&) } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.mod/reset.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.mod/reset.pass.cpp index e23e481f6a05d..a7ae22861d32e 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.mod/reset.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.mod/reset.pass.cpp @@ -75,6 +75,7 @@ int main(int, char**) optional opt(x); X::dtor_called = false; opt.reset(); + ASSERT_NOEXCEPT(opt.reset()); assert(X::dtor_called == false); assert(static_cast(opt) == false); } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/bool.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/bool.pass.cpp index 684f18ff3df20..ccc6dea8a111a 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/bool.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/bool.pass.cpp @@ -10,13 +10,36 @@ // // constexpr explicit optional::operator bool() const noexcept; +// constexpr explicit optional::operator bool() const noexcept; +#include #include #include -#include #include "test_macros.h" +#if TEST_STD_VER >= 26 + +constexpr bool test_ref() { + { + std::optional opt; + ASSERT_NOEXCEPT(bool(opt)); + assert(!opt); + static_assert(!std::is_convertible, bool>::value, ""); + } + { + int i = 1; + std::optional opt(i); + ASSERT_NOEXCEPT(bool(opt)); + assert(opt); + static_assert(!std::is_convertible, bool>::value, ""); + } + + return true; +} + +#endif + int main(int, char**) { using std::optional; @@ -34,5 +57,12 @@ int main(int, char**) static_assert(opt, ""); } - return 0; +#if TEST_STD_VER >= 26 + { + assert(test_ref()); + static_assert(test_ref()); + } +#endif + + return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference.pass.cpp index 6c1bf8aa15a8d..72cf0446c7eb9 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference.pass.cpp @@ -11,11 +11,14 @@ // constexpr T& optional::operator*() &; -#include -#include #include +#include +#include #include "test_macros.h" +#if TEST_STD_VER >= 26 +# include "copy_move_types.h" +#endif using std::optional; @@ -39,6 +42,38 @@ test() return (*opt).test(); } +#if TEST_STD_VER >= 26 +constexpr bool test_ref() { + { + TracedCopyMove x{}; + std::optional opt(x); + static_assert(noexcept(*opt)); + ASSERT_SAME_TYPE(decltype(*opt), TracedCopyMove&); + + assert(std::addressof(*opt) == std::addressof(x)); + assert(x.constMove == 0); + assert(x.nonConstMove == 0); + assert(x.constCopy == 0); + assert(x.nonConstCopy == 0); + } + + { + TracedCopyMove x{}; + std::optional opt(x); + static_assert(noexcept(*opt)); + ASSERT_SAME_TYPE(decltype(*opt), const TracedCopyMove&); + + assert(std::addressof(*opt) == std::addressof(x)); + assert(x.constMove == 0); + assert(x.nonConstMove == 0); + assert(x.constCopy == 0); + assert(x.nonConstCopy == 0); + } + + return true; +} +#endif + int main(int, char**) { { diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp index c15d4e4af74cc..aefe8d37a142a 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp @@ -11,11 +11,14 @@ // constexpr const T& optional::operator*() const &; -#include -#include #include +#include +#include #include "test_macros.h" +#if TEST_STD_VER >= 26 +# include "copy_move_types.h" +#endif using std::optional; @@ -32,6 +35,39 @@ struct Y int test() const {return 2;} }; +#if TEST_STD_VER >= 26 +constexpr bool test_ref() { + using T = TracedCopyMove; + { + T x{}; + const std::optional opt(x); + ASSERT_NOEXCEPT(*opt); + ASSERT_SAME_TYPE(decltype(*opt), TracedCopyMove&); + + assert(std::addressof(*opt) == std::addressof(x)); + assert((*opt).constMove == 0); + assert((*opt).nonConstMove == 0); + assert((*opt).constCopy == 0); + assert((*opt).nonConstCopy == 0); + } + + { + T x{}; + const std::optional opt(x); + ASSERT_NOEXCEPT(*opt); + ASSERT_SAME_TYPE(decltype(*opt), const TracedCopyMove&); + + assert(std::addressof(*opt) == std::addressof(x)); + assert((*opt).constMove == 0); + assert((*opt).nonConstMove == 0); + assert((*opt).constCopy == 0); + assert((*opt).nonConstCopy == 0); + } + + return true; +} +#endif + int main(int, char**) { { @@ -43,29 +79,15 @@ int main(int, char**) constexpr optional opt(X{}); static_assert((*opt).test() == 3, ""); } -#if TEST_STD_VER >= 26 - { - X x{}; - const optional opt{x}; - ASSERT_SAME_TYPE(decltype(*opt), X&); - ASSERT_NOEXCEPT(*opt); - } { - X x{}; - const optional opt{x}; - ASSERT_SAME_TYPE(decltype(*opt), const X&); - ASSERT_NOEXCEPT(*opt); + constexpr optional opt(Y{}); + assert((*opt).test() == 2); } +#if TEST_STD_VER >= 26 { - static constexpr X x{}; - constexpr optional opt(x); - static_assert((*opt).test() == 3); + assert(test_ref()); + static_assert(test_ref()); } #endif - { - constexpr optional opt(Y{}); - assert((*opt).test() == 2); - } - return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const_rvalue.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const_rvalue.pass.cpp index 646857fdc0465..cbff9cf3b1d6b 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const_rvalue.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_const_rvalue.pass.cpp @@ -11,11 +11,15 @@ // constexpr T&& optional::operator*() const &&; -#include -#include #include +#include +#include +#include #include "test_macros.h" +#if TEST_STD_VER >= 26 +# include "copy_move_types.h" +#endif using std::optional; @@ -32,6 +36,38 @@ struct Y int test() const && {return 2;} }; +#if TEST_STD_VER >= 26 +constexpr bool test_ref() { + { // const&& + TracedCopyMove x{}; + const std::optional opt(x); + ASSERT_NOEXCEPT(*std::move(opt)); + ASSERT_SAME_TYPE(decltype(*std::move(opt)), TracedCopyMove&); + + assert(std::addressof(*(std::move(opt))) == std::addressof(x)); + assert((*std::move(opt)).constMove == 0); + assert((*std::move(opt)).nonConstMove == 0); + assert((*std::move(opt)).constCopy == 0); + assert((*std::move(opt)).nonConstCopy == 0); + } + + { + TracedCopyMove x{}; + const std::optional opt(x); + ASSERT_NOEXCEPT(*std::move(opt)); + ASSERT_SAME_TYPE(decltype(*std::move(opt)), const TracedCopyMove&); + + assert(std::addressof(*(std::move(opt))) == std::addressof(x)); + assert((*std::move(opt)).constMove == 0); + assert((*std::move(opt)).nonConstMove == 0); + assert((*std::move(opt)).constCopy == 0); + assert((*std::move(opt)).nonConstCopy == 0); + } + + return true; +} +#endif + int main(int, char**) { { @@ -48,5 +84,10 @@ int main(int, char**) assert((*std::move(opt)).test() == 2); } +#if TEST_STD_VER >= 26 + assert(test_ref()); + static_assert(test_ref()); +#endif + return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_rvalue.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_rvalue.pass.cpp index 16bf2e4336c69..9c394bb13a2a0 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_rvalue.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/dereference_rvalue.pass.cpp @@ -11,11 +11,13 @@ // constexpr T&& optional::operator*() &&; -#include -#include #include +#include #include "test_macros.h" +#if TEST_STD_VER >= 26 +# include "copy_move_types.h" +#endif using std::optional; @@ -39,6 +41,40 @@ test() return (*std::move(opt)).test(); } +#if TEST_STD_VER >= 26 +constexpr bool test_ref() { + // ensure underlying value isn't moved from + { + TracedCopyMove x{}; + std::optional opt(x); + ASSERT_NOEXCEPT(*std::move(opt)); + ASSERT_SAME_TYPE(decltype(*std::move(opt)), TracedCopyMove&); + + assert(std::addressof(*std::move(opt)) == std::addressof(x)); + assert((*std::move(opt)).constMove == 0); + assert((*std::move(opt)).nonConstMove == 0); + assert((*std::move(opt)).constCopy == 0); + assert((*std::move(opt)).nonConstCopy == 0); + } + + { + TracedCopyMove x{}; + std::optional opt(x); + ASSERT_NOEXCEPT(*std::move(opt)); + ASSERT_SAME_TYPE(decltype(*std::move(opt)), const TracedCopyMove&); + + assert(std::addressof(*std::move(opt)) == std::addressof(x)); + assert((*std::move(opt)).constMove == 0); + assert((*std::move(opt)).nonConstMove == 0); + assert((*std::move(opt)).constCopy == 0); + assert((*std::move(opt)).nonConstCopy == 0); + } + + return true; +} + +#endif + int main(int, char**) { { @@ -52,5 +88,10 @@ int main(int, char**) } static_assert(test() == 7, ""); +#if TEST_STD_VER >= 26 + assert(test_ref()); + static_assert(test_ref()); +#endif + return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/has_value.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/has_value.pass.cpp index 9873a767cfbe6..d8c424fc41c74 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/has_value.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/has_value.pass.cpp @@ -11,9 +11,8 @@ // constexpr bool optional::has_value() const noexcept; -#include -#include #include +#include #include "test_macros.h" @@ -37,8 +36,13 @@ int main(int, char**) { static constexpr int i = 0; constexpr optional opt{i}; + ASSERT_NOEXCEPT(opt.has_value()); static_assert(opt.has_value()); } + { + constexpr optional opt{}; + static_assert(!opt.has_value()); + } #endif return 0; diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow.pass.cpp index 96d22743ac7fe..bb050f20debe1 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow.pass.cpp @@ -11,9 +11,9 @@ // constexpr T* optional::operator->(); -#include -#include #include +#include +#include #include "test_macros.h" @@ -53,17 +53,20 @@ int main(int, char**) std::optional opt(x); ASSERT_SAME_TYPE(decltype(opt.operator->()), X*); ASSERT_NOEXCEPT(opt.operator->()); + assert(opt.operator->() == std::addressof(x)); } { X x{}; std::optional opt(x); ASSERT_SAME_TYPE(decltype(opt.operator->()), const X*); ASSERT_NOEXCEPT(opt.operator->()); + assert(opt.operator->() == std::addressof(x)); } { X x{}; optional opt{x}; assert(opt->test() == 3); + assert(opt.operator->() == std::addressof(x)); } { X x{}; diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow_const.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow_const.pass.cpp index e9694fd6d9640..0461a04b85d45 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow_const.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/op_arrow_const.pass.cpp @@ -11,9 +11,9 @@ // constexpr const T* optional::operator->() const; -#include -#include #include +#include +#include #include "test_macros.h" @@ -60,17 +60,20 @@ int main(int, char**) const std::optional opt(x); ASSERT_SAME_TYPE(decltype(opt.operator->()), X*); ASSERT_NOEXCEPT(opt.operator->()); + assert(opt.operator->() == std::addressof(x)); } { X x{}; const std::optional opt(x); ASSERT_SAME_TYPE(decltype(opt.operator->()), const X*); ASSERT_NOEXCEPT(opt.operator->()); + assert(opt.operator->() == std::addressof(x)); } { static constexpr Z z{}; constexpr optional opt(z); static_assert(opt->test() == 1); + static_assert(opt.operator->() == std::addressof(z)); } #endif diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_const.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_const.pass.cpp index ea62f76d4082a..044134126a374 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_const.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_const.pass.cpp @@ -12,11 +12,14 @@ // constexpr const T& optional::value() const &; -#include -#include #include +#include +#include #include "test_macros.h" +#if TEST_STD_VER >= 26 +# include "copy_move_types.h" +#endif using std::optional; using std::in_place_t; @@ -33,6 +36,38 @@ struct X int test() && {return 6;} }; +#if TEST_STD_VER >= 26 +constexpr bool test_ref() { + { + TracedCopyMove x{}; + const std::optional opt(x); + ASSERT_NOT_NOEXCEPT(opt.value()); + ASSERT_SAME_TYPE(decltype(opt.value()), TracedCopyMove&); + + assert(std::addressof(opt.value()) == std::addressof(x)); + assert(opt->constMove == 0); + assert(opt->nonConstMove == 0); + assert(opt->constCopy == 0); + assert(opt->nonConstCopy == 0); + } + + { + TracedCopyMove x{}; + const std::optional opt(x); + ASSERT_NOT_NOEXCEPT(opt.value()); + ASSERT_SAME_TYPE(decltype(opt.value()), const TracedCopyMove&); + + assert(std::addressof(opt.value()) == std::addressof(x)); + assert(opt->constMove == 0); + assert(opt->nonConstMove == 0); + assert(opt->constCopy == 0); + assert(opt->nonConstCopy == 0); + } + + return true; +} +#endif + int main(int, char**) { { @@ -62,5 +97,10 @@ int main(int, char**) } #endif - return 0; +#if TEST_STD_VER >= 26 + assert(test_ref()); + static_assert(test_ref()); +#endif + + return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_const_rvalue.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_const_rvalue.pass.cpp index 49dbe4e58b57d..66911bf85f5fe 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_const_rvalue.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_const_rvalue.pass.cpp @@ -12,11 +12,14 @@ // constexpr const T& optional::value() const &&; +#include #include #include -#include #include "test_macros.h" +#if TEST_STD_VER >= 26 +# include "copy_move_types.h" +#endif using std::optional; using std::in_place_t; @@ -33,6 +36,40 @@ struct X int test() && {return 6;} }; +#if TEST_STD_VER >= 26 +constexpr bool test_ref() { + { + TracedCopyMove x{}; + const std::optional opt(x); + ASSERT_NOT_NOEXCEPT(std::move(opt).value()); + ASSERT_SAME_TYPE(decltype(std::move(opt).value()), TracedCopyMove&); + + assert(std::addressof(std::move(opt).value()) == std::addressof(x)); + auto& val = std::move(opt).value(); + assert(val.constMove == 0); + assert(val.nonConstMove == 0); + assert(val.constCopy == 0); + assert(val.nonConstCopy == 0); + } + + { + TracedCopyMove x{}; + const std::optional opt(x); + ASSERT_NOT_NOEXCEPT(std::move(opt).value()); + ASSERT_SAME_TYPE(decltype(std::move(opt).value()), const TracedCopyMove&); + + assert(std::addressof(std::move(opt).value()) == std::addressof(x)); + auto& val = std::move(opt).value(); + assert(val.constMove == 0); + assert(val.nonConstMove == 0); + assert(val.constCopy == 0); + assert(val.nonConstCopy == 0); + } + + return true; +} +#endif + int main(int, char**) { { @@ -61,6 +98,9 @@ int main(int, char**) } } #endif - - return 0; +#if TEST_STD_VER >= 26 + assert(test_ref()); + static_assert(test_ref()); +#endif + return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp index 66890ff9c9b91..f4ad896ed9816 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp @@ -84,6 +84,7 @@ constexpr int test() { int y = 2; optional opt; + ASSERT_SAME_TYPE(decltype(std::move(opt).value_or(y)), int); assert(std::move(opt).value_or(y) == 2); assert(!opt); } @@ -93,7 +94,8 @@ constexpr int test() int main(int, char**) { - static_assert(test() == 0); + assert(test() == 0); + static_assert(test() == 0); return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.swap/swap.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.swap/swap.pass.cpp index a82ca615e0c8c..afc1617affe3a 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.swap/swap.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.swap/swap.pass.cpp @@ -397,6 +397,51 @@ int main(int, char**) assert(static_cast(opt2) == true); assert(*opt2 == 2); } + +# if TEST_STD_VER >= 26 + { + Z z{1}; + optional opt1{z}; + optional opt2; + ASSERT_NOEXCEPT(opt1.swap(opt2)); + assert(opt1); + assert(*opt1 == 1); + assert(&(*opt1) == &z); + assert(!opt2); + + opt1.swap(opt2); + assert(*opt2 == 1); + assert(&(*opt2) == &z); + assert(opt2 && !opt1); + } + + { + Z z1{1}; + optional opt1; + optional opt2{z1}; + ASSERT_NOEXCEPT(opt1.swap(opt2)); + + opt1.swap(opt2); + assert(opt1 && !opt2); + assert(*opt1 == 1); + assert(&(*opt1) == &z1); + } + + { + Z z1{1}; + Z z2{2}; + optional opt1{z1}; + optional opt2{z2}; + ASSERT_NOEXCEPT(opt1.swap(opt2)); + + opt1.swap(opt2); + assert(opt1 && opt2); + assert(*opt1 == 2); + assert(*opt2 == 1); + assert(&(*opt1) == &z2); + assert(&(*opt2) == &z1); + } +# endif #endif return 0; diff --git a/libcxx/test/std/utilities/optional/optional.object/optional_helper_types.h b/libcxx/test/std/utilities/optional/optional.object/optional_helper_types.h new file mode 100644 index 0000000000000..704d99acb15e9 --- /dev/null +++ b/libcxx/test/std/utilities/optional/optional.object/optional_helper_types.h @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// TODO: We should consolidate all the helper types used in the optional tests into here. + +#include "test_macros.h" + +#ifndef LIBCXX_UTILITIES_OPTIONAL_HELPER_TYPES_H +# define LIBCXX_UTILITIES_OPTIONAL_HELPER_TYPES_H + +template +struct ReferenceConversion { + T lvalue; + T rvalue; + + constexpr ReferenceConversion(T lval, T rval) : lvalue(lval), rvalue(rval) {} + + constexpr operator T&() & noexcept { return lvalue; } + + constexpr operator T&() && noexcept { return rvalue; } +}; + +template +struct ReferenceConversionThrows { + T lvalue; + T rvalue; + bool throws{false}; + + constexpr ReferenceConversionThrows(T lval, T rval, bool except = false) + : lvalue(lval), rvalue(rval), throws(except) {} + + constexpr operator T&() & { + if (throws) { + TEST_THROW(1); + } + + return lvalue; + } + + constexpr operator T&() && { + if (throws) { + TEST_THROW(2); + } + + return rvalue; + } +}; + +#endif