https://gcc.gnu.org/g:23657d3972605542b02a33e67edcc74a7eede02c
commit r16-4552-g23657d3972605542b02a33e67edcc74a7eede02c Author: Giuseppe D'Angelo <[email protected]> Date: Wed Oct 22 09:31:46 2025 +0200 libstdc++: Implement optional<T&> from P2988R12 [PR121748] This patch implements optional<T&> based on the P2988R12 paper, incorporating corrections from LWG4300, LWG4304, and LWG3467. The resolution for LWG4015 is also extended to cover optional<T&>. We introduce _M_fwd() helper, that is equivalent to operator*(), except that it does not check non-empty precondition. It is used in to correctly propagate the value during move construction from optional<T&>. This is necessary because moving an optional<T&> must not move the contained object, which is the key distinction between *std::move(opt) and std::move(*opt). The implementation deviates from the standard by providing a separate std::swap overload for std::optional<T&>, which simplifies preserving the resolution of LWG2766. This introduces a few changes to make_optional behavior (see included test): * some previously valid uses of make_optional<T>({...}) (where T is not a reference type) now become ill-formed (see optional/make_optional_neg.cc). * make_optional<T&>(t) and make_optional<const T&>(ct), where decltype(t) is T&, and decltype(ct) is const T& now produce optional<T&> and optional<const T&> respectively, instead of optional<T>. * a few other uses of make_optional<R> with reference type R are now ill-formed. PR libstdc++/121748 libstdc++-v3/ChangeLog: * include/bits/version.def: Bump value for optional, * include/bits/version.h: Regenerate. * include/std/optional (std::__is_valid_contained_type_for_optional): Define. (std::optional<T>): Use __is_valid_contained_type_for_optional. (optional<T>(const optional<_Up>&), optional<T>(optional<_Up>&&)) (optional<T>::operator=(const optional<_Up>&)) (optional<T>::operator=(optional<_Up>&&)): Replacex._M_get() with x._M_fwd(), and std::move(x._M_get()) with std::move(x)._M_fwd(). (optional<T>::and_then): Remove uncessary remove_cvref_t. (optional<T>::_M_fwd): Define. (std::optional<T&>): Define new partial specialization. (std::swap(std::optional<T&>, std::optional<T&>)): Define. (std::make_optional(_Tp&&)): Add non-type template parameter. (std::make_optional): Use parenthesis to constructor optional. (std::hash<optional<T>>): Add comment. * testsuite/20_util/optional/make_optional-2.cc: Guarded not longer working example. * testsuite/20_util/optional/relops/constrained.cc: Expand test to cover optionals of reference. * testsuite/20_util/optional/requirements.cc: Ammend for optional<T&>. * testsuite/20_util/optional/requirements_neg.cc: Likewise. * testsuite/20_util/optional/version.cc: Test new value of __cpp_lib_optional. * testsuite/20_util/optional/make_optional_neg.cc: New test. * testsuite/20_util/optional/monadic/ref_neg.cc: New test. * testsuite/20_util/optional/ref/access.cc: New test. * testsuite/20_util/optional/ref/assign.cc: New test. * testsuite/20_util/optional/ref/cons.cc: New test. * testsuite/20_util/optional/ref/internal_traits.cc: New test. * testsuite/20_util/optional/ref/make_optional/1.cc: New test. * testsuite/20_util/optional/ref/make_optional/from_args_neg.cc: New test. * testsuite/20_util/optional/ref/make_optional/from_lvalue_neg.cc: New test. * testsuite/20_util/optional/ref/make_optional/from_rvalue_neg.cc: New test. * testsuite/20_util/optional/ref/monadic.cc: New test. * testsuite/20_util/optional/ref/relops.cc: New test. Reviewed-by: Jonathan Wakely <[email protected]> Co-authored-by: Tomasz KamiĆski <[email protected]> Diff: --- libstdc++-v3/include/bits/version.def | 9 +- libstdc++-v3/include/bits/version.h | 9 +- libstdc++-v3/include/std/optional | 385 +++++++++++++++++- .../testsuite/20_util/optional/make_optional-2.cc | 2 + .../20_util/optional/make_optional_neg.cc | 20 + .../testsuite/20_util/optional/monadic/ref_neg.cc | 20 + .../testsuite/20_util/optional/ref/access.cc | 119 ++++++ .../testsuite/20_util/optional/ref/assign.cc | 430 +++++++++++++++++++++ .../testsuite/20_util/optional/ref/cons.cc | 356 +++++++++++++++++ .../20_util/optional/ref/internal_traits.cc | 11 + .../20_util/optional/ref/make_optional/1.cc | 74 ++++ .../optional/ref/make_optional/from_args_neg.cc | 43 +++ .../optional/ref/make_optional/from_lvalue_neg.cc | 40 ++ .../optional/ref/make_optional/from_rvalue_neg.cc | 38 ++ .../testsuite/20_util/optional/ref/monadic.cc | 192 +++++++++ .../testsuite/20_util/optional/ref/relops.cc | 229 +++++++++++ .../20_util/optional/relops/constrained.cc | 86 ++++- .../testsuite/20_util/optional/requirements.cc | 6 +- .../testsuite/20_util/optional/requirements_neg.cc | 17 +- libstdc++-v3/testsuite/20_util/optional/version.cc | 4 +- 20 files changed, 2056 insertions(+), 34 deletions(-) diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 04232187965f..1bf98f74d459 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -878,12 +878,17 @@ ftms = { // Moved down here (after concepts) by topological sort. ftms = { name = optional; - values = { + values = { // optional<T&> + v = 202506; + cxxmin = 26; + extra_cond = "__glibcxx_concepts"; + }; + values = { // monadic operations v = 202110; cxxmin = 23; extra_cond = "__glibcxx_concepts"; }; - values = { + values = { // full constexpr support v = 202106; cxxmin = 20; }; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index df7a291b05fb..66de8b487e35 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -985,7 +985,12 @@ #undef __glibcxx_want_concepts #if !defined(__cpp_lib_optional) -# if (__cplusplus >= 202100L) && (__glibcxx_concepts) +# if (__cplusplus > 202302L) && (__glibcxx_concepts) +# define __glibcxx_optional 202506L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_optional) +# define __cpp_lib_optional 202506L +# endif +# elif (__cplusplus >= 202100L) && (__glibcxx_concepts) # define __glibcxx_optional 202110L # if defined(__glibcxx_want_all) || defined(__glibcxx_want_optional) # define __cpp_lib_optional 202110L @@ -2198,7 +2203,7 @@ # define __cpp_lib_observable_checkpoint 202506L # endif # endif -#endif /* !defined(__cpp_lib_observable_checkpoint) && defined(__glibcxx_want_observable_checkpoint) */ +#endif /* !defined(__cpp_lib_observable_checkpoint) */ #undef __glibcxx_want_observable_checkpoint #if !defined(__cpp_lib_algorithm_default_value_type) diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional index e5051d72c828..c4b56e31d589 100644 --- a/libstdc++-v3/include/std/optional +++ b/libstdc++-v3/include/std/optional @@ -777,6 +777,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION # define _GLIBCXX_USE_CONSTRAINTS_FOR_OPTIONAL 1 #endif + template<typename _Tp> + inline constexpr bool __is_valid_contained_type_for_optional = + ( +#if __cpp_lib_optional >= 202506L + is_lvalue_reference_v<_Tp> || +#endif + (is_object_v<_Tp> && is_destructible_v<_Tp> && !is_array_v<_Tp>) + ) + && !is_same_v<remove_cv_t<remove_reference_t<_Tp>>, nullopt_t> + && !is_same_v<remove_cv_t<remove_reference_t<_Tp>>, in_place_t>; + /** * @brief Class template for optional values. */ @@ -795,9 +806,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Unique tag type. optional<_Tp>> { - static_assert(!is_same_v<remove_cv_t<_Tp>, nullopt_t>); - static_assert(!is_same_v<remove_cv_t<_Tp>, in_place_t>); - static_assert(is_object_v<_Tp> && !is_array_v<_Tp>); + static_assert(__is_valid_contained_type_for_optional<_Tp>); private: using _Base = _Optional_base<_Tp>; @@ -894,7 +903,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(is_nothrow_constructible_v<_Tp, const _Up&>) { if (__t) - emplace(__t._M_get()); + emplace(__t._M_fwd()); } template<typename _Up> @@ -906,7 +915,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(is_nothrow_constructible_v<_Tp, _Up>) { if (__t) - emplace(std::move(__t._M_get())); + emplace(std::move(__t)._M_fwd()); } template<typename... _Args> @@ -956,7 +965,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(is_nothrow_constructible_v<_Tp, const _Up&>) { if (__t) - emplace(__t._M_get()); + emplace(__t._M_fwd()); } template<typename _Up, @@ -969,7 +978,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(is_nothrow_constructible_v<_Tp, const _Up&>) { if (__t) - emplace(__t._M_get()); + emplace(__t._M_fwd()); } template<typename _Up, @@ -982,7 +991,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(is_nothrow_constructible_v<_Tp, _Up>) { if (__t) - emplace(std::move(__t._M_get())); + emplace(std::move(__t)._M_fwd()); } template<typename _Up, @@ -995,7 +1004,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(is_nothrow_constructible_v<_Tp, _Up>) { if (__t) - emplace(std::move(__t._M_get())); + emplace(std::move(__t)._M_fwd()); } template<typename... _Args, @@ -1074,9 +1083,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (__u) { if (this->_M_is_engaged()) - this->_M_get() = __u._M_get(); + this->_M_get() = __u._M_fwd(); else - this->_M_construct(__u._M_get()); + this->_M_construct(__u._M_fwd()); } else { @@ -1108,9 +1117,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (__u) { if (this->_M_is_engaged()) - this->_M_get() = std::move(__u._M_get()); + this->_M_get() = std::move(__u)._M_fwd(); else - this->_M_construct(std::move(__u._M_get())); + this->_M_construct(std::move(__u)._M_fwd()); } else { @@ -1310,7 +1319,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION and_then(_Fn&& __f) & { using _Up = remove_cvref_t<invoke_result_t<_Fn, _Tp&>>; - static_assert(__is_optional_v<remove_cvref_t<_Up>>, + static_assert(__is_optional_v<_Up>, "the function passed to std::optional<T>::and_then " "must return a std::optional"); if (has_value()) @@ -1338,7 +1347,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION and_then(_Fn&& __f) && { using _Up = remove_cvref_t<invoke_result_t<_Fn, _Tp>>; - static_assert(__is_optional_v<remove_cvref_t<_Up>>, + static_assert(__is_optional_v<_Up>, "the function passed to std::optional<T>::and_then " "must return a std::optional"); if (has_value()) @@ -1352,7 +1361,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION and_then(_Fn&& __f) const && { using _Up = remove_cvref_t<invoke_result_t<_Fn, const _Tp>>; - static_assert(__is_optional_v<remove_cvref_t<_Up>>, + static_assert(__is_optional_v<_Up>, "the function passed to std::optional<T>::and_then " "must return a std::optional"); if (has_value()) @@ -1441,6 +1450,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION private: using _Base::_M_get; + [[__gnu__::__always_inline__]] + constexpr _Tp& + _M_fwd() & noexcept + { return _M_get(); } + + [[__gnu__::__always_inline__]] + constexpr _Tp&& + _M_fwd() && noexcept + { return std::move(_M_get()); } + + [[__gnu__::__always_inline__]] + constexpr const _Tp& + _M_fwd() const& noexcept + { return _M_get(); } + + [[__gnu__::__always_inline__]] + constexpr const _Tp&& + _M_fwd() const&& noexcept + { return std::move(_M_get()); } + template<typename _Up> friend class optional; #if __cpp_lib_optional >= 202110L // C++23 @@ -1453,6 +1482,310 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif }; +#if __cpp_lib_optional >= 202506L // C++26 + template<typename _Tp> + class optional<_Tp&> + { + static_assert(__is_valid_contained_type_for_optional<_Tp&>); + + public: + using value_type = _Tp; + using iterator = __gnu_cxx::__normal_iterator<_Tp*, optional>; + + // Constructors. + constexpr optional() noexcept = default; + constexpr optional(nullopt_t) noexcept : optional() {} + constexpr optional(const optional&) noexcept = default; + + template<typename _Arg> + requires is_constructible_v<_Tp&, _Arg> + && (!reference_constructs_from_temporary_v<_Tp&, _Arg>) + explicit constexpr + optional(in_place_t, _Arg&& __arg) + { + __convert_ref_init_val(std::forward<_Arg>(__arg)); + } + + template<typename _Up> + requires (!is_same_v<remove_cvref_t<_Up>, optional>) + && (!is_same_v<remove_cvref_t<_Up>, in_place_t>) + && is_constructible_v<_Tp&, _Up> + && (!reference_constructs_from_temporary_v<_Tp&, _Up>) + explicit(!is_convertible_v<_Up, _Tp&>) + constexpr + optional(_Up&& __u) + noexcept(is_nothrow_constructible_v<_Tp&, _Up>) + { + __convert_ref_init_val(std::forward<_Up>(__u)); + } + + template<typename _Up> + requires (!is_same_v<remove_cvref_t<_Up>, optional>) + && (!is_same_v<remove_cvref_t<_Up>, in_place_t>) + && is_constructible_v<_Tp&, _Up> + && reference_constructs_from_temporary_v<_Tp&, _Up> + explicit(!is_convertible_v<_Up, _Tp&>) + constexpr + optional(_Up&& __u) = delete; + + // optional<U> & + template<typename _Up> + requires (!is_same_v<remove_cv_t<_Tp>, optional<_Up>>) + && (!is_same_v<_Tp&, _Up>) + && is_constructible_v<_Tp&, _Up&> + && (!reference_constructs_from_temporary_v<_Tp&, _Up&>) + explicit(!is_convertible_v<_Up&, _Tp&>) + constexpr + optional(optional<_Up>& __rhs) + noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) + { + if (__rhs) + __convert_ref_init_val(__rhs._M_fwd()); + } + + template<typename _Up> + requires (!is_same_v<remove_cv_t<_Tp>, optional<_Up>>) + && (!is_same_v<_Tp&, _Up>) + && is_constructible_v<_Tp&, _Up&> + && reference_constructs_from_temporary_v<_Tp&, _Up&> + explicit(!is_convertible_v<_Up&, _Tp&>) + constexpr + optional(optional<_Up>& __rhs) = delete; + + // const optional<U>& + template<typename _Up> + requires (!is_same_v<remove_cv_t<_Tp>, optional<_Up>>) + && (!is_same_v<_Tp&, _Up>) + && is_constructible_v<_Tp&, const _Up&> + && (!reference_constructs_from_temporary_v<_Tp&, const _Up&>) + explicit(!is_convertible_v<const _Up&, _Tp&>) + constexpr + optional(const optional<_Up>& __rhs) + noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) + { + if (__rhs) + __convert_ref_init_val(__rhs._M_fwd()); + } + + template<typename _Up> + requires (!is_same_v<remove_cv_t<_Tp>, optional<_Up>>) + && (!is_same_v<_Tp&, _Up>) + && is_constructible_v<_Tp&, const _Up&> + && reference_constructs_from_temporary_v<_Tp&, const _Up&> + explicit(!is_convertible_v<const _Up&, _Tp&>) + constexpr + optional(const optional<_Up>& __rhs) = delete; + + // optional<U>&& + template<typename _Up> + requires (!is_same_v<remove_cv_t<_Tp>, optional<_Up>>) + && (!is_same_v<_Tp&, _Up>) + && is_constructible_v<_Tp&, _Up> + && (!reference_constructs_from_temporary_v<_Tp&, _Up>) + explicit(!is_convertible_v<_Up, _Tp&>) + constexpr + optional(optional<_Up>&& __rhs) + noexcept(is_nothrow_constructible_v<_Tp&, _Up>) + { + if (__rhs) + __convert_ref_init_val(std::move(__rhs)._M_fwd()); + } + + template<typename _Up> + requires (!is_same_v<remove_cv_t<_Tp>, optional<_Up>>) + && (!is_same_v<_Tp&, _Up>) + && is_constructible_v<_Tp&, _Up> + && reference_constructs_from_temporary_v<_Tp&, _Up> + explicit(!is_convertible_v<_Up, _Tp&>) + constexpr + optional(optional<_Up>&& __rhs) = delete; + + // const optional<U>&& + template<typename _Up> + requires (!is_same_v<remove_cv_t<_Tp>, optional<_Up>>) + && (!is_same_v<_Tp&, _Up>) + && is_constructible_v<_Tp&, const _Up> + && (!reference_constructs_from_temporary_v<_Tp&, _Up>) + explicit(!is_convertible_v<const _Up, _Tp&>) + constexpr + optional(const optional<_Up>&& __rhs) + noexcept(is_nothrow_constructible_v<_Tp&, const _Up>) + { + if (__rhs) + __convert_ref_init_val(std::move(__rhs)._M_fwd()); + } + + template<typename _Up> + requires (!is_same_v<remove_cv_t<_Tp>, optional<_Up>>) + && (!is_same_v<_Tp&, _Up>) + && is_constructible_v<_Tp&, const _Up> + && reference_constructs_from_temporary_v<_Tp&, const _Up> + explicit(!is_convertible_v<const _Up, _Tp&>) + constexpr + optional(const optional<_Up>&& __rhs) = delete; + + constexpr ~optional() = default; + + // Assignment. + constexpr optional& operator=(nullopt_t) noexcept + { + _M_val = nullptr; + return *this; + } + + constexpr optional& operator=(const optional&) noexcept = default; + + template<typename _Up> + requires is_constructible_v<_Tp&, _Up> + && (!reference_constructs_from_temporary_v<_Tp&, _Up>) + constexpr _Tp& + emplace(_Up&& __u) + noexcept(is_nothrow_constructible_v<_Tp&, _Up>) + { + __convert_ref_init_val(std::forward<_Up>(__u)); + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4300. Missing Returns: element in optional<T&>::emplace + return *_M_val; + } + + // Swap. + constexpr void swap(optional& __rhs) noexcept + { std::swap(_M_val, __rhs._M_val); } + + // Iterator support. + constexpr iterator begin() const noexcept + { + return iterator(_M_val); + } + + constexpr iterator end() const noexcept + { + return begin() + has_value(); + } + + // Observers. + constexpr _Tp* operator->() const noexcept + { + __glibcxx_assert(_M_val); // hardened precondition + return _M_val; + } + + constexpr _Tp& operator*() const noexcept + { + __glibcxx_assert(_M_val); // hardened precondition + return *_M_val; + } + + constexpr explicit operator bool() const noexcept + { + return _M_val; + } + + constexpr bool has_value() const noexcept + { + return _M_val; + } + + constexpr _Tp& value() const + { + if (_M_val) + return *_M_val; + __throw_bad_optional_access(); + } + + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4304. std::optional<NonReturnable&> is ill-formed due to value_or + template<typename _Up = remove_cv_t<_Tp>> + requires is_object_v<_Tp> && (!is_array_v<_Tp>) + constexpr decay_t<_Tp> + value_or(_Up&& __u) const + { + using _Xp = remove_cv_t<_Tp>; + static_assert(is_constructible_v<_Xp, _Tp&>); + static_assert(is_convertible_v<_Up, _Xp>); + return _M_val ? *_M_val : static_cast<_Xp>(std::forward<_Up>(__u)); + } + + // Monadic operations. + template<typename _Fn> + constexpr auto + and_then(_Fn&& __f) const + { + using _Up = remove_cvref_t<invoke_result_t<_Fn, _Tp&>>; + static_assert(__is_optional_v<_Up>, + "the function passed to std::optional<T&>::and_then " + "must return a std::optional"); + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f), *_M_val); + else + return _Up(); + } + + template<typename _Fn> + constexpr + optional<remove_cv_t<invoke_result_t<_Fn, _Tp&>>> + transform(_Fn&& __f) const + { + using _Up = remove_cv_t<invoke_result_t<_Fn, _Tp&>>; + if (has_value()) + return optional<_Up>(_Optional_func<_Fn>{__f}, *_M_val); + else + return optional<_Up>(); + } + + template<typename _Fn> + requires invocable<_Fn> + constexpr + optional + or_else(_Fn&& __f) const + { + static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Fn>>, optional>, + "the function passed to std::optional<T&>::or_else " + "must return a std::optional<T&>"); + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4367. Improve optional<T&>::or_else + if (has_value()) + return *this; + else + return std::forward<_Fn>(__f)(); + } + + // Modifiers. + constexpr void reset() noexcept + { + _M_val = nullptr; + } + + private: + _Tp *_M_val = nullptr; + + [[__gnu__::__always_inline__]] + constexpr _Tp& + _M_fwd() const noexcept + { return *_M_val; } + + template<typename _Up> friend class optional; + + template<typename _Up> + constexpr + void + __convert_ref_init_val(_Up&& __u) + noexcept + { + _Tp& __r(std::forward<_Up>(__u)); + _M_val = std::addressof(__r); + } + + template<typename _Fn, typename _Value> + explicit constexpr + optional(_Optional_func<_Fn> __f, _Value&& __v) + { + _Tp& __r = std::__invoke(std::forward<_Fn>(__f._M_f), std::forward<_Value>(__v)); + _M_val = std::addressof(__r); + } + }; +#endif // __cpp_lib_optional >= 202506L + template<typename _Tp> using __optional_relop_t = enable_if_t<is_convertible_v<_Tp, bool>, bool>; @@ -1740,19 +2073,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(noexcept(__lhs.swap(__rhs))) { __lhs.swap(__rhs); } +#if __cpp_lib_optional >= 202506L + // We deviate from standard, that do not declared separate swap overload + // from optional<T&>. + template<typename _Tp> + constexpr void + swap(optional<_Tp&>& __lhs, optional<_Tp&>& __rhs) noexcept + { __lhs.swap(__rhs); } +#endif + // _GLIBCXX_RESOLVE_LIB_DEFECTS // 2766. Swapping non-swappable types template<typename _Tp> enable_if_t<!(is_move_constructible_v<_Tp> && is_swappable_v<_Tp>)> swap(optional<_Tp>&, optional<_Tp>&) = delete; +#if __cpp_lib_optional >= 202506L + template<int = 0, typename _Tp> +#else template<typename _Tp> +#endif constexpr enable_if_t<is_constructible_v<decay_t<_Tp>, _Tp>, optional<decay_t<_Tp>>> make_optional(_Tp&& __t) noexcept(is_nothrow_constructible_v<optional<decay_t<_Tp>>, _Tp>) - { return optional<decay_t<_Tp>>{ std::forward<_Tp>(__t) }; } + { return optional<decay_t<_Tp>>( std::forward<_Tp>(__t) ); } template<typename _Tp, typename... _Args> constexpr @@ -1760,7 +2106,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION optional<_Tp>> make_optional(_Args&&... __args) noexcept(is_nothrow_constructible_v<_Tp, _Args...>) - { return optional<_Tp>{ in_place, std::forward<_Args>(__args)... }; } + { return optional<_Tp>( in_place, std::forward<_Args>(__args)... ); } template<typename _Tp, typename _Up, typename... _Args> constexpr @@ -1768,7 +2114,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION optional<_Tp>> make_optional(initializer_list<_Up> __il, _Args&&... __args) noexcept(is_nothrow_constructible_v<_Tp, initializer_list<_Up>&, _Args...>) - { return optional<_Tp>{ in_place, __il, std::forward<_Args>(__args)... }; } + { return optional<_Tp>( in_place, __il, std::forward<_Args>(__args)... ); } // Hash. @@ -1796,6 +2142,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp> struct hash<optional<_Tp>> + // hash for optional<T&> is disabled because is_hash_enabled_for<T&> is false : public __conditional_t<__is_hash_enabled_for<remove_const_t<_Tp>>, __optional_hash<_Tp>, __hash_not_enabled<_Tp>> diff --git a/libstdc++-v3/testsuite/20_util/optional/make_optional-2.cc b/libstdc++-v3/testsuite/20_util/optional/make_optional-2.cc index f383b519c89a..0aa2ac5ed93b 100644 --- a/libstdc++-v3/testsuite/20_util/optional/make_optional-2.cc +++ b/libstdc++-v3/testsuite/20_util/optional/make_optional-2.cc @@ -70,7 +70,9 @@ static_assert( can_make_optional2<int, int&>::value ); static_assert( noexcept(std::make_optional(i)) ); static_assert( ! can_make_optional2<void, void>::value ); static_assert( can_make_optional2<Cont, Cont>::value ); +#if __cplusplus <= 202302 static_assert( noexcept(std::make_optional<Cont>({})) ); +#endif static_assert( can_make_optional2<Cont, const Cont&>::value ); static_assert( ! noexcept(std::make_optional(c)) ); static_assert( can_make_optional2<Cont, int>::value ); diff --git a/libstdc++-v3/testsuite/20_util/optional/make_optional_neg.cc b/libstdc++-v3/testsuite/20_util/optional/make_optional_neg.cc new file mode 100644 index 000000000000..09499477233b --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/make_optional_neg.cc @@ -0,0 +1,20 @@ +// { dg-do compile { target c++17 } } + +#include <initializer_list> +#include <optional> + +struct S { int x; int* p; }; +int v; + +auto os1 = std::make_optional<S>({1, &v}); // { dg-error "no matching function for" "" { target c++26 } } + +struct Cont +{ + Cont(); + Cont(std::initializer_list<int>, int); +}; + +auto oc1 = std::make_optional<Cont>({}); // { dg-error "no matching function for" "" { target c++26 } } + +// { dg-prune-output "no type named 'type' in 'struct std::enable_if" } +// { dg-prune-output "type/value mismatch at argument 1 in template parameter list" } diff --git a/libstdc++-v3/testsuite/20_util/optional/monadic/ref_neg.cc b/libstdc++-v3/testsuite/20_util/optional/monadic/ref_neg.cc new file mode 100644 index 000000000000..ed1b42b1992d --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/monadic/ref_neg.cc @@ -0,0 +1,20 @@ +// { dg-do compile { target c++23 } } + +#include <optional> + +struct S { int x; int y; }; + +void test() +{ + std::optional<S> o; + const std::optional<S>& co = o; + + o.transform(&S::x); // { dg-error "from here" "optional<int&>" { target c++23_down } } + co.transform(&S::x); // { dg-error "from here" "optional<const int&>" { target c++23_down } } + std::move(o).transform(&S::x); // { dg-error "from here" "optional<int&&>" } + std::move(co).transform(&S::x); // { dg-error "from here" "optional<const int&&>" } +} + +// { dg-prune-output "in a union may not have reference type" } +// { dg-prune-output "static assertion failed" } +// { dg-prune-output "forming pointer to reference type" } diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/access.cc b/libstdc++-v3/testsuite/20_util/optional/ref/access.cc new file mode 100644 index 000000000000..37c8ff355a66 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/ref/access.cc @@ -0,0 +1,119 @@ +// { dg-do run { target c++26 } } + +#include <optional> +#include <type_traits> +#include <testsuite_hooks.h> + +struct NonMovable +{ + constexpr NonMovable() {} + NonMovable(NonMovable&&) = delete; +}; + +template<typename T> +constexpr +void test_value(T& t) +{ + std::optional<T&> o1(t); + const std::optional<T&> co1(t); + + static_assert( std::is_same_v<decltype(o1.value()), T&> ); + VERIFY( &o1.value() == &t ); + + static_assert( std::is_same_v<decltype(co1.value()), T&> ); + VERIFY( &co1.value() == &t ); + + static_assert( std::is_same_v<decltype(std::move(o1).value()), T&> ); + VERIFY( &std::move(o1).value() == &t ); + + static_assert( std::is_same_v<decltype(std::move(co1).value()), T&> ); + VERIFY( &std::move(co1).value() == &t ); + + std::optional<const T&> o2(t); + static_assert( std::is_same_v<decltype(o2.value()), const T&> ); + VERIFY( &o2.value() == &t ); +} + +struct Tracker +{ + int copy = 0; + int move = 0; + + constexpr Tracker(int v) : copy(v), move(v) {} + + constexpr Tracker(Tracker const& o) + : copy(o.copy+1), move(o.move) + {} + + constexpr Tracker(Tracker&& o) + : copy(o.copy), move(o.move+1) + {} + + Tracker& operator=(Tracker) = delete; +}; + +constexpr +void test_value_or() +{ + Tracker t(100), u(200); + std::optional<Tracker&> o(t); + + Tracker r1 = o.value_or(u); + VERIFY( r1.copy == 101 ); + VERIFY( r1.move == 100 ); + Tracker r2 = o.value_or(std::move(u)); + VERIFY( r2.copy == 101 ); + VERIFY( r2.move == 100 ); + Tracker r3 = std::move(o).value_or(u); + VERIFY( r3.copy == 101 ); + VERIFY( r3.move == 100 ); + Tracker r4 = std::move(o).value_or(std::move(u)); + VERIFY( r4.copy == 101 ); + VERIFY( r4.move == 100 ); + + o.reset(); + Tracker r5 = o.value_or(u); + VERIFY( r5.copy == 201 ); + VERIFY( r5.move == 200 ); + Tracker r6 = o.value_or(std::move(u)); + VERIFY( r6.copy == 200 ); + VERIFY( r6.move == 201 ); + Tracker r7 = std::move(o).value_or(u); + VERIFY( r7.copy == 201 ); + VERIFY( r7.move == 200 ); + Tracker r8 = std::move(o).value_or(std::move(u)); + VERIFY( r8.copy == 200 ); + VERIFY( r8.move == 201 ); +} + +template<typename T> +concept has_value_or_for = requires(std::optional<T&> t, T& u) +{ t.value_or(u); }; + +static_assert( has_value_or_for<int> ); +static_assert( has_value_or_for<NonMovable> ); +static_assert( !has_value_or_for<int[2]> ); +static_assert( has_value_or_for<int(*)[2]> ); +static_assert( !has_value_or_for<int()> ); +static_assert( has_value_or_for<int(*)()> ); + +int i; +NonMovable nm; +int arr[2]; +int foo(); + +int main() +{ + auto test_all = [] { + test_value<int>(i); + test_value<NonMovable>(nm); + test_value<int[2]>(arr); + test_value<int()>(foo); + + test_value_or(); + return true; + }; + + test_all(); + static_assert(test_all()); +} diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/assign.cc b/libstdc++-v3/testsuite/20_util/optional/ref/assign.cc new file mode 100644 index 000000000000..be8b1c84ecae --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/ref/assign.cc @@ -0,0 +1,430 @@ +// { dg-do run { target c++26 } } + +#include <optional> +#include <type_traits> +#include <testsuite_hooks.h> + +struct NonTrivial +{ + constexpr NonTrivial() {} + constexpr NonTrivial(NonTrivial const&) {}; + constexpr ~NonTrivial() {}; +}; + +struct NonMovable +{ + NonMovable() = default; + NonMovable(NonMovable&&) = delete; +}; + +template<typename T> +struct Conv +{ + T t; + + constexpr operator T() const noexcept + { return t; } +}; + +struct Tracker +{ + struct Counter + { + int copy; + int move; + }; + + Counter ctor{0,0}; + Counter assign{0,0}; + + Tracker() = default; + + constexpr Tracker(Tracker const& o) + : ctor(o.ctor), assign(o.assign) + { + ctor.copy += 1; + } + + constexpr Tracker(Tracker&& o) + : ctor(o.ctor), assign(o.assign) + { + ctor.move += 1; + } + + constexpr Tracker& operator=(const Tracker& o) + { + assign.copy += 1; + return *this; + } + + constexpr Tracker& operator=(Tracker&& o) + { + assign.move += 1; + return *this; + } + +}; + +template<typename T> +void +test_trivial() +{ + static_assert(std::is_copy_assignable_v<std::optional<T&>>); + static_assert(std::is_move_assignable_v<std::optional<T&>>); +} + +constexpr void +test_trivial_all() +{ + test_trivial<int>(); + test_trivial<NonTrivial>(); + test_trivial<std::optional<int&>>(); +} + +constexpr void +test_copy() +{ + Tracker t, u; + std::optional<Tracker&> e; + std::optional<Tracker&> o1(t); + std::optional<Tracker&> o2(u); + + o2 = o1; + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &t ); + VERIFY( o2.has_value() ); + VERIFY( &o2.value() == &t ); + VERIFY( t.ctor.copy == 0 ); + VERIFY( t.ctor.move == 0 ); + VERIFY( t.assign.copy == 0 ); + VERIFY( t.assign.move == 0 ); + + o2 = e; + VERIFY( !o2.has_value() ); + + o2 = std::move(o1); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &t ); + VERIFY( o2.has_value() ); + VERIFY( &o2.value() == &t ); + VERIFY( t.ctor.copy == 0 ); + VERIFY( t.ctor.move == 0 ); + VERIFY( t.assign.copy == 0 ); + VERIFY( t.assign.move == 0 ); + + o2 = std::move(e); + VERIFY( !o2.has_value() ); +} + +template<typename T, typename U> +concept can_emplace = requires (T t, U&& u) +{ t.emplace(std::forward<U>(u)); }; + +constexpr void +test_from_value() +{ + NonTrivial v, u; + const NonTrivial& cv = v; + const std::optional<NonTrivial&> s(u); + std::optional<NonTrivial&> o1; + std::optional<const NonTrivial&> co1; + + o1 = s; + o1 = v; + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + + o1.reset(); + VERIFY( !o1.has_value() ); + + o1 = v; + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + const NonTrivial&> ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + NonTrivial> ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + const NonTrivial> ); + + o1 = s; + o1.emplace(v); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + + o1 = std::nullopt; + VERIFY( !o1.has_value() ); + + o1.emplace(v); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + + static_assert( !can_emplace<std::optional<NonTrivial&>, const NonTrivial&> ); + static_assert( !can_emplace<std::optional<NonTrivial&>, NonTrivial> ); + static_assert( !can_emplace<std::optional<NonTrivial&>, const NonTrivial> ); + + co1 = s; + co1 = v; + VERIFY( co1.has_value() ); + VERIFY( &co1.value() == &v ); + + co1 = std::nullopt; + co1 = cv; + VERIFY( co1.has_value() ); + VERIFY( &co1.value() == &v ); + // No binding to rvalue + static_assert( !std::is_assignable_v<std::optional<const NonTrivial&>, + NonTrivial> ); + static_assert( !std::is_assignable_v<std::optional<const NonTrivial&>, + const NonTrivial> ); + + co1 = std::nullopt; + co1.emplace(v); + VERIFY( co1.has_value() ); + VERIFY( &co1.value() == &v ); + + co1 = s; + co1.emplace(cv); + VERIFY( co1.has_value() ); + VERIFY( &co1.value() == &v ); + // No binding to rvalue + static_assert( !can_emplace<std::optional<const NonTrivial&>, const NonTrivial> ); + static_assert( !can_emplace<std::optional<const NonTrivial&>, NonTrivial> ); + + + // Conversion create a pr-value that would bind to temporary + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + Conv<NonTrivial>&> ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + const Conv<NonTrivial>&> ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + Conv<NonTrivial>> ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + const Conv<NonTrivial>> ); + + static_assert( !can_emplace<std::optional<NonTrivial&>, Conv<NonTrivial>&> ); + static_assert( !can_emplace<std::optional<NonTrivial&>, const Conv<NonTrivial>&> ); + static_assert( !can_emplace<std::optional<NonTrivial&>, Conv<NonTrivial>> ); + static_assert( !can_emplace<std::optional<NonTrivial&>, const Conv<NonTrivial>> ); + + Conv<NonTrivial&> rw(v); + const Conv<NonTrivial&> crw(v); + + o1 = std::nullopt; + o1 = rw; + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + o1 = s; + o1 = crw; + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + o1 = s; + o1 = std::move(rw); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + o1 = std::nullopt; + o1 = std::move(crw); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + + o1 = s; + o1.emplace(rw); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + o1 = std::nullopt; + o1.emplace(crw); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + o1 = std::nullopt; + o1.emplace(std::move(rw)); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + o1 = s; + o1.emplace(std::move(crw)); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); +} + +constexpr void +test_from_opt_value() +{ + NonTrivial u; + std::optional<NonTrivial> v(std::in_place); + const std::optional<NonTrivial>& cv = v; + + const std::optional<NonTrivial&> s(u); + std::optional<NonTrivial&> o1; + std::optional<const NonTrivial&> co1; + + o1 = s; + o1 = v; + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v.value() ); + o1 = std::nullopt; + o1 = v; + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v.value() ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + const std::optional<NonTrivial>&> ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + std::optional<NonTrivial>> ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + const std::optional<NonTrivial>> ); + + co1 = s; + co1 = v; + VERIFY( co1.has_value() ); + VERIFY( &co1.value() == &v.value() ); + co1 = std::nullopt; + co1 = cv; + VERIFY( co1.has_value() ); + VERIFY( &co1.value() == &v.value() ); + // No binding to rvalue + static_assert( !std::is_assignable_v<std::optional<const NonTrivial&>, + std::optional<NonTrivial>> ); + static_assert( !std::is_assignable_v<std::optional<const NonTrivial&>, + const std::optional<NonTrivial>> ); + + // Conversion create a pr-value that would bind to temporary + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + std::optional<Conv<NonTrivial>>&> ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + const std::optional<Conv<NonTrivial>>&> ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + std::optional<Conv<NonTrivial>>> ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + const std::optional<Conv<NonTrivial>>> ); + + std::optional<Conv<NonTrivial&>> rw(*v); + std::optional<const Conv<NonTrivial&>> crw(*v); + + o1 = std::nullopt; + o1 = rw; + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v.value() ); + o1 = s; + o1 = crw; + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v.value() ); + o1 = s; + o1 = std::move(rw); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v.value() ); + o1 = std::nullopt; + o1 = std::move(crw); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v.value() ); +} + +constexpr void +test_to_opt_value() +{ + Tracker t; + std::optional<Tracker&> er; + std::optional<Tracker&> r(t); + const std::optional<Tracker&> cr(t); + + std::optional<Tracker> o1; + o1 = r; + VERIFY( o1.has_value() ); + VERIFY( o1->ctor.copy == 1 ); + VERIFY( o1->ctor.move == 0 ); + VERIFY( o1->assign.copy == 0 ); + VERIFY( o1->assign.move == 0 ); + + o1 = r; + VERIFY( o1.has_value() ); + VERIFY( o1->ctor.copy == 1 ); + VERIFY( o1->ctor.move == 0 ); + VERIFY( o1->assign.copy == 1 ); + VERIFY( o1->assign.move == 0 ); + + o1 = er; + VERIFY( !o1.has_value() ); + + o1 = cr; + VERIFY( o1.has_value() ); + VERIFY( o1->ctor.copy == 1 ); + VERIFY( o1->ctor.move == 0 ); + VERIFY( o1->assign.copy == 0 ); + VERIFY( o1->assign.move == 0 ); + + o1 = cr; + VERIFY( o1.has_value() ); + VERIFY( o1->ctor.copy == 1 ); + VERIFY( o1->ctor.move == 0 ); + VERIFY( o1->assign.copy == 1 ); + VERIFY( o1->assign.move == 0 ); + + o1 = std::move(er); + + o1 = std::move(r); + VERIFY( o1.has_value() ); + VERIFY( o1->ctor.copy == 1 ); + VERIFY( o1->ctor.move == 0 ); + VERIFY( o1->assign.copy == 0 ); + VERIFY( o1->assign.move == 0 ); + + o1 = std::move(cr); + VERIFY( o1.has_value() ); + VERIFY( o1->ctor.copy == 1 ); + VERIFY( o1->ctor.move == 0 ); + VERIFY( o1->assign.copy == 1 ); + VERIFY( o1->assign.move == 0 ); +} + +constexpr void +test_swap() +{ + NonMovable a, b; + std::optional<NonMovable&> oa(a), ob(b); + + oa.swap(ob); + static_assert(noexcept(oa.swap(ob))); + VERIFY( oa.has_value() ); + VERIFY( &oa.value() == &b ); + VERIFY( ob.has_value() ); + VERIFY( &ob.value() == &a ); + + swap(oa, ob); + static_assert(std::is_nothrow_swappable_v<std::optional<NonMovable&>>); + VERIFY( oa.has_value() ); + VERIFY( &oa.value() == &a ); + VERIFY( ob.has_value() ); + VERIFY( &ob.value() == &b ); + + ob.reset(); + oa.swap(ob); + VERIFY( !oa.has_value() ); + VERIFY( ob.has_value() ); + VERIFY( &ob.value() == &a ); + + ob.reset(); + std::swap(oa, ob); + VERIFY( !oa.has_value() ); + VERIFY( !ob.has_value() ); + + std::optional<const NonMovable&> ca(a), cb(b); + swap(ca, cb); + VERIFY( ca.has_value() ); + VERIFY( &ca.value() == &b ); + VERIFY( cb.has_value() ); + VERIFY( &cb.value() == &a ); + + static_assert(!std::is_swappable_with_v<std::optional<int>&, std::optional<int&>&>); +} + +int main() +{ + auto test_all = [] { + test_copy(); + test_from_value(); + test_from_opt_value(); + test_to_opt_value(); + test_swap(); + return true; + }; + + test_all(); + static_assert(test_all()); +} diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/cons.cc b/libstdc++-v3/testsuite/20_util/optional/ref/cons.cc new file mode 100644 index 000000000000..b13e8b92329f --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/ref/cons.cc @@ -0,0 +1,356 @@ +// { dg-do run { target c++26 } } + +#include <optional> +#include <type_traits> +#include <testsuite_hooks.h> + +struct NonTrivial +{ + constexpr NonTrivial() {} + constexpr NonTrivial(NonTrivial const&) {}; + constexpr ~NonTrivial() {}; +}; + +struct NonMovable +{ + constexpr NonMovable() {} + NonMovable(NonMovable&&) = delete; +}; + +template<typename T> +struct Conv +{ + T t; + + constexpr operator T() const noexcept + { return t; } +}; + +struct Tracker +{ + int copy = 0; + int move = 0; + + Tracker() = default; + + constexpr Tracker(Tracker const& o) + : copy(o.copy+1), move(o.move) + {} + + constexpr Tracker(Tracker&& o) + : copy(o.copy), move(o.move+1) + {} + + Tracker& operator=(Tracker) = delete; +}; + +template<typename T> +void +test_trivial() +{ + static_assert(std::is_trivially_copyable_v<std::optional<T&>>); + static_assert(std::is_copy_constructible_v<std::optional<T&>>); + static_assert(std::is_move_constructible_v<std::optional<T&>>); + static_assert(std::is_destructible_v<std::optional<T&>>); +} + +constexpr void +test_trivial_all() +{ + test_trivial<int>(); + test_trivial<NonTrivial>(); + test_trivial<NonMovable>(); + test_trivial<std::optional<int&>>(); +} + +constexpr void +test_copy() +{ + Tracker t; + std::optional<Tracker&> o1(t); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &t ); + VERIFY( t.copy == 0 ); + VERIFY( t.move == 0 ); + + std::optional<Tracker&> o2(o1); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &t ); + VERIFY( o2.has_value() ); + VERIFY( &o2.value() == &t ); + VERIFY( t.copy == 0 ); + VERIFY( t.move == 0 ); + + std::optional<Tracker&> o3(std::move(o1)); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &t ); + VERIFY( o3.has_value() ); + VERIFY( &o3.value() == &t ); + VERIFY( t.copy == 0 ); + VERIFY( t.move == 0 ); + + std::optional<Tracker&> e; + VERIFY( !e.has_value() ); + + std::optional<Tracker&> o4(e); + VERIFY( !e.has_value() ); + VERIFY( !o4.has_value() ); + + std::optional<Tracker&> o5(std::move(e)); + VERIFY( !e.has_value() ); + VERIFY( !o5.has_value() ); +} + + +constexpr void +test_from_value() +{ + NonTrivial v; + const NonTrivial& cv = v; + + std::optional<NonTrivial&> o1(v); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + const NonTrivial&> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + NonTrivial> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + const NonTrivial> ); + + std::optional<NonTrivial&> o2(std::in_place, v); + VERIFY( o2.has_value() ); + VERIFY( &o2.value() == &v ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + std::in_place_t, const NonTrivial&> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + std::in_place_t, NonTrivial> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + std::in_place_t, const NonTrivial> ); + + std::optional<const NonTrivial&> co1(v); + VERIFY( co1.has_value() ); + VERIFY( &co1.value() == &v ); + std::optional<const NonTrivial&> co2(cv); + VERIFY( co2.has_value() ); + VERIFY( &co2.value() == &v ); + // No binding to rvalue + static_assert( !std::is_constructible_v<std::optional<const NonTrivial&>, + NonTrivial> ); + static_assert( !std::is_constructible_v<std::optional<const NonTrivial&>, + const NonTrivial> ); + + std::optional<const NonTrivial&> co3(std::in_place, v); + VERIFY( co3.has_value() ); + VERIFY( &co3.value() == &v ); + std::optional<const NonTrivial&> co4(std::in_place, cv); + VERIFY( co4.has_value() ); + VERIFY( &co4.value() == &v ); + // No binding to rvalue + static_assert( !std::is_constructible_v<std::optional<const NonTrivial&>, + std::in_place_t, NonTrivial> ); + static_assert( !std::is_constructible_v<std::optional<const NonTrivial&>, + std::in_place_t, const NonTrivial> ); + + // Conversion create a pr-value that would bind to temporary + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + Conv<NonTrivial>&> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + const Conv<NonTrivial>&> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + Conv<NonTrivial>> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + const Conv<NonTrivial>> ); + + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + std::in_place_t, Conv<NonTrivial>&> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + std::in_place_t, const Conv<NonTrivial>&> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + std::in_place_t, Conv<NonTrivial>> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + std::in_place_t, const Conv<NonTrivial>> ); + + Conv<NonTrivial&> rw(v); + const Conv<NonTrivial&> crw(v); + + std::optional<NonTrivial&> ro1(rw); + VERIFY( ro1.has_value() ); + VERIFY( &ro1.value() == &v ); + std::optional<NonTrivial&> ro2(crw); + VERIFY( ro2.has_value() ); + VERIFY( &ro2.value() == &v ); + std::optional<NonTrivial&> ro3(std::move(rw)); + VERIFY( ro3.has_value() ); + VERIFY( &ro3.value() == &v ); + std::optional<NonTrivial&> ro4(std::move(crw)); + VERIFY( ro4.has_value() ); + VERIFY( &ro4.value() == &v ); + + std::optional<NonTrivial&> ro5(std::in_place, rw); + VERIFY( ro5.has_value() ); + VERIFY( &ro5.value() == &v ); + std::optional<NonTrivial&> ro6(std::in_place, crw); + VERIFY( ro6.has_value() ); + VERIFY( &ro6.value() == &v ); + std::optional<NonTrivial&> ro7(std::in_place, std::move(rw)); + VERIFY( ro7.has_value() ); + VERIFY( &ro7.value() == &v ); + std::optional<NonTrivial&> ro8(std::in_place, std::move(crw)); + VERIFY( ro8.has_value() ); + VERIFY( &ro8.value() == &v ); +} + +constexpr void +test_from_opt_value() +{ + std::optional<NonTrivial> v(std::in_place); + const std::optional<NonTrivial>& cv = v; + + std::optional<NonTrivial&> o1(v); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v.value() ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + const std::optional<NonTrivial>&> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + std::optional<NonTrivial>> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + const std::optional<NonTrivial>> ); + + std::optional<const NonTrivial&> co1(v); + VERIFY( co1.has_value() ); + VERIFY( &co1.value() == &v.value() ); + std::optional<const NonTrivial&> co2(cv); + VERIFY( co2.has_value() ); + VERIFY( &co2.value() == &v.value() ); + // No binding to rvalue + static_assert( !std::is_constructible_v<std::optional<const NonTrivial&>, + std::optional<NonTrivial>> ); + static_assert( !std::is_constructible_v<std::optional<const NonTrivial&>, + const std::optional<NonTrivial>> ); + + // Conversion create a pr-value that would bind to temporary + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + std::optional<Conv<NonTrivial>>&> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + const std::optional<Conv<NonTrivial>>&> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + std::optional<Conv<NonTrivial>>> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + const std::optional<Conv<NonTrivial>>> ); + + std::optional<Conv<NonTrivial&>> rw(*v); + std::optional<const Conv<NonTrivial&>> crw(*v); + + std::optional<NonTrivial&> ro1(rw); + VERIFY( ro1.has_value() ); + VERIFY( &ro1.value() == &v.value() ); + std::optional<NonTrivial&> ro2(crw); + VERIFY( ro2.has_value() ); + VERIFY( &ro2.value() == &v.value() ); + std::optional<NonTrivial&> ro3(std::move(rw)); + VERIFY( ro3.has_value() ); + VERIFY( &ro3.value() == &v.value() ); + std::optional<NonTrivial&> ro4(std::move(crw)); + VERIFY( ro4.has_value() ); + VERIFY( &ro4.value() == &v.value() ); +} + +constexpr void +test_to_opt_value() +{ + Tracker t; + std::optional<Tracker&> r(t); + const std::optional<Tracker&> cr(t); + + std::optional<Tracker> o1(r); + VERIFY( o1.has_value() ); + VERIFY( o1->copy == 1 ); + VERIFY( o1->move == 0 ); + + std::optional<Tracker> o2(cr); + VERIFY( o2.has_value() ); + VERIFY( o2->copy == 1 ); + VERIFY( o2->move == 0 ); + + std::optional<Tracker> o3(std::move(r)); + VERIFY( o3.has_value() ); + VERIFY( o3->copy == 1 ); + VERIFY( o3->move == 0 ); + + std::optional<Tracker> o4(std::move(cr)); + VERIFY( o4.has_value() ); + VERIFY( o4->copy == 1 ); + VERIFY( o4->move == 0 ); + + std::optional<Tracker&> er; + const std::optional<Tracker&> cer; + + std::optional<Tracker> e1(er); + VERIFY( !e1.has_value() ); + + std::optional<Tracker> e2(cer); + VERIFY( !e2.has_value() ); + + std::optional<Tracker> e3(std::move(er)); + VERIFY( !e3.has_value() ); + + std::optional<Tracker> e4(std::move(cer)); + VERIFY( !e4.has_value() ); +} + +constexpr void +test_opt_opt() +{ + std::optional<int> s(43); + + std::optional<std::optional<int>&> o1(s); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &s ); + + std::optional<std::optional<int>&> o2(std::in_place, s); + VERIFY( o2.has_value() ); + VERIFY( &o2.value() == &s ); + + std::optional<std::optional<int>> o3(o1); + VERIFY( o2.has_value() ); + VERIFY( o2.value().has_value() ); + VERIFY( o2.value() == 43 ); + + s.reset(); + std::optional<std::optional<int>&> o4(s); + VERIFY( o4.has_value() ); + VERIFY( &o4.value() == &s ); + + std::optional<std::optional<int>&> o5(std::in_place, s); + VERIFY( o5.has_value() ); + VERIFY( &o5.value() == &s ); + + std::optional<std::optional<int>> o6(o1); + VERIFY( o6.has_value() ); + VERIFY( !o6.value().has_value() ); + + std::optional<std::optional<int>> s2(std::in_place); + std::optional<std::optional<int>&> oo1(s2); + VERIFY( oo1.has_value() ); + VERIFY( &oo1.value() == &s2.value() ); + + s2.reset(); + std::optional<std::optional<int>&> oo2(s2); + VERIFY( !oo2.has_value() ); +} + +int main() +{ + auto test_all = [] { + test_copy(); + test_from_value(); + test_from_opt_value(); + test_to_opt_value(); + test_opt_opt(); + return true; + }; + + test_all(); + static_assert(test_all()); +} diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/internal_traits.cc b/libstdc++-v3/testsuite/20_util/optional/ref/internal_traits.cc new file mode 100644 index 000000000000..ef50da7e9fa2 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/ref/internal_traits.cc @@ -0,0 +1,11 @@ +// { dg-do compile { target c++26 } } + +#include <optional> +#include <variant> + +template<typename T> +constexpr bool _Never_valueless + = std::__detail::__variant::_Never_valueless_alt<T>::value; + +static_assert( _Never_valueless<std::optional<int&>> ); +static_assert( _Never_valueless<std::optional<const int&>> ); diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/1.cc b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/1.cc new file mode 100644 index 000000000000..d9946d034efb --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/1.cc @@ -0,0 +1,74 @@ +// { dg-do run { target c++20 } } + +#include <optional> +#include <utility> +#include <testsuite_hooks.h> + +struct NonTrivial +{ + constexpr NonTrivial() {} + constexpr NonTrivial(NonTrivial const&) {}; + constexpr ~NonTrivial() {}; +}; + +template<typename T> +struct Conv +{ + T t; + + constexpr operator T() const noexcept + { return t; } +}; + +constexpr bool test() +{ + NonTrivial t; + const NonTrivial& ct = t; + +#if __cplusplus > 202302 + auto o1 = std::make_optional<NonTrivial&>(t); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &t ); + + auto o2 = std::make_optional<const NonTrivial&>(t); + VERIFY( o2.has_value() ); + VERIFY( &o2.value() == &t ); + + auto o3 = std::make_optional<const NonTrivial&>(ct); + VERIFY( o3.has_value() ); + VERIFY( &o3.value() == &t ); + + Conv<NonTrivial&> rw(t); + auto o4 = std::make_optional<NonTrivial&>(rw); + VERIFY( o4.has_value() ); + VERIFY( &o4.value() == &t ); + + auto o5 = std::make_optional<NonTrivial&>(std::as_const(rw)); + VERIFY( o5.has_value() ); + VERIFY( &o5.value() == &t ); + + auto o6 = std::make_optional<NonTrivial&>(Conv<NonTrivial&>(t)); + VERIFY( o6.has_value() ); + VERIFY( &o6.value() == &t ); +#else + auto o1 = std::make_optional<NonTrivial&>(t); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() != &t ); + + auto o3 = std::make_optional<const NonTrivial&>(ct); + VERIFY( o3.has_value() ); + VERIFY( &o3.value() != &t ); + + auto o2 = std::make_optional<NonTrivial&&>(std::move(t)); + VERIFY( o2.has_value() ); + VERIFY( &o2.value() != &t ); +#endif + + return true; +} + +int main() +{ + test(); + static_assert(test()); +} diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_args_neg.cc b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_args_neg.cc new file mode 100644 index 000000000000..6166c658c54b --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_args_neg.cc @@ -0,0 +1,43 @@ +// { dg-do compile { target c++17 } } + +#include <optional> + +struct C { + C(); + C(int); +}; +C s(10); +const C cs(1); + +template<typename T> +using decay_pre26 = +#if __cplusplus > 202302 + T; +#else + std::decay_t<T>; +#endif + +auto z1 = std::make_optional<C&>(); // { dg-error "no matching function for call" } +auto z2 = std::make_optional<const C&>(); // { dg-error "no matching function for call" } +auto z3 = std::make_optional<C&&>(); // { dg-error "no matching function for call" } +auto z4 = std::make_optional<const C&&>(); // { dg-error "no matching function for call" } + +auto o1 = std::make_optional<C&>(10); // { dg-error "no matching function for call" } +auto o2 = std::make_optional<const C&>(10); // { dg-error "from here" } +auto o3 = std::make_optional<C&&>(10); // { dg-error "from here" } +auto o4 = std::make_optional<const C&&>(10); // { dg-error "from here" } + +auto t1 = std::make_optional<C&>(10, 20); // { dg-error "no matching function for call" } +auto t2 = std::make_optional<const C&>(10, 20); // { dg-error "no matching function for call" } +auto t3 = std::make_optional<C&&>(10, 20); // { dg-error "no matching function for call" } +auto t3 = std::make_optional<const C&&>(10, 20); // { dg-error "no matching function for call" } + +// { dg-prune-output "no type named 'type' in 'struct std::enable_if" } +// { dg-prune-output "type/value mismatch at argument 1 in template parameter list" } +// { dg-prune-output "in a union may not have reference type" } +// { dg-prune-output "static assertion failed" } +// { dg-prune-output "forming pointer to reference type" } +// { dg-prune-output "cannot bind .* reference of type" } +// { dg-prune-output "binding reference of type" } +// { dg-prune-output "no matching function for call to 'std::optional" } + diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_lvalue_neg.cc b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_lvalue_neg.cc new file mode 100644 index 000000000000..aed6791a3ff6 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_lvalue_neg.cc @@ -0,0 +1,40 @@ +// { dg-do compile { target c++17 } } + +#include <optional> +#include <type_traits> + +struct C { + C(); + C(int); +}; +C s(10); +const C cs(1); + +template<typename T> +using decay_pre26 = +#if __cplusplus > 202302 + T; +#else + std::decay_t<T>; +#endif + +auto lr1 = std::make_optional<C&>(s); // changed meaning +static_assert( std::is_same_v< decltype(lr1), std::optional<decay_pre26<C&>>> ); +auto lr2 = std::make_optional<const C&>(s); // { dg-error "here" "" { target c++23_down } } +auto lr3 = std::make_optional<C&&>(s); // { dg-error "no matching function for call" } +auto lr4 = std::make_optional<const C&&>(s); // { dg-error "no matching function for call" } + +auto clr1 = std::make_optional<C&>(cs); // { dg-error "no matching function for call" } +auto clr2 = std::make_optional<const C&>(cs); // changed meaning +static_assert( std::is_same_v< decltype(clr2), std::optional<decay_pre26<const C&>>> ); +auto clr3 = std::make_optional<C&&>(cs); // { dg-error "no matching function for call" } +auto clr3 = std::make_optional<const C&&>(cs); // { dg-error "no matching function for call" } + +// { dg-prune-output "no type named 'type' in 'struct std::enable_if" } +// { dg-prune-output "type/value mismatch at argument 1 in template parameter list" } +// { dg-prune-output "in a union may not have reference type" } +// { dg-prune-output "static assertion failed" } +// { dg-prune-output "forming pointer to reference type" } +// { dg-prune-output "cannot bind .* reference of type" } +// { dg-prune-output "binding reference of type" } +// { dg-prune-output "no matching function for call to `std::optional" } diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_rvalue_neg.cc b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_rvalue_neg.cc new file mode 100644 index 000000000000..22f669ceb68e --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_rvalue_neg.cc @@ -0,0 +1,38 @@ +// { dg-do compile { target c++17 } } + +#include <optional> +#include <type_traits> + +struct C { + C(); + C(int); +}; +C s(10); +const C cs(1); + +template<typename T> +using decay_pre26 = +#if __cplusplus > 202302 + T; +#else + std::decay_t<T>; +#endif + +auto p1 = std::make_optional<C&>(C(10)); // { dg-error "no matching function for call" } +auto p2 = std::make_optional<const C&>(C(10)); // { dg-error "from here" } +auto p3 = std::make_optional<C&&>(C(10)); // { dg-error "from here" "" { target c++26 } } +auto p4 = std::make_optional<const C&&>(C(10)); // { dg-error "from here" } + +auto b1 = std::make_optional<C&>({10}); // { dg-error "no matching function for call" } +auto b2 = std::make_optional<const C&>({10}); // { dg-error "no matching function for call" "" { target c++26 } } +auto b3 = std::make_optional<C&&>({10}); // { dg-error "no matching function for call" "" { target c++26 } } +auto b4 = std::make_optional<const C&&>({10}); // { dg-error "no matching function for call" "" { target c++26 } } + +// { dg-prune-output "no type named 'type' in 'struct std::enable_if" } +// { dg-prune-output "type/value mismatch at argument 1 in template parameter list" } +// { dg-prune-output "in a union may not have reference type" } +// { dg-prune-output "static assertion failed" } +// { dg-prune-output "forming pointer to reference type" } +// { dg-prune-output "cannot bind .* reference of type" } +// { dg-prune-output "binding reference of type" } +// { dg-prune-output "no matching function for call to 'std::optional" } diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/monadic.cc b/libstdc++-v3/testsuite/20_util/optional/ref/monadic.cc new file mode 100644 index 000000000000..e8460c6528bd --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/ref/monadic.cc @@ -0,0 +1,192 @@ +// { dg-do run { target c++26 } } + +#include <optional> +#include <type_traits> +#include <testsuite_hooks.h> +#include <utility> + +struct NonMovable +{ + constexpr NonMovable() {} + NonMovable(NonMovable&&) = delete; +}; + +struct Tracker +{ + int copy = 0; + int move = 0; + + Tracker() = default; + + constexpr Tracker(Tracker const& o) + : copy(o.copy+1), move(o.move) + {} + + constexpr Tracker(Tracker&& o) + : copy(o.copy), move(o.move+1) + {} + + Tracker& operator=(Tracker) = delete; + + void reset() { + copy = move = 0; + } +}; + +template<typename T> +auto identity_of = []<typename U>(U&& t) -> std::optional<T> +{ + static_assert( std::is_same_v<T, U&&> ); + VERIFY( t.copy == 0 ); + VERIFY( t.move == 0 ); + return std::optional<T>(t); +}; + +constexpr void +test_and_then() +{ + std::optional<Tracker> t(std::in_place); + std::optional<Tracker&> rt(t); + std::optional<const Tracker&> rct(t); + + auto r1 = t.and_then(identity_of<Tracker&>); + VERIFY( r1.has_value() ); + VERIFY( &r1.value() == &t.value() ); + + auto r2 = rt.and_then(identity_of<Tracker&>); + VERIFY( r2.has_value() ); + VERIFY( &r2.value() == &t.value() ); + + std::as_const(rt).and_then(identity_of<Tracker&>); + std::move(rt).and_then(identity_of<Tracker&>); + + auto r4 = rct.and_then(identity_of<const Tracker&>); + VERIFY( r4.has_value() ); + VERIFY( &r4.value() == &t.value() ); + + std::as_const(rct).and_then(identity_of<const Tracker&>); + std::move(rct).and_then(identity_of<const Tracker&>); + + auto r5 = rt.and_then([](Tracker&) { return std::optional<int>(42); }); + static_assert( std::is_same_v<decltype(r5), std::optional<int>> ); + VERIFY( r5.has_value() ); + VERIFY( r5.value() == 42 ); + + auto r6 = rct.and_then([](const Tracker&) { return std::optional<int>(); }); + static_assert( std::is_same_v<decltype(r6), std::optional<int>> ); + VERIFY( !r6.has_value() ); + + rct.reset(); + auto r7 = rct.and_then([](const Tracker&) { VERIFY(false); return std::optional<int>(42); }); + static_assert( std::is_same_v<decltype(r7), std::optional<int>> ); + VERIFY( !r7.has_value() ); + + rt.reset(); + auto r8 = rt.and_then([](Tracker&) { VERIFY(false); return std::optional<int>(); }); + static_assert( std::is_same_v<decltype(r8), std::optional<int>> ); + VERIFY( !r8.has_value() ); +} + +template<typename T> +constexpr void +test_or_else() +{ + T t, u; + + std::optional<T&> ot(t); + auto r1 = ot.or_else([&] { VERIFY(false); return std::optional<T&>(u); }); + VERIFY( &ot.value() == &t ); + VERIFY( r1.has_value() ); + VERIFY( &r1.value() == &t ); + auto r2 = std::move(ot).or_else([&] { VERIFY(false); return std::optional<T&>(); }); + VERIFY( &ot.value() == &t ); + VERIFY( r2.has_value() ); + VERIFY( &r2.value() == &t ); + + ot.reset(); + auto r3 = ot.or_else([&] { return std::optional<T&>(u); }); + VERIFY( !ot.has_value() ); + VERIFY( r3.has_value() ); + VERIFY( &r3.value() == &u ); + auto r4 = std::move(ot).or_else([] { return std::optional<T&>(); }); + VERIFY( !ot.has_value() ); + VERIFY( !r4.has_value() ); +} + +constexpr void +test_transform() +{ + std::optional<Tracker> t(std::in_place); + + auto r1 = t.transform(&Tracker::copy); + static_assert( std::is_same_v<decltype(r1), std::optional<int&>> ); + VERIFY( r1.has_value() ); + VERIFY( &r1.value() == &t->copy ); + auto r2 = std::as_const(t).transform(&Tracker::move); + static_assert( std::is_same_v<decltype(r2), std::optional<const int&>> ); + VERIFY( r2.has_value() ); + VERIFY( &r2.value() == &t->move ); + + std::optional<Tracker&> rt(t); + auto r3 = rt.transform(&Tracker::copy); + static_assert( std::is_same_v<decltype(r3), std::optional<int&>> ); + VERIFY( r3.has_value() ); + VERIFY( &r3.value() == &t->copy ); + auto r4 = std::as_const(rt).transform(&Tracker::copy); + static_assert( std::is_same_v<decltype(r4), std::optional<int&>> ); + VERIFY( r4.has_value() ); + VERIFY( &r4.value() == &t->copy ); + auto r5 = std::move(rt).transform(&Tracker::copy); + static_assert( std::is_same_v<decltype(r5), std::optional<int&>> ); + VERIFY( r5.has_value() ); + VERIFY( &r5.value() == &t->copy ); + + auto r6 = rt.transform([] (Tracker& t) { return 10; }); + static_assert( std::is_same_v<decltype(r6), std::optional<int>> ); + VERIFY( r6.has_value() ); + VERIFY( &r6.value() != &t->copy ); + VERIFY( r6.value() == 10 ); + + auto r7 = rt.transform([] (Tracker& t) { return NonMovable(); }); + static_assert( std::is_same_v<decltype(r7), std::optional<NonMovable>> ); + VERIFY( r7.has_value() ); + + rt.reset(); + auto r8 = rt.transform([] (Tracker& t) { VERIFY(false); return 42; }); + static_assert( std::is_same_v<decltype(r8), std::optional<int>> ); + VERIFY( !r8.has_value() ); + + std::optional<const Tracker&> crt(t); + auto r9 = crt.transform(&Tracker::copy); + static_assert( std::is_same_v<decltype(r9), std::optional<const int&>> ); + VERIFY( r9.has_value() ); + VERIFY( &r9.value() == &t->copy ); + auto r10 = std::as_const(crt).transform(&Tracker::copy); + static_assert( std::is_same_v<decltype(r10), std::optional<const int&>> ); + VERIFY( r10.has_value() ); + VERIFY( &r10.value() == &t->copy ); + auto r11 = std::move(crt).transform(&Tracker::copy); + static_assert( std::is_same_v<decltype(r11), std::optional<const int&>> ); + VERIFY( r11.has_value() ); + VERIFY( &r11.value() == &t->copy ); + + crt.reset(); + auto r12 = rt.transform([] (Tracker& t) { VERIFY(false); return 42; }); + static_assert( std::is_same_v<decltype(r12), std::optional<int>> ); + VERIFY( !r12.has_value() ); +} + +int main() +{ + auto test_all = [] { + test_and_then(); + test_transform(); + test_or_else<Tracker>(); + test_or_else<const Tracker>(); + test_or_else<NonMovable>(); + return true; + }; + + test_all(); + static_assert(test_all()); +} diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/relops.cc b/libstdc++-v3/testsuite/20_util/optional/ref/relops.cc new file mode 100644 index 000000000000..e0b30c8e898d --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/ref/relops.cc @@ -0,0 +1,229 @@ +// { dg-do run { target c++26 } } + +#include <optional> +#include <functional> +#include <testsuite_hooks.h> +#include <type_traits> + +template<typename T, typename H = std::hash<T>> +constexpr bool has_disabled_hash + = !std::is_default_constructible_v<H> + && !std::is_copy_constructible_v<H> + && !std::is_move_constructible_v<H> + && !std::is_copy_assignable_v<H> + && !std::is_move_assignable_v<H>; + +static_assert(has_disabled_hash<std::optional<int&>>); +static_assert(has_disabled_hash<std::optional<const int&>>); + +template<typename T, typename V> +constexpr void +test_compare_val(V& l, V& h) +{ + std::optional<T> t; + + VERIFY( !(t == l) ); + VERIFY( (t != l) ); + VERIFY( (t < l) ); + VERIFY( (t <= l) ); + VERIFY( !(t > l) ); + VERIFY( !(t >= l) ); + VERIFY( (t <=> l) < 0 ); + + VERIFY( !(l == t) ); + VERIFY( (l != t) ); + VERIFY( !(l < t) ); + VERIFY( !(l <= t) ); + VERIFY( (l > t) ); + VERIFY( (l >= t) ); + VERIFY( (l <=> t) > 0 ); + + t.emplace(l); + VERIFY( (t == l) ); + VERIFY( !(t != l) ); + VERIFY( !(t < l) ); + VERIFY( (t <= l) ); + VERIFY( !(t > l) ); + VERIFY( (t >= l) ); + VERIFY( (t <=> l) == 0 ); + + VERIFY( (l == t) ); + VERIFY( !(l != t) ); + VERIFY( !(l < t) ); + VERIFY( (l <= t) ); + VERIFY( !(l > t) ); + VERIFY( (l >= t) ); + VERIFY( (t <=> l) == 0 ); + + t.emplace(h); + VERIFY( !(t == l) ); + VERIFY( (t != l) ); + VERIFY( !(t < l) ); + VERIFY( !(t <= l) ); + VERIFY( (t > l) ); + VERIFY( (t >= l) ); + VERIFY( (t <=> l) > 0 ); + + VERIFY( !(l == t) ); + VERIFY( (l != t) ); + VERIFY( (l < t) ); + VERIFY( (l <= t) ); + VERIFY( !(l > t) ); + VERIFY( !(l >= t) ); + VERIFY( (l <=> t) < 0 ); +} + +template<typename T, typename U, typename V> +constexpr void +test_compare_opts(V& l, V& h) +{ + std::optional<T> t; + std::optional<U> u; + + VERIFY( (t == u) ); + VERIFY( !(t != u) ); + VERIFY( !(t < u) ); + VERIFY( (t <= u) ); + VERIFY( !(t > u) ); + VERIFY( (t >= u) ); + VERIFY( (t <=> u) == 0 ); + + t.emplace(l); + VERIFY( !(t == u) ); + VERIFY( (t != u) ); + VERIFY( !(t < u) ); + VERIFY( !(t <= u) ); + VERIFY( (t > u) ); + VERIFY( (t >= u) ); + VERIFY( (t <=> u) > 0 ); + + u.emplace(l); + VERIFY( (t == u) ); + VERIFY( !(t != u) ); + VERIFY( !(t < u) ); + VERIFY( (t <= u) ); + VERIFY( !(t > u) ); + VERIFY( (t <= u) ); + VERIFY( (t <=> u) == 0 ); + + u.emplace(h); + VERIFY( !(t == u) ); + VERIFY( (t != u) ); + VERIFY( (t < u) ); + VERIFY( (t <= u) ); + VERIFY( !(t > u) ); + VERIFY( !(t >= u) ); + VERIFY( (t <=> u) < 0 ); + + t.reset(); + u.emplace(l); + VERIFY( !(t == u) ); + VERIFY( (t != u) ); + VERIFY( (t < u) ); + VERIFY( (t <= u) ); + VERIFY( !(t > u) ); + VERIFY( !(t >= u) ); + VERIFY( (t <=> u) < 0 ); + + t.emplace(h); + VERIFY( !(t == u) ); + VERIFY( (t != u) ); + VERIFY( !(t < u) ); + VERIFY( !(t <= u) ); + VERIFY( (t > u) ); + VERIFY( (t >= u) ); + VERIFY( (t <=> u) > 0 ); +} + +template<typename V> +constexpr void +test_compare(V l, V h) +{ + test_compare_val<V&>(l, h); + test_compare_val<const V&>(l, h); + + test_compare_opts<V&, V&>(l, h); + test_compare_opts<V, V&>(l, h); + test_compare_opts<V&, V>(l, h); + + test_compare_opts<const V&, const V&>(l, h); + test_compare_opts<V, const V&>(l, h); + test_compare_opts<const V&, V>(l, h); + + test_compare_opts<V&, const V&>(l, h); + test_compare_opts<const V&, V&>(l, h); +} + +struct TreeWay +{ + int v; + friend auto operator<=>(TreeWay, TreeWay) = default; +}; + +struct Other +{ + int v; + + constexpr Other(int p) : v(p) {} + constexpr Other(TreeWay p) : v(p.v) {} + + friend bool operator==(Other, Other) = default; + friend auto operator<=>(Other, Other) = default; + + friend constexpr bool + operator==(const Other& lhs, const TreeWay& rhs) + { return lhs.v == rhs.v; } + + friend constexpr std::strong_ordering + operator<=>(const Other& lhs, const TreeWay& rhs) + { return lhs.v <=> rhs.v; } +}; + +constexpr void +test_heterogeneus_cmp() +{ + TreeWay l{10}; + Other h{20}; + + std::optional<TreeWay&> t; + std::optional<const Other&> u; + + VERIFY( (t == u) ); + VERIFY( !(t != u) ); + VERIFY( !(t < u) ); + VERIFY( (t <= u) ); + VERIFY( !(t > u) ); + VERIFY( (t >= u) ); + VERIFY( (t <=> u) == 0 ); + + t.emplace(l); + VERIFY( !(t == u) ); + VERIFY( (t != u) ); + VERIFY( !(t < u) ); + VERIFY( !(t <= u) ); + VERIFY( (t > u) ); + VERIFY( (t >= u) ); + VERIFY( (t <=> u) > 0 ); + + u.emplace(h); + VERIFY( !(t == u) ); + VERIFY( (t != u) ); + VERIFY( (t < u) ); + VERIFY( (t <= u) ); + VERIFY( !(t > u) ); + VERIFY( !(t >= u) ); + VERIFY( (t <=> u) < 0 ); +} + +int main() +{ + auto test_all = [] { + test_compare(2, 5); + test_compare(TreeWay{11}, TreeWay{12}); + test_heterogeneus_cmp(); + return true; + }; + + test_all(); + static_assert(test_all()); +} diff --git a/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc b/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc index 3e3932579286..ec47552c4753 100644 --- a/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc +++ b/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc @@ -8,8 +8,8 @@ # error "Feature-test macro for constrained_equality has wrong value" #endif -template<typename T, typename U = T> -concept eq_comparable +template<typename T, typename U> +concept eq_comparable_impl = requires (const std::optional<T>& t, const std::optional<U>& u) { t == u; *t == u; @@ -17,7 +17,19 @@ concept eq_comparable }; template<typename T, typename U = T> -concept ne_comparable +concept eq_comparable = + eq_comparable_impl<T, U> +#if __cplusplus > 202302l + && eq_comparable_impl<T&, U&> + && eq_comparable_impl<T const&, U const&> + && eq_comparable_impl<T const&, U&> + && eq_comparable_impl<T, U const&> + && eq_comparable_impl<T&, U> +#endif +; + +template<typename T, typename U> +concept ne_comparable_impl = requires (const std::optional<T>& t, const std::optional<U>& u) { t != u; *t != u; @@ -25,7 +37,19 @@ concept ne_comparable }; template<typename T, typename U = T> -concept lt_comparable +concept ne_comparable = + ne_comparable_impl<T, U> +#if __cplusplus > 202302l + && ne_comparable_impl<T&, U&> + && ne_comparable_impl<T const&, U const&> + && ne_comparable_impl<T const&, U&> + && ne_comparable_impl<T, U const&> + && ne_comparable_impl<T&, U> +#endif +; + +template<typename T, typename U> +concept lt_comparable_impl = requires (const std::optional<T>& t, const std::optional<U>& u) { t < u; *t < u; @@ -33,7 +57,19 @@ concept lt_comparable }; template<typename T, typename U = T> -concept le_comparable +concept lt_comparable = + lt_comparable_impl<T, U> +#if __cplusplus > 202302l + && lt_comparable_impl<T&, U&> + && lt_comparable_impl<T const&, U const&> + && lt_comparable_impl<T const&, U&> + && lt_comparable_impl<T, U const&> + && lt_comparable_impl<T&, U> +#endif +; + +template<typename T, typename U> +concept le_comparable_impl = requires (const std::optional<T>& t, const std::optional<U>& u) { t <= u; *t <= u; @@ -41,7 +77,19 @@ concept le_comparable }; template<typename T, typename U = T> -concept gt_comparable +concept le_comparable = + le_comparable_impl<T, U> +#if __cplusplus > 202302l + && le_comparable_impl<T&, U&> + && le_comparable_impl<T const&, U const&> + && le_comparable_impl<T const&, U&> + && le_comparable_impl<T, U const&> + && le_comparable_impl<T&, U> +#endif +; + +template<typename T, typename U> +concept gt_comparable_impl = requires (const std::optional<T>& t, const std::optional<U>& u) { t > u; *t > u; @@ -49,13 +97,37 @@ concept gt_comparable }; template<typename T, typename U = T> -concept ge_comparable +concept gt_comparable = + gt_comparable_impl<T, U> +#if __cplusplus > 202302l + && gt_comparable_impl<T&, U&> + && gt_comparable_impl<T const&, U const&> + && gt_comparable_impl<T const&, U&> + && gt_comparable_impl<T, U const&> + && gt_comparable_impl<T&, U> +#endif +; + +template<typename T, typename U> +concept ge_comparable_impl = requires (const std::optional<T>& t, const std::optional<U>& u) { t >= u; *t >= u; t >= *u; }; +template<typename T, typename U = T> +concept ge_comparable = + ge_comparable_impl<T, U> +#if __cplusplus > 202302l + && ge_comparable_impl<T&, U&> + && ge_comparable_impl<T const&, U const&> + && ge_comparable_impl<T const&, U&> + && ge_comparable_impl<T, U const&> + && ge_comparable_impl<T&, U> +#endif +; + static_assert( eq_comparable<int> ); static_assert( ne_comparable<int> ); static_assert( lt_comparable<int> ); diff --git a/libstdc++-v3/testsuite/20_util/optional/requirements.cc b/libstdc++-v3/testsuite/20_util/optional/requirements.cc index 68e59057b5e3..9e8cf83da449 100644 --- a/libstdc++-v3/testsuite/20_util/optional/requirements.cc +++ b/libstdc++-v3/testsuite/20_util/optional/requirements.cc @@ -26,8 +26,10 @@ # error "Feature test macro for optional has wrong value in <optional>" #elif __cplusplus == 202002L && __cpp_lib_optional != 202106L # error "Feature test macro for optional has wrong value for C++20 in <optional>" -#elif __cplusplus > 202002L && __cpp_lib_optional != 202110L -# error "Feature test macro for optional has wrong value for C++23 in <version>" +#elif __cplusplus == 202302L && __cpp_lib_optional != 202110L +# error "Feature test macro for optional has wrong value for C++23 in <optional>" +#elif __cplusplus > 202302L && __cpp_lib_optional != 202506L +# error "Feature test macro for optional has wrong value for C++26 in <optional>" #endif #include <testsuite_hooks.h> diff --git a/libstdc++-v3/testsuite/20_util/optional/requirements_neg.cc b/libstdc++-v3/testsuite/20_util/optional/requirements_neg.cc index 688c305803e2..142fbbfc5151 100644 --- a/libstdc++-v3/testsuite/20_util/optional/requirements_neg.cc +++ b/libstdc++-v3/testsuite/20_util/optional/requirements_neg.cc @@ -2,17 +2,32 @@ #include <optional> +// C++ < 26: // T shall be a type other than cv in_place_t or cv nullopt_t // that meets the Cpp17Destructible requirements +// C++ >= 26: +// A type X is a valid contained type for optional if X is an lvalue reference +// type or a complete non-array object type, and remove_cvref_t<X> is a type +// other than in_place_t or nullopt_t. If a specialization of optional +// is instantiated with a type T that is not a valid contained type for +// optional, the program is ill-formed. If T is an object type, +// T shall meet the Cpp17Destructible requirements std::optional<std::nullopt_t> o1; // { dg-error "here" } std::optional<const std::nullopt_t> o2; // { dg-error "here" } std::optional<std::in_place_t> o3; // { dg-error "here" } std::optional<const std::in_place_t> o4; // { dg-error "here" } -std::optional<int&> o5; // { dg-error "here" } +std::optional<int&> o5; // { dg-error "here" "optional<T&> is a C++26 feature" { target c++23_down } } std::optional<int[1]> o6; // { dg-error "here" } std::optional<int[]> o7; // { dg-error "here" } std::optional<int()> o8; // { dg-error "here" } +std::optional<const int &> o9; // { dg-error "here" "optional<T&> is a C++26 feature" { target c++23_down } } +std::optional<std::in_place_t &> o10; // { dg-error "here" } +std::optional<const std::in_place_t &> o11; // { dg-error "here" } +std::optional<std::nullopt_t &> o12; // { dg-error "here" } +std::optional<const std::nullopt_t &> o13; // { dg-error "here" } +std::optional<int &&> o14; // { dg-error "here" } +std::optional<const int &&> o15; // { dg-error "here" } // { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/20_util/optional/version.cc b/libstdc++-v3/testsuite/20_util/optional/version.cc index ba44aa525356..ae9339a01af8 100644 --- a/libstdc++-v3/testsuite/20_util/optional/version.cc +++ b/libstdc++-v3/testsuite/20_util/optional/version.cc @@ -9,8 +9,10 @@ # error "Feature test macro for optional has wrong value for C++17 in <version>" #elif __cplusplus == 202002L && __cpp_lib_optional != 202106L # error "Feature test macro for optional has wrong value for C++20 in <version>" -#elif __cplusplus > 202002L && __cpp_lib_optional != 202110L +#elif __cplusplus == 202302L && __cpp_lib_optional != 202110L # error "Feature test macro for optional has wrong value for C++23 in <version>" +#elif __cplusplus > 202302L && __cpp_lib_optional != 202506L +# error "Feature test macro for optional has wrong value for C++26 in <version>" #endif #if __cplusplus >= 202302L
