From: Giuseppe D'Angelo Date: Wed, 22 Oct 2025 07:31:46 +0000 (+0200) Subject: libstdc++: Implement optional from P2988R12 [PR121748] X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=23657d3972605542b02a33e67edcc74a7eede02c;p=thirdparty%2Fgcc.git libstdc++: Implement optional from P2988R12 [PR121748] This patch implements optional based on the P2988R12 paper, incorporating corrections from LWG4300, LWG4304, and LWG3467. The resolution for LWG4015 is also extended to cover optional. 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. This is necessary because moving an optional 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, 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({...}) (where T is not a reference type) now become ill-formed (see optional/make_optional_neg.cc). * make_optional(t) and make_optional(ct), where decltype(t) is T&, and decltype(ct) is const T& now produce optional and optional respectively, instead of optional. * a few other uses of make_optional 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): Use __is_valid_contained_type_for_optional. (optional(const optional<_Up>&), optional(optional<_Up>&&)) (optional::operator=(const optional<_Up>&)) (optional::operator=(optional<_Up>&&)): Replacex._M_get() with x._M_fwd(), and std::move(x._M_get()) with std::move(x)._M_fwd(). (optional::and_then): Remove uncessary remove_cvref_t. (optional::_M_fwd): Define. (std::optional): Define new partial specialization. (std::swap(std::optional, std::optional)): Define. (std::make_optional(_Tp&&)): Add non-type template parameter. (std::make_optional): Use parenthesis to constructor optional. (std::hash>): 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. * 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 Co-authored-by: Tomasz KamiƄski --- 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 + 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 + 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>, nullopt_t> + && !is_same_v>, 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, nullopt_t>); - static_assert(!is_same_v, 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 @@ -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 @@ -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) { if (__t) - emplace(__t._M_get()); + emplace(__t._M_fwd()); } template) { if (__t) - emplace(std::move(__t._M_get())); + emplace(std::move(__t)._M_fwd()); } template) { if (__t) - emplace(std::move(__t._M_get())); + emplace(std::move(__t)._M_fwd()); } template_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>; - static_assert(__is_optional_v>, + static_assert(__is_optional_v<_Up>, "the function passed to std::optional::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>; - static_assert(__is_optional_v>, + static_assert(__is_optional_v<_Up>, "the function passed to std::optional::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>; - static_assert(__is_optional_v>, + static_assert(__is_optional_v<_Up>, "the function passed to std::optional::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 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 + 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 + 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 + requires (!is_same_v, optional>) + && (!is_same_v, 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 + requires (!is_same_v, optional>) + && (!is_same_v, 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 & + template + requires (!is_same_v, 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 + requires (!is_same_v, 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& + template + requires (!is_same_v, 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) + constexpr + optional(const optional<_Up>& __rhs) + noexcept(is_nothrow_constructible_v<_Tp&, _Up&>) + { + if (__rhs) + __convert_ref_init_val(__rhs._M_fwd()); + } + + template + requires (!is_same_v, 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) + constexpr + optional(const optional<_Up>& __rhs) = delete; + + // optional&& + template + requires (!is_same_v, 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 + requires (!is_same_v, 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&& + template + requires (!is_same_v, optional<_Up>>) + && (!is_same_v<_Tp&, _Up>) + && is_constructible_v<_Tp&, const _Up> + && (!reference_constructs_from_temporary_v<_Tp&, _Up>) + explicit(!is_convertible_v) + 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 + requires (!is_same_v, 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) + 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 + 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::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 is ill-formed due to value_or + template> + 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 + constexpr auto + and_then(_Fn&& __f) const + { + using _Up = remove_cvref_t>; + static_assert(__is_optional_v<_Up>, + "the function passed to std::optional::and_then " + "must return a std::optional"); + if (has_value()) + return std::__invoke(std::forward<_Fn>(__f), *_M_val); + else + return _Up(); + } + + template + constexpr + optional>> + transform(_Fn&& __f) const + { + using _Up = remove_cv_t>; + if (has_value()) + return optional<_Up>(_Optional_func<_Fn>{__f}, *_M_val); + else + return optional<_Up>(); + } + + template + requires invocable<_Fn> + constexpr + optional + or_else(_Fn&& __f) const + { + static_assert(is_same_v>, optional>, + "the function passed to std::optional::or_else " + "must return a std::optional"); + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4367. Improve optional::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 friend class optional; + + template + constexpr + void + __convert_ref_init_val(_Up&& __u) + noexcept + { + _Tp& __r(std::forward<_Up>(__u)); + _M_val = std::addressof(__r); + } + + template + 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 using __optional_relop_t = enable_if_t, 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. + template + constexpr void + swap(optional<_Tp&>& __lhs, optional<_Tp&>& __rhs) noexcept + { __lhs.swap(__rhs); } +#endif + // _GLIBCXX_RESOLVE_LIB_DEFECTS // 2766. Swapping non-swappable types template enable_if_t && is_swappable_v<_Tp>)> swap(optional<_Tp>&, optional<_Tp>&) = delete; +#if __cpp_lib_optional >= 202506L + template +#else template +#endif constexpr enable_if_t, _Tp>, optional>> make_optional(_Tp&& __t) noexcept(is_nothrow_constructible_v>, _Tp>) - { return optional>{ std::forward<_Tp>(__t) }; } + { return optional>( std::forward<_Tp>(__t) ); } template 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 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 struct hash> + // hash for optional is disabled because is_hash_enabled_for is false : public __conditional_t<__is_hash_enabled_for>, __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::value ); static_assert( noexcept(std::make_optional(i)) ); static_assert( ! can_make_optional2::value ); static_assert( can_make_optional2::value ); +#if __cplusplus <= 202302 static_assert( noexcept(std::make_optional({})) ); +#endif static_assert( can_make_optional2::value ); static_assert( ! noexcept(std::make_optional(c)) ); static_assert( can_make_optional2::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 +#include + +struct S { int x; int* p; }; +int v; + +auto os1 = std::make_optional({1, &v}); // { dg-error "no matching function for" "" { target c++26 } } + +struct Cont +{ + Cont(); + Cont(std::initializer_list, int); +}; + +auto oc1 = std::make_optional({}); // { 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 + +struct S { int x; int y; }; + +void test() +{ + std::optional o; + const std::optional& co = o; + + o.transform(&S::x); // { dg-error "from here" "optional" { target c++23_down } } + co.transform(&S::x); // { dg-error "from here" "optional" { target c++23_down } } + std::move(o).transform(&S::x); // { dg-error "from here" "optional" } + std::move(co).transform(&S::x); // { dg-error "from here" "optional" } +} + +// { 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 +#include +#include + +struct NonMovable +{ + constexpr NonMovable() {} + NonMovable(NonMovable&&) = delete; +}; + +template +constexpr +void test_value(T& t) +{ + std::optional o1(t); + const std::optional co1(t); + + static_assert( std::is_same_v ); + VERIFY( &o1.value() == &t ); + + static_assert( std::is_same_v ); + VERIFY( &co1.value() == &t ); + + static_assert( std::is_same_v ); + VERIFY( &std::move(o1).value() == &t ); + + static_assert( std::is_same_v ); + VERIFY( &std::move(co1).value() == &t ); + + std::optional o2(t); + static_assert( std::is_same_v ); + 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 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 +concept has_value_or_for = requires(std::optional t, T& u) +{ t.value_or(u); }; + +static_assert( has_value_or_for ); +static_assert( has_value_or_for ); +static_assert( !has_value_or_for ); +static_assert( has_value_or_for ); +static_assert( !has_value_or_for ); +static_assert( has_value_or_for ); + +int i; +NonMovable nm; +int arr[2]; +int foo(); + +int main() +{ + auto test_all = [] { + test_value(i); + test_value(nm); + test_value(arr); + test_value(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 +#include +#include + +struct NonTrivial +{ + constexpr NonTrivial() {} + constexpr NonTrivial(NonTrivial const&) {}; + constexpr ~NonTrivial() {}; +}; + +struct NonMovable +{ + NonMovable() = default; + NonMovable(NonMovable&&) = delete; +}; + +template +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 +void +test_trivial() +{ + static_assert(std::is_copy_assignable_v>); + static_assert(std::is_move_assignable_v>); +} + +constexpr void +test_trivial_all() +{ + test_trivial(); + test_trivial(); + test_trivial>(); +} + +constexpr void +test_copy() +{ + Tracker t, u; + std::optional e; + std::optional o1(t); + std::optional 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 +concept can_emplace = requires (T t, U&& u) +{ t.emplace(std::forward(u)); }; + +constexpr void +test_from_value() +{ + NonTrivial v, u; + const NonTrivial& cv = v; + const std::optional s(u); + std::optional o1; + std::optional 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, + const NonTrivial&> ); + static_assert( !std::is_assignable_v, + NonTrivial> ); + static_assert( !std::is_assignable_v, + 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, const NonTrivial&> ); + static_assert( !can_emplace, NonTrivial> ); + static_assert( !can_emplace, 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, + NonTrivial> ); + static_assert( !std::is_assignable_v, + 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, const NonTrivial> ); + static_assert( !can_emplace, NonTrivial> ); + + + // Conversion create a pr-value that would bind to temporary + static_assert( !std::is_assignable_v, + Conv&> ); + static_assert( !std::is_assignable_v, + const Conv&> ); + static_assert( !std::is_assignable_v, + Conv> ); + static_assert( !std::is_assignable_v, + const Conv> ); + + static_assert( !can_emplace, Conv&> ); + static_assert( !can_emplace, const Conv&> ); + static_assert( !can_emplace, Conv> ); + static_assert( !can_emplace, const Conv> ); + + Conv rw(v); + const Conv 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 v(std::in_place); + const std::optional& cv = v; + + const std::optional s(u); + std::optional o1; + std::optional 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, + const std::optional&> ); + static_assert( !std::is_assignable_v, + std::optional> ); + static_assert( !std::is_assignable_v, + const std::optional> ); + + 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> ); + static_assert( !std::is_assignable_v, + const std::optional> ); + + // Conversion create a pr-value that would bind to temporary + static_assert( !std::is_assignable_v, + std::optional>&> ); + static_assert( !std::is_assignable_v, + const std::optional>&> ); + static_assert( !std::is_assignable_v, + std::optional>> ); + static_assert( !std::is_assignable_v, + const std::optional>> ); + + std::optional> rw(*v); + std::optional> 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 er; + std::optional r(t); + const std::optional cr(t); + + std::optional 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 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>); + 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 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 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 +#include +#include + +struct NonTrivial +{ + constexpr NonTrivial() {} + constexpr NonTrivial(NonTrivial const&) {}; + constexpr ~NonTrivial() {}; +}; + +struct NonMovable +{ + constexpr NonMovable() {} + NonMovable(NonMovable&&) = delete; +}; + +template +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 +void +test_trivial() +{ + static_assert(std::is_trivially_copyable_v>); + static_assert(std::is_copy_constructible_v>); + static_assert(std::is_move_constructible_v>); + static_assert(std::is_destructible_v>); +} + +constexpr void +test_trivial_all() +{ + test_trivial(); + test_trivial(); + test_trivial(); + test_trivial>(); +} + +constexpr void +test_copy() +{ + Tracker t; + std::optional o1(t); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &t ); + VERIFY( t.copy == 0 ); + VERIFY( t.move == 0 ); + + std::optional 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 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 e; + VERIFY( !e.has_value() ); + + std::optional o4(e); + VERIFY( !e.has_value() ); + VERIFY( !o4.has_value() ); + + std::optional 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 o1(v); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + static_assert( !std::is_constructible_v, + const NonTrivial&> ); + static_assert( !std::is_constructible_v, + NonTrivial> ); + static_assert( !std::is_constructible_v, + const NonTrivial> ); + + std::optional o2(std::in_place, v); + VERIFY( o2.has_value() ); + VERIFY( &o2.value() == &v ); + static_assert( !std::is_constructible_v, + std::in_place_t, const NonTrivial&> ); + static_assert( !std::is_constructible_v, + std::in_place_t, NonTrivial> ); + static_assert( !std::is_constructible_v, + std::in_place_t, const NonTrivial> ); + + std::optional co1(v); + VERIFY( co1.has_value() ); + VERIFY( &co1.value() == &v ); + std::optional co2(cv); + VERIFY( co2.has_value() ); + VERIFY( &co2.value() == &v ); + // No binding to rvalue + static_assert( !std::is_constructible_v, + NonTrivial> ); + static_assert( !std::is_constructible_v, + const NonTrivial> ); + + std::optional co3(std::in_place, v); + VERIFY( co3.has_value() ); + VERIFY( &co3.value() == &v ); + std::optional co4(std::in_place, cv); + VERIFY( co4.has_value() ); + VERIFY( &co4.value() == &v ); + // No binding to rvalue + static_assert( !std::is_constructible_v, + std::in_place_t, NonTrivial> ); + static_assert( !std::is_constructible_v, + std::in_place_t, const NonTrivial> ); + + // Conversion create a pr-value that would bind to temporary + static_assert( !std::is_constructible_v, + Conv&> ); + static_assert( !std::is_constructible_v, + const Conv&> ); + static_assert( !std::is_constructible_v, + Conv> ); + static_assert( !std::is_constructible_v, + const Conv> ); + + static_assert( !std::is_constructible_v, + std::in_place_t, Conv&> ); + static_assert( !std::is_constructible_v, + std::in_place_t, const Conv&> ); + static_assert( !std::is_constructible_v, + std::in_place_t, Conv> ); + static_assert( !std::is_constructible_v, + std::in_place_t, const Conv> ); + + Conv rw(v); + const Conv crw(v); + + std::optional ro1(rw); + VERIFY( ro1.has_value() ); + VERIFY( &ro1.value() == &v ); + std::optional ro2(crw); + VERIFY( ro2.has_value() ); + VERIFY( &ro2.value() == &v ); + std::optional ro3(std::move(rw)); + VERIFY( ro3.has_value() ); + VERIFY( &ro3.value() == &v ); + std::optional ro4(std::move(crw)); + VERIFY( ro4.has_value() ); + VERIFY( &ro4.value() == &v ); + + std::optional ro5(std::in_place, rw); + VERIFY( ro5.has_value() ); + VERIFY( &ro5.value() == &v ); + std::optional ro6(std::in_place, crw); + VERIFY( ro6.has_value() ); + VERIFY( &ro6.value() == &v ); + std::optional ro7(std::in_place, std::move(rw)); + VERIFY( ro7.has_value() ); + VERIFY( &ro7.value() == &v ); + std::optional ro8(std::in_place, std::move(crw)); + VERIFY( ro8.has_value() ); + VERIFY( &ro8.value() == &v ); +} + +constexpr void +test_from_opt_value() +{ + std::optional v(std::in_place); + const std::optional& cv = v; + + std::optional o1(v); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v.value() ); + static_assert( !std::is_constructible_v, + const std::optional&> ); + static_assert( !std::is_constructible_v, + std::optional> ); + static_assert( !std::is_constructible_v, + const std::optional> ); + + std::optional co1(v); + VERIFY( co1.has_value() ); + VERIFY( &co1.value() == &v.value() ); + std::optional co2(cv); + VERIFY( co2.has_value() ); + VERIFY( &co2.value() == &v.value() ); + // No binding to rvalue + static_assert( !std::is_constructible_v, + std::optional> ); + static_assert( !std::is_constructible_v, + const std::optional> ); + + // Conversion create a pr-value that would bind to temporary + static_assert( !std::is_constructible_v, + std::optional>&> ); + static_assert( !std::is_constructible_v, + const std::optional>&> ); + static_assert( !std::is_constructible_v, + std::optional>> ); + static_assert( !std::is_constructible_v, + const std::optional>> ); + + std::optional> rw(*v); + std::optional> crw(*v); + + std::optional ro1(rw); + VERIFY( ro1.has_value() ); + VERIFY( &ro1.value() == &v.value() ); + std::optional ro2(crw); + VERIFY( ro2.has_value() ); + VERIFY( &ro2.value() == &v.value() ); + std::optional ro3(std::move(rw)); + VERIFY( ro3.has_value() ); + VERIFY( &ro3.value() == &v.value() ); + std::optional ro4(std::move(crw)); + VERIFY( ro4.has_value() ); + VERIFY( &ro4.value() == &v.value() ); +} + +constexpr void +test_to_opt_value() +{ + Tracker t; + std::optional r(t); + const std::optional cr(t); + + std::optional o1(r); + VERIFY( o1.has_value() ); + VERIFY( o1->copy == 1 ); + VERIFY( o1->move == 0 ); + + std::optional o2(cr); + VERIFY( o2.has_value() ); + VERIFY( o2->copy == 1 ); + VERIFY( o2->move == 0 ); + + std::optional o3(std::move(r)); + VERIFY( o3.has_value() ); + VERIFY( o3->copy == 1 ); + VERIFY( o3->move == 0 ); + + std::optional o4(std::move(cr)); + VERIFY( o4.has_value() ); + VERIFY( o4->copy == 1 ); + VERIFY( o4->move == 0 ); + + std::optional er; + const std::optional cer; + + std::optional e1(er); + VERIFY( !e1.has_value() ); + + std::optional e2(cer); + VERIFY( !e2.has_value() ); + + std::optional e3(std::move(er)); + VERIFY( !e3.has_value() ); + + std::optional e4(std::move(cer)); + VERIFY( !e4.has_value() ); +} + +constexpr void +test_opt_opt() +{ + std::optional s(43); + + std::optional&> o1(s); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &s ); + + std::optional&> o2(std::in_place, s); + VERIFY( o2.has_value() ); + VERIFY( &o2.value() == &s ); + + std::optional> o3(o1); + VERIFY( o2.has_value() ); + VERIFY( o2.value().has_value() ); + VERIFY( o2.value() == 43 ); + + s.reset(); + std::optional&> o4(s); + VERIFY( o4.has_value() ); + VERIFY( &o4.value() == &s ); + + std::optional&> o5(std::in_place, s); + VERIFY( o5.has_value() ); + VERIFY( &o5.value() == &s ); + + std::optional> o6(o1); + VERIFY( o6.has_value() ); + VERIFY( !o6.value().has_value() ); + + std::optional> s2(std::in_place); + std::optional&> oo1(s2); + VERIFY( oo1.has_value() ); + VERIFY( &oo1.value() == &s2.value() ); + + s2.reset(); + std::optional&> 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 +#include + +template +constexpr bool _Never_valueless + = std::__detail::__variant::_Never_valueless_alt::value; + +static_assert( _Never_valueless> ); +static_assert( _Never_valueless> ); 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 +#include +#include + +struct NonTrivial +{ + constexpr NonTrivial() {} + constexpr NonTrivial(NonTrivial const&) {}; + constexpr ~NonTrivial() {}; +}; + +template +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(t); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &t ); + + auto o2 = std::make_optional(t); + VERIFY( o2.has_value() ); + VERIFY( &o2.value() == &t ); + + auto o3 = std::make_optional(ct); + VERIFY( o3.has_value() ); + VERIFY( &o3.value() == &t ); + + Conv rw(t); + auto o4 = std::make_optional(rw); + VERIFY( o4.has_value() ); + VERIFY( &o4.value() == &t ); + + auto o5 = std::make_optional(std::as_const(rw)); + VERIFY( o5.has_value() ); + VERIFY( &o5.value() == &t ); + + auto o6 = std::make_optional(Conv(t)); + VERIFY( o6.has_value() ); + VERIFY( &o6.value() == &t ); +#else + auto o1 = std::make_optional(t); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() != &t ); + + auto o3 = std::make_optional(ct); + VERIFY( o3.has_value() ); + VERIFY( &o3.value() != &t ); + + auto o2 = std::make_optional(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 + +struct C { + C(); + C(int); +}; +C s(10); +const C cs(1); + +template +using decay_pre26 = +#if __cplusplus > 202302 + T; +#else + std::decay_t; +#endif + +auto z1 = std::make_optional(); // { dg-error "no matching function for call" } +auto z2 = std::make_optional(); // { dg-error "no matching function for call" } +auto z3 = std::make_optional(); // { dg-error "no matching function for call" } +auto z4 = std::make_optional(); // { dg-error "no matching function for call" } + +auto o1 = std::make_optional(10); // { dg-error "no matching function for call" } +auto o2 = std::make_optional(10); // { dg-error "from here" } +auto o3 = std::make_optional(10); // { dg-error "from here" } +auto o4 = std::make_optional(10); // { dg-error "from here" } + +auto t1 = std::make_optional(10, 20); // { dg-error "no matching function for call" } +auto t2 = std::make_optional(10, 20); // { dg-error "no matching function for call" } +auto t3 = std::make_optional(10, 20); // { dg-error "no matching function for call" } +auto t3 = std::make_optional(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 +#include + +struct C { + C(); + C(int); +}; +C s(10); +const C cs(1); + +template +using decay_pre26 = +#if __cplusplus > 202302 + T; +#else + std::decay_t; +#endif + +auto lr1 = std::make_optional(s); // changed meaning +static_assert( std::is_same_v< decltype(lr1), std::optional>> ); +auto lr2 = std::make_optional(s); // { dg-error "here" "" { target c++23_down } } +auto lr3 = std::make_optional(s); // { dg-error "no matching function for call" } +auto lr4 = std::make_optional(s); // { dg-error "no matching function for call" } + +auto clr1 = std::make_optional(cs); // { dg-error "no matching function for call" } +auto clr2 = std::make_optional(cs); // changed meaning +static_assert( std::is_same_v< decltype(clr2), std::optional>> ); +auto clr3 = std::make_optional(cs); // { dg-error "no matching function for call" } +auto clr3 = std::make_optional(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 +#include + +struct C { + C(); + C(int); +}; +C s(10); +const C cs(1); + +template +using decay_pre26 = +#if __cplusplus > 202302 + T; +#else + std::decay_t; +#endif + +auto p1 = std::make_optional(C(10)); // { dg-error "no matching function for call" } +auto p2 = std::make_optional(C(10)); // { dg-error "from here" } +auto p3 = std::make_optional(C(10)); // { dg-error "from here" "" { target c++26 } } +auto p4 = std::make_optional(C(10)); // { dg-error "from here" } + +auto b1 = std::make_optional({10}); // { dg-error "no matching function for call" } +auto b2 = std::make_optional({10}); // { dg-error "no matching function for call" "" { target c++26 } } +auto b3 = std::make_optional({10}); // { dg-error "no matching function for call" "" { target c++26 } } +auto b4 = std::make_optional({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 +#include +#include +#include + +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 +auto identity_of = [](U&& t) -> std::optional +{ + static_assert( std::is_same_v ); + VERIFY( t.copy == 0 ); + VERIFY( t.move == 0 ); + return std::optional(t); +}; + +constexpr void +test_and_then() +{ + std::optional t(std::in_place); + std::optional rt(t); + std::optional rct(t); + + auto r1 = t.and_then(identity_of); + VERIFY( r1.has_value() ); + VERIFY( &r1.value() == &t.value() ); + + auto r2 = rt.and_then(identity_of); + VERIFY( r2.has_value() ); + VERIFY( &r2.value() == &t.value() ); + + std::as_const(rt).and_then(identity_of); + std::move(rt).and_then(identity_of); + + auto r4 = rct.and_then(identity_of); + VERIFY( r4.has_value() ); + VERIFY( &r4.value() == &t.value() ); + + std::as_const(rct).and_then(identity_of); + std::move(rct).and_then(identity_of); + + auto r5 = rt.and_then([](Tracker&) { return std::optional(42); }); + static_assert( std::is_same_v> ); + VERIFY( r5.has_value() ); + VERIFY( r5.value() == 42 ); + + auto r6 = rct.and_then([](const Tracker&) { return std::optional(); }); + static_assert( std::is_same_v> ); + VERIFY( !r6.has_value() ); + + rct.reset(); + auto r7 = rct.and_then([](const Tracker&) { VERIFY(false); return std::optional(42); }); + static_assert( std::is_same_v> ); + VERIFY( !r7.has_value() ); + + rt.reset(); + auto r8 = rt.and_then([](Tracker&) { VERIFY(false); return std::optional(); }); + static_assert( std::is_same_v> ); + VERIFY( !r8.has_value() ); +} + +template +constexpr void +test_or_else() +{ + T t, u; + + std::optional ot(t); + auto r1 = ot.or_else([&] { VERIFY(false); return std::optional(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(); }); + VERIFY( &ot.value() == &t ); + VERIFY( r2.has_value() ); + VERIFY( &r2.value() == &t ); + + ot.reset(); + auto r3 = ot.or_else([&] { return std::optional(u); }); + VERIFY( !ot.has_value() ); + VERIFY( r3.has_value() ); + VERIFY( &r3.value() == &u ); + auto r4 = std::move(ot).or_else([] { return std::optional(); }); + VERIFY( !ot.has_value() ); + VERIFY( !r4.has_value() ); +} + +constexpr void +test_transform() +{ + std::optional t(std::in_place); + + auto r1 = t.transform(&Tracker::copy); + static_assert( std::is_same_v> ); + VERIFY( r1.has_value() ); + VERIFY( &r1.value() == &t->copy ); + auto r2 = std::as_const(t).transform(&Tracker::move); + static_assert( std::is_same_v> ); + VERIFY( r2.has_value() ); + VERIFY( &r2.value() == &t->move ); + + std::optional rt(t); + auto r3 = rt.transform(&Tracker::copy); + static_assert( std::is_same_v> ); + VERIFY( r3.has_value() ); + VERIFY( &r3.value() == &t->copy ); + auto r4 = std::as_const(rt).transform(&Tracker::copy); + static_assert( std::is_same_v> ); + VERIFY( r4.has_value() ); + VERIFY( &r4.value() == &t->copy ); + auto r5 = std::move(rt).transform(&Tracker::copy); + static_assert( std::is_same_v> ); + VERIFY( r5.has_value() ); + VERIFY( &r5.value() == &t->copy ); + + auto r6 = rt.transform([] (Tracker& t) { return 10; }); + static_assert( std::is_same_v> ); + 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> ); + VERIFY( r7.has_value() ); + + rt.reset(); + auto r8 = rt.transform([] (Tracker& t) { VERIFY(false); return 42; }); + static_assert( std::is_same_v> ); + VERIFY( !r8.has_value() ); + + std::optional crt(t); + auto r9 = crt.transform(&Tracker::copy); + static_assert( std::is_same_v> ); + VERIFY( r9.has_value() ); + VERIFY( &r9.value() == &t->copy ); + auto r10 = std::as_const(crt).transform(&Tracker::copy); + static_assert( std::is_same_v> ); + VERIFY( r10.has_value() ); + VERIFY( &r10.value() == &t->copy ); + auto r11 = std::move(crt).transform(&Tracker::copy); + static_assert( std::is_same_v> ); + 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> ); + VERIFY( !r12.has_value() ); +} + +int main() +{ + auto test_all = [] { + test_and_then(); + test_transform(); + test_or_else(); + test_or_else(); + test_or_else(); + 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 +#include +#include +#include + +template> +constexpr bool has_disabled_hash + = !std::is_default_constructible_v + && !std::is_copy_constructible_v + && !std::is_move_constructible_v + && !std::is_copy_assignable_v + && !std::is_move_assignable_v; + +static_assert(has_disabled_hash>); +static_assert(has_disabled_hash>); + +template +constexpr void +test_compare_val(V& l, V& h) +{ + std::optional 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 +constexpr void +test_compare_opts(V& l, V& h) +{ + std::optional t; + std::optional 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 +constexpr void +test_compare(V l, V h) +{ + test_compare_val(l, h); + test_compare_val(l, h); + + test_compare_opts(l, h); + test_compare_opts(l, h); + test_compare_opts(l, h); + + test_compare_opts(l, h); + test_compare_opts(l, h); + test_compare_opts(l, h); + + test_compare_opts(l, h); + test_compare_opts(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 t; + std::optional 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 -concept eq_comparable +template +concept eq_comparable_impl = requires (const std::optional& t, const std::optional& u) { t == u; *t == u; @@ -17,7 +17,19 @@ concept eq_comparable }; template -concept ne_comparable +concept eq_comparable = + eq_comparable_impl +#if __cplusplus > 202302l + && eq_comparable_impl + && eq_comparable_impl + && eq_comparable_impl + && eq_comparable_impl + && eq_comparable_impl +#endif +; + +template +concept ne_comparable_impl = requires (const std::optional& t, const std::optional& u) { t != u; *t != u; @@ -25,7 +37,19 @@ concept ne_comparable }; template -concept lt_comparable +concept ne_comparable = + ne_comparable_impl +#if __cplusplus > 202302l + && ne_comparable_impl + && ne_comparable_impl + && ne_comparable_impl + && ne_comparable_impl + && ne_comparable_impl +#endif +; + +template +concept lt_comparable_impl = requires (const std::optional& t, const std::optional& u) { t < u; *t < u; @@ -33,7 +57,19 @@ concept lt_comparable }; template -concept le_comparable +concept lt_comparable = + lt_comparable_impl +#if __cplusplus > 202302l + && lt_comparable_impl + && lt_comparable_impl + && lt_comparable_impl + && lt_comparable_impl + && lt_comparable_impl +#endif +; + +template +concept le_comparable_impl = requires (const std::optional& t, const std::optional& u) { t <= u; *t <= u; @@ -41,7 +77,19 @@ concept le_comparable }; template -concept gt_comparable +concept le_comparable = + le_comparable_impl +#if __cplusplus > 202302l + && le_comparable_impl + && le_comparable_impl + && le_comparable_impl + && le_comparable_impl + && le_comparable_impl +#endif +; + +template +concept gt_comparable_impl = requires (const std::optional& t, const std::optional& u) { t > u; *t > u; @@ -49,13 +97,37 @@ concept gt_comparable }; template -concept ge_comparable +concept gt_comparable = + gt_comparable_impl +#if __cplusplus > 202302l + && gt_comparable_impl + && gt_comparable_impl + && gt_comparable_impl + && gt_comparable_impl + && gt_comparable_impl +#endif +; + +template +concept ge_comparable_impl = requires (const std::optional& t, const std::optional& u) { t >= u; *t >= u; t >= *u; }; +template +concept ge_comparable = + ge_comparable_impl +#if __cplusplus > 202302l + && ge_comparable_impl + && ge_comparable_impl + && ge_comparable_impl + && ge_comparable_impl + && ge_comparable_impl +#endif +; + static_assert( eq_comparable ); static_assert( ne_comparable ); static_assert( lt_comparable ); 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 " #elif __cplusplus == 202002L && __cpp_lib_optional != 202106L # error "Feature test macro for optional has wrong value for C++20 in " -#elif __cplusplus > 202002L && __cpp_lib_optional != 202110L -# error "Feature test macro for optional has wrong value for C++23 in " +#elif __cplusplus == 202302L && __cpp_lib_optional != 202110L +# error "Feature test macro for optional has wrong value for C++23 in " +#elif __cplusplus > 202302L && __cpp_lib_optional != 202506L +# error "Feature test macro for optional has wrong value for C++26 in " #endif #include 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 +// 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 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 o1; // { dg-error "here" } std::optional o2; // { dg-error "here" } std::optional o3; // { dg-error "here" } std::optional o4; // { dg-error "here" } -std::optional o5; // { dg-error "here" } +std::optional o5; // { dg-error "here" "optional is a C++26 feature" { target c++23_down } } std::optional o6; // { dg-error "here" } std::optional o7; // { dg-error "here" } std::optional o8; // { dg-error "here" } +std::optional o9; // { dg-error "here" "optional is a C++26 feature" { target c++23_down } } +std::optional o10; // { dg-error "here" } +std::optional o11; // { dg-error "here" } +std::optional o12; // { dg-error "here" } +std::optional o13; // { dg-error "here" } +std::optional o14; // { dg-error "here" } +std::optional 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 " #elif __cplusplus == 202002L && __cpp_lib_optional != 202106L # error "Feature test macro for optional has wrong value for C++20 in " -#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 " +#elif __cplusplus > 202302L && __cpp_lib_optional != 202506L +# error "Feature test macro for optional has wrong value for C++26 in " #endif #if __cplusplus >= 202302L